[otbn] Detect glitches on stack write pointer control signal
This hardening applies to the call stack and the loop info stack, as
`otbn_stack` is used in both of them.
Signed-off-by: Andreas Kurth <adk@lowrisc.org>
diff --git a/hw/ip/otbn/data/otbn.hjson b/hw/ip/otbn/data/otbn.hjson
index a579cc1..c46b531 100644
--- a/hw/ip/otbn/data/otbn.hjson
+++ b/hw/ip/otbn/data/otbn.hjson
@@ -184,6 +184,9 @@
instances of the counter mismatch, an error is emitted.
'''
}
+ { name: "STACK_WR_PTR.CTR.GLITCH_DETECT"
+ desc: "Glitches are detected on the stack counter increment / decrement control signal."
+ }
{ name: "RF_BIGNUM.DATA_REG_SW.GLITCH_DETECT"
desc: '''
This countermeasure checks for spurious write-enable signals on the register
diff --git a/hw/ip/otbn/data/otbn_sec_cm_testplan.hjson b/hw/ip/otbn/data/otbn_sec_cm_testplan.hjson
index 9031fb3..addb9f4 100644
--- a/hw/ip/otbn/data/otbn_sec_cm_testplan.hjson
+++ b/hw/ip/otbn/data/otbn_sec_cm_testplan.hjson
@@ -114,6 +114,12 @@
tests: []
}
{
+ name: sec_cm_stack_wr_ptr_ctr_glitch_detect
+ desc: "Verify the countermeasure(s) STACK_WR_PTR.CTR.GLITCH_DETECT."
+ milestone: V2S
+ tests: []
+ }
+ {
name: sec_cm_rf_bignum_data_reg_sw_glitch_detect
desc: "Verify the countermeasure(s) RF_BIGNUM.DATA_REG_SW.GLITCH_DETECT."
milestone: V2S
diff --git a/hw/ip/otbn/rtl/otbn_stack.sv b/hw/ip/otbn/rtl/otbn_stack.sv
index c513752..894cd0e 100644
--- a/hw/ip/otbn/rtl/otbn_stack.sv
+++ b/hw/ip/otbn/rtl/otbn_stack.sv
@@ -67,10 +67,43 @@
endcase
end
+ // SEC_CM: STACK_WR_PTR.CTR.GLITCH_DETECT
+ // Detect glitches on the `step_i` input of the stack write pointer. If a glitch is detected,
+ // latch it until the stack gets cleared (or the module is reset). Detecting glitches on the
+ // clock edge instead of combinationally is required because the error output drives the control
+ // path, thus feeding the glitch detector output back combinationally would result in
+ // combinational loops.
+ logic stack_wr_ptr_step_err_d, stack_wr_ptr_step_err_q;
+ always_comb begin
+ stack_wr_ptr_step_err_d = stack_wr_ptr_step_err_q;
+ if (clear_i) stack_wr_ptr_step_err_d = 1'b0;
+ if (stack_wr_ptr_step > 1 || stack_wr_ptr_step < -1) stack_wr_ptr_step_err_d = 1'b1;
+ if (stack_wr_ptr_step == 1 && !stack_write) stack_wr_ptr_step_err_d = 1'b1;
+ if (stack_wr_ptr_step == -1 && !stack_read) stack_wr_ptr_step_err_d = 1'b1;
+ if (stack_wr_ptr_step == 0 && (stack_write ^ stack_read)) stack_wr_ptr_step_err_d = 1'b1;
+ end
+
+ logic stack_wr_ptr_step_err_buf;
+ prim_buf #(
+ .Width (1)
+ ) u_stack_wr_ptr_step_err_buf (
+ .in_i (stack_wr_ptr_step_err_d),
+ .out_o (stack_wr_ptr_step_err_buf)
+ );
+
+ always_ff @(posedge clk_i, negedge rst_ni) begin
+ if (!rst_ni) begin
+ stack_wr_ptr_step_err_q <= 1'b0;
+ end else begin
+ stack_wr_ptr_step_err_q <= stack_wr_ptr_step_err_buf;
+ end
+ end
+
logic stack_wr_ptr_en;
assign stack_wr_ptr_en = stack_wr_ptr_step != '0;
// SEC_CM: STACK_WR_PTR.CTR.REDUN
+ logic stack_wr_ptr_err;
prim_count #(
.Width (StackDepthW+1),
.OutSelDnCnt (0),
@@ -84,7 +117,7 @@
.en_i (stack_wr_ptr_en),
.step_i (stack_wr_ptr_step),
.cnt_o (stack_wr_ptr),
- .err_o (cnt_err_o)
+ .err_o (stack_wr_ptr_err)
);
always_ff @(posedge clk_i) begin
@@ -96,4 +129,5 @@
assign full_o = stack_full;
assign top_data_o = stack_storage[stack_rd_idx];
assign top_valid_o = ~stack_empty;
+ assign cnt_err_o = stack_wr_ptr_err | stack_wr_ptr_step_err_q;
endmodule