Files
mlkem-sync/sync_rtl/sha3/keccak_round.v
FallenSigh 453bc899fc feat(sha3): implement synchronous Keccak-f[1600] core with G/H/J modes
Phase 1.1 of ML-KEM sync rewrite.
- keccak_round.v: combinational theta/rho/pi/chi/iota
- keccak_core.v: 24-round pipeline, valid/ready
- sha3_top.v: sponge FSM, modes G(SHA3-512)/H(SHA3-256)/J(SHAKE-256)
- Verilator C++ TB + Python vector gen against reference
- Verified: 25/25 vectors bit-exact vs Python G()/H()/J()
2026-06-24 20:33:44 +08:00

249 lines
11 KiB
Verilog

// keccak_round.v - Single Keccak-f round (theta/rho/pi/chi/iota)
//
// Pure combinational: no clock, no valid/ready.
// Input: 1600-bit state, 5-bit round index (0-23).
// Output: transformed 1600-bit state.
//
// State layout: s[64*(5*y + x) + z] = A[x][y][z]
// Lane(x,y) = s[64*(5*y + x) +: 64]
module keccak_round (
input [1599:0] state_i,
input [4:0] round_i,
output [1599:0] state_o
);
// ================================================================
// Round constants (24 entries, FIPS 202)
// ================================================================
wire [63:0] RC [0:23];
assign RC[ 0] = 64'h0000000000000001;
assign RC[ 1] = 64'h0000000000008082;
assign RC[ 2] = 64'h800000000000808A;
assign RC[ 3] = 64'h8000000080008000;
assign RC[ 4] = 64'h000000000000808B;
assign RC[ 5] = 64'h0000000080000001;
assign RC[ 6] = 64'h8000000080008081;
assign RC[ 7] = 64'h8000000000008009;
assign RC[ 8] = 64'h000000000000008A;
assign RC[ 9] = 64'h0000000000000088;
assign RC[10] = 64'h0000000080008009;
assign RC[11] = 64'h000000008000000A;
assign RC[12] = 64'h000000008000808B;
assign RC[13] = 64'h800000000000008B;
assign RC[14] = 64'h8000000000008089;
assign RC[15] = 64'h8000000000008003;
assign RC[16] = 64'h8000000000008002;
assign RC[17] = 64'h8000000000000080;
assign RC[18] = 64'h000000000000800A;
assign RC[19] = 64'h800000008000000A;
assign RC[20] = 64'h8000000080008081;
assign RC[21] = 64'h8000000000008080;
assign RC[22] = 64'h0000000080000001;
assign RC[23] = 64'h8000000080008008;
// ================================================================
// Rho rotation offsets (FIPS 202, Table 2)
// Inlined as generate-if chains for tool compatibility
// ================================================================
// ================================================================
// Unpack flat 1600-bit input into 25 lanes (each 64-bit)
// Lane index: 5*y + x
// ================================================================
wire [63:0] A [0:4][0:4];
genvar gx, gy;
generate
for (gx = 0; gx < 5; gx = gx + 1) begin : up_x
for (gy = 0; gy < 5; gy = gy + 1) begin : up_y
assign A[gx][gy] = state_i[((5 * gy + gx) * 64) +: 64];
end
end
endgenerate
// ================================================================
// Theta step
// C[x] = XOR over y of A[x][y]
// D[x] = C[x-1] ^ ROTL(C[x+1], 1)
// A_theta[x][y] = A[x][y] ^ D[x]
// ================================================================
wire [63:0] C [0:4];
wire [63:0] D [0:4];
wire [63:0] A_t [0:4][0:4];
generate
for (gx = 0; gx < 5; gx = gx + 1) begin : th_x
// C[x] = XOR of the 5 lanes in column x
assign C[gx] = A[gx][0] ^ A[gx][1] ^ A[gx][2] ^ A[gx][3] ^ A[gx][4];
// D[x] = C[(x-1) mod 5] ^ ROTL(C[(x+1) mod 5], 1)
localparam [2:0] xp1 = (gx == 4) ? 3'd0 : (gx + 3'd1);
localparam [2:0] xm1 = (gx == 0) ? 3'd4 : (gx - 3'd1);
assign D[gx] = C[xm1] ^ {C[xp1][62:0], C[xp1][63]};
for (gy = 0; gy < 5; gy = gy + 1) begin : th_y
assign A_t[gx][gy] = A[gx][gy] ^ D[gx];
end
end
endgenerate
// ================================================================
// Rho step (rotation of each lane by precomputed offset)
// ================================================================
wire [63:0] A_r [0:4][0:4];
generate
for (gx = 0; gx < 5; gx = gx + 1) begin : rh_x
for (gy = 0; gy < 5; gy = gy + 1) begin : rh_y
if (gx == 0 && gy == 0) begin : rho_00
assign A_r[gx][gy] = A_t[gx][gy];
end else
if (gy == 0 && gx == 1) begin : rho_10
assign A_r[gx][gy] = {A_t[gx][gy][62:0], A_t[gx][gy][63]};
end else
if (gy == 0 && gx == 2) begin : rho_20
assign A_r[gx][gy] = {A_t[gx][gy][1:0], A_t[gx][gy][63:2]};
end else
if (gy == 0 && gx == 3) begin : rho_30
assign A_r[gx][gy] = {A_t[gx][gy][35:0], A_t[gx][gy][63:36]};
end else
if (gy == 0 && gx == 4) begin : rho_40
assign A_r[gx][gy] = {A_t[gx][gy][36:0], A_t[gx][gy][63:37]};
end else
if (gy == 1 && gx == 0) begin : rho_01
assign A_r[gx][gy] = {A_t[gx][gy][27:0], A_t[gx][gy][63:28]};
end else
if (gy == 1 && gx == 1) begin : rho_11
assign A_r[gx][gy] = {A_t[gx][gy][19:0], A_t[gx][gy][63:20]};
end else
if (gy == 1 && gx == 2) begin : rho_21
assign A_r[gx][gy] = {A_t[gx][gy][57:0], A_t[gx][gy][63:58]};
end else
if (gy == 1 && gx == 3) begin : rho_31
assign A_r[gx][gy] = {A_t[gx][gy][8:0], A_t[gx][gy][63:9]};
end else
if (gy == 1 && gx == 4) begin : rho_41
assign A_r[gx][gy] = {A_t[gx][gy][43:0], A_t[gx][gy][63:44]};
end else
if (gy == 2 && gx == 0) begin : rho_02
assign A_r[gx][gy] = {A_t[gx][gy][60:0], A_t[gx][gy][63:61]};
end else
if (gy == 2 && gx == 1) begin : rho_12
assign A_r[gx][gy] = {A_t[gx][gy][53:0], A_t[gx][gy][63:54]};
end else
if (gy == 2 && gx == 2) begin : rho_22
assign A_r[gx][gy] = {A_t[gx][gy][20:0], A_t[gx][gy][63:21]};
end else
if (gy == 2 && gx == 3) begin : rho_32
assign A_r[gx][gy] = {A_t[gx][gy][38:0], A_t[gx][gy][63:39]};
end else
if (gy == 2 && gx == 4) begin : rho_42
assign A_r[gx][gy] = {A_t[gx][gy][24:0], A_t[gx][gy][63:25]};
end else
if (gy == 3 && gx == 0) begin : rho_03
assign A_r[gx][gy] = {A_t[gx][gy][22:0], A_t[gx][gy][63:23]};
end else
if (gy == 3 && gx == 1) begin : rho_13
assign A_r[gx][gy] = {A_t[gx][gy][18:0], A_t[gx][gy][63:19]};
end else
if (gy == 3 && gx == 2) begin : rho_23
assign A_r[gx][gy] = {A_t[gx][gy][48:0], A_t[gx][gy][63:49]};
end else
if (gy == 3 && gx == 3) begin : rho_33
assign A_r[gx][gy] = {A_t[gx][gy][42:0], A_t[gx][gy][63:43]};
end else
if (gy == 3 && gx == 4) begin : rho_43
assign A_r[gx][gy] = {A_t[gx][gy][55:0], A_t[gx][gy][63:56]};
end else
if (gy == 4 && gx == 0) begin : rho_04
assign A_r[gx][gy] = {A_t[gx][gy][45:0], A_t[gx][gy][63:46]};
end else
if (gy == 4 && gx == 1) begin : rho_14
assign A_r[gx][gy] = {A_t[gx][gy][61:0], A_t[gx][gy][63:62]};
end else
if (gy == 4 && gx == 2) begin : rho_24
assign A_r[gx][gy] = {A_t[gx][gy][2:0], A_t[gx][gy][63:3]};
end else
if (gy == 4 && gx == 3) begin : rho_34
assign A_r[gx][gy] = {A_t[gx][gy][7:0], A_t[gx][gy][63:8]};
end else
if (gy == 4 && gx == 4) begin : rho_44
assign A_r[gx][gy] = {A_t[gx][gy][49:0], A_t[gx][gy][63:50]};
end
end
end
endgenerate
// ================================================================
// Pi step (permute lanes)
// A_pi[x][y] = A_rho[(x + 3*y) % 5][x]
// ================================================================
wire [63:0] A_p [0:4][0:4];
generate
for (gx = 0; gx < 5; gx = gx + 1) begin : pi_x
for (gy = 0; gy < 5; gy = gy + 1) begin : pi_y
localparam [2:0] src_x = (gx + 3 * gy) % 5;
assign A_p[gx][gy] = A_r[src_x][gx];
end
end
endgenerate
// ================================================================
// Chi step
// A_chi[x][y] = A_pi[x][y] ^ (~A_pi[x+1][y] & A_pi[x+2][y])
// ================================================================
wire [63:0] A_c [0:4][0:4];
generate
for (gx = 0; gx < 5; gx = gx + 1) begin : ch_x
localparam [2:0] xp1 = (gx == 4) ? 3'd0 : (gx + 3'd1);
localparam [2:0] xp2 = (gx == 3) ? 3'd0 :
(gx == 4) ? 3'd1 : (gx + 3'd2);
for (gy = 0; gy < 5; gy = gy + 1) begin : ch_y
assign A_c[gx][gy] = A_p[gx][gy] ^
((~A_p[xp1][gy]) & A_p[xp2][gy]);
end
end
endgenerate
// ================================================================
// Iota step
// A_iota[0][0] = A_chi[0][0] ^ RC[round_i]
// Other lanes unchanged
// ================================================================
wire [63:0] A_io [0:4][0:4];
assign A_io[0][0] = A_c[0][0] ^ RC[round_i];
assign A_io[0][1] = A_c[0][1];
assign A_io[0][2] = A_c[0][2];
assign A_io[0][3] = A_c[0][3];
assign A_io[0][4] = A_c[0][4];
assign A_io[1][0] = A_c[1][0];
assign A_io[1][1] = A_c[1][1];
assign A_io[1][2] = A_c[1][2];
assign A_io[1][3] = A_c[1][3];
assign A_io[1][4] = A_c[1][4];
assign A_io[2][0] = A_c[2][0];
assign A_io[2][1] = A_c[2][1];
assign A_io[2][2] = A_c[2][2];
assign A_io[2][3] = A_c[2][3];
assign A_io[2][4] = A_c[2][4];
assign A_io[3][0] = A_c[3][0];
assign A_io[3][1] = A_c[3][1];
assign A_io[3][2] = A_c[3][2];
assign A_io[3][3] = A_c[3][3];
assign A_io[3][4] = A_c[3][4];
assign A_io[4][0] = A_c[4][0];
assign A_io[4][1] = A_c[4][1];
assign A_io[4][2] = A_c[4][2];
assign A_io[4][3] = A_c[4][3];
assign A_io[4][4] = A_c[4][4];
// ================================================================
// Pack 25 lanes back into 1600-bit flat output
// ================================================================
assign state_o = {A_io[4][4], A_io[3][4], A_io[2][4], A_io[1][4], A_io[0][4],
A_io[4][3], A_io[3][3], A_io[2][3], A_io[1][3], A_io[0][3],
A_io[4][2], A_io[3][2], A_io[2][2], A_io[1][2], A_io[0][2],
A_io[4][1], A_io[3][1], A_io[2][1], A_io[1][1], A_io[0][1],
A_io[4][0], A_io[3][0], A_io[2][0], A_io[1][0], A_io[0][0]};
endmodule