diff --git a/Cargo.lock b/Cargo.lock index 589bc2d11e..88d6869a1b 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..6800c630eb 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 fw_id: [u8; 4], + pub measurement: [u8; 48], // Image digest. pub context: [u8; 48], pub svn: u32, pub flags: u32, @@ -1070,7 +1070,7 @@ impl Default for AuthorizeAndStashReq { fn default() -> Self { Self { hdr: Default::default(), - metadata: Default::default(), + fw_id: Default::default(), measurement: [0u8; 48], context: [0u8; 48], svn: Default::default(), 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 ff0e8804ea..0f73e1851f 100644 --- a/error/src/lib.rs +++ b/error/src/lib.rs @@ -504,10 +504,12 @@ impl CaliptraError { CaliptraError::new_const(0x000E0051); pub const RUNTIME_GET_IDEV_ID_UNSUPPORTED_ROM: CaliptraError = CaliptraError::new_const(0x000E0052); - pub const RUNTIME_AUTH_MANIFEST_LMS_VENDOR_PUB_KEY_INVALID: CaliptraError = + pub const RUNTIME_AUTH_MANIFEST_IMAGE_METADATA_LIST_DUPLICATE_FIRMWARE_ID: CaliptraError = CaliptraError::new_const(0x000E0053); - pub const RUNTIME_AUTH_MANIFEST_LMS_OWNER_PUB_KEY_INVALID: CaliptraError = + pub const RUNTIME_AUTH_MANIFEST_LMS_VENDOR_PUB_KEY_INVALID: CaliptraError = CaliptraError::new_const(0x000E0054); + pub const RUNTIME_AUTH_MANIFEST_LMS_OWNER_PUB_KEY_INVALID: CaliptraError = + CaliptraError::new_const(0x000E0055); /// FMC Errors pub const FMC_GLOBAL_NMI: CaliptraError = CaliptraError::new_const(0x000F0001); diff --git a/runtime/README.md b/runtime/README.md index d1b970062d..2b3dd9c944 100644 --- a/runtime/README.md +++ b/runtime/README.md @@ -918,65 +918,51 @@ The SoC uses this command and `SET_IMAGE_METADTA` to program an image manifest f Command Code: `0x4154_4D4E` ("ATMN") *Table: `SET_AUTH_MANIFEST` input arguments* -| **Name** | **Type** | **Description** | -| -------------------------- | --------- | --------------------------------------------------------------------------- | -| chksum | u32 | Checksum over other input arguments, computed by the caller. Little endian. | -| manifest size | u32 | The size of the full Authentication Manifest | -| 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_vendor_ecc384_key | u32[24] | Vendor ECC384 public key with X and Y coordinates in that order | -| preamble_vendor_lms_key | u32[6] | Vendor LMS-SHA192-H15 public key | -| preamble_vendor_ecc384_sig | u32[24] | Vendor ECC384 signature | -| preamble_vendor_LMS_sig | u32[1344] | Vendor LMOTS-SHA192-W4 signature | -| preamble_owner_ecc384_key | u32[24] | Owner ECC384 key with X and Y coordinates in that order | -| preamble_owner_lms_key | u32[6] | Owner LMS-SHA192-H15 key | -| preamble_owner_ecc384_sig | u32[24] | Owner ECC384 signature | -| preamble_owner_LMS_sig | u32[1344] | Owner LMOTS-SHA192-W4 signature | + +| **Name** | **Type** | **Description** +| -------- | -------- | --------------- +| chksum | u32 | Checksum over other input arguments, computed by the caller. Little endian. | +| manifest size | u32 | The size of the full Authentication Manifest | +| 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 | 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 | +| preamble\_vendor\_LMS\_sig | u32[1344] | Vendor LMOTS-SHA192-W4 signature | +| preamble\_owner\_ecc384\_key | u32[24] | Owner ECC384 key with X and Y coordinates in that order | +| preamble\_owner\_lms\_key | u32[6] | Owner LMS-SHA192-H15 key | +| preamble\_owner\_ecc384\_sig | u32[24] | Owner ECC384 signature | +| preamble\_owner\_LMS\_sig | u32[1344] | Owner LMOTS-SHA192-W4 signature | +| metadata\_vendor\_ecc384\_sig | u32[24] | Metadata Vendor ECC384 signature | +| metadata\_vendor\_LMS\_sig | u32[1344] | Metadata Vendor LMOTS-SHA192-W4 signature | +| 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[127] | The max number of metadata entries is 127 but less can be used | + *Table: `AUTH_MANIFEST_FLAGS` input flags* | **Name** | **Value** | | ------------------------- | --------- | | VENDOR_SIGNATURE_REQUIRED | 1 << 0 | -*Table: `SET_AUTH_MANIFEST` output arguments* - -| **Name** | **Type** | **Description** | -| ------------ | -------- | -------------------------------------------------------------------------- | -| 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. | - -### SET_IMAGE_METADATA +*Table: `AUTH_MANIFEST_METADATA_ENTRY` digest entries* -This command is used alonside `SET_AUTH_MANIFEST` to provide image metadata signatures for manifest-based image authorization. +| **Name** | **Type** | **Description** | +|---------------|----------|--------------------------------| +| fw\_id | u32 | Id of the image | +| flags | u32 | See METADATA_ENTRY_FLAGS below | +| digest | u32[48] | Digest of the image | -Command Code: `0x5349_4D44` ("SIMD") -*Table: `SET_IMAGE_METADATA` input arguments* -| **Name** | **Type** | **Description** | -| -------------------------- | ------------ | --------------------------------------------------------------------------- | -| chksum | u32 | Checksum over other input arguments, computed by the caller. Little endian. | -| metadata_vendor_ecc384_sig | u32[24] | Metadata Vendor ECC384 signature | -| metadata_vendor_LMS_sig | u32[1344] | Metadata Vendor LMOTS-SHA192-W4 signature | -| metadata_owner_ecc384_sig | u32[24] | Metadata Owner ECC384 signature | -| metadata_owner_LMS_sig | u32[1344] | Metadata Owner LMOTS-SHA192-W4 signature | -| metadata_header_revision | u32 | Revision of the metadata header | -| metadata_header_reserved | u32[3] | Reserved | -| metadata_entry_entry_count | u32 | number of metadata entries | -| metadata_entries | MetaData[16] | The max number of metadata is 16 but less can be used | +*Table: `METADATA_ENTRY_FLAGS` input flags* -*Table: `SET_IMAGE_METADATA_ENTRY` digest entries* -| **Name** | **Type** | **Description** | -| ------------ | -------- | ---------------------- | -| digest | u32[48] | Digest of the metadata | -| image_source | u32 | Image source | - -*Table: `SET_IMAGE_METADATA` output arguments* -| **Name** | **Type** | **Description** | -| ----------- | -------- | -------------------------------------------------------------------------- | -| 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. | +| **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 | ### AUTHORIZE_AND_STASH @@ -988,30 +974,28 @@ The SoC uses this command repeatedly to ask for authorization to run its differe Command Code: `0x4154_5348` ("ATSH") *Table: `AUTHORIZE_AND_STASH` input arguments* -| **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 | + +| **Name** | **Type** | **Description** +| ------------| -------- | --------------- +| chksum | u32 | Checksum over other input arguments, computed by the caller. Little endian. | +| fw_id | 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 | -| source | u32 | Enumeration values: | -| | | 1 = InRequest | -| | | 2 = ShaAcc | +| flags | u32 | See AUTHORIZE_AND_STASH_FLAGS below | +| source | u32 | Enumeration values: { InRequest(1), ShaAcc (2) } | *Table: `AUTHORIZE_AND_STASH_FLAGS` input flags* | **Name** | **Value** | | ----------- | --------- | -| SKIP_STASH | 1 << 0 | +| SKIP\_STASH | 1 << 0 | *Table: `AUTHORIZE_AND_STASH` output arguments* | **Name** | **Type** | **Description** | | --------------- | -------- | -------------------------------------------------------------------------- | | 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 | Authorized: `0xDEADC0DE` | -| | | Denied: `0x21523F21` | +| auth_req_result | u32 |AUTHORIZE_IMAGE (0xDEADC0DE), IMAGE_NOT_AUTHORIZED (0x21523F21) or IMAGE_HASH_MISMATCH (0x8BFB95CB) ## Mailbox commands: Cryptographic Mailbox (2.0) diff --git a/runtime/src/authorize_and_stash.rs b/runtime/src/authorize_and_stash.rs index 3105c5e063..326840e490 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,30 +59,40 @@ 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.fw_id); + 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( drivers, - &cmd.metadata, + &cmd.fw_id, &cmd.measurement, )?; if dpe_result != DpeErrorCode::NoError { @@ -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 fd8480f23c..c8057bff47 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 eeca2f9de0..770e9093a6 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( sha2: &mut Sha2_512_384, - 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) @@ -337,7 +338,7 @@ impl SetAuthManifestCmd { fn process_image_metadata_col( cmd_buf: &[u8], auth_manifest_preamble: &AuthManifestPreamble, - image_metadata_col: &mut AuthManifestImageMetadataCollection, + metadata_persistent: &mut AuthManifestImageMetadataCollection, sha2: &mut Sha2_512_384, ecc384: &mut Ecc384, sha256: &mut Sha256, @@ -347,23 +348,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(sha2, 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(sha2, buf, 0, metadata_size as u32)?; Self::verify_vendor_image_metadata_col( auth_manifest_preamble, @@ -383,6 +397,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(()) } @@ -468,3 +521,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: 127, + flags: 0, + digest: [0u8; 48], + }, + AuthManifestImageMetadata { + fw_id: 5, + 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 + ); + } +} 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..cbe9fbc3cd 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::test_auth_manifest; +use crate::test_set_auth_manifest::{create_auth_manifest, create_auth_manifest_with_metadata}; use caliptra_api::SocManager; +use caliptra_auth_man_types::{ + AuthManifestFlags, AuthManifestImageMetadata, AuthorizationManifest, ImageMetadataFlags, +}; use caliptra_builder::{ firmware::{self, FMC_WITH_UART}, ImageOptions, @@ -11,19 +14,66 @@ use caliptra_common::mailbox_api::{ AuthorizeAndStashReq, AuthorizeAndStashResp, CommandId, ImageHashSource, MailboxReq, MailboxReqHeader, SetAuthManifestReq, }; -use caliptra_hw_model::HwModel; +use caliptra_hw_model::{DefaultHwModel, 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; +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, 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(auth_manifest: Option) -> DefaultHwModel { + 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 = if let Some(auth_manifest) = auth_manifest { + auth_manifest + } else { + 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); + + 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"); + + model +} + #[test] fn test_authorize_and_stash_cmd_deny_authorization() { let mut model = run_rt_test(RuntimeTestArgs::default()); @@ -37,6 +87,7 @@ fn test_authorize_and_stash_cmd_deny_authorization() { measurement: IMAGE_DIGEST1, source: ImageHashSource::InRequest as u32, flags: 0, // Don't skip stash + fw_id: FW_ID_BAD, ..Default::default() }); authorize_and_stash_cmd.populate_chksum().unwrap(); @@ -52,7 +103,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 @@ -87,35 +138,12 @@ 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 = test_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(None); let mut authorize_and_stash_cmd = MailboxReq::AuthorizeAndStash(AuthorizeAndStashReq { hdr: MailboxReqHeader { chksum: 0 }, + fw_id: FW_ID_1, measurement: IMAGE_DIGEST1, source: ImageHashSource::InRequest as u32, flags: 0, // Don't skip stash @@ -132,7 +160,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( @@ -165,3 +193,255 @@ 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(None); + + 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(None); + + let mut authorize_and_stash_cmd = MailboxReq::AuthorizeAndStash(AuthorizeAndStashReq { + hdr: MailboxReqHeader { chksum: 0 }, + fw_id: FW_ID_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_NOT_AUTHORIZED + ); +} + +#[test] +fn test_authorize_and_stash_cmd_deny_authorization_wrong_hash() { + let mut model = set_auth_manifest(None); + + let mut authorize_and_stash_cmd = MailboxReq::AuthorizeAndStash(AuthorizeAndStashReq { + hdr: MailboxReqHeader { chksum: 0 }, + fw_id: 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(); + 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(None); + + let mut authorize_and_stash_cmd = MailboxReq::AuthorizeAndStash(AuthorizeAndStashReq { + hdr: MailboxReqHeader { chksum: 0 }, + fw_id: 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); +} + +#[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_metadata(image_metadata); + let mut model = set_auth_manifest(Some(auth_manifest)); + + let mut authorize_and_stash_cmd = MailboxReq::AuthorizeAndStash(AuthorizeAndStashReq { + hdr: MailboxReqHeader { chksum: 0 }, + fw_id: 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_metadata(image_metadata); + let mut model = set_auth_manifest(Some(auth_manifest)); + + let mut authorize_and_stash_cmd = MailboxReq::AuthorizeAndStash(AuthorizeAndStashReq { + hdr: MailboxReqHeader { chksum: 0 }, + fw_id: 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); +} + +#[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 }, + fw_id: 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_metadata(image_metadata); + let mut model = set_auth_manifest(Some(auth_manifest)); + + let mut authorize_and_stash_cmd = MailboxReq::AuthorizeAndStash(AuthorizeAndStashReq { + hdr: MailboxReqHeader { chksum: 0 }, + fw_id: 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 + ); + } +} 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 d88ad95415..9b936ce845 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, AUTH_MANIFEST_IMAGE_METADATA_MAX_COUNT, }; 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,18 +74,168 @@ 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, }, ]; + 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: manifest_flags, + }; + + let gen = AuthManifestGenerator::new(Crypto::default()); + gen.generate(&gen_config).unwrap() +} + +pub fn create_auth_manifest_with_metadata( + 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_metadata_size(metadata_size: usize) -> 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 mut flags = ImageMetadataFlags(0); + flags.set_ignore_auth_check(true); + flags.set_image_source(ImageHashSource::ShaAcc as u32); + let mut digest = crate::test_authorize_and_stash::IMAGE_DIGEST1; + + // Generate authorization manifest with a specific amount of elements. + let mut image_metadata_list = Vec::new(); + for id in 0..metadata_size { + digest[0] = id as u8; + image_metadata_list.push(AuthManifestImageMetadata { + fw_id: id as u32, + flags: flags.0, + digest, + }) + } + let gen_config: AuthManifestGeneratorConfig = AuthManifestGeneratorConfig { vendor_fw_key_info, vendor_man_key_info, @@ -108,7 +258,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); @@ -206,9 +356,78 @@ 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_metadata_size(0); + 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_metadata_size(AUTH_MANIFEST_IMAGE_METADATA_MAX_COUNT); + + let mut model = run_rt_test_lms(RuntimeTestArgs::default()); + + 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 mut auth_manifest = + create_auth_manifest_of_metadata_size(AUTH_MANIFEST_IMAGE_METADATA_MAX_COUNT); + auth_manifest.image_metadata_col.entry_count += 1; + + let mut flags = ImageMetadataFlags(0); + flags.set_ignore_auth_check(true); + flags.set_image_source(ImageHashSource::ShaAcc as u32); + + let ptr = auth_manifest + .image_metadata_col + .image_metadata_list + .as_mut_ptr(); + + unsafe { + *ptr.add(AUTH_MANIFEST_IMAGE_METADATA_MAX_COUNT) = AuthManifestImageMetadata { + fw_id: 127, + flags: flags.0, + digest: IMAGE_DIGEST1, + }; + } + + test_manifest_expect_err( + auth_manifest, + CaliptraError::RUNTIME_AUTH_MANIFEST_IMAGE_METADATA_LIST_INVALID_ENTRY_COUNT, + ); +} + #[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 +437,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 +447,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 +457,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 +467,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 +477,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 +487,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 +497,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 +510,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 +523,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 +536,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 +546,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()); + + 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()); + + 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"); +}