Ciphers

Triple DES (ECB)

DES three times, encrypt, decrypt and encrypt again. UTF-8 text in, hex out, and every 8-byte block encrypted on its own.
create
create("triple-des")
family
Feistel
options
--key
self-inverse
no
keyspace
2^112 or 2^168 keys (56 bits of every 64-bit DES key)
try it
ciphers triple-des "ATTACK AT DAWN" --key 0123456789abcdef23456789abcdef01456789abcdef0123

Triple DES, also 3DES or TDEA, from NIST SP 800-67. Each 8-byte block is encrypted with DES under K1, decrypted under K2, then encrypted again under K3. The key is hex. 48 digits are three keys. 32 digits are two, and K3 is K1 again. Case doesn't matter and spaces are ignored. The last bit of every key byte is a parity bit in DES. Nothing checks it. Keys that differ only there encrypt the same way.

Text goes in as UTF-8 with PKCS#7 padding, ciphertext comes out as lowercase hex, and decode wants hex back. The mode is ECB, same as AES.

const tdes = create("triple-des");
const key = "0123456789abcdef23456789abcdef01456789abcdef0123";
tdes.encode("ATTACK AT DAWN", { key }).text; // "a1a3679052607883b30ef4b95156ff29"
tdes.decode("a1a3679052607883b30ef4b95156ff29", { key }).text; // "ATTACK AT DAWN"

OpenSSL agrees. -des-ede3-ecb takes the 48-digit key, -des-ede-ecb the 32-digit one:

printf %s "ATTACK AT DAWN" | openssl enc -des-ede3-ecb -K 0123456789abcdef23456789abcdef01456789abcdef0123 | xxd -p

The tests start from three outside sources. The example in SP 800-67, three blocks long. The known-answer tables of NIST SP 800-17. And the DES walkthrough from J. Orlin Grabbe's "The DES Algorithm Illustrated".

Three equal keys make one DES

Why decrypt in the middle? So that K1 = K2 = K3 cancels out. The first encryption and the decryption undo each other, and what's left is plain single DES. Take Grabbe's key three times:

tdes.encode("ATTACK AT DAWN", { key: "133457799bbcdff1".repeat(3) }).text;
// "1739739759a5caa5b6544c0af3cad197"

That is DES with 133457799bbcdff1 and nothing more, 56 bits of key. openssl enc -des-ecb -provider legacy -K 133457799bbcdff1 prints the same bytes. It's the whole point of the EDE order: one Triple DES implementation still speaks plain DES.

Why ECB leaks

8-byte blocks make it easy to see. Sixteen As are two equal blocks, and they come out as two equal blocks:

tdes.encode("A".repeat(16), { key }).text;
// 9f6ca443ceebf424 9f6ca443ceebf424 832846b52f9e213d

Spaces added to show the blocks. The last one is only padding.

A key that isn't 32 or 48 hex digits is an InvalidOptionError, a missing one a MissingOptionError. On decode, ciphertext that isn't whole 8-byte blocks of hex is a CipherError. A wrong key almost always breaks the padding, and that's a CipherError too. So are decrypted bytes that aren't UTF-8.

NIST disallowed Triple DES for encryption after 2023. Its 64-bit block is too small for a lot of data under one key. This one is plain TypeScript over bit arrays, slow and not constant time. It's here for puzzles and for learning how a Feistel network works. Don't protect anything real with it.

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