About This Project
Two from-scratch Python implementations of the classic symmetric block ciphers DES and AES, built for two university cryptography assignments to understand what actually happens inside each algorithm instead of treating it as a library call. They live in two separate repositories since they were separate assignments with separate reference data, but both share the same goal: every key-schedule step and round transformation implemented by hand and checked against the values given in the assignment.
DES (Data Encryption Standard)
- Full key schedule: PC-1/PC-2 permutations and the per-round left circular shifts producing all 16 round keys
- Complete Feistel round function: E-expansion, XOR with the round key, S-box substitution, and P-permutation
- Full 16-round encryption pipeline, from the initial permutation (IP) to the final inverse permutation (IP⁻¹)
- Verified stage-by-stage against course-provided reference values for the key schedule, a single round's f-function output, and the final ciphertext
AES (Advanced Encryption Standard)
- Rijndael key schedule for AES-128 (10 round keys), including the g-function's byte rotation, S-box substitution, and a round constant derived directly from GF(2⁸) polynomial arithmetic rather than a hardcoded table
- Full round transformation: SubBytes (S-box lookup), ShiftRows, MixColumns as true finite-field matrix multiplication (skipped on the final round, per the AES spec), and AddRoundKey
- Round keys verified against course-provided target values
Challenges & Solutions
Bit-level correctness leaves no room for "almost right": a mis-indexed permutation table or a mistake in the finite-field arithmetic behind MixColumns corrupts every round downstream. For MixColumns, working in GF(2⁸) meant multiplication couldn't be plain integer arithmetic — a Galois-field library was used so multiply and XOR-add behave the way the AES specification actually defines them. For both ciphers, checking intermediate values (a single round key, one round's f-function output) against the values given in the assignment, rather than only the final ciphertext, made it possible to trace a wrong bit back to the exact stage that produced it.