/* Copyright (C) 2003 Donald J Bindner.
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 */

var delta = 0x9E3779B9;
var k = new Array();
var r=0;

function tea_encode( str, key ) {
/* Encodes a string 'str' using secret key 'key' and returns
 * a Base64 encoded string.
 */
    return TEAArrayToBase64( 
	    tea_encodeCBC( stringToTEAArray(str), MD5key(key))
	    );
}

function tea_decode( str, key ) {
/* Decodes a Base64 encoded string 'str' using secret key 'key' and
 * returns the original plaintext.
 */
    return TEAArrayToString(
	    tea_decodeCBC( base64ToTEAArray(str), MD5key(key))
	    );
}

function stringToTEAArray( str ) {
    /* converts a string to an padded array of longs with an IV */

    var i,j,l;
    var strl = str.length;

    var y,z;

    y = Math.floor( Math.random() * 256 * 256 * 256 * 256 );
    z = Math.floor( Math.random() * 256 * 256 * 256 * 256 );

    var arr = [ 0x646e6152, 0x56496d6f, y, z ];
    arr.length = Math.floor(strl/8+1)*2+4;

    i=4;
    j=0;
    l = strl - strl%8;
    while( j < l ) {
        arr[i++] = str.charCodeAt( j++ ) |
                   str.charCodeAt( j++ )<<8 |
                   str.charCodeAt( j++ )<<16 |
                   str.charCodeAt( j++ )<<24;
        arr[i++] = str.charCodeAt( j++ ) |
                   str.charCodeAt( j++ )<<8 |
                   str.charCodeAt( j++ )<<16 |
                   str.charCodeAt( j++ )<<24;
    }

    /* Pad the end of the string */
    var tail = str.slice(l,strl);
    for( j = 0; j < 8-strl+l; j++ ) {
      tail += String.fromCharCode( 8-strl+l );
    }
    arr[i++] = tail.charCodeAt( 0 ) |
               tail.charCodeAt( 1 )<<8 |
               tail.charCodeAt( 2 )<<16 |
               tail.charCodeAt( 3 )<<24;
    arr[i++] = tail.charCodeAt( 4 ) |
               tail.charCodeAt( 5 )<<8 |
               tail.charCodeAt( 6 )<<16 |
               tail.charCodeAt( 7 )<<24;

    return arr;
}


function TEAArrayToString( arr ) {
    /* converts a padded array of longs to a string */
    var l = arr.length - 2;
    str = new String;

    for( var i = 4; i < l; i++ ) {
        str += String.fromCharCode( arr[i] & 0xff ) +
               String.fromCharCode( arr[i]>>>8 & 0xff ) +
               String.fromCharCode( arr[i]>>>16 & 0xff ) +
               String.fromCharCode( arr[i]>>>24 & 0xff );
    }

    var tail = String.fromCharCode( arr[l] & 0xff ) +
               String.fromCharCode( arr[l]>>>8 & 0xff ) +
               String.fromCharCode( arr[l]>>>16 & 0xff ) +
               String.fromCharCode( arr[l]>>>24 & 0xff ) +
               String.fromCharCode( arr[l+1] & 0xff ) +
               String.fromCharCode( arr[l+1]>>>8 & 0xff ) +
               String.fromCharCode( arr[l+1]>>>16 & 0xff ) +
               String.fromCharCode( arr[l+1]>>>24 & 0xff );

    /* clip off the padding characters */
    var clip = arr[l+1]>>>24;
    str += tail.slice(0,8-clip);

    return str;
}

function tea_setup( key, rounds ) {
    /* sets up the cipher with key and rounds */
    r = rounds;
    k = key;
}

function tea_encodeCBC( arr, md5key ) {
    /* accepts a padded array of longs and a key array of 4 32 bit numbers
     * (typically the MD5 of the password), and returns an encrypted array
     * of longs 
     */

    var sum;
    var y, z;

    /* Get the IV */
    y = arr[2];
    z = arr[3];

    /* initialize TEA with MD5 hash of our key and 32 rounds */
    tea_setup( md5key, 32 );

    var l = arr.length;
    var i = 4;
    /* xor each block with the cipher text from previous round and encode */
    while( i < l ) {
	y ^= arr[i];
	z ^= arr[i+1];

        /* This is the actual encode step */
	sum=0;
	for( var j = 0; j < r; j++ ) {
            y += ( ((z<<4)^(z>>>5)) + z ) ^ ( sum + k[sum&3] );
	    sum += delta;
	    z += ( ((y<<4)^(y>>>5)) + y ) ^ ( sum + k[(sum>>>11)&3]);
	}

	arr[i++] = y^0;
	arr[i++] = z^0;
    }

    return arr;
}

function tea_decodeCBC( arr, md5key ) {
    /* accepts a padded array of longs and a key array of 4 32 bit
     * numbers (typically the MD5 of the password), and returns a
     * decrypted array
     */

    var y,z;
    var ny, nz;

    /* random initial vector */
    var oy = arr[2];
    var oz = arr[3];

    /* initialize TEA with MD5 hash of our key and 32 rounds */
    tea_setup( md5key, 32 );

    var l = arr.length;
    var i = 4;
    /* decrypt each block and xor with the cipher text from previous round */
    while( i < l ) {
	y = ny = arr[i];
	z = nz = arr[i+1];

	/* This is the actual decode step */
	sum = delta*r;
	for( var j = 0; j < r; j++ ) {
	    z -= ( ((y<<4)^(y>>>5)) + y ) ^ ( sum + k[(sum>>>11)&3]);
	    sum -= delta;
	    y -= ( ((z<<4)^(z>>>5)) + z ) ^ ( sum + k[sum&3] );
	}

	arr[i++] = y^oy;
	arr[i++] = z^oz;

	oy = ny;
	oz = nz;
    }

    return arr;
}

var b64chr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";

function TEAArrayToBase64( arr ) {
    /* Converts an array of longs to a base64 string */
    var str = new String();
    var a, b, c, d;
    var llen;

    /* characters to keep in the next iteration of the loop */
    var o1, o2;
    var j;

    var offset = 0;
    var l = arr.length;
    var llen = 0;
    for( var i = 0; i < l; i++ ) {
        a = arr[i] & 0xff;
	b = arr[i]>>>8 & 0xff;
	c = arr[i]>>>16 & 0xff;
	d = arr[i]>>>24 & 0xff;

        if( offset == 0 ) {
	    str += b64chr.charAt( a>>>2 & 0x3f );
	    str += b64chr.charAt( ((a&3)<<4) + (b>>>4) );
	    str += b64chr.charAt( ((b&15)<<2) + (c>>>6) );
	    str += b64chr.charAt( c & 0x3f );

	    o1 = d;
	} else if( offset == 1 ) {
	    str += b64chr.charAt( o1>>>2 & 0x3f );
	    str += b64chr.charAt( ((o1&3)<<4) + (a>>>4) );
	    str += b64chr.charAt( ((a&15)<<2) + (b>>>6) );
	    str += b64chr.charAt( b & 0x3f );

	    o1 = c;
	    o2 = d;
        } else {
	    str += b64chr.charAt( o1>>>2 & 0x3f );
	    str += b64chr.charAt( ((o1&3)<<4) + (o2>>>4) );
	    str += b64chr.charAt( ((o2&15)<<2) + (a>>>6) );
	    str += b64chr.charAt( a & 0x3f );

	    llen += 4;
	    if( llen >= 60 ) {
		str += "\n";
		llen = 0;
	    }

	    str += b64chr.charAt( b>>>2 & 0x3f );
	    str += b64chr.charAt( ((b&3)<<4) + (c>>>4) );
	    str += b64chr.charAt( ((c&15)<<2) + (d>>>6) );
	    str += b64chr.charAt( d & 0x3f );
	}
	offset = ( offset + 1 )%3;

	llen += 4;
	if( llen >= 60 ) {
	    str += "\n";
	    llen = 0;
	}
    }

    if( offset == 1 ) {
	str += b64chr.charAt( o1>>>2 );
	str += b64chr.charAt( (o1&3)<<4 );
	str += "==";
    } else if( offset == 2 ) {
	str += b64chr.charAt( o1>>>2 );
	str += b64chr.charAt( ((o1&3)<<4) + (o2>>>4) );
	str += b64chr.charAt( (o2&15)<<2 );
	str += "=";
    }

    return str;
}

function base64ToTEAArray( str ) {
    /* Decodes a base64 string to an array of longs */
    var arr = new Array;
    var j = 0;
    var offset = 0;
    var tmp, pad=0;

    var l = str.length;
    var i = 0;
    while( i < l ) {
	while( i<l  &&  b64chr.indexOf( str.charAt(i)) == -1 ) i++;
	if( i >= l ) break;
	if( str.charAt(i) == "=" ) break;
	a = b64chr.indexOf( str.charAt( i++ ));

	while( i<l  &&  b64chr.indexOf( str.charAt(i)) == -1 ) i++;
	if( i >= l ) break;
	if( str.charAt(i) == "=" ) break;
	b = b64chr.indexOf( str.charAt( i++ ));

	while( i<l  &&  b64chr.indexOf( str.charAt(i)) == -1 ) i++;
	if( i >= l ) break;
	if( str.charAt(i) == "=" ) {
	    pad = 2;
	    c = d = 0;
	    break;
	}
	c = b64chr.indexOf( str.charAt( i++ ));

	while( i<l  &&  b64chr.indexOf( str.charAt(i)) == -1 ) i++;
	if( i >= l ) break;
	if( str.charAt(i) == "=" ) {
	    pad = 1;
	    d = 0;
	    break;
	}
	d = b64chr.indexOf( str.charAt( i++ ));

        tmp = (a<<2) | (d<<16) |
	      (b>>>4) | ((b & 0xf)<<12 ) |
	      ((c & 0x03)<<22) | ((c & 0x3c)<<6 );

	if( offset == 0 ) {
	    arr[j] = tmp;
	} else if( offset == 1 ) {
	    arr[j++] |= tmp<<8;
	} else if( offset == 2 ) {
	    arr[j++] |= tmp<<16;
	    arr[j] = tmp>>>16;
	} else if( offset == 3 ) {
	    arr[j++] |= tmp<<24;
	    arr[j] = tmp>>>8;
	}

	offset = (offset + 3)%4;
    }

    tmp = (a<<2) |
	(b>>>4) | ((b & 0xf)<<12 ) |
	((c & 0x03)<<22) | ((c & 0x3c)<<6 );

    if( pad == 2 ) {
	arr[j] |= tmp << 24;
    } else if( pad == 1 ) {
	arr[j] |= tmp << 16;
    }

    return arr;
}

