A from-scratch, single-file implementation of the DES block cipher in C99 that prints its round-by-round internal state, wrapped in a small two-block CBC-mode demo.
This is a personal, educational implementation of the DES (Data Encryption Standard) block cipher, hand-written in C from the standard DES permutation and S-box tables. It exists to inspect how DES transforms a 64-bit block — the core routine prints intermediate state on every round — rather than to serve as a drop-in cryptographic library. Every input is hardcoded; there is no API, command-line interface, or file I/O.
The whole cipher lives in one function, des(uint64_t input, uint64_t key, char mode), in des.c. The rest of the repository is that same code with different main() drivers, kept as .txt files.
des.c— the only file theMakefilebuilds. Contains the DES tables, thedes()routine, and amain()that runs a two-block CBC encrypt-then-decrypt demonstration on hardcoded test vectors.Makefile— agcc/ C99 build that compilesdes.cinto an executable nameddes.cbc mode.txt— a byte-for-byte copy ofdes.c(identical SHA-256), kept as a plain-text snapshot; the CBC logic it refers to already lives indes.c.reversible proof.txt— the same tables and the samedes()function asdes.c(the two files differ only insidemain()), with a driver that encrypts one block directly (no CBC) and then decrypts the hardcoded constant0x3fa40e8a984d4815. Under the sample key that constant is exactly the encryption of the sample plaintext0x4e6f772069732074, so the decrypt call returns that plaintext, demonstrating that'd'mode inverts'e'mode. It is a complete program but is not referenced by theMakefile.main.txt— amain()snippet only: no#includes and nodes()definition, so it does not compile on its own. It prints aRound Round Output Round Keyheader and encrypts a single block; paste it overdes.c'smain()to get single-block round output instead of the CBC demo.
There is no license file and no test suite in the repository.
des(uint64_t input, uint64_t key, char mode) implements textbook DES over 64-bit blocks:
- Initial / final permutation — the input is permuted by
IP[]into a 64-bit value that is split into a 32-bit left halfLand right halfR; after the 16 rounds,PI[](the inverse permutation) is applied to the pre-output to produce the result. - Key schedule —
PC1[]selects 56 bits of the 64-bit key into two 28-bit halvesCandD. For each round,CandDare left-rotated byiteration_shift[]bits (the schedule1,1,2,2,2,2,2,2,1,2,2,2,2,2,2,1), andPC2[]compresses the pair into a 48-bit subkey. All sixteen subkeys are precomputed intosub_key[16]before the rounds run. - Round (Feistel) function — for each of the 16 rounds the 32-bit right half is expanded to 48 bits with
E[], XORed with the round subkey, substituted through the eight S-boxesS[8][64], and permuted withP[]; that result is XORed into the left half (temp = R; R = L ^ f_function_res; L = temp). - Encrypt vs. decrypt — the
modeargument only changes subkey order:'d'appliessub_key[15-i](reverse order); any other value — the code passes'e'— appliessub_key[i](forward order). Applying the subkeys in reverse inverts encryption. - Diagnostics — every call to
des()prints 16 lines to stdout (round numbers 1 through 16), one per round, each line being the round number (%2d), a 64-bit intermediate block (%016llx), and a 48-bit subkey (%012llx). The function then returns the 64-bit result. There is no way to run the cipher without this output.
des.c's main() wraps the cipher in CBC mode: each plaintext block is XORed with the previous ciphertext block (the first with the IV) before encryption, and the chaining is reversed on decryption. The hardcoded inputs are two 64-bit plaintext blocks 0x4e6f772069732074 and 0x6f6d65206d6f7265, key 0x0123456789abcdef, and IV 0xaabbccddeeff0011; running the program decrypts the two ciphertext blocks back to those plaintext values.
The Makefile sets CC = gcc and CFLAGS = -Wall -std=c99:
make # compiles des.c and links the ./des executable
./des # runs the two-block CBC encrypt/decrypt demo
make clean # removes des.o and des
The build succeeds but is not warning-clean: under -Wall, gcc emits several -Wparentheses warnings about operator precedence in the key-rotation and S-box index expressions (no errors). The program takes no command-line arguments and does no file I/O — every value is hardcoded in main().
To exercise the other drivers, edit the sources by hand:
- replace
des.c'smain()with the snippet inmain.txtto get single-block round output; or - build
reversible proof.txtdirectly. Because it has a.txtextension you must tell the compiler it is C, for examplegcc -x c -std=c99 "reversible proof.txt" -o reversible.
- Educational / inspection code, not a library or tool:
des()always prints round diagnostics, and every input is hardcoded — no API, CLI, or file I/O. - The default build is the two-block CBC demo, not a single-block driver.
- Operates on whole 64-bit blocks only: no padding, no arbitrary-length message handling, and no key-parity or weak-key checking.
- The tables are the standard DES tables and the demo round-trips (decrypt inverts encrypt), but the repository contains no known-answer tests against the official DES vectors, so bit-exact standard conformance is not asserted here.
- Not written to be constant-time or resistant to timing / side-channel attacks.
- No automated tests and no license file are present in the repository.
- DES has a 56-bit effective key and is not secure for real-world use; this code is for understanding the algorithm, not for protecting data.