Source: md5.js

/**
 *	MD5 Hashing Class
 *	Copyright (c) 2013, Jeff Lyon. (http://rubbingalcoholic.com)
 * 
 *	Licensed under The MIT License. (http://www.opensource.org/licenses/mit-license.php)
 *	Redistributions of files must retain the above copyright notice.
 *
 *	@class
 *	@classdesc				Implements the MD5 Hashing Algorithm specified in RFC 1321 ({@link https://tools.ietf.org/html/rfc1321})
 *	@extends				Hasher
 *	@requires				convert
 *
 *	@author					Jeff Lyon <jeff@rubbingalcoholic.com>
 *	@copyright				Copyright (c) 2013, Jeff Lyon.
 *	@license				{@link http://www.opensource.org/licenses/mit-license.php|The MIT License}
 *
 *	@desc					Creates a new MD5 instance
 */
var MD5 = new Class(
/** @lends MD5.prototype */
{
	Extends: Hasher,

	/**
	 *	The block size in bits for the hashing operation
	 *	@private
	 *	@type {number}
	 */
	block_size: 512,

	/**
	 *	The length in bits of the output block
	 *	@private
	 *	@type {number}
	 */
	outlen: 128,

	/**
	 *	Whether this hasher lists the word bytes in small endian order (from small to big)
	 *	@private
	 *	@type {boolean}
	 */
	reverse_endian_words: true,

	/**
	 *	T lookup table (filled in by {@link MD5#initialize} method)
	 *	@type {Array}
	 *	@private
	 */
	_t: [],

	/**
	 *	This is called automatically on class instantiation. It performs some one-time
	 *	computation to create a lookup table used by the hashing algorithm.
	 *	@return {MD5}
	 */
	initialize: function()
	{
		this.clear();

		if (this._t.length == 0)
			for (var i = 0; i < 64; i++)
				this._t[i] = Math.floor(Math.abs(Math.sin(i+1)) * 4294967296);

		return this;
	},

	/**
	 *	Initialize class values, invoking {@link Hasher#clear} before clearing values specific to {@link MD5}.
	 *	This is called internally at the end of {@link MD5#finalize}, and can be called explicitly when
	 *	using progressive hashing.
	 *	@override
	 */
	clear: function()
	{
		this.parent();
		this.h = [
			0x67452301,
			0xEFCDAB89,
			0x98BADCFE,
			0x10325476
		];
	},

	/**
	 *	Hashes data into the MD5 class instance.
	 *	
	 *	@param {string} data							ASCII-encoded binary string data to hash. Any non-ASCII characters
	 *													should be first encoded out using {@link convert.utf8.encode}
	 *	@param {Object} [options]						Optional options object
	 *	@param {boolean} [options.stream=false]			Whether to use streaming (progressive hashing) mode. In streaming
	 *													mode, you can repeatedly hash data into the MD5 object.
	 *													The hash will not be finalized and returned until you call
	 *													{@link MD5#finalize}. Streaming mode is useful when you have to
	 *													hash a huge amount of data and you don't want to store all
	 *													of it in memory at one time.
	 *	@param {string} [options.return_format='hex']	(binary|hex|words) The return format. Default: hex
	 *
	 *	@return {string|Array|MD5}						Desired output format if streaming mode is turned off. Otherwise this instance (chainable)
	 */
	hash: function(data, options)
	{
		options || (options = {});
		options.stream || (options.stream = false);

		if (typeof data == 'string')
			data = convert.to_bytes(data);

		var _buffer 			= this.buffer.concat(data);
		var _processed_length 	= this.processed_length;
		var h					= this.h;
		var t					= this._t;

		var r1 = function(a, b, c, d, k, s, i)
		{
			var val = a + ((b & c) | (~b & d)) + x[k] + t[i];
			return ((val << s) | (val >>> (32 - s))) + b;
		}

		var r2 = function(a, b, c, d, k, s, i)
		{
			var val = a + ((b & d) | (c & ~d)) + x[k] + t[i];
			return ((val << s) | (val >>> (32 - s))) + b;
		}

		var r3 = function(a, b, c, d, k, s, i)
		{
			var val = a + (b ^ c ^ d) + x[k] + t[i];
			return ((val << s) | (val >>> (32 - s))) + b;
		}

		var r4 = function(a, b, c, d, k, s, i)
		{
			var val = a + (c ^ (b | ~d)) + x[k] + t[i];
			return ((val << s) | (val >>> (32 - s))) + b;
		}

		for (var i = 0; (i+64) <= _buffer.length; i += 64)
		{
			var a = h[0], b = h[1], c = h[2], d = h[3], w = _buffer.slice(i, i+64), x = [];

			// Convert the 64-bytes to an array of reverse endian words
			for (var j = 0; j < 64; j += 4)
				x.push(((w[j+3] & 255) << 24) | ((w[j+2] & 255) << 16) | ((w[j+1] & 255) << 8) | (w[j] & 255));

			var a = r1(a, b, c, d, 0,	7,	0);
			var d = r1(d, a, b, c, 1,	12,	1);
			var c = r1(c, d, a, b, 2,	17,	2);
			var b = r1(b, c, d, a, 3,	22,	3);
			var a = r1(a, b, c, d, 4,	7,	4);
			var d = r1(d, a, b, c, 5,	12,	5);
			var c = r1(c, d, a, b, 6,	17,	6);
			var b = r1(b, c, d, a, 7,	22,	7);
			var a = r1(a, b, c, d, 8,	7,	8);
			var d = r1(d, a, b, c, 9,	12,	9);
			var c = r1(c, d, a, b, 10,	17,	10);
			var b = r1(b, c, d, a, 11,	22,	11);
			var a = r1(a, b, c, d, 12,	7,	12);
			var d = r1(d, a, b, c, 13,	12,	13);
			var c = r1(c, d, a, b, 14,	17,	14);
			var b = r1(b, c, d, a, 15,	22,	15);

			var a = r2(a, b, c, d, 1,	5,	16);
			var d = r2(d, a, b, c, 6,	9,	17);
			var c = r2(c, d, a, b, 11,	14,	18);
			var b = r2(b, c, d, a, 0,	20,	19);
			var a = r2(a, b, c, d, 5,	5,	20);
			var d = r2(d, a, b, c, 10,	9,	21);
			var c = r2(c, d, a, b, 15,	14,	22);
			var b = r2(b, c, d, a, 4,	20,	23);
			var a = r2(a, b, c, d, 9,	5,	24);
			var d = r2(d, a, b, c, 14,	9,	25);
			var c = r2(c, d, a, b, 3,	14,	26);
			var b = r2(b, c, d, a, 8,	20,	27);
			var a = r2(a, b, c, d, 13,	5,	28);
			var d = r2(d, a, b, c, 2,	9,	29);
			var c = r2(c, d, a, b, 7,	14,	30);
			var b = r2(b, c, d, a, 12,	20,	31);

			var a = r3(a, b, c, d, 5,	4,	32);
			var d = r3(d, a, b, c, 8,	11,	33);
			var c = r3(c, d, a, b, 11,	16,	34);
			var b = r3(b, c, d, a, 14,	23,	35);
			var a = r3(a, b, c, d, 1,	4,	36);
			var d = r3(d, a, b, c, 4,	11,	37);
			var c = r3(c, d, a, b, 7,	16,	38);
			var b = r3(b, c, d, a, 10,	23,	39);
			var a = r3(a, b, c, d, 13,	4,	40);
			var d = r3(d, a, b, c, 0,	11,	41);
			var c = r3(c, d, a, b, 3,	16,	42);
			var b = r3(b, c, d, a, 6,	23,	43);
			var a = r3(a, b, c, d, 9,	4,	44);
			var d = r3(d, a, b, c, 12,	11,	45);
			var c = r3(c, d, a, b, 15,	16,	46);
			var b = r3(b, c, d, a, 2,	23,	47);

			var a = r4(a, b, c, d, 0,	6,	48);
			var d = r4(d, a, b, c, 7,	10,	49);
			var c = r4(c, d, a, b, 14,	15,	50);
			var b = r4(b, c, d, a, 5,	21,	51);
			var a = r4(a, b, c, d, 12,	6,	52);
			var d = r4(d, a, b, c, 3,	10,	53);
			var c = r4(c, d, a, b, 10,	15,	54);
			var b = r4(b, c, d, a, 1,	21,	55);
			var a = r4(a, b, c, d, 8,	6,	56);
			var d = r4(d, a, b, c, 15,	10,	57);
			var c = r4(c, d, a, b, 6,	15,	58);
			var b = r4(b, c, d, a, 13,	21,	59);
			var a = r4(a, b, c, d, 4,	6,	60);
			var d = r4(d, a, b, c, 11,	10,	61);
			var c = r4(c, d, a, b, 2,	15,	62);
			var b = r4(b, c, d, a, 9,	21,	63);

			h[0] = (h[0] + a) | 0;
			h[1] = (h[1] + b) | 0;
			h[2] = (h[2] + c) | 0;
			h[3] = (h[3] + d) | 0;

			_processed_length += 512;
		}

		this.buffer 			= _buffer.slice(i);
		this.processed_length	= _processed_length;
		
		if (options.stream == false)
			return this.finalize(options);

		return this;
	}
});