Skip to content

Commit

Permalink
[pentest] Update EDN SCA tests
Browse files Browse the repository at this point in the history
This commit updates the EDN SCA tests to use asm instead of C
code such that the SCA target is more accurate.

Signed-off-by: Pascal Nasahl <[email protected]>
Co-authored-by: Alexander Wagner <[email protected]>
  • Loading branch information
nasahlpa and aewag committed Dec 12, 2024
1 parent b70973d commit 058acb2
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 183 deletions.
222 changes: 72 additions & 150 deletions sw/device/tests/penetrationtests/firmware/sca/edn_sca.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,19 @@

#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/ibex.h"
#include "sw/device/lib/runtime/log.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/ujson_ottf.h"
#include "sw/device/lib/ujson/ujson.h"
#include "sw/device/sca/lib/prng.h"
#include "sw/device/tests/penetrationtests/firmware/lib/pentest_lib.h"
#include "sw/device/tests/penetrationtests/json/edn_sca_commands.h"

#include "edn_regs.h" // Generated
#include "hw/top_earlgrey/sw/autogen/top_earlgrey.h"
#include "rv_core_ibex_regs.h" // Generated

// NOP macros.
#define NOP1 "addi x0, x0, 0\n"
Expand All @@ -38,165 +35,95 @@ enum {
};

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;

// Generate random values used by the test by calling the SCA PRNG.
static void generate_random(size_t num_values, uint32_t values[]) {
for (size_t i = 0; i < num_values; i++) {
values[i] = prng_rand_uint32();

/**
* Read randomness.
*
* The goal of this function is to allow the SCA setup to measure randomness
* transmitted from the 128-bit FIFO within the EDN to the Ibex RND_DATA
* register.
*
* To do so, this function first clears the 128-bit FIFO by reading it. When the
* FIFO is empty, the EDN sends a request to the CSRNG. When the FIFO again is
* full, set the SCA trigger and read the randomness from the FIFO to the
* RND_DATA register.
*
* @param ibex_rnd_data The array containing the randomness.
* @return OK or error.
*/
static status_t read_rnd_data_reg(uint32_t ibex_rnd_data[4]) {
// Clear the CSRNG FIFO containing randomness before starting the test.
for (size_t it = 0; it < 4; it++) {
TRY(dif_rv_core_ibex_read_rnd_data(&rv_core_ibex, &ibex_rnd_data[0]));
}
memset(ibex_rnd_data, 0, 4 * sizeof(uint32_t));
// Wait until RND_DATA_VALID becomes true, i.e., randomness is available.
bool rnd_data_valid = rv_core_ibex_testutils_is_rnd_data_valid(&rv_core_ibex);
while (!rnd_data_valid) {
rnd_data_valid = rv_core_ibex_testutils_is_rnd_data_valid(&rv_core_ibex);
}
}

// Configure the EDN with the provided seed material, set the SCA trigger,
// generate and receive random data, and unset the trigger.
static status_t config_run_edn(uint32_t init_seed[12], uint32_t reseed[12]) {
// Setup seed material.
// Seed material for the EDN instantiate command.
dif_edn_seed_material_t kEdnKatSeedMaterialInstantiate = {
.len = kEdnKatMaxClen,
.data = {init_seed[0], init_seed[1], init_seed[2], init_seed[3],
init_seed[4], init_seed[5], init_seed[6], init_seed[7],
init_seed[8], init_seed[9], init_seed[10], init_seed[11]}};
// Seed material for the EDN reseed command.
dif_edn_seed_material_t kEdnKatSeedMaterialReseed = {
.len = kEdnKatMaxClen,
.data = {reseed[0], reseed[1], reseed[2], reseed[3], reseed[4], reseed[5],
reseed[6], reseed[7], reseed[8], reseed[9], reseed[10],
reseed[11]}};
// Seed material for the EDN generate command.
const dif_edn_seed_material_t kEdnKatSeedMaterialGenerate = {
.len = 0,
};

dif_edn_auto_params_t edn_params;
edn_params.instantiate_cmd.cmd = csrng_cmd_header_build(
kCsrngAppCmdInstantiate, kDifCsrngEntropySrcToggleDisable,
kEdnKatSeedMaterialInstantiate.len, /*generate_len=*/0);
edn_params.instantiate_cmd.seed_material = kEdnKatSeedMaterialInstantiate;
edn_params.reseed_cmd.cmd = csrng_cmd_header_build(
kCsrngAppCmdReseed, kDifCsrngEntropySrcToggleDisable,
kEdnKatSeedMaterialReseed.len,
/*generate_len=*/0);
edn_params.reseed_cmd.seed_material = kEdnKatSeedMaterialReseed;
edn_params.generate_cmd.cmd = csrng_cmd_header_build(
kCsrngAppCmdGenerate, kDifCsrngEntropySrcToggleDisable,
kEdnKatSeedMaterialGenerate.len,
/*generate_len=*/
kEdnKatOutputLen / kEdnKatWordsPerBlock);

edn_params.generate_cmd.seed_material = kEdnKatSeedMaterialGenerate;
edn_params.reseed_interval = 32;

// 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 EDN0 in auto request mode.
TRY(dif_edn_set_auto_mode(&edn0, edn_params));

uint32_t ibex_rnd_data;

// Capture trace during generation and transportation of random data.
// Read RND_DATA register. First access contains randomness that already was
// transmitted over the bus. Afterwards, data needs to be transmitted from the
// randomness FIFO into the RND_DATA register - this is what we want to
// measure.
pentest_set_trigger_high();
asm volatile(NOP30);
TRY(rv_core_ibex_testutils_get_rnd_data(&rv_core_ibex, kEdnKatTimeout,
&ibex_rnd_data));
pentest_set_trigger_low();
asm volatile("li t0, %0"
:
: "i"(TOP_EARLGREY_RV_CORE_IBEX_CFG_BASE_ADDR)
: "t0");
asm volatile("lw t1, %0(t0)"
:
: "i"(RV_CORE_IBEX_RND_DATA_REG_OFFSET)
: "t1");
asm volatile(NOP30);
asm volatile(NOP30);
pentest_set_trigger_low();
// Read RND_DATA which was transmitted from FIFO into RND_DATA register.
// Read it after the trigger window to not measure load into Ibex register
TRY(dif_rv_core_ibex_read_rnd_data(&rv_core_ibex, &ibex_rnd_data[3]));

return OK_STATUS();
}

status_t handle_edn_sca_bus_data(ujson_t *uj) {
// Get seed material.
edn_sca_seed_t uj_data;
TRY(ujson_deserialize_edn_sca_seed_t(uj, &uj_data));

// Configure EDN with provided seed material, set trigger and start generating
// and fetching data.
config_run_edn(uj_data.init_seed, uj_data.reseed);

// Acknowledge test.
edn_sca_result_t uj_output;
uj_output.result = 0;
RESP_OK(ujson_serialize_edn_sca_result_t, uj, &uj_output);
return OK_STATUS();
}

status_t handle_edn_sca_bus_data_batch_fvsr(ujson_t *uj) {
// Get seed material.
edn_sca_seed_batch_t uj_data;
TRY(ujson_deserialize_edn_sca_seed_batch_t(uj, &uj_data));

bool sample_fixed = true;
uint32_t last_value = 0;

uint32_t init_seed[kNumBatchOpsMax][12];
uint32_t reseed[kNumBatchOpsMax][12];
status_t handle_edn_sca_bus_data_batch(ujson_t *uj) {
// Get number of iterations.
edn_sca_batch_t uj_data;
uint32_t max_iterations = 128;
TRY(ujson_deserialize_edn_sca_batch_t(uj, &uj_data));
CHECK(uj_data.num_iterations <= max_iterations);

// Start num_iterations trigger windows.
uint32_t rand_data[max_iterations][4];
for (size_t it = 0; it < uj_data.num_iterations; it++) {
if (sample_fixed) {
memcpy(init_seed[it], uj_data.init_seed, 12 * sizeof(uint32_t));
memcpy(reseed[it], uj_data.reseed, 12 * sizeof(uint32_t));
} else {
// Generate random seeds for the EDN configuration.
generate_random(12, init_seed[it]);
generate_random(12, reseed[it]);
}
sample_fixed = prng_rand_uint32() & 0x1;
TRY(read_rnd_data_reg(rand_data[it]));
}

// Send back num_iterations rand_data.
for (size_t it = 0; it < uj_data.num_iterations; it++) {
// Configure EDN with random seed material, set trigger and start generating
// and fetching data.
TRY(config_run_edn(init_seed[it], reseed[it]));
last_value = reseed[it][11];
edn_sca_result_t uj_output;
memcpy(&uj_output.rnd_data, rand_data[it], 4 * sizeof(uint32_t));
RESP_OK(ujson_serialize_edn_sca_result_t, uj, &uj_output);
}

// Acknowledge test, send last reseed value back to host
// for verification.
edn_sca_result_t uj_output;
uj_output.result = last_value;
RESP_OK(ujson_serialize_edn_sca_result_t, uj, &uj_output);
return OK_STATUS();
}

status_t handle_edn_sca_bus_data_batch_random(ujson_t *uj) {
// Get number of iterations.
edn_sca_batch_t uj_data;
TRY(ujson_deserialize_edn_sca_batch_t(uj, &uj_data));

uint32_t init_seed[kNumBatchOpsMax][12];
uint32_t reseed[kNumBatchOpsMax][12];
for (size_t it = 0; it < uj_data.num_iterations; it++) {
// Generate random seeds for the EDN configuration.
generate_random(12, init_seed[it]);
generate_random(12, reseed[it]);
}
status_t handle_edn_sca_bus_data(ujson_t *uj) {
edn_sca_result_t uj_output;

for (size_t it = 0; it < uj_data.num_iterations; it++) {
// Configure EDN with random seed material, set trigger and start generating
// and fetching data.
TRY(config_run_edn(init_seed[it], reseed[it]));
}
TRY(read_rnd_data_reg(uj_output.rnd_data));

// Acknowledge test, send last random reseed value back to host
// for verification.
edn_sca_result_t uj_output;
uj_output.result = reseed[uj_data.num_iterations - 1][11];
// Send data back to host.
RESP_OK(ujson_serialize_edn_sca_result_t, uj, &uj_output);
return OK_STATUS();
}

status_t handle_edn_pentest_init(ujson_t *uj) {
status_t handle_edn_sca_init(ujson_t *uj) {
pentest_select_trigger_type(kPentestTriggerTypeSw);
// As we are using the software defined trigger, the first argument of
// pentest_init is not needed. kPentestTriggerSourceAes is selected as a
// sca_init is not needed. kPentestTriggerSourceAes is selected as a
// placeholder.
pentest_init(kPentestTriggerSourceAes,
kPentestPeripheralIoDiv4 | kPentestPeripheralEntropy |
Expand All @@ -210,12 +137,9 @@ status_t handle_edn_pentest_init(ujson_t *uj) {
mmio_region_from_addr(TOP_EARLGREY_RV_CORE_IBEX_CFG_BASE_ADDR),
&rv_core_ibex));

// Initialize peripherals used in this SCA 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));
// Configure the entropy complex. Set the reseed interval to max to avoid
// reseed during the trigger window.
TRY(pentest_configure_entropy_source_max_reseed_interval());

return OK_STATUS();
}
Expand All @@ -224,14 +148,12 @@ status_t handle_edn_sca(ujson_t *uj) {
edn_sca_subcommand_t cmd;
TRY(ujson_deserialize_edn_sca_subcommand_t(uj, &cmd));
switch (cmd) {
case kEdnScaSubcommandInit:
return handle_edn_sca_init(uj);
case kEdnScaSubcommandBusData:
return handle_edn_sca_bus_data(uj);
case kEdnScaSubcommandBusDataBatchFvsr:
return handle_edn_sca_bus_data_batch_fvsr(uj);
case kEdnScaSubcommandBusDataBatchRandom:
return handle_edn_sca_bus_data_batch_random(uj);
case kEdnScaSubcommandInit:
return handle_edn_pentest_init(uj);
case kEdnScaSubcommandBusDataBatch:
return handle_edn_sca_bus_data_batch(uj);
default:
LOG_ERROR("Unrecognized EDN SCA subcommand: %d", cmd);
return INVALID_ARGUMENT();
Expand Down
26 changes: 8 additions & 18 deletions sw/device/tests/penetrationtests/firmware/sca/edn_sca.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,43 +9,33 @@
#include "sw/device/lib/ujson/ujson.h"

/**
* edn.sca.bus_data command handler.
*
* The goal of this penetration test is to capture traces when
* the EDN generated random data and transfers it to Ibex.
* edn.sca.bus_data_batch command handler.
*
* @param uj An initialized uJSON context.
* @return OK or error.
*/
status_t handle_edn_sca_bus_data(ujson_t *uj);

/**
* edn.sca.bus_data_batch_fvsr command handler.
*
* Batch version of edn.sca.bus_data with FvsR data.
* Batch version of edn.sca.bus_data with random data.
*
* @param uj An initialized uJSON context.
* @return OK or error.
*/
status_t handle_edn_sca_bus_data_batch_fvsr(ujson_t *uj);
status_t handle_edn_sca_bus_data_batch(ujson_t *uj);

/**
* edn.sca.bus_data_batch_random command handler.
* edn.sca.bus_data command handler.
*
* Batch version of edn.sca.bus_data with random data.
* The goal of this penetration test is to capture traces when
* randomness is transported over the bus to Ibex.
*
* @param uj An initialized uJSON context.
* @return OK or error.
*/
status_t handle_edn_sca_bus_data_batch_random(ujson_t *uj);
status_t handle_edn_sca_bus_data(ujson_t *uj);

/**
* Initializes the trigger and configures the device for the EDN SCA test.
*
* @param uj An initialized uJSON context.
* @return OK or error.
*/
status_t handle_edn_pentest_init(ujson_t *uj);
status_t handle_edn_sca_init(ujson_t *uj);

/**
* EDN SCA command handler.
Expand Down
18 changes: 3 additions & 15 deletions sw/device/tests/penetrationtests/json/edn_sca_commands.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,27 +12,15 @@ extern "C" {
// clang-format off

#define EDNSCA_SUBCOMMAND(_, value) \
value(_, Init) \
value(_, BusData) \
value(_, BusDataBatchFvsr) \
value(_, BusDataBatchRandom) \
value(_, Init)
value(_, BusDataBatch)
UJSON_SERDE_ENUM(EdnScaSubcommand, edn_sca_subcommand_t, EDNSCA_SUBCOMMAND);

#define EDNSCA_RESULT(field, string) \
field(result, uint32_t)
field(rnd_data, uint32_t, 4)
UJSON_SERDE_STRUCT(EdnScaResult, edn_sca_result_t, EDNSCA_RESULT);

#define EDNSCA_SEED(field, string) \
field(init_seed, uint32_t, 12) \
field(reseed, uint32_t, 12)
UJSON_SERDE_STRUCT(EdnScaSeed, edn_sca_seed_t, EDNSCA_SEED);

#define EDNSCA_SEED_BATCH(field, string) \
field(init_seed, uint32_t, 12) \
field(reseed, uint32_t, 12) \
field(num_iterations, uint32_t)
UJSON_SERDE_STRUCT(EdnScaSeedBatch, edn_sca_seed_batch_t, EDNSCA_SEED_BATCH);

#define EDNSCA_BATCH(field, string) \
field(num_iterations, uint32_t)
UJSON_SERDE_STRUCT(EdnScaBatch, edn_sca_batch_t, EDNSCA_BATCH);
Expand Down

0 comments on commit 058acb2

Please sign in to comment.