[keymgr] Enhance checks
- update data checks
- add second enable to control datapath to sideload and software
- directly feed stored key state out instead of storing into sideload
Signed-off-by: Timothy Chen <timothytim@google.com>
diff --git a/hw/ip/keymgr/rtl/keymgr.sv b/hw/ip/keymgr/rtl/keymgr.sv
index 023099d..2759489 100644
--- a/hw/ip/keymgr/rtl/keymgr.sv
+++ b/hw/ip/keymgr/rtl/keymgr.sv
@@ -140,9 +140,10 @@
logic adv_en, id_en, gen_en;
logic load_key;
logic wipe_key;
- logic [Shares-1:0][KeyWidth-1:0] kmac_key;
+ hw_key_req_t kmac_key;
logic op_done;
logic data_valid;
+ logic data_en;
logic kmac_done;
logic kmac_input_invalid;
logic kmac_cmd_err;
@@ -166,6 +167,7 @@
.sw_binding_unlock_o(sw_binding_unlock),
.status_o(hw2reg.op_status.d),
.error_o(err_code),
+ .data_en_o(data_en),
.data_valid_o(data_valid),
.working_state_o(hw2reg.working_state.d),
.root_key_i(otp_key_i),
@@ -213,6 +215,7 @@
// The various arrays of inputs for each operation
logic [2**StageWidth-1:0][AdvDataWidth-1:0] adv_matrix;
+ logic [2**StageWidth-1:0] adv_dvalid;
logic [2**StageWidth-1:0][IdDataWidth-1:0] id_matrix;
logic [GenDataWidth-1:0] gen_in;
@@ -224,12 +227,18 @@
localparam int IdLfsrCopies = IdDataWidth / 32;
localparam int GenLfsrCopies = GenDataWidth / 32;
- // input checking - currently only MAX version, others TBD
- logic key_version_good;
+ // input checking
+ logic creator_seed_vld;
+ logic owner_seed_vld;
+ logic devid_vld;
+ logic health_state_vld;
+ logic key_version_vld;
+
// Advance state operation input construction
for (genvar i = KeyMgrStages; i < 2**StageWidth; i++) begin : gen_adv_matrix_fill
assign adv_matrix[i] = {AdvLfsrCopies{lfsr[31:0]}};
+ assign adv_dvalid[i] = 1'b0;
end
// Advance to creator_root_key
@@ -241,14 +250,19 @@
lc_i.health_state,
creator_seed});
+ assign adv_dvalid[Creator] = creator_seed_vld &
+ devid_vld &
+ health_state_vld;
+
// Advance to owner_intermediate_key
logic [KeyWidth-1:0] owner_seed;
assign owner_seed = flash_i.seeds[flash_ctrl_pkg::OwnerSeedIdx];
assign adv_matrix[OwnerInt] = AdvDataWidth'({reg2hw.sw_binding,owner_seed});
+ assign adv_dvalid[OwnerInt] = owner_seed_vld;
// Advance to owner_key
- assign adv_matrix[Owner] = AdvDataWidth'(reg2hw.sw_binding);
-
+ assign adv_matrix[Owner] = AdvDataWidth'(reg2hw.sw_binding);
+ assign adv_dvalid[Owner] = 1'b1;
// Generate Identity operation input construction
for (genvar i = KeyMgrStages; i < 2**StageWidth; i++) begin : gen_id_matrix_fill
@@ -279,11 +293,22 @@
// General module for checking inputs
+ logic key_vld;
keymgr_input_checks u_checks (
.max_key_versions_i(max_key_versions),
.stage_sel_i(stage_sel),
.key_version_i(reg2hw.key_version),
- .key_version_good_o(key_version_good)
+ .creator_seed_i(creator_seed),
+ .owner_seed_i(owner_seed),
+ .key_i(kmac_key_o),
+ .devid_i(otp_i.devid),
+ .health_state_i(lc_i.health_state),
+ .creator_seed_vld_o(creator_seed_vld),
+ .owner_seed_vld_o(owner_seed_vld),
+ .devid_vld_o(devid_vld),
+ .health_state_vld_o(health_state_vld),
+ .key_version_vld_o(key_version_vld),
+ .key_vld_o(key_vld)
);
@@ -291,15 +316,11 @@
// KMAC Control
/////////////////////////////////////
- logic key_version_err;
logic [3:0] invalid_data;
-
- assign key_version_err = !key_version_good;
-
- assign invalid_data[OpAdvance] = '0; // TBD
- assign invalid_data[OpGenId] = '0; // TBD
- assign invalid_data[OpGenSwOut] = key_version_err; // TBD, more to come
- assign invalid_data[OpGenHwOut] = key_version_err; // TBD, more to come
+ assign invalid_data[OpAdvance] = ~key_vld | ~adv_dvalid[stage_sel];
+ assign invalid_data[OpGenId] = ~key_vld;
+ assign invalid_data[OpGenSwOut] = ~key_vld | ~key_version_vld;
+ assign invalid_data[OpGenHwOut] = ~key_vld | ~key_version_vld;
keymgr_kmac_if u_kmac_if (
.clk_i,
@@ -339,6 +360,7 @@
.dest_sel_i(keymgr_key_dest_e'(reg2hw.control.dest_sel)),
.key_sel_i(key_sel),
.load_key_i(load_key),
+ .data_en_i(data_en),
.data_valid_i(data_valid),
.key_i(kmac_key),
.data_i(kmac_data),
@@ -349,8 +371,10 @@
);
for (genvar i = 0; i < 8; i++) begin : gen_sw_assigns
- assign hw2reg.sw_share0_output[i].d = wipe_key ? key_entropy : kmac_data[0][i*32 +: 32];
- assign hw2reg.sw_share1_output[i].d = wipe_key ? key_entropy : kmac_data[1][i*32 +: 32];
+ assign hw2reg.sw_share0_output[i].d = ~data_en | wipe_key ? key_entropy :
+ kmac_data[0][i*32 +: 32];
+ assign hw2reg.sw_share1_output[i].d = ~data_en | wipe_key ? key_entropy :
+ kmac_data[1][i*32 +: 32];
assign hw2reg.sw_share0_output[i].de = wipe_key | data_valid & (key_sel == SwKey);
assign hw2reg.sw_share1_output[i].de = wipe_key | data_valid & (key_sel == SwKey);
end
diff --git a/hw/ip/keymgr/rtl/keymgr_ctrl.sv b/hw/ip/keymgr/rtl/keymgr_ctrl.sv
index 112000a..68bdd34 100644
--- a/hw/ip/keymgr/rtl/keymgr_ctrl.sv
+++ b/hw/ip/keymgr/rtl/keymgr_ctrl.sv
@@ -20,6 +20,7 @@
output logic op_done_o,
output keymgr_op_status_e status_o,
output logic [ErrLastPos-1:0] error_o,
+ output logic data_en_o,
output logic data_valid_o,
output logic wipe_key_o,
output keymgr_working_state_e working_state_o,
@@ -34,7 +35,7 @@
output logic adv_en_o,
output logic id_en_o,
output logic gen_en_o,
- output logic [Shares-1:0][KeyWidth-1:0] key_o,
+ output hw_key_req_t key_o,
output logic load_key_o,
input kmac_done_i,
input kmac_input_invalid_i, // asserted when selected data fails criteria check
@@ -74,7 +75,6 @@
logic cnt_en;
logic cnt_clr;
logic data_valid;
- logic adv_en_q;
logic op_accepted;
logic invalid_op;
@@ -105,7 +105,7 @@
assign adv_en_o = op_accepted & (advance_sel | disable_sel);
assign id_en_o = op_accepted & gen_id_sel;
assign gen_en_o = op_accepted & gen_out_sel;
- assign load_key_o = adv_en_o & !adv_en_q;
+ assign load_key_o = op_accepted;
// unlock sw binding configuration whenever an advance call is made without errors
assign sw_binding_unlock_o = adv_en_o & op_done_o & ~|error_o;
@@ -119,10 +119,8 @@
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
state_q <= StCtrlReset;
- adv_en_q <= 1'b0;
end else begin
state_q <= state_d;
- adv_en_q <= adv_en_o;
end
end
@@ -130,7 +128,11 @@
// - whatever operation causes the input data select to be disabled should not expose the key
// state.
// - when there are no operations, the key state also should be exposed.
- assign key_o = (~op_start_i | disable_sel) ? {EntropyRounds * Shares {entropy_i}} : key_state_q;
+ assign key_o.valid = op_accepted;
+ assign key_o.key_share0 = (~op_start_i | disable_sel) ? {EntropyRounds{entropy_i}} :
+ key_state_q[0];
+ assign key_o.key_share1 = (~op_start_i | disable_sel) ? {EntropyRounds{entropy_i}} :
+ key_state_q[1];
// key state is intentionally not reset
always_ff @(posedge clk_i) begin
@@ -413,6 +415,8 @@
// if operation was never accepted (ie a generate was called in StReset / StRandom), then
// never update the sw / hw outputs when operation is complete
+ // TODO: This is a critical single point of failure, need to think deeply about how to
+ // enhance this.
assign data_valid_o = op_done_o & op_accepted & data_valid & gen_sel;
// data errors are not relevant when operation was not accepted.
@@ -431,6 +435,78 @@
end
+ ///////////////////////////////
+ // Suppress kmac return data
+ ///////////////////////////////
+ // This is a separate data path from the FSM used to control the data_en_o output
+
+ typedef enum logic [1:0] {
+ StCtrlDataIdle,
+ StCtrlDataEn,
+ StCtrlDataDis,
+ StCtrlDataWait
+ } keymgr_ctrl_data_state_e;
+
+ keymgr_ctrl_data_state_e data_st_d, data_st_q;
+
+ always_ff @(posedge clk_i or negedge rst_ni) begin
+ if (!rst_ni) begin
+ data_st_q <= StCtrlDataIdle;
+ end else begin
+ data_st_q <= data_st_d;
+ end
+ end
+
+ // The below control path is used for modulating the datapath to sideload and sw keys.
+ // This path is separate from the data_valid_o path, thus creating two separate attack points.
+ // The data is only enabled when a non-advance operation is invoked.
+ // When an advance operation is called, the data is disabled. It will stay disabled until an
+ // entire completion sequence is seen (op_done_o assert -> start_i de-assertion).
+ // When a generate operation is called, the data is enabled. However, any indication of this
+ // supposedly being an advance call will force the path to disable again.
+ always_comb begin
+ data_st_d = data_st_q;
+ data_en_o = 1'b0;
+ unique case (data_st_q)
+
+ StCtrlDataIdle: begin
+ if (adv_en_o) begin
+ data_st_d = StCtrlDataDis;
+ end else if (id_en_o || gen_en_o) begin
+ data_st_d = StCtrlDataEn;
+ end
+ end
+
+ StCtrlDataEn: begin
+ data_en_o = 1'b1;
+ if (adv_en_o) begin
+ data_st_d = StCtrlDataDis;
+ end
+ end
+
+ StCtrlDataDis: begin
+ if (op_done_o) begin
+ data_st_d = StCtrlDataWait;
+ end
+ end
+
+ StCtrlDataWait: begin
+ if (!op_start_i) begin
+ data_st_d = StCtrlDataIdle;
+ end
+ end
+
+ default:;
+
+ endcase // unique case (data_st_q)
+ end
+
+
+
+
+
+
+
///////////////////////////////
// Functions
diff --git a/hw/ip/keymgr/rtl/keymgr_input_checks.sv b/hw/ip/keymgr/rtl/keymgr_input_checks.sv
index 1a0222e..ab1da17 100644
--- a/hw/ip/keymgr/rtl/keymgr_input_checks.sv
+++ b/hw/ip/keymgr/rtl/keymgr_input_checks.sv
@@ -11,16 +11,44 @@
module keymgr_input_checks import keymgr_pkg::*;(
input [2**StageWidth-1:0][31:0] max_key_versions_i,
input keymgr_stage_e stage_sel_i,
+ input hw_key_req_t key_i,
input [31:0] key_version_i,
- output logic key_version_good_o
+ input [KeyWidth-1:0] creator_seed_i,
+ input [KeyWidth-1:0] owner_seed_i,
+ input [DevIdWidth-1:0] devid_i,
+ input [HealthStateWidth-1:0] health_state_i,
+ output logic creator_seed_vld_o,
+ output logic owner_seed_vld_o,
+ output logic devid_vld_o,
+ output logic health_state_vld_o,
+ output logic key_version_vld_o,
+ output logic key_vld_o
);
logic [31:0] cur_max_key_version;
assign cur_max_key_version = max_key_versions_i[stage_sel_i];
// key version must be smaller than or equal to max version
- assign key_version_good_o = key_version_i <= cur_max_key_version;
+ assign key_version_vld_o = key_version_i <= cur_max_key_version;
+ // general data check
+ assign creator_seed_vld_o = valid_chk(MaxWidth'(creator_seed_i));
+ assign owner_seed_vld_o = valid_chk(MaxWidth'(owner_seed_i));
+ assign devid_vld_o = valid_chk(MaxWidth'(devid_i));
+ assign health_state_vld_o = valid_chk(MaxWidth'(health_state_i));
+
+ // key check
+ logic unused_key_vld;
+ assign unused_key_vld = key_i.valid;
+ assign key_vld_o = valid_chk(MaxWidth'(key_i.key_share0)) &
+ valid_chk(MaxWidth'(key_i.key_share1));
+
+ // checks for all 0's or all 1's of value
+ function automatic logic valid_chk (logic [MaxWidth-1:0] value);
+
+ return |value & ~&value;
+
+ endfunction // valid_chk
endmodule // keymgr_input_checks
diff --git a/hw/ip/keymgr/rtl/keymgr_kmac_if.sv b/hw/ip/keymgr/rtl/keymgr_kmac_if.sv
index 414388a..167d443 100644
--- a/hw/ip/keymgr/rtl/keymgr_kmac_if.sv
+++ b/hw/ip/keymgr/rtl/keymgr_kmac_if.sv
@@ -15,7 +15,7 @@
input [AdvDataWidth-1:0] adv_data_i,
input [IdDataWidth-1:0] id_data_i,
input [GenDataWidth-1:0] gen_data_i,
- input [3:0] inputs_invalid_i, // probably should break down into categories later
+ input [3:0] inputs_invalid_i,
output logic inputs_invalid_o,
// keymgr control to select appropriate inputs
diff --git a/hw/ip/keymgr/rtl/keymgr_pkg.sv b/hw/ip/keymgr/rtl/keymgr_pkg.sv
index 92d3a90..5444f1f 100644
--- a/hw/ip/keymgr/rtl/keymgr_pkg.sv
+++ b/hw/ip/keymgr/rtl/keymgr_pkg.sv
@@ -18,6 +18,7 @@
// These should be defined in another module's package
parameter int HealthStateWidth = 128;
parameter int DevIdWidth = 256;
+ parameter int MaxWidth = 256;
// Default seeds
// These are generated using random.org byte dumper
diff --git a/hw/ip/keymgr/rtl/keymgr_sideload_key.sv b/hw/ip/keymgr/rtl/keymgr_sideload_key.sv
index d69cb1a..d2d3e94 100644
--- a/hw/ip/keymgr/rtl/keymgr_sideload_key.sv
+++ b/hw/ip/keymgr/rtl/keymgr_sideload_key.sv
@@ -10,6 +10,7 @@
input clk_i,
input rst_ni,
input en_i,
+ input set_en_i,
input set_i,
input clr_i,
input [31:0] entropy_i,
@@ -42,7 +43,9 @@
key_q[i] <= {EntropyCopies{entropy_i}};
end
end else if (set_i) begin
- key_q <= key_i;
+ for (int i = 0; i < Shares; i++) begin
+ key_q[i] <= set_en_i ? key_i[i] : {EntropyCopies{entropy_i}};
+ end
end
end
diff --git a/hw/ip/keymgr/rtl/keymgr_sideload_key_ctrl.sv b/hw/ip/keymgr/rtl/keymgr_sideload_key_ctrl.sv
index 95b0825..0fb9b35 100644
--- a/hw/ip/keymgr/rtl/keymgr_sideload_key_ctrl.sv
+++ b/hw/ip/keymgr/rtl/keymgr_sideload_key_ctrl.sv
@@ -15,8 +15,9 @@
input keymgr_key_dest_e dest_sel_i,
input keymgr_gen_out_e key_sel_i,
input load_key_i,
+ input data_en_i,
input data_valid_i,
- input [Shares-1:0][KeyWidth-1:0] key_i,
+ input hw_key_req_t key_i,
input [Shares-1:0][KeyWidth-1:0] data_i,
output logic prng_en_o,
output hw_key_req_t aes_key_o,
@@ -100,6 +101,7 @@
.clk_i,
.rst_ni,
.en_i(keys_en),
+ .set_en_i(data_en_i),
.set_i(data_valid_i & aes_sel),
.clr_i(clr), // TBD, should add an option for software clear later
.entropy_i(entropy_i),
@@ -111,6 +113,7 @@
.clk_i,
.rst_ni,
.en_i(keys_en),
+ .set_en_i(data_en_i),
.set_i(data_valid_i & hmac_sel),
.clr_i(clr), // TBD, should add an option for software clear later
.entropy_i(entropy_i),
@@ -118,17 +121,22 @@
.key_o(hmac_key_o)
);
+ hw_key_req_t kmac_sideload_key;
keymgr_sideload_key u_kmac_key (
.clk_i,
.rst_ni,
.en_i(keys_en),
- .set_i(load_key_i | (data_valid_i & kmac_sel)),
+ .set_en_i(data_en_i),
+ .set_i(data_valid_i & kmac_sel),
.clr_i(clr), // TBD, should add an option for software clear laterclr
.entropy_i(entropy_i),
- .key_i(load_key_i ? key_i : data_i),
- .key_o(kmac_key_o)
+ .key_i(data_i),
+ .key_o(kmac_sideload_key)
);
+ // when directed by keymgr_ctrl, switch over to internal key and feed to kmac
+ assign kmac_key_o = load_key_i ? key_i : kmac_sideload_key;
+
// when clearing, request prng
assign prng_en_o = clr;