includes/clientside/static/rijndael.js
changeset 1 fe660c52c48f
child 348 87e08a6e4fec
equal deleted inserted replaced
0:902822492a68 1:fe660c52c48f
       
     1 /* rijndael.js      Rijndael Reference Implementation
       
     2    Copyright (c) 2001 Fritz Schneider
       
     3  
       
     4  This software is provided as-is, without express or implied warranty.  
       
     5  Permission to use, copy, modify, distribute or sell this software, with or
       
     6  without fee, for any purpose and by any individual or organization, is hereby
       
     7  granted, provided that the above copyright notice and this paragraph appear 
       
     8  in all copies. Distribution as a part of an application or binary must
       
     9  include the above copyright notice in the documentation and/or other materials
       
    10  provided with the application or distribution.
       
    11 
       
    12 
       
    13    As the above disclaimer notes, you are free to use this code however you
       
    14    want. However, I would request that you send me an email 
       
    15    (fritz /at/ cs /dot/ ucsd /dot/ edu) to say hi if you find this code useful
       
    16    or instructional. Seeing that people are using the code acts as 
       
    17    encouragement for me to continue development. If you *really* want to thank
       
    18    me you can buy the book I wrote with Thomas Powell, _JavaScript:
       
    19    _The_Complete_Reference_ :)
       
    20 
       
    21    This code is an UNOPTIMIZED REFERENCE implementation of Rijndael. 
       
    22    If there is sufficient interest I can write an optimized (word-based, 
       
    23    table-driven) version, although you might want to consider using a 
       
    24    compiled language if speed is critical to your application. As it stands,
       
    25    one run of the monte carlo test (10,000 encryptions) can take up to 
       
    26    several minutes, depending upon your processor. You shouldn't expect more
       
    27    than a few kilobytes per second in throughput.
       
    28 
       
    29    Also note that there is very little error checking in these functions. 
       
    30    Doing proper error checking is always a good idea, but the ideal 
       
    31    implementation (using the instanceof operator and exceptions) requires
       
    32    IE5+/NS6+, and I've chosen to implement this code so that it is compatible
       
    33    with IE4/NS4. 
       
    34 
       
    35    And finally, because JavaScript doesn't have an explicit byte/char data 
       
    36    type (although JavaScript 2.0 most likely will), when I refer to "byte" 
       
    37    in this code I generally mean "32 bit integer with value in the interval 
       
    38    [0,255]" which I treat as a byte.
       
    39 
       
    40    See http://www-cse.ucsd.edu/~fritz/rijndael.html for more documentation
       
    41    of the (very simple) API provided by this code.
       
    42 
       
    43                                                Fritz Schneider
       
    44                                                fritz at cs.ucsd.edu
       
    45  
       
    46 */
       
    47 
       
    48 // Rijndael parameters --  Valid values are 128, 192, or 256
       
    49 
       
    50 var keySizeInBits =   ( typeof AES_BITS == 'number' ) ? AES_BITS : 128;
       
    51 var blockSizeInBits = ( typeof AES_BLOCKSIZE == 'number' ) ? AES_BLOCKSIZE : 128;
       
    52 
       
    53 ///////  You shouldn't have to modify anything below this line except for
       
    54 ///////  the function getRandomBytes().
       
    55 //
       
    56 // Note: in the following code the two dimensional arrays are indexed as
       
    57 //       you would probably expect, as array[row][column]. The state arrays
       
    58 //       are 2d arrays of the form state[4][Nb].
       
    59 
       
    60 
       
    61 // The number of rounds for the cipher, indexed by [Nk][Nb]
       
    62 var roundsArray = [ ,,,,[,,,,10,, 12,, 14],, 
       
    63                         [,,,,12,, 12,, 14],, 
       
    64                         [,,,,14,, 14,, 14] ];
       
    65 
       
    66 // The number of bytes to shift by in shiftRow, indexed by [Nb][row]
       
    67 var shiftOffsets = [ ,,,,[,1, 2, 3],,[,1, 2, 3],,[,1, 3, 4] ];
       
    68 
       
    69 // The round constants used in subkey expansion
       
    70 var Rcon = [ 
       
    71 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 
       
    72 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 
       
    73 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 
       
    74 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 
       
    75 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91 ];
       
    76 
       
    77 // Precomputed lookup table for the SBox
       
    78 var SBox = [
       
    79  99, 124, 119, 123, 242, 107, 111, 197,  48,   1, 103,  43, 254, 215, 171, 
       
    80 118, 202, 130, 201, 125, 250,  89,  71, 240, 173, 212, 162, 175, 156, 164, 
       
    81 114, 192, 183, 253, 147,  38,  54,  63, 247, 204,  52, 165, 229, 241, 113, 
       
    82 216,  49,  21,   4, 199,  35, 195,  24, 150,   5, 154,   7,  18, 128, 226, 
       
    83 235,  39, 178, 117,   9, 131,  44,  26,  27, 110,  90, 160,  82,  59, 214, 
       
    84 179,  41, 227,  47, 132,  83, 209,   0, 237,  32, 252, 177,  91, 106, 203, 
       
    85 190,  57,  74,  76,  88, 207, 208, 239, 170, 251,  67,  77,  51, 133,  69, 
       
    86 249,   2, 127,  80,  60, 159, 168,  81, 163,  64, 143, 146, 157,  56, 245, 
       
    87 188, 182, 218,  33,  16, 255, 243, 210, 205,  12,  19, 236,  95, 151,  68,  
       
    88 23,  196, 167, 126,  61, 100,  93,  25, 115,  96, 129,  79, 220,  34,  42, 
       
    89 144, 136,  70, 238, 184,  20, 222,  94,  11, 219, 224,  50,  58,  10,  73,
       
    90   6,  36,  92, 194, 211, 172,  98, 145, 149, 228, 121, 231, 200,  55, 109, 
       
    91 141, 213,  78, 169, 108,  86, 244, 234, 101, 122, 174,   8, 186, 120,  37,  
       
    92  46,  28, 166, 180, 198, 232, 221, 116,  31,  75, 189, 139, 138, 112,  62, 
       
    93 181, 102,  72,   3, 246,  14,  97,  53,  87, 185, 134, 193,  29, 158, 225,
       
    94 248, 152,  17, 105, 217, 142, 148, 155,  30, 135, 233, 206,  85,  40, 223,
       
    95 140, 161, 137,  13, 191, 230,  66, 104,  65, 153,  45,  15, 176,  84, 187,  
       
    96  22 ];
       
    97 
       
    98 // Precomputed lookup table for the inverse SBox
       
    99 var SBoxInverse = [
       
   100  82,   9, 106, 213,  48,  54, 165,  56, 191,  64, 163, 158, 129, 243, 215, 
       
   101 251, 124, 227,  57, 130, 155,  47, 255, 135,  52, 142,  67,  68, 196, 222, 
       
   102 233, 203,  84, 123, 148,  50, 166, 194,  35,  61, 238,  76, 149,  11,  66, 
       
   103 250, 195,  78,   8,  46, 161, 102,  40, 217,  36, 178, 118,  91, 162,  73, 
       
   104 109, 139, 209,  37, 114, 248, 246, 100, 134, 104, 152,  22, 212, 164,  92, 
       
   105 204,  93, 101, 182, 146, 108, 112,  72,  80, 253, 237, 185, 218,  94,  21,  
       
   106  70,  87, 167, 141, 157, 132, 144, 216, 171,   0, 140, 188, 211,  10, 247, 
       
   107 228,  88,   5, 184, 179,  69,   6, 208,  44,  30, 143, 202,  63,  15,   2, 
       
   108 193, 175, 189,   3,   1,  19, 138, 107,  58, 145,  17,  65,  79, 103, 220, 
       
   109 234, 151, 242, 207, 206, 240, 180, 230, 115, 150, 172, 116,  34, 231, 173,
       
   110  53, 133, 226, 249,  55, 232,  28, 117, 223, 110,  71, 241,  26, 113,  29, 
       
   111  41, 197, 137, 111, 183,  98,  14, 170,  24, 190,  27, 252,  86,  62,  75, 
       
   112 198, 210, 121,  32, 154, 219, 192, 254, 120, 205,  90, 244,  31, 221, 168,
       
   113  51, 136,   7, 199,  49, 177,  18,  16,  89,  39, 128, 236,  95,  96,  81,
       
   114 127, 169,  25, 181,  74,  13,  45, 229, 122, 159, 147, 201, 156, 239, 160,
       
   115 224,  59,  77, 174,  42, 245, 176, 200, 235, 187,  60, 131,  83, 153,  97, 
       
   116  23,  43,   4, 126, 186, 119, 214,  38, 225, 105,  20,  99,  85,  33,  12,
       
   117 125 ];
       
   118 
       
   119 function str_split(string, chunklen)
       
   120 {
       
   121   if(!chunklen) chunklen = 1;
       
   122   ret = new Array();
       
   123   for ( i = 0; i < string.length; i+=chunklen )
       
   124   {
       
   125     ret[ret.length] = string.slice(i, i+chunklen);
       
   126   }
       
   127   return ret;
       
   128 }
       
   129 
       
   130 // This method circularly shifts the array left by the number of elements
       
   131 // given in its parameter. It returns the resulting array and is used for 
       
   132 // the ShiftRow step. Note that shift() and push() could be used for a more 
       
   133 // elegant solution, but they require IE5.5+, so I chose to do it manually. 
       
   134 
       
   135 function cyclicShiftLeft(theArray, positions) {
       
   136   var temp = theArray.slice(0, positions);
       
   137   theArray = theArray.slice(positions).concat(temp);
       
   138   return theArray;
       
   139 }
       
   140 
       
   141 // Cipher parameters ... do not change these
       
   142 var Nk = keySizeInBits / 32;                   
       
   143 var Nb = blockSizeInBits / 32;
       
   144 var Nr = roundsArray[Nk][Nb];
       
   145 
       
   146 // Multiplies the element "poly" of GF(2^8) by x. See the Rijndael spec.
       
   147 
       
   148 function xtime(poly) {
       
   149   poly <<= 1;
       
   150   return ((poly & 0x100) ? (poly ^ 0x11B) : (poly));
       
   151 }
       
   152 
       
   153 // Multiplies the two elements of GF(2^8) together and returns the result.
       
   154 // See the Rijndael spec, but should be straightforward: for each power of
       
   155 // the indeterminant that has a 1 coefficient in x, add y times that power
       
   156 // to the result. x and y should be bytes representing elements of GF(2^8)
       
   157 
       
   158 function mult_GF256(x, y) {
       
   159   var bit, result = 0;
       
   160   
       
   161   for (bit = 1; bit < 256; bit *= 2, y = xtime(y)) {
       
   162     if (x & bit) 
       
   163       result ^= y;
       
   164   }
       
   165   return result;
       
   166 }
       
   167 
       
   168 // Performs the substitution step of the cipher. State is the 2d array of
       
   169 // state information (see spec) and direction is string indicating whether
       
   170 // we are performing the forward substitution ("encrypt") or inverse 
       
   171 // substitution (anything else)
       
   172 
       
   173 function byteSub(state, direction) {
       
   174   var S;
       
   175   if (direction == "encrypt")           // Point S to the SBox we're using
       
   176     S = SBox;
       
   177   else
       
   178     S = SBoxInverse;
       
   179   for (var i = 0; i < 4; i++)           // Substitute for every byte in state
       
   180     for (var j = 0; j < Nb; j++)
       
   181        state[i][j] = S[state[i][j]];
       
   182 }
       
   183 
       
   184 // Performs the row shifting step of the cipher.
       
   185 
       
   186 function shiftRow(state, direction) {
       
   187   for (var i=1; i<4; i++)               // Row 0 never shifts
       
   188     if (direction == "encrypt")
       
   189        state[i] = cyclicShiftLeft(state[i], shiftOffsets[Nb][i]);
       
   190     else
       
   191        state[i] = cyclicShiftLeft(state[i], Nb - shiftOffsets[Nb][i]);
       
   192 
       
   193 }
       
   194 
       
   195 // Performs the column mixing step of the cipher. Most of these steps can
       
   196 // be combined into table lookups on 32bit values (at least for encryption)
       
   197 // to greatly increase the speed. 
       
   198 
       
   199 function mixColumn(state, direction) {
       
   200   var b = [];                            // Result of matrix multiplications
       
   201   for (var j = 0; j < Nb; j++) {         // Go through each column...
       
   202     for (var i = 0; i < 4; i++) {        // and for each row in the column...
       
   203       if (direction == "encrypt")
       
   204         b[i] = mult_GF256(state[i][j], 2) ^          // perform mixing
       
   205                mult_GF256(state[(i+1)%4][j], 3) ^ 
       
   206                state[(i+2)%4][j] ^ 
       
   207                state[(i+3)%4][j];
       
   208       else 
       
   209         b[i] = mult_GF256(state[i][j], 0xE) ^ 
       
   210                mult_GF256(state[(i+1)%4][j], 0xB) ^
       
   211                mult_GF256(state[(i+2)%4][j], 0xD) ^
       
   212                mult_GF256(state[(i+3)%4][j], 9);
       
   213     }
       
   214     for (var i = 0; i < 4; i++)          // Place result back into column
       
   215       state[i][j] = b[i];
       
   216   }
       
   217 }
       
   218 
       
   219 // Adds the current round key to the state information. Straightforward.
       
   220 
       
   221 function addRoundKey(state, roundKey) {
       
   222   for (var j = 0; j < Nb; j++) {                 // Step through columns...
       
   223     state[0][j] ^= (roundKey[j] & 0xFF);         // and XOR
       
   224     state[1][j] ^= ((roundKey[j]>>8) & 0xFF);
       
   225     state[2][j] ^= ((roundKey[j]>>16) & 0xFF);
       
   226     state[3][j] ^= ((roundKey[j]>>24) & 0xFF);
       
   227   }
       
   228 }
       
   229 
       
   230 // This function creates the expanded key from the input (128/192/256-bit)
       
   231 // key. The parameter key is an array of bytes holding the value of the key.
       
   232 // The returned value is an array whose elements are the 32-bit words that 
       
   233 // make up the expanded key.
       
   234 
       
   235 function keyExpansion(key) {
       
   236   var expandedKey = new Array();
       
   237   var temp;
       
   238 
       
   239   // in case the key size or parameters were changed...
       
   240   Nk = keySizeInBits / 32;                   
       
   241   Nb = blockSizeInBits / 32;
       
   242   Nr = roundsArray[Nk][Nb];
       
   243 
       
   244   for (var j=0; j < Nk; j++)     // Fill in input key first
       
   245     expandedKey[j] = 
       
   246       (key[4*j]) | (key[4*j+1]<<8) | (key[4*j+2]<<16) | (key[4*j+3]<<24);
       
   247 
       
   248   // Now walk down the rest of the array filling in expanded key bytes as
       
   249   // per Rijndael's spec
       
   250   for (j = Nk; j < Nb * (Nr + 1); j++) {    // For each word of expanded key
       
   251     temp = expandedKey[j - 1];
       
   252     if (j % Nk == 0) 
       
   253       temp = ( (SBox[(temp>>8) & 0xFF]) |
       
   254                (SBox[(temp>>16) & 0xFF]<<8) |
       
   255                (SBox[(temp>>24) & 0xFF]<<16) |
       
   256                (SBox[temp & 0xFF]<<24) ) ^ Rcon[Math.floor(j / Nk) - 1];
       
   257     else if (Nk > 6 && j % Nk == 4)
       
   258       temp = (SBox[(temp>>24) & 0xFF]<<24) |
       
   259              (SBox[(temp>>16) & 0xFF]<<16) |
       
   260              (SBox[(temp>>8) & 0xFF]<<8) |
       
   261              (SBox[temp & 0xFF]);
       
   262     expandedKey[j] = expandedKey[j-Nk] ^ temp;
       
   263   }
       
   264   return expandedKey;
       
   265 }
       
   266 
       
   267 // Rijndael's round functions... 
       
   268 
       
   269 function Round(state, roundKey) {
       
   270   byteSub(state, "encrypt");
       
   271   shiftRow(state, "encrypt");
       
   272   mixColumn(state, "encrypt");
       
   273   addRoundKey(state, roundKey);
       
   274 }
       
   275 
       
   276 function InverseRound(state, roundKey) {
       
   277   addRoundKey(state, roundKey);
       
   278   mixColumn(state, "decrypt");
       
   279   shiftRow(state, "decrypt");
       
   280   byteSub(state, "decrypt");
       
   281 }
       
   282 
       
   283 function FinalRound(state, roundKey) {
       
   284   byteSub(state, "encrypt");
       
   285   shiftRow(state, "encrypt");
       
   286   addRoundKey(state, roundKey);
       
   287 }
       
   288 
       
   289 function InverseFinalRound(state, roundKey){
       
   290   addRoundKey(state, roundKey);
       
   291   shiftRow(state, "decrypt");
       
   292   byteSub(state, "decrypt");  
       
   293 }
       
   294 
       
   295 // encrypt is the basic encryption function. It takes parameters
       
   296 // block, an array of bytes representing a plaintext block, and expandedKey,
       
   297 // an array of words representing the expanded key previously returned by
       
   298 // keyExpansion(). The ciphertext block is returned as an array of bytes.
       
   299 
       
   300 function encrypt(block, expandedKey) {
       
   301   var i;  
       
   302   if (!block || block.length*8 != blockSizeInBits)
       
   303      return; 
       
   304   if (!expandedKey)
       
   305      return;
       
   306 
       
   307   block = packBytes(block);
       
   308   addRoundKey(block, expandedKey);
       
   309   for (i=1; i<Nr; i++) 
       
   310     Round(block, expandedKey.slice(Nb*i, Nb*(i+1)));
       
   311   FinalRound(block, expandedKey.slice(Nb*Nr)); 
       
   312   return unpackBytes(block);
       
   313 }
       
   314 
       
   315 // decrypt is the basic decryption function. It takes parameters
       
   316 // block, an array of bytes representing a ciphertext block, and expandedKey,
       
   317 // an array of words representing the expanded key previously returned by
       
   318 // keyExpansion(). The decrypted block is returned as an array of bytes.
       
   319 
       
   320 function decrypt(block, expandedKey) {
       
   321   var i;
       
   322   if (!block || block.length*8 != blockSizeInBits)
       
   323      return;
       
   324   if (!expandedKey)
       
   325      return;
       
   326 
       
   327   block = packBytes(block);
       
   328   InverseFinalRound(block, expandedKey.slice(Nb*Nr)); 
       
   329   for (i = Nr - 1; i>0; i--) 
       
   330     InverseRound(block, expandedKey.slice(Nb*i, Nb*(i+1)));
       
   331   addRoundKey(block, expandedKey);
       
   332   return unpackBytes(block);
       
   333 }
       
   334 
       
   335 // This method takes a byte array (byteArray) and converts it to a string by
       
   336 // applying String.fromCharCode() to each value and concatenating the result.
       
   337 // The resulting string is returned. Note that this function SKIPS zero bytes
       
   338 // under the assumption that they are padding added in formatPlaintext().
       
   339 // Obviously, do not invoke this method on raw data that can contain zero
       
   340 // bytes. It is really only appropriate for printable ASCII/Latin-1 
       
   341 // values. Roll your own function for more robust functionality :)
       
   342 
       
   343 function byteArrayToString(byteArray) {
       
   344   var result = "";
       
   345   for(var i=0; i<byteArray.length; i++)
       
   346     if (byteArray[i] != 0) 
       
   347       result += String.fromCharCode(byteArray[i]);
       
   348   return result;
       
   349 }
       
   350 
       
   351 // This function takes an array of bytes (byteArray) and converts them
       
   352 // to a hexadecimal string. Array element 0 is found at the beginning of 
       
   353 // the resulting string, high nibble first. Consecutive elements follow
       
   354 // similarly, for example [16, 255] --> "10ff". The function returns a 
       
   355 // string.
       
   356 
       
   357 function byteArrayToHex(byteArray) {
       
   358   var result = "";
       
   359   if (!byteArray)
       
   360     return;
       
   361   for (var i=0; i<byteArray.length; i++)
       
   362     result += ((byteArray[i]<16) ? "0" : "") + byteArray[i].toString(16);
       
   363 
       
   364   return result;
       
   365 }
       
   366 
       
   367 // This function converts a string containing hexadecimal digits to an 
       
   368 // array of bytes. The resulting byte array is filled in the order the
       
   369 // values occur in the string, for example "10FF" --> [16, 255]. This
       
   370 // function returns an array. 
       
   371 
       
   372 function hexToByteArray(hexString) {
       
   373   /*
       
   374   var byteArray = [];
       
   375   if (hexString.length % 2)             // must have even length
       
   376     return;
       
   377   if (hexString.indexOf("0x") == 0 || hexString.indexOf("0X") == 0)
       
   378     hexString = hexString.substring(2);
       
   379   for (var i = 0; i<hexString.length; i += 2) 
       
   380     byteArray[Math.floor(i/2)] = parseInt(hexString.slice(i, i+2), 16);
       
   381   return byteArray;
       
   382   */
       
   383   var bytes = new Array();
       
   384   hexString = str_split(hexString, 2);
       
   385   //alert(hexString.toString());
       
   386   //return false;
       
   387   for( var i in hexString )
       
   388   {
       
   389     bytes[bytes.length] = parseInt(hexString[i], 16);
       
   390   }
       
   391   //alert(bytes.toString());
       
   392   return bytes;
       
   393 }
       
   394 
       
   395 // This function packs an array of bytes into the four row form defined by
       
   396 // Rijndael. It assumes the length of the array of bytes is divisible by
       
   397 // four. Bytes are filled in according to the Rijndael spec (starting with
       
   398 // column 0, row 0 to 3). This function returns a 2d array.
       
   399 
       
   400 function packBytes(octets) {
       
   401   var state = new Array();
       
   402   if (!octets || octets.length % 4)
       
   403     return;
       
   404 
       
   405   state[0] = new Array();  state[1] = new Array(); 
       
   406   state[2] = new Array();  state[3] = new Array();
       
   407   for (var j=0; j<octets.length; j+= 4) {
       
   408      state[0][j/4] = octets[j];
       
   409      state[1][j/4] = octets[j+1];
       
   410      state[2][j/4] = octets[j+2];
       
   411      state[3][j/4] = octets[j+3];
       
   412   }
       
   413   return state;  
       
   414 }
       
   415 
       
   416 // This function unpacks an array of bytes from the four row format preferred
       
   417 // by Rijndael into a single 1d array of bytes. It assumes the input "packed"
       
   418 // is a packed array. Bytes are filled in according to the Rijndael spec. 
       
   419 // This function returns a 1d array of bytes.
       
   420 
       
   421 function unpackBytes(packed) {
       
   422   var result = new Array();
       
   423   for (var j=0; j<packed[0].length; j++) {
       
   424     result[result.length] = packed[0][j];
       
   425     result[result.length] = packed[1][j];
       
   426     result[result.length] = packed[2][j];
       
   427     result[result.length] = packed[3][j];
       
   428   }
       
   429   return result;
       
   430 }
       
   431 
       
   432 // This function takes a prospective plaintext (string or array of bytes)
       
   433 // and pads it with zero bytes if its length is not a multiple of the block 
       
   434 // size. If plaintext is a string, it is converted to an array of bytes
       
   435 // in the process. The type checking can be made much nicer using the 
       
   436 // instanceof operator, but this operator is not available until IE5.0 so I 
       
   437 // chose to use the heuristic below. 
       
   438 
       
   439 function formatPlaintext(plaintext) {
       
   440   var bpb = blockSizeInBits / 8;               // bytes per block
       
   441   var i;
       
   442 
       
   443   // if primitive string or String instance
       
   444   if (typeof plaintext == "string" || plaintext.split) {
       
   445     // alert('AUUGH you idiot it\'s NOT A STRING ITS A '+typeof(plaintext)+'!!!');
       
   446     // return false;
       
   447     plaintext = plaintext.split("");
       
   448     // Unicode issues here (ignoring high byte)
       
   449     for (i=0; i<plaintext.length; i++)
       
   450       plaintext[i] = plaintext[i].charCodeAt(0) & 0xFF;
       
   451   } 
       
   452 
       
   453   for (i = bpb - (plaintext.length % bpb); i > 0 && i < bpb; i--) 
       
   454     plaintext[plaintext.length] = 0;
       
   455   
       
   456   return plaintext;
       
   457 }
       
   458 
       
   459 // Returns an array containing "howMany" random bytes. YOU SHOULD CHANGE THIS
       
   460 // TO RETURN HIGHER QUALITY RANDOM BYTES IF YOU ARE USING THIS FOR A "REAL"
       
   461 // APPLICATION.
       
   462 
       
   463 function getRandomBytes(howMany) {
       
   464   var i;
       
   465   var bytes = new Array();
       
   466   for (i=0; i<howMany; i++)
       
   467     bytes[i] = Math.round(Math.random()*255);
       
   468   return bytes;
       
   469 }
       
   470 
       
   471 // rijndaelEncrypt(plaintext, key, mode)
       
   472 // Encrypts the plaintext using the given key and in the given mode. 
       
   473 // The parameter "plaintext" can either be a string or an array of bytes. 
       
   474 // The parameter "key" must be an array of key bytes. If you have a hex 
       
   475 // string representing the key, invoke hexToByteArray() on it to convert it 
       
   476 // to an array of bytes. The third parameter "mode" is a string indicating
       
   477 // the encryption mode to use, either "ECB" or "CBC". If the parameter is
       
   478 // omitted, ECB is assumed.
       
   479 // 
       
   480 // An array of bytes representing the cihpertext is returned. To convert 
       
   481 // this array to hex, invoke byteArrayToHex() on it. If you are using this 
       
   482 // "for real" it is a good idea to change the function getRandomBytes() to 
       
   483 // something that returns truly random bits.
       
   484 
       
   485 function rijndaelEncrypt(plaintext, key, mode) {
       
   486   var expandedKey, i, aBlock;
       
   487   var bpb = blockSizeInBits / 8;          // bytes per block
       
   488   var ct;                                 // ciphertext
       
   489 
       
   490   if (typeof plaintext != 'object' || typeof key != 'object')
       
   491   {
       
   492     alert( 'Invalid params\nplaintext: '+typeof(plaintext)+'\nkey: '+typeof(key) );
       
   493     return false;
       
   494   }
       
   495   if (key.length*8 == keySizeInBits+8)
       
   496     key.length = keySizeInBits / 8;
       
   497   if (key.length*8 != keySizeInBits)
       
   498   {
       
   499     alert( 'Key length is bad!\nLength: '+key.length+'\nExpected: '+keySizeInBits / 8 );
       
   500     return false;
       
   501   }
       
   502   if (mode == "CBC")
       
   503     ct = getRandomBytes(bpb);             // get IV
       
   504   else {
       
   505     mode = "ECB";
       
   506     ct = new Array();
       
   507   }
       
   508 
       
   509   // convert plaintext to byte array and pad with zeros if necessary. 
       
   510   plaintext = formatPlaintext(plaintext);
       
   511 
       
   512   expandedKey = keyExpansion(key);
       
   513   
       
   514   for (var block=0; block<plaintext.length / bpb; block++) {
       
   515     aBlock = plaintext.slice(block*bpb, (block+1)*bpb);
       
   516     if (mode == "CBC")
       
   517       for (var i=0; i<bpb; i++) 
       
   518         aBlock[i] ^= ct[block*bpb + i];
       
   519     ct = ct.concat(encrypt(aBlock, expandedKey));
       
   520   }
       
   521 
       
   522   return ct;
       
   523 }
       
   524 
       
   525 // rijndaelDecrypt(ciphertext, key, mode)
       
   526 // Decrypts the using the given key and mode. The parameter "ciphertext" 
       
   527 // must be an array of bytes. The parameter "key" must be an array of key 
       
   528 // bytes. If you have a hex string representing the ciphertext or key, 
       
   529 // invoke hexToByteArray() on it to convert it to an array of bytes. The
       
   530 // parameter "mode" is a string, either "CBC" or "ECB".
       
   531 // 
       
   532 // An array of bytes representing the plaintext is returned. To convert 
       
   533 // this array to a hex string, invoke byteArrayToHex() on it. To convert it 
       
   534 // to a string of characters, you can use byteArrayToString().
       
   535 
       
   536 function rijndaelDecrypt(ciphertext, key, mode) {
       
   537   var expandedKey;
       
   538   var bpb = blockSizeInBits / 8;          // bytes per block
       
   539   var pt = new Array();                   // plaintext array
       
   540   var aBlock;                             // a decrypted block
       
   541   var block;                              // current block number
       
   542 
       
   543   if (!ciphertext || !key || typeof ciphertext == "string")
       
   544     return;
       
   545   if (key.length*8 != keySizeInBits)
       
   546     return; 
       
   547   if (!mode)
       
   548     mode = "ECB";                         // assume ECB if mode omitted
       
   549 
       
   550   expandedKey = keyExpansion(key);
       
   551  
       
   552   // work backwards to accomodate CBC mode 
       
   553   for (block=(ciphertext.length / bpb)-1; block>0; block--) {
       
   554     aBlock = 
       
   555      decrypt(ciphertext.slice(block*bpb,(block+1)*bpb), expandedKey);
       
   556     if (mode == "CBC") 
       
   557       for (var i=0; i<bpb; i++) 
       
   558         pt[(block-1)*bpb + i] = aBlock[i] ^ ciphertext[(block-1)*bpb + i];
       
   559     else 
       
   560       pt = aBlock.concat(pt);
       
   561   }
       
   562 
       
   563   // do last block if ECB (skips the IV in CBC)
       
   564   if (mode == "ECB")
       
   565     pt = decrypt(ciphertext.slice(0, bpb), expandedKey).concat(pt);
       
   566 
       
   567   return pt;
       
   568 }
       
   569 
       
   570 function stringToByteArray(text)
       
   571 {
       
   572   result = new Array();
       
   573   for ( i=0; i<text.length; i++ )
       
   574   {
       
   575     result[result.length] = text.charCodeAt(i);
       
   576   }
       
   577   return result;
       
   578 }
       
   579