[sw/silicon_creator] Secure wipe DMEM and IMEM
Signed-off-by: Alphan Ulusoy <alphan@google.com>
diff --git a/sw/device/silicon_creator/lib/drivers/otbn.c b/sw/device/silicon_creator/lib/drivers/otbn.c
index 90a578d..560fa9f 100644
--- a/sw/device/silicon_creator/lib/drivers/otbn.c
+++ b/sw/device/silicon_creator/lib/drivers/otbn.c
@@ -82,8 +82,50 @@
HARDENED_CHECK_EQ(iter_cnt, num_words);
}
-void otbn_execute(void) {
- abs_mmio_write32(kBase + OTBN_CMD_REG_OFFSET, kOtbnCmdExecute);
+/**
+ * Helper function for running an OTBN command.
+ *
+ * This function blocks until OTBN is idle.
+ *
+ * @param cmd OTBN command.
+ * @param exp_status Expected OTBN status after issuing the command.
+ * @param error Error to return if operation fails.
+ * @return Result of the operation.
+ */
+static rom_error_t otbn_cmd_run(otbn_cmd_t cmd, rom_error_t error) {
+ enum {
+ kIntrStateDone = (1 << OTBN_INTR_COMMON_DONE_BIT),
+ // Use a bit index that doesn't overlap with `otbn_err_bits_t`.
+ kResDoneBit = 31,
+ };
+ static_assert((UINT32_C(1) << kResDoneBit) > kOtbnErrBitsLast,
+ "kResDoneBit must not overlap with `otbn_err_bits_t`");
+
+ abs_mmio_write32(kBase + OTBN_INTR_STATE_REG_OFFSET, kIntrStateDone);
+ abs_mmio_write32(kBase + OTBN_CMD_REG_OFFSET, cmd);
+
+ rom_error_t res = kErrorOk ^ (UINT32_C(1) << kResDoneBit);
+ uint32_t reg = 0;
+ while (launder32(reg) != kIntrStateDone) {
+ reg = abs_mmio_read32(kBase + OTBN_INTR_STATE_REG_OFFSET);
+ res ^= bitfield_bit32_read(reg, OTBN_INTR_COMMON_DONE_BIT) << kResDoneBit;
+ }
+ HARDENED_CHECK_EQ(reg, kIntrStateDone);
+ abs_mmio_write32(kBase + OTBN_INTR_STATE_REG_OFFSET, kIntrStateDone);
+
+ otbn_err_bits_t err_bits;
+ otbn_get_err_bits(&err_bits);
+ res ^= err_bits;
+
+ if (launder32(res) == kErrorOk) {
+ HARDENED_CHECK_EQ(res, kErrorOk);
+ return res;
+ }
+ return error;
+}
+
+rom_error_t otbn_execute(void) {
+ return otbn_cmd_run(kOtbnCmdExecute, kErrorOtbnExecutionFailed);
}
bool otbn_is_busy() {
@@ -95,17 +137,25 @@
*err_bits = abs_mmio_read32(kBase + OTBN_ERR_BITS_REG_OFFSET);
}
+rom_error_t otbn_imem_sec_wipe(void) {
+ return otbn_cmd_run(kOtbnCmdSecWipeImem, kErrorOtbnSecWipeImemFailed);
+}
+
rom_error_t otbn_imem_write(uint32_t offset_bytes, const uint32_t *src,
size_t num_words) {
- RETURN_IF_ERROR(
+ HARDENED_RETURN_IF_ERROR(
check_offset_len(offset_bytes, num_words, kOtbnIMemSizeBytes));
otbn_write(kBase + OTBN_IMEM_REG_OFFSET + offset_bytes, src, num_words);
return kErrorOk;
}
+rom_error_t otbn_dmem_sec_wipe(void) {
+ return otbn_cmd_run(kOtbnCmdSecWipeDmem, kErrorOtbnSecWipeDmemFailed);
+}
+
rom_error_t otbn_dmem_write(uint32_t offset_bytes, const uint32_t *src,
size_t num_words) {
- RETURN_IF_ERROR(
+ HARDENED_RETURN_IF_ERROR(
check_offset_len(offset_bytes, num_words, kOtbnDMemSizeBytes));
otbn_write(kBase + OTBN_DMEM_REG_OFFSET + offset_bytes, src, num_words);
return kErrorOk;
@@ -113,7 +163,7 @@
rom_error_t otbn_dmem_read(uint32_t offset_bytes, uint32_t *dest,
size_t num_words) {
- RETURN_IF_ERROR(
+ HARDENED_RETURN_IF_ERROR(
check_offset_len(offset_bytes, num_words, kOtbnDMemSizeBytes));
size_t i = 0;
diff --git a/sw/device/silicon_creator/lib/drivers/otbn.h b/sw/device/silicon_creator/lib/drivers/otbn.h
index a4cb2c3..9ee6566 100644
--- a/sw/device/silicon_creator/lib/drivers/otbn.h
+++ b/sw/device/silicon_creator/lib/drivers/otbn.h
@@ -28,7 +28,7 @@
/**
* OTBN commands
*/
-typedef enum dif_otbn_cmd {
+typedef enum otbn_cmd {
kOtbnCmdExecute = 0xd8,
kOtbnCmdSecWipeDmem = 0xc3,
kOtbnCmdSecWipeImem = 0x1e,
@@ -47,8 +47,12 @@
/**
* Start the execution of the application loaded into OTBN.
+ *
+ * This function blocks until OTBN is idle.
+ *
+ * @return Result of the operation.
*/
-void otbn_execute(void);
+rom_error_t otbn_execute(void);
/**
* Is OTBN busy executing an application?
@@ -92,6 +96,7 @@
kOtbnErrBitsLifecycleEscalation = (1 << 22),
/** A FATAL_SOFTWARE error was observed. */
kOtbnErrBitsFatalSoftware = (1 << 23),
+ kOtbnErrBitsLast = kOtbnErrBitsFatalSoftware,
} otbn_err_bits_t;
/**
@@ -102,6 +107,15 @@
void otbn_get_err_bits(otbn_err_bits_t *err_bits);
/**
+ * Wipe IMEM securely.
+ *
+ * This function blocks until OTBN is idle.
+ *
+ * @return Result of the operation.
+ */
+rom_error_t otbn_imem_sec_wipe(void);
+
+/**
* Write an OTBN application into its instruction memory (IMEM)
*
* Only 32b-aligned 32b word accesses are allowed.
@@ -116,6 +130,15 @@
size_t len);
/**
+ * Wipe DMEM securely.
+ *
+ * This function blocks until OTBN is idle.
+ *
+ * @return Result of the operation.
+ */
+rom_error_t otbn_dmem_sec_wipe(void);
+
+/**
* Write to OTBN's data memory (DMEM)
*
* Only 32b-aligned 32b word accesses are allowed.
diff --git a/sw/device/silicon_creator/lib/drivers/otbn_unittest.cc b/sw/device/silicon_creator/lib/drivers/otbn_unittest.cc
index 25ce156..a20fde4 100644
--- a/sw/device/silicon_creator/lib/drivers/otbn_unittest.cc
+++ b/sw/device/silicon_creator/lib/drivers/otbn_unittest.cc
@@ -21,21 +21,52 @@
class OtbnTest : public mask_rom_test::MaskRomTest {
protected:
+ /**
+ * Sets expectations for running an OTBN command.
+ *
+ * @param cmd Command.
+ * @param err_bits Error bits.
+ */
+ void ExpectCmdRun(otbn_cmd_t cmd, otbn_err_bits_t err_bits) {
+ EXPECT_ABS_WRITE32(base_ + OTBN_INTR_STATE_REG_OFFSET,
+ {
+ {OTBN_INTR_COMMON_DONE_BIT, 1},
+ });
+ EXPECT_ABS_WRITE32(base_ + OTBN_CMD_REG_OFFSET, cmd);
+
+ EXPECT_ABS_READ32(base_ + OTBN_INTR_STATE_REG_OFFSET, 0);
+ EXPECT_ABS_READ32(base_ + OTBN_INTR_STATE_REG_OFFSET,
+ {
+ {OTBN_INTR_COMMON_DONE_BIT, 1},
+ });
+ EXPECT_ABS_WRITE32(base_ + OTBN_INTR_STATE_REG_OFFSET,
+ {
+ {OTBN_INTR_COMMON_DONE_BIT, 1},
+ });
+
+ EXPECT_ABS_READ32(base_ + OTBN_ERR_BITS_REG_OFFSET, err_bits);
+ }
+
uint32_t base_ = TOP_EARLGREY_OTBN_BASE_ADDR;
mask_rom_test::MockAbsMmio mmio_;
mask_rom_test::MockRnd rnd_;
};
-class StartTest : public OtbnTest {};
+class ExecuteTest : public OtbnTest {};
-TEST_F(StartTest, Success) {
+TEST_F(ExecuteTest, Success) {
// Test assumption.
static_assert(OTBN_IMEM_SIZE_BYTES >= 8, "OTBN IMEM size too small.");
- // Send EXECUTE command.
- EXPECT_ABS_WRITE32(base_ + OTBN_CMD_REG_OFFSET, kOtbnCmdExecute);
+ ExpectCmdRun(kOtbnCmdExecute, kOtbnErrBitsNoError);
- otbn_execute();
+ EXPECT_EQ(otbn_execute(), kErrorOk);
+}
+
+TEST_F(ExecuteTest, Failure) {
+ ExpectCmdRun(kOtbnCmdExecute, kOtbnErrBitsFatalSoftware);
+
+ EXPECT_EQ(otbn_execute(), kErrorOtbnExecutionFailed);
}
class IsBusyTest : public OtbnTest {};
@@ -57,6 +88,20 @@
EXPECT_EQ(err_bits, kOtbnErrBitsIllegalInsn | kOtbnErrBitsRegIntgViolation);
}
+class ImemSecWipeTest : public OtbnTest {};
+
+TEST_F(ImemSecWipeTest, Success) {
+ ExpectCmdRun(kOtbnCmdSecWipeImem, kOtbnErrBitsNoError);
+
+ EXPECT_EQ(otbn_imem_sec_wipe(), kErrorOk);
+}
+
+TEST_F(ImemSecWipeTest, Failure) {
+ ExpectCmdRun(kOtbnCmdSecWipeImem, kOtbnErrBitsFatalSoftware);
+
+ EXPECT_EQ(otbn_imem_sec_wipe(), kErrorOtbnSecWipeImemFailed);
+}
+
class ImemWriteTest : public OtbnTest {};
TEST_F(ImemWriteTest, BadAddressBeyondMemorySize) {
@@ -99,6 +144,20 @@
EXPECT_EQ(otbn_imem_write(4, test_data.data(), 2), kErrorOk);
}
+class DmemSecWipeTest : public OtbnTest {};
+
+TEST_F(DmemSecWipeTest, Success) {
+ ExpectCmdRun(kOtbnCmdSecWipeDmem, kOtbnErrBitsNoError);
+
+ EXPECT_EQ(otbn_dmem_sec_wipe(), kErrorOk);
+}
+
+TEST_F(DmemSecWipeTest, Failure) {
+ ExpectCmdRun(kOtbnCmdSecWipeDmem, kOtbnErrBitsFatalSoftware);
+
+ EXPECT_EQ(otbn_dmem_sec_wipe(), kErrorOtbnSecWipeDmemFailed);
+}
+
class DmemWriteTest : public OtbnTest {};
TEST_F(DmemWriteTest, SuccessWithoutOffset) {
diff --git a/sw/device/silicon_creator/lib/error.h b/sw/device/silicon_creator/lib/error.h
index c21168e..9810d07 100644
--- a/sw/device/silicon_creator/lib/error.h
+++ b/sw/device/silicon_creator/lib/error.h
@@ -94,7 +94,9 @@
X(kErrorOtbnInvalidArgument, ERROR_(1, kModuleOtbn, kInvalidArgument)), \
X(kErrorOtbnBadOffsetLen, ERROR_(2, kModuleOtbn, kInvalidArgument)), \
X(kErrorOtbnExecutionFailed, ERROR_(3, kModuleOtbn, kInternal)), \
- X(kErrorOtbnUnavailable, ERROR_(4, kModuleOtbn, kInternal)), \
+ X(kErrorOtbnSecWipeImemFailed, ERROR_(4, kModuleOtbn, kInternal)), \
+ X(kErrorOtbnSecWipeDmemFailed, ERROR_(5, kModuleOtbn, kInternal)), \
+ X(kErrorOtbnUnavailable, ERROR_(6, kModuleOtbn, kInternal)), \
X(kErrorFlashCtrlDataRead, ERROR_(1, kModuleFlashCtrl, kInternal)), \
X(kErrorFlashCtrlInfoRead, ERROR_(2, kModuleFlashCtrl, kInternal)), \
X(kErrorFlashCtrlDataWrite, ERROR_(3, kModuleFlashCtrl, kInternal)), \
diff --git a/sw/device/silicon_creator/lib/otbn_util.c b/sw/device/silicon_creator/lib/otbn_util.c
index 1d64d83..057f447 100644
--- a/sw/device/silicon_creator/lib/otbn_util.c
+++ b/sw/device/silicon_creator/lib/otbn_util.c
@@ -44,7 +44,7 @@
* @param app the OTBN application to check
* @return true if the addresses are valid, otherwise false.
*/
-bool check_app_address_ranges(const otbn_app_t *app) {
+static bool check_app_address_ranges(const otbn_app_t *app) {
// IMEM must have a strictly positive range (cannot be backwards or empty)
if (app->imem_end <= app->imem_start) {
return false;
@@ -66,11 +66,13 @@
ctx->app_is_loaded = kHardenedBoolFalse;
- RETURN_IF_ERROR(otbn_imem_write(0, app.imem_start, imem_num_words));
+ HARDENED_RETURN_IF_ERROR(otbn_imem_sec_wipe());
+ HARDENED_RETURN_IF_ERROR(otbn_imem_write(0, app.imem_start, imem_num_words));
- otbn_zero_dmem();
+ HARDENED_RETURN_IF_ERROR(otbn_dmem_sec_wipe());
if (data_num_words > 0) {
- RETURN_IF_ERROR(otbn_dmem_write(0, app.dmem_data_start, data_num_words));
+ HARDENED_RETURN_IF_ERROR(
+ otbn_dmem_write(0, app.dmem_data_start, data_num_words));
}
ctx->app = app;
@@ -84,20 +86,15 @@
}
HARDENED_CHECK_EQ(ctx->app_is_loaded, kHardenedBoolTrue);
- otbn_execute();
- return kErrorOk;
+ return otbn_execute();
}
rom_error_t otbn_copy_data_to_otbn(otbn_t *ctx, size_t len, const uint32_t *src,
otbn_addr_t dest) {
- RETURN_IF_ERROR(otbn_dmem_write(dest, src, len));
-
- return kErrorOk;
+ return otbn_dmem_write(dest, src, len);
}
rom_error_t otbn_copy_data_from_otbn(otbn_t *ctx, size_t len_bytes,
otbn_addr_t src, uint32_t *dest) {
- RETURN_IF_ERROR(otbn_dmem_read(src, dest, len_bytes));
-
- return kErrorOk;
+ return otbn_dmem_read(src, dest, len_bytes);
}
diff --git a/sw/device/silicon_creator/lib/otbn_util.h b/sw/device/silicon_creator/lib/otbn_util.h
index 4567caf..25bc861 100644
--- a/sw/device/silicon_creator/lib/otbn_util.h
+++ b/sw/device/silicon_creator/lib/otbn_util.h
@@ -200,7 +200,7 @@
/**
* Start the OTBN application.
*
- * Use `otbn_busy_wait_for_done()` to wait for the function call to complete.
+ * This function blocks until OTBN is idle.
*
* @param ctx The context object.
* @return The result of the operation.
@@ -208,14 +208,6 @@
rom_error_t otbn_execute_app(otbn_t *ctx);
/**
- * Busy waits for OTBN to be done with its operation.
- *
- * @param ctx The context object.
- * @return The result of the operation.
- */
-rom_error_t otbn_busy_wait_for_done(otbn_t *ctx);
-
-/**
* Copies data from the CPU memory to OTBN data memory.
*
* @param ctx The context object.
diff --git a/sw/device/silicon_creator/lib/sigverify/mod_exp_otbn.c b/sw/device/silicon_creator/lib/sigverify/mod_exp_otbn.c
index e2a8b92..9fda662 100644
--- a/sw/device/silicon_creator/lib/sigverify/mod_exp_otbn.c
+++ b/sw/device/silicon_creator/lib/sigverify/mod_exp_otbn.c
@@ -71,31 +71,26 @@
// Initialize OTBN and load the RSA app.
otbn_init(&otbn);
- RETURN_IF_ERROR(otbn_load_app(&otbn, kOtbnAppRsa));
+ HARDENED_RETURN_IF_ERROR(otbn_load_app(&otbn, kOtbnAppRsa));
// Set the modulus (n).
- RETURN_IF_ERROR(
+ HARDENED_RETURN_IF_ERROR(
write_rsa_3072_int_to_otbn(&otbn, &public_key->n, kOtbnVarRsaInMod));
// Set the signature.
- RETURN_IF_ERROR(
+ HARDENED_RETURN_IF_ERROR(
write_rsa_3072_int_to_otbn(&otbn, signature, kOtbnVarRsaInBuf));
// Set the precomputed constant m0_inv.
- RETURN_IF_ERROR(otbn_copy_data_to_otbn(&otbn, kOtbnWideWordNumWords,
- public_key->n0_inv, kOtbnVarRsaM0Inv));
+ HARDENED_RETURN_IF_ERROR(otbn_copy_data_to_otbn(
+ &otbn, kOtbnWideWordNumWords, public_key->n0_inv, kOtbnVarRsaM0Inv));
// Start the OTBN routine.
- RETURN_IF_ERROR(otbn_execute_app(&otbn));
-
- // Spin here waiting for OTBN to complete.
- RETURN_IF_ERROR(otbn_busy_wait_for_done(&otbn));
+ HARDENED_RETURN_IF_ERROR(otbn_execute_app(&otbn));
// Read recovered message out of OTBN dmem.
- RETURN_IF_ERROR(
- read_rsa_3072_int_from_otbn(&otbn, kOtbnVarRsaOutBuf, recovered_message));
-
- return kErrorOk;
+ return read_rsa_3072_int_from_otbn(&otbn, kOtbnVarRsaOutBuf,
+ recovered_message);
}
rom_error_t sigverify_mod_exp_otbn(const sigverify_rsa_key_t *key,
@@ -108,7 +103,5 @@
}
// Run OTBN application.
- RETURN_IF_ERROR(run_otbn_rsa_3072_modexp(key, sig, result));
-
- return kErrorOk;
+ return run_otbn_rsa_3072_modexp(key, sig, result);
}