Movable Type Home Page

Movable Type Scripts


AES Advanced Encryption Standard

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.

This is principally a learning exercise, and I am not a cryptographic expert. I can provide no warranty or guarantees if you choose to use this code in production environments.

Functional demo

 

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(28), 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(28) 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

(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 253, this allows a message size up to 257 (c. 1017) 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

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:

import AesCtr from './aes-ctr.js';
const password = 'L0ck it up saf3';
const plaintext = 'pssst ... đon’t tell anyøne!';
const ciphertext = AesCtr.encrypt(plaintext, password, 256);
const origtext = AesCtr.decrypt(ciphertext, password, 256);

Note that there are no standards for data storage formats of AES encryption mode wrapper functions, so this is unlikely to inter-operate with standard library functions. It inter-operates between client-side JavaScript and Node.js: see gist.github.com/​chrisveness/​b28bd30b2b0c03806b0c. I have also written a matching PHP version.

 

Tests: if you want to be confident the AES-CTR encryption/decryption is working properly (and you should!), a set of mocha/chai tests based on NIST test vectors & a range of encryptions/decryptions of different lengths is available at www.movable-type.co.uk/scripts/test/aes-test.html.

 


It is also quite simple to encrypt files, by using web workers, FileReader & Blob objects, and Eli Grey’s saveAs():

Encrypt file

Source file will be encrypted and downloaded with '.encrypted' suffix.

Decrypt file

Encrypted file will be decrypted and downloaded with '.decrypted' suffix.

Note that AesCtr.encrypt expects a string: as binary files may include invalid Unicode sequences if treated as strings, I treat the file contents as a byte-stream, converting it to single-byte characters before passing it to AesCtr.encrypt.


Does it make sense to implement AES in JavaScript? This is really intended as a reference implementation to help understand the AES standard, but sometimes JavaScript can be used for real-world cryptographic applications (particularly web-based ones). A JavaScript implementation such as this can also 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). For production use, it’s always a good idea to make use of a standard library where possible, in preference to home-grown solutions.

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 – using Chrome on a low-to-middling 2014 machine (Core-i5), this processes around 1MB/sec [still some 100× faster than back in 2008!).

For more information, have a look at

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.

Note that these scripts are intended to assist in studying the algorithms, not for production use. For production use, I would recommend the Web Cryptography API for the browser (see example), or the crypto library in Node.js – though if you are using this code, I emphasise that I have no reason at all to doubt its integrity.


See below for the source code of the JavaScript implementation, also available on GitHub. §ection numbers relate the code back to sections in the standard.

With its untyped C-style syntax, JavaScript reads remarkably close to pseudo-code: exposing the algorithms with a minimum of syntactic distractions. These functions should be simple to translate into other languages if required, though can also be used as-is in browsers and Node.js.

OSI MIT License I offer these scripts for free use and adaptation to balance my debt to the open-source info-verse. You are welcome to re-use these scripts [under an MIT licence, without any warranty express or implied] provided solely that you retain my copyright notice and a link to this page.

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

© 2005-2018 Chris Veness