/*
	Calcurate MD5 Hash JavaScript

  	md5hash.js

	Written by:	Takashi Miyagi

	Copyright (c) 2004-2014 GUSUKU All rights reserved.

	Last update:	2014/05/23
*/

var	ABCD_VAL = new Array(0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476);
var	ABCD_VAL_WK = new Array(4);

var	round_arr1 = new Array(
					new Array(0, 1, 2, 3,  0,  7,  1),
					new Array(3, 0, 1, 2,  1, 12,  2),
					new Array(2, 3, 0, 1,  2, 17,  3),
					new Array(1, 2, 3, 0,  3, 22,  4), 

					new Array(0, 1, 2, 3,  4,  7,  5),
					new Array(3, 0, 1, 2,  5, 12,  6),
					new Array(2, 3, 0, 1,  6, 17,  7),
					new Array(1, 2, 3, 0,  7, 22,  8),

					new Array(0, 1, 2, 3,  8,  7,  9),
					new Array(3, 0, 1, 2,  9, 12, 10),
					new Array(2, 3, 0, 1, 10, 17, 11),
					new Array(1, 2, 3, 0, 11, 22, 12),

					new Array(0, 1, 2, 3, 12,  7, 13),
					new Array(3, 0, 1, 2, 13, 12, 14),
					new Array(2, 3, 0, 1, 14, 17, 15),
					new Array(1, 2, 3, 0, 15, 22, 16)
				);

var	round_arr2 = new Array(
					new Array(0, 1, 2, 3,  1,  5, 17),
					new Array(3, 0, 1, 2,  6,  9, 18),
					new Array(2, 3, 0, 1, 11, 14, 19),
					new Array(1, 2, 3, 0,  0, 20, 20),

					new Array(0, 1, 2, 3,  5,  5, 21),
					new Array(3, 0, 1, 2, 10,  9, 22),
					new Array(2, 3, 0, 1, 15, 14, 23),
					new Array(1, 2, 3, 0,  4, 20, 24),

					new Array(0, 1, 2, 3,  9,  5, 25),
					new Array(3, 0, 1, 2, 14,  9, 26),
					new Array(2, 3, 0, 1,  3, 14, 27),
					new Array(1, 2, 3, 0,  8, 20, 28),

					new Array(0, 1, 2, 3, 13,  5, 29),
					new Array(3, 0, 1, 2,  2,  9, 30),
					new Array(2, 3, 0, 1,  7, 14, 31),
					new Array(1, 2, 3, 0, 12, 20, 32)
				);

var	round_arr3 = new Array(
					new Array(0, 1, 2, 3,  5,  4, 33),
					new Array(3, 0, 1, 2,  8, 11, 34),
					new Array(2, 3, 0, 1, 11, 16, 35),
					new Array(1, 2, 3, 0, 14, 23, 36),

					new Array(0, 1, 2, 3,  1,  4, 37),
					new Array(3, 0, 1, 2,  4, 11, 38),
					new Array(2, 3, 0, 1,  7, 16, 39),
					new Array(1, 2, 3, 0, 10, 23, 40),

					new Array(0, 1, 2, 3, 13,  4, 41),
					new Array(3, 0, 1, 2,  0, 11, 42),
					new Array(2, 3, 0, 1,  3, 16, 43),
					new Array(1, 2, 3, 0,  6, 23, 44),

					new Array(0, 1, 2, 3,  9,  4, 45),
					new Array(3, 0, 1, 2, 12, 11, 46),
					new Array(2, 3, 0, 1, 15, 16, 47),
					new Array(1, 2, 3, 0,  2, 23, 48)
				);

var	round_arr4 = new Array(
					new Array(0, 1, 2, 3,  0,  6, 49),
					new Array(3, 0, 1, 2,  7, 10, 50),
					new Array(2, 3, 0, 1, 14, 15, 51),
					new Array(1, 2, 3, 0,  5, 21, 52),

					new Array(0, 1, 2, 3, 12,  6, 53),
					new Array(3, 0, 1, 2,  3, 10, 54),
					new Array(2, 3, 0, 1, 10, 15, 55),
					new Array(1, 2, 3, 0,  1, 21, 56),

					new Array(0, 1, 2, 3,  8,  6, 57),
					new Array(3, 0, 1, 2, 15, 10, 58),
					new Array(2, 3, 0, 1,  6, 15, 59),
					new Array(1, 2, 3, 0, 13, 21, 60),

					new Array(0, 1, 2, 3,  4,  6, 61),
					new Array(3, 0, 1, 2, 11, 10, 62),
					new Array(2, 3, 0, 1,  2, 15, 63),
					new Array(1, 2, 3, 0,  9, 21, 64)
				);

var	RoundArray = new Array(round_arr1, round_arr2, round_arr3, round_arr4);

var	MD5_T = new Array(65);

var DataBuf;

/* 32bit rotate left */
function md5_rotate_left(val, bits)
{
	var	v1, v2;

	v1 = val << bits;
	v2 = val >>> (32 - bits);

	return (v1 | v2);
}

function md5_dword_to_str(val)
{
	var	val_str;

	val_str = String.fromCharCode(val & 0xff);
	val_str += String.fromCharCode((val >> 8) & 0xff);
	val_str += String.fromCharCode((val >> 16) & 0xff);
	val_str += String.fromCharCode((val >> 24) & 0xff);

	return val_str;
}

function md5_str_to_dword(str)
{
	var	val;

	val = str.charCodeAt(0);
	val |= str.charCodeAt(1) << 8;
	val |= str.charCodeAt(2) << 16
	val |= str.charCodeAt(3) << 24;

	return val;
}


/* padding input string */
function md5_str_padding(instr)
{
	var	inplen, padlen, modlen;
	var	inpbits;
	var	lenstr;
	var	i, idx;

	inplen = instr.length;

	DataBuf = instr;

	modlen = inplen % 64;

	if (modlen < 56) {
		padlen = 56 - modlen;
	} else {
		padlen = 64 - (modlen - 56);
	}
	if (padlen > 0) {
		DataBuf += "\x80";
		padlen--;
		while (padlen) {
			DataBuf += "\x00";
			padlen--;
		}
	}
	lenstr = md5_dword_to_str(inplen * 8);
	lenstr += md5_dword_to_str(0);

	DataBuf += lenstr;

	return (inplen + padlen + 8);
}

// MD5 function F(x,y,z)
function md5_F(x, y, z)
{
	return ((x & y) | (~x & z));
}

// MD5 function G(x,y,z)
function md5_G(x, y, z)
{
	return ((x & z) | (y & ~z));
}

// MD5 function H(x,y,z)
function md5_H(x, y, z)
{
	return (x ^ y ^ z);
}

// MD5 function I(x,y,z)
function md5_I(x, y, z)
{
	return (y ^ (x | ~z));
}

// Make table T[65]
function md5_makeT()
{
	var i;

	MD5_T[0] = 0;

	for (i = 1; i < 65; i++) {
		MD5_T[i] = Math.floor(4294967296.0 * Math.abs(Math.sin(i)));
	}
	return MD5_T;
}

// Round 1 - 4
function md5_round(X)
{
	var	round;
	var	n;
	var	a, b, c, d, k, s, i;
	var idx_a, idx_b, idx_c, idx_d;
	var val;
	var	func_arr = new Array(md5_F, md5_G, md5_H, md5_I);
	var	abcd = new Array(4);

	//[abcd k s i] ¡§ a = b + ((a + func(b,c,d) + X[k] + T[i]) <<< s) 
	for (n = 0; n < 4; n++) {
		abcd[n] = ABCD_VAL_WK[n];
	}

	for (round = 0; round < 4; round++) {
		arr = RoundArray[round];
		func = func_arr[round];

		for (n = 0; n < 16; n++) {
			idx_a = arr[n][0];
			idx_b = arr[n][1];
			idx_c = arr[n][2];
			idx_d = arr[n][3];

			a = abcd[idx_a];
			b = abcd[idx_b];
			c = abcd[idx_c];
			d = abcd[idx_d];

			k = arr[n][4];
			s = arr[n][5];
			i = arr[n][6];

			val = a + func(b, c, d) + X[k] + MD5_T[i];

			val = md5_rotate_left(val, s);

			val += b;

			abcd[idx_a] = val;
		}
	}
	for (n = 0; n < 4; n++) {
		ABCD_VAL_WK[n] += abcd[n];
	}
}

// String MD5 Hash Value (128bit)
function md5hash(instr)
{
	var	len, i, j, k;
	var	str;
	var X = new Array(16);

	// Initialize T table
	md5_makeT();

	// padding input string
	len = md5_str_padding(instr);

	for (i = 0; i < 4; i++) {
		ABCD_VAL_WK[i] = ABCD_VAL[i];
	}

	for (i = 0; i < len; i += 64) {
		for (j = 0; j < 16; j++) {
			k = i + (j * 4);
			str = DataBuf.substring(k, k + 4);
			X[j] = md5_str_to_dword(str);
		}
		md5_round(X);
	}
	outstr = md5_dword_to_str(ABCD_VAL_WK[0]);
	outstr += md5_dword_to_str(ABCD_VAL_WK[1]);
	outstr += md5_dword_to_str(ABCD_VAL_WK[2]);
	outstr += md5_dword_to_str(ABCD_VAL_WK[3]);

	return outstr;
}

// MD5 Hash (128bits) Hex Str
function md5hash_hexstr(instr)
{
	var hash_val;
	var	hex_str = "";
	var i;
	var	c;
	var s;

	hash_val = md5hash(instr);

	for (i = 0; i < 16; i++) {
		c = hash_val.charCodeAt(i);
		s = c.toString(16);
		if (s.length < 2) {
			hex_str += "0";
		}
		hex_str += s;
	}
	return hex_str;
}
