Rijndael (ECB)
- create
- create("rijndael")
- family
- Substitution-permutation
- options
- --key --blockSize?
- self-inverse
- no
- keyspace
- 2^128 to 2^256 keys
- try it
- ciphers rijndael "ATTACK AT DAWN" --key 2b7e151628aed2a6abf7158809cf4f3c762e7160f38b4da56a784d9045190cfe --blockSize 256
AES is a subset. Daemen and Rijmen sent NIST a cipher whose block and key could each be 128, 160, 192, 224 or 256 bits. NIST kept the 128-bit block and three of the keys and called that AES. Everything else stayed Rijndael, and it didn't go away. PHP's old mcrypt called the 256-bit block MCRYPT_RIJNDAEL_256, which is not AES-256, whatever the name suggests.
blockSize is in bits, 128, 160, 192, 224 or 256, default 128. The key is 32, 40, 48, 56 or 64 hex digits, so the 160 and 224-bit keys AES never took work here too. The mode is ECB. Text goes in as UTF-8 with PKCS#7 padding up to a whole block, and the ciphertext comes out as lowercase hex.
const rijndael = create("rijndael");
const key = "2b7e151628aed2a6abf7158809cf4f3c762e7160f38b4da56a784d9045190cfe";
rijndael.encode("ATTACK AT DAWN", { key, blockSize: 256 }).text;
// "4e0085db1697ce5f34911401d53bc05637a158856ca148bb212050ebfd20d208"
rijndael.decode("4e0085db1697ce5f34911401d53bc05637a158856ca148bb212050ebfd20d208", { key, blockSize: 256 }).text;
// "ATTACK AT DAWN"
Leave blockSize out and you get AES. The same key gives bb1b417494158895d7224a297dac330f here and in AES, because it's the same function.
What changes with the block
The state stops being a 4×4 square and gets five to eight columns. Three things follow from that, all from the Rijndael proposal:
- More rounds. The count is the larger of the key and block length in 32-bit words, plus six. So a 128-bit key on a 256-bit block runs 14 rounds, not 10.
- More round key. Every round needs a key as wide as the block, and the key schedule simply keeps going until there's enough.
- Different row shifts. Rows 1 to 3 move by 1, 2 and 3 bytes up to six columns, by 1, 2 and 4 at seven, and by 1, 3 and 4 at eight.
S-box and MixColumns stay the same. Each column still gets mixed on its own, there are just more of them.
Checking it
OpenSSL has no Rijndael beyond AES. The block function is tested against Brian Gladman's known answer tests for all 25 block and key lengths, the files ecbnt44.txt to ecbnt88.txt. The padded text vectors match py3rijndael, which is a separate implementation but only knows 128, 192 and 256-bit blocks and keys.
Watch the padding if the ciphertext came from mcrypt. mcrypt filled the last block with zero bytes, not PKCS#7, so its output fails the padding check here. You get a CipherError, not text with zeros at the end.
A key or blockSize Rijndael doesn't have is an InvalidOptionError. A ciphertext that isn't whole blocks of the length you asked for is a CipherError, which also catches an AES ciphertext handed to a 256-bit block. Like AES this is plain TypeScript for learning and puzzles, not constant time, and ECB shows every repeated block.
Triple DES (CBC)
Triple DES with the blocks chained. Every 8-byte block is mixed with the ciphertext before it, so equal blocks stop looking equal. UTF-8 text in, hex out.
AES (XTS)
The disk mode that replaced LRW. Every block masked by its sector and its position, a short last block steals from the one before, and nothing is padded. UTF-8 text in, hex out.