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
This commit is contained in:
299
README.md
Normal file
299
README.md
Normal file
@@ -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
|
||||
│ ├── <module>/test_plan.json # Test configuration
|
||||
│ └── <module>/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 <repo-url> 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/<module>/TB/`:
|
||||
- `tb_<module>_xsim.v` — Verilog testbench (file-based vectors via `$readmemh`)
|
||||
- `gen_vectors.py` — Python vector generator
|
||||
- `vectors/<module>_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 . <file>.v
|
||||
|
||||
# Add --timescale to xelab
|
||||
xelab <top> -s <snap> --timescale 1ns/1ps
|
||||
|
||||
# Add --relax for strict SystemVerilog mode
|
||||
xvlog -sv --relax <file>.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
|
||||
Reference in New Issue
Block a user