    var keySizeInBits=256;var blockSizeInBits = 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];

    var loadTime = (new Date()).getTime();  // Save time page was loaded
    var key;	    	    	    	    // Key (byte array)
    var prng;	    	    	    	    // Pseudorandom number generator

    function array(n) {
      for (var i=0; i<n; i++) {
        this[i] = 0;
      }
      this.length = n;
    }
    
    function integer(n) {
      return n % (0xffffffff+1);
    }
    
    function shr(a, b) {
      a = integer(a);
      b = integer(b);
      if (a-0x80000000 >= 0) {
        a = a % 0x80000000;
        a >>= b;
        a += 0x40000000>>(b-1);
      } else {
        a >>= b;
      }
      return a;
    }    

    function shl1(a) {
      a = a % 0x80000000;
      if (a&0x40000000 == 0x40000000) {
        a -= 0x40000000;
        a *= 2;
        a += 0x80000000;
      } else {
        a *= 2;
      }
      return a;
    }

    function shl(a, b) {
      a = integer(a);
      b = integer(b);
      for (var i=0; i<b; i++) {
        a = shl1(a);
      }
      return a;
    }
    
    function and(a, b) {
      a = integer(a);
      b = integer(b);
      var t1 = a - 0x80000000;
      var t2 = b - 0x80000000;
      if (t1 >= 0) {
        if(t2 >= 0) {
          return((t1 & t2) + 0x80000000);
        } else { 
          return(t1 & b);
        }
      } else {
        if (t2 >= 0) {
          return(a & t2);
        } else { 
          return(a & b);
        }
      }
    }

    function or(a, b) { 
      a = integer(a);
      b = integer(b);
      var t1 = a - 0x80000000;
      var t2 = b - 0x80000000;
      if (t1 >= 0) {
        if (t2 >= 0) {
          return((t1 | t2) + 0x80000000);
        } else {
          return((t1 | b) + 0x80000000);
        }
      } else {
        if (t2 >= 0) {
          return((a | t2) + 0x80000000);
        } else {
          return(a | b);
        }
      }
    }
    
    function xor(a, b) {
      a = integer(a);
      b = integer(b);
      var t1 = a - 0x80000000;
      var t2 = b - 0x80000000;
      if (t1 >= 0) {
        if (t2 >= 0) {
          return(t1 ^ t2);
        } else {
          return((t1 ^ b) + 0x80000000);
        }
      } else {
        if (t2 >= 0) {
          return((a ^ t2) + 0x80000000);
        } else {
          return(a ^ b);
        }
      }
    }
    
    function not(a) {
      a = integer(a);
      return 0xffffffff - a;
    }    
    
    var count = new array(2);
    count[0] = 0;
    count[1] = 0;
    var state = new array(4);
    var buffer = new array(64);
    var transformBuffer = new array(16);
    var digestBits = new array(16);
    var S11=7;var S12=12;var S13=17;var S14=22;var S21=5;var S22=9;var S23=14;var S24=20;var S31=4;var S32=11;var S33=16;var S34=23;var S41=6;var S42=10;var S43=15;var S44=21;

    function F(x,y,z) { 
      return or(and(x,y), and(not(x),z));
    }
    
    function G(x,y,z) {
      return or(and(x,z), and(y,not(z)));
    }
    
    function H(x,y,z) {
      return xor(xor(x,y), z);
    }
    
    function I(x,y,z) {
      return xor(y,or(x, not(z)));
    }
    
    function rotateLeft(a,n) {
      return or(shl(a,n), (shr(a,(32-n))));
    }
    
    function FF(a,b,c,d,x,s,ac) {
      a = a + F(b,c,d) + x + ac;
      a = rotateLeft(a,s);
      a = a + b;
      return a;
    }
    
    function GG(a,b,c,d,x,s,ac) {
      a = a + G(b,c,d) + x + ac;
      a = rotateLeft(a,s);
      a = a + b;
      return a;
    }
    
    function HH(a,b,c,d,x,s,ac) { 
      a = a + H(b,c,d) + x + ac;
      a = rotateLeft(a,s);
      a = a + b;
      return a;
    }
    
    function II(a,b,c,d,x,s,ac) { 
      a = a + I(b,c,d) + x + ac;
      a = rotateLeft(a,s);
      a = a + b;
      return a;
    }
    
function byteArrayToHex(byteArray){var result="";
if(!byteArray)return null;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 null;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 md5_init() {
      count[0] = count[1] = 0;
      state[0] = 0x67452301;
      state[1] = 0xefcdab89;
      state[2] = 0x98badcfe;
      state[3] = 0x10325476;
      for (i=0; i<digestBits.length;i++) {
        digestBits[i] = 0;
      }
    }
    function md5_update(b) {
      var index, i;
      index = and(shr(count[0],3), 0x3F);
      if (count[0] < 0xFFFFFFFF-7) {
        count[0] += 8;
      } else {
        count[1]++;
        count[0] -= 0xFFFFFFFF+1;
        count[0] += 8;
      }
      buffer[index] = and(b, 0xFF);
      if (index >= 63) {
        transform(buffer,0);
      }
    }
    
    function md5_finish() {
      var bits = new array(8);
      var padding;
      var i = 0, index = 0, padLen = 0;
      for (i=0; i<4; i++) {
        bits[i] = and(shr(count[0], (i*8)), 0xFF);
      }
      for (i=0; i<4; i++) {
        bits[i+4] = and(shr(count[1], (i*8)), 0xFF);
      }
      index = and(shr(count[0],3), 0x3F);
      padLen = (index<56) ? (56-index) : (120-index);
      padding = new array(64);
      padding[0] = 0x80;
      for (i=0; i<padLen; i++) {
        md5_update(padding[i]);
      }
      for (i=0; i<8; i++) {
        md5_update(bits[i]);
      }
      for(i=0; i<4; i++) {
        for(j=0; j<4; j++) {
          digestBits[i*4+j] = and(shr(state[i],(j*8)), 0xFF);
        }
      }
    }
    
    function unicode_to_utf8(s) {
      var utf8 = "";
      for (var n=0; n<s.length; n++) {
        var c = s.charCodeAt(n);
        if (c <= 0x7F) { 
          utf8 += String.fromCharCode(c);
        } else if((c >= 0x80) && (c <= 0x7FF)) {
          utf8 += String.fromCharCode((c>>6) | 0xC0);
          utf8 += String.fromCharCode((c&0x3F) | 0x80);
        } else {
          utf8 += String.fromCharCode((c>>12) | 0xE0);
          utf8 += String.fromCharCode(((c>>6) & 0x3F) | 0x80);
          utf8 += String.fromCharCode((c&0x3F) | 0x80);
        }
      }
      return utf8;
    }
    
    function utf8_to_unicode(utf8) {
      var s = "", i=0, b1, b2, b2;
      while (i < utf8.length) {
        b1 = utf8.charCodeAt(i);
        if (b1 < 0x80) {
          s += String.fromCharCode(b1);
          i++;
        } else if((b1 >= 0xC0) && (b1 < 0xE0)) {
          b2 = utf8.charCodeAt(i+1);
          s += String.fromCharCode(((b1 & 0x1F)<<6) | (b2 & 0x3F));
          i+=2;
        } else { 
          b2 = utf8.charCodeAt(i+1);
          b3 = utf8.charCodeAt(i+2);
          s += String.fromCharCode(((b1 & 0xF)<<12) | ((b2 & 0x3F)<<6) | (b3 & 0x3F));
          i+=3;
        }
      }
      return s;
    }
    
    function encode_utf8(s) {
      var i, necessary=false;
      for (i=0; i<s.length; i++) {
        if ((s.charCodeAt(i) == 0x9D) || (s.charCodeAt(i) > 0xFF)) {
          necessary = true;
          break;
        }
      }
      if (!necessary) {
        return s;
      }
      return String.fromCharCode(0x9D) + unicode_to_utf8(s);
    }

    function decode_utf8(s) {
      if ((s.length > 0) && (s.charCodeAt(0) == 0x9D)) {
        return utf8_to_unicode(s.substring(1));
      }
      return s;
    }

    function transform(buf,offset) {
      var a=0, b=0, c=0, d=0;
      var x = transformBuffer;
      a=state[0]; b=state[1]; c=state[2]; d=state[3];
      for (i=0; i<16; i++) {
        x[i] = and(buf[i*4+offset], 0xFF);
        for (j=1; j<4; j++) {
          x[i] += shl(and(buf[i*4+j+offset], 0xFF), j*8);
        }
      }
      a = FF(a,b,c,d,x[0],S11,0xd76aa478);
      d = FF(d,a,b,c,x[1],S12,0xe8c7b756);
      c = FF(c,d,a,b,x[2],S13,0x242070db);
      b = FF(b,c,d,a,x[3],S14,0xc1bdceee);
      a = FF(a,b,c,d,x[4],S11,0xf57c0faf);
      d = FF(d,a,b,c,x[5],S12,0x4787c62a);
      c = FF(c,d,a,b,x[6],S13,0xa8304613);
      b = FF(b,c,d,a,x[7],S14,0xfd469501);
      a = FF(a,b,c,d,x[8],S11,0x698098d8);
      d = FF(d,a,b,c,x[9],S12,0x8b44f7af);
      c = FF(c,d,a,b,x[10],S13,0xffff5bb1);
      b = FF(b,c,d,a,x[11],S14,0x895cd7be);
      a = FF(a,b,c,d,x[12],S11,0x6b901122);
      d = FF(d,a,b,c,x[13],S12,0xfd987193);
      c = FF(c,d,a,b,x[14],S13,0xa679438e);
      b = FF(b,c,d,a,x[15],S14,0x49b40821);
      a = GG(a,b,c,d,x[1],S21,0xf61e2562);
      d = GG(d,a,b,c,x[6],S22,0xc040b340);
      c = GG(c,d,a,b,x[11],S23,0x265e5a51);
      b = GG(b,c,d,a,x[0],S24,0xe9b6c7aa);
      a = GG(a,b,c,d,x[5],S21,0xd62f105d);
      d = GG(d,a,b,c,x[10],S22,0x2441453);
      c = GG(c,d,a,b,x[15],S23,0xd8a1e681);
      b = GG(b,c,d,a,x[4],S24,0xe7d3fbc8);
      a = GG(a,b,c,d,x[9],S21,0x21e1cde6);
      d = GG(d,a,b,c,x[14],S22,0xc33707d6);
      c = GG(c,d,a,b,x[3],S23,0xf4d50d87);
      b = GG(b,c,d,a,x[8],S24,0x455a14ed);
      a = GG(a,b,c,d,x[13],S21,0xa9e3e905);
      d = GG(d,a,b,c,x[2],S22,0xfcefa3f8);
      c = GG(c,d,a,b,x[7],S23,0x676f02d9);
      b = GG(b,c,d,a,x[12],S24,0x8d2a4c8a);
      a = HH(a,b,c,d,x[5],S31,0xfffa3942);
      d = HH(d,a,b,c,x[8],S32,0x8771f681);
      c = HH(c,d,a,b,x[11],S33,0x6d9d6122);
      b = HH(b,c,d,a,x[14],S34,0xfde5380c);
      a = HH(a,b,c,d,x[1],S31,0xa4beea44);
      d = HH(d,a,b,c,x[4],S32,0x4bdecfa9);
      c = HH(c,d,a,b,x[7],S33,0xf6bb4b60);
      b = HH(b,c,d,a,x[10],S34,0xbebfbc70);
      a = HH(a,b,c,d,x[13],S31,0x289b7ec6);
      d = HH(d,a,b,c,x[0],S32,0xeaa127fa);
      c = HH(c,d,a,b,x[3],S33,0xd4ef3085);
      b = HH(b,c,d,a,x[6],S34,0x4881d05);
      a = HH(a,b,c,d,x[9],S31,0xd9d4d039);
      d = HH(d,a,b,c,x[12],S32,0xe6db99e5);
      c = HH(c,d,a,b,x[15],S33,0x1fa27cf8);
      b = HH(b,c,d,a,x[2],S34,0xc4ac5665);
      a = II(a,b,c,d,x[0],S41,0xf4292244);
      d = II(d,a,b,c,x[7],S42,0x432aff97);
      c = II(c,d,a,b,x[14],S43,0xab9423a7);
      b = II(b,c,d,a,x[5],S44,0xfc93a039);
      a = II(a,b,c,d,x[12],S41,0x655b59c3);
      d = II(d,a,b,c,x[3],S42,0x8f0ccc92);
      c = II(c,d,a,b,x[10],S43,0xffeff47d);
      b = II(b,c,d,a,x[1],S44,0x85845dd1);
      a = II(a,b,c,d,x[8],S41,0x6fa87e4f);
      d = II(d,a,b,c,x[15],S42,0xfe2ce6e0);
      c = II(c,d,a,b,x[6],S43,0xa3014314);
      b = II(b,c,d,a,x[13],S44,0x4e0811a1);
      a = II(a,b,c,d,x[4],S41,0xf7537e82);
      d = II(d,a,b,c,x[11],S42,0xbd3af235);
      c = II(c,d,a,b,x[2],S43,0x2ad7d2bb);
      b = II(b,c,d,a,x[9],S44,0xeb86d391);
      state[0] += a;
      state[1] += b;
      state[2] += c;
      state[3] += d;
    }
    
    //	setKey  --  Set key from string or hexadecimal specification
    function setKey() {
      var s = encode_utf8(document.frmChallenge.key.value);
      var i, kmd5e, kmd5o;
      if (s.length == 1) {
        s += s;
      }
      md5_init();
      for (i=0; i<s.length; i+=2) {
        md5_update(s.charCodeAt(i));
      }
      md5_finish();
      kmd5e = byteArrayToHex(digestBits);
	    
      md5_init();
      for (i=0; i<s.length; i+=2) {
        md5_update(s.charCodeAt(i));
      }
      md5_finish();
      kmd5o = byteArrayToHex(digestBits);

      var hs = kmd5e + kmd5o;
      key = hexToByteArray(hs);
      hs = byteArrayToHex(key);
      key =  hexToByteArray(hs);
alert(key);
      addEntropyTime();
      prng = new AESprng(keyFromEntropy());
      var plaintext = encode_utf8(document.plain.text.value);

    }
    
    /*	Generate a key from the pseudorandom number generator
    	and stuff it in the key field.  The kind of key generated
	(text or hexadecimal) is determined by which box is checked
	below the key field.  */
    
    function Generate_key() {
    	var i, j, k = "";
	
    	var i, j, k = "";
	
	addEntropyTime();
	var seed = keyFromEntropy();
	
    	var prng = new AESprng(seed);
	if (document.key.keytype[0].checked) {
	    //	Text key
	    var charA = ("A").charCodeAt(0);
	    
	    for (i = 0; i < 12; i++) {
		if (i > 0) {
	    	    k += "-";
		}
		for (j = 0; j < 5; j++) {
	    	    k += String.fromCharCode(charA + prng.nextInt(25));
		}
	    }
	} else {
	    // Hexadecimal key
	    var hexDigits = "0123456789ABCDEF";
	    
	    for (i = 0; i < 64; i++) {
	    	k += hexDigits.charAt(prng.nextInt(15));
	    }
	}
    	document.key.text.value = k;
	delete prng;
    }
    
    function Encrypt_text() {
	var v, i;
	var prefix = "#####  Encrypted: decrypt with http://www.fourmilab.ch/javascrypt/\n",
	    suffix = "#####  End encrypted message\n";
	
    	if (document.key.text.value.length == 0) {
	    alert("Please specify a key with which to encrypt the message.");
	    return;
	}
    	if (document.plain.text.value.length == 0) {
	    alert("No plain text to encrypt!  Please enter or paste plain text in the field above.");
	    return;
	}
    	document.cipher.text.value = "";
    	setKey();

	addEntropyTime();
    	prng = new AESprng(keyFromEntropy());
	var plaintext = encode_utf8(document.plain.text.value);
	
	//  Compute MD5 sum of message text and add to header
	
	md5_init();
	for (i = 0; i < plaintext.length; i++) {
	    md5_update(plaintext.charCodeAt(i));
	}
	md5_finish();
	var header = "";
	for (i = 0; i < digestBits.length; i++) {
	    header += String.fromCharCode(digestBits[i]);
	}
	
	//  Add message length in bytes to header
	
	i = plaintext.length;
	header += String.fromCharCode(i >>> 24);
	header += String.fromCharCode(i >>> 16);
	header += String.fromCharCode(i >>> 8);
	header += String.fromCharCode(i & 0xFF);

    	/*  The format of the actual message passed to rijndaelEncrypt
	    is:
	    
	    	    Bytes   	Content
		     0-15   	MD5 signature of plaintext
		    16-19   	Length of plaintext, big-endian order
		    20-end  	Plaintext
		    
	    Note that this message will be padded with zero bytes
	    to an integral number of AES blocks (blockSizeInBits / 8).
	    This does not include the initial vector for CBC
	    encryption, which is added internally by rijndaelEncrypt.
	    
	*/

	var ct = rijndaelEncrypt(header + plaintext, key, "CBC");
    	if (document.plain.encoding[0].checked) {
	    v = armour_codegroup(ct);
	} else if (document.plain.encoding[1].checked) {
    	    v = armour_hex(ct);
	} else if (document.plain.encoding[2].checked) {
    	    v = armour_base64(ct);
	}
	document.cipher.text.value = prefix + v + suffix;
    	delete prng;
    }
    
    /*  Examine the message and determine which kind of ASCII
    	armour it uses from the sentinel preceding the message.
	We test for each of the sentinels and, if any are
	found, decide based on the one found first in the
	message (since, for example, the sentinel for
	codegroup armour might appear in a Base64 message,
	but only after the Base64 sentinel).  If none of
	the sentinels are found, we decode using the armour
	type specified by the checkboxes for encryption.
	The return value is an integer which identifies the
	armour type as follows:
	
	    	0   Codegroup
		1   Hexadecimal
		2   Base 64
    */
    
    function determineArmourType(s) {
    	var kt, pcg, phex, pb64, pmin;
	
	pcg = s.indexOf(codegroupSentinel);
	phex = s.indexOf(hexSentinel);
	pb64 = s.indexOf(base64sent);
	if (pcg == -1) {
	    pcg = s.length;
	}
	if (phex == -1) {
	    phex = s.length;
	}
	if (pb64 == -1) {
	    pb64 = s.length;
	}
	pmin = Math.min(pcg, Math.min(phex, pb64));
	if (pmin < s.length) {
	    if (pmin == pcg) {
	    	kt = 0;
	    } else if (pmin == phex) {
	    	kt = 1;
	    } else {
	    	kt = 2;
	    }
	} else {
    	    if (document.plain.encoding[0].checked) {
    		kt = 0;
	    } else if (document.plain.encoding[1].checked) {
    		kt = 1;
	    } else if (document.plain.encoding[2].checked) {
    		kt = 2;
	    }
	}
	return kt;
    }
    
    //	Decrypt ciphertext with key, place result in plaintext field
/*    
    function Decrypt_text() {
	
    	if (document.key.text.value.length == 0) {
	    alert("Please specify a key with which to decrypt the message.");
	    return;
	}
    	if (document.cipher.text.value.length == 0) {
	    alert("No cipher text to decrypt!  Please enter or paste cipher text in the field above.");
	    return '';
	}
    	document.plain.text.value = "";
    	setKey();
	var ct = new Array(), kt;
	kt = determineArmourType(document.cipher.text.value);
    	if (kt == 0) {
    	    ct = disarm_codegroup(document.cipher.text.value);
	} else if (kt == 1) {
    	    ct = disarm_hex(document.cipher.text.value);
	} else if (kt == 2) {
    	    ct = disarm_base64(document.cipher.text.value);
	}

	var result = rijndaelDecrypt(ct, key, "CBC");
	
	var header = result.slice(0, 20);
	result = result.slice(20);
*/	
	/*  Extract the length of the plaintext transmitted and
	    verify its consistency with the length decoded.  Note
	    that in many cases the decrypted messages will include
	    pad bytes added to expand the plaintext to an integral
	    number of AES blocks (blockSizeInBits / 8).  */
/*	
	var dl = (header[16] << 24) | (header[17] << 16) | (header[18] << 8) | header[19];
    	if ((dl < 0) || (dl > result.length)) {
	    alert("Message (length " + result.length + ") truncated.  " +
	    	dl + " characters expected.");
	    //	Try to sauve qui peut by setting length to entire message
    	    dl = result.length;
	}
*/	
	/*  Compute MD5 signature of message body and verify
	    against signature in message.  While we're at it,
	    we assemble the plaintext result string.  Note that
	    the length is that just extracted above from the
	    message, *not* the full decrypted message text.
	    AES requires all messages to be an integral number
	    of blocks, and the message may have been padded with
	    zero bytes to fill out the last block; using the
	    length from the message header elides them from
	    both the MD5 computation and plaintext result.  */
/*	    
	var i, plaintext = "";
	
	md5_init();
	for (i = 0; i < dl; i++) {
	    plaintext += String.fromCharCode(result[i]);
	    md5_update(result[i]);
	}
	md5_finish();

	for (i = 0; i < digestBits.length; i++) {
	    if (digestBits[i] != header[i]) {
	    	alert("Message corrupted.  Checksum of decrypted message does not match.");
		break;
	    }
	}
	
	//  That's it; plug plaintext into the result field
	
	document.plain.text.value = decode_utf8(plaintext);
    }
*/