|
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 |