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; +}