Student Project Catalogue#
This is a list of ten project topics suitable for the post-quantum half of the course. Each is sized to be doable by one or two students in a few weekends. You are not expected to deliver a production implementation — partial code, a small demo, and a clear write-up beat a “full” implementation that you do not understand.
Each entry has the same structure:
Difficulty — ★ (one weekend) to ★★★★ (real research project).
Description — what you will build and why it matters.
Literature — the small reading list that should anchor your work.
Implement — the concrete code pieces you should write yourself.
Present — what to show on lab day (W8).
Pick one topic, send your choice to the instructor before W5, and plan a 10-minute presentation with a 2-page write-up due W8.
Submission format
Code: a single GitHub repository (or a zip), one
README.md, one driver script that produces the demo output in under 60 seconds.Write-up: 2 pages PDF — problem, your approach, results, what you could not get to and why.
Slides: 5–8 Reveal.js or PDF slides for the W8 presentation.
No production claims — every piece of code should be labelled “toy” / “educational” in its docstring header.
Project 1 — ★ Lamport one-time signatures with a CLI#
Description. Build a Lamport one-time signature scheme using
SHA-256 and wrap it in a small command-line interface
(lamport keygen, lamport sign, lamport verify). The goal is to
internalise that hash-based signatures are one-time and to make the
state-reuse failure mode concretely observable.
Literature.
Exercise sheet 47, Ex 47.E1.
Lamport, L. (1979). Constructing Digital Signatures from a One-Way Function, SRI CSL-98.
Implement.
lamport_keygen(n=32)returning(sk, pk)where each is a list of \(2 \cdot 8n\) random byte-strings.lamport_sign(sk, msg)andlamport_verify(pk, msg, sig).A CLI driver (
argparse) that writes keys / signatures to disk in JSON or hex.A state-reuse demonstrator: sign two different messages with the same key, then forge a third valid signature.
Present.
Demo: keygen, sign two messages, then run the forge attack live.
Size table: secret key, public key, signature in bytes for \(n = 32\).
Two sentences on why “stateless” hash signatures (SLH-DSA) exist.
Project 2 — ★ Merkle tree multi-signature scheme#
Description. Combine Project 1’s Lamport keys into a binary Merkle tree of height \(h = 4\) (16 leaves). Publish only the root as the public key. Each signature carries the OTS payload plus an authentication path that the verifier uses to recompute the root.
Literature.
Chapter 47, §47.4.
Exercise sheet 47, Ex 47.E2 and E4.
Merkle, R. (1979). Secrecy, Authentication and Public Key Systems. Stanford PhD thesis.
RFC 8391 — XMSS, §3 (tree construction).
Implement.
build_merkle(leaf_pks)returning level-by-level node hashes.auth_path(levels, j)extracting the sibling hashes from leaf \(j\) to the root.verify_merkle_path(root, leaf, j, path).A combined
mss_sign(state, msg)/mss_verify(root, sig)that routes through your Lamport from Project 1.
Present.
An SVG (or matplotlib) drawing of the height-4 tree with leaf \(j = 5\) highlighted and the auth-path siblings coloured.
A table: signature byte count as \(h\) grows from 2 to 6.
Project 3 — ★★ Number Theoretic Transform (NTT) for Kyber#
Description. Implement the NTT and inverse NTT for Kyber’s ring \(R_q = \mathbb{Z}_{3329}[x]/(x^{256}+1)\) in pure Python. Verify the round-trip identity and time it against schoolbook multiplication.
Literature.
Upstream cryptanalysis course, Chapter 41 — ML-KEM (Kyber), §41.3.
FIPS 203, §4 (NTT specification).
Cooley & Tukey (1965). An algorithm for the machine calculation of complex Fourier series. (For historical context.)
Implement.
Pre-computation of primitive 256-th roots of unity mod 3329.
Cooley–Tukey forward NTT (bit-reversal permutation + butterflies).
Gentleman–Sande inverse NTT.
A pointwise multiplication in NTT domain.
A timing comparison: schoolbook multiplication vs. NTT-based, at \(n = 64, 128, 256, 512\).
Present.
A log–log plot of multiplication time vs. polynomial degree.
The 256-point twiddle table as a tiny figure.
One paragraph: why \(q = 3329\) and \(\zeta = 17\) are the right numbers for \(n = 256\).
Project 4 — ★★ Toy Regev LWE encryption#
Description. Implement the original (Regev 2005) LWE-based public-key encryption scheme. Use \(n = 64\), \(q = 521\), and a discrete Gaussian error. Measure the decryption failure rate as you vary the error standard deviation \(\sigma\).
Literature.
Regev, O. (2005). On Lattices, Learning with Errors, Random Linear Codes, and Cryptography. STOC ‘05.
Peikert, C. (2016). A Decade of Lattice Cryptography, §4.
Upstream Chapter 40 — Lattice Problems, §40.8–40.12.
Implement.
A small discrete Gaussian sampler \(\mathrm{D}_{\mathbb{Z}, \sigma}\).
lwe_keygen(n, q, m, sigma)returning $(A, \mathbf{b} = A \mathbf{s}\mathbf{e} \bmod q)\( for secret \)\mathbf{s}$.
lwe_encrypt(pk, bit)andlwe_decrypt(sk, ct)— one bit per ciphertext.A Monte-Carlo decryption-failure curve: 1000 random encryptions at each \(\sigma \in \{1, 2, 4, 8, 16, 32\}\).
Present.
Plot DFR vs \(\sigma\) with the predicted Gaussian-tail bound overlay.
Comment on the trade-off: large \(\sigma\) → more security against LWE, more decryption failures.
Project 5 — ★★ Toy NTRU encryption (with decryption-failure study)#
Description. Build NTRU with the small parameters \((N, p, q) = (11, 3, 31)\). Implement polynomial multiplication in \(R = \mathbb{Z}[x]/(x^N - 1)\), polynomial inversion in \(R_p\) and \(R_q\), keygen, encrypt, decrypt. Estimate the decryption-failure rate as a function of \(q\).
Literature.
Exercise sheet 48, Ex 48.E3.
Hoffstein, J., Pipher, J., Silverman, J. H. (1998). NTRU: A Ring-Based Public Key Cryptosystem, ANTS-III.
Implement.
Cyclic-convolution polynomial multiplication in \(R\).
Polynomial extended Euclidean for inversion in \(R_p\) and \(R_q\) (do not depend on SymPy — see Ch 48 for a pure-Python version).
ntru_keygen,ntru_encrypt,ntru_decrypt.A Monte-Carlo DFR estimator over 500 random plaintexts at each \(q \in \{17, 31, 61, 127\}\).
Present.
DFR vs \(q\) plot (semilog y-axis).
The full set of polynomials \((f, g, F_p, F_q, h, m, r, c, a, b, m')\) for one decryption traced end-to-end.
Project 6 — ★★ McEliece on tiny linear codes#
Description. Implement the McEliece cryptosystem on small linear codes — start with Hamming\((7, 4)\) (trivial: corrects 1 error), then try a small Goppa code if time permits. Show the brute-force information-set-decoding attack on the public key.
Literature.
Upstream Chapter 43 — Code-Based Cryptography.
McEliece, R. J. (1978). A Public-Key Cryptosystem Based on Algebraic Coding Theory, DSN Progress Report 42-44.
Prange, E. (1962). The use of information sets in decoding cyclic codes. IRE Trans. Information Theory.
Implement.
LinearCodeclass with generator + parity-check matrices over \(\mathrm{GF}(2)\), plus syndrome decoding for Hamming\((7, 4)\).McEliece
keygen(scrambler \(S\) + permutation \(P\)),encrypt,decrypt.Brute-force ISD attack: pick random information sets, attempt decode, measure time vs code parameters.
Present.
One run of keygen, encrypt, decrypt with the matrices printed.
ISD running time on Hamming\((7, 4)\) vs Hamming\((15, 11)\), in a log plot.
Two sentences on why structured Goppa codes resist ISD where random codes do not.
Project 7 — ★★★ WOTS+ with bitmasks (Hülsing 2013)#
Description. Implement the full WOTS+ scheme with the per-step bitmask trick that defeats the multi-target attack on plain WOTS. Measure signature size, verifier hash count, and total wall-clock time vs. Lamport.
Literature.
Chapter 47, §47.3.
Hülsing, A. (2013). W-OTS+ — Shorter Signatures for Hash-Based Signature Schemes. AFRICACRYPT 2013.
RFC 8391 (XMSS), §3.1 (WOTS+ subroutines).
Implement.
Chain function \(H_{r_i}(x) := H(x \oplus r_i)\) with public bitmasks \(r_i\).
WOTS+ keygen, sign, verify for \(w = 16\) and \(n = 32\).
The Winternitz checksum (Ex 47.E3 is a warm-up).
Benchmark harness comparing WOTS+, Lamport, and Ed25519 (for reference) on signature size and signing time.
Present.
Bar chart: signature size in bytes for the three schemes.
A one-sentence reason why removing the bitmasks reintroduces the multi-target attack (cite Hülsing §3 or the chapter).
Project 8 — ★★★ Toy ML-KEM-512 without NTT#
Description. Implement a schoolbook (no-NTT) version of
ML-KEM-512: keygen, encapsulate, decapsulate, plus the
Fujisaki–Okamoto transform. Use the same parameters as FIPS 203
(n = 256, k = 2, q = 3329) but with naive polynomial multiplication.
Literature.
FIPS 203 (ML-KEM), the standardised spec.
Upstream Chapter 41 — ML-KEM (Kyber) Design and Implementation.
Bos et al. (2018). CRYSTALS-Kyber: a CCA-secure module-lattice-based KEM. EuroS&P.
Implement.
Centered binomial distribution (CBD) sampler — only \(\eta = 2\) or \(3\) needed.
Module operations \(R_q^{k \times k} \times R_q^k\).
K-PKE (CPA encryption); then FO-transform for IND-CCA security.
KAT (Known Answer Test) cross-check: pick a fixed seed, derive a ciphertext, and verify it matches a reference (
pqcryptopackage on PyPI is a good comparison point).
Present.
Sizes: pk = 800 B, sk = 1632 B, ct = 768 B — verify against FIPS 203.
One paragraph: which step needs the FO transform and why.
(Stretch) timing comparison vs.
pip install pqcrypto’s ML-KEM-512.
Project 9 — ★★★ LLL implementation + Merkle–Hellman break#
Description. Implement the LLL algorithm in pure Python (size reduction + Lovász condition + swap, working with Python integer arithmetic to stay exact). Then use your implementation to break a low-density Merkle–Hellman knapsack via the Lagarias–Odlyzko lattice.
Literature.
Chapter 49 — LLL Step-by-Step Tutorial — every building block you need, with a worked 2-D example and four applications.
Lenstra, Lenstra, Lovász (1982). Factoring polynomials with rational coefficients. Math. Ann. 261.
Lagarias & Odlyzko (1985). Solving low-density subset sum problems. J. ACM 32(1).
Implement.
gram_schmidt(B)returning \((B^*, \mu)\).size_reduce(B, mu)and a completelll(B, delta).Lagarias–Odlyzko lattice constructor
lo_lattice(a, S).A driver that generates a low-density \(n = 16\) Merkle–Hellman key, encrypts a plaintext, and runs LLL on the LO lattice to recover it.
Present.
For \(n = 8, 10, 12, 16\): density of the generated key vs LLL recovery rate (50 random instances per \(n\)).
A 2-D visualisation of the basis before/after LLL (Hadamard ratio).
One slide on why the attack works only below density 0.94 (cite Coster et al. 1992).
Mapping projects to course weeks#
Project |
Difficulty |
W1 |
W2 |
W3 |
W4 |
W5 |
W6 |
W7 |
W8 |
|---|---|---|---|---|---|---|---|---|---|
1 — Lamport CLI |
★ |
● |
|||||||
2 — Merkle MSS |
★ |
● |
|||||||
3 — NTT for Kyber |
★★ |
● |
|||||||
4 — Toy Regev LWE |
★★ |
● |
● |
||||||
5 — Toy NTRU + DFR |
★★ |
● |
|||||||
6 — McEliece on small codes |
★★ |
● |
|||||||
7 — WOTS+ with bitmasks |
★★★ |
● |
|||||||
8 — Toy ML-KEM-512 |
★★★ |
● |
|||||||
9 — LLL + knapsack break |
★★★ |
● |
● |
||||||
10 — HNP / biased ECDSA |
★★★★ |
● |
● |
A “●” means the project’s prerequisites are taught in that week. The four-week practical window (W5 → W8) is what you should plan around: pick the topic by end of W4, code in W5–W7, present in W8.
Things that are NOT good projects#
(Listed so you don’t waste a week on them.)
“Implement Falcon from FIPS 206 draft.” Falcon’s Gaussian sampler uses fast Fourier sampling on NTRU lattices; the implementation is a multi-month undertaking and the security-critical constant-time requirements are unforgiving. Read the spec for fun, do not try to reimplement.
“Break RSA-2048 with Shor on a real quantum computer.” Even the largest current quantum devices cannot factor 21 reliably. Pick a classical project.
“Implement SLH-DSA-SHA2-256s end to end.” ~1500 lines of careful code, lots of stateless-deterministic-derivation traps. The size-accounting exercise (Ex 47.E6) is the right scope.
“Rederive the Castryck–Decru attack.” Months of supersingular- isogeny background required. Project 10 (HNP/ECDSA) is the more tractable lattice-cryptanalysis project at the same level of cryptanalytic drama.
Office hours#
If you are stuck on any of these, bring a minimal failing example to
office hours. “My LLL output is wrong” is hard to debug; “here is a
2-cell notebook where my gram_schmidt(B_bad) returns a non-orthogonal
result” is easy.
Good luck.