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).
188 lines
8.1 KiB
Verilog
188 lines
8.1 KiB
Verilog
// sha3_top_shared.v - SHA3/SHAKE wrapper with EXTERNAL (shared) keccak_core.
|
|
//
|
|
// Identical to sha3_top.v except the internal keccak_core instance is
|
|
// replaced by explicit kc_* ports, so several Keccak consumers can share a
|
|
// single keccak_core via a phase mux (see mlkem_top). The sponge state
|
|
// (mb_state_r) and all FSM/squeeze logic stay here; only the 24-round
|
|
// permutation is external. Bit-identical to sha3_top when wired to a
|
|
// dedicated keccak_core.
|
|
//
|
|
// Single-block modes (mb_en=0):
|
|
// 00 = G (SHA3-512): rate=576, suffix=01, msg_len=264, out=512
|
|
// 01 = H (SHA3-256): rate=1088, suffix=01, msg_len=256, out=256
|
|
// 10 = J (SHAKE-256): rate=1088, suffix=1111,msg_len=512,out=256
|
|
//
|
|
// Multi-block SHA3-256 (mb_en=1): streams pre-padded 1088-bit rate blocks;
|
|
// the CALLER applies SHA3-256 padding to the final block.
|
|
//
|
|
// Keccak interface (port names mirror the keccak_core port each connects to):
|
|
// kc_state_o[1599:0] - keccak result (input, from keccak_core.state_o)
|
|
// kc_valid_o - keccak output valid (input, from keccak_core.valid_o)
|
|
// kc_ready_o - keccak ready for input (input, from keccak_core.ready_o)
|
|
// kc_state_i[1599:0] - keccak input state (output, to keccak_core.state_i)
|
|
// kc_valid_i - request permutation (output, to keccak_core.valid_i)
|
|
// kc_ready_i - accept keccak output (output, to keccak_core.ready_i, always 1)
|
|
|
|
module sha3_top_shared (
|
|
input clk,
|
|
input rst_n,
|
|
input [1:0] mode,
|
|
input [511:0] data_i,
|
|
input valid_i,
|
|
output ready_o,
|
|
output [511:0] hash_o,
|
|
output valid_o,
|
|
input ready_i,
|
|
// --- multi-block SHA3-256 absorb (tie mb_en=0 to disable) ---
|
|
input mb_en,
|
|
input [1087:0] mb_block_i,
|
|
input mb_valid_i,
|
|
input mb_last_i,
|
|
output mb_ready_o,
|
|
// --- external (shared) keccak_core interface ---
|
|
input [1599:0] kc_state_o,
|
|
input kc_valid_o,
|
|
/* verilator lint_off UNUSEDSIGNAL */
|
|
input kc_ready_o,
|
|
/* verilator lint_on UNUSEDSIGNAL */
|
|
output [1599:0] kc_state_i,
|
|
output kc_valid_i,
|
|
output kc_ready_i
|
|
);
|
|
|
|
// ================================================================
|
|
// FSM state encoding
|
|
// ================================================================
|
|
localparam ST_IDLE = 2'd0;
|
|
localparam ST_PERMUTE = 2'd1;
|
|
localparam ST_SQUEEZE = 2'd2;
|
|
|
|
reg [1:0] state_r, state_next;
|
|
|
|
// ================================================================
|
|
// Absorb state: message || suffix || pad10*1 into rate bits
|
|
// ================================================================
|
|
wire [575:0] g_pad;
|
|
wire [575:0] g512_pad;
|
|
wire [1087:0] h_pad;
|
|
wire [1087:0] j_pad;
|
|
|
|
assign g_pad = {1'b1, {308{1'b0}}, 1'b1, 2'b10, data_i[263:0]};
|
|
assign h_pad = {1'b1, {828{1'b0}}, 1'b1, 2'b10, data_i[255:0]};
|
|
// J: SHAKE suffix is "1111" — all ones, order irrelevant
|
|
assign j_pad = {1'b1, {570{1'b0}}, 1'b1, 4'b1111, data_i[511:0]};
|
|
// mode 11 = G over a full 64-byte (512-bit) message (Encaps: G(m||H(ek))).
|
|
// SHA3-512 rate=576; suffix 01 + pad10*1: 1 + 60 zeros + 1 + 2'b10 + 512 = 576.
|
|
assign g512_pad = {1'b1, {60{1'b0}}, 1'b1, 2'b10, data_i[511:0]};
|
|
|
|
wire [1599:0] absorb_state;
|
|
assign absorb_state = (mode == 2'b00) ? {{(1600-576){1'b0}}, g_pad} :
|
|
(mode == 2'b01) ? {{(1600-1088){1'b0}}, h_pad} :
|
|
(mode == 2'b10) ? {{(1600-1088){1'b0}}, j_pad} :
|
|
{{(1600-576){1'b0}}, g512_pad};
|
|
|
|
// ================================================================
|
|
// Multi-block SHA3-256 absorb FSM (active only when mb_en=1)
|
|
// ================================================================
|
|
localparam MB_IDLE = 2'd0; // ready for a block (or first block)
|
|
localparam MB_PERMUTE = 2'd1; // keccak running on xored state
|
|
localparam MB_DONE = 2'd2; // squeeze: present 256-bit digest
|
|
|
|
reg [1:0] mb_state, mb_state_next;
|
|
reg [1599:0] mb_state_r; // running sponge state
|
|
reg mb_last_r; // captured last-block flag
|
|
reg [255:0] mb_digest_r; // latched 256-bit digest (sticky in MB_DONE)
|
|
|
|
// XOR the incoming block into the low 1088 bits (rate) of the state.
|
|
wire [1599:0] mb_xored;
|
|
assign mb_xored = mb_state_r ^ {{(1600-1088){1'b0}}, mb_block_i};
|
|
|
|
// Accept a block only in MB_IDLE while enabled.
|
|
assign mb_ready_o = mb_en && (mb_state == MB_IDLE);
|
|
wire mb_accept = mb_en && (mb_state == MB_IDLE) && mb_valid_i;
|
|
wire mb_kc_valid = mb_accept; // start keccak on the accept cycle
|
|
|
|
always @(*) begin
|
|
mb_state_next = mb_state;
|
|
case (mb_state)
|
|
MB_IDLE: if (mb_accept) mb_state_next = MB_PERMUTE;
|
|
MB_PERMUTE: if (kc_valid_o) mb_state_next = mb_last_r ? MB_DONE : MB_IDLE;
|
|
MB_DONE: if (ready_i) mb_state_next = MB_IDLE;
|
|
default: mb_state_next = MB_IDLE;
|
|
endcase
|
|
end
|
|
|
|
// ================================================================
|
|
// External keccak_core interface (was internal keccak_core)
|
|
// ================================================================
|
|
// Keccak input: multi-block xored state when mb_en, else single-block absorb.
|
|
assign kc_state_i = mb_en ? mb_xored : absorb_state;
|
|
// Always accept keccak output (matches the dedicated-core ready_i=1'b1).
|
|
assign kc_ready_i = 1'b1;
|
|
// kc_valid_i: single-block start (state_next==PERMUTE) OR multi-block accept.
|
|
assign kc_valid_i = mb_en ? mb_kc_valid : (state_next == ST_PERMUTE);
|
|
|
|
// ================================================================
|
|
// FSM combinational logic
|
|
// ================================================================
|
|
assign ready_o = !mb_en && (state_r == ST_IDLE);
|
|
|
|
always @(*) begin
|
|
state_next = state_r;
|
|
case (state_r)
|
|
ST_IDLE: if (valid_i && ready_o) state_next = ST_PERMUTE;
|
|
ST_PERMUTE: if (kc_valid_o) state_next = ST_SQUEEZE;
|
|
ST_SQUEEZE: if (valid_o && ready_i) state_next = ST_IDLE;
|
|
default: state_next = ST_IDLE;
|
|
endcase
|
|
end
|
|
|
|
// ================================================================
|
|
// Output
|
|
// ================================================================
|
|
reg [511:0] squeezed_state_r;
|
|
|
|
// valid_o / hash_o serve both paths, selected by mb_en.
|
|
assign valid_o = mb_en ? (mb_state == MB_DONE) : (state_r == ST_SQUEEZE);
|
|
assign hash_o = mb_en ? {256'b0, mb_digest_r} : squeezed_state_r;
|
|
|
|
// ================================================================
|
|
// Sequential logic
|
|
// ================================================================
|
|
always @(posedge clk or negedge rst_n) begin
|
|
if (!rst_n) begin
|
|
state_r <= ST_IDLE;
|
|
squeezed_state_r <= 512'd0;
|
|
mb_state <= MB_IDLE;
|
|
mb_state_r <= 1600'd0;
|
|
mb_last_r <= 1'b0;
|
|
mb_digest_r <= 256'd0;
|
|
end else begin
|
|
state_r <= state_next;
|
|
mb_state <= mb_state_next;
|
|
|
|
// --- single-block: latch squeezed output ---
|
|
if (state_r == ST_PERMUTE && kc_valid_o) begin
|
|
squeezed_state_r <= kc_state_o[511:0];
|
|
end
|
|
|
|
// --- multi-block: capture last flag on accept ---
|
|
if (mb_accept) begin
|
|
mb_last_r <= mb_last_i;
|
|
end
|
|
|
|
// --- multi-block: latch permuted state when keccak finishes ---
|
|
if (mb_state == MB_PERMUTE && kc_valid_o) begin
|
|
mb_state_r <= kc_state_o;
|
|
if (mb_last_r) mb_digest_r <= kc_state_o[255:0];
|
|
end
|
|
|
|
// --- multi-block: clear running state after digest consumed ---
|
|
if (mb_state == MB_DONE && ready_i) begin
|
|
mb_state_r <= 1600'd0;
|
|
end
|
|
end
|
|
end
|
|
|
|
endmodule
|