chore: 删除无用测试文件与 ml-kem-r 痕迹

- 删除 top/TB 下已被 katK 参数化 TB 取代的增量开发 TB:
  tb_mlkem_kg_{2a,2c,2d,2e,2f,kat,katN}_xsim.v
- 删除依赖外部 ml-kem-r Rust 参考的向量生成 / 交叉校验脚本:
  gen_vectors.py / gen_encaps_vectors.py / gen_decaps_vectors.py /
  xcheck_mlkemr.py(KAT/golden 向量已提交,无需运行期依赖)
- 清理源码与文档注释中的 ml-kem-r / Rust 字样

保留的 5 个 TB(kg/enc/dec katK + hello_world + two_inst)即 run_tb.sh /
xsim_run.tcl 实际引用的全部。KeyGen KAT 与 hello_world 烟囱测试通过。
This commit is contained in:
2026-06-29 22:42:49 +08:00
parent df4d5cd572
commit 971ce97d50
15 changed files with 11 additions and 875 deletions

View File

@@ -203,7 +203,7 @@ c' = K-PKE.Encrypt(ek_pke, m', r') ←── 复用整条 Encaps 流水
| `tb_mlkem_kg_katK_xsim.v` | KeyGen | 逐字节核对 `ek==pk``dk==sk` | | `tb_mlkem_kg_katK_xsim.v` | KeyGen | 逐字节核对 `ek==pk``dk==sk` |
| `tb_mlkem_enc_katK_xsim.v` | Encaps | 核对 `ss``c` | | `tb_mlkem_enc_katK_xsim.v` | Encaps | 核对 `ss``c` |
| `tb_mlkem_dec_katK_xsim.v` | Decaps | 核对 D0..D7 各阶段;正常密文 `ss==KAT.ss`、损坏密文 `ss==KAT.ss_n` | | `tb_mlkem_dec_katK_xsim.v` | Decaps | 核对 D0..D7 各阶段;正常密文 `ss==KAT.ss`、损坏密文 `ss==KAT.ss_n` |
| `tb_mlkem_hello_world_xsim.v` | 全流程(单实例) | 复现 `hello_world.rs`KeyGen→Encaps→XOR→Decaps→XOR | | `tb_mlkem_hello_world_xsim.v` | 全流程(单实例) | 端到端演示KeyGen→Encaps→XOR→Decaps→XOR |
| `tb_mlkem_two_inst_xsim.v` | 全流程(双实例) | 实例 A 做 KeyGen+Encaps实例 B 做 Decaps | | `tb_mlkem_two_inst_xsim.v` | 全流程(双实例) | 实例 A 做 KeyGen+Encaps实例 B 做 Decaps |
每个参数化 TB 通过 `xelab -generic_top KP=2|3|4` 选安全等级(驱动到运行时 `k_i``+CASE=n` 选用例号。 每个参数化 TB 通过 `xelab -generic_top KP=2|3|4` 选安全等级(驱动到运行时 `k_i``+CASE=n` 选用例号。
@@ -222,7 +222,7 @@ c' = K-PKE.Encrypt(ek_pke, m', r') ←── 复用整条 Encaps 流水
./run_tb.sh hello two # hello_world 端到端双实例genenc + dec ./run_tb.sh hello two # hello_world 端到端双实例genenc + dec
``` ```
`hello_world` 硬件输出与 Rust 参考逐字节一致`shared_key=ced0c031a4bee34a...``encrypted=a6b5ac5dcb9e9425b9e3b8``decrypted="hello world"` `hello_world` 硬件端到端输出:`shared_key=ced0c031a4bee34a...``encrypted=a6b5ac5dcb9e9425b9e3b8``decrypted="hello world"`,密钥匹配、消息正确还原
### 验证注意事项 ### 验证注意事项

View File

@@ -1,80 +0,0 @@
#!/usr/bin/env python3
"""gen_decaps_vectors.py - Emit per-byte Decaps KAT vectors for the XSIM TB.
For ML-KEM-512/768/1024, parse the NIST .rsp and write, per case:
dec_k{K}_c{N}_dk.hex : dk (=sk) bytes, one hex byte per line, byte 0 first
dec_k{K}_c{N}_ct.hex : ct bytes (valid ciphertext), one per line, byte 0 first
dec_k{K}_c{N}_ss.hex : ss bytes (shared secret for valid ct), byte 0 first
dec_k{K}_c{N}_ctn.hex : ct_n bytes (corrupted ct -> implicit reject)
dec_k{K}_c{N}_ssn.hex : ss_n bytes (shared secret K-bar for the reject path)
The TB streams dk into the design via dk_in_* (routed to dk_pke/ek_pke/h/z by
region) and ct into c_in_*, runs Decaps, and checks ss:
valid ct -> ss == ss (c'==c, K' kept)
ct_n -> ss == ss_n (c'!=c, K-bar via J(z||c_n))
Run: python3 gen_decaps_vectors.py [num_cases]
"""
import os
import re
import sys
ML_KEM_R = os.environ.get("ML_KEM_R", os.path.expanduser("~/Dev/ml-kem-r"))
OUT_DIR = os.path.join(os.path.dirname(__file__), "vectors")
KATS = {2: "kat_MLKEM_512.rsp", 3: "kat_MLKEM_768.rsp", 4: "kat_MLKEM_1024.rsp"}
def parse_kat(path, n):
"""Return list of dicts {count, sk, ct, ss, ct_n, ss_n} (hex) for first n."""
vecs, cur = [], {}
with open(path) as f:
for line in f:
line = line.strip()
m = re.match(r"^count\s*=\s*(\d+)$", line)
if m:
if cur:
vecs.append(cur)
if len(vecs) >= n:
break
cur = {"count": int(m.group(1))}
continue
# exact-key match: 'ct =' vs 'ct_n =', 'ss =' vs 'ss_n ='
m = re.match(r"^(sk|ct|ss|ct_n|ss_n)\s*=\s*([0-9a-fA-F]+)$", line)
if m and cur:
cur[m.group(1)] = m.group(2).lower()
if cur and len(vecs) < n:
vecs.append(cur)
return vecs
def write_bytes(path, hexstr):
"""Write hex string as one byte per line (byte 0 = first 2 hex chars)."""
with open(path, "w") as f:
for i in range(0, len(hexstr), 2):
f.write(hexstr[i:i + 2] + "\n")
def main():
ncases = int(sys.argv[1]) if len(sys.argv) > 1 else 3
os.makedirs(OUT_DIR, exist_ok=True)
for k, fname in KATS.items():
path = os.path.join(ML_KEM_R, "test_data", fname)
if not os.path.exists(path):
print(f"skip K={k}: {path} not found", file=sys.stderr)
continue
vecs = parse_kat(path, ncases)
for v in vecs:
n = v["count"]
base = os.path.join(OUT_DIR, f"dec_k{k}_c{n}")
write_bytes(f"{base}_dk.hex", v["sk"])
write_bytes(f"{base}_ct.hex", v["ct"])
write_bytes(f"{base}_ss.hex", v["ss"])
write_bytes(f"{base}_ctn.hex", v["ct_n"])
write_bytes(f"{base}_ssn.hex", v["ss_n"])
print(f"K={k}: wrote {len(vecs)} cases "
f"(dk={len(vecs[0]['sk'])//2}B ct={len(vecs[0]['ct'])//2}B)")
if __name__ == "__main__":
main()

View File

@@ -1,76 +0,0 @@
#!/usr/bin/env python3
"""gen_encaps_vectors.py - Emit per-byte Encaps KAT vectors for the XSIM TB.
For ML-KEM-512/768/1024, parse the NIST .rsp and write, per case:
enc_k{K}_c{N}_ek.hex : ek (=pk) bytes, one hex byte per line, byte 0 first
enc_k{K}_c{N}_m.hex : msg bytes, one per line, byte 0 first
enc_k{K}_c{N}_ct.hex : ct bytes, one per line, byte 0 first (golden)
enc_k{K}_c{N}_ss.hex : ss bytes, one per line, byte 0 first (golden)
Per-byte layout removes all endianness ambiguity: the TB preloads ek_bram with
addr i = line i, builds m_i[8*i +: 8] = m byte i, and compares ct/ss byte-wise.
Run: python3 gen_encaps_vectors.py [num_cases]
"""
import os
import re
import sys
ML_KEM_R = os.environ.get("ML_KEM_R", os.path.expanduser("~/Dev/ml-kem-r"))
OUT_DIR = os.path.join(os.path.dirname(__file__), "vectors")
KATS = {2: "kat_MLKEM_512.rsp", 3: "kat_MLKEM_768.rsp", 4: "kat_MLKEM_1024.rsp"}
def parse_kat(path, n):
"""Return list of dicts {count, msg, pk, ct, ss} (hex strings) for first n."""
vecs, cur = [], {}
with open(path) as f:
for line in f:
line = line.strip()
m = re.match(r"^count\s*=\s*(\d+)$", line)
if m:
if cur:
vecs.append(cur)
if len(vecs) >= n:
break
cur = {"count": int(m.group(1))}
continue
# exact-key match so "ct =" doesn't catch "ct_n ="
m = re.match(r"^(msg|pk|ct|ss)\s*=\s*([0-9a-fA-F]+)$", line)
if m and cur:
cur[m.group(1)] = m.group(2).lower()
if cur and len(vecs) < n:
vecs.append(cur)
return vecs
def write_bytes(path, hexstr):
"""Write hex string as one byte per line (byte 0 = first 2 hex chars)."""
with open(path, "w") as f:
for i in range(0, len(hexstr), 2):
f.write(hexstr[i:i + 2] + "\n")
def main():
ncases = int(sys.argv[1]) if len(sys.argv) > 1 else 3
os.makedirs(OUT_DIR, exist_ok=True)
for k, fname in KATS.items():
path = os.path.join(ML_KEM_R, "test_data", fname)
if not os.path.exists(path):
print(f"skip K={k}: {path} not found", file=sys.stderr)
continue
vecs = parse_kat(path, ncases)
for v in vecs:
n = v["count"]
base = os.path.join(OUT_DIR, f"enc_k{k}_c{n}")
write_bytes(f"{base}_ek.hex", v["pk"])
write_bytes(f"{base}_m.hex", v["msg"])
write_bytes(f"{base}_ct.hex", v["ct"])
write_bytes(f"{base}_ss.hex", v["ss"])
print(f"K={k}: wrote {len(vecs)} cases "
f"(ek={len(vecs[0]['pk'])//2}B ct={len(vecs[0]['ct'])//2}B)")
if __name__ == "__main__":
main()

View File

@@ -1,165 +0,0 @@
#!/usr/bin/env python3
"""gen_vectors.py - Parse FIPS 203 KAT test vectors for ML-KEM-512.
Reads kat_MLKEM_512.rsp and generates hex vector files for the
mlkem_top XSIM testbench.
Output files (written to vectors/ subdirectory):
mlkem_top_input.hex - d (256b) + msg (256b) + z (256b) per line
mlkem_top_expected.hex - pk (6400b) + sk (13056b) + ct (6144b) + ss (256b)
KAT file format (per test vector):
count = N
z = <64 hex chars> → 32 bytes, implicit rejection seed
d = <64 hex chars> → 32 bytes, KeyGen randomness
msg = <64 hex chars> → 32 bytes, Encaps message
seed = <128 hex chars> → DRBG seed (ignored)
pk = <1600 hex chars> → 800 bytes, public key
sk = <3264 hex chars> → 1632 bytes, secret key
ct_n = <hex> → invalid ciphertext (ignored)
ss_n = <hex> → shared secret from invalid ct (ignored)
ct = <1536 hex chars> → 768 bytes, ciphertext
ss = <64 hex chars> → 32 bytes, shared secret
"""
import os
import re
import sys
KAT_PATH = "/home/fallensigh/Dev/ml-kem-r/test_data/kat_MLKEM_512.rsp"
OUT_DIR = os.path.join(os.path.dirname(__file__), "vectors")
NUM_VECTORS = 5
def parse_kat(filepath: str, num_vectors: int = NUM_VECTORS) -> list[dict]:
"""Parse KAT .rsp file and return list of test vector dicts."""
vectors: list[dict] = []
current: dict = {}
with open(filepath, "r") as f:
for line in f:
line = line.strip()
# New vector starts with "count = N"
m = re.match(r"^count\s*=\s*(\d+)$", line)
if m:
if current and "count" in current:
vectors.append(current)
current = {}
idx = int(m.group(1))
if idx >= num_vectors:
break
current = {"count": idx}
continue
# Parse key = value lines
m = re.match(r"^(\w+)\s*=\s*([0-9a-fA-F]+)$", line)
if m and current:
key = m.group(1)
value = m.group(2).lower()
current[key] = value
continue
# Add last vector if present
if current and "count" in current:
vectors.append(current)
# Validate each vector has required fields
required = {"d", "z", "msg", "pk", "sk", "ct", "ss"}
for v in vectors:
missing = required - set(v.keys())
if missing:
print(f"WARNING: count={v['count']} missing fields: {missing}",
file=sys.stderr)
return vectors
def verify_lengths(vectors: list[dict]) -> bool:
"""Verify hex field lengths match FIPS 203 expected sizes."""
expected = {
"d": 64, # 32 bytes
"z": 64, # 32 bytes
"msg": 64, # 32 bytes
"pk": 1600, # 800 bytes
"sk": 3264, # 1632 bytes
"ct": 1536, # 768 bytes
"ss": 64, # 32 bytes
}
ok = True
for v in vectors:
for field, elen in expected.items():
if field not in v:
continue
actual = len(v[field])
if actual != elen:
print(f"WARNING: count={v['count']} {field}: "
f"expected {elen} hex chars, got {actual}",
file=sys.stderr)
ok = False
return ok
def write_input_hex(vectors: list[dict], out_dir: str) -> str:
"""Write input vectors: d || msg || z (768 bits = 192 hex chars each)."""
os.makedirs(out_dir, exist_ok=True)
out_path = os.path.join(out_dir, "mlkem_top_input.hex")
with open(out_path, "w") as f:
for v in vectors:
d = v.get("d", "0" * 64)
msg = v.get("msg", "0" * 64)
z = v.get("z", "0" * 64)
# Concatenate: [d:255:0][msg:255:0][z:255:0]
f.write(f"{d}{msg}{z}\n")
print(f"Wrote {len(vectors)} input vectors to {out_path}")
return out_path
def write_expected_hex(vectors: list[dict], out_dir: str) -> str:
"""Write expected output vectors: pk || sk || ct || ss.
pk: 800 bytes = 1600 hex chars
sk: 1632 bytes = 3264 hex chars
ct: 768 bytes = 1536 hex chars
ss: 32 bytes = 64 hex chars
Total: 3232 bytes = 6464 hex chars per line
"""
os.makedirs(out_dir, exist_ok=True)
out_path = os.path.join(out_dir, "mlkem_top_expected.hex")
with open(out_path, "w") as f:
for v in vectors:
pk = v.get("pk", "0" * 1600)
sk = v.get("sk", "0" * 3264)
ct = v.get("ct", "0" * 1536)
ss = v.get("ss", "0" * 64)
f.write(f"{pk}{sk}{ct}{ss}\n")
print(f"Wrote {len(vectors)} expected vectors to {out_path}")
return out_path
def main():
vectors = parse_kat(KAT_PATH, NUM_VECTORS)
print(f"Parsed {len(vectors)} test vectors from KAT file")
if len(vectors) == 0:
print("ERROR: No vectors parsed!", file=sys.stderr)
sys.exit(1)
verify_lengths(vectors)
# Print summary
for v in vectors:
print(f" count={v['count']}: "
f"d={v.get('d', 'N/A')[:8]}... "
f"msg={v.get('msg', 'N/A')[:8]}... "
f"z={v.get('z', 'N/A')[:8]}... "
f"ss={v.get('ss', 'N/A')[:8]}...")
write_input_hex(vectors, OUT_DIR)
write_expected_hex(vectors, OUT_DIR)
print("Done.")
if __name__ == "__main__":
main()

View File

@@ -103,7 +103,7 @@ module tb_mlkem_enc_katK_xsim;
// ---- E1: verify A_hat (slots 0..K^2-1). t_hat (byteDecode12) is re- // ---- E1: verify A_hat (slots 0..K^2-1). t_hat (byteDecode12) is re-
// verified at E6 (V uses it); TDEC is deferred to V-prep so e2 can use // verified at E6 (V uses it); TDEC is deferred to V-prep so e2 can use
// bank_t during C/N/U. A_hat equals KeyGen golden (K=2 c0). ---- // bank_t during C/N/U. A_hat equals KeyGen golden (K=2 c0). ----
// ---- E2: verify y[i], e1[i] (bank_se), e2 (bank_t slot_t) vs ml-kem-r. // ---- E2: verify y[i], e1[i] (bank_se), e2 (bank_t slot_t) vs reference.
if (KP == 2 && casenum == 0) begin if (KP == 2 && casenum == 0) begin
// E1 (verify_e1) dropped: E6's TDEC overwrites bank_a (A_hat slots) // E1 (verify_e1) dropped: E6's TDEC overwrites bank_a (A_hat slots)
// with t_hat, so a post-run A_hat readback is invalid. A_hat is // with t_hat, so a post-run A_hat readback is invalid. A_hat is
@@ -164,7 +164,7 @@ module tb_mlkem_enc_katK_xsim;
ce = ce + 1; ce = ce + 1;
end end
end end
if (ce == 0) $display("K=2 CASE 0 PASS (E2): e2 == ml-kem-r golden"); if (ce == 0) $display("K=2 CASE 0 PASS (E2): e2 == reference golden");
else $display("K=2 CASE 0 FAIL (E2): %0d e2 mismatches", ce); else $display("K=2 CASE 0 FAIL (E2): %0d e2 mismatches", ce);
end end
endtask endtask
@@ -184,7 +184,7 @@ module tb_mlkem_enc_katK_xsim;
ce = ce + 1; ce = ce + 1;
end end
end end
if (ce == 0) $display("K=2 CASE 0 PASS (E3): y_hat[0..1] == ml-kem-r golden"); if (ce == 0) $display("K=2 CASE 0 PASS (E3): y_hat[0..1] == reference golden");
else $display("K=2 CASE 0 FAIL (E3): %0d coeff mismatches", ce); else $display("K=2 CASE 0 FAIL (E3): %0d coeff mismatches", ce);
end end
endtask endtask
@@ -204,7 +204,7 @@ module tb_mlkem_enc_katK_xsim;
ce = ce + 1; ce = ce + 1;
end end
end end
if (ce == 0) $display("K=2 CASE 0 PASS (E4): u[0..1] == ml-kem-r golden"); if (ce == 0) $display("K=2 CASE 0 PASS (E4): u[0..1] == reference golden");
else $display("K=2 CASE 0 FAIL (E4): %0d coeff mismatches", ce); else $display("K=2 CASE 0 FAIL (E4): %0d coeff mismatches", ce);
end end
endtask endtask
@@ -231,7 +231,7 @@ module tb_mlkem_enc_katK_xsim;
endtask endtask
// E6: v = INTT(sum_j t_hat[j] o y_hat[j]) + e2 + mu lives in bank_t rel slot // E6: v = INTT(sum_j t_hat[j] o y_hat[j]) + e2 + mu lives in bank_t rel slot
// UPSUM=1 -> dbg slot slot_t_rt+1 = 9 (K=2). Compare to ml-kem-r golden v. // UPSUM=1 -> dbg slot slot_t_rt+1 = 9 (K=2). Compare to reference golden v.
reg [11:0] gv [0:255]; reg [11:0] gv [0:255];
task verify_e6; task verify_e6;
begin begin
@@ -245,7 +245,7 @@ module tb_mlkem_enc_katK_xsim;
ce = ce + 1; ce = ce + 1;
end end
end end
if (ce == 0) $display("K=2 CASE 0 PASS (E6): v == ml-kem-r golden"); if (ce == 0) $display("K=2 CASE 0 PASS (E6): v == reference golden");
else $display("K=2 CASE 0 FAIL (E6): %0d coeff mismatches", ce); else $display("K=2 CASE 0 FAIL (E6): %0d coeff mismatches", ce);
end end
endtask endtask
@@ -259,7 +259,7 @@ module tb_mlkem_enc_katK_xsim;
be = 0; be = 0;
// Dump the hardware-produced ct (read from ct_bram via dbg_ct tap), // Dump the hardware-produced ct (read from ct_bram via dbg_ct tap),
// then compare to KAT.ct byte-exact. The $write loop prints the whole // then compare to KAT.ct byte-exact. The $write loop prints the whole
// ciphertext on one line (byte 0 first), same format as ml-kem-r's // ciphertext on one line (byte 0 first), same format as the reference's
// encaps_io example, so the two can be eyeballed / diffed. // encaps_io example, so the two can be eyeballed / diffed.
$write(" ct = "); $write(" ct = ");
for (i = 0; i < CTB; i = i + 1) begin for (i = 0; i < CTB; i = i + 1) begin

View File

@@ -1,4 +1,4 @@
// tb_mlkem_hello_world_xsim.v - Hardware run of ml-kem-r examples/hello_world.rs. // tb_mlkem_hello_world_xsim.v - Hardware run of the ML-KEM hello_world demo.
// //
// Mirrors the Rust example end-to-end on the mlkem_top DUT (ML-KEM-512, K=2): // Mirrors the Rust example end-to-end on the mlkem_top DUT (ML-KEM-512, K=2):
// 1. Alice: KeyGen(d=0x42..., z=0x77...) -> (ek 800B, dk 1632B) // 1. Alice: KeyGen(d=0x42..., z=0x77...) -> (ek 800B, dk 1632B)

View File

@@ -1,45 +0,0 @@
// tb_mlkem_kg_2a_xsim.v - Stage 2a: verify G(d||K) -> rho/sigma in mlkem_top.
`timescale 1ns/1ps
module tb_mlkem_kg_2a_xsim;
reg clk=0, rst_n=0, start_i=0;
reg [255:0] d_i, z_i=0;
wire busy_o, done_o;
reg [3:0] dbg_slot_i=0; reg [7:0] dbg_idx_i=0; wire [11:0] dbg_coeff_o;
wire [255:0] dbg_rho_o, dbg_sigma_o;
mlkem_top #(.K(2)) dut (
.clk(clk), .rst_n(rst_n), .d_i(d_i), .z_i(z_i), .start_i(start_i),
.busy_o(busy_o), .done_o(done_o),
.dbg_slot_i(dbg_slot_i), .dbg_idx_i(dbg_idx_i), .dbg_coeff_o(dbg_coeff_o),
.dbg_rho_o(dbg_rho_o), .dbg_sigma_o(dbg_sigma_o)
);
always #5 clk = ~clk;
localparam [255:0] D_LIT = 256'h2426f1941779574d3f1b163bd57f7e173e229e630ec7f7073bdf365137c4bb6d;
localparam [255:0] RHO_EXP = 256'h15f74355ca862c3cdf3dab780c35cf24b88bf144706090a1c17e41205f9f1379;
localparam [255:0] SIG_EXP = 256'h69b042001b5630b1a039116cbfd29f62c0bde5a6b571504a9fcce68bed667fd5;
integer c;
initial begin
d_i = D_LIT;
rst_n=0; repeat(4) @(posedge clk); rst_n=1; @(posedge clk);
start_i=1; @(posedge clk); start_i=0;
c=0; while(!done_o && c<1000) begin @(posedge clk); c=c+1; end
if(!done_o) begin $display("FAIL: timeout"); $finish; end
$display("=== Stage 2a: G(d||K) ===");
if (dbg_rho_o===RHO_EXP && dbg_sigma_o===SIG_EXP) begin
$display("PASS: rho = %064x", dbg_rho_o);
$display("PASS: sigma = %064x", dbg_sigma_o);
$display("ALL TESTS PASSED");
end else begin
$display("FAIL:");
$display(" rho got=%064x", dbg_rho_o);
$display(" rho exp=%064x", RHO_EXP);
$display(" sig got=%064x", dbg_sigma_o);
$display(" sig exp=%064x", SIG_EXP);
end
$finish;
end
initial begin #50000; $display("FAIL: global timeout"); $finish; end
endmodule

View File

@@ -1,60 +0,0 @@
// tb_mlkem_kg_2c_xsim.v - Stage 2c: verify A_hat + s + e stored in mlkem_top.
// Reads golden kg_c000_AsE.hex (8 polys x 256 = 2048 lines, mod-q) and checks
// polymem slots A00,A01,A10,A11,S0,S1,E0,E1 via the debug readback tap.
`timescale 1ns/1ps
module tb_mlkem_kg_2c_xsim;
reg clk=0, rst_n=0, start_i=0;
reg [255:0] d_i, z_i=0;
wire busy_o, done_o;
reg [3:0] dbg_slot_i=0; reg [7:0] dbg_idx_i=0; wire [11:0] dbg_coeff_o;
wire [255:0] dbg_rho_o, dbg_sigma_o;
mlkem_top #(.K(2)) dut (
.clk(clk), .rst_n(rst_n), .d_i(d_i), .z_i(z_i), .start_i(start_i),
.busy_o(busy_o), .done_o(done_o),
.dbg_slot_i(dbg_slot_i), .dbg_idx_i(dbg_idx_i), .dbg_coeff_o(dbg_coeff_o),
.dbg_rho_o(dbg_rho_o), .dbg_sigma_o(dbg_sigma_o)
);
always #5 clk = ~clk;
localparam [255:0] D_LIT = 256'h2426f1941779574d3f1b163bd57f7e173e229e630ec7f7073bdf365137c4bb6d;
reg [11:0] gold [0:2047];
// slot order matches golden file order
reg [3:0] slot_of [0:7];
integer c, p, idx, errors, gi;
initial begin
$readmemh("sync_rtl/top/TB/vectors/kg_c000_AsE.hex", gold);
slot_of[0]=4'd0; slot_of[1]=4'd1; slot_of[2]=4'd2; slot_of[3]=4'd3; // A00..A11
slot_of[4]=4'd4; slot_of[5]=4'd5; // S0,S1
slot_of[6]=4'd6; slot_of[7]=4'd7; // E0,E1
d_i = D_LIT;
rst_n=0; repeat(4) @(posedge clk); rst_n=1; @(posedge clk);
start_i=1; @(posedge clk); start_i=0;
c=0; while(!done_o && c<200000) begin @(posedge clk); c=c+1; end
if(!done_o) begin $display("FAIL: timeout after %0d cyc", c); $finish; end
$display("=== Stage 2c: A_hat + s + e (8 polys) === done in %0d cyc", c);
errors = 0;
for (p = 0; p < 8; p = p + 1) begin
for (idx = 0; idx < 256; idx = idx + 1) begin
dbg_slot_i = slot_of[p];
dbg_idx_i = idx[7:0];
@(posedge clk); @(posedge clk); // 2 cyc for registered readback
gi = p*256 + idx;
if (dbg_coeff_o !== gold[gi]) begin
if (errors < 8)
$display(" MISMATCH slot%0d[%0d]: got=%03x exp=%03x",
slot_of[p], idx, dbg_coeff_o, gold[gi]);
errors = errors + 1;
end
end
end
if (errors == 0) $display("ALL TESTS PASSED (2048/2048 coeffs)");
else $display("TESTS FAILED: %0d mismatches", errors);
$finish;
end
initial begin #5000000; $display("FAIL: global timeout"); $finish; end
endmodule

View File

@@ -1,57 +0,0 @@
// tb_mlkem_kg_2d_xsim.v - Stage 2d: verify forward NTT of s/e in mlkem_top.
// After ST_N, slots S0,S1,E0,E1 must hold shat_0,shat_1,ehat_0,ehat_1.
// Golden: kg_c000_sehat.hex (4 polys x 256 = 1024 lines, mod-q).
`timescale 1ns/1ps
module tb_mlkem_kg_2d_xsim;
reg clk=0, rst_n=0, start_i=0;
reg [255:0] d_i, z_i=0;
wire busy_o, done_o;
reg [3:0] dbg_slot_i=0; reg [7:0] dbg_idx_i=0; wire [11:0] dbg_coeff_o;
wire [255:0] dbg_rho_o, dbg_sigma_o;
mlkem_top #(.K(2)) dut (
.clk(clk), .rst_n(rst_n), .d_i(d_i), .z_i(z_i), .start_i(start_i),
.busy_o(busy_o), .done_o(done_o),
.dbg_slot_i(dbg_slot_i), .dbg_idx_i(dbg_idx_i), .dbg_coeff_o(dbg_coeff_o),
.dbg_rho_o(dbg_rho_o), .dbg_sigma_o(dbg_sigma_o)
);
always #5 clk = ~clk;
localparam [255:0] D_LIT = 256'h2426f1941779574d3f1b163bd57f7e173e229e630ec7f7073bdf365137c4bb6d;
reg [11:0] gold [0:1023];
reg [3:0] slot_of [0:3]; // S0,S1,E0,E1
integer c, p, idx, errors, gi;
initial begin
$readmemh("sync_rtl/top/TB/vectors/kg_c000_sehat.hex", gold);
slot_of[0]=4'd4; slot_of[1]=4'd5; slot_of[2]=4'd6; slot_of[3]=4'd7;
d_i = D_LIT;
rst_n=0; repeat(4) @(posedge clk); rst_n=1; @(posedge clk);
start_i=1; @(posedge clk); start_i=0;
c=0; while(!done_o && c<300000) begin @(posedge clk); c=c+1; end
if(!done_o) begin $display("FAIL: timeout after %0d cyc", c); $finish; end
$display("=== Stage 2d: NTT(s/e) -> shat/ehat === done in %0d cyc", c);
errors = 0;
for (p = 0; p < 4; p = p + 1) begin
for (idx = 0; idx < 256; idx = idx + 1) begin
dbg_slot_i = slot_of[p];
dbg_idx_i = idx[7:0];
@(posedge clk); @(posedge clk);
gi = p*256 + idx;
if (dbg_coeff_o !== gold[gi]) begin
if (errors < 8)
$display(" MISMATCH slot%0d[%0d]: got=%03x exp=%03x",
slot_of[p], idx, dbg_coeff_o, gold[gi]);
errors = errors + 1;
end
end
end
if (errors == 0) $display("ALL TESTS PASSED (1024/1024 shat/ehat coeffs)");
else $display("TESTS FAILED: %0d mismatches", errors);
$finish;
end
initial begin #10000000; $display("FAIL: global timeout"); $finish; end
endmodule

View File

@@ -1,57 +0,0 @@
// tb_mlkem_kg_2e_xsim.v - Stage 2e: verify t_hat = e_hat + sum_j A[i][j]*s_hat[j].
// After ST_M, slots T0,T1 must hold that_0,that_1.
// Golden: kg_c000_that.hex (2 polys x 256 = 512 lines, mod-q).
`timescale 1ns/1ps
module tb_mlkem_kg_2e_xsim;
reg clk=0, rst_n=0, start_i=0;
reg [255:0] d_i, z_i=0;
wire busy_o, done_o;
reg [3:0] dbg_slot_i=0; reg [7:0] dbg_idx_i=0; wire [11:0] dbg_coeff_o;
wire [255:0] dbg_rho_o, dbg_sigma_o;
mlkem_top #(.K(2)) dut (
.clk(clk), .rst_n(rst_n), .d_i(d_i), .z_i(z_i), .start_i(start_i),
.busy_o(busy_o), .done_o(done_o),
.dbg_slot_i(dbg_slot_i), .dbg_idx_i(dbg_idx_i), .dbg_coeff_o(dbg_coeff_o),
.dbg_rho_o(dbg_rho_o), .dbg_sigma_o(dbg_sigma_o)
);
always #5 clk = ~clk;
localparam [255:0] D_LIT = 256'h2426f1941779574d3f1b163bd57f7e173e229e630ec7f7073bdf365137c4bb6d;
reg [11:0] gold [0:511];
reg [3:0] slot_of [0:1]; // T0,T1
integer c, p, idx, errors, gi;
initial begin
$readmemh("sync_rtl/top/TB/vectors/kg_c000_that.hex", gold);
slot_of[0]=4'd8; slot_of[1]=4'd9;
d_i = D_LIT;
rst_n=0; repeat(4) @(posedge clk); rst_n=1; @(posedge clk);
start_i=1; @(posedge clk); start_i=0;
c=0; while(!done_o && c<400000) begin @(posedge clk); c=c+1; end
if(!done_o) begin $display("FAIL: timeout after %0d cyc", c); $finish; end
$display("=== Stage 2e: t_hat = e_hat + sum A o s_hat === done in %0d cyc", c);
errors = 0;
for (p = 0; p < 2; p = p + 1) begin
for (idx = 0; idx < 256; idx = idx + 1) begin
dbg_slot_i = slot_of[p];
dbg_idx_i = idx[7:0];
@(posedge clk); @(posedge clk);
gi = p*256 + idx;
if (dbg_coeff_o !== gold[gi]) begin
if (errors < 8)
$display(" MISMATCH slot%0d[%0d]: got=%03x exp=%03x",
slot_of[p], idx, dbg_coeff_o, gold[gi]);
errors = errors + 1;
end
end
end
if (errors == 0) $display("ALL TESTS PASSED (512/512 t_hat coeffs)");
else $display("TESTS FAILED: %0d mismatches", errors);
$finish;
end
initial begin #20000000; $display("FAIL: global timeout"); $finish; end
endmodule

View File

@@ -1,66 +0,0 @@
// tb_mlkem_kg_2f_xsim.v - Stage 2f: verify byteEncode12 -> ek (800B), dk_pke (768B).
// ek = byteEncode12(t_hat[0..1]) || rho ; dk_pke = byteEncode12(s_hat[0..1]).
// Golden: c000_ek.hex (single 1600-hex line), c000_dkpke.hex (1536-hex line).
`timescale 1ns/1ps
module tb_mlkem_kg_2f_xsim;
reg clk=0, rst_n=0, start_i=0;
reg [255:0] d_i, z_i=0;
wire busy_o, done_o;
reg [3:0] dbg_slot_i=0; reg [7:0] dbg_idx_i=0; wire [11:0] dbg_coeff_o;
reg dbg_byte_sel_i=0; reg [9:0] dbg_byte_idx_i=0; wire [7:0] dbg_byte_o;
wire [255:0] dbg_rho_o, dbg_sigma_o;
mlkem_top #(.K(2)) dut (
.clk(clk), .rst_n(rst_n), .d_i(d_i), .z_i(z_i), .start_i(start_i),
.busy_o(busy_o), .done_o(done_o),
.dbg_slot_i(dbg_slot_i), .dbg_idx_i(dbg_idx_i), .dbg_coeff_o(dbg_coeff_o),
.dbg_byte_sel_i(dbg_byte_sel_i), .dbg_byte_idx_i(dbg_byte_idx_i), .dbg_byte_o(dbg_byte_o),
.dbg_rho_o(dbg_rho_o), .dbg_sigma_o(dbg_sigma_o)
);
always #5 clk = ~clk;
localparam [255:0] D_LIT = 256'h2426f1941779574d3f1b163bd57f7e173e229e630ec7f7073bdf365137c4bb6d;
// golden bytes loaded as memories: 1 byte per entry
reg [7:0] ek_gold [0:799];
reg [7:0] dkp_gold [0:767];
integer c, i, errors;
initial begin
$readmemh("sync_rtl/top/TB/vectors/c000_ek_bytes.hex", ek_gold);
$readmemh("sync_rtl/top/TB/vectors/c000_dkpke_bytes.hex", dkp_gold);
d_i = D_LIT;
rst_n=0; repeat(4) @(posedge clk); rst_n=1; @(posedge clk);
start_i=1; @(posedge clk); start_i=0;
c=0; while(!done_o && c<500000) begin @(posedge clk); c=c+1; end
if(!done_o) begin $display("FAIL: timeout after %0d cyc", c); $finish; end
$display("=== Stage 2f: byteEncode12 -> ek/dk_pke === done in %0d cyc", c);
errors = 0;
// ek: 800 bytes (sel=0)
dbg_byte_sel_i = 1'b0;
for (i = 0; i < 800; i = i + 1) begin
dbg_byte_idx_i = i[9:0];
@(posedge clk); @(posedge clk);
if (dbg_byte_o !== ek_gold[i]) begin
if (errors < 8) $display(" EK MISMATCH[%0d]: got=%02x exp=%02x", i, dbg_byte_o, ek_gold[i]);
errors = errors + 1;
end
end
// dk_pke: 768 bytes (sel=1)
dbg_byte_sel_i = 1'b1;
for (i = 0; i < 768; i = i + 1) begin
dbg_byte_idx_i = i[9:0];
@(posedge clk); @(posedge clk);
if (dbg_byte_o !== dkp_gold[i]) begin
if (errors < 8) $display(" DKP MISMATCH[%0d]: got=%02x exp=%02x", i, dbg_byte_o, dkp_gold[i]);
errors = errors + 1;
end
end
if (errors == 0) $display("ALL TESTS PASSED (ek 800B + dk_pke 768B exact)");
else $display("TESTS FAILED: %0d byte mismatches", errors);
$finish;
end
initial begin #30000000; $display("FAIL: global timeout"); $finish; end
endmodule

View File

@@ -1,72 +0,0 @@
// tb_mlkem_kg_katN_xsim.v - ML-KEM-512 KeyGen vs NIST KAT, case selected by +CASE=n.
// Loads d, z, ek, dk from per-case hex files (no literals via text channel).
// d/z: single 256-bit word ($readmemh, byte0 in [7:0]).
// ek : 800 bytes (== KAT pk). dk: 1632 bytes (== KAT sk).
`timescale 1ns/1ps
module tb_mlkem_kg_katN_xsim;
reg clk=0, rst_n=0, start_i=0;
reg [255:0] d_i, z_i;
wire busy_o, done_o;
reg [3:0] dbg_slot_i=0; reg [7:0] dbg_idx_i=0; wire [11:0] dbg_coeff_o;
reg dbg_byte_sel_i=0; reg [9:0] dbg_byte_idx_i=0; wire [7:0] dbg_byte_o;
reg [11:0] dbg_dk_idx_i=0; wire [7:0] dbg_dk_o;
wire [255:0] dbg_rho_o, dbg_sigma_o;
mlkem_top #(.K(2)) dut (
.clk(clk), .rst_n(rst_n), .d_i(d_i), .z_i(z_i), .start_i(start_i),
.busy_o(busy_o), .done_o(done_o),
.dbg_slot_i(dbg_slot_i), .dbg_idx_i(dbg_idx_i), .dbg_coeff_o(dbg_coeff_o),
.dbg_byte_sel_i(dbg_byte_sel_i), .dbg_byte_idx_i(dbg_byte_idx_i), .dbg_byte_o(dbg_byte_o),
.dbg_dk_idx_i(dbg_dk_idx_i), .dbg_dk_o(dbg_dk_o),
.dbg_rho_o(dbg_rho_o), .dbg_sigma_o(dbg_sigma_o)
);
always #5 clk = ~clk;
reg [255:0] dmem [0:0];
reg [255:0] zmem [0:0];
reg [7:0] ek_gold [0:799];
reg [7:0] dk_gold [0:1631];
integer c, i, errors, casenum;
reg [8*64-1:0] dfile, zfile, ekfile, dkfile;
initial begin
if (!$value$plusargs("CASE=%d", casenum)) casenum = 0;
$sformat(dfile, "sync_rtl/top/TB/vectors/kat_c%0d_d.hex", casenum);
$sformat(zfile, "sync_rtl/top/TB/vectors/kat_c%0d_z.hex", casenum);
$sformat(ekfile, "sync_rtl/top/TB/vectors/kat_c%0d_ek.hex", casenum);
$sformat(dkfile, "sync_rtl/top/TB/vectors/kat_c%0d_dk.hex", casenum);
$readmemh(dfile, dmem);
$readmemh(zfile, zmem);
$readmemh(ekfile, ek_gold);
$readmemh(dkfile, dk_gold);
d_i = dmem[0];
z_i = zmem[0];
rst_n=0; repeat(4) @(posedge clk); rst_n=1; @(posedge clk);
start_i=1; @(posedge clk); start_i=0;
c=0; while(!done_o && c<600000) begin @(posedge clk); c=c+1; end
if(!done_o) begin $display("FAIL case %0d: timeout", casenum); $finish; end
$display("=== KAT case %0d: KeyGen done in %0d cyc ===", casenum, c);
errors = 0;
dbg_byte_sel_i = 1'b0;
for (i = 0; i < 800; i = i + 1) begin
dbg_byte_idx_i = i[9:0]; @(posedge clk); @(posedge clk);
if (dbg_byte_o !== ek_gold[i]) begin
if (errors < 8) $display(" EK[%0d] got=%02x exp=%02x", i, dbg_byte_o, ek_gold[i]);
errors = errors + 1;
end
end
for (i = 0; i < 1632; i = i + 1) begin
dbg_dk_idx_i = i[11:0]; @(posedge clk); @(posedge clk);
if (dbg_dk_o !== dk_gold[i]) begin
if (errors < 8) $display(" DK[%0d] got=%02x exp=%02x", i, dbg_dk_o, dk_gold[i]);
errors = errors + 1;
end
end
if (errors == 0) $display("CASE %0d PASS: ek==KAT.pk (800B), dk==KAT.sk (1632B)", casenum);
else $display("CASE %0d FAIL: %0d byte mismatches", casenum, errors);
$finish;
end
initial begin #40000000; $display("FAIL: global timeout"); $finish; end
endmodule

View File

@@ -1,68 +0,0 @@
// tb_mlkem_kg_kat_xsim.v - Stage 4 end-to-end: full ML-KEM-512 KeyGen vs NIST KAT.
// Drives d/z (KAT count=0), runs KeyGen, verifies:
// ek (800B, sel=0) == KAT pk
// dk (1632B, dk tap) == KAT sk (= dk_pke || ek || H(ek) || z)
// No force/release pure valid/ready via start_i/done_o.
`timescale 1ns/1ps
module tb_mlkem_kg_kat_xsim;
reg clk=0, rst_n=0, start_i=0;
reg [255:0] d_i, z_i;
wire busy_o, done_o;
reg [3:0] dbg_slot_i=0; reg [7:0] dbg_idx_i=0; wire [11:0] dbg_coeff_o;
reg dbg_byte_sel_i=0; reg [9:0] dbg_byte_idx_i=0; wire [7:0] dbg_byte_o;
reg [11:0] dbg_dk_idx_i=0; wire [7:0] dbg_dk_o;
wire [255:0] dbg_rho_o, dbg_sigma_o;
mlkem_top #(.K(2)) dut (
.clk(clk), .rst_n(rst_n), .d_i(d_i), .z_i(z_i), .start_i(start_i),
.busy_o(busy_o), .done_o(done_o),
.dbg_slot_i(dbg_slot_i), .dbg_idx_i(dbg_idx_i), .dbg_coeff_o(dbg_coeff_o),
.dbg_byte_sel_i(dbg_byte_sel_i), .dbg_byte_idx_i(dbg_byte_idx_i), .dbg_byte_o(dbg_byte_o),
.dbg_dk_idx_i(dbg_dk_idx_i), .dbg_dk_o(dbg_dk_o),
.dbg_rho_o(dbg_rho_o), .dbg_sigma_o(dbg_sigma_o)
);
always #5 clk = ~clk;
// KAT count=0 (byte0-low literals)
localparam [255:0] D_LIT = 256'h2426f1941779574d3f1b163bd57f7e173e229e630ec7f7073bdf365137c4bb6d;
localparam [255:0] Z_LIT = 256'h687acf9406694974d383032f7579378f449c75d0560af56cf921ec48404896f6;
reg [7:0] ek_gold [0:799];
reg [7:0] dk_gold [0:1631];
integer c, i, errors;
initial begin
$readmemh("sync_rtl/top/TB/vectors/c000_ek_bytes.hex", ek_gold);
$readmemh("sync_rtl/top/TB/vectors/c000_dk_full_bytes.hex", dk_gold);
d_i = D_LIT; z_i = Z_LIT;
rst_n=0; repeat(4) @(posedge clk); rst_n=1; @(posedge clk);
start_i=1; @(posedge clk); start_i=0;
c=0; while(!done_o && c<600000) begin @(posedge clk); c=c+1; end
if(!done_o) begin $display("FAIL: timeout after %0d cyc", c); $finish; end
$display("=== Stage 4: ML-KEM-512 KeyGen end-to-end vs NIST KAT === done in %0d cyc", c);
errors = 0;
// ek == KAT pk (800B)
dbg_byte_sel_i = 1'b0;
for (i = 0; i < 800; i = i + 1) begin
dbg_byte_idx_i = i[9:0]; @(posedge clk); @(posedge clk);
if (dbg_byte_o !== ek_gold[i]) begin
if (errors < 8) $display(" EK[%0d] got=%02x exp=%02x", i, dbg_byte_o, ek_gold[i]);
errors = errors + 1;
end
end
// dk == KAT sk (1632B)
for (i = 0; i < 1632; i = i + 1) begin
dbg_dk_idx_i = i[11:0]; @(posedge clk); @(posedge clk);
if (dbg_dk_o !== dk_gold[i]) begin
if (errors < 8) $display(" DK[%0d] got=%02x exp=%02x", i, dbg_dk_o, dk_gold[i]);
errors = errors + 1;
end
end
if (errors == 0) $display("ALL TESTS PASSED: ek==KAT.pk (800B), dk==KAT.sk (1632B)");
else $display("TESTS FAILED: %0d byte mismatches", errors);
$finish;
end
initial begin #40000000; $display("FAIL: global timeout"); $finish; end
endmodule

View File

@@ -1,118 +0,0 @@
#!/usr/bin/env python3
# xcheck_mlkemr.py - Cross-validate the hardware ML-KEM KeyGen KAT vectors
# against the ml-kem-r Rust reference's NIST .rsp files.
#
# WHY THIS SCRIPT EXISTS (byte-order convention):
# The hardware feeds G(reverse(d) || K), i.e. it byte-reverses the d/z seeds
# before hashing. Consequently the hardware vector files
# sync_rtl/top/TB/vectors/kat_k{K}_c{C}_{d,z}.hex
# store d/z in the REVERSE byte order of the NIST .rsp `d`/`z` fields. Both
# conventions hash to the SAME pk/sk (reverse(reverse(d)) == d), so both the
# hardware KAT and ml-kem-r's own KAT pass -- they just disagree on how d/z
# are written down. A naive byte-for-byte compare of d/z therefore "fails"
# even though the implementations are equivalent. This script compares with
# the reversal applied, and compares ek/dk (== pk/sk) directly.
#
# See also: the TB d/z dump in tb_mlkem_kg_katK_xsim.v and the keygen G-hash
# reversal in sync_rtl/top/mlkem_top.v.
#
# Usage:
# python3 sync_rtl/top/TB/xcheck_mlkemr.py
# ML_KEM_R=/path/to/ml-kem-r python3 sync_rtl/top/TB/xcheck_mlkemr.py
#
# Exit code 0 = all consistent, 1 = discrepancy / missing files.
import os
import sys
# Hardware vector dir (relative to repo root, where this script's ../../.. is).
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
HW_DIR = SCRIPT_DIR # vectors live under SCRIPT_DIR/vectors
# ml-kem-r location: env override, else common sibling path.
ML_KEM_R = os.environ.get("ML_KEM_R", os.path.expanduser("~/Dev/ml-kem-r"))
RSP = {
2: "test_data/kat_MLKEM_512.rsp",
3: "test_data/kat_MLKEM_768.rsp",
4: "test_data/kat_MLKEM_1024.rsp",
}
# Cases present in the hardware vector set per K (K=2 has 0..4, K=3/4 have 0..2).
CASES = {2: range(5), 3: range(3), 4: range(3)}
def die(msg):
print(f"ERROR: {msg}", file=sys.stderr)
sys.exit(1)
def rsp_cases(path):
"""Parse a NIST .rsp into a list of dicts keyed by count."""
if not os.path.isfile(path):
die(f"ml-kem-r .rsp not found: {path}\n"
f" set ML_KEM_R=/path/to/ml-kem-r (current: {ML_KEM_R})")
cur, out = None, []
with open(path) as f:
for ln in f:
ln = ln.strip()
if ln.startswith("count = "):
if cur is not None:
out.append(cur)
cur = {"count": int(ln[8:])}
elif " = " in ln and cur is not None:
k, v = ln.split(" = ", 1)
cur[k] = v.strip().lower()
if cur is not None:
out.append(cur)
return out
def hwcat(k, c, which):
"""Concatenate a hardware .hex vector file into one lowercase hex string."""
path = os.path.join(HW_DIR, "vectors", f"kat_k{k}_c{c}_{which}.hex")
if not os.path.isfile(path):
die(f"hardware vector not found: {path}")
with open(path) as f:
return "".join(f.read().split()).lower()
def revhex(h):
"""Byte-reverse a hex string."""
return bytes.fromhex(h)[::-1].hex()
def main():
print(f"ml-kem-r: {ML_KEM_R}")
print(f"hardware: {os.path.join(HW_DIR, 'vectors')}")
print("convention: hardware d/z == reverse(NIST d/z); ek==pk, dk==sk\n")
all_ok = True
total = 0
for k in (2, 3, 4):
cases = rsp_cases(os.path.join(ML_KEM_R, RSP[k]))
bycount = {c["count"]: c for c in cases}
for c in CASES[k]:
total += 1
hd, hz = hwcat(k, c, "d"), hwcat(k, c, "z")
hek, hdk = hwcat(k, c, "ek"), hwcat(k, c, "dk")
rc = bycount.get(c)
if rc is None:
die(f"ml-kem-r .rsp for K={k} has no count={c}")
d_ok = revhex(hd) == rc["d"]
z_ok = revhex(hz) == rc["z"]
ek_ok = hek == rc["pk"]
dk_ok = hdk == rc["sk"]
case_ok = d_ok and z_ok and ek_ok and dk_ok
all_ok = all_ok and case_ok
print(f"K={k} c{c}: d(rev)={'Y' if d_ok else 'N'} "
f"z(rev)={'Y' if z_ok else 'N'} "
f"ek={'Y' if ek_ok else 'N'} "
f"dk={'Y' if dk_ok else 'N'} -> "
f"{'OK' if case_ok else 'MISMATCH'}")
print(f"\n{total} cases checked: "
f"{'ALL CONSISTENT' if all_ok else 'DISCREPANCY FOUND'}")
sys.exit(0 if all_ok else 1)
if __name__ == "__main__":
main()

View File

@@ -13,7 +13,7 @@
// ek = byteEncode12(t_hat[0..K-1]) || rho // ek = byteEncode12(t_hat[0..K-1]) || rho
// dk = byteEncode12(s_hat[0..K-1]) || ek || H(ek) || z // dk = byteEncode12(s_hat[0..K-1]) || ek || H(ek) || z
// //
// Built incrementally and verified stage-by-stage against ml-kem-r golden // Built incrementally and verified stage-by-stage against reference golden
// vectors (test_framework/modules/mlkem_keygen/golden) and NIST KAT. // vectors (test_framework/modules/mlkem_keygen/golden) and NIST KAT.
// //
// Uses independent (verified) leaf modules, each with its own keccak_core: // Uses independent (verified) leaf modules, each with its own keccak_core: