blob: 3ed1c0c26a926ec8fbecf44d03fe4ddcd3cc0e7d [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 rand_perm_t RndCnstRandPerm = RndCnstRandPermDefault,
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,
parameter seed_t RndCnstNoneSeed = RndCnstNoneSeedDefault,
parameter seed_t RndCnstAesSeed = RndCnstAesSeedDefault,
parameter seed_t RndCnstHmacSeed = RndCnstHmacSeedDefault,
parameter seed_t RndCnstKmacSeed = RndCnstKmacSeedDefault
) (
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_ctrl_pkg::lc_tx_t lc_keymgr_en_i,
input lc_ctrl_pkg::lc_keymgr_div_t lc_keymgr_div_i,
input otp_ctrl_pkg::otp_keymgr_key_t otp_key_i,
input otp_ctrl_part_pkg::otp_hw_cfg_t otp_hw_cfg_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,
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
);
/////////////////////////////////////
// Synchronize lc_ctrl control inputs
// Data inputs are not synchronized and assumed quasi-static
/////////////////////////////////////
lc_ctrl_pkg::lc_tx_t [KeyMgrEnLast-1:0] lc_keymgr_en;
prim_lc_sync #(
.NumCopies(int'(KeyMgrEnLast))
) u_lc_keymgr_en_sync (
.clk_i,
.rst_ni,
.lc_en_i(lc_keymgr_en_i),
.lc_en_o(lc_keymgr_en)
);
/////////////////////////////////////
// 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)
);
logic [Shares-1:0][RandWidth-1:0] ctrl_rand;
logic [Shares-1:0][RandWidth-1:0] data_rand;
assign ctrl_rand[0] = lfsr[63:32];
assign ctrl_rand[1] = perm_data(lfsr[31:0], RndCnstRandPerm);
assign data_rand[0] = lfsr[31:0];
assign data_rand[1] = perm_data(lfsr[63:32], RndCnstRandPerm);
/////////////////////////////////////
// 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;
hw_key_req_t kmac_key;
logic op_done;
logic init;
logic data_valid;
logic data_en;
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;
logic sw_binding_unlock;
keymgr_ctrl u_ctrl (
.clk_i,
.rst_ni,
.en_i(lc_keymgr_en[KeyMgrEnCtrl] == lc_ctrl_pkg::On),
.prng_reseed_req_o(reseed_req),
.prng_reseed_ack_i(reseed_ack),
.prng_en_o(ctrl_lfsr_en),
.entropy_i(ctrl_rand),
.op_i(keymgr_ops_e'(reg2hw.control.operation.q)),
.op_start_i(reg2hw.control.start.q),
.op_done_o(op_done),
.init_o(init),
.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),
.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;
// 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
keymgr_cfg_en #(
.NonInitClr(1'b1) // clear has effect even when non-init
) u_cfgen (
.clk_i,
.rst_ni,
.init_i(init),
.en_i(lc_keymgr_en[KeyMgrEnCfgEn] == lc_ctrl_pkg::On),
.set_i(reg2hw.control.start.q & op_done),
.clr_i(reg2hw.control.start.q),
.out_o(hw2reg.cfgen.d)
);
logic sw_binding_set;
logic sw_binding_clr;
logic sw_binding_en;
// set on a successful advance
assign sw_binding_set = reg2hw.control.operation.q == OpAdvance &
sw_binding_unlock;
// this is w0c
assign sw_binding_clr = reg2hw.sw_binding_en.qe & ~reg2hw.sw_binding_en.q;
// software clears the enable
// hardware restores it upon successful advance
keymgr_cfg_en #(
.NonInitClr(1'b0) // clear has no effect until init
) u_sw_binding_en (
.clk_i,
.rst_ni,
.init_i(init),
.en_i(lc_keymgr_en[KeyMgrEnSwBindingEn] == lc_ctrl_pkg::On),
.set_i(sw_binding_set),
.clr_i(sw_binding_clr),
.out_o(sw_binding_en)
);
assign hw2reg.sw_binding_en.d = sw_binding_en & 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] adv_dvalid;
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
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{data_rand[0]}};
assign adv_dvalid[i] = 1'b1;
end
// Advance to creator_root_key
// The values coming from otp_ctrl / lc_ctrl are treat as quasi-static for CDC purposes
logic [KeyWidth-1:0] creator_seed;
assign creator_seed = flash_i.seeds[flash_ctrl_pkg::CreatorSeedIdx];
assign adv_matrix[Creator] = AdvDataWidth'({reg2hw.sw_binding,
RndCnstRevisionSeed,
otp_hw_cfg_i.data.device_id,
HealthStateWidth'(lc_keymgr_div_i),
creator_seed});
logic unused_otp_bits;
assign unused_otp_bits = ^otp_hw_cfg_i;
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_dvalid[Owner] = 1'b1;
// Generate Identity operation input construction
for (genvar i = KeyMgrStages; i < 2**StageWidth; i++) begin : gen_id_matrix_fill
assign id_matrix[i] = {IdLfsrCopies{data_rand[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;
keymgr_key_dest_e cipher_sel;
logic [KeyWidth-1:0] cipher_seed;
assign cipher_sel = keymgr_key_dest_e'(reg2hw.control.dest_sel);
assign cipher_seed = cipher_sel == Aes ? RndCnstAesSeed :
cipher_sel == Hmac ? RndCnstHmacSeed :
cipher_sel == Kmac ? RndCnstKmacSeed : RndCnstNoneSeed;
assign output_key = (key_sel == HwKey) ? RndCnstHardOutputSeed : RndCnstSoftOutputSeed;
assign gen_in = (stage_sel == Disable) ? {GenLfsrCopies{lfsr[31:0]}} : {reg2hw.key_version,
reg2hw.salt,
cipher_seed,
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
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),
.creator_seed_i(creator_seed),
.owner_seed_i(owner_seed),
.key_i(kmac_key_o),
.devid_i(otp_hw_cfg_i.data.device_id),
.health_state_i(HealthStateWidth'(lc_keymgr_div_i)),
.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)
);
/////////////////////////////////////
// KMAC Control
/////////////////////////////////////
logic [3:0] invalid_data;
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,
.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(data_rand),
.fsm_error_o(kmac_fsm_err),
.kmac_error_o(kmac_op_err),
.cmd_error_o(kmac_cmd_err)
);
/////////////////////////////////////
// Side load key storage
/////////////////////////////////////
keymgr_sideload_key_ctrl u_sideload_ctrl (
.clk_i,
.rst_ni,
.init_i(init),
.entropy_i(data_rand),
.clr_key_i(reg2hw.sideload_clear.q),
.wipe_key_i(wipe_key),
.dest_sel_i(cipher_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),
.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 = ~data_en | wipe_key ? data_rand[0] :
kmac_data[0][i*32 +: 32];
assign hw2reg.sw_share1_output[i].d = ~data_en | wipe_key ? data_rand[1] :
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.q),
.reg2hw_intr_test_q_i (reg2hw.intr_test.q),
.reg2hw_intr_test_qe_i (reg2hw.intr_test.qe),
.reg2hw_intr_state_q_i (reg2hw.intr_state.q),
.hw2reg_intr_state_de_o (hw2reg.intr_state.de),
.hw2reg_intr_state_d_o (hw2reg.intr_state.d),
.intr_o (intr_op_done_o)
);
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;
// 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.fatal_fault_err.q &
reg2hw.alert_test.fatal_fault_err.qe;
prim_alert_sender #(
.AsyncOn(AlertAsyncOn),
.IsFatal(1)
) u_fault_alert (
.clk_i,
.rst_ni,
.alert_test_i(fault_alert_test),
.alert_req_i(fault_err_req_q),
.alert_ack_o(fault_err_ack),
.alert_state_o(),
.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.recov_operation_err.q &
reg2hw.alert_test.recov_operation_err.qe;
prim_alert_sender #(
.AsyncOn(AlertAsyncOn),
.IsFatal(0)
) u_op_err_alert (
.clk_i,
.rst_ni,
.alert_test_i(op_err_alert_test),
.alert_req_i(op_err_req_q),
.alert_ack_o(op_err_ack),
.alert_state_o(),
.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)
`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