Skip to content

Commit

Permalink
[pentest] Add OTP FI tests
Browse files Browse the repository at this point in the history
This commit pulls over the following tests from the pentest branch of
nasahlpa/opentitan that has been used for the penetration testing:
- OTP LC FI
- OTP HW CFG FI
- OTP SW CFG FI
- OTP Vendor FI

The tests have been developed by @wettermo.

Signed-off-by: Pascal Nasahl <nasahlpa@lowrisc.org>
Co-authored-by: Moritz Wettermann <moritz.wettermann@gi-de.com>
  • Loading branch information
2 people authored and andreaskurth committed Sep 5, 2024
1 parent 3e047f4 commit 9226336
Show file tree
Hide file tree
Showing 10 changed files with 493 additions and 0 deletions.
2 changes: 2 additions & 0 deletions sw/device/tests/penetrationtests/firmware/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ FIRMWARE_DEPS_FPGA = [
"//sw/device/tests/penetrationtests/firmware/fi:ibex_fi",
"//sw/device/tests/penetrationtests/firmware/fi:lc_ctrl_fi",
"//sw/device/tests/penetrationtests/firmware/fi:otbn_fi",
"//sw/device/tests/penetrationtests/firmware/fi:otp_fi",
"//sw/device/tests/penetrationtests/firmware/fi:rng_fi",
"//sw/device/tests/penetrationtests/firmware/fi:rom_fi",
"//sw/device/tests/penetrationtests/firmware/sca:aes_sca",
Expand Down Expand Up @@ -43,6 +44,7 @@ FIRMWARE_DEPS_FI = [
"//sw/device/tests/penetrationtests/firmware/fi:ibex_fi",
"//sw/device/tests/penetrationtests/firmware/fi:lc_ctrl_fi",
"//sw/device/tests/penetrationtests/firmware/fi:otbn_fi",
"//sw/device/tests/penetrationtests/firmware/fi:otp_fi",
"//sw/device/tests/penetrationtests/firmware/fi:rng_fi",
"//sw/device/tests/penetrationtests/firmware/fi:rom_fi",
"//sw/device/tests/penetrationtests/firmware/lib:extclk_sca_fi",
Expand Down
18 changes: 18 additions & 0 deletions sw/device/tests/penetrationtests/firmware/fi/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,24 @@ cc_library(
],
)

cc_library(
name = "otp_fi",
srcs = ["otp_fi.c"],
hdrs = ["otp_fi.h"],
deps = [
"//sw/device/lib/base:memory",
"//sw/device/lib/base:status",
"//sw/device/lib/dif:otp_ctrl",
"//sw/device/lib/runtime:log",
"//sw/device/lib/testing:otp_ctrl_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:otp_fi_commands",
],
)

cc_library(
name = "rom_fi",
srcs = ["rom_fi.c"],
Expand Down
320 changes: 320 additions & 0 deletions sw/device/tests/penetrationtests/firmware/fi/otp_fi.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,320 @@
// 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/tests/penetrationtests/firmware/fi/otp_fi.h"

#include "sw/device/lib/base/memory.h"
#include "sw/device/lib/base/status.h"
#include "sw/device/lib/dif/dif_otp_ctrl.h"
#include "sw/device/lib/runtime/log.h"
#include "sw/device/lib/testing/otp_ctrl_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/otp_fi_commands.h"

#include "hw/top_earlgrey/sw/autogen/top_earlgrey.h"
#include "otp_ctrl_regs.h" // Generated.

// 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
#define NOP1000 \
NOP100 NOP100 NOP100 NOP100 NOP100 NOP100 NOP100 NOP100 NOP100 NOP100

static dif_otp_ctrl_t otp;

uint32_t
otp_read32_result_vendor_test_comp[OTP_CTRL_PARAM_VENDOR_TEST_SIZE / 4];
uint32_t otp_read32_result_vendor_test_fi[OTP_CTRL_PARAM_VENDOR_TEST_SIZE / 4];
uint32_t
otp_read32_result_owner_sw_cfg_comp[OTP_CTRL_PARAM_OWNER_SW_CFG_SIZE / 4];
uint32_t
otp_read32_result_owner_sw_cfg_fi[OTP_CTRL_PARAM_OWNER_SW_CFG_SIZE / 4];
uint32_t otp_read32_result_hw_cfg_comp[OTP_CTRL_PARAM_HW_CFG0_SIZE / 4];
uint32_t otp_read32_result_hw_cfg_fi[OTP_CTRL_PARAM_HW_CFG0_SIZE / 4];
uint32_t otp_read32_result_life_cycle_comp[OTP_CTRL_PARAM_LIFE_CYCLE_SIZE / 4];
uint32_t otp_read32_result_life_cycle_fi[OTP_CTRL_PARAM_LIFE_CYCLE_SIZE / 4];

void init_otp_mem_dump_buffers(void) {
for (uint32_t i = 0; i < OTP_CTRL_PARAM_VENDOR_TEST_SIZE / 4; i++) {
otp_read32_result_vendor_test_comp[i] = 0x00000001;
otp_read32_result_vendor_test_fi[i] = 0x00000001;
}
for (uint32_t i = 0; i < OTP_CTRL_PARAM_OWNER_SW_CFG_SIZE / 4; i++) {
otp_read32_result_owner_sw_cfg_comp[i] = 0x00000001;
otp_read32_result_owner_sw_cfg_fi[i] = 0x00000001;
}
for (uint32_t i = 0; i < OTP_CTRL_PARAM_HW_CFG0_SIZE / 4; i++) {
otp_read32_result_hw_cfg_comp[i] = 0x00000001;
otp_read32_result_hw_cfg_fi[i] = 0x00000001;
}
for (uint32_t i = 0; i < OTP_CTRL_PARAM_LIFE_CYCLE_SIZE / 4; i++) {
otp_read32_result_life_cycle_comp[i] = 0x00000001;
otp_read32_result_life_cycle_fi[i] = 0x00000001;
}
}

status_t otp_vendor_test_dump(uint32_t *buffer) {
// Read VENDOR_TEST partition
TRY(otp_ctrl_testutils_dai_read32_array(&otp, kDifOtpCtrlPartitionVendorTest,
0, buffer,
OTP_CTRL_PARAM_VENDOR_TEST_SIZE / 4));

return OK_STATUS();
}

status_t otp_owner_sw_cfg_dump(uint32_t *buffer) {
// Read OWNER_SW_CFG partition
TRY(otp_ctrl_testutils_dai_read32_array(
&otp, kDifOtpCtrlPartitionOwnerSwCfg, 0, buffer,
OTP_CTRL_PARAM_OWNER_SW_CFG_SIZE / 4));

return OK_STATUS();
}

status_t otp_hw_cfg_dump(uint32_t *buffer) {
// Read HW_CFG partition
TRY(otp_ctrl_testutils_dai_read32_array(&otp, kDifOtpCtrlPartitionHwCfg0, 0,
buffer,
OTP_CTRL_PARAM_HW_CFG0_SIZE / 4));

return OK_STATUS();
}

status_t otp_life_cycle_dump(uint32_t *buffer) {
// Read LIFE_CYCLE partition
TRY(otp_ctrl_testutils_dai_read32_array(&otp, kDifOtpCtrlPartitionLifeCycle,
0, buffer,
OTP_CTRL_PARAM_LIFE_CYCLE_SIZE / 4));

return OK_STATUS();
}

status_t handle_otp_fi_hw_cfg(ujson_t *uj) {
// Clear registered alerts in alert handler.
sca_registered_alerts_t reg_alerts = sca_get_triggered_alerts();

// Read OTP partition for comparison values
TRY(otp_hw_cfg_dump(otp_read32_result_hw_cfg_comp));

// FI code target.
sca_set_trigger_high();

// Point for FI
asm volatile(NOP1000);
asm volatile(NOP1000);
asm volatile(NOP1000);
asm volatile(NOP1000);

sca_set_trigger_low();

// Read OTP partition again to see if values changed
TRY(otp_hw_cfg_dump(otp_read32_result_hw_cfg_fi));

// Get registered alerts from alert handler.
reg_alerts = sca_get_triggered_alerts();

// Get OTP CTRL status
dif_otp_ctrl_status_t status;
TRY(dif_otp_ctrl_get_status(&otp, &status));

// Send result & status codes to host.
otp_fi_hwcfg_partition_t uj_output;
for (uint32_t i = 0; i < OTP_CTRL_PARAM_HW_CFG0_SIZE / 4; i++) {
uj_output.hw_cfg_comp[i] = otp_read32_result_hw_cfg_comp[i];
uj_output.hw_cfg_fi[i] = otp_read32_result_hw_cfg_fi[i];
}
uj_output.otp_status_codes = status.codes;
memcpy(uj_output.otp_error_causes, (uint8_t *)status.causes,
kDifOtpCtrlStatusCodeHasCauseLast + 1);
uj_output.alerts[0] = reg_alerts.alerts[0];
uj_output.alerts[1] = reg_alerts.alerts[1];
uj_output.alerts[2] = reg_alerts.alerts[2];
RESP_OK(ujson_serialize_otp_fi_hwcfg_partition_t, uj, &uj_output);

return OK_STATUS();
}

status_t handle_otp_fi_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 | kScaPeripheralEdn | kScaPeripheralCsrng |
kScaPeripheralEntropy | kScaPeripheralAes | kScaPeripheralHmac |
kScaPeripheralKmac | kScaPeripheralOtbn);

// Configure the alert handler. Alerts triggered by IP blocks are captured
// and reported to the test.
sca_configure_alert_handler();

// Disable the instruction cache and dummy instructions for FI attacks.
sca_configure_cpu();

TRY(dif_otp_ctrl_init(
mmio_region_from_addr(TOP_EARLGREY_OTP_CTRL_CORE_BASE_ADDR), &otp));

init_otp_mem_dump_buffers();

return OK_STATUS();
}

status_t handle_otp_fi_life_cycle(ujson_t *uj) {
// Clear registered alerts in alert handler.
sca_registered_alerts_t reg_alerts = sca_get_triggered_alerts();

// Read OTP partition for comparison values
TRY(otp_life_cycle_dump(otp_read32_result_life_cycle_comp));

// FI code target.
sca_set_trigger_high();

// Point for FI
asm volatile(NOP1000);
asm volatile(NOP1000);
asm volatile(NOP1000);
asm volatile(NOP1000);

sca_set_trigger_low();

// Read OTP partition again to see if values changed
TRY(otp_life_cycle_dump(otp_read32_result_life_cycle_fi));

// Get registered alerts from alert handler.
reg_alerts = sca_get_triggered_alerts();

// Get OTP CTRL status
dif_otp_ctrl_status_t status;
TRY(dif_otp_ctrl_get_status(&otp, &status));

// Send result & status codes to host.
otp_fi_lifecycle_partition_t uj_output;
for (uint32_t i = 0; i < OTP_CTRL_PARAM_LIFE_CYCLE_SIZE / 4; i++) {
uj_output.life_cycle_comp[i] = otp_read32_result_life_cycle_comp[i];
uj_output.life_cycle_fi[i] = otp_read32_result_life_cycle_fi[i];
}
uj_output.otp_status_codes = status.codes;
memcpy(uj_output.otp_error_causes, (uint8_t *)status.causes,
kDifOtpCtrlStatusCodeHasCauseLast + 1);
uj_output.alerts[0] = reg_alerts.alerts[0];
uj_output.alerts[1] = reg_alerts.alerts[1];
uj_output.alerts[2] = reg_alerts.alerts[2];
RESP_OK(ujson_serialize_otp_fi_lifecycle_partition_t, uj, &uj_output);

return OK_STATUS();
}

status_t handle_otp_fi_owner_sw_cfg(ujson_t *uj) {
// Clear registered alerts in alert handler.
sca_registered_alerts_t reg_alerts = sca_get_triggered_alerts();

// Read OTP partition for comparison values
TRY(otp_owner_sw_cfg_dump(otp_read32_result_owner_sw_cfg_comp));

// FI code target.
sca_set_trigger_high();

// Point for FI
asm volatile(NOP1000);
asm volatile(NOP1000);
asm volatile(NOP1000);
asm volatile(NOP1000);

sca_set_trigger_low();

// Read OTP partition again to see if values changed
TRY(otp_owner_sw_cfg_dump(otp_read32_result_owner_sw_cfg_fi));

// Get registered alerts from alert handler.
reg_alerts = sca_get_triggered_alerts();

// Get OTP CTRL status
dif_otp_ctrl_status_t status;
TRY(dif_otp_ctrl_get_status(&otp, &status));

// Send result & status codes to host.
otp_fi_ownerswcfg_partition_t uj_output;
for (uint32_t i = 0; i < OTP_CTRL_PARAM_OWNER_SW_CFG_SIZE / 4; i++) {
uj_output.owner_sw_cfg_comp[i] = otp_read32_result_owner_sw_cfg_comp[i];
uj_output.owner_sw_cfg_fi[i] = otp_read32_result_owner_sw_cfg_fi[i];
}
uj_output.otp_status_codes = status.codes;
memcpy(uj_output.otp_error_causes, (uint8_t *)status.causes,
kDifOtpCtrlStatusCodeHasCauseLast + 1);
uj_output.alerts[0] = reg_alerts.alerts[0];
uj_output.alerts[1] = reg_alerts.alerts[1];
uj_output.alerts[2] = reg_alerts.alerts[2];
RESP_OK(ujson_serialize_otp_fi_ownerswcfg_partition_t, uj, &uj_output);

return OK_STATUS();
}

status_t handle_otp_fi_vendor_test(ujson_t *uj) {
// Clear registered alerts in alert handler.
sca_registered_alerts_t reg_alerts = sca_get_triggered_alerts();

// Read OTP partition for comparison values
TRY(otp_vendor_test_dump(otp_read32_result_vendor_test_comp));

// FI code target.
sca_set_trigger_high();

// Point for FI
asm volatile(NOP1000);
asm volatile(NOP1000);
asm volatile(NOP1000);
asm volatile(NOP1000);

sca_set_trigger_low();

// Read OTP partition again to see if values changed
TRY(otp_vendor_test_dump(otp_read32_result_vendor_test_fi));

// Get registered alerts from alert handler.
reg_alerts = sca_get_triggered_alerts();

// Get OTP CTRL status
dif_otp_ctrl_status_t status;
TRY(dif_otp_ctrl_get_status(&otp, &status));

// Send result & status codes to host.
otp_fi_vendortest_partition_t uj_output;
for (uint32_t i = 0; i < OTP_CTRL_PARAM_VENDOR_TEST_SIZE / 4; i++) {
uj_output.vendor_test_comp[i] = otp_read32_result_vendor_test_comp[i];
uj_output.vendor_test_fi[i] = otp_read32_result_vendor_test_fi[i];
}
uj_output.otp_status_codes = status.codes;
memcpy(uj_output.otp_error_causes, (uint8_t *)status.causes,
kDifOtpCtrlStatusCodeHasCauseLast + 1);
uj_output.alerts[0] = reg_alerts.alerts[0];
uj_output.alerts[1] = reg_alerts.alerts[1];
uj_output.alerts[2] = reg_alerts.alerts[2];
RESP_OK(ujson_serialize_otp_fi_vendortest_partition_t, uj, &uj_output);

return OK_STATUS();
}

status_t handle_otp_fi(ujson_t *uj) {
otp_fi_subcommand_t cmd;
TRY(ujson_deserialize_otp_fi_subcommand_t(uj, &cmd));
switch (cmd) {
case kOtpFiSubcommandInit:
return handle_otp_fi_init(uj);
case kOtpFiSubcommandVendorTest:
return handle_otp_fi_vendor_test(uj);
case kOtpFiSubcommandOwnerSwCfg:
return handle_otp_fi_owner_sw_cfg(uj);
case kOtpFiSubcommandHwCfg:
return handle_otp_fi_hw_cfg(uj);
case kOtpFiSubcommandLifeCycle:
return handle_otp_fi_life_cycle(uj);
default:
LOG_ERROR("Unrecognized OTP FI subcommand: %d", cmd);
return INVALID_ARGUMENT();
}
return OK_STATUS();
}
Loading

0 comments on commit 9226336

Please sign in to comment.