diff --git a/sw/device/sca/lib/prng.c b/sw/device/sca/lib/prng.c index d89ef2600c4dc..9ad23e3f31106 100644 --- a/sw/device/sca/lib/prng.c +++ b/sw/device/sca/lib/prng.c @@ -158,6 +158,8 @@ __attribute__((noinline)) static uint32_t genrand_int32(void) { void prng_seed(uint32_t seed) { init_by_array(&seed, 1); } +uint32_t prng_rand_uint32(void) { return genrand_int32(); } + uint8_t prng_rand_byte(void) { uint32_t rand = 0; do { diff --git a/sw/device/sca/lib/prng.h b/sw/device/sca/lib/prng.h index 53de2d56a5645..d1806c1bf65fd 100644 --- a/sw/device/sca/lib/prng.h +++ b/sw/device/sca/lib/prng.h @@ -33,6 +33,16 @@ extern "C" { */ void prng_seed(uint32_t seed); +/** + * Generates a random uint32_t. + * + * The behavior of this function matches the behavior of `random.randint(0, + * 0xFFFFFFFF)` in python. + * + * @return A random uint32_t. + */ +uint32_t prng_rand_uint32(void); + /** * Generates a random byte. * diff --git a/sw/device/tests/penetrationtests/firmware/BUILD b/sw/device/tests/penetrationtests/firmware/BUILD index fc595938cf083..03386079ad13b 100644 --- a/sw/device/tests/penetrationtests/firmware/BUILD +++ b/sw/device/tests/penetrationtests/firmware/BUILD @@ -67,6 +67,7 @@ cc_library( "//sw/device/lib/testing:keymgr_testutils", "//sw/device/lib/testing/test_framework:ujson_ottf", "//sw/device/lib/ujson", + "//sw/device/sca/lib:prng", "//sw/device/sca/lib:sca", "//sw/device/tests/penetrationtests/firmware:sca_lib", "//sw/device/tests/penetrationtests/json:ibex_sca_commands", diff --git a/sw/device/tests/penetrationtests/firmware/ibex_fi.c b/sw/device/tests/penetrationtests/firmware/ibex_fi.c index 39c62ab5cc241..8365947ff4fd3 100644 --- a/sw/device/tests/penetrationtests/firmware/ibex_fi.c +++ b/sw/device/tests/penetrationtests/firmware/ibex_fi.c @@ -103,6 +103,7 @@ static dif_flash_ctrl_device_info_t flash_info; // used by the program. OT_SECTION(".data") static volatile uint32_t sram_main_buffer[32]; +static volatile uint32_t sram_main_buffer_large[4000]; status_t handle_ibex_fi_address_translation(ujson_t *uj) { // Clear registered alerts in alert handler. @@ -471,6 +472,57 @@ status_t handle_ibex_fi_char_flash_write(ujson_t *uj) { return OK_STATUS(); } +status_t handle_ibex_fi_char_sram_static(ujson_t *uj) { + // Clear registered alerts in alert handler. + uint32_t reg_alerts = sca_get_triggered_alerts(); + + // Get address of buffer located in SRAM. + uintptr_t sram_main_buffer_addr = (uintptr_t)&sram_main_buffer_large; + mmio_region_t sram_region_main_addr = + mmio_region_from_addr(sram_main_buffer_addr); + + // Write reference value into SRAM. + for (int i = 0; i < 4000; i++) { + mmio_region_write32(sram_region_main_addr, i * (ptrdiff_t)sizeof(uint32_t), + ref_values[0]); + } + + // FI code target. + sca_set_trigger_high(); + asm volatile(NOP1000); + sca_set_trigger_low(); + // Get registered alerts from alert handler. + reg_alerts = sca_get_triggered_alerts(); + + // Compare against reference values. + ibex_fi_faulty_addresses_t uj_output; + memset(uj_output.addresses, 0, sizeof(uj_output.addresses)); + int faulty_address_pos = 0; + for (int sram_pos = 0; sram_pos < 4000; sram_pos++) { + uint32_t res_value = mmio_region_read32( + sram_region_main_addr, sram_pos * (ptrdiff_t)sizeof(uint32_t)); + if (res_value != ref_values[0]) { + uj_output.addresses[faulty_address_pos] = (uint32_t)sram_pos; + faulty_address_pos++; + // Currently, we register only up to 8 faulty SRAM positions. If there + // are more, we overwrite the addresses array. + if (faulty_address_pos > 7) { + faulty_address_pos = 0; + } + } + } + + // Read ERR_STATUS register. + dif_rv_core_ibex_error_status_t codes; + TRY(dif_rv_core_ibex_get_error_status(&rv_core_ibex, &codes)); + + // Send res & ERR_STATUS to host. + uj_output.err_status = codes; + uj_output.alerts = reg_alerts; + RESP_OK(ujson_serialize_ibex_fi_faulty_addresses_t, uj, &uj_output); + return OK_STATUS(0); +} + status_t handle_ibex_fi_char_sram_read(ujson_t *uj) { // Clear registered alerts in alert handler. uint32_t reg_alerts = sca_get_triggered_alerts(); @@ -1014,6 +1066,8 @@ status_t handle_ibex_fi(ujson_t *uj) { return handle_ibex_fi_char_sram_write(uj); case kIbexFiSubcommandCharSramRead: return handle_ibex_fi_char_sram_read(uj); + case kIbexFiSubcommandCharSramStatic: + return handle_ibex_fi_char_sram_static(uj); case kIbexFiSubcommandCharFlashWrite: return handle_ibex_fi_char_flash_write(uj); case kIbexFiSubcommandCharFlashRead: diff --git a/sw/device/tests/penetrationtests/firmware/ibex_fi.h b/sw/device/tests/penetrationtests/firmware/ibex_fi.h index 2b3a356f8afcd..e5db51f5ac856 100644 --- a/sw/device/tests/penetrationtests/firmware/ibex_fi.h +++ b/sw/device/tests/penetrationtests/firmware/ibex_fi.h @@ -92,7 +92,7 @@ status_t handle_ibex_fi_char_csr_write(ujson_t *uj); status_t handle_ibex_fi_char_csr_read(ujson_t *uj); /** - * ibex.char.flash_read command handler. + * ibex.fi.char.flash_read command handler. * * This FI penetration tests executes the following instructions: * - Write reference values into flash. @@ -112,7 +112,7 @@ status_t handle_ibex_fi_char_csr_read(ujson_t *uj); status_t handle_ibex_fi_char_flash_read(ujson_t *uj); /** - * ibex.char.flash_write command handler. + * ibex.fi.char.flash_write command handler. * * This FI penetration tests executes the following instructions: * - Set the trigger. @@ -131,7 +131,26 @@ status_t handle_ibex_fi_char_flash_read(ujson_t *uj); status_t handle_ibex_fi_char_flash_write(ujson_t *uj); /** - * ibex.char.sram_read command handler. + * ibex.fi.char.sram_static command handler. + * + * This FI penetration tests executes the following instructions: + * - Write ref_values[0] to 16kb of SRAM. + * - Set the trigger. + * - Add 1000 NOPs to give the setup the chance to inject faults. + * - Unset the trigger. + * - Read back content of 16kb of SRAM and compare against reference value. + * - If faulty words are detected, transmit addresses back to host. + * + * Faults are injected during the trigger_high & trigger_low. + * It needs to be ensured that the compiler does not optimize this code. + * + * @param uj An initialized uJSON context. + * @return OK or error. + */ +status_t handle_ibex_fi_char_sram_static(ujson_t *uj); + +/** + * ibex.fi.char.sram_read command handler. * * This FI penetration tests executes the following instructions: * - Write reference values into SRAM. @@ -151,7 +170,7 @@ status_t handle_ibex_fi_char_flash_write(ujson_t *uj); status_t handle_ibex_fi_char_sram_read(ujson_t *uj); /** - * ibex.char.sram_write command handler. + * ibex.fi.char.sram_write command handler. * * This FI penetration tests executes the following instructions: * - Set the trigger. @@ -170,7 +189,7 @@ status_t handle_ibex_fi_char_sram_read(ujson_t *uj); status_t handle_ibex_fi_char_sram_write(ujson_t *uj); /** - * ibex.char.unconditional_branch command handler. + * ibex.fi.char.unconditional_branch command handler. * * This FI penetration tests executes the following instructions: * - Add 10 NOPs to delay the trigger @@ -187,7 +206,7 @@ status_t handle_ibex_fi_char_sram_write(ujson_t *uj); status_t handle_ibex_fi_char_unconditional_branch(ujson_t *uj); /** - * ibex.char.conditional_branch command handler. + * ibex.fi.char.conditional_branch command handler. * * This FI penetration tests executes the following instructions: * - Add 10 NOPs to delay the trigger @@ -204,7 +223,7 @@ status_t handle_ibex_fi_char_unconditional_branch(ujson_t *uj); status_t handle_ibex_fi_char_conditional_branch(ujson_t *uj); /** - * ibex.char.mem_op_loop command handler. + * ibex.fi.char.mem_op_loop command handler. * * This FI penetration tests executes the following instructions: * - Add 100 NOPs to delay the trigger @@ -225,7 +244,7 @@ status_t handle_ibex_fi_char_conditional_branch(ujson_t *uj); status_t handle_ibex_fi_char_mem_op_loop(ujson_t *uj); /** - * ibex.char.reg_op_loop command handler. + * ibex.fi.char.reg_op_loop command handler. * * This FI penetration tests executes the following instructions: * - Initialize register x5=0 & x6=10000 @@ -242,7 +261,7 @@ status_t handle_ibex_fi_char_mem_op_loop(ujson_t *uj); status_t handle_ibex_fi_char_reg_op_loop(ujson_t *uj); /** - * ibex.char.unrolled_mem_op_loop command handler. + * ibex.fi.char.unrolled_mem_op_loop command handler. * * This FI penetration tests executes the following instructions: * - Add 100 NOPs to delay the trigger @@ -260,7 +279,7 @@ status_t handle_ibex_fi_char_reg_op_loop(ujson_t *uj); status_t handle_ibex_fi_char_unrolled_mem_op_loop(ujson_t *uj); /** - * ibex.char.unrolled_reg_op_loop command handler. + * ibex.fi.char.unrolled_reg_op_loop command handler. * * This FI penetration tests executes the following instructions: * - Initialize register x5=0 @@ -285,7 +304,7 @@ status_t handle_ibex_fi_char_unrolled_reg_op_loop(ujson_t *uj); status_t handle_ibex_fi_init(ujson_t *uj); /** - * ibex.char.register_file command handler. + * ibex.fi.char.register_file command handler. * * This FI penetration test executes the following instructions: * - Initialize temp. registers with reference values @@ -301,7 +320,7 @@ status_t handle_ibex_fi_init(ujson_t *uj); status_t handle_ibex_fi_char_register_file(ujson_t *uj); /** - * ibex.char.register_file_read command handler. + * ibex.fi.char.register_file_read command handler. * * This FI penetration test executes the following instructions: * - Initialize temp. registers with reference values diff --git a/sw/device/tests/penetrationtests/firmware/ibex_sca.c b/sw/device/tests/penetrationtests/firmware/ibex_sca.c index 2aa5c93e8d748..bb96a6b1bbbe4 100644 --- a/sw/device/tests/penetrationtests/firmware/ibex_sca.c +++ b/sw/device/tests/penetrationtests/firmware/ibex_sca.c @@ -10,9 +10,11 @@ #include "sw/device/lib/dif/dif_kmac.h" #include "sw/device/lib/runtime/log.h" #include "sw/device/lib/testing/keymgr_testutils.h" +#include "sw/device/lib/testing/test_framework/check.h" #include "sw/device/lib/testing/test_framework/ottf_test_config.h" #include "sw/device/lib/testing/test_framework/ujson_ottf.h" #include "sw/device/lib/ujson/ujson.h" +#include "sw/device/sca/lib/prng.h" #include "sw/device/sca/lib/sca.h" #include "sw/device/tests/penetrationtests/firmware/sca_lib.h" #include "sw/device/tests/penetrationtests/json/ibex_sca_commands.h" @@ -22,11 +24,189 @@ static dif_keymgr_t keymgr; static dif_kmac_t kmac; +// NOP macros. +#define NOP1 "addi x0, x0, 0\n" +#define NOP10 NOP1 NOP1 NOP1 NOP1 NOP1 NOP1 NOP1 NOP1 NOP1 NOP1 +#define NOP100 NOP10 NOP10 NOP10 NOP10 NOP10 NOP10 NOP10 NOP10 NOP10 NOP10 + // Buffer to allow the compiler to allocate a safe area in Main SRAM where // we can do the write/read test without the risk of clobbering data // used by the program. OT_SECTION(".data") static volatile uint32_t sram_main_buffer[8]; +static volatile uint32_t sram_main_buffer_batch[256]; + +// Function to assign x5...x7 and x28...x31 the provided values val0...val6. +// Inline to avoid function calls for SCA measurements. +static inline void copy_to_registers(uint32_t val0, uint32_t val1, + uint32_t val2, uint32_t val3, + uint32_t val4, uint32_t val5, + uint32_t val6) { + asm volatile("mv %0, x5" : "=r"(val0)); + asm volatile("mv %0, x6" : "=r"(val1)); + asm volatile("mv %0, x7" : "=r"(val2)); + asm volatile("mv %0, x28" : "=r"(val3)); + asm volatile("mv %0, x29" : "=r"(val4)); + asm volatile("mv %0, x30" : "=r"(val5)); + asm volatile("mv %0, x31" : "=r"(val6)); +} + +// Generate Fixed vs Random (FvsR) array of values. The fixed value is provided +// by the user and the random values are generated by the PRNG provided in the +// SCA library. +static void generate_fvsr(size_t num_iterations, uint32_t fixed_data, + uint32_t values[]) { + bool sample_fixed = true; + for (size_t i = 0; i < num_iterations; i++) { + if (sample_fixed) { + values[i] = fixed_data; + } else { + values[i] = prng_rand_uint32(); + } + sample_fixed = prng_rand_uint32() & 0x1; + } +} + +// Generate random values used by the test by calling the SCA PRNG. +static void generate_random(size_t num_iterations, uint32_t values[]) { + for (size_t i = 0; i < num_iterations; i++) { + values[i] = prng_rand_uint32(); + } +} + +status_t handle_ibex_sca_tl_write_batch_fvsr_fix_address(ujson_t *uj) { + // Get number of iterations and fixed data. + ibex_sca_test_fvsr_t uj_data; + TRY(ujson_deserialize_ibex_sca_test_fvsr_t(uj, &uj_data)); + TRY_CHECK(uj_data.num_iterations < 256); + + // Generate FvsR values. + uint32_t values[256]; + generate_fvsr(uj_data.num_iterations, uj_data.fixed_data, values); + + // Get address of buffer located in SRAM. + uintptr_t sram_main_buffer_addr = (uintptr_t)&sram_main_buffer_batch; + mmio_region_t sram_region_main_addr = + mmio_region_from_addr(sram_main_buffer_addr); + + // SCA code target. + for (int it = 0; it < uj_data.num_iterations; it++) { + sca_set_trigger_high(); + // Give the trigger time to rise. + asm volatile(NOP100); + // Write random data into SRAM at the first address. + mmio_region_write32(sram_region_main_addr, 0, values[it]); + sca_set_trigger_low(); + asm volatile(NOP100); + } + + // Write back last value written into SRAM to validate generated data. + ibex_sca_result_t uj_output; + uj_output.result = values[uj_data.num_iterations - 1]; + RESP_OK(ujson_serialize_ibex_sca_result_t, uj, &uj_output); + return OK_STATUS(); +} + +status_t handle_ibex_sca_tl_write_batch_fvsr(ujson_t *uj) { + // Get number of iterations and fixed data. + ibex_sca_test_fvsr_t uj_data; + TRY(ujson_deserialize_ibex_sca_test_fvsr_t(uj, &uj_data)); + TRY_CHECK(uj_data.num_iterations < 256); + + // Generate FvsR values. + uint32_t values[256]; + generate_fvsr(uj_data.num_iterations, uj_data.fixed_data, values); + + // Get address of buffer located in SRAM. + uintptr_t sram_main_buffer_addr = (uintptr_t)&sram_main_buffer_batch; + mmio_region_t sram_region_main_addr = + mmio_region_from_addr(sram_main_buffer_addr); + + // SCA code target. + for (int it = 0; it < uj_data.num_iterations; it++) { + sca_set_trigger_high(); + // Give the trigger time to rise. + asm volatile(NOP100); + // Write random data into SRAM. + mmio_region_write32(sram_region_main_addr, it * (ptrdiff_t)sizeof(uint32_t), + values[it]); + sca_set_trigger_low(); + asm volatile(NOP100); + } + + // Write back last value written into SRAM to validate generated data. + ibex_sca_result_t uj_output; + uj_output.result = values[uj_data.num_iterations - 1]; + RESP_OK(ujson_serialize_ibex_sca_result_t, uj, &uj_output); + return OK_STATUS(); +} + +status_t handle_ibex_sca_tl_write_batch_random_fix_address(ujson_t *uj) { + // Get number of iterations. + ibex_sca_batch_t uj_data; + TRY(ujson_deserialize_ibex_sca_batch_t(uj, &uj_data)); + TRY_CHECK(uj_data.num_iterations < 256); + + // Generate random values. + uint32_t values[256]; + generate_random(uj_data.num_iterations, values); + + // Get address of buffer located in SRAM. + uintptr_t sram_main_buffer_addr = (uintptr_t)&sram_main_buffer_batch; + mmio_region_t sram_region_main_addr = + mmio_region_from_addr(sram_main_buffer_addr); + + // SCA code target. + for (int it = 0; it < uj_data.num_iterations; it++) { + sca_set_trigger_high(); + // Give the trigger time to rise. + asm volatile(NOP100); + // Write random data into SRAM. + mmio_region_write32(sram_region_main_addr, 0, values[it]); + sca_set_trigger_low(); + asm volatile(NOP100); + } + + // Write back last value written into SRAM to validate generated data. + ibex_sca_result_t uj_output; + uj_output.result = values[uj_data.num_iterations - 1]; + RESP_OK(ujson_serialize_ibex_sca_result_t, uj, &uj_output); + return OK_STATUS(); +} + +status_t handle_ibex_sca_tl_write_batch_random(ujson_t *uj) { + // Get number of iterations. + ibex_sca_batch_t uj_data; + TRY(ujson_deserialize_ibex_sca_batch_t(uj, &uj_data)); + TRY_CHECK(uj_data.num_iterations < 256); + + // Generate random values. + uint32_t values[256]; + generate_random(uj_data.num_iterations, values); + + // Get address of buffer located in SRAM. + uintptr_t sram_main_buffer_addr = (uintptr_t)&sram_main_buffer_batch; + mmio_region_t sram_region_main_addr = + mmio_region_from_addr(sram_main_buffer_addr); + + // SCA code target. + for (int it = 0; it < uj_data.num_iterations; it++) { + sca_set_trigger_high(); + // Give the trigger time to rise. + asm volatile(NOP100); + // Write random data into SRAM. + mmio_region_write32(sram_region_main_addr, it * (ptrdiff_t)sizeof(uint32_t), + values[it]); + sca_set_trigger_low(); + asm volatile(NOP100); + } + + // Write back last value written into SRAM to validate generated data. + ibex_sca_result_t uj_output; + uj_output.result = values[uj_data.num_iterations - 1]; + RESP_OK(ujson_serialize_ibex_sca_result_t, uj, &uj_output); + return OK_STATUS(); +} status_t handle_ibex_sca_key_sideloading(ujson_t *uj) { ibex_sca_salt_t uj_data; @@ -80,15 +260,15 @@ status_t handle_ibex_sca_tl_write(ujson_t *uj) { mmio_region_from_addr(sram_main_buffer_addr); // SCA code target. - for (int it = 0; it < uj_data.num_iterations; it++) { - sca_set_trigger_high(); - // Write provided data into SRAM. - for (int i = 0; i < 8; i++) { - mmio_region_write32(sram_region_main_addr, - i * (ptrdiff_t)sizeof(uint32_t), uj_data.data[i]); - } - sca_set_trigger_low(); + sca_set_trigger_high(); + // Give the trigger time to rise. + asm volatile(NOP100); + // Write provided data into SRAM. + for (int i = 0; i < 8; i++) { + mmio_region_write32(sram_region_main_addr, i * (ptrdiff_t)sizeof(uint32_t), + uj_data.data[i]); } + sca_set_trigger_low(); // Acknowledge test. ibex_sca_result_t uj_output; @@ -97,6 +277,162 @@ status_t handle_ibex_sca_tl_write(ujson_t *uj) { return OK_STATUS(); } +status_t handle_ibex_sca_tl_read_batch_fvsr_fix_address(ujson_t *uj) { + // Get number of iterations and fixed data. + ibex_sca_test_fvsr_t uj_data; + TRY(ujson_deserialize_ibex_sca_test_fvsr_t(uj, &uj_data)); + TRY_CHECK(uj_data.num_iterations < 256); + + // Generate FvsR values. + uint32_t values[256]; + generate_fvsr(uj_data.num_iterations, uj_data.fixed_data, values); + + // Get address of buffer located in SRAM. + uintptr_t sram_main_buffer_addr = (uintptr_t)&sram_main_buffer_batch; + mmio_region_t sram_region_main_addr = + mmio_region_from_addr(sram_main_buffer_addr); + + uint32_t read_data[256]; + + // SCA code target. + // Fetch data from SRAM. + for (size_t i = 0; i < uj_data.num_iterations; i++) { + mmio_region_write32(sram_region_main_addr, 0, values[i]); + asm volatile(NOP100); + sca_set_trigger_high(); + // Give the trigger time to rise. + asm volatile(NOP100); + read_data[i] = mmio_region_read32(sram_region_main_addr, 0); + sca_set_trigger_low(); + } + + // Write back last value read from SRAM to validate generated data. + ibex_sca_result_t uj_output; + uj_output.result = read_data[uj_data.num_iterations - 1]; + RESP_OK(ujson_serialize_ibex_sca_result_t, uj, &uj_output); + return OK_STATUS(); +} + +status_t handle_ibex_sca_tl_read_batch_fvsr(ujson_t *uj) { + // Get number of iterations and fixed data. + ibex_sca_test_fvsr_t uj_data; + TRY(ujson_deserialize_ibex_sca_test_fvsr_t(uj, &uj_data)); + TRY_CHECK(uj_data.num_iterations < 256); + + // Generate FvsR values. + uint32_t values[256]; + generate_fvsr(uj_data.num_iterations, uj_data.fixed_data, values); + + // Get address of buffer located in SRAM. + uintptr_t sram_main_buffer_addr = (uintptr_t)&sram_main_buffer_batch; + mmio_region_t sram_region_main_addr = + mmio_region_from_addr(sram_main_buffer_addr); + + // Write provided data into SRAM. + for (int i = 0; i < uj_data.num_iterations; i++) { + mmio_region_write32(sram_region_main_addr, i * (ptrdiff_t)sizeof(uint32_t), + values[i]); + } + + uint32_t read_data[256]; + + // SCA code target. + // Fetch data from SRAM. + for (int i = 0; i < uj_data.num_iterations; i++) { + sca_set_trigger_high(); + // Give the trigger time to rise. + asm volatile(NOP100); + read_data[i] = mmio_region_read32(sram_region_main_addr, + i * (ptrdiff_t)sizeof(uint32_t)); + sca_set_trigger_low(); + asm volatile(NOP100); + } + + // Write back last value read from SRAM to validate generated data. + ibex_sca_result_t uj_output; + uj_output.result = read_data[uj_data.num_iterations - 1]; + RESP_OK(ujson_serialize_ibex_sca_result_t, uj, &uj_output); + return OK_STATUS(); +} + +status_t handle_ibex_sca_tl_read_batch_random_fix_address(ujson_t *uj) { + // Get number of iterations. + ibex_sca_batch_t uj_data; + TRY(ujson_deserialize_ibex_sca_batch_t(uj, &uj_data)); + TRY_CHECK(uj_data.num_iterations < 256); + + // Generate random values. + uint32_t values[256]; + generate_random(uj_data.num_iterations, values); + + // Get address of buffer located in SRAM. + uintptr_t sram_main_buffer_addr = (uintptr_t)&sram_main_buffer_batch; + mmio_region_t sram_region_main_addr = + mmio_region_from_addr(sram_main_buffer_addr); + + uint32_t read_data[256]; + // SCA code target. + // Fetch data from SRAM. + for (size_t i = 0; i < uj_data.num_iterations; i++) { + mmio_region_write32(sram_region_main_addr, 0, values[i]); + asm volatile(NOP100); + sca_set_trigger_high(); + // Give the trigger time to rise. + asm volatile(NOP100); + read_data[i] = mmio_region_read32(sram_region_main_addr, 0); + sca_set_trigger_low(); + } + + // Write back last value read from SRAM to validate generated data. + ibex_sca_result_t uj_output; + uj_output.result = read_data[uj_data.num_iterations - 1]; + RESP_OK(ujson_serialize_ibex_sca_result_t, uj, &uj_output); + return OK_STATUS(); +} + +status_t handle_ibex_sca_tl_read_batch_random(ujson_t *uj) { + // Get number of iterations. + ibex_sca_batch_t uj_data; + TRY(ujson_deserialize_ibex_sca_batch_t(uj, &uj_data)); + TRY_CHECK(uj_data.num_iterations < 256); + + // Generate random values. + uint32_t values[256]; + generate_random(uj_data.num_iterations, values); + + // Get address of buffer located in SRAM. + uintptr_t sram_main_buffer_addr = (uintptr_t)&sram_main_buffer_batch; + mmio_region_t sram_region_main_addr = + mmio_region_from_addr(sram_main_buffer_addr); + + // Write provided data into SRAM. + for (int i = 0; i < uj_data.num_iterations; i++) { + mmio_region_write32(sram_region_main_addr, i * (ptrdiff_t)sizeof(uint32_t), + values[i]); + } + + uint32_t read_data[256]; + + // SCA code target. + + // Fetch data from SRAM. + for (int i = 0; i < uj_data.num_iterations; i++) { + sca_set_trigger_high(); + // Give the trigger time to rise. + asm volatile(NOP100); + read_data[i] = mmio_region_read32(sram_region_main_addr, + i * (ptrdiff_t)sizeof(uint32_t)); + sca_set_trigger_low(); + asm volatile(NOP100); + } + + // Write back last value read from SRAM to validate generated data. + ibex_sca_result_t uj_output; + uj_output.result = read_data[uj_data.num_iterations - 1]; + RESP_OK(ujson_serialize_ibex_sca_result_t, uj, &uj_output); + return OK_STATUS(); +} + status_t handle_ibex_sca_tl_read(ujson_t *uj) { // Get data to write into SRAM. ibex_sca_test_data_t uj_data; @@ -116,15 +452,15 @@ status_t handle_ibex_sca_tl_read(ujson_t *uj) { uint32_t read_data[8]; // SCA code target. - for (int it = 0; it < uj_data.num_iterations; it++) { - sca_set_trigger_high(); - // Fetch data from SRAM. - for (int i = 0; i < 8; i++) { - read_data[i] = mmio_region_read32(sram_region_main_addr, - i * (ptrdiff_t)sizeof(uint32_t)); - } - sca_set_trigger_low(); + sca_set_trigger_high(); + // Give the trigger time to rise. + asm volatile(NOP100); + // Fetch data from SRAM. + for (int i = 0; i < 8; i++) { + read_data[i] = mmio_region_read32(sram_region_main_addr, + i * (ptrdiff_t)sizeof(uint32_t)); } + sca_set_trigger_low(); // Acknowledge test. ibex_sca_result_t uj_output; uj_output.result = 0; @@ -132,26 +468,146 @@ status_t handle_ibex_sca_tl_read(ujson_t *uj) { return OK_STATUS(); } +status_t handle_ibex_sca_register_file_write_batch_fvsr(ujson_t *uj) { + // Get number of iterations and fixed data. + ibex_sca_test_fvsr_t uj_data; + TRY(ujson_deserialize_ibex_sca_test_fvsr_t(uj, &uj_data)); + TRY_CHECK(uj_data.num_iterations < 256); + + // Generate FvsR values. + uint32_t values[256]; + generate_fvsr(uj_data.num_iterations, uj_data.fixed_data, values); + + // SCA code target. + for (size_t i = 0; i < uj_data.num_iterations; i++) { + sca_set_trigger_high(); + // Give the trigger time to rise. + asm volatile(NOP100); + // Write provided data into register file. + copy_to_registers(values[i], values[i], values[i], values[i], values[i], + values[i], values[i]); + sca_set_trigger_low(); + asm volatile(NOP100); + } + + // Write back last value written into the RF to validate generated data. + ibex_sca_result_t uj_output; + uj_output.result = values[uj_data.num_iterations - 1]; + RESP_OK(ujson_serialize_ibex_sca_result_t, uj, &uj_output); + return OK_STATUS(); +} + +status_t handle_ibex_sca_register_file_write_batch_random(ujson_t *uj) { + // Get number of iterations. + ibex_sca_batch_t uj_data; + TRY(ujson_deserialize_ibex_sca_batch_t(uj, &uj_data)); + TRY_CHECK(uj_data.num_iterations < 256); + + // Generate random values. + uint32_t values[256]; + generate_random(uj_data.num_iterations, values); + + // SCA code target. + for (size_t i = 0; i < uj_data.num_iterations; i++) { + sca_set_trigger_high(); + // Give the trigger time to rise. + asm volatile(NOP100); + // Write provided data into register file. + copy_to_registers(values[i], values[i], values[i], values[i], values[i], + values[i], values[i]); + sca_set_trigger_low(); + asm volatile(NOP100); + } + + // Write back last value written into the RF to validate generated data. + ibex_sca_result_t uj_output; + uj_output.result = values[uj_data.num_iterations - 1]; + RESP_OK(ujson_serialize_ibex_sca_result_t, uj, &uj_output); + return OK_STATUS(); +} + status_t handle_ibex_sca_register_file_write(ujson_t *uj) { // Get data to write into RF. ibex_sca_test_data_t uj_data; TRY(ujson_deserialize_ibex_sca_test_data_t(uj, &uj_data)); + // SCA code target. - for (int it = 0; it < uj_data.num_iterations; it++) { + sca_set_trigger_high(); + // Give the trigger time to rise. + asm volatile(NOP100); + // Write provided data into register file. + copy_to_registers(uj_data.data[0], uj_data.data[1], uj_data.data[2], + uj_data.data[3], uj_data.data[4], uj_data.data[5], + uj_data.data[6]); + sca_set_trigger_low(); + + // Acknowledge test. + ibex_sca_result_t uj_output; + uj_output.result = 0; + RESP_OK(ujson_serialize_ibex_sca_result_t, uj, &uj_output); + return OK_STATUS(); +} + +status_t handle_ibex_sca_register_file_read_batch_fvsr(ujson_t *uj) { + // Get number of iterations and fixed data. + ibex_sca_test_fvsr_t uj_data; + TRY(ujson_deserialize_ibex_sca_test_fvsr_t(uj, &uj_data)); + TRY_CHECK(uj_data.num_iterations < 256); + + // Generate FvsR values. + uint32_t values[256]; + generate_fvsr(uj_data.num_iterations, uj_data.fixed_data, values); + + for (size_t i = 0; i < uj_data.num_iterations; i++) { + // Initialize temporary registers with reference values. + copy_to_registers(0, 0, 0, values[i], values[i], values[i], values[i]); + asm volatile(NOP100); + // SCA code target. sca_set_trigger_high(); - // Write provided data into register file. - asm volatile("mv x5, %0" : : "r"(uj_data.data[0])); - asm volatile("mv x6, %0" : : "r"(uj_data.data[1])); - asm volatile("mv x7, %0" : : "r"(uj_data.data[2])); - asm volatile("mv x28, %0" : : "r"(uj_data.data[3])); - asm volatile("mv x29, %0" : : "r"(uj_data.data[4])); - asm volatile("mv x30, %0" : : "r"(uj_data.data[5])); - asm volatile("mv x31, %0" : : "r"(uj_data.data[6])); + // Give the trigger time to rise. + asm volatile(NOP100); + // Copy registers. + asm volatile("mv x5, x28"); + asm volatile("mv x6, x29"); + asm volatile("mv x7, x30"); sca_set_trigger_low(); } - // Acknowledge test. + + // Write back last value written into the RF to validate generated data. ibex_sca_result_t uj_output; - uj_output.result = 0; + uj_output.result = values[uj_data.num_iterations - 1]; + RESP_OK(ujson_serialize_ibex_sca_result_t, uj, &uj_output); + return OK_STATUS(); +} + +status_t handle_ibex_sca_register_file_read_batch_random(ujson_t *uj) { + // Get number of iterations. + ibex_sca_batch_t uj_data; + TRY(ujson_deserialize_ibex_sca_batch_t(uj, &uj_data)); + TRY_CHECK(uj_data.num_iterations < 256); + + // Generate random values. + uint32_t values[256]; + generate_random(uj_data.num_iterations, values); + + for (size_t i = 0; i < uj_data.num_iterations; i++) { + // Initialize temporary registers with reference values. + copy_to_registers(0, 0, 0, values[i], values[i], values[i], values[i]); + asm volatile(NOP100); + // SCA code target. + sca_set_trigger_high(); + // Give the trigger time to rise. + asm volatile(NOP100); + // Copy registers. + asm volatile("mv x5, x28"); + asm volatile("mv x6, x29"); + asm volatile("mv x7, x30"); + sca_set_trigger_low(); + } + + // Write back last value written into the RF to validate generated data. + ibex_sca_result_t uj_output; + uj_output.result = values[uj_data.num_iterations - 1]; RESP_OK(ujson_serialize_ibex_sca_result_t, uj, &uj_output); return OK_STATUS(); } @@ -161,22 +617,19 @@ status_t handle_ibex_sca_register_file_read(ujson_t *uj) { ibex_sca_test_data_t uj_data; TRY(ujson_deserialize_ibex_sca_test_data_t(uj, &uj_data)); // Initialize temporary registers with reference values. - asm volatile("mv x5, %0" : : "r"(uj_data.data[0])); - asm volatile("mv x6, %0" : : "r"(uj_data.data[1])); - asm volatile("mv x7, %0" : : "r"(uj_data.data[2])); - asm volatile("mv x28, %0" : : "r"(uj_data.data[3])); - asm volatile("mv x29, %0" : : "r"(uj_data.data[4])); - asm volatile("mv x30, %0" : : "r"(uj_data.data[5])); + copy_to_registers(uj_data.data[0], uj_data.data[1], uj_data.data[2], + uj_data.data[3], uj_data.data[4], uj_data.data[5], 0); // SCA code target. - for (int it = 0; it < uj_data.num_iterations; it++) { - sca_set_trigger_high(); - // Copy registers. - asm volatile("mv x28, x5"); - asm volatile("mv x29, x6"); - asm volatile("mv x30, x7"); - sca_set_trigger_low(); - } + sca_set_trigger_high(); + // Give the trigger time to rise. + asm volatile(NOP100); + // Copy registers. + asm volatile("mv x28, x5"); + asm volatile("mv x29, x6"); + asm volatile("mv x30, x7"); + sca_set_trigger_low(); + // Acknowledge test. ibex_sca_result_t uj_output; uj_output.result = 0; @@ -205,14 +658,38 @@ status_t handle_ibex_sca(ujson_t *uj) { return handle_ibex_sca_init(uj); case kIbexScaSubcommandRFRead: return handle_ibex_sca_register_file_read(uj); + case kIbexScaSubcommandRFReadBatchRandom: + return handle_ibex_sca_register_file_read_batch_random(uj); + case kIbexScaSubcommandRFReadBatchFvsr: + return handle_ibex_sca_register_file_read_batch_fvsr(uj); case kIbexScaSubcommandRFWrite: return handle_ibex_sca_register_file_write(uj); + case kIbexScaSubcommandRFWriteBatchRandom: + return handle_ibex_sca_register_file_write_batch_random(uj); + case kIbexScaSubcommandRFWriteBatchFvsr: + return handle_ibex_sca_register_file_write_batch_fvsr(uj); case kIbexScaSubcommandTLRead: return handle_ibex_sca_tl_read(uj); + case kIbexScaSubcommandTLReadBatchRandom: + return handle_ibex_sca_tl_read_batch_random(uj); + case kIbexScaSubcommandTLReadBatchRandomFixAddress: + return handle_ibex_sca_tl_read_batch_random_fix_address(uj); + case kIbexScaSubcommandTLReadBatchFvsr: + return handle_ibex_sca_tl_read_batch_fvsr(uj); + case kIbexScaSubcommandTLReadBatchFvsrFixAddress: + return handle_ibex_sca_tl_read_batch_fvsr_fix_address(uj); case kIbexScaSubcommandTLWrite: return handle_ibex_sca_tl_write(uj); case kIbexScaSubcommandKeySideloading: return handle_ibex_sca_key_sideloading(uj); + case kIbexScaSubcommandTLWriteBatchRandom: + return handle_ibex_sca_tl_write_batch_random(uj); + case kIbexScaSubcommandTLWriteBatchRandomFixAddress: + return handle_ibex_sca_tl_write_batch_random_fix_address(uj); + case kIbexScaSubcommandTLWriteBatchFvsr: + return handle_ibex_sca_tl_write_batch_fvsr(uj); + case kIbexScaSubcommandTLWriteBatchFvsrFixAddress: + return handle_ibex_sca_tl_write_batch_fvsr_fix_address(uj); default: LOG_ERROR("Unrecognized IBEX SCA subcommand: %d", cmd); return INVALID_ARGUMENT(); diff --git a/sw/device/tests/penetrationtests/firmware/ibex_sca.h b/sw/device/tests/penetrationtests/firmware/ibex_sca.h index 9518cff7ef24e..8194f2655e4de 100644 --- a/sw/device/tests/penetrationtests/firmware/ibex_sca.h +++ b/sw/device/tests/penetrationtests/firmware/ibex_sca.h @@ -26,9 +26,61 @@ status_t handle_ibex_sca_key_sideloading(ujson_t *uj); /** - * ibex.sca.tl_write + * ibex.sca.tl_write_batch_fvsr_fix_address + * + * This SCA penetration test executes the following instructions: + * - Generate num_iteration FvsR values using the software LFSR. + * - Loop num_iterations: + * - Set trigger + * - Write data over TL-UL into a single position in SRAM. + * - Unset trigger + * + * SCA traces are captured during trigger_high & trigger_low. + * + * @param uj An initialized uJSON context. + * @return OK or error. + */ +status_t handle_ibex_sca_tl_write_batch_fvsr_fix_address(ujson_t *uj); + +/** + * ibex.sca.tl_write_batch_fvsr + * + * This SCA penetration test executes the following instructions: + * - Generate num_iteration FvsR values using the software LFSR. + * - Loop num_iterations: + * - Set trigger + * - Write data over TL-UL into SRAM. + * - Unset trigger + * + * SCA traces are captured during trigger_high & trigger_low. + * + * @param uj An initialized uJSON context. + * @return OK or error. + */ +status_t handle_ibex_sca_tl_write_batch_fvsr(ujson_t *uj); + +/** + * ibex.sca.tl_write_batch_random_fix_address + * + * This SCA penetration test executes the following instructions: + * - Generate num_iteration random values using the software LFSR. + * - Loop num_iterations: + * - Set trigger + * - Write data over TL-UL into a single position in SRAM. + * - Unset trigger + * + * SCA traces are captured during trigger_high & trigger_low. + * + * @param uj An initialized uJSON context. + * @return OK or error. + */ +status_t handle_ibex_sca_tl_write_batch_random_fix_address(ujson_t *uj); + +/** + * ibex.sca.tl_write_batch_random * * This SCA penetration test executes the following instructions: + * - Generate num_iteration random values using the software LFSR. * - Loop num_iterations: * - Set trigger * - Write data over TL-UL into SRAM. @@ -39,13 +91,91 @@ status_t handle_ibex_sca_key_sideloading(ujson_t *uj); * @param uj An initialized uJSON context. * @return OK or error. */ +status_t handle_ibex_sca_tl_write_batch_random(ujson_t *uj); + +/** + * ibex.sca.tl_write + * + * This SCA penetration test executes the following instructions: + * - Set trigger + * - Write data over TL-UL into SRAM. + * - Unset trigger + * + * SCA traces are captured during trigger_high & trigger_low. + * + * @param uj An initialized uJSON context. + * @return OK or error. + */ status_t handle_ibex_sca_tl_write(ujson_t *uj); +/** + * ibex.sca.tl_read_batch_fvsr_fix_address + * + * This SCA penetration test executes the following instructions: + * - Generate num_iteration FvsR values using the software LFSR. + * - Set trigger + * - Read data from a single address in SRAM over TL-UL. + * - Unset trigger + * + * SCA traces are captured during trigger_high & trigger_low. + * + * @param uj An initialized uJSON context. + * @return OK or error. + */ +status_t handle_ibex_sca_tl_read_batch_fvsr_fix_address(ujson_t *uj); + +/** + * ibex.sca.tl_read_batch_fvsr + * + * This SCA penetration test executes the following instructions: + * - Generate num_iteration FvsR values using the software LFSR. + * - Set trigger + * - Read data from SRAM over TL-UL. + * - Unset trigger + * + * SCA traces are captured during trigger_high & trigger_low. + * + * @param uj An initialized uJSON context. + * @return OK or error. + */ +status_t handle_ibex_sca_tl_read_batch_fvsr(ujson_t *uj); + +/** + * ibex.sca.tl_read_batch_random_fix_address + * + * This SCA penetration test executes the following instructions: + * - Generate num_iteration random values using the software LFSR. + * - Set trigger + * - Read data from a single address in SRAM over TL-UL. + * - Unset trigger + * + * SCA traces are captured during trigger_high & trigger_low. + * + * @param uj An initialized uJSON context. + * @return OK or error. + */ +status_t handle_ibex_sca_tl_read_batch_random_fix_address(ujson_t *uj); + +/** + * ibex.sca.tl_read_batch_random + * + * This SCA penetration test executes the following instructions: + * - Generate num_iteration random values using the software LFSR. + * - Set trigger + * - Read data from SRAM over TL-UL. + * - Unset trigger + * + * SCA traces are captured during trigger_high & trigger_low. + * + * @param uj An initialized uJSON context. + * @return OK or error. + */ +status_t handle_ibex_sca_tl_read_batch_random(ujson_t *uj); + /** * ibex.sca.tl_read * * This SCA penetration test executes the following instructions: - * - Loop num_iterations: * - Set trigger * - Read data from SRAM over TL-UL. * - Unset trigger @@ -57,11 +187,42 @@ status_t handle_ibex_sca_tl_write(ujson_t *uj); */ status_t handle_ibex_sca_tl_read(ujson_t *uj); +/** + * ibex.sca.register_file_write_batch_fvsr + * + * This SCA penetration test executes the following instructions: + * - Generate num_iteration FvsR values using the software LFSR. + * - Set trigger + * - Write random data to registers in RF + * - Unset trigger + * + * SCA traces are captured during trigger_high & trigger_low. + * + * @param uj An initialized uJSON context. + * @return OK or error. + */ +status_t handle_ibex_sca_register_file_write_batch_fvsr(ujson_t *uj); + +/** + * ibex.sca.register_file_write_batch_random + * + * This SCA penetration test executes the following instructions: + * - Generate num_iteration random values using the software LFSR. + * - Set trigger + * - Write random data to registers in RF + * - Unset trigger + * + * SCA traces are captured during trigger_high & trigger_low. + * + * @param uj An initialized uJSON context. + * @return OK or error. + */ +status_t handle_ibex_sca_register_file_write_batch_random(ujson_t *uj); + /** * ibex.sca.register_file_write * * This SCA penetration test executes the following instructions: - * - Loop num_iterations: * - Set trigger * - Write provided data to registers in RF * - Unset trigger @@ -73,6 +234,40 @@ status_t handle_ibex_sca_tl_read(ujson_t *uj); */ status_t handle_ibex_sca_register_file_write(ujson_t *uj); +/** + * ibex.sca.register_file_read_batch_fvsr + * + * This SCA penetration test executes the following instructions: + * - Generate num_iteration FvsR values using the software LFSR. + * - Loop num_iterations: + * - Set trigger + * - Read data from RF + * - Unset trigger + * + * SCA traces are captured during trigger_high & trigger_low. + * + * @param uj An initialized uJSON context. + * @return OK or error. + */ +status_t handle_ibex_sca_register_file_read_batch_fvsr(ujson_t *uj); + +/** + * ibex.sca.register_file_read_batch_random + * + * This SCA penetration test executes the following instructions: + * - Generate num_iteration random values using the software LFSR. + * - Loop num_iterations: + * - Set trigger + * - Read data from RF + * - Unset trigger + * + * SCA traces are captured during trigger_high & trigger_low. + * + * @param uj An initialized uJSON context. + * @return OK or error. + */ +status_t handle_ibex_sca_register_file_read_batch_random(ujson_t *uj); + /** * ibex.sca.register_file_read * diff --git a/sw/device/tests/penetrationtests/json/ibex_fi_commands.h b/sw/device/tests/penetrationtests/json/ibex_fi_commands.h index de16804d76da9..b43fd717d6e6b 100644 --- a/sw/device/tests/penetrationtests/json/ibex_fi_commands.h +++ b/sw/device/tests/penetrationtests/json/ibex_fi_commands.h @@ -23,6 +23,7 @@ extern "C" { value(_, CharUncondBranch) \ value(_, CharSramWrite) \ value(_, CharSramRead) \ + value(_, CharSramStatic) \ value(_, CharFlashWrite) \ value(_, CharFlashRead) \ value(_, CharCsrRead) \ @@ -57,6 +58,12 @@ UJSON_SERDE_STRUCT(IbexFiLoopCounterOutput, ibex_fi_loop_counter_t, IBEXFI_LOOP_ field(alerts, uint32_t) UJSON_SERDE_STRUCT(IbexFiLoopCounterMirroredOutput, ibex_fi_loop_counter_mirrored_t, IBEXFI_LOOP_COUNTER_MIRRORED_OUTPUT); +#define IBEXFI_FAULTY_ADDRESSES(field, string) \ + field(err_status, uint32_t) \ + field(addresses, uint32_t, 8) \ + field(alerts, uint32_t) +UJSON_SERDE_STRUCT(IbexFiFaultyAddresses, ibex_fi_faulty_addresses_t, IBEXFI_FAULTY_ADDRESSES); + // clang-format on #ifdef __cplusplus diff --git a/sw/device/tests/penetrationtests/json/ibex_sca_commands.h b/sw/device/tests/penetrationtests/json/ibex_sca_commands.h index e80430926f127..e7e1ea46c7d3e 100644 --- a/sw/device/tests/penetrationtests/json/ibex_sca_commands.h +++ b/sw/device/tests/penetrationtests/json/ibex_sca_commands.h @@ -14,14 +14,25 @@ extern "C" { #define IBEXSCA_SUBCOMMAND(_, value) \ value(_, Init) \ value(_, RFRead) \ + value(_, RFReadBatchRandom) \ + value(_, RFReadBatchFvsr) \ value(_, RFWrite) \ + value(_, RFWriteBatchRandom) \ + value(_, RFWriteBatchFvsr) \ + value(_, KeySideloading) \ value(_, TLRead) \ + value(_, TLReadBatchRandom) \ + value(_, TLReadBatchRandomFixAddress) \ + value(_, TLReadBatchFvsr) \ + value(_, TLReadBatchFvsrFixAddress) \ value(_, TLWrite) \ - value(_, KeySideloading) + value(_, TLWriteBatchRandom) \ + value(_, TLWriteBatchRandomFixAddress) \ + value(_, TLWriteBatchFvsr) \ + value(_, TLWriteBatchFvsrFixAddress) UJSON_SERDE_ENUM(IbexScaSubcommand, ibex_sca_subcommand_t, IBEXSCA_SUBCOMMAND); #define IBEXSCA_TEST_DATA(field, string) \ - field(num_iterations, uint32_t) \ field(data, uint32_t, 8) UJSON_SERDE_STRUCT(IbexScaTestData, ibex_sca_test_data_t, IBEXSCA_TEST_DATA); @@ -34,10 +45,19 @@ UJSON_SERDE_STRUCT(IbexScaSalt, ibex_sca_salt_t, IBEXSCA_SALT); field(share1, uint32_t, 8) UJSON_SERDE_STRUCT(IbexScaKey, ibex_sca_key_t, IBEXSCA_KEY); +#define IBEXSCA_TEST_FVSR(field, string) \ + field(num_iterations, uint32_t) \ + field(fixed_data, uint32_t) +UJSON_SERDE_STRUCT(IbexScaTestFvsr, ibex_sca_test_fvsr_t, IBEXSCA_TEST_FVSR); + #define IBEXSCA_RESULT(field, string) \ field(result, uint32_t) UJSON_SERDE_STRUCT(IbexScaResult, ibex_sca_result_t, IBEXSCA_RESULT); +#define IBEXSCA_BATCH(field, string) \ + field(num_iterations, uint32_t) +UJSON_SERDE_STRUCT(IbexScaBatch, ibex_sca_batch_t, IBEXSCA_BATCH); + // clang-format on #ifdef __cplusplus