Ciphers

AES (ECB)

The standard block cipher in its simplest mode. UTF-8 text in, hex out, and every 16-byte block encrypted on its own, so repeats show through.
create
create("aes")
family
Substitution-permutation
options
--key
self-inverse
no
keyspace
2^128, 2^192 or 2^256 keys
try it
ciphers aes "ATTACK AT DAWN" --key 2b7e151628aed2a6abf7158809cf4f3c

AES from FIPS-197 with a 128, 192 or 256-bit key, picked by the key length: 32, 48 or 64 hex digits. Case doesn't matter and spaces are ignored. The mode is ECB, the electronic codebook. The text is encoded as UTF-8 and padded with PKCS#7, then each 16-byte block is encrypted with the same key and nothing else. The ciphertext comes back as lowercase hex, and decode wants hex too.

const aes = create("aes");
const key = "2b7e151628aed2a6abf7158809cf4f3c";
aes.encode("ATTACK AT DAWN", { key }).text; // "bef12e48d0f1739d732326cbecbef389"
aes.decode("bef12e48d0f1739d732326cbecbef389", { key }).text; // "ATTACK AT DAWN"

OpenSSL gives the same bytes, so you can check it without this package:

printf %s "ATTACK AT DAWN" | openssl enc -aes-128-ecb -K 2b7e151628aed2a6abf7158809cf4f3c | xxd -p

The block function is tested against the FIPS-197 Appendix C examples and the ECB vectors from NIST SP 800-38A.

Why ECB leaks

Same key, same block, same output. Thirty-two As are two identical blocks of plaintext, and they come out as two identical blocks of ciphertext:

aes.encode("A".repeat(32), { key: "000102030405060708090a0b0c0d0e0f" }).text;
// dd4b1a0b47daa7067d0b59d95d58a6ae dd4b1a0b47daa7067d0b59d95d58a6ae 954f64f2e4e86e9eee82d20216684899

Spaces added here to show the blocks. The third block is only padding. That's the whole weakness: nobody learns the key, but anyone sees which blocks repeat, and can cut blocks out or swap them without knowing the key either. It's why ECB is the mode CTFs pick and the mode textbooks warn about.

A key that isn't 32, 48 or 64 hex digits is an InvalidOptionError, a missing one a MissingOptionError. On decode, a ciphertext that isn't whole 16-byte blocks of hex is a CipherError. So is a wrong key, because the last block then almost never ends in valid PKCS#7 padding. Decrypted bytes that aren't UTF-8 are an error too, the result is always text.

This implementation is for learning and for puzzles. It's plain TypeScript with table lookups, not constant time, and ECB hides nothing about the structure of the message. Don't protect anything real with it.

@agntn/ciphers·MIT license· Classical ciphers, for lessons and puzzles. Not for protecting anything, ever.