**AES** is a ‘symmetric block cipher’ for encrypting texts which can be decrypted with the original
encryption key.

For many purposes, a simpler encryption algorithm such as TEA is perfectly
adequate – but if you suspect the world’s best cryptographic minds, and a few million dollars of
computing resource, might be attempting to crack your security, then AES,
based on the *Rijndael* algorithm, is the tightest security currently available (approved by
the US government for classified information up to ‘Secret’ – and in in 192 or 256 key lengths, up
to ‘Top Secret’). AES was adopted by NIST in 2001 as
FIPS-197, and is the replacement for DES which was
withdrawn in 2005.

I developed this JavaScript implementation to to illustrate the original AES standard (NIST FIPS-197) as closely as possible. It is intended as an introduction for people seeking to learn something about implementing encryption, not an authoritative implementation – cryptography experts will already know more than I present here. The emphasis is on transparency and fidelity to the standard rather than efficiency.

This script also includes a wrapper function which implements AES in the ‘Counter’ mode of operation (specified in NIST SP 800-38A) to encrypt arbitrary texts – many descriptions of AES limit themselves to the Cipher routine itself, and don’t consider how it can be used to encrypt texts.

Much of the Rijndael algorithm is based on arithmetic on a *finite
field*, or *Galois field* (after the mathematician). Regular arithmetic works on
an infinite range of numbers – keep doubling a number and it will get ever bigger. Arithmetic
in a finite field is limited to numbers within that field. The Rijndael algorithm works in GF(2^{8}),
in which arithmetic results can always be stored within one byte – which is pretty convenient
for computers. I can’t begin to understand the maths (considering
that addition and subtraction are the same thing – an xor operation
– and multiplication is performed ‘modulo an irreducible polynomial’: doubling 0x80 in GF(2^{8})
gives 0x1b).

The Rijndael algorithm lends itself to widely differing implementations, since the maths can be either coded directly, or pre-computed as lookup tables – directly parallel to using log tables for arithmetic. Different implementations can have varying pay-offs between speed, complexity, and storage requirements. Some may barely resemble each other. In this implementation, I have followed the standard closely; as per the standard, I have used a lookup table (‘S-box’) to implement the multiplicative inverse (i.e. 1/x) within a finite field (used for the SubBytes transformation), but other calculations are made directly rather than being pre-computed.

If you want to convince yourself that the Cipher function is working properly internally (and you
should!), NIST provide test vectors for AES (appendix C.1 of the standard). Click

and the cipher output block should be

- 128-bit:
*69 c4 e0 d8 6a 7b 04 30 d8 cd b7 80 70 b4 c5 5a* - 192-bit:
*dd a9 7c a4 86 4c df e0 6e af 70 a0 ec 0d 71 91* - 256-bit:
*8e a2 b7 ca 51 67 45 bf ea fc 49 90 4b 49 60 89*

(In counter mode, a text could decrypt correctly even if the cipher routine was flawed).

The Inverse Cipher is largely a mirror of the Cipher routine, with parallel functions for Cipher, SubBytes and ShiftRows. The MixColumns routine is slightly more complex in the inverse. I have not implemented the inverse cipher here as it is not required in counter mode.

**Counter mode of operation**: the AES standard concerns itself with numeric or binary data (Rijndael,
along with most other encryption algorithms, works on a fixed-size block of numbers – in the case
of AES, each block is 128 bits or 16 bytes).

In order to make use of it to encrypt real things (such as texts), it has to be used within a certain ‘mode of operation’. This is the interface between text or files, and the purely numerical encryption algorithm. See NIST Special Publication SP800-38A for more details and test vectors.

The simplest mode of operation (‘electronic codebook’) encrypts a text block-by-block – but since the same block of plaintext will always generate the same block of ciphertext, this can leave too many clues for attackers.

In the ‘counter
mode’ used in this implementation, a counter which changes with each block is first encrypted,
and the result is bitwise xor’d with the plaintext block to
get the ciphertext block (so the plaintext is not actually directly encrypted). A unique ‘nonce’
is incorporated in the counter to ensure different ciphertexts are always generated from the
same plaintext every time it is encrypted; this number is stored at the head of the ciphertext
to enable decryption. A combination of seconds since 1 Jan 1970, a millisecond-timestamp, and
a sub-millisecond random number gives a very effective nonce. (To resist cryptographic attacks,
the nonce does not need to be secret or unpredictable, but it is imperative that it is unique).
In this implementation, the initial block holds the nonce in the first 8 bytes, and the block
count in the second 8 bytes. Since JavaScript can represent integers up to 2^{53},
this allows a message size up to 2^{57} (c.
10^{17})
bytes – unlikely to be a limitation! Note that the nonce in counter mode is the equivalent
of the initialisation vector (IV)
in other modes of operation.

A curious quality of counter mode is that decryption also uses the cipher algorithm rather than the inverse-cipher algorithm. Though simple to implement, it has been established to be very secure.

Encrypting texts or files require not just the mode of operation. When implementing AES, you have to consider

- mode of operation; here the Counter (ctr) mode of operation – both simple to implement, and very secure
- conversion of text (including multi-byte Unicode texts) to binary/numeric data; here multi-byte Unicode characters are converted to UTF8, then the numeric character codes are used to pass to the cipher routine
- conversion of encrypted data to values which can be stored or transmitted without problem; here the binary encrypted texts are encoded in Base64, which is a very safe 7-bit encoding with no control codes or other troublesome characters.

The **key** in this script is obtained by applying the Cipher routine to encrypt the first
16/24/32 characters of the password (for 128-/192-/256-bit keys) to make the key. This is a
convenient way to obtain a secure key within an entirely self-contained script (in a production
environment, as opposed to this essentially tutorial code, the key might be generated
as a hash, e.g. simply `key = Sha256(password)`

).
In more detail, the supplied password is converted to to UTF-8 (to be byte-safe), then
the first 16/24/32 characters are converted to bytes. The resulting pwBytes is used as a seed
for the Aes.keyExpansion() which is then used as the key to encrypt pwBytes with Aes.cipher().
Examples of keys generated in this way from (unrealistically) simple passwords:

‘a’ (U+0061): | pwBytes = | 61 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |

key = | 60 84 dd 49 14 7b 5d 05 7a e3 f8 81 b9 0e e7 dd | |

‘b’ (U+0062): | pwBytes = | 62 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |

key = | b4 1a 83 4f da 4b aa 41 76 62 be d6 2c 66 83 6d | |

‘☺’ (U+263a): | pwBytes = | e2 98 ba 00 00 00 00 00 00 00 00 00 00 00 00 00 |

key = | d1 0c cd fd 44 45 54 ef 59 aa f8 dc 78 8e 9a 7c |

Even with a single bit difference between two passwords (‘a’ and ‘b’), the key is entirely different.

**Usage**: this implementation would be invoked as follows:

var password = 'L0ck it up saf3'; var plaintext = 'pssst ... đon’t tell anyøne!'; var ciphertext = Aes.Ctr.encrypt(plaintext, password, 256); var origtext = Aes.Ctr.decrypt(ciphertext, password, 256);

*Does it make sense to implement AES in JavaScript?* Sometimes JavaScript can be used for real-world
cryptographic applications (particularly web-based ones). And a JavaScript implementation, which
anyone can play around with, can provide an easy starting-point for implementation in other languages.
Though I still think that TEA is generally good enough for simple applications,
and a great deal simpler to use (as well as significantly faster, according to tests by Tom Doan, thanks Tom).

The test vectors have been confirmed in IE 6 & 7 (Win), FF 2 & 3 (Win), Safari 2 (Mac), Opera 9 (Linux), and Konqueror 3 (Linux) (thx Jon Passki, Vincenzo Buttazzo). If you can confirm any other versions (or find any problems), please let me know!

*In other languages:* I’ve developed a PHP version which directly
mirrors this JavaScript version; it differs in that PHP has Base64 encoding and UTF-8 encoding built-in,
and has no unsigned-right-shift operator(!), but is otherwise a straightforward port. In other languages,
be sure to use 64-bit integers/longs, either unsigned or with unsigned right-shift operators; you
may need to take into consideration the way different languages handle bitwise ops, and of course
standard issues such as array handling and strict typing. I’m not aware of any other issues.

I’m not familiar with Python, but there is a Python version available at wiki.birth-online.de/snippets/python/aes-rijndael.

*Speed:* as mentioned, this is not an optimised implementation – on a 2GHz Intel Core 2 machine,
this implementation processes around 6kB/sec using IE7, and 30kB/sec using FF3(!) (one page of text
at a standard 250 words is about 1.5kB). The 128-bit version is some 25% faster than the 256-bit
version.

For more information, have a look at

- Daemen & Rijnael’s AES proposal for Rijndael Block Cipher
- Wikipedia article
- article from John Savard’s Cryptographic Compendium

For some security applications, a cryptographic hash is more appropriate than encryption – if you are interested in a hash function, see my implementations of SHA-1 and SHA-256.

*October 2009*: I have updated the formulation of these scripts to use JavaScript namespaces for better
encapsulation of function names.

See below for the source code of the JavaScript implementation. §ection numbers relate the code back to sections in the standard.

I offer these formulæ & scripts for free use and adaptation as my contribution to the open-source info-sphere from which I have received so much. You are welcome to re-use these scripts [under a simple attribution license, without any warranty express or implied] provided solely that you retain my copyright notice and a link to this page.

If you would like to show your appreciation and support continued development of these scripts, I would most gratefully accept donations.

Note: this script was **revised** on **1 August 2008** to use **Base64 encoding** – the
previous version, which used less standard encoding, is still available if
you have need to refer back to it.

If you have any queries or find any problems, contact me at ku.oc.epyt-elbavom@cne-stpircs.

*© 2005-2012 Chris Veness*

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /* AES implementation in JavaScript (c) Chris Veness 2005-2012 */ /* - see http://csrc.nist.gov/publications/PubsFIPS.html#197 */ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ var Aes = {}; // Aes namespace /** * AES Cipher function: encrypt 'input' state with Rijndael algorithm * applies Nr rounds (10/12/14) using key schedule w for 'add round key' stage * * @param {Number[]} input 16-byte (128-bit) input state array * @param {Number[][]} w Key schedule as 2D byte-array (Nr+1 x Nb bytes) * @returns {Number[]} Encrypted output state array */ Aes.cipher = function(input, w) { // main Cipher function [§5.1] var Nb = 4; // block size (in words): no of columns in state (fixed at 4 for AES) var Nr = w.length/Nb - 1; // no of rounds: 10/12/14 for 128/192/256-bit keys var state = [[],[],[],[]]; // initialise 4xNb byte-array 'state' with input [§3.4] for (var i=0; i<4*Nb; i++) state[i%4][Math.floor(i/4)] = input[i]; state = Aes.addRoundKey(state, w, 0, Nb); for (var round=1; round<Nr; round++) { state = Aes.subBytes(state, Nb); state = Aes.shiftRows(state, Nb); state = Aes.mixColumns(state, Nb); state = Aes.addRoundKey(state, w, round, Nb); } state = Aes.subBytes(state, Nb); state = Aes.shiftRows(state, Nb); state = Aes.addRoundKey(state, w, Nr, Nb); var output = new Array(4*Nb); // convert state to 1-d array before returning [§3.4] for (var i=0; i<4*Nb; i++) output[i] = state[i%4][Math.floor(i/4)]; return output; } /** * Perform Key Expansion to generate a Key Schedule * * @param {Number[]} key Key as 16/24/32-byte array * @returns {Number[][]} Expanded key schedule as 2D byte-array (Nr+1 x Nb bytes) */ Aes.keyExpansion = function(key) { // generate Key Schedule (byte-array Nr+1 x Nb) from Key [§5.2] var Nb = 4; // block size (in words): no of columns in state (fixed at 4 for AES) var Nk = key.length/4 // key length (in words): 4/6/8 for 128/192/256-bit keys var Nr = Nk + 6; // no of rounds: 10/12/14 for 128/192/256-bit keys var w = new Array(Nb*(Nr+1)); var temp = new Array(4); for (var i=0; i<Nk; i++) { var r = [key[4*i], key[4*i+1], key[4*i+2], key[4*i+3]]; w[i] = r; } for (var i=Nk; i<(Nb*(Nr+1)); i++) { w[i] = new Array(4); for (var t=0; t<4; t++) temp[t] = w[i-1][t]; if (i % Nk == 0) { temp = Aes.subWord(Aes.rotWord(temp)); for (var t=0; t<4; t++) temp[t] ^= Aes.rCon[i/Nk][t]; } else if (Nk > 6 && i%Nk == 4) { temp = Aes.subWord(temp); } for (var t=0; t<4; t++) w[i][t] = w[i-Nk][t] ^ temp[t]; } return w; } /* * ---- remaining routines are private, not called externally ---- */ Aes.subBytes = function(s, Nb) { // apply SBox to state S [§5.1.1] for (var r=0; r<4; r++) { for (var c=0; c<Nb; c++) s[r][c] = Aes.sBox[s[r][c]]; } return s; } Aes.shiftRows = function(s, Nb) { // shift row r of state S left by r bytes [§5.1.2] var t = new Array(4); for (var r=1; r<4; r++) { for (var c=0; c<4; c++) t[c] = s[r][(c+r)%Nb]; // shift into temp copy for (var c=0; c<4; c++) s[r][c] = t[c]; // and copy back } // note that this will work for Nb=4,5,6, but not 7,8 (always 4 for AES): return s; // see asmaes.sourceforge.net/rijndael/rijndaelImplementation.pdf } Aes.mixColumns = function(s, Nb) { // combine bytes of each col of state S [§5.1.3] for (var c=0; c<4; c++) { var a = new Array(4); // 'a' is a copy of the current column from 's' var b = new Array(4); // 'b' is a•{02} in GF(2^8) for (var i=0; i<4; i++) { a[i] = s[i][c]; b[i] = s[i][c]&0x80 ? s[i][c]<<1 ^ 0x011b : s[i][c]<<1; } // a[n] ^ b[n] is a•{03} in GF(2^8) s[0][c] = b[0] ^ a[1] ^ b[1] ^ a[2] ^ a[3]; // 2*a0 + 3*a1 + a2 + a3 s[1][c] = a[0] ^ b[1] ^ a[2] ^ b[2] ^ a[3]; // a0 * 2*a1 + 3*a2 + a3 s[2][c] = a[0] ^ a[1] ^ b[2] ^ a[3] ^ b[3]; // a0 + a1 + 2*a2 + 3*a3 s[3][c] = a[0] ^ b[0] ^ a[1] ^ a[2] ^ b[3]; // 3*a0 + a1 + a2 + 2*a3 } return s; } Aes.addRoundKey = function(state, w, rnd, Nb) { // xor Round Key into state S [§5.1.4] for (var r=0; r<4; r++) { for (var c=0; c<Nb; c++) state[r][c] ^= w[rnd*4+c][r]; } return state; } Aes.subWord = function(w) { // apply SBox to 4-byte word w for (var i=0; i<4; i++) w[i] = Aes.sBox[w[i]]; return w; } Aes.rotWord = function(w) { // rotate 4-byte word w left by one byte var tmp = w[0]; for (var i=0; i<3; i++) w[i] = w[i+1]; w[3] = tmp; return w; } // sBox is pre-computed multiplicative inverse in GF(2^8) used in subBytes and keyExpansion [§5.1.1] Aes.sBox = [0x63,0x7c,0x77,0x7b,0xf2,0x6b,0x6f,0xc5,0x30,0x01,0x67,0x2b,0xfe,0xd7,0xab,0x76, 0xca,0x82,0xc9,0x7d,0xfa,0x59,0x47,0xf0,0xad,0xd4,0xa2,0xaf,0x9c,0xa4,0x72,0xc0, 0xb7,0xfd,0x93,0x26,0x36,0x3f,0xf7,0xcc,0x34,0xa5,0xe5,0xf1,0x71,0xd8,0x31,0x15, 0x04,0xc7,0x23,0xc3,0x18,0x96,0x05,0x9a,0x07,0x12,0x80,0xe2,0xeb,0x27,0xb2,0x75, 0x09,0x83,0x2c,0x1a,0x1b,0x6e,0x5a,0xa0,0x52,0x3b,0xd6,0xb3,0x29,0xe3,0x2f,0x84, 0x53,0xd1,0x00,0xed,0x20,0xfc,0xb1,0x5b,0x6a,0xcb,0xbe,0x39,0x4a,0x4c,0x58,0xcf, 0xd0,0xef,0xaa,0xfb,0x43,0x4d,0x33,0x85,0x45,0xf9,0x02,0x7f,0x50,0x3c,0x9f,0xa8, 0x51,0xa3,0x40,0x8f,0x92,0x9d,0x38,0xf5,0xbc,0xb6,0xda,0x21,0x10,0xff,0xf3,0xd2, 0xcd,0x0c,0x13,0xec,0x5f,0x97,0x44,0x17,0xc4,0xa7,0x7e,0x3d,0x64,0x5d,0x19,0x73, 0x60,0x81,0x4f,0xdc,0x22,0x2a,0x90,0x88,0x46,0xee,0xb8,0x14,0xde,0x5e,0x0b,0xdb, 0xe0,0x32,0x3a,0x0a,0x49,0x06,0x24,0x5c,0xc2,0xd3,0xac,0x62,0x91,0x95,0xe4,0x79, 0xe7,0xc8,0x37,0x6d,0x8d,0xd5,0x4e,0xa9,0x6c,0x56,0xf4,0xea,0x65,0x7a,0xae,0x08, 0xba,0x78,0x25,0x2e,0x1c,0xa6,0xb4,0xc6,0xe8,0xdd,0x74,0x1f,0x4b,0xbd,0x8b,0x8a, 0x70,0x3e,0xb5,0x66,0x48,0x03,0xf6,0x0e,0x61,0x35,0x57,0xb9,0x86,0xc1,0x1d,0x9e, 0xe1,0xf8,0x98,0x11,0x69,0xd9,0x8e,0x94,0x9b,0x1e,0x87,0xe9,0xce,0x55,0x28,0xdf, 0x8c,0xa1,0x89,0x0d,0xbf,0xe6,0x42,0x68,0x41,0x99,0x2d,0x0f,0xb0,0x54,0xbb,0x16]; // rCon is Round Constant used for the Key Expansion [1st col is 2^(r-1) in GF(2^8)] [§5.2] Aes.rCon = [ [0x00, 0x00, 0x00, 0x00], [0x01, 0x00, 0x00, 0x00], [0x02, 0x00, 0x00, 0x00], [0x04, 0x00, 0x00, 0x00], [0x08, 0x00, 0x00, 0x00], [0x10, 0x00, 0x00, 0x00], [0x20, 0x00, 0x00, 0x00], [0x40, 0x00, 0x00, 0x00], [0x80, 0x00, 0x00, 0x00], [0x1b, 0x00, 0x00, 0x00], [0x36, 0x00, 0x00, 0x00] ]; /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /* AES Counter-mode implementation in JavaScript (c) Chris Veness 2005-2012 */ /* - see http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf */ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ Aes.Ctr = {}; // Aes.Ctr namespace: a subclass or extension of Aes /** * Encrypt a text using AES encryption in Counter mode of operation * * Unicode multi-byte character safe * * @param {String} plaintext Source text to be encrypted * @param {String} password The password to use to generate a key * @param {Number} nBits Number of bits to be used in the key (128, 192, or 256) * @returns {string} Encrypted text */ Aes.Ctr.encrypt = function(plaintext, password, nBits) { var blockSize = 16; // block size fixed at 16 bytes / 128 bits (Nb=4) for AES if (!(nBits==128 || nBits==192 || nBits==256)) return ''; // standard allows 128/192/256 bit keys plaintext = Utf8.encode(plaintext); password = Utf8.encode(password); //var t = new Date(); // timer // use AES itself to encrypt password to get cipher key (using plain password as source for key // expansion) - gives us well encrypted key (though hashed key might be preferred for prod'n use) var nBytes = nBits/8; // no bytes in key (16/24/32) var pwBytes = new Array(nBytes); for (var i=0; i<nBytes; i++) { // use 1st 16/24/32 chars of password for key pwBytes[i] = isNaN(password.charCodeAt(i)) ? 0 : password.charCodeAt(i); } var key = Aes.cipher(pwBytes, Aes.keyExpansion(pwBytes)); // gives us 16-byte key key = key.concat(key.slice(0, nBytes-16)); // expand key to 16/24/32 bytes long // initialise 1st 8 bytes of counter block with nonce (NIST SP800-38A §B.2): [0-1] = millisec, // [2-3] = random, [4-7] = seconds, together giving full sub-millisec uniqueness up to Feb 2106 var counterBlock = new Array(blockSize); var nonce = (new Date()).getTime(); // timestamp: milliseconds since 1-Jan-1970 var nonceMs = nonce%1000; var nonceSec = Math.floor(nonce/1000); var nonceRnd = Math.floor(Math.random()*0xffff); for (var i=0; i<2; i++) counterBlock[i] = (nonceMs >>> i*8) & 0xff; for (var i=0; i<2; i++) counterBlock[i+2] = (nonceRnd >>> i*8) & 0xff; for (var i=0; i<4; i++) counterBlock[i+4] = (nonceSec >>> i*8) & 0xff; // and convert it to a string to go on the front of the ciphertext var ctrTxt = ''; for (var i=0; i<8; i++) ctrTxt += String.fromCharCode(counterBlock[i]); // generate key schedule - an expansion of the key into distinct Key Rounds for each round var keySchedule = Aes.keyExpansion(key); var blockCount = Math.ceil(plaintext.length/blockSize); var ciphertxt = new Array(blockCount); // ciphertext as array of strings for (var b=0; b<blockCount; b++) { // set counter (block #) in last 8 bytes of counter block (leaving nonce in 1st 8 bytes) // done in two stages for 32-bit ops: using two words allows us to go past 2^32 blocks (68GB) for (var c=0; c<4; c++) counterBlock[15-c] = (b >>> c*8) & 0xff; for (var c=0; c<4; c++) counterBlock[15-c-4] = (b/0x100000000 >>> c*8) var cipherCntr = Aes.cipher(counterBlock, keySchedule); // -- encrypt counter block -- // block size is reduced on final block var blockLength = b<blockCount-1 ? blockSize : (plaintext.length-1)%blockSize+1; var cipherChar = new Array(blockLength); for (var i=0; i<blockLength; i++) { // -- xor plaintext with ciphered counter char-by-char -- cipherChar[i] = cipherCntr[i] ^ plaintext.charCodeAt(b*blockSize+i); cipherChar[i] = String.fromCharCode(cipherChar[i]); } ciphertxt[b] = cipherChar.join(''); } // Array.join is more efficient than repeated string concatenation in IE var ciphertext = ctrTxt + ciphertxt.join(''); ciphertext = Base64.encode(ciphertext); // encode in base64 //alert((new Date()) - t); return ciphertext; } /** * Decrypt a text encrypted by AES in counter mode of operation * * @param {String} ciphertext Source text to be encrypted * @param {String} password The password to use to generate a key * @param {Number} nBits Number of bits to be used in the key (128, 192, or 256) * @returns {String} Decrypted text */ Aes.Ctr.decrypt = function(ciphertext, password, nBits) { var blockSize = 16; // block size fixed at 16 bytes / 128 bits (Nb=4) for AES if (!(nBits==128 || nBits==192 || nBits==256)) return ''; // standard allows 128/192/256 bit keys ciphertext = Base64.decode(ciphertext); password = Utf8.encode(password); //var t = new Date(); // timer // use AES to encrypt password (mirroring encrypt routine) var nBytes = nBits/8; // no bytes in key var pwBytes = new Array(nBytes); for (var i=0; i<nBytes; i++) { pwBytes[i] = isNaN(password.charCodeAt(i)) ? 0 : password.charCodeAt(i); } var key = Aes.cipher(pwBytes, Aes.keyExpansion(pwBytes)); key = key.concat(key.slice(0, nBytes-16)); // expand key to 16/24/32 bytes long // recover nonce from 1st 8 bytes of ciphertext var counterBlock = new Array(8); ctrTxt = ciphertext.slice(0, 8); for (var i=0; i<8; i++) counterBlock[i] = ctrTxt.charCodeAt(i); // generate key schedule var keySchedule = Aes.keyExpansion(key); // separate ciphertext into blocks (skipping past initial 8 bytes) var nBlocks = Math.ceil((ciphertext.length-8) / blockSize); var ct = new Array(nBlocks); for (var b=0; b<nBlocks; b++) ct[b] = ciphertext.slice(8+b*blockSize, 8+b*blockSize+blockSize); ciphertext = ct; // ciphertext is now array of block-length strings // plaintext will get generated block-by-block into array of block-length strings var plaintxt = new Array(ciphertext.length); for (var b=0; b<nBlocks; b++) { // set counter (block #) in last 8 bytes of counter block (leaving nonce in 1st 8 bytes) for (var c=0; c<4; c++) counterBlock[15-c] = ((b) >>> c*8) & 0xff; for (var c=0; c<4; c++) counterBlock[15-c-4] = (((b+1)/0x100000000-1) >>> c*8) & 0xff; var cipherCntr = Aes.cipher(counterBlock, keySchedule); // encrypt counter block var plaintxtByte = new Array(ciphertext[b].length); for (var i=0; i<ciphertext[b].length; i++) { // -- xor plaintxt with ciphered counter byte-by-byte -- plaintxtByte[i] = cipherCntr[i] ^ ciphertext[b].charCodeAt(i); plaintxtByte[i] = String.fromCharCode(plaintxtByte[i]); } plaintxt[b] = plaintxtByte.join(''); } // join array of blocks into single plaintext string var plaintext = plaintxt.join(''); plaintext = Utf8.decode(plaintext); // decode from UTF8 back to Unicode multi-byte chars //alert((new Date()) - t); return plaintext; } /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /* Base64 class: Base 64 encoding / decoding (c) Chris Veness 2002-2012 */ /* note: depends on Utf8 class */ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ var Base64 = {}; // Base64 namespace Base64.code = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; /** * Encode string into Base64, as defined by RFC 4648 [http://tools.ietf.org/html/rfc4648] * (instance method extending String object). As per RFC 4648, no newlines are added. * * @param {String} str The string to be encoded as base-64 * @param {Boolean} [utf8encode=false] Flag to indicate whether str is Unicode string to be encoded * to UTF8 before conversion to base64; otherwise string is assumed to be 8-bit characters * @returns {String} Base64-encoded string */ Base64.encode = function(str, utf8encode) { // http://tools.ietf.org/html/rfc4648 utf8encode = (typeof utf8encode == 'undefined') ? false : utf8encode; var o1, o2, o3, bits, h1, h2, h3, h4, e=[], pad = '', c, plain, coded; var b64 = Base64.code; plain = utf8encode ? str.encodeUTF8() : str; c = plain.length % 3; // pad string to length of multiple of 3 if (c > 0) { while (c++ < 3) { pad += '='; plain += '\0'; } } // note: doing padding here saves us doing special-case packing for trailing 1 or 2 chars for (c=0; c<plain.length; c+=3) { // pack three octets into four hexets o1 = plain.charCodeAt(c); o2 = plain.charCodeAt(c+1); o3 = plain.charCodeAt(c+2); bits = o1<<16 | o2<<8 | o3; h1 = bits>>18 & 0x3f; h2 = bits>>12 & 0x3f; h3 = bits>>6 & 0x3f; h4 = bits & 0x3f; // use hextets to index into code string e[c/3] = b64.charAt(h1) + b64.charAt(h2) + b64.charAt(h3) + b64.charAt(h4); } coded = e.join(''); // join() is far faster than repeated string concatenation in IE // replace 'A's from padded nulls with '='s coded = coded.slice(0, coded.length-pad.length) + pad; return coded; } /** * Decode string from Base64, as defined by RFC 4648 [http://tools.ietf.org/html/rfc4648] * (instance method extending String object). As per RFC 4648, newlines are not catered for. * * @param {String} str The string to be decoded from base-64 * @param {Boolean} [utf8decode=false] Flag to indicate whether str is Unicode string to be decoded * from UTF8 after conversion from base64 * @returns {String} decoded string */ Base64.decode = function(str, utf8decode) { utf8decode = (typeof utf8decode == 'undefined') ? false : utf8decode; var o1, o2, o3, h1, h2, h3, h4, bits, d=[], plain, coded; var b64 = Base64.code; coded = utf8decode ? str.decodeUTF8() : str; for (var c=0; c<coded.length; c+=4) { // unpack four hexets into three octets h1 = b64.indexOf(coded.charAt(c)); h2 = b64.indexOf(coded.charAt(c+1)); h3 = b64.indexOf(coded.charAt(c+2)); h4 = b64.indexOf(coded.charAt(c+3)); bits = h1<<18 | h2<<12 | h3<<6 | h4; o1 = bits>>>16 & 0xff; o2 = bits>>>8 & 0xff; o3 = bits & 0xff; d[c/4] = String.fromCharCode(o1, o2, o3); // check for padding if (h4 == 0x40) d[c/4] = String.fromCharCode(o1, o2); if (h3 == 0x40) d[c/4] = String.fromCharCode(o1); } plain = d.join(''); // join() is far faster than repeated string concatenation in IE return utf8decode ? plain.decodeUTF8() : plain; } /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /* Utf8 class: encode / decode between multi-byte Unicode characters and UTF-8 multiple */ /* single-byte character encoding (c) Chris Veness 2002-2012 */ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ var Utf8 = {}; // Utf8 namespace /** * Encode multi-byte Unicode string into utf-8 multiple single-byte characters * (BMP / basic multilingual plane only) * * Chars in range U+0080 - U+07FF are encoded in 2 chars, U+0800 - U+FFFF in 3 chars * * @param {String} strUni Unicode string to be encoded as UTF-8 * @returns {String} encoded string */ Utf8.encode = function(strUni) { // use regular expressions & String.replace callback function for better efficiency // than procedural approaches var strUtf = strUni.replace( /[\u0080-\u07ff]/g, // U+0080 - U+07FF => 2 bytes 110yyyyy, 10zzzzzz function(c) { var cc = c.charCodeAt(0); return String.fromCharCode(0xc0 | cc>>6, 0x80 | cc&0x3f); } ); strUtf = strUtf.replace( /[\u0800-\uffff]/g, // U+0800 - U+FFFF => 3 bytes 1110xxxx, 10yyyyyy, 10zzzzzz function(c) { var cc = c.charCodeAt(0); return String.fromCharCode(0xe0 | cc>>12, 0x80 | cc>>6&0x3F, 0x80 | cc&0x3f); } ); return strUtf; } /** * Decode utf-8 encoded string back into multi-byte Unicode characters * * @param {String} strUtf UTF-8 string to be decoded back to Unicode * @returns {String} decoded string */ Utf8.decode = function(strUtf) { // note: decode 3-byte chars first as decoded 2-byte strings could appear to be 3-byte char! var strUni = strUtf.replace( /[\u00e0-\u00ef][\u0080-\u00bf][\u0080-\u00bf]/g, // 3-byte chars function(c) { // (note parentheses for precence) var cc = ((c.charCodeAt(0)&0x0f)<<12) | ((c.charCodeAt(1)&0x3f)<<6) | ( c.charCodeAt(2)&0x3f); return String.fromCharCode(cc); } ); strUni = strUni.replace( /[\u00c0-\u00df][\u0080-\u00bf]/g, // 2-byte chars function(c) { // (note parentheses for precence) var cc = (c.charCodeAt(0)&0x1f)<<6 | c.charCodeAt(1)&0x3f; return String.fromCharCode(cc); } ); return strUni; } /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */