blob: 198f3a9b5afe41059009ab8a8875d92d7b573b4c [file] [log] [blame]
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
//
// Key manager top level
//
`include "prim_assert.sv"
module keymgr import keymgr_pkg::*; #(
parameter logic AlertAsyncOn = 1'b1,
parameter lfsr_seed_t RndCnstLfsrSeed = RndCnstLfsrSeedDefault,
parameter lfsr_perm_t RndCnstLfsrPerm = RndCnstLfsrPermDefault,
parameter seed_t RndCnstRevisionSeed = RndCnstRevisionSeedDefault,
parameter seed_t RndCnstCreatorIdentitySeed = RndCnstCreatorIdentitySeedDefault,
parameter seed_t RndCnstOwnerIntIdentitySeed = RndCnstOwnerIntIdentitySeedDefault,
parameter seed_t RndCnstOwnerIdentitySeed = RndCnstOwnerIdentitySeedDefault,
parameter seed_t RndCnstSoftOutputSeed = RndCnstSoftOutputSeedDefault,
parameter seed_t RndCnstHardOutputSeed = RndCnstHardOutputSeedDefault
) (
input clk_i,
input rst_ni,
input clk_edn_i,
input rst_edn_ni,
// Bus Interface
input tlul_pkg::tl_h2d_t tl_i,
output tlul_pkg::tl_d2h_t tl_o,
// key interface to crypto modules
output hw_key_req_t aes_key_o,
output hw_key_req_t hmac_key_o,
output hw_key_req_t kmac_key_o,
// data interface to/from crypto modules
output kmac_data_req_t kmac_data_o,
input kmac_data_rsp_t kmac_data_i,
// the following signals should eventually be wrapped into structs from other modules
input lc_data_t lc_i,
input otp_ctrl_pkg::otp_keymgr_key_t otp_key_i,
input otp_data_t otp_i,
input flash_ctrl_pkg::keymgr_flash_t flash_i,
// connection to edn
output edn_pkg::edn_req_t edn_o,
input edn_pkg::edn_rsp_t edn_i,
// interrupts and alerts
output logic intr_op_done_o,
output logic intr_err_o,
input prim_alert_pkg::alert_rx_t [keymgr_reg_pkg::NumAlerts-1:0] alert_rx_i,
output prim_alert_pkg::alert_tx_t [keymgr_reg_pkg::NumAlerts-1:0] alert_tx_o
);
import keymgr_reg_pkg::*;
`ASSERT_INIT(AdvDataWidth_A, AdvDataWidth <= KDFMaxWidth)
`ASSERT_INIT(IdDataWidth_A, IdDataWidth <= KDFMaxWidth)
`ASSERT_INIT(GenDataWidth_A, GenDataWidth <= KDFMaxWidth)
`ASSERT_INIT(OutputKeyDiff_A, RndCnstHardOutputSeed != RndCnstSoftOutputSeed)
// Register module
keymgr_reg2hw_t reg2hw;
keymgr_hw2reg_t hw2reg;
keymgr_reg_top u_reg (
.clk_i,
.rst_ni,
.tl_i,
.tl_o,
.reg2hw,
.hw2reg,
.devmode_i (1'b1) // connect to real devmode signal in the future
);
/////////////////////////////////////
// LFSR
/////////////////////////////////////
// A farily large lfsr is used here as entropy in multiple places.
// - populate the default working state
// - generate random inputs when a bad input is selected
//
// The first case is sensitive, and thus the working state is constructed
// through multiple rounds of the Lfsr
// The second case is less sensitive and is applied directly. If the inputs
// have more bits than the lfsr output, the lfsr value is simply replicated
logic seed_en;
logic [LfsrWidth-1:0] seed;
logic reseed_req;
logic reseed_ack;
keymgr_reseed_ctrl u_reseed_ctrl (
.clk_i,
.rst_ni,
.clk_edn_i,
.rst_edn_ni,
.reseed_req_i(reseed_req),
.reseed_ack_o(reseed_ack),
.reseed_interval_i(reg2hw.reseed_interval.q),
.edn_o,
.edn_i,
.seed_en_o(seed_en),
.seed_o(seed)
);
logic [63:0] lfsr;
logic ctrl_lfsr_en, data_lfsr_en, sideload_lfsr_en;
prim_lfsr #(
.LfsrDw(LfsrWidth),
.StateOutDw(LfsrWidth),
.DefaultSeed(RndCnstLfsrSeed),
.StatePermEn(1'b1),
.StatePerm(RndCnstLfsrPerm)
) u_lfsr (
.clk_i,
.rst_ni,
.lfsr_en_i(ctrl_lfsr_en | data_lfsr_en | sideload_lfsr_en),
// The seed update is skipped if there is an ongoing keymgr transaction.
// This is not really done for any functional purpose but more to simplify
// DV. When an invalid operation is selected, the keymgr just starts transmitting
// whatever is at the prng output, however, this may cause a dv protocol violation
// if a reseed happens to coincide.
.seed_en_i(seed_en & ~reg2hw.control.start.q),
.seed_i(seed),
.entropy_i('0),
.state_o(lfsr)
);
/////////////////////////////////////
// Key Manager Control
/////////////////////////////////////
keymgr_stage_e stage_sel;
keymgr_gen_out_e key_sel;
logic adv_en, id_en, gen_en;
logic load_key;
logic wipe_key;
logic [Shares-1:0][KeyWidth-1:0] kmac_key;
logic op_done;
logic init_done;
logic data_valid;
logic kmac_done;
logic kmac_input_invalid;
logic kmac_cmd_err;
logic kmac_fsm_err;
logic kmac_op_err;
logic [Shares-1:0][KeyWidth-1:0] kmac_data;
logic [ErrLastPos-1:0] err_code;
keymgr_ctrl u_ctrl (
.clk_i,
.rst_ni,
.en_i(lc_i.keymgr_en),
.prng_reseed_req_o(reseed_req),
.prng_reseed_ack_i(reseed_ack),
.prng_en_o(ctrl_lfsr_en),
.entropy_i(lfsr[63:32]),
.init_i(reg2hw.control.init.q),
.init_done_o(init_done),
.op_i(keymgr_ops_e'(reg2hw.control.operation.q)),
.op_start_i(reg2hw.control.start.q),
.op_done_o(op_done),
.status_o(hw2reg.op_status.d),
.error_o(err_code),
.data_valid_o(data_valid),
.working_state_o(hw2reg.working_state.d),
.root_key_i(otp_key_i),
.hw_sel_o(key_sel),
.stage_sel_o(stage_sel),
.load_key_o(load_key),
.wipe_key_o(wipe_key),
.adv_en_o(adv_en),
.id_en_o(id_en),
.gen_en_o(gen_en),
.key_o(kmac_key),
.kmac_done_i(kmac_done),
.kmac_input_invalid_i(kmac_input_invalid),
.kmac_fsm_err_i(kmac_fsm_err),
.kmac_op_err_i(kmac_op_err),
.kmac_cmd_err_i(kmac_cmd_err),
.kmac_data_i(kmac_data)
);
assign hw2reg.control.start.d = '0;
assign hw2reg.control.start.de = op_done;
assign hw2reg.control.init.d = '0;
assign hw2reg.control.init.de = init_done;
// as long as operation is ongoing, capture status
assign hw2reg.op_status.de = reg2hw.control.start.q;
// working state is always visible
assign hw2reg.working_state.de = 1'b1;
// key manager registers cannot be changed once an operation starts
logic op_set;
logic init_set;
assign op_set = reg2hw.control.start.q & op_done;
assign init_set = reg2hw.control.init.q & init_done;
keymgr_cfg_en u_cfgen (
.clk_i,
.rst_ni,
.en_i(lc_i.keymgr_en),
.set_i(op_set | init_set),
.clr_i(reg2hw.control.start.q | reg2hw.control.init.q),
.out_o(hw2reg.cfgen.d)
);
/////////////////////////////////////
// Key Manager Input Construction
/////////////////////////////////////
// The various arrays of inputs for each operation
logic [2**StageWidth-1:0][AdvDataWidth-1:0] adv_matrix;
logic [2**StageWidth-1:0][IdDataWidth-1:0] id_matrix;
logic [GenDataWidth-1:0] gen_in;
// The max key version for each stage
logic [2**StageWidth-1:0][31:0] max_key_versions;
// Number of times the lfsr output fits into the inputs
localparam int AdvLfsrCopies = AdvDataWidth / 32;
localparam int IdLfsrCopies = IdDataWidth / 32;
localparam int GenLfsrCopies = GenDataWidth / 32;
// input checking - currently only MAX version, others TBD
logic key_version_good;
// 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]}};
end
// Advance to creator_root_key
logic [KeyWidth-1:0] creator_seed;
assign creator_seed = flash_i.seeds[flash_ctrl_pkg::CreatorSeedIdx];
assign adv_matrix[Creator] = AdvDataWidth'({reg2hw.rom_ext_desc,
RndCnstRevisionSeed,
otp_i.devid,
lc_i.health_state,
creator_seed});
// 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.software_binding,owner_seed});
// Advance to owner_key
assign adv_matrix[Owner] = AdvDataWidth'(reg2hw.software_binding);
// Generate Identity operation input construction
for (genvar i = KeyMgrStages; i < 2**StageWidth; i++) begin : gen_id_matrix_fill
assign id_matrix[i] = {IdLfsrCopies{lfsr[31:0]}};
end
assign id_matrix[Creator] = RndCnstCreatorIdentitySeed;
assign id_matrix[OwnerInt] = RndCnstOwnerIntIdentitySeed;
assign id_matrix[Owner] = RndCnstOwnerIdentitySeed;
// Generate output operation input construction
logic [KeyWidth-1:0] output_key;
assign output_key = (key_sel == HwKey) ? RndCnstHardOutputSeed : RndCnstSoftOutputSeed;
assign gen_in = (stage_sel == Disable) ? {GenLfsrCopies{lfsr[31:0]}} : {reg2hw.key_version,
reg2hw.salt,
output_key};
// Advance state operation input construction
for (genvar i = KeyMgrStages; i < 2**StageWidth; i++) begin : gen_key_version_fill
assign max_key_versions[i] = '0;
end
assign max_key_versions[Creator] = reg2hw.max_creator_key_ver;
assign max_key_versions[OwnerInt] = reg2hw.max_owner_int_key_ver;
assign max_key_versions[Owner] = reg2hw.max_owner_key_ver;
// General module for checking inputs
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)
);
/////////////////////////////////////
// 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
keymgr_kmac_if u_kmac_if (
.clk_i,
.rst_ni,
.prng_en_o(data_lfsr_en),
.adv_data_i(adv_matrix[stage_sel]),
.id_data_i(id_matrix[stage_sel]),
.gen_data_i(gen_in),
.inputs_invalid_i(invalid_data),
.inputs_invalid_o(kmac_input_invalid),
.adv_en_i(adv_en),
.id_en_i(id_en),
.gen_en_i(gen_en),
.done_o(kmac_done),
.data_o(kmac_data),
.kmac_data_o,
.kmac_data_i,
.entropy_i(lfsr[31:0]),
.fsm_error_o(kmac_fsm_err),
.kmac_error_o(kmac_op_err),
.cmd_error_o(kmac_cmd_err)
);
/////////////////////////////////////
// Side load key storage
/////////////////////////////////////
logic [31:0] key_entropy;
assign key_entropy = lfsr[63:32];
keymgr_sideload_key_ctrl u_sideload_ctrl (
.clk_i,
.rst_ni,
.init_i(init_done),
.entropy_i(key_entropy),
.wipe_key_i(wipe_key),
.dest_sel_i(keymgr_key_dest_e'(reg2hw.control.dest_sel)),
.key_sel_i(key_sel),
.load_key_i(load_key),
.data_valid_i(data_valid),
.key_i(kmac_key),
.data_i(kmac_data),
.prng_en_o(sideload_lfsr_en),
.aes_key_o(aes_key_o),
.hmac_key_o(hmac_key_o),
.kmac_key_o(kmac_key_o)
);
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].de = wipe_key | data_valid & (key_sel == SwKey);
assign hw2reg.sw_share1_output[i].de = wipe_key | data_valid & (key_sel == SwKey);
end
/////////////////////////////////////
// Alerts and Interrupts
/////////////////////////////////////
prim_intr_hw #(.Width(1)) u_intr_op_done (
.clk_i,
.rst_ni,
.event_intr_i (op_done),
.reg2hw_intr_enable_q_i (reg2hw.intr_enable.op_done.q),
.reg2hw_intr_test_q_i (reg2hw.intr_test.op_done.q),
.reg2hw_intr_test_qe_i (reg2hw.intr_test.op_done.qe),
.reg2hw_intr_state_q_i (reg2hw.intr_state.op_done.q),
.hw2reg_intr_state_de_o (hw2reg.intr_state.op_done.de),
.hw2reg_intr_state_d_o (hw2reg.intr_state.op_done.d),
.intr_o (intr_op_done_o)
);
logic [ErrLastPos-1:0] err_code_q;
assign hw2reg.err_code.invalid_op.d = reg2hw.err_code.invalid_op.q |
err_code[ErrInvalidOp];
assign hw2reg.err_code.invalid_cmd.d = reg2hw.err_code.invalid_cmd.q |
err_code[ErrInvalidCmd];
assign hw2reg.err_code.invalid_kmac_input.d = reg2hw.err_code.invalid_kmac_input.q |
err_code[ErrInvalidIn];
assign hw2reg.err_code.invalid_kmac_data.d = reg2hw.err_code.invalid_kmac_data.q |
err_code[ErrInvalidOut];
assign hw2reg.err_code.invalid_op.de = 1'b1;
assign hw2reg.err_code.invalid_cmd.de = 1'b1;
assign hw2reg.err_code.invalid_kmac_input.de = 1'b1;
assign hw2reg.err_code.invalid_kmac_data.de = 1'b1;
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
err_code_q <= '0;
end else begin
err_code_q <= err_code;
end
end
// interrupts are only generated on changes to avoid interrupt storms
prim_intr_hw #(.Width(1)) u_err_code (
.clk_i,
.rst_ni,
// assert interrupt whenever error code is non-zero and is different
// from its previous value
.event_intr_i (|err_code & (err_code != err_code_q)),
.reg2hw_intr_enable_q_i (reg2hw.intr_enable.err.q),
.reg2hw_intr_test_q_i (reg2hw.intr_test.err.q),
.reg2hw_intr_test_qe_i (reg2hw.intr_test.err.qe),
.reg2hw_intr_state_q_i (reg2hw.intr_state.err.q),
.hw2reg_intr_state_de_o (hw2reg.intr_state.err.de),
.hw2reg_intr_state_d_o (hw2reg.intr_state.err.d),
.intr_o (intr_err_o)
);
// There are two types of alerts
// - alerts for hardware errors, these could not have been generated by software.
// - alerts for errors that may have been generated by software.
logic fault_errs, fault_err_req_q, fault_err_req_d, fault_err_ack;
logic op_errs, op_err_req_q, op_err_req_d, op_err_ack;
assign fault_errs = err_code[ErrInvalidOut] | err_code[ErrInvalidCmd];
assign fault_err_req_d = fault_errs ? 1'b1 :
fault_err_ack ? 1'b0 : fault_err_req_q;
assign op_errs = err_code[ErrInvalidOp] | err_code[ErrInvalidIn];
assign op_err_req_d = op_errs ? 1'b1 :
op_err_ack ? 1'b0 : op_err_req_q;
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
fault_err_req_q <= '0;
op_err_req_q <= '0;
end else begin
fault_err_req_q <= fault_err_req_d;
op_err_req_q <= op_err_req_d;
end
end
logic fault_alert_test;
assign fault_alert_test = reg2hw.alert_test.fault_err.q & reg2hw.alert_test.fault_err.qe;
prim_alert_sender #(
.AsyncOn(AlertAsyncOn)
) u_fault_alert (
.clk_i,
.rst_ni,
.alert_req_i(fault_err_req_q | fault_alert_test),
.alert_ack_o(fault_err_ack),
.alert_rx_i(alert_rx_i[0]),
.alert_tx_o(alert_tx_o[0])
);
logic op_err_alert_test;
assign op_err_alert_test = reg2hw.alert_test.operation_err.q &
reg2hw.alert_test.operation_err.qe;
prim_alert_sender #(
.AsyncOn(AlertAsyncOn)
) u_op_err_alert (
.clk_i,
.rst_ni,
.alert_req_i(op_err_req_q | op_err_alert_test),
.alert_ack_o(op_err_ack),
.alert_rx_i(alert_rx_i[1]),
.alert_tx_o(alert_tx_o[1])
);
// known asserts
`ASSERT_KNOWN(TlDValidKnownO_A, tl_o.d_valid)
`ASSERT_KNOWN(TlAReadyKnownO_A, tl_o.a_ready)
`ASSERT_KNOWN(IntrKnownO_A, {intr_op_done_o, intr_err_o})
`ASSERT_KNOWN(AlertKnownO_A, alert_tx_o)
// the keys are not reset to any specific values
// TBD this may be changed depending on whether we want to support this
// mode of operation going forward.
`ASSERT_KNOWN(AesKeyKnownO_A, aes_key_o.valid)
`ASSERT_KNOWN(HmacKeyKnownO_A, hmac_key_o.valid)
`ASSERT_KNOWN(KmacKeyKnownO_A, kmac_key_o.valid)
`ASSERT_KNOWN(KmacDataKnownO_A, kmac_data_o)
endmodule // keymgr