/*==============================================================================

	This file contains various encryption and hashing related functions.
	
		SHA-1	- sha1hash(), f(), ROTL(), Number.prototype.toHexString()
		TEA		- TEAEncrypt(), TEADecrypt(), strToLongs(), longsToStr()
		RC4		- RC4EnDeCrypt_js() - conversion of RC4EnDeCrypt_vb so that it will
				produce exactly the same results.
				rc4 - a different implementation which does not produce the
				same results as RC4EnDeCrypt_vb/js.
		EncodeQString - wrapper for javascript escape
		DecodeQString - wrapper for javascript unescape
		
==============================================================================*/
				
		
/*------------------------------------------------------------------------------

		SHA-1 Cryptographic Hashing Algorithm implemented in javascript
		
		SHA-1 is a widely-used cryptographic hash algorithm for purposes such as
		encoding passwords which shouldn’t be stored or transmitted in clear, 
		creating digital signatures, etc
		
		copyright:  Chris Veness
		http://www.movable-type.co.uk/scripts/SHA-1.html
		
------------------------------------------------------------------------------*/

function sha1Hash(msg)
{
    // constants [4.2.1]
    var K = new Array(0x5a827999, 0x6ed9eba1, 0x8f1bbcdc, 0xca62c1d6);

    // PREPROCESSING 
 
    msg += String.fromCharCode(0x80); // add trailing '1' bit to string [5.1.1]

    // convert string msg into 512-bit/16-integer blocks arrays of ints [5.2.1]
    var l = Math.ceil(msg.length/4) + 2;  // long enough to contain msg plus 2-word length
    var N = Math.ceil(l/16);              // in N 16-int blocks
    var M = new Array(N);
    for (var i=0; i<N; i++) {
        M[i] = new Array(16);
        for (var j=0; j<16; j++) {  // encode 4 chars per integer, big-endian encoding
            M[i][j] = (msg.charCodeAt(i*64+j*4)<<24) | (msg.charCodeAt(i*64+j*4+1)<<16) | 
                      (msg.charCodeAt(i*64+j*4+2)<<8) | (msg.charCodeAt(i*64+j*4+3));
        }
    }
    // add length (in bits) into final pair of 32-bit integers (big-endian) [5.1.1]
    M[N-1][14] = ((msg.length-1) >>> 30) * 8;
    M[N-1][15] = ((msg.length-1)*8) & 0xffffffff;

    // set initial hash value [5.3.1]
    var H0 = 0x67452301;
    var H1 = 0xefcdab89;
    var H2 = 0x98badcfe;
    var H3 = 0x10325476;
    var H4 = 0xc3d2e1f0;

    // HASH COMPUTATION [6.1.2]

    var W = new Array(80); var a, b, c, d, e;
    for (var i=0; i<N; i++) {

        // 1 - prepare message schedule 'W'
        for (var t=0;  t<16; t++) W[t] = M[i][t];
        for (var t=16; t<80; t++) W[t] = ROTL(W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16], 1);

        // 2 - initialise five working variables a, b, c, d, e with previous hash value
        a = H0; b = H1; c = H2; d = H3; e = H4;

        // 3 - main loop
        for (var t=0; t<80; t++) {
            var s = Math.floor(t/20); // seq for blocks of 'f' functions and 'K' constants
            T = (ROTL(a,5) + f(s,b,c,d) + e + K[s] + W[t]) & 0xffffffff;
            e = d;
            d = c;
            c = ROTL(b, 30);
            b = a;
            a = T;
        }

        // 4 - compute the new intermediate hash value
        H0 = (H0+a) & 0xffffffff;  // note 'addition modulo 2^32'
        H1 = (H1+b) & 0xffffffff; 
        H2 = (H2+c) & 0xffffffff; 
        H3 = (H3+d) & 0xffffffff; 
        H4 = (H4+e) & 0xffffffff;
    }

    return H0.toHexStr() + H1.toHexStr() + H2.toHexStr() + H3.toHexStr() + H4.toHexStr();
}

//
// function 'f' [4.1.1]
//
function f(s, x, y, z) 
{
    switch (s) {
    case 0: return (x & y) ^ (~x & z);
    case 1: return x ^ y ^ z;
    case 2: return (x & y) ^ (x & z) ^ (y & z);
    case 3: return x ^ y ^ z;
    }
}

//
// rotate left (circular left shift) value x by n positions [3.2.5]
//
function ROTL(x, n)
{
    return (x<<n) | (x>>>(32-n));
}

//
// extend Number class with a tailored hex-string method 
//   (note toString(16) is implementation-dependant, and 
//   in IE returns signed numbers when used on full words)
//
Number.prototype.toHexStr = function()
{
    var s="", v;
    for (var i=7; i>=0; i--) { v = (this>>>(i*4)) & 0xf; s += v.toString(16); }
    return s;
}

/*---------------------------- end SHA-1 -------------------------------------*/

/*------------------------------------------------------------------------------

	This function was converted from RC4EnDeCrypt_vb in	xegy_encrypt.asp so that
	we would have identical functions in both VBScript and JavaScript, i.e. you
	can encrypt with one and decrypt with the other. 
	
------------------------------------------------------------------------------*/

function RC4EnDeCrypt_js(text, pwd) {

	var key = new Array(256);
	var sbox = new Array(256);
	var i, j, k, m, t;
	var cipherby;
	var cipher;
	
	//-------------------------- initialization --------------------------------
	
	for (i = 0; i < 256; i++) {
		key[i] = pwd.charCodeAt(i % pwd.length);
		sbox[i] = i;
	}

	j = 0;
	for (i = 0; i < 256; i++) {
		j = (j + sbox[i] + key[i]) % 256;
		t = sbox[i];
		sbox[i] = sbox[j]
		sbox[j] = t;
	}
		
	//-------------------------- encrypt or decrypt ----------------------------
	
	j = 0;
	k = 0;
	
	cipher = "";
	for (i = 0; i < text.length; i++) {
		j = (j + 1) % 256;
		k = (k + sbox[j]) % 256;
		t = sbox[j];
		sbox[j] = sbox[k];
		sbox[k] = t;
		
		m = sbox[(sbox[j] + sbox[k]) % 256];
			
		cipherby = text.charCodeAt(i)^m;
		cipher += String.fromCharCode(cipherby);
		
		//alert("m = " + m + ", " + text.charCodeAt(i) + ", cipherby = " + cipherby + ", cipher = " + cipher);
	}
	
	return cipher;	
}

/*------------------------------------------------------------------------------

			TEA - Tiny Encryption Algorithm implemented in Javascript
			
			TEA is a simple DES-style encryption algorithm which is easily 
			implemented in JavaScript, and good for encrypting anything you want
			stored or transmitted confidentially
			
			copyright:  Chris Veness
			http://www.movable-type.co.uk/scripts/TEAblock.html
			
------------------------------------------------------------------------------*/
//
// encrypt: Use 128 bits (16 chars) of string 'key' to encrypt string 'val'
//          (note key & val must be strings not string objects)
//           - return encrypted text as string
//
function TEAEncrypt(val, key)
{
    // 'escape' val so chars outside ISO-8859-1 work in single-byte packing, but 
    // keep spaces as spaces (not '%20') so encrypted text doesn't grow too long!
    var v = strToLongs(escape(val).replace(/%20/g,' '));
    var k = strToLongs(key.slice(0,16)); var n = v.length;

    if (n == 0) return("");  // nothing to encrypt
    if (n == 1) v[n++] = 0;  // algorithm doesn't work for n<2 so fudge by adding nulls

    // TEA routine as per Wheeler & Needham, Oct 1998

    var z = v[n-1], y = v[0], delta = 0x9E3779B9;
    var mx, e, q = Math.floor(6 + 52/n), sum = 0;

    while (q-- > 0) {  // 6 + 52/n operations gives between 6 & 32 mixes on each word
        sum += delta;
        e = sum>>>2 & 3;
        for (var p = 0; p < n-1; p++) {
            y = v[p+1];
            mx = (z>>>5 ^ y<<2) + (y>>>3 ^ z<<4) ^ (sum^y) + (k[p&3 ^ e] ^ z)
            z = v[p] += mx;
        }
        y = v[0];
        mx = (z>>>5 ^ y<<2) + (y>>>3 ^ z<<4) ^ (sum^y) + (k[p&3 ^ e] ^ z)
        z = v[n-1] += mx;
    }

    // note: unsigned right-shift '>>>' is used in place of original '>>', 
    // due to lack of 'unsigned' type declaration in JavaScript

    return longsToStr(v);
}


//
// decrypt: use 128 bits of string 'key' to decrypt string 'val' encrypted as per above
//
function TEAdecrypt(val, key)
{
    var v = strToLongs(val); var k = strToLongs(key.slice(0,16)); var n = v.length;

    if (n == 0) return("");

    // TEA routine as per Wheeler & Needham, Oct 1998

    var z = v[n-1], y = v[0], delta = 0x9E3779B9;
    var mx, e, q = Math.floor(6 + 52/n), sum = q*delta;

    while (sum != 0) {
        e = sum>>>2 & 3;
        for (var p = n-1; p > 0; p--) {
            z = v[p-1];
            mx = (z>>>5 ^ y<<2) + (y>>>3 ^ z<<4) ^ (sum^y) + (k[p&3 ^ e] ^ z)
            y = v[p] -= mx;
        }
        z = v[n-1];
        mx = (z>>>5 ^ y<<2) + (y>>>3 ^ z<<4) ^ (sum^y) + (k[p&3 ^ e] ^ z)
        y = v[0] -= mx;
        sum -= delta;
    }

    var s = longsToStr(v);
    if (s.indexOf("\x00") != -1) {
        // strip trailing null chars resulting from filling 4-char blocks
        s = s.substr(0, s.indexOf("\x00"));
    }

    return unescape(s);
}


// supporting functions

function strToLongs(s) {  // convert string to array of longs, each containing 4 chars
    // note chars must be within ISO-8859-1 (with Unicode code-point < 256) to fit 4/long
    var l = new Array(Math.ceil(s.length/4))
    for (var i=0; i<l.length; i++) {
        l[i] = s.charCodeAt(i*4) + (s.charCodeAt(i*4+1)<<8) + 
               (s.charCodeAt(i*4+2)<<16) + (s.charCodeAt(i*4+3)<<24);
    }
    return l;  // note running off the end of the string generates nulls since 
}              // bitwise operators treat NaN as 0

function longsToStr(l) {  // convert array of longs back to string
    var a = new Array(l.length);
    for (var i=0; i<l.length; i++) {
        a[i] = String.fromCharCode(l[i] & 0xFF, l[i]>>>8 & 0xFF, 
                                 l[i]>>>16 & 0xFF, l[i]>>>24 & 0xFF);
    }
    return a.join('');  // use Array.join() rather than repeated string appends for efficiency
}

/*---------------------- end TEA ---------------------------------------------*/

/*------------------------------------------------------------------------------

	RC4 stream encryption
	adapted from www.cpan.org crypt::rc4 -- thanks!
	
------------------------------------------------------------------------------*/
	
function rc4(key, text) {

	var i, x, y, t, x2, kl = key.length;
	s = [];

	for (i = 0; i < 256; i++) {
		s[i] = i;
	}
	
	y = 0;
	x = kl;
	
	while(x--) {
		y = (key.charCodeAt(x) + s[x] + y) % 256;
		t = s[x];
		s[x] = s[y];
		s[y] = t;
	}
	
	x = 0;
	y = 0;
	var z = "";
	
	for (x = 0; x < text.length; x++) {
		x2 = x & 255;
		y = (s[x2] + y) & 255;
		t = s[x2];
		s[x2] = s[y];
		s[y] = t;
		z += String.fromCharCode((text.charCodeAt(x) ^ s[(s[x2] + s[y]) % 256]));
	}
	return z
}

/*----------------------- end rc4 --------------------------------------------*/

function EncodeQString(qStr) {
	return escape(qStr);
}

function DecodeQString(qStr) {
	return unescape(qStr);
}
	