From 4e0dd15dd9d0845d7b96aeae9294b16bcec71595 Mon Sep 17 00:00:00 2001 From: Vishal Mhatre Date: Mon, 2 Dec 2024 12:03:38 -0800 Subject: [PATCH] [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. (cherry picked from commit cbd59bfabb9c9e1a4e8ea1dbc859741fec05965b) --- Cargo.lock | 1 + api/src/mailbox.rs | 6 +- 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 | 6 +- runtime/README.md | 106 +++--- runtime/src/authorize_and_stash.rs | 67 +++- runtime/src/lib.rs | 2 +- runtime/src/set_auth_manifest.rs | 146 +++++++- .../test_authorize_and_stash.rs | 342 ++++++++++++++++-- .../test_set_auth_manifest.rs | 325 ++++++++++++++++- 13 files changed, 903 insertions(+), 150 deletions(-) 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"); +}