src/hasher/sha1.mjs
'use strict';
import Hasher32be from "./hasher32be";
import {rotateLeft} from "../tools/tools";
// Transform constants
/** @type {number[]} */
const K = [0x5a827999, 0x6ed9eba1, 0x8f1bbcdc, 0xca62c1d6];
/**
* Calculates [SHA1](https://tools.ietf.org/html/rfc3174) hash
*
* @example <caption>Calculates SHA1 hash from string "message" - ES6 style</caption>
* import Sha1 from "crypto-api/src/hasher/sha1";
* import {toHex} from "crypto-api/src/encoder/hex";
*
* let hasher = new Sha1();
* hasher.update('message');
* console.log(toHex(hasher.finalize()));
*
* @example <caption>Calculates SHA1 hash from UTF string "message" - ES6 style</caption>
* import Sha1 from "crypto-api/src/hasher/sha1";
* import {toHex} from "crypto-api/src/encoder/hex";
* import {fromUtf} from "crypto-api/src/encoder/utf";
*
* let hasher = new Sha1();
* hasher.update(fromUtf('message'));
* console.log(toHex(hasher.finalize()));
*
* @example <caption>Calculates SHA1 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('sha1');
* hasher.update('message');
* console.log(CryptoApi.encoder.toHex(hasher.finalize()));
* </script>
*
* @example <caption>Calculates SHA1 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('sha1', 'message'));
* </script>
*/
class Sha1 extends Hasher32be {
/**
* @param {Object} [options]
* @param {number} [options.rounds=80] - Number of rounds (Must be greater than 16)
*/
constructor(options) {
super(options);
this.options.rounds = this.options.rounds || 80;
/**
* Working variable (only for speed optimization)
* @private
* @ignore
* @type {number[]}
*/
this.W = new Array(80);
}
/**
* Reset hasher to initial state
*/
reset() {
super.reset();
this.state.hash = [
0x67452301 | 0,
0xefcdab89 | 0,
0x98badcfe | 0,
0x10325476 | 0,
0xc3d2e1f0 | 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;
let e = this.state.hash[4] | 0;
// Calculate hash
for (let i = 0; i < this.options.rounds; i++) {
if (i < 16) {
this.W[i] = block[i] | 0;
} else {
this.W[i] = rotateLeft(this.W[i - 3] ^ this.W[i - 8] ^ this.W[i - 14] ^ this.W[i - 16], 1) | 0;
}
let t = (rotateLeft(a, 5) + e + this.W[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 + ((b & c) | (b & d) | (c & d))) | 0;
} else {
t = (t + (b ^ c ^ d)) | 0;
}
e = d;
d = c;
c = rotateLeft(b, 30) | 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 Sha1;