blob: 21595b7457774e503207c82ae7de275b9acdb5b0 [file] [log] [blame]
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
//
// SHA3 core is a fully functional SHA3/SHAKE/cSHAKE hashing module.
//
// It instantiates a keccak_round with 1600 bits of the state.
`include "prim_assert.sv"
module sha3
import sha3_pkg::*;
#(
// Enable Masked Keccak if 1
parameter bit EnMasking = 0,
// derived parameter
localparam int Share = (EnMasking) ? 2 : 1
) (
input clk_i,
input rst_ni,
// MSG interface
input msg_valid_i,
input [MsgWidth-1:0] msg_data_i [Share],
input [MsgStrbW-1:0] msg_strb_i, // one strobe for shares
output logic msg_ready_o,
// Entropy interface
input rand_valid_i,
input rand_early_i,
input [StateW/2-1:0] rand_data_i,
input rand_aux_i,
output logic rand_consumed_o,
// N, S: Used in cSHAKE mode only
input [NSRegisterSize*8-1:0] ns_data_i, // See sha3_pkg for details
// configurations
input sha3_mode_e mode_i, // see sha3pad for details
input keccak_strength_e strength_i, // see sha3pad for details
// controls
input start_i, // see sha3pad for details
input process_i, // see sha3pad for details
// run_i is a pulse signal to trigger the keccak_round manually by SW.
// It is used to run additional keccak_f after sponge absorbing is completed.
// See `keccak_run` signal
input run_i,
input prim_mubi_pkg::mubi4_t done_i, // see sha3pad for details
output prim_mubi_pkg::mubi4_t absorbed_o,
output logic squeezing_o,
// Indicate of one block processed. KMAC main state tracks the progression
// based on this signal.
output logic block_processed_o,
output sha3_st_e sha3_fsm_o,
// digest output
// This value is valid only after all absorbing process is completed.
// In invalid state, the output `state` will be zero to prevent information
// leakage.
output logic state_valid_o,
output logic [StateW-1:0] state_o [Share],
// Life cycle
input lc_ctrl_pkg::lc_tx_t lc_escalate_en_i,
// error_o value is pushed to Error FIFO at KMAC/SHA3 top and reported to SW
output err_t error_o,
// sparse_fsm_alert
output logic sparse_fsm_error_o,
// counter error
output logic count_error_o,
// error on rst_storage in Keccak
output logic keccak_storage_rst_error_o
);
/////////////////
// Definitions //
/////////////////
typedef enum logic[2:0] {
MuxGuard = 3'b 010,
MuxRelease = 3'b 101
} state_mux_sel_e;
/////////////
// Signals //
/////////////
// State --> Digest
// State is exposed to the outside if the hashing process is completed.
logic state_valid;
logic [StateW-1:0] state [Share];
logic [StateW-1:0] state_guarded [Share];
// State --> digest mux select signal
state_mux_sel_e mux_sel;
// absorbed is a pulse signal that indicates sponge absorbing is done.
// After this, sha3 core allows software to manually run until squeezing
// is completed, which is the `done_i` pulse signal.
prim_mubi_pkg::mubi4_t absorbed;
// `squeezing` is a status indicator that SHA3 core is in sponge squeezing
// stage. In this stage, the state output is valid, and software can manually
// trigger keccak_round logic to get more digest outputs in case the output
// length is bigger than the block limit.
logic squeezing;
// If process_i is received, the logic initiates the final absorbing process.
// While absorbing, the processing inticator is turned on. This signal is used
// to check if multiple process_i is received or not.
logic processing;
// FSM variable
sha3_st_sparse_e st, st_d;
// Keccak control signal (filtered by State Machine)
logic keccak_start, keccak_process;
prim_mubi_pkg::mubi4_t keccak_done;
// alert signals
logic round_count_error, msg_count_error;
assign count_error_o = round_count_error | msg_count_error;
logic sha3_state_error;
logic keccak_round_state_error;
logic sha3pad_state_error;
assign sparse_fsm_error_o = sha3_state_error | keccak_round_state_error | sha3pad_state_error;
// Keccak rst_storage is asserted unexpectedly
logic keccak_storage_rst_error;
assign keccak_storage_rst_error_o = keccak_storage_rst_error;
/////////////////
// Connections //
/////////////////
logic keccak_valid;
logic [KeccakMsgAddrW-1:0] keccak_addr;
logic [MsgWidth-1:0] keccak_data [Share];
logic keccak_ready;
// Keccak round run signal can be controlled by sha3pad and also by software
// after all message feeding is done. it is mainly used for sponge squeezing
// operation after absorbing is completed when output length is longer than
// the block size.
logic keccak_run, sha3pad_keccak_run, sw_keccak_run;
logic keccak_complete;
assign keccak_run = sha3pad_keccak_run | sw_keccak_run;
// Absorb pulse output : used to generate interrupts
// Latch absorbed signal as kmac_keymgr asserts `CmdDone` when it sees
// `absorbed` signal. When this signal goes out, the state is still in
// `StAbsorb`. Next state is `StSqueeze`.
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) absorbed_o <= prim_mubi_pkg::MuBi4False;
else absorbed_o <= absorbed;
end
// Squeezing output
assign squeezing_o = squeezing;
// processing
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) processing <= 1'b 0;
else if (process_i) processing <= 1'b 1;
else if (prim_mubi_pkg::mubi4_test_true_strict(absorbed)) begin
processing <= 1'b 0;
end
end
assign block_processed_o = keccak_complete;
// State connection
assign state_valid_o = state_valid;
assign state_o = state_guarded;
assign sha3_fsm_o = sparse2logic(st);
///////////////////
// State Machine //
///////////////////
// State Register
`PRIM_FLOP_SPARSE_FSM(u_state_regs, st_d, st, sha3_st_sparse_e, StIdle_sparse)
// Next State and Output Logic
// Mainly the FSM controls the input signal access
// StIdle: only start_i signal is allowed
// StAbsorb: only process_i signal is allowed
// StSqueeze: only run_i, done_i signal is allowed
always_comb begin
st_d = st;
// default output values
keccak_start = 1'b 0;
keccak_process = 1'b 0;
sw_keccak_run = 1'b 0;
keccak_done = prim_mubi_pkg::MuBi4False;
squeezing = 1'b 0;
state_valid = 1'b 0;
mux_sel = MuxGuard ;
sha3_state_error = 1'b 0;
unique case (st)
StIdle_sparse: begin
if (start_i) begin
st_d = StAbsorb_sparse;
keccak_start = 1'b 1;
end else begin
st_d = StIdle_sparse;
end
end
StAbsorb_sparse: begin
if (process_i && !processing) begin
st_d = StAbsorb_sparse;
keccak_process = 1'b 1;
end else if (prim_mubi_pkg::mubi4_test_true_strict(absorbed)) begin
st_d = StSqueeze_sparse;
end else begin
st_d = StAbsorb_sparse;
end
end
StSqueeze_sparse: begin
state_valid = 1'b 1;
mux_sel = MuxRelease; // Expose state to register interface
squeezing = 1'b 1;
if (run_i) begin
st_d = StManualRun_sparse;
sw_keccak_run = 1'b 1;
end else if (prim_mubi_pkg::mubi4_test_true_strict(done_i)) begin
st_d = StFlush_sparse;
keccak_done = done_i;
end else begin
st_d = StSqueeze_sparse;
end
end
StManualRun_sparse: begin
if (keccak_complete) begin
st_d = StSqueeze_sparse;
end else begin
st_d = StManualRun_sparse;
end
end
StFlush_sparse: begin
st_d = StIdle_sparse;
end
StTerminalError_sparse: begin
//this state is terminal
st_d = StTerminalError_sparse;
sha3_state_error = 1'b 1;
end
default: begin
st_d = StTerminalError_sparse;
sha3_state_error = 1'b 1;
end
endcase
// SEC_CM: FSM.GLOBAL_ESC, FSM.LOCAL_ESC
// Unconditionally jump into the terminal error state
// if the life cycle controller triggers an escalation.
if (lc_escalate_en_i != lc_ctrl_pkg::Off) begin
st_d = StTerminalError_sparse;
end
end
//////////////
// Datapath //
//////////////
// State --> Digest output
always_comb begin : state_guarded_mux
unique case (mux_sel)
MuxGuard: state_guarded = '{default: '0};
MuxRelease: state_guarded = state;
default: state_guarded = '{default: '0}; // a valid, safe output
endcase
end
// Error Detecting
// ErrSha3SwControl:
// info[ 0]: start_i set
// info[ 1]: process_i set
// info[ 2]: run_i set
// info[ 3]: done_i set
// - Sw set process_i, run_i, done_i without start_i
always_comb begin
error_o = '{valid: 1'b0, code: ErrNone, info: '0};
unique case (st)
StIdle_sparse: begin
if (process_i || run_i ||
prim_mubi_pkg::mubi4_test_true_loose(done_i)) begin
error_o = '{
valid: 1'b 1,
code: ErrSha3SwControl,
info: 24'({done_i, run_i, process_i, start_i})
};
end
end
StAbsorb_sparse: begin
if (start_i || run_i || prim_mubi_pkg::mubi4_test_true_loose(done_i)
|| (process_i && processing)) begin
error_o = '{
valid: 1'b 1,
code: ErrSha3SwControl,
info: 24'({done_i, run_i, process_i, start_i})
};
end
end
StSqueeze_sparse: begin
if (start_i || process_i) begin
error_o = '{
valid: 1'b 1,
code: ErrSha3SwControl,
info: 24'({done_i, run_i, process_i, start_i})
};
end
end
StManualRun_sparse: begin
if (start_i || process_i || run_i ||
prim_mubi_pkg::mubi4_test_true_loose(done_i)) begin
error_o = '{
valid: 1'b 1,
code: ErrSha3SwControl,
info: 24'({done_i, run_i, process_i, start_i})
};
end
end
StFlush_sparse: begin
if (start_i || process_i || run_i ||
prim_mubi_pkg::mubi4_test_true_loose(done_i)) begin
error_o = '{
valid: 1'b 1,
code: ErrSha3SwControl,
info: 24'({done_i, run_i, process_i, start_i})
};
end
end
default: begin
end
endcase
end
///////////////
// Instances //
///////////////
// SHA3 pad logic
sha3pad #(
.EnMasking (EnMasking)
) u_pad (
.clk_i,
.rst_ni,
// MSG_FIFO (or from KMAC core)
.msg_valid_i,
.msg_data_i, // [Share]
.msg_strb_i,
.msg_ready_o,
// Encoded N, S
.ns_data_i,
// output to keccak_round: message path
.keccak_valid_o (keccak_valid),
.keccak_addr_o (keccak_addr ),
.keccak_data_o (keccak_data ), // [Share]
.keccak_ready_i (keccak_ready),
.keccak_run_o (sha3pad_keccak_run),
.keccak_complete_i (keccak_complete ),
// configurations
.mode_i,
.strength_i,
// LC
.lc_escalate_en_i (lc_escalate_en_i),
// controls
.start_i (keccak_start),
.process_i (keccak_process),
.done_i (keccak_done),
// output
.absorbed_o (absorbed),
.sparse_fsm_error_o (sha3pad_state_error),
.msg_count_error_o (msg_count_error)
);
// Keccak round logic
keccak_round #(
.Width (sha3_pkg::StateW),
.DInWidth (sha3_pkg::MsgWidth),
.EnMasking (EnMasking)
) u_keccak (
.clk_i,
.rst_ni,
.valid_i (keccak_valid),
.addr_i (keccak_addr ),
.data_i (keccak_data ),
.ready_o (keccak_ready),
.rand_valid_i,
.rand_early_i,
.rand_data_i,
.rand_aux_i,
.rand_consumed_o,
.run_i (keccak_run ),
.complete_o (keccak_complete),
.state_o (state),
// LC
.lc_escalate_en_i (lc_escalate_en_i),
.sparse_fsm_error_o (keccak_round_state_error),
.round_count_error_o (round_count_error),
.rst_storage_error_o (keccak_storage_rst_error),
.clear_i (keccak_done)
);
////////////////
// Assertions //
////////////////
// Unknown check for case statement
`ASSERT(MuxSelKnown_A, mux_sel inside {MuxGuard, MuxRelease})
`ASSERT(FsmKnown_A, st inside {StIdle_sparse, StAbsorb_sparse, StSqueeze_sparse,
StManualRun_sparse, StFlush_sparse, StTerminalError_sparse})
// `state` shall be 0 in invalid
if (EnMasking) begin: gen_chk_digest_masked
`ASSERT(StateZeroInvalid_A, !state_valid_o |-> ((|state_o[0]) | (|state_o[1])) == 1'b 0)
end else begin : gen_chk_digest_unmasked
`ASSERT(StateZeroInvalid_A, !state_valid_o |-> (|state_o[0]) == 1'b 0)
end
// `state_valid_o` asserts only in between the completion and done
//`ASSERT(StateValidPeriod_A, state_valid_o |-> )
// skip the msg interface assertions as they are in sha3pad.sv
// Software run signal happens in Squeezing stage
`ASSUME(SwRunInSqueezing_a, run_i |-> error_o.valid || (st == StSqueeze_sparse))
// If control received but not propagated into submodules, it is error condition
`ASSERT(ErrDetection_A, error_o.valid
|-> {start_i, process_i, run_i, done_i}
!= {keccak_start, keccak_process, sw_keccak_run, keccak_done})
endmodule