From 2378a13a86bb68ee528a2a91b362c34bc5db88eb Mon Sep 17 00:00:00 2001 From: Pirmin Vogel Date: Thu, 9 Jan 2025 17:26:40 +0100 Subject: [PATCH] [aes/rtl] Correct manual operation mode for GCM This commit cleans up the generation of GCM specific start signals and properly handles manual operation mode for GCM. Previously, some GCM phases (especially the ones not using the AES cipher core) would start automatically despite the module being configured in manual operation mode. Signed-off-by: Pirmin Vogel --- hw/ip/aes/rtl/aes_control_fsm.sv | 124 +++++++++++++++++++------------ 1 file changed, 78 insertions(+), 46 deletions(-) diff --git a/hw/ip/aes/rtl/aes_control_fsm.sv b/hw/ip/aes/rtl/aes_control_fsm.sv index 49b74c77c2c61..abd2fa54a9097 100644 --- a/hw/ip/aes/rtl/aes_control_fsm.sv +++ b/hw/ip/aes/rtl/aes_control_fsm.sv @@ -186,8 +186,8 @@ module aes_control_fsm logic input_ready_we; logic gcm_init, gcm_restore, gcm_aad, gcm_txt, gcm_save, gcm_tag; - logic start_common_gcm, start_gcm; - logic start_gcm_init, start_gcm_hash_subkey, start_gcm_s; + logic start_common_gcm, start_ghash; + logic start_gcm_init, start_gcm_hsk, start_gcm_s; logic start_gcm_restore, start_gcm_aad, start_gcm_txt, start_gcm_save; logic start_gcm_tag; logic doing_gcm_hsk, doing_gcm_s, doing_gcm_txt; @@ -230,41 +230,6 @@ module aes_control_fsm assign start_ofb = (mode_i == AES_OFB) & iv_ready; assign start_ctr = (mode_i == AES_CTR) & iv_ready & ctr_ready_i; - // For the initial GCM phase, we need key and IV but then encrypt two blocks. The input data - // is not required. - assign gcm_init = (mode_i == AES_GCM) & (gcm_phase_i == GCM_INIT); - assign start_common_gcm = key_init_ready & (sideload_i ? key_sideload_valid_i : 1'b1); - assign start_gcm_hash_subkey = gcm_init & ~hash_subkey_ready_q & iv_ready & ctr_ready_i; - assign start_gcm_s = gcm_init & ~s_ready_q & iv_ready & ctr_ready_i; - assign start_gcm_init = (start_gcm_hash_subkey | start_gcm_s) & start_common_gcm; - - // For the restore phase of GCM, we need key, IV and input data. - assign gcm_restore = (mode_i == AES_GCM) & (gcm_phase_i == GCM_RESTORE); - assign start_gcm_restore = gcm_restore & iv_ready & ctr_ready_i & data_in_new & start_common_gcm; - - // For the AAD and ciphertext/plaintext phases of GCM, we need key, IV and input data. But for - // the AAD phase, only the input data is really used and marked as used. - assign gcm_aad = (mode_i == AES_GCM) & (gcm_phase_i == GCM_AAD); - assign gcm_txt = (mode_i == AES_GCM) & (gcm_phase_i == GCM_TEXT); - assign start_gcm_aad = gcm_aad & iv_ready & ctr_ready_i & data_in_new; - assign start_gcm_txt = gcm_txt & iv_ready & ctr_ready_i & data_in_new; - assign start_gcm = start_gcm_aad | start_gcm_txt; - - // For the save phase of GCM, we need no inputs. - assign gcm_save = (mode_i == AES_GCM) & (gcm_phase_i == GCM_SAVE); - assign start_gcm_save = gcm_save & hash_subkey_ready_q & s_ready_q; - - // For the tag phase of GCM, we just need the input data. - assign gcm_tag = (mode_i == AES_GCM) & (gcm_phase_i == GCM_TAG); - assign start_gcm_tag = gcm_tag & data_in_new; - - // The GHASH block is idle whenever it's ready to receive inputs and where not about to start - // a GCM related operation. - assign ghash_idle = ghash_in_ready_i & ~(start_gcm_init | start_gcm | start_gcm_tag); - - // In GCM, the counter performs inc32() instead of inc128(), i.e., the counter wraps at 32 bits. - assign ctr_inc32_o = (mode_i == AES_GCM); - // If set to start manually, we just wait for the trigger. Otherwise, check common as well as // mode-specific start conditions. assign start = cfg_valid & no_alert & @@ -275,12 +240,11 @@ module aes_control_fsm start_cbc | start_cfb | start_ofb | - start_ctr | - start_gcm) & start_common) | - start_gcm_init | - start_gcm_restore | - start_gcm_save | - start_gcm_tag); + start_ctr) & start_common) | + // Only the initial as well as the ciphertext/plaintext phases of GCM require operating + // the AES cipher core. Common start conditions are already factored into these signals. + (start_gcm_init | + start_gcm_txt)); // If not set to overwrite data, we wait for any previous output data to be read. data_out_read // synchronously clears output_valid_q, unless new output data is written in the exact same @@ -291,6 +255,72 @@ module aes_control_fsm // Make sure previous output data has been read. (~output_valid_q | data_out_read)); + ////////////////////////// + // GCM Start Conditions // + ////////////////////////// + // Note that only the initial as well as the ciphertext/plaintext phases of GCM require operating + // the AES cipher core. The start conditions for these phases are factored into the regular start + // signal above. In contrast, the restore, AAD, save and tag phases use the GHASH block only. The + // corresponding start conditions are handled separately by the FSM and we need to explicilty + // factor in manual operation below. + + assign gcm_init = (mode_i == AES_GCM) & (gcm_phase_i == GCM_INIT); + assign gcm_restore = (mode_i == AES_GCM) & (gcm_phase_i == GCM_RESTORE); + assign gcm_aad = (mode_i == AES_GCM) & (gcm_phase_i == GCM_AAD); + assign gcm_txt = (mode_i == AES_GCM) & (gcm_phase_i == GCM_TEXT); + assign gcm_save = (mode_i == AES_GCM) & (gcm_phase_i == GCM_SAVE); + assign gcm_tag = (mode_i == AES_GCM) & (gcm_phase_i == GCM_TAG); + + // The common start conditions for GCM don't include the input data. + assign start_common_gcm = key_init_ready & + // If key sideload is enabled, we only start if the key is valid. + (sideload_i ? key_sideload_valid_i : 1'b1); + + // For the initial GCM phase, we need key and IV but then encrypt two blocks. The input data + // is not required. Requires operating the AES cipher core and thus factors into the regular + // start signal. + assign start_gcm_hsk = gcm_init & ~hash_subkey_ready_q & iv_ready & ctr_ready_i; + assign start_gcm_s = gcm_init & ~s_ready_q & iv_ready & ctr_ready_i; + assign start_gcm_init = (start_gcm_hsk | start_gcm_s) & start_common_gcm; + + // For the restore phase of GCM, we need key, IV and input data. If set to start manually, we + // just wait for the trigger. + assign start_gcm_restore = gcm_restore & cfg_valid & no_alert & + // Manual operation has priority. + (manual_operation_i ? start_i : iv_ready & ctr_ready_i & data_in_new & start_common_gcm); + + // For the AAD and ciphertext/plaintext phases of GCM, we need key, IV and input data. But for + // the AAD phase, only the input data is really used and marked as used. The start condition for + // the AAD phase is handled separately by the FSM and we need to factor in the manual operation + // mode here. + assign start_gcm_aad = gcm_aad & cfg_valid & no_alert & + // Manual operation has priority. + (manual_operation_i ? start_i : iv_ready & ctr_ready_i & start_common); + // The ciphertext/plaintext phase requires operating AES cipher core and thus factors into the + // regular start signal. + assign start_gcm_txt = gcm_txt & iv_ready & ctr_ready_i & start_common; + + // For the save phase of GCM, we need no inputs. But the hash subkey and S must not yet have been + // cleared (happens upon saving the GHASH state). + assign start_gcm_save = gcm_save & cfg_valid & no_alert & + // Manual operation has priority. + (manual_operation_i ? start_i : hash_subkey_ready_q & s_ready_q); + + // For the tag phase of GCM, we also need the input data. + assign start_gcm_tag = gcm_tag & cfg_valid & no_alert & + // Manual operation has priority. + (manual_operation_i ? start_i : hash_subkey_ready_q & s_ready_q & data_in_new); + + // The restore, AAD, save and tag phases use the GHASH block only. + assign start_ghash = start_gcm_restore | start_gcm_aad | start_gcm_save | start_gcm_tag; + + // The GHASH block is idle whenever it's ready to receive inputs and we're not about to start + // a GCM related operation. + assign ghash_idle = ghash_in_ready_i & ~start_ghash; + + // In GCM, the counter performs inc32() instead of inc128(), i.e., the counter wraps at 32 bits. + assign ctr_inc32_o = (mode_i == AES_GCM); + // Helper signals for FSM assign crypt = cipher_crypt_o | cipher_crypt_i; @@ -417,10 +447,12 @@ module aes_control_fsm // is enabled, software writes to the initial key registers are ignored. key_init_we_o = sideload_i ? {NumSharesKey * NumRegsKey{key_sideload}} : key_init_qe_i; iv_we_o = iv_qe; + end - // Updates to the main and GCM control registers are only allowed if the core is not - // about to start and there isn't a storage error. A storage error is unrecoverable and - // requires a reset. + if (!start_core && !start_ghash) begin + // Updates to the main and GCM control registers are only allowed if the cipher core and + // the GHASH block are not about to start and there isn't a storage error. A storage + // error is unrecoverable and requires a reset. ctrl_we_o = !ctrl_err_storage_i ? ctrl_qe_i : 1'b0; ctrl_gcm_we_o = !ctrl_err_storage_i ? ctrl_gcm_qe_i : 1'b0;