Home Manual Reference Source Test Repository

src/md5.js

import { get32 , add32 , rotl32 , lil32 } from '@aureooms/js-uint32' ;

function cycle (h, k, r, w) {

	// initialize hash value for this chunk:
	let a = h[0];
	let b = h[1];
	let c = h[2];
	let d = h[3];

	// main loop
	for (let i = 0; i < 64; ++i) {

		let f , g ;

		if (i < 16) {
			f = (b & c) | ((~ b) & d);
			g = i;
		}
		else if (i < 32) {
			f = (d & b) | ((~ d) & c);
			g = (5 * i + 1) % 16;
		}
		else if (i < 48) {
			f = b ^ c ^ d;
			g = (3 * i + 5) % 16;
		}
		else {
			f = c ^ (b | (~ d));
			g = (7 * i) % 16;
		}

		const t = d;
		d = c;
		c = b;
		b = add32(b, rotl32(add32(add32(a, f), add32(k[i], w[g])) , r[i]));
		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);

}

function call (h, k, r, data, o) {

	//break chunk into sixteen 32-bit little-endian words w[i], 0 ≤ i ≤ 15

	const w = [
		lil32(data, o +  0),
		lil32(data, o +  4),
		lil32(data, o +  8),
		lil32(data, o + 12),
		lil32(data, o + 16),
		lil32(data, o + 20),
		lil32(data, o + 24),
		lil32(data, o + 28),
		lil32(data, o + 32),
		lil32(data, o + 36),
		lil32(data, o + 40),
		lil32(data, o + 44),
		lil32(data, o + 48),
		lil32(data, o + 52),
		lil32(data, o + 56),
		lil32(data, o + 60)
	];

	cycle(h, k, r, w);

}

/**
 * MD5
 */
export function md5 (bytes, n, digest) {

	const k = [
		get32(0xd76aa478), get32(0xe8c7b756), get32(0x242070db), get32(0xc1bdceee),
		get32(0xf57c0faf), get32(0x4787c62a), get32(0xa8304613), get32(0xfd469501),
		get32(0x698098d8), get32(0x8b44f7af), get32(0xffff5bb1), get32(0x895cd7be),
		get32(0x6b901122), get32(0xfd987193), get32(0xa679438e), get32(0x49b40821),
		get32(0xf61e2562), get32(0xc040b340), get32(0x265e5a51), get32(0xe9b6c7aa),
		get32(0xd62f105d), get32(0x02441453), get32(0xd8a1e681), get32(0xe7d3fbc8),
		get32(0x21e1cde6), get32(0xc33707d6), get32(0xf4d50d87), get32(0x455a14ed),
		get32(0xa9e3e905), get32(0xfcefa3f8), get32(0x676f02d9), get32(0x8d2a4c8a),
		get32(0xfffa3942), get32(0x8771f681), get32(0x6d9d6122), get32(0xfde5380c),
		get32(0xa4beea44), get32(0x4bdecfa9), get32(0xf6bb4b60), get32(0xbebfbc70),
		get32(0x289b7ec6), get32(0xeaa127fa), get32(0xd4ef3085), get32(0x04881d05),
		get32(0xd9d4d039), get32(0xe6db99e5), get32(0x1fa27cf8), get32(0xc4ac5665),
		get32(0xf4292244), get32(0x432aff97), get32(0xab9423a7), get32(0xfc93a039),
		get32(0x655b59c3), get32(0x8f0ccc92), get32(0xffeff47d), get32(0x85845dd1),
		get32(0x6fa87e4f), get32(0xfe2ce6e0), get32(0xa3014314), get32(0x4e0811a1),
		get32(0xf7537e82), get32(0xbd3af235), get32(0x2ad7d2bb), get32(0xeb86d391),
	] ;

	const r = [
		7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,
		5,  9, 14, 20, 5,  9, 14, 20, 5,  9, 14, 20, 5,  9, 14, 20,
		4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23,
		6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21
	];

	// 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;
	}

	// initialize state
	const h = [
		get32(0x67452301),
		get32(0xefcdab89),
		get32(0x98badcfe),
		get32(0x10325476),
	] ;

	// 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, k, r, 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, k, r, 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 little-endian integer

	tail.push((n >>>  0) & 0xFF);
	tail.push((n >>>  8) & 0xFF);
	tail.push((n >>> 16) & 0xFF);
	tail.push((n >>> 24) & 0xFF);
	// JavaScript works with 32 bit integers.
	// tail.push((n >>> 32) & 0xFF);
	// tail.push((n >>> 40) & 0xFF);
	// tail.push((n >>> 48) & 0xFF);
	// tail.push((n >>> 56) & 0xFF);
	tail.push(0);
	tail.push(0);
	tail.push(0);
	tail.push(0);

	call(h, k, r, tail, 0);

	digest[0]  = (h[0] >>>  0) & 0xFF;
	digest[1]  = (h[0] >>>  8) & 0xFF;
	digest[2]  = (h[0] >>> 16) & 0xFF;
	digest[3]  = (h[0] >>> 24) & 0xFF;
	digest[4]  = (h[1] >>>  0) & 0xFF;
	digest[5]  = (h[1] >>>  8) & 0xFF;
	digest[6]  = (h[1] >>> 16) & 0xFF;
	digest[7]  = (h[1] >>> 24) & 0xFF;
	digest[8]  = (h[2] >>>  0) & 0xFF;
	digest[9]  = (h[2] >>>  8) & 0xFF;
	digest[10] = (h[2] >>> 16) & 0xFF;
	digest[11] = (h[2] >>> 24) & 0xFF;
	digest[12] = (h[3] >>>  0) & 0xFF;
	digest[13] = (h[3] >>>  8) & 0xFF;
	digest[14] = (h[3] >>> 16) & 0xFF;
	digest[15] = (h[3] >>> 24) & 0xFF;

	return digest;

}