[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); }