// BEGIN - CHRIS LOMONT'S ONLINE PASSWORD HASHER
// Modified by Mike Sherwood to follow OO properties. 2010.
// Inspiration of change from: http://www.webtoolkit.info/javascript-base64.html

/*
 * A JavaScript implementation of the Whirlpool hash function
 * Version 1.0 Copyright (C) Chris Lomont 2006.
 * Version 1.1 Copyright (C) Mike Sherwood 2010.
 * See www.lomont.org for more info.
 */

// functions from the definition paper, see 
// http://paginas.terra.com.br/informatica/paulobarreto/WhirlpoolPage.html

var	Whirlpool = {

	//private method, finite field multiplication. 
	_fieldMult: function( a, b ){
		// multiply two field elements in GF(2^8) with poly x^8 + x^4 + x^3 + x^2 + 1
		// finite field 2^8 is from poly x^8 + x^4 + x^3 + x^2 + 1, and then x is primitive
		poly = 16+8+4+1; // overflow value
		val = 0;
		while (0 != a){
			if (0 != (a&1))
				val ^= b;
			// divide a by x
			a >>= 1;     
			// multiply b by x
			if (0 != (b&128))
				b = (b<<1)^poly;
			else
				b <<= 1;
		}
		return val&255;
	}, //Whirlpool._fieldMult

	//private method, returns m1*m2 as matrices
	_matrixMult: function( m1, m2 ){
		dest = new Array(64); // in case dest is one of the inputs
		//tij = sum m1Whirlpool._ik m2Whirlpool._kj
		for (i = 0; i <= 7; ++i)
			for (j = 0; j <= 7; ++j){
				dest[8*i+j] = 0;
				for (k = 0; k <= 7; ++k)
					dest[8*i+j] ^= Whirlpool._fieldMult(m1[8*i+k],m2[8*k+j]);
			}
		return dest;
	}, //Whirlpool._matrixMult
	
	//private method, the SBox value using the table in the paper
	_sBox: function( val ) {
		vals = [
		    0x18, 0x23, 0xc6, 0xE8, 0x87, 0xB8, 0x01, 0x4F, 0x36, 0xA6, 0xd2, 0xF5, 0x79, 0x6F, 0x91, 0x52,
		    0x60, 0xBc, 0x9B, 0x8E, 0xA3, 0x0c, 0x7B, 0x35, 0x1d, 0xE0, 0xd7, 0xc2, 0x2E, 0x4B, 0xFE, 0x57,
		    0x15, 0x77, 0x37, 0xE5, 0x9F, 0xF0, 0x4A, 0xdA, 0x58, 0xc9, 0x29, 0x0A, 0xB1, 0xA0, 0x6B, 0x85,
		    0xBd, 0x5d, 0x10, 0xF4, 0xcB, 0x3E, 0x05, 0x67, 0xE4, 0x27, 0x41, 0x8B, 0xA7, 0x7d, 0x95, 0xd8,
		    0xFB, 0xEE, 0x7c, 0x66, 0xdd, 0x17, 0x47, 0x9E, 0xcA, 0x2d, 0xBF, 0x07, 0xAd, 0x5A, 0x83, 0x33,
		    0x63, 0x02, 0xAA, 0x71, 0xc8, 0x19, 0x49, 0xd9, 0xF2, 0xE3, 0x5B, 0x88, 0x9A, 0x26, 0x32, 0xB0,
		    0xE9, 0x0F, 0xd5, 0x80, 0xBE, 0xcd, 0x34, 0x48, 0xFF, 0x7A, 0x90, 0x5F, 0x20, 0x68, 0x1A, 0xAE,
		    0xB4, 0x54, 0x93, 0x22, 0x64, 0xF1, 0x73, 0x12, 0x40, 0x08, 0xc3, 0xEc, 0xdB, 0xA1, 0x8d, 0x3d,
		    0x97, 0x00, 0xcF, 0x2B, 0x76, 0x82, 0xd6, 0x1B, 0xB5, 0xAF, 0x6A, 0x50, 0x45, 0xF3, 0x30, 0xEF,
		    0x3F, 0x55, 0xA2, 0xEA, 0x65, 0xBA, 0x2F, 0xc0, 0xdE, 0x1c, 0xFd, 0x4d, 0x92, 0x75, 0x06, 0x8A,
		    0xB2, 0xE6, 0x0E, 0x1F, 0x62, 0xd4, 0xA8, 0x96, 0xF9, 0xc5, 0x25, 0x59, 0x84, 0x72, 0x39, 0x4c,
		    0x5E, 0x78, 0x38, 0x8c, 0xd1, 0xA5, 0xE2, 0x61, 0xB3, 0x21, 0x9c, 0x1E, 0x43, 0xc7, 0xFc, 0x04,
		    0x51, 0x99, 0x6d, 0x0d, 0xFA, 0xdF, 0x7E, 0x24, 0x3B, 0xAB, 0xcE, 0x11, 0x8F, 0x4E, 0xB7, 0xEB,
		    0x3c, 0x81, 0x94, 0xF7, 0xB9, 0x13, 0x2c, 0xd3, 0xE7, 0x6E, 0xc4, 0x03, 0x56, 0x44, 0x7F, 0xA9,
		    0x2A, 0xBB, 0xc1, 0x53, 0xdc, 0x0B, 0x9d, 0x6c, 0x31, 0x74, 0xF6, 0x46, 0xAc, 0x89, 0x14, 0xE1,
		    0x16, 0x3A, 0x69, 0x09, 0x70, 0xB6, 0xd0, 0xEd, 0xcc, 0x42, 0x98, 0xA4, 0x28, 0x5c, 0xF8, 0x86
		]; // 256 entries
		return vals[val];
	},
	
	//private method, XOR src into dest bytewise, return dest
	_xor: function( dest, src ) {
		for ( pos = 0; pos < 64; ++pos)
			dest[pos] ^= src[pos];
	}, //Whirlpool._xor
	
	//private method, compute the rho function from the paper
	_applyRho: function( value, parameter ) { // compute rho[parameter](value)
		
		// apply gamma: applies SBox to each byte in value
		for (pos = 0; pos < 64; ++pos)
			value[pos] = Whirlpool._sBox(value[pos]);

		// apply pi: cyclical permutation bWhirlpool._i,j = aWhirlpool._(i-j)mod 8, j
		temp = new Array(64);
		for (i = 0; i < 8; ++i)
			for (j = 0; j < 8; ++j)
				temp[8*i+j] = value[8*((i-j+8)&7)+j];
		for (i =0; i < 64; ++i)
			value[i] = temp[i]; // copy back

		// apply theta: linear diffusion 
		 C = [ // the constant matrix
			0x01, 0x01, 0x04, 0x01, 0x08, 0x05, 0x02, 0x09,
			0x09, 0x01, 0x01, 0x04, 0x01, 0x08, 0x05, 0x02,
			0x02, 0x09, 0x01, 0x01, 0x04, 0x01, 0x08, 0x05,
			0x05, 0x02, 0x09, 0x01, 0x01, 0x04, 0x01, 0x08,
			0x08, 0x05, 0x02, 0x09, 0x01, 0x01, 0x04, 0x01,
			0x01, 0x08, 0x05, 0x02, 0x09, 0x01, 0x01, 0x04,
			0x04, 0x01, 0x08, 0x05, 0x02, 0x09, 0x01, 0x01,
			0x01, 0x04, 0x01, 0x08, 0x05, 0x02, 0x09, 0x01
			]; // 64 entries
		value = Whirlpool._matrixMult(value,C);

		// apply sigma[parameter]: key addition
		Whirlpool._xor(value,parameter);
		return value;
	}, //Whirlpool._applyRho
	
	//Private method, update the round key
	_nextKey: function( key, c, round ) {// compute next key and round constant
		// next constant
		for (j = 0; j <= 7; ++j)
			c[j] = Whirlpool._sBox(8*(round-1)+j);

		key = Whirlpool._applyRho(key,c);
		return key;
	}, //Whirlpool._nextKey
	
	//Private method, convert val to hex string
	_hex: function( val ){
		 txt = [
			"0","1","2","3","4","5","6","7",
			"8","9","a","b","c","d","e","f"
		];
		temp = "";
		while (0 != val){
			temp = txt[val&15] + temp;
			val >>= 4;
		}
		while (temp.length < 2)
			temp = "0" + temp;
		return temp;
	}, //Whirlpool._hex
	
	//Private method, compute W[hash](data) function and store in W
	_computeW: function( W, hash, data ){
		key = new Array(64); // round key
		c   = new Array(64); // round constant
		
		for (i = 0; i < 64; ++i){
			c[i]   = 0;       // zero out round constant ci
			key[i] = hash[i]; // key = K0 = current hash, length 64
			W[i]   = data[i]; // copy here - this is work space, length 64
	    }
			
		Whirlpool._xor(W,key); // sigma[K0](data) = sigma[K0](ni) in paper

		// do rounds - we need to compose the sigma(K^round) functions
		for ( round = 1; round <= 10; ++round){
			key = Whirlpool._nextKey(key,c,round); // next round key and constant computed
			W   = Whirlpool._applyRho(W,key);      // and applied to work state
	    }
		return W;
	}, //Whirlpool._computeW
	
	//Private method, 
	_hashBlock: function( hash, data ) {
		W = new Array(64); 
		W = Whirlpool._computeW(W,hash,data); // compute the W[Hi](ni) = W[hash](data) function and store in W
		Whirlpool._xor(hash,W);
		Whirlpool._xor(hash,data);
	}, //Whirlpool._hashBlock
	
	//Private method, hash a block of data, given the size
	_whirlpool: function( data, size ){
		dataPtr = 0;
		dataBlock = new Array(64); // one block of data to hash
		hash      = new Array(64);
		for ( i = 0; i < 64; ++i)
			hash[i] = 0; // zero hash (IV - Initialization Vector)

		// do blocks of data until not enough data left
		tempSize = size;
		while (tempSize >= 64){
			for (i = 0; i < 64; ++i)
				dataBlock[i] = data[i+dataPtr];
			Whirlpool._hashBlock(hash,dataBlock);
			dataPtr  += 64;
			tempSize -= 64;
	    }

		// append final bit, and length, as required by the spec
		temp = new Array(64*2);
		for ( i = 0; i < 64*2; ++i)
			temp[i] = 0; // zero out - may need a lot of space
		
		for ( i = 0; i < tempSize; ++i)
			temp[i] = data[i+dataPtr]; // copy remaining data
		
		temp[tempSize] = 0x80;         // append final bit, next position
		
		bits = size*8;
		if (tempSize >= 64/2){
			// need 2 blocks - last one didn't leave 256 bits space for appending size
			tempPtr = 2*64-1; // start at back
			while (bits > 0)
				{
				temp[tempPtr--] = (bits&255);
				bits >>= 8;
				}
			Whirlpool._hashBlock(hash,temp);      // hash this block
			for ( i = 0; i < 64; ++i)
				dataBlock[i] = temp[i+64];		
			Whirlpool._hashBlock(hash,dataBlock); // and this one
	    } else {
	        // need 1 block - last one did leave 256 bits space for appending size
	        tempPtr = 64-1; // start at back
	        while (bits > 0)
	            {
	            temp[tempPtr--] = (bits&255);
	            bits >>= 8;
	            }
	        Whirlpool._hashBlock(hash,temp); // hash this block
	    }
		return hash;
	}, //Whirlpool._whirlpool
	
	//Private method, Convert a string to an array of bytes
	_stringToBytes: function( str ) {
		arr = Array(str.length);
		for ( i = 0; i < str.length; ++i)
			arr[i] = str.charCodeAt(i)&255;
		return arr;
	}, //Whirlpool._stringToBytes
	
	//Public method, convert a string to a hexstring with the whirlpool hash
	hash: function( str ) {
		vHash = "";
		var temp = "";
		str = Whirlpool._stringToBytes(str); // todo unicode?
		vHash = Whirlpool._whirlpool(str, str.length);
		for ( i = 0; i < 64; ++i)
			temp += Whirlpool._hex(vHash[i]);
		return temp;
	}, //hash

	// divide array bigVal.val representing number by integer divisor < 256, and 
	// return remainder in bigVal.remainder
	// modifies bigVal
	//Private method
	_divideRemainder: function( bigVal, divisor ) {
		top = bigVal.val.length-1;
		if (top < 0)		{
			bigVal.remainder = 0;
			return; // nothing to do?!
		}
			
		answer   = new Array(); // our answer goes here
		dividend = 0; // current dividend
		ansPos   = 0; // current answer digit (reversed)
		digit    = 0; // last digit added
			
		// standard long division
		while (top >= -1)		{
			digit = Math.floor(dividend/divisor); // next digit
			answer[ansPos++] = digit; // save digit
			dividend = dividend-digit*divisor; // remove amount
			if (top >= 0)
				dividend = 256*dividend + bigVal.val[top]; // bring next digit down
			--top;
		}
				
		bigVal.remainder = dividend;   // leftover
			
		// copy back reversed, strip off leading 0's
		bigVal.val = new Array();
		firstNonzero = 0;
		while ((firstNonzero < answer.length) && (0 == answer[firstNonzero]))
			firstNonzero++;
		j = 0;
		for ( i = answer.length-1; i >= firstNonzero; --i){
			bigVal.val[j++] = answer[i];
			seenNonzero = true;
		}
	},
	
	test: function(){
		return hash("abc") == "4e2448a4c6f486bb16b6562c73b4020bf3043e3a731bce721ae1b303d97e6d4c7181eebdb6c57e277d0e34957114cbd6c797fc9d95d8b582d225292076d4eef5";
	}
};
