Scaffolding for ML-KEM Decaps (FIPS 203 Alg 18):
- op_i widened to 2-bit: 00=KeyGen, 01=Encaps, 10=Decaps (op_r too).
- New ST_DEC_LOAD state (D0: settles to DONE so load/parse is dbg-checkable).
- dk (=sk) streamed via dk_in_*; load logic routes each byte by region:
[0,384K)->dk_pke (dkp_bram), [384K,768K+32)->ek_pke (ek_bram),
[768K+32,+32)->H(ek) (hek_r), [768K+64,+32)->z (z_r). Routing uses the
LIVE k_i input, not start-captured k_r (dk is streamed before start_i).
- c (=ct) streamed via c_in_* into a SEPARATE c_in_bram, so the computed c'
(ct_bram) can later be compared against original c and J(z||c) can read c.
- New dbg taps: dbg_mprime_o/dbg_kbar_o/dbg_decz_o/dbg_dech_o.
TB: tb_mlkem_dec_katK_xsim verifies dk parse (H(ek), z, ek_pke/dk_pke BRAM
round-trip). gen_decaps_vectors.py emits dec_k{K}_c{N}_{dk,ct,ss,ctn,ssn}.hex
from the NIST KAT. run_tb.sh gains a 'dec' module (mirrors 'enc').
Regression fix: old KeyGen/Encaps TBs didn't connect the new input ports,
floating them to X and corrupting the ek/dkp write muxes -> tied off
dk_in_*/c_in_*/new dbg taps in both.
Verified: dec D0 K=2/3/4 PASS; KeyGen K=2 + Encaps K=2 unregressed.
90 lines
7.9 KiB
Markdown
90 lines
7.9 KiB
Markdown
# ML-KEM Decaps 顶层集成 — 实现计划
|
||
|
||
> 在 `mlkem_top`(KeyGen + Encaps 均 KAT 通过)基础上扩展 Decaps(FIPS 203 Alg 18 + K-PKE.Decrypt Alg 15)。
|
||
> 决策(已与用户确认):**(1) 实现完整隐式拒绝路径(J + 常量时间 c'==c 比较 + K̄/K' mux),并用 corrupted-ct(KAT ct_n/ss_n)验证拒绝路径;(2) 逐级 dbg tap 对拍 ml-kem-r golden(m'/w/u_hat 等)+ 端到端 ss==KAT.ss。**
|
||
|
||
## 算法(Decaps_internal,全 K)
|
||
输入:dk(=KAT.sk,768K+96 B)、c(=KAT.ct,32(du·K+dv) B)。输出:K'=ss(32 B)。
|
||
1. 解析 dk:`dk_pke = dk[0:384K]`(s_hat)、`ek_pke = dk[384K:768K+32]`、`h = dk[768K+32:768K+64]`、`z = dk[768K+64:768K+96]`。
|
||
2. `m' = K-PKE.Decrypt(dk_pke, c)`(**唯一全新数据通路**,见下)。
|
||
3. `(K', r') = G(m' ‖ h)` = SHA3-512(64 B 单块)。**与 Encaps G 完全相同**(mode=11)。
|
||
4. `K̄ = J(z ‖ c)` = SHAKE-256(z‖c, 32 B 输出)。**多块,块数 6/9/12 = 与 H(ek) 相同**。
|
||
5. `c' = K-PKE.Encrypt(ek_pke, m', r')`。**这就是 Encaps E1–E7**(A 再生 + 采样 y/e1/e2(seed=r') + NTT + u + v + 压缩成 ct')。**已 KAT 通过,零改动复用**。
|
||
6. 常量时间比较 `c' == c`(逐字节 XOR 累加)。
|
||
7. `K' = (c'==c) ? K' : K̄`(隐式拒绝 mux)。返回 K'=ss。
|
||
|
||
### K-PKE.Decrypt(Alg 15,全新)
|
||
- 解析 c:`c1 = c[0:32·du·K]`、`c2 = c[32·du·K : +32·dv]`。
|
||
- `u'[i] = Decompress_du(byteDecode_du(c1[i]))`,i=0..K-1(**新:通用 byteDecode_d 解包器 + comp_decomp mode=1 解压**)。
|
||
- `v' = Decompress_dv(byteDecode_dv(c2))`(1 poly)。
|
||
- `s_hat[i] = byteDecode12(dk_pke[i·384..])`,i=0..K-1(**复用 Encaps TDEC 机器**,5-cyc/triple)。
|
||
- `u_hat[i] = NTT(u'[i])`(mode=0,K polys,**复用 ST_ENC_N 的 ntt_core**)。
|
||
- `w = v' − INTT(Σⱼ s_hat[j]∘u_hat[j])` mod Q(**复用 Encaps V 机器(MAC+INTT),ADD 改 SUB,加数 v' 替 e2+mu**)。
|
||
- `m' = byteEncode₁(Compress₁(w))` = 32 B(**复用 C1/C2 打包器,d=1**)。
|
||
|
||
## 复用与新增
|
||
|
||
### 直接复用(零或极小改动)
|
||
- **整个 Encaps E1–E7**(c' = Encrypt):A 再生、CBD(seed=r')、NTT、U、V、C1、C2 → ct_bram。完全复用,只是 seed 来自 r'(已是 r_r 路径)、ek 来自 ek_pke(已在 ek_bram)、m 来自 m'(新:m' 寄存器替 m_r)。
|
||
- `u_sha3` G(mode=11,m'‖h):与 Encaps G 同,只是 enc_g_data 高半改 h(来自 dk 而非 H(ek) 重算)。
|
||
- `u_keccak` 共享核 + J 多块:复用 H 的 mb 路径,仅末块 pad 常量 0x06→0x1F(SHAKE)。
|
||
- `ntt_core`:u_hat fwd NTT(mode=0,复用 ST_ENC_N);decrypt 的 INTT(mode=1,复用 V 的 u_intt 路径)。
|
||
- `poly_mul` / 3 银行 / comp_decomp:加 Decaps phase mux。
|
||
- Encaps TDEC(byteDecode12 → bank_a):decrypt 的 s_hat 解码复用(改落 dk_pke 源 + 目标 bank)。
|
||
|
||
### 新增 RTL
|
||
- **`byteDecode_d` 通用解包器**(d∈{4,5,10,11}):c 字节流 → d-bit 系数,LSB-first(byteEncode_d 的逆)。新写,流式读 ct/c_bram,写银行。
|
||
- **comp_decomp mode=1 解压**:实例已有(E5/E7 用 mode=0);Decaps decode-decompress 用 mode=1,d=du/dv。加 phase。
|
||
- **dk 载入路径**:`dk_in_*`(或复用 ek_in_* 加宽地址),Decaps 前流入 dk_bram。顶层解析:dk_pke→bank/解码、ek_pke→ek_bram、h/z→寄存器。
|
||
- **c 载入路径**:`c_in_*` 流入 ct_bram 的「输入 c」区(注意 c' 也写 ct_bram → 需独立 c_in_bram 或分区,见存储编排)。
|
||
- **J 多块组装**:z(reg)‖c(c_in_bram)→ 136B 块,末块 0x1F pad。新地址逻辑(类 H 的 h_g_addr)。
|
||
- **w = v' − INTT(...)**:V 机器 ADD 子相改 SUB(`(v' − psum) mod Q`,负则 +Q),加数源 v'(银行)替 e2(bank_a)+mu。
|
||
- **m' Compress₁+byteEncode₁**:打包器加 d=1 路径(每系数 1 bit,256 bit = 32 B)。m' 落 32-bit 寄存器(供 G 与 c'-Encrypt)。
|
||
- **常量时间比较 c'==c**:逐字节读 ct_bram(c')与 c_in_bram(c),XOR 累加进 1-bit `ct_ne_r`(全程扫完,不早退)。
|
||
- **隐式拒绝 mux**:`ss_r = ct_ne_r ? k_bar_r : kprime_r`。
|
||
- **op_i 加宽 2-bit**:00=KeyGen,01=Encaps,10=Decaps。新增 ST_DEC_* 状态。
|
||
|
||
## 存储编排(关键)
|
||
- **c 输入 vs c' 输出冲突**:Decaps 既要保留输入 c(给 J 和最终比较),又要算 c'(写 ct_bram)。**解法:c 输入存独立 `c_in_bram`(sd_bram W=8 D=2048);c' 仍写 ct_bram。** 比较阶段两个 bram 各一读口,无冲突。J 从 c_in_bram 读。
|
||
- **Decrypt 阶段银行**:s_hat(K)、u'(K)、u_hat(=NTT(u') 就地 K)、v'(1)、psum/w(1)。
|
||
- u' → bank_se rel 0..K-1;NTT 就地得 u_hat。
|
||
- s_hat → bank_a slot j·K(复用 TDEC 落点 + V-MAC 寻址)。
|
||
- v' → bank_t rel 0(或 bank_a 空 slot)。
|
||
- psum(Σ s∘u_hat)→ bank_t[UPSUM];INTT 就地;SUB 读 v'(bank_t rel 0)+ psum(bank_t UPSUM)→ 单口冲突 → v' 改存 bank_a 某 slot(类 e2 搬迁)或 bank_se 空区。**bring-up 定稿,dbg 验证。**
|
||
- m' 算完 → 32-bit 寄存器。Decrypt 阶段结束,银行清空。
|
||
- **Encrypt(c')阶段**:Decrypt 已出 m'(reg),银行重新被 Encaps E1–E7 占用,无并发。
|
||
|
||
## 顶层接口新增
|
||
- `op_i [1:0]`:00/01/10。start_i 锁存 op_r[1:0]。
|
||
- `dk_in_*`(we/addr/byte):dk 流入。`c_in_*`(we/addr/byte):c 流入。
|
||
- `ss_o`(复用):Decaps 输出 K'。
|
||
- dbg taps:m'(`dbg_mprime_o[255:0]`)、w/u_hat 经现有 dbg_coeff_o、k_bar(`dbg_kbar_o`)。
|
||
|
||
## 实现阶段(逐阶段 dbg/KAT 对拍)
|
||
- **D0 — 脚手架 + dk/c 载入 + 解析** ✅:op_i 加宽 2-bit(00 KG/01 Enc/10 Dec),ST_DEC_LOAD(D0 暂直接→DONE)。dk 流入按 region 路由:dk_pke→dkp_bram、ek_pke→ek_bram、h→hek_r、z→z_r;ct→c_in_bram(独立于 ct_bram)。dbg 验证 h/z/ek_pke/dk_pke。**踩坑1:载入路由用 k_r 但 k_r 在 start_i 才锁存 → 预载期 region 边界全 0,路由全错。改用 LIVE k_i 边界(dkp_bytes_ld 等)。踩坑2:旧 KG/Enc TB 未接新端口(dk_in_*/c_in_*/dbg_*)→ X 漂入 write mux,KeyGen/Encaps 超时回归。补 tie-off 0。** runner = `./run_tb.sh dec [K] [CASE]`。K=2/3/4 D0 全过,KG/Enc 回归通过。
|
||
- **D1 — byteDecode_d + Decompress → u'/v'**:新解包器 + comp_decomp mode=1。dbg 对 u'/v'(ml-kem-r golden,新 dump_decaps)。
|
||
- **D2 — s_hat 解码 + u_hat = NTT(u')**:TDEC 复用(dk_pke 源)、ntt_core fwd。dbg 对 s_hat / u_hat。
|
||
- **D3 — w = v' − INTT(Σ s∘u_hat)**:V 机器 SUB 变体。dbg 对 w。
|
||
- **D4 — m' = byteEncode₁(Compress₁(w))**:打包器 d=1。dbg 对 m'(== KAT 解密的 m')。
|
||
- **D5 — G(m'‖h) → (K',r') + J(z‖c) → K̄**:G 复用、J 多块(0x1F pad)。dbg 对 K'/r'/K̄。
|
||
- **D6 — c' = Encrypt(ek_pke,m',r')**:复用 Encaps E1–E7 写 ct_bram。dbg 对 c'==KAT.ct(有效 ct 时)。
|
||
- **D7 — 比较 + 拒绝 mux + 端到端 KAT**:c'==c 常量时间比较,ss=mux。干净 TB:
|
||
- 有效 ct(KAT.ct):ss==KAT.ss(c'==c → K')。
|
||
- 损坏 ct(KAT ct_n / ss_n):ss==KAT.ss_n(c'≠c → K̄)。
|
||
- K=2/3/4 各 count=0..N。
|
||
|
||
## 验证
|
||
- 新 `dump_decaps.rs`(ml-kem-r examples,工作树):出 D1–D6 中间量(u'/v'/s_hat/u_hat/w/m'/K'/K̄)256-coeff / 32B golden。
|
||
- 新 TB `tb_mlkem_dec_katK_xsim.v`:从 KAT 取 sk(→dk)、ct、ss、ct_n、ss_n,载入 dk/c,跑 Decaps,比 ss。
|
||
- runner:`./run_tb.sh dec [K] [CASE]`(并入 run_tb,复用 top tcl + dec TB)。
|
||
- XSIM 环境同前:`source settings64.sh; export LD_PRELOAD=libtinfo.so.5; rm -rf xsim.dir .Xil`。
|
||
|
||
## 风险 / 注意
|
||
- **c 输入 / c' 输出共存**:必须分两个 bram(c_in_bram + ct_bram),否则 c' 覆盖 c 后无法比较 / 算 J。最易错,D0 定。
|
||
- **w 的 SUB**:`(v' − psum) mod Q`,结果可能负 → +Q 修正(类 mod_sub)。v'/psum 读口冲突 → v' 搬到不同 bank。D3 dbg 验证。
|
||
- **J SHAKE pad**:末块 0x1F(非 0x06);单字节差异,复用 H mb 机器加 phase 选择 pad 常量。
|
||
- **op_i 加宽**:1-bit→2-bit,改 IDLE 分发、reset、所有 op_r 判断。回归 KeyGen/Encaps 不破。
|
||
- **m' 双用**:既喂 G(m'‖h)又喂 c'-Encrypt(替 m_r)。确保 Encrypt 路径读 m'_r 而非 m_r(加 mux 或 Decaps 时把 m'_r 写入 m_r)。
|
||
- **隐式拒绝常量时间**:比较全程扫完不早退(XOR 累加),硬件天然如此;但 mux 不可短路。
|
||
- **ek_pke 来自 dk**:Decaps 的 Encrypt 用 dk 内嵌的 ek_pke(解析到 ek_bram),不是外部 ek。
|