Home Manual Reference Source Test Repository

src/sha512.js

import { get64 , add64 , and64 , xor64 , rotr64 , not64 , big64 , shr64 } from '@aureooms/js-uint64' ;

// Initialize table of round constants
// (first 64 bits of the fractional parts of the cube roots of the first 80 primes 2..311):
const k = [
	get64(0x428a2f98, 0xd728ae22), get64(0x71374491, 0x23ef65cd), get64(0xb5c0fbcf, 0xec4d3b2f), get64(0xe9b5dba5, 0x8189dbbc),
	get64(0x3956c25b, 0xf348b538), get64(0x59f111f1, 0xb605d019), get64(0x923f82a4, 0xaf194f9b), get64(0xab1c5ed5, 0xda6d8118),
	get64(0xd807aa98, 0xa3030242), get64(0x12835b01, 0x45706fbe), get64(0x243185be, 0x4ee4b28c), get64(0x550c7dc3, 0xd5ffb4e2),
	get64(0x72be5d74, 0xf27b896f), get64(0x80deb1fe, 0x3b1696b1), get64(0x9bdc06a7, 0x25c71235), get64(0xc19bf174, 0xcf692694),
	get64(0xe49b69c1, 0x9ef14ad2), get64(0xefbe4786, 0x384f25e3), get64(0x0fc19dc6, 0x8b8cd5b5), get64(0x240ca1cc, 0x77ac9c65),
	get64(0x2de92c6f, 0x592b0275), get64(0x4a7484aa, 0x6ea6e483), get64(0x5cb0a9dc, 0xbd41fbd4), get64(0x76f988da, 0x831153b5),
	get64(0x983e5152, 0xee66dfab), get64(0xa831c66d, 0x2db43210), get64(0xb00327c8, 0x98fb213f), get64(0xbf597fc7, 0xbeef0ee4),
	get64(0xc6e00bf3, 0x3da88fc2), get64(0xd5a79147, 0x930aa725), get64(0x06ca6351, 0xe003826f), get64(0x14292967, 0x0a0e6e70),
	get64(0x27b70a85, 0x46d22ffc), get64(0x2e1b2138, 0x5c26c926), get64(0x4d2c6dfc, 0x5ac42aed), get64(0x53380d13, 0x9d95b3df),
	get64(0x650a7354, 0x8baf63de), get64(0x766a0abb, 0x3c77b2a8), get64(0x81c2c92e, 0x47edaee6), get64(0x92722c85, 0x1482353b),
	get64(0xa2bfe8a1, 0x4cf10364), get64(0xa81a664b, 0xbc423001), get64(0xc24b8b70, 0xd0f89791), get64(0xc76c51a3, 0x0654be30),
	get64(0xd192e819, 0xd6ef5218), get64(0xd6990624, 0x5565a910), get64(0xf40e3585, 0x5771202a), get64(0x106aa070, 0x32bbd1b8),
	get64(0x19a4c116, 0xb8d2d0c8), get64(0x1e376c08, 0x5141ab53), get64(0x2748774c, 0xdf8eeb99), get64(0x34b0bcb5, 0xe19b48a8),
	get64(0x391c0cb3, 0xc5c95a63), get64(0x4ed8aa4a, 0xe3418acb), get64(0x5b9cca4f, 0x7763e373), get64(0x682e6ff3, 0xd6b2b8a3),
	get64(0x748f82ee, 0x5defb2fc), get64(0x78a5636f, 0x43172f60), get64(0x84c87814, 0xa1f0ab72), get64(0x8cc70208, 0x1a6439ec),
	get64(0x90befffa, 0x23631e28), get64(0xa4506ceb, 0xde82bde9), get64(0xbef9a3f7, 0xb2c67915), get64(0xc67178f2, 0xe372532b),
	get64(0xca273ece, 0xea26619c), get64(0xd186b8c7, 0x21c0c207), get64(0xeada7dd6, 0xcde0eb1e), get64(0xf57d4f7f, 0xee6ed178),
	get64(0x06f067aa, 0x72176fba), get64(0x0a637dc5, 0xa2c898a6), get64(0x113f9804, 0xbef90dae), get64(0x1b710b35, 0x131c471b),
	get64(0x28db77f5, 0x23047d84), get64(0x32caab7b, 0x40c72493), get64(0x3c9ebe0a, 0x15c9bebc), get64(0x431d67c4, 0x9c100d4c),
	get64(0x4cc5d4be, 0xcb3e42b6), get64(0x597f299c, 0xfc657e2a), get64(0x5fcb6fab, 0x3ad6faec), get64(0x6c44198c, 0x4a475817),
] ;

export function sha512_cycle (state, w) {

	// initialize hash value for this chunk:
	let a = state[0];
	let b = state[1];
	let c = state[2];
	let d = state[3];
	let e = state[4];
	let f = state[5];
	let g = state[6];
	let h = state[7];

	// Main loop:
	// for j from 0 to 79
	for (let j = 0; j < 80; ++j) {
		// S1 := (e rightrotate 14) xor (e rightrotate 18) xor (e rightrotate 41)
		const s1 = xor64(xor64(rotr64(e, 14), rotr64(e, 18)), rotr64(e, 41));
		// ch := (e and f) xor ((not e) and g)
		const ch = xor64(and64(e, f), and64(not64(e), g));
		// temp := h + S1 + ch + k[j] + w[j]
		let temp = add64(add64(h, s1), add64(add64(ch, k[j]), w[j]));
		// d := d + temp;
		d = add64(d, temp);
		// S0 := (a rightrotate 28) xor (a rightrotate 34) xor (a rightrotate 39)
		const s0 = xor64(xor64(rotr64(a, 28), rotr64(a, 34)), rotr64(a, 39));
		// maj := (a and (b xor c)) xor (b and c)
		const maj = xor64(and64(a, xor64(b, c)), and64(b, c));
		// temp := temp + S0 + maj
		temp = add64(add64(temp, s0), maj);

		h = g;
		g = f;
		f = e;
		e = d;
		d = c;
		c = b;
		b = a;
		a = temp;
	}

	// Add this chunk's hash to result so far:
	state[0] = add64(state[0], a);
	state[1] = add64(state[1], b);
	state[2] = add64(state[2], c);
	state[3] = add64(state[3], d);
	state[4] = add64(state[4], e);
	state[5] = add64(state[5], f);
	state[6] = add64(state[6], g);
	state[7] = add64(state[7], h);
}

export function sha512_call (h, data, o) {

	const w = new Array(80);

	// break chunk into sixteen 64-bit big-endian words w[i], 0 ≤ i ≤ 15
	for (let j = 0; j < 16; ++j) {
		w[j] = big64(data, o + j * 8);
	}

	// Extend the sixteen 64-bit words into 80 64-bit words:
	// for j from 16 to 79
	for (let j = 16; j < 80; ++j) {
		// s0 := (w[j-15] rightrotate 1) xor (w[j-15] rightrotate 8) xor (w[j-15] rightshift 7)
		const s0 = xor64(xor64(rotr64(w[j-15],  1), rotr64(w[j-15],  8)), shr64(w[j-15], 7));
		// s1 := (w[j-2] rightrotate 19) xor (w[j-2] rightrotate 61) xor (w[j-2] rightshift 6)
		const s1 = xor64(xor64(rotr64(w[j- 2], 19), rotr64(w[j- 2], 61)), shr64(w[j- 2], 6));
		// w[j] := w[j-16] + s0 + w[j-7] + s1
		w[j] = add64(add64(w[j-16], s0), add64(w[j-7], s1));
	}

	sha512_cycle(h, w);

}

export function sha512_initial_state ( ) {

	// (first 64 bits of the fractional parts of the square roots of the first 8 primes 2..19):
	return [
		get64(0x6a09e667, 0xf3bcc908),
		get64(0xbb67ae85, 0x84caa73b),
		get64(0x3c6ef372, 0xfe94f82b),
		get64(0xa54ff53a, 0x5f1d36f1),
		get64(0x510e527f, 0xade682d1),
		get64(0x9b05688c, 0x2b3e6c1f),
		get64(0x1f83d9ab, 0xfb41bd6b),
		get64(0x5be0cd19, 0x137e2179),
	] ;

}


export function sha512_finalize (bytes, n, digest, y, o, right, h) {

	// last bytes + padding + length
	let tail = [];

	// last bytes
	for (let j = 0; j < y; ++j) {
		tail.push(bytes[o + j]);
	}

	const q = right / 8 | 0;
	const z = q * 8;
	const u = right - z;

	// append the bit '1' to the message
	// special care taken for the very last byte which could
	// have been modified if n is not a multiple of 8
	const last = u > 0 ? bytes[q] & (~0) << (7-u) : 0x80 ;
	tail.push(last);


	// append 0 ≤ k < 1024 bits '0', so that the resulting
	// message length (in bits) is congruent to 896 (mod 1024)
	const g = (896 - (n + 1) % 1024);
	let zeroes = g / 8 | 0;

	if (g < 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);
		}

		sha512_call(h, tail, 0);

		zeroes = 896 / 8;
		tail = [];
	}


	// pad with zeroes
	for (let j = 0; j < zeroes; ++j) {
		tail.push(0);
	}

	// append length of message (before preparation), in bits,
	// as 128-bit big-endian integer

	// JavaScript works with 32 bit integers.
	// tail.push((n >>> 124) & 0xff);
	// tail.push((n >>> 116) & 0xff);
	// tail.push((n >>> 108) & 0xff);
	// tail.push((n >>> 96) & 0xff);
	// tail.push((n >>> 88) & 0xff);
	// tail.push((n >>> 80) & 0xff);
	// tail.push((n >>> 72) & 0xff);
	// tail.push((n >>> 64) & 0xff);
	// 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(0);
	tail.push(0);
	tail.push(0);
	tail.push(0);
	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);

	sha512_call(h, tail, 0);

	for (let i = 0, j = 0; j < 8; ++j) {
		digest[i] = (h[j][0] >>> 24) & 0xff;
		++i;
		digest[i] = (h[j][0] >>> 16) & 0xff;
		++i;
		digest[i] = (h[j][0] >>>  8) & 0xff;
		++i;
		digest[i] = (h[j][0] >>>  0) & 0xff;
		++i;
		digest[i] = (h[j][1] >>> 24) & 0xff;
		++i;
		digest[i] = (h[j][1] >>> 16) & 0xff;
		++i;
		digest[i] = (h[j][1] >>>  8) & 0xff;
		++i;
		digest[i] = (h[j][1] >>>  0) & 0xff;
		++i;
	}

	return digest;

}


/**
 * SHA-512
 */
export function sha512 (bytes, n, digest) {

	// Note 1: All variables are unsigned 64 bits and wrap modulo 2^64 when calculating
	// Note 2: All constants in this pseudo code are big endian

	// Initialize state
	const h = sha512_initial_state();

	// Process the message in successive 1024-bit chunks:
	// break message into 1024-bit chunks
	const m = n / 1024 | 0;

	// offset in data
	let o = 0;

	// for each chunk
	for (let j = 0; j < m; ++j, o += 128) {
		sha512_call(h, bytes, o);
	}

	const y = (n - 1024 * m) / 8 | 0;

	return sha512_finalize(bytes, n, digest, y, o, n, h);

}