| // Copyright lowRISC contributors. |
| // Licensed under the Apache License, Version 2.0, see LICENSE for details. |
| // SPDX-License-Identifier: Apache-2.0 |
| // |
| // AES core implementation |
| |
| `include "prim_assert.sv" |
| |
| module aes_core |
| import aes_pkg::*; |
| import aes_reg_pkg::*; |
| #( |
| parameter bit AES192Enable = 1, |
| parameter bit SecMasking = 1, |
| parameter sbox_impl_e SecSBoxImpl = SBoxImplDom, |
| parameter int unsigned SecStartTriggerDelay = 0, |
| parameter bit SecAllowForcingMasks = 0, |
| parameter bit SecSkipPRNGReseeding = 0, |
| parameter int unsigned EntropyWidth = edn_pkg::ENDPOINT_BUS_WIDTH, |
| |
| localparam int NumShares = SecMasking ? 2 : 1, // derived parameter |
| |
| parameter clearing_lfsr_seed_t RndCnstClearingLfsrSeed = RndCnstClearingLfsrSeedDefault, |
| parameter clearing_lfsr_perm_t RndCnstClearingLfsrPerm = RndCnstClearingLfsrPermDefault, |
| parameter clearing_lfsr_perm_t RndCnstClearingSharePerm = RndCnstClearingSharePermDefault, |
| parameter masking_lfsr_seed_t RndCnstMaskingLfsrSeed = RndCnstMaskingLfsrSeedDefault, |
| parameter masking_lfsr_perm_t RndCnstMaskingLfsrPerm = RndCnstMaskingLfsrPermDefault |
| ) ( |
| input logic clk_i, |
| input logic rst_ni, |
| input logic rst_shadowed_ni, |
| |
| // Entropy request interfaces for clearing and masking PRNGs |
| output logic entropy_clearing_req_o, |
| input logic entropy_clearing_ack_i, |
| input logic [EntropyWidth-1:0] entropy_clearing_i, |
| output logic entropy_masking_req_o, |
| input logic entropy_masking_ack_i, |
| input logic [EntropyWidth-1:0] entropy_masking_i, |
| |
| // Key manager (keymgr) key sideload interface |
| input keymgr_pkg::hw_key_req_t keymgr_key_i, |
| |
| // Life cycle |
| input lc_ctrl_pkg::lc_tx_t lc_escalate_en_i, |
| |
| // Alerts |
| input logic shadowed_storage_err_i, |
| input logic shadowed_update_err_i, |
| input logic intg_err_alert_i, |
| output logic alert_recov_o, |
| output logic alert_fatal_o, |
| |
| // Bus Interface |
| input aes_reg2hw_t reg2hw, |
| output aes_hw2reg_t hw2reg |
| ); |
| |
| // Signals |
| logic ctrl_qe; |
| logic ctrl_we; |
| logic ctrl_phase; |
| aes_op_e aes_op_q; |
| aes_mode_e aes_mode_q; |
| ciph_op_e cipher_op; |
| ciph_op_e cipher_op_buf; |
| key_len_e key_len_q; |
| logic sideload_q; |
| prs_rate_e prng_reseed_rate_q; |
| logic manual_operation_q; |
| logic ctrl_reg_err_update; |
| logic ctrl_reg_err_storage; |
| logic ctrl_err_update; |
| logic ctrl_err_storage; |
| logic ctrl_err_storage_d; |
| logic ctrl_err_storage_q; |
| logic ctrl_alert; |
| logic key_touch_forces_reseed; |
| logic force_masks; |
| logic mux_sel_err; |
| logic sp_enc_err_d, sp_enc_err_q; |
| logic clear_on_fatal; |
| |
| logic [3:0][3:0][7:0] state_in; |
| logic [SISelWidth-1:0] state_in_sel_raw; |
| si_sel_e state_in_sel_ctrl; |
| si_sel_e state_in_sel; |
| logic state_in_sel_err; |
| logic [3:0][3:0][7:0] add_state_in; |
| logic [AddSISelWidth-1:0] add_state_in_sel_raw; |
| add_si_sel_e add_state_in_sel_ctrl; |
| add_si_sel_e add_state_in_sel; |
| logic add_state_in_sel_err; |
| |
| logic [3:0][3:0][7:0] state_mask; |
| logic [3:0][3:0][7:0] state_init [NumShares]; |
| logic [3:0][3:0][7:0] state_done [NumShares]; |
| logic [3:0][3:0][7:0] state_out; |
| |
| logic [NumRegsKey-1:0][31:0] key_init [NumSharesKey]; |
| logic [NumRegsKey-1:0] key_init_qe [NumSharesKey]; |
| logic [NumRegsKey-1:0] key_init_qe_buf [NumSharesKey]; |
| logic [NumRegsKey-1:0][31:0] key_init_d [NumSharesKey]; |
| logic [NumRegsKey-1:0][31:0] key_init_q [NumSharesKey]; |
| logic [NumRegsKey-1:0][31:0] key_init_cipher [NumShares]; |
| sp2v_e [NumRegsKey-1:0] key_init_we_ctrl [NumSharesKey]; |
| sp2v_e [NumRegsKey-1:0] key_init_we [NumSharesKey]; |
| logic [KeyInitSelWidth-1:0] key_init_sel_raw; |
| key_init_sel_e key_init_sel_ctrl; |
| key_init_sel_e key_init_sel; |
| logic key_init_sel_err; |
| logic [NumRegsKey-1:0][31:0] key_sideload [NumSharesKey]; |
| |
| logic [NumRegsIv-1:0][31:0] iv; |
| logic [NumRegsIv-1:0] iv_qe; |
| logic [NumRegsIv-1:0] iv_qe_buf; |
| logic [NumSlicesCtr-1:0][SliceSizeCtr-1:0] iv_d; |
| logic [NumSlicesCtr-1:0][SliceSizeCtr-1:0] iv_q; |
| sp2v_e [NumSlicesCtr-1:0] iv_we_ctrl; |
| sp2v_e [NumSlicesCtr-1:0] iv_we; |
| logic [IVSelWidth-1:0] iv_sel_raw; |
| iv_sel_e iv_sel_ctrl; |
| iv_sel_e iv_sel; |
| logic iv_sel_err; |
| |
| logic [NumSlicesCtr-1:0][SliceSizeCtr-1:0] ctr; |
| sp2v_e [NumSlicesCtr-1:0] ctr_we; |
| sp2v_e ctr_incr; |
| sp2v_e ctr_ready; |
| logic ctr_alert; |
| |
| logic [NumRegsData-1:0][31:0] data_in_prev_d; |
| logic [NumRegsData-1:0][31:0] data_in_prev_q; |
| sp2v_e data_in_prev_we_ctrl; |
| sp2v_e data_in_prev_we; |
| logic [DIPSelWidth-1:0] data_in_prev_sel_raw; |
| dip_sel_e data_in_prev_sel_ctrl; |
| dip_sel_e data_in_prev_sel; |
| logic data_in_prev_sel_err; |
| |
| logic [NumRegsData-1:0][31:0] data_in; |
| logic [NumRegsData-1:0] data_in_qe; |
| logic [NumRegsData-1:0] data_in_qe_buf; |
| logic data_in_we; |
| |
| logic [3:0][3:0][7:0] add_state_out; |
| logic [AddSOSelWidth-1:0] add_state_out_sel_raw; |
| add_so_sel_e add_state_out_sel_ctrl; |
| add_so_sel_e add_state_out_sel; |
| logic add_state_out_sel_err; |
| |
| logic [NumRegsData-1:0][31:0] data_out_d; |
| logic [NumRegsData-1:0][31:0] data_out_q; |
| sp2v_e data_out_we_ctrl; |
| sp2v_e data_out_we; |
| logic [NumRegsData-1:0] data_out_re; |
| logic [NumRegsData-1:0] data_out_re_buf; |
| |
| sp2v_e cipher_in_valid; |
| sp2v_e cipher_in_ready; |
| sp2v_e cipher_out_valid; |
| sp2v_e cipher_out_ready; |
| sp2v_e cipher_crypt; |
| sp2v_e cipher_crypt_busy; |
| sp2v_e cipher_dec_key_gen; |
| sp2v_e cipher_dec_key_gen_busy; |
| logic cipher_prng_reseed; |
| logic cipher_prng_reseed_busy; |
| logic cipher_key_clear; |
| logic cipher_key_clear_busy; |
| logic cipher_data_out_clear; |
| logic cipher_data_out_clear_busy; |
| logic cipher_alert; |
| |
| // Pseudo-random data for clearing purposes |
| logic [WidthPRDClearing-1:0] cipher_prd_clearing [NumShares]; |
| logic [WidthPRDClearing-1:0] prd_clearing [NumSharesKey]; |
| logic prd_clearing_upd_req; |
| logic prd_clearing_upd_ack; |
| logic prd_clearing_rsd_req; |
| logic prd_clearing_rsd_ack; |
| logic [127:0] prd_clearing_128 [NumShares]; |
| logic [255:0] prd_clearing_256 [NumSharesKey]; |
| |
| // Unused signals |
| logic [NumRegsData-1:0][31:0] unused_data_out_q; |
| |
| // The clearing PRNG provides pseudo-random data for register clearing purposes. |
| aes_prng_clearing #( |
| .Width ( WidthPRDClearing ), |
| .EntropyWidth ( EntropyWidth ), |
| .SecSkipPRNGReseeding ( SecSkipPRNGReseeding ), |
| .RndCnstLfsrSeed ( RndCnstClearingLfsrSeed ), |
| .RndCnstLfsrPerm ( RndCnstClearingLfsrPerm ), |
| .RndCnstSharePerm ( RndCnstClearingSharePerm ) |
| ) u_aes_prng_clearing ( |
| .clk_i ( clk_i ), |
| .rst_ni ( rst_ni ), |
| |
| .data_req_i ( prd_clearing_upd_req ), |
| .data_ack_o ( prd_clearing_upd_ack ), |
| .data_o ( prd_clearing ), |
| .reseed_req_i ( prd_clearing_rsd_req ), |
| .reseed_ack_o ( prd_clearing_rsd_ack ), |
| |
| .entropy_req_o ( entropy_clearing_req_o ), |
| .entropy_ack_i ( entropy_clearing_ack_i ), |
| .entropy_i ( entropy_clearing_i ) |
| ); |
| |
| // Generate clearing signals of appropriate widths. |
| // Different shares need to be cleared with different pseudo-random data. |
| for (genvar s = 0; s < NumShares; s++) begin : gen_prd_clearing_128_shares |
| for (genvar c = 0; c < NumChunksPRDClearing128; c++) begin : gen_prd_clearing_128 |
| assign prd_clearing_128[s][c * WidthPRDClearing +: WidthPRDClearing] = prd_clearing[s]; |
| end |
| end |
| // The initial key is always provided in two shares. The two shares of the initial key register |
| // need to be cleared with different pseudo-random data. |
| for (genvar s = 0; s < NumSharesKey; s++) begin : gen_prd_clearing_256_shares |
| for (genvar c = 0; c < NumChunksPRDClearing256; c++) begin : gen_prd_clearing_256 |
| assign prd_clearing_256[s][c * WidthPRDClearing +: WidthPRDClearing] = prd_clearing[s]; |
| end |
| end |
| |
| //////////// |
| // Inputs // |
| //////////// |
| |
| always_comb begin : key_init_get |
| for (int i = 0; i < NumRegsKey; i++) begin |
| key_init[0][i] = reg2hw.key_share0[i].q; |
| key_init_qe[0][i] = reg2hw.key_share0[i].qe; |
| key_init[1][i] = reg2hw.key_share1[i].q; |
| key_init_qe[1][i] = reg2hw.key_share1[i].qe; |
| end |
| end |
| |
| prim_sec_anchor_buf #( |
| .Width ( NumSharesKey * NumRegsKey ) |
| ) u_prim_buf_key_init_qe ( |
| .in_i ( {key_init_qe[1], key_init_qe[0]} ), |
| .out_o ( {key_init_qe_buf[1], key_init_qe_buf[0]} ) |
| ); |
| |
| always_comb begin : key_sideload_get |
| for (int s = 0; s < NumSharesKey; s++) begin |
| for (int i = 0; i < NumRegsKey; i++) begin |
| key_sideload[s][i] = keymgr_key_i.key[s][i * 32 +: 32]; |
| end |
| end |
| end |
| |
| always_comb begin : iv_get |
| for (int i = 0; i < NumRegsIv; i++) begin |
| iv[i] = reg2hw.iv[i].q; |
| iv_qe[i] = reg2hw.iv[i].qe; |
| end |
| end |
| |
| prim_sec_anchor_buf #( |
| .Width ( NumRegsIv ) |
| ) u_prim_buf_iv_qe ( |
| .in_i ( iv_qe ), |
| .out_o ( iv_qe_buf ) |
| ); |
| |
| always_comb begin : data_in_get |
| for (int i = 0; i < NumRegsData; i++) begin |
| data_in[i] = reg2hw.data_in[i].q; |
| data_in_qe[i] = reg2hw.data_in[i].qe; |
| end |
| end |
| |
| prim_sec_anchor_buf #( |
| .Width ( NumRegsData ) |
| ) u_prim_buf_data_in_qe ( |
| .in_i ( data_in_qe ), |
| .out_o ( data_in_qe_buf ) |
| ); |
| |
| always_comb begin : data_out_get |
| for (int i = 0; i < NumRegsData; i++) begin |
| // data_out is actually hwo, but we need hrw for hwre |
| unused_data_out_q[i] = reg2hw.data_out[i].q; |
| data_out_re[i] = reg2hw.data_out[i].re; |
| end |
| end |
| |
| prim_sec_anchor_buf #( |
| .Width ( NumRegsData ) |
| ) u_prim_buf_data_out_re ( |
| .in_i ( data_out_re ), |
| .out_o ( data_out_re_buf ) |
| ); |
| |
| ////////////////////// |
| // Key, IV and Data // |
| ////////////////////// |
| |
| // SEC_CM: KEY.SEC_WIPE |
| // SEC_CM: KEY.SIDELOAD |
| // Initial Key registers |
| always_comb begin : key_init_mux |
| unique case (key_init_sel) |
| KEY_INIT_INPUT: key_init_d = key_init; |
| KEY_INIT_KEYMGR: key_init_d = key_sideload; |
| KEY_INIT_CLEAR: key_init_d = prd_clearing_256; |
| default: key_init_d = prd_clearing_256; |
| endcase |
| end |
| |
| always_ff @(posedge clk_i or negedge rst_ni) begin : key_init_reg |
| if (!rst_ni) begin |
| key_init_q <= '{default: '0}; |
| end else begin |
| for (int s = 0; s < NumSharesKey; s++) begin |
| for (int i = 0; i < NumRegsKey; i++) begin |
| if (key_init_we[s][i] == SP2V_HIGH) begin |
| key_init_q[s][i] <= key_init_d[s][i]; |
| end |
| end |
| end |
| end |
| end |
| |
| // SEC_CM: IV.CONFIG.SEC_WIPE |
| // IV registers |
| always_comb begin : iv_mux |
| unique case (iv_sel) |
| IV_INPUT: iv_d = iv; |
| IV_DATA_OUT: iv_d = data_out_d; |
| IV_DATA_OUT_RAW: iv_d = aes_transpose(state_out); |
| IV_DATA_IN_PREV: iv_d = data_in_prev_q; |
| IV_CTR: iv_d = ctr; |
| IV_CLEAR: iv_d = prd_clearing_128[0]; |
| default: iv_d = prd_clearing_128[0]; |
| endcase |
| end |
| |
| always_ff @(posedge clk_i or negedge rst_ni) begin : iv_reg |
| if (!rst_ni) begin |
| iv_q <= '0; |
| end else begin |
| for (int i = 0; i < NumSlicesCtr; i++) begin |
| if (iv_we[i] == SP2V_HIGH) begin |
| iv_q[i] <= iv_d[i]; |
| end |
| end |
| end |
| end |
| |
| // SEC_CM: DATA_REG.SEC_WIPE |
| // Previous input data register |
| always_comb begin : data_in_prev_mux |
| unique case (data_in_prev_sel) |
| DIP_DATA_IN: data_in_prev_d = data_in; |
| DIP_CLEAR: data_in_prev_d = prd_clearing_128[0]; |
| default: data_in_prev_d = prd_clearing_128[0]; |
| endcase |
| end |
| |
| always_ff @(posedge clk_i or negedge rst_ni) begin : data_in_prev_reg |
| if (!rst_ni) begin |
| data_in_prev_q <= '0; |
| end else if (data_in_prev_we == SP2V_HIGH) begin |
| data_in_prev_q <= data_in_prev_d; |
| end |
| end |
| |
| ///////////// |
| // Counter // |
| ///////////// |
| |
| aes_ctr u_aes_ctr ( |
| .clk_i ( clk_i ), |
| .rst_ni ( rst_ni ), |
| |
| .incr_i ( ctr_incr ), |
| .ready_o ( ctr_ready ), |
| .alert_o ( ctr_alert ), |
| |
| .ctr_i ( iv_q ), |
| .ctr_o ( ctr ), |
| .ctr_we_o ( ctr_we ) |
| ); |
| |
| ///////////////// |
| // Cipher Core // |
| ///////////////// |
| |
| // Cipher core operation |
| assign cipher_op = (aes_mode_q == AES_ECB && aes_op_q == AES_ENC) ? CIPH_FWD : |
| (aes_mode_q == AES_ECB && aes_op_q == AES_DEC) ? CIPH_INV : |
| (aes_mode_q == AES_CBC && aes_op_q == AES_ENC) ? CIPH_FWD : |
| (aes_mode_q == AES_CBC && aes_op_q == AES_DEC) ? CIPH_INV : |
| (aes_mode_q == AES_CFB) ? CIPH_FWD : |
| (aes_mode_q == AES_OFB) ? CIPH_FWD : |
| (aes_mode_q == AES_CTR) ? CIPH_FWD : CIPH_FWD; |
| |
| // This primitive is used to place a size-only constraint on the |
| // buffers to act as a synthesis optimization barrier. |
| logic [$bits(ciph_op_e)-1:0] cipher_op_raw; |
| prim_buf #( |
| .Width($bits(ciph_op_e)) |
| ) u_prim_buf_op ( |
| .in_i(cipher_op), |
| .out_o(cipher_op_raw) |
| ); |
| assign cipher_op_buf = ciph_op_e'(cipher_op_raw); |
| |
| for (genvar s = 0; s < NumShares; s++) begin : gen_cipher_prd_clearing |
| assign cipher_prd_clearing[s] = prd_clearing[s]; |
| end |
| |
| // Convert input data/IV to state format (every word corresponds to one state column). |
| // Mux for state input |
| always_comb begin : state_in_mux |
| unique case (state_in_sel) |
| SI_ZERO: state_in = '0; |
| SI_DATA: state_in = aes_transpose(data_in); |
| default: state_in = '0; |
| endcase |
| end |
| |
| // Mux for addition to state input |
| always_comb begin : add_state_in_mux |
| unique case (add_state_in_sel) |
| ADD_SI_ZERO: add_state_in = '0; |
| ADD_SI_IV: add_state_in = aes_transpose(iv_q); |
| default: add_state_in = '0; |
| endcase |
| end |
| |
| if (!SecMasking) begin : gen_state_init_unmasked |
| assign state_init[0] = state_in ^ add_state_in; |
| |
| logic [3:0][3:0][7:0] unused_state_mask; |
| assign unused_state_mask = state_mask; |
| |
| end else begin : gen_state_init_masked |
| assign state_init[0] = (state_in ^ add_state_in) ^ state_mask; // Masked data share |
| assign state_init[1] = state_mask; // Mask share |
| end |
| |
| if (!SecMasking) begin : gen_key_init_unmasked |
| // Combine the two key shares for the unmasked cipher core. This causes SCA leakage of the key |
| // and thus should be avoided. |
| assign key_init_cipher[0] = key_init_q[0] ^ key_init_q[1]; |
| |
| end else begin : gen_key_init_masked |
| // Forward the masked key share and the mask share to the masked cipher core. |
| assign key_init_cipher = key_init_q; |
| end |
| |
| // SEC_CM: KEY.MASKING |
| // Cipher core |
| aes_cipher_core #( |
| .AES192Enable ( AES192Enable ), |
| .SecMasking ( SecMasking ), |
| .SecSBoxImpl ( SecSBoxImpl ), |
| .SecAllowForcingMasks ( SecAllowForcingMasks ), |
| .SecSkipPRNGReseeding ( SecSkipPRNGReseeding ), |
| .RndCnstMaskingLfsrSeed ( RndCnstMaskingLfsrSeed ), |
| .RndCnstMaskingLfsrPerm ( RndCnstMaskingLfsrPerm ) |
| ) u_aes_cipher_core ( |
| .clk_i ( clk_i ), |
| .rst_ni ( rst_ni ), |
| |
| .in_valid_i ( cipher_in_valid ), |
| .in_ready_o ( cipher_in_ready ), |
| |
| .out_valid_o ( cipher_out_valid ), |
| .out_ready_i ( cipher_out_ready ), |
| |
| .cfg_valid_i ( ~ctrl_err_storage ), // Used for gating assertions only. |
| .op_i ( cipher_op_buf ), |
| .key_len_i ( key_len_q ), |
| .crypt_i ( cipher_crypt ), |
| .crypt_o ( cipher_crypt_busy ), |
| .dec_key_gen_i ( cipher_dec_key_gen ), |
| .dec_key_gen_o ( cipher_dec_key_gen_busy ), |
| .prng_reseed_i ( cipher_prng_reseed ), |
| .prng_reseed_o ( cipher_prng_reseed_busy ), |
| .key_clear_i ( cipher_key_clear ), |
| .key_clear_o ( cipher_key_clear_busy ), |
| .data_out_clear_i ( cipher_data_out_clear ), |
| .data_out_clear_o ( cipher_data_out_clear_busy ), |
| .alert_fatal_i ( alert_fatal_o ), |
| .alert_o ( cipher_alert ), |
| |
| .prd_clearing_i ( cipher_prd_clearing ), |
| |
| .force_masks_i ( force_masks ), |
| .data_in_mask_o ( state_mask ), |
| .entropy_req_o ( entropy_masking_req_o ), |
| .entropy_ack_i ( entropy_masking_ack_i ), |
| .entropy_i ( entropy_masking_i ), |
| |
| .state_init_i ( state_init ), |
| .key_init_i ( key_init_cipher ), |
| .state_o ( state_done ) |
| ); |
| |
| if (!SecMasking) begin : gen_state_out_unmasked |
| assign state_out = state_done[0]; |
| end else begin : gen_state_out_masked |
| // Unmask the cipher core output. This might get reworked in the future when masking the |
| // counter and feedback path through the IV regs. |
| |
| // Only unmask the final cipher core output. Unmasking intermediate output data causes |
| // additional SCA leakage and thus has to be avoided. Forward PRD instead of a determinsitic |
| // value to avoid leaking the cipher core output when it becomes valid. |
| logic [3:0][3:0][7:0] state_done_muxed [NumShares]; |
| for (genvar s = 0; s < NumShares; s++) begin : gen_state_done_muxed |
| assign state_done_muxed[s] = |
| (cipher_out_valid == SP2V_HIGH) ? state_done[s] : prd_clearing_128[s]; |
| end |
| |
| // Avoid aggressive synthesis optimizations. |
| logic [3:0][3:0][7:0] state_done_buf [NumShares]; |
| prim_buf #( |
| .Width ( 128 * NumShares ) |
| ) u_prim_state_done_muxed ( |
| .in_i ( {state_done_muxed[1], state_done_muxed[0]} ), |
| .out_o ( {state_done_buf[1], state_done_buf[0]} ) |
| ); |
| |
| // Unmask the cipher core output. |
| assign state_out = state_done_buf[0] ^ state_done_buf[1]; |
| end |
| |
| // Mux for addition to state output |
| always_comb begin : add_state_out_mux |
| unique case (add_state_out_sel) |
| ADD_SO_ZERO: add_state_out = '0; |
| ADD_SO_IV: add_state_out = aes_transpose(iv_q); |
| ADD_SO_DIP: add_state_out = aes_transpose(data_in_prev_q); |
| default: add_state_out = '0; |
| endcase |
| end |
| |
| // Convert output state to output data format (every column corresponds to one output word). |
| assign data_out_d = aes_transpose(state_out ^ add_state_out); |
| |
| ////////////////////// |
| // Control Register // |
| ////////////////////// |
| |
| // Shadowed register primitve |
| aes_ctrl_reg_shadowed #( |
| .AES192Enable ( AES192Enable ) |
| ) u_ctrl_reg_shadowed ( |
| .clk_i ( clk_i ), |
| .rst_ni ( rst_ni ), |
| .rst_shadowed_ni ( rst_shadowed_ni ), |
| .qe_o ( ctrl_qe ), |
| .we_i ( ctrl_we ), |
| .phase_o ( ctrl_phase ), |
| .operation_o ( aes_op_q ), |
| .mode_o ( aes_mode_q ), |
| .key_len_o ( key_len_q ), |
| .sideload_o ( sideload_q ), |
| .prng_reseed_rate_o ( prng_reseed_rate_q ), |
| .manual_operation_o ( manual_operation_q ), |
| .err_update_o ( ctrl_reg_err_update ), |
| .err_storage_o ( ctrl_reg_err_storage ), |
| .reg2hw_ctrl_i ( reg2hw.ctrl_shadowed ), |
| .hw2reg_ctrl_o ( hw2reg.ctrl_shadowed ) |
| ); |
| |
| // Auxiliary control register signals |
| assign key_touch_forces_reseed = reg2hw.ctrl_aux_shadowed.key_touch_forces_reseed.q; |
| assign force_masks = reg2hw.ctrl_aux_shadowed.force_masks.q; |
| |
| ///////////// |
| // Control // |
| ///////////// |
| |
| // Control |
| aes_control #( |
| .SecMasking ( SecMasking ), |
| .SecStartTriggerDelay ( SecStartTriggerDelay ) |
| ) u_aes_control ( |
| .clk_i ( clk_i ), |
| .rst_ni ( rst_ni ), |
| |
| .ctrl_qe_i ( ctrl_qe ), |
| .ctrl_we_o ( ctrl_we ), |
| .ctrl_phase_i ( ctrl_phase ), |
| .ctrl_err_storage_i ( ctrl_err_storage ), |
| .op_i ( aes_op_q ), |
| .mode_i ( aes_mode_q ), |
| .cipher_op_i ( cipher_op_buf ), |
| .sideload_i ( sideload_q ), |
| .prng_reseed_rate_i ( prng_reseed_rate_q ), |
| .manual_operation_i ( manual_operation_q ), |
| .key_touch_forces_reseed_i ( key_touch_forces_reseed ), |
| .start_i ( reg2hw.trigger.start.q ), |
| .key_iv_data_in_clear_i ( reg2hw.trigger.key_iv_data_in_clear.q ), |
| .data_out_clear_i ( reg2hw.trigger.data_out_clear.q ), |
| .prng_reseed_i ( reg2hw.trigger.prng_reseed.q ), |
| .mux_sel_err_i ( mux_sel_err ), |
| .sp_enc_err_i ( sp_enc_err_q ), |
| .lc_escalate_en_i ( lc_escalate_en_i ), |
| .alert_fatal_i ( alert_fatal_o ), |
| .alert_o ( ctrl_alert ), |
| |
| .key_sideload_valid_i ( keymgr_key_i.valid ), |
| .key_init_qe_i ( key_init_qe_buf ), |
| .iv_qe_i ( iv_qe_buf ), |
| .data_in_qe_i ( data_in_qe_buf ), |
| .data_out_re_i ( data_out_re_buf ), |
| .data_in_we_o ( data_in_we ), |
| .data_out_we_o ( data_out_we_ctrl ), |
| |
| .data_in_prev_sel_o ( data_in_prev_sel_ctrl ), |
| .data_in_prev_we_o ( data_in_prev_we_ctrl ), |
| |
| .state_in_sel_o ( state_in_sel_ctrl ), |
| .add_state_in_sel_o ( add_state_in_sel_ctrl ), |
| .add_state_out_sel_o ( add_state_out_sel_ctrl ), |
| |
| .ctr_incr_o ( ctr_incr ), |
| .ctr_ready_i ( ctr_ready ), |
| .ctr_we_i ( ctr_we ), |
| |
| .cipher_in_valid_o ( cipher_in_valid ), |
| .cipher_in_ready_i ( cipher_in_ready ), |
| .cipher_out_valid_i ( cipher_out_valid ), |
| .cipher_out_ready_o ( cipher_out_ready ), |
| .cipher_crypt_o ( cipher_crypt ), |
| .cipher_crypt_i ( cipher_crypt_busy ), |
| .cipher_dec_key_gen_o ( cipher_dec_key_gen ), |
| .cipher_dec_key_gen_i ( cipher_dec_key_gen_busy ), |
| .cipher_prng_reseed_o ( cipher_prng_reseed ), |
| .cipher_prng_reseed_i ( cipher_prng_reseed_busy ), |
| .cipher_key_clear_o ( cipher_key_clear ), |
| .cipher_key_clear_i ( cipher_key_clear_busy ), |
| .cipher_data_out_clear_o ( cipher_data_out_clear ), |
| .cipher_data_out_clear_i ( cipher_data_out_clear_busy ), |
| |
| .key_init_sel_o ( key_init_sel_ctrl ), |
| .key_init_we_o ( key_init_we_ctrl ), |
| .iv_sel_o ( iv_sel_ctrl ), |
| .iv_we_o ( iv_we_ctrl ), |
| |
| .prng_data_req_o ( prd_clearing_upd_req ), |
| .prng_data_ack_i ( prd_clearing_upd_ack ), |
| .prng_reseed_req_o ( prd_clearing_rsd_req ), |
| .prng_reseed_ack_i ( prd_clearing_rsd_ack ), |
| |
| .start_o ( hw2reg.trigger.start.d ), |
| .start_we_o ( hw2reg.trigger.start.de ), |
| .key_iv_data_in_clear_o ( hw2reg.trigger.key_iv_data_in_clear.d ), |
| .key_iv_data_in_clear_we_o ( hw2reg.trigger.key_iv_data_in_clear.de ), |
| .data_out_clear_o ( hw2reg.trigger.data_out_clear.d ), |
| .data_out_clear_we_o ( hw2reg.trigger.data_out_clear.de ), |
| .prng_reseed_o ( hw2reg.trigger.prng_reseed.d ), |
| .prng_reseed_we_o ( hw2reg.trigger.prng_reseed.de ), |
| |
| .idle_o ( hw2reg.status.idle.d ), |
| .idle_we_o ( hw2reg.status.idle.de ), |
| .stall_o ( hw2reg.status.stall.d ), |
| .stall_we_o ( hw2reg.status.stall.de ), |
| .output_lost_i ( reg2hw.status.output_lost.q ), |
| .output_lost_o ( hw2reg.status.output_lost.d ), |
| .output_lost_we_o ( hw2reg.status.output_lost.de ), |
| .output_valid_o ( hw2reg.status.output_valid.d ), |
| .output_valid_we_o ( hw2reg.status.output_valid.de ), |
| .input_ready_o ( hw2reg.status.input_ready.d ), |
| .input_ready_we_o ( hw2reg.status.input_ready.de ) |
| ); |
| |
| // SEC_CM: DATA_REG.SEC_WIPE |
| // Input data register clear |
| always_comb begin : data_in_reg_clear |
| for (int i = 0; i < NumRegsData; i++) begin |
| hw2reg.data_in[i].d = prd_clearing_128[0][i * 32 +: 32]; |
| hw2reg.data_in[i].de = data_in_we; |
| end |
| end |
| |
| /////////////// |
| // Selectors // |
| /////////////// |
| |
| // We use sparse encodings for these mux selector signals and must ensure that: |
| // 1. The synthesis tool doesn't optimize away the sparse encoding. |
| // 2. The selector signal is always valid. More precisely, an alert or SVA is triggered if a |
| // selector signal takes on an invalid value. |
| // 3. The alert signal remains asserted until reset even if the selector signal becomes valid |
| // again. This is achieved by driving the control FSM into the terminal error state whenever |
| // any mux selector signal becomes invalid. |
| // |
| // If any mux selector signal becomes invalid, the control FSM further prevents any data from |
| // being released from the cipher core by de-asserting the write enable of the output data |
| // registers. |
| |
| aes_sel_buf_chk #( |
| .Num ( DIPSelNum ), |
| .Width ( DIPSelWidth ), |
| .EnSecBuf ( 1'b1 ) |
| ) u_aes_data_in_prev_sel_buf_chk ( |
| .clk_i ( clk_i ), |
| .rst_ni ( rst_ni ), |
| .sel_i ( data_in_prev_sel_ctrl ), |
| .sel_o ( data_in_prev_sel_raw ), |
| .err_o ( data_in_prev_sel_err ) |
| ); |
| assign data_in_prev_sel = dip_sel_e'(data_in_prev_sel_raw); |
| |
| aes_sel_buf_chk #( |
| .Num ( SISelNum ), |
| .Width ( SISelWidth ), |
| .EnSecBuf ( 1'b1 ) |
| ) u_aes_state_in_sel_buf_chk ( |
| .clk_i ( clk_i ), |
| .rst_ni ( rst_ni ), |
| .sel_i ( state_in_sel_ctrl ), |
| .sel_o ( state_in_sel_raw ), |
| .err_o ( state_in_sel_err ) |
| ); |
| assign state_in_sel = si_sel_e'(state_in_sel_raw); |
| |
| aes_sel_buf_chk #( |
| .Num ( AddSISelNum ), |
| .Width ( AddSISelWidth ), |
| .EnSecBuf ( 1'b1 ) |
| ) u_aes_add_state_in_sel_buf_chk ( |
| .clk_i ( clk_i ), |
| .rst_ni ( rst_ni ), |
| .sel_i ( add_state_in_sel_ctrl ), |
| .sel_o ( add_state_in_sel_raw ), |
| .err_o ( add_state_in_sel_err ) |
| ); |
| assign add_state_in_sel = add_si_sel_e'(add_state_in_sel_raw); |
| |
| aes_sel_buf_chk #( |
| .Num ( AddSOSelNum ), |
| .Width ( AddSOSelWidth ), |
| .EnSecBuf ( 1'b1 ) |
| ) u_aes_add_state_out_sel_buf_chk ( |
| .clk_i ( clk_i ), |
| .rst_ni ( rst_ni ), |
| .sel_i ( add_state_out_sel_ctrl ), |
| .sel_o ( add_state_out_sel_raw ), |
| .err_o ( add_state_out_sel_err ) |
| ); |
| assign add_state_out_sel = add_so_sel_e'(add_state_out_sel_raw); |
| |
| aes_sel_buf_chk #( |
| .Num ( KeyInitSelNum ), |
| .Width ( KeyInitSelWidth ), |
| .EnSecBuf ( 1'b1 ) |
| ) u_aes_key_init_sel_buf_chk ( |
| .clk_i ( clk_i ), |
| .rst_ni ( rst_ni ), |
| .sel_i ( key_init_sel_ctrl ), |
| .sel_o ( key_init_sel_raw ), |
| .err_o ( key_init_sel_err ) |
| ); |
| assign key_init_sel = key_init_sel_e'(key_init_sel_raw); |
| |
| aes_sel_buf_chk #( |
| .Num ( IVSelNum ), |
| .Width ( IVSelWidth ), |
| .EnSecBuf ( 1'b1 ) |
| ) u_aes_iv_sel_buf_chk ( |
| .clk_i ( clk_i ), |
| .rst_ni ( rst_ni ), |
| .sel_i ( iv_sel_ctrl ), |
| .sel_o ( iv_sel_raw ), |
| .err_o ( iv_sel_err ) |
| ); |
| assign iv_sel = iv_sel_e'(iv_sel_raw); |
| |
| // Signal invalid mux selector signals to control FSM which will lock up and trigger an alert. |
| assign mux_sel_err = data_in_prev_sel_err | state_in_sel_err | add_state_in_sel_err | |
| add_state_out_sel_err | key_init_sel_err | iv_sel_err; |
| |
| ////////////////////////////// |
| // Sparsely Encoded Signals // |
| ////////////////////////////// |
| |
| // We use sparse encodings for various critical signals and must ensure that: |
| // 1. The synthesis tool doesn't optimize away the sparse encoding. |
| // 2. The sparsely encoded signal is always valid. More precisely, an alert or SVA is triggered |
| // if a sparse signal takes on an invalid value. |
| // 3. The alert signal remains asserted until reset even if the sparse signal becomes valid again |
| // This is achieved by driving the control FSM into the terminal error state whenever any |
| // sparsely encoded signal becomes invalid. |
| // |
| // If any sparsely encoded signal becomes invalid, the core controller further immediately |
| // de-asserts the data_out_we_o signal to prevent any data from being released. |
| |
| // We use vectors of sparsely encoded signals to reduce code duplication. |
| localparam int unsigned NumSp2VSig = NumSharesKey * NumRegsKey + NumSlicesCtr + 2; |
| sp2v_e [NumSp2VSig-1:0] sp2v_sig; |
| sp2v_e [NumSp2VSig-1:0] sp2v_sig_chk; |
| logic [NumSp2VSig-1:0][Sp2VWidth-1:0] sp2v_sig_chk_raw; |
| logic [NumSp2VSig-1:0] sp2v_sig_err; |
| |
| for (genvar s = 0; s < NumSharesKey; s++) begin : gen_use_key_init_we_ctrl_shares |
| for (genvar i = 0; i < NumRegsKey; i++) begin : gen_use_key_init_we_ctrl |
| assign sp2v_sig[s * NumRegsKey + i] = key_init_we_ctrl[s][i]; |
| end |
| end |
| for (genvar i = 0; i < NumSlicesCtr; i++) begin : gen_use_iv_we_ctrl |
| assign sp2v_sig[NumSharesKey * NumRegsKey + i] = iv_we_ctrl[i]; |
| end |
| assign sp2v_sig[NumSharesKey * NumRegsKey + NumSlicesCtr + 0] = data_in_prev_we_ctrl; |
| assign sp2v_sig[NumSharesKey * NumRegsKey + NumSlicesCtr + 1] = data_out_we_ctrl; |
| |
| // All signals inside sp2v_sig are eventually converted to single-rail signals. |
| localparam bit [NumSp2VSig-1:0] Sp2VEnSecBuf = {NumSp2VSig{1'b1}}; |
| |
| // Individually check sparsely encoded signals. |
| for (genvar i = 0; i < NumSp2VSig; i++) begin : gen_sel_buf_chk |
| aes_sel_buf_chk #( |
| .Num ( Sp2VNum ), |
| .Width ( Sp2VWidth ), |
| .EnSecBuf ( Sp2VEnSecBuf[i] ) |
| ) u_aes_sp2v_sig_buf_chk_i ( |
| .clk_i ( clk_i ), |
| .rst_ni ( rst_ni ), |
| .sel_i ( sp2v_sig[i] ), |
| .sel_o ( sp2v_sig_chk_raw[i] ), |
| .err_o ( sp2v_sig_err[i] ) |
| ); |
| assign sp2v_sig_chk[i] = sp2v_e'(sp2v_sig_chk_raw[i]); |
| end |
| |
| for (genvar s = 0; s < NumSharesKey; s++) begin : gen_key_init_we_shares |
| for (genvar i = 0; i < NumRegsKey; i++) begin : gen_key_init_we |
| assign key_init_we[s][i] = sp2v_sig_chk[s * NumRegsKey + i]; |
| end |
| end |
| for (genvar i = 0; i < NumSlicesCtr; i++) begin : gen_iv_we |
| assign iv_we[i] = sp2v_sig_chk[NumSharesKey * NumRegsKey + i]; |
| end |
| assign data_in_prev_we = sp2v_sig_chk[NumSharesKey * NumRegsKey + NumSlicesCtr + 0]; |
| assign data_out_we = sp2v_sig_chk[NumSharesKey * NumRegsKey + NumSlicesCtr + 1]; |
| |
| // Collect encoding errors. |
| // We instantiate the checker modules as close as possible to where the sparsely encoded signals |
| // are used. Here, we collect also encoding errors detected in other places of the core. |
| assign sp_enc_err_d = |sp2v_sig_err; |
| |
| // We need to register the collected error signal to avoid circular loops in the core controller |
| // related to iv_we and data_out_we. |
| always_ff @(posedge clk_i or negedge rst_ni) begin : reg_sp_enc_err |
| if (!rst_ni) begin |
| sp_enc_err_q <= 1'b0; |
| end else if (sp_enc_err_d) begin |
| sp_enc_err_q <= 1'b1; |
| end |
| end |
| |
| ///////////// |
| // Outputs // |
| ///////////// |
| |
| always_ff @(posedge clk_i or negedge rst_ni) begin : data_out_reg |
| if (!rst_ni) begin |
| data_out_q <= '0; |
| end else if (data_out_we == SP2V_HIGH) begin |
| data_out_q <= data_out_d; |
| end |
| end |
| |
| always_comb begin : key_reg_put |
| for (int i = 0; i < NumRegsKey; i++) begin |
| hw2reg.key_share0[i].d = key_init_q[0][i]; |
| hw2reg.key_share1[i].d = key_init_q[1][i]; |
| end |
| end |
| |
| always_comb begin : iv_reg_put |
| for (int i = 0; i < NumRegsIv; i++) begin |
| // Software updates IV in chunks of 32 bits. Internally, the counter updates SliceSizeCtr |
| // bits at a time. |
| hw2reg.iv[i].d = {iv_q[2 * i + 1], iv_q[2 * i]}; |
| end |
| end |
| |
| always_comb begin : data_out_put |
| for (int i = 0; i < NumRegsData; i++) begin |
| hw2reg.data_out[i].d = data_out_q[i]; |
| end |
| end |
| |
| //////////// |
| // Alerts // |
| //////////// |
| |
| // Should fatal alerts clear the status register? |
| assign clear_on_fatal = ClearStatusOnFatalAlert ? alert_fatal_o : 1'b0; |
| |
| // Recoverable alert conditions are signaled as a single alert event. |
| assign ctrl_err_update = ctrl_reg_err_update | shadowed_update_err_i; |
| assign alert_recov_o = ctrl_err_update; |
| |
| // The recoverable alert is observable via status register until the AES operation is restarted |
| // by re-writing the Control Register. Fatal alerts clear all other bits in the status register. |
| assign hw2reg.status.alert_recov_ctrl_update_err.d = ctrl_err_update & ~clear_on_fatal; |
| assign hw2reg.status.alert_recov_ctrl_update_err.de = ctrl_err_update | ctrl_we | clear_on_fatal; |
| |
| // Fatal alert conditions need to remain asserted until reset. |
| assign ctrl_err_storage_d = ctrl_reg_err_storage | shadowed_storage_err_i; |
| always_ff @(posedge clk_i or negedge rst_ni) begin : ctrl_err_storage_reg |
| if (!rst_ni) begin |
| ctrl_err_storage_q <= 1'b0; |
| end else if (ctrl_err_storage_d) begin |
| ctrl_err_storage_q <= 1'b1; |
| end |
| end |
| assign ctrl_err_storage = ctrl_err_storage_d | ctrl_err_storage_q; |
| |
| // Collect fatal alert signals. |
| assign alert_fatal_o = ctrl_err_storage | |
| ctr_alert | |
| cipher_alert | |
| ctrl_alert | |
| intg_err_alert_i; |
| |
| // Make the fatal alert observable via status register. |
| assign hw2reg.status.alert_fatal_fault.d = alert_fatal_o; |
| assign hw2reg.status.alert_fatal_fault.de = alert_fatal_o; |
| |
| // Unused alert signals |
| logic unused_alert_signals; |
| assign unused_alert_signals = ^reg2hw.alert_test; |
| |
| // Unused inputs |
| logic unused_idle; |
| assign unused_idle = reg2hw.status.idle.q; |
| |
| //////////////// |
| // Assertions // |
| //////////////// |
| |
| // Create a lint error to reduce the risk of accidentally disabling the masking. |
| `ASSERT_STATIC_LINT_ERROR(AesCoreSecMaskingNonDefault, SecMasking == 1) |
| |
| // Selectors must be known/valid |
| `ASSERT(AesModeValid, !ctrl_err_storage |-> aes_mode_q inside { |
| AES_ECB, |
| AES_CBC, |
| AES_CFB, |
| AES_OFB, |
| AES_CTR, |
| AES_NONE |
| }) |
| `ASSERT(AesOpValid, !ctrl_err_storage |-> aes_op_q inside { |
| AES_ENC, |
| AES_DEC |
| }) |
| |
| // Check parameters |
| `ASSERT_INIT(AesNumSlicesCtr, NumSlicesCtr == 8) |
| |
| // Signals used for assertions only. |
| logic [3:0][31:0] state_done_transposed, unused_state_done_transposed; |
| if (!SecMasking) begin : gen_state_done_transposed_unmasked |
| assign state_done_transposed = aes_transpose(state_done[0]); |
| end else begin : gen_state_done_transposed_masked |
| assign state_done_transposed = aes_transpose(state_done[0] ^ state_done[1]); |
| end |
| assign unused_state_done_transposed = state_done_transposed; |
| |
| // Ensure that upon local escalation of any of the FSMs, no intermediate state is released from |
| // the cipher core into the software readable output data or IV registers. |
| `ASSERT(AesSecCmDataRegLocalEscDataOut, $changed(data_out_q) && alert_fatal_o && |
| ($past(cipher_crypt, 2) == SP2V_HIGH || $past(cipher_crypt_busy, 2) == SP2V_HIGH) |=> |
| ($past(data_out_q) != $past(state_done_transposed, 2)) && |
| ($past(data_out_q) != $past(state_done_transposed, 2) ^ $past(iv_q, 2)) && |
| ($past(data_out_q) != $past(state_done_transposed, 2) ^ $past(data_in_prev_q, 2))) |
| |
| `ASSERT(AesSecCmDataRegLocalEscIv, $changed(iv_q) && alert_fatal_o && |
| ($past(cipher_crypt, 2) == SP2V_HIGH || $past(cipher_crypt_busy, 2) == SP2V_HIGH) |=> |
| ($past(iv_q) != $past(state_done_transposed, 2)) && |
| ($past(iv_q) != $past(state_done_transposed, 2) ^ $past(iv_q, 2)) && |
| ($past(iv_q) != $past(state_done_transposed, 2) ^ $past(data_in_prev_q, 2))) |
| |
| endmodule |