From 04d830826bb1ffa26ae07c86b43567e11fe84934 Mon Sep 17 00:00:00 2001 From: Varunkumar Trivedi Date: Fri, 20 Sep 2024 16:01:18 -0700 Subject: [PATCH] [sival/aes] Add aes_prng_reseed,aes_prng_force_reseed This commit introduces the AES PRNG reseed test (chip_sw_aes_prng_reseed) and (chip_sw_aes_force_prng_reseed) and AES FORCE PRNG reseed, to handle the reseed process by disabling entrpy complex and triggering PRNG reseed accordingly to test the actual behavior of the AES module's PRNG reseeding in ECB mode. - Updated the test plan descriptions in `chip_aes_testplan.hjson` to match the implemented tests. Added missing test feature. - Resoloved latest code suggestions provided in PR #24574 and updated code with suggestions from other thread (using TRY and remove .c suffix in bazel tag). - Single commit for a cleaner history. - Resolve multiple commits. - Manualy resolve conflict See: hw/top_earlgrey/data/ip/chip_aes_testplan.hjson Closes Issue #24857 Closes Issue #24899 PR #24574 Signed-off-by: Varunkumar Trivedi (cherry picked from commit 06976d752d5ac1baa848ade96133f38cf9d6b29e) --- .../data/ip/chip_aes_testplan.hjson | 149 ++++++---- sw/device/tests/BUILD | 66 +++++ sw/device/tests/aes_force_prng_reseed_test.c | 219 +++++++++++++++ sw/device/tests/aes_prng_reseed_test.c | 257 ++++++++++++++++++ 4 files changed, 643 insertions(+), 48 deletions(-) create mode 100644 sw/device/tests/aes_force_prng_reseed_test.c create mode 100644 sw/device/tests/aes_prng_reseed_test.c diff --git a/hw/top_earlgrey/data/ip/chip_aes_testplan.hjson b/hw/top_earlgrey/data/ip/chip_aes_testplan.hjson index 3dac9e4aa5fbf..d12734ce83a24 100644 --- a/hw/top_earlgrey/data/ip/chip_aes_testplan.hjson +++ b/hw/top_earlgrey/data/ip/chip_aes_testplan.hjson @@ -144,34 +144,65 @@ } { name: chip_sw_aes_prng_reseed - desc: '''Verify that PRNG reseeding is triggered - - This test should start encryption, disable the EDN, and verify that the - encryption stalls when reseed is required. Afterwards it should verify that AES - finishes encryption after EDN is re-enabled. + desc: ''' Verify that the AES module halts when a PRNG reseed is + required but the entropy complex is disabled, and resumes + encryption once the entropy complex is re-enabled. Procedure: - -Configure encryption using the ECB mode, desired key length, and set the engine to - automatic operation. - -Set the mask_reseeding to once per 64 blocks (kDifAesReseedPer64Block) - -Using dif_aes_process_data() encrypt some number of blocks (fewer than 64). - -Disable the EDN using dif_edn_stop(). - -Encrypt more blocks and verify that encryption stalls after 64 blocks. - -Once the first 64 blocks are encrypted, a reseed request will be sent to EDN. - Since the EDN should be disabled at this point, this should cause encryption - to stall. - -Re-enable EDN. In order to do this it is required to first disable and re-enable the - CSRNG. In order to re-enable CSRNG, it is required to first disable and re-enable - the entropy source. The following sequence of steps should be used: - -Disable CSRNG - -Disable Entropy Source - -Enable Entropy Source - -Enable CSRNG - -Enable EDN - -Verify that the AES completes encryption of the remaining blocks. + - After proper initialization of the AES and entropy modules, + configure the AES encryption operation to process a number of + blocks with: + - Operation: Encrypt + - Mode: ECB + - Key Length: 128 bits + - Key Provider: Software-provided key + - Manual Operation: Disabled (Automatic operation) + - Mask Reseeding: Reseed per 64 blocks + (`kDifAesReseedPer64Block`) + - Reseed on Key Change: Enabled + - Force Masks: Disabled + + - Generate AES key. + + - Initialize the AES module and start the AES operation using + `dif_aes_start()`. + + - Process blocks using the following steps: + - Disable entropy for a block fewer than 64. + - For each block, + - Continue encrypting blocks (fewer than 64 blocks) + - Once processing reaches the block where the entropy complex + is disabled + - Disable the entire entropy complex using + `entropy_testutils_stop_all()`. + - This will halt the AES module when a PRNG reseed is + required (i.e after 64 blocks). + - Re-enable the entropy complex to resume encryption: + - Verify that the AES module is halted by checking that + `kDifAesStatusOutputValid` is not set for the 63rd block + - Re-enable the entropy complex using + `entropy_testutils_auto_mode_init()`. + - Wait for some time for the entropy complex to stabilize. + - Wait for the AES module to produce output valid + (`kDifAesStatusOutputValid`). + - Read the encrypted output data using + `dif_aes_read_output()`. + + - Finish the AES encryption operation by calling `dif_aes_end()`. - -Repeat the test for a reseed rate kDifAesReseedPer8kBlock and a message longer than 8K - blocks. + - Verify that the AES module is idle at the end of the encryption + operation (`kDifAesStatusIdle`). + - Repeat the test for a reseed rate kDifAesReseedPer8kBlock and + a message longer than 8K blocks. + + Expected Outcome: + - The AES module halts when a PRNG reseed is required but the + entropy complex is disabled. + - After re-enabling the entropy complex, the AES module resumes + encryption and produces the expected output. + - The test verifies that the AES module correctly handles halting + and resuming based on the availability of entropy and the + configured mask reseeding rate. ''' features: [ "AES.MODE.ECB", @@ -180,32 +211,54 @@ stage: V2 si_stage: SV3 lc_states: ["PROD"] - tests: [] - bazel: [] + tests: ["chip_sw_aes_prng_reseed"] + bazel: ["//sw/device/tests:aes_prng_reseed_test"] } { name: chip_sw_aes_force_prng_reseed - desc: '''Verify that when KEY_TOUCH_FORCES_RESEED is set, the PRNG reseed is - triggered every time a key changes + desc: '''Verify that the AES module stalls/halts when the entropy + complex is disabled before starting encryption and enables + output of encryption once the entropy complex is re-enabled. Procedure: - -Configures the auxiliary options for AES to enable KEY_TOUCH_FORCES_RESEED - -Disable the EDN using dif_edn_stop() - -Configure the encryption operation, mode, key length, and set the engine to manual - operation. - -Set up encryption using aes_testutils_setup_encryption() - -At this point dif_aes_start() will write the key register which will trigger reseed - request. This request will stall the encryption until EDN is re-enabled. - -Verify that the encryption stalls. - -Re-enable EDN. In order to do this it is required to first disable and re-enable the - CSRNG. In order to re-enable CSRNG, it is required to first disable and re-enable - the entropy source. The following sequence of steps should be used: - -Disable CSRNG - -Disable Entropy Source - -Enable Entropy Source - -Enable CSRNG - -Enable EDN - -Verify that the AES completes encryption. + - After proper initialization of AES and entropy modules, disable + the entire entropy complex using `entropy_testutils_stop_all()`. + - Configure the AES encryption operation to process some number of + blocks with: + - Operation: Encrypt + - Mode: ECB + - Key Length: 128 bits + - Key Provider: Software-provided key + - Manual Operation: Enabled + - Mask Reseeding: Reseed per block + - Reseed on Key Change: Enabled (When KEY_TOUCH_FORCES_RESEED is + set, the PRNG reseed is triggered every time a key changes) + - Force Masks: Enabled + - Generate AES key. + - At this point dif_aes_start() will write the key register which + will trigger reseed request. + - This request will stall the output of the encrypted data until + entropy is re-enabled. + - Verify that the AES module is halted by checking that + `kDifAesStatusOutputValid` is not set. + - Re-enable the entropy complex using + `entropy_testutils_auto_mode_init()`. + - Wait for some time for the entropy complex to stabilize. + - Wait for the AES module to produce output valid + (`kDifAesStatusOutputValid`). + - Read the encrypted output data using `dif_aes_read_output()`. + - Process all the blocks and ensure AES completes encryption + operation. + - Verify that the AES module is idle at the end of encryption + operation (`kDifAesStatusIdle`). + + Expected Outcome: + - The AES module halts output when the entropy complex is disabled + before starting encryption. + - After re-enabling the entropy complex, the AES module resumes + encryption and produces the expected output. + - The test verifies that the AES module correctly handles halting + and resuming based on the availability of entropy. ''' features: [ "AES.MODE.ECB", @@ -214,8 +267,8 @@ stage: V2 si_stage: SV3 lc_states: ["PROD"] - tests: [] - bazel: [] + tests: ["chip_sw_aes_force_prng_reseed"] + bazel: ["//sw/device/tests:aes_force_prng_reseed_test"] } { name: chip_sw_aes_idle diff --git a/sw/device/tests/BUILD b/sw/device/tests/BUILD index cca6c61d2ecad..2ef0b46d04f72 100644 --- a/sw/device/tests/BUILD +++ b/sw/device/tests/BUILD @@ -183,6 +183,72 @@ opentitan_test( ], ) +opentitan_test( + name = "aes_force_prng_reseed_test", + srcs = ["aes_force_prng_reseed_test.c"], + exec_env = dicts.add( + EARLGREY_TEST_ENVS, + EARLGREY_SILICON_OWNER_ROM_EXT_ENVS, + { + "//hw/top_earlgrey:fpga_cw310_sival": None, + "//hw/top_earlgrey:sim_verilator": None, + "//hw/top_earlgrey:fpga_cw340_sival": None, + "//hw/top_earlgrey:silicon_creator": None, + }, + ), + verilator = verilator_params( + timeout = "long", + ), + deps = [ + "//hw/ip/aes:model", + "//hw/top_earlgrey/sw/autogen:top_earlgrey", + "//sw/device/lib/base:memory", + "//sw/device/lib/base:mmio", + "//sw/device/lib/dif:aes", + "//sw/device/lib/dif:edn", + "//sw/device/lib/dif:entropy_src", + "//sw/device/lib/runtime:log", + "//sw/device/lib/testing:aes_testutils", + "//sw/device/lib/testing:entropy_testutils", + "//sw/device/lib/testing/test_framework:ottf_main", + "//sw/device/sca/lib:simple_serial", + ], +) + +opentitan_test( + name = "aes_prng_reseed_test", + srcs = ["aes_prng_reseed_test.c"], + exec_env = dicts.add( + EARLGREY_TEST_ENVS, + EARLGREY_SILICON_OWNER_ROM_EXT_ENVS, + { + "//hw/top_earlgrey:fpga_cw310_sival": None, + "//hw/top_earlgrey:sim_verilator": None, + "//hw/top_earlgrey:fpga_cw340_sival": None, + "//hw/top_earlgrey:silicon_creator": None, + }, + ), + verilator = verilator_params( + timeout = "long", + ), + deps = [ + "//hw/ip/aes:model", + "//hw/top_earlgrey/sw/autogen:top_earlgrey", + "//sw/device/lib/base:memory", + "//sw/device/lib/base:mmio", + "//sw/device/lib/dif:aes", + "//sw/device/lib/dif:edn", + "//sw/device/lib/dif:entropy_src", + "//sw/device/lib/runtime:log", + "//sw/device/lib/testing:aes_testutils", + "//sw/device/lib/testing:entropy_testutils", + "//sw/device/lib/testing/test_framework:check", + "//sw/device/lib/testing/test_framework:ottf_main", + "//sw/device/lib/testing/test_framework:ottf_utils", + "//sw/device/sca/lib:simple_serial", + ], +) + opentitan_test( name = "alert_handler_ping_timeout_test", srcs = ["alert_handler_ping_timeout_test.c"], diff --git a/sw/device/tests/aes_force_prng_reseed_test.c b/sw/device/tests/aes_force_prng_reseed_test.c new file mode 100644 index 0000000000000..b24f5c6bb14eb --- /dev/null +++ b/sw/device/tests/aes_force_prng_reseed_test.c @@ -0,0 +1,219 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +#include "hw/ip/aes/model/aes_modes.h" +#include "sw/device/lib/base/memory.h" +#include "sw/device/lib/base/mmio.h" +#include "sw/device/lib/base/multibits.h" +#include "sw/device/lib/dif/dif_aes.h" +#include "sw/device/lib/dif/dif_csrng.h" +#include "sw/device/lib/dif/dif_csrng_shared.h" +#include "sw/device/lib/dif/dif_edn.h" +#include "sw/device/lib/dif/dif_entropy_src.h" +#include "sw/device/lib/dif/dif_rv_core_ibex.h" +#include "sw/device/lib/runtime/hart.h" +#include "sw/device/lib/runtime/log.h" +#include "sw/device/lib/testing/aes_testutils.h" +#include "sw/device/lib/testing/csrng_testutils.h" +#include "sw/device/lib/testing/entropy_testutils.h" +#include "sw/device/lib/testing/rv_core_ibex_testutils.h" +#include "sw/device/lib/testing/test_framework/check.h" +#include "sw/device/lib/testing/test_framework/ottf_main.h" +#include "sw/device/sca/lib/simple_serial.h" + +#include "hw/top_earlgrey/sw/autogen/top_earlgrey.h" + +OTTF_DEFINE_TEST_CONFIG(); + +static dif_aes_t aes; +dif_aes_key_share_t key; +enum { + kAesNumBlocks = 8, + // NumBytes in one block is 4x8 i.e. 32 bits in 1 block + kDifAesBlockNumBytes = 4, + kDisableEntropyAtStartEn = 1, +}; + +static const uint8_t kKeyShare1[16] = { + 0x0f, 0x1f, 0x2f, 0x3f, 0x4f, 0x5f, 0x6f, 0x7f, + 0x8f, 0x9f, 0xaf, 0xbf, 0xcf, 0xdf, 0xef, 0xff, +}; + +/////////////////////////////////////////////////////////////////////////////// +static dif_edn_t edn0, edn1; +static dif_csrng_t csrng; +static dif_entropy_src_t entropy_src; + +// Function to disable entropy complex +status_t disable_entropy_complex(void) { + // Using entropy test utility to stop all EDN0, EDN1, CSRNG, and the Entropy + // Source + TRY(entropy_testutils_stop_all()); + LOG_INFO("The entire entropy complex is stopped"); + return OK_STATUS(); +} + +// Function to enable entropy complex +status_t enable_entropy_complex(void) { + // Using entropy test utility to enable all EDN0, EDN1, CSRNG, and the + // Entropy Source + TRY(entropy_testutils_auto_mode_init()); + LOG_INFO("The entire entropy complex is enabled"); + return OK_STATUS(); +} + +// Function to generate a new key based on provided index value +void generate_new_key(dif_aes_key_share_t *key, int index) { + uint8_t new_key_share0[sizeof(kAesModesKey128)]; + // Generate new key share0 by XOR-ing base key with kKeyShare1*index + for (size_t i = 0; i < sizeof(kAesModesKey128); ++i) { + new_key_share0[i] = kAesModesKey128[i] ^ kKeyShare1[i] * (uint8_t)(index); + } + // Update the key shares + memcpy(key->share0, new_key_share0, sizeof(key->share0)); + memcpy(key->share1, kKeyShare1, sizeof(key->share1)); +} + +status_t execute_test(void) { + bool aes_idle = false; + bool input_ready = false; + bool output_valid = false; + + LOG_INFO( + "Testing aes_prng_reseed_test with number of blocks: %d (Block 0 to %d), " + "and disabling entropy at start enable" + ": %d", + kAesNumBlocks, kAesNumBlocks - 1, kDisableEntropyAtStartEn); + // Initialize AES + TRY(dif_aes_init(mmio_region_from_addr(TOP_EARLGREY_AES_BASE_ADDR), &aes)); + TRY(dif_aes_reset(&aes)); + // Initialize EDN0, EDN1, CSRNG and Entropy Source + TRY(dif_edn_init(mmio_region_from_addr(TOP_EARLGREY_EDN0_BASE_ADDR), &edn0)); + TRY(dif_edn_init(mmio_region_from_addr(TOP_EARLGREY_EDN1_BASE_ADDR), &edn1)); + TRY(dif_csrng_init(mmio_region_from_addr(TOP_EARLGREY_CSRNG_BASE_ADDR), + &csrng)); + TRY(dif_entropy_src_init( + mmio_region_from_addr(TOP_EARLGREY_ENTROPY_SRC_BASE_ADDR), &entropy_src)); + + // Generate key with index 0 + generate_new_key(&key, 0); + + // Prepare transaction + dif_aes_transaction_t transaction = { + .operation = kDifAesOperationEncrypt, + .mode = kDifAesModeEcb, + .key_len = kDifAesKey128, + .key_provider = kDifAesKeySoftwareProvided, + .mask_reseeding = kDifAesReseedPerBlock, + .manual_operation = kDifAesManualOperationManual, + .reseed_on_key_change = true, + .force_masks = true, + .ctrl_aux_lock = false, + }; + + dif_aes_data_t plain_text[kAesNumBlocks]; + dif_aes_data_t cipher_text[kAesNumBlocks]; + + // Initialize plaintext data dynamically + // Create plaintext with random data + dif_rv_core_ibex_t rv_core_ibex; + TRY(dif_rv_core_ibex_init( + mmio_region_from_addr(TOP_EARLGREY_RV_CORE_IBEX_CFG_BASE_ADDR), + &rv_core_ibex)); + for (uint32_t i = 0; i < ARRAYSIZE(plain_text); ++i) { + for (uint32_t j = 0; j < kDifAesBlockNumBytes; ++j) { + uint32_t rand; + TRY(rv_core_ibex_testutils_get_rnd_data(&rv_core_ibex, 2000, &rand)); + plain_text[i].data[j] = rand; + } + } + + // Start the AES operation + // Wait for AES to be idle before starting encryption + aes_idle = false; + do { + TRY(dif_aes_get_status(&aes, kDifAesStatusIdle, &aes_idle)); + } while (!aes_idle); + + TRY(dif_aes_start(&aes, &transaction, &key, NULL)); + + // Disabling entropy complex + if (kDisableEntropyAtStartEn == 1) { + LOG_INFO( + "Disabling entropy complex to induce AES stall before starting AES"); + CHECK_STATUS_OK(disable_entropy_complex()); + busy_spin_micros(500); + } + + // Start encryption process + for (int i = 0; i < kAesNumBlocks; ++i) { + if (transaction.manual_operation == kDifAesManualOperationManual) { + // Wait for AES to be idle before starting encryption + TRY(dif_aes_trigger(&aes, kDifAesTriggerPrngReseed)); + } + + // Wait for input ready + input_ready = false; + do { + TRY(dif_aes_get_status(&aes, kDifAesStatusInputReady, &input_ready)); + } while (!input_ready); + + // Load data + TRY(dif_aes_load_data(&aes, plain_text[i])); + + // Trigger AES reseed request, specific for manual operation case as per + // documentation + if (transaction.manual_operation == kDifAesManualOperationManual) { + TRY(dif_aes_trigger(&aes, kDifAesTriggerStart)); + } + + // Check for stall after entropy disabled + if (i == 0 && kDisableEntropyAtStartEn == 1) { + LOG_INFO( + "Checking AES status OutputValid status after stopping " + "EDN and CSRNG"); + output_valid = false; + TRY(dif_aes_get_status(&aes, kDifAesStatusOutputValid, &output_valid)); + CHECK(!output_valid, + "ERROR:AES encryption AES encryption generated output_valid after " + "stopping entropy indicating no stall!"); + LOG_INFO( + "AES encryption is waiting for output_valid as expected at block " + "%d", + i); + + LOG_INFO("Enabling entropy complex at block %d", i); + CHECK_STATUS_OK(enable_entropy_complex()); + busy_spin_micros(500); + } + + // Wait for the AES module to resume and produce output valid + output_valid = false; + do { + TRY(dif_aes_get_status(&aes, kDifAesStatusOutputValid, &output_valid)); + } while (!output_valid); + + // Read output data + TRY(dif_aes_read_output(&aes, &cipher_text[i])); + } + + // Finish the AES encryption operation + LOG_INFO("End AES encryption operation"); + TRY(dif_aes_end(&aes)); + + // Wait for AES to be idle before starting decryption + aes_idle = false; + do { + TRY(dif_aes_get_status(&aes, kDifAesStatusIdle, &aes_idle)); + } while (!aes_idle); + LOG_INFO("AES module is idle"); + + return OK_STATUS(); +} + +bool test_main(void) { + LOG_INFO("Entering AES aes_force_prng_reseed_test Test"); + + return status_ok(execute_test()); +} diff --git a/sw/device/tests/aes_prng_reseed_test.c b/sw/device/tests/aes_prng_reseed_test.c new file mode 100644 index 0000000000000..943e32589e816 --- /dev/null +++ b/sw/device/tests/aes_prng_reseed_test.c @@ -0,0 +1,257 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +#include "hw/ip/aes/model/aes_modes.h" +#include "sw/device/lib/base/memory.h" +#include "sw/device/lib/base/mmio.h" +#include "sw/device/lib/base/multibits.h" +#include "sw/device/lib/dif/dif_aes.h" +#include "sw/device/lib/dif/dif_csrng.h" +#include "sw/device/lib/dif/dif_csrng_shared.h" +#include "sw/device/lib/dif/dif_edn.h" +#include "sw/device/lib/dif/dif_entropy_src.h" +#include "sw/device/lib/dif/dif_rv_core_ibex.h" +#include "sw/device/lib/runtime/hart.h" +#include "sw/device/lib/runtime/log.h" +#include "sw/device/lib/testing/csrng_testutils.h" +#include "sw/device/lib/testing/entropy_testutils.h" +#include "sw/device/lib/testing/rv_core_ibex_testutils.h" +#include "sw/device/lib/testing/test_framework/check.h" +#include "sw/device/lib/testing/test_framework/ottf_main.h" +#include "sw/device/sca/lib/simple_serial.h" + +#include "hw/top_earlgrey/sw/autogen/top_earlgrey.h" + +OTTF_DEFINE_TEST_CONFIG(); + +static dif_aes_t aes; +dif_aes_key_share_t key; +enum { + kNumTestConfigs = 2, + kMaxChunkSize = 300, +}; + +typedef struct { + dif_aes_mask_reseeding_t mask_reseeding; + uint32_t kAesNumBlocks; + // NumBytes in one block is 4x8 i.e. 32 bits in 1 block + uint32_t kDifAesBlockNumBytes; + uint32_t disable_entropy_after_block; + uint32_t enable_entropy_at_block; +} aes_test_config_t; + +aes_test_config_t test_configs[kNumTestConfigs] = { + // Test 1 configuration with kDifAesReseedPer64Block + { + .mask_reseeding = kDifAesReseedPer64Block, + .kAesNumBlocks = 68, + .kDifAesBlockNumBytes = 4, + .disable_entropy_after_block = 32, + .enable_entropy_at_block = 63, + }, + // Test 2 configuration with kDifAesReseedPer8kBlock + { + .mask_reseeding = kDifAesReseedPer8kBlock, + .kAesNumBlocks = 8400, + .kDifAesBlockNumBytes = 4, + .disable_entropy_after_block = 41, + .enable_entropy_at_block = 8191, + }, +}; + +static const uint8_t kKeyShare1[16] = { + 0x0f, 0x1f, 0x2f, 0x3f, 0x4f, 0x5f, 0x6f, 0x7f, + 0x8f, 0x9f, 0xaf, 0xbf, 0xcf, 0xdf, 0xef, 0xff, +}; + +static dif_edn_t edn0, edn1; +static dif_csrng_t csrng; +static dif_entropy_src_t entropy_src; +// Function to disable entropy complex +status_t disable_entropy_complex(void) { + // Using entropy test utility to stop all EDN0, EDN1, CSRNG, and the Entropy + // Source + TRY(entropy_testutils_stop_all()); + LOG_INFO("The entire entropy complex is stopped"); + return OK_STATUS(); +} + +// Function to enable entropy complex +status_t enable_entropy_complex(void) { + // Using entropy test utility to enable all EDN0, EDN1, CSRNG, and the + // Entropy Source + TRY(entropy_testutils_auto_mode_init()); + LOG_INFO("The entire entropy complex is enabled"); + return OK_STATUS(); +} + +// Function to generate a new key based on provided index value +void generate_new_key(dif_aes_key_share_t *key, int index) { + uint8_t new_key_share0[sizeof(kAesModesKey128)]; + // Generate new key share0 by XOR-ing base key with kKeyShare1*index + for (size_t i = 0; i < sizeof(kAesModesKey128); ++i) { + new_key_share0[i] = kAesModesKey128[i] ^ kKeyShare1[i] * (uint8_t)(index); + } + // Update the key shares + memcpy(key->share0, new_key_share0, sizeof(key->share0)); + memcpy(key->share1, kKeyShare1, sizeof(key->share1)); +} + +// status_t execute_test(void) { +status_t execute_test(aes_test_config_t *config) { + bool aes_idle = false; + bool input_ready = false; + bool output_valid = false; + + LOG_INFO( + "Testing AES PRNG Reseed Test with number of blocks: %d (Block 0 to %d), " + "and disabling entropy after block number: %d", + config->kAesNumBlocks, config->kAesNumBlocks - 1, + config->disable_entropy_after_block - 1); + // Initialize AES + TRY(dif_aes_init(mmio_region_from_addr(TOP_EARLGREY_AES_BASE_ADDR), &aes)); + TRY(dif_aes_reset(&aes)); + // Initialize EDN0, EDN1, CSRNG and Entropy Source + TRY(dif_edn_init(mmio_region_from_addr(TOP_EARLGREY_EDN0_BASE_ADDR), &edn0)); + TRY(dif_edn_init(mmio_region_from_addr(TOP_EARLGREY_EDN1_BASE_ADDR), &edn1)); + TRY(dif_csrng_init(mmio_region_from_addr(TOP_EARLGREY_CSRNG_BASE_ADDR), + &csrng)); + TRY(dif_entropy_src_init( + mmio_region_from_addr(TOP_EARLGREY_ENTROPY_SRC_BASE_ADDR), &entropy_src)); + + // Generate key with index 0 + generate_new_key(&key, 0); + + // Prepare transaction + dif_aes_transaction_t transaction = { + .operation = kDifAesOperationEncrypt, + .mode = kDifAesModeEcb, + .key_len = kDifAesKey128, + .key_provider = kDifAesKeySoftwareProvided, + .mask_reseeding = config->mask_reseeding, + .manual_operation = kDifAesManualOperationAuto, + .reseed_on_key_change = true, + .force_masks = false, + .ctrl_aux_lock = false, + }; + // Start the AES operation + // Wait for AES to be idle before starting encryption + aes_idle = false; + do { + TRY(dif_aes_get_status(&aes, kDifAesStatusIdle, &aes_idle)); + } while (!aes_idle); + TRY(dif_aes_start(&aes, &transaction, &key, NULL)); + + dif_aes_data_t plain_text[kMaxChunkSize]; + dif_aes_data_t cipher_text[kMaxChunkSize]; + + uint32_t total_blocks = config->kAesNumBlocks; + + uint32_t blocks_processed = 0; + + // Initialize plaintext data dynamically + // Create plaintext with random data + dif_rv_core_ibex_t rv_core_ibex; + TRY(dif_rv_core_ibex_init( + mmio_region_from_addr(TOP_EARLGREY_RV_CORE_IBEX_CFG_BASE_ADDR), + &rv_core_ibex)); + for (uint32_t i = 0; i < ARRAYSIZE(plain_text); ++i) { + for (uint32_t j = 0; j < config->kDifAesBlockNumBytes; ++j) { + uint32_t rand; + TRY(rv_core_ibex_testutils_get_rnd_data(&rv_core_ibex, 2000, &rand)); + plain_text[i].data[j] = rand; + } + } + + // Now start encryption process for the blocks in chunks + while (blocks_processed < total_blocks) { + uint32_t blocks_to_process = total_blocks - blocks_processed; + if (blocks_to_process > kMaxChunkSize) { + blocks_to_process = kMaxChunkSize; + } + + // Start encryption process + // Process the blocks in this chunk + for (uint32_t i = 0; i < blocks_to_process; ++i) { + uint32_t block_index = blocks_processed + i; + + // Wait for input ready + input_ready = false; + do { + TRY(dif_aes_get_status(&aes, kDifAesStatusInputReady, &input_ready)); + } while (!input_ready); + + // Load data + TRY(dif_aes_load_data(&aes, plain_text[i])); + + // Disable entropy complex after processing enough blocks + if (block_index == config->disable_entropy_after_block) { + LOG_INFO("Disabling entropy complex to induce AES halt at block %d", + block_index); + CHECK_STATUS_OK(disable_entropy_complex()); + busy_spin_micros(1000); + } + + // Check for halt when PRNG reseed is required + if (block_index == config->enable_entropy_at_block) { + output_valid = false; + TRY(dif_aes_get_status(&aes, kDifAesStatusOutputValid, &output_valid)); + CHECK(!output_valid, + "ERROR: AES encryption did not halt when entropy was disabled at " + "block %d", + block_index); + LOG_INFO("AES encryption is halted as expected at block %d", + block_index); + + // Re-enable the entropy complex + LOG_INFO("Re-enabling entropy complex at block %d", block_index); + CHECK_STATUS_OK(enable_entropy_complex()); + busy_spin_micros(500); + } + + // Wait for the AES module to produce output valid + output_valid = false; + do { + TRY(dif_aes_get_status(&aes, kDifAesStatusOutputValid, &output_valid)); + } while (!output_valid); + + // Read output data + TRY(dif_aes_read_output(&aes, &cipher_text[i])); + } + // Update blocks_processed + blocks_processed += blocks_to_process; + } + + // Finish the AES encryption operation + LOG_INFO("End AES encryption operation"); + TRY(dif_aes_end(&aes)); + + // Wait for AES to be idle to check aes encryption process has ended + aes_idle = false; + do { + TRY(dif_aes_get_status(&aes, kDifAesStatusIdle, &aes_idle)); + } while (!aes_idle); + LOG_INFO("AES module is idle"); + + // After ensuring the AES module is idle + // Reset the AES module before starting decryption + TRY(dif_aes_reset(&aes)); + + return OK_STATUS(); +} + +bool test_main(void) { + for (size_t i = 0; i < kNumTestConfigs; ++i) { + aes_test_config_t *config = &test_configs[i]; + + // Log the current test configuration + LOG_INFO("Starting AES PRNG Reseed Test with mask_reseeding: %d", + config->mask_reseeding); + + // Call execute_test with the current configuration + CHECK_STATUS_OK(execute_test(config)); + } + + return true; +}