From 1983d840a7040d89ef80df6c29e017c74ebf2804 Mon Sep 17 00:00:00 2001 From: FallenSigh Date: Fri, 26 Jun 2026 00:41:52 +0800 Subject: [PATCH] docs: add comprehensive README.md - Project overview and FIPS 203 context - Full repository structure with module descriptions - Interface protocol documentation (valid/ready handshake) - Getting Started guide (XSIM and Verilator) - Design decisions (Barrett, NTT, Keccak architecture) - Module reference table with ports/latency - Test coverage matrix (22 modules) - Vivado 2019.2 compatibility notes - TODO roadmap --- README.md | 299 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 299 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..50cd420 --- /dev/null +++ b/README.md @@ -0,0 +1,299 @@ +# ML-KEM Hardware Implementation (FIPS 203) + +A synchronous, pipelined hardware implementation of **ML-KEM** (Module-Lattice-based Key Encapsulation Mechanism), the NIST PQC standard based on Kyber. Written in SystemVerilog, targeting FPGA simulation with Vivado XSIM and verified with Verilator. + +## Overview + +ML-KEM is a post-quantum key encapsulation mechanism (KEM) standardized by NIST in FIPS 203. It provides IND-CCA2 security based on the hardness of the Module Learning With Errors (MLWE) problem over the polynomial ring Z_q[x]/(x^256 + 1). + +This implementation decomposes ML-KEM's core operations into independent, synchronous hardware modules with standardized valid/ready streaming interfaces. All modules operate at **100 MHz** (10ns period) and use active-low reset. + +### Parameters + +| Parameter | Value | Description | +|-----------|-------|-------------| +| **q** | 3329 | Prime modulus | +| **n** | 256 | Polynomial degree | +| **k** | 2 | Module rank (ML-KEM-512) | +| η₁ | 3 | CBD parameter (secret key) | +| η₂ | 2 | CBD parameter (ciphertext) | +| d_u | 10 | Compress bits | +| d_v | 4 | Compress bits | + +## Repository Structure + +``` +mlkem/ +├── sync_rtl/ # RTL source (SystemVerilog) +│ ├── common/ # Shared infrastructure +│ │ ├── pipeline_reg.v # Single-stage valid/ready pipeline register +│ │ ├── skid_buffer.v # 2-entry skid buffer for backpressure +│ │ └── defines.vh # Global parameters (Q, N, CLK_PERIOD) +│ ├── sha3/ # Keccak-f[1600] and SHA-3/SHAKE modes +│ │ ├── keccak_round.v # Single Keccak-f round (θ,ρ,π,χ,ι) +│ │ ├── keccak_core.v # 24-round sequential Keccak-f[1600] core +│ │ └── sha3_top.v # SHA3-512(G)/SHA3-256(H)/SHAKE-256(J) wrapper +│ ├── sha3_chain/ # G function for key generation +│ │ └── sha3_chain_top.v # SHA3-512 chain: G(d||k=2) → rho, sigma +│ ├── rng/ # Pseudorandom number generator +│ │ └── rng_sync.v # 256-bit Galois LFSR (taps: 255,253,252,247,0) +│ ├── ntt/ # Number Theoretic Transform +│ │ ├── zeta_rom.v # Twiddle factor ROM (128 × 12-bit, ζ^br(i)) +│ │ ├── barrett_mul.v # Barrett modular multiplier (a·b mod q) +│ │ ├── butterfly_unit.v # CT/GS butterfly (NTT/INTT) +│ │ └── ntt_core.v # NTT core: LOAD→COMPUTE→OUTPUT FSM +│ ├── poly_arith/ # Polynomial arithmetic +│ │ └── poly_arith_sync.v # Element-wise poly add/sub (PolyAdd/PolySub) +│ ├── poly_mul/ # Polynomial multiplication +│ │ ├── poly_mul_zeta_rom.v # Zeta ROM for degree-1 basecase multiply +│ │ ├── basecase_mul.v # Degree-1 Karatsuba basecase multiplier +│ │ └── poly_mul_sync.v # Full NTT-domain polynomial multiplier +│ ├── sample_cbd/ # Centered Binomial Distribution sampling +│ │ └── sample_cbd_sync.v # CBDη via SHAKE-256 PRF(seed, nonce) +│ ├── sample_ntt/ # NTT-domain sampling (A matrix) +│ │ └── sample_ntt_sync.v # SampleNTT via SHAKE-128 rejection sampling +│ ├── comp_decomp/ # Coefficient compression +│ │ └── comp_decomp_sync.v # Compress_q / Decompress_q +│ ├── mod_add/ # Modular arithmetic +│ │ └── mod_add_sync.v # (a + b) mod q, streaming +│ └── storage/ # On-chip storage +│ ├── s_bram.v # Single-port behavioral BRAM +│ └── sd_bram.v # Simple dual-port behavioral BRAM +│ +├── test_framework/ # Verilator C++ test framework +│ ├── run_all.py # CLI entry point +│ ├── config.json # Verilator path, clock period, timeouts +│ ├── lib/ # Core framework libraries +│ │ ├── test_runner.py # Discovery, compile, run, compare pipeline +│ │ ├── sim_controller.py # Verilator compile/run wrapper +│ │ ├── vector_gen.py # Base class for vector generators +│ │ ├── result_checker.py # Hex-file comparison +│ │ └── reporter.py # Terminal + HTML output +│ └── modules/ # Per-module test definitions +│ ├── /test_plan.json # Test configuration +│ └── /gen_vectors.py # Python reference + vector generator +│ +├── run_tb.sh # Vivado XSIM testbench runner +├── .trellis/ # Trellis workflow system +│ ├── workflow.md # Development phases +│ ├── spec/ # Coding specs (RTL, testbench conventions) +│ └── tasks/ # Active and archived tasks +└── .opencode/ # OpenCode agent configuration +``` + +## Module Architecture + +### Core Operations + +``` + ┌──────────┐ + seed │ sample_ │ coeffs (256 × 12-bit) + nonce ─┤ cbd_sync ├─────────────────────┐ + └──────────┘ │ + ▼ + ┌──────────┐ ┌─────────────┐ + rho │ sample_ │ coeffs │ poly_arith │ + k,i,j─┤ ntt_sync ├─────────────┤ poly_mul │──► result + └──────────┘ │ comp_decomp │ + └─────────────┘ + ┌──────────┐ ▲ + d_in │ sha3_ │ rho, sigma │ + │ chain_top├────────────────────┘ + └──────────┘ +``` + +### Interface Protocol + +All modules use a uniform **valid/ready** streaming interface: + +``` +clk ──╮ ╰──╮ ╰──╮ ╰──╮ ╰── +valid_i ──╯ ╰─────╯ ╰───── +ready_o ──────╮ ╰───────── +data_i ──[A]─────[B]─────────[C]── +valid_o ─────────╮ ╰─────── +ready_i ─────────────╮ ╰───────── +data_o ─────────[A']───────[B']── +``` + +- **Input**: Assert `valid_i` when `ready_o` is high; data transferred on posedge when both are high. +- **Output**: Module asserts `valid_o` when result is ready; downstream asserts `ready_i` to consume. +- **Pipeline**: Modules use `pipeline_reg` internally for 1-cycle latency. + +Modules with multi-cycle operations (NTT, sampling) additionally use a `done_o` signal or `last_o` flag. + +## Getting Started + +### Prerequisites + +- **Vivado 2019.2+** (for XSIM simulation): `/opt/Xilinx/Vivado/2019.2/` +- **Verilator 5.046** (for C++ testbench): available via `dnf` on Fedora +- **Python 3.10+** (for vector generation): stdlib only + +### Setup + +```bash +# Clone repository +git clone mlkem +cd mlkem + +# Source Vivado (for XSIM) +source /opt/Xilinx/Vivado/2019.2/settings64.sh +export LD_PRELOAD=/usr/lib64/libtinfo.so.5 # ncurses fix for 2019.2 on modern Linux +``` + +### Running Tests + +#### Vivado XSIM (Verilog Testbench) + +```bash +# List available modules +./run_tb.sh --list + +# Run a specific module +./run_tb.sh mod_add +./run_tb.sh ntt +./run_tb.sh sample_cbd + +# Run all modules +for m in mod_add rng poly_arith comp_decomp storage \ + sha3_chain ntt poly_mul sample_cbd sample_ntt; do + ./run_tb.sh "$m" +done +``` + +Each module's testbench is in `sync_rtl//TB/`: +- `tb__xsim.v` — Verilog testbench (file-based vectors via `$readmemh`) +- `gen_vectors.py` — Python vector generator +- `vectors/_input.hex` — Test input vectors +- `xsim_run.tcl` — Vivado compile/elaborate/simulate script + +#### Verilator (C++ Testbench) + +```bash +cd test_framework +python3 run_all.py --list # List modules +python3 run_all.py --module ntt # Test a single module +python3 run_all.py --quick # Smoke test all modules +``` + +### Manual XSIM Commands + +```bash +# Compile +xvlog -sv -i . sync_rtl/common/pipeline_reg.v sync_rtl/mod_add/mod_add_sync.v +xvlog -sv sync_rtl/mod_add/TB/tb_mod_add_xsim.v + +# Elaborate +xelab tb_mod_add_xsim -s sim --timescale 1ns/1ps + +# Simulate +xsim sim -R +``` + +## Design Decisions + +### Synchronous Valid/Ready Streaming + +All modules use a synchronous valid/ready handshake rather than fixed-latency interfaces. This allows: +- Natural backpressure propagation +- Easy composition of modules in pipelines +- Deterministic timing closure at 100MHz + +### Barrett Modular Reduction + +All modular multiplications use Barrett reduction (no DSP blocks, no division units): +- Precompute μ = ⌊2^k / q⌋ (k = 24 for q=3329) +- Compute a·b ≈ (a·b·μ) >> k, then correct with conditional subtraction +- Fully combinational, no pipeline stalls + +### Cooley-Tukey / Gentleman-Sande NTT + +The NTT core implements both forward (Cooley-Tukey) and inverse (Gentleman-Sande) transforms using a radix-2 decimation-in-time architecture: +- 7 butterfly stages (256 = 2^7 coefficients) +- Bit-reversed input/output ordering +- On-chip coefficient register file (256 × 12-bit) +- 24-cycle pipeline for Keccak permutations (shared with SHA-3 modules) + +### Keccak-f[1600] Core + +A single Keccak-f[1600] permutation engine is shared across all SHA-3/SHAKE modules (`sha3_top`, `sample_cbd_sync`, `sample_ntt_sync`). The core implements: +- 24 rounds with round constants (ι step) +- Full 1600-bit state (5×5×64 lanes) +- 24-cycle latency per permutation +- Input/output via valid/ready streaming interface + +## Module Reference + +| Module | Ports | Latency | Description | +|--------|-------|---------|-------------| +| `pipeline_reg` | data_i/o, valid_i/o, ready_i/o | 1 cycle | Generic pipeline stage | +| `skid_buffer` | data_i/o, valid_i/o, ready_i/o | 0-1 cycles | Backpressure buffer | +| `rng_sync` | valid_i → data_o[255:0] | 1 cycle | Galois LFSR PRNG | +| `mod_add_sync` | a[11:0], b[11:0] → sum[11:0] | 1 cycle | Modular addition | +| `ntt_core` | 256×coeff_in → 256×coeff_out | ~200 cycles | NTT/INTT transform | +| `poly_arith_sync` | coeff_a/b[11:0] → coeff_out[11:0] | 1 cycle | Poly add/sub | +| `poly_mul_sync` | 512×coeff → 256×coeff | ~300 cycles | NTT-domain poly multiply | +| `comp_decomp_sync` | coeff_in[11:0], d[4:0] → coeff_out | 1 cycle | Compress/Decompress | +| `sha3_top` | data_i[511:0], mode → hash_o[511:0] | ~24 cycles | SHA3/SHAKE | +| `sha3_chain_top` | d_in[255:0], start → rho, sigma | ~24 cycles | G function | +| `sample_cbd_sync` | seed[255:0], nonce, eta → 256×coeff | ~300 cycles | CBD sampling | +| `sample_ntt_sync` | rho[255:0], k,i,j → 256×coeff | ~4000 cycles | SampleNTT | +| `s_bram` | rd/wr addr, data | 1 cycle | Single-port BRAM | +| `sd_bram` | rd addr, wr addr, data | 1 cycle | Dual-port BRAM | + +## Test Coverage + +| Module | Verilator (C++) | XSIM (Verilog) | Status | +|--------|:---:|:---:|:---:| +| sha3_top | ✅ | ✅ | PASS | +| keccak_core | — | ✅ | PASS | +| sha3_chain_top | ✅ | ✅ | PASS | +| rng_sync | ✅ | ✅ | PASS | +| mod_add_sync | ✅ | ✅ | PASS | +| ntt_core | ✅ | ✅ | PASS | +| poly_arith_sync | ✅ | ✅ | PASS | +| poly_mul_sync | ✅ | ✅ | PASS | +| comp_decomp_sync | ✅ | ✅ | PASS | +| sample_cbd_sync | ✅ | ✅ | PASS | +| sample_ntt_sync | ✅ | ✅ | PASS | +| s_bram / sd_bram | ✅ | ✅ | PASS | +| pipeline_reg | Through parent | — | OK | +| skid_buffer | Through parent | — | OK | + +## Vivado 2019.2 Compatibility Notes + +This project was tested with Vivado 2019.2 on Fedora 44. Known workarounds: + +```bash +# Required: ncurses compatibility library +export LD_PRELOAD=/usr/lib64/libtinfo.so.5 + +# Use -i flag (not -include_dirs) for include paths +xvlog -sv -i . .v + +# Add --timescale to xelab +xelab -s --timescale 1ns/1ps + +# Add --relax for strict SystemVerilog mode +xvlog -sv --relax .v +``` + +## TODO / Roadmap + +- [ ] Top-level integration module (full KeyGen / Encaps / Decaps FSM) +- [ ] AXI-Stream bridge for FPGA integration +- [ ] Resource optimization (share Keccak instances, pipeline balancing) +- [ ] Formal verification of Barrett multiplier +- [ ] Power analysis and side-channel hardening +- [ ] XDC constraints for FPGA synthesis (timing, I/O) + +## License + +[Specify license] + +## References + +- [FIPS 203: ML-KEM](https://csrc.nist.gov/pubs/fips/203/final) — NIST standard +- [FIPS 202: SHA-3 / SHAKE](https://csrc.nist.gov/pubs/fips/202/final) — Keccak-based hash +- [CRYSTALS-Kyber](https://pq-crystals.org/kyber/) — Original submission