blob: 70db6144420ebb068c7d86cf3ed4ff1a1f01d4f2 [file] [log] [blame]
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
//
// Manage all sideload keys
`include "prim_assert.sv"
module keymgr_sideload_key_ctrl import keymgr_pkg::*;(
input clk_i,
input rst_ni,
input init_i,
input keymgr_sideload_clr_e clr_key_i, // clear key just deletes the key
input wipe_key_i, // wipe key deletes and renders sideloads useless until reboot
input [Shares-1:0][RandWidth-1:0] entropy_i,
input keymgr_key_dest_e dest_sel_i,
input keymgr_gen_out_e key_sel_i,
input data_en_i,
input data_valid_i,
input hw_key_req_t key_i,
input [Shares-1:0][kmac_pkg::AppDigestW-1:0] data_i,
output logic prng_en_o,
output hw_key_req_t aes_key_o,
output hw_key_req_t hmac_key_o,
output hw_key_req_t kmac_key_o,
output otbn_key_req_t otbn_key_o
);
// Enumeration for working state
typedef enum logic [2:0] {
StSideloadReset,
StSideloadIdle,
StSideloadClear,
StSideloadWipe,
StSideloadStop
} keymgr_sideload_e;
keymgr_sideload_e state_q, state_d;
logic [3:0] cnt_q, cnt_d;
logic cnt_end;
logic clr;
logic keys_en;
logic [Shares-1:0][KeyWidth-1:0] data_truncated;
for(genvar i = 0; i < Shares; i++) begin : gen_truncate_data
assign data_truncated[i] = data_i[i][KeyWidth-1:0];
end
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
state_q <= StSideloadReset;
cnt_q <= '0;
end else begin
state_q <= state_d;
cnt_q <= cnt_d;
end
end
assign cnt_end = cnt_q[3];
assign cnt_d = cnt_end ? cnt_q :
clr ? cnt_q + 1'b1 : cnt_q;
logic clr_key;
assign clr_key = (clr_key_i != SideLoadClrIdle);
logic clr_all_keys;
assign clr_all_keys = !(clr_key_i inside {SideLoadClrIdle, SideLoadClrAes,
SideLoadClrHmac, SideLoadClrKmac,
SideLoadClrOtbn});
logic aes_clr, hmac_clr, kmac_clr, otbn_clr;
assign aes_clr = clr & (clr_all_keys | (clr_key_i == SideLoadClrAes));
assign hmac_clr = clr & (clr_all_keys | (clr_key_i == SideLoadClrHmac));
assign kmac_clr = clr & (clr_all_keys | (clr_key_i == SideLoadClrKmac));
assign otbn_clr = clr & (clr_all_keys | (clr_key_i == SideLoadClrOtbn));
always_comb begin
clr = 1'b0;
keys_en = 1'b0;
state_d = state_q;
unique case (state_q)
StSideloadReset: begin
if (init_i) begin
state_d = StSideloadIdle;
end
end
// when clear is received, delete the key and return to idle.
// when wipe is received, delete the key and disable sideload until reboot.
StSideloadIdle: begin
keys_en = 1'b1;
if (wipe_key_i || clr_key) begin
state_d = wipe_key_i ? StSideloadWipe : StSideloadClear;
end
end
// if wipe asserts while clearing, follow the normal wipe protocol
StSideloadClear: begin
keys_en = 1'b0;
clr = 1'b1;
if (wipe_key_i) begin
state_d = StSideloadWipe;
end else if (!clr_key) begin
state_d = StSideloadIdle;
end
end
StSideloadWipe: begin
keys_en = 1'b0;
clr = 1'b1;
if (!wipe_key_i) begin
state_d = StSideloadStop;
end
end
// intentional terminal state
StSideloadStop: begin
keys_en = 1'b0;
end
default:;
endcase // unique case (state_q)
end
logic aes_sel, hmac_sel, kmac_sel, otbn_sel;
assign aes_sel = dest_sel_i == Aes & key_sel_i == HwKey;
assign hmac_sel = dest_sel_i == Hmac & key_sel_i == HwKey;
assign kmac_sel = dest_sel_i == Kmac & key_sel_i == HwKey;
assign otbn_sel = dest_sel_i == Otbn & key_sel_i == HwKey;
keymgr_sideload_key u_aes_key (
.clk_i,
.rst_ni,
.en_i(keys_en),
.set_en_i(data_en_i),
.set_i(data_valid_i & aes_sel),
.clr_i(aes_clr),
.entropy_i(entropy_i),
.key_i(data_truncated),
.valid_o(aes_key_o.valid),
.key_o(aes_key_o.key)
);
keymgr_sideload_key u_hmac_key (
.clk_i,
.rst_ni,
.en_i(keys_en),
.set_en_i(data_en_i),
.set_i(data_valid_i & hmac_sel),
.clr_i(hmac_clr),
.entropy_i(entropy_i),
.key_i(data_truncated),
.valid_o(hmac_key_o.valid),
.key_o(hmac_key_o.key)
);
keymgr_sideload_key #(
.Width(OtbnKeyWidth)
) u_otbn_key (
.clk_i,
.rst_ni,
.en_i(keys_en),
.set_en_i(data_en_i),
.set_i(data_valid_i & otbn_sel),
.clr_i(otbn_clr),
.entropy_i(entropy_i),
.key_i(data_i),
.valid_o(otbn_key_o.valid),
.key_o(otbn_key_o.key)
);
hw_key_req_t kmac_sideload_key;
keymgr_sideload_key u_kmac_key (
.clk_i,
.rst_ni,
.en_i(keys_en),
.set_en_i(data_en_i),
.set_i(data_valid_i & kmac_sel),
.clr_i(kmac_clr),
.entropy_i(entropy_i),
.key_i(data_truncated),
.valid_o(kmac_sideload_key.valid),
.key_o(kmac_sideload_key.key)
);
// when directed by keymgr_ctrl, switch over to internal key and feed to kmac
assign kmac_key_o = key_i.valid ? key_i : kmac_sideload_key;
// when clearing, request prng
assign prng_en_o = clr;
/////////////////////////////////////
// Assertions
/////////////////////////////////////
// When updating a sideload key, the secret key state must always be used as the source
`ASSERT(KmacKeySource_a, data_valid_i |-> key_i.valid)
endmodule // keymgr_sideload_key_ctrl