//////////////////////////////////////////////////////////////////////////////////////////////
//  This file contains the javascript necessary for secure communication between client and server
//  The functions that will be used external
//  to this file are located at the very bottom of this file
//  Everything in the middle is supporting code and should not be modified
//  This file contains the following sections:
//    Key Generation Section that creates random keys for encryption
//    Encoding Section for UTF8 and Base64 Encoding
//    AES Section, a Javascript implementation of Rijndael Encryption
//      courtesy of Fritz Schneider http://www-cse.ucsd.edu/~fritz/rijndael.html
//    BIG INT LIBRARY, used for RSA Encryption
//      courtesy of Leemon Baird www.leemon.com
//    External Function Section that contains all of the functions used externally
//  In the interest of size, I have deleted any unused functions and all comments
//  The origional javascript can be found in SecurityReference.js
//////////////////////////////////////////////////////////////////////////////////////////////
var prefixEncryptedFull = "_";
var prefixEncrypted = "-";
var prefixBase64 = "#";
///////////////////////////////// BEGIN KEY GENERATION SECTION ////////////////////////////
var GlobalWindowName = window.name;
window.name = "window";
var KeyMaterial = Array();
var hexCharacters = "0123456789ABCDEF";
function setSignificantBit(mychar) {
  mycode = mychar.charCodeAt(0);
  if(mycode == 48 || mycode == 49)
    return String.fromCharCode(mycode + 8);
  if(mycode >= 50 && mycode <= 55)
    return String.fromCharCode(mycode + 15);
  return mychar;
}
function generateAESKey() {
  ret = Array();
  for(i=0; i<AESKeySize/4; i++)
    ret[i] = KeyMaterial[i];
  ret[0] = setSignificantBit(ret[0]);
  return ret.join("");
}
function generateAESIV() {
  ret = Array();
  for(i=0; i<AESBlockSize/4; i++)
    ret[i] = hexCharacters.charAt(Math.floor(Math.random()*16));
  if(ret[0] == "0")
    ret[0] = hexCharacters.charAt(Math.floor(Math.random()*15)+1);
  return ret.join("");
}
function generateTripleDESKey() {
  ret = Array();
  for(i=0; i<48; i++)
    ret[i] = KeyMaterial[i];
  ret[0] = setSignificantBit(ret[0]);
  return ret.join("");
}
function generateTripleDESIV() {
  ret = Array();
  for(i=0; i<16; i++)
    ret[i] = hexCharacters.charAt(Math.floor(Math.random()*16));
  if(ret[0] == "0")
    ret[0] = hexCharacters.charAt(Math.floor(Math.random()*15)+1);
  return ret.join("");
}
function RandomizeKeyMaterial() {
  KeyMaterial[ Math.floor(Math.random()*64) ] = hexCharacters.charAt(Math.floor(Math.random()*16));
  // note: 500 is optimized for IE and Firefox on my machine.  The performance impact is significant
  setTimeout("RandomizeKeyMaterial()", Math.floor(Math.random()*500));
}
function InitializeRandom() {
  for(i=0; i<64; i++) {
    KeyMaterial[i] = hexCharacters.charAt(Math.floor(Math.random()*16));
  }
  //uncomment this line to enable active key scrambling
  //RandomizeKeyMaterial();  
}  
InitializeRandom();
///////////////////////////////// END KEY GENERATION SECTION ////////////////////////////
///////////////////////////////////////// BEGIN ENCODING SECTION /////////////////////////////////
//I (Brian) programmed these, so there are no license problems
function utf8Encrypt(s) {
  ret = Array();
  retIndex = 0;
  for(i=0; i<s.length; i++) {
    code = s.charCodeAt(i);
    if(code < 0x80) {
      ret[retIndex++] = code;
    }
    else if(code >= 0x80 && code < 0x800) {
      ret[retIndex++] = ((code >> 6) | 0xc0);
      ret[retIndex++] = ((code & 0x00003f) | 0x80);
    }
    else if(code >= 0x800 && code < 0x10000) {
      ret[retIndex++] = ((code >> 12) | 0xe0);
      ret[retIndex++] = (((code >> 6) & 0x00003f) | 0x80);
      ret[retIndex++] = ((code & 0x00003f) | 0x80);
    }
    else {
      if(code > 0x10FFFF)
        return "Error";
      ret[retIndex++] = ((code >> 18) | 0xf0);
      ret[retIndex++] = (((code >> 12) & 0x00003f) | 0x80);
      ret[retIndex++] = (((code >> 6) & 0x00003f) | 0x80);
      ret[retIndex++] = ((code & 0x00003f) | 0x80);  
    }    
  }
  return ret;
}

// Brian didn't program utfDecrypt and base64Decrypt - need to rewrite these (current ones are perhaps GNU public)
function utf8Decrypt(bData)
{
  var sData = "";
  var i=0;
  while (i<bData.length){
    var c = bData[i++];
    var e = '0x'+c.toString(16);
    var k=0; while(c&0x80){ c=(c<<1)&0xFF; k++; }
    c >>= k;
    if (k==1||k>4) throw('UTF-8: invalid first byte '+e);
    for (var n=1;n<k;n++){
      var d = bData[i++];
      e+=',0x'+d.toString(16);
      if (d<0x80||d>0xBF) break;
      c=(c<<6)+(d&0x3F);
    }
    if ( (k==2&&c<0x80) || (k>2&&c<utf8sets[k-3]) ) throw("UTF-8: invalid sequence "+e+'; c='+c);
    sData+=String.fromCharCode(c);
  }
  return sData;
}

var b64Characters = new Array('A','B','C','D','E','F','G','H','I','J','K','L',
  'M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f',
  'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z',
  '0','1','2','3','4','5','6','7','8','9','+','/');

function b64EncryptBytes(bData) {
  var i=0;
  var b1,b2,b3;
  var result = new Array();
  var resultIndex = 0;
  var blength = bData.length;
  while(i+3 <= blength) {
    b1 = bData[i++];
    b2 = bData[i++];
    b3 = bData[i++];
    result[resultIndex++] = b64Characters[b1 >> 2];
    result[resultIndex++] = b64Characters[ ((b1 << 4) & 0x30) | (b2 >> 4) ];
    result[resultIndex++] = b64Characters[ ((b2 << 2) & 0x3c) | (b3 >> 6) ];
    result[resultIndex++] = b64Characters[ b3 & 0x3f ];
  }
  if(i+1 == bData.length) {
    b1 = bData[i++];
    b2 = 0x00;
    result[resultIndex++] = b64Characters[b1 >> 2];
    result[resultIndex++] = b64Characters[ ((b1 << 4) & 0x30) | (b2 >> 4) ];
    result[resultIndex++] = "=";
    result[resultIndex++] = "=";
  } else if(i+2 == bData.length) {
    b1 = bData[i++];
    b2 = bData[i++];
    b3 = 0x00;
    result[resultIndex++] = b64Characters[b1 >> 2];
    result[resultIndex++] = b64Characters[ ((b1 << 4) & 0x30) | (b2 >> 4) ];
    result[resultIndex++] = b64Characters[ ((b2 << 2) & 0x3c) | (b3 >> 6) ];
    result[resultIndex++] = "=";
  }
  return result.join("");
}

function b64DecryptBytes(sData) { // Should rewrite this - since I think comes from GNU public
  var x = new Array(4);
  var i = 0;
  var j=0;
  var tot = sData.length;
  var bData=[];
  while(i<sData.length)
  {
    for (var k=0;k<4;k++)
    {
      var n = sData.substr(i).search(/\S/);
      i = n<0?tot:i+n;
      if (i==tot){
        if (k!=0) throw( "Base64: unexpected #chars." );
        return bData;
      }
      var c = sData.charCodeAt(i++);
      x[k] = c==43?62:c==47?63:c==61?64:c>47&&c<58?c+4:c>64&&c<91?c-65:c>96&&c<123?c-71:-1;
      if (x[k]<0||(x[k]==64&&k<2)) throw( "Base64: "+unExpChar(c)
        +"\nAllowed characters:\n['A'-'Z'], ['a'-'z'], ['0'-'9'], '+', '-' and '='"  );
    }
    bData[j++] = (x[0]<<2)+(x[1]>>4);
    if (x[2]<64) bData[j++] = ((x[1]&15)<<4)+(x[2]>>2);
    if (x[3]<64) bData[j++] = ((x[2]&3)<<6)+x[3];
  }
  return bData;
}

///////////////////////////////////////// END ENCODING SECTION /////////////////////////////////
///////////////////////////////////////// BEGIN AES SECTION /////////////////////////////////
/* rijndael.js      Rijndael Reference Implementation
   Copyright (c) 2001 Fritz Schneider
 
 This software is provided as-is, without express or implied warranty.  
 Permission to use, copy, modify, distribute or sell this software, with or
 without fee, for any purpose and by any individual or organization, is hereby
 granted, provided that the above copyright notice and this paragraph appear 
 in all copies. Distribution as a part of an application or binary must
 include the above copyright notice in the documentation and/or other materials
 provided with the application or distribution.

   See http://www-cse.ucsd.edu/~fritz/rijndael.html for more documentation
   of the (very simple) API provided by this code.

                                               Fritz Schneider
                                               fritz at cs.ucsd.edu
 
*/
// these are just default values...the external function "setAESKeyAndBlockSize" allows
// the user to change them
var AESKeySize = 128;
var AESBlockSize = 128;
var roundsArray = [ ,,,,[,,,,10,, 12,, 14],, 
                        [,,,,12,, 12,, 14],, 
                        [,,,,14,, 14,, 14] ];
var shiftOffsets = [ ,,,,[,1, 2, 3],,[,1, 2, 3],,[,1, 3, 4] ];
var Rcon = [ 
0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 
0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 
0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 
0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 
0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91 ];
var SBox = [
 99, 124, 119, 123, 242, 107, 111, 197,  48,   1, 103,  43, 254, 215, 171, 
118, 202, 130, 201, 125, 250,  89,  71, 240, 173, 212, 162, 175, 156, 164, 
114, 192, 183, 253, 147,  38,  54,  63, 247, 204,  52, 165, 229, 241, 113, 
216,  49,  21,   4, 199,  35, 195,  24, 150,   5, 154,   7,  18, 128, 226, 
235,  39, 178, 117,   9, 131,  44,  26,  27, 110,  90, 160,  82,  59, 214, 
179,  41, 227,  47, 132,  83, 209,   0, 237,  32, 252, 177,  91, 106, 203, 
190,  57,  74,  76,  88, 207, 208, 239, 170, 251,  67,  77,  51, 133,  69, 
249,   2, 127,  80,  60, 159, 168,  81, 163,  64, 143, 146, 157,  56, 245, 
188, 182, 218,  33,  16, 255, 243, 210, 205,  12,  19, 236,  95, 151,  68,  
23,  196, 167, 126,  61, 100,  93,  25, 115,  96, 129,  79, 220,  34,  42, 
144, 136,  70, 238, 184,  20, 222,  94,  11, 219, 224,  50,  58,  10,  73,
  6,  36,  92, 194, 211, 172,  98, 145, 149, 228, 121, 231, 200,  55, 109, 
141, 213,  78, 169, 108,  86, 244, 234, 101, 122, 174,   8, 186, 120,  37,  
 46,  28, 166, 180, 198, 232, 221, 116,  31,  75, 189, 139, 138, 112,  62, 
181, 102,  72,   3, 246,  14,  97,  53,  87, 185, 134, 193,  29, 158, 225,
248, 152,  17, 105, 217, 142, 148, 155,  30, 135, 233, 206,  85,  40, 223,
140, 161, 137,  13, 191, 230,  66, 104,  65, 153,  45,  15, 176,  84, 187,  
 22 ];
var SBoxInverse = [
 82,   9, 106, 213,  48,  54, 165,  56, 191,  64, 163, 158, 129, 243, 215, 
251, 124, 227,  57, 130, 155,  47, 255, 135,  52, 142,  67,  68, 196, 222, 
233, 203,  84, 123, 148,  50, 166, 194,  35,  61, 238,  76, 149,  11,  66, 
250, 195,  78,   8,  46, 161, 102,  40, 217,  36, 178, 118,  91, 162,  73, 
109, 139, 209,  37, 114, 248, 246, 100, 134, 104, 152,  22, 212, 164,  92, 
204,  93, 101, 182, 146, 108, 112,  72,  80, 253, 237, 185, 218,  94,  21,  
 70,  87, 167, 141, 157, 132, 144, 216, 171,   0, 140, 188, 211,  10, 247, 
228,  88,   5, 184, 179,  69,   6, 208,  44,  30, 143, 202,  63,  15,   2, 
193, 175, 189,   3,   1,  19, 138, 107,  58, 145,  17,  65,  79, 103, 220, 
234, 151, 242, 207, 206, 240, 180, 230, 115, 150, 172, 116,  34, 231, 173,
 53, 133, 226, 249,  55, 232,  28, 117, 223, 110,  71, 241,  26, 113,  29, 
 41, 197, 137, 111, 183,  98,  14, 170,  24, 190,  27, 252,  86,  62,  75, 
198, 210, 121,  32, 154, 219, 192, 254, 120, 205,  90, 244,  31, 221, 168,
 51, 136,   7, 199,  49, 177,  18,  16,  89,  39, 128, 236,  95,  96,  81,
127, 169,  25, 181,  74,  13,  45, 229, 122, 159, 147, 201, 156, 239, 160,
224,  59,  77, 174,  42, 245, 176, 200, 235, 187,  60, 131,  83, 153,  97, 
 23,  43,   4, 126, 186, 119, 214,  38, 225, 105,  20,  99,  85,  33,  12,
125 ];
function cyclicShiftLeft(theArray, positions) {
  var temp = theArray.slice(0, positions);
  theArray = theArray.slice(positions).concat(temp);
  return theArray;
}
var Nk = AESKeySize / 32;                   
var Nb = AESBlockSize / 32;
var Nr = roundsArray[Nk][Nb];
function xtime(poly) {
  poly <<= 1;
  return ((poly & 0x100) ? (poly ^ 0x11B) : (poly));
}
function mult_GF256(x, y) {
  var bit, result = 0;
  
  for (bit = 1; bit < 256; bit *= 2, y = xtime(y)) {
    if (x & bit) 
      result ^= y;
  }
  return result;
}
function byteSub(state, direction) {
  var S;
  if (direction == "encrypt")
    S = SBox;
  else
    S = SBoxInverse;
  for (var i = 0; i < 4; i++)
    for (var j = 0; j < Nb; j++)
       state[i][j] = S[state[i][j]];
}
function shiftRow(state, direction) {
  for (var i=1; i<4; i++)
    if (direction == "encrypt")
       state[i] = cyclicShiftLeft(state[i], shiftOffsets[Nb][i]);
    else
       state[i] = cyclicShiftLeft(state[i], Nb - shiftOffsets[Nb][i]);

}
function mixColumn(state, direction) {
  var b = [];
  for (var j = 0; j < Nb; j++) {
    for (var i = 0; i < 4; i++) {
      if (direction == "encrypt")
        b[i] = mult_GF256(state[i][j], 2) ^
               mult_GF256(state[(i+1)%4][j], 3) ^ 
               state[(i+2)%4][j] ^ 
               state[(i+3)%4][j];
      else 
        b[i] = mult_GF256(state[i][j], 0xE) ^ 
               mult_GF256(state[(i+1)%4][j], 0xB) ^
               mult_GF256(state[(i+2)%4][j], 0xD) ^
               mult_GF256(state[(i+3)%4][j], 9);
    }
    for (var i = 0; i < 4; i++)
      state[i][j] = b[i];
  }
}
function addRoundKey(state, roundKey) {
  for (var j = 0; j < Nb; j++) {
    state[0][j] ^= (roundKey[j] & 0xFF);
    state[1][j] ^= ((roundKey[j]>>8) & 0xFF);
    state[2][j] ^= ((roundKey[j]>>16) & 0xFF);
    state[3][j] ^= ((roundKey[j]>>24) & 0xFF);
  }
}
function keyExpansion(key) {
  var expandedKey = new Array();
  var temp;
  Nk = AESKeySize / 32;                   
  Nb = AESBlockSize / 32;
  Nr = roundsArray[Nk][Nb];
  for (var j=0; j < Nk; j++)
    expandedKey[j] = 
      (key[4*j]) | (key[4*j+1]<<8) | (key[4*j+2]<<16) | (key[4*j+3]<<24);
  for (j = Nk; j < Nb * (Nr + 1); j++) {
    temp = expandedKey[j - 1];
    if (j % Nk == 0) 
      temp = ( (SBox[(temp>>8) & 0xFF]) |
               (SBox[(temp>>16) & 0xFF]<<8) |
               (SBox[(temp>>24) & 0xFF]<<16) |
               (SBox[temp & 0xFF]<<24) ) ^ Rcon[Math.floor(j / Nk) - 1];
    else if (Nk > 6 && j % Nk == 4)
      temp = (SBox[(temp>>24) & 0xFF]<<24) |
             (SBox[(temp>>16) & 0xFF]<<16) |
             (SBox[(temp>>8) & 0xFF]<<8) |
             (SBox[temp & 0xFF]);
    expandedKey[j] = expandedKey[j-Nk] ^ temp;
  }
  return expandedKey;
}
function Round(state, roundKey) {
  byteSub(state, "encrypt");
  shiftRow(state, "encrypt");
  mixColumn(state, "encrypt");
  addRoundKey(state, roundKey);
}
function InverseRound(state, roundKey) {
  addRoundKey(state, roundKey);
  mixColumn(state, "decrypt");
  shiftRow(state, "decrypt");
  byteSub(state, "decrypt");
}
function FinalRound(state, roundKey) {
  byteSub(state, "encrypt");
  shiftRow(state, "encrypt");
  addRoundKey(state, roundKey);
}
function InverseFinalRound(state, roundKey){
  addRoundKey(state, roundKey);
  shiftRow(state, "decrypt");
  byteSub(state, "decrypt");  
}
function encrypt(block, expandedKey) {
  var i;  
  if (!block || block.length*8 != AESBlockSize)
     return; 
  if (!expandedKey)
     return;

  block = packBytes(block);
  addRoundKey(block, expandedKey);
  for (i=1; i<Nr; i++) 
    Round(block, expandedKey.slice(Nb*i, Nb*(i+1)));
  FinalRound(block, expandedKey.slice(Nb*Nr)); 
  return unpackBytes(block);
}
function decrypt(block, expandedKey) {
  var i;
  if (!block || block.length*8 != AESBlockSize)
     return;
  if (!expandedKey)
     return;

  block = packBytes(block);
  InverseFinalRound(block, expandedKey.slice(Nb*Nr)); 
  for (i = Nr - 1; i>0; i--) 
    InverseRound(block, expandedKey.slice(Nb*i, Nb*(i+1)));
  addRoundKey(block, expandedKey);
  return unpackBytes(block);
}
function byteArrayToString(byteArray) {
  var result = "";
  for(var i=0; i<byteArray.length; i++)
    if (byteArray[i] != 0)
      result += String.fromCharCode(byteArray[i]);
  return result;
}
function byteArrayToHex(byteArray) {
  var result = "";
  if (!byteArray)
    return;
  for (var i=0; i<byteArray.length; i++)
    result += ((byteArray[i]<16) ? "0" : "") + byteArray[i].toString(16);

  return result;
}
function hexToByteArray(hexString) {
  var byteArray = [];
  if (hexString.length % 2)
    return;
  if (hexString.indexOf("0x") == 0 || hexString.indexOf("0X") == 0)
    hexString = hexString.substring(2);
  for (var i = 0; i<hexString.length; i += 2) 
    byteArray[Math.floor(i/2)] = parseInt(hexString.slice(i, i+2), 16);
  return byteArray;
}
function packBytes(octets) {
  var state = new Array();
  if (!octets || octets.length % 4)
    return;
  state[0] = new Array();  state[1] = new Array(); 
  state[2] = new Array();  state[3] = new Array();
  for (var j=0; j<octets.length; j+= 4) {
     state[0][j/4] = octets[j];
     state[1][j/4] = octets[j+1];
     state[2][j/4] = octets[j+2];
     state[3][j/4] = octets[j+3];
  }
  return state;  
}
function unpackBytes(packed) {
  var result = new Array();
  for (var j=0; j<packed[0].length; j++) {
    result[result.length] = packed[0][j];
    result[result.length] = packed[1][j];
    result[result.length] = packed[2][j];
    result[result.length] = packed[3][j];
  }
  return result;
}
function formatPlaintext(plaintext) {
  var bpb = AESBlockSize / 8;
  var i;
  if (typeof plaintext == "string" || plaintext.indexOf) {
    plaintext = plaintext.split("");
    for (i=0; i<plaintext.length; i++)
      plaintext[i] = plaintext[i].charCodeAt(0) & 0xFF;
  } 
  for (i = bpb - (plaintext.length % bpb); i > 0 && i < bpb; i--) 
    plaintext[plaintext.length] = 0;
  return plaintext;
}
function getRandomBytes(howMany) {
  var i;
  var bytes = new Array();
  for (i=0; i<howMany; i++)
    bytes[i] = Math.round(Math.random()*255);
  return bytes;
}
function rijndaelEncrypt(plaintext, hexkey, hexiv, mode) {
  var key = hexToByteArray(hexkey);
  var iv = hexToByteArray(hexiv);
  var expandedKey, i, aBlock;
  var bpb = AESBlockSize / 8;
  var ct;
  if (!plaintext || !key)
    return;
  if (key.length*8 != AESKeySize)
    return; 
  if (mode == "CBC")
    ct = iv;
  else {
    mode = "ECB";
    ct = new Array();
  } 
  plaintext = formatPlaintext(plaintext);
  expandedKey = keyExpansion(key);
  for (var block=0; block<plaintext.length / bpb; block++) {
    aBlock = plaintext.slice(block*bpb, (block+1)*bpb);
    if (mode == "CBC")
      for (var i=0; i<bpb; i++) 
        aBlock[i] ^= ct[block*bpb + i];
    ct = ct.concat(encrypt(aBlock, expandedKey));
  }
  return ct.slice(iv.length);
}
function rijndaelDecrypt(ciphertext, key, mode) {
  var expandedKey;
  var bpb = AESBlockSize / 8;
  var pt = new Array();
  var aBlock;
  var block;
  if (!ciphertext || !key || typeof ciphertext == "string")
    return;
  if (key.length*8 != AESKeySize)
    return; 
  if (!mode)
    mode = "ECB";
  expandedKey = keyExpansion(key);
  for (block=(ciphertext.length / bpb)-1; block>0; block--) {
    aBlock = 
     decrypt(ciphertext.slice(block*bpb,(block+1)*bpb), expandedKey);
    if (mode == "CBC") 
      for (var i=0; i<bpb; i++) 
        pt[(block-1)*bpb + i] = aBlock[i] ^ ciphertext[(block-1)*bpb + i];
    else 
      pt = aBlock.concat(pt);
  }
  if (mode == "ECB")
    pt = decrypt(ciphertext.slice(0, bpb), expandedKey).concat(pt);
  return pt;
}
///////////////////////////////////////// END AES SECTION /////////////////////////////////
///////////////////////////////////////// BIG INT LIBRARY (RSA) ///////////////////////////
// Big Integer Library v. 4.0
// (c)2000-2004 Leemon Baird
// www.leemon.com
//
// I retain the copyright to this code, but you may redistribute 
// or use it for any purpose.  If you use this code, please leave 
// an acknowledgement and pointer to my home page in the comments.
// I make no claims about whether it works correctly, so use it 
// at your own risk.
bpe=0;
mask=0;
radix=mask+1;
digitsStr='0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+/';
for (bpe=0; (1<<(bpe+1)) > (1<<bpe); bpe++);
bpe>>=1;
mask=(1<<bpe)-1;
radix=mask+1;
one=int2bigInt(1,1,1);
t=new Array(0);
ss=t; s0=t; s1=t; s2=t; s3=t; s4=t; s5=t; s6=t; s7=t; sa=t;
md_q1=t; md_q2=t; md_q3=t; md_r=t; md_r1=t; md_r2=t; md_tt=t;
function bitSize(x) {
  var j,z,w;
  for (j=x.length-1; (x[j]==0) && (j>0); j--);
  for (z=0,w=x[j]; w; (w>>=1),z++);
  z+=bpe*j;
  return z;
}
function expand(x,n) {
  var ans=int2bigInt(0,(x.length>n ? x.length : n)*bpe,0);
  copy_(ans,x);
  return ans;
}
function mod(x,n) {
  var ans=dup(x);
  mod_(ans,n);
  return trim(ans,1);
}
function addInt(x,n) {
  var ans=expand(x,x.length+1);
  addInt_(ans,n);
  return trim(ans,1);
}
function mult(x,y) {
  var ans=expand(x,x.length+y.length);
  mult_(ans,y);
  return trim(ans,1);
}
function powMod(x,y,n) {
  var ans=expand(x,n.length);  
  powMod_(ans,trim(y,2),trim(n,2),0);  //this should work without the trim, but doesn't
  return trim(ans,1);
}
function sub(x,y) {
  var ans=expand(x,(x.length>y.length ? x.length+1 : y.length+1)); 
  sub_(ans,y);
  return trim(ans,1);
}
function add(x,y) {
  var ans=expand(x,(x.length>y.length ? x.length+1 : y.length+1)); 
  add_(ans,y);
  return trim(ans,1);
}
function multMod(x,y,n) {
  var ans=expand(x,n.length);
  multMod_(ans,y,n);
  return trim(ans,1);
}
function inverseModInt_(x,n) {
  var a=1,b=0,t;
  for (;;) {
    if (x==1) return a;
    if (x==0) return 0;
    b-=a*Math.floor(n/x);
    n%=x;

    if (n==1) return b;
    if (n==0) return 0;
    a-=b*Math.floor(x/n);
    x%=n;
  }
}
function negative(x) {
  return ((x[x.length-1]>>(bpe-1))&1);
}
function greaterShift(x,y,shift) {
  var kx=x.length, ky=y.length;
  k=((kx+shift)<ky) ? (kx+shift) : ky;
  for (i=ky-1-shift; i<kx && i>=0; i++) 
    if (x[i]>0)
      return 1;
  for (i=kx-1+shift; i<ky; i++)
    if (y[i]>0)
      return 0;
  for (i=k-1; i>=shift; i--)
    if      (x[i-shift]>y[i]) return 1;
    else if (x[i-shift]<y[i]) return 0;
  return 0;
}
function greater(x,y) {
  var i;
  var k=(x.length<y.length) ? x.length : y.length;
  for (i=x.length;i<y.length;i++)
    if (y[i])
      return 0;
  for (i=y.length;i<x.length;i++)
    if (x[i])
      return 1;
  for (i=k-1;i>=0;i--)
    if (x[i]>y[i])
      return 1;
    else if (x[i]<y[i])
      return 0;
  return 0;
}
function divide_(x,y,q,r) {
  var kx, ky;
  var i,j,y1,y2,c,a,b;
  copy_(r,x);
  for (ky=y.length;y[ky-1]==0;ky--);
  for (kx=r.length;r[kx-1]==0 && kx>ky;kx--);
  b=y[ky-1];
  for (a=0; b; a++)
    b>>=1;  
  a=bpe-a;
  leftShift_(y,a);
  leftShift_(r,a);
  copyInt_(q,0);
  while (!greaterShift(y,r,kx-ky)) {
    subShift_(r,y,kx-ky);
    q[kx-ky]++;
  }
  for (i=kx-1; i>=ky; i--) {
    if (r[i]==y[ky-1])
      q[i-ky]=mask;
    else
      q[i-ky]=Math.floor((r[i]*radix+r[i-1])/y[ky-1]);	
    for (;;) {
      y2=(ky>1 ? y[ky-2] : 0)*q[i-ky];
      c=y2>>bpe;
      y2=y2 & mask;
      y1=c+q[i-ky]*y[ky-1];
      c=y1>>bpe;
      y1=y1 & mask;
      if (c==r[i] ? y1==r[i-1] ? y2>(i>1 ? r[i-2] : 0) : y1>r[i-1] : c>r[i]) 
        q[i-ky]--;
      else
        break;
    }
    linCombShift_(r,y,-q[i-ky],i-ky);
    if (negative(r)) {
      addShift_(r,y,i-ky);
      q[i-ky]--;
    }
  }

  rightShift_(y,a);
  rightShift_(r,a);
}
function carry_(x) {
  var i,k,c,b;
  k=x.length;
  c=0;
  for (i=0;i<k;i++) {
    c+=x[i];
    b=0;
    if (c<0) {
      b=-(c>>bpe);
      c+=b*radix;
    }
    x[i]=c & mask;
    c=(c>>bpe)-b;
  }
}
function modInt(x,n) {
  var i,c=0;
  for (i=x.length-1; i>=0; i--)
    c=(c*radix+x[i])%n;
  return c;
}
function int2bigInt(t,bits,minSize) {   
  var i,k;
  k=Math.ceil(bits/bpe)+1;
  k=minSize>k ? minSize : k;
  buff=new Array(k);
  copyInt_(buff,t);
  return buff;
}
function str2bigInt(s,base,minSize) {
  var d, i, j, x, y, kk;
  var k=s.length;
  if (base==-1) {
    x=new Array(0);
    for (;;) {
      y=new Array(x.length+1);
      for (i=0;i<x.length;i++)
        y[i+1]=x[i];
      y[0]=parseInt(s,10);
      x=y;
      d=s.indexOf(',',0);
      if (d<1) 
        break;
      s=s.substring(d+1);
      if (s.length==0)
        break;
    }
    if (x.length<minSize) {
      y=new Array(minSize);
      copy_(y,x);
      return y;
    }
    return x;
  }
  x=int2bigInt(0,base*k,0);
  for (i=0;i<k;i++) {
    d=digitsStr.indexOf(s.substring(i,i+1),0);
    if (base<=36 && d>=36)
      d-=26;
    if (d<base && d>=0) {
      multInt_(x,base);
      addInt_(x,d);
    }
  }
  for (k=x.length;k>0 && !x[k-1];k--);
  k=minSize>k+1 ? minSize : k+1;
  y=new Array(k);
  kk=k<x.length ? k : x.length;
  for (i=0;i<kk;i++)
    y[i]=x[i];
  for (;i<k;i++)
    y[i]=0;
  return y;
}
function equalsInt(x,y) {
  var i;
  if (x[0]!=y)
    return 0;
  for (i=1;i<x.length;i++)
    if (x[i])
      return 0;
  return 1;
}
function equals(x,y) {
  var i;
  var k=x.length<y.length ? x.length : y.length;
  for (i=0;i<k;i++)
    if (x[i]!=y[i])
      return 0;
  if (x.length>y.length) {
    for (;i<x.length;i++)
      if (x[i])
        return 0;
  } else {
    for (;i<y.length;i++)
      if (y[i])
        return 0;
  }
  return 1;
}
function isZero(x) {
  var i;
  for (i=0;i<x.length;i++)
    if (x[i])
      return 0;
  return 1;
}
function bigInt2str(x,base) {
  var i,t,s="";
  if (s6.length!=x.length) 
    s6=dup(x);
  else
    copy_(s6,x);
  if (base==-1) {
    for (i=x.length-1;i>0;i--)
      s+=x[i]+',';
    s+=x[0];
  }
  else {
    while (!isZero(s6)) {
      t=divInt_(s6,base);
      s=digitsStr.substring(t,t+1)+s;
    }
  }
  if (s.length==0)
    s="0";
  return s;
}
function dup(x) {
  var i;
  buff=new Array(x.length);
  copy_(buff,x);
  return buff;
}
function copy_(x,y) {
  var i;
  var k=x.length<y.length ? x.length : y.length;
  for (i=0;i<k;i++)
    x[i]=y[i];
  for (i=k;i<x.length;i++)
    x[i]=0;
}
function copyInt_(x,n) {
  var i,c;
  for (c=n,i=0;i<x.length;i++) {
    x[i]=c & mask;
    c>>=bpe;
  }
}
function addInt_(x,n) {
  var i,k,c,b;
  x[0]+=n;
  k=x.length;
  c=0;
  for (i=0;i<k;i++) {
    c+=x[i];
    b=0;
    if (c<0) {
      b=-(c>>bpe);
      c+=b*radix;
    }
    x[i]=c & mask;
    c=(c>>bpe)-b;
    if (!c) return;
  }
}
function rightShift_(x,n) {
  var i;
  var k=Math.floor(n/bpe);
  if (k) {
    for (i=0;i<x.length-k;i++)
      x[i]=x[i+k];
    for (;i<x.length;i++)
      x[i]=0;
    n%=bpe;
  }
  for (i=0;i<x.length-1;i++) {
    x[i]=mask & ((x[i+1]<<(bpe-n)) | (x[i]>>n));
  }
  x[i]>>=n;
}
function halve_(x) {
  var i;
  for (i=0;i<x.length-1;i++) {
    x[i]=mask & ((x[i+1]<<(bpe-1)) | (x[i]>>1));
  }
  x[i]=(x[i]>>1) | (x[i] & (radix>>1));
}
function leftShift_(x,n) {
  var i;
  var k=Math.floor(n/bpe);
  if (k) {
    for (i=x.length; i>=k; i--)
      x[i]=x[i-k];
    for (;i>=0;i--)
      x[i]=0;  
    n%=bpe;
  }
  if (!n)
    return;
  for (i=x.length-1;i>0;i--) {
    x[i]=mask & ((x[i]<<n) | (x[i-1]>>(bpe-n)));
  }
  x[i]=mask & (x[i]<<n);
}
function multInt_(x,n) {
  var i,k,c,b;
  if (!n)
    return;
  k=x.length;
  c=0;
  for (i=0;i<k;i++) {
    c+=x[i]*n;
    b=0;
    if (c<0) {
      b=-(c>>bpe);
      c+=b*radix;
    }
    x[i]=c & mask;
    c=(c>>bpe)-b;
  }
}
function divInt_(x,n) {
  var i,r=0,s;
  for (i=x.length-1;i>=0;i--) {
    s=r*radix+x[i];
    x[i]=Math.floor(s/n);
    r=s%n;
  }
  return r;
}
function linComb_(x,y,a,b) {
  var i,c,k,kk;
  k=x.length<y.length ? x.length : y.length;
  kk=x.length;
  for (c=0,i=0;i<k;i++) {
    c+=a*x[i]+b*y[i];
    x[i]=c & mask;
    c>>=bpe;
  }
  for (i=k;i<kk;i++) {
    c+=a*x[i];
    x[i]=c & mask;
    c>>=bpe;
  }
}
function linCombShift_(x,y,b,ys) {
  var i,c,k,kk;
  k=x.length<ys+y.length ? x.length : ys+y.length;
  kk=x.length;
  for (c=0,i=ys;i<k;i++) {
    c+=x[i]+b*y[i-ys];
    x[i]=c & mask;
    c>>=bpe;
  }
  for (i=k;c && i<kk;i++) {
    c+=x[i];
    x[i]=c & mask;
    c>>=bpe;
  }
}
function addShift_(x,y,ys) {
  var i,c,k,kk;
  k=x.length<ys+y.length ? x.length : ys+y.length;
  kk=x.length;
  for (c=0,i=ys;i<k;i++) {
    c+=x[i]+y[i-ys];
    x[i]=c & mask;
    c>>=bpe;
  }
  for (i=k;c && i<kk;i++) {
    c+=x[i];
    x[i]=c & mask;
    c>>=bpe;
  }
}
function subShift_(x,y,ys) {
  var i,c,k,kk;
  k=x.length<ys+y.length ? x.length : ys+y.length;
  kk=x.length;
  for (c=0,i=ys;i<k;i++) {
    c+=x[i]-y[i-ys];
    x[i]=c & mask;
    c>>=bpe;
  }
  for (i=k;c && i<kk;i++) {
    c+=x[i];
    x[i]=c & mask;
    c>>=bpe;
  }
}
function sub_(x,y) {
  var i,c,k,kk;
  k=x.length<y.length ? x.length : y.length;
  for (c=0,i=0;i<k;i++) {
    c+=x[i]-y[i];
    x[i]=c & mask;
    c>>=bpe;
  }
  for (i=k;c && i<x.length;i++) {
    c+=x[i];
    x[i]=c & mask;
    c>>=bpe;
  }
}
function add_(x,y) {
  var i,c,k,kk;
  k=x.length<y.length ? x.length : y.length;
  for (c=0,i=0;i<k;i++) {
    c+=x[i]+y[i];
    x[i]=c & mask;
    c>>=bpe;
  }
  for (i=k;c && i<x.length;i++) {
    c+=x[i];
    x[i]=c & mask;
    c>>=bpe;
  }
}
function mult_(x,y) {
  var i;
  if (ss.length!=2*x.length)
    ss=new Array(2*x.length);
  copyInt_(ss,0);
  for (i=0;i<y.length;i++)
    if (y[i])
      linCombShift_(ss,x,y[i],i);
  copy_(x,ss);
}
function mod_(x,n) {
  if (s4.length!=x.length)
    s4=dup(x);
  else
    copy_(s4,x);
  if (s5.length!=x.length)
    s5=dup(x);  
  divide_(s4,n,s5,x);
}
function multMod_(x,y,n) {
  var i;
  if (s0.length!=2*x.length)
    s0=new Array(2*x.length);
  copyInt_(s0,0);
  for (i=0;i<y.length;i++)
    if (y[i])
      linCombShift_(s0,x,y[i],i);
  mod_(s0,n);
  copy_(x,s0);
}
function squareMod_(x,n) {
  var i,j,d,c,kx,kn,k;
  for (kx=x.length; kx>0 && !x[kx-1]; kx--);
  k=kx>n.length ? 2*kx : 2*n.length;
  if (s0.length!=k) 
    s0=new Array(k);
  copyInt_(s0,0);
  for (i=0;i<kx;i++) {
    c=s0[2*i]+x[i]*x[i];
    s0[2*i]=c & mask;
    c>>=bpe;
    for (j=i+1;j<kx;j++) {
      c=s0[i+j]+2*x[i]*x[j]+c;
      s0[i+j]=(c & mask);
      c>>=bpe;
    }
    s0[i+kx]=c;
  }
  mod_(s0,n);
  copy_(x,s0);
}
function trim(x,k) {
  var i,y;
  for (i=x.length; i>0 && !x[i-1]; i--);
  y=new Array(i+k);
  copy_(y,x);
  return y;
}
function powMod_(x,y,n) {
  var k1,k2,kn,np;
  if(s7.length!=n.length)
    s7=dup(n);
  if ((n[0]&1)==0) {
    copy_(s7,x);
    copyInt_(x,1);
    while(!equalsInt(y,0)) {
      if (y[0]&1)
        multMod_(x,s7,n);
      divInt_(y,2);
      squareMod_(s7,n); 
    }
    return;
  }
  copyInt_(s7,0);
  for (kn=n.length;kn>0 && !n[kn-1];kn--);
  np=radix-inverseModInt_(modInt(n,radix),radix);
  s7[kn]=1;
  multMod_(x ,s7,n);

  if (s3.length!=x.length)
    s3=dup(x);
  else
    copy_(s3,x);

  for (k1=y.length-1;k1>0 & !y[k1]; k1--);
  if (y[k1]==0) {
    copyInt_(x,1);
    return;
  }
  for (k2=1<<(bpe-1);k2 && !(y[k1] & k2); k2>>=1);
  for (;;) {
    if (!(k2>>=1)) {
      k1--;
      if (k1<0) {
        mont_(x,one,n,np);
        return;
      }
      k2=1<<(bpe-1);
    }    
    mont_(x,x,n,np);

    if (k2 & y[k1])
      mont_(x,s3,n,np);
  }
}    
function mont_(x,y,n,np) {
  var i,j,c,ui,t;
  var kn=n.length;
  var ky=y.length;
  if (sa.length!=kn)
    sa=new Array(kn);
  for (;kn>0 && n[kn-1]==0;kn--);
  copyInt_(sa,0);
  for (i=0; i<kn; i++) {
    t=sa[0]+x[i]*y[0];
    ui=((t & mask) * np) & mask;
    c=(t+ui*n[0]) >> bpe;
    t=x[i];
    for (j=1;j<ky;j++) { 
      c+=sa[j]+t*y[j]+ui*n[j];
      sa[j-1]=c & mask;
      c>>=bpe;
    }    
    for (;j<kn;j++) { 
      c+=sa[j]+ui*n[j];
      sa[j-1]=c & mask;
      c>>=bpe;
    }    
    sa[j-1]=c & mask;
  }

  if (!greater(n,sa))
    sub_(sa,n);
  copy_(x,sa);
}
///////////////////////////////////////// END BIG INT LIBRARY (RSA) ///////////////////////////
/////////////////////////////////////////// BEGIN DES SECTION /////////////////////////////////
//(c) Paul Tero, July 2001
//http://www.shopable.co.uk/des.html
//
//Optimised for performance with large blocks by Michael Hayworth, November 2001
//http://www.netdealing.com
//
//THIS SOFTWARE IS PROVIDED "AS IS" AND
//ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
//IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
//ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
//FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
//DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
//OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
//HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
//LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
//OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
//SUCH DAMAGE.

//this takes the key, the message, and whether to encrypt or decrypt (encrypt = 1, decrypt = 0)
//the mode can be "ECB" or "CBC"
function des (hexkey, message, encrypt, mode, hexiv) {
  key = hexToByteArray(hexkey);
  iv = hexToByteArray(hexiv);
  var spfunction1 = new Array (0x1010400,0,0x10000,0x1010404,0x1010004,0x10404,0x4,0x10000,0x400,0x1010400,0x1010404,0x400,0x1000404,0x1010004,0x1000000,0x4,0x404,0x1000400,0x1000400,0x10400,0x10400,0x1010000,0x1010000,0x1000404,0x10004,0x1000004,0x1000004,0x10004,0,0x404,0x10404,0x1000000,0x10000,0x1010404,0x4,0x1010000,0x1010400,0x1000000,0x1000000,0x400,0x1010004,0x10000,0x10400,0x1000004,0x400,0x4,0x1000404,0x10404,0x1010404,0x10004,0x1010000,0x1000404,0x1000004,0x404,0x10404,0x1010400,0x404,0x1000400,0x1000400,0,0x10004,0x10400,0,0x1010004);
  var spfunction2 = new Array (0x80108020,0x80008000,0x8000,0x108020,0x100000,0x20,0x80100020,0x80008020,0x80000020,0x80108020,0x80108000,0x80000000,0x80008000,0x100000,0x20,0x80100020,0x108000,0x100020,0x80008020,0,0x80000000,0x8000,0x108020,0x80100000,0x100020,0x80000020,0,0x108000,0x8020,0x80108000,0x80100000,0x8020,0,0x108020,0x80100020,0x100000,0x80008020,0x80100000,0x80108000,0x8000,0x80100000,0x80008000,0x20,0x80108020,0x108020,0x20,0x8000,0x80000000,0x8020,0x80108000,0x100000,0x80000020,0x100020,0x80008020,0x80000020,0x100020,0x108000,0,0x80008000,0x8020,0x80000000,0x80100020,0x80108020,0x108000);
  var spfunction3 = new Array (0x208,0x8020200,0,0x8020008,0x8000200,0,0x20208,0x8000200,0x20008,0x8000008,0x8000008,0x20000,0x8020208,0x20008,0x8020000,0x208,0x8000000,0x8,0x8020200,0x200,0x20200,0x8020000,0x8020008,0x20208,0x8000208,0x20200,0x20000,0x8000208,0x8,0x8020208,0x200,0x8000000,0x8020200,0x8000000,0x20008,0x208,0x20000,0x8020200,0x8000200,0,0x200,0x20008,0x8020208,0x8000200,0x8000008,0x200,0,0x8020008,0x8000208,0x20000,0x8000000,0x8020208,0x8,0x20208,0x20200,0x8000008,0x8020000,0x8000208,0x208,0x8020000,0x20208,0x8,0x8020008,0x20200);
  var spfunction4 = new Array (0x802001,0x2081,0x2081,0x80,0x802080,0x800081,0x800001,0x2001,0,0x802000,0x802000,0x802081,0x81,0,0x800080,0x800001,0x1,0x2000,0x800000,0x802001,0x80,0x800000,0x2001,0x2080,0x800081,0x1,0x2080,0x800080,0x2000,0x802080,0x802081,0x81,0x800080,0x800001,0x802000,0x802081,0x81,0,0,0x802000,0x2080,0x800080,0x800081,0x1,0x802001,0x2081,0x2081,0x80,0x802081,0x81,0x1,0x2000,0x800001,0x2001,0x802080,0x800081,0x2001,0x2080,0x800000,0x802001,0x80,0x800000,0x2000,0x802080);
  var spfunction5 = new Array (0x100,0x2080100,0x2080000,0x42000100,0x80000,0x100,0x40000000,0x2080000,0x40080100,0x80000,0x2000100,0x40080100,0x42000100,0x42080000,0x80100,0x40000000,0x2000000,0x40080000,0x40080000,0,0x40000100,0x42080100,0x42080100,0x2000100,0x42080000,0x40000100,0,0x42000000,0x2080100,0x2000000,0x42000000,0x80100,0x80000,0x42000100,0x100,0x2000000,0x40000000,0x2080000,0x42000100,0x40080100,0x2000100,0x40000000,0x42080000,0x2080100,0x40080100,0x100,0x2000000,0x42080000,0x42080100,0x80100,0x42000000,0x42080100,0x2080000,0,0x40080000,0x42000000,0x80100,0x2000100,0x40000100,0x80000,0,0x40080000,0x2080100,0x40000100);
  var spfunction6 = new Array (0x20000010,0x20400000,0x4000,0x20404010,0x20400000,0x10,0x20404010,0x400000,0x20004000,0x404010,0x400000,0x20000010,0x400010,0x20004000,0x20000000,0x4010,0,0x400010,0x20004010,0x4000,0x404000,0x20004010,0x10,0x20400010,0x20400010,0,0x404010,0x20404000,0x4010,0x404000,0x20404000,0x20000000,0x20004000,0x10,0x20400010,0x404000,0x20404010,0x400000,0x4010,0x20000010,0x400000,0x20004000,0x20000000,0x4010,0x20000010,0x20404010,0x404000,0x20400000,0x404010,0x20404000,0,0x20400010,0x10,0x4000,0x20400000,0x404010,0x4000,0x400010,0x20004010,0,0x20404000,0x20000000,0x400010,0x20004010);
  var spfunction7 = new Array (0x200000,0x4200002,0x4000802,0,0x800,0x4000802,0x200802,0x4200800,0x4200802,0x200000,0,0x4000002,0x2,0x4000000,0x4200002,0x802,0x4000800,0x200802,0x200002,0x4000800,0x4000002,0x4200000,0x4200800,0x200002,0x4200000,0x800,0x802,0x4200802,0x200800,0x2,0x4000000,0x200800,0x4000000,0x200800,0x200000,0x4000802,0x4000802,0x4200002,0x4200002,0x2,0x200002,0x4000000,0x4000800,0x200000,0x4200800,0x802,0x200802,0x4200800,0x802,0x4000002,0x4200802,0x4200000,0x200800,0,0x2,0x4200802,0,0x200802,0x4200000,0x800,0x4000002,0x4000800,0x800,0x200002);
  var spfunction8 = new Array (0x10001040,0x1000,0x40000,0x10041040,0x10000000,0x10001040,0x40,0x10000000,0x40040,0x10040000,0x10041040,0x41000,0x10041000,0x41040,0x1000,0x40,0x10040000,0x10000040,0x10001000,0x1040,0x41000,0x40040,0x10040040,0x10041000,0x1040,0,0,0x10040040,0x10000040,0x10001000,0x41040,0x40000,0x41040,0x40000,0x10041000,0x1000,0x40,0x10040040,0x1000,0x41040,0x10001000,0x40,0x10000040,0x10040000,0x10040040,0x10000000,0x40000,0x10001040,0,0x10041040,0x40040,0x10000040,0x10040000,0x10001000,0x10001040,0,0x10041040,0x41000,0x41000,0x1040,0x1040,0x40040,0x10000000,0x10041000);

  var keys = des_createKeys (key);
  var m=0, i, j, temp, temp2, right1, right2, left, right, looping;
  var cbcleft, cbcleft2, cbcright, cbcright2
  var endloop, loopinc;
  var len = message.length;
  var chunk = 0;
  var iterations = keys.length == 32 ? 3 : 9;
  if (iterations == 3) {looping = encrypt ? new Array (0, 32, 2) : new Array (30, -2, -2);}
  else {looping = encrypt ? new Array (0, 32, 2, 62, 30, -2, 64, 96, 2) : new Array (94, 62, -2, 32, 64, 2, 30, -2, -2);}
  result = new Array(message.length);
  for(i=len; i<len+8; i++)
    message[i] = 0x00;
  if (mode == 1) {
    cbcleft = (iv[m++] << 24) | (iv[m++] << 16) | (iv[m++] << 8) | iv[m++];
    cbcright = (iv[m++] << 24) | (iv[m++] << 16) | (iv[m++] << 8) | iv[m++];
    m=0;
  }
  while (m < len) {
    left = (message[m++] << 24) | (message[m++] << 16) | (message[m++] << 8) | message[m++];
    right = (message[m++] << 24) | (message[m++] << 16) | (message[m++] << 8) | message[m++];
    if (mode == 1) {if (encrypt) {left ^= cbcleft; right ^= cbcright;} else {cbcleft2 = cbcleft; cbcright2 = cbcright; cbcleft = left; cbcright = right;}}
    temp = ((left >>> 4) ^ right) & 0x0f0f0f0f; right ^= temp; left ^= (temp << 4);
    temp = ((left >>> 16) ^ right) & 0x0000ffff; right ^= temp; left ^= (temp << 16);
    temp = ((right >>> 2) ^ left) & 0x33333333; left ^= temp; right ^= (temp << 2);
    temp = ((right >>> 8) ^ left) & 0x00ff00ff; left ^= temp; right ^= (temp << 8);
    temp = ((left >>> 1) ^ right) & 0x55555555; right ^= temp; left ^= (temp << 1);
    left = ((left << 1) | (left >>> 31)); 
    right = ((right << 1) | (right >>> 31)); 
    for (j=0; j<iterations; j+=3) {
      endloop = looping[j+1];
      loopinc = looping[j+2];
      for (i=looping[j]; i!=endloop; i+=loopinc) {
        right1 = right ^ keys[i]; 
        right2 = ((right >>> 4) | (right << 28)) ^ keys[i+1];
        temp = left;
        left = right;
        right = temp ^ (spfunction2[(right1 >>> 24) & 0x3f] | spfunction4[(right1 >>> 16) & 0x3f]
              | spfunction6[(right1 >>>  8) & 0x3f] | spfunction8[right1 & 0x3f]
              | spfunction1[(right2 >>> 24) & 0x3f] | spfunction3[(right2 >>> 16) & 0x3f]
              | spfunction5[(right2 >>>  8) & 0x3f] | spfunction7[right2 & 0x3f]);
      }
      temp = left; left = right; right = temp;
    }
    left = ((left >>> 1) | (left << 31)); 
    right = ((right >>> 1) | (right << 31)); 
    temp = ((left >>> 1) ^ right) & 0x55555555; right ^= temp; left ^= (temp << 1);
    temp = ((right >>> 8) ^ left) & 0x00ff00ff; left ^= temp; right ^= (temp << 8);
    temp = ((right >>> 2) ^ left) & 0x33333333; left ^= temp; right ^= (temp << 2);
    temp = ((left >>> 16) ^ right) & 0x0000ffff; right ^= temp; left ^= (temp << 16);
    temp = ((left >>> 4) ^ right) & 0x0f0f0f0f; right ^= temp; left ^= (temp << 4);
    if (mode == 1) {if (encrypt) {cbcleft = left; cbcright = right;} else {left ^= cbcleft2; right ^= cbcright2;}}
    result[chunk++] = (left>>>24);
    result[chunk++] = ((left>>>16) & 0xff);
    result[chunk++] = ((left>>>8) & 0xff);
    result[chunk++] = (left & 0xff);
    result[chunk++] = (right>>>24);
    result[chunk++] = ((right>>>16) & 0xff);
    result[chunk++] = ((right>>>8) & 0xff);
    result[chunk++] = (right & 0xff); 
  }
  return result;
}

function des_createKeys (key) {
  pc2bytes0  = new Array (0,0x4,0x20000000,0x20000004,0x10000,0x10004,0x20010000,0x20010004,0x200,0x204,0x20000200,0x20000204,0x10200,0x10204,0x20010200,0x20010204);
  pc2bytes1  = new Array (0,0x1,0x100000,0x100001,0x4000000,0x4000001,0x4100000,0x4100001,0x100,0x101,0x100100,0x100101,0x4000100,0x4000101,0x4100100,0x4100101);
  pc2bytes2  = new Array (0,0x8,0x800,0x808,0x1000000,0x1000008,0x1000800,0x1000808,0,0x8,0x800,0x808,0x1000000,0x1000008,0x1000800,0x1000808);
  pc2bytes3  = new Array (0,0x200000,0x8000000,0x8200000,0x2000,0x202000,0x8002000,0x8202000,0x20000,0x220000,0x8020000,0x8220000,0x22000,0x222000,0x8022000,0x8222000);
  pc2bytes4  = new Array (0,0x40000,0x10,0x40010,0,0x40000,0x10,0x40010,0x1000,0x41000,0x1010,0x41010,0x1000,0x41000,0x1010,0x41010);
  pc2bytes5  = new Array (0,0x400,0x20,0x420,0,0x400,0x20,0x420,0x2000000,0x2000400,0x2000020,0x2000420,0x2000000,0x2000400,0x2000020,0x2000420);
  pc2bytes6  = new Array (0,0x10000000,0x80000,0x10080000,0x2,0x10000002,0x80002,0x10080002,0,0x10000000,0x80000,0x10080000,0x2,0x10000002,0x80002,0x10080002);
  pc2bytes7  = new Array (0,0x10000,0x800,0x10800,0x20000000,0x20010000,0x20000800,0x20010800,0x20000,0x30000,0x20800,0x30800,0x20020000,0x20030000,0x20020800,0x20030800);
  pc2bytes8  = new Array (0,0x40000,0,0x40000,0x2,0x40002,0x2,0x40002,0x2000000,0x2040000,0x2000000,0x2040000,0x2000002,0x2040002,0x2000002,0x2040002);
  pc2bytes9  = new Array (0,0x10000000,0x8,0x10000008,0,0x10000000,0x8,0x10000008,0x400,0x10000400,0x408,0x10000408,0x400,0x10000400,0x408,0x10000408);
  pc2bytes10 = new Array (0,0x20,0,0x20,0x100000,0x100020,0x100000,0x100020,0x2000,0x2020,0x2000,0x2020,0x102000,0x102020,0x102000,0x102020);
  pc2bytes11 = new Array (0,0x1000000,0x200,0x1000200,0x200000,0x1200000,0x200200,0x1200200,0x4000000,0x5000000,0x4000200,0x5000200,0x4200000,0x5200000,0x4200200,0x5200200);
  pc2bytes12 = new Array (0,0x1000,0x8000000,0x8001000,0x80000,0x81000,0x8080000,0x8081000,0x10,0x1010,0x8000010,0x8001010,0x80010,0x81010,0x8080010,0x8081010);
  pc2bytes13 = new Array (0,0x4,0x100,0x104,0,0x4,0x100,0x104,0x1,0x5,0x101,0x105,0x1,0x5,0x101,0x105);
  var iterations = key.length >= 24 ? 3 : 1;
  var keys = new Array (32 * iterations);
  var shifts = new Array (0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0);
  var lefttemp, righttemp, m=0, n=0, temp;
  for (var j=0; j<iterations; j++) {
    left = (key[m++] << 24) | (key[m++] << 16) | (key[m++] << 8) | key[m++];
    right = (key[m++] << 24) | (key[m++] << 16) | (key[m++] << 8) | key[m++];
    temp = ((left >>> 4) ^ right) & 0x0f0f0f0f; right ^= temp; left ^= (temp << 4);
    temp = ((right >>> -16) ^ left) & 0x0000ffff; left ^= temp; right ^= (temp << -16);
    temp = ((left >>> 2) ^ right) & 0x33333333; right ^= temp; left ^= (temp << 2);
    temp = ((right >>> -16) ^ left) & 0x0000ffff; left ^= temp; right ^= (temp << -16);
    temp = ((left >>> 1) ^ right) & 0x55555555; right ^= temp; left ^= (temp << 1);
    temp = ((right >>> 8) ^ left) & 0x00ff00ff; left ^= temp; right ^= (temp << 8);
    temp = ((left >>> 1) ^ right) & 0x55555555; right ^= temp; left ^= (temp << 1);
    temp = (left << 8) | ((right >>> 20) & 0x000000f0);
    left = (right << 24) | ((right << 8) & 0xff0000) | ((right >>> 8) & 0xff00) | ((right >>> 24) & 0xf0);
    right = temp;
    for (i=0; i < shifts.length; i++) {
      if (shifts[i]) {left = (left << 2) | (left >>> 26); right = (right << 2) | (right >>> 26);}
      else {left = (left << 1) | (left >>> 27); right = (right << 1) | (right >>> 27);}
      left &= 0xfffffff0; right &= 0xfffffff0;
      lefttemp = pc2bytes0[left >>> 28] | pc2bytes1[(left >>> 24) & 0xf]
              | pc2bytes2[(left >>> 20) & 0xf] | pc2bytes3[(left >>> 16) & 0xf]
              | pc2bytes4[(left >>> 12) & 0xf] | pc2bytes5[(left >>> 8) & 0xf]
              | pc2bytes6[(left >>> 4) & 0xf];
      righttemp = pc2bytes7[right >>> 28] | pc2bytes8[(right >>> 24) & 0xf]
                | pc2bytes9[(right >>> 20) & 0xf] | pc2bytes10[(right >>> 16) & 0xf]
                | pc2bytes11[(right >>> 12) & 0xf] | pc2bytes12[(right >>> 8) & 0xf]
                | pc2bytes13[(right >>> 4) & 0xf];
      temp = ((righttemp >>> 16) ^ lefttemp) & 0x0000ffff; 
      keys[n++] = lefttemp ^ temp; keys[n++] = righttemp ^ (temp << 16);
    }
  }
  return keys;
}
/////////////////////////////////////////// END DES SECTION /////////////////////////////////
/////////////////////////////// BEGIN EXTERNAL FUNCTIONS SECTION ////////////////////////////
// this function accepts a string of HEX CHARACTERS and encrypts it using the server's public RSA key
//###################### BEGIN RSA ######################
var RSAKeySize = 512;
function RSAEncryptHex(s, rsaKey) {
  // if the key size is explicitly set to 1024, use the 1024 bit key, 
  // otherwise default to the 512 bit key for better performance

  var form = getObject('ayudaForm');
  if (form.CallbackUrl.value && rsaKey)
  {
    var keyValues = rsaKey.split('_');
    n=str2bigInt(keyValues[1],16,0);
    //if(RSAKeySize == 1024) 
      //n=str2bigInt("B13B8D7E461391A580B8BFB4825EE440D978BC988F798257DB7A2E5641B39D9A0EC217647E903CA65DE7C6807EE6AFA929F4CD675AD4DCD0808976183D8478FE07CFC28C71993593AB71E38679003CFEE6268B27186C8F018326DAB45E8883379B8B6F099B36BE9D21A781265ECFFB4F6348B3C71AC6F0B31FBD5C1BD2E71CF3",16,0);
    //else 
      //n=str2bigInt("C2000F6ECCD5EB8C93B7B49245536A94E872A6DF14FB03780B24EA8A2066CE20BA9EA3633A67D47F1F19E14152689417A5CF226957B3B4794850E5CD03F451FF",16,0);
    text=str2bigInt(s,16,0);
    //e=str2bigInt("10001",16,0);
    e=str2bigInt(keyValues[0],16,0);
    c=powMod(text,e,n);
  }
  return bigInt2str(c,16);
}
function setRSAKeySize(size) {
  if(size == 512 || size == 1024) {
    RSAKeySize = size;
  }
}
//###################### END RSA ######################
//###################### BEGIN AES ######################
// this function generates a random AES key and AES initialization vector and encrypts them
// with the server's public RSA key
function setAESKeyAndBlockSize(size) {
  if(size == 128 || size == 192 || size == 256) {
    AESKeySize = size;
    AESBlockSize = size;
  }
}
function GenerateRSAEncryptedAESKeys(rsaKey) {
  AESKey = generateAESKey();
  AESIV = generateAESIV();
  return RSAEncryptHex(AESKey + AESIV, rsaKey);
}
// this function accepts a string, an AES key as a string of hex characters,
// and an AES initialization vector as a string of hex characters and returns the AES encrypted string
function AESEncrypt(s, AESKey, AESIV) {
  return b64EncryptBytes(rijndaelEncrypt(utf8Encrypt(s), AESKey, AESIV, "CBC"));
}
// this function takes a string, generates a random AES key and initialization vector and RSA encrypts them
// then encrypts the string using AES and returns the RSA encrypted keys + '_' + AES encrypted string
function AESEncryptWithKeyGeneration(s, rsaKey) {
  AESKey = generateAESKey();
  AESIV = generateAESIV();
  AESCipher = b64EncryptBytes(rijndaelEncrypt(utf8Encrypt(s), AESKey, AESIV, "CBC"));
  return "1_" + RSAEncryptHex(AESKey + AESIV, rsaKey) + "_" + AESCipher;
}
// this is a utility function to time the AES encryption
// this can be removed after it outlives its usefulness
function AESTimer(s, rsaKey) {
  time = "";
  absoluteStartTime=new Date();
  AESKey = generateAESKey();
  AESIV = generateAESIV();
  startTime=new Date();
  message = utf8Encrypt(s);
  endTime=new Date();
  time += 'UTF8: '+(endTime.getTime()-startTime.getTime())/1000.0;
  startTime=new Date();
  ciphertext = rijndaelEncrypt(message, AESKey, AESIV, "CBC");
  endTime=new Date();
  time += '\nAES: '+(endTime.getTime()-startTime.getTime())/1000.0;
  startTime=new Date();
  b64ciphertext = b64EncryptBytes(ciphertext);
  endTime=new Date();
  time += '\nb64: '+(endTime.getTime()-startTime.getTime())/1000.0;
  document.getElementById('resultID').value=time;
  startTime=new Date();
  RSAstuff = RSAEncryptHex(AESKey + AESIV, rsaKey);
  endTime=new Date();
  absoluteEndTime=new Date();
  time += '\nRSA: '+(endTime.getTime()-startTime.getTime())/1000.0;
  time += '\nTotal Time: '+(absoluteEndTime.getTime()-absoluteStartTime.getTime())/1000.0;
  document.getElementById('resultID').value=time;
  return "1_" + RSAstuff + "_" + b64ciphertext;
}
//###################### END AES ######################
//###################### BEGIN TRIPLE DES ######################
// this function generates a random Triple DES key and initialization vector and encrypts them
// with the server's public RSA key
function GenerateRSAEncryptedTripleDESKeys() {
  TripleDESKey = generateTripleDESKey();
  TripleDESIV = generateTripleDESIV();
  return RSAEncryptHex(TripleDESKey + TripleDESIV, rsaKey);
}
// this function accepts a string, a Triple DES key as a string of hex characters,
// and a Triple DES initialization vector as a string of hex characters and returns the Triple DES encrypted string
function TripleDESEncrypt(s, TripleDESKey, TripleDESIV) {
  return b64EncryptBytes(des(TripleDESKey, utf8Encrypt(s), 1, 1, TripleDESIV));
}
// this function takes a string, generates a random Triple DES key and initialization vector and RSA encrypts them
// then encrypts the string using Triple DES and returns the RSA encrypted keys + '_' + Triple DES encrypted string
function TripleDESEncryptWithKeyGeneration(s, rsaKey) {
  TripleDESKey = generateTripleDESKey();
  TripleDESIV = generateTripleDESIV();
  TripleDESCipher = b64EncryptBytes(des(TripleDESKey, utf8Encrypt(s), 1, 1, TripleDESIV));
  return "2_" + RSAEncryptHex(TripleDESKey + TripleDESIV, rsaKey) + "_" + TripleDESCipher;
}
// this is a utility function to time the Triple DES encryption
// this can be removed after it outlives its usefulness
function TripleDESTimer(s, rsaKey) {
  time = "";
  absoluteStartTime=new Date();
  TripleDESKey = generateTripleDESKey();
  TripleDESIV = generateTripleDESIV();
  startTime=new Date();
  message = utf8Encrypt(s);
  endTime=new Date();
  time += 'UTF8: '+(endTime.getTime()-startTime.getTime())/1000.0;
  startTime=new Date();
  ciphertext = des(TripleDESKey, message, 1, 1, TripleDESIV);
  endTime=new Date();
  time += '\nDES: '+(endTime.getTime()-startTime.getTime())/1000.0;
  startTime=new Date();
  b64ciphertext = b64EncryptBytes(ciphertext);
  endTime=new Date();
  time += '\nb64: '+(endTime.getTime()-startTime.getTime())/1000.0;
  document.getElementById('resultID').value=time;
  startTime=new Date();
  RSAstuff = RSAEncryptHex(TripleDESKey + TripleDESIV, rsaKey);
  endTime=new Date();
  absoluteEndTime=new Date();
  time += '\nRSA: '+(endTime.getTime()-startTime.getTime())/1000.0;
  time += '\nTotal Time: '+(absoluteEndTime.getTime()-absoluteStartTime.getTime())/1000.0;
  document.getElementById('resultID').value=time;
  return "2_" + RSAstuff + "_" + b64ciphertext;
}
//###################### END TRIPLE DES ######################
/////////////////////////////// END EXTERNAL FUNCTIONS SECTION ////////////////////////////