src/hasher/has160.mjs
'use strict';
import Hasher32le from "./hasher32le";
import {rotateLeft} from "../tools/tools";
// Transform constants
/** @type {number[]} */
const K = [0x00000000, 0x5a827999, 0x6ed9eba1, 0x8f1bbcdc];
/** @type {number[]} */
const ROT = [
5, 11, 7, 15, 6, 13, 8, 14, 7, 12,
9, 11, 8, 15, 6, 12, 9, 14, 5, 13
];
/** @type {number[]} */
const ROT2 = [10, 17, 25, 30];
/** @type {number[]} */
const IND = [
18, 0, 1, 2, 3, 19, 4, 5, 6, 7, 16, 8, 9, 10, 11, 17, 12, 13, 14, 15,
22, 3, 6, 9, 12, 23, 15, 2, 5, 8, 20, 11, 14, 1, 4, 21, 7, 10, 13, 0,
26, 12, 5, 14, 7, 27, 0, 9, 2, 11, 24, 4, 13, 6, 15, 25, 8, 1, 10, 3,
30, 7, 2, 13, 8, 31, 3, 14, 9, 4, 28, 15, 10, 5, 0, 29, 11, 6, 1, 12
];
/**
* Calculates [HAS-160](https://www.randombit.net/has160.html) hash
*
* @example <caption>Calculates HAS-160 hash from string "message" - ES6 style</caption>
* import Has160 from "crypto-api/src/hasher/has160";
* import {toHex} from "crypto-api/src/encoder/hex";
*
* let hasher = new Has160();
* hasher.update('message');
* console.log(toHex(hasher.finalize()));
*
* @example <caption>Calculates HAS-160 hash from UTF string "message" - ES6 style</caption>
* import Has160 from "crypto-api/src/hasher/has160";
* import {toHex} from "crypto-api/src/encoder/hex";
* import {fromUtf} from "crypto-api/src/encoder/utf";
*
* let hasher = new Has160();
* hasher.update(fromUtf('message'));
* console.log(toHex(hasher.finalize()));
*
* @example <caption>Calculates HAS-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('has160');
* hasher.update('message');
* console.log(CryptoApi.encoder.toHex(hasher.finalize()));
* </script>
*
* @example <caption>Calculates HAS-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('has160', 'message'));
* </script>
*/
class Has160 extends Hasher32le {
/**
* @param {Object} [options]
* @param {number} [options.rounds=80] - Number of rounds (Can be from 1 to 80)
*/
constructor(options) {
super(options);
this.options.rounds = this.options.rounds || 80;
/**
* @private
* @ignore
* @type {number[]}
*/
this.W = new Array(32);
}
/**
* Reset hasher to initial state
*/
reset() {
super.reset();
this.state.hash = [
0x67452301,
0xefcdab89,
0x98badcfe,
0x10325476,
0xc3d2e1f0
];
}
/**
* 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;
let e = this.state.hash[4] | 0;
for (let i = 0; i < 16; i++) {
this.W[i] = block[i] | 0;
}
this.W[16] = (this.W[0] ^ this.W[1] ^ this.W[2] ^ this.W[3]) | 0;
this.W[17] = (this.W[4] ^ this.W[5] ^ this.W[6] ^ this.W[7]) | 0;
this.W[18] = (this.W[8] ^ this.W[9] ^ this.W[10] ^ this.W[11]) | 0;
this.W[19] = (this.W[12] ^ this.W[13] ^ this.W[14] ^ this.W[15]) | 0;
this.W[20] = (this.W[3] ^ this.W[6] ^ this.W[9] ^ this.W[12]) | 0;
this.W[21] = (this.W[2] ^ this.W[5] ^ this.W[8] ^ this.W[15]) | 0;
this.W[22] = (this.W[1] ^ this.W[4] ^ this.W[11] ^ this.W[14]) | 0;
this.W[23] = (this.W[0] ^ this.W[7] ^ this.W[10] ^ this.W[13]) | 0;
this.W[24] = (this.W[5] ^ this.W[7] ^ this.W[12] ^ this.W[14]) | 0;
this.W[25] = (this.W[0] ^ this.W[2] ^ this.W[9] ^ this.W[11]) | 0;
this.W[26] = (this.W[4] ^ this.W[6] ^ this.W[13] ^ this.W[15]) | 0;
this.W[27] = (this.W[1] ^ this.W[3] ^ this.W[8] ^ this.W[10]) | 0;
this.W[28] = (this.W[2] ^ this.W[7] ^ this.W[8] ^ this.W[13]) | 0;
this.W[29] = (this.W[3] ^ this.W[4] ^ this.W[9] ^ this.W[14]) | 0;
this.W[30] = (this.W[0] ^ this.W[5] ^ this.W[10] ^ this.W[15]) | 0;
this.W[31] = (this.W[1] ^ this.W[6] ^ this.W[11] ^ this.W[12]) | 0;
// Calculate hash
for (let i = 0; i < this.options.rounds; i++) {
let t = (rotateLeft(a, ROT[i % 20]) + e + this.W[IND[i]] + K[(i / 20) >> 0]) | 0;
if (i < 20) {
t = (t + ((b & c) | (~b & d))) | 0;
} else if (i < 40) {
t = (t + (b ^ c ^ d)) | 0;
} else if (i < 60) {
t = (t + (c ^ (b | ~d))) | 0;
} else {
t = (t + (b ^ c ^ d)) | 0;
}
e = d;
d = c;
c = rotateLeft(b, ROT2[(i / 20) >> 0]) | 0;
b = a;
a = t;
}
this.state.hash[0] = (this.state.hash[0] + a) | 0;
this.state.hash[1] = (this.state.hash[1] + b) | 0;
this.state.hash[2] = (this.state.hash[2] + c) | 0;
this.state.hash[3] = (this.state.hash[3] + d) | 0;
this.state.hash[4] = (this.state.hash[4] + e) | 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 Has160;