Post-quantum encryption & signature — learn, implement, migrate.
A hands-on, 12-week curriculum to go from cryptography fundamentals to post-quantum expert. You'll study the theory, write real Rust code using NIST-standardized PQC algorithms, and learn how to migrate existing systems (TLS, SSH, blockchains, wallets) before quantum computers arrive.
Shor's algorithm — running on a sufficiently large fault-tolerant quantum computer — breaks RSA, ECDSA, Ed25519, ECDH, and all classical public-key cryptography built on the discrete-log or integer-factorization problems. Grover's algorithm quadratically weakens symmetric primitives (mitigable by doubling key sizes). The NIST Post-Quantum Cryptography Standardization process has finalized three families of quantum-resistant algorithms, and the migration of global cryptographic infrastructure is already underway (CNSA 2.0, Mozilla, Google, Cloudflare).
This repository is your structured path to understanding, using, and deploying these algorithms.
- Rust — comfortable reading and writing Rust. Install via rustup.
- Basic cryptography — you should know what a hash function, a signature scheme, and a key exchange are. You don't need to be a cryptographer.
- Basic algebra — modular arithmetic, polynomials (helps for lattices).
Theory
- How Shor's algorithm breaks RSA/ECC (high-level, no quantum mechanics required)
- How Grover's algorithm affects symmetric primitives
- Timeline estimates: when will a CRQC (Cryptographically Relevant Quantum Computer) arrive?
- Post-Quantum Cryptography vs. Quantum Key Distribution (PQC ≠ QKD)
- NIST PQC standardization process: rounds, finalists, what got selected
Practice
- Set up this Rust project with
cargo init - Implement or review: SHA-256 hashing via
sha2crate, HMAC, keyed signatures with Ed25519 usinged25519-dalek - Write a small binary that generates an Ed25519 keypair, signs a message, and verifies
- Run a benchmark on Ed25519 signing throughput
Resources
- NIST PQC Standardization
- NSA CNSA 2.0
- Bernstein & Lange: Post-quantum cryptography — the canonical overview paper
Theory
- What is a lattice? Visual intuition in 2D
- Shortest Vector Problem (SVP) — the hard problem lattice crypto rests on
- Learning With Errors (LWE) and Short Integer Solution (SIS)
- Ring-LWE and Module-LWE (why rings make it practical)
- Why lattices are the most versatile PQC family (KEM + signatures + FHE)
Practice
- Implement a toy LWE encryption scheme in Rust (dimension ~10) using
ndarrayfor matrix ops - Add noise, encrypt a bit, decrypt — see LWE work with errors
- Use
aligned-vecor justVec<u8>for polynomial representations - Compare key/ciphertext sizes between your toy scheme and real Kyber parameters
Resources
- Regev's original LWE paper (2005)
- Peikert: A Decade of Lattice Cryptography
- The Learning With Errors problem — Simons Institute talk
Theory
- CRYSTALS-Kyber architecture: IND-CCA2 KEM built from Module-LWE
- Compression, noise flooding, Fujisaki-Okamoto transform
- Key generation, encapsulation, decapsulation flow
- Parameter sets: ML-KEM-512, ML-KEM-768, ML-KEM-1024
- Comparison with ECDH: what changes, what stays the same
Practice
- Add
pqcrypto-kyber(orlibcrux-kyber) to your project - Implement: keygen → encaps → decaps round-trip
- Write tests for all three security levels
- Serialize keys and ciphertexts to bytes, deserialize back
- Benchmark encapsulation/decapsulation throughput
Resources
Theory
- CRYSTALS-Dilithium architecture: Fiat-Shamir with aborts from Module-LWE/Module-SIS
- How Dilithium differs from classical Fiat-Shamir (Schnorr) signatures
- Key generation, signing, verification flow
- Parameter sets: ML-DSA-44, ML-DSA-65, ML-DSA-87 (security levels 2, 3, 5)
- Signature size vs. verification speed trade-off
Practice
- Add
pqcrypto-dilithium(orlibcrux-mldsa) to your project - Implement: keygen → sign → verify round-trip
- Sign a message, tamper with it, confirm verification fails
- Serialize/deserialize keys and signatures
- Compare Dilithium signature sizes to Ed25519 (vs. 64 bytes)
Resources
Theory
- FALCON: lattice signatures based on GPV framework and NTRU lattices
- Fast Fourier sampling — why FALCON is harder to implement
- Parameter sets (Falcon-512, Falcon-1024)
- Dilithium vs. FALCON: signature size vs. implementation complexity
- FPQC — FALCON's successor being standardized
Practice
- Add
pqcrypto-falconto your project - Implement keygen → sign → verify for Falcon-512
- Serialize Falcon signatures (very compact — ~666 bytes)
- Write a comparison table: key sizes, signature sizes, verify speed for Dilithium vs. FALCON
- Bonus: try building a combined
enum Signature { Dilithium(…), Falcon(…) }with a common trait
Resources
- FALCON website
- Prest et al.: FALCON specification
- NIST FPQC announcement
Theory
- Hash-based signatures: security only assumes hash function is secure
- One-time signatures (Winternitz OTS, WOTS+), Merkle trees (MSS, XMSS)
- SPHINCS+: combining few-time signatures with hypertrees (stateless)
- SLH-DSA parameter sets and signature sizes (~5–50 KB depending on parameters)
- Why hash-based signatures matter: conservative security, no lattice assumptions
Practice
- Add
pqcrypto-sphincsplus(orslh-dsa) to your project - Implement: keygen → sign → verify for SLH-DSA-SHAKE-128s and -128f
- Understand the speed/size trade-off: "s" (small) vs. "f" (fast) variants
- Benchmark signing speed (SPHINCS+ signing is slow — compare to Dilithium)
- Write a test that signs a 1 MB message and verifies
Resources
- FIPS 205 (SLH-DSA)
- SPHINCS+ website
- Bernstein et al.: SPHINCS+ specification
Theory
- Classic McEliece: code-based encryption from Goppa codes
- Key sizes (~1 MB public key) vs. fast encryption/decryption
- Why McEliece is relevant despite large keys (conservative, well-studied)
- SIKE (isogeny-based): what it was, and how it was broken by Castryck-Decru
- Brief overview: multivariate (Rainbow — broken by Tao), Picnic (MPC-in-the-head)
- The security assumption diversity argument: don't put all eggs in the lattice basket
Practice
- Add
pqcrypto-classicmcelieceto your project - Explore McEliece key sizes (generate, serialize, inspect the file size)
- Try generating a McEliece keypair — note how long it takes
- Encrypt and decrypt a message with McEliece
- Write a summary table comparing all three families: security assumption, key size, ciphertext/sig size, speed
Resources
- Classic McEliece website
- Bernstein, Lange, et al.: Classic McEliece: conservative code-based cryptography
- Castryck & Decru: An efficient key recovery attack on SIDH
Theory
- Hybrid key exchange: ECDHE + Kyber combined (why both, not just Kyber?)
- Hybrid signatures: Ed25519 + Dilithium together
- Combining strategies: concatenation, nested, and compound schemes
- Composite (OID-based) vs. hybrid (multi-cert) approaches in PKI
- X.509 hybrid certificates: how they look, how CAs issue them
Practice
- Build a hybrid signer in Rust: sign with both Ed25519 and ML-DSA-65, output a combined signature
- Build a hyrypt verifier: accept a combined signature and verify both components
- Build a hybrid KEM: encapsulate a shared secret using both ECDH and Kyber, derive a combined key via HKDF
- Write tests: hybrid round-trip, tamper one component → fail both
- Serialize hybrid keys/signatures to a custom format
Resources
Theory
- TLS 1.3 handshake with PQC: how the KEM replaces (or augments) ECDHE
- OQS-OpenSSL and BoringSSL forks with PQC support
- X.509 hybrid certificates in practice (CA issuance, chain validation, path building)
- SSH keys: OpenSSH's PQC KEX (sntrup761 + Curve25519 hybrid)
- SSH with PQC certificates and host keys
- Code signing: Windows Authenticode, macOS notarization with PQC
Practice
- Build OQS-OpenSSL from source (or use their Docker image)
- Set up a TLS 1.3 server with Kyber + X25519 hybrid KEM
- Connect with
s_clientand observe the hybrid KEX in the handshake - Set up a TLS 1.3 server with a Dilithium-signed certificate chain
- Practice: generate ML-DSA keys, create a self-signed cert, serve HTTPS
- Install OQS-BoringSSL and benchmark PQC vs. classical TLS handshake times
Resources
- Open Quantum Safe project
- OQS-OpenSSL GitHub
- OpenSSH PQC KEX
- Cloudflare: The TLS Post-Quantum Experiment
Theory
- What to measure: keygen time, sign/encrypt time, verify/decrypt time, signature/ciphertext size
- Constant-time execution: why crypto code must not branch on secrets
- Side-channel resistance: timing attacks, cache attacks (like Prime+Probe)
- SIMD optimizations: how Kyber uses AVX2, NEON, vector instructions
- Stack vs. heap: how crypto implementations manage memory for security
Practice
- Add
criterion,iai, ordivanbenchmarks to your project - Benchmark every PQC scheme you've implemented so far (Weeks 3–7):
- Key generation throughput
- Sign/encrypt throughput
- Verify/decrypt throughput
- Generate flamegraphs for hot functions (
perf+flamegraph-rs) - Compare Rust
pqcrypto-*bindings to the reference C implementations - Bonus: run under
valgrind/cargo auditablefor binary provenance
Resources
- The Benchmarking Book (Rust)
- Bernstein & Lange: ebacs — ECRYPT Benchmarking of Asymmetric Systems
- cargo-criterion
Theory
- Quantum threats to blockchain: Bitcoin address derivation (public key revealed on spend), Ethereum account recovery
- Address schemes: what information is on-chain (hash vs. public key)
- State management for signature migration: how do you change the signature scheme of an existing account?
- Account abstraction (ERC-4337) and how it enables smooth signature scheme upgrades
- Post-quantum blockchain designs: Algorand, QANplatform, Quantum Resistant Ledger
- Signature aggregation in PQ blockchains
Practice
- Design and implement a hybrid wallet scheme: a wallet that generates both an Ed25519 and an ML-DSA keypair
- Implement a "migration transaction": a tx that proves control of an old classical key and replaces it with a PQC key
- Build a simplified UTXO model (in Rust) where outputs can be spent with either classical OR hybrid signatures
- Benchmark transaction sizes: classical vs. hybrid vs. pure PQ
- Write a threat model: harvest-now-decrypt-later attacks on blockchain data
Resources
- Bitcoin and quantum computing (Waterland, 2020)
- Algorand's quantum-safe signature proposal
- ERC-4337 (Account Abstraction)
- Quantum Resistant Ledger
Theory (review, minimal new material)
- Recap: end-to-end migration checklist
- CNSA 2.0 compliance timeline
- Cryptographic agility: designing systems that can rotate signature/KEM schemes
- Organizational rollout: key rotation, backward compatibility, phased deprecation
Practice — Full capstone project:
Take a minimal blockchain node (or build a toy one with tendermint-rs or a custom chain) that currently uses Ed25519:
- Add PQC keys: users can generate ML-DSA or hybrid wallets
- Hybrid transactions: sign transactions with both Ed25519 + ML-DSA, verify either or both
- Migration transaction: an on-chain message that proves ownership of a classical key and installs a PQC one for future spends
- State upgrade: add a version byte to account state so validators know which signature scheme applies
- P2P layer: use Kyber + X25519 hybrid KEM for node-to-node encrypted communication (instead of just ECDH)
- Benchmark: compare block propagation time, verify throughput, and storage requirements before vs. after migration
Deliver: cargo test passes, README documents your decisions, and you can demo the hybrid node on localhost.
Resources
After completing the 12 weeks, you should be able to apply PQC in real systems. Here's a reference cheatsheet for common migration scenarios.
| Step | What to do |
|---|---|
| 1 | Use OQS-OpenSSL or BoringSSL with hybrid KEM (X25519 + Kyber) |
| 2 | Issue hybrid X.509 certificates (Ed25519 + ML-DSA) |
| 3 | Enable PQ key exchange in server config (Groups: X25519MLKEM768) |
| 4 | Monitor handshake latency; hybrid adds ~1 round-trip worth of processing |
| 5 | Fall back to classical-only if client doesn't support PQC |
| Step | What to do |
|---|---|
| 1 | Upgrade OpenSSH to ≥9.9 for sntrup761x25519-sha512 KEX |
| 2 | Generate ML-DSA host keys |
| 3 | Distribute new PQ host keys via SSHFP records in DNSSEC |
| 4 | Use hybrid KEX in ssh_config (KexAlgorithms sntrup761x25519-sha512,...) |
| 5 | Keep classical keys during transition for backward compat |
| Step | What to do |
|---|---|
| 1 | Generate ML-DSA signing keys |
| 2 | Submit PQ public key to your CA/timestamp authority |
| 3 | Sign binaries with both classical + PQ signature |
| 4 | Verify on systems that support PQ; fall back to classical on older systems |
| Challenge | Approach |
|---|---|
| Key generation | Generate hybrid keypair (Ed25519 + ML-DSA) at wallet creation time |
| Address scheme | Use hash of combined public key; no on-chain change needed |
| Signature scheme | Use account abstraction (ERC-4337) or explicit version field |
| State migration | Migration transaction: spend from classical key, create new PQ-locked output |
| P2P encryption | Use hybrid KEM (ECDH + Kyber) for block propagation |
| Backward compat | Accept both classical and PQ signatures during transition period |
| Harvest-now | Deploy NOW — attackers can record encrypted traffic today and decrypt it later with a CRQC |
Use these crates for the practice exercises above.
| Crate | Algorithm(s) | Notes |
|---|---|---|
pqcrypto-kyber |
ML-KEM (Kyber) | Wraps C reference impl |
pqcrypto-dilithium |
ML-DSA (Dilithium) | Wraps C reference impl |
pqcrypto-falcon |
FALCON | Wraps C reference impl |
pqcrypto-sphincsplus |
SLH-DSA (SPHINCS+) | Wraps C reference impl |
pqcrypto-classicmceliece |
Classic McEliece | KEM |
pqcrypto-traits |
Common traits | Shared interface for above |
libcrux-kyber |
ML-KEM (Kyber) | Pure Rust, formally verified |
libcrux-mldsa |
ML-DSA (Dilithium) | Pure Rust, formally verified |
libcrux-sphincs |
SLH-DSA (SPHINCS+) | Pure Rust |
oqs |
All OQS algorithms | Rust bindings to liboqs |
The libcrux-* crates are recommended for production use — they're pure Rust, formally verified, and actively maintained by the Cryspen team. The pqcrypto-* crates are great for learning and prototyping since they closely follow the NIST reference implementations.