diff --git a/Cargo.lock b/Cargo.lock index 915c15f660..0f0a9d2e87 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -559,6 +559,7 @@ version = "0.1.0" dependencies = [ "caliptra-drivers", "caliptra-lms-types", + "ufmt", "zerocopy", ] @@ -648,6 +649,7 @@ dependencies = [ "caliptra-image-openssl", "caliptra-image-serde", "caliptra-image-types", + "caliptra-image-verify", "caliptra-kat", "caliptra-registers", "caliptra-x509", @@ -744,6 +746,7 @@ dependencies = [ "bitflags 2.4.0", "caliptra-drivers", "caliptra-image-types", + "caliptra-image-verify", "caliptra-registers", "ufmt", "zerocopy", diff --git a/builder/src/lib.rs b/builder/src/lib.rs index b369e91a48..dfb59cb54c 100644 --- a/builder/src/lib.rs +++ b/builder/src/lib.rs @@ -64,7 +64,7 @@ pub const FMC_FAKE_WITH_UART: FwId = FwId { pub const APP_WITH_UART: FwId = FwId { crate_name: "caliptra-runtime", bin_name: "caliptra-runtime", - features: &["emu", "test_only_commands"], + features: &["emu", "test_only_commands", "fips_self_test"], workspace_dir: None, }; diff --git a/common/Cargo.toml b/common/Cargo.toml index 7fe7c9dd5d..5c3bc6677b 100644 --- a/common/Cargo.toml +++ b/common/Cargo.toml @@ -11,6 +11,7 @@ bitfield.workspace = true bitflags.workspace = true caliptra-drivers.workspace = true caliptra-image-types = { workspace = true, default-features = false } +caliptra-image-verify.workspace = true caliptra-registers.workspace = true ufmt.workspace = true zerocopy.workspace = true @@ -18,4 +19,4 @@ zerocopy.workspace = true [features] default = ["std"] emu = ["caliptra-drivers/emu"] -std = [] \ No newline at end of file +std = [] diff --git a/common/src/fips.rs b/common/src/fips.rs new file mode 100644 index 0000000000..8bdba040e8 --- /dev/null +++ b/common/src/fips.rs @@ -0,0 +1,25 @@ +// Licensed under the Apache-2.0 license + +use crate::cprintln; +use crate::mailbox_api::{FipsVersionResp, MailboxResp, MailboxRespHeader}; +use caliptra_drivers::CaliptraResult; +use caliptra_drivers::SocIfc; + +pub struct FipsVersionCmd; +impl FipsVersionCmd { + pub const NAME: [u8; 12] = *b"Caliptra RTM"; + pub const MODE: u32 = 0x46495053; + + pub fn execute(soc_ifc: &SocIfc) -> CaliptraResult { + cprintln!("[rt] FIPS Version"); + + let resp = FipsVersionResp { + hdr: MailboxRespHeader::default(), + mode: Self::MODE, + fips_rev: soc_ifc.get_version(), + name: Self::NAME, + }; + + Ok(MailboxResp::FipsVersion(resp)) + } +} diff --git a/common/src/lib.rs b/common/src/lib.rs index 5c6bc2d712..14b4c9b7a4 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -7,8 +7,10 @@ pub mod capabilities; pub mod checksum; pub mod crypto; pub mod dice; +pub mod fips; pub mod keyids; pub mod mailbox_api; +pub mod verifier; pub mod wdt; ///merge imports diff --git a/common/src/mailbox_api.rs b/common/src/mailbox_api.rs index 70e2a146d4..4cb8449178 100644 --- a/common/src/mailbox_api.rs +++ b/common/src/mailbox_api.rs @@ -27,7 +27,9 @@ impl CommandId { /// The status command. pub const VERSION: Self = Self(0x4650_5652); // "FPVR" /// The self-test command. - pub const SELF_TEST: Self = Self(0x4650_4C54); // "FPST" + pub const SELF_TEST_START: Self = Self(0x4650_4C54); // "FPST" + /// The self-test get results. + pub const SELF_TEST_GET_RESULTS: Self = Self(0x4650_4C67); // "FPGR" /// The shutdown command. pub const SHUTDOWN: Self = Self(0x4650_5344); // "FPSD" diff --git a/rom/dev/src/verifier.rs b/common/src/verifier.rs similarity index 87% rename from rom/dev/src/verifier.rs rename to common/src/verifier.rs index cceedb74b8..5ad677769e 100644 --- a/rom/dev/src/verifier.rs +++ b/common/src/verifier.rs @@ -17,21 +17,20 @@ use caliptra_image_types::*; use caliptra_image_verify::ImageVerificationEnv; use core::ops::Range; -use crate::rom_env::RomEnv; +use caliptra_drivers::memory_layout::ICCM_RANGE; /// ROM Verification Environemnt -pub(crate) struct RomImageVerificationEnv<'a> { - pub(crate) sha256: &'a mut Sha256, - pub(crate) sha384: &'a mut Sha384, - pub(crate) sha384_acc: &'a mut Sha384Acc, - pub(crate) soc_ifc: &'a mut SocIfc, - pub(crate) ecc384: &'a mut Ecc384, - pub(crate) data_vault: &'a mut DataVault, - pub(crate) pcr_bank: &'a mut PcrBank, - pub(crate) persistent_data: &'a mut PersistentDataAccessor, +pub struct FirmwareImageVerificationEnv<'a> { + pub sha256: &'a mut Sha256, + pub sha384: &'a mut Sha384, + pub sha384_acc: &'a mut Sha384Acc, + pub soc_ifc: &'a mut SocIfc, + pub ecc384: &'a mut Ecc384, + pub data_vault: &'a mut DataVault, + pub pcr_bank: &'a mut PcrBank, } -impl<'a> ImageVerificationEnv for &mut RomImageVerificationEnv<'a> { +impl<'a> ImageVerificationEnv for &mut FirmwareImageVerificationEnv<'a> { /// Calculate Digest using SHA-384 Accelerator fn sha384_digest(&mut self, offset: u32, len: u32) -> CaliptraResult { loop { @@ -134,7 +133,7 @@ impl<'a> ImageVerificationEnv for &mut RomImageVerificationEnv<'a> { } fn iccm_range(&self) -> Range { - RomEnv::ICCM_RANGE + ICCM_RANGE } fn lms_verify_enabled(&self) -> bool { diff --git a/drivers/src/hand_off.rs b/drivers/src/hand_off.rs index 0400c40403..3b6263bd20 100644 --- a/drivers/src/hand_off.rs +++ b/drivers/src/hand_off.rs @@ -89,18 +89,29 @@ pub enum DataVaultRegister { impl TryInto for HandOffDataHandle { type Error = CaliptraError; fn try_into(self) -> Result { - let vault = Vault::try_from(self.vault()) - .unwrap_or_else(|_| report_handoff_error_and_halt("Invalid Vault", 0xbadbad)); + let vault = Vault::try_from(self.vault()).unwrap_or_else(|_| { + report_handoff_error_and_halt( + "Invalid Vault", + CaliptraError::DRIVER_HANDOFF_INVALID_VAULT.into(), + ) + }); match vault { Vault::KeyVault => Ok(DataStore::KeyVaultSlot( - KeyId::try_from(self.reg_num() as u8) - .unwrap_or_else(|_| report_handoff_error_and_halt("Invalid KeyId", 0xbadbad)), + KeyId::try_from(self.reg_num() as u8).unwrap_or_else(|_| { + report_handoff_error_and_halt( + "Invalid KeyId", + CaliptraError::DRIVER_HANDOFF_INVALID_KEY_ID.into(), + ) + }), )), Vault::DataVault => match self.reg_type() { 1 => { let entry = DataStore::DataVaultSticky4( ColdResetEntry4::try_from(self.reg_num() as u8).unwrap_or_else(|_| { - report_handoff_error_and_halt("Invalid ColdResetEntry4", 0xbadbad) + report_handoff_error_and_halt( + "Invalid ColdResetEntry4", + CaliptraError::DRIVER_HANDOFF_INVALID_COLD_RESET_ENTRY4.into(), + ) }), ); Ok(entry) @@ -109,7 +120,10 @@ impl TryInto for HandOffDataHandle { 2 => { let entry = DataStore::DataVaultSticky48( ColdResetEntry48::try_from(self.reg_num() as u8).unwrap_or_else(|_| { - report_handoff_error_and_halt("Invalid ColdResetEntry48", 0xbadbad) + report_handoff_error_and_halt( + "Invalid ColdResetEntry48", + CaliptraError::DRIVER_HANDOFF_INVALID_COLD_RESET_ENTRY48.into(), + ) }), ); Ok(entry) @@ -118,7 +132,10 @@ impl TryInto for HandOffDataHandle { 3 => { let entry = WarmResetEntry4::try_from(self.reg_num() as u8).unwrap_or_else(|_| { - report_handoff_error_and_halt("Invalid WarmResetEntry4", 0xbadbad) + report_handoff_error_and_halt( + "Invalid WarmResetEntry4", + CaliptraError::DRIVER_HANDOFF_INVALID_WARM_RESET_ENTRY4.into(), + ) }); let ds = DataStore::DataVaultNonSticky4(entry); @@ -128,7 +145,10 @@ impl TryInto for HandOffDataHandle { 4 => { let entry = DataStore::DataVaultNonSticky48( WarmResetEntry48::try_from(self.reg_num() as u8).unwrap_or_else(|_| { - report_handoff_error_and_halt("Invalid WarmResetEntry48", 0xbadbad) + report_handoff_error_and_halt( + "Invalid WarmResetEntry48", + CaliptraError::DRIVER_HANDOFF_INVALID_WARM_RESET_ENTRY48.into(), + ) }), ); Ok(entry) diff --git a/drivers/src/lms.rs b/drivers/src/lms.rs index 3791f27590..5e524c96e3 100644 --- a/drivers/src/lms.rs +++ b/drivers/src/lms.rs @@ -379,12 +379,12 @@ impl Lms { /// Note: Use this function only if glitch protection is not needed. /// If glitch protection is needed, use `verify_lms_signature_cfi` instead. - pub fn verify_lms_signature( + pub fn verify_lms_signature( &self, sha256_driver: &mut Sha256, input_string: &[u8], - lms_public_key: &LmsPublicKey, - lms_sig: &LmsSignature, + lms_public_key: &LmsPublicKey<6>, + lms_sig: &LmsSignature<6, 51, 15>, ) -> CaliptraResult { let mut candidate_key = self.verify_lms_signature_cfi(sha256_driver, input_string, lms_public_key, lms_sig)?; @@ -397,7 +397,50 @@ impl Lms { result } - pub fn verify_lms_signature_cfi( + /// Note: Use this function only if glitch protection is not needed. + /// If glitch protection is needed, use `verify_lms_signature_cfi_generic` instead. + pub fn verify_lms_signature_generic( + &self, + sha256_driver: &mut Sha256, + input_string: &[u8], + lms_public_key: &LmsPublicKey, + lms_sig: &LmsSignature, + ) -> CaliptraResult { + let mut candidate_key = self.verify_lms_signature_cfi_generic( + sha256_driver, + input_string, + lms_public_key, + lms_sig, + )?; + let result = if candidate_key != HashValue::from(lms_public_key.digest) { + Ok(LmsResult::SigVerifyFailed) + } else { + Ok(LmsResult::Success) + }; + candidate_key.0.fill(0); + result + } + + // When callers from separate crates call a function like + // verify_lms_signature_cfi_generic(), Rustc 1.70 + // may build multiple versions (depending on optimizer heuristics), even when all the + // generic parameters are identical. This is bad, as it can bloat the binary and the + // second copy violates the FIPS requirements that the same machine code be used for the + // KAT as the actual implementation. To defend against it, we provide this non-generic + // function that production firmware should call instead. + #[inline(never)] + pub fn verify_lms_signature_cfi( + &self, + sha256_driver: &mut Sha256, + input_string: &[u8], + lms_public_key: &LmsPublicKey<6>, + lms_sig: &LmsSignature<6, 51, 15>, + ) -> CaliptraResult> { + self.verify_lms_signature_cfi_generic(sha256_driver, input_string, lms_public_key, lms_sig) + } + + #[inline(always)] + pub fn verify_lms_signature_cfi_generic( &self, sha256_driver: &mut Sha256, input_string: &[u8], diff --git a/drivers/src/memory_layout.rs b/drivers/src/memory_layout.rs index ed84f876c8..67078d1d1b 100644 --- a/drivers/src/memory_layout.rs +++ b/drivers/src/memory_layout.rs @@ -66,6 +66,11 @@ pub const STACK_SIZE: u32 = 22 * 1024; pub const ESTACK_SIZE: u32 = 1024; pub const NSTACK_SIZE: u32 = 1024; +pub const ICCM_RANGE: core::ops::Range = core::ops::Range { + start: ICCM_ORG, + end: ICCM_ORG + ICCM_SIZE, +}; + #[test] #[allow(clippy::assertions_on_constants)] fn mem_layout_test_manifest() { diff --git a/drivers/src/soc_ifc.rs b/drivers/src/soc_ifc.rs index eb1590265e..490bcaf688 100644 --- a/drivers/src/soc_ifc.rs +++ b/drivers/src/soc_ifc.rs @@ -237,6 +237,14 @@ impl SocIfc { let soc_ifc_regs = self.soc_ifc.regs_mut(); soc_ifc_regs.cptra_fw_rev_id().at(1).write(|_| rt_version); } + + pub fn get_version(&self) -> [u32; 3] { + [ + u32::from(self.soc_ifc.regs().cptra_hw_rev_id().read()), + self.soc_ifc.regs().cptra_fw_rev_id().at(0).read(), + self.soc_ifc.regs().cptra_fw_rev_id().at(1).read(), + ] + } } bitflags::bitflags! { diff --git a/drivers/test-fw/src/bin/lms_32_tests.rs b/drivers/test-fw/src/bin/lms_32_tests.rs index 35ab8d4052..c67cdf9b9d 100644 --- a/drivers/test-fw/src/bin/lms_32_tests.rs +++ b/drivers/test-fw/src/bin/lms_32_tests.rs @@ -551,12 +551,12 @@ fn test_lms_lower_32() { }; let final_result = Lms::default() - .verify_lms_signature(&mut sha256, &MESSAGE, &LMS_PUBLIC_KEY, &FINAL_LMS_SIG) + .verify_lms_signature_generic(&mut sha256, &MESSAGE, &LMS_PUBLIC_KEY, &FINAL_LMS_SIG) .unwrap(); assert_eq!(final_result, LmsResult::Success); let candidate_key = Lms::default() - .verify_lms_signature_cfi(&mut sha256, &MESSAGE, &LMS_PUBLIC_KEY, &FINAL_LMS_SIG) + .verify_lms_signature_cfi_generic(&mut sha256, &MESSAGE, &LMS_PUBLIC_KEY, &FINAL_LMS_SIG) .unwrap(); assert_eq!(candidate_key, HashValue::from(LMS_PUBLIC_KEY.digest)); } @@ -815,7 +815,7 @@ fn test_hss_upper_32() { }; let result = Lms::default() - .verify_lms_signature( + .verify_lms_signature_generic( &mut sha256, &PUBLIC_BUFFER, &HSS_PUBLIC_KEY, diff --git a/drivers/test-fw/src/bin/negative_tests_lms.rs b/drivers/test-fw/src/bin/negative_tests_lms.rs index 11437ebf4a..432e1c874a 100644 --- a/drivers/test-fw/src/bin/negative_tests_lms.rs +++ b/drivers/test-fw/src/bin/negative_tests_lms.rs @@ -436,7 +436,7 @@ fn test_failures_lms_24() { ); assert_eq!( - Lms::default().verify_lms_signature( + Lms::default().verify_lms_signature_generic( &mut sha256, &MESSAGE, &LmsPublicKey { diff --git a/error/src/lib.rs b/error/src/lib.rs index df7aaeb9d5..0527fa20fa 100644 --- a/error/src/lib.rs +++ b/error/src/lib.rs @@ -288,6 +288,17 @@ impl CaliptraError { pub const DRIVER_CSRNG_ADAPTP_HEALTH_CHECK_FAILED: CaliptraError = CaliptraError::new_const(0x000d0008); + pub const DRIVER_HANDOFF_INVALID_VAULT: CaliptraError = CaliptraError::new_const(0x000D100); + pub const DRIVER_HANDOFF_INVALID_KEY_ID: CaliptraError = CaliptraError::new_const(0x000D101); + pub const DRIVER_HANDOFF_INVALID_COLD_RESET_ENTRY4: CaliptraError = + CaliptraError::new_const(0x000D102); + pub const DRIVER_HANDOFF_INVALID_COLD_RESET_ENTRY48: CaliptraError = + CaliptraError::new_const(0x000D103); + pub const DRIVER_HANDOFF_INVALID_WARM_RESET_ENTRY4: CaliptraError = + CaliptraError::new_const(0x000D104); + pub const DRIVER_HANDOFF_INVALID_WARM_RESET_ENTRY48: CaliptraError = + CaliptraError::new_const(0x000D104); + /// Runtime Errors pub const RUNTIME_INTERNAL: CaliptraError = CaliptraError::new_const(0x000E0001); pub const RUNTIME_UNIMPLEMENTED_COMMAND: CaliptraError = CaliptraError::new_const(0x000E0002); @@ -312,6 +323,10 @@ impl CaliptraError { pub const RUNTIME_GET_DEVID_CERT_FAILED: CaliptraError = CaliptraError::new_const(0x000E0013); pub const RUNTIME_CERT_CHAIN_CREATION_FAILED: CaliptraError = CaliptraError::new_const(0x000E0014); + pub const RUNTIME_SELF_TEST_IN_PROGRESS: CaliptraError = CaliptraError::new_const(0x000E0015); + pub const RUNTIME_SELF_TEST_NOT_STARTED: CaliptraError = CaliptraError::new_const(0x000E0016); + pub const RUNTIME_INVALID_FMC_SIZE: CaliptraError = CaliptraError::new_const(0x000E0017); + pub const RUNTIME_INVALID_RUNTIME_SIZE: CaliptraError = CaliptraError::new_const(0x000E0018); /// FMC Errors pub const FMC_GLOBAL_NMI: CaliptraError = CaliptraError::new_const(0x000F0001); diff --git a/kat/Cargo.toml b/kat/Cargo.toml index 269e37dbbb..b5d513e5d7 100644 --- a/kat/Cargo.toml +++ b/kat/Cargo.toml @@ -12,4 +12,5 @@ doctest = false [dependencies] caliptra-drivers.workspace = true caliptra-lms-types.workspace = true -zerocopy.workspace = true \ No newline at end of file +zerocopy.workspace = true +ufmt.workspace = true diff --git a/kat/src/kats_env.rs b/kat/src/kats_env.rs new file mode 100644 index 0000000000..2a53739265 --- /dev/null +++ b/kat/src/kats_env.rs @@ -0,0 +1,29 @@ +// Licensed under the Apache-2.0 license + +use caliptra_drivers::{Ecc384, Hmac384, Lms, Sha1, Sha256, Sha384, Sha384Acc, Trng}; + +pub struct KatsEnv<'a> { + // SHA1 Engine + pub sha1: &'a mut Sha1, + + // SHA2-256 Engine + pub sha256: &'a mut Sha256, + + // SHA2-384 Engine + pub sha384: &'a mut Sha384, + + // SHA2-384 Accelerator + pub sha384_acc: &'a mut Sha384Acc, + + /// Hmac384 Engine + pub hmac384: &'a mut Hmac384, + + /// Cryptographically Secure Random Number Generator + pub trng: &'a mut Trng, + + /// LMS Engine + pub lms: &'a mut Lms, + + /// Ecc384 Engine + pub ecc384: &'a mut Ecc384, +} diff --git a/kat/src/lib.rs b/kat/src/lib.rs index 4762f0dfda..786c89ec5a 100644 --- a/kat/src/lib.rs +++ b/kat/src/lib.rs @@ -16,6 +16,7 @@ Abstract: mod ecc384_kat; mod hmac384_kat; +mod kats_env; mod lms_kat; mod sha1_kat; mod sha256_kat; @@ -25,8 +26,45 @@ mod sha384acc_kat; pub use caliptra_drivers::{CaliptraError, CaliptraResult}; pub use ecc384_kat::Ecc384Kat; pub use hmac384_kat::Hmac384Kat; +pub use kats_env::KatsEnv; pub use lms_kat::LmsKat; pub use sha1_kat::Sha1Kat; pub use sha256_kat::Sha256Kat; pub use sha384_kat::Sha384Kat; pub use sha384acc_kat::Sha384AccKat; + +use caliptra_drivers::cprintln; + +/// Execute Known Answer Tests +/// +/// # Arguments +/// +/// * `env` - ROM Environment +pub fn execute_kat(env: &mut KatsEnv) -> CaliptraResult<()> { + cprintln!("[kat] ++"); + + cprintln!("[kat] sha1"); + Sha1Kat::default().execute(env.sha1)?; + + cprintln!("[kat] SHA2-256"); + Sha256Kat::default().execute(env.sha256)?; + + cprintln!("[kat] SHA2-384"); + Sha384Kat::default().execute(env.sha384)?; + + cprintln!("[kat] SHA2-384-ACC"); + Sha384AccKat::default().execute(env.sha384_acc)?; + + cprintln!("[kat] ECC-384"); + Ecc384Kat::default().execute(env.ecc384, env.trng)?; + + cprintln!("[kat] HMAC-384"); + Hmac384Kat::default().execute(env.hmac384, env.trng)?; + + cprintln!("[kat] LMS"); + LmsKat::default().execute(env.sha256, env.lms)?; + + cprintln!("[kat] --"); + + Ok(()) +} diff --git a/rom/dev/src/flow/cold_reset/fw_processor.rs b/rom/dev/src/flow/cold_reset/fw_processor.rs index 7951f9589b..65dbb7f6d8 100644 --- a/rom/dev/src/flow/cold_reset/fw_processor.rs +++ b/rom/dev/src/flow/cold_reset/fw_processor.rs @@ -15,15 +15,20 @@ Abstract: use crate::flow::fake::FakeRomImageVerificationEnv; use crate::fuse::log_fuse_data; use crate::rom_env::RomEnv; -use crate::{cprintln, verifier::RomImageVerificationEnv}; +use crate::run_fips_tests; +use crate::CALIPTRA_ROM_INFO; use crate::{pcr, wdt}; use caliptra_cfi_derive::cfi_impl_fn; use caliptra_common::capabilities::Capabilities; +use caliptra_common::fips::FipsVersionCmd; use caliptra_common::mailbox_api::CommandId; -use caliptra_common::{cprint, FuseLogEntryId, RomBootStatus::*}; +use caliptra_common::mailbox_api::MailboxResp; +use caliptra_common::verifier::FirmwareImageVerificationEnv; +use caliptra_common::{FuseLogEntryId, RomBootStatus::*}; use caliptra_drivers::*; use caliptra_image_types::{ImageManifest, IMAGE_BYTE_SIZE}; use caliptra_image_verify::{ImageVerificationInfo, ImageVerificationLogInfo, ImageVerifier}; +use caliptra_kat::KatsEnv; use caliptra_x509::{NotAfter, NotBefore}; use core::mem::ManuallyDrop; use zerocopy::AsBytes; @@ -52,8 +57,34 @@ impl FirmwareProcessor { // Disable the watchdog timer during processing mailbox commands. wdt::stop_wdt(&mut env.soc_ifc); + let mut kats_env = caliptra_kat::KatsEnv { + // SHA1 Engine + sha1: &mut env.sha1, + + // sha256 + sha256: &mut env.sha256, + + // SHA2-384 Engine + sha384: &mut env.sha384, + + // SHA2-384 Accelerator + sha384_acc: &mut env.sha384_acc, + + // Hmac384 Engine + hmac384: &mut env.hmac384, + + /// Cryptographically Secure Random Number Generator + trng: &mut env.trng, + + // LMS Engine + lms: &mut env.lms, + + /// Ecc384 Engine + ecc384: &mut env.ecc384, + }; // Process mailbox commands. - let mut txn = Self::process_mailbox_commands(&mut env.soc_ifc, &mut env.mbox)?; + let mut txn = + Self::process_mailbox_commands(&mut env.soc_ifc, &mut env.mbox, &mut kats_env)?; // Renable the watchdog timer. wdt::start_wdt(&mut env.soc_ifc); @@ -62,7 +93,7 @@ impl FirmwareProcessor { let manifest = Self::load_manifest(&mut env.persistent_data, &mut txn); let manifest = okref(&manifest)?; - let mut venv = RomImageVerificationEnv { + let mut venv = FirmwareImageVerificationEnv { sha256: &mut env.sha256, sha384: &mut env.sha384, sha384_acc: &mut env.sha384_acc, @@ -70,20 +101,19 @@ impl FirmwareProcessor { ecc384: &mut env.ecc384, data_vault: &mut env.data_vault, pcr_bank: &mut env.pcr_bank, - persistent_data: &mut env.persistent_data, }; // Verify the image let info = Self::verify_image(&mut venv, manifest, txn.dlen()); let info = okref(&info)?; - Self::update_fuse_log(&mut venv.persistent_data.get_mut().fuse_log, &info.log_info)?; + Self::update_fuse_log(&mut env.persistent_data.get_mut().fuse_log, &info.log_info)?; // Populate data vault - Self::populate_data_vault(venv.data_vault, info, venv.persistent_data); + Self::populate_data_vault(venv.data_vault, info, &env.persistent_data); // Extend PCR0 and PCR1 - pcr::extend_pcrs(&mut venv, info)?; + pcr::extend_pcrs(&mut venv, info, &mut env.persistent_data)?; report_boot_status(FwProcessorExtendPcrComplete.into()); // Load the image @@ -126,18 +156,53 @@ impl FirmwareProcessor { fn process_mailbox_commands<'a>( soc_ifc: &mut SocIfc, mbox: &'a mut Mailbox, + env: &mut KatsEnv, ) -> CaliptraResult>> { soc_ifc.flow_status_set_ready_for_firmware(); - cprint!("[afmc] Waiting for Commands..."); + let mut self_test_in_progress = false; + + cprintln!("[afmc] Waiting for Commands..."); loop { if let Some(txn) = mbox.peek_recv() { report_fw_error_non_fatal(0); match CommandId::from(txn.cmd()) { - CommandId::SELF_TEST | CommandId::VERSION | CommandId::SHUTDOWN => { - // [TODO] Placeholder for FIPS ROM commands. - txn.start_txn().complete(false)?; - continue; + CommandId::VERSION => { + let mut resp = FipsVersionCmd::execute(soc_ifc)?; + resp.populate_chksum()?; + txn.start_txn().send_response(resp.as_bytes())?; + } + CommandId::SELF_TEST_START => { + cprintln!("[afmc] FIPS self test"); + if self_test_in_progress { + txn.start_txn().complete(false)?; + } else { + let rom_info = unsafe { &CALIPTRA_ROM_INFO }; + run_fips_tests(env, rom_info)?; + let mut resp = MailboxResp::default(); + resp.populate_chksum()?; + txn.start_txn().send_response(resp.as_bytes())?; + self_test_in_progress = true; + } + } + CommandId::SELF_TEST_GET_RESULTS => { + if !self_test_in_progress { + txn.start_txn().complete(false)?; + } else { + let mut resp = MailboxResp::default(); + resp.populate_chksum()?; + txn.start_txn().send_response(resp.as_bytes())?; + self_test_in_progress = false; + } + } + CommandId::SHUTDOWN => { + cprintln!("[afmc] FIPS shutdown"); + let mut resp = MailboxResp::default(); + resp.populate_chksum()?; + txn.start_txn().send_response(resp.as_bytes())?; + + // Causing a ROM Fatal Error will zeroize the module + return Err(CaliptraError::RUNTIME_SHUTDOWN); } CommandId::CAPABILITIES => { let mut capabilities = Capabilities::default(); @@ -199,7 +264,7 @@ impl FirmwareProcessor { /// * `env` - ROM Environment #[cfg_attr(not(feature = "no-cfi"), cfi_impl_fn)] fn verify_image( - venv: &mut RomImageVerificationEnv, + venv: &mut FirmwareImageVerificationEnv, manifest: &ImageManifest, img_bundle_sz: u32, ) -> CaliptraResult { diff --git a/rom/dev/src/flow/fake.rs b/rom/dev/src/flow/fake.rs index 40f1162834..0bf6311581 100644 --- a/rom/dev/src/flow/fake.rs +++ b/rom/dev/src/flow/fake.rs @@ -344,7 +344,7 @@ impl<'a> ImageVerificationEnv for &mut FakeRomImageVerificationEnv<'a> { } fn iccm_range(&self) -> Range { - RomEnv::ICCM_RANGE + caliptra_common::memory_layout::ICCM_RANGE } fn lms_verify_enabled(&self) -> bool { diff --git a/rom/dev/src/flow/update_reset.rs b/rom/dev/src/flow/update_reset.rs index 9e15f6bb83..0d0f741bed 100644 --- a/rom/dev/src/flow/update_reset.rs +++ b/rom/dev/src/flow/update_reset.rs @@ -13,7 +13,8 @@ Abstract: --*/ #[cfg(feature = "fake-rom")] use crate::flow::fake::FakeRomImageVerificationEnv; -use crate::{cprintln, pcr, rom_env::RomEnv, verifier::RomImageVerificationEnv}; +use crate::{cprintln, pcr, rom_env::RomEnv}; +use caliptra_common::verifier::FirmwareImageVerificationEnv; use caliptra_cfi_derive::cfi_impl_fn; use caliptra_common::mailbox_api::CommandId; @@ -55,7 +56,7 @@ impl UpdateResetFlow { let manifest = Self::load_manifest(env.persistent_data.get_mut(), &mut recv_txn)?; report_boot_status(UpdateResetLoadManifestComplete.into()); - let mut venv = RomImageVerificationEnv { + let mut venv = FirmwareImageVerificationEnv { sha256: &mut env.sha256, sha384: &mut env.sha384, sha384_acc: &mut env.sha384_acc, @@ -63,7 +64,6 @@ impl UpdateResetFlow { ecc384: &mut env.ecc384, data_vault: &mut env.data_vault, pcr_bank: &mut env.pcr_bank, - persistent_data: &mut env.persistent_data, }; let info = Self::verify_image(&mut venv, &manifest, recv_txn.dlen()); @@ -71,7 +71,7 @@ impl UpdateResetFlow { report_boot_status(UpdateResetImageVerificationComplete.into()); // Extend PCR0 and PCR1 - pcr::extend_pcrs(&mut venv, info)?; + pcr::extend_pcrs(&mut venv, info, &mut env.persistent_data)?; report_boot_status(UpdateResetExtendPcrComplete.into()); cprintln!( @@ -112,7 +112,7 @@ impl UpdateResetFlow { /// #[cfg_attr(not(feature = "no-cfi"), cfi_impl_fn)] fn verify_image( - env: &mut RomImageVerificationEnv, + env: &mut FirmwareImageVerificationEnv, manifest: &ImageManifest, img_bundle_sz: u32, ) -> CaliptraResult { diff --git a/rom/dev/src/kat.rs b/rom/dev/src/kat.rs index 330ab15647..caebfddef8 100644 --- a/rom/dev/src/kat.rs +++ b/rom/dev/src/kat.rs @@ -12,44 +12,19 @@ Abstract: --*/ -use caliptra_common::RomBootStatus::*; use caliptra_drivers::{report_boot_status, CaliptraResult}; -use caliptra_kat::{Ecc384Kat, Hmac384Kat, LmsKat, Sha1Kat, Sha256Kat, Sha384AccKat, Sha384Kat}; -use crate::{cprintln, rom_env::RomEnv}; +use crate::KatsEnv; +use caliptra_common::RomBootStatus::{KatComplete, KatStarted}; /// Execute Known Answer Tests /// /// # Arguments /// /// * `env` - ROM Environment -pub fn execute_kat(env: &mut RomEnv) -> CaliptraResult<()> { - cprintln!("[kat] ++"); +pub fn execute_kat(kats_env: &mut KatsEnv) -> CaliptraResult<()> { report_boot_status(KatStarted.into()); - - cprintln!("[kat] sha1"); - Sha1Kat::default().execute(&mut env.sha1)?; - - cprintln!("[kat] SHA2-256"); - Sha256Kat::default().execute(&mut env.sha256)?; - - cprintln!("[kat] SHA2-384"); - Sha384Kat::default().execute(&mut env.sha384)?; - - cprintln!("[kat] SHA2-384-ACC"); - Sha384AccKat::default().execute(&mut env.sha384_acc)?; - - cprintln!("[kat] ECC-384"); - Ecc384Kat::default().execute(&mut env.ecc384, &mut env.trng)?; - - cprintln!("[kat] HMAC-384"); - Hmac384Kat::default().execute(&mut env.hmac384, &mut env.trng)?; - - cprintln!("[kat] LMS"); - LmsKat::default().execute(&mut env.sha256, &env.lms)?; - + caliptra_kat::execute_kat(kats_env)?; report_boot_status(KatComplete.into()); - cprintln!("[kat] --"); - Ok(()) } diff --git a/rom/dev/src/main.rs b/rom/dev/src/main.rs index cea7af1267..4e63064bf2 100644 --- a/rom/dev/src/main.rs +++ b/rom/dev/src/main.rs @@ -25,6 +25,7 @@ use caliptra_drivers::{ }; use caliptra_error::CaliptraResult; use caliptra_image_types::RomInfo; +use caliptra_kat::KatsEnv; use rom_env::RomEnv; #[cfg(not(feature = "std"))] @@ -41,7 +42,6 @@ mod kat; mod lock; mod pcr; mod rom_env; -mod verifier; mod wdt; use caliptra_drivers::printer as print; @@ -103,7 +103,32 @@ pub extern "C" fn rom_entry() -> ! { wdt::start_wdt(&mut env.soc_ifc); if !cfg!(feature = "fake-rom") { - let result = run_fips_tests(&mut env, rom_info); + let mut kats_env = caliptra_kat::KatsEnv { + // SHA1 Engine + sha1: &mut env.sha1, + + // sha256 + sha256: &mut env.sha256, + + // SHA2-384 Engine + sha384: &mut env.sha384, + + // SHA2-384 Accelerator + sha384_acc: &mut env.sha384_acc, + + // Hmac384 Engine + hmac384: &mut env.hmac384, + + /// Cryptographically Secure Random Number Generator + trng: &mut env.trng, + + // LMS Engine + lms: &mut env.lms, + + /// Ecc384 Engine + ecc384: &mut env.ecc384, + }; + let result = run_fips_tests(&mut kats_env, rom_info); if let Err(err) = result { handle_fatal_error(err.into()); } @@ -151,12 +176,12 @@ pub extern "C" fn rom_entry() -> ! { caliptra_drivers::ExitCtrl::exit(0); } -fn run_fips_tests(env: &mut RomEnv, rom_info: &RomInfo) -> CaliptraResult<()> { +fn run_fips_tests(env: &mut KatsEnv, rom_info: &RomInfo) -> CaliptraResult<()> { rom_integrity_test(env, &rom_info.sha256_digest)?; kat::execute_kat(env) } -fn rom_integrity_test(env: &mut RomEnv, expected_digest: &[u32; 8]) -> CaliptraResult<()> { +fn rom_integrity_test(env: &mut KatsEnv, expected_digest: &[u32; 8]) -> CaliptraResult<()> { // WARNING: It is undefined behavior to dereference a zero (null) pointer in // rust code. This is only safe because the dereference is being done by an // an assembly routine ([`ureg::opt_riscv::copy_16_words`]) rather diff --git a/rom/dev/src/pcr.rs b/rom/dev/src/pcr.rs index 48f9cfdec4..646cd11ef6 100644 --- a/rom/dev/src/pcr.rs +++ b/rom/dev/src/pcr.rs @@ -21,13 +21,15 @@ Note: --*/ -use crate::verifier::RomImageVerificationEnv; use caliptra_cfi_derive::{cfi_impl_fn, cfi_mod_fn}; +use caliptra_common::verifier::FirmwareImageVerificationEnv; use caliptra_common::{ pcr::{PCR_ID_FMC_CURRENT, PCR_ID_FMC_JOURNEY}, PcrLogEntry, PcrLogEntryId, }; -use caliptra_drivers::{Array4x12, CaliptraError, CaliptraResult, PcrBank, PcrLogArray, Sha384}; +use caliptra_drivers::{ + Array4x12, CaliptraError, CaliptraResult, PcrBank, PcrLogArray, PersistentDataAccessor, Sha384, +}; use caliptra_image_verify::ImageVerificationInfo; use zerocopy::AsBytes; @@ -67,14 +69,15 @@ impl PcrExtender<'_> { /// * `env` - ROM Environment #[cfg_attr(not(feature = "no-cfi"), cfi_mod_fn)] pub(crate) fn extend_pcrs( - env: &mut RomImageVerificationEnv, + env: &mut FirmwareImageVerificationEnv, info: &ImageVerificationInfo, + persistent_data: &mut PersistentDataAccessor, ) -> CaliptraResult<()> { // Clear the Current PCR, but do not clear the Journey PCR env.pcr_bank.erase_pcr(PCR_ID_FMC_CURRENT)?; let mut pcr = PcrExtender { - pcr_log: &mut env.persistent_data.get_mut().pcr_log, + pcr_log: &mut persistent_data.get_mut().pcr_log, pcr_bank: env.pcr_bank, sha384: env.sha384, }; diff --git a/rom/dev/src/rom_env.rs b/rom/dev/src/rom_env.rs index cb84c8f069..bb6287462b 100644 --- a/rom/dev/src/rom_env.rs +++ b/rom/dev/src/rom_env.rs @@ -16,7 +16,6 @@ Abstract: --*/ use crate::fht::FhtDataStore; -use caliptra_common::memory_layout::*; use caliptra_drivers::{ DataVault, DeobfuscationEngine, Ecc384, Hmac384, KeyVault, Lms, Mailbox, PcrBank, PersistentDataAccessor, Sha1, Sha256, Sha384, Sha384Acc, SocIfc, Trng, @@ -27,7 +26,6 @@ use caliptra_registers::{ hmac::HmacReg, kv::KvReg, mbox::MboxCsr, pv::PvReg, sha256::Sha256Reg, sha512::Sha512Reg, sha512_acc::Sha512AccCsr, soc_ifc::SocIfcReg, soc_ifc_trng::SocIfcTrngReg, }; -use core::ops::Range; /// Rom Context pub struct RomEnv { @@ -81,11 +79,6 @@ pub struct RomEnv { } impl RomEnv { - pub const ICCM_RANGE: Range = Range { - start: ICCM_ORG, - end: ICCM_ORG + ICCM_SIZE, - }; - pub unsafe fn new_from_registers() -> CaliptraResult { let trng = Trng::new( CsrngReg::new(), diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index 2b35beb8d0..4d52a037b8 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -20,6 +20,7 @@ platform.workspace = true ufmt.workspace = true zerocopy.workspace = true arrayvec.workspace = true +caliptra-image-verify.workspace = true [build-dependencies] caliptra_common = { workspace = true, default-features = false } @@ -45,3 +46,4 @@ riscv = ["caliptra-cpu/riscv"] std = ["ufmt/std", "caliptra_common/std"] test_only_commands = [] verilator = ["caliptra-hw-model/verilator"] +fips_self_test=[] \ No newline at end of file diff --git a/runtime/src/fips.rs b/runtime/src/fips.rs index 4d08137d37..9b97d99c08 100644 --- a/runtime/src/fips.rs +++ b/runtime/src/fips.rs @@ -1,7 +1,7 @@ // Licensed under the Apache-2.0 license use caliptra_common::cprintln; -use caliptra_common::mailbox_api::{FipsVersionResp, MailboxResp, MailboxRespHeader}; +use caliptra_common::mailbox_api::{MailboxResp, MailboxRespHeader}; use caliptra_drivers::CaliptraError; use caliptra_drivers::CaliptraResult; use caliptra_drivers::Ecc384; @@ -10,7 +10,6 @@ use caliptra_drivers::KeyVault; use caliptra_drivers::Sha256; use caliptra_drivers::Sha384; use caliptra_drivers::Sha384Acc; -use caliptra_kat::{Ecc384Kat, Hmac384Kat, Sha256Kat, Sha384AccKat, Sha384Kat}; use caliptra_registers::mbox::enums::MboxStatusE; use crate::Drivers; @@ -37,58 +36,146 @@ impl FipsModule { } env.persistent_data.get_mut().zeroize(); } +} - /// Execute KAT for cryptographic algorithms implemented in H/W. - fn execute_kats(env: &mut Drivers) -> CaliptraResult<()> { - cprintln!("[kat] Executing SHA2-256 Engine KAT"); - Sha256Kat::default().execute(&mut env.sha256)?; - - cprintln!("[kat] Executing SHA2-384 Engine KAT"); - Sha384Kat::default().execute(&mut env.sha384)?; +#[cfg(feature = "fips_self_test")] +pub mod fips_self_test_cmd { + use super::*; + use crate::RtBootStatus::{RtFipSelfTestComplete, RtFipSelfTestStarted}; + use caliptra_common::HexBytes; + use caliptra_common::{ + verifier::FirmwareImageVerificationEnv, FMC_ORG, FMC_SIZE, RUNTIME_ORG, RUNTIME_SIZE, + }; + use caliptra_drivers::ResetReason; + use caliptra_image_types::RomInfo; + use caliptra_image_verify::ImageVerifier; + use zerocopy::AsBytes; + + // Helper function to create a slice from a memory region + unsafe fn create_slice(org: u32, size: usize) -> &'static [u8] { + let ptr = org as *mut u8; + core::slice::from_raw_parts(ptr, size) + } + pub enum SelfTestStatus { + Idle, + InProgress(fn(&mut Drivers) -> CaliptraResult<()>), + Done, + } - cprintln!("[kat] Executing SHA2-384 Accelerator KAT"); - Sha384AccKat::default().execute(&mut env.sha384_acc)?; + fn copy_and_verify_image(env: &mut Drivers) -> CaliptraResult<()> { + env.mbox.write_cmd(0)?; + env.mbox.set_dlen( + env.persistent_data.get().manifest1.size + + env.persistent_data.get().manifest1.fmc.size + + env.persistent_data.get().manifest1.runtime.size, + ); + env.mbox + .copy_bytes_to_mbox(env.persistent_data.get().manifest1.as_bytes())?; + + let fmc_size = env.persistent_data.get().manifest1.fmc.size; + if fmc_size > FMC_SIZE { + return Err(CaliptraError::RUNTIME_INVALID_FMC_SIZE); + } + let fmc = unsafe { create_slice(FMC_ORG, fmc_size as usize) }; + env.mbox.copy_bytes_to_mbox(fmc.as_bytes())?; - cprintln!("[kat] Executing ECC-384 Engine KAT"); - Ecc384Kat::default().execute(&mut env.ecc384, &mut env.trng)?; + let runtime_size = env.persistent_data.get().manifest1.runtime.size; + if runtime_size > RUNTIME_SIZE { + return Err(CaliptraError::RUNTIME_INVALID_RUNTIME_SIZE); + } + let rt = unsafe { create_slice(RUNTIME_ORG, runtime_size as usize) }; + env.mbox.copy_bytes_to_mbox(rt.as_bytes())?; + + let mut venv = FirmwareImageVerificationEnv { + sha256: &mut env.sha256, + sha384: &mut env.sha384, + sha384_acc: &mut env.sha384_acc, + soc_ifc: &mut env.soc_ifc, + ecc384: &mut env.ecc384, + data_vault: &mut env.data_vault, + pcr_bank: &mut env.pcr_bank, + }; - cprintln!("[kat] Executing HMAC-384 Engine KAT"); - Hmac384Kat::default().execute(&mut env.hmac384, &mut env.trng)?; + let mut verifier = ImageVerifier::new(&mut venv); + let _info = verifier.verify( + &env.persistent_data.get().manifest1, + env.persistent_data.get().manifest1.size + + env.persistent_data.get().manifest1.fmc.size + + env.persistent_data.get().manifest1.runtime.size, + ResetReason::UpdateReset, + )?; + env.mbox.unlock(); + cprintln!("[rt] Verify complete"); + Ok(()) + } + pub(crate) fn execute(env: &mut Drivers) -> CaliptraResult<()> { + caliptra_drivers::report_boot_status(RtFipSelfTestStarted.into()); + cprintln!("[rt] FIPS self test"); + rom_integrity_test(env)?; + execute_kats(env)?; + copy_and_verify_image(env)?; + caliptra_drivers::report_boot_status(RtFipSelfTestComplete.into()); Ok(()) } -} -pub struct FipsVersionCmd; -impl FipsVersionCmd { - pub const NAME: [u8; 12] = *b"Caliptra RTM"; - pub const MODE: u32 = 0x46495053; + /// Execute KAT for cryptographic algorithms implemented in H/W. + fn execute_kats(env: &mut Drivers) -> CaliptraResult<()> { + let mut kats_env = caliptra_kat::KatsEnv { + // SHA1 Engine + sha1: &mut env.sha1, + + // sha256 + sha256: &mut env.sha256, + + // SHA2-384 Engine + sha384: &mut env.sha384, + + // SHA2-384 Accelerator + sha384_acc: &mut env.sha384_acc, - pub(crate) fn execute(_env: &mut Drivers) -> CaliptraResult { - cprintln!("[rt] FIPS Version"); + // Hmac384 Engine + hmac384: &mut env.hmac384, - let resp = FipsVersionResp { - hdr: MailboxRespHeader::default(), - mode: Self::MODE, - // Just return all zeroes for now. - fips_rev: [1, 0, 0], - name: Self::NAME, + /// Cryptographically Secure Random Number Generator + trng: &mut env.trng, + + // LMS Engine + lms: &mut env.lms, + + /// Ecc384 Engine + ecc384: &mut env.ecc384, }; - Ok(MailboxResp::FipsVersion(resp)) + caliptra_kat::execute_kat(&mut kats_env)?; + Ok(()) } -} -pub struct FipsSelfTestCmd; -impl FipsSelfTestCmd { - pub(crate) fn execute(env: &mut Drivers) -> CaliptraResult { - cprintln!("[rt] FIPS self test"); - FipsModule::execute_kats(env)?; + fn rom_integrity_test(env: &mut Drivers) -> CaliptraResult<()> { + // Extract the expected has from the fht. + let rom_info = env.persistent_data.get().fht.rom_info_addr.get()?; + + // WARNING: It is undefined behavior to dereference a zero (null) pointer in + // rust code. This is only safe because the dereference is being done by an + // an assembly routine ([`ureg::opt_riscv::copy_16_words`]) rather + // than dereferencing directly in Rust. + #[allow(clippy::zero_ptr)] + let rom_start = 0 as *const [u32; 16]; + + let n_blocks = + env.persistent_data.get().fht.rom_info_addr.get()? as *const RomInfo as usize / 64; + + let digest = unsafe { env.sha256.digest_blocks_raw(rom_start, n_blocks)? }; + cprintln!("ROM Digest: {}", HexBytes(&<[u8; 32]>::from(digest))); + if digest.0 != rom_info.sha256_digest { + cprintln!("ROM integrity test failed"); + return Err(CaliptraError::ROM_INTEGRITY_FAILURE); + } - Ok(MailboxResp::default()) + // Run digest function and compare with expected hash. + Ok(()) } } - pub struct FipsShutdownCmd; impl FipsShutdownCmd { pub(crate) fn execute(env: &mut Drivers) -> CaliptraResult { diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index fbf399a923..c49ae43e19 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -1,6 +1,7 @@ // Licensed under the Apache-2.0 license #![no_std] +#![cfg_attr(not(feature = "fip-self-test"), allow(unused))] pub mod dice; mod disable; @@ -20,24 +21,31 @@ use arrayvec::ArrayVec; use crypto::{AlgLen, Crypto}; use mailbox::Mailbox; +pub use caliptra_common::fips::FipsVersionCmd; #[cfg(feature = "test_only_commands")] pub use dice::{GetLdevCertCmd, TestGetFmcAliasCertCmd}; pub use disable::DisableAttestationCmd; use dpe_crypto::DpeCrypto; pub use dpe_platform::{DpePlatform, VENDOR_ID, VENDOR_SKU}; -pub use fips::{FipsSelfTestCmd, FipsShutdownCmd, FipsVersionCmd}; +pub use fips::FipsShutdownCmd; +#[cfg(feature = "fips_self_test")] +pub use fips::{fips_self_test_cmd, fips_self_test_cmd::SelfTestStatus}; + pub use info::{FwInfoCmd, IDevIdCertCmd, IDevIdInfoCmd}; pub use invoke_dpe::InvokeDpeCmd; pub use stash_measurement::StashMeasurementCmd; pub use verify::EcdsaVerifyCmd; pub mod packet; +use caliptra_common::mailbox_api::CommandId; use packet::Packet; use caliptra_common::cprintln; -use caliptra_common::mailbox_api::CommandId; +#[cfg(feature = "fips_self_test")] +use caliptra_common::mailbox_api::MailboxResp; + use caliptra_drivers::{ - report_fw_error_non_fatal, CaliptraError, CaliptraResult, DataVault, Ecc384, KeyVault, - PersistentDataAccessor, SocIfc, + CaliptraError, CaliptraResult, DataVault, Ecc384, KeyVault, Lms, PersistentDataAccessor, Sha1, + SocIfc, }; use caliptra_drivers::{Hmac384, PcrBank, PcrId, Sha256, Sha384, Sha384Acc, Trng}; use caliptra_registers::mbox::enums::MboxStatusE; @@ -65,6 +73,8 @@ const RUNTIME_BOOT_STATUS_BASE: u32 = 0x600; pub enum RtBootStatus { // RtAlias Statuses RtReadyForCommands = RUNTIME_BOOT_STATUS_BASE, + RtFipSelfTestStarted = RUNTIME_BOOT_STATUS_BASE + 1, + RtFipSelfTestComplete = RUNTIME_BOOT_STATUS_BASE + 2, } impl From for u32 { @@ -102,11 +112,18 @@ pub struct Drivers { pub persistent_data: PersistentDataAccessor, + pub lms: Lms, + + pub sha1: Sha1, + pub dpe: DpeInstance, pub pcr_bank: PcrBank, pub cert_chain: ArrayVec, + + #[cfg(feature = "fips_self_test")] + pub self_test_status: SelfTestStatus, } pub struct CptraDpeTypes; @@ -171,10 +188,14 @@ impl Drivers { sha384_acc: Sha384Acc::new(Sha512AccCsr::new()), hmac384, ecc384, + sha1: Sha1::default(), + lms: Lms::default(), trng, persistent_data, dpe, pcr_bank, + #[cfg(feature = "fips_self_test")] + self_test_status: SelfTestStatus::Idle, cert_chain, }) } @@ -245,7 +266,19 @@ impl Drivers { } } -fn wait_for_cmd(_mbox: &mut Mailbox) { +/// Run pending jobs and enter low power mode. +fn enter_idle(drivers: &mut Drivers) { + // Run pending jobs before entering low power mode. + #[cfg(feature = "fips_self_test")] + if let SelfTestStatus::InProgress(execute) = drivers.self_test_status { + if drivers.mbox.lock() == false { + match execute(drivers) { + Ok(_) => drivers.self_test_status = SelfTestStatus::Done, + Err(e) => caliptra_drivers::report_fw_error_non_fatal(e.into()), + } + } + } + // TODO: Enable interrupts? //#[cfg(feature = "riscv")] //unsafe { @@ -294,8 +327,23 @@ fn handle_command(drivers: &mut Drivers) -> CaliptraResult { CommandId::TEST_ONLY_GET_FMC_ALIAS_CERT => TestGetFmcAliasCertCmd::execute(drivers), #[cfg(feature = "test_only_commands")] CommandId::TEST_ONLY_HMAC384_VERIFY => HmacVerifyCmd::execute(drivers, cmd_bytes), - CommandId::VERSION => FipsVersionCmd::execute(drivers), - CommandId::SELF_TEST => FipsSelfTestCmd::execute(drivers), + CommandId::VERSION => FipsVersionCmd::execute(&drivers.soc_ifc), + #[cfg(feature = "fips_self_test")] + CommandId::SELF_TEST_START => match drivers.self_test_status { + SelfTestStatus::Idle => { + drivers.self_test_status = SelfTestStatus::InProgress(fips_self_test_cmd::execute); + Ok(MailboxResp::default()) + } + _ => Err(CaliptraError::RUNTIME_SELF_TEST_IN_PROGRESS), + }, + #[cfg(feature = "fips_self_test")] + CommandId::SELF_TEST_GET_RESULTS => match drivers.self_test_status { + SelfTestStatus::Done => { + drivers.self_test_status = SelfTestStatus::Idle; + Ok(MailboxResp::default()) + } + _ => Err(CaliptraError::RUNTIME_SELF_TEST_NOT_STARTED), + }, CommandId::SHUTDOWN => FipsShutdownCmd::execute(drivers), _ => Err(CaliptraError::RUNTIME_UNIMPLEMENTED_COMMAND), }?; @@ -311,14 +359,14 @@ pub fn handle_mailbox_commands(drivers: &mut Drivers) -> ! { drivers.soc_ifc.assert_ready_for_runtime(); caliptra_drivers::report_boot_status(RtBootStatus::RtReadyForCommands.into()); loop { - wait_for_cmd(&mut drivers.mbox); + enter_idle(drivers); if drivers.mbox.is_cmd_ready() { // TODO : Move start/stop WDT to wait_for_cmd when NMI is implemented. caliptra_common::wdt::start_wdt( &mut drivers.soc_ifc, caliptra_common::WdtTimeout::default(), ); - report_fw_error_non_fatal(0); + caliptra_drivers::report_fw_error_non_fatal(0); match handle_command(drivers) { Ok(status) => { drivers.mbox.set_status(status); diff --git a/runtime/src/mailbox.rs b/runtime/src/mailbox.rs index 804a4a9c97..58af349066 100644 --- a/runtime/src/mailbox.rs +++ b/runtime/src/mailbox.rs @@ -1,7 +1,11 @@ // Licensed under the Apache-2.0 license use caliptra_drivers::CaliptraResult; -use caliptra_registers::mbox::{enums::MboxStatusE, MboxCsr}; +use caliptra_error::CaliptraError; +use caliptra_registers::mbox::{ + enums::{MboxFsmE, MboxStatusE}, + MboxCsr, +}; use zerocopy::{AsBytes, LayoutVerified, Unalign}; use crate::CommandId; @@ -45,6 +49,26 @@ impl Mailbox { CommandId(cmd_code) } + pub fn lock(&mut self) -> bool { + let mbox = self.mbox.regs(); + mbox.lock().read().lock() + } + pub fn unlock(&mut self) { + let mbox = self.mbox.regs_mut(); + mbox.unlock().write(|_| 1.into()); + } + + pub fn write_cmd(&mut self, cmd: u32) -> CaliptraResult<()> { + let mbox = self.mbox.regs_mut(); + match mbox.status().read().mbox_fsm_ps() { + MboxFsmE::MboxRdyForCmd => { + mbox.cmd().write(|_| cmd); + Ok(()) + } + _ => Err(CaliptraError::RUNTIME_INTERNAL), + } + } + pub fn user(&self) -> u32 { let mbox = self.mbox.regs(); mbox.user().read() @@ -57,27 +81,40 @@ impl Mailbox { } } - pub fn copy_to_mbox(&mut self, buf: &[Unalign]) { + pub fn flush(&mut self) { + let count = self.dlen_words(); + let mbox = self.mbox.regs_mut(); + for _ii in 0..count { + let _ = mbox.dataout().read(); + } + } + + pub fn copy_words_to_mbox(&mut self, buf: &[Unalign]) { let mbox = self.mbox.regs_mut(); for word in buf { mbox.datain().write(|_| word.get()); } } - /// Write a word-aligned `buf` to the mailbox - pub fn write_response(&mut self, buf: &[u8]) -> CaliptraResult<()> { + pub fn copy_bytes_to_mbox(&mut self, buf: &[u8]) -> CaliptraResult<()> { let (buf_words, suffix) = LayoutVerified::new_slice_unaligned_from_prefix(buf, buf.len() / 4).unwrap(); - self.set_dlen(buf.len() as u32); - self.copy_to_mbox(&buf_words); + self.copy_words_to_mbox(&buf_words); if !suffix.is_empty() { let mut last_word = 0_u32; last_word.as_bytes_mut()[..suffix.len()].copy_from_slice(suffix); - self.copy_to_mbox(&[Unalign::new(last_word)]); + self.copy_words_to_mbox(&[Unalign::new(last_word)]); } Ok(()) } + /// Write a word-aligned `buf` to the mailbox + pub fn write_response(&mut self, buf: &[u8]) -> CaliptraResult<()> { + self.set_dlen(buf.len() as u32); + self.copy_bytes_to_mbox(buf); + Ok(()) + } + pub fn set_status(&mut self, status: MboxStatusE) { let mbox = self.mbox.regs_mut(); mbox.status().write(|w| w.status(|_| status)); diff --git a/runtime/tests/integration_tests.rs b/runtime/tests/integration_tests.rs index 573e5ec937..e412a89eb3 100644 --- a/runtime/tests/integration_tests.rs +++ b/runtime/tests/integration_tests.rs @@ -665,25 +665,6 @@ fn test_fips_cmd_api() { let name = &fips_version.name[..]; assert_eq!(name, FipsVersionCmd::NAME.as_bytes()); - // SELF_TEST - let payload = MailboxReqHeader { - chksum: caliptra_common::checksum::calc_checksum(u32::from(CommandId::SELF_TEST), &[]), - }; - - let resp = model - .mailbox_execute(u32::from(CommandId::SELF_TEST), payload.as_bytes()) - .unwrap() - .unwrap(); - - let resp = MailboxRespHeader::read_from(resp.as_slice()).unwrap(); - // Verify checksum and FIPS status - assert!(caliptra_common::checksum::verify_checksum( - resp.chksum, - 0x0, - &resp.as_bytes()[core::mem::size_of_val(&resp.chksum)..], - )); - assert_eq!(resp.fips_status, MailboxRespHeader::FIPS_STATUS_APPROVED); - // SHUTDOWN let payload = MailboxReqHeader { chksum: caliptra_common::checksum::calc_checksum(u32::from(CommandId::SHUTDOWN), &[]), diff --git a/sw-emulator/lib/periph/src/soc_reg.rs b/sw-emulator/lib/periph/src/soc_reg.rs index 0ddb9e8046..bba9f9f1c6 100644 --- a/sw-emulator/lib/periph/src/soc_reg.rs +++ b/sw-emulator/lib/periph/src/soc_reg.rs @@ -627,7 +627,7 @@ impl SocRegistersImpl { cptra_clk_gating_en: ReadOnlyRegister::new(0), cptra_generic_input_wires: Default::default(), cptra_generic_output_wires: Default::default(), - cptra_hw_rev_id: ReadOnlyRegister::new(0), + cptra_hw_rev_id: ReadOnlyRegister::new(1), cptra_fw_rev_id: Default::default(), cptra_hw_config: 0, fuse_uds_seed: words_from_bytes_be(&Self::UDS), diff --git a/test/Cargo.toml b/test/Cargo.toml index 2383a13005..fffb9c1333 100644 --- a/test/Cargo.toml +++ b/test/Cargo.toml @@ -24,3 +24,4 @@ openssl.workspace = true fpga_realtime = ["caliptra-hw-model/fpga_realtime"] itrng = ["caliptra-hw-model/itrng"] verilator = ["caliptra-hw-model/verilator"] +fips_self_test = ["caliptra-runtime/fips_self_test"] diff --git a/test/tests/smoke_test.rs b/test/tests/smoke_test.rs index 1d1c674248..6748e88e3b 100644 --- a/test/tests/smoke_test.rs +++ b/test/tests/smoke_test.rs @@ -1,10 +1,12 @@ // Licensed under the Apache-2.0 license use caliptra_builder::{ImageOptions, APP_WITH_UART, FMC_WITH_UART, ROM_WITH_UART}; +use caliptra_common::fips::FipsVersionCmd; use caliptra_common::mailbox_api::{ - CommandId, GetLdevCertResp, MailboxReqHeader, MailboxRespHeader, TestGetFmcAliasCertResp, + CommandId, FipsVersionResp, GetLdevCertResp, MailboxReqHeader, MailboxRespHeader, + TestGetFmcAliasCertResp, }; -use caliptra_hw_model::{BootParams, HwModel, InitParams, SecurityState}; +use caliptra_hw_model::{BootParams, HwModel, InitParams, ModelError, SecurityState}; use caliptra_hw_model_types::{DeviceLifecycle, Fuses}; use caliptra_test::{ derive::{DoeInput, DoeOutput, FmcAliasKey, IDevId, LDevId, Pcr0, Pcr0Input}, @@ -362,3 +364,196 @@ fn smoke_test() { // TODO: Validate the rest of the fmc_alias certificate fields } + +fn test_fips(hw: &mut T, fmc_version: u32, app_version: u32) { + // VERSION + let payload = MailboxReqHeader { + chksum: caliptra_common::checksum::calc_checksum(u32::from(CommandId::VERSION), &[]), + }; + + let fips_version_resp = hw + .mailbox_execute(u32::from(CommandId::VERSION), payload.as_bytes()) + .unwrap() + .unwrap(); + + // Check command size + let fips_version_bytes: &[u8] = fips_version_resp.as_bytes(); + + // Check values against expected. + let fips_version = FipsVersionResp::read_from(fips_version_bytes).unwrap(); + assert!(caliptra_common::checksum::verify_checksum( + fips_version.hdr.chksum, + 0x0, + &fips_version.as_bytes()[core::mem::size_of_val(&fips_version.hdr.chksum)..], + )); + assert_eq!( + fips_version.hdr.fips_status, + MailboxRespHeader::FIPS_STATUS_APPROVED + ); + assert_eq!(fips_version.mode, FipsVersionCmd::MODE); + assert_eq!(fips_version.fips_rev, [0x01, fmc_version, app_version]); + let name = &fips_version.name[..]; + assert_eq!(name, FipsVersionCmd::NAME.as_bytes()); + + // SELF_TEST_START + let payload = MailboxReqHeader { + chksum: caliptra_common::checksum::calc_checksum( + u32::from(CommandId::SELF_TEST_START), + &[], + ), + }; + + let resp = hw + .mailbox_execute(u32::from(CommandId::SELF_TEST_START), payload.as_bytes()) + .unwrap() + .unwrap(); + + let resp = MailboxRespHeader::read_from(resp.as_slice()).unwrap(); + // Verify checksum and FIPS status + assert!(caliptra_common::checksum::verify_checksum( + resp.chksum, + 0x0, + &resp.as_bytes()[core::mem::size_of_val(&resp.chksum)..], + )); + assert_eq!(resp.fips_status, MailboxRespHeader::FIPS_STATUS_APPROVED); + + // Confirm we can't re-start the FIPS self test while it is in progress. + let _resp = hw + .mailbox_execute(u32::from(CommandId::SELF_TEST_START), payload.as_bytes()) + .unwrap_err(); + + // SELF_TEST_GET_RESULTS + let payload = MailboxReqHeader { + chksum: caliptra_common::checksum::calc_checksum( + u32::from(CommandId::SELF_TEST_GET_RESULTS), + &[], + ), + }; + + loop { + // Get self test results + match hw.mailbox_execute( + u32::from(CommandId::SELF_TEST_GET_RESULTS), + payload.as_bytes(), + ) { + Ok(Some(resp)) => { + let resp = MailboxRespHeader::read_from(resp.as_slice()).unwrap(); + // Verify checksum and FIPS status + assert!(caliptra_common::checksum::verify_checksum( + resp.chksum, + 0x0, + &resp.as_bytes()[core::mem::size_of_val(&resp.chksum)..], + )); + if resp.fips_status == MailboxRespHeader::FIPS_STATUS_APPROVED { + break; + } + } + _ => { + // Give FW time to run + let mut cycle_count = 10000; + hw.step_until(|_| -> bool { + cycle_count -= 1; + cycle_count == 0 + }); + } + } + } + + // SHUTDOWN + let payload = MailboxReqHeader { + chksum: caliptra_common::checksum::calc_checksum(u32::from(CommandId::SHUTDOWN), &[]), + }; + + let resp = hw.mailbox_execute(u32::from(CommandId::SHUTDOWN), payload.as_bytes()); + assert!(resp.is_ok()); + + // Check we are rejecting additional commands with the shutdown error code. + let expected_err = Err(ModelError::MailboxCmdFailed(0x000E0008)); + let payload = MailboxReqHeader { + chksum: caliptra_common::checksum::calc_checksum(u32::from(CommandId::VERSION), &[]), + }; + let resp = hw.mailbox_execute(u32::from(CommandId::VERSION), payload.as_bytes()); + assert_eq!(resp, expected_err); +} + +#[test] +fn fips_cmd_test_rom() { + let security_state = *SecurityState::default() + .set_debug_locked(true) + .set_device_lifecycle(DeviceLifecycle::Production); + + let rom = caliptra_builder::build_firmware_rom(&ROM_WITH_UART).unwrap(); + + let mut hw = caliptra_hw_model::new(BootParams { + init_params: InitParams { + rom: &rom, + security_state, + ..Default::default() + }, + fuses: Fuses { + fmc_key_manifest_svn: 0b1111111, + ..Default::default() + }, + fw_image: None, + ..Default::default() + }) + .unwrap(); + + // Wait for rom to be ready for firmware + while !hw.ready_for_fw() { + hw.step(); + } + + test_fips(&mut hw, 0, 0); +} + +#[test] +fn fips_cmd_test_rt() { + const FMC_VERSION: u32 = 0xFEFEFEFE; + const APP_VERSION: u32 = 0xCECECECE; + + let security_state = *SecurityState::default() + .set_debug_locked(false) + .set_device_lifecycle(DeviceLifecycle::Unprovisioned); + + let rom = caliptra_builder::build_firmware_rom(&ROM_WITH_UART).unwrap(); + let image = caliptra_builder::build_and_sign_image( + &FMC_WITH_UART, + &APP_WITH_UART, + ImageOptions { + fmc_version: FMC_VERSION, + fmc_min_svn: 5, + fmc_svn: 9, + app_version: APP_VERSION, + ..Default::default() + }, + ) + .unwrap(); + let vendor_pk_hash = + bytes_to_be_words_48(&sha384(image.manifest.preamble.vendor_pub_keys.as_bytes())); + let owner_pk_hash = + bytes_to_be_words_48(&sha384(image.manifest.preamble.owner_pub_keys.as_bytes())); + + let mut hw = caliptra_hw_model::new(BootParams { + init_params: InitParams { + rom: &rom, + security_state, + ..Default::default() + }, + fuses: Fuses { + key_manifest_pk_hash: vendor_pk_hash, + owner_pk_hash, + fmc_key_manifest_svn: 0b1111111, + ..Default::default() + }, + fw_image: Some(&image.to_bytes().unwrap()), + ..Default::default() + }) + .unwrap(); + + while !hw.soc_ifc().cptra_flow_status().read().ready_for_runtime() { + hw.step(); + } + + test_fips(&mut hw, FMC_VERSION, APP_VERSION); +}