feat(enc): Encaps E0 - op_i/msg_i/ek-load scaffold + H(ek)+G(m||H(ek))
Extend mlkem_top with a runtime op_i select (0=KeyGen, 1=Encaps) and the
first Encaps stages, reusing the shared keccak_core and the ST_H multi-block
SHA3-256 machinery:
ST_ENC_H: H(ek) over preloaded ek_bram (same FSM as KeyGen ST_H)
ST_ENC_G: (K,r) = G(m||H(ek)) via new 64-byte single-block SHA3-512
- sha3_top_shared: add mode=2'b11 = SHA3-512 over a full 512-bit message
(g512_pad). Standalone tb_sha3_g512 confirms it byte-exact.
- mlkem_top: new ports op_i, msg_i, ek_in_{we,addr,byte} (ek preload), ss_o,
dbg_ct_*, dbg_r_o/dbg_hek_o. st widened 4->5 bits; ST_ENC_* states added.
Renamed message port to msg_i to avoid collision with ST_M counter m_i.
- TB tb_mlkem_enc_katK + gen_encaps_vectors.py (per-byte ek/m/ct/ss vectors).
Verified ss==KAT.ss for K=2/3/4, cases 0-2 (all PASS). KeyGen unaffected
(K=2 c0 still ek==pk, dk==sk byte-exact).
This commit is contained in:
90
.claude/plans/encaps_plan.md
Normal file
90
.claude/plans/encaps_plan.md
Normal file
@@ -0,0 +1,90 @@
|
||||
# ML-KEM Encaps 顶层集成 — 实现计划
|
||||
|
||||
> 在现有 `mlkem_top`(KeyGen 已 KAT 通过)基础上扩展 Encaps(FIPS 203 Alg 17 + K-PKE.Encrypt Alg 14)。
|
||||
> 决策(已与用户确认):**(1) 统一进 mlkem_top(加 op_i 选择 + ST_ENC_* 状态,复用同一套叶子 + BRAM 银行);(2) 一上来就做全 K(2/3/4);(3) golden 只做端到端 KAT(ct==KAT.ct, ss==KAT.ss),逐级用 dbg tap 在 bring-up 时对拍。**
|
||||
|
||||
## 算法(Encaps,全 K)
|
||||
输入:ek(=KAT.pk,384K+32 B)、m(=KAT.msg,32 B)。输出:K=ss(32 B)、c=ct(32·(du·K+dv) B)。
|
||||
1. `h = H(ek)` = SHA3-256(ek),多块。
|
||||
2. `(K_ss, r) = G(m ‖ h)` = SHA3-512(64 B,单块)。`K_ss = hash[255:0]`,`r = hash[511:256]`。**ss 即 K_ss,无 KDF/J。**
|
||||
3. `t̂[i] = byteDecode₁₂(ek[i·384 .. (i+1)·384])`,i∈0..K-1;`rho = ek[384K .. 384K+32]`。
|
||||
4. `Â[i][j] = SampleNTT(rho‖j‖i)` —— 与 KeyGen ST_A 完全相同。
|
||||
5. `y[i] = CBD_η1(PRF(r, i))`,i=0..K-1(nonce 0..K-1)。
|
||||
6. `e1[i] = CBD_η2(PRF(r, K+i))`,i=0..K-1(nonce K..2K-1)。**η2 恒=2。**
|
||||
7. `e2 = CBD_η2(PRF(r, 2K))`(nonce 2K)。
|
||||
8. `ŷ[i] = NTT(y[i])`(mode=0)。
|
||||
9. `u[i] = INTT(Σⱼ Â[j][i]∘ŷ[j]) + e1[i]`,i=0..K-1。**注意转置:用 Â[j][i](slot=j·K+i),不是 KeyGen 的 Â[i][j]。** INTT = ntt_core mode=1(内置 ×3303 缩放)。
|
||||
10. `mu[c] = Decompress₁(byteDecode₁(m)[c])` = m 第 c bit ? 1665 : 0(无需存储,m 寄存器逐 bit 算)。
|
||||
11. `v = INTT(Σⱼ t̂[j]∘ŷ[j]) + e2 + mu`。
|
||||
12. `c1 = byteEncode_du(Compress_du(u[i]))` 拼 K 个;`c2 = byteEncode_dv(Compress_dv(v))`;`ct = c1 ‖ c2`。
|
||||
13. du/dv:K=2/3 → (10,4);K=4 → (11,5)。ct 长度:K=2→768,K=3→1088,K=4→1568。
|
||||
|
||||
## 复用与新增
|
||||
|
||||
### 直接复用(零改动或仅加 phase mux)
|
||||
- `u_sha3`(sha3_top_shared):H(ek) 多块 + G 单块 —— 与 KeyGen ST_H/ST_G 同。
|
||||
- 共享 `u_keccak`:扩 sel_* 4-way mux,加 Encaps phase 选择。
|
||||
- `u_snt`(sample_ntt):A 再生 —— 与 ST_A 同。
|
||||
- `u_cbd`(sample_cbd):y/e1/e2 —— **新增 η2=2 驱动**(eta_i 现仅接 eta1_rt;Encaps 的 e1/e2 phase 驱动 2)。
|
||||
- `u_ntt`(ntt_core):ŷ 用 mode=0;u/v 的 INTT 用 **mode=1**(缩放已内置)。
|
||||
- `u_pmul`(poly_mul):点乘 Â[j][i]∘ŷ[j] 与 t̂[j]∘ŷ[j];累加在顶层逐系数 modQ(同 ST_M)。
|
||||
- 3 个系数银行 bank_a/bank_se/bank_t:**容量够,见存储编排**,只加 Encaps 的读写 phase mux。
|
||||
|
||||
### 新增 RTL
|
||||
- `byteDecode₁₂`:ek 3 字节 → 2 系数。`c0=b0|((b1&0xF)<<8); c1=(b1>>4)|(b2<<4)`。组合逻辑,流式写 bank_t。
|
||||
- `Compress` 实例:实例化现有 `comp_decomp_sync`(mode=0,d=du/dv),top 当前未用。
|
||||
- `byteEncode_d` 通用位打包器(d∈{4,5,10,11}):把 d-bit 压缩值 LSB-first 流式打包成字节写 ct_bram。**新增**(现有 byteEncode12 是 2→3 硬编码,不通用)。
|
||||
- `mu` 流内生成:INTT v 输出时按 m 寄存器对应 bit 加 1665。
|
||||
- `ct_bram`(sd_bram W=8 D=2048 A=11):ct 字节缓冲(≤1568 B)。
|
||||
- ek 载入路径:`m_i[255:0]` 端口 + ek 流式载入接口(valid/ready 写入 ek_bram),Encaps 前预填。
|
||||
- 输出:`ss_o`(寄存器 + dbg tap);ct 经 dbg tap 读 ct_bram(对齐现有"无流式输出、靠 readback"的风格)。
|
||||
- `op_i`(KeyGen/Encaps 选择) + 新 FSM 状态。
|
||||
|
||||
## 存储编排(关键 — 全 K 用现有 28 slot 银行不扩容)
|
||||
- bank_a (D=4096, 16 slot):Â[i][j],K=4 用满 16。
|
||||
- bank_se (D=2048, 8 slot):y_hat[0..K-1] + e1[0..K-1] = 2K slot(K=4→8,刚好用满)。
|
||||
- bank_t (D=1024, 4 slot):**E0 先放 t̂[0..K-1](从 ek 解码),也用作 e2 的临时家**。
|
||||
- **e2 落点**:e2 只在算 v 时需要,算 u 时 bank_t 空闲 → e2 暂存 bank_t[K..](K=2/3 有空 slot)。K=4 时 bank_t 4 slot 被 t̂ 占满 → e2 先存 bank_se 某 slot(算 u 阶段 e1 仍在用,故 e2 需独立 slot)。
|
||||
- **解法(全 K 统一,避免分支)**:算 u 时 t̂ 尚未需要 → **先做 u(用 A+y_hat+e1),u 全部算完压缩进 ct 后,e1 已用完** → 复用 bank_se 的 e1 区放 e2,再从 ek 解码 t̂ 进 bank_t 算 v。峰值 slot = 16(A)+ 2K(se)+ 0(t 尚未填)= K=4 时 24 ≤ 28。✅
|
||||
- 时序:E_A(填 A)→ E_CBD(y/e1/e2... e2 先丢 bank_t[0])→ E_NTT(y→y_hat)→ E_U(算 u[i],压缩写 ct c1 区)→ E_V_PREP(t̂ 解码进 bank_t,e2 从 bank_t[0] 搬到 bank_se 空 slot 或就地)→ E_V(算 v,+e2+mu,压缩写 ct c2 区)。
|
||||
- **简化**:e2 在 E_CBD 阶段先存 bank_se 的 e1 区**之后**的 slot 不够(K=4)。改为 e2 存 bank_t[0](此时 t̂ 未填),E_V_PREP 时 t̂ 从 ek 流式解码覆盖 bank_t,但 e2 要先读出暂存(单 poly 256×12b 可进一个小 reg 阵列,或搬到 bank_se 算完 u 后的空位)。bring-up 时定稿,以 dbg tap 验证。
|
||||
|
||||
## 运行时参数(新增)
|
||||
```
|
||||
eta2_rt = 2'd2; // 恒定
|
||||
du_rt = (k==4) ? 5'd11 : 5'd10;
|
||||
dv_rt = (k==4) ? 5'd5 : 5'd4;
|
||||
c1_bytes_rt = 32*du_rt*k_r; // K2:640 K3:960 K4:1408
|
||||
c2_bytes_rt = 32*dv_rt; // K2:128 K3:128 K4:160
|
||||
ct_bytes_rt = c1_bytes_rt + c2_bytes_rt; // 768 / 1088 / 1568
|
||||
```
|
||||
|
||||
## 顶层接口新增
|
||||
- `op_i` (1b):0=KeyGen(现有),1=Encaps。在 start_i 锁存为 op_r。
|
||||
- `m_i [255:0]`:Encaps 消息(byte0 在低)。
|
||||
- ek 载入:`ek_in_valid_i/ek_in_byte_i[7:0]/ek_in_ready_o`(或复用 dbg 写口),Encaps 前把 ek 流进 ek_bram。
|
||||
- `ss_o [255:0]` + `done_o`(复用);ct 经 `dbg_ct_idx_i[10:0] → dbg_ct_o[7:0]` readback。
|
||||
|
||||
## 实现阶段(逐阶段 KAT/dbg 对拍)
|
||||
- **E0 — 脚手架 + ek 载入 + H/G**:加 op_i/m_i/ek 载入,ST_ENC_LOAD→ST_ENC_H(H(ek))→ST_ENC_G(G(m‖h))。dbg 验证 ss=K_ss、r(对 ml-kem-r logging::Encaps 的 K/r/H(ek))。
|
||||
- **E1 — A 再生 + t̂ 解码**:ST_ENC_A 复用 snt 写 bank_a;byteDecode12 把 ek 解码进 bank_t。dbg 读系数对 KeyGen golden 的 Ahat / 对 KAT pk 解码的 t̂。
|
||||
- **E2 — y/e1/e2 采样 (η1/η2)**:ST_ENC_C,nonce 0..2K,eta 在 e1/e2 切 2。dbg 验证 y/e1/e2(对 ml-kem-r)。
|
||||
- **E3 — ŷ = NTT(y)**:ST_ENC_N,mode=0,就地。dbg 对 y_hat。
|
||||
- **E4 — u = INTT(Σ Â[j][i]∘ŷ[j]) + e1**:ST_ENC_U,poly_mul + 累加 + INTT(mode=1) + 加 e1。**转置寻址 slot=j·K+i**。dbg 对 u。
|
||||
- **E5 — Compress_du + byteEncode_du → c1**:comp_decomp + 通用打包器写 ct_bram c1 区。dbg 对 ct[0..c1_bytes]==KAT.ct 前缀。
|
||||
- **E6 — v = INTT(Σ t̂[j]∘ŷ[j]) + e2 + mu**:ST_ENC_V,mu 流内由 m bit 生成。dbg 对 v。
|
||||
- **E7 — Compress_dv + byteEncode_dv → c2 + 端到端 KAT**:写 ct c2 区。干净 TB 喂 ek/m,比 ct(全长)==KAT.ct 且 ss==KAT.ss,K=2/3/4 各 count=0..N。
|
||||
|
||||
## 验证
|
||||
- TB `tb_mlkem_enc_katK_xsim.v`:从 `~/Dev/ml-kem-r/test_data/kat_MLKEM_{512,768,1024}.rsp` 取 pk(→ek)、msg(→m)、ct、ss,逐字节比 ct + 比 ss。
|
||||
- bring-up 中间对拍:`cargo run --example logging`(已有 Encaps log::debug,输出 K/r/H(ek)/|c|),或临时加 dump_encaps.rs 出 256-coeff 中间量(仅工作树,不提交 ml-kem-r)。
|
||||
- XSIM 环境同 KeyGen:`source settings64.sh; export LD_PRELOAD=libtinfo.so.5; rm -rf xsim.dir .Xil`。
|
||||
|
||||
## 风险 / 注意
|
||||
- **转置**:Encrypt 用 Â[j][i],KeyGen 用 Â[i][j]。slot = j*K+i(列优先)。最易错,E4 单列 dbg 对拍。
|
||||
- **G 字节序**:KeyGen G 输入 d 原序无翻转(keygen_plan 已确认);但 xcheck 发现 KAT 文件 d/z 存的是翻转序。**m 喂入字节序需 E0 用 KAT.msg 实测确认**(很可能与 d 同约定)。
|
||||
- **INTT 缩放**:ntt_core mode=1 已内置 ×3303,不要再缩放。
|
||||
- **e2 落点(K=4)**:见存储编排,bring-up 定稿,dbg 验证不踩 t̂/e1。
|
||||
- **byteEncode_d 通用打包**:d=10/11 跨字节,位序 LSB-first(同 byte_encode)。新写、单独 TB 或 E5 dbg 即验。
|
||||
- **共享 keccak 4-way mux**:加 Encaps phase 的 sel_ + kc_valid_o 门控,勿与 KeyGen phase 冲突(op_r 已区分)。
|
||||
|
||||
Reference in New Issue
Block a user