From f6d28a6eaeb81516e51e88134ad46e23d9cf094c Mon Sep 17 00:00:00 2001 From: Nick Quarton <139178705+nquarton@users.noreply.github.com> Date: Thu, 27 Jun 2024 11:18:54 -0700 Subject: [PATCH] Fips test suite tests --- Cargo.lock | 6 + builder/src/firmware.rs | 13 +- drivers/src/ecc384.rs | 30 + drivers/src/fips_test_hooks.rs | 64 +- drivers/src/hmac384.rs | 9 + drivers/src/hmac384_kdf.rs | 5 + drivers/src/lms.rs | 7 +- drivers/src/sha1.rs | 15 + drivers/src/sha256.rs | 13 + drivers/src/sha2_512_384acc.rs | 26 + drivers/src/sha384.rs | 7 +- error/src/lib.rs | 4 + image/verify/Cargo.toml | 1 + image/verify/src/verifier.rs | 80 + rom/dev/Cargo.toml | 2 +- rom/dev/README.md | 2 +- rom/dev/src/flow/cold_reset/fw_processor.rs | 7 + .../rom_integration_tests/test_fips_hooks.rs | 4 +- runtime/Cargo.toml | 1 + runtime/src/fips.rs | 8 + test/Cargo.toml | 6 + test/tests/fips_test_suite/common.rs | 135 +- test/tests/fips_test_suite/fw_load.rs | 1439 +++++++++++++++++ test/tests/fips_test_suite/main.rs | 2 + .../fips_test_suite/security_parameters.rs | 240 +++ test/tests/fips_test_suite/self_tests.rs | 526 +++++- test/tests/fips_test_suite/services.rs | 92 +- ureg/src/lib.rs | 2 +- 28 files changed, 2646 insertions(+), 100 deletions(-) create mode 100755 test/tests/fips_test_suite/fw_load.rs create mode 100755 test/tests/fips_test_suite/security_parameters.rs diff --git a/Cargo.lock b/Cargo.lock index 4460b2f4b7..312c143337 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -808,7 +808,12 @@ dependencies = [ "caliptra-emu-cpu", "caliptra-hw-model", "caliptra-hw-model-types", + "caliptra-image-crypto", + "caliptra-image-elf", + "caliptra-image-fake-keys", + "caliptra-image-gen", "caliptra-image-types", + "caliptra-image-verify", "caliptra-runtime", "caliptra_common", "dpe", @@ -816,6 +821,7 @@ dependencies = [ "openssl", "rand", "regex", + "ureg", "zerocopy", ] diff --git a/builder/src/firmware.rs b/builder/src/firmware.rs index 7de4f72a13..56cf61afa4 100644 --- a/builder/src/firmware.rs +++ b/builder/src/firmware.rs @@ -33,10 +33,10 @@ pub const ROM_FAKE_WITH_UART: FwId = FwId { features: &["emu", "fake-rom"], }; -pub const ROM_WITH_UART_FIPS_TEST_HOOKS: FwId = FwId { +pub const ROM_WITH_FIPS_TEST_HOOKS: FwId = FwId { crate_name: "caliptra-rom", bin_name: "caliptra-rom", - features: &["emu", "fips-test-hooks"], + features: &["fips-test-hooks"], }; pub const FMC_WITH_UART: FwId = FwId { @@ -63,6 +63,12 @@ pub const APP_WITH_UART: FwId = FwId { features: &["emu", "fips_self_test"], }; +pub const APP_WITH_UART_FIPS_TEST_HOOKS: FwId = FwId { + crate_name: "caliptra-runtime", + bin_name: "caliptra-runtime", + features: &["emu", "fips_self_test", "fips-test-hooks"], +}; + pub const APP_WITH_UART_FPGA: FwId = FwId { crate_name: "caliptra-runtime", bin_name: "caliptra-runtime", @@ -374,11 +380,12 @@ pub const REGISTERED_FW: &[&FwId] = &[ &ROM, &ROM_WITH_UART, &ROM_FAKE_WITH_UART, - &ROM_WITH_UART_FIPS_TEST_HOOKS, + &ROM_WITH_FIPS_TEST_HOOKS, &FMC_WITH_UART, &FMC_FAKE_WITH_UART, &APP, &APP_WITH_UART, + &APP_WITH_UART_FIPS_TEST_HOOKS, &APP_WITH_UART_FPGA, &caliptra_builder_tests::FWID, &hw_model_tests::MAILBOX_RESPONDER, diff --git a/drivers/src/ecc384.rs b/drivers/src/ecc384.rs index 1333c0ad1c..48bc925ee1 100644 --- a/drivers/src/ecc384.rs +++ b/drivers/src/ecc384.rs @@ -289,6 +289,15 @@ impl Ecc384 { // Pairwise consistency check. let digest = Array4x12::new([0u32; 12]); + + #[cfg(feature = "fips-test-hooks")] + let pub_key = unsafe { + crate::FipsTestHook::corrupt_data_if_hook_set( + crate::FipsTestHook::ECC384_PAIRWISE_CONSISTENCY_ERROR, + &pub_key, + ) + }; + match self.sign(&priv_key.into(), &pub_key, &digest, trng) { Ok(mut sig) => sig.zeroize(), Err(_) => { @@ -405,6 +414,13 @@ impl Ecc384 { data: &Ecc384Scalar, trng: &mut Trng, ) -> CaliptraResult { + #[cfg(feature = "fips-test-hooks")] + unsafe { + crate::FipsTestHook::error_if_hook_set( + crate::FipsTestHook::ECC384_SIGNATURE_GENERATE_FAILURE, + )? + } + let mut sig_result = self.sign_internal(priv_key, data, trng); let sig = okmutref(&mut sig_result)?; @@ -413,6 +429,15 @@ impl Ecc384 { // Not using standard error flow here for increased CFI safety // An error here will end up reporting the CFI assert failure caliptra_cfi_lib::cfi_assert_eq_12_words(&r.0, &sig.r.0); + + #[cfg(feature = "fips-test-hooks")] + let sig_result = unsafe { + crate::FipsTestHook::corrupt_data_if_hook_set( + crate::FipsTestHook::ECC384_CORRUPT_SIGNATURE, + &sig_result, + ) + }; + sig_result } @@ -473,6 +498,11 @@ impl Ecc384 { digest: &Ecc384Scalar, signature: &Ecc384Signature, ) -> CaliptraResult> { + #[cfg(feature = "fips-test-hooks")] + unsafe { + crate::FipsTestHook::error_if_hook_set(crate::FipsTestHook::ECC384_VERIFY_FAILURE)? + } + // If R or S are not in the range [1, N-1], signature check must fail if !Self::scalar_range_check(&signature.r) || !Self::scalar_range_check(&signature.s) { return Err(CaliptraError::DRIVER_ECC384_SCALAR_RANGE_CHECK_FAILED); diff --git a/drivers/src/fips_test_hooks.rs b/drivers/src/fips_test_hooks.rs index 66f964fd7c..0a02241027 100755 --- a/drivers/src/fips_test_hooks.rs +++ b/drivers/src/fips_test_hooks.rs @@ -1,5 +1,6 @@ // Licensed under the Apache-2.0 license +use caliptra_error::CaliptraResult; use caliptra_registers::soc_ifc::SocIfcReg; pub struct FipsTestHook; @@ -11,8 +12,38 @@ impl FipsTestHook { // Set by external test pub const CONTINUE: u8 = 0x10; pub const HALT_SELF_TESTS: u8 = 0x21; - pub const SHA384_ERROR: u8 = 0x22; - pub const LMS_ERROR: u8 = 0x23; + pub const SHA1_CORRUPT_DIGEST: u8 = 0x22; + pub const SHA256_CORRUPT_DIGEST: u8 = 0x23; + pub const SHA384_CORRUPT_DIGEST: u8 = 0x24; + pub const SHA2_512_384_ACC_CORRUPT_DIGEST_512: u8 = 0x25; + pub const ECC384_CORRUPT_SIGNATURE: u8 = 0x26; + pub const HMAC384_CORRUPT_TAG: u8 = 0x27; + pub const LMS_CORRUPT_INPUT: u8 = 0x28; + pub const ECC384_PAIRWISE_CONSISTENCY_ERROR: u8 = 0x29; + pub const HALT_FW_LOAD: u8 = 0x2A; + pub const HALT_SHUTDOWN_RT: u8 = 0x2B; + + pub const SHA1_DIGEST_FAILURE: u8 = 0x40; + pub const SHA256_DIGEST_FAILURE: u8 = 0x41; + pub const SHA384_DIGEST_FAILURE: u8 = 0x42; + pub const SHA2_512_384_ACC_DIGEST_512_FAILURE: u8 = 0x43; + pub const SHA2_512_384_ACC_START_OP_FAILURE: u8 = 0x44; + pub const ECC384_SIGNATURE_GENERATE_FAILURE: u8 = 0x45; + pub const ECC384_VERIFY_FAILURE: u8 = 0x46; + pub const HMAC384_FAILURE: u8 = 0x47; + pub const LMS_VERIFY_FAILURE: u8 = 0x48; + + // FW Load Errors + pub const FW_LOAD_VENDOR_PUB_KEY_DIGEST_FAILURE: u8 = 0x50; + pub const FW_LOAD_OWNER_PUB_KEY_DIGEST_FAILURE: u8 = 0x51; + pub const FW_LOAD_HEADER_DIGEST_FAILURE: u8 = 0x52; + pub const FW_LOAD_VENDOR_ECC_VERIFY_FAILURE: u8 = 0x53; + pub const FW_LOAD_OWNER_ECC_VERIFY_FAILURE: u8 = 0x54; + pub const FW_LOAD_OWNER_TOC_DIGEST_FAILURE: u8 = 0x55; + pub const FW_LOAD_FMC_DIGEST_FAILURE: u8 = 0x56; + pub const FW_LOAD_RUNTIME_DIGEST_FAILURE: u8 = 0x57; + pub const FW_LOAD_VENDOR_LMS_VERIFY_FAILURE: u8 = 0x58; + pub const FW_LOAD_OWNER_LMS_VERIFY_FAILURE: u8 = 0x59; /// # Safety /// @@ -50,6 +81,35 @@ impl FipsTestHook { *data } + + /// # Safety + /// + /// This function enables a different test hook to allow for basic state machines + /// (Only when the hook_cmd matches the value from get_fips_test_hook_code) + pub unsafe fn update_hook_cmd_if_hook_set(hook_cmd: u8, new_hook_cmd: u8) { + if get_fips_test_hook_code() == hook_cmd { + set_fips_test_hook_code(new_hook_cmd); + } + } + + /// # Safety + /// + /// This function calls other unsafe functions to check the test hook code + pub unsafe fn hook_cmd_is_set(hook_cmd: u8) -> bool { + get_fips_test_hook_code() == hook_cmd + } + + /// # Safety + /// + /// This function checks the current hook code and returns the + /// FIPS_HOOKS_INJECTED_ERROR if enabled + pub unsafe fn error_if_hook_set(hook_cmd: u8) -> CaliptraResult<()> { + if get_fips_test_hook_code() == hook_cmd { + Err(caliptra_error::CaliptraError::FIPS_HOOKS_INJECTED_ERROR) + } else { + Ok(()) + } + } } /// # Safety diff --git a/drivers/src/hmac384.rs b/drivers/src/hmac384.rs index 1ff53cc146..809026e19a 100644 --- a/drivers/src/hmac384.rs +++ b/drivers/src/hmac384.rs @@ -544,6 +544,15 @@ impl<'a> Hmac384Op<'a> { // Calculate the hmac of the final block let buf = &self.buf[..self.buf_idx]; + + #[cfg(feature = "fips-test-hooks")] + let buf = unsafe { + crate::FipsTestHook::corrupt_data_if_hook_set( + crate::FipsTestHook::HMAC384_CORRUPT_TAG, + &buf, + ) + }; + self.hmac_engine.hmac_partial_block( buf, self.is_first(), diff --git a/drivers/src/hmac384_kdf.rs b/drivers/src/hmac384_kdf.rs index b11a633cc0..d284282822 100644 --- a/drivers/src/hmac384_kdf.rs +++ b/drivers/src/hmac384_kdf.rs @@ -41,6 +41,11 @@ pub fn hmac384_kdf( trng: &mut Trng, output: Hmac384Tag, ) -> CaliptraResult<()> { + #[cfg(feature = "fips-test-hooks")] + unsafe { + crate::FipsTestHook::error_if_hook_set(crate::FipsTestHook::HMAC384_FAILURE)? + } + let mut hmac_op = hmac.hmac_init(&key, trng, output)?; hmac_op.update(&1_u32.to_be_bytes())?; diff --git a/drivers/src/lms.rs b/drivers/src/lms.rs index 884374336b..7e2a239953 100644 --- a/drivers/src/lms.rs +++ b/drivers/src/lms.rs @@ -447,7 +447,7 @@ impl Lms { #[cfg(feature = "fips-test-hooks")] let input_string = unsafe { crate::FipsTestHook::corrupt_data_if_hook_set( - crate::FipsTestHook::LMS_ERROR, + crate::FipsTestHook::LMS_CORRUPT_INPUT, &input_string, ) }; @@ -514,6 +514,11 @@ impl Lms { lms_public_key: &LmsPublicKey, lms_sig: &LmsSignature, ) -> CaliptraResult> { + #[cfg(feature = "fips-test-hooks")] + unsafe { + crate::FipsTestHook::error_if_hook_set(crate::FipsTestHook::LMS_VERIFY_FAILURE)? + } + if lms_sig.ots.ots_type != lms_public_key.otstype { return Err(CaliptraError::DRIVER_LMS_SIGNATURE_LMOTS_DOESNT_MATCH_PUBKEY_LMOTS); } diff --git a/drivers/src/sha1.rs b/drivers/src/sha1.rs index e47d902d9a..d332329a14 100644 --- a/drivers/src/sha1.rs +++ b/drivers/src/sha1.rs @@ -52,6 +52,11 @@ impl Sha1 { /// * `buf` - Buffer to calculate the digest over #[cfg_attr(not(feature = "no-cfi"), cfi_impl_fn)] pub fn digest(&mut self, buf: &[u8]) -> CaliptraResult { + #[cfg(feature = "fips-test-hooks")] + unsafe { + crate::FipsTestHook::error_if_hook_set(crate::FipsTestHook::SHA1_DIGEST_FAILURE)? + } + // Check if the buffer is not large if buf.len() > SHA1_MAX_DATA_SIZE { return Err(CaliptraError::DRIVER_SHA1_MAX_DATA); @@ -90,6 +95,16 @@ impl Sha1 { } } + #[cfg(feature = "fips-test-hooks")] + { + self.compressor.hash = unsafe { + crate::FipsTestHook::corrupt_data_if_hook_set( + crate::FipsTestHook::SHA1_CORRUPT_DIGEST, + &self.compressor.hash, + ) + }; + } + Ok(self.compressor.hash().into()) } diff --git a/drivers/src/sha256.rs b/drivers/src/sha256.rs index e1e25d31de..6966e50e8f 100644 --- a/drivers/src/sha256.rs +++ b/drivers/src/sha256.rs @@ -83,6 +83,11 @@ impl Sha256Alg for Sha256 { /// /// * `buf` - Buffer to calculate the digest over fn digest(&mut self, buf: &[u8]) -> CaliptraResult { + #[cfg(feature = "fips-test-hooks")] + unsafe { + crate::FipsTestHook::error_if_hook_set(crate::FipsTestHook::SHA256_DIGEST_FAILURE)? + } + // Check if the buffer is not large if buf.len() > SHA256_MAX_DATA_SIZE { return Err(CaliptraError::DRIVER_SHA256_MAX_DATA); @@ -123,6 +128,14 @@ impl Sha256Alg for Sha256 { let digest = Array4x8::read_from_reg(self.sha256.regs().digest()); + #[cfg(feature = "fips-test-hooks")] + let digest = unsafe { + crate::FipsTestHook::corrupt_data_if_hook_set( + crate::FipsTestHook::SHA256_CORRUPT_DIGEST, + &digest, + ) + }; + self.zeroize_internal(); Ok(digest) diff --git a/drivers/src/sha2_512_384acc.rs b/drivers/src/sha2_512_384acc.rs index e3ab097f81..0439bf9178 100644 --- a/drivers/src/sha2_512_384acc.rs +++ b/drivers/src/sha2_512_384acc.rs @@ -61,6 +61,15 @@ impl Sha2_512_384Acc { ) -> CaliptraResult> { let sha_acc = self.sha512_acc.regs(); + #[cfg(feature = "fips-test-hooks")] + if unsafe { + crate::FipsTestHook::hook_cmd_is_set( + crate::FipsTestHook::SHA2_512_384_ACC_START_OP_FAILURE, + ) + } { + return Ok(None); + } + match assumed_lock_state { ShaAccLockState::NotAcquired => { if sha_acc.lock().read().lock() { @@ -247,6 +256,13 @@ impl Sha2_512_384AccOp<'_> { maintain_data_endianess: bool, digest: Sha512Digest, ) -> CaliptraResult<()> { + #[cfg(feature = "fips-test-hooks")] + unsafe { + crate::FipsTestHook::error_if_hook_set( + crate::FipsTestHook::SHA2_512_384_ACC_DIGEST_512_FAILURE, + )? + } + self.digest_generic( dlen, start_address, @@ -258,6 +274,16 @@ impl Sha2_512_384AccOp<'_> { let sha_acc = self.sha512_acc.regs(); *digest = Array4x16::read_from_reg(sha_acc.digest()); + #[cfg(feature = "fips-test-hooks")] + { + *digest = unsafe { + crate::FipsTestHook::corrupt_data_if_hook_set( + crate::FipsTestHook::SHA2_512_384_ACC_CORRUPT_DIGEST_512, + digest, + ) + }; + } + // Zeroize the hardware registers. self.sha512_acc .regs_mut() diff --git a/drivers/src/sha384.rs b/drivers/src/sha384.rs index 9f9eaed2e2..15dc33668b 100644 --- a/drivers/src/sha384.rs +++ b/drivers/src/sha384.rs @@ -63,6 +63,11 @@ impl Sha384 { /// #[cfg_attr(not(feature = "no-cfi"), cfi_impl_fn)] pub fn digest(&mut self, buf: &[u8]) -> CaliptraResult { + #[cfg(feature = "fips-test-hooks")] + unsafe { + crate::FipsTestHook::error_if_hook_set(crate::FipsTestHook::SHA384_DIGEST_FAILURE)? + } + // Check if the buffer is not large if buf.len() > SHA384_MAX_DATA_SIZE { return Err(CaliptraError::DRIVER_SHA384_MAX_DATA_ERR); @@ -105,7 +110,7 @@ impl Sha384 { #[cfg(feature = "fips-test-hooks")] let digest = unsafe { crate::FipsTestHook::corrupt_data_if_hook_set( - crate::FipsTestHook::SHA384_ERROR, + crate::FipsTestHook::SHA384_CORRUPT_DIGEST, &digest, ) }; diff --git a/error/src/lib.rs b/error/src/lib.rs index 169894872e..2286fb9d80 100644 --- a/error/src/lib.rs +++ b/error/src/lib.rs @@ -575,6 +575,10 @@ impl CaliptraError { pub const KAT_LMS_DIGEST_MISMATCH: CaliptraError = CaliptraError::new_const(0x90070002); pub const ROM_INTEGRITY_FAILURE: CaliptraError = CaliptraError::new_const(0x90080001); + + // TODO: What base value is right for this? + // FIPS Hooks + pub const FIPS_HOOKS_INJECTED_ERROR: CaliptraError = CaliptraError::new_const(0x90100000); } impl From for crate::CaliptraError { diff --git a/image/verify/Cargo.toml b/image/verify/Cargo.toml index 9cb7a7a7c4..36a9864e65 100644 --- a/image/verify/Cargo.toml +++ b/image/verify/Cargo.toml @@ -25,3 +25,4 @@ caliptra-cfi-lib = { workspace = true, features = ["cfi-test" ] } default = ["std"] std = ["caliptra-image-types/std"] no-cfi = [] +fips-test-hooks = [] diff --git a/image/verify/src/verifier.rs b/image/verify/src/verifier.rs index e80deef6c9..0b5193eb6e 100644 --- a/image/verify/src/verifier.rs +++ b/image/verify/src/verifier.rs @@ -323,6 +323,14 @@ impl ImageVerifier { let range = ImageManifest::vendor_pub_keys_range(); + #[cfg(feature = "fips-test-hooks")] + unsafe { + caliptra_drivers::FipsTestHook::update_hook_cmd_if_hook_set( + caliptra_drivers::FipsTestHook::FW_LOAD_VENDOR_PUB_KEY_DIGEST_FAILURE, + caliptra_drivers::FipsTestHook::SHA384_DIGEST_FAILURE, + ) + }; + let actual = self .env .sha384_digest(range.start, range.len() as u32) @@ -348,6 +356,14 @@ impl ImageVerifier { ) -> CaliptraResult<(ImageDigest, bool)> { let range = ImageManifest::owner_pub_key_range(); + #[cfg(feature = "fips-test-hooks")] + unsafe { + caliptra_drivers::FipsTestHook::update_hook_cmd_if_hook_set( + caliptra_drivers::FipsTestHook::FW_LOAD_OWNER_PUB_KEY_DIGEST_FAILURE, + caliptra_drivers::FipsTestHook::SHA384_DIGEST_FAILURE, + ) + }; + let actual = self .env .sha384_digest(range.start, range.len() as u32) @@ -391,6 +407,14 @@ impl ImageVerifier { let range = ImageManifest::header_range(); let vendor_header_len = offset_of!(ImageHeader, owner_data); + #[cfg(feature = "fips-test-hooks")] + unsafe { + caliptra_drivers::FipsTestHook::update_hook_cmd_if_hook_set( + caliptra_drivers::FipsTestHook::FW_LOAD_HEADER_DIGEST_FAILURE, + caliptra_drivers::FipsTestHook::SHA384_DIGEST_FAILURE, + ) + }; + // Vendor header digest is calculated up to the owner_data field. let digest_vendor = self .env @@ -466,6 +490,14 @@ impl ImageVerifier { Err(CaliptraError::IMAGE_VERIFIER_ERR_OWNER_ECC_SIGNATURE_INVALID_ARG)?; } + #[cfg(feature = "fips-test-hooks")] + unsafe { + caliptra_drivers::FipsTestHook::update_hook_cmd_if_hook_set( + caliptra_drivers::FipsTestHook::FW_LOAD_OWNER_ECC_VERIFY_FAILURE, + caliptra_drivers::FipsTestHook::ECC384_VERIFY_FAILURE, + ) + }; + let verify_r = self .env .ecc384_verify(digest, pub_key, sig) @@ -498,6 +530,14 @@ impl ImageVerifier { Err(CaliptraError::IMAGE_VERIFIER_ERR_VENDOR_ECC_SIGNATURE_INVALID_ARG)?; } + #[cfg(feature = "fips-test-hooks")] + unsafe { + caliptra_drivers::FipsTestHook::update_hook_cmd_if_hook_set( + caliptra_drivers::FipsTestHook::FW_LOAD_VENDOR_ECC_VERIFY_FAILURE, + caliptra_drivers::FipsTestHook::ECC384_VERIFY_FAILURE, + ) + }; + let verify_r = self .env .ecc384_verify(digest, ecc_pub_key, ecc_sig) @@ -512,6 +552,14 @@ impl ImageVerifier { caliptra_cfi_lib::cfi_assert_eq_12_words(&verify_r.0, &ecc_sig.r); } + #[cfg(feature = "fips-test-hooks")] + unsafe { + caliptra_drivers::FipsTestHook::update_hook_cmd_if_hook_set( + caliptra_drivers::FipsTestHook::FW_LOAD_VENDOR_LMS_VERIFY_FAILURE, + caliptra_drivers::FipsTestHook::LMS_VERIFY_FAILURE, + ) + }; + if cfi_launder(self.env.lms_verify_enabled()) { if let Some(info) = lms_info { let (lms_pub_key, lms_sig) = info; @@ -543,6 +591,14 @@ impl ImageVerifier { lms_pub_key: &ImageLmsPublicKey, lms_sig: &ImageLmsSignature, ) -> CaliptraResult<()> { + #[cfg(feature = "fips-test-hooks")] + unsafe { + caliptra_drivers::FipsTestHook::update_hook_cmd_if_hook_set( + caliptra_drivers::FipsTestHook::FW_LOAD_OWNER_LMS_VERIFY_FAILURE, + caliptra_drivers::FipsTestHook::LMS_VERIFY_FAILURE, + ) + }; + let candidate_key = self .env .lms_verify(digest, lms_pub_key, lms_sig) @@ -577,6 +633,14 @@ impl ImageVerifier { let range = ImageManifest::toc_range(); + #[cfg(feature = "fips-test-hooks")] + unsafe { + caliptra_drivers::FipsTestHook::update_hook_cmd_if_hook_set( + caliptra_drivers::FipsTestHook::FW_LOAD_OWNER_TOC_DIGEST_FAILURE, + caliptra_drivers::FipsTestHook::SHA384_DIGEST_FAILURE, + ) + }; + let actual = self .env .sha384_digest(range.start, range.len() as u32) @@ -675,6 +739,14 @@ impl ImageVerifier { ) -> CaliptraResult<(ImageVerificationExeInfo, ImageSvnLogInfo)> { let range = verify_info.image_range()?; + #[cfg(feature = "fips-test-hooks")] + unsafe { + caliptra_drivers::FipsTestHook::update_hook_cmd_if_hook_set( + caliptra_drivers::FipsTestHook::FW_LOAD_FMC_DIGEST_FAILURE, + caliptra_drivers::FipsTestHook::SHA384_DIGEST_FAILURE, + ) + }; + let actual = self .env .sha384_digest(range.start, range.len() as u32) @@ -760,6 +832,14 @@ impl ImageVerifier { ) -> CaliptraResult<(ImageVerificationExeInfo, ImageSvnLogInfo)> { let range = verify_info.image_range()?; + #[cfg(feature = "fips-test-hooks")] + unsafe { + caliptra_drivers::FipsTestHook::update_hook_cmd_if_hook_set( + caliptra_drivers::FipsTestHook::FW_LOAD_RUNTIME_DIGEST_FAILURE, + caliptra_drivers::FipsTestHook::SHA384_DIGEST_FAILURE, + ) + }; + let actual = self .env .sha384_digest(range.start, range.len() as u32) diff --git a/rom/dev/Cargo.toml b/rom/dev/Cargo.toml index 02ee960b3d..7e1e8f880d 100644 --- a/rom/dev/Cargo.toml +++ b/rom/dev/Cargo.toml @@ -62,7 +62,7 @@ fake-rom = [] no-cfi = ["caliptra-image-verify/no-cfi", "caliptra-drivers/no-cfi"] slow_tests = [] "hw-1.0" = ["caliptra-builder/hw-1.0", "caliptra-drivers/hw-1.0", "caliptra-registers/hw-1.0", "caliptra-hw-model/hw-1.0"] -fips-test-hooks = ["caliptra-drivers/fips-test-hooks"] +fips-test-hooks = ["caliptra-drivers/fips-test-hooks", "caliptra-image-verify/fips-test-hooks"] [[bin]] name = "asm_tests" diff --git a/rom/dev/README.md b/rom/dev/README.md index 0812d45009..a18ec5b06c 100644 --- a/rom/dev/README.md +++ b/rom/dev/README.md @@ -58,7 +58,7 @@ Following are the main FUSE & Architectural Registers used by the Caliptra ROM f | FUSE_RUNTIME_SVN | 128 | Runtime Security Version Number | | FUSE_ANTI_ROLLBACK_DISABLE | 1 | Disable SVN checking for FMC & Runtime when bit is set | | FUSE_IDEVID_CERT_ATTR | 768 | FUSE containing information for generating IDEVID CSR
**Word 0**: X509 Key Id Algorithm (2 bits) 1: SHA1, 2: SHA256, 2: SHA384, 3: Fuse
**Word 1,2,3,4,5**: Subject Key Id
**Words 7,8**: Unique Endpoint ID | -| CPTRA_DBG_MANUF_SERVICE_REG | 16 | Manufacturing Services:
**Bit 0**: IDEVID CSR upload
**Bit 1**: Random Number Generator Unavailable
**Bit 30**: Fake ROM enable in production lifecycle mode
**Bit 31**: Fake ROM image verify enable | +| CPTRA_DBG_MANUF_SERVICE_REG | 16 | Manufacturing Services:
**Bit 0**: IDEVID CSR upload
**Bit 1**: Random Number Generator Unavailable
**Bit 15:8**: FIPS test hook code
**Bit 30**: Fake ROM enable in production lifecycle mode
**Bit 31**: Fake ROM image verify enable | ## Firmware image bundle diff --git a/rom/dev/src/flow/cold_reset/fw_processor.rs b/rom/dev/src/flow/cold_reset/fw_processor.rs index 5d0a2e87ea..cc137ccd78 100644 --- a/rom/dev/src/flow/cold_reset/fw_processor.rs +++ b/rom/dev/src/flow/cold_reset/fw_processor.rs @@ -92,6 +92,13 @@ impl FirmwareProcessor { env.persistent_data.get_mut(), )?; + #[cfg(feature = "fips-test-hooks")] + unsafe { + caliptra_drivers::FipsTestHook::halt_if_hook_set( + caliptra_drivers::FipsTestHook::HALT_FW_LOAD, + ) + }; + // Load the manifest let manifest = Self::load_manifest(&mut env.persistent_data, &mut txn); let manifest = okref(&manifest)?; diff --git a/rom/dev/tests/rom_integration_tests/test_fips_hooks.rs b/rom/dev/tests/rom_integration_tests/test_fips_hooks.rs index 87fca4565d..ee272c8fac 100644 --- a/rom/dev/tests/rom_integration_tests/test_fips_hooks.rs +++ b/rom/dev/tests/rom_integration_tests/test_fips_hooks.rs @@ -1,13 +1,13 @@ // Licensed under the Apache-2.0 license -use caliptra_builder::firmware::{APP_WITH_UART, FMC_WITH_UART, ROM_WITH_UART_FIPS_TEST_HOOKS}; +use caliptra_builder::firmware::{APP_WITH_UART, FMC_WITH_UART, ROM_WITH_FIPS_TEST_HOOKS}; use caliptra_builder::ImageOptions; use caliptra_drivers::CaliptraError; use caliptra_hw_model::{BootParams, HwModel, InitParams}; #[test] fn test_fips_hook_exit() { - let rom = caliptra_builder::build_firmware_rom(&ROM_WITH_UART_FIPS_TEST_HOOKS).unwrap(); + let rom = caliptra_builder::build_firmware_rom(&ROM_WITH_FIPS_TEST_HOOKS).unwrap(); let image_bundle = caliptra_builder::build_and_sign_image( &FMC_WITH_UART, diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index db739ef1c6..3fd32bf24e 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -60,3 +60,4 @@ fips_self_test=[] no-cfi = ["caliptra-image-verify/no-cfi", "caliptra-drivers/no-cfi"] fpga_realtime = ["caliptra-drivers/fpga_realtime"] "hw-1.0" = ["caliptra-builder/hw-1.0", "caliptra-drivers/hw-1.0", "caliptra-registers/hw-1.0", "caliptra-kat/hw-1.0","caliptra-cpu/hw-1.0"] +fips-test-hooks = ["caliptra-drivers/fips-test-hooks"] diff --git a/runtime/src/fips.rs b/runtime/src/fips.rs index d00d07cd89..78e77d7829 100644 --- a/runtime/src/fips.rs +++ b/runtime/src/fips.rs @@ -49,6 +49,14 @@ impl FipsModule { // Lock the SHA Accelerator. Sha2_512_384Acc::lock(); } + + #[cfg(feature = "fips-test-hooks")] + unsafe { + caliptra_drivers::FipsTestHook::halt_if_hook_set( + caliptra_drivers::FipsTestHook::HALT_SHUTDOWN_RT, + ) + }; + env.persistent_data.get_mut().zeroize(); } } diff --git a/test/Cargo.toml b/test/Cargo.toml index 31cff863c2..95f4309e01 100644 --- a/test/Cargo.toml +++ b/test/Cargo.toml @@ -15,7 +15,12 @@ caliptra_common = { workspace = true, default-features = false } caliptra-coverage.workspace = true caliptra-drivers = { workspace = true, features = ["fips-test-hooks"] } caliptra-hw-model-types.workspace = true +caliptra-image-crypto.workspace = true +caliptra-image-elf.workspace = true +caliptra-image-fake-keys.workspace = true +caliptra-image-gen.workspace = true caliptra-image-types.workspace = true +caliptra-image-verify = { workspace = true, default-features = false } caliptra-runtime = { workspace = true, default-features = false } elf.workspace = true openssl.workspace = true @@ -24,6 +29,7 @@ regex.workspace = true zerocopy.workspace = true caliptra-hw-model.workspace = true dpe.workspace = true +ureg.workspace = true [dev-dependencies] caliptra-builder.workspace = true diff --git a/test/tests/fips_test_suite/common.rs b/test/tests/fips_test_suite/common.rs index 3e89d2b0a5..7299436661 100755 --- a/test/tests/fips_test_suite/common.rs +++ b/test/tests/fips_test_suite/common.rs @@ -1,10 +1,11 @@ // Licensed under the Apache-2.0 license use caliptra_builder::firmware::{APP_WITH_UART, FMC_WITH_UART}; -use caliptra_builder::ImageOptions; +use caliptra_builder::{version, ImageOptions}; use caliptra_common::mailbox_api::*; use caliptra_drivers::FipsTestHook; use caliptra_hw_model::{BootParams, DefaultHwModel, HwModel, InitParams, ModelError, ShaAccMode}; +use caliptra_test::swap_word_bytes_inplace; use dpe::{ commands::*, response::{ @@ -57,14 +58,13 @@ pub struct RtExpVals { } const RT_EXP_1_0_0: RtExpVals = RtExpVals { - fmc_version: 0x0, - fw_version: 0x0100_0000, + fmc_version: 0x800, // 1.0.0 + fw_version: 0x0100_0000, // 1.0.0 }; const RT_EXP_CURRENT: RtExpVals = RtExpVals { - // Update expected versions - fmc_version: 0x0, - fw_version: 0x0, + fw_version: 0x0101_0000, // 1.1.0 + ..RT_EXP_1_0_0 }; // === Getter implementations === @@ -124,16 +124,8 @@ impl RtExpVals { // HELPER FUNCTIONS // ================================= -// Generic helper to boot to ROM or runtime -// Builds ROM, if not provided -// HW Model will boot to runtime if image is provided -fn fips_test_init_base( - init_params: Option, - boot_params: Option, - fw_image_override: Option<&[u8]>, -) -> DefaultHwModel { +pub fn fips_test_init_model(init_params: Option) -> DefaultHwModel { // Create params if not provided - let mut boot_params = boot_params.unwrap_or(BootParams::default()); let mut init_params = init_params.unwrap_or(InitParams::default()); // Check that ROM was not provided if the immutable_rom feature is set @@ -159,17 +151,30 @@ fn fips_test_init_base( init_params.rom = &rom; } - // Add fw image override to boot params if provided - if fw_image_override.is_some() { - // Sanity check that the caller functions are written correctly - if boot_params.fw_image.is_some() { - panic!("FIPS_TEST_SUITE BUG: Should never have a fw_image override and a fw_image in boot params") - } - boot_params.fw_image = fw_image_override; - } - // Create the model - caliptra_hw_model::new(init_params, boot_params).unwrap() + caliptra_hw_model::new_unbooted(init_params).unwrap() +} + +fn fips_test_boot(hw: &mut T, boot_params: Option) { + // Create params if not provided + let boot_params = boot_params.unwrap_or(BootParams::default()); + + // Boot + hw.boot(boot_params).unwrap(); +} + +// Generic helper to boot to ROM or runtime +// Builds ROM, if not provided +// HW Model will boot to runtime if image is provided +fn fips_test_init_base( + init_params: Option, + boot_params: Option, +) -> DefaultHwModel { + let mut hw = fips_test_init_model(init_params); + + fips_test_boot(&mut hw, boot_params); + + hw } // Initializes Caliptra @@ -185,7 +190,7 @@ pub fn fips_test_init_to_boot_start( } } - fips_test_init_base(init_params, boot_params, None) + fips_test_init_base(init_params, boot_params) } // Initializes caliptra to "ready_for_fw" @@ -194,14 +199,7 @@ pub fn fips_test_init_to_rom( init_params: Option, boot_params: Option, ) -> DefaultHwModel { - // Check that no fw_image is in boot params - if let Some(ref boot_params) = boot_params { - if boot_params.fw_image.is_some() { - panic!("No FW image should be provided when calling fips_test_init_to_rom") - } - } - - let mut model = fips_test_init_base(init_params, boot_params, None); + let mut model = fips_test_init_base(init_params, boot_params); // Step to ready for FW in ROM model.step_until(|m| m.soc_ifc().cptra_flow_status().read().ready_for_fw()); @@ -215,21 +213,15 @@ pub fn fips_test_init_to_rt( init_params: Option, boot_params: Option, ) -> DefaultHwModel { - let mut build_fw = true; - - if let Some(ref boot_params) = boot_params { - if boot_params.fw_image.is_some() { - build_fw = false; - } - } - - if build_fw { - // If FW was not provided, build it or get it from the specified path - let fw_image = fips_fw_image(); + // Create params if not provided + let mut boot_params = boot_params.unwrap_or(BootParams::default()); - fips_test_init_base(init_params, boot_params, Some(&fw_image)) + if boot_params.fw_image.is_some() { + fips_test_init_base(init_params, Some(boot_params)) } else { - fips_test_init_base(init_params, boot_params, None) + let fw_image = fips_fw_image(); + boot_params.fw_image = Some(&fw_image); + fips_test_init_base(init_params, Some(boot_params)) } // HW model will complete FW upload cmd, nothing to wait for @@ -343,7 +335,11 @@ pub fn fips_fw_image() -> Vec { Err(_) => caliptra_builder::build_and_sign_image( &FMC_WITH_UART, &APP_WITH_UART, - ImageOptions::default(), + ImageOptions { + fmc_version: version::get_fmc_version(), + app_version: version::get_runtime_version(), + ..Default::default() + }, ) .unwrap() .to_bytes() @@ -368,8 +364,29 @@ pub fn contains_some_data(data: &[T]) -> bool { false } -pub fn verify_output_inhibited(hw: &mut T) { - // Check mailbox output is inhibited +pub fn verify_mbox_cmds_fail(hw: &mut T, exp_error_code: u32) { + // Send an arbitrary message + let payload = MailboxReqHeader { + chksum: caliptra_common::checksum::calc_checksum(u32::from(CommandId::FW_INFO), &[]), + }; + + // Make sure we get the right failure + match mbx_send_and_check_resp_hdr::<_, FwInfoResp>( + hw, + u32::from(CommandId::FW_INFO), + payload.as_bytes(), + ) { + Ok(_) => panic!("MBX command should fail at this point"), + Err(act_error) => { + if act_error != ModelError::MailboxCmdFailed(exp_error_code) { + panic!("MBX command received unexpected error {}", act_error) + } + } + } +} + +// Check mailbox output is inhibited +pub fn verify_mbox_output_inhibited(hw: &mut T) { let payload = MailboxReqHeader { chksum: caliptra_common::checksum::calc_checksum(u32::from(CommandId::VERSION), &[]), }; @@ -377,10 +394,12 @@ pub fn verify_output_inhibited(hw: &mut T) { Ok(_) => panic!("Mailbox output is not inhibited"), Err(ModelError::MailboxTimeout) => (), Err(ModelError::UnableToLockMailbox) => (), - Err(_) => panic!("Unexpected error from mailbox_execute"), + Err(e) => panic!("Unexpected error from mailbox_execute {:?}", e), } +} - // Check sha engine output is inhibited (ensure sha engine is locked) +// Check sha engine output is inhibited (ensure sha engine is locked) +pub fn verify_sha_engine_output_inhibited(hw: &mut T) { let message: &[u8] = &[0x0, 0x1, 0x2, 0x3]; match hw.compute_sha512_acc_digest(message, ShaAccMode::Sha384Stream) { Ok(_) => panic!("SHA engine is not locked, output is not inhibited"), @@ -389,6 +408,18 @@ pub fn verify_output_inhibited(hw: &mut T) { } } +// Verify all output is inhibited +pub fn verify_output_inhibited(hw: &mut T) { + verify_mbox_output_inhibited(hw); + verify_sha_engine_output_inhibited(hw); +} + +pub fn bytes_to_be_words_48(buf: &[u8; 48]) -> [u32; 12] { + let mut result: [u32; 12] = zerocopy::transmute!(*buf); + swap_word_bytes_inplace(&mut result); + result +} + pub fn hook_code_read(hw: &mut T) -> u8 { ((hw.soc_ifc().cptra_dbg_manuf_service_reg().read() & HOOK_CODE_MASK) >> HOOK_CODE_OFFSET) as u8 } diff --git a/test/tests/fips_test_suite/fw_load.rs b/test/tests/fips_test_suite/fw_load.rs new file mode 100755 index 0000000000..adc6c1aa8d --- /dev/null +++ b/test/tests/fips_test_suite/fw_load.rs @@ -0,0 +1,1439 @@ +// Licensed under the Apache-2.0 license +use crate::common; + +use caliptra_builder::firmware::{ + APP_WITH_UART, FMC_FAKE_WITH_UART, FMC_WITH_UART, ROM_WITH_FIPS_TEST_HOOKS, +}; +use caliptra_builder::ImageOptions; +use caliptra_common::memory_layout::{ICCM_ORG, ICCM_SIZE}; +use caliptra_drivers::CaliptraError; +use caliptra_drivers::FipsTestHook; +use caliptra_hw_model::{ + BootParams, DeviceLifecycle, Fuses, HwModel, InitParams, ModelError, SecurityState, U4, +}; +use caliptra_image_crypto::OsslCrypto as Crypto; +use caliptra_image_fake_keys::{VENDOR_CONFIG_KEY_0, VENDOR_CONFIG_KEY_1}; +use caliptra_image_gen::{ImageGenerator, ImageGeneratorConfig, ImageGeneratorVendorConfig}; +use caliptra_image_types::{ImageBundle, VENDOR_ECC_KEY_COUNT, VENDOR_LMS_KEY_COUNT}; +use openssl::sha::sha384; + +use common::*; +use zerocopy::AsBytes; + +#[allow(dead_code)] +#[derive(PartialEq, Eq)] +enum HdrDigest { + Update, + Skip, +} + +#[derive(PartialEq, Eq)] +enum TocDigest { + Update, + Skip, +} + +pub fn build_fw_image(image_options: ImageOptions) -> ImageBundle { + caliptra_builder::build_and_sign_image(&FMC_WITH_UART, &APP_WITH_UART, image_options).unwrap() +} + +fn update_manifest(image_bundle: &mut ImageBundle, hdr_digest: HdrDigest, toc_digest: TocDigest) { + let opts = ImageOptions::default(); + let config = ImageGeneratorConfig { + fmc: caliptra_image_elf::ElfExecutable::default(), + runtime: caliptra_image_elf::ElfExecutable::default(), + vendor_config: opts.vendor_config, + owner_config: opts.owner_config, + }; + + let gen = ImageGenerator::new(Crypto::default()); + + // Update TOC digest + if toc_digest == TocDigest::Update { + image_bundle.manifest.header.toc_digest = gen + .toc_digest(&image_bundle.manifest.fmc, &image_bundle.manifest.runtime) + .unwrap(); + } + + if hdr_digest == HdrDigest::Update { + let header_digest_vendor = gen + .header_digest_vendor(&image_bundle.manifest.header) + .unwrap(); + let header_digest_owner = gen + .header_digest_owner(&image_bundle.manifest.header) + .unwrap(); + + // Update preamble + image_bundle.manifest.preamble = gen + .gen_preamble( + &config, + image_bundle.manifest.preamble.vendor_ecc_pub_key_idx, + image_bundle.manifest.preamble.vendor_lms_pub_key_idx, + &header_digest_vendor, + &header_digest_owner, + ) + .unwrap(); + } +} + +// Get a byte array from an image_bundle without any error checking +// Normally, to_bytes will perform some error checking +// We need to bypass this for the sake of these tests +fn image_to_bytes_no_error_check(image_bundle: &ImageBundle) -> Vec { + let mut image = vec![]; + image.extend_from_slice(image_bundle.manifest.as_bytes()); + image.extend_from_slice(&image_bundle.fmc); + image.extend_from_slice(&image_bundle.runtime); + image +} + +// Returns a fuse struct with safe values for boot +// (Mainly needed for manufacturing or production security states) +fn safe_fuses(fw_image: &ImageBundle) -> Fuses { + let gen = ImageGenerator::new(Crypto::default()); + + let vendor_pubkey_digest = gen + .vendor_pubkey_digest(&fw_image.manifest.preamble) + .unwrap(); + + let owner_pubkey_digest = gen + .owner_pubkey_digest(&fw_image.manifest.preamble) + .unwrap(); + + Fuses { + key_manifest_pk_hash: vendor_pubkey_digest, + owner_pk_hash: owner_pubkey_digest, + ..Default::default() + } +} + +// NOTE: These tests are about the image verification which is contained in ROM. +// The version of the FW used in the image bundles within these tests is irrelevant. +// Because of this, we are just building the FW so it's easier to modify components +// of the image bundle instead of using any pre-existing FW binary + +fn fw_load_error_flow(fw_image: Option, fuses: Option, exp_error_code: u32) { + fw_load_error_flow_base(fw_image, None, fuses, None, exp_error_code, None); +} + +fn fw_load_error_flow_with_test_hooks( + fw_image: Option, + fuses: Option, + exp_error_code: u32, + test_hook_cmd: u8, +) { + let rom = caliptra_builder::build_firmware_rom(&ROM_WITH_FIPS_TEST_HOOKS).unwrap(); + fw_load_error_flow_base( + fw_image, + Some(&rom), + fuses, + None, + exp_error_code, + Some((test_hook_cmd as u32) << HOOK_CODE_OFFSET), + ); +} + +fn update_fw_error_flow( + fw_image: Option, + fuses: Option, + update_fw_image: Option, + exp_error_code: u32, +) { + let update_fw_image = update_fw_image.unwrap_or(build_fw_image(ImageOptions::default())); + + fw_load_error_flow_base( + fw_image, + None, + fuses, + Some(update_fw_image), + exp_error_code, + None, + ); +} + +fn fw_load_error_flow_base( + fw_image: Option, + rom: Option<&[u8]>, + fuses: Option, + update_fw_image: Option, + exp_error_code: u32, + initial_dbg_manuf_service_reg: Option, +) { + // Use defaults if not provided + let fuses = fuses.unwrap_or(Fuses::default()); + let fw_image = fw_image.unwrap_or(build_fw_image(ImageOptions::default())); + + // Attempt to load the FW + let mut hw = fips_test_init_to_rom( + Some(InitParams { + security_state: SecurityState::from(fuses.life_cycle as u32), + rom: rom.unwrap_or_default(), + ..Default::default() + }), + Some(BootParams { + fuses, + initial_dbg_manuf_service_reg: initial_dbg_manuf_service_reg.unwrap_or_default(), + ..Default::default() + }), + ); + + // Upload initial FW + let mut fw_load_result = hw.upload_firmware(&image_to_bytes_no_error_check(&fw_image)); + + // Update the FW if specified + match update_fw_image { + None => { + // Verify the correct error was returned from FW load + assert_eq!( + ModelError::MailboxCmdFailed(exp_error_code), + fw_load_result.unwrap_err() + ); + + // Verify we cannot utilize RT FW by sending a message + verify_mbox_cmds_fail(&mut hw, exp_error_code); + + // Verify an undocumented attempt to clear the error fails + hw.soc_ifc().cptra_fw_error_fatal().write(|_| 0); + hw.soc_ifc().cptra_fw_error_non_fatal().write(|_| 0); + verify_mbox_cmds_fail(&mut hw, 0); + + // Clear the error with an approved method - restart Caliptra + // TODO: Reset to the default fuse state - provided fuses may be intended to cause errors + if cfg!(any(feature = "verilator", feature = "fpga_realtime")) { + hw.cold_reset(); + } else { + hw = fips_test_init_model(None) + } + + let clean_fw_image = build_fw_image(ImageOptions::default()); + + hw.boot(BootParams { + fuses: safe_fuses(&clean_fw_image), + ..Default::default() + }) + .unwrap(); + + hw.step_until(|m| m.soc_ifc().cptra_flow_status().read().ready_for_fw()); + + // Verify we can load FW (use clean FW) + hw.upload_firmware(&clean_fw_image.to_bytes().unwrap()) + .unwrap(); + } + Some(update_image) => { + // Verify initial FW load was successful + fw_load_result.unwrap(); + + // Update FW + fw_load_result = hw.upload_firmware(&image_to_bytes_no_error_check(&update_image)); + // Verify the correct error was returned from FW load + assert_eq!( + fw_load_result.unwrap_err(), + ModelError::MailboxCmdFailed(exp_error_code) + ); + + // In the update FW case, the error will be non-fatal and fall back to the previous, good FW + + // Verify we can load FW (use first FW) + hw.upload_firmware(&image_to_bytes_no_error_check(&fw_image)) + .unwrap(); + } + } +} + +#[test] +fn fw_load_error_manifest_marker_mismatch() { + // Generate image + let mut fw_image = build_fw_image(ImageOptions::default()); + // Corrupt manifest marker + fw_image.manifest.marker = 0xDEADBEEF; + + fw_load_error_flow( + Some(fw_image), + None, + CaliptraError::IMAGE_VERIFIER_ERR_MANIFEST_MARKER_MISMATCH.into(), + ); +} + +#[test] +fn fw_load_error_manifest_size_mismatch() { + // Generate image + let mut fw_image = build_fw_image(ImageOptions::default()); + // Change manifest size + fw_image.manifest.size -= 1; + + fw_load_error_flow( + Some(fw_image), + None, + CaliptraError::IMAGE_VERIFIER_ERR_MANIFEST_SIZE_MISMATCH.into(), + ); +} + +#[test] +fn fw_load_error_vendor_pub_key_digest_invalid() { + // Set fuses + let fuses = caliptra_hw_model::Fuses { + life_cycle: DeviceLifecycle::Manufacturing, + key_manifest_pk_hash: [0u32; 12], + ..Default::default() + }; + + fw_load_error_flow( + None, + Some(fuses), + CaliptraError::IMAGE_VERIFIER_ERR_VENDOR_PUB_KEY_DIGEST_INVALID.into(), + ); +} + +#[test] +#[cfg(not(feature = "test_env_immutable_rom"))] +fn fw_load_error_vendor_pub_key_digest_failure() { + // Set fuses + let fuses = caliptra_hw_model::Fuses { + life_cycle: DeviceLifecycle::Manufacturing, + key_manifest_pk_hash: [0xDEADBEEF; 12], + ..Default::default() + }; + + fw_load_error_flow_with_test_hooks( + None, + Some(fuses), + CaliptraError::IMAGE_VERIFIER_ERR_VENDOR_PUB_KEY_DIGEST_FAILURE.into(), + FipsTestHook::FW_LOAD_VENDOR_PUB_KEY_DIGEST_FAILURE, + ); +} + +#[test] +fn fw_load_error_vendor_pub_key_digest_mismatch() { + // Set fuses + let fuses = caliptra_hw_model::Fuses { + life_cycle: DeviceLifecycle::Manufacturing, + key_manifest_pk_hash: [0xDEADBEEF; 12], + ..Default::default() + }; + + fw_load_error_flow( + None, + Some(fuses), + CaliptraError::IMAGE_VERIFIER_ERR_VENDOR_PUB_KEY_DIGEST_MISMATCH.into(), + ); +} + +#[test] +#[cfg(not(feature = "test_env_immutable_rom"))] +fn fw_load_error_owner_pub_key_digest_failure() { + fw_load_error_flow_with_test_hooks( + None, + None, + CaliptraError::IMAGE_VERIFIER_ERR_OWNER_PUB_KEY_DIGEST_FAILURE.into(), + FipsTestHook::FW_LOAD_OWNER_PUB_KEY_DIGEST_FAILURE, + ); +} + +#[test] +fn fw_load_error_owner_pub_key_digest_mismatch() { + // Set fuses + let fuses = caliptra_hw_model::Fuses { + owner_pk_hash: [0xDEADBEEF; 12], + ..Default::default() + }; + + fw_load_error_flow( + None, + Some(fuses), + CaliptraError::IMAGE_VERIFIER_ERR_OWNER_PUB_KEY_DIGEST_MISMATCH.into(), + ); +} + +#[test] +fn fw_load_error_vendor_ecc_pub_key_index_out_of_bounds() { + // Generate image + let mut fw_image = build_fw_image(ImageOptions::default()); + // Change ECC pub key index to max+1 + fw_image.manifest.preamble.vendor_ecc_pub_key_idx = VENDOR_ECC_KEY_COUNT; + + fw_load_error_flow( + Some(fw_image), + None, + CaliptraError::IMAGE_VERIFIER_ERR_VENDOR_ECC_PUB_KEY_INDEX_OUT_OF_BOUNDS.into(), + ); +} + +#[test] +fn fw_load_error_vendor_ecc_pub_key_revoked() { + let vendor_config = VENDOR_CONFIG_KEY_1; + let image_options = ImageOptions { + vendor_config, + ..Default::default() + }; + + // Set fuses + let fuses = caliptra_hw_model::Fuses { + key_manifest_pk_hash_mask: U4::try_from(1u32 << image_options.vendor_config.ecc_key_idx) + .unwrap(), + ..Default::default() + }; + + // Generate image + let fw_image = build_fw_image(image_options); + + fw_load_error_flow( + Some(fw_image), + Some(fuses), + CaliptraError::IMAGE_VERIFIER_ERR_VENDOR_ECC_PUB_KEY_REVOKED.into(), + ); +} + +#[test] +#[cfg(not(feature = "test_env_immutable_rom"))] +fn fw_load_error_header_digest_failure() { + fw_load_error_flow_with_test_hooks( + None, + None, + CaliptraError::IMAGE_VERIFIER_ERR_HEADER_DIGEST_FAILURE.into(), + FipsTestHook::FW_LOAD_HEADER_DIGEST_FAILURE, + ); +} + +#[test] +#[cfg(not(feature = "test_env_immutable_rom"))] +fn fw_load_error_vendor_ecc_verify_failure() { + fw_load_error_flow_with_test_hooks( + None, + None, + CaliptraError::IMAGE_VERIFIER_ERR_VENDOR_ECC_VERIFY_FAILURE.into(), + FipsTestHook::FW_LOAD_VENDOR_ECC_VERIFY_FAILURE, + ); +} + +#[test] +fn fw_load_error_vendor_ecc_signature_invalid() { + // Generate image + let mut fw_image = build_fw_image(ImageOptions::default()); + // Corrupt vendor ECC sig + fw_image.manifest.preamble.vendor_sigs.ecc_sig.r.fill(1); + + fw_load_error_flow( + Some(fw_image), + None, + CaliptraError::IMAGE_VERIFIER_ERR_VENDOR_ECC_SIGNATURE_INVALID.into(), + ); +} + +#[test] +fn fw_load_error_vendor_ecc_pub_key_index_mismatch() { + // Generate image + let mut fw_image = build_fw_image(ImageOptions::default()); + // Change vendor pubkey index. + fw_image.manifest.header.vendor_ecc_pub_key_idx = + fw_image.manifest.preamble.vendor_ecc_pub_key_idx + 1; + update_manifest(&mut fw_image, HdrDigest::Update, TocDigest::Update); + + fw_load_error_flow( + Some(fw_image), + None, + CaliptraError::IMAGE_VERIFIER_ERR_VENDOR_ECC_PUB_KEY_INDEX_MISMATCH.into(), + ); +} + +#[test] +#[cfg(not(feature = "test_env_immutable_rom"))] +fn fw_load_error_owner_ecc_verify_failure() { + fw_load_error_flow_with_test_hooks( + None, + None, + CaliptraError::IMAGE_VERIFIER_ERR_OWNER_ECC_VERIFY_FAILURE.into(), + FipsTestHook::FW_LOAD_OWNER_ECC_VERIFY_FAILURE, + ); +} + +#[test] +fn fw_load_error_owner_ecc_signature_invalid() { + // Generate image + let mut fw_image = build_fw_image(ImageOptions::default()); + // Corrupt owner ECC sig + fw_image.manifest.preamble.owner_sigs.ecc_sig.r.fill(1); + + fw_load_error_flow( + Some(fw_image), + None, + CaliptraError::IMAGE_VERIFIER_ERR_OWNER_ECC_SIGNATURE_INVALID.into(), + ); +} + +#[test] +fn fw_load_error_toc_entry_count_invalid() { + // Generate image + let mut fw_image = build_fw_image(ImageOptions::default()); + // Change the TOC length to over the maximum + fw_image.manifest.header.toc_len = caliptra_image_types::MAX_TOC_ENTRY_COUNT + 1; + update_manifest(&mut fw_image, HdrDigest::Update, TocDigest::Update); + + fw_load_error_flow( + Some(fw_image), + None, + CaliptraError::IMAGE_VERIFIER_ERR_TOC_ENTRY_COUNT_INVALID.into(), + ); +} + +#[test] +#[cfg(not(feature = "test_env_immutable_rom"))] +fn fw_load_error_toc_digest_failure() { + fw_load_error_flow_with_test_hooks( + None, + None, + CaliptraError::IMAGE_VERIFIER_ERR_TOC_DIGEST_FAILURE.into(), + FipsTestHook::FW_LOAD_OWNER_TOC_DIGEST_FAILURE, + ); +} + +#[test] +fn fw_load_error_toc_digest_mismatch() { + // Generate image + let mut fw_image = build_fw_image(ImageOptions::default()); + // Change the TOC digest. + fw_image.manifest.header.toc_digest[0] = 0xDEADBEEF; + update_manifest(&mut fw_image, HdrDigest::Update, TocDigest::Skip); + + fw_load_error_flow( + Some(fw_image), + None, + CaliptraError::IMAGE_VERIFIER_ERR_TOC_DIGEST_MISMATCH.into(), + ); +} + +#[test] +#[cfg(not(feature = "test_env_immutable_rom"))] +fn fw_load_error_fmc_digest_failure() { + fw_load_error_flow_with_test_hooks( + None, + None, + CaliptraError::IMAGE_VERIFIER_ERR_FMC_DIGEST_FAILURE.into(), + FipsTestHook::FW_LOAD_FMC_DIGEST_FAILURE, + ); +} + +#[test] +fn fw_load_error_fmc_digest_mismatch() { + // Generate image + let mut fw_image = build_fw_image(ImageOptions::default()); + // Change the FMC image. + fw_image.fmc[0..4].copy_from_slice(0xDEADBEEFu32.as_bytes()); + + fw_load_error_flow( + Some(fw_image), + None, + CaliptraError::IMAGE_VERIFIER_ERR_FMC_DIGEST_MISMATCH.into(), + ); +} + +#[test] +#[cfg(not(feature = "test_env_immutable_rom"))] +fn fw_load_error_runtime_digest_failure() { + fw_load_error_flow_with_test_hooks( + None, + None, + CaliptraError::IMAGE_VERIFIER_ERR_RUNTIME_DIGEST_FAILURE.into(), + FipsTestHook::FW_LOAD_RUNTIME_DIGEST_FAILURE, + ); +} + +#[test] +fn fw_load_error_runtime_digest_mismatch() { + // Generate image + let mut fw_image = build_fw_image(ImageOptions::default()); + // Change the runtime image. + fw_image.runtime[0..4].copy_from_slice(0xDEADBEEFu32.as_bytes()); + + fw_load_error_flow( + Some(fw_image), + None, + CaliptraError::IMAGE_VERIFIER_ERR_RUNTIME_DIGEST_MISMATCH.into(), + ); +} + +#[test] +fn fw_load_error_fmc_runtime_overlap() { + // Generate image + let mut fw_image = build_fw_image(ImageOptions::default()); + // Corrupt FMC offset + fw_image.manifest.fmc.offset = fw_image.manifest.runtime.offset; + + update_manifest(&mut fw_image, HdrDigest::Update, TocDigest::Update); + + fw_load_error_flow( + Some(fw_image), + None, + CaliptraError::IMAGE_VERIFIER_ERR_FMC_RUNTIME_OVERLAP.into(), + ); +} + +#[test] +fn fw_load_error_fmc_runtime_incorrect_order() { + // Generate image + let mut fw_image = build_fw_image(ImageOptions::default()); + // Flip FMC and RT positions + let old_fmc_offset = fw_image.manifest.fmc.offset; + let old_fmc_size = fw_image.manifest.fmc.size; + fw_image.manifest.fmc.offset = fw_image.manifest.runtime.offset; + fw_image.manifest.fmc.size = fw_image.manifest.runtime.size; + fw_image.manifest.runtime.offset = old_fmc_offset; + fw_image.manifest.runtime.size = old_fmc_size; + + update_manifest(&mut fw_image, HdrDigest::Update, TocDigest::Update); + + fw_load_error_flow( + Some(fw_image), + None, + CaliptraError::IMAGE_VERIFIER_ERR_FMC_RUNTIME_INCORRECT_ORDER.into(), + ); +} + +#[test] +fn fw_load_error_owner_ecc_pub_key_invalid_arg() { + // Generate image + let mut fw_image = build_fw_image(ImageOptions::default()); + // Set ecc_pub_key.y to zero. + fw_image + .manifest + .preamble + .owner_pub_keys + .ecc_pub_key + .y + .fill(0); + + fw_load_error_flow( + Some(fw_image), + None, + CaliptraError::IMAGE_VERIFIER_ERR_OWNER_ECC_PUB_KEY_INVALID_ARG.into(), + ); +} + +#[test] +fn fw_load_error_owner_ecc_signature_invalid_arg() { + // Generate image + let mut fw_image = build_fw_image(ImageOptions::default()); + // Set owner_sig.s to zero. + fw_image.manifest.preamble.owner_sigs.ecc_sig.s.fill(0); + + fw_load_error_flow( + Some(fw_image), + None, + CaliptraError::IMAGE_VERIFIER_ERR_OWNER_ECC_SIGNATURE_INVALID_ARG.into(), + ); +} + +#[test] +fn fw_load_error_vendor_pub_key_digest_invalid_arg() { + // Generate image + let mut fw_image = build_fw_image(ImageOptions::default()); + // Set ecc_pub_key.x to zero. + fw_image.manifest.preamble.vendor_pub_keys.ecc_pub_keys + [fw_image.manifest.preamble.vendor_ecc_pub_key_idx as usize] + .x + .fill(0); + + fw_load_error_flow( + Some(fw_image), + None, + CaliptraError::IMAGE_VERIFIER_ERR_VENDOR_PUB_KEY_DIGEST_INVALID_ARG.into(), + ); +} + +#[test] +fn fw_load_error_vendor_ecc_signature_invalid_arg() { + // Generate image + let mut fw_image = build_fw_image(ImageOptions::default()); + // Set vendor_sig.r to zero. + fw_image.manifest.preamble.vendor_sigs.ecc_sig.r.fill(0); + + fw_load_error_flow( + Some(fw_image), + None, + CaliptraError::IMAGE_VERIFIER_ERR_VENDOR_ECC_SIGNATURE_INVALID_ARG.into(), + ); +} + +#[test] +fn fw_load_error_update_reset_owner_digest_failure() { + // Generate image + let mut update_image = build_fw_image(ImageOptions::default()); + + // Set ecc_pub_key.y to some corrupted, non-zero value + update_image + .manifest + .preamble + .owner_pub_keys + .ecc_pub_key + .y + .fill(0x1234abcd); + + update_fw_error_flow( + None, + None, + Some(update_image), + CaliptraError::IMAGE_VERIFIER_ERR_UPDATE_RESET_OWNER_DIGEST_FAILURE.into(), + ); +} + +#[test] +fn fw_load_error_update_reset_vendor_ecc_pub_key_idx_mismatch() { + let vendor_config_cold_boot = ImageGeneratorVendorConfig { + ecc_key_idx: 3, + ..VENDOR_CONFIG_KEY_0 + }; + let image_options_cold_boot = ImageOptions { + vendor_config: vendor_config_cold_boot, + ..Default::default() + }; + let vendor_config_update_reset = ImageGeneratorVendorConfig { + ecc_key_idx: 2, + ..VENDOR_CONFIG_KEY_0 + }; + let image_options_update_reset = ImageOptions { + vendor_config: vendor_config_update_reset, + ..Default::default() + }; + // Generate images + let first_image = build_fw_image(image_options_cold_boot); + let update_image = build_fw_image(image_options_update_reset); + + update_fw_error_flow( + Some(first_image), + None, + Some(update_image), + CaliptraError::IMAGE_VERIFIER_ERR_UPDATE_RESET_VENDOR_ECC_PUB_KEY_IDX_MISMATCH.into(), + ); +} + +#[test] +fn fw_load_error_update_reset_fmc_digest_mismatch() { + // Generate images + let first_image = build_fw_image(ImageOptions::default()); + // Use a different FMC for the update image + let update_image = caliptra_builder::build_and_sign_image( + &FMC_FAKE_WITH_UART, + &APP_WITH_UART, + ImageOptions::default(), + ) + .unwrap(); + + update_fw_error_flow( + Some(first_image), + None, + Some(update_image), + CaliptraError::IMAGE_VERIFIER_ERR_UPDATE_RESET_FMC_DIGEST_MISMATCH.into(), + ); +} + +#[test] +fn fw_load_error_fmc_load_addr_invalid() { + // Generate image + let mut fw_image = build_fw_image(ImageOptions::default()); + // Change FMC load addr + fw_image.manifest.fmc.load_addr = ICCM_ORG - 4; + update_manifest(&mut fw_image, HdrDigest::Update, TocDigest::Update); + + fw_load_error_flow( + Some(fw_image), + None, + CaliptraError::IMAGE_VERIFIER_ERR_FMC_LOAD_ADDR_INVALID.into(), + ); +} + +#[test] +fn fw_load_error_fmc_load_addr_unaligned() { + // Generate image + let mut fw_image = build_fw_image(ImageOptions::default()); + // Change FMC load addr + fw_image.manifest.fmc.load_addr += 1; + update_manifest(&mut fw_image, HdrDigest::Update, TocDigest::Update); + + fw_load_error_flow( + Some(fw_image), + None, + CaliptraError::IMAGE_VERIFIER_ERR_FMC_LOAD_ADDR_UNALIGNED.into(), + ); +} + +#[test] +fn fw_load_error_fmc_entry_point_invalid() { + // Generate image + let mut fw_image = build_fw_image(ImageOptions::default()); + // Change FMC entry point + fw_image.manifest.fmc.entry_point = ICCM_ORG - 4; + update_manifest(&mut fw_image, HdrDigest::Update, TocDigest::Update); + + fw_load_error_flow( + Some(fw_image), + None, + CaliptraError::IMAGE_VERIFIER_ERR_FMC_ENTRY_POINT_INVALID.into(), + ); +} + +#[test] +fn fw_load_error_fmc_entry_point_unaligned() { + // Generate image + let mut fw_image = build_fw_image(ImageOptions::default()); + // Change FMC entry point + fw_image.manifest.fmc.entry_point += 1; + update_manifest(&mut fw_image, HdrDigest::Update, TocDigest::Update); + + fw_load_error_flow( + Some(fw_image), + None, + CaliptraError::IMAGE_VERIFIER_ERR_FMC_ENTRY_POINT_UNALIGNED.into(), + ); +} + +#[test] +fn fw_load_error_fmc_svn_greater_than_max_supported() { + // Generate image + let image_options = ImageOptions { + fmc_svn: 33, + ..Default::default() + }; + let fw_image = build_fw_image(image_options); + + // Set fuses + let gen = ImageGenerator::new(Crypto::default()); + let vendor_pubkey_digest = gen + .vendor_pubkey_digest(&fw_image.manifest.preamble) + .unwrap(); + let fuses = caliptra_hw_model::Fuses { + life_cycle: DeviceLifecycle::Manufacturing, + anti_rollback_disable: false, + key_manifest_pk_hash: vendor_pubkey_digest, + ..Default::default() + }; + + fw_load_error_flow( + Some(fw_image), + Some(fuses), + CaliptraError::IMAGE_VERIFIER_ERR_FMC_SVN_GREATER_THAN_MAX_SUPPORTED.into(), + ); +} + +// IMAGE_VERIFIER_ERR_FMC_SVN_LESS_THAN_MIN_SUPPORTED is defined but never used in the code (svn is a u32) + +#[test] +fn fw_load_error_fmc_svn_less_than_fuse() { + // Generate image + let image_options = ImageOptions { + fmc_svn: 1, + ..Default::default() + }; + let fw_image = build_fw_image(image_options); + + // Set fuses + let gen = ImageGenerator::new(Crypto::default()); + let vendor_pubkey_digest = gen + .vendor_pubkey_digest(&fw_image.manifest.preamble) + .unwrap(); + let fuses = caliptra_hw_model::Fuses { + life_cycle: DeviceLifecycle::Manufacturing, + anti_rollback_disable: false, + key_manifest_pk_hash: vendor_pubkey_digest, + fmc_key_manifest_svn: 0b11, // fuse svn = 2 + ..Default::default() + }; + + fw_load_error_flow( + Some(fw_image), + Some(fuses), + CaliptraError::IMAGE_VERIFIER_ERR_FMC_SVN_LESS_THAN_FUSE.into(), + ); +} + +#[test] +fn fw_load_error_runtime_load_addr_invalid() { + // Generate image + let mut fw_image = build_fw_image(ImageOptions::default()); + // Change runtime load addr + fw_image.manifest.runtime.load_addr = ICCM_ORG + ICCM_SIZE; + update_manifest(&mut fw_image, HdrDigest::Update, TocDigest::Update); + + fw_load_error_flow( + Some(fw_image), + None, + CaliptraError::IMAGE_VERIFIER_ERR_RUNTIME_LOAD_ADDR_INVALID.into(), + ); +} + +#[test] +fn fw_load_error_runtime_load_addr_unaligned() { + // Generate image + let mut fw_image = build_fw_image(ImageOptions::default()); + // Change runtime load addr + fw_image.manifest.runtime.load_addr += 1; + update_manifest(&mut fw_image, HdrDigest::Update, TocDigest::Update); + + fw_load_error_flow( + Some(fw_image), + None, + CaliptraError::IMAGE_VERIFIER_ERR_RUNTIME_LOAD_ADDR_UNALIGNED.into(), + ); +} + +#[test] +fn fw_load_error_runtime_entry_point_invalid() { + // Generate image + let mut fw_image = build_fw_image(ImageOptions::default()); + // Change runtime entry point + fw_image.manifest.runtime.entry_point = ICCM_ORG - 4; + update_manifest(&mut fw_image, HdrDigest::Update, TocDigest::Update); + + fw_load_error_flow( + Some(fw_image), + None, + CaliptraError::IMAGE_VERIFIER_ERR_RUNTIME_ENTRY_POINT_INVALID.into(), + ); +} + +#[test] +fn fw_load_error_runtime_entry_point_unaligned() { + // Generate image + let mut fw_image = build_fw_image(ImageOptions::default()); + // Change runtime entry point + fw_image.manifest.runtime.entry_point += 1; + update_manifest(&mut fw_image, HdrDigest::Update, TocDigest::Update); + + fw_load_error_flow( + Some(fw_image), + None, + CaliptraError::IMAGE_VERIFIER_ERR_RUNTIME_ENTRY_POINT_UNALIGNED.into(), + ); +} + +#[test] +fn fw_load_error_runtime_svn_greater_than_max_supported() { + // Generate image + let image_options = ImageOptions { + app_svn: caliptra_image_verify::MAX_RUNTIME_SVN + 1, + ..Default::default() + }; + let fw_image = build_fw_image(image_options); + + // Set fuses + let gen = ImageGenerator::new(Crypto::default()); + let vendor_pubkey_digest = gen + .vendor_pubkey_digest(&fw_image.manifest.preamble) + .unwrap(); + let fuses = caliptra_hw_model::Fuses { + life_cycle: DeviceLifecycle::Manufacturing, + anti_rollback_disable: false, + key_manifest_pk_hash: vendor_pubkey_digest, + ..Default::default() + }; + + fw_load_error_flow( + Some(fw_image), + Some(fuses), + CaliptraError::IMAGE_VERIFIER_ERR_RUNTIME_SVN_GREATER_THAN_MAX_SUPPORTED.into(), + ); +} + +// IMAGE_VERIFIER_ERR_RUNTIME_SVN_LESS_THAN_MIN_SUPPORTED is defined but never used in the code (svn is a u32) + +#[test] +fn fw_load_error_runtime_svn_less_than_fuse() { + // Generate image + let image_options = ImageOptions { + app_svn: 62, + ..Default::default() + }; + let fw_image = build_fw_image(image_options); + + // Set fuses + let gen = ImageGenerator::new(Crypto::default()); + let vendor_pubkey_digest = gen + .vendor_pubkey_digest(&fw_image.manifest.preamble) + .unwrap(); + let fuses = caliptra_hw_model::Fuses { + life_cycle: DeviceLifecycle::Manufacturing, + anti_rollback_disable: false, + key_manifest_pk_hash: vendor_pubkey_digest, + runtime_svn: [0xffff_ffff, 0x7fff_ffff, 0, 0], // fuse svn = 63 + ..Default::default() + }; + + fw_load_error_flow( + Some(fw_image), + Some(fuses), + CaliptraError::IMAGE_VERIFIER_ERR_RUNTIME_SVN_LESS_THAN_FUSE.into(), + ); +} + +#[test] +fn fw_load_error_image_len_more_than_bundle_size() { + // Generate image + let mut fw_image = build_fw_image(ImageOptions::default()); + // Change runtime size to exceed bundle + fw_image.manifest.runtime.size += 4; + update_manifest(&mut fw_image, HdrDigest::Update, TocDigest::Update); + + fw_load_error_flow( + Some(fw_image), + None, + CaliptraError::IMAGE_VERIFIER_ERR_IMAGE_LEN_MORE_THAN_BUNDLE_SIZE.into(), + ); +} + +#[test] +fn fw_load_error_vendor_lms_pub_key_index_mismatch() { + // Generate image + let mut fw_image = build_fw_image(ImageOptions::default()); + // Change vendor pubkey index. + fw_image.manifest.header.vendor_lms_pub_key_idx = + fw_image.manifest.preamble.vendor_lms_pub_key_idx + 1; + update_manifest(&mut fw_image, HdrDigest::Update, TocDigest::Update); + + // Turn LMS verify on + let fuses = caliptra_hw_model::Fuses { + lms_verify: true, + ..Default::default() + }; + + fw_load_error_flow( + Some(fw_image), + Some(fuses), + CaliptraError::IMAGE_VERIFIER_ERR_VENDOR_LMS_PUB_KEY_INDEX_MISMATCH.into(), + ); +} + +#[test] +#[cfg(not(feature = "test_env_immutable_rom"))] +fn fw_load_error_vendor_lms_verify_failure() { + // Turn LMS verify on + let fuses = caliptra_hw_model::Fuses { + lms_verify: true, + ..Default::default() + }; + + fw_load_error_flow_with_test_hooks( + None, + Some(fuses), + CaliptraError::IMAGE_VERIFIER_ERR_VENDOR_LMS_VERIFY_FAILURE.into(), + FipsTestHook::FW_LOAD_VENDOR_LMS_VERIFY_FAILURE, + ); +} + +#[test] +fn fw_load_error_vendor_lms_pub_key_index_out_of_bounds() { + // Generate image + let mut fw_image = build_fw_image(ImageOptions::default()); + // Set LMS pub key index to MAX + 1 + fw_image.manifest.preamble.vendor_lms_pub_key_idx = VENDOR_LMS_KEY_COUNT; + + // Turn LMS verify on + let fuses = caliptra_hw_model::Fuses { + lms_verify: true, + ..Default::default() + }; + + fw_load_error_flow( + Some(fw_image), + Some(fuses), + CaliptraError::IMAGE_VERIFIER_ERR_VENDOR_LMS_PUB_KEY_INDEX_OUT_OF_BOUNDS.into(), + ); +} + +#[test] +fn fw_load_error_vendor_lms_signature_invalid() { + // Generate image + let mut fw_image = build_fw_image(ImageOptions::default()); + // Modify the vendor public key. + let vendor_lms_pub_key_idx = fw_image.manifest.preamble.vendor_lms_pub_key_idx as usize; + fw_image.manifest.preamble.vendor_pub_keys.lms_pub_keys[vendor_lms_pub_key_idx].digest = + [Default::default(); 6]; + + // Turn LMS verify on + let fuses = caliptra_hw_model::Fuses { + lms_verify: true, + ..Default::default() + }; + + fw_load_error_flow( + Some(fw_image), + Some(fuses), + CaliptraError::IMAGE_VERIFIER_ERR_VENDOR_LMS_SIGNATURE_INVALID.into(), + ); +} + +#[test] +fn fw_load_error_fmc_runtime_load_addr_overlap() { + // Generate image + let mut fw_image = build_fw_image(ImageOptions::default()); + // Change runtime entry point + fw_image.manifest.runtime.load_addr = fw_image.manifest.fmc.load_addr + 1; + update_manifest(&mut fw_image, HdrDigest::Update, TocDigest::Update); + + fw_load_error_flow( + Some(fw_image), + None, + CaliptraError::IMAGE_VERIFIER_ERR_FMC_RUNTIME_LOAD_ADDR_OVERLAP.into(), + ); +} + +#[test] +#[cfg(not(feature = "test_env_immutable_rom"))] +fn fw_load_error_owner_lms_verify_failure() { + // Turn LMS verify on + let fuses = caliptra_hw_model::Fuses { + lms_verify: true, + ..Default::default() + }; + + fw_load_error_flow_with_test_hooks( + None, + Some(fuses), + CaliptraError::IMAGE_VERIFIER_ERR_OWNER_LMS_VERIFY_FAILURE.into(), + FipsTestHook::FW_LOAD_OWNER_LMS_VERIFY_FAILURE, + ); +} + +#[test] +fn fw_load_error_owner_lms_signature_invalid() { + // Generate image + let mut fw_image = build_fw_image(ImageOptions::default()); + // Modify the owner public key + fw_image.manifest.preamble.owner_pub_keys.lms_pub_key.digest = [Default::default(); 6]; + + // Turn LMS verify on + let fuses = caliptra_hw_model::Fuses { + lms_verify: true, + ..Default::default() + }; + + fw_load_error_flow( + Some(fw_image), + Some(fuses), + CaliptraError::IMAGE_VERIFIER_ERR_OWNER_LMS_SIGNATURE_INVALID.into(), + ); +} + +#[test] +fn fw_load_error_vendor_lms_pub_key_revoked() { + let vendor_config = ImageGeneratorVendorConfig { + lms_key_idx: 5, + ..VENDOR_CONFIG_KEY_0 + }; + let image_options = ImageOptions { + vendor_config, + ..Default::default() + }; + + // Set fuses + let fuses = caliptra_hw_model::Fuses { + lms_verify: true, + fuse_lms_revocation: 1u32 << image_options.vendor_config.lms_key_idx, + ..Default::default() + }; + + // Generate image + let fw_image = build_fw_image(image_options); + + fw_load_error_flow( + Some(fw_image), + Some(fuses), + CaliptraError::IMAGE_VERIFIER_ERR_VENDOR_LMS_PUB_KEY_REVOKED.into(), + ); +} + +#[test] +fn fw_load_error_fmc_size_zero() { + // Generate image + let mut fw_image = build_fw_image(ImageOptions::default()); + // Change FMC size to 0 + fw_image.manifest.fmc.size = 0; + update_manifest(&mut fw_image, HdrDigest::Update, TocDigest::Update); + + fw_load_error_flow( + Some(fw_image), + None, + CaliptraError::IMAGE_VERIFIER_ERR_FMC_SIZE_ZERO.into(), + ); +} + +#[test] +fn fw_load_error_runtime_size_zero() { + // Generate image + let mut fw_image = build_fw_image(ImageOptions::default()); + // Change runtime size to 0 + fw_image.manifest.runtime.size = 0; + update_manifest(&mut fw_image, HdrDigest::Update, TocDigest::Update); + + fw_load_error_flow( + Some(fw_image), + None, + CaliptraError::IMAGE_VERIFIER_ERR_RUNTIME_SIZE_ZERO.into(), + ); +} + +#[test] +fn fw_load_error_update_reset_vendor_lms_pub_key_idx_mismatch() { + let vendor_config_update_reset = ImageGeneratorVendorConfig { + lms_key_idx: 2, + ..VENDOR_CONFIG_KEY_0 + }; + let image_options_update_reset = ImageOptions { + vendor_config: vendor_config_update_reset, + ..Default::default() + }; + // Generate image + let update_image = build_fw_image(image_options_update_reset); + + // Turn LMS verify on + let fuses = caliptra_hw_model::Fuses { + lms_verify: true, + ..Default::default() + }; + + update_fw_error_flow( + None, + Some(fuses), + Some(update_image), + CaliptraError::IMAGE_VERIFIER_ERR_UPDATE_RESET_VENDOR_LMS_PUB_KEY_IDX_MISMATCH.into(), + ); +} + +#[test] +fn fw_load_error_fmc_load_address_image_size_arithmetic_overflow() { + // Generate image + let mut fw_image = build_fw_image(ImageOptions::default()); + // Change FMC load addr to cause overflow + fw_image.manifest.fmc.load_addr = 0xFFFFFFF0; + update_manifest(&mut fw_image, HdrDigest::Update, TocDigest::Update); + + fw_load_error_flow( + Some(fw_image), + None, + CaliptraError::IMAGE_VERIFIER_ERR_FMC_LOAD_ADDRESS_IMAGE_SIZE_ARITHMETIC_OVERFLOW.into(), + ); +} + +#[test] +fn fw_load_error_runtime_load_address_image_size_arithmetic_overflow() { + // Generate image + let mut fw_image = build_fw_image(ImageOptions::default()); + // Change runtime load addr to cause overflow + fw_image.manifest.runtime.load_addr = 0xFFFFFFF0; + update_manifest(&mut fw_image, HdrDigest::Update, TocDigest::Update); + + fw_load_error_flow( + Some(fw_image), + None, + CaliptraError::IMAGE_VERIFIER_ERR_RUNTIME_LOAD_ADDRESS_IMAGE_SIZE_ARITHMETIC_OVERFLOW + .into(), + ); +} + +#[test] +fn fw_load_error_toc_entry_range_arithmetic_overflow() { + // Generate image + let mut fw_image = build_fw_image(ImageOptions::default()); + // Change fmc offset to cause overflow + fw_image.manifest.fmc.offset = 0xFFFFFFF0; + update_manifest(&mut fw_image, HdrDigest::Update, TocDigest::Update); + + fw_load_error_flow( + Some(fw_image), + None, + CaliptraError::IMAGE_VERIFIER_ERR_TOC_ENTRY_RANGE_ARITHMETIC_OVERFLOW.into(), + ); +} + +// IMAGE_VERIFIER_ERR_DIGEST_OUT_OF_BOUNDS is not possible if there is no SW bug +// IMAGE_VERIFIER_ERR_IMAGE_LEN_MORE_THAN_BUNDLE_SIZE or an ARITHMETIC_OVERFLOW error would catch this first + +fn fw_load_bad_pub_key_flow(fw_image: ImageBundle, exp_error_code: u32) { + // Generate pub key hashes and set fuses + // Use a fresh image (will NOT be loaded) + let pk_hash_src_image = build_fw_image(ImageOptions::default()); + let vendor_pk_hash = sha384( + pk_hash_src_image + .manifest + .preamble + .vendor_pub_keys + .as_bytes(), + ); + let owner_pk_hash = sha384( + pk_hash_src_image + .manifest + .preamble + .owner_pub_keys + .as_bytes(), + ); + let vendor_pk_hash_words = bytes_to_be_words_48(&vendor_pk_hash); + let owner_pk_hash_words = bytes_to_be_words_48(&owner_pk_hash); + + let fuses = Fuses { + life_cycle: DeviceLifecycle::Production, + key_manifest_pk_hash: vendor_pk_hash_words, + owner_pk_hash: owner_pk_hash_words, + lms_verify: true, + ..Default::default() + }; + + // Load the FW + let mut hw = fips_test_init_to_rom( + Some(InitParams { + security_state: SecurityState::from(fuses.life_cycle as u32), + ..Default::default() + }), + Some(BootParams { + fuses, + ..Default::default() + }), + ); + let fw_load_result = hw.upload_firmware(&image_to_bytes_no_error_check(&fw_image)); + + // Make sure we got the right error + assert_eq!( + ModelError::MailboxCmdFailed(exp_error_code), + fw_load_result.unwrap_err() + ); +} + +#[test] +fn fw_load_bad_vendor_ecc_pub_key() { + // Generate image + let mut fw_image = build_fw_image(ImageOptions::default()); + + // Modify the pub key + fw_image.manifest.preamble.vendor_pub_keys.ecc_pub_keys + [fw_image.manifest.preamble.vendor_ecc_pub_key_idx as usize] + .x[0] ^= 0x1; + + fw_load_bad_pub_key_flow( + fw_image, + CaliptraError::IMAGE_VERIFIER_ERR_VENDOR_PUB_KEY_DIGEST_MISMATCH.into(), + ); +} + +#[test] +fn fw_load_bad_owner_ecc_pub_key() { + // Generate image + let mut fw_image = build_fw_image(ImageOptions::default()); + + // Modify the pub key + fw_image.manifest.preamble.owner_pub_keys.ecc_pub_key.x[0] ^= 0x1; + + fw_load_bad_pub_key_flow( + fw_image, + CaliptraError::IMAGE_VERIFIER_ERR_OWNER_PUB_KEY_DIGEST_MISMATCH.into(), + ); +} + +#[test] +fn fw_load_bad_vendor_lms_pub_key() { + // Generate image + let mut fw_image = build_fw_image(ImageOptions::default()); + + // Modify the pub key + fw_image.manifest.preamble.vendor_pub_keys.lms_pub_keys + [fw_image.manifest.preamble.vendor_lms_pub_key_idx as usize] + .digest[0] = 0xDEADBEEF.into(); + + fw_load_bad_pub_key_flow( + fw_image, + CaliptraError::IMAGE_VERIFIER_ERR_VENDOR_PUB_KEY_DIGEST_MISMATCH.into(), + ); +} + +#[test] +fn fw_load_bad_owner_lms_pub_key() { + // Generate image + let mut fw_image = build_fw_image(ImageOptions::default()); + + // Modify the pub key + fw_image.manifest.preamble.owner_pub_keys.lms_pub_key.digest[0] = 0xDEADBEEF.into(); + + fw_load_bad_pub_key_flow( + fw_image, + CaliptraError::IMAGE_VERIFIER_ERR_OWNER_PUB_KEY_DIGEST_MISMATCH.into(), + ); +} + +#[test] +fn fw_load_blank_pub_keys() { + // Generate image + let mut fw_image = build_fw_image(ImageOptions::default()); + + // Clear all pub keys + fw_image.manifest.preamble.vendor_pub_keys = + caliptra_image_types::ImageVendorPubKeys::default(); + fw_image.manifest.preamble.owner_pub_keys = caliptra_image_types::ImageOwnerPubKeys::default(); + + fw_load_bad_pub_key_flow( + fw_image, + CaliptraError::IMAGE_VERIFIER_ERR_VENDOR_PUB_KEY_DIGEST_MISMATCH.into(), + ); +} + +#[test] +fn fw_load_blank_pub_key_hashes() { + // Generate image + let fw_image = build_fw_image(ImageOptions::default()); + + // Don't populate pub key hashes + let fuses = Fuses { + life_cycle: DeviceLifecycle::Production, + ..Default::default() + }; + + // Load the FW + let mut hw = fips_test_init_to_rom( + Some(InitParams { + security_state: SecurityState::from(fuses.life_cycle as u32), + ..Default::default() + }), + Some(BootParams { + fuses, + ..Default::default() + }), + ); + let fw_load_result = hw.upload_firmware(&image_to_bytes_no_error_check(&fw_image)); + + // Make sure we got the right error + assert_eq!( + ModelError::MailboxCmdFailed( + CaliptraError::IMAGE_VERIFIER_ERR_VENDOR_PUB_KEY_DIGEST_INVALID.into() + ), + fw_load_result.unwrap_err() + ); +} + +#[test] +pub fn corrupted_fw_load_version() { + let mut hw = fips_test_init_to_rom(None, None); + + // Generate image + let mut fw_image = build_fw_image(ImageOptions::default()); + // Change the runtime image. + fw_image.runtime[0..4].copy_from_slice(0xDEADBEEFu32.as_bytes()); + + // Get the initial version + // Normally we would use a command for this, but we cannot issue commands after a fatal error + // from a failed FW load. We will use the version/rev reg directly instead. (This is the source + // for the response of the version command) + let rom_fmc_fw_version_before = hw.soc_ifc().cptra_fw_rev_id().read(); + + // Load the FW + let fw_load_result = hw.upload_firmware(&image_to_bytes_no_error_check(&fw_image)); + + // Make sure we got the right error + let exp_err: u32 = CaliptraError::IMAGE_VERIFIER_ERR_RUNTIME_DIGEST_MISMATCH.into(); + assert_eq!( + ModelError::MailboxCmdFailed(exp_err), + fw_load_result.unwrap_err() + ); + + // Make sure we can't use the module + verify_mbox_cmds_fail(&mut hw, exp_err); + + // Verify version info is unchanged + assert_eq!( + rom_fmc_fw_version_before, + hw.soc_ifc().cptra_fw_rev_id().read() + ); +} diff --git a/test/tests/fips_test_suite/main.rs b/test/tests/fips_test_suite/main.rs index 2f6e2f759b..13a8bf5b1d 100644 --- a/test/tests/fips_test_suite/main.rs +++ b/test/tests/fips_test_suite/main.rs @@ -1,6 +1,8 @@ // Licensed under the Apache-2.0 license mod common; +mod fw_load; #[cfg(feature = "fpga_realtime")] mod jtag_locked; +mod security_parameters; mod self_tests; mod services; diff --git a/test/tests/fips_test_suite/security_parameters.rs b/test/tests/fips_test_suite/security_parameters.rs new file mode 100755 index 0000000000..795a2649e2 --- /dev/null +++ b/test/tests/fips_test_suite/security_parameters.rs @@ -0,0 +1,240 @@ +// Licensed under the Apache-2.0 license +#![allow(dead_code)] + +use crate::common; + +use caliptra_builder::firmware::ROM_WITH_FIPS_TEST_HOOKS; +use caliptra_common::mailbox_api::*; +use caliptra_drivers::FipsTestHook; +use caliptra_hw_model::{BootParams, DeviceLifecycle, HwModel, InitParams, SecurityState}; +use caliptra_image_crypto::OsslCrypto as Crypto; +use caliptra_image_gen::ImageGenerator; +use caliptra_image_types::ImageManifest; +use ureg::{Mmio, MmioMut}; + +use common::*; +use zerocopy::FromBytes; + +// TODO: This may differ per environment (0s vs Fs) +const INACCESSIBLE_READ_VALUE: u32 = 0x0; + +fn prove_jtag_inaccessible(_hw: &mut T) { + // TODO: Add generic JTAG functions to FW model so something like what's below can work + // #[cfg(feature = "fpga_realtime")] + // assert_eq!(_hw.launch_openocd().unwrap_err(), caliptra_hw_model::OpenOcdError::NotAccessible); +} + +fn attempt_csp_fuse_read(hw: &mut T) { + let uds_ptr = hw.soc_ifc().fuse_uds_seed().at(0).ptr; + let uds_read_val = + unsafe { caliptra_hw_model::BusMmio::new(hw.apb_bus()).read_volatile(uds_ptr) }; + + let field_entropy_ptr = hw.soc_ifc().fuse_field_entropy().at(0).ptr; + let field_entropy_read_val = + unsafe { caliptra_hw_model::BusMmio::new(hw.apb_bus()).read_volatile(field_entropy_ptr) }; + + // TODO: Add exception for SW emulator (does not model locked registers at the MMIO level) + assert_eq!(uds_read_val, INACCESSIBLE_READ_VALUE); + assert_eq!(field_entropy_read_val, INACCESSIBLE_READ_VALUE); +} + +fn attempt_psp_fuse_modify(hw: &mut T) { + let owner_pk_hash_read_orig_val = hw.soc_ifc().fuse_key_manifest_pk_hash().at(0).read(); + + // TODO: Add exception for SW emulator (write failure panics) + // Try to write a new value + let owner_pk_hash_ptr = hw.soc_ifc().fuse_key_manifest_pk_hash().at(0).ptr; + unsafe { + caliptra_hw_model::BusMmio::new(hw.apb_bus()).write_volatile(owner_pk_hash_ptr, 0xaaaaaaaa) + }; + + let owner_pk_hash_read_val = hw.soc_ifc().fuse_key_manifest_pk_hash().at(0).read(); + + // Make sure the value was unchanged + assert_eq!(owner_pk_hash_read_orig_val, owner_pk_hash_read_val); +} + +fn attempt_keyvault_access(hw: &mut T) { + const KV_KEY_CTRL_ADDR: u32 = 0x1001_8000; + let kv_key_ctrl_ptr = KV_KEY_CTRL_ADDR as *mut u32; + + // Attempt to read keyvault module from the SoC side + // This is not visible to the SoC, but shared modules (mailbox, SHA engine, etc.) use a 1:1 + // address mapping between the SoC and Caliptra + let kv_key_ctrl_val = + unsafe { caliptra_hw_model::BusMmio::new(hw.apb_bus()).read_volatile(kv_key_ctrl_ptr) }; + assert_eq!(kv_key_ctrl_val, INACCESSIBLE_READ_VALUE); + + // Attempt to write + unsafe { + caliptra_hw_model::BusMmio::new(hw.apb_bus()).write_volatile(kv_key_ctrl_ptr, 0xffffffff) + }; + + // Read again + let kv_key_ctrl_val = + unsafe { caliptra_hw_model::BusMmio::new(hw.apb_bus()).read_volatile(kv_key_ctrl_ptr) }; + assert_eq!(kv_key_ctrl_val, INACCESSIBLE_READ_VALUE); +} + +fn attempt_caliptra_dccm_access(hw: &mut T) { + const DCCM_BASE: u32 = 0x5000_0000; + let dccm_ptr = DCCM_BASE as *mut u32; + + // Attempt to read DCCM module from the SoC side + // This is not visible to the SoC, but shared modules (mailbox, SHA engine, etc.) use a 1:1 + // address mapping between the SoC and Caliptra + let dccm_val = unsafe { caliptra_hw_model::BusMmio::new(hw.apb_bus()).read_volatile(dccm_ptr) }; + assert_eq!(dccm_val, INACCESSIBLE_READ_VALUE); + + // Attempt to write + unsafe { caliptra_hw_model::BusMmio::new(hw.apb_bus()).write_volatile(dccm_ptr, 0xffffffff) }; + + // Read again + let dccm_val = unsafe { caliptra_hw_model::BusMmio::new(hw.apb_bus()).read_volatile(dccm_ptr) }; + assert_eq!(dccm_val, INACCESSIBLE_READ_VALUE); +} + +fn attempt_mbox_access(hw: &mut T) { + // Makes sure we can't read anything from dataout + let dataout_val = hw.soc_mbox().dataout().read(); + assert_eq!(dataout_val, 0x0); +} + +fn attempt_ssp_access(hw: &mut T) { + prove_jtag_inaccessible(hw); + + // TODO: Enable on other environments (not possible on SW emulator) + #[cfg(feature = "verilator")] + attempt_csp_fuse_read(hw); + + // TODO: Enable on other environments (not possible on SW emulator) + #[cfg(feature = "verilator")] + attempt_psp_fuse_modify(hw); + + // TODO: Enable on other environments (not possible on SW emulator) + #[cfg(feature = "verilator")] + attempt_keyvault_access(hw); + + // TODO: Enable on other environments (not possible on SW emulator) + #[cfg(feature = "verilator")] + attempt_caliptra_dccm_access(hw); + + // TODO: Enable on other environments (not possible on SW emulator) + #[cfg(feature = "verilator")] + attempt_mbox_access(hw); +} + +#[test] +pub fn attempt_ssp_access_rom() { + let fuses = caliptra_hw_model::Fuses { + //field_entropy + key_manifest_pk_hash: [0x55555555u32; 12], + ..Default::default() + }; + + let security_state = *SecurityState::default() + .set_debug_locked(true) + .set_device_lifecycle(DeviceLifecycle::Production); + + let mut hw = fips_test_init_to_rom( + Some(InitParams { + security_state, + ..Default::default() + }), + Some(BootParams { + fuses, + ..Default::default() + }), + ); + + // Perform all the SSP access attempts + attempt_ssp_access(&mut hw); +} + +#[test] +#[cfg(not(feature = "test_env_immutable_rom"))] +pub fn attempt_ssp_access_fw_load() { + let rom = caliptra_builder::build_firmware_rom(&ROM_WITH_FIPS_TEST_HOOKS).unwrap(); + + let fw_image = fips_fw_image(); + let manifest = ImageManifest::read_from_prefix(&*fw_image).unwrap(); + + let gen = ImageGenerator::new(Crypto::default()); + let vendor_pubkey_digest = gen.vendor_pubkey_digest(&manifest.preamble).unwrap(); + let fuses = caliptra_hw_model::Fuses { + //field_entropy + key_manifest_pk_hash: vendor_pubkey_digest, + life_cycle: DeviceLifecycle::Production, + ..Default::default() + }; + + let security_state = *SecurityState::default() + .set_debug_locked(true) + .set_device_lifecycle(DeviceLifecycle::Production); + + let mut hw = fips_test_init_to_rom( + Some(InitParams { + rom: &rom, + security_state, + ..Default::default() + }), + Some(BootParams { + fuses, + initial_dbg_manuf_service_reg: (FipsTestHook::HALT_FW_LOAD as u32) << HOOK_CODE_OFFSET, + ..Default::default() + }), + ); + + // Start the FW load (don't wait for a result) + hw.start_mailbox_execute(u32::from(CommandId::FIRMWARE_LOAD), &fw_image) + .unwrap(); + + // Wait for ACK that ROM reached halt point + hook_wait_for_complete(&mut hw); + + // Perform all the SSP access attempts + attempt_ssp_access(&mut hw); + + // Tell ROM to continue + hook_code_write(&mut hw, FipsTestHook::CONTINUE); + + // Wait for ACK that ROM continued + hook_wait_for_complete(&mut hw); + + // Wait for the FW load to report success + hw.finish_mailbox_execute().unwrap(); +} + +#[test] +pub fn attempt_ssp_access_rt() { + let fw_image = fips_fw_image(); + let manifest = ImageManifest::read_from_prefix(&*fw_image).unwrap(); + + let gen = ImageGenerator::new(Crypto::default()); + let vendor_pubkey_digest = gen.vendor_pubkey_digest(&manifest.preamble).unwrap(); + let fuses = caliptra_hw_model::Fuses { + //field_entropy + key_manifest_pk_hash: vendor_pubkey_digest, + life_cycle: DeviceLifecycle::Production, + ..Default::default() + }; + + let security_state = *SecurityState::default() + .set_debug_locked(true) + .set_device_lifecycle(DeviceLifecycle::Production); + + let mut hw = fips_test_init_to_rt( + Some(InitParams { + security_state, + ..Default::default() + }), + Some(BootParams { + fw_image: Some(&fw_image), + fuses, + ..Default::default() + }), + ); + + // Perform all the SSP access attempts + attempt_ssp_access(&mut hw); +} diff --git a/test/tests/fips_test_suite/self_tests.rs b/test/tests/fips_test_suite/self_tests.rs index 8342e07227..ea50ebe20f 100755 --- a/test/tests/fips_test_suite/self_tests.rs +++ b/test/tests/fips_test_suite/self_tests.rs @@ -1,16 +1,21 @@ // Licensed under the Apache-2.0 license use crate::common; -use caliptra_builder::firmware::ROM_WITH_UART_FIPS_TEST_HOOKS; +use caliptra_builder::firmware::{ + APP_WITH_UART_FIPS_TEST_HOOKS, FMC_WITH_UART, ROM_WITH_FIPS_TEST_HOOKS, ROM_WITH_UART, +}; +use caliptra_builder::ImageOptions; +use caliptra_common::mailbox_api::*; use caliptra_drivers::CaliptraError; use caliptra_drivers::FipsTestHook; -use caliptra_hw_model::{BootParams, HwModel, InitParams}; +use caliptra_hw_model::{BootParams, HwModel, InitParams, ModelError, ShaAccMode}; use common::*; +use zerocopy::AsBytes; #[test] #[cfg(not(feature = "test_env_immutable_rom"))] pub fn kat_halt_check_no_output() { - let rom = caliptra_builder::build_firmware_rom(&ROM_WITH_UART_FIPS_TEST_HOOKS).unwrap(); + let rom = caliptra_builder::build_firmware_rom(&ROM_WITH_FIPS_TEST_HOOKS).unwrap(); let mut hw = fips_test_init_to_boot_start( Some(InitParams { @@ -29,22 +34,39 @@ pub fn kat_halt_check_no_output() { // Check output is inhibited verify_output_inhibited(&mut hw); +} + +#[test] +#[cfg(not(feature = "test_env_immutable_rom"))] +pub fn fw_load_halt_check_no_output() { + let rom = caliptra_builder::build_firmware_rom(&ROM_WITH_FIPS_TEST_HOOKS).unwrap(); - // TODO: Remove continuing if it's not needed - // Tell ROM to continue - hook_code_write(&mut hw, FipsTestHook::CONTINUE); + let mut hw = fips_test_init_to_rom( + Some(InitParams { + rom: &rom, + ..Default::default() + }), + Some(BootParams { + initial_dbg_manuf_service_reg: (FipsTestHook::HALT_FW_LOAD as u32) << HOOK_CODE_OFFSET, + ..Default::default() + }), + ); - // Wait for ACK that ROM continued + // Start the FW load (don't wait for a result) + let fw_image = fips_fw_image(); + hw.start_mailbox_execute(u32::from(CommandId::FIRMWARE_LOAD), &fw_image) + .unwrap(); + + // Wait for ACK that ROM reached halt point hook_wait_for_complete(&mut hw); - // Step to ready for FW in ROM - hw.step_until(|m| m.soc_ifc().cptra_flow_status().read().ready_for_fw()); + // Check output is inhibited + verify_mbox_output_inhibited(&mut hw); + // NOTE: SHA engine is not locked during FW load } -#[test] -#[cfg(not(feature = "test_env_immutable_rom"))] -pub fn kat_sha384_error() { - let rom = caliptra_builder::build_firmware_rom(&ROM_WITH_UART_FIPS_TEST_HOOKS).unwrap(); +fn self_test_failure_flow_rom(hook_code: u8, exp_error_code: u32) { + let rom = caliptra_builder::build_firmware_rom(&ROM_WITH_FIPS_TEST_HOOKS).unwrap(); let mut hw = fips_test_init_to_boot_start( Some(InitParams { @@ -52,53 +74,487 @@ pub fn kat_sha384_error() { ..Default::default() }), Some(BootParams { - initial_dbg_manuf_service_reg: (FipsTestHook::SHA384_ERROR as u32) << HOOK_CODE_OFFSET, + initial_dbg_manuf_service_reg: (hook_code as u32) << HOOK_CODE_OFFSET, ..Default::default() }), ); // Wait for fatal error hw.step_until(|m| m.soc_ifc().cptra_fw_error_fatal().read() != 0); + // Wait for the remaining operations and cleanup from a fatal error to complete + // (This is mainly for the SW emulator which only runs when we step) + for _ in 0..1000 { + hw.step(); + } // Verify fatal code is correct - assert_eq!( - hw.soc_ifc().cptra_fw_error_fatal().read(), - u32::from(CaliptraError::KAT_SHA384_DIGEST_MISMATCH) + assert_eq!(hw.soc_ifc().cptra_fw_error_fatal().read(), exp_error_code); + + // Verify we cannot use the algorithm + // We can't directly call this algorithm, so check that Caliptra will not process messages + // Using the Load FW message since that is what uses most of the crypto anyway + // Check that the SHA engine is not usable + let fw_image = fips_fw_image(); + match hw.upload_firmware(&fw_image) { + Ok(_) => panic!("FW Load should fail at this point"), + Err(act_error) => { + if act_error != ModelError::MailboxCmdFailed(exp_error_code) { + panic!("FW Load received unexpected error {}", act_error) + } + } + } + verify_sha_engine_output_inhibited(&mut hw); + + // Attempt to clear the error in an undocumented way + // Clear the error reg and attempt output again + // Now that we have cleared the error, we expect an error code of 0 because + // The fatal error loop that marks all mbox messages as failed does not update the error code + hw.soc_ifc().cptra_fw_error_fatal().write(|_| 0); + hw.soc_ifc().cptra_fw_error_non_fatal().write(|_| 0); + match hw.upload_firmware(&fw_image) { + Ok(_) => panic!("FW Load should fail at this point"), + Err(ModelError::MailboxCmdFailed(0x0)) => (), + Err(e) => panic!("FW Load received unexpected error {}", e), + } + verify_sha_engine_output_inhibited(&mut hw); + + // Restart Caliptra + if cfg!(any(feature = "verilator", feature = "fpga_realtime")) { + hw.cold_reset(); + } else { + hw = fips_test_init_model(Some(InitParams { + rom: &rom, + ..Default::default() + })) + } + hw.boot(BootParams::default()).unwrap(); + hw.step_until(|m| m.soc_ifc().cptra_flow_status().read().ready_for_fw()); + + // Verify crypto operations can be performed + // Verify the SHA engine is usable + let message: &[u8] = &[0x0, 0x1, 0x2, 0x3]; + hw.compute_sha512_acc_digest(message, ShaAccMode::Sha384Stream) + .unwrap(); + + // Verify we can load FW + hw.upload_firmware(&fw_image).unwrap(); +} + +fn self_test_failure_flow_rt(hook_code: u8, exp_error_code: u32) { + // Build FW with test hooks and init to runtime + let fw_image = caliptra_builder::build_and_sign_image( + &FMC_WITH_UART, + &APP_WITH_UART_FIPS_TEST_HOOKS, + ImageOptions::default(), + ) + .unwrap() + .to_bytes() + .unwrap(); + + let mut hw = fips_test_init_to_rt( + None, + Some(BootParams { + fw_image: Some(&fw_image), + ..Default::default() + }), + ); + + // Wait for RT to be ready for commands before setting hook + hw.step_until(|m| m.soc_ifc().cptra_flow_status().read().ready_for_runtime()); + + // Set the test hook + hw.soc_ifc() + .cptra_dbg_manuf_service_reg() + .write(|_| (hook_code as u32) << HOOK_CODE_OFFSET); + + // Start the self tests + let payload = MailboxReqHeader { + chksum: caliptra_common::checksum::calc_checksum( + u32::from(CommandId::SELF_TEST_START), + &[], + ), + }; + mbx_send_and_check_resp_hdr::<_, MailboxRespHeader>( + &mut hw, + u32::from(CommandId::SELF_TEST_START), + payload.as_bytes(), + ) + .unwrap(); + + // Wait for error + hw.step_until(|m| m.soc_ifc().cptra_fw_error_fatal().read() != 0); + // Wait for the remaining operations and cleanup from a fatal error to complete + // (This is mainly for the SW emulator which only runs when we step) + for _ in 0..1000 { + hw.step(); + } + + // Verify error code is correct + assert_eq!(hw.soc_ifc().cptra_fw_error_fatal().read(), exp_error_code); + + // Verify we cannot use the algorithm + match hw.upload_firmware(&fw_image) { + Ok(_) => panic!("FW Load should fail at this point"), + Err(act_error) => { + if act_error != ModelError::MailboxCmdFailed(exp_error_code) { + panic!("FW Load received unexpected error {}", act_error) + } + } + } + // Check that the SHA engine is not usable + verify_sha_engine_output_inhibited(&mut hw); + + // Attempt to clear the error in an undocumented way + // Clear the error reg and attempt output again + // Now that we have cleared the error, we expect an error code of 0 because + // The fatal error loop that marks all mbox messages as failed does not update the error code + hw.soc_ifc().cptra_fw_error_fatal().write(|_| 0); + hw.soc_ifc().cptra_fw_error_non_fatal().write(|_| 0); + match hw.upload_firmware(&fw_image) { + Ok(_) => panic!("FW Load should fail at this point"), + Err(ModelError::MailboxCmdFailed(0x0)) => (), + Err(e) => panic!("FW Load received unexpected error {}", e), + } + verify_sha_engine_output_inhibited(&mut hw); + + // Restart Caliptra + if cfg!(any(feature = "verilator", feature = "fpga_realtime")) { + hw.cold_reset(); + } else { + hw = fips_test_init_model(None) + } + hw.boot(BootParams::default()).unwrap(); + hw.step_until(|m| m.soc_ifc().cptra_flow_status().read().ready_for_fw()); + + // Verify crypto operations can be performed + // Verify the SHA engine is usable + let message: &[u8] = &[0x0, 0x1, 0x2, 0x3]; + hw.compute_sha512_acc_digest(message, ShaAccMode::Sha384Stream) + .unwrap(); + + // Verify we can load FW + hw.upload_firmware(&fw_image).unwrap(); +} + +#[test] +#[cfg(not(feature = "test_env_immutable_rom"))] +pub fn kat_sha1_digest_failure_rom() { + self_test_failure_flow_rom( + FipsTestHook::SHA1_DIGEST_FAILURE, + u32::from(CaliptraError::KAT_SHA1_DIGEST_FAILURE), + ); +} + +#[test] +pub fn kat_sha1_digest_failure_rt() { + self_test_failure_flow_rt( + FipsTestHook::SHA1_DIGEST_FAILURE, + u32::from(CaliptraError::KAT_SHA1_DIGEST_FAILURE), + ); +} + +#[test] +#[cfg(not(feature = "test_env_immutable_rom"))] +pub fn kat_sha1_digest_mismatch_rom() { + self_test_failure_flow_rom( + FipsTestHook::SHA1_CORRUPT_DIGEST, + u32::from(CaliptraError::KAT_SHA1_DIGEST_MISMATCH), + ); +} + +#[test] +pub fn kat_sha1_digest_mismatch_rt() { + self_test_failure_flow_rt( + FipsTestHook::SHA1_CORRUPT_DIGEST, + u32::from(CaliptraError::KAT_SHA1_DIGEST_MISMATCH), + ); +} + +#[test] +#[cfg(not(feature = "test_env_immutable_rom"))] +pub fn kat_sha256_digest_failure_rom() { + self_test_failure_flow_rom( + FipsTestHook::SHA256_DIGEST_FAILURE, + u32::from(CaliptraError::KAT_SHA256_DIGEST_FAILURE), + ); +} + +#[test] +pub fn kat_sha256_digest_failure_rt() { + self_test_failure_flow_rt( + FipsTestHook::SHA256_DIGEST_FAILURE, + u32::from(CaliptraError::KAT_SHA256_DIGEST_FAILURE), + ); +} + +#[test] +#[cfg(not(feature = "test_env_immutable_rom"))] +pub fn kat_sha256_digest_mismatch_rom() { + self_test_failure_flow_rom( + FipsTestHook::SHA256_CORRUPT_DIGEST, + u32::from(CaliptraError::KAT_SHA256_DIGEST_MISMATCH), + ); +} + +#[test] +pub fn kat_sha256_digest_mismatch_rt() { + self_test_failure_flow_rt( + FipsTestHook::SHA256_CORRUPT_DIGEST, + u32::from(CaliptraError::KAT_SHA256_DIGEST_MISMATCH), + ); +} + +#[test] +#[cfg(not(feature = "test_env_immutable_rom"))] +pub fn kat_sha384_digest_failure_rom() { + self_test_failure_flow_rom( + FipsTestHook::SHA384_DIGEST_FAILURE, + u32::from(CaliptraError::KAT_SHA384_DIGEST_FAILURE), + ); +} + +#[test] +pub fn kat_sha384_digest_failure_rt() { + self_test_failure_flow_rt( + FipsTestHook::SHA384_DIGEST_FAILURE, + u32::from(CaliptraError::KAT_SHA384_DIGEST_FAILURE), + ); +} + +#[test] +#[cfg(not(feature = "test_env_immutable_rom"))] +pub fn kat_sha384_digest_mismatch_rom() { + self_test_failure_flow_rom( + FipsTestHook::SHA384_CORRUPT_DIGEST, + u32::from(CaliptraError::KAT_SHA384_DIGEST_MISMATCH), + ); +} + +#[test] +pub fn kat_sha384_digest_mismatch_rt() { + self_test_failure_flow_rt( + FipsTestHook::SHA384_CORRUPT_DIGEST, + u32::from(CaliptraError::KAT_SHA384_DIGEST_MISMATCH), + ); +} + +#[test] +#[cfg(not(feature = "test_env_immutable_rom"))] +pub fn kat_sha2_512_384acc_digest_start_op_failure_rom() { + self_test_failure_flow_rom( + FipsTestHook::SHA2_512_384_ACC_START_OP_FAILURE, + u32::from(CaliptraError::KAT_SHA2_512_384_ACC_DIGEST_START_OP_FAILURE), + ); +} + +#[test] +pub fn kat_sha2_512_384acc_digest_start_op_failure_rt() { + self_test_failure_flow_rt( + FipsTestHook::SHA2_512_384_ACC_START_OP_FAILURE, + u32::from(CaliptraError::KAT_SHA2_512_384_ACC_DIGEST_START_OP_FAILURE), + ); +} + +#[test] +#[cfg(not(feature = "test_env_immutable_rom"))] +pub fn kat_sha2_512_384acc_digest_failure_rom() { + self_test_failure_flow_rom( + FipsTestHook::SHA2_512_384_ACC_DIGEST_512_FAILURE, + u32::from(CaliptraError::KAT_SHA2_512_384_ACC_DIGEST_FAILURE), + ); +} + +#[test] +pub fn kat_sha2_512_384acc_digest_failure_rt() { + self_test_failure_flow_rt( + FipsTestHook::SHA2_512_384_ACC_DIGEST_512_FAILURE, + u32::from(CaliptraError::KAT_SHA2_512_384_ACC_DIGEST_FAILURE), + ); +} + +#[test] +#[cfg(not(feature = "test_env_immutable_rom"))] +pub fn kat_sha2_512_384acc_digest_mismatch_rom() { + self_test_failure_flow_rom( + FipsTestHook::SHA2_512_384_ACC_CORRUPT_DIGEST_512, + u32::from(CaliptraError::KAT_SHA2_512_384_ACC_DIGEST_MISMATCH), + ); +} + +#[test] +pub fn kat_sha2_512_384acc_digest_mismatch_rt() { + self_test_failure_flow_rt( + FipsTestHook::SHA2_512_384_ACC_CORRUPT_DIGEST_512, + u32::from(CaliptraError::KAT_SHA2_512_384_ACC_DIGEST_MISMATCH), + ); +} + +#[test] +#[cfg(not(feature = "test_env_immutable_rom"))] +pub fn kat_ecc384_signature_generate_failure_rom() { + // Should be KAT_ECC384_SIGNATURE_VERIFY_FAILURE but ROM is using the wrong code + self_test_failure_flow_rom( + FipsTestHook::ECC384_SIGNATURE_GENERATE_FAILURE, + u32::from(CaliptraError::KAT_ECC384_SIGNATURE_GENERATE_FAILURE), + ); +} + +#[test] +pub fn kat_ecc384_signature_generate_failure_rt() { + // Should be KAT_ECC384_SIGNATURE_VERIFY_FAILURE but ROM is using the wrong code + self_test_failure_flow_rt( + FipsTestHook::ECC384_SIGNATURE_GENERATE_FAILURE, + u32::from(CaliptraError::KAT_ECC384_SIGNATURE_GENERATE_FAILURE), + ); +} + +#[test] +#[cfg(not(feature = "test_env_immutable_rom"))] +pub fn kat_ecc384_signature_verify_failure_rom() { + // Should be KAT_ECC384_SIGNATURE_VERIFY_FAILURE but ROM is using the wrong code + self_test_failure_flow_rom( + FipsTestHook::ECC384_CORRUPT_SIGNATURE, + u32::from(CaliptraError::KAT_ECC384_SIGNATURE_GENERATE_FAILURE), ); +} - // TODO: Verify we cannot use the algorithm - // TODO: Attempt to clear the error in an undocumented way - // TODO: Restart Caliptra - // TODO: Verify crypto operations can be performed +#[test] +pub fn kat_ecc384_signature_verify_failure_rt() { + // Should be KAT_ECC384_SIGNATURE_VERIFY_FAILURE but ROM is using the wrong code + self_test_failure_flow_rt( + FipsTestHook::ECC384_CORRUPT_SIGNATURE, + u32::from(CaliptraError::KAT_ECC384_SIGNATURE_GENERATE_FAILURE), + ); } #[test] #[cfg(not(feature = "test_env_immutable_rom"))] -pub fn kat_lms_error() { - let rom = caliptra_builder::build_firmware_rom(&ROM_WITH_UART_FIPS_TEST_HOOKS).unwrap(); +pub fn kat_hmac384_failure_rom() { + self_test_failure_flow_rom( + FipsTestHook::HMAC384_FAILURE, + u32::from(CaliptraError::KAT_HMAC384_FAILURE), + ); +} + +#[test] +pub fn kat_hmac384_failure_rt() { + self_test_failure_flow_rt( + FipsTestHook::HMAC384_FAILURE, + u32::from(CaliptraError::KAT_HMAC384_FAILURE), + ); +} + +#[test] +#[cfg(not(feature = "test_env_immutable_rom"))] +pub fn kat_hmac384_tag_mismatch_rom() { + self_test_failure_flow_rom( + FipsTestHook::HMAC384_CORRUPT_TAG, + u32::from(CaliptraError::KAT_HMAC384_TAG_MISMATCH), + ); +} + +#[test] +pub fn kat_hmac384_tag_mismatch_rt() { + self_test_failure_flow_rt( + FipsTestHook::HMAC384_CORRUPT_TAG, + u32::from(CaliptraError::KAT_HMAC384_TAG_MISMATCH), + ); +} + +#[test] +#[cfg(not(feature = "test_env_immutable_rom"))] +pub fn kat_lms_digest_mismatch_rom() { + self_test_failure_flow_rom( + FipsTestHook::LMS_CORRUPT_INPUT, + u32::from(CaliptraError::KAT_LMS_DIGEST_MISMATCH), + ); +} + +#[test] +pub fn kat_lms_digest_mismatch_rt() { + self_test_failure_flow_rt( + FipsTestHook::LMS_CORRUPT_INPUT, + u32::from(CaliptraError::KAT_LMS_DIGEST_MISMATCH), + ); +} + +fn find_rom_info_offset(rom: &[u8]) -> usize { + for i in (0..rom.len()).step_by(64).rev() { + if rom[i..][..64] != [0u8; 64] { + return i; + } + } + panic!("Could not find RomInfo"); +} + +#[test] +#[cfg(not(feature = "test_env_immutable_rom"))] +pub fn integrity_check_failure_rom() { + // NOTE: Corruption steps from test_rom_integrity.rs + let exp_error_code = u32::from(CaliptraError::ROM_INTEGRITY_FAILURE); + let mut rom = caliptra_builder::build_firmware_rom(&ROM_WITH_UART).unwrap(); + + let rom_info_offset = find_rom_info_offset(&rom); + + // Corrupt a bit in the ROM info hash (we don't want to pick an arbitrary + // location in the image as that might make the CPU crazy) + rom[rom_info_offset + 9] ^= 1; let mut hw = fips_test_init_to_boot_start( Some(InitParams { rom: &rom, ..Default::default() }), - Some(BootParams { - initial_dbg_manuf_service_reg: (FipsTestHook::LMS_ERROR as u32) << HOOK_CODE_OFFSET, - ..Default::default() - }), + None, ); // Wait for fatal error hw.step_until(|m| m.soc_ifc().cptra_fw_error_fatal().read() != 0); + // Wait for the remaining operations and cleanup from a fatal error to complete + // (This is mainly for the SW emulator which only runs when we step) + for _ in 0..1000 { + hw.step(); + } // Verify fatal code is correct - assert_eq!( - hw.soc_ifc().cptra_fw_error_fatal().read(), - u32::from(CaliptraError::KAT_LMS_DIGEST_MISMATCH) - ); + assert_eq!(hw.soc_ifc().cptra_fw_error_fatal().read(), exp_error_code); + + // Verify we cannot send messages or use the SHA engine + let fw_image = fips_fw_image(); + match hw.upload_firmware(&fw_image) { + Ok(_) => panic!("FW Load should fail at this point"), + Err(act_error) => { + if act_error != ModelError::MailboxCmdFailed(exp_error_code) { + panic!("FW Load received unexpected error {}", act_error) + } + } + } + verify_sha_engine_output_inhibited(&mut hw); - // TODO: Verify we cannot use the algorithm - // TODO: Attempt to clear the error in an undocumented way - // TODO: Restart Caliptra - // TODO: Verify crypto operations can be performed + // Attempt to clear the error in an undocumented way + // Clear the error reg and attempt output again + // Now that we have cleared the error, we expect an error code of 0 because + // The fatal error loop that marks all mbox messages as failed does not update the error code + hw.soc_ifc().cptra_fw_error_fatal().write(|_| 0); + hw.soc_ifc().cptra_fw_error_non_fatal().write(|_| 0); + match hw.upload_firmware(&fw_image) { + Ok(_) => panic!("FW Load should fail at this point"), + Err(ModelError::MailboxCmdFailed(0x0)) => (), + Err(e) => panic!("FW Load received unexpected error {}", e), + } + verify_sha_engine_output_inhibited(&mut hw); + + // This error cannot be cleared. } + +// TODO: Enable once https://github.com/chipsalliance/caliptra-sw/issues/1598 is addressed +// Operations with invalid key pairs not supported by SW emulator +// #[test] +// #[cfg(not(feature = "test_env_immutable_rom"))] +// #[cfg(any(feature = "verilator", feature = "fpga_realtime"))] +// pub fn ecc384_pairwise_consistency_error() { +// self_test_failure_flow_rom( +// FipsTestHook::ECC384_PAIRWISE_CONSISTENCY_ERROR, +// u32::from(CaliptraError::DRIVER_ECC384_KEYGEN_PAIRWISE_CONSISTENCY_FAILURE), +// ); +// } diff --git a/test/tests/fips_test_suite/services.rs b/test/tests/fips_test_suite/services.rs index e49e27b5ab..ff4105686c 100755 --- a/test/tests/fips_test_suite/services.rs +++ b/test/tests/fips_test_suite/services.rs @@ -1,10 +1,13 @@ // Licensed under the Apache-2.0 license use crate::common; +use caliptra_builder::firmware::{APP_WITH_UART_FIPS_TEST_HOOKS, FMC_WITH_UART}; +use caliptra_builder::ImageOptions; use caliptra_common::fips::FipsVersionCmd; use caliptra_common::mailbox_api::*; use caliptra_drivers::CaliptraError; -use caliptra_hw_model::{BootParams, HwModel, ModelError, ShaAccMode}; +use caliptra_drivers::FipsTestHook; +use caliptra_hw_model::{BootParams, HwModel, InitParams, ModelError, ShaAccMode}; use caliptra_image_types::ImageManifest; use common::*; use dpe::{commands::*, context::ContextHandle, response::Response, DPE_PROFILE}; @@ -42,6 +45,9 @@ pub fn exec_cmd_version(hw: &mut T, fmc_version: u16, app_version: u ) .unwrap(); + println!("Expecting app version of {}", app_version); + println!("Received version of of {:?}", version_resp.fips_rev); + // Verify command-specific response data assert_eq!(version_resp.mode, FipsVersionCmd::MODE); let fw_version_0_expected = @@ -611,6 +617,30 @@ pub fn check_version_rt() { ); } +#[test] +pub fn version_info_update() { + let mut hw = fips_test_init_to_rom(None, None); + + let pre_load_fmc_version = 0x0; + let pre_load_fw_version = 0x0; + let fmc_version = RtExpVals::get().fmc_version; + let fw_version = RtExpVals::get().fw_version; + + // Prove the expected versions are different + assert!(fmc_version != 0x0); + assert!(fw_version != 0x0); + + // Check pre-load versions + exec_cmd_version(&mut hw, pre_load_fmc_version, pre_load_fw_version); + + // Load the FW + let fw_image = fips_fw_image(); + hw.upload_firmware(&fw_image).unwrap(); + + // FMC and FW version should be populated after loading FW + exec_cmd_version(&mut hw, fmc_version, fw_version); +} + #[test] pub fn execute_all_services_rom() { let mut hw = fips_test_init_to_rom(None, None); @@ -726,3 +756,63 @@ pub fn execute_all_services_rt() { // SHUTDOWN exec_cmd_shutdown(&mut hw); } + +#[test] +pub fn zeroize_halt_check_no_output() { + // Build FW with test hooks and init to runtime + let fw_image = caliptra_builder::build_and_sign_image( + &FMC_WITH_UART, + &APP_WITH_UART_FIPS_TEST_HOOKS, + ImageOptions::default(), + ) + .unwrap() + .to_bytes() + .unwrap(); + + let mut hw = fips_test_init_to_rt( + Some(InitParams { + ..Default::default() + }), + Some(BootParams { + fw_image: Some(&fw_image), + initial_dbg_manuf_service_reg: (FipsTestHook::HALT_SHUTDOWN_RT as u32) + << HOOK_CODE_OFFSET, + ..Default::default() + }), + ); + + // Send the shutdown command (do not wait for response) + let payload = MailboxReqHeader { + chksum: caliptra_common::checksum::calc_checksum(u32::from(CommandId::SHUTDOWN), &[]), + }; + hw.start_mailbox_execute(u32::from(CommandId::SHUTDOWN), payload.as_bytes()) + .unwrap(); + + // Wait for ACK that ROM reached halt point + hook_wait_for_complete(&mut hw); + + // Check output is inhibited + verify_output_inhibited(&mut hw); +} + +#[test] +pub fn fips_self_test_rom() { + let mut hw = fips_test_init_to_rom(None, None); + + // SELF TEST START + exec_cmd_self_test_start(&mut hw); + + // SELF TEST GET RESULTS + exec_cmd_self_test_get_results(&mut hw); +} + +#[test] +pub fn fips_self_test_rt() { + let mut hw = fips_test_init_to_rt(None, None); + + // SELF TEST START + exec_cmd_self_test_start(&mut hw); + + // SELF TEST GET RESULTS + exec_cmd_self_test_get_results(&mut hw); +} diff --git a/ureg/src/lib.rs b/ureg/src/lib.rs index 9a21d2aefa..45e0413fb9 100644 --- a/ureg/src/lib.rs +++ b/ureg/src/lib.rs @@ -331,7 +331,7 @@ pub trait FromMmioPtr { #[derive(Clone, Copy)] pub struct RegRef { mmio: TMmio, - ptr: *mut TReg::Raw, + pub ptr: *mut TReg::Raw, } impl RegRef { /// Creates a new RegRef from a raw register pointer and Mmio implementation.