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:
@@ -1,12 +1,14 @@
|
||||
// tb_sha3_xsim_simple.v - Simple self-checking testbench for sha3_top
|
||||
// tb_sha3_xsim_simple.v - Self-checking testbench for sha3_top (G/H/J modes)
|
||||
//
|
||||
// Tests sha3_top in G mode (SHA3-512) with a hardcoded all-zero input.
|
||||
// Verifies the output hash against an expected value.
|
||||
// Uses $display for output and $error for mismatches.
|
||||
// Self-checking: pass/fail determined by $error count at $finish.
|
||||
// Drives sha3_top in all three modes and checks hash_o against expected
|
||||
// values computed by the verified Python reference (server_code SHA_3,
|
||||
// KAT-validated). Vectors are deterministic (gen_vectors seed=20260627).
|
||||
//
|
||||
// NOTE: This testbench uses the RTL's actual padding (suffix "10").
|
||||
// The expected hash was pre-computed using the same algorithm as the RTL.
|
||||
// G (mode=00, SHA3-512): data_i[263:0] = d||k, output 512 bits
|
||||
// H (mode=01, SHA3-256): data_i[255:0] = ek, output low 256 bits
|
||||
// J (mode=10, SHAKE-256): data_i[511:0] = z||c, output low 256 bits
|
||||
//
|
||||
// data_i / hash_o use MSB-first hex packing (hex[511:0] literal == data_i).
|
||||
//
|
||||
// Usage:
|
||||
// xvlog -sv sha3_top.v tb_sha3_xsim_simple.v
|
||||
@@ -52,111 +54,119 @@ module tb_sha3_xsim_simple;
|
||||
always #5 clk = ~clk;
|
||||
|
||||
// ================================================================
|
||||
// Expected hash value for G mode with all-zero input
|
||||
//
|
||||
// Input: data_i[263:0] = 264'd0 (all zeros)
|
||||
// mode = 2'b00 (G mode, SHA3-512)
|
||||
//
|
||||
// RTL g_pad = {1'b1, 308'b0, 1'b1, 2'b10, data_i[263:0]}
|
||||
// absorb_state = {1024'b0, g_pad}
|
||||
//
|
||||
// Expected hash_o = Keccak-f[1600](absorb_state) lower 512 bits
|
||||
// Expected vectors (verified Python reference, seed=20260627)
|
||||
// ================================================================
|
||||
parameter [511:0] G_EXPECTED_HASH = 512'h93d50514dbf28b7f2b6aa4f34bc6bd53368a9a20c6568940dc8eb3ce0a8e357f8608c63ce7b579f6916c69ca3f196527ccc92b87c515edc12e159e0f3092e1d9;
|
||||
// G: data_i[263:0] = {k=2, d}; output 512-bit SHA3-512
|
||||
localparam [511:0] G_DATA = 512'h000000000000000000000000000000000000000000000000000000000000007DBC2AC0D13D719B37B4E2D4691951FF890A97854EF5D3A8957EF67A54978E26C9;
|
||||
localparam [511:0] G_EXP = 512'h615E530C77D5D834311E922DB99D5B7F1D57C7B08F029FD829914D3F4035FB730350FD00A852A5CCE9CFC79CF8C61384FEA115030E41750A6AE2CFE055D7976D;
|
||||
|
||||
// H: data_i[255:0] = ek; output 256-bit SHA3-256 (in hash_o[255:0])
|
||||
localparam [511:0] H_DATA = 512'h000000000000000000000000000000000000000000000000000000000000000047885BF4E257CF39645D34C593047B7570D6ABBA300599D96171A950BB5027D5;
|
||||
localparam [255:0] H_EXP = 256'h6B44867E24B29A9231570DF5E1D6D14DB0C29EBD7A40AE98606EF66B8244C308;
|
||||
|
||||
// J: data_i[511:0] = {c, z}; output 256-bit SHAKE-256 (in hash_o[255:0])
|
||||
localparam [511:0] J_DATA = 512'hB73DCA0F437D2334320494E5D0F728D73F5275E342572FF9B0219DC338CB3C2F0F7398474A3D68C4C90B777F42FA4C12B1FC8F70E1DAADF20755473CC2653D3C;
|
||||
localparam [255:0] J_EXP = 256'hECEC4DAD11DFF42D911925DB83F13D119209AB4EE182E9E9BA0F29F5524B240E;
|
||||
|
||||
// ================================================================
|
||||
// Test sequence
|
||||
// ================================================================
|
||||
reg [511:0] captured_hash;
|
||||
integer error_count;
|
||||
integer cycle_count;
|
||||
parameter TIMEOUT = 200;
|
||||
|
||||
// ================================================================
|
||||
// Drive one mode and self-check against expected output
|
||||
// ================================================================
|
||||
task run_mode(input [1:0] m, input [511:0] din,
|
||||
input [511:0] exp, input integer out_bits,
|
||||
input [127:0] label);
|
||||
reg [511:0] got;
|
||||
begin
|
||||
@(posedge clk);
|
||||
mode <= m;
|
||||
data_i <= din;
|
||||
valid_i <= 1'b1;
|
||||
@(posedge clk);
|
||||
valid_i <= 1'b0;
|
||||
|
||||
cycle_count = 0;
|
||||
while (!valid_o && cycle_count < TIMEOUT) begin
|
||||
@(posedge clk);
|
||||
cycle_count = cycle_count + 1;
|
||||
end
|
||||
|
||||
if (cycle_count >= TIMEOUT) begin
|
||||
$display("FAIL [%0s]: TIMEOUT, valid_o not asserted", label);
|
||||
error_count = error_count + 1;
|
||||
end else begin
|
||||
got = hash_o;
|
||||
if (out_bits == 512) begin
|
||||
if (got !== exp) begin
|
||||
$display("FAIL [%0s]: hash mismatch", label);
|
||||
$display(" got = 512'h%0h", got);
|
||||
$display(" exp = 512'h%0h", exp);
|
||||
error_count = error_count + 1;
|
||||
end else begin
|
||||
$display("PASS [%0s]: hash_o = 512'h%0h", label, got);
|
||||
end
|
||||
end else begin
|
||||
// Compare low 256 bits only
|
||||
if (got[255:0] !== exp[255:0]) begin
|
||||
$display("FAIL [%0s]: hash mismatch", label);
|
||||
$display(" got = 256'h%0h", got[255:0]);
|
||||
$display(" exp = 256'h%0h", exp[255:0]);
|
||||
error_count = error_count + 1;
|
||||
end else begin
|
||||
$display("PASS [%0s]: hash_o = 256'h%0h", label, got[255:0]);
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
// Wait for DUT to return to IDLE
|
||||
@(posedge clk);
|
||||
while (!ready_o) @(posedge clk);
|
||||
end
|
||||
endtask
|
||||
|
||||
// ================================================================
|
||||
// Test sequence
|
||||
// ================================================================
|
||||
initial begin
|
||||
error_count = 0;
|
||||
|
||||
$display("========================================");
|
||||
$display(" SHA3 Top Simple Self-Checking Testbench");
|
||||
$display(" Mode: G (SHA3-512)");
|
||||
$display(" Input: data_i = 512'd0");
|
||||
$display(" SHA3 Top Testbench (G / H / J modes)");
|
||||
$display("========================================");
|
||||
|
||||
// Initialize
|
||||
mode = 2'd0; // G mode
|
||||
data_i = 512'd0;
|
||||
valid_i = 1'b0;
|
||||
ready_i = 1'b1; // always ready
|
||||
mode <= 2'd0;
|
||||
data_i <= 512'd0;
|
||||
valid_i <= 1'b0;
|
||||
ready_i <= 1'b1;
|
||||
|
||||
// Reset: rst_n low for 3 cycles
|
||||
rst_n = 1'b0;
|
||||
rst_n <= 1'b0;
|
||||
repeat (3) @(posedge clk);
|
||||
rst_n = 1'b1;
|
||||
rst_n <= 1'b1;
|
||||
@(posedge clk);
|
||||
|
||||
$display("INFO: Reset complete. Starting test...");
|
||||
$display("INFO: Reset complete. Running G/H/J...");
|
||||
|
||||
// Drive test vector
|
||||
mode = 2'd0;
|
||||
data_i = 512'd0;
|
||||
valid_i = 1'b1;
|
||||
@(posedge clk);
|
||||
valid_i = 1'b0;
|
||||
run_mode(2'd0, G_DATA, G_EXP, 512, "G SHA3-512");
|
||||
run_mode(2'd1, H_DATA, {256'd0,H_EXP},256, "H SHA3-256");
|
||||
run_mode(2'd2, J_DATA, {256'd0,J_EXP},256, "J SHAKE-256");
|
||||
|
||||
$display("INFO: Vector driven (mode=G, data=0). Waiting for valid_o...");
|
||||
|
||||
// Wait for valid_o
|
||||
cycle_count = 0;
|
||||
while (!valid_o && cycle_count < TIMEOUT) begin
|
||||
@(posedge clk);
|
||||
cycle_count = cycle_count + 1;
|
||||
end
|
||||
|
||||
if (cycle_count >= TIMEOUT) begin
|
||||
$error("TIMEOUT: valid_o not asserted within %0d cycles", TIMEOUT);
|
||||
error_count = error_count + 1;
|
||||
end else begin
|
||||
captured_hash = hash_o;
|
||||
|
||||
$display("INFO: valid_o asserted after %0d cycles", cycle_count + 1);
|
||||
$display("INFO: hash_o = 512'h%0h", captured_hash);
|
||||
|
||||
// Check against expected
|
||||
if (captured_hash !== G_EXPECTED_HASH) begin
|
||||
$error("MISMATCH!");
|
||||
$display(" Expected: 512'h%0h", G_EXPECTED_HASH);
|
||||
$display(" Got: 512'h%0h", captured_hash);
|
||||
error_count = error_count + 1;
|
||||
end else begin
|
||||
$display("PASS: hash_o matches expected value.");
|
||||
end
|
||||
end
|
||||
|
||||
// One extra cycle
|
||||
@(posedge clk);
|
||||
|
||||
// ============================================================
|
||||
// Summary
|
||||
// ============================================================
|
||||
$display("========================================");
|
||||
if (error_count == 0) begin
|
||||
$display("ALL TESTS PASSED");
|
||||
end else begin
|
||||
$display("TESTS FAILED: %0d error(s)", error_count);
|
||||
end
|
||||
$display("========================================");
|
||||
|
||||
// Vivado xsim: $finish with error code
|
||||
if (error_count > 0)
|
||||
$finish;
|
||||
if (error_count == 0)
|
||||
$display("ALL TESTS PASSED (3/3 modes)");
|
||||
else
|
||||
$finish;
|
||||
$display("TESTS FAILED: %0d error(s)", error_count);
|
||||
$display("========================================");
|
||||
|
||||
$finish;
|
||||
end
|
||||
|
||||
// ================================================================
|
||||
// Timeout watchdog
|
||||
// ================================================================
|
||||
initial begin
|
||||
#(TIMEOUT * 10 * 10); // TIMEOUT * 10ns * extra margin
|
||||
#(TIMEOUT * 10 * 100);
|
||||
$display("FATAL: Global simulation timeout");
|
||||
$finish;
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user