src/hasher/ripemd.mjs
'use strict';
import Hasher32le from "./hasher32le";
import {rotateLeft} from "../tools/tools";
/** @type {number[]} */
const ZL = [
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
7, 4, 13, 1, 10, 6, 15, 3, 12, 0, 9, 5, 2, 14, 11, 8,
3, 10, 14, 4, 9, 15, 8, 1, 2, 7, 0, 6, 13, 11, 5, 12,
1, 9, 11, 10, 0, 8, 12, 4, 13, 3, 7, 15, 14, 5, 6, 2,
4, 0, 5, 9, 7, 12, 2, 10, 14, 1, 3, 8, 11, 6, 15, 13
];
/** @type {number[]} */
const ZR = [
5, 14, 7, 0, 9, 2, 11, 4, 13, 6, 15, 8, 1, 10, 3, 12,
6, 11, 3, 7, 0, 13, 5, 10, 14, 15, 8, 12, 4, 9, 1, 2,
15, 5, 1, 3, 7, 14, 6, 9, 11, 8, 12, 2, 10, 0, 4, 13,
8, 6, 4, 1, 3, 11, 15, 0, 5, 12, 2, 13, 9, 7, 10, 14,
12, 15, 10, 4, 1, 5, 8, 7, 6, 2, 13, 14, 0, 3, 9, 11
];
/** @type {number[]} */
const SL = [
11, 14, 15, 12, 5, 8, 7, 9, 11, 13, 14, 15, 6, 7, 9, 8,
7, 6, 8, 13, 11, 9, 7, 15, 7, 12, 15, 9, 11, 7, 13, 12,
11, 13, 6, 7, 14, 9, 13, 15, 14, 8, 13, 6, 5, 12, 7, 5,
11, 12, 14, 15, 14, 15, 9, 8, 9, 14, 5, 6, 8, 6, 5, 12,
9, 15, 5, 11, 6, 8, 13, 12, 5, 12, 13, 14, 11, 8, 5, 6
];
/** @type {number[]} */
const SR = [
8, 9, 9, 11, 13, 15, 15, 5, 7, 7, 8, 11, 14, 14, 12, 6,
9, 13, 15, 7, 12, 8, 9, 11, 7, 7, 12, 7, 6, 15, 13, 11,
9, 7, 15, 11, 8, 6, 6, 14, 12, 13, 5, 14, 13, 13, 7, 5,
15, 5, 8, 11, 14, 14, 6, 14, 6, 9, 12, 9, 12, 5, 15, 8,
8, 5, 12, 9, 12, 5, 14, 6, 8, 13, 6, 5, 15, 13, 11, 11
];
/**
* Calculates [RIPEMD-160 (RIPEMD-128, RIPEMD-256, RIPEMD-320)](http://homes.esat.kuleuven.be/~bosselae/ripemd160.html) hash
*
* @example <caption>Calculates RIPEMD-160 hash from string "message" - ES6 style</caption>
* import Ripemd from "crypto-api/src/hasher/ripemd";
* import {toHex} from "crypto-api/src/encoder/hex";
*
* let hasher = new Ripemd();
* hasher.update('message');
* console.log(toHex(hasher.finalize()));
*
* @example <caption>Calculates RIPEMD-160 hash from UTF string "message" - ES6 style</caption>
* import Ripemd from "crypto-api/src/hasher/ripemd";
* import {toHex} from "crypto-api/src/encoder/hex";
* import {fromUtf} from "crypto-api/src/encoder/utf";
*
* let hasher = new Ripemd();
* hasher.update(fromUtf('message'));
* console.log(toHex(hasher.finalize()));
*
* @example <caption>Calculates RIPEMD-160 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('ripemd160');
* hasher.update('message');
* console.log(CryptoApi.encoder.toHex(hasher.finalize()));
* </script>
*
* @example <caption>Calculates RIPEMD-160 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('ripemd160', 'message'));
* </script>
*/
class Ripemd extends Hasher32le {
/**
* @param {Object} [options]
* @param {number} [options.length=160] - Length of hash result
*
* | Hash type | Length |
* |-----------|--------|
* | ripemd128 | 128 |
* | ripemd160 | 160 |
* | ripemd256 | 256 |
* | ripemd320 | 320 |
*/
constructor(options) {
options = options || {};
options.length = options.length || 160;
super(options);
}
/**
* Reset hasher to initial state
*/
reset() {
super.reset();
switch (this.options.length) {
case 128:
this.state.hash = [0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476];
/**
* Process ready blocks
*
* @protected
* @ignore
* @method processBlock
* @param {number[]} block - Block
*/
this.processBlock = this.processBlock128;
break;
case 256:
this.state.hash = [
0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476,
0x76543210, 0xfedcba98, 0x89abcdef, 0x01234567
];
this.processBlock = this.processBlock256;
break;
case 320:
this.state.hash = [
0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0,
0x76543210, 0xfedcba98, 0x89abcdef, 0x01234567, 0x3c2d1e0f
];
this.processBlock = this.processBlock320;
break;
default: // 160
this.state.hash = [
0x67452301,
0xefcdab89,
0x98badcfe,
0x10325476,
0xc3d2e1f0
];
this.processBlock = this.processBlock160;
}
}
/**
* @private
* @ignore
* @param {number} x
* @param {number} y
* @param {number} z
* @returns {number}
*/
static F(x, y, z) {
return x ^ y ^ z;
}
/**
* @private
* @ignore
* @param {number} x
* @param {number} y
* @param {number} z
* @returns {number}
*/
static G(x, y, z) {
return (x & y) | ((~x) & z);
}
/**
* @private
* @ignore
* @param {number} x
* @param {number} y
* @param {number} z
* @returns {number}
*/
static H(x, y, z) {
return (x | (~y)) ^ z;
}
/**
* @private
* @ignore
* @param {number} x
* @param {number} y
* @param {number} z
* @returns {number}
*/
static I(x, y, z) {
return (x & z) | (y & (~z));
}
/**
* @private
* @ignore
* @param {number} x
* @param {number} y
* @param {number} z
* @returns {number}
*/
static J(x, y, z) {
return x ^ (y | (~z));
}
/**
* @private
* @ignore
* @param {number} i
* @param {number} bl
* @param {number} cl
* @param {number} dl
* @returns {number}
*/
static T(i, bl, cl, dl) {
if (i < 16) {
return this.F(bl, cl, dl);
}
if (i < 32) {
return (this.G(bl, cl, dl) + 0x5a827999) | 0;
}
if (i < 48) {
return (this.H(bl, cl, dl) + 0x6ed9eba1) | 0;
}
if (i < 64) {
return (this.I(bl, cl, dl) + 0x8f1bbcdc) | 0;
}
return (this.J(bl, cl, dl) + 0xa953fd4e) | 0;
}
/**
* @private
* @ignore
* @param {number} i
* @param {number} br
* @param {number} cr
* @param {number} dr
* @returns {number}
*/
static T64(i, br, cr, dr) {
if (i < 16) {
return (this.I(br, cr, dr) + 0x50a28be6) | 0;
}
if (i < 32) {
return (this.H(br, cr, dr) + 0x5c4dd124) | 0;
}
if (i < 48) {
return (this.G(br, cr, dr) + 0x6d703ef3) | 0;
}
return this.F(br, cr, dr);
}
/**
* @private
* @ignore
* @param {number} i
* @param {number} br
* @param {number} cr
* @param {number} dr
* @returns {number}
*/
static T80(i, br, cr, dr) {
if (i < 16) {
return (this.J(br, cr, dr) + 0x50a28be6) | 0;
}
if (i < 32) {
return (this.I(br, cr, dr) + 0x5c4dd124) | 0;
}
if (i < 48) {
return (this.H(br, cr, dr) + 0x6d703ef3) | 0;
}
if (i < 64) {
return (this.G(br, cr, dr) + 0x7a6d76e9) | 0;
}
return this.F(br, cr, dr);
}
/**
* Process ready blocks
*
* @protected
* @ignore
* @param {number[]} block - Block
*/
processBlock128(block) {
// Working variables
let al = this.state.hash[0] | 0;
let bl = this.state.hash[1] | 0;
let cl = this.state.hash[2] | 0;
let dl = this.state.hash[3] | 0;
let ar = al;
let br = bl;
let cr = cl;
let dr = dl;
for (let i = 0; i < 64; i++) {
let t = (al + block[ZL[i]]) | 0;
t = (t + Ripemd.T(i, bl, cl, dl)) | 0;
t = rotateLeft(t, SL[i]);
al = dl;
dl = cl;
cl = bl;
bl = t;
t = (ar + block[ZR[i]]) | 0;
t = (t + Ripemd.T64(i, br, cr, dr)) | 0;
t = rotateLeft(t, SR[i]);
ar = dr;
dr = cr;
cr = br;
br = t;
}
let t = (this.state.hash[1] + cl + dr) | 0;
this.state.hash[1] = (this.state.hash[2] + dl + ar) | 0;
this.state.hash[2] = (this.state.hash[3] + al + br) | 0;
this.state.hash[3] = (this.state.hash[0] + bl + cr) | 0;
this.state.hash[0] = t;
}
/**
* Process ready blocks
*
* @protected
* @ignore
* @param {number[]} block - Block
*/
processBlock160(block) {
// Working variables
let al = this.state.hash[0] | 0;
let bl = this.state.hash[1] | 0;
let cl = this.state.hash[2] | 0;
let dl = this.state.hash[3] | 0;
let el = this.state.hash[4] | 0;
let ar = al;
let br = bl;
let cr = cl;
let dr = dl;
let er = el;
for (let i = 0; i < 80; i++) {
let t = (al + block[ZL[i]]) | 0;
t = (t + Ripemd.T(i, bl, cl, dl)) | 0;
t = rotateLeft(t, SL[i]);
t = (t + el) | 0;
al = el;
el = dl;
dl = rotateLeft(cl, 10);
cl = bl;
bl = t;
t = (ar + block[ZR[i]]) | 0;
t = (t + Ripemd.T80(i, br, cr, dr)) | 0;
t = rotateLeft(t, SR[i]);
t = (t + er) | 0;
ar = er;
er = dr;
dr = rotateLeft(cr, 10);
cr = br;
br = t;
}
let t = (this.state.hash[1] + cl + dr) | 0;
this.state.hash[1] = (this.state.hash[2] + dl + er) | 0;
this.state.hash[2] = (this.state.hash[3] + el + ar) | 0;
this.state.hash[3] = (this.state.hash[4] + al + br) | 0;
this.state.hash[4] = (this.state.hash[0] + bl + cr) | 0;
this.state.hash[0] = t;
}
/**
* Process ready blocks
*
* @protected
* @ignore
* @param {number[]} block - Block
*/
processBlock256(block) {
// Working variables
let al = this.state.hash[0] | 0;
let bl = this.state.hash[1] | 0;
let cl = this.state.hash[2] | 0;
let dl = this.state.hash[3] | 0;
let ar = this.state.hash[4] | 0;
let br = this.state.hash[5] | 0;
let cr = this.state.hash[6] | 0;
let dr = this.state.hash[7] | 0;
for (let i = 0; i < 64; i += 1) {
let t = (al + block[ZL[i]]) | 0;
t = (t + Ripemd.T(i, bl, cl, dl)) | 0;
t = rotateLeft(t, SL[i]);
al = dl;
dl = cl;
cl = bl;
bl = t;
t = (ar + block[ZR[i]]) | 0;
t = (t + Ripemd.T64(i, br, cr, dr)) | 0;
t = rotateLeft(t, SR[i]);
ar = dr;
dr = cr;
cr = br;
br = t;
switch (i) {
case 15:
t = al;
al = ar;
ar = t;
break;
case 31:
t = bl;
bl = br;
br = t;
break;
case 47:
t = cl;
cl = cr;
cr = t;
break;
case 63:
t = dl;
dl = dr;
dr = t;
break;
}
}
this.state.hash[0] = (this.state.hash[0] + al) | 0;
this.state.hash[1] = (this.state.hash[1] + bl) | 0;
this.state.hash[2] = (this.state.hash[2] + cl) | 0;
this.state.hash[3] = (this.state.hash[3] + dl) | 0;
this.state.hash[4] = (this.state.hash[4] + ar) | 0;
this.state.hash[5] = (this.state.hash[5] + br) | 0;
this.state.hash[6] = (this.state.hash[6] + cr) | 0;
this.state.hash[7] = (this.state.hash[7] + dr) | 0;
}
/**
* Process ready blocks
*
* @protected
* @ignore
* @param {number[]} block - Block
*/
processBlock320(block) {
// Working variables
let al = this.state.hash[0] | 0;
let bl = this.state.hash[1] | 0;
let cl = this.state.hash[2] | 0;
let dl = this.state.hash[3] | 0;
let el = this.state.hash[4] | 0;
let ar = this.state.hash[5] | 0;
let br = this.state.hash[6] | 0;
let cr = this.state.hash[7] | 0;
let dr = this.state.hash[8] | 0;
let er = this.state.hash[9] | 0;
for (let i = 0; i < 80; i += 1) {
let t = (al + block[ZL[i]]) | 0;
t = (t + Ripemd.T(i, bl, cl, dl)) | 0;
t = rotateLeft(t, SL[i]);
t = (t + el) | 0;
al = el;
el = dl;
dl = rotateLeft(cl, 10);
cl = bl;
bl = t;
t = (ar + block[ZR[i]]) | 0;
t = (t + Ripemd.T80(i, br, cr, dr)) | 0;
t = rotateLeft(t, SR[i]);
t = (t + er) | 0;
ar = er;
er = dr;
dr = rotateLeft(cr, 10);
cr = br;
br = t;
switch (i) {
case 15:
t = bl;
bl = br;
br = t;
break;
case 31:
t = dl;
dl = dr;
dr = t;
break;
case 47:
t = al;
al = ar;
ar = t;
break;
case 63:
t = cl;
cl = cr;
cr = t;
break;
case 79:
t = el;
el = er;
er = t;
break;
}
}
this.state.hash[0] = (this.state.hash[0] + al) | 0;
this.state.hash[1] = (this.state.hash[1] + bl) | 0;
this.state.hash[2] = (this.state.hash[2] + cl) | 0;
this.state.hash[3] = (this.state.hash[3] + dl) | 0;
this.state.hash[4] = (this.state.hash[4] + el) | 0;
this.state.hash[5] = (this.state.hash[5] + ar) | 0;
this.state.hash[6] = (this.state.hash[6] + br) | 0;
this.state.hash[7] = (this.state.hash[7] + cr) | 0;
this.state.hash[8] = (this.state.hash[8] + dr) | 0;
this.state.hash[9] = (this.state.hash[9] + er) | 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 Ripemd;