From d0254252c4142ff180cb60bb993c28dbef543dde Mon Sep 17 00:00:00 2001 From: Vishal Mhatre Date: Mon, 2 Dec 2024 12:03:38 -0800 Subject: [PATCH 01/11] [update] Manifest Based Authorization feature update. This PR makes the following changes to the feature: 1. Adds a 'firmware id' to the image metadata entry (IME) structure. 2. Reduces max IME limit from 128 to 127 to account for size increase due to the firmware id addition. 3. SET_AUTH_MANIFEST command validates that the IME collection does not contain duplicate firmware ids. 4. SET_AUTH_MANIFEST command sorts the IME on the firmware ids. 5. Adds a new bit to the IME flags named 'SKIP AUTHORIZATION'. If set, AUTHORIZE_AND_STASH command authorizes an IME with a matching firmware id ignoring the image hash. 6. If a firmware id is not found, AUTHORIZE_AND_STASH command returns IMAGE_NOT_AUTHORIZED status code in the command output. 7. If a firmware id is found but the image hash does not match, AUTHORIZE_AND_STASH returns IMAGE_HASH_MISMATCH status code in the command output. 8. If a firmware id is found and the image hash matches or if the 'SKIP AUTHORIZATION' flag is set for the the firmware id, AUTHORIZE_AND_STASH command returns IMAGE_AUTHORIZED status code in the command output. 9. AUTHORIZE_AND_STASH command uses binary search on the firmware id to lookup an IME. --- Cargo.lock | 1 + api/src/mailbox.rs | 4 +- auth-manifest/app/src/auth-man.toml | 6 ++ auth-manifest/app/src/config.rs | 21 ++++- auth-manifest/types/Cargo.toml | 1 + auth-manifest/types/src/lib.rs | 24 ++++-- error/src/lib.rs | 2 + runtime/src/authorize_and_stash.rs | 78 ++++++++++++++++--- runtime/src/lib.rs | 2 +- runtime/src/set_auth_manifest.rs | 64 ++++++++++++--- .../test_authorize_and_stash.rs | 13 ++-- .../test_set_auth_manifest.rs | 44 +++++++---- 12 files changed, 202 insertions(+), 58 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 87edca4772..e0303892f0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -244,6 +244,7 @@ name = "caliptra-auth-man-types" version = "0.1.0" dependencies = [ "arbitrary", + "bitfield", "bitflags 2.4.0", "caliptra-error", "caliptra-image-types", diff --git a/api/src/mailbox.rs b/api/src/mailbox.rs index 9db5ff59f0..d319cff0f7 100644 --- a/api/src/mailbox.rs +++ b/api/src/mailbox.rs @@ -1059,8 +1059,8 @@ impl AuthAndStashFlags { #[derive(Debug, AsBytes, FromBytes, PartialEq, Eq)] pub struct AuthorizeAndStashReq { pub hdr: MailboxReqHeader, - pub metadata: [u8; 4], - pub measurement: [u8; 48], + pub metadata: [u8; 4], // Firmware Id. + pub measurement: [u8; 48], // Image digest. pub context: [u8; 48], pub svn: u32, pub flags: u32, diff --git a/auth-manifest/app/src/auth-man.toml b/auth-manifest/app/src/auth-man.toml index e141a7446b..f0d5047f19 100644 --- a/auth-manifest/app/src/auth-man.toml +++ b/auth-manifest/app/src/auth-man.toml @@ -27,11 +27,17 @@ lms_priv_key = "own-lms-priv-key.pem" [[image_metadata_list]] digest = "C120EED0004B4CF6C344B00F5F501E7B7167C7010B6EA1D36AEE20CC90F1AE373DF1EC91C9AD9E0A5A969326A54E2517" source = 1 +fw_id = 1 +ignore_auth_check = false [[image_metadata_list]] digest = "99514329186b2f6ae4a1329e7ee6c610a729636335174ac6b740f9028396fcc803d0e93863a7c3d90f86beee782f4f3f" source = 2 +fw_id = 2 +ignore_auth_check = true [[image_metadata_list]] digest = "9B514329186b2f6ae4a1329e7ee6c610a729636335174ac6b740f9028396fcc803d0e93863a7c3d90f86beee782f4f3f" source = 2 +fw_id = 3 +ignore_auth_check = false diff --git a/auth-manifest/app/src/config.rs b/auth-manifest/app/src/config.rs index a94f966b00..8e9a207b5a 100644 --- a/auth-manifest/app/src/config.rs +++ b/auth-manifest/app/src/config.rs @@ -14,8 +14,8 @@ Abstract: use anyhow::Context; use caliptra_auth_man_gen::AuthManifestGeneratorKeyConfig; -use caliptra_auth_man_types::AuthManifestPubKeys; use caliptra_auth_man_types::{AuthManifestImageMetadata, AuthManifestPrivKeys}; +use caliptra_auth_man_types::{AuthManifestPubKeys, ImageMetadataFlags}; #[cfg(feature = "openssl")] use caliptra_image_crypto::OsslCrypto as Crypto; #[cfg(feature = "rustcrypto")] @@ -41,6 +41,8 @@ pub(crate) struct AuthManifestKeyConfigFromFile { pub struct ImageMetadataConfigFromFile { digest: String, source: u32, + fw_id: u32, + ignore_auth_check: bool, } // Authorization Manifest configuration from TOML file @@ -119,14 +121,27 @@ pub(crate) fn image_metadata_config_from_file( config: &Vec, ) -> anyhow::Result> { let mut image_metadata_list = Vec::new(); + let mut fw_ids: Vec = Vec::new(); for image in config { + // Check if the firmware ID is already present in the list. + if fw_ids.contains(&image.fw_id) { + return Err(anyhow::anyhow!( + "Duplicate firmware ID found in the image metadata list" + )); + } else { + fw_ids.push(image.fw_id); + } + let digest_vec = hex::decode(&image.digest)?; - let image_source = image.source; + let mut flags = ImageMetadataFlags(0); + flags.set_ignore_auth_check(image.ignore_auth_check); + flags.set_image_source(image.source); let image_metadata = AuthManifestImageMetadata { + fw_id: image.fw_id, + flags: flags.0, digest: digest_vec.try_into().unwrap(), - image_source, }; image_metadata_list.push(image_metadata); diff --git a/auth-manifest/types/Cargo.toml b/auth-manifest/types/Cargo.toml index 0f43ee3672..33d9dfcd55 100644 --- a/auth-manifest/types/Cargo.toml +++ b/auth-manifest/types/Cargo.toml @@ -17,6 +17,7 @@ caliptra-error = { workspace = true, default-features = false } zeroize.workspace = true caliptra-image-types = { workspace = true, default-features = false } bitflags.workspace = true +bitfield.workspace = true [features] default = ["std"] diff --git a/auth-manifest/types/src/lib.rs b/auth-manifest/types/src/lib.rs index a6aa13e67b..5b4818cc91 100644 --- a/auth-manifest/types/src/lib.rs +++ b/auth-manifest/types/src/lib.rs @@ -14,16 +14,16 @@ Abstract: #![no_std] -use core::ops::Range; - +use bitfield::bitfield; use caliptra_image_types::*; use core::default::Default; +use core::ops::Range; use memoffset::span_of; use zerocopy::{AsBytes, FromBytes}; use zeroize::Zeroize; pub const AUTH_MANIFEST_MARKER: u32 = 0x4154_4D4E; -pub const AUTH_MANIFEST_IMAGE_METADATA_MAX_COUNT: usize = 128; +pub const AUTH_MANIFEST_IMAGE_METADATA_MAX_COUNT: usize = 127; bitflags::bitflags! { #[derive(Default, Copy, Clone, Debug)] @@ -76,7 +76,7 @@ pub struct AuthManifestPreamble { pub version: u32, - pub flags: u32, + pub flags: u32, // AuthManifestFlags(VENDOR_SIGNATURE_REQUIRED) pub vendor_pub_keys: AuthManifestPubKeys, @@ -129,21 +129,31 @@ impl AuthManifestPreamble { } } +bitfield! { + #[derive(Debug, PartialEq, Eq, Clone, Copy)] + pub struct ImageMetadataFlags(u32); + pub image_source, set_image_source: 1, 0; + pub ignore_auth_check, set_ignore_auth_check: 2; +} + /// Caliptra Authorization Manifest Image Metadata #[repr(C)] #[derive(AsBytes, FromBytes, Clone, Copy, Debug, Zeroize)] #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] pub struct AuthManifestImageMetadata { - pub digest: [u8; 48], + pub fw_id: u32, - pub image_source: u32, + pub flags: u32, // ImageMetadataFlags(image_source, ignore_auth_check) + + pub digest: [u8; 48], } impl Default for AuthManifestImageMetadata { fn default() -> Self { AuthManifestImageMetadata { + fw_id: u32::MAX, + flags: 0, digest: [0; 48], - image_source: 0, } } } diff --git a/error/src/lib.rs b/error/src/lib.rs index 750a1611ba..2bd9d10957 100644 --- a/error/src/lib.rs +++ b/error/src/lib.rs @@ -447,6 +447,8 @@ impl CaliptraError { CaliptraError::new_const(0x000E0051); pub const RUNTIME_GET_IDEV_ID_UNSUPPORTED_ROM: CaliptraError = CaliptraError::new_const(0x000E0052); + pub const RUNTIME_AUTH_MANIFEST_IMAGE_METADATA_LIST_DUPLICATE_FIRMWARE_ID: CaliptraError = + CaliptraError::new_const(0x000E0053); /// FMC Errors pub const FMC_GLOBAL_NMI: CaliptraError = CaliptraError::new_const(0x000F0001); diff --git a/runtime/src/authorize_and_stash.rs b/runtime/src/authorize_and_stash.rs index 9f74db5ba8..91ea8b4f42 100644 --- a/runtime/src/authorize_and_stash.rs +++ b/runtime/src/authorize_and_stash.rs @@ -12,12 +12,13 @@ Abstract: --*/ -use core::cmp::min; +use core::cmp::{self, min}; use core::mem::size_of; use crate::{dpe_crypto::DpeCrypto, CptraDpeTypes, DpePlatform, Drivers, StashMeasurementCmd}; use caliptra_auth_man_types::{ - AuthManifestImageMetadataCollection, AuthManifestPreamble, AUTH_MANIFEST_MARKER, + AuthManifestImageMetadata, AuthManifestImageMetadataCollection, AuthManifestPreamble, + ImageMetadataFlags, AUTH_MANIFEST_MARKER, }; use caliptra_cfi_derive_git::cfi_impl_fn; use caliptra_cfi_lib_git::cfi_launder; @@ -44,8 +45,9 @@ use dpe::{ use memoffset::offset_of; use zerocopy::{AsBytes, FromBytes}; -pub const AUTHORIZE_IMAGE: u32 = 0xDEADC0DE; -pub const DENY_IMAGE_AUTHORIZATION: u32 = 0x21523F21; +pub const IMAGE_AUTHORIZED: u32 = 0xDEADC0DE; // Either FW ID and image digest matched or 'ignore_auth_check' is set for the FW ID. +pub const IMAGE_NOT_AUTHORIZED: u32 = 0x21523F21; // FW ID not found in the image metadata entry collection. +pub const IMAGE_HASH_MISMATCH: u32 = 0x8BFB95CB; // FW ID matched, but image digest mismatched. pub struct AuthorizeAndStashCmd; impl AuthorizeAndStashCmd { @@ -57,25 +59,35 @@ impl AuthorizeAndStashCmd { Err(CaliptraError::RUNTIME_AUTH_AND_STASH_UNSUPPORTED_IMAGE_SOURCE)?; } - // Check if image hash is present in the image metadata entry collection. + // Check if firmware id is present in the image metadata entry collection. let persistent_data = drivers.persistent_data.get(); let auth_manifest_image_metadata_col = &persistent_data.auth_manifest_image_metadata_col; - let mut auth_result = DENY_IMAGE_AUTHORIZATION; - for metadata_entry in auth_manifest_image_metadata_col.image_metadata_list.iter() { - if cfi_launder(metadata_entry.digest) == cmd.measurement { + let auth_result = if let Some(metadata_entry) = + Self::find_metadata_entry(auth_manifest_image_metadata_col, &cmd.metadata) + { + // If 'ignore_auth_check' is set, then skip the image digest comparison and authorize the image. + let flags = ImageMetadataFlags(metadata_entry.flags); + let ignore_auth_check = flags.ignore_auth_check(); + + if flags.ignore_auth_check() == cfi_launder(ignore_auth_check) { + IMAGE_AUTHORIZED + } else if cfi_launder(metadata_entry.digest) == cmd.measurement { caliptra_cfi_lib_git::cfi_assert_eq_12_words( &Array4x12::from(metadata_entry.digest).0, &Array4x12::from(cmd.measurement).0, ); - auth_result = AUTHORIZE_IMAGE; - break; + IMAGE_AUTHORIZED + } else { + IMAGE_HASH_MISMATCH } - } + } else { + IMAGE_NOT_AUTHORIZED + }; // Stash the measurement if the image is authorized. - if auth_result == AUTHORIZE_IMAGE { + if auth_result == IMAGE_AUTHORIZED { let flags: AuthAndStashFlags = cmd.flags.into(); if !flags.contains(AuthAndStashFlags::SKIP_STASH) { let dpe_result = StashMeasurementCmd::stash_measurement( @@ -100,4 +112,46 @@ impl AuthorizeAndStashCmd { Err(CaliptraError::RUNTIME_INSUFFICIENT_MEMORY) } } + + /// Search for a metadata entry in the sorted `AuthManifestImageMetadataCollection` that matches the firmware ID. + /// + /// This function performs a binary search on the `image_metadata_list` of the provided `AuthManifestImageMetadataCollection`. + /// It compares the firmware ID (`fw_id`) of each metadata entry with the provided `cmd_fw_id_bytes`. + /// + /// # Arguments + /// + /// * `auth_manifest_image_metadata_col` - A reference to the `AuthManifestImageMetadataCollection` containing the metadata entries. + /// * `cmd_fw_id_bytes` - A reference to a `[u8; 4]` array representing the firmware ID from the command to search for. + /// + /// # Returns + /// + /// * `Option<&'a AuthManifestImageMetadata>` - Returns `Some(&AuthManifestImageMetadata)` if a matching entry is found, + /// otherwise returns `None`. + /// + #[inline(never)] + fn find_metadata_entry<'a>( + auth_manifest_image_metadata_col: &'a AuthManifestImageMetadataCollection, + cmd_fw_id_bytes: &[u8; 4], + ) -> Option<&'a AuthManifestImageMetadata> { + let mut left = 0; + let mut right = auth_manifest_image_metadata_col.entry_count as usize; + + while left < right { + let mid = (left + right) / 2; + // This check is needed to avoid out of bounds panic. + if mid >= auth_manifest_image_metadata_col.image_metadata_list.len() { + break; + } + let metadata_entry = &auth_manifest_image_metadata_col.image_metadata_list[mid]; + let entry_fw_id_bytes = metadata_entry.fw_id.to_le_bytes(); + + match cmd_fw_id_bytes.cmp(&entry_fw_id_bytes) { + cmp::Ordering::Less => right = mid, + cmp::Ordering::Greater => left = mid + 1, + cmp::Ordering::Equal => return Some(metadata_entry), + } + } + + None + } } diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 2e4fe0b862..a77f8e0385 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -47,7 +47,7 @@ use crate::capabilities::CapabilitiesCmd; pub use crate::certify_key_extended::CertifyKeyExtendedCmd; pub use crate::hmac::Hmac; pub use crate::subject_alt_name::AddSubjectAltNameCmd; -pub use authorize_and_stash::{AUTHORIZE_IMAGE, DENY_IMAGE_AUTHORIZATION}; +pub use authorize_and_stash::{IMAGE_AUTHORIZED, IMAGE_NOT_AUTHORIZED}; pub use caliptra_common::fips::FipsVersionCmd; pub use dice::{GetFmcAliasCertCmd, GetLdevCertCmd, IDevIdCertCmd}; pub use disable::DisableAttestationCmd; diff --git a/runtime/src/set_auth_manifest.rs b/runtime/src/set_auth_manifest.rs index 21a1592af4..baa93f97d7 100644 --- a/runtime/src/set_auth_manifest.rs +++ b/runtime/src/set_auth_manifest.rs @@ -18,8 +18,8 @@ use core::mem::size_of; use crate::verify; use crate::{dpe_crypto::DpeCrypto, CptraDpeTypes, DpePlatform, Drivers}; use caliptra_auth_man_types::{ - AuthManifestFlags, AuthManifestImageMetadataCollection, AuthManifestPreamble, - AUTH_MANIFEST_IMAGE_METADATA_MAX_COUNT, AUTH_MANIFEST_MARKER, + AuthManifestFlags, AuthManifestImageMetadata, AuthManifestImageMetadataCollection, + AuthManifestPreamble, AUTH_MANIFEST_IMAGE_METADATA_MAX_COUNT, AUTH_MANIFEST_MARKER, }; use caliptra_cfi_derive_git::cfi_impl_fn; use caliptra_cfi_lib_git::cfi_launder; @@ -44,17 +44,18 @@ use dpe::{ }; use memoffset::offset_of; use zerocopy::{AsBytes, FromBytes}; +use zeroize::Zeroize; pub struct SetAuthManifestCmd; impl SetAuthManifestCmd { fn sha384_digest( sha384: &mut Sha384, - manifest: &[u8], + buf: &[u8], offset: u32, len: u32, ) -> CaliptraResult { let err = CaliptraError::IMAGE_VERIFIER_ERR_DIGEST_OUT_OF_BOUNDS; - let data = manifest + let data = buf .get(offset as usize..) .ok_or(err)? .get(..len as usize) @@ -355,7 +356,7 @@ impl SetAuthManifestCmd { fn process_image_metadata_col( cmd_buf: &[u8], auth_manifest_preamble: &AuthManifestPreamble, - image_metadata_col: &mut AuthManifestImageMetadataCollection, + metadata_persistent: &mut AuthManifestImageMetadataCollection, sha384: &mut Sha384, ecc384: &mut Ecc384, sha256: &mut Sha256, @@ -365,23 +366,36 @@ impl SetAuthManifestCmd { Err(CaliptraError::RUNTIME_AUTH_MANIFEST_IMAGE_METADATA_LIST_INVALID_SIZE)?; } - let col_size = min( + let metadata_size = min( cmd_buf.len(), size_of::(), ); + + // Resize the buffer to the metadata size. let buf = cmd_buf - .get(..col_size) + .get(..metadata_size) .ok_or(CaliptraError::RUNTIME_AUTH_MANIFEST_IMAGE_METADATA_LIST_INVALID_SIZE)?; - image_metadata_col.as_bytes_mut()[..col_size].copy_from_slice(buf); + // Typecast the mailbox buffer to the image metadata collection. + let metadata_mailbox = + unsafe { &mut *(buf.as_ptr() as *mut AuthManifestImageMetadataCollection) }; - if image_metadata_col.entry_count == 0 - || image_metadata_col.entry_count > AUTH_MANIFEST_IMAGE_METADATA_MAX_COUNT as u32 + if metadata_mailbox.entry_count == 0 + || metadata_mailbox.entry_count > AUTH_MANIFEST_IMAGE_METADATA_MAX_COUNT as u32 { Err(CaliptraError::RUNTIME_AUTH_MANIFEST_IMAGE_METADATA_LIST_INVALID_ENTRY_COUNT)?; } - let digest_metadata_col = Self::sha384_digest(sha384, buf, 0, col_size as u32)?; + // Check if the buffer contains the entry count and all the image metadata entries specified by the entry count. + if buf.len() + < (size_of::() + + metadata_mailbox.entry_count as usize * size_of::()) + { + Err(CaliptraError::RUNTIME_AUTH_MANIFEST_IMAGE_METADATA_LIST_INVALID_SIZE)?; + } + + // Calculate the digest of the image metadata collection. + let digest_metadata_col = Self::sha384_digest(sha384, buf, 0, metadata_size as u32)?; Self::verify_vendor_image_metadata_col( auth_manifest_preamble, @@ -401,6 +415,34 @@ impl SetAuthManifestCmd { soc_ifc, )?; + // Sort the image metadata list by firmware ID in place. Also check for duplicate firmware IDs. + let slice = + &mut metadata_mailbox.image_metadata_list[..metadata_mailbox.entry_count as usize]; + for i in 0..slice.len() { + for j in 0..(slice.len() - 1 - i) { + // This check is needed to avoid out of bounds access panic. + if (j + 1) >= slice.len() { + break; + } + + match slice[j].fw_id.cmp(&slice[j + 1].fw_id) { + core::cmp::Ordering::Greater => { + slice.swap(j, j + 1); + } + core::cmp::Ordering::Equal => { + Err(CaliptraError::RUNTIME_AUTH_MANIFEST_IMAGE_METADATA_LIST_DUPLICATE_FIRMWARE_ID)?; + } + _ => {} + } + } + } + + // Clear the previous image metadata collection. + metadata_persistent.zeroize(); + + // Copy the image metadata collection to the persistent data. + metadata_persistent.as_bytes_mut()[..buf.len()].copy_from_slice(buf); + Ok(()) } diff --git a/runtime/tests/runtime_integration_tests/test_authorize_and_stash.rs b/runtime/tests/runtime_integration_tests/test_authorize_and_stash.rs index 588c440656..2db401cb9b 100644 --- a/runtime/tests/runtime_integration_tests/test_authorize_and_stash.rs +++ b/runtime/tests/runtime_integration_tests/test_authorize_and_stash.rs @@ -1,7 +1,7 @@ // Licensed under the Apache-2.0 license use crate::common::{run_rt_test, RuntimeTestArgs}; -use crate::test_set_auth_manifest::test_auth_manifest; +use crate::test_set_auth_manifest::create_auth_manifest; use caliptra_api::SocManager; use caliptra_builder::{ firmware::{self, FMC_WITH_UART}, @@ -13,7 +13,7 @@ use caliptra_common::mailbox_api::{ }; use caliptra_hw_model::HwModel; use caliptra_runtime::RtBootStatus; -use caliptra_runtime::{AUTHORIZE_IMAGE, DENY_IMAGE_AUTHORIZATION}; +use caliptra_runtime::{IMAGE_AUTHORIZED, IMAGE_NOT_AUTHORIZED}; use sha2::{Digest, Sha384}; use zerocopy::AsBytes; use zerocopy::FromBytes; @@ -24,6 +24,8 @@ pub const IMAGE_DIGEST1: [u8; 48] = [ 0x27, 0x4E, 0xDE, 0xBF, 0xE7, 0x6F, 0x65, 0xFB, 0xD5, 0x1A, 0xD2, 0xF1, 0x48, 0x98, 0xB9, 0x5B, ]; +pub const FW_ID_1: [u8; 4] = [0x01, 0x00, 0x00, 0x00]; + #[test] fn test_authorize_and_stash_cmd_deny_authorization() { let mut model = run_rt_test(RuntimeTestArgs::default()); @@ -52,7 +54,7 @@ fn test_authorize_and_stash_cmd_deny_authorization() { let authorize_and_stash_resp = AuthorizeAndStashResp::read_from(resp.as_slice()).unwrap(); assert_eq!( authorize_and_stash_resp.auth_req_result, - DENY_IMAGE_AUTHORIZATION + IMAGE_NOT_AUTHORIZED ); // create a new fw image with the runtime replaced by the mbox responder @@ -94,7 +96,7 @@ fn test_authorize_and_stash_cmd_succes() { m.soc_ifc().cptra_boot_status().read() == u32::from(RtBootStatus::RtReadyForCommands) }); - let auth_manifest = test_auth_manifest(); + let auth_manifest = create_auth_manifest(); let buf = auth_manifest.as_bytes(); let mut auth_manifest_slice = [0u8; SetAuthManifestReq::MAX_MAN_SIZE]; auth_manifest_slice[..buf.len()].copy_from_slice(buf); @@ -116,6 +118,7 @@ fn test_authorize_and_stash_cmd_succes() { let mut authorize_and_stash_cmd = MailboxReq::AuthorizeAndStash(AuthorizeAndStashReq { hdr: MailboxReqHeader { chksum: 0 }, + metadata: FW_ID_1, measurement: IMAGE_DIGEST1, source: ImageHashSource::InRequest as u32, flags: 0, // Don't skip stash @@ -132,7 +135,7 @@ fn test_authorize_and_stash_cmd_succes() { .expect("We should have received a response"); let authorize_and_stash_resp = AuthorizeAndStashResp::read_from(resp.as_slice()).unwrap(); - assert_eq!(authorize_and_stash_resp.auth_req_result, AUTHORIZE_IMAGE); + assert_eq!(authorize_and_stash_resp.auth_req_result, IMAGE_AUTHORIZED); // create a new fw image with the runtime replaced by the mbox responder let updated_fw_image = caliptra_builder::build_and_sign_image( diff --git a/runtime/tests/runtime_integration_tests/test_set_auth_manifest.rs b/runtime/tests/runtime_integration_tests/test_set_auth_manifest.rs index e4db31a51b..79aae472bb 100644 --- a/runtime/tests/runtime_integration_tests/test_set_auth_manifest.rs +++ b/runtime/tests/runtime_integration_tests/test_set_auth_manifest.rs @@ -4,13 +4,13 @@ use crate::{ common::{assert_error, run_rt_test_lms, RuntimeTestArgs}, test_authorize_and_stash::IMAGE_DIGEST1, }; -use caliptra_api::SocManager; +use caliptra_api::{mailbox::ImageHashSource, SocManager}; use caliptra_auth_man_gen::{ AuthManifestGenerator, AuthManifestGeneratorConfig, AuthManifestGeneratorKeyConfig, }; use caliptra_auth_man_types::{ AuthManifestFlags, AuthManifestImageMetadata, AuthManifestPrivKeys, AuthManifestPubKeys, - AuthorizationManifest, + AuthorizationManifest, ImageMetadataFlags, }; use caliptra_common::mailbox_api::{CommandId, MailboxReq, MailboxReqHeader, SetAuthManifestReq}; use caliptra_error::CaliptraError; @@ -20,7 +20,7 @@ use caliptra_image_fake_keys::*; use caliptra_runtime::RtBootStatus; use zerocopy::AsBytes; -pub fn test_auth_manifest() -> AuthorizationManifest { +pub fn create_auth_manifest() -> AuthorizationManifest { let vendor_fw_key_info: AuthManifestGeneratorKeyConfig = AuthManifestGeneratorKeyConfig { pub_keys: AuthManifestPubKeys { ecc_pub_key: VENDOR_ECC_KEY_0_PUBLIC, @@ -74,14 +74,24 @@ pub fn test_auth_manifest() -> AuthorizationManifest { 0xC8, 0x25, 0xA7, ]; + let mut flags1 = ImageMetadataFlags(0); + flags1.set_ignore_auth_check(false); + flags1.set_image_source(ImageHashSource::InRequest as u32); + + let mut flags2 = ImageMetadataFlags(0); + flags2.set_ignore_auth_check(true); + flags2.set_image_source(ImageHashSource::ShaAcc as u32); + // Generate authorization manifest. let image_metadata_list: Vec = vec![ AuthManifestImageMetadata { - image_source: 0, + fw_id: 1, + flags: flags1.0, digest: IMAGE_DIGEST1, }, AuthManifestImageMetadata { - image_source: 1, + fw_id: 2, + flags: flags2.0, digest: image_digest2, }, ]; @@ -108,7 +118,7 @@ fn test_set_auth_manifest_cmd() { m.soc_ifc().cptra_boot_status().read() == u32::from(RtBootStatus::RtReadyForCommands) }); - let auth_manifest = test_auth_manifest(); + let auth_manifest = create_auth_manifest(); let buf = auth_manifest.as_bytes(); let mut auth_manifest_slice = [0u8; SetAuthManifestReq::MAX_MAN_SIZE]; auth_manifest_slice[..buf.len()].copy_from_slice(buf); @@ -208,7 +218,7 @@ fn test_manifest_expect_err(manifest: AuthorizationManifest, expected_err: Calip #[test] fn test_set_auth_manifest_invalid_preamble_marker() { - let mut auth_manifest = test_auth_manifest(); + let mut auth_manifest = create_auth_manifest(); auth_manifest.preamble.marker = Default::default(); test_manifest_expect_err( auth_manifest, @@ -218,7 +228,7 @@ fn test_set_auth_manifest_invalid_preamble_marker() { #[test] fn test_set_auth_manifest_invalid_preamble_size() { - let mut auth_manifest = test_auth_manifest(); + let mut auth_manifest = create_auth_manifest(); auth_manifest.preamble.size -= 1; test_manifest_expect_err( auth_manifest, @@ -228,7 +238,7 @@ fn test_set_auth_manifest_invalid_preamble_size() { #[test] fn test_set_auth_manifest_invalid_vendor_ecc_sig() { - let mut auth_manifest = test_auth_manifest(); + let mut auth_manifest = create_auth_manifest(); auth_manifest.preamble.vendor_pub_keys_signatures.ecc_sig = Default::default(); test_manifest_expect_err( auth_manifest, @@ -238,7 +248,7 @@ fn test_set_auth_manifest_invalid_vendor_ecc_sig() { #[test] fn test_set_auth_manifest_invalid_vendor_lms_sig() { - let mut auth_manifest = test_auth_manifest(); + let mut auth_manifest = create_auth_manifest(); auth_manifest.preamble.vendor_pub_keys_signatures.lms_sig = Default::default(); test_manifest_expect_err( auth_manifest, @@ -248,7 +258,7 @@ fn test_set_auth_manifest_invalid_vendor_lms_sig() { #[test] fn test_set_auth_manifest_invalid_owner_ecc_sig() { - let mut auth_manifest = test_auth_manifest(); + let mut auth_manifest = create_auth_manifest(); auth_manifest.preamble.owner_pub_keys_signatures.ecc_sig = Default::default(); test_manifest_expect_err( auth_manifest, @@ -258,7 +268,7 @@ fn test_set_auth_manifest_invalid_owner_ecc_sig() { #[test] fn test_set_auth_manifest_invalid_owner_lms_sig() { - let mut auth_manifest = test_auth_manifest(); + let mut auth_manifest = create_auth_manifest(); auth_manifest.preamble.owner_pub_keys_signatures.lms_sig = Default::default(); test_manifest_expect_err( auth_manifest, @@ -268,7 +278,7 @@ fn test_set_auth_manifest_invalid_owner_lms_sig() { #[test] fn test_set_auth_manifest_invalid_metadata_list_count() { - let mut auth_manifest = test_auth_manifest(); + let mut auth_manifest = create_auth_manifest(); auth_manifest.image_metadata_col.entry_count = 0; test_manifest_expect_err( auth_manifest, @@ -278,7 +288,7 @@ fn test_set_auth_manifest_invalid_metadata_list_count() { #[test] fn test_set_auth_manifest_invalid_vendor_metadata_ecc_sig() { - let mut auth_manifest = test_auth_manifest(); + let mut auth_manifest = create_auth_manifest(); auth_manifest .preamble .vendor_image_metdata_signatures @@ -291,7 +301,7 @@ fn test_set_auth_manifest_invalid_vendor_metadata_ecc_sig() { #[test] fn test_set_auth_manifest_invalid_vendor_metadata_lms_sig() { - let mut auth_manifest = test_auth_manifest(); + let mut auth_manifest = create_auth_manifest(); auth_manifest .preamble .vendor_image_metdata_signatures @@ -304,7 +314,7 @@ fn test_set_auth_manifest_invalid_vendor_metadata_lms_sig() { #[test] fn test_set_auth_manifest_invalid_owner_metadata_ecc_sig() { - let mut auth_manifest = test_auth_manifest(); + let mut auth_manifest = create_auth_manifest(); auth_manifest .preamble .owner_image_metdata_signatures @@ -317,7 +327,7 @@ fn test_set_auth_manifest_invalid_owner_metadata_ecc_sig() { #[test] fn test_set_auth_manifest_invalid_owner_metadata_lms_sig() { - let mut auth_manifest = test_auth_manifest(); + let mut auth_manifest = create_auth_manifest(); auth_manifest .preamble .owner_image_metdata_signatures From 925da0a86a32cca3784f8643b01271d499f7ddc4 Mon Sep 17 00:00:00 2001 From: Max Timkovich Date: Mon, 2 Dec 2024 17:50:22 -0800 Subject: [PATCH 02/11] set auth manifest tests --- Cargo.lock | 1 + runtime/Cargo.toml | 3 +- .../test_set_auth_manifest.rs | 129 +++++++++++++++++- 3 files changed, 131 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e0303892f0..8c7e05fd8d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -803,6 +803,7 @@ dependencies = [ name = "caliptra-runtime" version = "0.1.0" dependencies = [ + "anyhow", "arrayvec", "bitflags 2.4.0", "caliptra-api", diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index 2a70738d43..3c8844f7f8 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -36,6 +36,7 @@ caliptra-gen-linker-scripts.workspace = true cfg-if.workspace = true [dev-dependencies] +anyhow.workspace = true caliptra-api.workspace = true caliptra-builder.workspace = true caliptra-hw-model.workspace = true @@ -64,4 +65,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"] \ No newline at end of file +fips-test-hooks = ["caliptra-drivers/fips-test-hooks"] diff --git a/runtime/tests/runtime_integration_tests/test_set_auth_manifest.rs b/runtime/tests/runtime_integration_tests/test_set_auth_manifest.rs index 79aae472bb..c3696511bf 100644 --- a/runtime/tests/runtime_integration_tests/test_set_auth_manifest.rs +++ b/runtime/tests/runtime_integration_tests/test_set_auth_manifest.rs @@ -10,7 +10,7 @@ use caliptra_auth_man_gen::{ }; use caliptra_auth_man_types::{ AuthManifestFlags, AuthManifestImageMetadata, AuthManifestPrivKeys, AuthManifestPubKeys, - AuthorizationManifest, ImageMetadataFlags, + AuthorizationManifest, ImageMetadataFlags, AUTH_MANIFEST_IMAGE_METADATA_MAX_COUNT, }; use caliptra_common::mailbox_api::{CommandId, MailboxReq, MailboxReqHeader, SetAuthManifestReq}; use caliptra_error::CaliptraError; @@ -110,6 +110,88 @@ pub fn create_auth_manifest() -> AuthorizationManifest { gen.generate(&gen_config).unwrap() } +fn create_auth_manifest_of_size(metadata_size: usize) -> anyhow::Result { + let vendor_fw_key_info: AuthManifestGeneratorKeyConfig = AuthManifestGeneratorKeyConfig { + pub_keys: AuthManifestPubKeys { + ecc_pub_key: VENDOR_ECC_KEY_0_PUBLIC, + lms_pub_key: VENDOR_LMS_KEY_0_PUBLIC, + }, + priv_keys: Some(AuthManifestPrivKeys { + ecc_priv_key: VENDOR_ECC_KEY_0_PRIVATE, + lms_priv_key: VENDOR_LMS_KEY_0_PRIVATE, + }), + }; + + let vendor_man_key_info: AuthManifestGeneratorKeyConfig = AuthManifestGeneratorKeyConfig { + pub_keys: AuthManifestPubKeys { + ecc_pub_key: VENDOR_ECC_KEY_1_PUBLIC, + lms_pub_key: VENDOR_LMS_KEY_1_PUBLIC, + }, + priv_keys: Some(AuthManifestPrivKeys { + ecc_priv_key: VENDOR_ECC_KEY_1_PRIVATE, + lms_priv_key: VENDOR_LMS_KEY_1_PRIVATE, + }), + }; + + let owner_fw_key_info: Option = + Some(AuthManifestGeneratorKeyConfig { + pub_keys: AuthManifestPubKeys { + ecc_pub_key: OWNER_ECC_KEY_PUBLIC, + lms_pub_key: OWNER_LMS_KEY_PUBLIC, + }, + priv_keys: Some(AuthManifestPrivKeys { + ecc_priv_key: OWNER_ECC_KEY_PRIVATE, + lms_priv_key: OWNER_LMS_KEY_PRIVATE, + }), + }); + + let owner_man_key_info: Option = + Some(AuthManifestGeneratorKeyConfig { + pub_keys: AuthManifestPubKeys { + ecc_pub_key: OWNER_ECC_KEY_PUBLIC, + lms_pub_key: OWNER_LMS_KEY_PUBLIC, + }, + priv_keys: Some(AuthManifestPrivKeys { + ecc_priv_key: OWNER_ECC_KEY_PRIVATE, + lms_priv_key: OWNER_LMS_KEY_PRIVATE, + }), + }); + + // let image_digest2: [u8; 48] = [ + // 0xCB, 0x00, 0x75, 0x3F, 0x45, 0xA3, 0x5E, 0x8B, 0xB5, 0xA0, 0x3D, 0x69, 0x9A, 0xC6, 0x50, + // 0x07, 0x27, 0x2C, 0x32, 0xAB, 0x0E, 0xDE, 0xD1, 0x63, 0x1A, 0x8B, 0x60, 0x5A, 0x43, 0xFF, + // 0x5B, 0xED, 0x80, 0x86, 0x07, 0x2B, 0xA1, 0xE7, 0xCC, 0x23, 0x58, 0xBA, 0xEC, 0xA1, 0x34, + // 0xC8, 0x25, 0xA7, + // ]; + + let mut flags = ImageMetadataFlags(0); + flags.set_ignore_auth_check(true); + flags.set_image_source(ImageHashSource::ShaAcc as u32); + + // Generate authorization manifest with a specific amount of elements. + let mut image_metadata_list = Vec::new(); + for id in 0..metadata_size { + image_metadata_list.push(AuthManifestImageMetadata { + fw_id: id as u32, + flags: flags.0, + digest: IMAGE_DIGEST1, + }) + } + + let gen_config: AuthManifestGeneratorConfig = AuthManifestGeneratorConfig { + vendor_fw_key_info, + vendor_man_key_info, + owner_fw_key_info, + owner_man_key_info, + image_metadata_list, + version: 1, + flags: AuthManifestFlags::VENDOR_SIGNATURE_REQUIRED, + }; + + let gen = AuthManifestGenerator::new(Crypto::default()); + gen.generate(&gen_config) +} + #[test] fn test_set_auth_manifest_cmd() { let mut model = run_rt_test_lms(RuntimeTestArgs::default(), true); @@ -216,6 +298,51 @@ fn test_manifest_expect_err(manifest: AuthorizationManifest, expected_err: Calip assert_error(&mut model, expected_err, resp); } +#[test] +fn test_set_auth_manifest_cmd_zero_metadata_entry() { + let auth_manifest = create_auth_manifest_of_size(0).unwrap(); + test_manifest_expect_err( + auth_manifest, + CaliptraError::RUNTIME_AUTH_MANIFEST_IMAGE_METADATA_LIST_INVALID_ENTRY_COUNT, + ); +} + +#[test] +fn test_set_auth_manifest_cmd_max_metadata_entry_limit() { + let auth_manifest = create_auth_manifest_of_size(AUTH_MANIFEST_IMAGE_METADATA_MAX_COUNT).unwrap(); + + let mut model = run_rt_test_lms(RuntimeTestArgs::default(), true); + + model.step_until(|m| { + m.soc_ifc().cptra_boot_status().read() == u32::from(RtBootStatus::RtReadyForCommands) + }); + + let buf = auth_manifest.as_bytes(); + let mut auth_manifest_slice = [0u8; SetAuthManifestReq::MAX_MAN_SIZE]; + auth_manifest_slice[..buf.len()].copy_from_slice(buf); + + let mut set_auth_manifest_cmd = MailboxReq::SetAuthManifest(SetAuthManifestReq { + hdr: MailboxReqHeader { chksum: 0 }, + manifest_size: buf.len() as u32, + manifest: auth_manifest_slice, + }); + set_auth_manifest_cmd.populate_chksum().unwrap(); + + model + .mailbox_execute( + u32::from(CommandId::SET_AUTH_MANIFEST), + set_auth_manifest_cmd.as_bytes().unwrap(), + ) + .unwrap() + .expect("We should have received a response"); +} + +#[test] +fn test_set_auth_manifest_cmd_max_plus_one_metadata_entry_limit() { + let auth_manifest = create_auth_manifest_of_size(AUTH_MANIFEST_IMAGE_METADATA_MAX_COUNT+1); + assert!(auth_manifest.is_err()); +} + #[test] fn test_set_auth_manifest_invalid_preamble_marker() { let mut auth_manifest = create_auth_manifest(); From 44a67dece8dc8c179f3956ab89a2821692b5aa8c Mon Sep 17 00:00:00 2001 From: Vishal Mhatre Date: Mon, 2 Dec 2024 12:03:38 -0800 Subject: [PATCH 03/11] [update] Manifest Based Authorization feature update. This PR makes the following changes to the feature: 1. Adds a 'firmware id' to the image metadata entry (IME) structure. 2. Reduces max IME limit from 128 to 127 to account for size increase due to the firmware id addition. 3. SET_AUTH_MANIFEST command validates that the IME collection does not contain duplicate firmware ids. 4. SET_AUTH_MANIFEST command sorts the IME on the firmware ids. 5. Adds a new bit to the IME flags named 'SKIP AUTHORIZATION'. If set, AUTHORIZE_AND_STASH command authorizes an IME with a matching firmware id ignoring the image hash. 6. If a firmware id is not found, AUTHORIZE_AND_STASH command returns IMAGE_NOT_AUTHORIZED status code in the command output. 7. If a firmware id is found but the image hash does not match, AUTHORIZE_AND_STASH returns IMAGE_HASH_MISMATCH status code in the command output. 8. If a firmware id is found and the image hash matches or if the 'SKIP AUTHORIZATION' flag is set for the the firmware id, AUTHORIZE_AND_STASH command returns IMAGE_AUTHORIZED status code in the command output. 9. AUTHORIZE_AND_STASH command uses binary search on the firmware id to lookup an IME. --- Cargo.lock | 1 + api/src/mailbox.rs | 4 +- auth-manifest/app/src/auth-man.toml | 6 ++ auth-manifest/app/src/config.rs | 21 ++++- auth-manifest/types/Cargo.toml | 1 + auth-manifest/types/src/lib.rs | 24 ++++-- error/src/lib.rs | 2 + runtime/src/authorize_and_stash.rs | 79 ++++++++++++++++--- runtime/src/lib.rs | 2 +- runtime/src/set_auth_manifest.rs | 64 ++++++++++++--- .../test_authorize_and_stash.rs | 13 +-- .../test_set_auth_manifest.rs | 44 +++++++---- 12 files changed, 202 insertions(+), 59 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 87edca4772..e0303892f0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -244,6 +244,7 @@ name = "caliptra-auth-man-types" version = "0.1.0" dependencies = [ "arbitrary", + "bitfield", "bitflags 2.4.0", "caliptra-error", "caliptra-image-types", diff --git a/api/src/mailbox.rs b/api/src/mailbox.rs index 9db5ff59f0..d319cff0f7 100644 --- a/api/src/mailbox.rs +++ b/api/src/mailbox.rs @@ -1059,8 +1059,8 @@ impl AuthAndStashFlags { #[derive(Debug, AsBytes, FromBytes, PartialEq, Eq)] pub struct AuthorizeAndStashReq { pub hdr: MailboxReqHeader, - pub metadata: [u8; 4], - pub measurement: [u8; 48], + pub metadata: [u8; 4], // Firmware Id. + pub measurement: [u8; 48], // Image digest. pub context: [u8; 48], pub svn: u32, pub flags: u32, diff --git a/auth-manifest/app/src/auth-man.toml b/auth-manifest/app/src/auth-man.toml index e141a7446b..f0d5047f19 100644 --- a/auth-manifest/app/src/auth-man.toml +++ b/auth-manifest/app/src/auth-man.toml @@ -27,11 +27,17 @@ lms_priv_key = "own-lms-priv-key.pem" [[image_metadata_list]] digest = "C120EED0004B4CF6C344B00F5F501E7B7167C7010B6EA1D36AEE20CC90F1AE373DF1EC91C9AD9E0A5A969326A54E2517" source = 1 +fw_id = 1 +ignore_auth_check = false [[image_metadata_list]] digest = "99514329186b2f6ae4a1329e7ee6c610a729636335174ac6b740f9028396fcc803d0e93863a7c3d90f86beee782f4f3f" source = 2 +fw_id = 2 +ignore_auth_check = true [[image_metadata_list]] digest = "9B514329186b2f6ae4a1329e7ee6c610a729636335174ac6b740f9028396fcc803d0e93863a7c3d90f86beee782f4f3f" source = 2 +fw_id = 3 +ignore_auth_check = false diff --git a/auth-manifest/app/src/config.rs b/auth-manifest/app/src/config.rs index a94f966b00..8e9a207b5a 100644 --- a/auth-manifest/app/src/config.rs +++ b/auth-manifest/app/src/config.rs @@ -14,8 +14,8 @@ Abstract: use anyhow::Context; use caliptra_auth_man_gen::AuthManifestGeneratorKeyConfig; -use caliptra_auth_man_types::AuthManifestPubKeys; use caliptra_auth_man_types::{AuthManifestImageMetadata, AuthManifestPrivKeys}; +use caliptra_auth_man_types::{AuthManifestPubKeys, ImageMetadataFlags}; #[cfg(feature = "openssl")] use caliptra_image_crypto::OsslCrypto as Crypto; #[cfg(feature = "rustcrypto")] @@ -41,6 +41,8 @@ pub(crate) struct AuthManifestKeyConfigFromFile { pub struct ImageMetadataConfigFromFile { digest: String, source: u32, + fw_id: u32, + ignore_auth_check: bool, } // Authorization Manifest configuration from TOML file @@ -119,14 +121,27 @@ pub(crate) fn image_metadata_config_from_file( config: &Vec, ) -> anyhow::Result> { let mut image_metadata_list = Vec::new(); + let mut fw_ids: Vec = Vec::new(); for image in config { + // Check if the firmware ID is already present in the list. + if fw_ids.contains(&image.fw_id) { + return Err(anyhow::anyhow!( + "Duplicate firmware ID found in the image metadata list" + )); + } else { + fw_ids.push(image.fw_id); + } + let digest_vec = hex::decode(&image.digest)?; - let image_source = image.source; + let mut flags = ImageMetadataFlags(0); + flags.set_ignore_auth_check(image.ignore_auth_check); + flags.set_image_source(image.source); let image_metadata = AuthManifestImageMetadata { + fw_id: image.fw_id, + flags: flags.0, digest: digest_vec.try_into().unwrap(), - image_source, }; image_metadata_list.push(image_metadata); diff --git a/auth-manifest/types/Cargo.toml b/auth-manifest/types/Cargo.toml index 0f43ee3672..33d9dfcd55 100644 --- a/auth-manifest/types/Cargo.toml +++ b/auth-manifest/types/Cargo.toml @@ -17,6 +17,7 @@ caliptra-error = { workspace = true, default-features = false } zeroize.workspace = true caliptra-image-types = { workspace = true, default-features = false } bitflags.workspace = true +bitfield.workspace = true [features] default = ["std"] diff --git a/auth-manifest/types/src/lib.rs b/auth-manifest/types/src/lib.rs index a6aa13e67b..5b4818cc91 100644 --- a/auth-manifest/types/src/lib.rs +++ b/auth-manifest/types/src/lib.rs @@ -14,16 +14,16 @@ Abstract: #![no_std] -use core::ops::Range; - +use bitfield::bitfield; use caliptra_image_types::*; use core::default::Default; +use core::ops::Range; use memoffset::span_of; use zerocopy::{AsBytes, FromBytes}; use zeroize::Zeroize; pub const AUTH_MANIFEST_MARKER: u32 = 0x4154_4D4E; -pub const AUTH_MANIFEST_IMAGE_METADATA_MAX_COUNT: usize = 128; +pub const AUTH_MANIFEST_IMAGE_METADATA_MAX_COUNT: usize = 127; bitflags::bitflags! { #[derive(Default, Copy, Clone, Debug)] @@ -76,7 +76,7 @@ pub struct AuthManifestPreamble { pub version: u32, - pub flags: u32, + pub flags: u32, // AuthManifestFlags(VENDOR_SIGNATURE_REQUIRED) pub vendor_pub_keys: AuthManifestPubKeys, @@ -129,21 +129,31 @@ impl AuthManifestPreamble { } } +bitfield! { + #[derive(Debug, PartialEq, Eq, Clone, Copy)] + pub struct ImageMetadataFlags(u32); + pub image_source, set_image_source: 1, 0; + pub ignore_auth_check, set_ignore_auth_check: 2; +} + /// Caliptra Authorization Manifest Image Metadata #[repr(C)] #[derive(AsBytes, FromBytes, Clone, Copy, Debug, Zeroize)] #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] pub struct AuthManifestImageMetadata { - pub digest: [u8; 48], + pub fw_id: u32, - pub image_source: u32, + pub flags: u32, // ImageMetadataFlags(image_source, ignore_auth_check) + + pub digest: [u8; 48], } impl Default for AuthManifestImageMetadata { fn default() -> Self { AuthManifestImageMetadata { + fw_id: u32::MAX, + flags: 0, digest: [0; 48], - image_source: 0, } } } diff --git a/error/src/lib.rs b/error/src/lib.rs index 750a1611ba..2bd9d10957 100644 --- a/error/src/lib.rs +++ b/error/src/lib.rs @@ -447,6 +447,8 @@ impl CaliptraError { CaliptraError::new_const(0x000E0051); pub const RUNTIME_GET_IDEV_ID_UNSUPPORTED_ROM: CaliptraError = CaliptraError::new_const(0x000E0052); + pub const RUNTIME_AUTH_MANIFEST_IMAGE_METADATA_LIST_DUPLICATE_FIRMWARE_ID: CaliptraError = + CaliptraError::new_const(0x000E0053); /// FMC Errors pub const FMC_GLOBAL_NMI: CaliptraError = CaliptraError::new_const(0x000F0001); diff --git a/runtime/src/authorize_and_stash.rs b/runtime/src/authorize_and_stash.rs index 9f74db5ba8..c0346f271e 100644 --- a/runtime/src/authorize_and_stash.rs +++ b/runtime/src/authorize_and_stash.rs @@ -12,15 +12,16 @@ Abstract: --*/ -use core::cmp::min; +use core::cmp::{self, min}; use core::mem::size_of; use crate::{dpe_crypto::DpeCrypto, CptraDpeTypes, DpePlatform, Drivers, StashMeasurementCmd}; use caliptra_auth_man_types::{ - AuthManifestImageMetadataCollection, AuthManifestPreamble, AUTH_MANIFEST_MARKER, + AuthManifestImageMetadata, AuthManifestImageMetadataCollection, AuthManifestPreamble, + ImageMetadataFlags, AUTH_MANIFEST_MARKER, }; use caliptra_cfi_derive_git::cfi_impl_fn; -use caliptra_cfi_lib_git::cfi_launder; +use caliptra_cfi_lib_git::{cfi_assert, cfi_assert_eq, cfi_launder}; use caliptra_common::mailbox_api::{ AuthAndStashFlags, AuthorizeAndStashReq, AuthorizeAndStashResp, ImageHashSource, MailboxResp, MailboxRespHeader, SetAuthManifestReq, @@ -44,8 +45,9 @@ use dpe::{ use memoffset::offset_of; use zerocopy::{AsBytes, FromBytes}; -pub const AUTHORIZE_IMAGE: u32 = 0xDEADC0DE; -pub const DENY_IMAGE_AUTHORIZATION: u32 = 0x21523F21; +pub const IMAGE_AUTHORIZED: u32 = 0xDEADC0DE; // Either FW ID and image digest matched or 'ignore_auth_check' is set for the FW ID. +pub const IMAGE_NOT_AUTHORIZED: u32 = 0x21523F21; // FW ID not found in the image metadata entry collection. +pub const IMAGE_HASH_MISMATCH: u32 = 0x8BFB95CB; // FW ID matched, but image digest mismatched. pub struct AuthorizeAndStashCmd; impl AuthorizeAndStashCmd { @@ -57,25 +59,34 @@ impl AuthorizeAndStashCmd { Err(CaliptraError::RUNTIME_AUTH_AND_STASH_UNSUPPORTED_IMAGE_SOURCE)?; } - // Check if image hash is present in the image metadata entry collection. + // Check if firmware id is present in the image metadata entry collection. let persistent_data = drivers.persistent_data.get(); let auth_manifest_image_metadata_col = &persistent_data.auth_manifest_image_metadata_col; - let mut auth_result = DENY_IMAGE_AUTHORIZATION; - for metadata_entry in auth_manifest_image_metadata_col.image_metadata_list.iter() { - if cfi_launder(metadata_entry.digest) == cmd.measurement { + let auth_result = if let Some(metadata_entry) = + Self::find_metadata_entry(auth_manifest_image_metadata_col, &cmd.metadata) + { + // If 'ignore_auth_check' is set, then skip the image digest comparison and authorize the image. + let flags = ImageMetadataFlags(metadata_entry.flags); + if flags.ignore_auth_check() { + cfi_assert!(cfi_launder(flags.ignore_auth_check())); + IMAGE_AUTHORIZED + } else if cfi_launder(metadata_entry.digest) == cmd.measurement { caliptra_cfi_lib_git::cfi_assert_eq_12_words( &Array4x12::from(metadata_entry.digest).0, &Array4x12::from(cmd.measurement).0, ); - auth_result = AUTHORIZE_IMAGE; - break; + IMAGE_AUTHORIZED + } else { + IMAGE_HASH_MISMATCH } - } + } else { + IMAGE_NOT_AUTHORIZED + }; // Stash the measurement if the image is authorized. - if auth_result == AUTHORIZE_IMAGE { + if auth_result == IMAGE_AUTHORIZED { let flags: AuthAndStashFlags = cmd.flags.into(); if !flags.contains(AuthAndStashFlags::SKIP_STASH) { let dpe_result = StashMeasurementCmd::stash_measurement( @@ -100,4 +111,46 @@ impl AuthorizeAndStashCmd { Err(CaliptraError::RUNTIME_INSUFFICIENT_MEMORY) } } + + /// Search for a metadata entry in the sorted `AuthManifestImageMetadataCollection` that matches the firmware ID. + /// + /// This function performs a binary search on the `image_metadata_list` of the provided `AuthManifestImageMetadataCollection`. + /// It compares the firmware ID (`fw_id`) of each metadata entry with the provided `cmd_fw_id_bytes`. + /// + /// # Arguments + /// + /// * `auth_manifest_image_metadata_col` - A reference to the `AuthManifestImageMetadataCollection` containing the metadata entries. + /// * `cmd_fw_id_bytes` - A reference to a `[u8; 4]` array representing the firmware ID from the command to search for. + /// + /// # Returns + /// + /// * `Option<&'a AuthManifestImageMetadata>` - Returns `Some(&AuthManifestImageMetadata)` if a matching entry is found, + /// otherwise returns `None`. + /// + #[inline(never)] + fn find_metadata_entry<'a>( + auth_manifest_image_metadata_col: &'a AuthManifestImageMetadataCollection, + cmd_fw_id_bytes: &[u8; 4], + ) -> Option<&'a AuthManifestImageMetadata> { + let mut left = 0; + let mut right = auth_manifest_image_metadata_col.entry_count as usize; + + while left < right { + let mid = (left + right) / 2; + // This check is needed to avoid out of bounds panic. + if mid >= auth_manifest_image_metadata_col.image_metadata_list.len() { + break; + } + let metadata_entry = &auth_manifest_image_metadata_col.image_metadata_list[mid]; + let entry_fw_id_bytes = metadata_entry.fw_id.to_le_bytes(); + + match cmd_fw_id_bytes.cmp(&entry_fw_id_bytes) { + cmp::Ordering::Less => right = mid, + cmp::Ordering::Greater => left = mid + 1, + cmp::Ordering::Equal => return Some(metadata_entry), + } + } + + None + } } diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 2e4fe0b862..a77f8e0385 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -47,7 +47,7 @@ use crate::capabilities::CapabilitiesCmd; pub use crate::certify_key_extended::CertifyKeyExtendedCmd; pub use crate::hmac::Hmac; pub use crate::subject_alt_name::AddSubjectAltNameCmd; -pub use authorize_and_stash::{AUTHORIZE_IMAGE, DENY_IMAGE_AUTHORIZATION}; +pub use authorize_and_stash::{IMAGE_AUTHORIZED, IMAGE_NOT_AUTHORIZED}; pub use caliptra_common::fips::FipsVersionCmd; pub use dice::{GetFmcAliasCertCmd, GetLdevCertCmd, IDevIdCertCmd}; pub use disable::DisableAttestationCmd; diff --git a/runtime/src/set_auth_manifest.rs b/runtime/src/set_auth_manifest.rs index 21a1592af4..baa93f97d7 100644 --- a/runtime/src/set_auth_manifest.rs +++ b/runtime/src/set_auth_manifest.rs @@ -18,8 +18,8 @@ use core::mem::size_of; use crate::verify; use crate::{dpe_crypto::DpeCrypto, CptraDpeTypes, DpePlatform, Drivers}; use caliptra_auth_man_types::{ - AuthManifestFlags, AuthManifestImageMetadataCollection, AuthManifestPreamble, - AUTH_MANIFEST_IMAGE_METADATA_MAX_COUNT, AUTH_MANIFEST_MARKER, + AuthManifestFlags, AuthManifestImageMetadata, AuthManifestImageMetadataCollection, + AuthManifestPreamble, AUTH_MANIFEST_IMAGE_METADATA_MAX_COUNT, AUTH_MANIFEST_MARKER, }; use caliptra_cfi_derive_git::cfi_impl_fn; use caliptra_cfi_lib_git::cfi_launder; @@ -44,17 +44,18 @@ use dpe::{ }; use memoffset::offset_of; use zerocopy::{AsBytes, FromBytes}; +use zeroize::Zeroize; pub struct SetAuthManifestCmd; impl SetAuthManifestCmd { fn sha384_digest( sha384: &mut Sha384, - manifest: &[u8], + buf: &[u8], offset: u32, len: u32, ) -> CaliptraResult { let err = CaliptraError::IMAGE_VERIFIER_ERR_DIGEST_OUT_OF_BOUNDS; - let data = manifest + let data = buf .get(offset as usize..) .ok_or(err)? .get(..len as usize) @@ -355,7 +356,7 @@ impl SetAuthManifestCmd { fn process_image_metadata_col( cmd_buf: &[u8], auth_manifest_preamble: &AuthManifestPreamble, - image_metadata_col: &mut AuthManifestImageMetadataCollection, + metadata_persistent: &mut AuthManifestImageMetadataCollection, sha384: &mut Sha384, ecc384: &mut Ecc384, sha256: &mut Sha256, @@ -365,23 +366,36 @@ impl SetAuthManifestCmd { Err(CaliptraError::RUNTIME_AUTH_MANIFEST_IMAGE_METADATA_LIST_INVALID_SIZE)?; } - let col_size = min( + let metadata_size = min( cmd_buf.len(), size_of::(), ); + + // Resize the buffer to the metadata size. let buf = cmd_buf - .get(..col_size) + .get(..metadata_size) .ok_or(CaliptraError::RUNTIME_AUTH_MANIFEST_IMAGE_METADATA_LIST_INVALID_SIZE)?; - image_metadata_col.as_bytes_mut()[..col_size].copy_from_slice(buf); + // Typecast the mailbox buffer to the image metadata collection. + let metadata_mailbox = + unsafe { &mut *(buf.as_ptr() as *mut AuthManifestImageMetadataCollection) }; - if image_metadata_col.entry_count == 0 - || image_metadata_col.entry_count > AUTH_MANIFEST_IMAGE_METADATA_MAX_COUNT as u32 + if metadata_mailbox.entry_count == 0 + || metadata_mailbox.entry_count > AUTH_MANIFEST_IMAGE_METADATA_MAX_COUNT as u32 { Err(CaliptraError::RUNTIME_AUTH_MANIFEST_IMAGE_METADATA_LIST_INVALID_ENTRY_COUNT)?; } - let digest_metadata_col = Self::sha384_digest(sha384, buf, 0, col_size as u32)?; + // Check if the buffer contains the entry count and all the image metadata entries specified by the entry count. + if buf.len() + < (size_of::() + + metadata_mailbox.entry_count as usize * size_of::()) + { + Err(CaliptraError::RUNTIME_AUTH_MANIFEST_IMAGE_METADATA_LIST_INVALID_SIZE)?; + } + + // Calculate the digest of the image metadata collection. + let digest_metadata_col = Self::sha384_digest(sha384, buf, 0, metadata_size as u32)?; Self::verify_vendor_image_metadata_col( auth_manifest_preamble, @@ -401,6 +415,34 @@ impl SetAuthManifestCmd { soc_ifc, )?; + // Sort the image metadata list by firmware ID in place. Also check for duplicate firmware IDs. + let slice = + &mut metadata_mailbox.image_metadata_list[..metadata_mailbox.entry_count as usize]; + for i in 0..slice.len() { + for j in 0..(slice.len() - 1 - i) { + // This check is needed to avoid out of bounds access panic. + if (j + 1) >= slice.len() { + break; + } + + match slice[j].fw_id.cmp(&slice[j + 1].fw_id) { + core::cmp::Ordering::Greater => { + slice.swap(j, j + 1); + } + core::cmp::Ordering::Equal => { + Err(CaliptraError::RUNTIME_AUTH_MANIFEST_IMAGE_METADATA_LIST_DUPLICATE_FIRMWARE_ID)?; + } + _ => {} + } + } + } + + // Clear the previous image metadata collection. + metadata_persistent.zeroize(); + + // Copy the image metadata collection to the persistent data. + metadata_persistent.as_bytes_mut()[..buf.len()].copy_from_slice(buf); + Ok(()) } diff --git a/runtime/tests/runtime_integration_tests/test_authorize_and_stash.rs b/runtime/tests/runtime_integration_tests/test_authorize_and_stash.rs index 588c440656..2db401cb9b 100644 --- a/runtime/tests/runtime_integration_tests/test_authorize_and_stash.rs +++ b/runtime/tests/runtime_integration_tests/test_authorize_and_stash.rs @@ -1,7 +1,7 @@ // Licensed under the Apache-2.0 license use crate::common::{run_rt_test, RuntimeTestArgs}; -use crate::test_set_auth_manifest::test_auth_manifest; +use crate::test_set_auth_manifest::create_auth_manifest; use caliptra_api::SocManager; use caliptra_builder::{ firmware::{self, FMC_WITH_UART}, @@ -13,7 +13,7 @@ use caliptra_common::mailbox_api::{ }; use caliptra_hw_model::HwModel; use caliptra_runtime::RtBootStatus; -use caliptra_runtime::{AUTHORIZE_IMAGE, DENY_IMAGE_AUTHORIZATION}; +use caliptra_runtime::{IMAGE_AUTHORIZED, IMAGE_NOT_AUTHORIZED}; use sha2::{Digest, Sha384}; use zerocopy::AsBytes; use zerocopy::FromBytes; @@ -24,6 +24,8 @@ pub const IMAGE_DIGEST1: [u8; 48] = [ 0x27, 0x4E, 0xDE, 0xBF, 0xE7, 0x6F, 0x65, 0xFB, 0xD5, 0x1A, 0xD2, 0xF1, 0x48, 0x98, 0xB9, 0x5B, ]; +pub const FW_ID_1: [u8; 4] = [0x01, 0x00, 0x00, 0x00]; + #[test] fn test_authorize_and_stash_cmd_deny_authorization() { let mut model = run_rt_test(RuntimeTestArgs::default()); @@ -52,7 +54,7 @@ fn test_authorize_and_stash_cmd_deny_authorization() { let authorize_and_stash_resp = AuthorizeAndStashResp::read_from(resp.as_slice()).unwrap(); assert_eq!( authorize_and_stash_resp.auth_req_result, - DENY_IMAGE_AUTHORIZATION + IMAGE_NOT_AUTHORIZED ); // create a new fw image with the runtime replaced by the mbox responder @@ -94,7 +96,7 @@ fn test_authorize_and_stash_cmd_succes() { m.soc_ifc().cptra_boot_status().read() == u32::from(RtBootStatus::RtReadyForCommands) }); - let auth_manifest = test_auth_manifest(); + let auth_manifest = create_auth_manifest(); let buf = auth_manifest.as_bytes(); let mut auth_manifest_slice = [0u8; SetAuthManifestReq::MAX_MAN_SIZE]; auth_manifest_slice[..buf.len()].copy_from_slice(buf); @@ -116,6 +118,7 @@ fn test_authorize_and_stash_cmd_succes() { let mut authorize_and_stash_cmd = MailboxReq::AuthorizeAndStash(AuthorizeAndStashReq { hdr: MailboxReqHeader { chksum: 0 }, + metadata: FW_ID_1, measurement: IMAGE_DIGEST1, source: ImageHashSource::InRequest as u32, flags: 0, // Don't skip stash @@ -132,7 +135,7 @@ fn test_authorize_and_stash_cmd_succes() { .expect("We should have received a response"); let authorize_and_stash_resp = AuthorizeAndStashResp::read_from(resp.as_slice()).unwrap(); - assert_eq!(authorize_and_stash_resp.auth_req_result, AUTHORIZE_IMAGE); + assert_eq!(authorize_and_stash_resp.auth_req_result, IMAGE_AUTHORIZED); // create a new fw image with the runtime replaced by the mbox responder let updated_fw_image = caliptra_builder::build_and_sign_image( diff --git a/runtime/tests/runtime_integration_tests/test_set_auth_manifest.rs b/runtime/tests/runtime_integration_tests/test_set_auth_manifest.rs index e4db31a51b..79aae472bb 100644 --- a/runtime/tests/runtime_integration_tests/test_set_auth_manifest.rs +++ b/runtime/tests/runtime_integration_tests/test_set_auth_manifest.rs @@ -4,13 +4,13 @@ use crate::{ common::{assert_error, run_rt_test_lms, RuntimeTestArgs}, test_authorize_and_stash::IMAGE_DIGEST1, }; -use caliptra_api::SocManager; +use caliptra_api::{mailbox::ImageHashSource, SocManager}; use caliptra_auth_man_gen::{ AuthManifestGenerator, AuthManifestGeneratorConfig, AuthManifestGeneratorKeyConfig, }; use caliptra_auth_man_types::{ AuthManifestFlags, AuthManifestImageMetadata, AuthManifestPrivKeys, AuthManifestPubKeys, - AuthorizationManifest, + AuthorizationManifest, ImageMetadataFlags, }; use caliptra_common::mailbox_api::{CommandId, MailboxReq, MailboxReqHeader, SetAuthManifestReq}; use caliptra_error::CaliptraError; @@ -20,7 +20,7 @@ use caliptra_image_fake_keys::*; use caliptra_runtime::RtBootStatus; use zerocopy::AsBytes; -pub fn test_auth_manifest() -> AuthorizationManifest { +pub fn create_auth_manifest() -> AuthorizationManifest { let vendor_fw_key_info: AuthManifestGeneratorKeyConfig = AuthManifestGeneratorKeyConfig { pub_keys: AuthManifestPubKeys { ecc_pub_key: VENDOR_ECC_KEY_0_PUBLIC, @@ -74,14 +74,24 @@ pub fn test_auth_manifest() -> AuthorizationManifest { 0xC8, 0x25, 0xA7, ]; + let mut flags1 = ImageMetadataFlags(0); + flags1.set_ignore_auth_check(false); + flags1.set_image_source(ImageHashSource::InRequest as u32); + + let mut flags2 = ImageMetadataFlags(0); + flags2.set_ignore_auth_check(true); + flags2.set_image_source(ImageHashSource::ShaAcc as u32); + // Generate authorization manifest. let image_metadata_list: Vec = vec![ AuthManifestImageMetadata { - image_source: 0, + fw_id: 1, + flags: flags1.0, digest: IMAGE_DIGEST1, }, AuthManifestImageMetadata { - image_source: 1, + fw_id: 2, + flags: flags2.0, digest: image_digest2, }, ]; @@ -108,7 +118,7 @@ fn test_set_auth_manifest_cmd() { m.soc_ifc().cptra_boot_status().read() == u32::from(RtBootStatus::RtReadyForCommands) }); - let auth_manifest = test_auth_manifest(); + let auth_manifest = create_auth_manifest(); let buf = auth_manifest.as_bytes(); let mut auth_manifest_slice = [0u8; SetAuthManifestReq::MAX_MAN_SIZE]; auth_manifest_slice[..buf.len()].copy_from_slice(buf); @@ -208,7 +218,7 @@ fn test_manifest_expect_err(manifest: AuthorizationManifest, expected_err: Calip #[test] fn test_set_auth_manifest_invalid_preamble_marker() { - let mut auth_manifest = test_auth_manifest(); + let mut auth_manifest = create_auth_manifest(); auth_manifest.preamble.marker = Default::default(); test_manifest_expect_err( auth_manifest, @@ -218,7 +228,7 @@ fn test_set_auth_manifest_invalid_preamble_marker() { #[test] fn test_set_auth_manifest_invalid_preamble_size() { - let mut auth_manifest = test_auth_manifest(); + let mut auth_manifest = create_auth_manifest(); auth_manifest.preamble.size -= 1; test_manifest_expect_err( auth_manifest, @@ -228,7 +238,7 @@ fn test_set_auth_manifest_invalid_preamble_size() { #[test] fn test_set_auth_manifest_invalid_vendor_ecc_sig() { - let mut auth_manifest = test_auth_manifest(); + let mut auth_manifest = create_auth_manifest(); auth_manifest.preamble.vendor_pub_keys_signatures.ecc_sig = Default::default(); test_manifest_expect_err( auth_manifest, @@ -238,7 +248,7 @@ fn test_set_auth_manifest_invalid_vendor_ecc_sig() { #[test] fn test_set_auth_manifest_invalid_vendor_lms_sig() { - let mut auth_manifest = test_auth_manifest(); + let mut auth_manifest = create_auth_manifest(); auth_manifest.preamble.vendor_pub_keys_signatures.lms_sig = Default::default(); test_manifest_expect_err( auth_manifest, @@ -248,7 +258,7 @@ fn test_set_auth_manifest_invalid_vendor_lms_sig() { #[test] fn test_set_auth_manifest_invalid_owner_ecc_sig() { - let mut auth_manifest = test_auth_manifest(); + let mut auth_manifest = create_auth_manifest(); auth_manifest.preamble.owner_pub_keys_signatures.ecc_sig = Default::default(); test_manifest_expect_err( auth_manifest, @@ -258,7 +268,7 @@ fn test_set_auth_manifest_invalid_owner_ecc_sig() { #[test] fn test_set_auth_manifest_invalid_owner_lms_sig() { - let mut auth_manifest = test_auth_manifest(); + let mut auth_manifest = create_auth_manifest(); auth_manifest.preamble.owner_pub_keys_signatures.lms_sig = Default::default(); test_manifest_expect_err( auth_manifest, @@ -268,7 +278,7 @@ fn test_set_auth_manifest_invalid_owner_lms_sig() { #[test] fn test_set_auth_manifest_invalid_metadata_list_count() { - let mut auth_manifest = test_auth_manifest(); + let mut auth_manifest = create_auth_manifest(); auth_manifest.image_metadata_col.entry_count = 0; test_manifest_expect_err( auth_manifest, @@ -278,7 +288,7 @@ fn test_set_auth_manifest_invalid_metadata_list_count() { #[test] fn test_set_auth_manifest_invalid_vendor_metadata_ecc_sig() { - let mut auth_manifest = test_auth_manifest(); + let mut auth_manifest = create_auth_manifest(); auth_manifest .preamble .vendor_image_metdata_signatures @@ -291,7 +301,7 @@ fn test_set_auth_manifest_invalid_vendor_metadata_ecc_sig() { #[test] fn test_set_auth_manifest_invalid_vendor_metadata_lms_sig() { - let mut auth_manifest = test_auth_manifest(); + let mut auth_manifest = create_auth_manifest(); auth_manifest .preamble .vendor_image_metdata_signatures @@ -304,7 +314,7 @@ fn test_set_auth_manifest_invalid_vendor_metadata_lms_sig() { #[test] fn test_set_auth_manifest_invalid_owner_metadata_ecc_sig() { - let mut auth_manifest = test_auth_manifest(); + let mut auth_manifest = create_auth_manifest(); auth_manifest .preamble .owner_image_metdata_signatures @@ -317,7 +327,7 @@ fn test_set_auth_manifest_invalid_owner_metadata_ecc_sig() { #[test] fn test_set_auth_manifest_invalid_owner_metadata_lms_sig() { - let mut auth_manifest = test_auth_manifest(); + let mut auth_manifest = create_auth_manifest(); auth_manifest .preamble .owner_image_metdata_signatures From 73128aa616cfe48d3df3d241a7c90dfd0ce79d67 Mon Sep 17 00:00:00 2001 From: Max Timkovich Date: Wed, 4 Dec 2024 16:07:16 -0800 Subject: [PATCH 04/11] more tests --- Cargo.lock | 2 +- runtime/Cargo.toml | 2 +- .../test_authorize_and_stash.rs | 149 ++++++++++++++---- .../test_set_auth_manifest.rs | 57 +++++-- 4 files changed, 167 insertions(+), 43 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8c7e05fd8d..6ca867e531 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -803,7 +803,6 @@ dependencies = [ name = "caliptra-runtime" version = "0.1.0" dependencies = [ - "anyhow", "arrayvec", "bitflags 2.4.0", "caliptra-api", @@ -836,6 +835,7 @@ dependencies = [ "memoffset 0.8.0", "openssl", "platform", + "rand", "sha2", "ufmt", "wycheproof", diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index 3c8844f7f8..8ab93262cc 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -36,7 +36,6 @@ caliptra-gen-linker-scripts.workspace = true cfg-if.workspace = true [dev-dependencies] -anyhow.workspace = true caliptra-api.workspace = true caliptra-builder.workspace = true caliptra-hw-model.workspace = true @@ -48,6 +47,7 @@ caliptra-auth-man-gen.workspace = true caliptra-image-serde.workspace = true caliptra-cfi-lib-git = { workspace = true, features = ["cfi-test"] } openssl.workspace = true +rand = "0.8" sha2 = { version = "0.10.2", default-features = false, features = ["compress"] } cms.workspace = true wycheproof.workspace = true diff --git a/runtime/tests/runtime_integration_tests/test_authorize_and_stash.rs b/runtime/tests/runtime_integration_tests/test_authorize_and_stash.rs index 2db401cb9b..2f0d18d4bb 100644 --- a/runtime/tests/runtime_integration_tests/test_authorize_and_stash.rs +++ b/runtime/tests/runtime_integration_tests/test_authorize_and_stash.rs @@ -11,7 +11,7 @@ use caliptra_common::mailbox_api::{ AuthorizeAndStashReq, AuthorizeAndStashResp, CommandId, ImageHashSource, MailboxReq, MailboxReqHeader, SetAuthManifestReq, }; -use caliptra_hw_model::HwModel; +use caliptra_hw_model::{HwModel, ModelEmulated}; use caliptra_runtime::RtBootStatus; use caliptra_runtime::{IMAGE_AUTHORIZED, IMAGE_NOT_AUTHORIZED}; use sha2::{Digest, Sha384}; @@ -24,7 +24,44 @@ pub const IMAGE_DIGEST1: [u8; 48] = [ 0x27, 0x4E, 0xDE, 0xBF, 0xE7, 0x6F, 0x65, 0xFB, 0xD5, 0x1A, 0xD2, 0xF1, 0x48, 0x98, 0xB9, 0x5B, ]; +pub const IMAGE_DIGEST_BAD: [u8; 48] = [ + 0x39, 0xB0, 0x60, 0xA7, 0x51, 0xAC, 0x96, 0x38, 0x4C, 0xD9, 0x32, 0x7E, 0xB1, 0xB1, 0xE3, 0x6A, + 0x21, 0xFD, 0xB7, 0x11, 0x14, 0xBE, 0x07, 0x43, 0x4C, 0x0C, 0xC7, 0xBF, 0x63, 0xF6, 0xE1, 0xDA, + 0x27, 0x4E, 0xDE, 0xBF, 0xE7, 0x6F, 0x65, 0xFB, 0xD5, 0x1A, 0xD2, 0xF1, 0x48, 0x98, 0xB9, 0x5B, +]; + pub const FW_ID_1: [u8; 4] = [0x01, 0x00, 0x00, 0x00]; +pub const FW_ID_2: [u8; 4] = [0x03, 0x03, 0x03, 0x03]; + +fn set_auth_manifest() -> ModelEmulated { + let mut model = run_rt_test(RuntimeTestArgs::default()); + + model.step_until(|m| { + m.soc_ifc().cptra_boot_status().read() == u32::from(RtBootStatus::RtReadyForCommands) + }); + + let auth_manifest = create_auth_manifest(); + let buf = auth_manifest.as_bytes(); + let mut auth_manifest_slice = [0u8; SetAuthManifestReq::MAX_MAN_SIZE]; + auth_manifest_slice[..buf.len()].copy_from_slice(buf); + + let mut set_auth_manifest_cmd = MailboxReq::SetAuthManifest(SetAuthManifestReq { + hdr: MailboxReqHeader { chksum: 0 }, + manifest_size: buf.len() as u32, + manifest: auth_manifest_slice, + }); + set_auth_manifest_cmd.populate_chksum().unwrap(); + + model + .mailbox_execute( + u32::from(CommandId::SET_AUTH_MANIFEST), + set_auth_manifest_cmd.as_bytes().unwrap(), + ) + .unwrap() + .expect("We should have received a response"); + + return model; +} #[test] fn test_authorize_and_stash_cmd_deny_authorization() { @@ -89,32 +126,8 @@ fn test_authorize_and_stash_cmd_deny_authorization() { } #[test] -fn test_authorize_and_stash_cmd_succes() { - let mut model = run_rt_test(RuntimeTestArgs::default()); - - model.step_until(|m| { - m.soc_ifc().cptra_boot_status().read() == u32::from(RtBootStatus::RtReadyForCommands) - }); - - let auth_manifest = create_auth_manifest(); - let buf = auth_manifest.as_bytes(); - let mut auth_manifest_slice = [0u8; SetAuthManifestReq::MAX_MAN_SIZE]; - auth_manifest_slice[..buf.len()].copy_from_slice(buf); - - let mut set_auth_manifest_cmd = MailboxReq::SetAuthManifest(SetAuthManifestReq { - hdr: MailboxReqHeader { chksum: 0 }, - manifest_size: buf.len() as u32, - manifest: auth_manifest_slice, - }); - set_auth_manifest_cmd.populate_chksum().unwrap(); - - model - .mailbox_execute( - u32::from(CommandId::SET_AUTH_MANIFEST), - set_auth_manifest_cmd.as_bytes().unwrap(), - ) - .unwrap() - .expect("We should have received a response"); +fn test_authorize_and_stash_cmd_success() { + let mut model = set_auth_manifest(); let mut authorize_and_stash_cmd = MailboxReq::AuthorizeAndStash(AuthorizeAndStashReq { hdr: MailboxReqHeader { chksum: 0 }, @@ -168,3 +181,85 @@ fn test_authorize_and_stash_cmd_succes() { let dpe_measurement_hash = model.mailbox_execute(0x3000_0000, &[]).unwrap().unwrap(); assert_eq!(expected_measurement_hash.as_bytes(), dpe_measurement_hash); } + +#[test] +fn test_authorize_and_stash_cmd_deny_authorization_no_hash_or_id() { + let mut model = set_auth_manifest(); + + let mut authorize_and_stash_cmd = MailboxReq::AuthorizeAndStash(AuthorizeAndStashReq { + hdr: MailboxReqHeader { chksum: 0 }, + source: ImageHashSource::InRequest as u32, + flags: 0, // Don't skip stash + ..Default::default() + }); + authorize_and_stash_cmd.populate_chksum().unwrap(); + + let resp = model + .mailbox_execute( + u32::from(CommandId::AUTHORIZE_AND_STASH), + authorize_and_stash_cmd.as_bytes().unwrap(), + ) + .unwrap() + .expect("We should have received a response"); + + let authorize_and_stash_resp = AuthorizeAndStashResp::read_from(resp.as_slice()).unwrap(); + assert_eq!( + authorize_and_stash_resp.auth_req_result, + IMAGE_NOT_AUTHORIZED + ); +} + +#[test] +fn test_authorize_and_stash_cmd_deny_authorization_wrong_id_no_hash() { + let mut model = set_auth_manifest(); + + let mut authorize_and_stash_cmd = MailboxReq::AuthorizeAndStash(AuthorizeAndStashReq { + hdr: MailboxReqHeader { chksum: 0 }, + metadata: FW_ID_2, + source: ImageHashSource::InRequest as u32, + flags: 0, // Don't skip stash + ..Default::default() + }); + authorize_and_stash_cmd.populate_chksum().unwrap(); + + let resp = model + .mailbox_execute( + u32::from(CommandId::AUTHORIZE_AND_STASH), + authorize_and_stash_cmd.as_bytes().unwrap(), + ) + .unwrap() + .expect("We should have received a response"); + + let authorize_and_stash_resp = AuthorizeAndStashResp::read_from(resp.as_slice()).unwrap(); + assert_eq!( + authorize_and_stash_resp.auth_req_result, + IMAGE_NOT_AUTHORIZED + ); +} + +#[test] +fn test_authorize_and_stash_cmd_deny_authorization_wrong_hash() { + let mut model = set_auth_manifest(); + + let mut authorize_and_stash_cmd = MailboxReq::AuthorizeAndStash(AuthorizeAndStashReq { + hdr: MailboxReqHeader { chksum: 0 }, + metadata: FW_ID_1, + measurement: IMAGE_DIGEST_BAD, + source: ImageHashSource::InRequest as u32, + flags: 0, // Don't skip stash + ..Default::default() + }); + authorize_and_stash_cmd.populate_chksum().unwrap(); + + let resp = model + .mailbox_execute( + u32::from(CommandId::AUTHORIZE_AND_STASH), + authorize_and_stash_cmd.as_bytes().unwrap(), + ) + .unwrap() + .expect("We should have received a response"); + + let authorize_and_stash_resp = AuthorizeAndStashResp::read_from(resp.as_slice()).unwrap(); + // TODO: This should NOT pass. + assert_eq!(authorize_and_stash_resp.auth_req_result, IMAGE_AUTHORIZED); +} diff --git a/runtime/tests/runtime_integration_tests/test_set_auth_manifest.rs b/runtime/tests/runtime_integration_tests/test_set_auth_manifest.rs index c3696511bf..c0cef421d6 100644 --- a/runtime/tests/runtime_integration_tests/test_set_auth_manifest.rs +++ b/runtime/tests/runtime_integration_tests/test_set_auth_manifest.rs @@ -18,8 +18,22 @@ use caliptra_hw_model::HwModel; use caliptra_image_crypto::OsslCrypto as Crypto; use caliptra_image_fake_keys::*; use caliptra_runtime::RtBootStatus; +use rand::distributions::Uniform; +use rand::Rng; use zerocopy::AsBytes; +fn random_digest() -> [u8; 48] { + let mut rng = rand::thread_rng(); + let range = Uniform::new_inclusive(u8::MIN, u8::MAX); + let mut digest = [0u8; 48]; + + for e in &mut digest { + *e = rng.sample(&range); + } + + digest +} + pub fn create_auth_manifest() -> AuthorizationManifest { let vendor_fw_key_info: AuthManifestGeneratorKeyConfig = AuthManifestGeneratorKeyConfig { pub_keys: AuthManifestPubKeys { @@ -110,7 +124,7 @@ pub fn create_auth_manifest() -> AuthorizationManifest { gen.generate(&gen_config).unwrap() } -fn create_auth_manifest_of_size(metadata_size: usize) -> anyhow::Result { +fn create_auth_manifest_of_size(metadata_size: usize) -> AuthorizationManifest { let vendor_fw_key_info: AuthManifestGeneratorKeyConfig = AuthManifestGeneratorKeyConfig { pub_keys: AuthManifestPubKeys { ecc_pub_key: VENDOR_ECC_KEY_0_PUBLIC, @@ -157,13 +171,6 @@ fn create_auth_manifest_of_size(metadata_size: usize) -> anyhow::Result anyhow::Result anyhow::Result Date: Wed, 4 Dec 2024 16:21:41 -0800 Subject: [PATCH 05/11] set manifest and auth and stash tests --- .../test_authorize_and_stash.rs | 37 +++++++++++++++++-- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/runtime/tests/runtime_integration_tests/test_authorize_and_stash.rs b/runtime/tests/runtime_integration_tests/test_authorize_and_stash.rs index 2f0d18d4bb..ec1200e482 100644 --- a/runtime/tests/runtime_integration_tests/test_authorize_and_stash.rs +++ b/runtime/tests/runtime_integration_tests/test_authorize_and_stash.rs @@ -18,6 +18,8 @@ use sha2::{Digest, Sha384}; use zerocopy::AsBytes; use zerocopy::FromBytes; +const IMAGE_HASH_MISMATCH: u32 = 0x8BFB95CB; // FW ID matched, but image digest mismatched. + pub const IMAGE_DIGEST1: [u8; 48] = [ 0x38, 0xB0, 0x60, 0xA7, 0x51, 0xAC, 0x96, 0x38, 0x4C, 0xD9, 0x32, 0x7E, 0xB1, 0xB1, 0xE3, 0x6A, 0x21, 0xFD, 0xB7, 0x11, 0x14, 0xBE, 0x07, 0x43, 0x4C, 0x0C, 0xC7, 0xBF, 0x63, 0xF6, 0xE1, 0xDA, @@ -31,7 +33,8 @@ pub const IMAGE_DIGEST_BAD: [u8; 48] = [ ]; pub const FW_ID_1: [u8; 4] = [0x01, 0x00, 0x00, 0x00]; -pub const FW_ID_2: [u8; 4] = [0x03, 0x03, 0x03, 0x03]; +pub const FW_ID_2: [u8; 4] = [0x02, 0x00, 0x00, 0x00]; +pub const FW_ID_BAD: [u8; 4] = [0x03, 0x03, 0x03, 0x03]; fn set_auth_manifest() -> ModelEmulated { let mut model = run_rt_test(RuntimeTestArgs::default()); @@ -215,7 +218,7 @@ fn test_authorize_and_stash_cmd_deny_authorization_wrong_id_no_hash() { let mut authorize_and_stash_cmd = MailboxReq::AuthorizeAndStash(AuthorizeAndStashReq { hdr: MailboxReqHeader { chksum: 0 }, - metadata: FW_ID_2, + metadata: FW_ID_BAD, source: ImageHashSource::InRequest as u32, flags: 0, // Don't skip stash ..Default::default() @@ -260,6 +263,34 @@ fn test_authorize_and_stash_cmd_deny_authorization_wrong_hash() { .expect("We should have received a response"); let authorize_and_stash_resp = AuthorizeAndStashResp::read_from(resp.as_slice()).unwrap(); - // TODO: This should NOT pass. + assert_eq!( + authorize_and_stash_resp.auth_req_result, + IMAGE_HASH_MISMATCH + ); +} + +#[test] +fn test_authorize_and_stash_cmd_success_skip_auth() { + let mut model = set_auth_manifest(); + + let mut authorize_and_stash_cmd = MailboxReq::AuthorizeAndStash(AuthorizeAndStashReq { + hdr: MailboxReqHeader { chksum: 0 }, + metadata: FW_ID_2, + measurement: IMAGE_DIGEST_BAD, + source: ImageHashSource::InRequest as u32, + flags: 0, // Don't skip stash + ..Default::default() + }); + authorize_and_stash_cmd.populate_chksum().unwrap(); + + let resp = model + .mailbox_execute( + u32::from(CommandId::AUTHORIZE_AND_STASH), + authorize_and_stash_cmd.as_bytes().unwrap(), + ) + .unwrap() + .expect("We should have received a response"); + + let authorize_and_stash_resp = AuthorizeAndStashResp::read_from(resp.as_slice()).unwrap(); assert_eq!(authorize_and_stash_resp.auth_req_result, IMAGE_AUTHORIZED); } From 528514de53f42f1f57b107a3e1e140dd227a18e3 Mon Sep 17 00:00:00 2001 From: Vishal Mhatre Date: Mon, 2 Dec 2024 12:03:38 -0800 Subject: [PATCH 06/11] [update] Manifest Based Authorization feature update. This PR makes the following changes to the feature: 1. Adds a 'firmware id' to the image metadata entry (IME) structure. 2. Reduces max IME limit from 128 to 127 to account for size increase due to the firmware id addition. 3. SET_AUTH_MANIFEST command validates that the IME collection does not contain duplicate firmware ids. 4. SET_AUTH_MANIFEST command sorts the IME on the firmware ids. 5. Adds a new bit to the IME flags named 'SKIP AUTHORIZATION'. If set, AUTHORIZE_AND_STASH command authorizes an IME with a matching firmware id ignoring the image hash. 6. If a firmware id is not found, AUTHORIZE_AND_STASH command returns IMAGE_NOT_AUTHORIZED status code in the command output. 7. If a firmware id is found but the image hash does not match, AUTHORIZE_AND_STASH returns IMAGE_HASH_MISMATCH status code in the command output. 8. If a firmware id is found and the image hash matches or if the 'SKIP AUTHORIZATION' flag is set for the the firmware id, AUTHORIZE_AND_STASH command returns IMAGE_AUTHORIZED status code in the command output. 9. AUTHORIZE_AND_STASH command uses binary search on the firmware id to lookup an IME. --- Cargo.lock | 1 + api/src/mailbox.rs | 4 +- auth-manifest/app/src/auth-man.toml | 6 ++ auth-manifest/app/src/config.rs | 21 +++++- auth-manifest/types/Cargo.toml | 1 + auth-manifest/types/src/lib.rs | 24 +++++-- error/src/lib.rs | 2 + runtime/README.md | 29 ++++++--- runtime/src/authorize_and_stash.rs | 65 +++++++++++++++---- runtime/src/lib.rs | 2 +- runtime/src/set_auth_manifest.rs | 64 ++++++++++++++---- .../test_authorize_and_stash.rs | 15 +++-- .../test_set_auth_manifest.rs | 44 ++++++++----- 13 files changed, 209 insertions(+), 69 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 87edca4772..e0303892f0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -244,6 +244,7 @@ name = "caliptra-auth-man-types" version = "0.1.0" dependencies = [ "arbitrary", + "bitfield", "bitflags 2.4.0", "caliptra-error", "caliptra-image-types", diff --git a/api/src/mailbox.rs b/api/src/mailbox.rs index 9db5ff59f0..d319cff0f7 100644 --- a/api/src/mailbox.rs +++ b/api/src/mailbox.rs @@ -1059,8 +1059,8 @@ impl AuthAndStashFlags { #[derive(Debug, AsBytes, FromBytes, PartialEq, Eq)] pub struct AuthorizeAndStashReq { pub hdr: MailboxReqHeader, - pub metadata: [u8; 4], - pub measurement: [u8; 48], + pub metadata: [u8; 4], // Firmware Id. + pub measurement: [u8; 48], // Image digest. pub context: [u8; 48], pub svn: u32, pub flags: u32, diff --git a/auth-manifest/app/src/auth-man.toml b/auth-manifest/app/src/auth-man.toml index e141a7446b..f0d5047f19 100644 --- a/auth-manifest/app/src/auth-man.toml +++ b/auth-manifest/app/src/auth-man.toml @@ -27,11 +27,17 @@ lms_priv_key = "own-lms-priv-key.pem" [[image_metadata_list]] digest = "C120EED0004B4CF6C344B00F5F501E7B7167C7010B6EA1D36AEE20CC90F1AE373DF1EC91C9AD9E0A5A969326A54E2517" source = 1 +fw_id = 1 +ignore_auth_check = false [[image_metadata_list]] digest = "99514329186b2f6ae4a1329e7ee6c610a729636335174ac6b740f9028396fcc803d0e93863a7c3d90f86beee782f4f3f" source = 2 +fw_id = 2 +ignore_auth_check = true [[image_metadata_list]] digest = "9B514329186b2f6ae4a1329e7ee6c610a729636335174ac6b740f9028396fcc803d0e93863a7c3d90f86beee782f4f3f" source = 2 +fw_id = 3 +ignore_auth_check = false diff --git a/auth-manifest/app/src/config.rs b/auth-manifest/app/src/config.rs index a94f966b00..8e9a207b5a 100644 --- a/auth-manifest/app/src/config.rs +++ b/auth-manifest/app/src/config.rs @@ -14,8 +14,8 @@ Abstract: use anyhow::Context; use caliptra_auth_man_gen::AuthManifestGeneratorKeyConfig; -use caliptra_auth_man_types::AuthManifestPubKeys; use caliptra_auth_man_types::{AuthManifestImageMetadata, AuthManifestPrivKeys}; +use caliptra_auth_man_types::{AuthManifestPubKeys, ImageMetadataFlags}; #[cfg(feature = "openssl")] use caliptra_image_crypto::OsslCrypto as Crypto; #[cfg(feature = "rustcrypto")] @@ -41,6 +41,8 @@ pub(crate) struct AuthManifestKeyConfigFromFile { pub struct ImageMetadataConfigFromFile { digest: String, source: u32, + fw_id: u32, + ignore_auth_check: bool, } // Authorization Manifest configuration from TOML file @@ -119,14 +121,27 @@ pub(crate) fn image_metadata_config_from_file( config: &Vec, ) -> anyhow::Result> { let mut image_metadata_list = Vec::new(); + let mut fw_ids: Vec = Vec::new(); for image in config { + // Check if the firmware ID is already present in the list. + if fw_ids.contains(&image.fw_id) { + return Err(anyhow::anyhow!( + "Duplicate firmware ID found in the image metadata list" + )); + } else { + fw_ids.push(image.fw_id); + } + let digest_vec = hex::decode(&image.digest)?; - let image_source = image.source; + let mut flags = ImageMetadataFlags(0); + flags.set_ignore_auth_check(image.ignore_auth_check); + flags.set_image_source(image.source); let image_metadata = AuthManifestImageMetadata { + fw_id: image.fw_id, + flags: flags.0, digest: digest_vec.try_into().unwrap(), - image_source, }; image_metadata_list.push(image_metadata); diff --git a/auth-manifest/types/Cargo.toml b/auth-manifest/types/Cargo.toml index 0f43ee3672..33d9dfcd55 100644 --- a/auth-manifest/types/Cargo.toml +++ b/auth-manifest/types/Cargo.toml @@ -17,6 +17,7 @@ caliptra-error = { workspace = true, default-features = false } zeroize.workspace = true caliptra-image-types = { workspace = true, default-features = false } bitflags.workspace = true +bitfield.workspace = true [features] default = ["std"] diff --git a/auth-manifest/types/src/lib.rs b/auth-manifest/types/src/lib.rs index a6aa13e67b..5b4818cc91 100644 --- a/auth-manifest/types/src/lib.rs +++ b/auth-manifest/types/src/lib.rs @@ -14,16 +14,16 @@ Abstract: #![no_std] -use core::ops::Range; - +use bitfield::bitfield; use caliptra_image_types::*; use core::default::Default; +use core::ops::Range; use memoffset::span_of; use zerocopy::{AsBytes, FromBytes}; use zeroize::Zeroize; pub const AUTH_MANIFEST_MARKER: u32 = 0x4154_4D4E; -pub const AUTH_MANIFEST_IMAGE_METADATA_MAX_COUNT: usize = 128; +pub const AUTH_MANIFEST_IMAGE_METADATA_MAX_COUNT: usize = 127; bitflags::bitflags! { #[derive(Default, Copy, Clone, Debug)] @@ -76,7 +76,7 @@ pub struct AuthManifestPreamble { pub version: u32, - pub flags: u32, + pub flags: u32, // AuthManifestFlags(VENDOR_SIGNATURE_REQUIRED) pub vendor_pub_keys: AuthManifestPubKeys, @@ -129,21 +129,31 @@ impl AuthManifestPreamble { } } +bitfield! { + #[derive(Debug, PartialEq, Eq, Clone, Copy)] + pub struct ImageMetadataFlags(u32); + pub image_source, set_image_source: 1, 0; + pub ignore_auth_check, set_ignore_auth_check: 2; +} + /// Caliptra Authorization Manifest Image Metadata #[repr(C)] #[derive(AsBytes, FromBytes, Clone, Copy, Debug, Zeroize)] #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] pub struct AuthManifestImageMetadata { - pub digest: [u8; 48], + pub fw_id: u32, - pub image_source: u32, + pub flags: u32, // ImageMetadataFlags(image_source, ignore_auth_check) + + pub digest: [u8; 48], } impl Default for AuthManifestImageMetadata { fn default() -> Self { AuthManifestImageMetadata { + fw_id: u32::MAX, + flags: 0, digest: [0; 48], - image_source: 0, } } } diff --git a/error/src/lib.rs b/error/src/lib.rs index 750a1611ba..2bd9d10957 100644 --- a/error/src/lib.rs +++ b/error/src/lib.rs @@ -447,6 +447,8 @@ impl CaliptraError { CaliptraError::new_const(0x000E0051); pub const RUNTIME_GET_IDEV_ID_UNSUPPORTED_ROM: CaliptraError = CaliptraError::new_const(0x000E0052); + pub const RUNTIME_AUTH_MANIFEST_IMAGE_METADATA_LIST_DUPLICATE_FIRMWARE_ID: CaliptraError = + CaliptraError::new_const(0x000E0053); /// FMC Errors pub const FMC_GLOBAL_NMI: CaliptraError = CaliptraError::new_const(0x000F0001); diff --git a/runtime/README.md b/runtime/README.md index 5a1e74e964..5894684e6d 100644 --- a/runtime/README.md +++ b/runtime/README.md @@ -725,7 +725,7 @@ Command Code: `0x4154_4D4E` ("ATMN") | preamble\_marker | u32 | Marker needs to be 0x4154_4D4E for the preamble to be valid | | preamble\_size | u32 | Size of the preamble | | preamble\_version | u32 | Version of the preamble | -| preamble\_flags | u32 | Preamble flags | +| preamble\_flags | u32 | Manifest flags. See AUTH_MANIFEST_FLAGS below | | preamble\_vendor\_ecc384\_key | u32[24] | Vendor ECC384 key with X and Y coordinates in that order | | preamble\_vendor\_lms\_key | u32[6] | Vendor LMS-SHA192-H15 key | | preamble\_vendor\_ecc384\_sig | u32[24] | Vendor ECC384 signature | @@ -739,7 +739,7 @@ Command Code: `0x4154_4D4E` ("ATMN") | metadata\_owner\_ecc384\_sig | u32[24] | Metadata Owner ECC384 signature | | metadata\_owner\_LMS\_sig | u32[1344] | Metadata Owner LMOTS-SHA192-W4 signature | | metadata\_entry\_entry\_count | u32 | number of metadata entries | -| metadata\_entries | MetaData[128] | The max number of metadata entries is 128 but less can be used | +| metadata\_entries | Metadata[127] | The max number of metadata entries is 127 but less can be used | *Table: `AUTH_MANIFEST_FLAGS` input flags* @@ -750,10 +750,19 @@ Command Code: `0x4154_4D4E` ("ATMN") *Table: `AUTH_MANIFEST_METADATA_ENTRY` digest entries* -| **Name** | **Type** | **Description** | -|---------------|----------|------------------------| -| digest | u32[48] | Digest of the metadata | -| image\_source | u32 | Image source | +| **Name** | **Type** | **Description** | +|---------------|----------|--------------------------------| +| fw\_id | u32 | Id of the image | +| flags | u32 | See METADATA_ENTRY_FLAGS below | +| digest | u32[48] | Digest of the image | + + +*Table: `METADATA_ENTRY_FLAGS` input flags* + +| **Name** | **Size (Bits)** | **Description** | +|---------------------|-----------------|-----------------| +| image\_source | 2 | 1: InRequest | +| ignore\_auth\_check | 1 | If set, the image digest is not compared for the firmware id | *Table: `SET_AUTH_MANIFEST` output arguments* @@ -772,11 +781,11 @@ Command Code: `0x4154_5348` ("ATSH") | **Name** | **Type** | **Description** | -------- | -------- | --------------- | chksum | u32 | Checksum over other input arguments, computed by the caller. Little endian. | -| metadata | u8[4] | 4-byte measurement identifier. | -| measurement | u8[48] | Digest of measured | +| metadata | u8[4] | Firmware id of the image, in little-endian format | +| measurement | u8[48] | Digest of the image requested for authorization | | context | u8[48] | Context field for `svn`; e.g., a hash of the public key that authenticated the SVN. | | svn | u32 | SVN | -| flags | u32 | Flags | +| flags | u32 | See AUTHORIZE_AND_STASH_FLAGS below | | source | u32 | Enumeration values: { InRequest(1), ShaAcc (2) } | *Table: `AUTHORIZE_AND_STASH_FLAGS` input flags* @@ -790,7 +799,7 @@ Command Code: `0x4154_5348` ("ATSH") | -------- | -------- | --------------- | chksum | u32 | Checksum over other output arguments, computed by Caliptra. Little endian. | | fips_status | u32 | Indicates if the command is FIPS approved or an error. | -| auth_req_result | u32 | AUTHORIZE_IMAGE: 0xDEADC0DE and DENY_IMAGE_AUTHORIZATION: 0x21523F21 | +| auth_req_result | u32 | AUTHORIZE_IMAGE (0xDEADC0DE), IMAGE_NOT_AUTHORIZED (0x21523F21) or IMAGE_HASH_MISMATCH (0x8BFB95CB) | ### GET\_IDEVID\_CSR diff --git a/runtime/src/authorize_and_stash.rs b/runtime/src/authorize_and_stash.rs index 9f74db5ba8..9566d831bd 100644 --- a/runtime/src/authorize_and_stash.rs +++ b/runtime/src/authorize_and_stash.rs @@ -12,15 +12,16 @@ Abstract: --*/ -use core::cmp::min; +use core::cmp::{self, min}; use core::mem::size_of; use crate::{dpe_crypto::DpeCrypto, CptraDpeTypes, DpePlatform, Drivers, StashMeasurementCmd}; use caliptra_auth_man_types::{ - AuthManifestImageMetadataCollection, AuthManifestPreamble, AUTH_MANIFEST_MARKER, + AuthManifestImageMetadata, AuthManifestImageMetadataCollection, AuthManifestPreamble, + ImageMetadataFlags, AUTH_MANIFEST_MARKER, }; use caliptra_cfi_derive_git::cfi_impl_fn; -use caliptra_cfi_lib_git::cfi_launder; +use caliptra_cfi_lib_git::{cfi_assert, cfi_assert_eq, cfi_launder}; use caliptra_common::mailbox_api::{ AuthAndStashFlags, AuthorizeAndStashReq, AuthorizeAndStashResp, ImageHashSource, MailboxResp, MailboxRespHeader, SetAuthManifestReq, @@ -44,8 +45,9 @@ use dpe::{ use memoffset::offset_of; use zerocopy::{AsBytes, FromBytes}; -pub const AUTHORIZE_IMAGE: u32 = 0xDEADC0DE; -pub const DENY_IMAGE_AUTHORIZATION: u32 = 0x21523F21; +pub const IMAGE_AUTHORIZED: u32 = 0xDEADC0DE; // Either FW ID and image digest matched or 'ignore_auth_check' is set for the FW ID. +pub const IMAGE_NOT_AUTHORIZED: u32 = 0x21523F21; // FW ID not found in the image metadata entry collection. +pub const IMAGE_HASH_MISMATCH: u32 = 0x8BFB95CB; // FW ID matched, but image digest mismatched. pub struct AuthorizeAndStashCmd; impl AuthorizeAndStashCmd { @@ -57,25 +59,35 @@ impl AuthorizeAndStashCmd { Err(CaliptraError::RUNTIME_AUTH_AND_STASH_UNSUPPORTED_IMAGE_SOURCE)?; } - // Check if image hash is present in the image metadata entry collection. + // Check if firmware id is present in the image metadata entry collection. let persistent_data = drivers.persistent_data.get(); let auth_manifest_image_metadata_col = &persistent_data.auth_manifest_image_metadata_col; - let mut auth_result = DENY_IMAGE_AUTHORIZATION; - for metadata_entry in auth_manifest_image_metadata_col.image_metadata_list.iter() { - if cfi_launder(metadata_entry.digest) == cmd.measurement { + let cmd_fw_id = u32::from_le_bytes(cmd.metadata); + let auth_result = if let Some(metadata_entry) = + Self::find_metadata_entry(auth_manifest_image_metadata_col, cmd_fw_id) + { + // If 'ignore_auth_check' is set, then skip the image digest comparison and authorize the image. + let flags = ImageMetadataFlags(metadata_entry.flags); + if flags.ignore_auth_check() { + cfi_assert!(cfi_launder(flags.ignore_auth_check())); + IMAGE_AUTHORIZED + } else if cfi_launder(metadata_entry.digest) == cmd.measurement { caliptra_cfi_lib_git::cfi_assert_eq_12_words( &Array4x12::from(metadata_entry.digest).0, &Array4x12::from(cmd.measurement).0, ); - auth_result = AUTHORIZE_IMAGE; - break; + IMAGE_AUTHORIZED + } else { + IMAGE_HASH_MISMATCH } - } + } else { + IMAGE_NOT_AUTHORIZED + }; // Stash the measurement if the image is authorized. - if auth_result == AUTHORIZE_IMAGE { + if auth_result == IMAGE_AUTHORIZED { let flags: AuthAndStashFlags = cmd.flags.into(); if !flags.contains(AuthAndStashFlags::SKIP_STASH) { let dpe_result = StashMeasurementCmd::stash_measurement( @@ -100,4 +112,31 @@ impl AuthorizeAndStashCmd { Err(CaliptraError::RUNTIME_INSUFFICIENT_MEMORY) } } + + /// Search for a metadata entry in the sorted `AuthManifestImageMetadataCollection` that matches the firmware ID. + /// + /// This function performs a binary search on the `image_metadata_list` of the provided `AuthManifestImageMetadataCollection`. + /// It compares the firmware ID (`fw_id`) of each metadata entry with the provided `cmd_fw_id`. + /// + /// # Arguments + /// + /// * `auth_manifest_image_metadata_col` - A reference to the `AuthManifestImageMetadataCollection` containing the metadata entries. + /// * `cmd_fw_id` - The firmware ID from the command to search for. + /// + /// # Returns + /// + /// * `Option<&AuthManifestImageMetadata>` - Returns `Some(&AuthManifestImageMetadata)` if a matching entry is found, + /// otherwise returns `None`. + /// + #[inline(never)] + fn find_metadata_entry( + auth_manifest_image_metadata_col: &AuthManifestImageMetadataCollection, + cmd_fw_id: u32, + ) -> Option<&AuthManifestImageMetadata> { + auth_manifest_image_metadata_col + .image_metadata_list + .binary_search_by(|metadata| metadata.fw_id.cmp(&cmd_fw_id)) + .ok() + .map(|index| &auth_manifest_image_metadata_col.image_metadata_list[index]) + } } diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 2e4fe0b862..a7aa642a89 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -47,7 +47,7 @@ use crate::capabilities::CapabilitiesCmd; pub use crate::certify_key_extended::CertifyKeyExtendedCmd; pub use crate::hmac::Hmac; pub use crate::subject_alt_name::AddSubjectAltNameCmd; -pub use authorize_and_stash::{AUTHORIZE_IMAGE, DENY_IMAGE_AUTHORIZATION}; +pub use authorize_and_stash::{IMAGE_AUTHORIZED, IMAGE_HASH_MISMATCH, IMAGE_NOT_AUTHORIZED}; pub use caliptra_common::fips::FipsVersionCmd; pub use dice::{GetFmcAliasCertCmd, GetLdevCertCmd, IDevIdCertCmd}; pub use disable::DisableAttestationCmd; diff --git a/runtime/src/set_auth_manifest.rs b/runtime/src/set_auth_manifest.rs index 21a1592af4..baa93f97d7 100644 --- a/runtime/src/set_auth_manifest.rs +++ b/runtime/src/set_auth_manifest.rs @@ -18,8 +18,8 @@ use core::mem::size_of; use crate::verify; use crate::{dpe_crypto::DpeCrypto, CptraDpeTypes, DpePlatform, Drivers}; use caliptra_auth_man_types::{ - AuthManifestFlags, AuthManifestImageMetadataCollection, AuthManifestPreamble, - AUTH_MANIFEST_IMAGE_METADATA_MAX_COUNT, AUTH_MANIFEST_MARKER, + AuthManifestFlags, AuthManifestImageMetadata, AuthManifestImageMetadataCollection, + AuthManifestPreamble, AUTH_MANIFEST_IMAGE_METADATA_MAX_COUNT, AUTH_MANIFEST_MARKER, }; use caliptra_cfi_derive_git::cfi_impl_fn; use caliptra_cfi_lib_git::cfi_launder; @@ -44,17 +44,18 @@ use dpe::{ }; use memoffset::offset_of; use zerocopy::{AsBytes, FromBytes}; +use zeroize::Zeroize; pub struct SetAuthManifestCmd; impl SetAuthManifestCmd { fn sha384_digest( sha384: &mut Sha384, - manifest: &[u8], + buf: &[u8], offset: u32, len: u32, ) -> CaliptraResult { let err = CaliptraError::IMAGE_VERIFIER_ERR_DIGEST_OUT_OF_BOUNDS; - let data = manifest + let data = buf .get(offset as usize..) .ok_or(err)? .get(..len as usize) @@ -355,7 +356,7 @@ impl SetAuthManifestCmd { fn process_image_metadata_col( cmd_buf: &[u8], auth_manifest_preamble: &AuthManifestPreamble, - image_metadata_col: &mut AuthManifestImageMetadataCollection, + metadata_persistent: &mut AuthManifestImageMetadataCollection, sha384: &mut Sha384, ecc384: &mut Ecc384, sha256: &mut Sha256, @@ -365,23 +366,36 @@ impl SetAuthManifestCmd { Err(CaliptraError::RUNTIME_AUTH_MANIFEST_IMAGE_METADATA_LIST_INVALID_SIZE)?; } - let col_size = min( + let metadata_size = min( cmd_buf.len(), size_of::(), ); + + // Resize the buffer to the metadata size. let buf = cmd_buf - .get(..col_size) + .get(..metadata_size) .ok_or(CaliptraError::RUNTIME_AUTH_MANIFEST_IMAGE_METADATA_LIST_INVALID_SIZE)?; - image_metadata_col.as_bytes_mut()[..col_size].copy_from_slice(buf); + // Typecast the mailbox buffer to the image metadata collection. + let metadata_mailbox = + unsafe { &mut *(buf.as_ptr() as *mut AuthManifestImageMetadataCollection) }; - if image_metadata_col.entry_count == 0 - || image_metadata_col.entry_count > AUTH_MANIFEST_IMAGE_METADATA_MAX_COUNT as u32 + if metadata_mailbox.entry_count == 0 + || metadata_mailbox.entry_count > AUTH_MANIFEST_IMAGE_METADATA_MAX_COUNT as u32 { Err(CaliptraError::RUNTIME_AUTH_MANIFEST_IMAGE_METADATA_LIST_INVALID_ENTRY_COUNT)?; } - let digest_metadata_col = Self::sha384_digest(sha384, buf, 0, col_size as u32)?; + // Check if the buffer contains the entry count and all the image metadata entries specified by the entry count. + if buf.len() + < (size_of::() + + metadata_mailbox.entry_count as usize * size_of::()) + { + Err(CaliptraError::RUNTIME_AUTH_MANIFEST_IMAGE_METADATA_LIST_INVALID_SIZE)?; + } + + // Calculate the digest of the image metadata collection. + let digest_metadata_col = Self::sha384_digest(sha384, buf, 0, metadata_size as u32)?; Self::verify_vendor_image_metadata_col( auth_manifest_preamble, @@ -401,6 +415,34 @@ impl SetAuthManifestCmd { soc_ifc, )?; + // Sort the image metadata list by firmware ID in place. Also check for duplicate firmware IDs. + let slice = + &mut metadata_mailbox.image_metadata_list[..metadata_mailbox.entry_count as usize]; + for i in 0..slice.len() { + for j in 0..(slice.len() - 1 - i) { + // This check is needed to avoid out of bounds access panic. + if (j + 1) >= slice.len() { + break; + } + + match slice[j].fw_id.cmp(&slice[j + 1].fw_id) { + core::cmp::Ordering::Greater => { + slice.swap(j, j + 1); + } + core::cmp::Ordering::Equal => { + Err(CaliptraError::RUNTIME_AUTH_MANIFEST_IMAGE_METADATA_LIST_DUPLICATE_FIRMWARE_ID)?; + } + _ => {} + } + } + } + + // Clear the previous image metadata collection. + metadata_persistent.zeroize(); + + // Copy the image metadata collection to the persistent data. + metadata_persistent.as_bytes_mut()[..buf.len()].copy_from_slice(buf); + Ok(()) } diff --git a/runtime/tests/runtime_integration_tests/test_authorize_and_stash.rs b/runtime/tests/runtime_integration_tests/test_authorize_and_stash.rs index 588c440656..be727f0ac2 100644 --- a/runtime/tests/runtime_integration_tests/test_authorize_and_stash.rs +++ b/runtime/tests/runtime_integration_tests/test_authorize_and_stash.rs @@ -1,7 +1,7 @@ // Licensed under the Apache-2.0 license use crate::common::{run_rt_test, RuntimeTestArgs}; -use crate::test_set_auth_manifest::test_auth_manifest; +use crate::test_set_auth_manifest::create_auth_manifest; use caliptra_api::SocManager; use caliptra_builder::{ firmware::{self, FMC_WITH_UART}, @@ -13,7 +13,7 @@ use caliptra_common::mailbox_api::{ }; use caliptra_hw_model::HwModel; use caliptra_runtime::RtBootStatus; -use caliptra_runtime::{AUTHORIZE_IMAGE, DENY_IMAGE_AUTHORIZATION}; +use caliptra_runtime::{IMAGE_AUTHORIZED, IMAGE_NOT_AUTHORIZED}; use sha2::{Digest, Sha384}; use zerocopy::AsBytes; use zerocopy::FromBytes; @@ -24,6 +24,9 @@ pub const IMAGE_DIGEST1: [u8; 48] = [ 0x27, 0x4E, 0xDE, 0xBF, 0xE7, 0x6F, 0x65, 0xFB, 0xD5, 0x1A, 0xD2, 0xF1, 0x48, 0x98, 0xB9, 0x5B, ]; +pub const FW_ID_1: [u8; 4] = [0x01, 0x00, 0x00, 0x00]; +pub const FW_ID_BAD: [u8; 4] = [0xDE, 0xED, 0xBE, 0xEF]; + #[test] fn test_authorize_and_stash_cmd_deny_authorization() { let mut model = run_rt_test(RuntimeTestArgs::default()); @@ -37,6 +40,7 @@ fn test_authorize_and_stash_cmd_deny_authorization() { measurement: IMAGE_DIGEST1, source: ImageHashSource::InRequest as u32, flags: 0, // Don't skip stash + metadata: FW_ID_BAD, ..Default::default() }); authorize_and_stash_cmd.populate_chksum().unwrap(); @@ -52,7 +56,7 @@ fn test_authorize_and_stash_cmd_deny_authorization() { let authorize_and_stash_resp = AuthorizeAndStashResp::read_from(resp.as_slice()).unwrap(); assert_eq!( authorize_and_stash_resp.auth_req_result, - DENY_IMAGE_AUTHORIZATION + IMAGE_NOT_AUTHORIZED ); // create a new fw image with the runtime replaced by the mbox responder @@ -94,7 +98,7 @@ fn test_authorize_and_stash_cmd_succes() { m.soc_ifc().cptra_boot_status().read() == u32::from(RtBootStatus::RtReadyForCommands) }); - let auth_manifest = test_auth_manifest(); + let auth_manifest = create_auth_manifest(); let buf = auth_manifest.as_bytes(); let mut auth_manifest_slice = [0u8; SetAuthManifestReq::MAX_MAN_SIZE]; auth_manifest_slice[..buf.len()].copy_from_slice(buf); @@ -116,6 +120,7 @@ fn test_authorize_and_stash_cmd_succes() { let mut authorize_and_stash_cmd = MailboxReq::AuthorizeAndStash(AuthorizeAndStashReq { hdr: MailboxReqHeader { chksum: 0 }, + metadata: FW_ID_1, measurement: IMAGE_DIGEST1, source: ImageHashSource::InRequest as u32, flags: 0, // Don't skip stash @@ -132,7 +137,7 @@ fn test_authorize_and_stash_cmd_succes() { .expect("We should have received a response"); let authorize_and_stash_resp = AuthorizeAndStashResp::read_from(resp.as_slice()).unwrap(); - assert_eq!(authorize_and_stash_resp.auth_req_result, AUTHORIZE_IMAGE); + assert_eq!(authorize_and_stash_resp.auth_req_result, IMAGE_AUTHORIZED); // create a new fw image with the runtime replaced by the mbox responder let updated_fw_image = caliptra_builder::build_and_sign_image( diff --git a/runtime/tests/runtime_integration_tests/test_set_auth_manifest.rs b/runtime/tests/runtime_integration_tests/test_set_auth_manifest.rs index e4db31a51b..79aae472bb 100644 --- a/runtime/tests/runtime_integration_tests/test_set_auth_manifest.rs +++ b/runtime/tests/runtime_integration_tests/test_set_auth_manifest.rs @@ -4,13 +4,13 @@ use crate::{ common::{assert_error, run_rt_test_lms, RuntimeTestArgs}, test_authorize_and_stash::IMAGE_DIGEST1, }; -use caliptra_api::SocManager; +use caliptra_api::{mailbox::ImageHashSource, SocManager}; use caliptra_auth_man_gen::{ AuthManifestGenerator, AuthManifestGeneratorConfig, AuthManifestGeneratorKeyConfig, }; use caliptra_auth_man_types::{ AuthManifestFlags, AuthManifestImageMetadata, AuthManifestPrivKeys, AuthManifestPubKeys, - AuthorizationManifest, + AuthorizationManifest, ImageMetadataFlags, }; use caliptra_common::mailbox_api::{CommandId, MailboxReq, MailboxReqHeader, SetAuthManifestReq}; use caliptra_error::CaliptraError; @@ -20,7 +20,7 @@ use caliptra_image_fake_keys::*; use caliptra_runtime::RtBootStatus; use zerocopy::AsBytes; -pub fn test_auth_manifest() -> AuthorizationManifest { +pub fn create_auth_manifest() -> AuthorizationManifest { let vendor_fw_key_info: AuthManifestGeneratorKeyConfig = AuthManifestGeneratorKeyConfig { pub_keys: AuthManifestPubKeys { ecc_pub_key: VENDOR_ECC_KEY_0_PUBLIC, @@ -74,14 +74,24 @@ pub fn test_auth_manifest() -> AuthorizationManifest { 0xC8, 0x25, 0xA7, ]; + let mut flags1 = ImageMetadataFlags(0); + flags1.set_ignore_auth_check(false); + flags1.set_image_source(ImageHashSource::InRequest as u32); + + let mut flags2 = ImageMetadataFlags(0); + flags2.set_ignore_auth_check(true); + flags2.set_image_source(ImageHashSource::ShaAcc as u32); + // Generate authorization manifest. let image_metadata_list: Vec = vec![ AuthManifestImageMetadata { - image_source: 0, + fw_id: 1, + flags: flags1.0, digest: IMAGE_DIGEST1, }, AuthManifestImageMetadata { - image_source: 1, + fw_id: 2, + flags: flags2.0, digest: image_digest2, }, ]; @@ -108,7 +118,7 @@ fn test_set_auth_manifest_cmd() { m.soc_ifc().cptra_boot_status().read() == u32::from(RtBootStatus::RtReadyForCommands) }); - let auth_manifest = test_auth_manifest(); + let auth_manifest = create_auth_manifest(); let buf = auth_manifest.as_bytes(); let mut auth_manifest_slice = [0u8; SetAuthManifestReq::MAX_MAN_SIZE]; auth_manifest_slice[..buf.len()].copy_from_slice(buf); @@ -208,7 +218,7 @@ fn test_manifest_expect_err(manifest: AuthorizationManifest, expected_err: Calip #[test] fn test_set_auth_manifest_invalid_preamble_marker() { - let mut auth_manifest = test_auth_manifest(); + let mut auth_manifest = create_auth_manifest(); auth_manifest.preamble.marker = Default::default(); test_manifest_expect_err( auth_manifest, @@ -218,7 +228,7 @@ fn test_set_auth_manifest_invalid_preamble_marker() { #[test] fn test_set_auth_manifest_invalid_preamble_size() { - let mut auth_manifest = test_auth_manifest(); + let mut auth_manifest = create_auth_manifest(); auth_manifest.preamble.size -= 1; test_manifest_expect_err( auth_manifest, @@ -228,7 +238,7 @@ fn test_set_auth_manifest_invalid_preamble_size() { #[test] fn test_set_auth_manifest_invalid_vendor_ecc_sig() { - let mut auth_manifest = test_auth_manifest(); + let mut auth_manifest = create_auth_manifest(); auth_manifest.preamble.vendor_pub_keys_signatures.ecc_sig = Default::default(); test_manifest_expect_err( auth_manifest, @@ -238,7 +248,7 @@ fn test_set_auth_manifest_invalid_vendor_ecc_sig() { #[test] fn test_set_auth_manifest_invalid_vendor_lms_sig() { - let mut auth_manifest = test_auth_manifest(); + let mut auth_manifest = create_auth_manifest(); auth_manifest.preamble.vendor_pub_keys_signatures.lms_sig = Default::default(); test_manifest_expect_err( auth_manifest, @@ -248,7 +258,7 @@ fn test_set_auth_manifest_invalid_vendor_lms_sig() { #[test] fn test_set_auth_manifest_invalid_owner_ecc_sig() { - let mut auth_manifest = test_auth_manifest(); + let mut auth_manifest = create_auth_manifest(); auth_manifest.preamble.owner_pub_keys_signatures.ecc_sig = Default::default(); test_manifest_expect_err( auth_manifest, @@ -258,7 +268,7 @@ fn test_set_auth_manifest_invalid_owner_ecc_sig() { #[test] fn test_set_auth_manifest_invalid_owner_lms_sig() { - let mut auth_manifest = test_auth_manifest(); + let mut auth_manifest = create_auth_manifest(); auth_manifest.preamble.owner_pub_keys_signatures.lms_sig = Default::default(); test_manifest_expect_err( auth_manifest, @@ -268,7 +278,7 @@ fn test_set_auth_manifest_invalid_owner_lms_sig() { #[test] fn test_set_auth_manifest_invalid_metadata_list_count() { - let mut auth_manifest = test_auth_manifest(); + let mut auth_manifest = create_auth_manifest(); auth_manifest.image_metadata_col.entry_count = 0; test_manifest_expect_err( auth_manifest, @@ -278,7 +288,7 @@ fn test_set_auth_manifest_invalid_metadata_list_count() { #[test] fn test_set_auth_manifest_invalid_vendor_metadata_ecc_sig() { - let mut auth_manifest = test_auth_manifest(); + let mut auth_manifest = create_auth_manifest(); auth_manifest .preamble .vendor_image_metdata_signatures @@ -291,7 +301,7 @@ fn test_set_auth_manifest_invalid_vendor_metadata_ecc_sig() { #[test] fn test_set_auth_manifest_invalid_vendor_metadata_lms_sig() { - let mut auth_manifest = test_auth_manifest(); + let mut auth_manifest = create_auth_manifest(); auth_manifest .preamble .vendor_image_metdata_signatures @@ -304,7 +314,7 @@ fn test_set_auth_manifest_invalid_vendor_metadata_lms_sig() { #[test] fn test_set_auth_manifest_invalid_owner_metadata_ecc_sig() { - let mut auth_manifest = test_auth_manifest(); + let mut auth_manifest = create_auth_manifest(); auth_manifest .preamble .owner_image_metdata_signatures @@ -317,7 +327,7 @@ fn test_set_auth_manifest_invalid_owner_metadata_ecc_sig() { #[test] fn test_set_auth_manifest_invalid_owner_metadata_lms_sig() { - let mut auth_manifest = test_auth_manifest(); + let mut auth_manifest = create_auth_manifest(); auth_manifest .preamble .owner_image_metdata_signatures From e0681b88f49e5e96c7966168214cabe0062e615a Mon Sep 17 00:00:00 2001 From: Max Timkovich Date: Thu, 5 Dec 2024 11:39:28 -0800 Subject: [PATCH 07/11] merge --- .../test_authorize_and_stash.rs | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/runtime/tests/runtime_integration_tests/test_authorize_and_stash.rs b/runtime/tests/runtime_integration_tests/test_authorize_and_stash.rs index 91f9a0a4b1..cca43dd850 100644 --- a/runtime/tests/runtime_integration_tests/test_authorize_and_stash.rs +++ b/runtime/tests/runtime_integration_tests/test_authorize_and_stash.rs @@ -26,9 +26,46 @@ pub const IMAGE_DIGEST1: [u8; 48] = [ 0x27, 0x4E, 0xDE, 0xBF, 0xE7, 0x6F, 0x65, 0xFB, 0xD5, 0x1A, 0xD2, 0xF1, 0x48, 0x98, 0xB9, 0x5B, ]; +pub const IMAGE_DIGEST_BAD: [u8; 48] = [ + 0x39, 0xB0, 0x60, 0xA7, 0x51, 0xAC, 0x96, 0x38, 0x4C, 0xD9, 0x32, 0x7E, 0xB1, 0xB1, 0xE3, 0x6A, + 0x21, 0xFD, 0xB7, 0x11, 0x14, 0xBE, 0x07, 0x43, 0x4C, 0x0C, 0xC7, 0xBF, 0x63, 0xF6, 0xE1, 0xDA, + 0x27, 0x4E, 0xDE, 0xBF, 0xE7, 0x6F, 0x65, 0xFB, 0xD5, 0x1A, 0xD2, 0xF1, 0x48, 0x98, 0xB9, 0x5B, +]; + pub const FW_ID_1: [u8; 4] = [0x01, 0x00, 0x00, 0x00]; +pub const FW_ID_2: [u8; 4] = [0x02, 0x00, 0x00, 0x00]; pub const FW_ID_BAD: [u8; 4] = [0xDE, 0xED, 0xBE, 0xEF]; +fn set_auth_manifest() -> ModelEmulated { + let mut model = run_rt_test(RuntimeTestArgs::default()); + + model.step_until(|m| { + m.soc_ifc().cptra_boot_status().read() == u32::from(RtBootStatus::RtReadyForCommands) + }); + + let auth_manifest = create_auth_manifest(); + let buf = auth_manifest.as_bytes(); + let mut auth_manifest_slice = [0u8; SetAuthManifestReq::MAX_MAN_SIZE]; + auth_manifest_slice[..buf.len()].copy_from_slice(buf); + + let mut set_auth_manifest_cmd = MailboxReq::SetAuthManifest(SetAuthManifestReq { + hdr: MailboxReqHeader { chksum: 0 }, + manifest_size: buf.len() as u32, + manifest: auth_manifest_slice, + }); + set_auth_manifest_cmd.populate_chksum().unwrap(); + + model + .mailbox_execute( + u32::from(CommandId::SET_AUTH_MANIFEST), + set_auth_manifest_cmd.as_bytes().unwrap(), + ) + .unwrap() + .expect("We should have received a response"); + + return model; +} + #[test] fn test_authorize_and_stash_cmd_deny_authorization() { let mut model = run_rt_test(RuntimeTestArgs::default()); From 2eb5139358f6a3ac6a72220d1c811820b3e726cb Mon Sep 17 00:00:00 2001 From: Max Timkovich Date: Thu, 5 Dec 2024 12:43:45 -0800 Subject: [PATCH 08/11] extreme fwids --- .../test_authorize_and_stash.rs | 100 ++++++++++++++++-- .../test_set_auth_manifest.rs | 63 +++++++++++ 2 files changed, 155 insertions(+), 8 deletions(-) diff --git a/runtime/tests/runtime_integration_tests/test_authorize_and_stash.rs b/runtime/tests/runtime_integration_tests/test_authorize_and_stash.rs index cca43dd850..a1dbec5f2b 100644 --- a/runtime/tests/runtime_integration_tests/test_authorize_and_stash.rs +++ b/runtime/tests/runtime_integration_tests/test_authorize_and_stash.rs @@ -1,8 +1,11 @@ // Licensed under the Apache-2.0 license use crate::common::{run_rt_test, RuntimeTestArgs}; -use crate::test_set_auth_manifest::create_auth_manifest; +use crate::test_set_auth_manifest::{create_auth_manifest, create_auth_manifest_with}; use caliptra_api::SocManager; +use caliptra_auth_man_types::{ + AuthManifestImageMetadata, AuthorizationManifest, ImageMetadataFlags, +}; use caliptra_builder::{ firmware::{self, FMC_WITH_UART}, ImageOptions, @@ -36,14 +39,19 @@ pub const FW_ID_1: [u8; 4] = [0x01, 0x00, 0x00, 0x00]; pub const FW_ID_2: [u8; 4] = [0x02, 0x00, 0x00, 0x00]; pub const FW_ID_BAD: [u8; 4] = [0xDE, 0xED, 0xBE, 0xEF]; -fn set_auth_manifest() -> ModelEmulated { +fn set_auth_manifest(auth_manifest: Option) -> ModelEmulated { let mut model = run_rt_test(RuntimeTestArgs::default()); model.step_until(|m| { m.soc_ifc().cptra_boot_status().read() == u32::from(RtBootStatus::RtReadyForCommands) }); - let auth_manifest = create_auth_manifest(); + let auth_manifest = if let Some(auth_manifest) = auth_manifest { + auth_manifest + } else { + create_auth_manifest() + }; + let buf = auth_manifest.as_bytes(); let mut auth_manifest_slice = [0u8; SetAuthManifestReq::MAX_MAN_SIZE]; auth_manifest_slice[..buf.len()].copy_from_slice(buf); @@ -131,7 +139,7 @@ fn test_authorize_and_stash_cmd_deny_authorization() { #[test] fn test_authorize_and_stash_cmd_success() { - let mut model = set_auth_manifest(); + let mut model = set_auth_manifest(None); let mut authorize_and_stash_cmd = MailboxReq::AuthorizeAndStash(AuthorizeAndStashReq { hdr: MailboxReqHeader { chksum: 0 }, @@ -188,7 +196,7 @@ fn test_authorize_and_stash_cmd_success() { #[test] fn test_authorize_and_stash_cmd_deny_authorization_no_hash_or_id() { - let mut model = set_auth_manifest(); + let mut model = set_auth_manifest(None); let mut authorize_and_stash_cmd = MailboxReq::AuthorizeAndStash(AuthorizeAndStashReq { hdr: MailboxReqHeader { chksum: 0 }, @@ -215,7 +223,7 @@ fn test_authorize_and_stash_cmd_deny_authorization_no_hash_or_id() { #[test] fn test_authorize_and_stash_cmd_deny_authorization_wrong_id_no_hash() { - let mut model = set_auth_manifest(); + let mut model = set_auth_manifest(None); let mut authorize_and_stash_cmd = MailboxReq::AuthorizeAndStash(AuthorizeAndStashReq { hdr: MailboxReqHeader { chksum: 0 }, @@ -243,7 +251,7 @@ fn test_authorize_and_stash_cmd_deny_authorization_wrong_id_no_hash() { #[test] fn test_authorize_and_stash_cmd_deny_authorization_wrong_hash() { - let mut model = set_auth_manifest(); + let mut model = set_auth_manifest(None); let mut authorize_and_stash_cmd = MailboxReq::AuthorizeAndStash(AuthorizeAndStashReq { hdr: MailboxReqHeader { chksum: 0 }, @@ -272,7 +280,7 @@ fn test_authorize_and_stash_cmd_deny_authorization_wrong_hash() { #[test] fn test_authorize_and_stash_cmd_success_skip_auth() { - let mut model = set_auth_manifest(); + let mut model = set_auth_manifest(None); let mut authorize_and_stash_cmd = MailboxReq::AuthorizeAndStash(AuthorizeAndStashReq { hdr: MailboxReqHeader { chksum: 0 }, @@ -295,3 +303,79 @@ fn test_authorize_and_stash_cmd_success_skip_auth() { let authorize_and_stash_resp = AuthorizeAndStashResp::read_from(resp.as_slice()).unwrap(); assert_eq!(authorize_and_stash_resp.auth_req_result, IMAGE_AUTHORIZED); } + +#[test] +fn test_authorize_and_stash_fwid_0() { + let mut flags = ImageMetadataFlags(0); + flags.set_ignore_auth_check(false); + flags.set_image_source(ImageHashSource::InRequest as u32); + + const FW_ID_0: [u8; 4] = [0x00, 0x00, 0x00, 0x00]; + + let image_metadata = vec![AuthManifestImageMetadata { + fw_id: 0, + flags: flags.0, + digest: IMAGE_DIGEST1, + }]; + let auth_manifest = create_auth_manifest_with(image_metadata); + let mut model = set_auth_manifest(Some(auth_manifest)); + + let mut authorize_and_stash_cmd = MailboxReq::AuthorizeAndStash(AuthorizeAndStashReq { + hdr: MailboxReqHeader { chksum: 0 }, + metadata: FW_ID_0, + measurement: IMAGE_DIGEST1, + source: ImageHashSource::InRequest as u32, + flags: 0, // Don't skip stash + ..Default::default() + }); + authorize_and_stash_cmd.populate_chksum().unwrap(); + + let resp = model + .mailbox_execute( + u32::from(CommandId::AUTHORIZE_AND_STASH), + authorize_and_stash_cmd.as_bytes().unwrap(), + ) + .unwrap() + .expect("We should have received a response"); + + let authorize_and_stash_resp = AuthorizeAndStashResp::read_from(resp.as_slice()).unwrap(); + assert_eq!(authorize_and_stash_resp.auth_req_result, IMAGE_AUTHORIZED); +} + +#[test] +fn test_authorize_and_stash_fwid_127() { + let mut flags = ImageMetadataFlags(0); + flags.set_ignore_auth_check(false); + flags.set_image_source(ImageHashSource::InRequest as u32); + + const FW_ID_127: [u8; 4] = [0x7F, 0x00, 0x00, 0x00]; + + let image_metadata = vec![AuthManifestImageMetadata { + fw_id: 127, + flags: flags.0, + digest: IMAGE_DIGEST1, + }]; + let auth_manifest = create_auth_manifest_with(image_metadata); + let mut model = set_auth_manifest(Some(auth_manifest)); + + let mut authorize_and_stash_cmd = MailboxReq::AuthorizeAndStash(AuthorizeAndStashReq { + hdr: MailboxReqHeader { chksum: 0 }, + metadata: FW_ID_127, + measurement: IMAGE_DIGEST1, + source: ImageHashSource::InRequest as u32, + flags: 0, // Don't skip stash + ..Default::default() + }); + authorize_and_stash_cmd.populate_chksum().unwrap(); + + let resp = model + .mailbox_execute( + u32::from(CommandId::AUTHORIZE_AND_STASH), + authorize_and_stash_cmd.as_bytes().unwrap(), + ) + .unwrap() + .expect("We should have received a response"); + + let authorize_and_stash_resp = AuthorizeAndStashResp::read_from(resp.as_slice()).unwrap(); + assert_eq!(authorize_and_stash_resp.auth_req_result, IMAGE_AUTHORIZED); +} diff --git a/runtime/tests/runtime_integration_tests/test_set_auth_manifest.rs b/runtime/tests/runtime_integration_tests/test_set_auth_manifest.rs index c0cef421d6..18cf2be9cc 100644 --- a/runtime/tests/runtime_integration_tests/test_set_auth_manifest.rs +++ b/runtime/tests/runtime_integration_tests/test_set_auth_manifest.rs @@ -124,6 +124,69 @@ pub fn create_auth_manifest() -> AuthorizationManifest { gen.generate(&gen_config).unwrap() } +pub fn create_auth_manifest_with( + image_metadata_list: Vec, +) -> AuthorizationManifest { + let vendor_fw_key_info: AuthManifestGeneratorKeyConfig = AuthManifestGeneratorKeyConfig { + pub_keys: AuthManifestPubKeys { + ecc_pub_key: VENDOR_ECC_KEY_0_PUBLIC, + lms_pub_key: VENDOR_LMS_KEY_0_PUBLIC, + }, + priv_keys: Some(AuthManifestPrivKeys { + ecc_priv_key: VENDOR_ECC_KEY_0_PRIVATE, + lms_priv_key: VENDOR_LMS_KEY_0_PRIVATE, + }), + }; + + let vendor_man_key_info: AuthManifestGeneratorKeyConfig = AuthManifestGeneratorKeyConfig { + pub_keys: AuthManifestPubKeys { + ecc_pub_key: VENDOR_ECC_KEY_1_PUBLIC, + lms_pub_key: VENDOR_LMS_KEY_1_PUBLIC, + }, + priv_keys: Some(AuthManifestPrivKeys { + ecc_priv_key: VENDOR_ECC_KEY_1_PRIVATE, + lms_priv_key: VENDOR_LMS_KEY_1_PRIVATE, + }), + }; + + let owner_fw_key_info: Option = + Some(AuthManifestGeneratorKeyConfig { + pub_keys: AuthManifestPubKeys { + ecc_pub_key: OWNER_ECC_KEY_PUBLIC, + lms_pub_key: OWNER_LMS_KEY_PUBLIC, + }, + priv_keys: Some(AuthManifestPrivKeys { + ecc_priv_key: OWNER_ECC_KEY_PRIVATE, + lms_priv_key: OWNER_LMS_KEY_PRIVATE, + }), + }); + + let owner_man_key_info: Option = + Some(AuthManifestGeneratorKeyConfig { + pub_keys: AuthManifestPubKeys { + ecc_pub_key: OWNER_ECC_KEY_PUBLIC, + lms_pub_key: OWNER_LMS_KEY_PUBLIC, + }, + priv_keys: Some(AuthManifestPrivKeys { + ecc_priv_key: OWNER_ECC_KEY_PRIVATE, + lms_priv_key: OWNER_LMS_KEY_PRIVATE, + }), + }); + + let gen_config: AuthManifestGeneratorConfig = AuthManifestGeneratorConfig { + vendor_fw_key_info, + vendor_man_key_info, + owner_fw_key_info, + owner_man_key_info, + image_metadata_list, + version: 1, + flags: AuthManifestFlags::VENDOR_SIGNATURE_REQUIRED, + }; + + let gen = AuthManifestGenerator::new(Crypto::default()); + gen.generate(&gen_config).unwrap() +} + fn create_auth_manifest_of_size(metadata_size: usize) -> AuthorizationManifest { let vendor_fw_key_info: AuthManifestGeneratorKeyConfig = AuthManifestGeneratorKeyConfig { pub_keys: AuthManifestPubKeys { From 00c08e888454c74ae34a6d348da54db679caa699 Mon Sep 17 00:00:00 2001 From: Vishal Mhatre Date: Mon, 2 Dec 2024 12:03:38 -0800 Subject: [PATCH 09/11] [update] Manifest Based Authorization feature update. This PR makes the following changes to the feature: 1. Adds a 'firmware id' to the image metadata entry (IME) structure. 2. Reduces max IME limit from 128 to 127 to account for size increase due to the firmware id addition. 3. SET_AUTH_MANIFEST command validates that the IME collection does not contain duplicate firmware ids. 4. SET_AUTH_MANIFEST command sorts the IME on the firmware ids. 5. Adds a new bit to the IME flags named 'SKIP AUTHORIZATION'. If set, AUTHORIZE_AND_STASH command authorizes an IME with a matching firmware id ignoring the image hash. 6. If a firmware id is not found, AUTHORIZE_AND_STASH command returns IMAGE_NOT_AUTHORIZED status code in the command output. 7. If a firmware id is found but the image hash does not match, AUTHORIZE_AND_STASH returns IMAGE_HASH_MISMATCH status code in the command output. 8. If a firmware id is found and the image hash matches or if the 'SKIP AUTHORIZATION' flag is set for the the firmware id, AUTHORIZE_AND_STASH command returns IMAGE_AUTHORIZED status code in the command output. 9. AUTHORIZE_AND_STASH command uses binary search on the firmware id to lookup an IME. --- Cargo.lock | 1 + api/src/mailbox.rs | 4 +- auth-manifest/app/src/auth-man.toml | 6 + auth-manifest/app/src/config.rs | 21 +++- auth-manifest/types/Cargo.toml | 1 + auth-manifest/types/src/lib.rs | 24 ++-- error/src/lib.rs | 2 + runtime/README.md | 29 +++-- runtime/src/authorize_and_stash.rs | 65 ++++++++-- runtime/src/lib.rs | 2 +- runtime/src/set_auth_manifest.rs | 75 +++++++++-- .../test_authorize_and_stash.rs | 16 ++- .../test_set_auth_manifest.rs | 118 +++++++++++++++--- 13 files changed, 294 insertions(+), 70 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 87edca4772..e0303892f0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -244,6 +244,7 @@ name = "caliptra-auth-man-types" version = "0.1.0" dependencies = [ "arbitrary", + "bitfield", "bitflags 2.4.0", "caliptra-error", "caliptra-image-types", diff --git a/api/src/mailbox.rs b/api/src/mailbox.rs index 9db5ff59f0..d319cff0f7 100644 --- a/api/src/mailbox.rs +++ b/api/src/mailbox.rs @@ -1059,8 +1059,8 @@ impl AuthAndStashFlags { #[derive(Debug, AsBytes, FromBytes, PartialEq, Eq)] pub struct AuthorizeAndStashReq { pub hdr: MailboxReqHeader, - pub metadata: [u8; 4], - pub measurement: [u8; 48], + pub metadata: [u8; 4], // Firmware Id. + pub measurement: [u8; 48], // Image digest. pub context: [u8; 48], pub svn: u32, pub flags: u32, diff --git a/auth-manifest/app/src/auth-man.toml b/auth-manifest/app/src/auth-man.toml index e141a7446b..f0d5047f19 100644 --- a/auth-manifest/app/src/auth-man.toml +++ b/auth-manifest/app/src/auth-man.toml @@ -27,11 +27,17 @@ lms_priv_key = "own-lms-priv-key.pem" [[image_metadata_list]] digest = "C120EED0004B4CF6C344B00F5F501E7B7167C7010B6EA1D36AEE20CC90F1AE373DF1EC91C9AD9E0A5A969326A54E2517" source = 1 +fw_id = 1 +ignore_auth_check = false [[image_metadata_list]] digest = "99514329186b2f6ae4a1329e7ee6c610a729636335174ac6b740f9028396fcc803d0e93863a7c3d90f86beee782f4f3f" source = 2 +fw_id = 2 +ignore_auth_check = true [[image_metadata_list]] digest = "9B514329186b2f6ae4a1329e7ee6c610a729636335174ac6b740f9028396fcc803d0e93863a7c3d90f86beee782f4f3f" source = 2 +fw_id = 3 +ignore_auth_check = false diff --git a/auth-manifest/app/src/config.rs b/auth-manifest/app/src/config.rs index a94f966b00..8e9a207b5a 100644 --- a/auth-manifest/app/src/config.rs +++ b/auth-manifest/app/src/config.rs @@ -14,8 +14,8 @@ Abstract: use anyhow::Context; use caliptra_auth_man_gen::AuthManifestGeneratorKeyConfig; -use caliptra_auth_man_types::AuthManifestPubKeys; use caliptra_auth_man_types::{AuthManifestImageMetadata, AuthManifestPrivKeys}; +use caliptra_auth_man_types::{AuthManifestPubKeys, ImageMetadataFlags}; #[cfg(feature = "openssl")] use caliptra_image_crypto::OsslCrypto as Crypto; #[cfg(feature = "rustcrypto")] @@ -41,6 +41,8 @@ pub(crate) struct AuthManifestKeyConfigFromFile { pub struct ImageMetadataConfigFromFile { digest: String, source: u32, + fw_id: u32, + ignore_auth_check: bool, } // Authorization Manifest configuration from TOML file @@ -119,14 +121,27 @@ pub(crate) fn image_metadata_config_from_file( config: &Vec, ) -> anyhow::Result> { let mut image_metadata_list = Vec::new(); + let mut fw_ids: Vec = Vec::new(); for image in config { + // Check if the firmware ID is already present in the list. + if fw_ids.contains(&image.fw_id) { + return Err(anyhow::anyhow!( + "Duplicate firmware ID found in the image metadata list" + )); + } else { + fw_ids.push(image.fw_id); + } + let digest_vec = hex::decode(&image.digest)?; - let image_source = image.source; + let mut flags = ImageMetadataFlags(0); + flags.set_ignore_auth_check(image.ignore_auth_check); + flags.set_image_source(image.source); let image_metadata = AuthManifestImageMetadata { + fw_id: image.fw_id, + flags: flags.0, digest: digest_vec.try_into().unwrap(), - image_source, }; image_metadata_list.push(image_metadata); diff --git a/auth-manifest/types/Cargo.toml b/auth-manifest/types/Cargo.toml index 0f43ee3672..33d9dfcd55 100644 --- a/auth-manifest/types/Cargo.toml +++ b/auth-manifest/types/Cargo.toml @@ -17,6 +17,7 @@ caliptra-error = { workspace = true, default-features = false } zeroize.workspace = true caliptra-image-types = { workspace = true, default-features = false } bitflags.workspace = true +bitfield.workspace = true [features] default = ["std"] diff --git a/auth-manifest/types/src/lib.rs b/auth-manifest/types/src/lib.rs index a6aa13e67b..5b4818cc91 100644 --- a/auth-manifest/types/src/lib.rs +++ b/auth-manifest/types/src/lib.rs @@ -14,16 +14,16 @@ Abstract: #![no_std] -use core::ops::Range; - +use bitfield::bitfield; use caliptra_image_types::*; use core::default::Default; +use core::ops::Range; use memoffset::span_of; use zerocopy::{AsBytes, FromBytes}; use zeroize::Zeroize; pub const AUTH_MANIFEST_MARKER: u32 = 0x4154_4D4E; -pub const AUTH_MANIFEST_IMAGE_METADATA_MAX_COUNT: usize = 128; +pub const AUTH_MANIFEST_IMAGE_METADATA_MAX_COUNT: usize = 127; bitflags::bitflags! { #[derive(Default, Copy, Clone, Debug)] @@ -76,7 +76,7 @@ pub struct AuthManifestPreamble { pub version: u32, - pub flags: u32, + pub flags: u32, // AuthManifestFlags(VENDOR_SIGNATURE_REQUIRED) pub vendor_pub_keys: AuthManifestPubKeys, @@ -129,21 +129,31 @@ impl AuthManifestPreamble { } } +bitfield! { + #[derive(Debug, PartialEq, Eq, Clone, Copy)] + pub struct ImageMetadataFlags(u32); + pub image_source, set_image_source: 1, 0; + pub ignore_auth_check, set_ignore_auth_check: 2; +} + /// Caliptra Authorization Manifest Image Metadata #[repr(C)] #[derive(AsBytes, FromBytes, Clone, Copy, Debug, Zeroize)] #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] pub struct AuthManifestImageMetadata { - pub digest: [u8; 48], + pub fw_id: u32, - pub image_source: u32, + pub flags: u32, // ImageMetadataFlags(image_source, ignore_auth_check) + + pub digest: [u8; 48], } impl Default for AuthManifestImageMetadata { fn default() -> Self { AuthManifestImageMetadata { + fw_id: u32::MAX, + flags: 0, digest: [0; 48], - image_source: 0, } } } diff --git a/error/src/lib.rs b/error/src/lib.rs index 750a1611ba..2bd9d10957 100644 --- a/error/src/lib.rs +++ b/error/src/lib.rs @@ -447,6 +447,8 @@ impl CaliptraError { CaliptraError::new_const(0x000E0051); pub const RUNTIME_GET_IDEV_ID_UNSUPPORTED_ROM: CaliptraError = CaliptraError::new_const(0x000E0052); + pub const RUNTIME_AUTH_MANIFEST_IMAGE_METADATA_LIST_DUPLICATE_FIRMWARE_ID: CaliptraError = + CaliptraError::new_const(0x000E0053); /// FMC Errors pub const FMC_GLOBAL_NMI: CaliptraError = CaliptraError::new_const(0x000F0001); diff --git a/runtime/README.md b/runtime/README.md index 5a1e74e964..5894684e6d 100644 --- a/runtime/README.md +++ b/runtime/README.md @@ -725,7 +725,7 @@ Command Code: `0x4154_4D4E` ("ATMN") | preamble\_marker | u32 | Marker needs to be 0x4154_4D4E for the preamble to be valid | | preamble\_size | u32 | Size of the preamble | | preamble\_version | u32 | Version of the preamble | -| preamble\_flags | u32 | Preamble flags | +| preamble\_flags | u32 | Manifest flags. See AUTH_MANIFEST_FLAGS below | | preamble\_vendor\_ecc384\_key | u32[24] | Vendor ECC384 key with X and Y coordinates in that order | | preamble\_vendor\_lms\_key | u32[6] | Vendor LMS-SHA192-H15 key | | preamble\_vendor\_ecc384\_sig | u32[24] | Vendor ECC384 signature | @@ -739,7 +739,7 @@ Command Code: `0x4154_4D4E` ("ATMN") | metadata\_owner\_ecc384\_sig | u32[24] | Metadata Owner ECC384 signature | | metadata\_owner\_LMS\_sig | u32[1344] | Metadata Owner LMOTS-SHA192-W4 signature | | metadata\_entry\_entry\_count | u32 | number of metadata entries | -| metadata\_entries | MetaData[128] | The max number of metadata entries is 128 but less can be used | +| metadata\_entries | Metadata[127] | The max number of metadata entries is 127 but less can be used | *Table: `AUTH_MANIFEST_FLAGS` input flags* @@ -750,10 +750,19 @@ Command Code: `0x4154_4D4E` ("ATMN") *Table: `AUTH_MANIFEST_METADATA_ENTRY` digest entries* -| **Name** | **Type** | **Description** | -|---------------|----------|------------------------| -| digest | u32[48] | Digest of the metadata | -| image\_source | u32 | Image source | +| **Name** | **Type** | **Description** | +|---------------|----------|--------------------------------| +| fw\_id | u32 | Id of the image | +| flags | u32 | See METADATA_ENTRY_FLAGS below | +| digest | u32[48] | Digest of the image | + + +*Table: `METADATA_ENTRY_FLAGS` input flags* + +| **Name** | **Size (Bits)** | **Description** | +|---------------------|-----------------|-----------------| +| image\_source | 2 | 1: InRequest | +| ignore\_auth\_check | 1 | If set, the image digest is not compared for the firmware id | *Table: `SET_AUTH_MANIFEST` output arguments* @@ -772,11 +781,11 @@ Command Code: `0x4154_5348` ("ATSH") | **Name** | **Type** | **Description** | -------- | -------- | --------------- | chksum | u32 | Checksum over other input arguments, computed by the caller. Little endian. | -| metadata | u8[4] | 4-byte measurement identifier. | -| measurement | u8[48] | Digest of measured | +| metadata | u8[4] | Firmware id of the image, in little-endian format | +| measurement | u8[48] | Digest of the image requested for authorization | | context | u8[48] | Context field for `svn`; e.g., a hash of the public key that authenticated the SVN. | | svn | u32 | SVN | -| flags | u32 | Flags | +| flags | u32 | See AUTHORIZE_AND_STASH_FLAGS below | | source | u32 | Enumeration values: { InRequest(1), ShaAcc (2) } | *Table: `AUTHORIZE_AND_STASH_FLAGS` input flags* @@ -790,7 +799,7 @@ Command Code: `0x4154_5348` ("ATSH") | -------- | -------- | --------------- | chksum | u32 | Checksum over other output arguments, computed by Caliptra. Little endian. | | fips_status | u32 | Indicates if the command is FIPS approved or an error. | -| auth_req_result | u32 | AUTHORIZE_IMAGE: 0xDEADC0DE and DENY_IMAGE_AUTHORIZATION: 0x21523F21 | +| auth_req_result | u32 | AUTHORIZE_IMAGE (0xDEADC0DE), IMAGE_NOT_AUTHORIZED (0x21523F21) or IMAGE_HASH_MISMATCH (0x8BFB95CB) | ### GET\_IDEVID\_CSR diff --git a/runtime/src/authorize_and_stash.rs b/runtime/src/authorize_and_stash.rs index 9f74db5ba8..9566d831bd 100644 --- a/runtime/src/authorize_and_stash.rs +++ b/runtime/src/authorize_and_stash.rs @@ -12,15 +12,16 @@ Abstract: --*/ -use core::cmp::min; +use core::cmp::{self, min}; use core::mem::size_of; use crate::{dpe_crypto::DpeCrypto, CptraDpeTypes, DpePlatform, Drivers, StashMeasurementCmd}; use caliptra_auth_man_types::{ - AuthManifestImageMetadataCollection, AuthManifestPreamble, AUTH_MANIFEST_MARKER, + AuthManifestImageMetadata, AuthManifestImageMetadataCollection, AuthManifestPreamble, + ImageMetadataFlags, AUTH_MANIFEST_MARKER, }; use caliptra_cfi_derive_git::cfi_impl_fn; -use caliptra_cfi_lib_git::cfi_launder; +use caliptra_cfi_lib_git::{cfi_assert, cfi_assert_eq, cfi_launder}; use caliptra_common::mailbox_api::{ AuthAndStashFlags, AuthorizeAndStashReq, AuthorizeAndStashResp, ImageHashSource, MailboxResp, MailboxRespHeader, SetAuthManifestReq, @@ -44,8 +45,9 @@ use dpe::{ use memoffset::offset_of; use zerocopy::{AsBytes, FromBytes}; -pub const AUTHORIZE_IMAGE: u32 = 0xDEADC0DE; -pub const DENY_IMAGE_AUTHORIZATION: u32 = 0x21523F21; +pub const IMAGE_AUTHORIZED: u32 = 0xDEADC0DE; // Either FW ID and image digest matched or 'ignore_auth_check' is set for the FW ID. +pub const IMAGE_NOT_AUTHORIZED: u32 = 0x21523F21; // FW ID not found in the image metadata entry collection. +pub const IMAGE_HASH_MISMATCH: u32 = 0x8BFB95CB; // FW ID matched, but image digest mismatched. pub struct AuthorizeAndStashCmd; impl AuthorizeAndStashCmd { @@ -57,25 +59,35 @@ impl AuthorizeAndStashCmd { Err(CaliptraError::RUNTIME_AUTH_AND_STASH_UNSUPPORTED_IMAGE_SOURCE)?; } - // Check if image hash is present in the image metadata entry collection. + // Check if firmware id is present in the image metadata entry collection. let persistent_data = drivers.persistent_data.get(); let auth_manifest_image_metadata_col = &persistent_data.auth_manifest_image_metadata_col; - let mut auth_result = DENY_IMAGE_AUTHORIZATION; - for metadata_entry in auth_manifest_image_metadata_col.image_metadata_list.iter() { - if cfi_launder(metadata_entry.digest) == cmd.measurement { + let cmd_fw_id = u32::from_le_bytes(cmd.metadata); + let auth_result = if let Some(metadata_entry) = + Self::find_metadata_entry(auth_manifest_image_metadata_col, cmd_fw_id) + { + // If 'ignore_auth_check' is set, then skip the image digest comparison and authorize the image. + let flags = ImageMetadataFlags(metadata_entry.flags); + if flags.ignore_auth_check() { + cfi_assert!(cfi_launder(flags.ignore_auth_check())); + IMAGE_AUTHORIZED + } else if cfi_launder(metadata_entry.digest) == cmd.measurement { caliptra_cfi_lib_git::cfi_assert_eq_12_words( &Array4x12::from(metadata_entry.digest).0, &Array4x12::from(cmd.measurement).0, ); - auth_result = AUTHORIZE_IMAGE; - break; + IMAGE_AUTHORIZED + } else { + IMAGE_HASH_MISMATCH } - } + } else { + IMAGE_NOT_AUTHORIZED + }; // Stash the measurement if the image is authorized. - if auth_result == AUTHORIZE_IMAGE { + if auth_result == IMAGE_AUTHORIZED { let flags: AuthAndStashFlags = cmd.flags.into(); if !flags.contains(AuthAndStashFlags::SKIP_STASH) { let dpe_result = StashMeasurementCmd::stash_measurement( @@ -100,4 +112,31 @@ impl AuthorizeAndStashCmd { Err(CaliptraError::RUNTIME_INSUFFICIENT_MEMORY) } } + + /// Search for a metadata entry in the sorted `AuthManifestImageMetadataCollection` that matches the firmware ID. + /// + /// This function performs a binary search on the `image_metadata_list` of the provided `AuthManifestImageMetadataCollection`. + /// It compares the firmware ID (`fw_id`) of each metadata entry with the provided `cmd_fw_id`. + /// + /// # Arguments + /// + /// * `auth_manifest_image_metadata_col` - A reference to the `AuthManifestImageMetadataCollection` containing the metadata entries. + /// * `cmd_fw_id` - The firmware ID from the command to search for. + /// + /// # Returns + /// + /// * `Option<&AuthManifestImageMetadata>` - Returns `Some(&AuthManifestImageMetadata)` if a matching entry is found, + /// otherwise returns `None`. + /// + #[inline(never)] + fn find_metadata_entry( + auth_manifest_image_metadata_col: &AuthManifestImageMetadataCollection, + cmd_fw_id: u32, + ) -> Option<&AuthManifestImageMetadata> { + auth_manifest_image_metadata_col + .image_metadata_list + .binary_search_by(|metadata| metadata.fw_id.cmp(&cmd_fw_id)) + .ok() + .map(|index| &auth_manifest_image_metadata_col.image_metadata_list[index]) + } } diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 2e4fe0b862..a7aa642a89 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -47,7 +47,7 @@ use crate::capabilities::CapabilitiesCmd; pub use crate::certify_key_extended::CertifyKeyExtendedCmd; pub use crate::hmac::Hmac; pub use crate::subject_alt_name::AddSubjectAltNameCmd; -pub use authorize_and_stash::{AUTHORIZE_IMAGE, DENY_IMAGE_AUTHORIZATION}; +pub use authorize_and_stash::{IMAGE_AUTHORIZED, IMAGE_HASH_MISMATCH, IMAGE_NOT_AUTHORIZED}; pub use caliptra_common::fips::FipsVersionCmd; pub use dice::{GetFmcAliasCertCmd, GetLdevCertCmd, IDevIdCertCmd}; pub use disable::DisableAttestationCmd; diff --git a/runtime/src/set_auth_manifest.rs b/runtime/src/set_auth_manifest.rs index 21a1592af4..d61238111f 100644 --- a/runtime/src/set_auth_manifest.rs +++ b/runtime/src/set_auth_manifest.rs @@ -18,8 +18,8 @@ use core::mem::size_of; use crate::verify; use crate::{dpe_crypto::DpeCrypto, CptraDpeTypes, DpePlatform, Drivers}; use caliptra_auth_man_types::{ - AuthManifestFlags, AuthManifestImageMetadataCollection, AuthManifestPreamble, - AUTH_MANIFEST_IMAGE_METADATA_MAX_COUNT, AUTH_MANIFEST_MARKER, + AuthManifestFlags, AuthManifestImageMetadata, AuthManifestImageMetadataCollection, + AuthManifestPreamble, AUTH_MANIFEST_IMAGE_METADATA_MAX_COUNT, AUTH_MANIFEST_MARKER, }; use caliptra_cfi_derive_git::cfi_impl_fn; use caliptra_cfi_lib_git::cfi_launder; @@ -44,17 +44,18 @@ use dpe::{ }; use memoffset::offset_of; use zerocopy::{AsBytes, FromBytes}; +use zeroize::Zeroize; pub struct SetAuthManifestCmd; impl SetAuthManifestCmd { fn sha384_digest( sha384: &mut Sha384, - manifest: &[u8], + buf: &[u8], offset: u32, len: u32, ) -> CaliptraResult { let err = CaliptraError::IMAGE_VERIFIER_ERR_DIGEST_OUT_OF_BOUNDS; - let data = manifest + let data = buf .get(offset as usize..) .ok_or(err)? .get(..len as usize) @@ -355,7 +356,7 @@ impl SetAuthManifestCmd { fn process_image_metadata_col( cmd_buf: &[u8], auth_manifest_preamble: &AuthManifestPreamble, - image_metadata_col: &mut AuthManifestImageMetadataCollection, + metadata_persistent: &mut AuthManifestImageMetadataCollection, sha384: &mut Sha384, ecc384: &mut Ecc384, sha256: &mut Sha256, @@ -365,23 +366,36 @@ impl SetAuthManifestCmd { Err(CaliptraError::RUNTIME_AUTH_MANIFEST_IMAGE_METADATA_LIST_INVALID_SIZE)?; } - let col_size = min( + let metadata_size = min( cmd_buf.len(), size_of::(), ); + + // Resize the buffer to the metadata size. let buf = cmd_buf - .get(..col_size) + .get(..metadata_size) .ok_or(CaliptraError::RUNTIME_AUTH_MANIFEST_IMAGE_METADATA_LIST_INVALID_SIZE)?; - image_metadata_col.as_bytes_mut()[..col_size].copy_from_slice(buf); + // Typecast the mailbox buffer to the image metadata collection. + let metadata_mailbox = + unsafe { &mut *(buf.as_ptr() as *mut AuthManifestImageMetadataCollection) }; - if image_metadata_col.entry_count == 0 - || image_metadata_col.entry_count > AUTH_MANIFEST_IMAGE_METADATA_MAX_COUNT as u32 + if metadata_mailbox.entry_count == 0 + || metadata_mailbox.entry_count > AUTH_MANIFEST_IMAGE_METADATA_MAX_COUNT as u32 { Err(CaliptraError::RUNTIME_AUTH_MANIFEST_IMAGE_METADATA_LIST_INVALID_ENTRY_COUNT)?; } - let digest_metadata_col = Self::sha384_digest(sha384, buf, 0, col_size as u32)?; + // Check if the buffer contains the entry count and all the image metadata entries specified by the entry count. + if buf.len() + < (size_of::() + + metadata_mailbox.entry_count as usize * size_of::()) + { + Err(CaliptraError::RUNTIME_AUTH_MANIFEST_IMAGE_METADATA_LIST_INVALID_SIZE)?; + } + + // Calculate the digest of the image metadata collection. + let digest_metadata_col = Self::sha384_digest(sha384, buf, 0, metadata_size as u32)?; Self::verify_vendor_image_metadata_col( auth_manifest_preamble, @@ -401,6 +415,45 @@ impl SetAuthManifestCmd { soc_ifc, )?; + // Sort the image metadata list by firmware ID in place. Also check for duplicate firmware IDs. let slice = + let slice = + &mut metadata_mailbox.image_metadata_list[..metadata_mailbox.entry_count as usize]; + + Self::sort_and_check_duplicate_fwid(slice)?; + + // Clear the previous image metadata collection. + metadata_persistent.zeroize(); + + // Copy the image metadata collection to the persistent data. + metadata_persistent.as_bytes_mut()[..buf.len()].copy_from_slice(buf); + + Ok(()) + } + + fn sort_and_check_duplicate_fwid( + slice: &mut [AuthManifestImageMetadata], + ) -> CaliptraResult<()> { + for i in 1..slice.len() { + let mut j = i; + while j > 0 { + if j >= slice.len() { + break; + } + + match slice[j - 1].fw_id.cmp(&slice[j].fw_id) { + core::cmp::Ordering::Greater => { + slice.swap(j - 1, j); + j -= 1; + } + core::cmp::Ordering::Equal => { + Err(CaliptraError::RUNTIME_AUTH_MANIFEST_IMAGE_METADATA_LIST_DUPLICATE_FIRMWARE_ID)?; + } + _ => { + break; + } + } + } + } Ok(()) } diff --git a/runtime/tests/runtime_integration_tests/test_authorize_and_stash.rs b/runtime/tests/runtime_integration_tests/test_authorize_and_stash.rs index 588c440656..cbe8ede592 100644 --- a/runtime/tests/runtime_integration_tests/test_authorize_and_stash.rs +++ b/runtime/tests/runtime_integration_tests/test_authorize_and_stash.rs @@ -1,8 +1,9 @@ // Licensed under the Apache-2.0 license use crate::common::{run_rt_test, RuntimeTestArgs}; -use crate::test_set_auth_manifest::test_auth_manifest; +use crate::test_set_auth_manifest::create_auth_manifest; use caliptra_api::SocManager; +use caliptra_auth_man_types::AuthManifestFlags; use caliptra_builder::{ firmware::{self, FMC_WITH_UART}, ImageOptions, @@ -13,7 +14,7 @@ use caliptra_common::mailbox_api::{ }; use caliptra_hw_model::HwModel; use caliptra_runtime::RtBootStatus; -use caliptra_runtime::{AUTHORIZE_IMAGE, DENY_IMAGE_AUTHORIZATION}; +use caliptra_runtime::{IMAGE_AUTHORIZED, IMAGE_NOT_AUTHORIZED}; use sha2::{Digest, Sha384}; use zerocopy::AsBytes; use zerocopy::FromBytes; @@ -24,6 +25,9 @@ pub const IMAGE_DIGEST1: [u8; 48] = [ 0x27, 0x4E, 0xDE, 0xBF, 0xE7, 0x6F, 0x65, 0xFB, 0xD5, 0x1A, 0xD2, 0xF1, 0x48, 0x98, 0xB9, 0x5B, ]; +pub const FW_ID_1: [u8; 4] = [0x01, 0x00, 0x00, 0x00]; +pub const FW_ID_BAD: [u8; 4] = [0xDE, 0xED, 0xBE, 0xEF]; + #[test] fn test_authorize_and_stash_cmd_deny_authorization() { let mut model = run_rt_test(RuntimeTestArgs::default()); @@ -37,6 +41,7 @@ fn test_authorize_and_stash_cmd_deny_authorization() { measurement: IMAGE_DIGEST1, source: ImageHashSource::InRequest as u32, flags: 0, // Don't skip stash + metadata: FW_ID_BAD, ..Default::default() }); authorize_and_stash_cmd.populate_chksum().unwrap(); @@ -52,7 +57,7 @@ fn test_authorize_and_stash_cmd_deny_authorization() { let authorize_and_stash_resp = AuthorizeAndStashResp::read_from(resp.as_slice()).unwrap(); assert_eq!( authorize_and_stash_resp.auth_req_result, - DENY_IMAGE_AUTHORIZATION + IMAGE_NOT_AUTHORIZED ); // create a new fw image with the runtime replaced by the mbox responder @@ -94,7 +99,7 @@ fn test_authorize_and_stash_cmd_succes() { m.soc_ifc().cptra_boot_status().read() == u32::from(RtBootStatus::RtReadyForCommands) }); - let auth_manifest = test_auth_manifest(); + let auth_manifest = create_auth_manifest(AuthManifestFlags::VENDOR_SIGNATURE_REQUIRED); let buf = auth_manifest.as_bytes(); let mut auth_manifest_slice = [0u8; SetAuthManifestReq::MAX_MAN_SIZE]; auth_manifest_slice[..buf.len()].copy_from_slice(buf); @@ -116,6 +121,7 @@ fn test_authorize_and_stash_cmd_succes() { let mut authorize_and_stash_cmd = MailboxReq::AuthorizeAndStash(AuthorizeAndStashReq { hdr: MailboxReqHeader { chksum: 0 }, + metadata: FW_ID_1, measurement: IMAGE_DIGEST1, source: ImageHashSource::InRequest as u32, flags: 0, // Don't skip stash @@ -132,7 +138,7 @@ fn test_authorize_and_stash_cmd_succes() { .expect("We should have received a response"); let authorize_and_stash_resp = AuthorizeAndStashResp::read_from(resp.as_slice()).unwrap(); - assert_eq!(authorize_and_stash_resp.auth_req_result, AUTHORIZE_IMAGE); + assert_eq!(authorize_and_stash_resp.auth_req_result, IMAGE_AUTHORIZED); // create a new fw image with the runtime replaced by the mbox responder let updated_fw_image = caliptra_builder::build_and_sign_image( diff --git a/runtime/tests/runtime_integration_tests/test_set_auth_manifest.rs b/runtime/tests/runtime_integration_tests/test_set_auth_manifest.rs index e4db31a51b..1729229d9d 100644 --- a/runtime/tests/runtime_integration_tests/test_set_auth_manifest.rs +++ b/runtime/tests/runtime_integration_tests/test_set_auth_manifest.rs @@ -4,13 +4,13 @@ use crate::{ common::{assert_error, run_rt_test_lms, RuntimeTestArgs}, test_authorize_and_stash::IMAGE_DIGEST1, }; -use caliptra_api::SocManager; +use caliptra_api::{mailbox::ImageHashSource, SocManager}; use caliptra_auth_man_gen::{ AuthManifestGenerator, AuthManifestGeneratorConfig, AuthManifestGeneratorKeyConfig, }; use caliptra_auth_man_types::{ AuthManifestFlags, AuthManifestImageMetadata, AuthManifestPrivKeys, AuthManifestPubKeys, - AuthorizationManifest, + AuthorizationManifest, ImageMetadataFlags, }; use caliptra_common::mailbox_api::{CommandId, MailboxReq, MailboxReqHeader, SetAuthManifestReq}; use caliptra_error::CaliptraError; @@ -20,7 +20,7 @@ use caliptra_image_fake_keys::*; use caliptra_runtime::RtBootStatus; use zerocopy::AsBytes; -pub fn test_auth_manifest() -> AuthorizationManifest { +pub fn create_auth_manifest(manifest_flags: AuthManifestFlags) -> AuthorizationManifest { let vendor_fw_key_info: AuthManifestGeneratorKeyConfig = AuthManifestGeneratorKeyConfig { pub_keys: AuthManifestPubKeys { ecc_pub_key: VENDOR_ECC_KEY_0_PUBLIC, @@ -74,14 +74,24 @@ pub fn test_auth_manifest() -> AuthorizationManifest { 0xC8, 0x25, 0xA7, ]; + let mut flags1 = ImageMetadataFlags(0); + flags1.set_ignore_auth_check(false); + flags1.set_image_source(ImageHashSource::InRequest as u32); + + let mut flags2 = ImageMetadataFlags(0); + flags2.set_ignore_auth_check(true); + flags2.set_image_source(ImageHashSource::ShaAcc as u32); + // Generate authorization manifest. let image_metadata_list: Vec = vec![ AuthManifestImageMetadata { - image_source: 0, + fw_id: 1, + flags: flags1.0, digest: IMAGE_DIGEST1, }, AuthManifestImageMetadata { - image_source: 1, + fw_id: 2, + flags: flags2.0, digest: image_digest2, }, ]; @@ -93,7 +103,7 @@ pub fn test_auth_manifest() -> AuthorizationManifest { owner_man_key_info, image_metadata_list, version: 1, - flags: AuthManifestFlags::VENDOR_SIGNATURE_REQUIRED, + flags: manifest_flags, }; let gen = AuthManifestGenerator::new(Crypto::default()); @@ -108,7 +118,7 @@ fn test_set_auth_manifest_cmd() { m.soc_ifc().cptra_boot_status().read() == u32::from(RtBootStatus::RtReadyForCommands) }); - let auth_manifest = test_auth_manifest(); + let auth_manifest = create_auth_manifest(AuthManifestFlags::VENDOR_SIGNATURE_REQUIRED); let buf = auth_manifest.as_bytes(); let mut auth_manifest_slice = [0u8; SetAuthManifestReq::MAX_MAN_SIZE]; auth_manifest_slice[..buf.len()].copy_from_slice(buf); @@ -208,7 +218,7 @@ fn test_manifest_expect_err(manifest: AuthorizationManifest, expected_err: Calip #[test] fn test_set_auth_manifest_invalid_preamble_marker() { - let mut auth_manifest = test_auth_manifest(); + let mut auth_manifest = create_auth_manifest(AuthManifestFlags::VENDOR_SIGNATURE_REQUIRED); auth_manifest.preamble.marker = Default::default(); test_manifest_expect_err( auth_manifest, @@ -218,7 +228,7 @@ fn test_set_auth_manifest_invalid_preamble_marker() { #[test] fn test_set_auth_manifest_invalid_preamble_size() { - let mut auth_manifest = test_auth_manifest(); + let mut auth_manifest = create_auth_manifest(AuthManifestFlags::VENDOR_SIGNATURE_REQUIRED); auth_manifest.preamble.size -= 1; test_manifest_expect_err( auth_manifest, @@ -228,7 +238,7 @@ fn test_set_auth_manifest_invalid_preamble_size() { #[test] fn test_set_auth_manifest_invalid_vendor_ecc_sig() { - let mut auth_manifest = test_auth_manifest(); + let mut auth_manifest = create_auth_manifest(AuthManifestFlags::VENDOR_SIGNATURE_REQUIRED); auth_manifest.preamble.vendor_pub_keys_signatures.ecc_sig = Default::default(); test_manifest_expect_err( auth_manifest, @@ -238,7 +248,7 @@ fn test_set_auth_manifest_invalid_vendor_ecc_sig() { #[test] fn test_set_auth_manifest_invalid_vendor_lms_sig() { - let mut auth_manifest = test_auth_manifest(); + let mut auth_manifest = create_auth_manifest(AuthManifestFlags::VENDOR_SIGNATURE_REQUIRED); auth_manifest.preamble.vendor_pub_keys_signatures.lms_sig = Default::default(); test_manifest_expect_err( auth_manifest, @@ -248,7 +258,7 @@ fn test_set_auth_manifest_invalid_vendor_lms_sig() { #[test] fn test_set_auth_manifest_invalid_owner_ecc_sig() { - let mut auth_manifest = test_auth_manifest(); + let mut auth_manifest = create_auth_manifest(AuthManifestFlags::VENDOR_SIGNATURE_REQUIRED); auth_manifest.preamble.owner_pub_keys_signatures.ecc_sig = Default::default(); test_manifest_expect_err( auth_manifest, @@ -258,7 +268,7 @@ fn test_set_auth_manifest_invalid_owner_ecc_sig() { #[test] fn test_set_auth_manifest_invalid_owner_lms_sig() { - let mut auth_manifest = test_auth_manifest(); + let mut auth_manifest = create_auth_manifest(AuthManifestFlags::VENDOR_SIGNATURE_REQUIRED); auth_manifest.preamble.owner_pub_keys_signatures.lms_sig = Default::default(); test_manifest_expect_err( auth_manifest, @@ -268,7 +278,7 @@ fn test_set_auth_manifest_invalid_owner_lms_sig() { #[test] fn test_set_auth_manifest_invalid_metadata_list_count() { - let mut auth_manifest = test_auth_manifest(); + let mut auth_manifest = create_auth_manifest(AuthManifestFlags::VENDOR_SIGNATURE_REQUIRED); auth_manifest.image_metadata_col.entry_count = 0; test_manifest_expect_err( auth_manifest, @@ -278,7 +288,7 @@ fn test_set_auth_manifest_invalid_metadata_list_count() { #[test] fn test_set_auth_manifest_invalid_vendor_metadata_ecc_sig() { - let mut auth_manifest = test_auth_manifest(); + let mut auth_manifest = create_auth_manifest(AuthManifestFlags::VENDOR_SIGNATURE_REQUIRED); auth_manifest .preamble .vendor_image_metdata_signatures @@ -291,7 +301,7 @@ fn test_set_auth_manifest_invalid_vendor_metadata_ecc_sig() { #[test] fn test_set_auth_manifest_invalid_vendor_metadata_lms_sig() { - let mut auth_manifest = test_auth_manifest(); + let mut auth_manifest = create_auth_manifest(AuthManifestFlags::VENDOR_SIGNATURE_REQUIRED); auth_manifest .preamble .vendor_image_metdata_signatures @@ -304,7 +314,7 @@ fn test_set_auth_manifest_invalid_vendor_metadata_lms_sig() { #[test] fn test_set_auth_manifest_invalid_owner_metadata_ecc_sig() { - let mut auth_manifest = test_auth_manifest(); + let mut auth_manifest = create_auth_manifest(AuthManifestFlags::VENDOR_SIGNATURE_REQUIRED); auth_manifest .preamble .owner_image_metdata_signatures @@ -317,7 +327,7 @@ fn test_set_auth_manifest_invalid_owner_metadata_ecc_sig() { #[test] fn test_set_auth_manifest_invalid_owner_metadata_lms_sig() { - let mut auth_manifest = test_auth_manifest(); + let mut auth_manifest = create_auth_manifest(AuthManifestFlags::VENDOR_SIGNATURE_REQUIRED); auth_manifest .preamble .owner_image_metdata_signatures @@ -327,3 +337,75 @@ fn test_set_auth_manifest_invalid_owner_metadata_lms_sig() { CaliptraError::RUNTIME_AUTH_MANIFEST_OWNER_LMS_SIGNATURE_INVALID, ); } + +#[test] +fn test_set_auth_manifest_cmd_ignore_vendor_ecc_sig() { + let mut auth_manifest = create_auth_manifest(0.into()); + + // Erase the vendor manifest ECC signature. + auth_manifest + .preamble + .vendor_image_metdata_signatures + .ecc_sig = Default::default(); + + let buf = auth_manifest.as_bytes(); + let mut auth_manifest_slice = [0u8; SetAuthManifestReq::MAX_MAN_SIZE]; + auth_manifest_slice[..buf.len()].copy_from_slice(buf); + + let mut model = run_rt_test_lms(RuntimeTestArgs::default(), true); + + model.step_until(|m| { + m.soc_ifc().cptra_boot_status().read() == u32::from(RtBootStatus::RtReadyForCommands) + }); + + let mut set_auth_manifest_cmd = MailboxReq::SetAuthManifest(SetAuthManifestReq { + hdr: MailboxReqHeader { chksum: 0 }, + manifest_size: buf.len() as u32, + manifest: auth_manifest_slice, + }); + set_auth_manifest_cmd.populate_chksum().unwrap(); + + model + .mailbox_execute( + u32::from(CommandId::SET_AUTH_MANIFEST), + set_auth_manifest_cmd.as_bytes().unwrap(), + ) + .unwrap() + .expect("We should have received a response"); +} + +#[test] +fn test_set_auth_manifest_cmd_ignore_vendor_lms_sig() { + let mut auth_manifest = create_auth_manifest(0.into()); + + // Erase the vendor manifest LMS signature. + auth_manifest + .preamble + .vendor_image_metdata_signatures + .lms_sig = Default::default(); + + let buf = auth_manifest.as_bytes(); + let mut auth_manifest_slice = [0u8; SetAuthManifestReq::MAX_MAN_SIZE]; + auth_manifest_slice[..buf.len()].copy_from_slice(buf); + + let mut model = run_rt_test_lms(RuntimeTestArgs::default(), true); + + model.step_until(|m| { + m.soc_ifc().cptra_boot_status().read() == u32::from(RtBootStatus::RtReadyForCommands) + }); + + let mut set_auth_manifest_cmd = MailboxReq::SetAuthManifest(SetAuthManifestReq { + hdr: MailboxReqHeader { chksum: 0 }, + manifest_size: buf.len() as u32, + manifest: auth_manifest_slice, + }); + set_auth_manifest_cmd.populate_chksum().unwrap(); + + model + .mailbox_execute( + u32::from(CommandId::SET_AUTH_MANIFEST), + set_auth_manifest_cmd.as_bytes().unwrap(), + ) + .unwrap() + .expect("We should have received a response"); +} From ba55a576a8501891f0949abe48c227043f4973a8 Mon Sep 17 00:00:00 2001 From: Max Timkovich Date: Thu, 5 Dec 2024 15:54:02 -0800 Subject: [PATCH 10/11] second auth test --- .../test_authorize_and_stash.rs | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/runtime/tests/runtime_integration_tests/test_authorize_and_stash.rs b/runtime/tests/runtime_integration_tests/test_authorize_and_stash.rs index a1dbec5f2b..4d23949433 100644 --- a/runtime/tests/runtime_integration_tests/test_authorize_and_stash.rs +++ b/runtime/tests/runtime_integration_tests/test_authorize_and_stash.rs @@ -379,3 +379,69 @@ fn test_authorize_and_stash_fwid_127() { let authorize_and_stash_resp = AuthorizeAndStashResp::read_from(resp.as_slice()).unwrap(); assert_eq!(authorize_and_stash_resp.auth_req_result, IMAGE_AUTHORIZED); } + +#[test] +fn test_authorize_and_stash_cmd_deny_second_bad_hash() { + { + let mut model = set_auth_manifest(None); + + let mut authorize_and_stash_cmd = MailboxReq::AuthorizeAndStash(AuthorizeAndStashReq { + hdr: MailboxReqHeader { chksum: 0 }, + metadata: FW_ID_1, + measurement: IMAGE_DIGEST1, + source: ImageHashSource::InRequest as u32, + flags: 0, // Don't skip stash + ..Default::default() + }); + authorize_and_stash_cmd.populate_chksum().unwrap(); + + let resp = model + .mailbox_execute( + u32::from(CommandId::AUTHORIZE_AND_STASH), + authorize_and_stash_cmd.as_bytes().unwrap(), + ) + .unwrap() + .expect("We should have received a response"); + + let authorize_and_stash_resp = AuthorizeAndStashResp::read_from(resp.as_slice()).unwrap(); + assert_eq!(authorize_and_stash_resp.auth_req_result, IMAGE_AUTHORIZED); + } + + { + let mut flags = ImageMetadataFlags(0); + flags.set_ignore_auth_check(false); + flags.set_image_source(ImageHashSource::InRequest as u32); + + let image_metadata = vec![AuthManifestImageMetadata { + fw_id: 1, + flags: flags.0, + digest: IMAGE_DIGEST_BAD, + }]; + let auth_manifest = create_auth_manifest_with(image_metadata); + let mut model = set_auth_manifest(Some(auth_manifest)); + + let mut authorize_and_stash_cmd = MailboxReq::AuthorizeAndStash(AuthorizeAndStashReq { + hdr: MailboxReqHeader { chksum: 0 }, + metadata: FW_ID_1, + measurement: IMAGE_DIGEST1, + source: ImageHashSource::InRequest as u32, + flags: 0, // Don't skip stash + ..Default::default() + }); + authorize_and_stash_cmd.populate_chksum().unwrap(); + + let resp = model + .mailbox_execute( + u32::from(CommandId::AUTHORIZE_AND_STASH), + authorize_and_stash_cmd.as_bytes().unwrap(), + ) + .unwrap() + .expect("We should have received a response"); + + let authorize_and_stash_resp = AuthorizeAndStashResp::read_from(resp.as_slice()).unwrap(); + assert_eq!( + authorize_and_stash_resp.auth_req_result, + IMAGE_HASH_MISMATCH + ); + } +} From 8a3bbdf36fece109d3a4913a0451c2538f037f40 Mon Sep 17 00:00:00 2001 From: Max Timkovich Date: Thu, 5 Dec 2024 17:45:53 -0800 Subject: [PATCH 11/11] sort_and_dupe unit tests --- runtime/src/set_auth_manifest.rs | 71 ++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/runtime/src/set_auth_manifest.rs b/runtime/src/set_auth_manifest.rs index d61238111f..8f2dccebe1 100644 --- a/runtime/src/set_auth_manifest.rs +++ b/runtime/src/set_auth_manifest.rs @@ -539,3 +539,74 @@ impl SetAuthManifestCmd { Ok(MailboxResp::default()) } } + +#[cfg(all(test))] +mod tests { + use super::*; + + fn is_sorted(slice: &[AuthManifestImageMetadata]) -> bool { + for i in 0..slice.len() - 1 { + if slice[i].fw_id > slice[i + 1].fw_id { + return false; + } + } + + true + } + + #[test] + fn test_sort_and_duplicate_empty() { + let resp = SetAuthManifestCmd::sort_and_check_duplicate_fwid(&mut []); + assert!(resp.is_ok()); + } + + #[test] + fn test_sort_and_duplicate_sort() { + let mut list = [ + AuthManifestImageMetadata { + fw_id: 5, + flags: 0, + digest: [0u8; 48], + }, + AuthManifestImageMetadata { + fw_id: 127, + flags: 0, + digest: [0u8; 48], + }, + AuthManifestImageMetadata { + fw_id: 48, + flags: 0, + digest: [0u8; 48], + }, + ]; + let resp = SetAuthManifestCmd::sort_and_check_duplicate_fwid(&mut list); + assert!(resp.is_ok()); + assert!(is_sorted(&list)); + } + + #[test] + fn test_sort_and_duplicate_dupe() { + let mut list = [ + AuthManifestImageMetadata { + fw_id: 5, + flags: 0, + digest: [0u8; 48], + }, + AuthManifestImageMetadata { + fw_id: 127, + flags: 0, + digest: [0u8; 48], + }, + AuthManifestImageMetadata { + fw_id: 127, + flags: 0, + digest: [0u8; 48], + }, + ]; + let resp = SetAuthManifestCmd::sort_and_check_duplicate_fwid(&mut list); + assert_eq!( + resp.unwrap_err(), + CaliptraError::RUNTIME_AUTH_MANIFEST_IMAGE_METADATA_LIST_DUPLICATE_FIRMWARE_ID + ); + } +}