From b4d17c9821dcaa5a9479ca8c009b71aebcab8fbf Mon Sep 17 00:00:00 2001 From: Pascal Nasahl Date: Fri, 9 Aug 2024 15:25:19 +0200 Subject: [PATCH] [pentest] Add RNG FI tests This commit pulls over the following tests from the pentest_edn branch of nasahlpa/opentitan that has been used for the penetration testing: - rng_fi_entropy_src_bias - rng_fi_firmware_override - rng_fi_edn_bias - rng_fi_edn_resp_ack - csrng_bias Signed-off-by: Pascal Nasahl --- .../tests/penetrationtests/firmware/BUILD | 2 + .../tests/penetrationtests/firmware/fi/BUILD | 25 + .../penetrationtests/firmware/fi/rng_fi.c | 535 ++++++++++++++++++ .../penetrationtests/firmware/fi/rng_fi.h | 100 ++++ .../penetrationtests/firmware/firmware.c | 5 + .../penetrationtests/firmware/firmware_fi.c | 5 + sw/device/tests/penetrationtests/json/BUILD | 7 + .../tests/penetrationtests/json/commands.h | 1 + .../penetrationtests/json/rng_fi_commands.c | 6 + .../penetrationtests/json/rng_fi_commands.h | 66 +++ 10 files changed, 752 insertions(+) create mode 100644 sw/device/tests/penetrationtests/firmware/fi/rng_fi.c create mode 100644 sw/device/tests/penetrationtests/firmware/fi/rng_fi.h create mode 100644 sw/device/tests/penetrationtests/json/rng_fi_commands.c create mode 100644 sw/device/tests/penetrationtests/json/rng_fi_commands.h diff --git a/sw/device/tests/penetrationtests/firmware/BUILD b/sw/device/tests/penetrationtests/firmware/BUILD index 9f95a30c66593..7605ba398929c 100644 --- a/sw/device/tests/penetrationtests/firmware/BUILD +++ b/sw/device/tests/penetrationtests/firmware/BUILD @@ -14,6 +14,7 @@ package(default_visibility = ["//visibility:public"]) FIRMWARE_DEPS_FPGA = [ "//sw/device/tests/penetrationtests/firmware/fi:ibex_fi", "//sw/device/tests/penetrationtests/firmware/fi:otbn_fi", + "//sw/device/tests/penetrationtests/firmware/fi:rng_fi", "//sw/device/tests/penetrationtests/firmware/sca:aes_sca", "//sw/device/tests/penetrationtests/firmware/sca:ibex_sca", "//sw/device/tests/penetrationtests/firmware/sca:kmac_sca", @@ -36,6 +37,7 @@ FIRMWARE_DEPS_FPGA = [ FIRMWARE_DEPS_FI = [ "//sw/device/tests/penetrationtests/firmware/fi:ibex_fi", "//sw/device/tests/penetrationtests/firmware/fi:otbn_fi", + "//sw/device/tests/penetrationtests/firmware/fi:rng_fi", "//sw/device/tests/penetrationtests/firmware/lib:extclk_sca_fi", "//sw/device/lib/base:csr", "//sw/device/lib/base:status", diff --git a/sw/device/tests/penetrationtests/firmware/fi/BUILD b/sw/device/tests/penetrationtests/firmware/fi/BUILD index f7cca8939149d..3ae4040cc8997 100644 --- a/sw/device/tests/penetrationtests/firmware/fi/BUILD +++ b/sw/device/tests/penetrationtests/firmware/fi/BUILD @@ -50,3 +50,28 @@ cc_library( "//sw/device/tests/penetrationtests/json:otbn_fi_commands", ], ) + +cc_library( + name = "rng_fi", + srcs = ["rng_fi.c"], + hdrs = ["rng_fi.h"], + deps = [ + "//sw/device/lib/base:memory", + "//sw/device/lib/base:status", + "//sw/device/lib/dif:csrng", + "//sw/device/lib/dif:csrng_shared", + "//sw/device/lib/dif:edn", + "//sw/device/lib/dif:entropy_src", + "//sw/device/lib/dif:rv_core_ibex", + "//sw/device/lib/runtime:log", + "//sw/device/lib/testing:csrng_testutils", + "//sw/device/lib/testing:edn_testutils", + "//sw/device/lib/testing:entropy_testutils", + "//sw/device/lib/testing:rv_core_ibex_testutils", + "//sw/device/lib/testing/test_framework:ujson_ottf", + "//sw/device/lib/ujson", + "//sw/device/sca/lib:sca", + "//sw/device/tests/penetrationtests/firmware/lib:sca_lib", + "//sw/device/tests/penetrationtests/json:rng_fi_commands", + ], +) diff --git a/sw/device/tests/penetrationtests/firmware/fi/rng_fi.c b/sw/device/tests/penetrationtests/firmware/fi/rng_fi.c new file mode 100644 index 0000000000000..0d86b35c41b3e --- /dev/null +++ b/sw/device/tests/penetrationtests/firmware/fi/rng_fi.c @@ -0,0 +1,535 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +#include "sw/device/lib/base/memory.h" +#include "sw/device/lib/base/status.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/log.h" +#include "sw/device/lib/testing/csrng_testutils.h" +#include "sw/device/lib/testing/edn_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/ujson_ottf.h" +#include "sw/device/lib/ujson/ujson.h" +#include "sw/device/sca/lib/sca.h" +#include "sw/device/tests/penetrationtests/firmware/lib/sca_lib.h" +#include "sw/device/tests/penetrationtests/json/rng_fi_commands.h" + +#include "edn_regs.h" // Generated +#include "hw/top_earlgrey/sw/autogen/top_earlgrey.h" + +// NOP macros. +#define NOP1 "addi x0, x0, 0\n" +#define NOP10 NOP1 NOP1 NOP1 NOP1 NOP1 NOP1 NOP1 NOP1 NOP1 NOP1 +#define NOP30 NOP10 NOP10 NOP10 + +enum { + kEdnKatTimeout = (10 * 1000 * 1000), + kCsrngExpectedOutputLen = 16, + kEdnBusAckMaxData = 64, + kEdnBiasMaxData = 16, + kEdnKatMaxClen = 12, + kEdnKatOutputLen = 16, + kEdnKatWordsPerBlock = 4, + kEntropyFifoBufferSize = 32, + kMaxReadCountNotBlocking = 32, +}; + +static dif_rv_core_ibex_t rv_core_ibex; +static dif_entropy_src_t entropy_src; +static dif_csrng_t csrng; +static dif_edn_t edn0; +static dif_edn_t edn1; +static bool disable_health_check; + +static bool firmware_override_init; + +// Seed material for the EDN KAT instantiate command. +static const dif_edn_seed_material_t kEdnKatSeedMaterialInstantiate = { + .len = kEdnKatMaxClen, + .data = {0x73bec010, 0x9262474c, 0x16a30f76, 0x531b51de, 0x2ee494e5, + 0xdfec9db3, 0xcb7a879d, 0x5600419c, 0xca79b0b0, 0xdda33b5c, + 0xa468649e, 0xdf5d73fa}, +}; +// Seed material for the EDN KAT reseed command. +static const dif_edn_seed_material_t kEdnKatSeedMaterialReseed = { + .len = kEdnKatMaxClen, + .data = {0x73bec010, 0x9262474c, 0x16a30f76, 0x531b51de, 0x2ee494e5, + 0xdfec9db3, 0xcb7a879d, 0x5600419c, 0xca79b0b0, 0xdda33b5c, + 0xa468649e, 0xdf5d73fa}, +}; +// Seed material for the EDN KAT generate command. +static const dif_edn_seed_material_t kEdnKatSeedMaterialGenerate = { + .len = 0, +}; + +static dif_edn_auto_params_t kat_auto_params_build(void) { + return (dif_edn_auto_params_t){ + .instantiate_cmd = + { + .cmd = csrng_cmd_header_build(kCsrngAppCmdInstantiate, + kDifCsrngEntropySrcToggleDisable, + kEdnKatSeedMaterialInstantiate.len, + /*generate_len=*/0), + .seed_material = kEdnKatSeedMaterialInstantiate, + }, + .reseed_cmd = + { + .cmd = csrng_cmd_header_build(kCsrngAppCmdReseed, + kDifCsrngEntropySrcToggleDisable, + kEdnKatSeedMaterialReseed.len, + /*generate_len=*/0), + .seed_material = kEdnKatSeedMaterialReseed, + }, + .generate_cmd = + { + .cmd = csrng_cmd_header_build( + kCsrngAppCmdGenerate, kDifCsrngEntropySrcToggleDisable, + kEdnKatSeedMaterialGenerate.len, + /*generate_len=*/ + kEdnKatOutputLen / kEdnKatWordsPerBlock), + .seed_material = kEdnKatSeedMaterialGenerate, + }, + .reseed_interval = 32, + }; +} + +static const uint32_t kExpectedOutput[kEdnKatOutputLen] = { + 0xe48bb8cb, 0x1012c84c, 0x5af8a7f1, 0xd1c07cd9, 0xdf82ab22, 0x771c619b, + 0xd40fccb1, 0x87189e99, 0x510494b3, 0x64f7ac0c, 0x2581f391, 0x80b1dc2f, + 0x793e01c5, 0x87b107ae, 0xdb17514c, 0xa43c41b7, +}; + +/** + * Flushes the data entropy buffer until there is no data available to read. + * + * Asserts test error if any of the returned words is equal to zero. Logs the + * number of entropy words flushed in a single call. + * + * @param entropy An entropy source instance. + */ +static void entropy_data_flush(dif_entropy_src_t *entropy_src) { + uint32_t entropy_bits; + uint32_t read_count = 0; + + // TODO: Remove this limit. Entropy source should block if there is no entropy + // available in FW override mode. + const uint32_t kMaxReadCount = 128; + + while (dif_entropy_src_is_entropy_available(entropy_src) == kDifOk) { + CHECK_DIF_OK(dif_entropy_src_non_blocking_read(entropy_src, &entropy_bits)); + CHECK(entropy_bits != 0); + read_count++; + if (read_count >= kMaxReadCount) { + break; + } + } +} + +status_t handle_rng_fi_entropy_src_bias(ujson_t *uj) { + // Clear registered alerts in alert handler. + sca_registered_alerts_t reg_alerts = sca_get_triggered_alerts(); + + TRY(dif_entropy_src_set_enabled(&entropy_src, kDifToggleDisabled)); + + // Setup fips grade entropy that can be read by firmware. + const dif_entropy_src_config_t config = { + .fips_enable = true, + .route_to_firmware = true, + .single_bit_mode = kDifEntropySrcSingleBitModeDisabled, + .health_test_threshold_scope = false, /*default*/ + .health_test_window_size = 0x0200, /*default*/ + .alert_threshold = 2, /*default*/ + }; + + // Re-enable entropy src. + TRY(dif_entropy_src_configure(&entropy_src, config, kDifToggleEnabled)); + // ensure health tests are actually running + TRY(entropy_testutils_wait_for_state( + &entropy_src, kDifEntropySrcMainFsmStateContHTRunning)); + + entropy_data_flush(&entropy_src); + + uint32_t entropy_bits[kMaxReadCountNotBlocking] = {0}; + + sca_set_trigger_high(); + asm volatile(NOP30); + for (size_t it = 0; it < kMaxReadCountNotBlocking; it++) { + while (dif_entropy_src_non_blocking_read(&entropy_src, &entropy_bits[it]) != + kDifOk) + ; + } + asm volatile(NOP30); + sca_set_trigger_low(); + + // Get registered alerts from alert handler. + reg_alerts = sca_get_triggered_alerts(); + + // Read ERR_STATUS register from Ibex. + dif_rv_core_ibex_error_status_t err_ibx; + TRY(dif_rv_core_ibex_get_error_status(&rv_core_ibex, &err_ibx)); + + // Send result & ERR_STATUS to host. + rng_fi_entropy_src_bias_t uj_output; + // Send result & ERR_STATUS to host. + memcpy(uj_output.rand, entropy_bits, sizeof(entropy_bits)); + uj_output.err_status = err_ibx; + memcpy(uj_output.alerts, reg_alerts.alerts, sizeof(reg_alerts.alerts)); + RESP_OK(ujson_serialize_rng_fi_entropy_src_bias_t, uj, &uj_output); + + return OK_STATUS(); +} + +status_t handle_rng_fi_firmware_override(ujson_t *uj) { + // Clear registered alerts in alert handler. + sca_registered_alerts_t reg_alerts = sca_get_triggered_alerts(); + + if (!firmware_override_init) { + // Check if we keep heal tests enabled. + rng_fi_fw_overwrite_health_t uj_data; + TRY(ujson_deserialize_rng_fi_fw_overwrite_health_t(uj, &uj_data)); + disable_health_check = uj_data.disable_health_check; + + firmware_override_init = true; + } + + TRY(entropy_testutils_stop_all()); + + if (disable_health_check) { + // Disable all health tests. + TRY(entropy_testutils_disable_health_tests(&entropy_src)); + } + + TRY(entropy_testutils_fw_override_enable(&entropy_src, kEntropyFifoBufferSize, + /*route_to_firmware=*/true, + /*bypass_conditioner=*/true)); + + entropy_data_flush(&entropy_src); + + uint32_t buf[kEntropyFifoBufferSize] = {0}; + + sca_set_trigger_high(); + asm volatile(NOP30); + for (size_t it = 0; it < kEntropyFifoBufferSize; it++) { + while (buf[it] == 0) { + TRY(dif_entropy_src_observe_fifo_blocking_read(&entropy_src, &buf[it], + kEntropyFifoBufferSize)); + } + } + + asm volatile(NOP30); + sca_set_trigger_low(); + + // Get registered alerts from alert handler. + reg_alerts = sca_get_triggered_alerts(); + + // Read ERR_STATUS register from Ibex. + dif_rv_core_ibex_error_status_t err_ibx; + TRY(dif_rv_core_ibex_get_error_status(&rv_core_ibex, &err_ibx)); + + // Send result & ERR_STATUS to host. + rng_fi_fw_overwrite_t uj_output; + // Send result & ERR_STATUS to host. + memcpy(uj_output.rand, buf, sizeof(buf)); + uj_output.err_status = err_ibx; + memcpy(uj_output.alerts, reg_alerts.alerts, sizeof(reg_alerts.alerts)); + RESP_OK(ujson_serialize_rng_fi_fw_overwrite_t, uj, &uj_output); + + return OK_STATUS(); +} + +status_t handle_rng_fi_edn_bias(ujson_t *uj) { + // Clear registered alerts in alert handler. + sca_registered_alerts_t reg_alerts = sca_get_triggered_alerts(); + + TRY(dif_entropy_src_init( + mmio_region_from_addr(TOP_EARLGREY_ENTROPY_SRC_BASE_ADDR), &entropy_src)); + TRY(dif_csrng_init(mmio_region_from_addr(TOP_EARLGREY_CSRNG_BASE_ADDR), + &csrng)); + 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_rv_core_ibex_init( + mmio_region_from_addr(TOP_EARLGREY_RV_CORE_IBEX_CFG_BASE_ADDR), + &rv_core_ibex)); + + dif_edn_auto_params_t edn_params = + edn_testutils_auto_params_build(true, /*res_itval=*/0, /*glen_val=*/0); + // Disable the entropy complex. + TRY(entropy_testutils_stop_all()); + // Enable ENTROPY_SRC in FIPS mode. + TRY(dif_entropy_src_configure( + &entropy_src, entropy_testutils_config_default(), kDifToggleEnabled)); + // Enable CSRNG. + TRY(dif_csrng_configure(&csrng)); + // Enable EDN1 in auto request mode. + TRY(dif_edn_set_auto_mode(&edn1, edn_params)); + // Enable EDN0 in auto request mode. + TRY(dif_edn_set_auto_mode(&edn0, kat_auto_params_build())); + + uint32_t ibex_rnd_data_got[kEdnBiasMaxData]; + + sca_set_trigger_high(); + asm volatile(NOP30); + for (size_t it = 0; it < kEdnBiasMaxData; it++) { + CHECK_STATUS_OK(rv_core_ibex_testutils_get_rnd_data( + &rv_core_ibex, kEdnKatTimeout, &ibex_rnd_data_got[it])); + } + asm volatile(NOP30); + sca_set_trigger_low(); + + rng_fi_edn_t uj_output; + memset(uj_output.rand, 0, sizeof(uj_output.rand)); + size_t collisions = 0; + for (size_t got = 0; got < kEdnBiasMaxData; got++) { + for (size_t ref = 0; ref < kEdnBiasMaxData; ref++) { + if (ibex_rnd_data_got[got] == kExpectedOutput[ref]) { + uj_output.rand[collisions] = ibex_rnd_data_got[got]; + collisions++; + } + } + } + + // Get registered alerts from alert handler. + reg_alerts = sca_get_triggered_alerts(); + + // Read ERR_STATUS register from Ibex. + dif_rv_core_ibex_error_status_t err_ibx; + TRY(dif_rv_core_ibex_get_error_status(&rv_core_ibex, &err_ibx)); + + // Send result & ERR_STATUS to host. + uj_output.collisions = collisions; + memcpy(uj_output.alerts, reg_alerts.alerts, sizeof(reg_alerts.alerts)); + uj_output.err_status = err_ibx; + RESP_OK(ujson_serialize_rng_fi_edn_t, uj, &uj_output); + return OK_STATUS(); +} + +status_t handle_rng_fi_edn_resp_ack(ujson_t *uj) { + // Clear registered alerts in alert handler. + sca_registered_alerts_t reg_alerts = sca_get_triggered_alerts(); + // Enable entropy complex, CSRNG and EDN so Ibex can get entropy. + // Configure entropy in auto_mode to avoid starving the system from entropy, + // given that boot mode entropy has a limited number of generated bits. + TRY(entropy_testutils_auto_mode_init()); + + uint32_t ibex_rnd_data[kEdnBusAckMaxData]; + + // Inject faults during generating and receiving random data. + // Goal is to manipulate ACK on bus to trigger that the same + // data chunk is transmitted multiple times. + sca_set_trigger_high(); + asm volatile(NOP30); + for (size_t it = 0; it < kEdnBusAckMaxData; it++) { + TRY(rv_core_ibex_testutils_get_rnd_data(&rv_core_ibex, kEdnKatTimeout, + &ibex_rnd_data[it])); + } + sca_set_trigger_low(); + + // Check if there are any collisions. + rng_fi_edn_t uj_output; + memset(uj_output.rand, 0, sizeof(uj_output.rand)); + size_t collisions = 0; + for (size_t outer = 0; outer < kEdnBusAckMaxData; outer++) { + for (size_t inner = 0; inner < kEdnBusAckMaxData; inner++) { + if (outer != inner && ibex_rnd_data[outer] == ibex_rnd_data[inner]) { + if (collisions < 16) { + uj_output.rand[collisions] = ibex_rnd_data[outer]; + } + collisions++; + } + } + } + + // Get registered alerts from alert handler. + reg_alerts = sca_get_triggered_alerts(); + + // Read ERR_STATUS register from Ibex. + dif_rv_core_ibex_error_status_t err_ibx; + TRY(dif_rv_core_ibex_get_error_status(&rv_core_ibex, &err_ibx)); + + // Send result & ERR_STATUS to host. + uj_output.collisions = collisions; + memcpy(uj_output.alerts, reg_alerts.alerts, sizeof(reg_alerts.alerts)); + uj_output.err_status = err_ibx; + RESP_OK(ujson_serialize_rng_fi_edn_t, uj, &uj_output); + return OK_STATUS(); +} + +status_t handle_rng_fi_edn_init(ujson_t *uj) { + sca_select_trigger_type(kScaTriggerTypeSw); + // As we are using the software defined trigger, the first argument of + // sca_init is not needed. kScaTriggerSourceAes is selected as a placeholder. + sca_init(kScaTriggerSourceAes, kScaPeripheralIoDiv4 | kScaPeripheralEntropy | + kScaPeripheralCsrng | kScaPeripheralEdn); + + // Disable the instruction cache and dummy instructions for FI attacks. + sca_configure_cpu(); + + // Configure Ibex to allow reading ERR_STATUS register. + TRY(dif_rv_core_ibex_init( + mmio_region_from_addr(TOP_EARLGREY_RV_CORE_IBEX_CFG_BASE_ADDR), + &rv_core_ibex)); + + // Configure the alert handler. Alerts triggered by IP blocks are captured + // and reported to the test. + sca_configure_alert_handler(); + + // Initialize peripherals used in this FI test. + TRY(dif_entropy_src_init( + mmio_region_from_addr(TOP_EARLGREY_ENTROPY_SRC_BASE_ADDR), &entropy_src)); + TRY(dif_csrng_init(mmio_region_from_addr(TOP_EARLGREY_CSRNG_BASE_ADDR), + &csrng)); + 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)); + + // Read device ID and return to host. + penetrationtest_device_id_t uj_output; + TRY(sca_read_device_id(uj_output.device_id)); + RESP_OK(ujson_serialize_penetrationtest_device_id_t, uj, &uj_output); + + firmware_override_init = false; + + return OK_STATUS(); +} + +status_t handle_rng_fi_csrng_bias(ujson_t *uj) { + // Get the test mode. + crypto_fi_csrng_mode_t uj_data; + TRY(ujson_deserialize_crypto_fi_csrng_mode_t(uj, &uj_data)); + // Clear registered alerts in alert handler. + sca_registered_alerts_t reg_alerts = sca_get_triggered_alerts(); + + TRY(csrng_testutils_cmd_ready_wait(&csrng)); + TRY(dif_csrng_uninstantiate(&csrng)); + + const dif_csrng_seed_material_t kEntropyInput = { + .seed_material = {0x73bec010, 0x9262474c, 0x16a30f76, 0x531b51de, + 0x2ee494e5, 0xdfec9db3, 0xcb7a879d, 0x5600419c, + 0xca79b0b0, 0xdda33b5c, 0xa468649e, 0xdf5d73fa}, + .seed_material_len = 12, + }; + + CHECK_DIF_OK(dif_csrng_instantiate(&csrng, kDifCsrngEntropySrcToggleDisable, + &kEntropyInput)); + + // FI code target. + uint32_t rand_data_got[kCsrngExpectedOutputLen]; + TRY(csrng_testutils_cmd_ready_wait(&csrng)); + + if (uj_data.all_trigger || uj_data.start_trigger) { + sca_set_trigger_high(); + } + TRY(dif_csrng_generate_start(&csrng, kCsrngExpectedOutputLen)); + if (uj_data.start_trigger) { + sca_set_trigger_low(); + } + + if (uj_data.valid_trigger) { + sca_set_trigger_high(); + } + dif_csrng_output_status_t output_status; + do { + TRY(dif_csrng_get_output_status(&csrng, &output_status)); + } while (!output_status.valid_data); + if (uj_data.valid_trigger) { + sca_set_trigger_low(); + } + + if (uj_data.read_trigger) { + sca_set_trigger_high(); + } + TRY(dif_csrng_generate_read(&csrng, rand_data_got, kCsrngExpectedOutputLen)); + if (uj_data.all_trigger || uj_data.read_trigger) { + sca_set_trigger_low(); + } + + // Get registered alerts from alert handler. + reg_alerts = sca_get_triggered_alerts(); + + // Read ERR_STATUS register from Ibex. + dif_rv_core_ibex_error_status_t err_ibx; + TRY(dif_rv_core_ibex_get_error_status(&rv_core_ibex, &err_ibx)); + + // Compare with expected data. + const uint32_t kExpectedOutput[kCsrngExpectedOutputLen] = { + 932170270, 3480632584, 387346064, 186012424, 899661374, 2795183089, + 336687633, 3222931513, 1490543709, 3319795384, 3464147855, 1850271046, + 1239323641, 2292604615, 3314177342, 1567494162, + }; + rng_fi_csrng_output_t uj_output; + uj_output.res = 0; + for (size_t it = 0; it < kCsrngExpectedOutputLen; it++) { + if (rand_data_got[it] != kExpectedOutput[it]) { + uj_output.res = 1; + } + } + + // Send result & ERR_STATUS to host. + memcpy(uj_output.rand, rand_data_got, sizeof(rand_data_got)); + uj_output.err_status = err_ibx; + memcpy(uj_output.alerts, reg_alerts.alerts, sizeof(reg_alerts.alerts)); + RESP_OK(ujson_serialize_rng_fi_csrng_output_t, uj, &uj_output); + + return OK_STATUS(); +} + +status_t handle_rng_fi_csrng_init(ujson_t *uj) { + sca_select_trigger_type(kScaTriggerTypeSw); + // As we are using the software defined trigger, the first argument of + // sca_init is not needed. kScaTriggerSourceAes is selected as a placeholder. + sca_init(kScaTriggerSourceAes, kScaPeripheralIoDiv4 | kScaPeripheralCsrng); + + // Disable the instruction cache and dummy instructions for FI attacks. + sca_configure_cpu(); + + // Configure Ibex to allow reading ERR_STATUS register. + TRY(dif_rv_core_ibex_init( + mmio_region_from_addr(TOP_EARLGREY_RV_CORE_IBEX_CFG_BASE_ADDR), + &rv_core_ibex)); + + // Configure the alert handler. Alerts triggered by IP blocks are captured + // and reported to the test. + sca_configure_alert_handler(); + + // Initialize CSRNG. + mmio_region_t base_addr = mmio_region_from_addr(TOP_EARLGREY_CSRNG_BASE_ADDR); + CHECK_DIF_OK(dif_csrng_init(base_addr, &csrng)); + CHECK_DIF_OK(dif_csrng_configure(&csrng)); + + // Read device ID and return to host. + penetrationtest_device_id_t uj_output; + TRY(sca_read_device_id(uj_output.device_id)); + RESP_OK(ujson_serialize_penetrationtest_device_id_t, uj, &uj_output); + + return OK_STATUS(); +} + +status_t handle_rng_fi(ujson_t *uj) { + rng_fi_subcommand_t cmd; + TRY(ujson_deserialize_rng_fi_subcommand_t(uj, &cmd)); + switch (cmd) { + case kRngFiSubcommandCsrngInit: + return handle_rng_fi_csrng_init(uj); + case kRngFiSubcommandCsrngBias: + return handle_rng_fi_csrng_bias(uj); + case kRngFiSubcommandEdnInit: + return handle_rng_fi_edn_init(uj); + case kRngFiSubcommandEdnRespAck: + return handle_rng_fi_edn_resp_ack(uj); + case kRngFiSubcommandEdnBias: + return handle_rng_fi_edn_bias(uj); + case kRngFiSubcommandFWOverride: + return handle_rng_fi_firmware_override(uj); + case kRngFiSubcommandEntropySrcBias: + return handle_rng_fi_entropy_src_bias(uj); + default: + LOG_ERROR("Unrecognized RNG FI subcommand: %d", cmd); + return INVALID_ARGUMENT(); + } + return OK_STATUS(); +} diff --git a/sw/device/tests/penetrationtests/firmware/fi/rng_fi.h b/sw/device/tests/penetrationtests/firmware/fi/rng_fi.h new file mode 100644 index 0000000000000..244e6b782b81a --- /dev/null +++ b/sw/device/tests/penetrationtests/firmware/fi/rng_fi.h @@ -0,0 +1,100 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +#ifndef OPENTITAN_SW_DEVICE_TESTS_PENETRATIONTESTS_FIRMWARE_FI_RNG_FI_H_ +#define OPENTITAN_SW_DEVICE_TESTS_PENETRATIONTESTS_FIRMWARE_FI_RNG_FI_H_ + +#include "sw/device/lib/base/status.h" +#include "sw/device/lib/ujson/ujson.h" + +/** + * rng_fi_entropy_src_bias command handler. + * + * Configures the entropy source module to return data on the ENTROPY_DATA + * register. The goal is to inject a fault such that a BIAS is visible. + * + * Faults are injected during the trigger_high & trigger_low. + * + * @param uj An initialized uJSON context. + * @return OK or error. + */ +status_t handle_rng_fi_entropy_src_bias(ujson_t *uj); + +/** + * rng_fi_firmware_override command handler. + * + * Configures the entropy source module with the firmware override mode allowing + * the test to directly access the generated entropy bits. + * + * Faults are injected during the trigger_high & trigger_low. + * + * @param uj An initialized uJSON context. + * @return OK or error. + */ +status_t handle_rng_fi_firmware_override(ujson_t *uj); + +/** + * rng_fi_edn_bias command handler. + * + * Induce a bias into the random data generated by the EDN. + * + * Faults are injected during the trigger_high & trigger_low. + * + * @param uj An initialized uJSON context. + * @return OK or error. + */ +status_t handle_rng_fi_edn_bias(ujson_t *uj); + +/** + * rng_fi_edn_resp_ack command handler. + * + * Generate random data. Try to inject fault during bus ack to receive the same + * random data multiple times. + * + * Faults are injected during the trigger_high & trigger_low. + * + * @param uj An initialized uJSON context. + * @return OK or error. + */ +status_t handle_rng_fi_edn_resp_ack(ujson_t *uj); + +/** + * Initializes the trigger and configures the device for the EDN FI test. + * + * @param uj An initialized uJSON context. + * @return OK or error. + */ +status_t handle_rng_fi_edn_init(ujson_t *uj); + +/** + * csrng_bias command handler. + * + * Generate known data using the CSRNG. + * + * Faults are injected during the trigger_high & trigger_low. + * + * @param uj An initialized uJSON context. + * @return OK or error. + */ +status_t handle_rng_fi_csrng_bias(ujson_t *uj); + +/** + * Initializes the trigger and configures the device for the CSRNG FI test. + * + * @param uj An initialized uJSON context. + * @return OK or error. + */ +status_t handle_rng_fi_csrng_init(ujson_t *uj); + +/** + * Rng FI command handler. + * + * Command handler for the Rng FI command. + * + * @param uj An initialized uJSON context. + * @return OK or error. + */ +status_t handle_rng_fi(ujson_t *uj); + +#endif // OPENTITAN_SW_DEVICE_TESTS_PENETRATIONTESTS_FIRMWARE_FI_RNG_FI_H_ diff --git a/sw/device/tests/penetrationtests/firmware/firmware.c b/sw/device/tests/penetrationtests/firmware/firmware.c index dd4cb340b12b7..dff27400088d4 100644 --- a/sw/device/tests/penetrationtests/firmware/firmware.c +++ b/sw/device/tests/penetrationtests/firmware/firmware.c @@ -19,12 +19,14 @@ #include "sw/device/tests/penetrationtests/json/kmac_sca_commands.h" #include "sw/device/tests/penetrationtests/json/otbn_fi_commands.h" #include "sw/device/tests/penetrationtests/json/prng_sca_commands.h" +#include "sw/device/tests/penetrationtests/json/rng_fi_commands.h" #include "sw/device/tests/penetrationtests/json/sha3_sca_commands.h" #include "sw/device/tests/penetrationtests/json/trigger_sca_commands.h" // Include handlers #include "fi/ibex_fi.h" #include "fi/otbn_fi.h" +#include "fi/rng_fi.h" #include "lib/extclk_sca_fi.h" #include "sca/aes_sca.h" #include "sca/ibex_sca.h" @@ -61,6 +63,9 @@ status_t process_cmd(ujson_t *uj) { case kPenetrationtestCommandPrngSca: RESP_ERR(uj, handle_prng_sca(uj)); break; + case kPenetrationtestCommandRngFi: + RESP_ERR(uj, handle_rng_fi(uj)); + break; case kPenetrationtestCommandSha3Sca: RESP_ERR(uj, handle_sha3_sca(uj)); break; diff --git a/sw/device/tests/penetrationtests/firmware/firmware_fi.c b/sw/device/tests/penetrationtests/firmware/firmware_fi.c index 266c090c13eee..14b083a80e52b 100644 --- a/sw/device/tests/penetrationtests/firmware/firmware_fi.c +++ b/sw/device/tests/penetrationtests/firmware/firmware_fi.c @@ -14,10 +14,12 @@ #include "sw/device/tests/penetrationtests/json/commands.h" #include "sw/device/tests/penetrationtests/json/ibex_fi_commands.h" #include "sw/device/tests/penetrationtests/json/otbn_fi_commands.h" +#include "sw/device/tests/penetrationtests/json/rng_fi_commands.h" // Include handlers #include "fi/ibex_fi.h" #include "fi/otbn_fi.h" +#include "fi/rng_fi.h" #include "lib/extclk_sca_fi.h" OTTF_DEFINE_TEST_CONFIG(.enable_uart_flow_control = true); @@ -36,6 +38,9 @@ status_t process_cmd(ujson_t *uj) { case kPenetrationtestCommandOtbnFi: RESP_ERR(uj, handle_otbn_fi(uj)); break; + case kPenetrationtestCommandRngFi: + RESP_ERR(uj, handle_rng_fi(uj)); + break; default: LOG_ERROR("Unrecognized command: %d", cmd); RESP_ERR(uj, INVALID_ARGUMENT()); diff --git a/sw/device/tests/penetrationtests/json/BUILD b/sw/device/tests/penetrationtests/json/BUILD index 29cbe6757e08b..9f6d969a124b2 100644 --- a/sw/device/tests/penetrationtests/json/BUILD +++ b/sw/device/tests/penetrationtests/json/BUILD @@ -70,6 +70,13 @@ cc_library( deps = ["//sw/device/lib/ujson"], ) +cc_library( + name = "rng_fi_commands", + srcs = ["rng_fi_commands.c"], + hdrs = ["rng_fi_commands.h"], + deps = ["//sw/device/lib/ujson"], +) + cc_library( name = "sca_lib_commands", srcs = ["sca_lib_commands.c"], diff --git a/sw/device/tests/penetrationtests/json/commands.h b/sw/device/tests/penetrationtests/json/commands.h index e9b7fa2d60366..1906dbbb7b4ee 100644 --- a/sw/device/tests/penetrationtests/json/commands.h +++ b/sw/device/tests/penetrationtests/json/commands.h @@ -19,6 +19,7 @@ extern "C" { value(_, KmacSca) \ value(_, OtbnFi) \ value(_, PrngSca) \ + value(_, RngFi) \ value(_, Sha3Sca) \ value(_, TriggerSca) UJSON_SERDE_ENUM(PenetrationtestCommand, penetrationtest_cmd_t, COMMAND); diff --git a/sw/device/tests/penetrationtests/json/rng_fi_commands.c b/sw/device/tests/penetrationtests/json/rng_fi_commands.c new file mode 100644 index 0000000000000..edaca4ecd1582 --- /dev/null +++ b/sw/device/tests/penetrationtests/json/rng_fi_commands.c @@ -0,0 +1,6 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +#define UJSON_SERDE_IMPL 1 +#include "rng_fi_commands.h" diff --git a/sw/device/tests/penetrationtests/json/rng_fi_commands.h b/sw/device/tests/penetrationtests/json/rng_fi_commands.h new file mode 100644 index 0000000000000..a46ebaae723a0 --- /dev/null +++ b/sw/device/tests/penetrationtests/json/rng_fi_commands.h @@ -0,0 +1,66 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +#ifndef OPENTITAN_SW_DEVICE_TESTS_PENETRATIONTESTS_JSON_RNG_FI_COMMANDS_H_ +#define OPENTITAN_SW_DEVICE_TESTS_PENETRATIONTESTS_JSON_RNG_FI_COMMANDS_H_ +#include "sw/device/lib/ujson/ujson_derive.h" +#ifdef __cplusplus +extern "C" { +#endif + +// clang-format off + +#define RNGFI_SUBCOMMAND(_, value) \ + value(_, CsrngInit) \ + value(_, CsrngBias) \ + value(_, EdnInit) \ + value(_, EdnRespAck) \ + value(_, EdnBias) \ + value(_, FWOverride) \ + value(_, EntropySrcBias) +UJSON_SERDE_ENUM(RngFiSubcommand, rng_fi_subcommand_t, RNGFI_SUBCOMMAND); + +#define CRYPTOFI_CSRNG_MODE(field, string) \ + field(start_trigger, bool) \ + field(valid_trigger, bool) \ + field(read_trigger, bool) \ + field(all_trigger, bool) +UJSON_SERDE_STRUCT(CryptoFiCsrngMode, crypto_fi_csrng_mode_t, CRYPTOFI_CSRNG_MODE); + +#define RNGFI_CSRNG_OUTPUT(field, string) \ + field(res, uint32_t) \ + field(rand, uint32_t, 16) \ + field(alerts, uint32_t, 3) \ + field(err_status, uint32_t) +UJSON_SERDE_STRUCT(RngFiCsrngOutput, rng_fi_csrng_output_t, RNGFI_CSRNG_OUTPUT); + +#define RNGFI_ENTRBIAS_OUTPUT(field, string) \ + field(rand, uint32_t, 32) \ + field(alerts, uint32_t, 3) \ + field(err_status, uint32_t) +UJSON_SERDE_STRUCT(RngFiEntrBiasOutput, rng_fi_entropy_src_bias_t, RNGFI_ENTRBIAS_OUTPUT); + +#define RNGFI_FWOVERWRITE_OUTPUT(field, string) \ + field(rand, uint32_t, 32) \ + field(alerts, uint32_t, 3) \ + field(err_status, uint32_t) +UJSON_SERDE_STRUCT(RngFiFwOverwriteOutput, rng_fi_fw_overwrite_t, RNGFI_FWOVERWRITE_OUTPUT); + +#define RNGFI_EDN(field, string) \ + field(collisions, uint32_t) \ + field(rand, uint32_t, 16) \ + field(alerts, uint32_t, 3) \ + field(err_status, uint32_t) +UJSON_SERDE_STRUCT(RngFiEdn, rng_fi_edn_t, RNGFI_EDN); + +#define RNGFI_FWOVERWRITE_HEALTH(field, string) \ + field(disable_health_check, bool) +UJSON_SERDE_STRUCT(RngFiFwOverwriteHealt, rng_fi_fw_overwrite_health_t, RNGFI_FWOVERWRITE_HEALTH); + +// clang-format on + +#ifdef __cplusplus +} +#endif +#endif // OPENTITAN_SW_DEVICE_TESTS_PENETRATIONTESTS_JSON_RNG_FI_COMMANDS_H_