1 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
4 * AES Cipher function: encrypt 'input' with Rijndael algorithm
6 * takes byte-array 'input' (16 bytes)
7 * 2D byte-array key schedule 'w' (Nr+1 x Nb bytes)
9 * applies Nr rounds (10/12/14) using key schedule w for 'add round key' stage
11 * returns byte-array encrypted value (16 bytes)
13 function Cipher(input, w) { // main Cipher function [§5.1]
14 var Nb = 4; // block size (in words): no of columns in state (fixed at 4 for AES)
15 var Nr = w.length/Nb - 1; // no of rounds: 10/12/14 for 128/192/256-bit keys
17 var state = [[],[],[],[]]; // initialise 4xNb byte-array 'state' with input [§3.4]
19 for (var i=0; i<4*Nb; i++) state[i%4][Math.floor(i/4)] = input[i];
22 state = AddRoundKey(state, w, 0, Nb);
25 for (var round=1; round<Nr; round++) {
26 state = SubBytes(state, Nb);
27 state = ShiftRows(state, Nb);
28 state = MixColumns(state, Nb);
29 state = AddRoundKey(state, w, round, Nb);
33 state = SubBytes(state, Nb);
34 state = ShiftRows(state, Nb);
35 state = AddRoundKey(state, w, Nr, Nb);
37 var output = new Array(4*Nb); // convert state to 1-d array before returning [§3.4]
39 for (var i=0; i<4*Nb; i++) output[i] = state[i%4][Math.floor(i/4)];
45 function SubBytes(s, Nb) { // apply SBox to state S [§5.1.1]
47 for (var r=0; r<4; r++) {
49 for (var c=0; c<Nb; c++) s[r][c] = Sbox[s[r][c]];
57 function ShiftRows(s, Nb) { // shift row r of state S left by r bytes [§5.1.2]
60 for (var r=1; r<4; r++) {
62 for (var c=0; c<4; c++) t[c] = s[r][(c+r)%Nb]; // shift into temp copy
65 for (var c=0; c<4; c++) s[r][c] = t[c]; // and copy back
67 } // note that this will work for Nb=4,5,6, but not 7,8 (always 4 for AES):
69 return s; // see fp.gladman.plus.com/cryptography_technology/rijndael/aes.spec.311.pdf
73 function MixColumns(s, Nb) { // combine bytes of each col of state S [§5.1.3]
75 for (var c=0; c<4; c++) {
76 var a = new Array(4); // 'a' is a copy of the current column from 's'
77 var b = new Array(4); // 'b' is a•{02} in GF(2^8)
79 for (var i=0; i<4; i++) {
81 b[i] = s[i][c]&0x80 ? s[i][c]<<1 ^ 0x011b : s[i][c]<<1;
84 // a[n] ^ b[n] is a•{03} in GF(2^8)
85 s[0][c] = b[0] ^ a[1] ^ b[1] ^ a[2] ^ a[3]; // 2*a0 + 3*a1 + a2 + a3
86 s[1][c] = a[0] ^ b[1] ^ a[2] ^ b[2] ^ a[3]; // a0 * 2*a1 + 3*a2 + a3
87 s[2][c] = a[0] ^ a[1] ^ b[2] ^ a[3] ^ b[3]; // a0 + a1 + 2*a2 + 3*a3
88 s[3][c] = a[0] ^ b[0] ^ a[1] ^ a[2] ^ b[3]; // 3*a0 + a1 + a2 + 2*a3
95 function AddRoundKey(state, w, rnd, Nb) { // xor Round Key into state S [§5.1.4]
97 for (var r=0; r<4; r++) {
99 for (var c=0; c<Nb; c++) state[r][c] ^= w[rnd*4+c][r];
107 function KeyExpansion(key) { // generate Key Schedule (byte-array Nr+1 x Nb) from Key [§5.2]
108 var Nb = 4; // block size (in words): no of columns in state (fixed at 4 for AES)
109 var Nk = key.length/4 // key length (in words): 4/6/8 for 128/192/256-bit keys
110 var Nr = Nk + 6; // no of rounds: 10/12/14 for 128/192/256-bit keys
112 var w = new Array(Nb*(Nr+1));
113 var temp = new Array(4);
116 for (var i=0; i<Nk; i++) {
117 var r = [key[4*i], key[4*i+1], key[4*i+2], key[4*i+3]];
123 for (var i=Nk; i<(Nb*(Nr+1)); i++) {
126 for (var t=0; t<4; t++) temp[t] = w[i-1][t];
129 temp = SubWord(RotWord(temp));
131 for (var t=0; t<4; t++) temp[t] ^= Rcon[i/Nk][t];
133 } else if (Nk > 6 && i%Nk == 4) {
134 temp = SubWord(temp);
137 for (var t=0; t<4; t++) w[i][t] = w[i-Nk][t] ^ temp[t];
145 function SubWord(w) { // apply SBox to 4-byte word w
147 for (var i=0; i<4; i++) w[i] = Sbox[w[i]];
152 function RotWord(w) { // rotate 4-byte word w left by one byte
155 for (var i=0; i<4; i++) w[i] = w[i+1];
161 // Sbox is pre-computed multiplicative inverse in GF(2^8) used in SubBytes and KeyExpansion [§5.1.1]
162 var Sbox = [0x63,0x7c,0x77,0x7b,0xf2,0x6b,0x6f,0xc5,0x30,0x01,0x67,0x2b,0xfe,0xd7,0xab,0x76,
163 0xca,0x82,0xc9,0x7d,0xfa,0x59,0x47,0xf0,0xad,0xd4,0xa2,0xaf,0x9c,0xa4,0x72,0xc0,
164 0xb7,0xfd,0x93,0x26,0x36,0x3f,0xf7,0xcc,0x34,0xa5,0xe5,0xf1,0x71,0xd8,0x31,0x15,
165 0x04,0xc7,0x23,0xc3,0x18,0x96,0x05,0x9a,0x07,0x12,0x80,0xe2,0xeb,0x27,0xb2,0x75,
166 0x09,0x83,0x2c,0x1a,0x1b,0x6e,0x5a,0xa0,0x52,0x3b,0xd6,0xb3,0x29,0xe3,0x2f,0x84,
167 0x53,0xd1,0x00,0xed,0x20,0xfc,0xb1,0x5b,0x6a,0xcb,0xbe,0x39,0x4a,0x4c,0x58,0xcf,
168 0xd0,0xef,0xaa,0xfb,0x43,0x4d,0x33,0x85,0x45,0xf9,0x02,0x7f,0x50,0x3c,0x9f,0xa8,
169 0x51,0xa3,0x40,0x8f,0x92,0x9d,0x38,0xf5,0xbc,0xb6,0xda,0x21,0x10,0xff,0xf3,0xd2,
170 0xcd,0x0c,0x13,0xec,0x5f,0x97,0x44,0x17,0xc4,0xa7,0x7e,0x3d,0x64,0x5d,0x19,0x73,
171 0x60,0x81,0x4f,0xdc,0x22,0x2a,0x90,0x88,0x46,0xee,0xb8,0x14,0xde,0x5e,0x0b,0xdb,
172 0xe0,0x32,0x3a,0x0a,0x49,0x06,0x24,0x5c,0xc2,0xd3,0xac,0x62,0x91,0x95,0xe4,0x79,
173 0xe7,0xc8,0x37,0x6d,0x8d,0xd5,0x4e,0xa9,0x6c,0x56,0xf4,0xea,0x65,0x7a,0xae,0x08,
174 0xba,0x78,0x25,0x2e,0x1c,0xa6,0xb4,0xc6,0xe8,0xdd,0x74,0x1f,0x4b,0xbd,0x8b,0x8a,
175 0x70,0x3e,0xb5,0x66,0x48,0x03,0xf6,0x0e,0x61,0x35,0x57,0xb9,0x86,0xc1,0x1d,0x9e,
176 0xe1,0xf8,0x98,0x11,0x69,0xd9,0x8e,0x94,0x9b,0x1e,0x87,0xe9,0xce,0x55,0x28,0xdf,
177 0x8c,0xa1,0x89,0x0d,0xbf,0xe6,0x42,0x68,0x41,0x99,0x2d,0x0f,0xb0,0x54,0xbb,0x16];
179 // Rcon is Round Constant used for the Key Expansion [1st col is 2^(r-1) in GF(2^8)] [§5.2]
180 var Rcon = [ [0x00, 0x00, 0x00, 0x00],
181 [0x01, 0x00, 0x00, 0x00],
182 [0x02, 0x00, 0x00, 0x00],
183 [0x04, 0x00, 0x00, 0x00],
184 [0x08, 0x00, 0x00, 0x00],
185 [0x10, 0x00, 0x00, 0x00],
186 [0x20, 0x00, 0x00, 0x00],
187 [0x40, 0x00, 0x00, 0x00],
188 [0x80, 0x00, 0x00, 0x00],
189 [0x1b, 0x00, 0x00, 0x00],
190 [0x36, 0x00, 0x00, 0x00] ];
193 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
196 * Use AES to encrypt 'plaintext' with 'password' using 'nBits' key, in 'Counter' mode of operation
197 * - see http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf
199 * - outputblock = cipher(counter, key)
200 * - cipherblock = plaintext xor outputblock
202 function AESEncryptCtr(plaintext, password, nBits) {
203 if (!(nBits==128 || nBits==192 || nBits==256)) return ''; // standard allows 128/192/256 bit keys
205 // for this example script, generate the key by applying Cipher to 1st 16/24/32 chars of password;
206 // for real-world applications, a more secure approach would be to hash the password e.g. with SHA-1
207 var nBytes = nBits/8; // no bytes in key
208 var pwBytes = new Array(nBytes);
210 for (var i=0; i<nBytes; i++) pwBytes[i] = password.charCodeAt(i) & 0xff;
212 var key = Cipher(pwBytes, KeyExpansion(pwBytes));
213 key = key.concat(key.slice(0, nBytes-16)); // key is now 16/24/32 bytes long
215 // initialise counter block (NIST SP800-38A §B.2): millisecond time-stamp for nonce in 1st 8 bytes,
216 // block counter in 2nd 8 bytes
217 var blockSize = 16; // block size fixed at 16 bytes / 128 bits (Nb=4) for AES
218 var counterBlock = new Array(blockSize); // block size fixed at 16 bytes / 128 bits (Nb=4) for AES
219 var nonce = (new Date()).getTime(); // milliseconds since 1-Jan-1970
221 // encode nonce in two stages to cater for JavaScript 32-bit limit on bitwise ops
223 for (var i=0; i<4; i++) counterBlock[i] = (nonce >>> i*8) & 0xff;
226 for (var i=0; i<4; i++) counterBlock[i+4] = (nonce/0x100000000 >>> i*8) & 0xff;
229 // generate key schedule - an expansion of the key into distinct Key Rounds for each round
230 var keySchedule = KeyExpansion(key);
232 var blockCount = Math.ceil(plaintext.length/blockSize);
233 var ciphertext = new Array(blockCount); // ciphertext as array of strings
236 for (var b=0; b<blockCount; b++) {
237 // set counter (block #) in last 8 bytes of counter block (leaving nonce in 1st 8 bytes)
238 // again done in two stages for 32-bit ops
240 for (var c=0; c<4; c++) counterBlock[15-c] = (b >>> c*8) & 0xff;
243 for (var c=0; c<4; c++) counterBlock[15-c-4] = (b/0x100000000 >>> c*8)
246 var cipherCntr = Cipher(counterBlock, keySchedule); // -- encrypt counter block --
248 // calculate length of final block:
249 var blockLength = b<blockCount-1 ? blockSize : (plaintext.length-1)%blockSize+1;
253 for (var i=0; i<blockLength; i++) { // -- xor plaintext with ciphered counter byte-by-byte --
254 var plaintextByte = plaintext.charCodeAt(b*blockSize+i);
255 var cipherByte = plaintextByte ^ cipherCntr[i];
256 ct += String.fromCharCode(cipherByte);
259 // ct is now ciphertext for this block
261 ciphertext[b] = escCtrlChars(ct); // escape troublesome characters in ciphertext
265 // convert the nonce to a string to go on the front of the ciphertext
268 for (var i=0; i<8; i++) ctrTxt += String.fromCharCode(counterBlock[i]);
270 ctrTxt = escCtrlChars(ctrTxt);
272 // use '-' to separate blocks, use Array.join to concatenate arrays of strings for efficiency
273 return ctrTxt + '-' + ciphertext.join('-');
278 * Use AES to decrypt 'ciphertext' with 'password' using 'nBits' key, in Counter mode of operation
281 * - outputblock = cipher(counter, key)
282 * - cipherblock = plaintext xor outputblock
284 function AESDecryptCtr(ciphertext, password, nBits) {
285 if (!(nBits==128 || nBits==192 || nBits==256)) return ''; // standard allows 128/192/256 bit keys
287 var nBytes = nBits/8; // no bytes in key
288 var pwBytes = new Array(nBytes);
290 for (var i=0; i<nBytes; i++) pwBytes[i] = password.charCodeAt(i) & 0xff;
292 var pwKeySchedule = KeyExpansion(pwBytes);
293 var key = Cipher(pwBytes, pwKeySchedule);
294 key = key.concat(key.slice(0, nBytes-16)); // key is now 16/24/32 bytes long
296 var keySchedule = KeyExpansion(key);
298 ciphertext = ciphertext.split('-'); // split ciphertext into array of block-length strings
300 // recover nonce from 1st element of ciphertext
301 var blockSize = 16; // block size fixed at 16 bytes / 128 bits (Nb=4) for AES
302 var counterBlock = new Array(blockSize);
303 var ctrTxt = unescCtrlChars(ciphertext[0]);
305 for (var i=0; i<8; i++) counterBlock[i] = ctrTxt.charCodeAt(i);
308 var plaintext = new Array(ciphertext.length-1);
311 for (var b=1; b<ciphertext.length; b++) {
312 // set counter (block #) in last 8 bytes of counter block (leaving nonce in 1st 8 bytes)
314 for (var c=0; c<4; c++) counterBlock[15-c] = ((b-1) >>> c*8) & 0xff;
317 for (var c=0; c<4; c++) counterBlock[15-c-4] = ((b/0x100000000-1) >>> c*8) & 0xff;
320 var cipherCntr = Cipher(counterBlock, keySchedule); // encrypt counter block
322 ciphertext[b] = unescCtrlChars(ciphertext[b]);
326 for (var i=0; i<ciphertext[b].length; i++) {
327 // -- xor plaintext with ciphered counter byte-by-byte --
328 var ciphertextByte = ciphertext[b].charCodeAt(i);
329 var plaintextByte = ciphertextByte ^ cipherCntr[i];
330 pt += String.fromCharCode(plaintextByte);
333 // pt is now plaintext for this block
335 plaintext[b-1] = pt; // b-1 'cos no initial nonce block in plaintext
339 return plaintext.join('');
342 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
344 function escCtrlChars(str) { // escape control chars which might cause problems handling ciphertext
345 return str.replace(/[\0\t\n\v\f\r\xa0'"!-]/g, function(c) { return '!' + c.charCodeAt(0) + '!'; });
346 } // \xa0 to cater for bug in Firefox; include '-' to leave it free for use as a block marker
348 function unescCtrlChars(str) { // unescape potentially problematic control characters
349 return str.replace(/!\d\d?\d?!/g, function(c) { return String.fromCharCode(c.slice(1,-1)); });
351 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
354 * if escCtrlChars()/unescCtrlChars() still gives problems, use encodeBase64()/decodeBase64() instead
356 var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
358 function encodeBase64(str) { // http://tools.ietf.org/html/rfc4648
359 var o1, o2, o3, h1, h2, h3, h4, bits, i=0, enc='';
361 str = encodeUTF8(str); // encode multi-byte chars into UTF-8 for byte-array
364 do { // pack three octets into four hexets
365 o1 = str.charCodeAt(i++);
366 o2 = str.charCodeAt(i++);
367 o3 = str.charCodeAt(i++);
369 bits = o1<<16 | o2<<8 | o3;
371 h1 = bits>>18 & 0x3f;
372 h2 = bits>>12 & 0x3f;
376 // end of string? index to '=' in b64
377 if (isNaN(o3)) h4 = 64;
378 if (isNaN(o2)) h3 = 64;
380 // use hexets to index into b64, and append result to encoded string
381 enc += b64.charAt(h1) + b64.charAt(h2) + b64.charAt(h3) + b64.charAt(h4);
382 } while (i < str.length);
388 function decodeBase64(str) {
389 var o1, o2, o3, h1, h2, h3, h4, bits, i=0, enc='';
392 do { // unpack four hexets into three octets using index points in b64
393 h1 = b64.indexOf(str.charAt(i++));
394 h2 = b64.indexOf(str.charAt(i++));
395 h3 = b64.indexOf(str.charAt(i++));
396 h4 = b64.indexOf(str.charAt(i++));
398 bits = h1<<18 | h2<<12 | h3<<6 | h4;
400 o1 = bits>>16 & 0xff;
404 if (h3 == 64) enc += String.fromCharCode(o1);
405 else if (h4 == 64) enc += String.fromCharCode(o1, o2);
406 else enc += String.fromCharCode(o1, o2, o3);
407 } while (i < str.length);
410 return decodeUTF8(enc); // decode UTF-8 byte-array back to Unicode
413 function encodeUTF8(str) { // encode multi-byte string into utf-8 multiple single-byte characters
415 /[\u0080-\u07ff]/g, // U+0080 - U+07FF = 2-byte chars
417 var cc = c.charCodeAt(0);
418 return String.fromCharCode(0xc0 | cc>>6, 0x80 | cc&0x3f); }
421 /[\u0800-\uffff]/g, // U+0800 - U+FFFF = 3-byte chars
423 var cc = c.charCodeAt(0);
424 return String.fromCharCode(0xe0 | cc>>12, 0x80 | cc>>6&0x3F, 0x80 | cc&0x3f); }
429 function decodeUTF8(str) { // decode utf-8 encoded string back into multi-byte characters
431 /[\u00c0-\u00df][\u0080-\u00bf]/g, // 2-byte chars
433 var cc = (c.charCodeAt(0)&0x1f)<<6 | c.charCodeAt(1)&0x3f;
434 return String.fromCharCode(cc); }
437 /[\u00e0-\u00ef][\u0080-\u00bf][\u0080-\u00bf]/g, // 3-byte chars
439 var cc = (c.charCodeAt(0)&0x0f)<<12 | (c.charCodeAt(1)&0x3f<<6) | c.charCodeAt(2)&0x3f;
440 return String.fromCharCode(cc); }
446 function byteArrayToHexStr(b) { // convert byte array to hex string for displaying test vectors
449 for (var i=0; i<b.length; i++) s += b[i].toString(16) + ' ';
454 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
457 var plainText = "ROMEO: But, soft! what light through yonder window breaks?\n\
458 It is the east, and Juliet is the sun.\n\
459 Arise, fair sun, and kill the envious moon,\n\
460 Who is already sick and pale with grief,\n\
461 That thou her maid art far more fair than she:\n\
462 Be not her maid, since she is envious;\n\
463 Her vestal livery is but sick and green\n\
464 And none but fools do wear it; cast it off.\n\
465 It is my lady, O, it is my love!\n\
466 O, that she knew she were!\n\
467 She speaks yet she says nothing: what of that?\n\
468 Her eye discourses; I will answer it.\n\
469 I am too bold, 'tis not to me she speaks:\n\
470 Two of the fairest stars in all the heaven,\n\
471 Having some business, do entreat her eyes\n\
472 To twinkle in their spheres till they return.\n\
473 What if her eyes were there, they in her head?\n\
474 The brightness of her cheek would shame those stars,\n\
475 As daylight doth a lamp; her eyes in heaven\n\
476 Would through the airy region stream so bright\n\
477 That birds would sing and think it were not night.\n\
478 See, how she leans her cheek upon her hand!\n\
479 O, that I were a glove upon that hand,\n\
480 That I might touch that cheek!\n\
482 ROMEO: She speaks:\n\
483 O, speak again, bright angel! for thou art\n\
484 As glorious to this night, being o'er my head\n\
485 As is a winged messenger of heaven\n\
486 Unto the white-upturned wondering eyes\n\
487 Of mortals that fall back to gaze on him\n\
488 When he bestrides the lazy-pacing clouds\n\
489 And sails upon the bosom of the air.";
491 var password = "O Romeo, Romeo! wherefore art thou Romeo?";
493 var cipherText = AESEncryptCtr(plainText, password, 256);
494 var decryptedText = AESDecryptCtr(cipherText, password, 256);