author | Dan |
Mon, 11 Feb 2008 13:55:14 -0500 | |
changeset 409 | 267be9e394c2 |
parent 348 | 87e08a6e4fec |
permissions | -rw-r--r-- |
1 | 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 |
||
348
87e08a6e4fec
Welcome to the new Enano installer. Much distance still to be covered but the basics are there.
Dan
parents:
1
diff
changeset
|
580 |
function aes_self_test() |
87e08a6e4fec
Welcome to the new Enano installer. Much distance still to be covered but the basics are there.
Dan
parents:
1
diff
changeset
|
581 |
{ |
87e08a6e4fec
Welcome to the new Enano installer. Much distance still to be covered but the basics are there.
Dan
parents:
1
diff
changeset
|
582 |
// |
87e08a6e4fec
Welcome to the new Enano installer. Much distance still to be covered but the basics are there.
Dan
parents:
1
diff
changeset
|
583 |
// Encryption test |
87e08a6e4fec
Welcome to the new Enano installer. Much distance still to be covered but the basics are there.
Dan
parents:
1
diff
changeset
|
584 |
// |
87e08a6e4fec
Welcome to the new Enano installer. Much distance still to be covered but the basics are there.
Dan
parents:
1
diff
changeset
|
585 |
|
87e08a6e4fec
Welcome to the new Enano installer. Much distance still to be covered but the basics are there.
Dan
parents:
1
diff
changeset
|
586 |
var str = ''; |
87e08a6e4fec
Welcome to the new Enano installer. Much distance still to be covered but the basics are there.
Dan
parents:
1
diff
changeset
|
587 |
for(i=0;i<keySizeInBits/4;i++) |
87e08a6e4fec
Welcome to the new Enano installer. Much distance still to be covered but the basics are there.
Dan
parents:
1
diff
changeset
|
588 |
{ |
87e08a6e4fec
Welcome to the new Enano installer. Much distance still to be covered but the basics are there.
Dan
parents:
1
diff
changeset
|
589 |
str+='0'; |
87e08a6e4fec
Welcome to the new Enano installer. Much distance still to be covered but the basics are there.
Dan
parents:
1
diff
changeset
|
590 |
} |
87e08a6e4fec
Welcome to the new Enano installer. Much distance still to be covered but the basics are there.
Dan
parents:
1
diff
changeset
|
591 |
str = hexToByteArray(str); |
87e08a6e4fec
Welcome to the new Enano installer. Much distance still to be covered but the basics are there.
Dan
parents:
1
diff
changeset
|
592 |
var ct = rijndaelEncrypt(str, str, 'ECB'); |
87e08a6e4fec
Welcome to the new Enano installer. Much distance still to be covered but the basics are there.
Dan
parents:
1
diff
changeset
|
593 |
ct = byteArrayToHex(ct); |
87e08a6e4fec
Welcome to the new Enano installer. Much distance still to be covered but the basics are there.
Dan
parents:
1
diff
changeset
|
594 |
var v; |
87e08a6e4fec
Welcome to the new Enano installer. Much distance still to be covered but the basics are there.
Dan
parents:
1
diff
changeset
|
595 |
switch(keySizeInBits) |
87e08a6e4fec
Welcome to the new Enano installer. Much distance still to be covered but the basics are there.
Dan
parents:
1
diff
changeset
|
596 |
{ |
87e08a6e4fec
Welcome to the new Enano installer. Much distance still to be covered but the basics are there.
Dan
parents:
1
diff
changeset
|
597 |
// These test vectors are for 128-bit block size. |
87e08a6e4fec
Welcome to the new Enano installer. Much distance still to be covered but the basics are there.
Dan
parents:
1
diff
changeset
|
598 |
case 128: |
87e08a6e4fec
Welcome to the new Enano installer. Much distance still to be covered but the basics are there.
Dan
parents:
1
diff
changeset
|
599 |
v = '66e94bd4ef8a2c3b884cfa59ca342b2e'; |
87e08a6e4fec
Welcome to the new Enano installer. Much distance still to be covered but the basics are there.
Dan
parents:
1
diff
changeset
|
600 |
break; |
87e08a6e4fec
Welcome to the new Enano installer. Much distance still to be covered but the basics are there.
Dan
parents:
1
diff
changeset
|
601 |
case 192: |
87e08a6e4fec
Welcome to the new Enano installer. Much distance still to be covered but the basics are there.
Dan
parents:
1
diff
changeset
|
602 |
v = 'aae06992acbf52a3e8f4a96ec9300bd7aae06992acbf52a3e8f4a96ec9300bd7'; |
87e08a6e4fec
Welcome to the new Enano installer. Much distance still to be covered but the basics are there.
Dan
parents:
1
diff
changeset
|
603 |
break; |
87e08a6e4fec
Welcome to the new Enano installer. Much distance still to be covered but the basics are there.
Dan
parents:
1
diff
changeset
|
604 |
case 256: |
87e08a6e4fec
Welcome to the new Enano installer. Much distance still to be covered but the basics are there.
Dan
parents:
1
diff
changeset
|
605 |
v = 'dc95c078a2408989ad48a21492842087dc95c078a2408989ad48a21492842087'; |
87e08a6e4fec
Welcome to the new Enano installer. Much distance still to be covered but the basics are there.
Dan
parents:
1
diff
changeset
|
606 |
break; |
87e08a6e4fec
Welcome to the new Enano installer. Much distance still to be covered but the basics are there.
Dan
parents:
1
diff
changeset
|
607 |
} |
87e08a6e4fec
Welcome to the new Enano installer. Much distance still to be covered but the basics are there.
Dan
parents:
1
diff
changeset
|
608 |
return ( ct == v && md5_vm_test() ); |
87e08a6e4fec
Welcome to the new Enano installer. Much distance still to be covered but the basics are there.
Dan
parents:
1
diff
changeset
|
609 |
} |
87e08a6e4fec
Welcome to the new Enano installer. Much distance still to be covered but the basics are there.
Dan
parents:
1
diff
changeset
|
610 |