[aes] Clear internal registers with pseudo-random data
This commit adds an LFSR-based pseudo-random number generator to the AES
module that is used to clear various registers (input data, output data,
IV, key, internal state). The LFSR can be reseeded at runtime using a
bit in the TRIGGER register.
The reset for all these registers is removed. After a reset, the AES unit
first reseeds the LFSR and then clears all these registers with
pseudo-random data.
Signed-off-by: Pirmin Vogel <vogelpi@lowrisc.org>
diff --git a/hw/ip/aes/aes.core b/hw/ip/aes/aes.core
index 723d25b..da2a4f1 100644
--- a/hw/ip/aes/aes.core
+++ b/hw/ip/aes/aes.core
@@ -14,6 +14,7 @@
- rtl/aes_reg_pkg.sv
- rtl/aes_reg_top.sv
- rtl/aes_core.sv
+ - rtl/aes_prng.sv
- rtl/aes_ctr.sv
- rtl/aes_control.sv
- rtl/aes_cipher_core.sv
diff --git a/hw/ip/aes/data/aes.hjson b/hw/ip/aes/data/aes.hjson
index 588d09d..c507ae6 100644
--- a/hw/ip/aes/data/aes.hjson
+++ b/hw/ip/aes/data/aes.hjson
@@ -121,8 +121,10 @@
fields: [
{ bits: "31:0", name: "data_out", desc: "Output Data" }
]
- tags: [// Updated by the hw. Exclude from write-checks.
- "excl:CsrNonInitTests:CsrExclWriteCheck"]
+ tags: [// Updated by the HW.
+ // No reset but sync clear with random data.
+ // Exclude from init and write checks.
+ "excl:CsrAllTests:CsrExclCheck"]
}
},
##############################################################################
@@ -181,6 +183,7 @@
desc: '''
Trigger Register.
Each bit is individually cleared to zero when executing the corresponding trigger.
+
'''
swaccess: "wo",
hwaccess: "hrw",
@@ -194,32 +197,45 @@
}
{ bits: "1",
name: "KEY_CLEAR",
+ resval: "1"
desc: '''
Keep current values in Initial Key, internal Full Key and Decryption Key registers (0)
- or clear those registers to zero (1).
+ or clear those registers with pseudo-random data (1).
'''
}
{ bits: "2",
name: "IV_CLEAR",
+ resval: "1"
desc: '''
- Keep current values in IV registers (0) or clear those registers to zero (1).
+ Keep current values in IV registers (0) or clear those registers with pseudo-random data (1).
'''
}
{ bits: "3",
name: "DATA_IN_CLEAR",
+ resval: "1"
desc: '''
- Keep current values in input registers (0) or clear those registers to zero (1).
+ Keep current values in input registers (0) or clear those registers with pseudo-random data (1).
'''
}
{ bits: "4",
name: "DATA_OUT_CLEAR",
+ resval: "1"
desc: '''
- Keep current values in output registers (0) or clear those registers to zero (1).
+ Keep current values in output registers (0) or clear those registers with pseudo-random data (1).
+ '''
+ }
+ { bits: "5",
+ name: "PRNG_RESEED",
+ resval: "1"
+ desc: '''
+ Keep continuing with the current internal state of the internal pseudo-random number generator (0) or perform a reseed of the internal state from the connected entropy source (1).
'''
}
]
- tags: [// Updated by hw. Exclude from write-checks.
- "excl:CsrNonInitTests:CsrExclWriteCheck"]
+ tags: [// Updated by the HW.
+ // Reset value cleared to zero.
+ // Exclude from init and write checks.
+ "excl:CsrAllTests:CsrExclCheck"]
},
{ name: "STATUS",
desc: "Status Register",
diff --git a/hw/ip/aes/doc/_index.md b/hw/ip/aes/doc/_index.md
index 8f46597..d3f1d91 100644
--- a/hw/ip/aes/doc/_index.md
+++ b/hw/ip/aes/doc/_index.md
@@ -183,7 +183,7 @@
Since the counter value is used in the first round only, and since the encryption/decryption of a single block takes 12/14/16 cycles, the iterative counter implementation does not affect the throughput of the AES unit._
1. Finally, the AES cipher core performs the final encryption/decryption round in which the MixColumns operation is skipped.
The output is forwarded to the output register in the CSRs but not stored back into the State register.
- The internal State register is cleared to zero.
+ The internal State register is cleared with pseudo-random data.
_Depending on the cipher mode, the output of the final round is potentially XORed with either the value in the IV registers (CBC decryption) or the value stored in the previous input data register (CTR mode), before being forwarded to the output register in the CSRs.
If running in CBC mode, the IV registers are updated with the output data (encryption) or the value stored in the previous input data register (decryption)._
@@ -411,7 +411,7 @@
After finishing operation, software must:
1. Disable the AES unit to no longer automatically start encryption/decryption by setting the MANUAL_OPERATION bit in {{< regref "CTRL" >}} to `1`.
-1. Clear all key registers, IV registers as well as the Input Data and the Output Data registers by setting the KEY_CLEAR, IV_CLEAR, DATA_IN_CLEAR and DATA_OUT_CLEAR bits in {{< regref "TRIGGER" >}} to `1`.
+1. Clear all key registers with pseudo-random data, IV registers as well as the Input Data and the Output Data registers by setting the KEY_CLEAR, IV_CLEAR, DATA_IN_CLEAR and DATA_OUT_CLEAR bits in {{< regref "TRIGGER" >}} to `1`.
The code snippet below shows how to perform this task.
diff --git a/hw/ip/aes/rtl/aes.sv b/hw/ip/aes/rtl/aes.sv
index 605bc6c..4040067 100644
--- a/hw/ip/aes/rtl/aes.sv
+++ b/hw/ip/aes/rtl/aes.sv
@@ -13,7 +13,14 @@
input clk_i,
input rst_ni,
- // Bus Interface
+ // Entropy source interface
+ // TODO: This still needs to be connected.
+ // See https://github.com/lowRISC/opentitan/issues/1005
+ //output logic entropy_req_o,
+ //input logic entropy_ack_i,
+ //input logic [63:0] entropy_i,
+
+ // Bus interface
input tlul_pkg::tl_h2d_t tl_i,
output tlul_pkg::tl_d2h_t tl_o
);
@@ -23,7 +30,13 @@
aes_reg2hw_t reg2hw;
aes_hw2reg_t hw2reg;
- aes_reg_top u_reg (
+ logic prng_data_req;
+ logic prng_data_ack;
+ logic [63:0] prng_data;
+ logic prng_reseed_req;
+ logic prng_reseed_ack;
+
+ aes_reg_top aes_reg_top (
.clk_i,
.rst_ni,
.tl_i,
@@ -39,10 +52,34 @@
) aes_core (
.clk_i,
.rst_ni,
+
+ .prng_data_req_o ( prng_data_req ),
+ .prng_data_ack_i ( prng_data_ack ),
+ .prng_data_i ( prng_data ),
+ .prng_reseed_req_o ( prng_reseed_req ),
+ .prng_reseed_ack_i ( prng_reseed_ack ),
+
.reg2hw,
.hw2reg
);
+ aes_prng aes_prng (
+ .clk_i,
+ .rst_ni,
+
+ .data_req_i ( prng_data_req ),
+ .data_ack_o ( prng_data_ack ),
+ .data_o ( prng_data ),
+ .reseed_req_i ( prng_reseed_req ),
+ .reseed_ack_o ( prng_reseed_ack ),
+
+ // TODO: This still needs to be connected to the entropy source.
+ // See https://github.com/lowRISC/opentitan/issues/1005
+ .entropy_req_o( ),
+ .entropy_ack_i( 1'b1 ),
+ .entropy_i ( 64'hFEDCBA9876543210 )
+ );
+
// All outputs should have a known value after reset
`ASSERT_KNOWN(TlODValidKnown, tl_o.d_valid)
`ASSERT_KNOWN(TlOAReadyKnown, tl_o.a_ready)
diff --git a/hw/ip/aes/rtl/aes_cipher_control.sv b/hw/ip/aes/rtl/aes_cipher_control.sv
index 9be1d92..d5287f9 100644
--- a/hw/ip/aes/rtl/aes_cipher_control.sv
+++ b/hw/ip/aes/rtl/aes_cipher_control.sv
@@ -23,7 +23,8 @@
// Control and sync signals
input aes_pkg::ciph_op_e op_i,
input aes_pkg::key_len_e key_len_i,
- input logic start_i,
+ input logic crypt_i,
+ output logic crypt_o,
input logic dec_key_gen_i,
output logic dec_key_gen_o,
input logic key_clear_i,
@@ -53,7 +54,7 @@
// Types
typedef enum logic [2:0] {
- IDLE, INIT, ROUND, FINISH, CLEAR
+ IDLE, INIT, ROUND, FINISH, CLEAR_S, CLEAR_KD
} aes_cipher_ctrl_e;
aes_cipher_ctrl_e aes_cipher_ctrl_ns, aes_cipher_ctrl_cs;
@@ -62,6 +63,7 @@
logic [3:0] round_d, round_q;
logic [3:0] num_rounds_d, num_rounds_q;
logic [3:0] num_rounds_regular;
+ logic crypt_d, crypt_q;
logic dec_key_gen_d, dec_key_gen_q;
logic key_clear_d, key_clear_q;
logic data_out_clear_d, data_out_clear_q;
@@ -92,6 +94,7 @@
aes_cipher_ctrl_ns = aes_cipher_ctrl_cs;
round_d = round_q;
num_rounds_d = num_rounds_q;
+ crypt_d = crypt_q;
dec_key_gen_d = dec_key_gen_q;
key_clear_d = key_clear_q;
data_out_clear_d = data_out_clear_q;
@@ -104,9 +107,19 @@
// Signal that we are ready, wait for handshake.
in_ready_o = 1'b1;
if (in_valid_i) begin
- if (start_i) begin
- // Start generation of start key for decryption.
- dec_key_gen_d = dec_key_gen_i;
+ if (key_clear_i || data_out_clear_i) begin
+ // Clear internal key registers. The cipher core muxes are used to clear the data
+ // output registers.
+ key_clear_d = key_clear_i;
+ data_out_clear_d = data_out_clear_i;
+
+ // To clear the data output registers, we must first clear the state.
+ aes_cipher_ctrl_ns = data_out_clear_i ? CLEAR_S : CLEAR_KD;
+
+ end else if (dec_key_gen_i || crypt_i) begin
+ // Start encryption/decryption or generation of start key for decryption.
+ crypt_d = ~dec_key_gen_i;
+ dec_key_gen_d = dec_key_gen_i;
// Load input data to state
state_sel_o = dec_key_gen_d ? STATE_CLEAR : STATE_INIT;
@@ -127,11 +140,6 @@
(key_len_i == AES_192) ? 4'd12 :
4'd14;
aes_cipher_ctrl_ns = INIT;
- end else if (key_clear_i || data_out_clear_i) begin
- key_clear_d = key_clear_i;
- data_out_clear_d = data_out_clear_i;
-
- aes_cipher_ctrl_ns = CLEAR;
end
end
end
@@ -221,6 +229,7 @@
// We don't need the state anymore, clear it.
state_we_o = 1'b1;
state_sel_o = STATE_CLEAR;
+ crypt_d = 1'b0;
// If we were generating the decryption key and didn't get the handshake in the last
// regular round, we should clear dec_key_gen now.
dec_key_gen_d = 1'b0;
@@ -228,7 +237,15 @@
end
end
- CLEAR: begin
+ CLEAR_S: begin
+ // Clear the state with pseudo-random data.
+ state_we_o = 1'b1;
+ state_sel_o = STATE_CLEAR;
+ aes_cipher_ctrl_ns = CLEAR_KD;
+ end
+
+ CLEAR_KD: begin
+ // Clear internal key registers and/or external data output registers.
if (key_clear_q) begin
key_full_sel_o = KEY_FULL_CLEAR;
key_full_we_o = 1'b1;
@@ -236,6 +253,7 @@
key_dec_we_o = 1'b1;
end
if (data_out_clear_q) begin
+ // Forward the state (previously cleared with psuedo-random data).
add_rk_sel_o = ADD_RK_INIT;
key_words_sel_o = KEY_WORDS_ZERO;
round_key_sel_o = ROUND_KEY_DIRECT;
@@ -258,6 +276,7 @@
aes_cipher_ctrl_cs <= IDLE;
round_q <= '0;
num_rounds_q <= '0;
+ crypt_q <= 1'b0;
dec_key_gen_q <= 1'b0;
key_clear_q <= 1'b0;
data_out_clear_q <= 1'b0;
@@ -265,6 +284,7 @@
aes_cipher_ctrl_cs <= aes_cipher_ctrl_ns;
round_q <= round_d;
num_rounds_q <= num_rounds_d;
+ crypt_q <= crypt_d;
dec_key_gen_q <= dec_key_gen_d;
key_clear_q <= key_clear_d;
data_out_clear_q <= data_out_clear_d;
@@ -279,6 +299,7 @@
assign key_expand_round_o = round_d;
// Let the main controller know whate we are doing.
+ assign crypt_o = crypt_q;
assign dec_key_gen_o = dec_key_gen_q;
assign key_clear_o = key_clear_q;
assign data_out_clear_o = data_out_clear_q;
@@ -299,7 +320,8 @@
INIT,
ROUND,
FINISH,
- CLEAR
+ CLEAR_S,
+ CLEAR_KD
})
endmodule
diff --git a/hw/ip/aes/rtl/aes_cipher_core.sv b/hw/ip/aes/rtl/aes_cipher_core.sv
index 913e044..ed35dc5 100644
--- a/hw/ip/aes/rtl/aes_cipher_core.sv
+++ b/hw/ip/aes/rtl/aes_cipher_core.sv
@@ -27,7 +27,8 @@
// Control and sync signals
input aes_pkg::ciph_op_e op_i,
input aes_pkg::key_len_e key_len_i,
- input logic start_i,
+ input logic crypt_i,
+ output logic crypt_o,
input logic dec_key_gen_i,
output logic dec_key_gen_o,
input logic key_clear_i,
@@ -35,6 +36,9 @@
input logic data_out_clear_i, // Re-use the cipher core muxes.
output logic data_out_clear_o,
+ // Pseudo-random data
+ input logic [63:0] prng_data_i,
+
// I/O data & initial key
input logic [3:0][3:0][7:0] state_init_i,
input logic [7:0][31:0] key_init_i,
@@ -85,15 +89,13 @@
unique case (state_sel)
STATE_INIT: state_d = state_init_i;
STATE_ROUND: state_d = add_round_key_out;
- STATE_CLEAR: state_d = '0;
- default: state_d = '0;
+ STATE_CLEAR: state_d = {prng_data_i, prng_data_i};
+ default: state_d = {prng_data_i, prng_data_i};
endcase
end
- always_ff @(posedge clk_i or negedge rst_ni) begin : state_reg
- if (!rst_ni) begin
- state_q <= '0;
- end else if (state_we) begin
+ always_ff @(posedge clk_i) begin : state_reg
+ if (state_we) begin
state_q <= state_d;
end
end
@@ -140,15 +142,13 @@
KEY_FULL_ENC_INIT: key_full_d = key_init_i;
KEY_FULL_DEC_INIT: key_full_d = key_dec_q;
KEY_FULL_ROUND: key_full_d = key_expand_out;
- KEY_FULL_CLEAR: key_full_d = '0;
- default: key_full_d = '0;
+ KEY_FULL_CLEAR: key_full_d = {prng_data_i, prng_data_i, prng_data_i, prng_data_i};
+ default: key_full_d = {prng_data_i, prng_data_i, prng_data_i, prng_data_i};
endcase
end
- always_ff @(posedge clk_i or negedge rst_ni) begin : key_full_reg
- if (!rst_ni) begin
- key_full_q <= '0;
- end else if (key_full_we) begin
+ always_ff @(posedge clk_i) begin : key_full_reg
+ if (key_full_we) begin
key_full_q <= key_full_d;
end
end
@@ -157,15 +157,13 @@
always_comb begin : key_dec_mux
unique case (key_dec_sel)
KEY_DEC_EXPAND: key_dec_d = key_expand_out;
- KEY_DEC_CLEAR: key_dec_d = '0;
- default: key_dec_d = '0;
+ KEY_DEC_CLEAR: key_dec_d = {prng_data_i, prng_data_i, prng_data_i, prng_data_i};
+ default: key_dec_d = {prng_data_i, prng_data_i, prng_data_i, prng_data_i};
endcase
end
- always_ff @(posedge clk_i or negedge rst_ni) begin : key_dec_reg
- if (!rst_ni) begin
- key_dec_q <= '0;
- end else if (key_dec_we) begin
+ always_ff @(posedge clk_i) begin : key_dec_reg
+ if (key_dec_we) begin
key_dec_q <= key_dec_d;
end
end
@@ -228,7 +226,8 @@
.out_ready_i ( out_ready_i ),
.op_i ( op_i ),
.key_len_i ( key_len_i ),
- .start_i ( start_i ),
+ .crypt_i ( crypt_i ),
+ .crypt_o ( crypt_o ),
.dec_key_gen_i ( dec_key_gen_i ),
.dec_key_gen_o ( dec_key_gen_o ),
.key_clear_i ( key_clear_i ),
diff --git a/hw/ip/aes/rtl/aes_control.sv b/hw/ip/aes/rtl/aes_control.sv
index 67adb02..177c7d9 100644
--- a/hw/ip/aes/rtl/aes_control.sv
+++ b/hw/ip/aes/rtl/aes_control.sv
@@ -22,6 +22,7 @@
input logic iv_clear_i,
input logic data_in_clear_i,
input logic data_out_clear_i,
+ input logic prng_reseed_i,
// I/O register read/write enables
input logic [7:0] key_init_qe_i,
@@ -50,7 +51,8 @@
input logic cipher_in_ready_i,
input logic cipher_out_valid_i,
output logic cipher_out_ready_o,
- output logic cipher_start_o,
+ output logic cipher_crypt_o,
+ input logic cipher_crypt_i,
output logic cipher_dec_key_gen_o,
input logic cipher_dec_key_gen_i,
output logic cipher_key_clear_o,
@@ -66,6 +68,12 @@
output aes_pkg::iv_sel_e iv_sel_o,
output logic [7:0] iv_we_o,
+ // Pseudo-random number generator interface
+ output logic prng_data_req_o,
+ input logic prng_data_ack_i,
+ output logic prng_reseed_req_o,
+ input logic prng_reseed_ack_i,
+
// Trigger register
output logic start_o,
output logic start_we_o,
@@ -77,6 +85,8 @@
output logic data_in_clear_we_o,
output logic data_out_clear_o,
output logic data_out_clear_we_o,
+ output logic prng_reseed_o,
+ output logic prng_reseed_we_o,
// Status register
output logic output_valid_o,
@@ -92,8 +102,8 @@
import aes_pkg::*;
// Types
- typedef enum logic [1:0] {
- IDLE, LOAD, FINISH, CLEAR
+ typedef enum logic [2:0] {
+ IDLE, LOAD, UPDATE_PRNG, FINISH, CLEAR
} aes_ctrl_e;
aes_ctrl_e aes_ctrl_ns, aes_ctrl_cs;
@@ -121,6 +131,8 @@
logic output_valid_q;
logic start, finish;
+ logic doing_cbc_enc, doing_cbc_dec;
+ logic doing_ctr;
// Software updates IV in chunks of 32 bits, the counter updates 16 bits at a time.
// Convert word write enable to internal half-word write enable.
@@ -140,6 +152,11 @@
// clock cycle.
assign finish = manual_operation_i ? 1'b1 : ~output_valid_q | data_out_read;
+ // Helper signals for FSM
+ assign doing_cbc_enc = (cipher_crypt_o | cipher_crypt_i) & (mode_i == AES_CBC) & (op_i == AES_ENC);
+ assign doing_cbc_dec = (cipher_crypt_o | cipher_crypt_i) & (mode_i == AES_CBC) & (op_i == AES_DEC);
+ assign doing_ctr = (cipher_crypt_o | cipher_crypt_i) & (mode_i == AES_CTR);
+
// FSM
always_comb begin : aes_ctrl_fsm
@@ -158,7 +175,7 @@
// Cipher core control
cipher_in_valid_o = 1'b0;
cipher_out_ready_o = 1'b0;
- cipher_start_o = 1'b0;
+ cipher_crypt_o = 1'b0;
cipher_dec_key_gen_o = 1'b0;
cipher_key_clear_o = 1'b0;
cipher_data_out_clear_o = 1'b0;
@@ -172,12 +189,17 @@
iv_we_o = 8'h00;
iv_load = 1'b0;
+ // Pseudo-random number generator control
+ prng_data_req_o = 1'b0;
+ prng_reseed_req_o = 1'b0;
+
// Trigger register control
start_we_o = 1'b0;
key_clear_we_o = 1'b0;
iv_clear_we_o = 1'b0;
data_in_clear_we_o = 1'b0;
data_out_clear_we_o = 1'b0;
+ prng_reseed_we_o = 1'b0;
// Status register
idle_o = 1'b0;
@@ -202,33 +224,45 @@
IDLE: begin
idle_o = (start || key_clear_i || iv_clear_i ||
- data_in_clear_i || data_out_clear_i) ? 1'b0 : 1'b1;
+ data_in_clear_i || data_out_clear_i || prng_reseed_i) ? 1'b0 : 1'b1;
idle_we_o = 1'b1;
// Initial key and IV updates are ignored if we are not idle.
key_init_we_o = idle_o ? key_init_qe_i : 8'h00;
iv_we_o = idle_o ? iv_qe : 8'h00;
- if (start) begin
+ if (prng_reseed_i) begin
+ // Request a reseed of the PRNG, perform handshake.
+ prng_reseed_req_o = 1'b1;
+ if (prng_reseed_ack_i) begin
+ // Clear the trigger.
+ prng_reseed_we_o = 1'b1;
+ end
+
+ end else if (key_clear_i || data_out_clear_i || iv_clear_i || data_in_clear_i) begin
+ // To clear registers, we must first request fresh pseudo-random data.
+ aes_ctrl_ns = UPDATE_PRNG;
+
+ end else if (start) begin
// Signal that we want to start encryption/decryption.
- cipher_start_o = 1'b1;
+ cipher_crypt_o = 1'b1;
// We got a new initial key, but want to do decryption. The cipher core must first
// generate the start key for decryption.
cipher_dec_key_gen_o = key_init_new & (cipher_op_i == CIPH_INV);
// Previous input data register control
- data_in_prev_sel_o = (mode_i == AES_CBC && op_i == AES_DEC) ? DIP_DATA_IN :
- (mode_i == AES_CTR) ? DIP_DATA_IN : DIP_CLEAR;
- data_in_prev_we_o = (mode_i == AES_CBC && op_i == AES_DEC) ? 1'b1 :
- (mode_i == AES_CTR) ? 1'b1 : 1'b0;
+ data_in_prev_sel_o = doing_cbc_dec ? DIP_DATA_IN :
+ doing_ctr ? DIP_DATA_IN : DIP_CLEAR;
+ data_in_prev_we_o = doing_cbc_dec ? 1'b1 :
+ doing_ctr ? 1'b1 : 1'b0;
// State input mux control
- state_in_sel_o = (mode_i == AES_CTR) ? SI_ZERO : SI_DATA;
+ state_in_sel_o = doing_ctr ? SI_ZERO : SI_DATA;
// State input additon mux control
- add_state_in_sel_o = (mode_i == AES_CBC && op_i == AES_ENC) ? ADD_SI_IV :
- (mode_i == AES_CTR) ? ADD_SI_IV : ADD_SI_ZERO;
+ add_state_in_sel_o = doing_cbc_enc ? ADD_SI_IV :
+ doing_ctr ? ADD_SI_IV : ADD_SI_ZERO;
// We have work for the cipher core, perform handshake.
cipher_in_valid_o = 1'b1;
@@ -238,21 +272,6 @@
start_we_o = ~cipher_dec_key_gen_o;
aes_ctrl_ns = LOAD;
end
- end else if (key_clear_i || data_out_clear_i) begin
- // To clear the output data registers, we re-use the muxing resources of the cipher core.
- // To clear all key material, some key registers inside the cipher core need to be
- // cleared.
- cipher_key_clear_o = key_clear_i;
- cipher_data_out_clear_o = data_out_clear_i;
-
- // We have work for the cipher core, perform handshake.
- cipher_in_valid_o = 1'b1;
- if (cipher_in_ready_i) begin
- aes_ctrl_ns = CLEAR;
- end
- end else if (iv_clear_i || data_in_clear_i) begin
- // To clear the IV or input data registers, no handshake with the cipher core is needed.
- aes_ctrl_ns = CLEAR;
end
end
@@ -263,9 +282,49 @@
data_in_load = ~cipher_dec_key_gen_i;
// Trigger counter increment.
- ctr_incr_o = (mode_i == AES_CTR) ? 1'b1 : 1'b0;
+ ctr_incr_o = doing_ctr ? 1'b1 : 1'b0;
- aes_ctrl_ns = FINISH;
+ // Unless we are just generating the start key for decryption, we must update the PRNG.
+ aes_ctrl_ns = ~cipher_dec_key_gen_i ? UPDATE_PRNG : FINISH;
+ end
+
+ UPDATE_PRNG: begin
+ // Fresh pseudo-random data is used to:
+ // - clear the state in the final cipher round,
+ // - clear any other registers in the CLEAR state.
+
+ // IV control in case of ongoing encryption/decryption
+ // - CTR: IV registers are updated by counter during cipher operation
+ iv_sel_o = doing_ctr ? IV_CTR : IV_INPUT;
+ iv_we_o = doing_ctr ? ctr_we_i : 8'h00;
+
+ // Request fresh pseudo-random data, perform handshake.
+ prng_data_req_o = 1'b1;
+ if (prng_data_ack_i) begin
+
+ // Ongoing encryption/decryption operations have the highest priority. The clear triggers
+ // might have become asserted after the handshake with the cipher core.
+ if (cipher_crypt_i) begin
+ aes_ctrl_ns = FINISH;
+
+ end else if (key_clear_i || data_out_clear_i) begin
+ // To clear the output data registers, we re-use the muxing resources of the cipher
+ // core. To clear all key material, some key registers inside the cipher core need to
+ // be cleared.
+ cipher_key_clear_o = key_clear_i;
+ cipher_data_out_clear_o = data_out_clear_i;
+
+ // We have work for the cipher core, perform handshake.
+ cipher_in_valid_o = 1'b1;
+ if (cipher_in_ready_i) begin
+ aes_ctrl_ns = CLEAR;
+ end
+ end else begin // (iv_clear_i || data_in_clear_i)
+ // To clear the IV or input data registers, no handshake with the cipher core is
+ // needed.
+ aes_ctrl_ns = CLEAR;
+ end
+ end
end
FINISH: begin
@@ -283,17 +342,17 @@
stall_we_o = 1'b1;
// State out addition mux control
- add_state_out_sel_o = (mode_i == AES_CBC && op_i == AES_DEC) ? ADD_SO_IV :
- (mode_i == AES_CTR) ? ADD_SO_DIP : ADD_SO_ZERO;
+ add_state_out_sel_o = doing_cbc_dec ? ADD_SO_IV :
+ doing_ctr ? ADD_SO_DIP : ADD_SO_ZERO;
// IV control
// - CBC: IV registers can only be updated when cipher finishes
// - CTR: IV registers are updated by counter during cipher operation
- iv_sel_o = (mode_i == AES_CBC && op_i == AES_ENC) ? IV_DATA_OUT :
- (mode_i == AES_CBC && op_i == AES_DEC) ? IV_DATA_IN_PREV :
- (mode_i == AES_CTR) ? IV_CTR : IV_INPUT;
- iv_we_o = (mode_i == AES_CBC) ? {8{finish & cipher_out_valid_i}} :
- (mode_i == AES_CTR) ? ctr_we_i : 8'h00;
+ iv_sel_o = doing_cbc_enc ? IV_DATA_OUT :
+ doing_cbc_dec ? IV_DATA_IN_PREV :
+ doing_ctr ? IV_CTR : IV_INPUT;
+ iv_we_o = (doing_cbc_enc || doing_cbc_dec) ? {8{finish & cipher_out_valid_i}} :
+ doing_ctr ? ctr_we_i : 8'h00;
// We are ready once the output data registers can be written.
cipher_out_ready_o = finish;
@@ -425,6 +484,7 @@
assign iv_clear_o = 1'b0;
assign data_in_clear_o = 1'b0;
assign data_out_clear_o = 1'b0;
+ assign prng_reseed_o = 1'b0;
// Selectors must be known/valid
`ASSERT(AesModeValid, mode_i inside {
diff --git a/hw/ip/aes/rtl/aes_core.sv b/hw/ip/aes/rtl/aes_core.sv
index 4022f85..148e2cc 100644
--- a/hw/ip/aes/rtl/aes_core.sv
+++ b/hw/ip/aes/rtl/aes_core.sv
@@ -10,8 +10,16 @@
parameter bit AES192Enable = 1,
parameter SBoxImpl = "lut"
) (
- input logic clk_i,
- input logic rst_ni,
+ input logic clk_i,
+ input logic rst_ni,
+
+ // PRNG Interface
+ output logic prng_data_req_o,
+ input logic prng_data_ack_i,
+ input logic [63:0] prng_data_i,
+
+ output logic prng_reseed_req_o,
+ input logic prng_reseed_ack_i,
// Bus Interface
input aes_reg_pkg::aes_reg2hw_t reg2hw,
@@ -80,7 +88,8 @@
logic cipher_in_ready;
logic cipher_out_valid;
logic cipher_out_ready;
- logic cipher_start;
+ logic cipher_crypt;
+ logic cipher_crypt_busy;
logic cipher_dec_key_gen;
logic cipher_dec_key_gen_busy;
logic cipher_key_clear;
@@ -157,19 +166,15 @@
always_comb begin : key_init_mux
unique case (key_init_sel)
KEY_INIT_INPUT: key_init_d = key_init;
- KEY_INIT_CLEAR: key_init_d = '0;
- default: key_init_d = '0;
+ KEY_INIT_CLEAR: key_init_d = {prng_data_i, prng_data_i, prng_data_i, prng_data_i};
+ default: key_init_d = {prng_data_i, prng_data_i, prng_data_i, prng_data_i};
endcase
end
- always_ff @(posedge clk_i or negedge rst_ni) begin : key_init_reg
- if (!rst_ni) begin
- key_init_q <= '0;
- end else begin
- for (int i=0; i<8; i++) begin
- if (key_init_we[i]) begin
- key_init_q[i] <= key_init_d[i];
- end
+ always_ff @(posedge clk_i) begin : key_init_reg
+ for (int i=0; i<8; i++) begin
+ if (key_init_we[i]) begin
+ key_init_q[i] <= key_init_d[i];
end
end
end
@@ -181,19 +186,15 @@
IV_DATA_OUT: iv_d = data_out_d;
IV_DATA_IN_PREV: iv_d = data_in_prev_q;
IV_CTR: iv_d = ctr;
- IV_CLEAR: iv_d = '0;
- default: iv_d = '0;
+ IV_CLEAR: iv_d = {prng_data_i, prng_data_i};
+ default: iv_d = {prng_data_i, prng_data_i};
endcase
end
- always_ff @(posedge clk_i or negedge rst_ni) begin : iv_reg
- if (!rst_ni) begin
- iv_q <= '0;
- end else begin
- for (int i=0; i<8; i++) begin
- if (iv_we[i]) begin
- iv_q[i] <= iv_d[i];
- end
+ always_ff @(posedge clk_i) begin : iv_reg
+ for (int i=0; i<8; i++) begin
+ if (iv_we[i]) begin
+ iv_q[i] <= iv_d[i];
end
end
end
@@ -202,18 +203,14 @@
always_comb begin : data_in_prev_mux
unique case (data_in_prev_sel)
DIP_DATA_IN: data_in_prev_d = data_in;
- DIP_CLEAR: data_in_prev_d = '0;
- default: data_in_prev_d = '0;
+ DIP_CLEAR: data_in_prev_d = {prng_data_i, prng_data_i};
+ default: data_in_prev_d = {prng_data_i, prng_data_i};
endcase
end
- always_ff @(posedge clk_i or negedge rst_ni) begin : data_in_prev_reg
- if (!rst_ni) begin
- data_in_prev_q <= '0;
- end else begin
- if (data_in_prev_we) begin
- data_in_prev_q <= data_in_prev_d;
- end
+ always_ff @(posedge clk_i) begin : data_in_prev_reg
+ if (data_in_prev_we) begin
+ data_in_prev_q <= data_in_prev_d;
end
end
@@ -279,7 +276,8 @@
.out_ready_i ( cipher_out_ready ),
.op_i ( cipher_op ),
.key_len_i ( key_len_q ),
- .start_i ( cipher_start ),
+ .crypt_i ( cipher_crypt ),
+ .crypt_o ( cipher_crypt_busy ),
.dec_key_gen_i ( cipher_dec_key_gen ),
.dec_key_gen_o ( cipher_dec_key_gen_busy ),
.key_clear_i ( cipher_key_clear ),
@@ -287,6 +285,8 @@
.data_out_clear_i ( cipher_data_out_clear ),
.data_out_clear_o ( cipher_data_out_clear_busy ),
+ .prng_data_i ( prng_data_i ),
+
.state_init_i ( state_init ),
.key_init_i ( key_init_q ),
.state_o ( state_done )
@@ -323,6 +323,7 @@
.iv_clear_i ( reg2hw.trigger.iv_clear.q ),
.data_in_clear_i ( reg2hw.trigger.data_in_clear.q ),
.data_out_clear_i ( reg2hw.trigger.data_out_clear.q ),
+ .prng_reseed_i ( reg2hw.trigger.prng_reseed.q ),
.key_init_qe_i ( key_init_qe ),
.iv_qe_i ( iv_qe ),
@@ -346,7 +347,8 @@
.cipher_in_ready_i ( cipher_in_ready ),
.cipher_out_valid_i ( cipher_out_valid ),
.cipher_out_ready_o ( cipher_out_ready ),
- .cipher_start_o ( cipher_start ),
+ .cipher_crypt_o ( cipher_crypt ),
+ .cipher_crypt_i ( cipher_crypt_busy ),
.cipher_dec_key_gen_o ( cipher_dec_key_gen ),
.cipher_dec_key_gen_i ( cipher_dec_key_gen_busy ),
.cipher_key_clear_o ( cipher_key_clear ),
@@ -359,6 +361,11 @@
.iv_sel_o ( iv_sel ),
.iv_we_o ( iv_we ),
+ .prng_data_req_o ( prng_data_req_o ),
+ .prng_data_ack_i ( prng_data_ack_i ),
+ .prng_reseed_req_o ( prng_reseed_req_o ),
+ .prng_reseed_ack_i ( prng_reseed_ack_i ),
+
.start_o ( hw2reg.trigger.start.d ),
.start_we_o ( hw2reg.trigger.start.de ),
.key_clear_o ( hw2reg.trigger.key_clear.d ),
@@ -369,6 +376,8 @@
.data_in_clear_we_o ( hw2reg.trigger.data_in_clear.de ),
.data_out_clear_o ( hw2reg.trigger.data_out_clear.d ),
.data_out_clear_we_o ( hw2reg.trigger.data_out_clear.de ),
+ .prng_reseed_o ( hw2reg.trigger.prng_reseed.d ),
+ .prng_reseed_we_o ( hw2reg.trigger.prng_reseed.de ),
.output_valid_o ( hw2reg.status.output_valid.d ),
.output_valid_we_o ( hw2reg.status.output_valid.de ),
@@ -409,10 +418,8 @@
// Outputs //
/////////////
- always_ff @(posedge clk_i or negedge rst_ni) begin : data_out_reg
- if (!rst_ni) begin
- data_out_q <= '0;
- end else if (data_out_we) begin
+ always_ff @(posedge clk_i) begin : data_out_reg
+ if (data_out_we) begin
data_out_q <= data_out_d;
end
end
diff --git a/hw/ip/aes/rtl/aes_prng.sv b/hw/ip/aes/rtl/aes_prng.sv
new file mode 100644
index 0000000..f0c7046
--- /dev/null
+++ b/hw/ip/aes/rtl/aes_prng.sv
@@ -0,0 +1,78 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+//
+// AES pseudo-random number generator
+//
+// This module uses an LFSR connected to a PRINCE S-Box to provide pseudo-random data to the AES
+// module primarily for clearing registers. The LFSR can be reseeded using an external interface.
+
+module aes_prng(
+ input logic clk_i,
+ input logic rst_ni,
+
+ // Connections to AES internals, PRNG consumers
+ input logic data_req_i,
+ output logic data_ack_o,
+ output logic [63:0] data_o,
+ input logic reseed_req_i,
+ output logic reseed_ack_o,
+
+ // Connections to outer world, LFSR re-seed
+ output logic entropy_req_o,
+ input logic entropy_ack_i,
+ input logic [63:0] entropy_i
+);
+
+ localparam int unsigned DATA_WIDTH = 64;
+
+ // The S-Box of the PRINCE cipher is used to "scramble" the LFSR output.
+ localparam logic[15:0][3:0] PRINCE_SBOX_FWD = {4'h4, 4'hD, 4'h5, 4'hE,
+ 4'h0, 4'h8, 4'h7, 4'h6,
+ 4'h1, 4'h9, 4'hC, 4'hA,
+ 4'h2, 4'h3, 4'hF, 4'hB};
+
+ // "Scramble" with PRINCE cipher S-Box.
+ function automatic logic [63:0] aes_prng_scramble(logic [63:0] in);
+ logic [63:0] out;
+ // The PRINCE cipher S-Box operates on 4-bit nibbles.
+ for (int i=0; i<16; i++) begin
+ out[i*4 +: 4] = PRINCE_SBOX_FWD[in[i*4 +: 4]];
+ end
+ return out;
+ endfunction
+
+ logic [DATA_WIDTH-1:0] lfsr_state;
+ logic lfsr_en;
+ logic seed_en;
+
+ // The data requests are fed from the LFSR, reseed requests have the highest priority.
+ assign data_ack_o = reseed_req_i ? 1'b0 : data_req_i;
+
+ // Reseed requests are directly forwarded to the external interface.
+ assign reseed_ack_o = entropy_ack_i;
+ assign entropy_req_o = reseed_req_i;
+
+ // LFSR control
+ assign lfsr_en = data_req_i & data_ack_o;
+ assign seed_en = entropy_req_o & entropy_ack_i;
+
+ // LFSR instance
+ prim_lfsr #(
+ .LfsrType ( "GAL_XOR" ),
+ .LfsrDw ( DATA_WIDTH ),
+ .StateOutDw ( DATA_WIDTH )
+ ) aes_prng_lfsr (
+ .clk_i ( clk_i ),
+ .rst_ni ( rst_ni ),
+ .seed_en_i ( seed_en ),
+ .seed_i ( entropy_i ),
+ .lfsr_en_i ( lfsr_en ),
+ .entropy_i ( '0 ),
+ .state_o ( lfsr_state )
+ );
+
+ // "Scramble" the LFSR state.
+ assign data_o = aes_prng_scramble(lfsr_state);
+
+endmodule
diff --git a/hw/ip/aes/rtl/aes_reg_pkg.sv b/hw/ip/aes/rtl/aes_reg_pkg.sv
index 57437d8..fdb809d 100644
--- a/hw/ip/aes/rtl/aes_reg_pkg.sv
+++ b/hw/ip/aes/rtl/aes_reg_pkg.sv
@@ -69,6 +69,9 @@
struct packed {
logic q;
} data_out_clear;
+ struct packed {
+ logic q;
+ } prng_reseed;
} aes_reg2hw_trigger_reg_t;
@@ -125,6 +128,10 @@
logic d;
logic de;
} data_out_clear;
+ struct packed {
+ logic d;
+ logic de;
+ } prng_reseed;
} aes_hw2reg_trigger_reg_t;
typedef struct packed {
@@ -151,25 +158,25 @@
// Register to internal design logic //
///////////////////////////////////////
typedef struct packed {
- aes_reg2hw_key_mreg_t [7:0] key; // [676:413]
- aes_reg2hw_iv_mreg_t [3:0] iv; // [412:281]
- aes_reg2hw_data_in_mreg_t [3:0] data_in; // [280:149]
- aes_reg2hw_data_out_mreg_t [3:0] data_out; // [148:17]
- aes_reg2hw_ctrl_reg_t ctrl; // [16:5]
- aes_reg2hw_trigger_reg_t trigger; // [4:0]
+ aes_reg2hw_key_mreg_t [7:0] key; // [677:414]
+ aes_reg2hw_iv_mreg_t [3:0] iv; // [413:282]
+ aes_reg2hw_data_in_mreg_t [3:0] data_in; // [281:150]
+ aes_reg2hw_data_out_mreg_t [3:0] data_out; // [149:18]
+ aes_reg2hw_ctrl_reg_t ctrl; // [17:6]
+ aes_reg2hw_trigger_reg_t trigger; // [5:0]
} aes_reg2hw_t;
///////////////////////////////////////
// Internal design logic to register //
///////////////////////////////////////
typedef struct packed {
- aes_hw2reg_key_mreg_t [7:0] key; // [669:414]
- aes_hw2reg_iv_mreg_t [3:0] iv; // [413:286]
- aes_hw2reg_data_in_mreg_t [3:0] data_in; // [285:154]
- aes_hw2reg_data_out_mreg_t [3:0] data_out; // [153:26]
- aes_hw2reg_ctrl_reg_t ctrl; // [25:14]
- aes_hw2reg_trigger_reg_t trigger; // [13:9]
- aes_hw2reg_status_reg_t status; // [8:9]
+ aes_hw2reg_key_mreg_t [7:0] key; // [671:416]
+ aes_hw2reg_iv_mreg_t [3:0] iv; // [415:288]
+ aes_hw2reg_data_in_mreg_t [3:0] data_in; // [287:156]
+ aes_hw2reg_data_out_mreg_t [3:0] data_out; // [155:28]
+ aes_hw2reg_ctrl_reg_t ctrl; // [27:16]
+ aes_hw2reg_trigger_reg_t trigger; // [15:10]
+ aes_hw2reg_status_reg_t status; // [9:10]
} aes_hw2reg_t;
// Register Address
diff --git a/hw/ip/aes/rtl/aes_reg_top.sv b/hw/ip/aes/rtl/aes_reg_top.sv
index eac8a94..3760ff6 100644
--- a/hw/ip/aes/rtl/aes_reg_top.sv
+++ b/hw/ip/aes/rtl/aes_reg_top.sv
@@ -137,6 +137,8 @@
logic trigger_data_in_clear_we;
logic trigger_data_out_clear_wd;
logic trigger_data_out_clear_we;
+ logic trigger_prng_reseed_wd;
+ logic trigger_prng_reseed_we;
logic status_idle_qs;
logic status_stall_qs;
logic status_output_valid_qs;
@@ -604,7 +606,7 @@
prim_subreg #(
.DW (1),
.SWACCESS("WO"),
- .RESVAL (1'h0)
+ .RESVAL (1'h1)
) u_trigger_key_clear (
.clk_i (clk_i ),
.rst_ni (rst_ni ),
@@ -629,7 +631,7 @@
prim_subreg #(
.DW (1),
.SWACCESS("WO"),
- .RESVAL (1'h0)
+ .RESVAL (1'h1)
) u_trigger_iv_clear (
.clk_i (clk_i ),
.rst_ni (rst_ni ),
@@ -654,7 +656,7 @@
prim_subreg #(
.DW (1),
.SWACCESS("WO"),
- .RESVAL (1'h0)
+ .RESVAL (1'h1)
) u_trigger_data_in_clear (
.clk_i (clk_i ),
.rst_ni (rst_ni ),
@@ -679,7 +681,7 @@
prim_subreg #(
.DW (1),
.SWACCESS("WO"),
- .RESVAL (1'h0)
+ .RESVAL (1'h1)
) u_trigger_data_out_clear (
.clk_i (clk_i ),
.rst_ni (rst_ni ),
@@ -700,6 +702,31 @@
);
+ // F[prng_reseed]: 5:5
+ prim_subreg #(
+ .DW (1),
+ .SWACCESS("WO"),
+ .RESVAL (1'h1)
+ ) u_trigger_prng_reseed (
+ .clk_i (clk_i ),
+ .rst_ni (rst_ni ),
+
+ // from register interface
+ .we (trigger_prng_reseed_we),
+ .wd (trigger_prng_reseed_wd),
+
+ // from internal hardware
+ .de (hw2reg.trigger.prng_reseed.de),
+ .d (hw2reg.trigger.prng_reseed.d ),
+
+ // to internal hardware
+ .qe (),
+ .q (reg2hw.trigger.prng_reseed.q ),
+
+ .qs ()
+ );
+
+
// R[status]: V(False)
// F[idle]: 0:0
@@ -949,6 +976,9 @@
assign trigger_data_out_clear_we = addr_hit[21] & reg_we & ~wr_err;
assign trigger_data_out_clear_wd = reg_wdata[4];
+ assign trigger_prng_reseed_we = addr_hit[21] & reg_we & ~wr_err;
+ assign trigger_prng_reseed_wd = reg_wdata[5];
+
@@ -1050,6 +1080,7 @@
reg_rdata_next[2] = '0;
reg_rdata_next[3] = '0;
reg_rdata_next[4] = '0;
+ reg_rdata_next[5] = '0;
end
addr_hit[22]: begin