| // 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 |
| ) ( |
| input clk_i, |
| input rst_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_data_t otp_i, |
| input flash_key_t flash_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, HardOutputKey != SoftOutputKey) |
| |
| // 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 [63:0] lfsr; |
| logic lfsr_en; |
| |
| prim_lfsr #( |
| .LfsrDw(64), |
| .StateOutDw(64) |
| ) u_lfsr ( |
| .clk_i, |
| .rst_ni, |
| .lfsr_en_i(lfsr_en), |
| .seed_en_i(1'b0), |
| .seed_i('0), |
| .entropy_i('0), // TBD, this should be hooked up to an entropy distribution pkg |
| .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 [Shares-1:0][KeyWidth-1:0] kmac_key; |
| logic op_done; |
| logic data_valid; |
| logic kmac_done; |
| logic kmac_input_invalid; |
| logic kmac_cmd_err; |
| logic kmac_fsm_err; |
| logic [Shares-1:0][KeyWidth-1:0] kmac_data; |
| logic [ErrLastPos-1:0] err_code; |
| |
| keymgr_ctrl u_ctrl ( |
| .clk_i, |
| .rst_ni, |
| .keymgr_en_i(lc_i.keymgr_en), |
| .prng_en_o(lfsr_en), |
| .entropy_i(lfsr[63:32]), // TBD, recommend directly interfacing with DRBG for keymgr_ctrl |
| .init_i(reg2hw.control.init.q), |
| .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_i.root_key), |
| .hw_sel_o(key_sel), |
| .stage_sel_o(stage_sel), |
| .load_key_o(load_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_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 = op_done; |
| assign hw2reg.op_status.de = op_done | reg2hw.control.start.q; |
| |
| |
| // working state is always visible |
| assign hw2reg.working_state.de = 1'b1; |
| |
| // TBD |
| // software q / qe have no impact on cfgen |
| // however they exist due to a reggen script limitation, this should be addressed later |
| keymgr_cfg_en u_cfgen ( |
| .clk_i, |
| .rst_ni, |
| .keymgr_en_i(lc_i.keymgr_en), |
| .set_i(op_done), |
| .clr_i(reg2hw.control.start.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 |
| assign adv_matrix[Creator] = AdvDataWidth'({reg2hw.rom_ext_desc, |
| RevisionSecret, |
| otp_i.devid, |
| lc_i.health_state, |
| flash_i.div_key}); |
| |
| // Advance to owner_intermediate_key |
| assign adv_matrix[OwnerInt] = AdvDataWidth'({reg2hw.software_binding,flash_i.owner_secret}); |
| |
| // 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] = CreatorIdentityKey; |
| assign id_matrix[OwnerInt] = OwnerIntIdentityKey; |
| assign id_matrix[Owner] = OwnerIdentityKey; |
| |
| |
| // Generate output operation input construction |
| logic [KeyWidth-1:0] output_key; |
| |
| assign output_key = (key_sel == HwKey) ? HardOutputKey : SoftOutputKey; |
| 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, |
| .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), |
| .cmd_error_o(kmac_cmd_err) |
| ); |
| |
| |
| ///////////////////////////////////// |
| // Side load key storage |
| ///////////////////////////////////// |
| keymgr_key_dest_e dest_sel; |
| logic aes_sel, hmac_sel, kmac_sel; |
| |
| assign dest_sel = keymgr_key_dest_e'(reg2hw.control.dest_sel); |
| assign aes_sel = dest_sel == Aes & key_sel == HwKey; |
| assign hmac_sel = dest_sel == Hmac & key_sel == HwKey; |
| assign kmac_sel = dest_sel == Kmac & key_sel == HwKey; |
| |
| keymgr_sideload_key u_aes_key ( |
| .clk_i, |
| .rst_ni, |
| .keymgr_en_i(lc_i.keymgr_en), |
| .set_i(data_valid & aes_sel), |
| .clr_i(lc_i.keymgr_en), // TBD, should add an option for software clear later |
| .entropy_i(lfsr[31:0]), //TBD, recommend directly interfacign with DRBG entropy for keys |
| .key_i(kmac_data), |
| .key_o(aes_key_o) |
| ); |
| |
| keymgr_sideload_key u_hmac_key ( |
| .clk_i, |
| .rst_ni, |
| .keymgr_en_i(lc_i.keymgr_en), |
| .set_i(data_valid & hmac_sel), |
| .clr_i(lc_i.keymgr_en), // TBD, should add an option for software clear later |
| .entropy_i(lfsr[31:0]), |
| .key_i(kmac_data), |
| .key_o(hmac_key_o) |
| ); |
| |
| keymgr_sideload_key u_kmac_key ( |
| .clk_i, |
| .rst_ni, |
| .keymgr_en_i(lc_i.keymgr_en), |
| .set_i(load_key | (data_valid & kmac_sel)), |
| .clr_i(~lc_i.keymgr_en), // TBD, should add an option for software clear later |
| .entropy_i(lfsr[31:0]), |
| .key_i(load_key ? kmac_key : kmac_data), |
| .key_o(kmac_key_o) |
| ); |
| |
| for (genvar i = 0; i < 8; i++) begin : gen_sw_assigns |
| assign hw2reg.sw_share0_output[i].d = kmac_data[0][i*32 +: 32]; |
| assign hw2reg.sw_share1_output[i].d = kmac_data[1][i*32 +: 32]; |
| assign hw2reg.sw_share0_output[i].de = data_valid & (key_sel == SwKey); |
| assign hw2reg.sw_share1_output[i].de = data_valid & (key_sel == SwKey); |
| end |
| |
| |
| ///////////////////////////////////// |
| // Alerts and Interrupts |
| ///////////////////////////////////// |
| |
| prim_intr_hw #(.Width(1)) u_intr_op_done ( |
| .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 = err_code[ErrInvalidOp]; |
| assign hw2reg.err_code.invalid_cmd.d = err_code[ErrInvalidCmd]; |
| assign hw2reg.err_code.invalid_kmac_input.d = err_code[ErrInvalidIn]; |
| assign hw2reg.err_code.invalid_kmac_data.d = 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 ( |
| .event_intr_i (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) |
| ); |
| |
| // alerts are generated whenever there is a persisting error status |
| prim_alert_sender #( |
| .AsyncOn(AlertAsyncOn) |
| ) u_err_alert ( |
| .clk_i, |
| .rst_ni, |
| .alert_i(|reg2hw.err_code), |
| .alert_rx_i(alert_rx_i), |
| .alert_tx_o(alert_tx_o) |
| ); |
| |
| ///////////////////////////////////// |
| // Assertions |
| ///////////////////////////////////// |
| |
| // Only 1 entity should be trying to use the secret kmac key input |
| `ASSERT(KmacKeyLoadExclusive_a, $onehot0({load_key, data_valid & kmac_sel})) |
| |
| endmodule // keymgr |