[aes/rtl] Fix status tracking for initial key and IV registers
Whenever the key is used, this must be signaled. When starting decryption,
the key is declared as not new any more to re-trigger decryption key
generation upon future key updates. When performing encryption, the key
is now declared as armed: It is still new and will trigger decryption key
generation, but any future write to a key register will reset the status
tracking (declares the key as dirty) to prevent partial key updates.
Similarly, when the IV has been fully updated internally, any write by
software must reset the status tracking to prevent partial updates by
software.
This resolves lowRISC/OpenTitan#2913.
Signed-off-by: Pirmin Vogel <vogelpi@lowrisc.org>
diff --git a/hw/ip/aes/rtl/aes_control.sv b/hw/ip/aes/rtl/aes_control.sv
index 9efa4f0..af7f6ef 100644
--- a/hw/ip/aes/rtl/aes_control.sv
+++ b/hw/ip/aes/rtl/aes_control.sv
@@ -112,12 +112,14 @@
// Signals
logic key_init_clear;
logic key_init_new;
- logic dec_key_gen;
+ logic key_init_load;
+ logic key_init_arm;
logic key_init_ready;
logic [7:0] iv_qe;
logic iv_clear;
logic iv_load;
+ logic iv_arm;
logic iv_ready;
logic [3:0] data_in_new_d, data_in_new_q;
@@ -193,7 +195,6 @@
// IV registers
iv_sel_o = IV_INPUT;
iv_we_o = 8'h00;
- iv_load = 1'b0;
// Pseudo-random number generator control
prng_data_req_o = 1'b0;
@@ -214,14 +215,17 @@
stall_we_o = 1'b0;
// Key, data I/O register control
- dec_key_gen = 1'b0;
data_in_load = 1'b0;
data_in_we_o = 1'b0;
data_out_we_o = 1'b0;
- // Edge detector control
+ // Register status tracker control
key_init_clear = 1'b0;
+ key_init_load = 1'b0;
+ key_init_arm = 1'b0;
iv_clear = 1'b0;
+ iv_load = 1'b0;
+ iv_arm = 1'b0;
// FSM
aes_ctrl_ns = aes_ctrl_cs;
@@ -282,10 +286,11 @@
end
LOAD: begin
- // Clear key_init_new, iv_new, data_in_new
- dec_key_gen = cipher_dec_key_gen_i;
- iv_load = ~cipher_dec_key_gen_i;
- data_in_load = ~cipher_dec_key_gen_i;
+ // Signal that we have used the current key, IV, data input to register status tracking.
+ key_init_load = cipher_dec_key_gen_i; // This key is no longer "new", but still clean.
+ key_init_arm = ~cipher_dec_key_gen_i; // The key is still "new", prevent partial updates.
+ iv_load = ~cipher_dec_key_gen_i & (doing_cbc_enc | doing_cbc_dec | doing_ctr);
+ data_in_load = ~cipher_dec_key_gen_i;
// Trigger counter increment.
ctr_incr_o = doing_ctr ? 1'b1 : 1'b0;
@@ -363,6 +368,9 @@
// We are ready once the output data registers can be written.
cipher_out_ready_o = finish;
if (finish & cipher_out_valid_i) begin
+ // Arm the IV status tracker. In the next cycle, the IV registers can be written again
+ // by software. We need to make sure software does not partially update the IV.
+ iv_arm = (doing_cbc_enc || doing_cbc_dec || doing_ctr) ? 1'b1 : 1'b0;
data_out_we_o = 1'b1;
aes_ctrl_ns = IDLE;
end
@@ -432,8 +440,9 @@
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.we_i ( key_init_we_o ),
- .use_i ( dec_key_gen ),
+ .use_i ( key_init_load ),
.clear_i ( key_init_clear ),
+ .arm_i ( key_init_arm ),
.new_o ( key_init_new ),
.clean_o ( key_init_ready )
);
@@ -450,6 +459,7 @@
.we_i ( iv_we_o ),
.use_i ( iv_load ),
.clear_i ( iv_clear ),
+ .arm_i ( iv_arm ),
.new_o ( iv_ready ),
.clean_o ( )
);
diff --git a/hw/ip/aes/rtl/aes_reg_status.sv b/hw/ip/aes/rtl/aes_reg_status.sv
index b274ad6..8429636 100644
--- a/hw/ip/aes/rtl/aes_reg_status.sv
+++ b/hw/ip/aes/rtl/aes_reg_status.sv
@@ -15,24 +15,32 @@
input logic [Width-1:0] we_i,
input logic use_i,
input logic clear_i,
+ input logic arm_i,
output logic new_o,
output logic clean_o
);
logic [Width-1:0] we_d, we_q;
+ logic armed_d, armed_q;
logic all_written;
logic none_written;
logic new_d, new_q;
logic clean_d, clean_q;
- // Collect write operations. Upon clear or use, we start over.
- assign we_d = (clear_i || use_i) ? '0 : (we_q | we_i);
+ // Collect write operations. Upon clear or use, we start over. If armed, the next write will
+ // restart the tracking.
+ assign we_d = (clear_i || use_i) ? '0 :
+ (armed_q && |we_i) ? we_i : (we_q | we_i);
+ assign armed_d = (clear_i || use_i) ? 1'b0 :
+ (armed_q && |we_i) ? 1'b0 : armed_q | arm_i;
always_ff @(posedge clk_i or negedge rst_ni) begin : reg_ops
if (!rst_ni) begin
- we_q <= '0;
+ we_q <= '0;
+ armed_q <= 1'b0;
end else begin
- we_q <= we_d;
+ we_q <= we_d;
+ armed_q <= armed_d;
end
end