Repository: chipsalliance/caliptra-sw
Pull request #620 (2023-08-09 19:14:11)

Val ROM
- Changing no-kats feature to val-rom
- Adding check to prohibit fast mode in production lifecycle
- Bypassing ECC and LMS verify with a mock verifier env for val rom
- Adding new flow implementation to bypass cert generation


Patch:
diff --git a/builder/src/lib.rs b/builder/src/lib.rs
index 0f58d18e..98d08116 100644
--- a/builder/src/lib.rs
+++ b/builder/src/lib.rs
@@ -37,10 +37,10 @@ pub const ROM_WITH_UART: FwId = FwId {
     workspace_dir: None,
 };
 
-pub const ROM_FAST_WITH_UART: FwId = FwId {
+pub const ROM_VAL_WITH_UART: FwId = FwId {
     crate_name: "caliptra-rom",
     bin_name: "caliptra-rom",
-    features: &["emu", "no-kats"],
+    features: &["emu", "val-rom"],
     workspace_dir: None,
 };
 
diff --git a/error/src/lib.rs b/error/src/lib.rs
index 88bee390..53dbaf39 100644
--- a/error/src/lib.rs
+++ b/error/src/lib.rs
@@ -364,6 +364,9 @@ impl CaliptraError {
     pub const ROM_GLOBAL_UNSUPPORTED_FMCALIAS_TBS_SIZE: CaliptraError =
         CaliptraError::new_const(0x0105000A);
 
+    pub const ROM_GLOBAL_VAL_ROM_IN_PRODUCTION: CaliptraError =
+        CaliptraError::new_const(0x0105000A);
+
     /// ROM KAT Errors
     pub const ROM_KAT_SHA256_DIGEST_FAILURE: CaliptraError = CaliptraError::new_const(0x90010001);
     pub const ROM_KAT_SHA256_DIGEST_MISMATCH: CaliptraError = CaliptraError::new_const(0x90010002);
diff --git a/rom/dev/Cargo.toml b/rom/dev/Cargo.toml
index e1987620..0ad4d535 100644
--- a/rom/dev/Cargo.toml
+++ b/rom/dev/Cargo.toml
@@ -47,7 +47,7 @@ std = [
   "ufmt/std",
 ]
 no-fmc = []
-no-kats = []
+val-rom = []
 
 [[bin]]
 name = "asm_tests"
diff --git a/rom/dev/src/flow/cold_reset/fw_processor.rs b/rom/dev/src/flow/cold_reset/fw_processor.rs
index fa2a99b0..398e7ca5 100644
--- a/rom/dev/src/flow/cold_reset/fw_processor.rs
+++ b/rom/dev/src/flow/cold_reset/fw_processor.rs
@@ -11,7 +11,8 @@ Abstract:
     File contains the code to download and validate the firmware.
 
 --*/
-
+#[cfg(feature = "val-rom")]
+use crate::flow::val::ValRomImageVerificationEnv;
 use crate::fuse::log_fuse_data;
 use crate::rom_env::RomEnv;
 use crate::{cprintln, verifier::RomImageVerificationEnv};
@@ -187,6 +188,13 @@ impl FirmwareProcessor {
         manifest: &ImageManifest,
         img_bundle_sz: u32,
     ) -> CaliptraResult<ImageVerificationInfo> {
+        #[cfg(feature = "val-rom")]
+        let venv = &mut ValRomImageVerificationEnv {
+            sha384_acc: venv.sha384_acc,
+            soc_ifc: venv.soc_ifc,
+            data_vault: venv.data_vault,
+        };
+
         let mut verifier = ImageVerifier::new(venv);
         let info = verifier.verify(manifest, img_bundle_sz, ResetReason::ColdReset)?;
 
diff --git a/rom/dev/src/flow/mod.rs b/rom/dev/src/flow/mod.rs
index 0efd7001..0cc74c2f 100644
--- a/rom/dev/src/flow/mod.rs
+++ b/rom/dev/src/flow/mod.rs
@@ -14,9 +14,12 @@ Abstract:
 
 mod cold_reset;
 mod update_reset;
+#[cfg(feature = "val-rom")]
+mod val;
 mod warm_reset;
 
-use crate::rom_env::RomEnv;
+use crate::cprintln;
+use crate::{handle_fatal_error, rom_env::RomEnv};
 use caliptra_common::FirmwareHandoffTable;
 use caliptra_drivers::{CaliptraResult, ResetReason};
 use caliptra_error::CaliptraError;
@@ -28,17 +31,33 @@ use caliptra_error::CaliptraError;
 /// * `env` - ROM Environment
 pub fn run(env: &mut RomEnv) -> CaliptraResult<Option<FirmwareHandoffTable>> {
     let reset_reason = env.soc_ifc.reset_reason();
-    match reset_reason {
-        // Cold Reset Flow
-        ResetReason::ColdReset => cold_reset::ColdResetFlow::run(env),
 
-        // Warm Reset Flow
-        ResetReason::WarmReset => warm_reset::WarmResetFlow::run(env),
+    if cfg!(not(feature = "val-rom")) {
+        match reset_reason {
+            // Cold Reset Flow
+            ResetReason::ColdReset => cold_reset::ColdResetFlow::run(env),
 
-        // Update Reset Flow
-        ResetReason::UpdateReset => update_reset::UpdateResetFlow::run(env),
+            // Warm Reset Flow
+            ResetReason::WarmReset => warm_reset::WarmResetFlow::run(env),
 
-        // Unknown/Spurious Reset Flow
-        ResetReason::Unknown => Err(CaliptraError::ROM_UNKNOWN_RESET_FLOW),
+            // Update Reset Flow
+            ResetReason::UpdateReset => update_reset::UpdateResetFlow::run(env),
+
+            // Unknown/Spurious Reset Flow
+            ResetReason::Unknown => Err(CaliptraError::ROM_UNKNOWN_RESET_FLOW),
+        }
+    } else {
+        let _result: CaliptraResult<Option<FirmwareHandoffTable>> =
+            Err(CaliptraError::ROM_GLOBAL_PANIC);
+
+        if env.soc_ifc.lifecycle() == caliptra_drivers::Lifecycle::Production {
+            cprintln!("Validation ROM in Production lifecycle prohibited");
+            handle_fatal_error(CaliptraError::ROM_GLOBAL_VAL_ROM_IN_PRODUCTION.into());
+        }
+
+        #[cfg(feature = "val-rom")]
+        let _result = val::ValRomFlow::run(env);
+
+        _result
     }
 }
diff --git a/rom/dev/src/flow/val.rs b/rom/dev/src/flow/val.rs
new file mode 100644
index 00000000..e9364c31
--- /dev/null
+++ b/rom/dev/src/flow/val.rs
@@ -0,0 +1,182 @@
+/*++
+
+Licensed under the Apache-2.0 license.
+
+File Name:
+
+    val.rs
+
+Abstract:
+
+    File contains the implementation of the validation ROM reset flows
+
+--*/
+
+#[cfg(not(feature = "val-rom"))]
+compile_error!("This file should NEVER be included except for the val-rom feature");
+
+#[allow(dead_code)]
+#[path = "cold_reset/fw_processor.rs"]
+mod fw_processor;
+
+use crate::fht;
+use crate::rom_env::RomEnv;
+use caliptra_common::FirmwareHandoffTable;
+use caliptra_common::RomBootStatus::*;
+use caliptra_drivers::cprintln;
+use caliptra_drivers::Lifecycle;
+use caliptra_drivers::LmsResult;
+use caliptra_drivers::VendorPubKeyRevocation;
+use caliptra_drivers::*;
+use caliptra_error::CaliptraError;
+use caliptra_image_types::*;
+use caliptra_image_verify::ImageVerificationEnv;
+use core::ops::Range;
+use fw_processor::FirmwareProcessor;
+
+pub struct ValRomFlow {}
+
+impl ValRomFlow {
+    /// Execute ROM Flows based on reset reason
+    ///
+    /// # Arguments
+    ///
+    /// * `env` - ROM Environment
+    #[inline(never)]
+    pub fn run(env: &mut RomEnv) -> CaliptraResult<Option<FirmwareHandoffTable>> {
+        let reset_reason = env.soc_ifc.reset_reason();
+        match reset_reason {
+            // Cold Reset Flow
+            ResetReason::ColdReset => {
+                cprintln!("[val-rom-cold-reset] ++");
+                report_boot_status(ColdResetStarted.into());
+
+                // SKIP Execute IDEVID layer
+                // SKIP Execute LDEVID layer
+
+                // Download and validate firmware.
+                _ = FirmwareProcessor::process(env)?;
+
+                // SKIP Execute FMCALIAS layer
+
+                cprintln!("[val-rom-cold-reset] --");
+                report_boot_status(ColdResetComplete.into());
+
+                Ok(Some(fht::make_fht(env)))
+            }
+
+            // TODO: Warm Reset Flow
+            ResetReason::WarmReset => Err(CaliptraError::ROM_UNKNOWN_RESET_FLOW),
+
+            // TODO: Update Reset Flow
+            ResetReason::UpdateReset => Err(CaliptraError::ROM_UNKNOWN_RESET_FLOW),
+
+            // Unknown/Spurious Reset Flow
+            ResetReason::Unknown => Err(CaliptraError::ROM_UNKNOWN_RESET_FLOW),
+        }
+    }
+}
+
+// ROM Verification Environemnt
+pub(crate) struct ValRomImageVerificationEnv<'a> {
+    pub(crate) sha384_acc: &'a mut Sha384Acc,
+    pub(crate) soc_ifc: &'a mut SocIfc,
+    pub(crate) data_vault: &'a mut DataVault,
+}
+
+impl<'a> ImageVerificationEnv for &mut ValRomImageVerificationEnv<'a> {
+    /// Calculate Digest using SHA-384 Accelerator
+    fn sha384_digest(&mut self, offset: u32, len: u32) -> CaliptraResult<ImageDigest> {
+        loop {
+            if let Some(mut txn) = self.sha384_acc.try_start_operation() {
+                let mut digest = Array4x12::default();
+                txn.digest(len, offset, false, &mut digest)?;
+                return Ok(digest.0);
+            }
+        }
+    }
+
+    /// ECC-384 Verification routine
+    fn ecc384_verify(
+        &mut self,
+        _digest: &ImageDigest,
+        _pub_key: &ImageEccPubKey,
+        _sig: &ImageEccSignature,
+    ) -> CaliptraResult<Ecc384Result> {
+        // Mock verify, just always return success
+        Ok(Ecc384Result::Success)
+    }
+
+    fn lms_verify(
+        &mut self,
+        _digest: &ImageDigest,
+        _pub_key: &ImageLmsPublicKey,
+        _sig: &ImageLmsSignature,
+    ) -> CaliptraResult<LmsResult> {
+        // Mock verify, just always return success
+        Ok(LmsResult::Success)
+    }
+
+    /// Retrieve Vendor Public Key Digest
+    fn vendor_pub_key_digest(&self) -> ImageDigest {
+        self.soc_ifc.fuse_bank().vendor_pub_key_hash().into()
+    }
+
+    /// Retrieve Vendor ECC Public Key Revocation Bitmask
+    fn vendor_ecc_pub_key_revocation(&self) -> VendorPubKeyRevocation {
+        self.soc_ifc.fuse_bank().vendor_ecc_pub_key_revocation()
+    }
+
+    /// Retrieve Vendor LMS Public Key Revocation Bitmask
+    fn vendor_lms_pub_key_revocation(&self) -> u32 {
+        self.soc_ifc.fuse_bank().vendor_lms_pub_key_revocation()
+    }
+
+    /// Retrieve Owner Public Key Digest from fuses
+    fn owner_pub_key_digest_fuses(&self) -> ImageDigest {
+        self.soc_ifc.fuse_bank().owner_pub_key_hash().into()
+    }
+
+    /// Retrieve Anti-Rollback disable fuse value
+    fn anti_rollback_disable(&self) -> bool {
+        self.soc_ifc.fuse_bank().anti_rollback_disable()
+    }
+
+    /// Retrieve Device Lifecycle state
+    fn dev_lifecycle(&self) -> Lifecycle {
+        self.soc_ifc.lifecycle()
+    }
+
+    /// Get the vendor key index saved in data vault on cold boot
+    fn vendor_pub_key_idx_dv(&self) -> u32 {
+        self.data_vault.ecc_vendor_pk_index()
+    }
+
+    /// Get the owner public key digest saved in the dv on cold boot
+    fn owner_pub_key_digest_dv(&self) -> ImageDigest {
+        self.data_vault.owner_pk_hash().into()
+    }
+
+    // Get the fmc digest from the data vault on cold boot
+    fn get_fmc_digest_dv(&self) -> ImageDigest {
+        self.data_vault.fmc_tci().into()
+    }
+
+    // Get Fuse FMC Key Manifest SVN
+    fn fmc_fuse_svn(&self) -> u32 {
+        self.soc_ifc.fuse_bank().fmc_fuse_svn()
+    }
+
+    // Get Runtime fuse SVN
+    fn runtime_fuse_svn(&self) -> u32 {
+        self.soc_ifc.fuse_bank().runtime_fuse_svn()
+    }
+
+    fn iccm_range(&self) -> Range<u32> {
+        RomEnv::ICCM_RANGE
+    }
+
+    fn lms_verify_enabled(&self) -> bool {
+        self.soc_ifc.fuse_bank().lms_verify() == RomVerifyConfig::EcdsaAndLms
+    }
+}
diff --git a/rom/dev/src/main.rs b/rom/dev/src/main.rs
index af52b901..03168a0e 100644
--- a/rom/dev/src/main.rs
+++ b/rom/dev/src/main.rs
@@ -13,7 +13,7 @@ Abstract:
 --*/
 #![cfg_attr(not(feature = "std"), no_std)]
 #![cfg_attr(not(feature = "std"), no_main)]
-#![cfg_attr(not(feature = "no-kats"), allow(unused_imports))]
+#![cfg_attr(feature = "val-rom", allow(unused_imports))]
 
 use crate::lock::lock_registers;
 use core::hint::black_box;
@@ -67,6 +67,13 @@ pub extern "C" fn rom_entry() -> ! {
     };
     cprintln!("[state] LifecycleState = {}", _lifecyle);
 
+    if cfg!(feature = "val-rom")
+        && env.soc_ifc.lifecycle() == caliptra_drivers::Lifecycle::Production
+    {
+        cprintln!("Val ROM in Production lifecycle prohibited");
+        handle_fatal_error(CaliptraError::ROM_GLOBAL_VAL_ROM_IN_PRODUCTION.into());
+    }
+
     cprintln!(
         "[state] DebugLocked = {}",
         if env.soc_ifc.debug_locked() {
@@ -79,7 +86,7 @@ pub extern "C" fn rom_entry() -> ! {
     // Start the watchdog timer
     wdt::start_wdt(&mut env.soc_ifc);
 
-    if !cfg!(feature = "no-kats") {
+    if !cfg!(feature = "val-rom") {
         let result = kat::execute_kat(&mut env);
         if let Err(err) = result {
             handle_fatal_error(err.into());


Test patch:
diff --git a/rom/dev/tests/test_fast_rom.rs b/rom/dev/tests/test_fast_rom.rs
deleted file mode 100644
index 3b37fd6d..00000000
--- a/rom/dev/tests/test_fast_rom.rs
+++ /dev/null
@@ -1,23 +0,0 @@
-// Licensed under the Apache-2.0 license
-
-use caliptra_builder::ROM_FAST_WITH_UART;
-use caliptra_hw_model::{BootParams, HwModel, InitParams};
-
-#[test]
-fn test_skip_kats() {
-    let rom = caliptra_builder::build_firmware_rom(&ROM_FAST_WITH_UART).unwrap();
-    let mut hw = caliptra_hw_model::new(BootParams {
-        init_params: InitParams {
-            rom: &rom,
-            ..Default::default()
-        },
-        ..Default::default()
-    })
-    .unwrap();
-
-    // If KatStarted boot status is posted before ColResetStarted, the statement below will trigger panic.
-    hw.step_until_boot_status(
-        caliptra_common::RomBootStatus::ColdResetStarted.into(),
-        false,
-    );
-}
diff --git a/rom/dev/tests/test_val_rom.rs b/rom/dev/tests/test_val_rom.rs
new file mode 100644
index 00000000..885379d7
--- /dev/null
+++ b/rom/dev/tests/test_val_rom.rs
@@ -0,0 +1,97 @@
+// Licensed under the Apache-2.0 license
+
+use caliptra_builder::ROM_VAL_WITH_UART;
+use caliptra_builder::{FwId, ImageOptions, APP_WITH_UART};
+use caliptra_drivers::CaliptraError;
+use caliptra_hw_model::{BootParams, DeviceLifecycle, Fuses, HwModel, InitParams, SecurityState};
+
+pub mod helpers;
+
+#[test]
+fn test_skip_kats() {
+    let rom = caliptra_builder::build_firmware_rom(&ROM_VAL_WITH_UART).unwrap();
+    let mut hw = caliptra_hw_model::new(BootParams {
+        init_params: InitParams {
+            rom: &rom,
+            ..Default::default()
+        },
+        ..Default::default()
+    })
+    .unwrap();
+
+    // If KatStarted boot status is posted before ColResetStarted, the statement below will trigger panic.
+    hw.step_until_boot_status(
+        caliptra_common::RomBootStatus::ColdResetStarted.into(),
+        false,
+    );
+}
+
+#[test]
+fn test_val_rom_production_error() {
+    let security_state =
+        *SecurityState::default().set_device_lifecycle(DeviceLifecycle::Production);
+
+    let rom = caliptra_builder::build_firmware_rom(&ROM_VAL_WITH_UART).unwrap();
+    let mut hw = caliptra_hw_model::new(BootParams {
+        init_params: InitParams {
+            rom: &rom,
+            security_state,
+            ..Default::default()
+        },
+        ..Default::default()
+    })
+    .unwrap();
+
+    // Let it run until a fatal error (should fail very early)
+    hw.step_until(|m| m.soc_ifc().cptra_fw_error_fatal().read() != 0);
+
+    // Make sure we see the right fatal error
+    assert_eq!(
+        hw.soc_ifc().cptra_fw_error_fatal().read(),
+        CaliptraError::ROM_GLOBAL_VAL_ROM_IN_PRODUCTION.into()
+    );
+}
+
+#[test]
+fn test_val_rom_fw_load() {
+    pub const TEST_FMC_WITH_UART: FwId = FwId {
+        crate_name: "caliptra-rom-test-fmc",
+        bin_name: "caliptra-rom-test-fmc",
+        features: &["emu"],
+        workspace_dir: None,
+    };
+
+    let fuses = Fuses::default();
+    let rom = caliptra_builder::build_firmware_rom(&ROM_VAL_WITH_UART).unwrap();
+    let mut hw = caliptra_hw_model::new(BootParams {
+        init_params: InitParams {
+            rom: &rom,
+            security_state: SecurityState::from(fuses.life_cycle as u32),
+            ..Default::default()
+        },
+        fuses,
+        ..Default::default()
+    })
+    .unwrap();
+
+    // Build the image we are going to send to ROM to load
+    let image_bundle = caliptra_builder::build_and_sign_image(
+        &TEST_FMC_WITH_UART,
+        &APP_WITH_UART,
+        ImageOptions::default(),
+    )
+    .unwrap();
+
+    // Upload the FW once ROM is at the right point
+    hw.step_until(|m| m.soc_ifc().cptra_flow_status().read().ready_for_fw());
+    hw.upload_firmware(&image_bundle.to_bytes().unwrap())
+        .unwrap();
+
+    // Keep going until we launch FMC
+    hw.step_until_output_contains("[exit] Launching FMC")
+        .unwrap();
+
+    // Make sure we actually get into FMC
+    hw.step_until_output_contains("Running Caliptra FMC")
+        .unwrap();
+}