Home Reference Source Test

src/hasher/md4.mjs

'use strict';

import Hasher32le from "./hasher32le";
import {rotateLeft} from "../tools/tools";

// Transform constants
/** @type {number[]} */
const S = [
  [3, 7, 11, 19],
  [3, 5, 9, 13],
  [3, 9, 11, 15]
];
/** @type {number} */
const F = 0x00000000;
/** @type {number} */
const G = 0x5a827999;
/** @type {number} */
const H = 0x6ed9eba1;

/**
 * Calculates [MD4](https://tools.ietf.org/html/rfc1320) hash
 *
 * @example <caption>Calculates MD4 hash from string "message" - ES6 style</caption>
 * import Md4 from "crypto-api/src/hasher/md4";
 * import {toHex} from "crypto-api/src/encoder/hex";
 *
 * let hasher = new Md4();
 * hasher.update('message');
 * console.log(toHex(hasher.finalize()));
 *
 * @example <caption>Calculates MD4 hash from UTF string "message" - ES6 style</caption>
 * import Md4 from "crypto-api/src/hasher/md4";
 * import {toHex} from "crypto-api/src/encoder/hex";
 * import {fromUtf} from "crypto-api/src/encoder/utf";
 *
 * let hasher = new Md4();
 * hasher.update(fromUtf('message'));
 * console.log(toHex(hasher.finalize()));
 *
 * @example <caption>Calculates MD4 hash from string "message" - ES5 style</caption>
 * <script src="https://nf404.github.io/crypto-api/crypto-api.min.js"></script>
 * <script>
 *   var hasher = CryptoApi.getHasher('md4');
 *   hasher.update('message');
 *   console.log(CryptoApi.encoder.toHex(hasher.finalize()));
 * </script>
 *
 * @example <caption>Calculates MD4 hash from UTF string "message" - ES5 style</caption>
 * <script src="https://nf404.github.io/crypto-api/crypto-api.min.js"></script>
 * <script>
 *   console.log(CryptoApi.hash('md4', 'message'));
 * </script>
 */
class Md4 extends Hasher32le {
  /**
   * Reset hasher to initial state
   */
  reset() {
    super.reset();
    this.state.hash = [
      0x67452301 | 0,
      0xefcdab89 | 0,
      0x98badcfe | 0,
      0x10325476 | 0
    ];
  }

  /**
   * @private
   * @ignore
   * @param {number} x
   * @param {number} y
   * @param {number} z
   * @returns {number}
   */
  static FF(x, y, z) {
    return (x & y) | ((~x) & z);
  }

  /**
   * @private
   * @ignore
   * @param {number} x
   * @param {number} y
   * @param {number} z
   * @returns {number}
   */
  static GG(x, y, z) {
    return (x & y) | (x & z) | (y & z);
  }

  /**
   * @private
   * @ignore
   * @param {number} x
   * @param {number} y
   * @param {number} z
   * @returns {number}
   */
  static HH(x, y, z) {
    return x ^ y ^ z;
  }

  /**
   * @private
   * @ignore
   * @param {function} f
   * @param {number} k
   * @param {number} a
   * @param {number} x
   * @param {number} y
   * @param {number} z
   * @param {number} m
   * @param {number} s
   * @returns {number}
   * @constructor
   */
  static CC(f, k, a, x, y, z, m, s) {
    return rotateLeft((a + f(x, y, z) + m + k), s) | 0;
  }

  /**
   * Process ready blocks
   *
   * @protected
   * @ignore
   * @param {number[]} block - Block
   */
  processBlock(block) {
    // Working variables
    let a = this.state.hash[0] | 0;
    let b = this.state.hash[1] | 0;
    let c = this.state.hash[2] | 0;
    let d = this.state.hash[3] | 0;

    // Round 1
    a = Md4.CC(Md4.FF, F, a, b, c, d, block[0], S[0][0]);
    d = Md4.CC(Md4.FF, F, d, a, b, c, block[1], S[0][1]);
    c = Md4.CC(Md4.FF, F, c, d, a, b, block[2], S[0][2]);
    b = Md4.CC(Md4.FF, F, b, c, d, a, block[3], S[0][3]);
    a = Md4.CC(Md4.FF, F, a, b, c, d, block[4], S[0][0]);
    d = Md4.CC(Md4.FF, F, d, a, b, c, block[5], S[0][1]);
    c = Md4.CC(Md4.FF, F, c, d, a, b, block[6], S[0][2]);
    b = Md4.CC(Md4.FF, F, b, c, d, a, block[7], S[0][3]);
    a = Md4.CC(Md4.FF, F, a, b, c, d, block[8], S[0][0]);
    d = Md4.CC(Md4.FF, F, d, a, b, c, block[9], S[0][1]);
    c = Md4.CC(Md4.FF, F, c, d, a, b, block[10], S[0][2]);
    b = Md4.CC(Md4.FF, F, b, c, d, a, block[11], S[0][3]);
    a = Md4.CC(Md4.FF, F, a, b, c, d, block[12], S[0][0]);
    d = Md4.CC(Md4.FF, F, d, a, b, c, block[13], S[0][1]);
    c = Md4.CC(Md4.FF, F, c, d, a, b, block[14], S[0][2]);
    b = Md4.CC(Md4.FF, F, b, c, d, a, block[15], S[0][3]);

    // Round 2
    a = Md4.CC(Md4.GG, G, a, b, c, d, block[0], S[1][0]);
    d = Md4.CC(Md4.GG, G, d, a, b, c, block[4], S[1][1]);
    c = Md4.CC(Md4.GG, G, c, d, a, b, block[8], S[1][2]);
    b = Md4.CC(Md4.GG, G, b, c, d, a, block[12], S[1][3]);
    a = Md4.CC(Md4.GG, G, a, b, c, d, block[1], S[1][0]);
    d = Md4.CC(Md4.GG, G, d, a, b, c, block[5], S[1][1]);
    c = Md4.CC(Md4.GG, G, c, d, a, b, block[9], S[1][2]);
    b = Md4.CC(Md4.GG, G, b, c, d, a, block[13], S[1][3]);
    a = Md4.CC(Md4.GG, G, a, b, c, d, block[2], S[1][0]);
    d = Md4.CC(Md4.GG, G, d, a, b, c, block[6], S[1][1]);
    c = Md4.CC(Md4.GG, G, c, d, a, b, block[10], S[1][2]);
    b = Md4.CC(Md4.GG, G, b, c, d, a, block[14], S[1][3]);
    a = Md4.CC(Md4.GG, G, a, b, c, d, block[3], S[1][0]);
    d = Md4.CC(Md4.GG, G, d, a, b, c, block[7], S[1][1]);
    c = Md4.CC(Md4.GG, G, c, d, a, b, block[11], S[1][2]);
    b = Md4.CC(Md4.GG, G, b, c, d, a, block[15], S[1][3]);

    // Round 3
    a = Md4.CC(Md4.HH, H, a, b, c, d, block[0], S[2][0]);
    d = Md4.CC(Md4.HH, H, d, a, b, c, block[8], S[2][1]);
    c = Md4.CC(Md4.HH, H, c, d, a, b, block[4], S[2][2]);
    b = Md4.CC(Md4.HH, H, b, c, d, a, block[12], S[2][3]);
    a = Md4.CC(Md4.HH, H, a, b, c, d, block[2], S[2][0]);
    d = Md4.CC(Md4.HH, H, d, a, b, c, block[10], S[2][1]);
    c = Md4.CC(Md4.HH, H, c, d, a, b, block[6], S[2][2]);
    b = Md4.CC(Md4.HH, H, b, c, d, a, block[14], S[2][3]);
    a = Md4.CC(Md4.HH, H, a, b, c, d, block[1], S[2][0]);
    d = Md4.CC(Md4.HH, H, d, a, b, c, block[9], S[2][1]);
    c = Md4.CC(Md4.HH, H, c, d, a, b, block[5], S[2][2]);
    b = Md4.CC(Md4.HH, H, b, c, d, a, block[13], S[2][3]);
    a = Md4.CC(Md4.HH, H, a, b, c, d, block[3], S[2][0]);
    d = Md4.CC(Md4.HH, H, d, a, b, c, block[11], S[2][1]);
    c = Md4.CC(Md4.HH, H, c, d, a, b, block[7], S[2][2]);
    b = Md4.CC(Md4.HH, H, b, c, d, a, block[15], S[2][3]);

    this.state.hash = [
      (this.state.hash[0] + a) | 0,
      (this.state.hash[1] + b) | 0,
      (this.state.hash[2] + c) | 0,
      (this.state.hash[3] + d) | 0
    ];
  }

  /**
   * Finalize hash and return result
   *
   * @returns {string}
   */
  finalize() {
    this.addPaddingISO7816(
      this.state.message.length < 56 ?
        56 - this.state.message.length | 0 :
        120 - this.state.message.length | 0);
    this.addLengthBits();
    this.process();
    return this.getStateHash();
  }
}

export default Md4;