diff --git a/Cargo.lock b/Cargo.lock index 1ba667dce6..d25530e7e8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -708,6 +708,7 @@ version = "0.1.0" dependencies = [ "arrayvec", "caliptra-builder", + "caliptra-cfi-derive", "caliptra-cfi-lib", "caliptra-cpu", "caliptra-drivers", diff --git a/cfi/lib/src/cfi.rs b/cfi/lib/src/cfi.rs index 51b822eb5e..e45997992f 100644 --- a/cfi/lib/src/cfi.rs +++ b/cfi/lib/src/cfi.rs @@ -255,6 +255,58 @@ pub fn cfi_assert_eq_12_words(a: &[u32; 12], b: &[u32; 12]) { } } +#[cfg(not(any(target_arch = "riscv32", target_arch = "riscv64")))] +pub fn cfi_assert_eq_8_words(a: &[u32; 8], b: &[u32; 8]) { + if a != b { + cfi_panic(CfiPanicInfo::AssertEqFail) + } +} + +/// Unrolled comparison of 8 words +/// +/// Written in assembly so the trampoline is above the comparisons rather than +/// below +#[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))] +#[inline(always)] +pub fn cfi_assert_eq_8_words(a: &[u32; 8], b: &[u32; 8]) { + unsafe { + core::arch::asm!( + "j 3f", + "2:", + "li a0, 0x01040055", + "j cfi_panic_handler", + "3:", + "lw {tmp0}, 0(a4)", + "lw {tmp1}, 0(a5)", + "bne {tmp0}, {tmp1}, 2b", + "lw {tmp0}, 4(a4)", + "lw {tmp1}, 4(a5)", + "bne {tmp0}, {tmp1}, 2b", + "lw {tmp0}, 8(a4)", + "lw {tmp1}, 8(a5)", + "bne {tmp0}, {tmp1}, 2b", + "lw {tmp0}, 12(a4)", + "lw {tmp1}, 12(a5)", + "bne {tmp0}, {tmp1}, 2b", + "lw {tmp0}, 16(a4)", + "lw {tmp1}, 16(a5)", + "bne {tmp0}, {tmp1}, 2b", + "lw {tmp0}, 20(a4)", + "lw {tmp1}, 20(a5)", + "bne {tmp0}, {tmp1}, 2b", + "lw {tmp0}, 24(a4)", + "lw {tmp1}, 24(a5)", + "bne {tmp0}, {tmp1}, 2b", + "lw {tmp0}, 28(a4)", + "lw {tmp1}, 28(a5)", + "bne {tmp0}, {tmp1}, 2b", + in("a4") a.as_ptr(), + in("a5") b.as_ptr(), + tmp0 = out(reg) _, + tmp1 = out(reg) _); + } +} + #[cfg(not(any(target_arch = "riscv32", target_arch = "riscv64")))] pub fn cfi_assert_eq_6_words(a: &[u32; 6], b: &[u32; 6]) { if a != b { diff --git a/cfi/lib/tests/test_asm.rs b/cfi/lib/tests/test_asm.rs index 8a91f555a9..c4c04e0567 100644 --- a/cfi/lib/tests/test_asm.rs +++ b/cfi/lib/tests/test_asm.rs @@ -68,6 +68,41 @@ pub fn test_assert_eq_12words_failure() { // Leak thread in infinite loop... } +#[test] +pub fn test_assert_eq_8words_success() { + CFI_PANIC_CALLED.with(|c| c.borrow_mut().store(0, Relaxed)); + use caliptra_cfi_lib::cfi_assert_eq_8_words; + let a = [0, 1, 2, 3, 4, 5, 6, 7]; + let b = [0, 1, 2, 3, 4, 5, 6, 7]; + // Make sure these are separate memory addresses + assert_ne!(a.as_ptr(), b.as_ptr()); + cfi_assert_eq_8_words(&a, &b); + assert_eq!(CFI_PANIC_CALLED.with(|c| c.borrow_mut().load(Relaxed)), 0); +} + +#[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))] +#[test] +pub fn test_assert_eq_8words_failure() { + use caliptra_cfi_lib::cfi_assert_eq_8_words; + + let cfi_panic_called = Arc::new(AtomicU32::new(0)); + let cfi_panic_called2 = cfi_panic_called.clone(); + + std::thread::spawn(|| { + CFI_PANIC_CALLED.with(|c| c.replace(cfi_panic_called2)); + cfi_assert_eq_8_words(&[0, 1, 2, 3, 4, 5, 6, 7], &[0, 1, 2, 3, 4, 5, 6, 8]); + }); + let val = loop { + let val = cfi_panic_called.load(Relaxed); + if val != 0 { + break val; + } + }; + assert_eq!(val, CaliptraError::ROM_CFI_PANIC_ASSERT_EQ_FAILURE.into()); + + // Leak thread in infinite loop... +} + #[test] pub fn test_assert_eq_6words_success() { CFI_PANIC_CALLED.with(|c| c.borrow_mut().store(0, Relaxed)); diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index 378aa5aa51..29d6210256 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -6,9 +6,11 @@ version = "0.1.0" edition = "2021" [dependencies] +caliptra-cfi-lib = { workspace = true, default-features = false, features = ["cfi", "cfi-counter" ] } +caliptra-cfi-derive.workspace = true caliptra_common = { workspace = true, default-features = false, features = ["runtime"] } caliptra-cpu.workspace = true -caliptra-drivers = { workspace = true, features = ["runtime", "no-cfi"] } +caliptra-drivers = { workspace = true, features = ["runtime"] } caliptra-error = { workspace = true, default-features = false } caliptra-image-types = { workspace = true, default-features = false } caliptra-kat.workspace = true @@ -20,7 +22,7 @@ platform.workspace = true ufmt.workspace = true zerocopy.workspace = true arrayvec.workspace = true -caliptra-image-verify = { workspace = true, default-features = false, features = ["no-cfi"] } +caliptra-image-verify = { workspace = true, default-features = false } zeroize.workspace = true [build-dependencies] @@ -52,4 +54,5 @@ test_only_commands = ["caliptra_common/test_only_commands"] slow_tests = [] verilator = ["caliptra-hw-model/verilator"] fips_self_test=[] +no-cfi = ["caliptra-image-verify/no-cfi", "caliptra-drivers/no-cfi"] fpga_realtime = ["caliptra-drivers/fpga_realtime"] diff --git a/runtime/src/disable.rs b/runtime/src/disable.rs index 905f56bd40..bddc7c55c8 100644 --- a/runtime/src/disable.rs +++ b/runtime/src/disable.rs @@ -13,6 +13,7 @@ Abstract: --*/ use crate::Drivers; +use caliptra_cfi_derive::cfi_impl_fn; use caliptra_common::mailbox_api::MailboxResp; use caliptra_drivers::{ hmac384_kdf, Array4x12, CaliptraError, CaliptraResult, Ecc384Seed, Hmac384Key, KeyReadArgs, @@ -22,23 +23,35 @@ use dpe::U8Bool; pub struct DisableAttestationCmd; impl DisableAttestationCmd { + #[cfg_attr(not(feature = "no-cfi"), cfi_impl_fn)] + #[inline(never)] pub(crate) fn execute(drivers: &mut Drivers) -> CaliptraResult { - let key_id_rt_cdi = Drivers::get_key_id_rt_cdi(drivers)?; - let key_id_rt_priv_key = Drivers::get_key_id_rt_priv_key(drivers)?; - drivers.key_vault.erase_key(key_id_rt_cdi)?; - drivers.key_vault.erase_key(key_id_rt_priv_key)?; - + Self::erase_keys(drivers)?; Self::zero_rt_cdi(drivers)?; Self::generate_dice_key(drivers)?; drivers.persistent_data.get_mut().attestation_disabled = U8Bool::new(true); Ok(MailboxResp::default()) } + /// Erase the RT CDI and RT Private Key from the key vault + /// + /// # Arguments + /// + /// * `drivers` - Drivers + #[cfg_attr(not(feature = "no-cfi"), cfi_impl_fn)] + fn erase_keys(drivers: &mut Drivers) -> CaliptraResult<()> { + let key_id_rt_cdi = Drivers::get_key_id_rt_cdi(drivers)?; + let key_id_rt_priv_key = Drivers::get_key_id_rt_priv_key(drivers)?; + drivers.key_vault.erase_key(key_id_rt_cdi)?; + drivers.key_vault.erase_key(key_id_rt_priv_key) + } + /// Set CDI key vault slot to a KDF of a buffer of 0s. /// /// # Arguments /// /// * `drivers` - Drivers + #[cfg_attr(not(feature = "no-cfi"), cfi_impl_fn)] fn zero_rt_cdi(drivers: &mut Drivers) -> CaliptraResult<()> { let key_id_rt_cdi = Drivers::get_key_id_rt_cdi(drivers)?; hmac384_kdf( @@ -66,6 +79,7 @@ impl DisableAttestationCmd { /// # Arguments /// /// * `drivers` - Drivers + #[cfg_attr(not(feature = "no-cfi"), cfi_impl_fn)] fn generate_dice_key(drivers: &mut Drivers) -> CaliptraResult<()> { let key_id_rt_cdi = Drivers::get_key_id_rt_cdi(drivers)?; let key_id_rt_priv_key = Drivers::get_key_id_rt_priv_key(drivers)?; diff --git a/runtime/src/dpe_crypto.rs b/runtime/src/dpe_crypto.rs index 976445eb29..d8625c7633 100644 --- a/runtime/src/dpe_crypto.rs +++ b/runtime/src/dpe_crypto.rs @@ -14,6 +14,7 @@ Abstract: use core::cmp::min; +use caliptra_cfi_lib::{cfi_assert, cfi_assert_eq, cfi_launder}; use caliptra_common::keyids::{KEY_ID_DPE_CDI, KEY_ID_DPE_PRIV_KEY, KEY_ID_TMP}; use caliptra_drivers::{ cprintln, hmac384_kdf, Array4x12, Ecc384, Ecc384PrivKeyIn, Ecc384PubKey, Ecc384Scalar, @@ -303,7 +304,13 @@ impl<'a> Crypto for DpeCrypto<'a> { // note: the output point must be kept secret since it is derived from the private key, // so as long as that output is kept secret and not released outside of Caliptra, // it is safe to use it as key material. - let (_, hmac_seed) = Self::derive_key_pair(self, algs, cdi, label, info)?; + let key_pair = Self::derive_key_pair(self, algs, cdi, label, info); + if cfi_launder(key_pair.is_ok()) { + cfi_assert!(key_pair.is_ok()); + } else { + cfi_assert!(key_pair.is_err()); + } + let (_, hmac_seed) = key_pair?; // create ikm to the hmac kdf by hashing the seed entropy from the pub key // this is more secure than directly using the pub key components in the hmac diff --git a/runtime/src/drivers.rs b/runtime/src/drivers.rs index d2030982c3..63831b7fd1 100644 --- a/runtime/src/drivers.rs +++ b/runtime/src/drivers.rs @@ -24,6 +24,8 @@ use crate::{ }; use arrayvec::ArrayVec; +use caliptra_cfi_derive::{cfi_impl_fn, cfi_mod_fn}; +use caliptra_cfi_lib::{cfi_assert, cfi_assert_eq, cfi_assert_eq_12_words, cfi_launder}; use caliptra_drivers::KeyId; use caliptra_drivers::{ cprint, cprintln, pcr_log::RT_FW_JOURNEY_PCR, Array4x12, CaliptraError, CaliptraResult, @@ -115,19 +117,23 @@ impl Drivers { let reset_reason = drivers.soc_ifc.reset_reason(); match reset_reason { ResetReason::ColdReset => { + cfi_assert_eq(drivers.soc_ifc.reset_reason(), ResetReason::ColdReset); Self::initialize_dpe(&mut drivers)?; } ResetReason::UpdateReset => { + cfi_assert_eq(drivers.soc_ifc.reset_reason(), ResetReason::UpdateReset); Self::validate_dpe_structure(&mut drivers)?; Self::validate_context_tags(&mut drivers)?; Self::update_dpe_rt_journey(&mut drivers)?; } ResetReason::WarmReset => { + cfi_assert_eq(drivers.soc_ifc.reset_reason(), ResetReason::WarmReset); Self::validate_dpe_structure(&mut drivers)?; Self::validate_context_tags(&mut drivers)?; Self::check_dpe_rt_journey_unchanged(&mut drivers)?; } ResetReason::Unknown => { + cfi_assert_eq(drivers.soc_ifc.reset_reason(), ResetReason::Unknown); return Err(CaliptraError::RUNTIME_UNKNOWN_RESET_FLOW); } } @@ -206,6 +212,11 @@ impl Drivers { if let Err(e) = validation_result { // If SRAM Dpe Instance validation fails, disable attestation let mut result = DisableAttestationCmd::execute(drivers); + if cfi_launder(result.is_ok()) { + cfi_assert!(result.is_ok()); + } else { + cfi_assert!(result.is_err()); + } match result { Ok(_) => { cprintln!("Disabled attestation due to DPE validation failure"); @@ -225,14 +236,25 @@ impl Drivers { let flags = drivers.persistent_data.get().manifest1.header.flags; let locality = drivers.mbox.user(); // check that DPE used context limits are not exceeded - if let Err(e) = Self::is_dpe_context_threshold_exceeded( + let dpe_context_threshold_exceeded = Self::is_dpe_context_threshold_exceeded( pl0_pauser, flags, locality, &drivers.persistent_data.get().dpe, true, - ) { + ); + if cfi_launder(dpe_context_threshold_exceeded.is_ok()) { + cfi_assert!(dpe_context_threshold_exceeded.is_ok()); + } else { + cfi_assert!(dpe_context_threshold_exceeded.is_err()); + } + if let Err(e) = dpe_context_threshold_exceeded { let mut result = DisableAttestationCmd::execute(drivers); + if cfi_launder(result.is_ok()) { + cfi_assert!(result.is_ok()); + } else { + cfi_assert!(result.is_err()); + } match result { Ok(_) => { cprintln!( @@ -252,6 +274,7 @@ impl Drivers { } /// Update DPE root context's TCI measurement with RT_FW_JOURNEY_PCR + #[cfg_attr(not(feature = "no-cfi"), cfi_impl_fn)] fn update_dpe_rt_journey(drivers: &mut Drivers) -> CaliptraResult<()> { let dpe = &mut drivers.persistent_data.get_mut().dpe; let root_idx = Self::get_dpe_root_context_idx(dpe)?; @@ -266,13 +289,18 @@ impl Drivers { fn check_dpe_rt_journey_unchanged(mut drivers: &mut Drivers) -> CaliptraResult<()> { let dpe = &drivers.persistent_data.get().dpe; let root_idx = Self::get_dpe_root_context_idx(dpe)?; - let latest_tci = dpe.contexts[root_idx].tci.tci_current; + let latest_tci = Array4x12::from(&dpe.contexts[root_idx].tci.tci_current.0); let latest_pcr = drivers.pcr_bank.read_pcr(RT_FW_JOURNEY_PCR); // Ensure TCI from SRAM == RT_FW_JOURNEY_PCR - if latest_pcr != Array4x12::from(&latest_tci.0) { + if latest_pcr != latest_tci { // If latest pcr validation fails, disable attestation let mut result = DisableAttestationCmd::execute(drivers); + if cfi_launder(result.is_ok()) { + cfi_assert!(result.is_ok()); + } else { + cfi_assert!(result.is_err()); + } match result { Ok(_) => { cprintln!("Disabled attestation due to latest TCI of the node containing the runtime journey PCR not matching the runtime PCR"); @@ -285,6 +313,11 @@ impl Drivers { return Err(CaliptraError::RUNTIME_GLOBAL_EXCEPTION); } } + } else { + cfi_assert_eq_12_words( + &<[u32; 12]>::from(latest_tci), + &<[u32; 12]>::from(latest_pcr), + ) } Ok(()) @@ -310,6 +343,7 @@ impl Drivers { } /// Compute the Caliptra Name SerialNumber by Sha256 hashing the RT Alias public key + #[cfg_attr(not(feature = "no-cfi"), cfi_impl_fn)] pub fn compute_rt_alias_sn(&mut self) -> CaliptraResult { let key = self.persistent_data.get().fht.rt_dice_pub_key.to_der(); @@ -321,6 +355,7 @@ impl Drivers { } /// Initialize DPE with measurements and store in Drivers + #[cfg_attr(not(feature = "no-cfi"), cfi_impl_fn)] fn initialize_dpe(drivers: &mut Drivers) -> CaliptraResult<()> { let caliptra_locality = 0xFFFFFFFF; let pl0_pauser_locality = drivers.persistent_data.get().manifest1.header.pl0_pauser; @@ -442,6 +477,7 @@ impl Drivers { } /// Create certificate chain and store in Drivers + #[cfg_attr(not(feature = "no-cfi"), cfi_impl_fn)] fn create_cert_chain(drivers: &mut Drivers) -> CaliptraResult<()> { let data_vault = &drivers.data_vault; let persistent_data = &drivers.persistent_data; diff --git a/runtime/src/fips.rs b/runtime/src/fips.rs index effe761140..0b35897d14 100644 --- a/runtime/src/fips.rs +++ b/runtime/src/fips.rs @@ -11,6 +11,7 @@ Abstract: File contains FIPS module and FIPS self test. --*/ +use caliptra_cfi_derive::{cfi_impl_fn, cfi_mod_fn}; use caliptra_common::cprintln; use caliptra_common::mailbox_api::{MailboxResp, MailboxRespHeader}; use caliptra_drivers::CaliptraError; @@ -31,6 +32,8 @@ pub struct FipsModule; /// Fips command handler. impl FipsModule { /// Clear data structures in DCCM. + #[cfg_attr(not(feature = "no-cfi"), cfi_impl_fn)] + #[inline(never)] fn zeroize(env: &mut Drivers) { unsafe { // Zeroize the crypto blocks. @@ -54,6 +57,7 @@ impl FipsModule { pub mod fips_self_test_cmd { use super::*; use crate::RtBootStatus::{RtFipSelfTestComplete, RtFipSelfTestStarted}; + use caliptra_cfi_lib::cfi_assert_eq_8_words; use caliptra_common::HexBytes; use caliptra_common::{ verifier::FirmwareImageVerificationEnv, FMC_ORG, FMC_SIZE, RUNTIME_ORG, RUNTIME_SIZE, @@ -74,6 +78,7 @@ pub mod fips_self_test_cmd { Done, } + #[cfg_attr(not(feature = "no-cfi"), cfi_mod_fn)] fn copy_and_verify_image(env: &mut Drivers) -> CaliptraResult<()> { env.mbox.write_cmd(0)?; env.mbox.set_dlen( @@ -121,6 +126,7 @@ pub mod fips_self_test_cmd { Ok(()) } + #[cfg_attr(not(feature = "no-cfi"), cfi_mod_fn)] pub(crate) fn execute(env: &mut Drivers) -> CaliptraResult<()> { caliptra_drivers::report_boot_status(RtFipSelfTestStarted.into()); cprintln!("[rt] FIPS self test"); @@ -166,6 +172,7 @@ pub mod fips_self_test_cmd { Ok(()) } + #[cfg_attr(not(feature = "no-cfi"), cfi_mod_fn)] 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()?; @@ -185,6 +192,8 @@ pub mod fips_self_test_cmd { if digest.0 != rom_info.sha256_digest { cprintln!("ROM integrity test failed"); return Err(CaliptraError::ROM_INTEGRITY_FAILURE); + } else { + cfi_assert_eq_8_words(&digest.0, &rom_info.sha256_digest); } // Run digest function and compare with expected hash. @@ -193,6 +202,7 @@ pub mod fips_self_test_cmd { } pub struct FipsShutdownCmd; impl FipsShutdownCmd { + #[cfg_attr(not(feature = "no-cfi"), cfi_impl_fn)] pub(crate) fn execute(env: &mut Drivers) -> CaliptraResult { FipsModule::zeroize(env); env.mbox.set_status(MboxStatusE::CmdComplete); diff --git a/runtime/src/hmac.rs b/runtime/src/hmac.rs index 4a11028dde..8f8f74fb7f 100644 --- a/runtime/src/hmac.rs +++ b/runtime/src/hmac.rs @@ -12,6 +12,8 @@ Abstract: --*/ +use caliptra_cfi_derive::{cfi_impl_fn, cfi_mod_fn}; +use caliptra_cfi_lib::{cfi_assert, cfi_assert_eq, cfi_launder}; use caliptra_common::{crypto::Ecc384KeyPair, keyids::KEY_ID_TMP}; use caliptra_drivers::{ hmac384_kdf, Array4x12, Ecc384PrivKeyOut, Ecc384PubKey, Hmac384Data, Hmac384Key, Hmac384Tag, @@ -35,6 +37,7 @@ use crate::Drivers; /// # Returns /// /// * `Ecc384KeyPair` - Generated key pair +#[cfg_attr(not(feature = "no-cfi"), cfi_mod_fn)] fn ecc384_key_gen( drivers: &mut Drivers, input: KeyId, @@ -65,6 +68,8 @@ fn ecc384_key_gen( if KEY_ID_TMP != priv_key { drivers.key_vault.erase_key(KEY_ID_TMP)?; + } else { + cfi_assert_eq(KEY_ID_TMP, priv_key); } Ok(Ecc384KeyPair { @@ -85,6 +90,7 @@ impl Hmac { /// * `drivers` - Drivers /// * `input` - KeyId containing the input data /// * `output` - KeyId which the output hash should be written to + #[cfg_attr(not(feature = "no-cfi"), cfi_impl_fn)] pub fn hmac384_hash(drivers: &mut Drivers, input: KeyId, output: KeyId) -> CaliptraResult<()> { drivers.hmac384.hmac( &KeyReadArgs::new(input).into(), @@ -116,13 +122,20 @@ impl Hmac { /// # Returns /// /// * `Array4x12` - Computed HMAC result + #[cfg_attr(not(feature = "no-cfi"), cfi_impl_fn)] pub fn ecc384_hmac( drivers: &mut Drivers, input: KeyId, label: &[u8], data: &[u8], ) -> CaliptraResult { - let mut keypair = ecc384_key_gen(drivers, input, label, KEY_ID_TMP)?; + let keypair_result = ecc384_key_gen(drivers, input, label, KEY_ID_TMP); + if cfi_launder(keypair_result.is_ok()) { + cfi_assert!(keypair_result.is_ok()); + } else { + cfi_assert!(keypair_result.is_err()); + } + let mut keypair = keypair_result?; let mut pubkey_digest = Array4x12::default(); diff --git a/runtime/src/invoke_dpe.rs b/runtime/src/invoke_dpe.rs index 520003783c..dbc2e099fc 100644 --- a/runtime/src/invoke_dpe.rs +++ b/runtime/src/invoke_dpe.rs @@ -13,6 +13,7 @@ Abstract: --*/ use crate::{CptraDpeTypes, DpeCrypto, DpeEnv, DpePlatform, Drivers, PL0_PAUSER_FLAG}; +use caliptra_cfi_derive::cfi_impl_fn; use caliptra_common::mailbox_api::{InvokeDpeReq, InvokeDpeResp, MailboxResp, MailboxRespHeader}; use caliptra_drivers::{CaliptraError, CaliptraResult}; use crypto::{AlgLen, Crypto}; @@ -28,6 +29,7 @@ use zerocopy::{AsBytes, FromBytes}; pub struct InvokeDpeCmd; impl InvokeDpeCmd { + #[cfg_attr(not(feature = "no-cfi"), cfi_impl_fn)] pub(crate) fn execute(drivers: &mut Drivers, cmd_args: &[u8]) -> CaliptraResult { if cmd_args.len() <= core::mem::size_of::() { let mut cmd = InvokeDpeReq::default(); @@ -155,6 +157,7 @@ impl InvokeDpeCmd { /// * `dpe` - DpeInstance /// * `context_has_tag` - Bool slice indicating if a DPE context has a tag /// * `context_tags` - Tags for each DPE context + #[cfg_attr(not(feature = "no-cfi"), cfi_impl_fn)] pub fn clear_tags_for_inactive_contexts( dpe: &mut DpeInstance, context_has_tag: &mut [U8Bool; MAX_HANDLES], diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index a95f15c4fb..2e81e4cac2 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -31,6 +31,7 @@ mod verify; // Used by runtime tests pub mod mailbox; +use caliptra_cfi_lib::{cfi_assert, cfi_assert_eq, cfi_assert_ne, cfi_launder, CfiCounter}; pub use drivers::Drivers; use mailbox::Mailbox; @@ -120,6 +121,7 @@ fn enter_idle(drivers: &mut Drivers) { Err(e) => caliptra_drivers::report_fw_error_non_fatal(e.into()), } } else { + cfi_assert!(drivers.mbox.lock()); // Don't enter low power mode when in progress return; } @@ -145,6 +147,8 @@ fn handle_command(drivers: &mut Drivers) -> CaliptraResult { // If the handler succeeds but does not invoke reset that is // unexpected. Denote that the update failed. return Err(CaliptraError::RUNTIME_UNEXPECTED_UPDATE_RETURN); + } else { + cfi_assert_ne(drivers.mbox.cmd(), CommandId::FIRMWARE_LOAD); } // Get the command bytes @@ -229,10 +233,17 @@ pub fn handle_mailbox_commands(drivers: &mut Drivers) -> CaliptraResult<()> { drivers.soc_ifc.assert_ready_for_runtime(); caliptra_drivers::report_boot_status(RtBootStatus::RtReadyForCommands.into()); // Disable attestation if in the middle of executing an mbox cmd during warm reset - if drivers.mbox.cmd_busy() { + let cmd_busy = drivers.mbox.cmd_busy(); + if cmd_busy { let reset_reason = drivers.soc_ifc.reset_reason(); if reset_reason == ResetReason::WarmReset { + cfi_assert_eq(drivers.soc_ifc.reset_reason(), ResetReason::WarmReset); let mut result = DisableAttestationCmd::execute(drivers); + if cfi_launder(result.is_ok()) { + cfi_assert!(result.is_ok()); + } else { + cfi_assert!(result.is_err()); + } match result { Ok(_) => { cprintln!("Disabled attestation due to cmd busy during warm reset"); @@ -246,6 +257,8 @@ pub fn handle_mailbox_commands(drivers: &mut Drivers) -> CaliptraResult<()> { } } } + } else { + cfi_assert!(!cmd_busy); } #[cfg(feature = "riscv")] if cfg!(feature = "fpga_realtime") { @@ -253,19 +266,29 @@ pub fn handle_mailbox_commands(drivers: &mut Drivers) -> CaliptraResult<()> { } loop { + // Random delay for CFI glitch protection. + CfiCounter::delay(); + enter_idle(drivers); if drivers.is_shutdown { return Err(CaliptraError::RUNTIME_SHUTDOWN); } - if drivers.mbox.is_cmd_ready() { + let cmd_ready = drivers.mbox.is_cmd_ready(); + if 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(), ); caliptra_drivers::report_fw_error_non_fatal(0); - match handle_command(drivers) { + let commmand_result = handle_command(drivers); + if cfi_launder(commmand_result.is_ok()) { + cfi_assert!(commmand_result.is_ok()); + } else { + cfi_assert!(commmand_result.is_err()); + } + match commmand_result { Ok(status) => { drivers.mbox.set_status(status); } @@ -275,6 +298,8 @@ pub fn handle_mailbox_commands(drivers: &mut Drivers) -> CaliptraResult<()> { } } caliptra_common::wdt::stop_wdt(&mut drivers.soc_ifc); + } else { + cfi_assert!(!cmd_ready); } } Ok(()) diff --git a/runtime/src/main.rs b/runtime/src/main.rs index 8c79ae0b89..a55ecca4f0 100644 --- a/runtime/src/main.rs +++ b/runtime/src/main.rs @@ -14,6 +14,7 @@ Abstract: #![cfg_attr(not(feature = "std"), no_std)] #![cfg_attr(not(feature = "std"), no_main)] +use caliptra_cfi_lib::CfiCounter; use caliptra_common::{cprintln, handle_fatal_error}; use caliptra_cpu::{log_trap_record, TrapRecord}; use caliptra_error::CaliptraError; @@ -51,6 +52,16 @@ pub extern "C" fn entry_point() -> ! { }; caliptra_common::stop_wdt(&mut drivers.soc_ifc); + if !cfg!(feature = "no-cfi") { + cprintln!("[state] CFI Enabled"); + let mut entropy_gen = || drivers.trng.generate().map(|a| a.0); + CfiCounter::reset(&mut entropy_gen); + CfiCounter::reset(&mut entropy_gen); + CfiCounter::reset(&mut entropy_gen); + } else { + cprintln!("[state] CFI Disabled"); + } + if !drivers.persistent_data.get().fht.is_valid() { cprintln!("[rt] Runtime can't load FHT"); handle_fatal_error(caliptra_drivers::CaliptraError::RUNTIME_HANDOFF_FHT_NOT_LOADED.into()); diff --git a/runtime/src/pcr.rs b/runtime/src/pcr.rs index d7b5a2742e..2c44477e91 100644 --- a/runtime/src/pcr.rs +++ b/runtime/src/pcr.rs @@ -13,6 +13,7 @@ Abstract: --*/ use crate::Drivers; +use caliptra_cfi_derive::cfi_impl_fn; use caliptra_common::mailbox_api::{ ExtendPcrReq, IncrementPcrResetCounterReq, MailboxResp, MailboxRespHeader, QuotePcrsReq, QuotePcrsResp, @@ -22,6 +23,7 @@ use zerocopy::FromBytes; pub struct IncrementPcrResetCounterCmd; impl IncrementPcrResetCounterCmd { + #[cfg_attr(not(feature = "no-cfi"), cfi_impl_fn)] pub(crate) fn execute(drivers: &mut Drivers, cmd_args: &[u8]) -> CaliptraResult { let cmd = IncrementPcrResetCounterReq::read_from(cmd_args) .ok_or(CaliptraError::RUNTIME_INSUFFICIENT_MEMORY)?; @@ -42,6 +44,7 @@ impl IncrementPcrResetCounterCmd { pub struct GetPcrQuoteCmd; impl GetPcrQuoteCmd { + #[cfg_attr(not(feature = "no-cfi"), cfi_impl_fn)] pub(crate) fn execute(drivers: &mut Drivers, cmd_bytes: &[u8]) -> CaliptraResult { let args: QuotePcrsReq = QuotePcrsReq::read_from(cmd_bytes) .ok_or(CaliptraError::RUNTIME_MAILBOX_INVALID_PARAMS)?; @@ -75,6 +78,7 @@ impl GetPcrQuoteCmd { pub struct ExtendPcrCmd; impl ExtendPcrCmd { + #[cfg_attr(not(feature = "no-cfi"), cfi_impl_fn)] pub(crate) fn execute(drivers: &mut Drivers, cmd_args: &[u8]) -> CaliptraResult { let cmd = ExtendPcrReq::read_from(cmd_args).ok_or(CaliptraError::RUNTIME_INSUFFICIENT_MEMORY)?; diff --git a/runtime/src/stash_measurement.rs b/runtime/src/stash_measurement.rs index 7441f4a21a..cfafefb611 100644 --- a/runtime/src/stash_measurement.rs +++ b/runtime/src/stash_measurement.rs @@ -13,6 +13,7 @@ Abstract: --*/ use crate::{dpe_crypto::DpeCrypto, CptraDpeTypes, DpePlatform, Drivers}; +use caliptra_cfi_derive::cfi_impl_fn; use caliptra_common::mailbox_api::{ MailboxResp, MailboxRespHeader, StashMeasurementReq, StashMeasurementResp, }; @@ -28,6 +29,8 @@ use zerocopy::{AsBytes, FromBytes}; pub struct StashMeasurementCmd; impl StashMeasurementCmd { + #[cfg_attr(not(feature = "no-cfi"), cfi_impl_fn)] + #[inline(never)] pub(crate) fn execute(drivers: &mut Drivers, cmd_args: &[u8]) -> CaliptraResult { if let Some(cmd) = StashMeasurementReq::read_from(cmd_args) { let dpe_result = { diff --git a/runtime/src/tagging.rs b/runtime/src/tagging.rs index da2fed89e5..19f7bbd154 100644 --- a/runtime/src/tagging.rs +++ b/runtime/src/tagging.rs @@ -12,6 +12,7 @@ Abstract: --*/ +use caliptra_cfi_derive::cfi_impl_fn; use caliptra_common::mailbox_api::{ GetTaggedTciReq, GetTaggedTciResp, MailboxResp, MailboxRespHeader, TagTciReq, }; @@ -28,6 +29,7 @@ use crate::{dpe_crypto::DpeCrypto, CptraDpeTypes, DpePlatform, Drivers}; pub struct TagTciCmd; impl TagTciCmd { + #[cfg_attr(not(feature = "no-cfi"), cfi_impl_fn)] pub(crate) fn execute(drivers: &mut Drivers, cmd_args: &[u8]) -> CaliptraResult { if let Some(cmd) = TagTciReq::read_from(cmd_args) { let pdata_mut = drivers.persistent_data.get_mut(); @@ -67,6 +69,8 @@ impl TagTciCmd { pub struct GetTaggedTciCmd; impl GetTaggedTciCmd { + #[cfg_attr(not(feature = "no-cfi"), cfi_impl_fn)] + #[inline(never)] pub(crate) fn execute(drivers: &Drivers, cmd_args: &[u8]) -> CaliptraResult { if let Some(cmd) = GetTaggedTciReq::read_from(cmd_args) { let persistent_data = drivers.persistent_data.get(); diff --git a/runtime/src/update.rs b/runtime/src/update.rs index f2e899133f..97215749b2 100644 --- a/runtime/src/update.rs +++ b/runtime/src/update.rs @@ -13,8 +13,10 @@ Abstract: --*/ use crate::Drivers; +use caliptra_cfi_derive::cfi_mod_fn; use caliptra_drivers::{CaliptraError, CaliptraResult}; +#[cfg_attr(not(feature = "no-cfi"), cfi_mod_fn)] pub(crate) fn handle_impactless_update(drivers: &mut Drivers) -> CaliptraResult<()> { let cycles = drivers.soc_ifc.internal_fw_update_reset_wait_cycles(); for _ in 0..cycles { diff --git a/runtime/src/verify.rs b/runtime/src/verify.rs index 56b4c09f54..6709e4dd71 100644 --- a/runtime/src/verify.rs +++ b/runtime/src/verify.rs @@ -13,6 +13,7 @@ Abstract: --*/ use crate::Drivers; +use caliptra_cfi_derive::cfi_impl_fn; #[cfg(feature = "test_only_commands")] use caliptra_common::mailbox_api::HmacVerifyReq; use caliptra_common::mailbox_api::{EcdsaVerifyReq, MailboxResp}; @@ -31,6 +32,7 @@ use zerocopy::FromBytes; pub struct EcdsaVerifyCmd; impl EcdsaVerifyCmd { + #[cfg_attr(not(feature = "no-cfi"), cfi_impl_fn)] pub(crate) fn execute(drivers: &mut Drivers, cmd_args: &[u8]) -> CaliptraResult { if let Some(cmd) = EcdsaVerifyReq::read_from(cmd_args) { // Won't panic, full_digest is always larger than digest diff --git a/test/tests/caliptra_integration_tests/smoke_test.rs b/test/tests/caliptra_integration_tests/smoke_test.rs index 2904cd4b34..0e7af02aec 100644 --- a/test/tests/caliptra_integration_tests/smoke_test.rs +++ b/test/tests/caliptra_integration_tests/smoke_test.rs @@ -667,11 +667,11 @@ fn test_rt_wdt_timeout() { // TODO: Don't hard-code these; maybe measure from a previous boot? let rt_wdt_timeout_cycles = if cfg!(any(feature = "verilator", feature = "fpga_realtime")) { - 27_100_000 + 27_200_000 } else if firmware::rom_from_env() == &firmware::ROM_WITH_UART { - 3_100_000 + 3_200_000 } else { - 2_900_000 + 3_000_000 }; let security_state = *caliptra_hw_model::SecurityState::default().set_debug_locked(true); @@ -716,9 +716,9 @@ fn test_fmc_wdt_timeout() { // TODO: Don't hard-code these; maybe measure from a previous boot? let fmc_wdt_timeout_cycles = if cfg!(any(feature = "verilator", feature = "fpga_realtime")) { - 25_100_000 + 25_200_000 } else { - 2_820_000 + 2_920_000 }; let rom = caliptra_builder::build_firmware_rom(firmware::rom_from_env()).unwrap();