src/sha1.js
import { get32 , big32 , rotl32 , add32 } from '@aureooms/js-uint32' ;
function cycle (h, w) {
// initialize hash value for this chunk:
let a = h[0];
let b = h[1];
let c = h[2];
let d = h[3];
let e = h[4];
// Main loop:[35]
// for j from 0 to 79
for (let j = 0; j < 80; ++j) {
let f, k;
// if 0 ≤ j ≤ 19 then
if(0 <= j && j <= 19){
// f = (b and c) or ((not b) and d)
f = (b & c) | ((~ b) & d);
k = 1518500249 ; // 0x5A827999
}
// else if 20 ≤ j ≤ 39
else if(20 <= j && j <= 39){
// f = b xor c xor d
f = b ^ c ^ d;
k = 1859775393 ; // 0x6ED9EBA1
}
// else if 40 ≤ j ≤ 59
else if(40 <= j && j <= 59){
// f = (b and c) or (b and d) or (c and d)
f = (b & c) | (b & d) | (c & d);
k = -1894007588 ; // 0x8F1BBCDC
}
// else if 60 ≤ j ≤ 79
else{
// f = b xor c xor d
f = b ^ c ^ d;
k = -899497514 ; // 0xCA62C1D6
}
// t = (a leftrotate 5) + f + e + k + w[j]
const t = add32(add32(rotl32(a, 5), f), add32(add32(e, k), w[j]));
e = d;
d = c;
// c = b leftrotate 30
c = rotl32(b, 30);
b = a;
a = t;
}
// Add this chunk's hash to result so far:
h[0] = add32(h[0], a);
h[1] = add32(h[1], b);
h[2] = add32(h[2], c);
h[3] = add32(h[3], d);
h[4] = add32(h[4], e);
}
function call (h, data, o) {
const w = new Array(80);
// break chunk into sixteen 32-bit big-endian words w[i], 0 ≤ i ≤ 15
for (let j = 0; j < 16; ++j) {
w[j] = big32(data, o + j * 4);
}
// Extend the sixteen 32-bit words into eighty 32-bit words:
// for j from 16 to 79
for(let j = 16; j < 80; ++j){
// w[j] = (w[j-3] xor w[j-8] xor w[j-14] xor w[j-16]) leftrotate 1
const k = (w[j-3] ^ w[j-8] ^ w[j-14] ^ w[j-16]);
w[j] = rotl32(k, 1);
}
cycle(h, w);
}
/**
* SHA1
*/
export function sha1 (bytes, n, digest) {
// PREPARE
const q = n / 8 | 0;
const z = q * 8;
const u = n - z;
// append the bit '1' to the message
let last ;
if (u > 0) {
last = bytes[q] & (~0) << (7-u);
}
else {
last = 0x80;
}
// Note 1: All variables are unsigned 32 bits and wrap modulo 2^32 when calculating
// Note 2: All constants in this pseudo code are in big endian.
// Within each word, the most significant byte is stored in the leftmost byte position
// Initialize state:
const h = [
get32(0x67452301),
get32(0xEFCDAB89),
get32(0x98BADCFE),
get32(0x10325476),
get32(0xC3D2E1F0),
] ;
// Process the message in successive 512-bit chunks:
// break message into 512-bit chunks
const m = n / 512 | 0;
const y = (n - 512 * m) / 8 | 0;
// offset in data
let o = 0;
// for each chunk
for (let j = 0; j < m; ++j, o += 64) {
call(h, bytes, o);
}
// last bytes + padding + length
let tail = [];
// last bytes
for (let j = 0; j < y; ++j) {
tail.push(bytes[o + j]);
}
// special care taken for the very last byte which could
// have been modified if n is not a multiple of 8
tail.push(last);
// append 0 ≤ k < 512 bits '0', so that the resulting
// message length (in bits) is congruent to 448 (mod 512)
let zeroes = (448 - (n + 1) % 512) / 8 | 0;
if (zeroes < 0) {
// we need an additional block as there is
// not enough space left to append
// the length of the data in bits
for (let j = 0; j < -zeroes; ++j) {
tail.push(0);
}
call(h, tail, 0);
zeroes = 448 / 8;
tail = [];
}
// pad with zeroes
for (let j = 0; j < zeroes; ++j) {
tail.push(0);
}
// append length of message (before preparation), in bits,
// as 64-bit big-endian integer
// JavaScript works with 32 bit integers.
// tail.push((n >>> 56) & 0xFF);
// tail.push((n >>> 48) & 0xFF);
// tail.push((n >>> 40) & 0xFF);
// tail.push((n >>> 32) & 0xFF);
tail.push(0);
tail.push(0);
tail.push(0);
tail.push(0);
tail.push((n >>> 24) & 0xFF);
tail.push((n >>> 16) & 0xFF);
tail.push((n >>> 8) & 0xFF);
tail.push((n >>> 0) & 0xFF);
call(h, tail, 0);
digest[0] = (h[0] >>> 24) & 0xFF;
digest[1] = (h[0] >>> 16) & 0xFF;
digest[2] = (h[0] >>> 8) & 0xFF;
digest[3] = (h[0] >>> 0) & 0xFF;
digest[4] = (h[1] >>> 24) & 0xFF;
digest[5] = (h[1] >>> 16) & 0xFF;
digest[6] = (h[1] >>> 8) & 0xFF;
digest[7] = (h[1] >>> 0) & 0xFF;
digest[8] = (h[2] >>> 24) & 0xFF;
digest[9] = (h[2] >>> 16) & 0xFF;
digest[10] = (h[2] >>> 8) & 0xFF;
digest[11] = (h[2] >>> 0) & 0xFF;
digest[12] = (h[3] >>> 24) & 0xFF;
digest[13] = (h[3] >>> 16) & 0xFF;
digest[14] = (h[3] >>> 8) & 0xFF;
digest[15] = (h[3] >>> 0) & 0xFF;
digest[16] = (h[4] >>> 24) & 0xFF;
digest[17] = (h[4] >>> 16) & 0xFF;
digest[18] = (h[4] >>> 8) & 0xFF;
digest[19] = (h[4] >>> 0) & 0xFF;
return digest;
}