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