//
// xxTEAencrypt: Use Corrected Block TEA to encrypt plaintext using password
//            (note plaintext & password must be strings not string objects)
//
// Return encrypted text as string
//
function xxTEAencrypt(plaintext, password)
{
    if (plaintext.length == 0) return('');  // nothing to encrypt
    var v = strToLongs(plaintext);
    if (v.length == 1) v[1] = 0;  // algorithm doesn't work for n<2 so fudge by adding nulls
    var k = strToLongs(password.slice(0,16));  // simply convert first 16 chars of password as key
    var n = v.length;

    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 use of >>> in place of >> due to lack of 'unsigned' type in JavaScript 

    //return escape(longsToStr(v)); // alternate style
    return longsToHexStr(v);
}

//
// xxTEAdecrypt: Use Corrected Block TEA to decrypt ciphertext using password
//
function xxTEAdecrypt(ciphertext, password)
{
    if (ciphertext.length == 0) return('');
    var v = HexStrToLongs(ciphertext);
    // var v = strToLongs(unescape(ciphertext)); // alternate style
    var k = strToLongs(password.slice(0,16)); 
    var n = v.length;

    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 plaintext = longsToStr(v);
    // strip trailing null chars resulting from filling 4-char blocks:
    if (plaintext.search(/\0/) != -1) plaintext = plaintext.slice(0, plaintext.search(/\0/));

    return plaintext;
}

// 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));
    var j = 0;
    for (var i=0; i < s.length; i += 4, j++) {
        l[j] = str4ToLong(s.slice(i, i+4));
    }
    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 full = '';
    for (var i=0; i<l.length; i++) {
        var s = '';
        s = longToStr4(l[i]);
        full += s;
    }
    return(full);
}

function str4ToLong(s)  // convert 4 chars of s to a numeric long
{
    var v = 0;
    for (i=0, shift = 24; i<4; i++, shift -= 8) {
        ccode = s.charCodeAt(i) << shift;
        if (isNaN(ccode))
            ccode = 0;
        v += ccode;
    }
    return v;
}

function longToStr4(v)  // convert a numeric long to 4 char string
{
    var s = String.fromCharCode(v>>>24 & 0xFF, v>>>16 & 0xFF, v>>>8 & 0xFF, v & 0xFF);
    return(s);
}

function longsToHexStr(l) {  // convert array of longs to a hex string
    var full = '';
    for (var i=0; i<l.length; i++) {
        var s = '';
        s = LongToStr8Hex(l[i]);
        full += s;
    }
    return(full);
}

function HexStrToLongs(str)  // convert a hex string to an array of longs
{
    var l = new Array(str.length/8);
    var j = 0;
    for (var i=0; i < str.length; i += 8, j++) {
        var k = Str8HexToLong(str.slice(i, i+8));
        l[j] = k;
    }
    return l;
}

function Str8HexToLong(str)  // convert an 8 char hex string to a long
{
    var v = 0;
    for (i=0, shift = 24; i<8; i += 2, shift -= 8) {
        v += unNibble(str.charCodeAt(i), str.charCodeAt(i+1)) << shift;
    }
    return v;

}

function LongToStr8Hex(v)  // convert a numeric long to 8 char hex string
{
    var s = '';
    s = nibble(v >>> 28);
    s += nibble(v >>> 24);
    s += nibble(v >>> 20);
    s += nibble(v >>> 16);
    s += nibble(v >>> 12);
    s += nibble(v >>> 8);
    s += nibble(v >>> 4);
    s += nibble(v);
    return(s);
}

function nibble(dec) {
    return "0123456789ABCDEF".substr(dec & 0x0F, 1);
}

function unNibble(highHexDigit, lowHexDigit) {
    var n = 0;
    if (highHexDigit >= 48 && highHexDigit <= 57)   // '0' thru '9'
        highHexDigit = highHexDigit - 48;
    else if (highHexDigit >= 65 && highHexDigit <= 70) // 'A' thru 'F'
        highHexDigit = (highHexDigit - 65) + 10;
    if (lowHexDigit >= 48 && lowHexDigit <= 57)
        lowHexDigit = lowHexDigit - 48;
    else if (lowHexDigit >= 65 && lowHexDigit <= 70)
        lowHexDigit = (lowHexDigit - 65) + 10;
    n = (highHexDigit << 4) + lowHexDigit;
    return n;
}


