DESX (ECB)
- create
- create("desx")
- family
- Feistel
- options
- --key
- self-inverse
- no
- keyspace
- 2^184 keys (56 DES bits and two 64-bit whitening keys)
- try it
- ciphers desx "ATTACK AT DAWN" --key 0123456789abcdeff0e1d2c3b4a596871122334455667788
By the early 1980s everyone could see the 56-bit key of DES was the weak spot. Triple DES fixes that by running the cipher three times. Ron Rivest had a cheaper idea in 1984. Leave DES alone and XOR the block with one extra 64-bit key before it goes in and with another after it comes out. That's the whole cipher. RSA shipped it in BSAFE as DESX.
So every 8-byte block goes like this:
C = K_out ⊕ DES_K(P ⊕ K_in)
The key is 48 hex digits, three parts of 16. First the DES key, then K_in, then K_out. That's 56 + 64 + 64 = 184 bits once the DES parity bits are gone. Case doesn't matter and spaces are ignored, so spacing the parts out works fine.
Mind the order. The Polish Wikipedia and Botan list the input whitening key first and the DES key second. OpenSSL puts the DES key first, and so does this cipher, because OpenSSL is what you'll check it against.
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 DES.
const desx = create("desx");
const key = "0123456789abcdef f0e1d2c3b4a59687 1122334455667788";
desx.encode("ATTACK AT DAWN", { key }).text; // "e66c99d05c13ecf7cb70b505d3d77a8e"
desx.decode("e66c99d05c13ecf7cb70b505d3d77a8e", { key }).text; // "ATTACK AT DAWN"
OpenSSL has DESX only in CBC mode, in the legacy provider. With a zero IV the first block of CBC is ECB, so a text that fits in one block gives the same bytes:
printf %s "ATTACK" | openssl enc -desx-cbc -provider legacy -provider default -K 0123456789abcdeff0e1d2c3b4a596871122334455667788 -iv 0000000000000000 | xxd -p
# ea779f4b4f979f31
The tests check the nine DESX vectors from Botan 2, with the key parts reordered, and OpenSSL output for whole texts, one block at a time. Zero whitening keys have to give plain DES too.
Why two XORs help
The XORs cost nothing, and DES stays one pass, so DESX is as fast as DES and three times faster than Triple DES. Brute force gets much worse though. Guessing the DES key alone tells you nothing, because you don't know what went into DES or what came out. Kilian and Rogaway proved in 1996 that a generic key search against DESX costs about 2^119 / m DES runs, where m is how many plaintext and ciphertext pairs the attacker has.
Against attacks on DES itself the gain is much smaller. Differential and linear cryptanalysis get a bit harder, nothing like the jump brute force takes. And in 2000 Biryukov and Wagner found a slide attack on DESX, 2^32.5 known plaintexts and 2^87.5 work. Nowhere near practical, but a lot less than 184 bits.
Why ECB leaks
Same as every ECB here. Sixteen As are two equal blocks, and they come out as two equal blocks:
desx.encode("A".repeat(16), { key }).text;
// 7df87a008ee59961 7df87a008ee59961 f5cb6e9d23564414
Spaces added to show the blocks. The last one is only padding.
A key that isn't 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. This one is plain TypeScript over bit arrays, slow and not constant time. Puzzles and learning, not secrets.
DES (ECB)
The Data Encryption Standard, one pass of 16 Feistel rounds under a 56-bit key. UTF-8 text in, hex out, and every 8-byte block encrypted on its own.
IDEA (ECB)
Lai and Massey's cipher from early PGP. No S-boxes, just XOR, addition and multiplication on 16-bit words under a 128-bit key. UTF-8 text in, hex out.