report_EncryptMin.js

/**
 *  File    : report/Encryption.js
 *  Created : 18/06/2015
 *  By      : Francesc Busquets <francesc@gmail.com>
 *
 *  JClic.js
 *  An HTML5 player of JClic activities
 *  https://projectestac.github.io/jclic.js
 *
 *  @source https://github.com/projectestac/jclic.js
 *
 *  @license EUPL-1.2
 *  @licstart
 *  (c) 2000-2020 Educational Telematic Network of Catalonia (XTEC)
 *
 *  Licensed under the EUPL, Version 1.1 or -as soon they will be approved by
 *  the European Commission- subsequent versions of the EUPL (the "Licence");
 *  You may not use this work except in compliance with the Licence.
 *
 *  You may obtain a copy of the Licence at:
 *  https://joinup.ec.europa.eu/software/page/eupl
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the Licence is distributed on an "AS IS" basis, WITHOUT
 *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 *  Licence for the specific language governing permissions and limitations
 *  under the Licence.
 *  @licend
 *  @module
 */

/**
 *
 * Utilities to encrypt and decrypt strings using simple methods, just to avoid write
 * passwords in plain text in data and configuration files. Do not use it as a
 * secure cryptographic system!
 *
 * Based on {@link https://github.com/projectestac/jclic/blob/master/src/utilities/edu/xtec/util/Encryption.java Encryption}
 * utilities, created by Albert Llastarri for {@link https://github.com/projectestac/jclic JClic}.
 *
 * IMPORTANT: This is a shortened version of Encryption with only the methods needed to decrypt
 * stored passwords. Full version is on file `src/misc/encryption/Encryption.js`
 *
 * @abstract
 */
export class Encryption {
  /**
   * Decrypts the given code
   * @param {string} txt - Code to be decrypted
   * @returns {string}
   */
  static Decrypt(txt) {
    if (txt === null || txt.length === 0)
      return null;
    const s = Encryption.decodify(txt);
    return s === Encryption.BLANK ? '' : s;
  }

  /**
   * @param {string} cA (was char[])
   * @param {number} fromIndex
   * @returns {string} (was char)
   */
  static hexCharArrayToChar(cA, fromIndex) {
    let n = 0;
    for (let i = 0; i <= 3; i++) {
      const j = Number.parseInt(cA[fromIndex + i], 16);
      if (isNaN(j))
        throw 'Invalid expression!';
      else
        n = n * 16 + j;
    }
    return String.fromCharCode(n);
  }

  /**
   * @param {string} cA - (was char[])
   * @param {number} fromIndex
   * @returns {number}
   */
  static hexCharArrayToInt(cA, fromIndex) {
    let n = 0;
    for (let i = 0; i <= 1; i++) {
      const j = Number.parseInt(cA[fromIndex + i], 16);
      if (isNaN(j))
        throw 'Invalid expression!';
      else
        n = n * 16 + j;
    }
    return n;
  }

  /**
   * @param {string} cA - (was char[])
   * @returns {string}
   */
  static decodifyZerosField(cA) {
    let
      sb = '',
      num = Number.parseInt(cA[0], 32),
      k = 0,
      i = 0;

    for (i = 0; num !== 0; i++) {
      while (num > 0) {
        sb = sb + cA[i * 3 + 1] + cA[i * 3 + 2];
        num--;
        k++;
      }
      if (cA.length > i * 3 + 3)
        num = Number.parseInt(cA[i * 3 + 3], 32);
      else
        num = 0;
    }
    for (let j = i * 3 + 1; j < cA.length; j++)
      sb = sb + cA[j];

    return Number.parseInt(k, 32) + sb;
  }

  /**
   * @param {string} cA - (was char[])
   * @returns {string} (was StringBuilder)
   */
  static decompressZeros(cA) {
    cA = Encryption.decodifyZerosField(cA);
    let
      numBytesZeros = Number.parseInt(cA[0], 32),
      iniNoZeros = numBytesZeros * 2 + 1,
      bFi = false,
      sb = '';

    for (let i = 0; i < numBytesZeros && !bFi; i++) {
      const zeros = Encryption.hexCharArrayToInt(cA, 1 + i * 2);
      let s = zeros.toString(2);
      while (s.length < 8)
        s = '0' + s;
      for (let j = 0; j <= 7 && !bFi; j++) {
        if (s[j] === '1')
          sb = sb + '0';
        else if (iniNoZeros < cA.length)
          sb = sb + cA[iniNoZeros++];
        else
          bFi = true;
      }
    }
    return sb;
  }

  /**
   * @param {string} sb1 - (was StringBuilder)
   * @returns {string}
   */
  static decodifyFromHex(sb1) {
    let sb = '', j = 0;
    for (let i = 0; j < sb1.length; i++) {
      const c = Encryption.hexCharArrayToChar(sb1, j);
      sb = sb + c;
      j += 4;
    }
    return sb;
  }

  /**
   * @param {string} s
   * @returns {string} (was char[])
   */
  static unchangeOrder(s) {
    let m = 0, n = s.length - 1;
    const cA = [];
    for (let p = 0; p < s.length; p++)
      cA[p] = '';
    for (let i = 0; i < s.length; i++)
      if (i % 2 === 0)
        cA[i] = s[m++];
      else
        cA[i] = s[n--];
    return cA.join('');
  }

  /**
   * @param {string} word
   * @returns {string}
   */
  static codify(word) {
    if (word.length > 24)
      throw 'Password is too large!';
    return Encryption.changeOrder(Encryption.compressZeros(Encryption.codifyToHexWord(word)));
  }

  /**
   * @param {string} word
   * @returns {string}
   */
  static decodify(word) {
    try {
      return Encryption.decodifyFromHex(Encryption.decompressZeros(Encryption.unchangeOrder(word)));
    } catch (_ex) { //The supplied word was not codified using this system
      return '';
    }
  }
}

/**
* Default bank password
* @type {string}
*/
Encryption.BLANK = '___blank___##';

export default Encryption;