fix(sample_ntt,sha3): FIPS-203 SHAKE-128 squeeze + self-checking sha3 TBs

sample_ntt was non-conformant: both RTL and the test reference re-ran
keccak_p after every 3-byte squeeze instead of consuming the full
1344-bit SHAKE-128 rate. Only coeff[0] matched a standard sampler, so
the generated A matrix would not interoperate with any compliant ML-KEM.

- sample_ntt_sync{,_shared}.v: walk all 56 groups of the rate block via
  grp_ptr_r; re-permute only when the block is exhausted. Verified
  256/256 against ml-kem-r Rust sample_ntt on two seeds, and 1536/1536
  in the Verilator framework (runtime ~128x faster per poly).
- gen_vectors.py: use a self-contained hashlib.shake_128 oracle.

sha3 testbench fixes (all now self-check hash_o against verified vectors,
cross-checked with hashlib and ml-kem-r mlkem_G):
- tb_sha3_xsim_simple.v: test G/H/J modes, not just G.
- tb_keccak_core_xsim.v: correct the wrong EXPECTED_STATE constant
  (RTL was correct; lane0 = 0xf1258f7940e1dde7 per FIPS 202).
- tb_sha3_xsim.v: read expected file and self-check per vector; add
  vectors/g_basic_{input,expected}.hex (3 G / 2 H / 2 J).

Remove stale sha3_chain test (its RTL was deleted in 1cace51) and its
README references. Extend .gitignore for XSIM artifacts and result dumps.
This commit is contained in:
2026-06-27 17:23:28 +08:00
parent 5d86000231
commit 4d7ce69405
12 changed files with 318 additions and 295 deletions

View File

@@ -26,6 +26,7 @@ module tb_sha3_xsim;
// Parameters
// ================================================================
parameter VECTOR_FILE = "sync_rtl/sha3/TB/vectors/g_basic_input.hex";
parameter EXPECTED_FILE = "sync_rtl/sha3/TB/vectors/g_basic_expected.hex";
parameter RESULT_FILE = "sync_rtl/sha3/TB/vectors/g_basic_result.hex";
parameter MAX_VECTORS = 256;
parameter TIMEOUT_CYCLES = 1000;
@@ -69,6 +70,7 @@ module tb_sha3_xsim;
// 520 bits per word: bits[519:512]=padding+mode, bits[511:0]=data_i
// ================================================================
reg [519:0] vector_mem [0:MAX_VECTORS-1];
reg [511:0] expected_mem [0:MAX_VECTORS-1]; // expected hash per vector
integer vec_count;
integer idx;
integer cycle_count;
@@ -100,6 +102,8 @@ module tb_sha3_xsim;
// Load vectors from hex file
$readmemh(VECTOR_FILE, vector_mem);
// Load expected hashes (one 512-bit hex per line, MSB-first)
$readmemh(EXPECTED_FILE, expected_mem);
// Count non-zero entries to determine actual vector count
// (XSim leaves unloaded entries as 520'hX)
@@ -180,9 +184,29 @@ module tb_sha3_xsim;
$display("ERROR: Timeout waiting for valid_o on vector %0d", idx);
fail_count = fail_count + 1;
end else begin
// Capture hash output
// Capture hash output and self-check against expected.
// G (mode 0) uses all 512 bits; H/J use the low 256 bits.
captured_hash = hash_o;
pass_count = pass_count + 1;
begin
reg [511:0] exp_hash;
reg match;
exp_hash = expected_mem[idx];
if (vec_mode == 2'd0)
match = (captured_hash === exp_hash);
else
match = (captured_hash[255:0] === exp_hash[255:0]);
if (match) begin
pass_count = pass_count + 1;
$display("PASS: Vector %0d (mode=%0d)", idx, vec_mode);
end else begin
fail_count = fail_count + 1;
$display("FAIL: Vector %0d (mode=%0d) hash mismatch", idx, vec_mode);
$display(" got = %0h", (vec_mode==2'd0) ? captured_hash : {256'd0, captured_hash[255:0]});
$display(" exp = %0h", (vec_mode==2'd0) ? exp_hash : {256'd0, exp_hash[255:0]});
end
end
// Write result to output file
// Format: "RESULT: MODE HASH_HEX"
@@ -216,6 +240,11 @@ module tb_sha3_xsim;
$display(" Failed: %0d", fail_count);
$display(" Results written to: %s", RESULT_FILE);
$display("========================================");
if (fail_count == 0)
$display("ALL TESTS PASSED (%0d/%0d)", pass_count, vec_count);
else
$display("TESTS FAILED: %0d of %0d", fail_count, vec_count);
$display("========================================");
$finish;
end