From a9020ee9703d11b9b03826c9c77251000151c619 Mon Sep 17 00:00:00 2001 From: Sree Revoori Date: Mon, 12 Feb 2024 02:57:06 +0000 Subject: [PATCH] Add CFI to DPE --- Cargo.lock | 27 +++ Cargo.toml | 4 + ci.sh | 10 +- crypto/Cargo.toml | 3 + crypto/src/lib.rs | 25 +++ crypto/src/openssl.rs | 4 + dpe/Cargo.toml | 7 +- dpe/fuzz/Cargo.lock | 27 +++ dpe/src/commands/certify_key.rs | 48 ++++-- dpe/src/commands/derive_context.rs | 67 +++++++- dpe/src/commands/destroy_context.rs | 26 ++- dpe/src/commands/get_certificate_chain.rs | 6 + dpe/src/commands/initialize_context.rs | 17 ++ dpe/src/commands/mod.rs | 11 ++ dpe/src/commands/rotate_context.rs | 24 ++- dpe/src/commands/sign.rs | 45 ++++- dpe/src/dpe_instance.rs | 49 +++++- dpe/src/validation.rs | 193 ++++++++++++++++++++-- simulator/Cargo.toml | 2 +- tools/Cargo.toml | 2 +- 20 files changed, 545 insertions(+), 52 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a277adc4..16f04aeb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -173,6 +173,22 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" +[[package]] +name = "caliptra-cfi-derive-git" +version = "0.1.0" +source = "git+https://github.com/chipsalliance/caliptra-cfi.git#9f315fcf11fe006e95e62149f54f98620e5244e7" +dependencies = [ + "paste", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "caliptra-cfi-lib-git" +version = "0.1.0" +source = "git+https://github.com/chipsalliance/caliptra-cfi.git#9f315fcf11fe006e95e62149f54f98620e5244e7" + [[package]] name = "cc" version = "1.0.83" @@ -282,6 +298,8 @@ version = "0.1.0" dependencies = [ "arrayvec", "base64ct", + "caliptra-cfi-derive-git", + "caliptra-cfi-lib-git", "ecdsa", "hkdf", "hmac", @@ -410,6 +428,9 @@ version = "0.1.0" dependencies = [ "asn1", "bitflags", + "caliptra-cfi-derive-git", + "caliptra-cfi-lib-git", + "cfg-if", "cms", "constant_time_eq", "crypto", @@ -763,6 +784,12 @@ dependencies = [ "sha2", ] +[[package]] +name = "paste" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" + [[package]] name = "pem" version = "2.0.1" diff --git a/Cargo.toml b/Cargo.toml index 4b2428fe..74d94f76 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,3 +9,7 @@ members = [ "simulator", "tools", ] + +[workspace.dependencies] +caliptra-cfi-lib-git = { git = "https://github.com/chipsalliance/caliptra-cfi.git", package = "caliptra-cfi-lib-git", default-features = false, features = ["cfi", "cfi-counter" ] } +caliptra-cfi-derive-git = { git = "https://github.com/chipsalliance/caliptra-cfi.git", package = "caliptra-cfi-derive-git" } \ No newline at end of file diff --git a/ci.sh b/ci.sh index 70c34be9..e90cb5be 100755 --- a/ci.sh +++ b/ci.sh @@ -47,7 +47,7 @@ function test_rust_targets() { cargo test --manifest-path platform/Cargo.toml --features=$profile --no-default-features cargo test --manifest-path crypto/Cargo.toml --no-default-features - cargo test --manifest-path dpe/Cargo.toml --features=$profile --no-default-features + cargo test --manifest-path dpe/Cargo.toml --features=$profile --no-default-features -- --test-threads=1 cargo test --manifest-path simulator/Cargo.toml --features=$profile,openssl --no-default-features } @@ -80,13 +80,13 @@ run_verification_tests dpe_profile_p384_sha384 rustcrypto # Build fuzz target ( cd dpe/fuzz - rustup toolchain install nightly-2023-04-15 - cargo +nightly-2023-04-15 install cargo-fuzz cargo-afl + rustup toolchain install nightly-2023-11-16 + cargo +nightly-2023-11-16 install cargo-fuzz cargo-afl cargo fmt --check cargo clippy --features libfuzzer-sys cargo clippy --features afl - cargo +nightly-2023-04-15 fuzz build --features libfuzzer-sys - cargo +nightly-2023-04-15 afl build --features afl + cargo +nightly-2023-11-16 fuzz build --features libfuzzer-sys + cargo +nightly-2023-11-16 afl build --features afl ) # Fix license headers diff --git a/crypto/Cargo.toml b/crypto/Cargo.toml index 710cee9a..1280b6b7 100644 --- a/crypto/Cargo.toml +++ b/crypto/Cargo.toml @@ -9,9 +9,12 @@ edition = "2021" openssl = ["dep:openssl", "dep:hkdf", "dep:sha2"] rustcrypto = ["dep:hkdf", "dep:hmac", "dep:p256", "dep:p384", "dep:rand", "dep:sha2", "dep:base64ct", "dep:ecdsa", "dep:sec1"] deterministic_rand = ["dep:rand"] +no-cfi = [] [dependencies] arrayvec = { version = "0.7.4", default-features = false, features = ["zeroize"] } +caliptra-cfi-lib-git.workspace = true +caliptra-cfi-derive-git.workspace = true ecdsa = { version = "0.16.9", optional = true, features = ["pem"]} hkdf = { version = "0.12.3", optional = true } hmac = {version="0.12.1", optional = true} diff --git a/crypto/src/lib.rs b/crypto/src/lib.rs index d4431c5c..cb9aee03 100644 --- a/crypto/src/lib.rs +++ b/crypto/src/lib.rs @@ -170,6 +170,18 @@ pub trait Crypto { info: &[u8], ) -> Result; + /// CFI wrapper around derive_cdi + /// + /// To implement this function, you need to add the + /// cfi_impl_fn proc_macro to derive_cdi. + #[cfg(not(feature = "no-cfi"))] + fn __cfi_derive_cdi( + &mut self, + algs: AlgLen, + measurement: &Digest, + info: &[u8], + ) -> Result; + /// Derives a key pair using a cryptographically secure KDF /// /// # Arguments @@ -187,6 +199,19 @@ pub trait Crypto { info: &[u8], ) -> Result<(Self::PrivKey, EcdsaPub), CryptoError>; + /// CFI wrapper around derive_key_pair + /// + /// To implement this function, you need to add the + /// cfi_impl_fn proc_macro to derive_key_pair. + #[cfg(not(feature = "no-cfi"))] + fn __cfi_derive_key_pair( + &mut self, + algs: AlgLen, + cdi: &Self::Cdi, + label: &[u8], + info: &[u8], + ) -> Result<(Self::PrivKey, EcdsaPub), CryptoError>; + /// Sign `digest` with the platform Alias Key /// /// # Arguments diff --git a/crypto/src/openssl.rs b/crypto/src/openssl.rs index 6a4c3944..29d27fae 100644 --- a/crypto/src/openssl.rs +++ b/crypto/src/openssl.rs @@ -1,6 +1,8 @@ // Licensed under the Apache-2.0 license use crate::{hkdf::*, AlgLen, Crypto, CryptoBuf, CryptoError, Digest, EcdsaPub, Hasher, HmacSig}; +#[cfg(not(feature = "no-cfi"))] +use caliptra_cfi_derive_git::cfi_impl_fn; use openssl::{ bn::{BigNum, BigNumContext}, ec::{EcGroup, EcKey, EcPoint}, @@ -122,6 +124,7 @@ impl Crypto for OpensslCrypto { Ok(OpensslHasher(openssl::hash::Hasher::new(md)?)) } + #[cfg_attr(not(feature = "no-cfi"), cfi_impl_fn)] fn derive_cdi( &mut self, algs: AlgLen, @@ -131,6 +134,7 @@ impl Crypto for OpensslCrypto { hkdf_derive_cdi(algs, measurement, info) } + #[cfg_attr(not(feature = "no-cfi"), cfi_impl_fn)] fn derive_key_pair( &mut self, algs: AlgLen, diff --git a/dpe/Cargo.toml b/dpe/Cargo.toml index d91b3779..fe5c80c6 100644 --- a/dpe/Cargo.toml +++ b/dpe/Cargo.toml @@ -6,7 +6,7 @@ version = "0.1.0" edition = "2021" [features] -default = ["dpe_profile_p256_sha256"] +default = ["dpe_profile_p256_sha256", "no-cfi"] dpe_profile_p256_sha256 = ["platform/dpe_profile_p256_sha256"] dpe_profile_p384_sha384 = ["platform/dpe_profile_p384_sha384"] # Run ARBITRARY_MAX_HANDLES=n cargo build --features arbitrary_max_handles to use this feature @@ -22,18 +22,23 @@ disable_internal_info = [] disable_internal_dice = [] disable_is_ca = [] disable_retain_parent_context = [] +no-cfi = ["crypto/no-cfi"] [dependencies] bitflags = "2.4.0" +caliptra-cfi-lib-git.workspace = true +caliptra-cfi-derive-git.workspace = true constant_time_eq = "0.3.0" crypto = {path = "../crypto", default-features = false} platform = {path = "../platform", default-features = false} ufmt = { git = "https://github.com/korran/ufmt.git", rev = "1d0743c1ffffc68bc05ca8eeb81c166192863f33", features = ["inline"] } zerocopy = "0.6.1" zeroize = { version = "1.6.0", default-features = false, features = ["zeroize_derive"] } +cfg-if = "1.0.0" [dev-dependencies] asn1 = "0.13.0" +caliptra-cfi-lib-git = { workspace = true, features = ["cfi-test"] } openssl = "0.10.57" x509-parser = "0.15.1" crypto = {path = "../crypto", features = ["deterministic_rand", "openssl"]} diff --git a/dpe/fuzz/Cargo.lock b/dpe/fuzz/Cargo.lock index 44e1d6d8..9b612c76 100644 --- a/dpe/fuzz/Cargo.lock +++ b/dpe/fuzz/Cargo.lock @@ -113,6 +113,22 @@ version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +[[package]] +name = "caliptra-cfi-derive-git" +version = "0.1.0" +source = "git+https://github.com/chipsalliance/caliptra-cfi.git#9f315fcf11fe006e95e62149f54f98620e5244e7" +dependencies = [ + "paste", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "caliptra-cfi-lib-git" +version = "0.1.0" +source = "git+https://github.com/chipsalliance/caliptra-cfi.git#9f315fcf11fe006e95e62149f54f98620e5244e7" + [[package]] name = "cc" version = "1.0.83" @@ -188,6 +204,8 @@ name = "crypto" version = "0.1.0" dependencies = [ "arrayvec", + "caliptra-cfi-derive-git", + "caliptra-cfi-lib-git", "hkdf", "openssl", "sha2", @@ -250,6 +268,9 @@ name = "dpe" version = "0.1.0" dependencies = [ "bitflags 2.4.0", + "caliptra-cfi-derive-git", + "caliptra-cfi-lib-git", + "cfg-if", "constant_time_eq", "crypto", "platform", @@ -459,6 +480,12 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "paste" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" + [[package]] name = "pem-rfc7468" version = "0.7.0" diff --git a/dpe/src/commands/certify_key.rs b/dpe/src/commands/certify_key.rs index cde41f84..fbaa5850 100644 --- a/dpe/src/commands/certify_key.rs +++ b/dpe/src/commands/certify_key.rs @@ -9,6 +9,12 @@ use crate::{ DPE_PROFILE, MAX_CERT_SIZE, MAX_HANDLES, }; use bitflags::bitflags; +#[cfg(not(feature = "no-cfi"))] +use caliptra_cfi_derive_git::cfi_impl_fn; +use caliptra_cfi_lib_git::cfi_launder; +#[cfg(not(feature = "no-cfi"))] +use caliptra_cfi_lib_git::{cfi_assert, cfi_assert_eq}; +use cfg_if::cfg_if; use crypto::Crypto; use platform::{Platform, MAX_ISSUER_NAME_SIZE}; @@ -41,6 +47,7 @@ impl CertifyKeyCmd { } impl CommandExecution for CertifyKeyCmd { + #[cfg_attr(not(feature = "no-cfi"), cfi_impl_fn)] fn execute( &self, dpe: &mut DpeInstance, @@ -64,6 +71,8 @@ impl CommandExecution for CertifyKeyCmd { if !context.allow_x509() { return Err(DpeErrorCode::InvalidArgument); } + } else if self.format == Self::FORMAT_CSR && !dpe.support.csr() { + return Err(DpeErrorCode::ArgumentNotSupported); } // Make sure the command is coming from the right locality. @@ -71,14 +80,31 @@ impl CommandExecution for CertifyKeyCmd { return Err(DpeErrorCode::InvalidLocality); } + cfg_if! { + if #[cfg(not(feature = "no-cfi"))] { + cfi_assert!(!self.uses_is_ca() || dpe.support.is_ca()); + cfi_assert!(!self.uses_is_ca() || context.allow_ca()); + cfi_assert!(self.format != Self::FORMAT_X509 || dpe.support.x509()); + cfi_assert!(self.format != Self::FORMAT_X509 || context.allow_x509()); + cfi_assert!(self.format != Self::FORMAT_CSR || dpe.support.csr()); + cfi_assert_eq(context.locality, locality); + } + } + let algs = DPE_PROFILE.alg_len(); let digest = dpe.compute_measurement_hash(env, idx)?; let cdi = env .crypto .derive_cdi(DPE_PROFILE.alg_len(), &digest, b"DPE")?; - let (priv_key, pub_key) = env - .crypto - .derive_key_pair(algs, &cdi, &self.label, b"ECC")?; + let key_pair = env.crypto.derive_key_pair(algs, &cdi, &self.label, b"ECC"); + if cfi_launder(key_pair.is_ok()) { + #[cfg(not(feature = "no-cfi"))] + cfi_assert!(key_pair.is_ok()); + } else { + #[cfg(not(feature = "no-cfi"))] + cfi_assert!(key_pair.is_err()); + } + let (priv_key, pub_key) = key_pair?; let mut subj_serial = [0u8; DPE_PROFILE.get_hash_size() * 2]; env.crypto @@ -109,6 +135,8 @@ impl CommandExecution for CertifyKeyCmd { let mut cert = [0u8; MAX_CERT_SIZE]; let cert_size = match self.format { Self::FORMAT_X509 => { + #[cfg(not(feature = "no-cfi"))] + cfi_assert_eq(self.format, Self::FORMAT_X509); let mut tbs_buffer = [0u8; MAX_CERT_SIZE]; let mut tbs_writer = CertWriter::new(&mut tbs_buffer, true); if issuer_len > MAX_ISSUER_NAME_SIZE { @@ -141,15 +169,10 @@ impl CommandExecution for CertifyKeyCmd { u32::try_from(bytes_written).map_err(|_| DpeErrorCode::InternalError)? } Self::FORMAT_CSR => { - if !dpe.support.csr() { - return Err(DpeErrorCode::ArgumentNotSupported); - } - + #[cfg(not(feature = "no-cfi"))] + cfi_assert_eq(self.format, Self::FORMAT_CSR); let mut cert_req_info_buffer = [0u8; MAX_CERT_SIZE]; let mut cert_req_info_writer = CertWriter::new(&mut cert_req_info_buffer, true); - if issuer_len > MAX_ISSUER_NAME_SIZE { - return Err(DpeErrorCode::InternalError); - } let mut bytes_written = cert_req_info_writer.encode_certification_request_info( &pub_key, &subject_name, @@ -230,6 +253,7 @@ mod tests { dpe_instance::tests::{TestTypes, SIMULATION_HANDLE, TEST_LOCALITIES}, support::Support, }; + use caliptra_cfi_lib_git::CfiCounter; use cms::{ content_info::{CmsVersion, ContentInfo}, signed_data::{SignedData, SignerIdentifier}, @@ -262,6 +286,7 @@ mod tests { #[test] fn test_deserialize_certify_key() { + CfiCounter::reset_for_test(); let mut command = CommandHdr::new_for_test(Command::CERTIFY_KEY) .as_bytes() .to_vec(); @@ -274,6 +299,7 @@ mod tests { #[test] fn test_certify_key_x509() { + CfiCounter::reset_for_test(); let mut env = DpeEnv:: { crypto: OpensslCrypto::new(), platform: DefaultPlatform, @@ -314,6 +340,7 @@ mod tests { #[test] fn test_is_ca() { + CfiCounter::reset_for_test(); let mut env = DpeEnv:: { crypto: OpensslCrypto::new(), platform: DefaultPlatform, @@ -385,6 +412,7 @@ mod tests { #[test] fn test_certify_key_csr() { + CfiCounter::reset_for_test(); let mut env = DpeEnv:: { crypto: OpensslCrypto::new(), platform: DefaultPlatform, diff --git a/dpe/src/commands/derive_context.rs b/dpe/src/commands/derive_context.rs index dee92ed9..203cb91f 100644 --- a/dpe/src/commands/derive_context.rs +++ b/dpe/src/commands/derive_context.rs @@ -8,6 +8,11 @@ use crate::{ DPE_PROFILE, }; use bitflags::bitflags; +#[cfg(not(feature = "no-cfi"))] +use caliptra_cfi_derive_git::cfi_impl_fn; +#[cfg(not(feature = "no-cfi"))] +use caliptra_cfi_lib_git::{cfi_assert, cfi_assert_eq}; +use cfg_if::cfg_if; #[repr(C)] #[derive(Debug, PartialEq, Eq, zerocopy::FromBytes, zerocopy::AsBytes)] @@ -163,6 +168,7 @@ impl DeriveContextCmd { } impl CommandExecution for DeriveContextCmd { + #[cfg_attr(not(feature = "no-cfi"), cfi_impl_fn)] fn execute( &self, dpe: &mut DpeInstance, @@ -173,11 +179,8 @@ impl CommandExecution for DeriveContextCmd { if (!dpe.support.internal_info() && self.uses_internal_info_input()) || (!dpe.support.internal_dice() && self.uses_internal_dice_input()) || (!dpe.support.retain_parent_context() && self.retains_parent()) - { - return Err(DpeErrorCode::ArgumentNotSupported); - } - - if (!dpe.support.is_ca() && self.allows_ca()) || (!dpe.support.x509() && self.allows_x509()) + || (!dpe.support.is_ca() && self.allows_ca()) + || (!dpe.support.x509() && self.allows_x509()) { return Err(DpeErrorCode::ArgumentNotSupported); } @@ -191,15 +194,35 @@ impl CommandExecution for DeriveContextCmd { } let target_locality = if !self.changes_locality() { + #[cfg(not(feature = "no-cfi"))] + cfi_assert!(!self.changes_locality()); locality } else { + #[cfg(not(feature = "no-cfi"))] + cfi_assert!(self.changes_locality()); self.target_locality }; + cfg_if! { + if #[cfg(not(feature = "no-cfi"))] { + cfi_assert!(dpe.support.internal_info() || !self.uses_internal_info_input()); + cfi_assert!(dpe.support.internal_dice() || !self.uses_internal_dice_input()); + cfi_assert!(dpe.support.retain_parent_context() || !self.retains_parent()); + cfi_assert!(dpe.support.is_ca() || !self.allows_ca()); + cfi_assert!(dpe.support.x509() || !self.allows_x509()); + cfi_assert!(dpe.contexts[parent_idx].allow_ca() || !self.allows_ca()); + cfi_assert!(dpe.contexts[parent_idx].allow_x509() || !self.allows_x509()); + cfi_assert!(!self.is_recursive() || !self.retains_parent()); + } + } + if self.is_recursive() { let mut tmp_context = dpe.contexts[parent_idx]; if tmp_context.tci.tci_type != self.tci_type { return Err(DpeErrorCode::InvalidArgument); + } else { + #[cfg(not(feature = "no-cfi"))] + cfi_assert_eq(tmp_context.tci.tci_type, self.tci_type); } dpe.add_tci_measurement( env, @@ -227,13 +250,21 @@ impl CommandExecution for DeriveContextCmd { .get_next_inactive_context_pos() .ok_or(DpeErrorCode::MaxTcis)?; - if !self.safe_to_make_child(dpe, parent_idx, target_locality)? { + let safe_to_make_child = self.safe_to_make_child(dpe, parent_idx, target_locality)?; + if !safe_to_make_child { return Err(DpeErrorCode::InvalidArgument); + } else { + #[cfg(not(feature = "no-cfi"))] + cfi_assert!(safe_to_make_child); } let child_handle = if self.makes_default() { + #[cfg(not(feature = "no-cfi"))] + cfi_assert!(self.makes_default()); ContextHandle::default() } else { + #[cfg(not(feature = "no-cfi"))] + cfi_assert!(!self.makes_default()); dpe.generate_new_handle(env)? }; @@ -266,10 +297,19 @@ impl CommandExecution for DeriveContextCmd { // Copy the parent context to mutate so that we avoid mutating internal state upon an error. let mut tmp_parent_context = dpe.contexts[parent_idx]; if !self.retains_parent() { + #[cfg(not(feature = "no-cfi"))] + cfi_assert!(!self.retains_parent()); tmp_parent_context.state = ContextState::Retired; tmp_parent_context.handle = ContextHandle([0xff; ContextHandle::SIZE]); } else if !tmp_parent_context.handle.is_default() { tmp_parent_context.handle = dpe.generate_new_handle(env)?; + } else { + cfg_if! { + if #[cfg(not(feature = "no-cfi"))] { + cfi_assert!(self.retains_parent()); + cfi_assert!(tmp_parent_context.handle.is_default()); + } + } } // Add child to the parent's list of children. @@ -303,6 +343,7 @@ mod tests { support::Support, MAX_HANDLES, }; + use caliptra_cfi_lib_git::CfiCounter; use crypto::{Crypto, Hasher, OpensslCrypto}; use openssl::x509::X509; use openssl::{bn::BigNum, ecdsa::EcdsaSig}; @@ -319,6 +360,7 @@ mod tests { #[test] fn test_deserialize_derive_context() { + CfiCounter::reset_for_test(); let mut command = CommandHdr::new_for_test(Command::DERIVE_CONTEXT) .as_bytes() .to_vec(); @@ -331,6 +373,7 @@ mod tests { #[test] fn test_support() { + CfiCounter::reset_for_test(); let mut env = DpeEnv:: { crypto: OpensslCrypto::new(), platform: DefaultPlatform, @@ -392,6 +435,7 @@ mod tests { #[test] fn test_initial_conditions() { + CfiCounter::reset_for_test(); let mut env = DpeEnv:: { crypto: OpensslCrypto::new(), platform: DefaultPlatform, @@ -418,6 +462,7 @@ mod tests { #[test] fn test_max_tcis() { + CfiCounter::reset_for_test(); let mut env = DpeEnv:: { crypto: OpensslCrypto::new(), platform: DefaultPlatform, @@ -453,6 +498,7 @@ mod tests { #[test] fn test_set_child_parent_relationship() { + CfiCounter::reset_for_test(); let mut env = DpeEnv:: { crypto: OpensslCrypto::new(), platform: DefaultPlatform, @@ -487,6 +533,7 @@ mod tests { #[test] fn test_set_other_values() { + CfiCounter::reset_for_test(); let mut env = DpeEnv:: { crypto: OpensslCrypto::new(), platform: DefaultPlatform, @@ -514,6 +561,7 @@ mod tests { #[test] fn test_correct_child_handle() { + CfiCounter::reset_for_test(); let mut env = DpeEnv:: { crypto: OpensslCrypto::new(), platform: DefaultPlatform, @@ -557,6 +605,7 @@ mod tests { #[test] fn test_full_attestation_flow() { + CfiCounter::reset_for_test(); let mut env = DpeEnv:: { crypto: OpensslCrypto::new(), platform: DefaultPlatform, @@ -653,6 +702,7 @@ mod tests { #[test] fn test_correct_parent_handle() { + CfiCounter::reset_for_test(); let mut env = DpeEnv:: { crypto: OpensslCrypto::new(), platform: DefaultPlatform, @@ -732,6 +782,7 @@ mod tests { #[test] fn test_safe_to_make_default() { + CfiCounter::reset_for_test(); let mut make_default_in_0 = DeriveContextCmd { handle: ContextHandle::default(), data: TciMeasurement::default().0, @@ -771,6 +822,7 @@ mod tests { #[test] fn test_safe_to_make_non_default() { + CfiCounter::reset_for_test(); let non_default = DeriveContextCmd { handle: ContextHandle::default(), data: TciMeasurement::default().0, @@ -789,6 +841,7 @@ mod tests { #[test] fn test_default_context_cannot_be_retained() { + CfiCounter::reset_for_test(); let mut env = DpeEnv:: { crypto: OpensslCrypto::new(), platform: DefaultPlatform, @@ -814,6 +867,7 @@ mod tests { #[test] fn test_make_default_in_other_locality_that_has_non_default() { + CfiCounter::reset_for_test(); let mut env = DpeEnv:: { crypto: OpensslCrypto::new(), platform: DefaultPlatform, @@ -852,6 +906,7 @@ mod tests { #[test] fn test_recursive() { + CfiCounter::reset_for_test(); let mut env = DpeEnv:: { crypto: OpensslCrypto::new(), platform: DefaultPlatform, diff --git a/dpe/src/commands/destroy_context.rs b/dpe/src/commands/destroy_context.rs index 00015f1c..c1e6aec6 100644 --- a/dpe/src/commands/destroy_context.rs +++ b/dpe/src/commands/destroy_context.rs @@ -6,6 +6,11 @@ use crate::{ response::{DpeErrorCode, Response, ResponseHdr}, MAX_HANDLES, }; +#[cfg(not(feature = "no-cfi"))] +use caliptra_cfi_derive_git::cfi_impl_fn; +use caliptra_cfi_lib_git::cfi_launder; +#[cfg(not(feature = "no-cfi"))] +use caliptra_cfi_lib_git::{cfi_assert, cfi_assert_eq}; #[repr(C)] #[derive(Debug, PartialEq, Eq, zerocopy::FromBytes, zerocopy::AsBytes)] @@ -14,6 +19,7 @@ pub struct DestroyCtxCmd { } impl CommandExecution for DestroyCtxCmd { + #[cfg_attr(not(feature = "no-cfi"), cfi_impl_fn)] fn execute( &self, dpe: &mut DpeInstance, @@ -25,6 +31,9 @@ impl CommandExecution for DestroyCtxCmd { // Make sure the command is coming from the right locality. if context.locality != locality { return Err(DpeErrorCode::InvalidLocality); + } else { + #[cfg(not(feature = "no-cfi"))] + cfi_assert_eq(context.locality, locality); } // mark consecutive retired parent contexts without active children to be destroyed @@ -38,11 +47,16 @@ impl CommandExecution for DestroyCtxCmd { } let parent_context = &dpe.contexts[parent_idx]; // make sure the retired context does not have other active child contexts + let child_context_count = flags_iter(parent_context.children, MAX_HANDLES).count(); if parent_context.state == ContextState::Retired - && flags_iter(parent_context.children, MAX_HANDLES).count() == 1 + && cfi_launder(child_context_count) == 1 { retired_contexts |= 1 << parent_idx; } else { + #[cfg(not(feature = "no-cfi"))] + cfi_assert!( + parent_context.state != ContextState::Retired || child_context_count != 1 + ); break; } @@ -55,9 +69,12 @@ impl CommandExecution for DestroyCtxCmd { for (idx, c) in dpe.contexts.iter_mut().enumerate() { // Clears all the to_destroy bits in the children of every context - c.children &= !to_destroy; + c.children &= !cfi_launder(to_destroy); if to_destroy & (1 << idx) != 0 { c.destroy(); + } else { + #[cfg(not(feature = "no-cfi"))] + cfi_assert_eq(to_destroy & (1 << idx), 0); } } @@ -77,6 +94,7 @@ mod tests { support::{test::SUPPORT, Support}, DPE_PROFILE, }; + use caliptra_cfi_lib_git::CfiCounter; use crypto::OpensslCrypto; use platform::default::DefaultPlatform; use zerocopy::AsBytes; @@ -87,6 +105,7 @@ mod tests { #[test] fn test_deserialize_destroy_context() { + CfiCounter::reset_for_test(); let mut command = CommandHdr::new_for_test(Command::DESTROY_CONTEXT) .as_bytes() .to_vec(); @@ -99,6 +118,7 @@ mod tests { #[test] fn test_destroy_context() { + CfiCounter::reset_for_test(); let mut env = DpeEnv:: { crypto: OpensslCrypto::new(), platform: DefaultPlatform, @@ -255,6 +275,7 @@ mod tests { #[test] fn test_retired_parent_contexts_destroyed() { + CfiCounter::reset_for_test(); let mut env = DpeEnv:: { crypto: OpensslCrypto::new(), platform: DefaultPlatform, @@ -323,6 +344,7 @@ mod tests { #[test] fn test_retired_parent_context_not_destroyed_if_it_has_other_active_children() { + CfiCounter::reset_for_test(); let mut env = DpeEnv:: { crypto: OpensslCrypto::new(), platform: DefaultPlatform, diff --git a/dpe/src/commands/get_certificate_chain.rs b/dpe/src/commands/get_certificate_chain.rs index a6e5511c..c110e920 100644 --- a/dpe/src/commands/get_certificate_chain.rs +++ b/dpe/src/commands/get_certificate_chain.rs @@ -4,6 +4,8 @@ use crate::{ dpe_instance::{DpeEnv, DpeInstance, DpeTypes}, response::{DpeErrorCode, GetCertificateChainResp, Response, ResponseHdr}, }; +#[cfg(not(feature = "no-cfi"))] +use caliptra_cfi_derive_git::cfi_impl_fn; use platform::{Platform, MAX_CHUNK_SIZE}; #[repr(C)] @@ -14,6 +16,7 @@ pub struct GetCertificateChainCmd { } impl CommandExecution for GetCertificateChainCmd { + #[cfg_attr(not(feature = "no-cfi"), cfi_impl_fn)] fn execute( &self, _dpe: &mut DpeInstance, @@ -45,6 +48,7 @@ mod tests { dpe_instance::tests::{TestTypes, TEST_LOCALITIES}, support::test::SUPPORT, }; + use caliptra_cfi_lib_git::CfiCounter; use crypto::OpensslCrypto; use platform::default::DefaultPlatform; use zerocopy::AsBytes; @@ -56,6 +60,7 @@ mod tests { #[test] fn test_deserialize_get_certificate_chain() { + CfiCounter::reset_for_test(); let mut command = CommandHdr::new_for_test(Command::GET_CERTIFICATE_CHAIN) .as_bytes() .to_vec(); @@ -68,6 +73,7 @@ mod tests { #[test] fn test_fails_if_size_greater_than_max_chunk_size() { + CfiCounter::reset_for_test(); let mut env = DpeEnv:: { crypto: OpensslCrypto::new(), platform: DefaultPlatform, diff --git a/dpe/src/commands/initialize_context.rs b/dpe/src/commands/initialize_context.rs index 60e2c410..e460e78a 100644 --- a/dpe/src/commands/initialize_context.rs +++ b/dpe/src/commands/initialize_context.rs @@ -6,6 +6,11 @@ use crate::{ response::{DpeErrorCode, NewHandleResp, Response, ResponseHdr}, }; use bitflags::bitflags; +#[cfg(not(feature = "no-cfi"))] +use caliptra_cfi_derive_git::cfi_impl_fn; +#[cfg(not(feature = "no-cfi"))] +use caliptra_cfi_lib_git::{cfi_assert, cfi_assert_eq}; +use cfg_if::cfg_if; #[repr(C)] #[derive(Debug, PartialEq, Eq, zerocopy::FromBytes, zerocopy::AsBytes)] @@ -37,6 +42,7 @@ impl InitCtxCmd { } impl CommandExecution for InitCtxCmd { + #[cfg_attr(not(feature = "no-cfi"), cfi_impl_fn)] fn execute( &self, dpe: &mut DpeInstance, @@ -57,6 +63,14 @@ impl CommandExecution for InitCtxCmd { return Err(DpeErrorCode::InvalidArgument); } + cfg_if! { + if #[cfg(not(feature = "no-cfi"))] { + cfi_assert!(!self.flag_is_default() || !dpe.has_initialized()); + cfi_assert!(!self.flag_is_simulation() || dpe.support.simulation()); + cfi_assert!(self.flag_is_default() ^ self.flag_is_simulation()); + } + } + let idx = dpe .get_next_inactive_context_pos() .ok_or(DpeErrorCode::MaxTcis)?; @@ -95,6 +109,7 @@ mod tests { dpe_instance::tests::{TestTypes, TEST_LOCALITIES}, support::Support, }; + use caliptra_cfi_lib_git::CfiCounter; use crypto::OpensslCrypto; use platform::default::DefaultPlatform; use zerocopy::AsBytes; @@ -103,6 +118,7 @@ mod tests { #[test] fn test_deserialize_init_ctx() { + CfiCounter::reset_for_test(); let mut command = CommandHdr::new_for_test(Command::INITIALIZE_CONTEXT) .as_bytes() .to_vec(); @@ -115,6 +131,7 @@ mod tests { #[test] fn test_initialize_context() { + CfiCounter::reset_for_test(); let mut env = DpeEnv:: { crypto: OpensslCrypto::new(), platform: DefaultPlatform, diff --git a/dpe/src/commands/mod.rs b/dpe/src/commands/mod.rs index cb417807..a2938948 100644 --- a/dpe/src/commands/mod.rs +++ b/dpe/src/commands/mod.rs @@ -108,6 +108,14 @@ pub trait CommandExecution { env: &mut DpeEnv, locality: u32, ) -> Result; + + #[cfg(not(feature = "no-cfi"))] + fn __cfi_execute( + &self, + dpe: &mut DpeInstance, + env: &mut DpeEnv, + locality: u32, + ) -> Result; } // ABI Command structures @@ -153,6 +161,7 @@ impl TryFrom<&[u8]> for CommandHdr { pub mod tests { use super::*; use crate::{DpeProfile, DPE_PROFILE}; + use caliptra_cfi_lib_git::CfiCounter; use zerocopy::AsBytes; #[cfg(feature = "dpe_profile_p256_sha256")] @@ -185,6 +194,7 @@ pub mod tests { #[test] fn test_deserialize_get_profile() { + CfiCounter::reset_for_test(); // Commands that can be deserialized. assert_eq!( Ok(Command::GetProfile), @@ -194,6 +204,7 @@ pub mod tests { #[test] fn test_slice_to_command_hdr() { + CfiCounter::reset_for_test(); let invalid_command: Result = Err(DpeErrorCode::InvalidCommand); // Test if too small. diff --git a/dpe/src/commands/rotate_context.rs b/dpe/src/commands/rotate_context.rs index 4933118e..1ef9af58 100644 --- a/dpe/src/commands/rotate_context.rs +++ b/dpe/src/commands/rotate_context.rs @@ -6,6 +6,11 @@ use crate::{ response::{DpeErrorCode, NewHandleResp, Response, ResponseHdr}, }; use bitflags::bitflags; +#[cfg(not(feature = "no-cfi"))] +use caliptra_cfi_derive_git::cfi_impl_fn; +use caliptra_cfi_lib_git::cfi_launder; +#[cfg(not(feature = "no-cfi"))] +use caliptra_cfi_lib_git::{cfi_assert, cfi_assert_eq}; #[repr(C)] #[derive(Debug, PartialEq, Eq, zerocopy::FromBytes, zerocopy::AsBytes)] @@ -47,6 +52,7 @@ impl RotateCtxCmd { } impl CommandExecution for RotateCtxCmd { + #[cfg_attr(not(feature = "no-cfi"), cfi_impl_fn)] fn execute( &self, dpe: &mut DpeInstance, @@ -55,6 +61,9 @@ impl CommandExecution for RotateCtxCmd { ) -> Result { if !dpe.support.rotate_context() { return Err(DpeErrorCode::InvalidCommand); + } else { + #[cfg(not(feature = "no-cfi"))] + cfi_assert!(dpe.support.rotate_context()); } let idx = dpe.get_active_context_pos(&self.handle, locality)?; @@ -62,11 +71,17 @@ impl CommandExecution for RotateCtxCmd { if self.uses_target_is_default() { let default_context_idx = dpe.get_active_context_pos(&ContextHandle::default(), locality); - if default_context_idx.is_ok() - || self.non_default_valid_handles_exist(dpe, locality, idx) - { + let non_default_valid_handles_exist = + self.non_default_valid_handles_exist(dpe, locality, idx); + if default_context_idx.is_ok() || cfi_launder(non_default_valid_handles_exist) { return Err(DpeErrorCode::InvalidArgument); + } else { + #[cfg(not(feature = "no-cfi"))] + cfi_assert!(default_context_idx.is_err() && !non_default_valid_handles_exist); } + } else { + #[cfg(not(feature = "no-cfi"))] + cfi_assert!(!self.uses_target_is_default()); } let new_handle = if self.uses_target_is_default() { @@ -93,6 +108,7 @@ mod tests { }, support::Support, }; + use caliptra_cfi_lib_git::CfiCounter; use crypto::OpensslCrypto; use platform::default::DefaultPlatform; use zerocopy::AsBytes; @@ -104,6 +120,7 @@ mod tests { #[test] fn test_deserialize_rotate_context() { + CfiCounter::reset_for_test(); let mut command = CommandHdr::new_for_test(Command::ROTATE_CONTEXT_HANDLE) .as_bytes() .to_vec(); @@ -116,6 +133,7 @@ mod tests { #[test] fn test_rotate_context() { + CfiCounter::reset_for_test(); let mut env = DpeEnv:: { crypto: OpensslCrypto::new(), platform: DefaultPlatform, diff --git a/dpe/src/commands/sign.rs b/dpe/src/commands/sign.rs index c3f5dfb4..c962d245 100644 --- a/dpe/src/commands/sign.rs +++ b/dpe/src/commands/sign.rs @@ -7,6 +7,12 @@ use crate::{ DPE_PROFILE, }; use bitflags::bitflags; +#[cfg(not(feature = "no-cfi"))] +use caliptra_cfi_derive_git::cfi_impl_fn; +use caliptra_cfi_lib_git::cfi_launder; +#[cfg(not(feature = "no-cfi"))] +use caliptra_cfi_lib_git::{cfi_assert, cfi_assert_eq, cfi_assert_ne}; +use cfg_if::cfg_if; use crypto::{Crypto, CryptoBuf, Digest, EcdsaSig, HmacSig}; #[repr(C)] @@ -33,6 +39,7 @@ impl SignCmd { self.flags.contains(SignFlags::IS_SYMMETRIC) } + #[cfg_attr(not(feature = "no-cfi"), cfi_impl_fn)] fn ecdsa_sign( &self, dpe: &mut DpeInstance, @@ -45,9 +52,15 @@ impl SignCmd { let cdi = env .crypto .derive_cdi(DPE_PROFILE.alg_len(), &cdi_digest, b"DPE")?; - let (priv_key, pub_key) = env - .crypto - .derive_key_pair(algs, &cdi, &self.label, b"ECC")?; + let key_pair = env.crypto.derive_key_pair(algs, &cdi, &self.label, b"ECC"); + if cfi_launder(key_pair.is_ok()) { + #[cfg(not(feature = "no-cfi"))] + cfi_assert!(key_pair.is_ok()); + } else { + #[cfg(not(feature = "no-cfi"))] + cfi_assert!(key_pair.is_err()); + } + let (priv_key, pub_key) = key_pair?; let sig = env .crypto @@ -56,6 +69,7 @@ impl SignCmd { Ok(sig) } + #[cfg_attr(not(feature = "no-cfi"), cfi_impl_fn)] fn hmac_sign( &self, dpe: &mut DpeInstance, @@ -67,14 +81,22 @@ impl SignCmd { let cdi_digest = dpe.compute_measurement_hash(env, idx)?; let cdi = env .crypto - .derive_cdi(DPE_PROFILE.alg_len(), &cdi_digest, b"DPE")?; + .derive_cdi(DPE_PROFILE.alg_len(), &cdi_digest, b"DPE"); + if cfi_launder(cdi.is_ok()) { + #[cfg(not(feature = "no-cfi"))] + cfi_assert!(cdi.is_ok()); + } else { + #[cfg(not(feature = "no-cfi"))] + cfi_assert!(cdi.is_err()); + } Ok(env .crypto - .hmac_sign_with_derived(algs, &cdi, &self.label, b"HMAC", digest)?) + .hmac_sign_with_derived(algs, &cdi?, &self.label, b"HMAC", digest)?) } } impl CommandExecution for SignCmd { + #[cfg_attr(not(feature = "no-cfi"), cfi_impl_fn)] fn execute( &self, dpe: &mut DpeInstance, @@ -93,6 +115,13 @@ impl CommandExecution for SignCmd { return Err(DpeErrorCode::InvalidArgument); } + cfg_if! { + if #[cfg(not(feature = "no-cfi"))] { + cfi_assert!(dpe.support.is_symmetric() || !self.uses_symmetric()); + cfi_assert_ne(context.context_type, ContextType::Simulation); + } + } + let algs = DPE_PROFILE.alg_len(); let digest = Digest::new(&self.digest)?; @@ -140,6 +169,7 @@ mod tests { dpe_instance::tests::{TestTypes, RANDOM_HANDLE, SIMULATION_HANDLE, TEST_LOCALITIES}, support::{test::SUPPORT, Support}, }; + use caliptra_cfi_lib_git::CfiCounter; use crypto::OpensslCrypto; use openssl::x509::X509; use openssl::{bn::BigNum, ecdsa::EcdsaSig}; @@ -155,6 +185,7 @@ mod tests { #[test] fn test_deserialize_sign() { + CfiCounter::reset_for_test(); let mut command = CommandHdr::new_for_test(Command::SIGN).as_bytes().to_vec(); command.extend(TEST_SIGN_CMD.as_bytes()); assert_eq!( @@ -165,6 +196,7 @@ mod tests { #[test] fn test_uses_symmetric() { + CfiCounter::reset_for_test(); // No flags set. assert!(!SignCmd { flags: SignFlags::empty(), @@ -182,6 +214,7 @@ mod tests { #[test] fn test_bad_command_inputs() { + CfiCounter::reset_for_test(); let mut env = DpeEnv:: { crypto: OpensslCrypto::new(), platform: DefaultPlatform, @@ -248,6 +281,7 @@ mod tests { #[test] fn test_asymmetric() { + CfiCounter::reset_for_test(); let mut env = DpeEnv:: { crypto: OpensslCrypto::new(), platform: DefaultPlatform, @@ -307,6 +341,7 @@ mod tests { #[test] fn test_symmetric() { + CfiCounter::reset_for_test(); let mut env = DpeEnv:: { crypto: OpensslCrypto::new(), platform: DefaultPlatform, diff --git a/dpe/src/dpe_instance.rs b/dpe/src/dpe_instance.rs index cd889a08..efe37e65 100644 --- a/dpe/src/dpe_instance.rs +++ b/dpe/src/dpe_instance.rs @@ -12,6 +12,12 @@ use crate::{ tci::{TciMeasurement, TciNodeData}, U8Bool, DPE_PROFILE, INTERNAL_INPUT_INFO_SIZE, MAX_HANDLES, }; +#[cfg(not(feature = "no-cfi"))] +use caliptra_cfi_derive_git::cfi_impl_fn; +use caliptra_cfi_lib_git::cfi_launder; +#[cfg(not(feature = "no-cfi"))] +use caliptra_cfi_lib_git::{cfi_assert, cfi_assert_eq}; +use cfg_if::cfg_if; use constant_time_eq::constant_time_eq; use crypto::{Crypto, Digest, Hasher}; use platform::{Platform, MAX_CHUNK_SIZE}; @@ -55,6 +61,7 @@ impl DpeInstance { /// /// * `support` - optional functionality the instance supports /// * `issuer_cn` - issuer Common Name to use in DPE leaf certs + #[cfg_attr(not(feature = "no-cfi"), cfi_impl_fn)] pub fn new( env: &mut DpeEnv, support: Support, @@ -71,10 +78,14 @@ impl DpeInstance { if dpe.support.auto_init() { let locality = env.platform.get_auto_init_locality()?; InitCtxCmd::new_use_default().execute(&mut dpe, env, locality)?; + } else { + #[cfg(not(feature = "no-cfi"))] + cfi_assert!(!dpe.support.auto_init()); } Ok(dpe) } + #[cfg_attr(not(feature = "no-cfi"), cfi_impl_fn)] pub fn new_auto_init( env: &mut DpeEnv, support: Support, @@ -85,6 +96,9 @@ impl DpeInstance { // auto-init must be supported to add an auto init measurement if !updated_support.auto_init() { return Err(DpeErrorCode::ArgumentNotSupported); + } else { + #[cfg(not(feature = "no-cfi"))] + cfi_assert!(updated_support.auto_init()); } let mut dpe = Self::new(env, updated_support)?; @@ -127,6 +141,7 @@ impl DpeInstance { /// * `locality` - which hardware locality is making the request /// * `cmd` - serialized command /// * `crypto` - Crypto interface + #[cfg_attr(not(feature = "no-cfi"), cfi_impl_fn)] pub fn execute_serialized_command( &mut self, env: &mut DpeEnv, @@ -134,7 +149,7 @@ impl DpeInstance { cmd: &[u8], ) -> Result { let command = Command::deserialize(cmd)?; - let resp = match command { + let resp = match cfi_launder(command) { Command::GetProfile => Ok(Response::GetProfile(self.get_profile(&mut env.platform)?)), Command::InitCtx(cmd) => cmd.execute(self, env, locality), Command::DeriveContext(cmd) => cmd.execute(self, env, locality), @@ -220,7 +235,7 @@ impl DpeInstance { if idx >= self.contexts.len() { return Err(DpeErrorCode::InternalError); } - descendants |= self.get_descendants(&self.contexts[idx])?; + descendants |= cfi_launder(self.get_descendants(&self.contexts[idx])?); } Ok(descendants) } @@ -258,8 +273,11 @@ impl DpeInstance { return Err(DpeErrorCode::MaxTcis); } if !self.contexts[idx].handle.is_default() { - self.contexts[idx].handle = self.generate_new_handle(env)? - }; + self.contexts[idx].handle = self.generate_new_handle(env)?; + } else { + #[cfg(not(feature = "no-cfi"))] + cfi_assert!(self.contexts[idx].handle.is_default()); + } Ok(()) } @@ -268,6 +286,7 @@ impl DpeInstance { /// derivation for the context at `start_idx`. /// /// Returns the number of TCIs written to `nodes` + #[cfg_attr(not(feature = "no-cfi"), cfi_impl_fn)] pub(crate) fn get_tcb_nodes( &self, start_idx: usize, @@ -291,6 +310,7 @@ impl DpeInstance { Ok(out_idx) } + #[cfg_attr(not(feature = "no-cfi"), cfi_impl_fn)] pub(crate) fn add_tci_measurement( &self, env: &mut DpeEnv, @@ -304,6 +324,12 @@ impl DpeInstance { if context.locality != locality { return Err(DpeErrorCode::InvalidLocality); } + cfg_if! { + if #[cfg(not(feature = "no-cfi"))] { + cfi_assert_eq(context.state, ContextState::Active); + cfi_assert_eq(context.locality, locality); + } + } // Derive the new TCI as HASH(TCI_CUMULATIVE || INPUT_DATA). let mut hasher = env.crypto.hash_initialize(DPE_PROFILE.alg_len())?; @@ -349,6 +375,7 @@ impl DpeInstance { /// # Arguments /// /// * `start_idx` - index of the leaf context + #[cfg_attr(not(feature = "no-cfi"), cfi_impl_fn)] pub(crate) fn compute_measurement_hash( &mut self, env: &mut DpeEnv, @@ -373,14 +400,14 @@ impl DpeInstance { } // Add internal input info to hash - if uses_internal_input_info { + if cfi_launder(uses_internal_input_info) { let mut internal_input_info = [0u8; INTERNAL_INPUT_INFO_SIZE]; self.serialize_internal_input_info(&mut env.platform, &mut internal_input_info)?; hasher.update(&internal_input_info[..INTERNAL_INPUT_INFO_SIZE])?; } // Add internal input dice to hash - if uses_internal_input_dice { + if cfi_launder(uses_internal_input_dice) { let mut offset = 0; let mut cert_chunk = [0u8; MAX_CHUNK_SIZE]; while let Ok(len) = @@ -443,6 +470,7 @@ pub mod tests { use crate::response::NewHandleResp; use crate::support::test::SUPPORT; use crate::{commands::CommandHdr, CURRENT_PROFILE_MAJOR_VERSION}; + use caliptra_cfi_lib_git::CfiCounter; use crypto::OpensslCrypto; use platform::default::{DefaultPlatform, AUTO_INIT_LOCALITY, TEST_CERT_CHAIN}; use zerocopy::AsBytes; @@ -465,6 +493,7 @@ pub mod tests { #[test] fn test_execute_serialized_command() { + CfiCounter::reset_for_test(); let mut env = DpeEnv:: { crypto: OpensslCrypto::new(), platform: DefaultPlatform, @@ -503,6 +532,7 @@ pub mod tests { #[test] fn test_get_profile() { + CfiCounter::reset_for_test(); let mut env = DpeEnv:: { crypto: OpensslCrypto::new(), platform: DefaultPlatform, @@ -515,6 +545,7 @@ pub mod tests { #[test] fn test_get_active_context_index() { + CfiCounter::reset_for_test(); let mut env = DpeEnv:: { crypto: OpensslCrypto::new(), platform: DefaultPlatform, @@ -552,6 +583,7 @@ pub mod tests { #[test] fn test_add_tci_measurement() { + CfiCounter::reset_for_test(); let mut env = DpeEnv:: { crypto: OpensslCrypto::new(), platform: DefaultPlatform, @@ -603,6 +635,7 @@ pub mod tests { #[test] fn test_get_descendants() { + CfiCounter::reset_for_test(); let mut env = DpeEnv:: { crypto: OpensslCrypto::new(), platform: DefaultPlatform, @@ -658,6 +691,7 @@ pub mod tests { #[test] fn test_derive_cdi() { + CfiCounter::reset_for_test(); let mut env = DpeEnv:: { crypto: OpensslCrypto::new(), platform: DefaultPlatform, @@ -713,6 +747,7 @@ pub mod tests { #[test] fn test_hash_internal_input_info() { + CfiCounter::reset_for_test(); let mut env = DpeEnv:: { crypto: OpensslCrypto::new(), platform: DefaultPlatform, @@ -769,6 +804,7 @@ pub mod tests { #[test] fn test_hash_internal_input_dice() { + CfiCounter::reset_for_test(); let mut env = DpeEnv:: { crypto: OpensslCrypto::new(), platform: DefaultPlatform, @@ -821,6 +857,7 @@ pub mod tests { #[test] fn test_new_auto_init() { + CfiCounter::reset_for_test(); let mut env = DpeEnv:: { crypto: OpensslCrypto::new(), platform: DefaultPlatform, diff --git a/dpe/src/validation.rs b/dpe/src/validation.rs index b89e970a..3f7ea4a8 100644 --- a/dpe/src/validation.rs +++ b/dpe/src/validation.rs @@ -8,6 +8,15 @@ use crate::{ DpeInstance, MAX_HANDLES, }; +#[cfg(not(feature = "no-cfi"))] +use caliptra_cfi_derive_git::cfi_impl_fn; +use caliptra_cfi_lib_git::cfi_launder; +#[cfg(not(feature = "no-cfi"))] +use caliptra_cfi_lib_git::{ + cfi_assert, cfi_assert_eq, cfi_assert_le, cfi_assert_lt, cfi_assert_ne, +}; +use cfg_if::cfg_if; + #[derive(Debug, PartialEq, Eq, Clone, Copy)] #[repr(u16)] /// It is possible that there are multiple issues with the DPE state. At most one will be found. @@ -53,37 +62,98 @@ pub struct DpeValidator<'a> { impl<'a> DpeValidator<'a> { /// Validates that the shape of the DPE instance is well-formed and that /// there is no illegal state present within the DPE. + #[cfg_attr(not(feature = "no-cfi"), cfi_impl_fn)] pub fn validate_dpe(&self) -> Result<(), DpeErrorCode> { - self.validate_dpe_state() - .map_err(DpeErrorCode::Validation)?; - self.validate_context_forest() - .map_err(DpeErrorCode::Validation) + let dpe_state_validation = self.validate_dpe_state().map_err(DpeErrorCode::Validation); + if cfi_launder(dpe_state_validation.is_ok()) { + #[cfg(not(feature = "no-cfi"))] + cfi_assert!(dpe_state_validation.is_ok()); + } else { + #[cfg(not(feature = "no-cfi"))] + cfi_assert!(dpe_state_validation.is_err()); + } + dpe_state_validation?; + + let context_forest_validation = self + .validate_context_forest() + .map_err(DpeErrorCode::Validation); + if cfi_launder(context_forest_validation.is_ok()) { + #[cfg(not(feature = "no-cfi"))] + cfi_assert!(context_forest_validation.is_ok()); + } else { + #[cfg(not(feature = "no-cfi"))] + cfi_assert!(context_forest_validation.is_err()); + } + context_forest_validation } /// Returns an error if there is any illegal state or inconsistencies /// present within the DPE instance. + #[cfg_attr(not(feature = "no-cfi"), cfi_impl_fn)] fn validate_dpe_state(&self) -> Result<(), ValidationError> { for i in 0..MAX_HANDLES { let context = &self.dpe.contexts[i]; - self.check_support(context)?; + let support_check = self.check_support(context); + if cfi_launder(support_check.is_ok()) { + #[cfg(not(feature = "no-cfi"))] + cfi_assert!(support_check.is_ok()); + } else { + #[cfg(not(feature = "no-cfi"))] + cfi_assert!(support_check.is_err()); + } + support_check?; match context.state { ContextState::Inactive => { - self.validate_inactive_context(context)?; + #[cfg(not(feature = "no-cfi"))] + cfi_assert_eq(context.state, ContextState::Inactive); + let inactive_context_validation = self.validate_inactive_context(context); + if cfi_launder(inactive_context_validation.is_ok()) { + #[cfg(not(feature = "no-cfi"))] + cfi_assert!(inactive_context_validation.is_ok()); + } else { + #[cfg(not(feature = "no-cfi"))] + cfi_assert!(inactive_context_validation.is_err()); + } + inactive_context_validation?; } ContextState::Active => { + #[cfg(not(feature = "no-cfi"))] + cfi_assert_eq(context.state, ContextState::Active); // has_initialized must be true if there is a normal, active context if context.context_type == ContextType::Normal && !self.dpe.has_initialized() { return Err(ValidationError::DpeNotMarkedInitialized); } - self.check_children_and_parent(i)?; + let children_and_parent_check = self.check_children_and_parent(i); + if cfi_launder(children_and_parent_check.is_ok()) { + #[cfg(not(feature = "no-cfi"))] + cfi_assert!(children_and_parent_check.is_ok()); + } else { + #[cfg(not(feature = "no-cfi"))] + cfi_assert!(children_and_parent_check.is_err()); + } + children_and_parent_check?; } ContextState::Retired => { - self.check_children_and_parent(i)?; + #[cfg(not(feature = "no-cfi"))] + cfi_assert_eq(context.state, ContextState::Retired); + let children_and_parent_check = self.check_children_and_parent(i); + if cfi_launder(children_and_parent_check.is_ok()) { + #[cfg(not(feature = "no-cfi"))] + cfi_assert!(children_and_parent_check.is_ok()); + } else { + #[cfg(not(feature = "no-cfi"))] + cfi_assert!(children_and_parent_check.is_err()); + } + children_and_parent_check?; // retired contexts must have at least one child context - if flags_iter(context.children, MAX_HANDLES).count() == 0 { + let child_context_count = flags_iter(context.children, MAX_HANDLES).count(); + if cfi_launder(child_context_count) == 0 { return Err(ValidationError::DanglingRetiredContext); + } else { + #[cfg(not(feature = "no-cfi"))] + cfi_assert_ne(child_context_count, 0); } } _ => { @@ -100,14 +170,30 @@ impl<'a> DpeValidator<'a> { if context.locality != context.tci.locality { return Err(ValidationError::LocalityMismatch); } + + cfg_if! { + if #[cfg(not(feature = "no-cfi"))] { + cfi_assert!(context.context_type == ContextType::Normal || context.context_type == ContextType::Simulation); + cfi_assert_eq(context.locality, context.tci.locality); + } + } } - self.check_context_handles_per_locality()?; + let context_handles_per_locality_check = self.check_context_handles_per_locality(); + if cfi_launder(context_handles_per_locality_check.is_ok()) { + #[cfg(not(feature = "no-cfi"))] + cfi_assert!(context_handles_per_locality_check.is_ok()); + } else { + #[cfg(not(feature = "no-cfi"))] + cfi_assert!(context_handles_per_locality_check.is_err()); + } + context_handles_per_locality_check?; Ok(()) } /// Checks that the context fields do not violate supported flags + #[cfg_attr(not(feature = "no-cfi"), cfi_impl_fn)] fn check_support(&self, context: &Context) -> Result<(), ValidationError> { if !self.dpe.support.simulation() && context.context_type == ContextType::Simulation { return Err(ValidationError::SimulationNotSupported); @@ -118,6 +204,13 @@ impl<'a> DpeValidator<'a> { if !self.dpe.support.internal_info() && context.uses_internal_input_info() { return Err(ValidationError::InternalInfoNotSupported); } + cfg_if! { + if #[cfg(not(feature = "no-cfi"))] { + cfi_assert!(self.dpe.support.simulation() || context.context_type != ContextType::Simulation); + cfi_assert!(self.dpe.support.internal_dice() || !context.uses_internal_input_dice()); + cfi_assert!(self.dpe.support.internal_info() || !context.uses_internal_input_info()); + } + } // initialized contexts will always have parent = Context::ROOT_INDEX and their allow_ca and allow_x509 // fields will always be true regardless of support if context.parent_idx != Context::ROOT_INDEX { @@ -127,11 +220,21 @@ impl<'a> DpeValidator<'a> { if !self.dpe.support.x509() && context.allow_x509() { return Err(ValidationError::AllowX509NotSupported); } + cfg_if! { + if #[cfg(not(feature = "no-cfi"))] { + cfi_assert!(self.dpe.support.csr() || !context.allow_ca()); + cfi_assert!(self.dpe.support.x509() || !context.allow_x509()); + } + } + } else { + #[cfg(not(feature = "no-cfi"))] + cfi_assert_eq(context.parent_idx, Context::ROOT_INDEX); } Ok(()) } /// Checks that the fields of an inactive context are all default + #[cfg_attr(not(feature = "no-cfi"), cfi_impl_fn)] fn validate_inactive_context(&self, context: &Context) -> Result<(), ValidationError> { if context.parent_idx != Context::ROOT_INDEX { Err(ValidationError::InactiveContextInvalidParent) @@ -146,11 +249,23 @@ impl<'a> DpeValidator<'a> { { Err(ValidationError::InactiveContextWithFlagSet) } else { + cfg_if! { + if #[cfg(not(feature = "no-cfi"))] { + cfi_assert_eq(context.parent_idx, Context::ROOT_INDEX); + cfi_assert_eq(context.children, 0); + cfi_assert_eq(context.tci, TciNodeData::default()); + cfi_assert!(!context.uses_internal_input_dice()); + cfi_assert!(!context.allow_ca()); + cfi_assert!(!context.allow_x509()); + cfi_assert!(!context.uses_internal_input_info()); + } + } Ok(()) } } /// Checks that children and parent indices of a context are valid + #[cfg_attr(not(feature = "no-cfi"), cfi_impl_fn)] fn check_children_and_parent(&self, idx: usize) -> Result<(), ValidationError> { let context = &self.dpe.contexts[idx]; // Check if parent does not exist @@ -178,12 +293,20 @@ impl<'a> DpeValidator<'a> { if self.dpe.contexts[child].parent_idx as usize != idx { return Err(ValidationError::ParentChildLinksCorrupted); } + cfg_if! { + if #[cfg(not(feature = "no-cfi"))] { + cfi_assert_lt(child, MAX_HANDLES); + cfi_assert_ne(self.dpe.contexts[child].state, ContextState::Inactive); + cfi_assert_eq(self.dpe.contexts[child].parent_idx as usize, idx); + } + } } Ok(()) } /// Checks if there are multiple active default contexts or a mix of default /// and non-default contexts within the same locality. + #[cfg_attr(not(feature = "no-cfi"), cfi_impl_fn)] fn check_context_handles_per_locality(&self) -> Result<(), ValidationError> { for locality in self.dpe.contexts.iter().map(|context| context.locality) { let mut default_count = 0; @@ -203,6 +326,12 @@ impl<'a> DpeValidator<'a> { return Err(ValidationError::MixedContextLocality); } } + cfg_if! { + if #[cfg(not(feature = "no-cfi"))] { + cfi_assert_le(default_count, 1); + cfi_assert!(!(default_count > 0 && non_default_count > 0)); + } + } } Ok(()) @@ -210,6 +339,7 @@ impl<'a> DpeValidator<'a> { /// Determines if the context array represents a valid collection of disjoint /// directed connnected acyclic graphs (forest) using depth-first search. + #[cfg_attr(not(feature = "no-cfi"), cfi_impl_fn)] fn validate_context_forest(&self) -> Result<(), ValidationError> { let mut seen = [false; MAX_HANDLES]; let mut in_degree = [0; MAX_HANDLES]; @@ -228,6 +358,9 @@ impl<'a> DpeValidator<'a> { // all nodes must have only one parent if node_in_degree > 1 { return Err(ValidationError::ChildWithMultipleParents); + } else { + #[cfg(not(feature = "no-cfi"))] + cfi_assert_le(node_in_degree, 1); } } @@ -239,24 +372,39 @@ impl<'a> DpeValidator<'a> { if context_type == ContextType::Normal { normal_tree_count += 1; } - self.detect_invalid_subtree(i, &mut seen, context_type)?; + let invalid_subtree_check = self.detect_invalid_subtree(i, &mut seen, context_type); + if cfi_launder(invalid_subtree_check.is_ok()) { + #[cfg(not(feature = "no-cfi"))] + cfi_assert!(invalid_subtree_check.is_ok()); + } else { + #[cfg(not(feature = "no-cfi"))] + cfi_assert!(invalid_subtree_check.is_err()); + } + invalid_subtree_check?; } } // there can be at most one tree of contexts with ContextType::Normal if normal_tree_count > 1 { return Err(ValidationError::MultipleNormalConnectedComponents); + } else { + #[cfg(not(feature = "no-cfi"))] + cfi_assert_le(normal_tree_count, 1); } // if any node is undiscovered the graph must have a simple cycle for (context, node_visited) in self.dpe.contexts.iter().zip(seen) { if context.state != ContextState::Inactive && !node_visited { return Err(ValidationError::CyclesInTree); + } else { + #[cfg(not(feature = "no-cfi"))] + cfi_assert!(context.state == ContextState::Inactive || node_visited); } } Ok(()) } + #[cfg_attr(not(feature = "no-cfi"), cfi_impl_fn)] fn detect_invalid_subtree( &self, curr_idx: usize, @@ -274,10 +422,26 @@ impl<'a> DpeValidator<'a> { if self.dpe.contexts[curr_idx].context_type != context_type { return Err(ValidationError::MixedContextTypeConnectedComponents); } + cfg_if! { + if #[cfg(not(feature = "no-cfi"))] { + cfi_assert_le(curr_idx, MAX_HANDLES); + cfi_assert_ne(self.dpe.contexts[curr_idx].state, ContextState::Inactive); + cfi_assert!(!seen[curr_idx]); + cfi_assert_eq(self.dpe.contexts[curr_idx].context_type, context_type); + } + } seen[curr_idx] = true; // dfs on all child nodes for child_idx in flags_iter(self.dpe.contexts[curr_idx].children, MAX_HANDLES) { - self.detect_invalid_subtree(child_idx, seen, context_type)?; + let invalid_subtree_check = self.detect_invalid_subtree(child_idx, seen, context_type); + if cfi_launder(invalid_subtree_check.is_ok()) { + #[cfg(not(feature = "no-cfi"))] + cfi_assert!(invalid_subtree_check.is_ok()); + } else { + #[cfg(not(feature = "no-cfi"))] + cfi_assert!(invalid_subtree_check.is_err()); + } + invalid_subtree_check?; } Ok(()) } @@ -285,6 +449,7 @@ impl<'a> DpeValidator<'a> { #[cfg(test)] pub mod tests { + use caliptra_cfi_lib_git::CfiCounter; use crypto::OpensslCrypto; use platform::default::DefaultPlatform; @@ -299,6 +464,7 @@ pub mod tests { #[test] fn test_validate_context_forest() { + CfiCounter::reset_for_test(); let mut env = DpeEnv:: { crypto: OpensslCrypto::new(), platform: DefaultPlatform, @@ -361,6 +527,7 @@ pub mod tests { #[test] fn test_support_validation() { + CfiCounter::reset_for_test(); let mut env = DpeEnv:: { crypto: OpensslCrypto::new(), platform: DefaultPlatform, @@ -412,6 +579,7 @@ pub mod tests { #[test] fn test_context_specific_validation() { + CfiCounter::reset_for_test(); let mut env = DpeEnv:: { crypto: OpensslCrypto::new(), platform: DefaultPlatform, @@ -536,6 +704,7 @@ pub mod tests { #[test] fn test_contexts_within_same_locality_validation() { + CfiCounter::reset_for_test(); let mut env = DpeEnv:: { crypto: OpensslCrypto::new(), platform: DefaultPlatform, diff --git a/simulator/Cargo.toml b/simulator/Cargo.toml index 1d3fb2a1..abab633e 100644 --- a/simulator/Cargo.toml +++ b/simulator/Cargo.toml @@ -20,6 +20,6 @@ openssl = {version="0.10.57", optional = true} clap = { version = "4.1.8", features = ["derive"] } log = "0.4.17" env_logger = "0.10.0" -dpe = { path = "../dpe", default-features = false } +dpe = { path = "../dpe", default-features = false, features = ["no-cfi"] } crypto = { path = "../crypto", default-features = false } platform = { path = "../platform", default-features = false} diff --git a/tools/Cargo.toml b/tools/Cargo.toml index 30829fae..05bb0d32 100644 --- a/tools/Cargo.toml +++ b/tools/Cargo.toml @@ -17,7 +17,7 @@ dpe_profile_p384_sha384 = [ ] [dependencies] -dpe = {path = "../dpe", default-features = false} +dpe = {path = "../dpe", default-features = false, features = ["no-cfi"]} crypto = {path = "../crypto", default-features = false, features = ["deterministic_rand", "openssl"]} pem = "2" platform = {path = "../platform", default-features = false, features = ["openssl"]}