This is a JavaScript AES and CBC implementation using ArrayBuffer. Why you would want that you might ask and the reason is that I wanted to do AES CBC crypto in a Chrome extension. I also wanted to make it reasonable fast (for being done in JavaScript) so the code might look a bit weird and it is also only optimized to run with Chrome v8.
Note that if you read this in a future when the WebCrypto standard is available in all browser you need to support I suggest that you use that instead.
2008 MacBook, Chrome 24, 2 GHz Core 2 Duo, 1067Mhz DDR3:
Encrypt ~12.2 MB/s, Decrypt ~8.4 MB/s
2011 MacBook Air, Chrome 24, 1.5 GHz Core i5, 1333Mhz DDR3:
Encrypt ~17.5 MB/s, Decrypt ~12.0 MB/s
If you want to benchmark yourself you can check the "CBC AES-128 Benchmark" test case in the unit tests.
// make sure both aes.js and crypto.js is included
var iv = new Uint8Array([
0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef,
0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef
]);
var key = new Uint8Array([
0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef,
0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef
]);
var input = new Uint8Array(123456);
// pad input to be AES block size aligned and then encrypt
var encrypted = Crypto.encrypt_aes_cbc(Crypto.pkcs_pad(input.buffer), key.buffer, iv.buffer);
// decrypt and then remove pad bytes
var decrypted = Crypto.pkcs_unpad(Crypto.decrypt_aes_cbc(encrypted, key.buffer, iv.buffer));
// decrypted is now a ArrayBuffer with same bytes as in input
Test if inverse equivalent cipher is faster. crypto-js AES seems to use that but haven't benchmarked their code yet.
I should write a short journal of the optimization steps and tests done to end up with the current code. The short version is: use lookup tables, don't create new objects, unroll and inline functions yourself as the v8 optimizer seem hard to predict and has some default limits that is hard to fulfill.