diff --git a/Cargo.lock b/Cargo.lock index 6d84f6cb2e..77ba59bc44 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -354,6 +354,7 @@ version = "0.1.0" dependencies = [ "bitfield", "bitflags 2.4.0", + "caliptra-auth-man-types", "caliptra-builder", "caliptra-cfi-derive", "caliptra-cfi-derive-git", @@ -784,6 +785,8 @@ version = "0.1.0" dependencies = [ "arrayvec", "bitflags 2.4.0", + "caliptra-auth-man-gen", + "caliptra-auth-man-types", "caliptra-builder", "caliptra-cfi-derive-git", "caliptra-cfi-lib-git", @@ -808,6 +811,7 @@ dependencies = [ "cms", "crypto", "dpe", + "memoffset 0.8.0", "openssl", "platform", "sha2", diff --git a/api/src/mailbox.rs b/api/src/mailbox.rs index a0308b3702..6eb6c0dcc5 100644 --- a/api/src/mailbox.rs +++ b/api/src/mailbox.rs @@ -41,6 +41,12 @@ impl CommandId { // The capabilities command. pub const CAPABILITIES: Self = Self(0x4341_5053); // "CAPS" + + // The authorization manifest set command. + pub const SET_AUTH_MANIFEST: Self = Self(0x4154_4D4E); // "ATMN" + + // The authorize and stash command. + pub const AUTHORIZE_AND_STASH: Self = Self(0x4154_5348); // "ATSH" } impl From for CommandId { @@ -140,6 +146,7 @@ pub enum MailboxResp { GetRtAliasCert(GetRtAliasCertResp), QuotePcrs(QuotePcrsResp), CertifyKeyExtended(CertifyKeyExtendedResp), + AuthorizeAndStash(AuthorizeAndStashResp), } impl MailboxResp { @@ -159,6 +166,7 @@ impl MailboxResp { MailboxResp::GetRtAliasCert(resp) => resp.as_bytes_partial(), MailboxResp::QuotePcrs(resp) => Ok(resp.as_bytes()), MailboxResp::CertifyKeyExtended(resp) => Ok(resp.as_bytes()), + MailboxResp::AuthorizeAndStash(resp) => Ok(resp.as_bytes()), } } @@ -178,6 +186,7 @@ impl MailboxResp { MailboxResp::GetRtAliasCert(resp) => resp.as_bytes_partial_mut(), MailboxResp::QuotePcrs(resp) => Ok(resp.as_bytes_mut()), MailboxResp::CertifyKeyExtended(resp) => Ok(resp.as_bytes_mut()), + MailboxResp::AuthorizeAndStash(resp) => Ok(resp.as_bytes_mut()), } } @@ -236,6 +245,8 @@ pub enum MailboxReq { ExtendPcr(ExtendPcrReq), AddSubjectAltName(AddSubjectAltNameReq), CertifyKeyExtended(CertifyKeyExtendedReq), + SetAuthManifest(SetAuthManifestReq), + AuthorizeAndStash(AuthorizeAndStashReq), } impl MailboxReq { @@ -259,6 +270,8 @@ impl MailboxReq { MailboxReq::ExtendPcr(req) => Ok(req.as_bytes()), MailboxReq::AddSubjectAltName(req) => req.as_bytes_partial(), MailboxReq::CertifyKeyExtended(req) => Ok(req.as_bytes()), + MailboxReq::SetAuthManifest(req) => Ok(req.as_bytes()), + MailboxReq::AuthorizeAndStash(req) => Ok(req.as_bytes()), } } @@ -282,6 +295,8 @@ impl MailboxReq { MailboxReq::ExtendPcr(req) => Ok(req.as_bytes_mut()), MailboxReq::AddSubjectAltName(req) => req.as_bytes_partial_mut(), MailboxReq::CertifyKeyExtended(req) => Ok(req.as_bytes_mut()), + MailboxReq::SetAuthManifest(req) => Ok(req.as_bytes_mut()), + MailboxReq::AuthorizeAndStash(req) => Ok(req.as_bytes_mut()), } } @@ -305,6 +320,8 @@ impl MailboxReq { MailboxReq::ExtendPcr(_) => CommandId::EXTEND_PCR, MailboxReq::AddSubjectAltName(_) => CommandId::ADD_SUBJECT_ALT_NAME, MailboxReq::CertifyKeyExtended(_) => CommandId::CERTIFY_KEY_EXTENDED, + MailboxReq::SetAuthManifest(_) => CommandId::SET_AUTH_MANIFEST, + MailboxReq::AuthorizeAndStash(_) => CommandId::AUTHORIZE_AND_STASH, } } @@ -918,6 +935,118 @@ impl Request for QuotePcrsReq { type Resp = QuotePcrsResp; } +// SET_AUTH_MANIFEST +#[repr(C)] +#[derive(Debug, AsBytes, FromBytes, PartialEq, Eq)] +pub struct SetAuthManifestReq { + pub hdr: MailboxReqHeader, + pub manifest_size: u32, + pub manifest: [u8; SetAuthManifestReq::MAX_MAN_SIZE], +} +impl SetAuthManifestReq { + pub const MAX_MAN_SIZE: usize = 8192; + + pub fn as_bytes_partial(&self) -> CaliptraResult<&[u8]> { + if self.manifest_size as usize > Self::MAX_MAN_SIZE { + return Err(CaliptraError::RUNTIME_MAILBOX_API_REQUEST_DATA_LEN_TOO_LARGE); + } + let unused_byte_count = Self::MAX_MAN_SIZE - self.manifest_size as usize; + Ok(&self.as_bytes()[..size_of::() - unused_byte_count]) + } + + pub fn as_bytes_partial_mut(&mut self) -> CaliptraResult<&mut [u8]> { + if self.manifest_size as usize > Self::MAX_MAN_SIZE { + return Err(CaliptraError::RUNTIME_MAILBOX_API_REQUEST_DATA_LEN_TOO_LARGE); + } + let unused_byte_count = Self::MAX_MAN_SIZE - self.manifest_size as usize; + Ok(&mut self.as_bytes_mut()[..size_of::() - unused_byte_count]) + } +} +impl Default for SetAuthManifestReq { + fn default() -> Self { + Self { + hdr: MailboxReqHeader::default(), + manifest_size: 0, + manifest: [0u8; SetAuthManifestReq::MAX_MAN_SIZE], + } + } +} + +#[repr(u32)] +#[derive(Debug, PartialEq, Eq)] +pub enum ImageHashSource { + Invalid = 0, + InRequest, + ShaAcc, +} + +impl From for ImageHashSource { + fn from(val: u32) -> Self { + match val { + 1_u32 => ImageHashSource::InRequest, + 2_u32 => ImageHashSource::ShaAcc, + _ => ImageHashSource::Invalid, + } + } +} + +bitflags::bitflags! { + pub struct AuthAndStashFlags : u32 { + const SKIP_STASH = 0x1; + } +} + +impl From for AuthAndStashFlags { + /// Converts to this type from the input type. + fn from(value: u32) -> Self { + AuthAndStashFlags::from_bits_truncate(value) + } +} + +impl AuthAndStashFlags { + pub fn set_skip_stash(&mut self, skip_stash: bool) { + self.set(AuthAndStashFlags::SKIP_STASH, skip_stash); + } +} + +// AUTHORIZE_AND_STASH +#[repr(C)] +#[derive(Debug, AsBytes, FromBytes, PartialEq, Eq)] +pub struct AuthorizeAndStashReq { + pub hdr: MailboxReqHeader, + pub metadata: [u8; 4], + pub measurement: [u8; 48], + pub context: [u8; 48], + pub svn: u32, + pub flags: u32, + pub source: u32, +} +impl Default for AuthorizeAndStashReq { + fn default() -> Self { + Self { + hdr: Default::default(), + metadata: Default::default(), + measurement: [0u8; 48], + context: [0u8; 48], + svn: Default::default(), + flags: AuthAndStashFlags::SKIP_STASH.bits(), + source: ImageHashSource::InRequest as u32, + } + } +} +impl Request for AuthorizeAndStashReq { + const ID: CommandId = CommandId::AUTHORIZE_AND_STASH; + type Resp = StashMeasurementResp; +} + +#[repr(C)] +#[derive(Debug, Default, AsBytes, FromBytes, PartialEq, Eq)] +pub struct AuthorizeAndStashResp { + pub hdr: MailboxRespHeader, + pub auth_req_result: u32, +} +impl Response for AuthorizeAndStashResp {} + #[cfg(test)] mod tests { use super::*; diff --git a/auth-manifest/gen/src/generator.rs b/auth-manifest/gen/src/generator.rs index c11e3e5437..09f0e7a04c 100644 --- a/auth-manifest/gen/src/generator.rs +++ b/auth-manifest/gen/src/generator.rs @@ -45,7 +45,7 @@ impl AuthManifestGenerator { // Generate the Image Metadata List. let slice = config.image_metadata_list.as_slice(); - auth_manifest.image_metadata_col.image_metadata_array[..slice.len()].copy_from_slice(slice); + auth_manifest.image_metadata_col.image_metadata_list[..slice.len()].copy_from_slice(slice); auth_manifest.image_metadata_col.header.entry_count = config.image_metadata_list.len() as u32; diff --git a/auth-manifest/types/src/lib.rs b/auth-manifest/types/src/lib.rs index 4f2700fed9..004e9c0b50 100644 --- a/auth-manifest/types/src/lib.rs +++ b/auth-manifest/types/src/lib.rs @@ -24,7 +24,6 @@ use zeroize::Zeroize; pub const AUTH_MANIFEST_MARKER: u32 = 0x4154_4D4E; pub const AUTH_MANIFEST_IMAGE_METADATA_MAX_COUNT: usize = 16; -pub const AUTH_MANIFEST_VENDOR_SIGNATURE_REQURIED_FLAG: u32 = 0x1; bitflags::bitflags! { #[derive(Default, Copy, Clone, Debug)] @@ -33,6 +32,13 @@ bitflags::bitflags! { } } +impl From for AuthManifestFlags { + /// Converts to this type from the input type. + fn from(value: u32) -> Self { + AuthManifestFlags::from_bits_truncate(value) + } +} + #[repr(C)] #[derive(AsBytes, FromBytes, Default, Debug, Clone, Copy, Zeroize)] #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] @@ -161,7 +167,7 @@ impl Default for AuthManifestImageMetadata { pub struct AuthManifestImageMetadataCollection { pub header: AuthManifestImageMetadataCollectionHeader, - pub image_metadata_array: [AuthManifestImageMetadata; AUTH_MANIFEST_IMAGE_METADATA_MAX_COUNT], + pub image_metadata_list: [AuthManifestImageMetadata; AUTH_MANIFEST_IMAGE_METADATA_MAX_COUNT], } /// Caliptra Image Authorization Manifest diff --git a/common/src/lib.rs b/common/src/lib.rs index a0d45590cd..ba4e884d24 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -40,7 +40,7 @@ pub use pcr::{PcrLogEntry, PcrLogEntryId, RT_FW_CURRENT_PCR, RT_FW_JOURNEY_PCR}; pub const FMC_ORG: u32 = 0x40000000; pub const FMC_SIZE: u32 = 20 * 1024; pub const RUNTIME_ORG: u32 = FMC_ORG + FMC_SIZE; -pub const RUNTIME_SIZE: u32 = 94 * 1024; +pub const RUNTIME_SIZE: u32 = 97 * 1024; pub use memory_layout::{DATA_ORG, FHT_ORG, FHT_SIZE, MAN1_ORG}; pub use wdt::{restart_wdt, start_wdt, stop_wdt, WdtTimeout}; diff --git a/drivers/Cargo.toml b/drivers/Cargo.toml index 0825f13e72..98d0bfa77d 100644 --- a/drivers/Cargo.toml +++ b/drivers/Cargo.toml @@ -14,6 +14,7 @@ bitflags.workspace = true caliptra-error = { workspace = true, default-features = false } caliptra-image-types.workspace = true caliptra-lms-types.workspace = true +caliptra-auth-man-types.workspace = true caliptra-registers.workspace = true cfg-if.workspace = true dpe = { workspace = true, optional = true } diff --git a/drivers/src/lib.rs b/drivers/src/lib.rs index 632567e00f..23ccd648cd 100644 --- a/drivers/src/lib.rs +++ b/drivers/src/lib.rs @@ -86,6 +86,8 @@ pub use okref::okmutref; pub use okref::okref; pub use pcr_bank::{PcrBank, PcrId}; pub use pcr_reset::PcrResetCounter; +#[cfg(feature = "runtime")] +pub use persistent::{AuthManifestImageMetadataList, AUTH_MANIFEST_IMAGE_METADATA_LIST_MAX_COUNT}; pub use persistent::{ FuseLogArray, PcrLogArray, PersistentData, PersistentDataAccessor, StashMeasurementArray, FUSE_LOG_MAX_COUNT, MEASUREMENT_MAX_COUNT, PCR_LOG_MAX_COUNT, diff --git a/drivers/src/memory_layout.rs b/drivers/src/memory_layout.rs index c1b6e48ecc..0d4ec9c0c4 100644 --- a/drivers/src/memory_layout.rs +++ b/drivers/src/memory_layout.rs @@ -39,7 +39,8 @@ pub const MEASUREMENT_LOG_ORG: u32 = 0x50004C00; pub const FUSE_LOG_ORG: u32 = 0x50005000; pub const DPE_ORG: u32 = 0x50005400; pub const PCR_RESET_COUNTER_ORG: u32 = 0x50006800; -pub const DATA_ORG: u32 = 0x50006C00; +pub const AUTH_MAN_IMAGE_METADATA_LIST_ORG: u32 = 0x50006C00; +pub const DATA_ORG: u32 = 0x50007000; pub const STACK_ORG: u32 = 0x5001A000; pub const ROM_STACK_ORG: u32 = 0x5001C000; @@ -70,7 +71,8 @@ pub const MEASUREMENT_LOG_SIZE: u32 = 1024; pub const FUSE_LOG_SIZE: u32 = 1024; pub const DPE_SIZE: u32 = 5 * 1024; pub const PCR_RESET_COUNTER_SIZE: u32 = 1024; -pub const DATA_SIZE: u32 = 77 * 1024; +pub const AUTH_MAN_IMAGE_METADATA_LIST_MAX_SIZE: u32 = 1024; +pub const DATA_SIZE: u32 = 76 * 1024; pub const STACK_SIZE: u32 = 22 * 1024; pub const ROM_STACK_SIZE: u32 = 14 * 1024; pub const ESTACK_SIZE: u32 = 1024; @@ -145,7 +147,10 @@ fn mem_layout_test_dpe() { #[test] #[allow(clippy::assertions_on_constants)] fn mem_layout_test_pcr_reset_counter() { - assert_eq!((DATA_ORG - PCR_RESET_COUNTER_ORG), PCR_RESET_COUNTER_SIZE); + assert_eq!( + (AUTH_MAN_IMAGE_METADATA_LIST_ORG - PCR_RESET_COUNTER_ORG), + PCR_RESET_COUNTER_SIZE + ); } #[test] diff --git a/drivers/src/persistent.rs b/drivers/src/persistent.rs index 4e0bfddcdc..b4b0ee0d57 100644 --- a/drivers/src/persistent.rs +++ b/drivers/src/persistent.rs @@ -2,6 +2,10 @@ use core::{marker::PhantomData, mem::size_of, ptr::addr_of}; +#[cfg(feature = "runtime")] +use caliptra_auth_man_types::AuthManifestImageMetadata; +#[cfg(feature = "runtime")] +use caliptra_auth_man_types::AuthManifestImageMetadataCollection; use caliptra_image_types::ImageManifest; #[cfg(feature = "runtime")] use dpe::{DpeInstance, U8Bool, MAX_HANDLES}; @@ -21,6 +25,8 @@ use crate::pcr_reset::PcrResetCounter; pub const PCR_LOG_MAX_COUNT: usize = 17; pub const FUSE_LOG_MAX_COUNT: usize = 62; pub const MEASUREMENT_MAX_COUNT: usize = 8; +#[cfg(feature = "runtime")] +pub const AUTH_MANIFEST_IMAGE_METADATA_LIST_MAX_COUNT: usize = 8; #[cfg(feature = "runtime")] const DPE_DCCM_STORAGE: usize = size_of::() @@ -34,6 +40,9 @@ const _: () = assert!(DPE_DCCM_STORAGE < memory_layout::DPE_SIZE as usize); pub type PcrLogArray = [PcrLogEntry; PCR_LOG_MAX_COUNT]; pub type FuseLogArray = [FuseLogEntry; FUSE_LOG_MAX_COUNT]; pub type StashMeasurementArray = [MeasurementLogEntry; MEASUREMENT_MAX_COUNT]; +#[cfg(feature = "runtime")] +pub type AuthManifestImageMetadataList = + [AuthManifestImageMetadata; AUTH_MANIFEST_IMAGE_METADATA_LIST_MAX_COUNT]; #[derive(FromBytes, AsBytes, Zeroize)] #[repr(C)] @@ -82,6 +91,16 @@ pub struct PersistentData { #[cfg(not(feature = "runtime"))] pcr_reset: [u8; memory_layout::PCR_RESET_COUNTER_SIZE as usize], + + #[cfg(feature = "runtime")] + pub auth_manifest_image_metadata_col: AuthManifestImageMetadataCollection, + #[cfg(feature = "runtime")] + reserved9: [u8; memory_layout::AUTH_MAN_IMAGE_METADATA_LIST_MAX_SIZE as usize + - size_of::()], + + #[cfg(not(feature = "runtime"))] + pub auth_manifest_image_metadata_col: + [u8; memory_layout::AUTH_MAN_IMAGE_METADATA_LIST_MAX_SIZE as usize], } impl PersistentData { pub fn assert_matches_layout() { @@ -105,9 +124,14 @@ impl PersistentData { addr_of!((*P).pcr_reset) as u32, memory_layout::PCR_RESET_COUNTER_ORG ); + assert_eq!( + addr_of!((*P).auth_manifest_image_metadata_col) as u32, + memory_layout::AUTH_MAN_IMAGE_METADATA_LIST_ORG + ); assert_eq!( P.add(1) as u32, - memory_layout::PCR_RESET_COUNTER_ORG + memory_layout::PCR_RESET_COUNTER_SIZE + memory_layout::AUTH_MAN_IMAGE_METADATA_LIST_ORG + + memory_layout::AUTH_MAN_IMAGE_METADATA_LIST_MAX_SIZE ); } } diff --git a/error/src/lib.rs b/error/src/lib.rs index 47875821ea..05848f07c6 100644 --- a/error/src/lib.rs +++ b/error/src/lib.rs @@ -420,6 +420,26 @@ impl CaliptraError { CaliptraError::new_const(0x000E0043); pub const RUNTIME_LMS_VERIFY_INVALID_LMOTS_ALGORITHM: CaliptraError = CaliptraError::new_const(0x000E0044); + pub const RUNTIME_INVALID_AUTH_MANIFEST_MARKER: CaliptraError = + CaliptraError::new_const(0x000E0045); + pub const RUNTIME_AUTH_MANIFEST_PREAMBLE_SIZE_MISMATCH: CaliptraError = + CaliptraError::new_const(0x000E0046); + pub const RUNTIME_AUTH_MANIFEST_VENDOR_ECC_SIGNATURE_INVALID: CaliptraError = + CaliptraError::new_const(0x000E0047); + pub const RUNTIME_AUTH_MANIFEST_VENDOR_LMS_SIGNATURE_INVALID: CaliptraError = + CaliptraError::new_const(0x000E0048); + pub const RUNTIME_AUTH_MANIFEST_OWNER_ECC_SIGNATURE_INVALID: CaliptraError = + CaliptraError::new_const(0x000E0049); + pub const RUNTIME_AUTH_MANIFEST_OWNER_LMS_SIGNATURE_INVALID: CaliptraError = + CaliptraError::new_const(0x000E004A); + pub const RUNTIME_AUTH_MANIFEST_PREAMBLE_SIZE_LT_MIN: CaliptraError = + CaliptraError::new_const(0x000E004B); + pub const RUNTIME_AUTH_MANIFEST_IMAGE_METADATA_LIST_INVALID_SIZE: CaliptraError = + CaliptraError::new_const(0x000E004C); + pub const RUNTIME_AUTH_MANIFEST_IMAGE_METADATA_LIST_INVALID_ENTRY_COUNT: CaliptraError = + CaliptraError::new_const(0x000E004D); + pub const RUNTIME_AUTH_AND_STASH_UNSUPPORTED_IMAGE_SOURCE: CaliptraError = + CaliptraError::new_const(0x000E004E); /// FMC Errors pub const FMC_GLOBAL_NMI: CaliptraError = CaliptraError::new_const(0x000F0001); diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index 3fd32bf24e..8f5469f5fe 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -14,6 +14,7 @@ caliptra-cpu.workspace = true caliptra-drivers = { workspace = true, features = ["runtime"] } caliptra-error = { workspace = true, default-features = false } caliptra-image-types = { workspace = true, default-features = false } +caliptra-auth-man-types = { workspace = true, default-features = false } caliptra-kat.workspace = true caliptra-lms-types.workspace = true caliptra-registers.workspace = true @@ -27,6 +28,7 @@ arrayvec.workspace = true caliptra-image-verify = { workspace = true, default-features = false } zeroize.workspace = true bitflags.workspace = true +memoffset.workspace = true [build-dependencies] caliptra_common = { workspace = true, default-features = false } @@ -40,6 +42,7 @@ caliptra-image-elf.workspace = true caliptra-image-fake-keys.workspace = true caliptra-image-gen.workspace = true caliptra-image-crypto.workspace = true +caliptra-auth-man-gen.workspace = true caliptra-image-serde.workspace = true caliptra-cfi-lib-git = { workspace = true, features = ["cfi-test"] } openssl.workspace = true diff --git a/runtime/README.md b/runtime/README.md index 1a8bc839f6..f84e783fe0 100644 --- a/runtime/README.md +++ b/runtime/README.md @@ -711,6 +711,88 @@ Command Code: `0x434B_4558` ("CKEX") | fips\_status | u32 | Indicates if the command is FIPS approved or an error. | certify\_key\_resp | u8[2176] | Certify Key Response. +### SET\_AUTH\_MANIFEST + +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 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\_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: `AUTH_MANIFEST_FLAGS` input flags* + +| **Name** | **Value** | +|---------------------------|-----------| +| VENDOR_SIGNATURE_REQUIRED | 1 << 0 | + +*Table: `AUTH_MANIFEST_METADATA_ENTRY` digest entries* + +| **Name** | **Type** | **Description** | +|---------------|----------|------------------------| +| digest | u32[48] | Digest of the metadata | +| image\_source | u32 | Image source | + +*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. + + +### AUTHORIZE_AND_STASH + +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 | +| 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: { InRequest(1), ShaAcc (2) } | + +*Table: `AUTHORIZE_AND_STASH_FLAGS` input flags* + +| **Name** | **Value** | +|------------|-----------| +| 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 | AUTHORIZE_IMAGE: 0xDEADC0DE and DENY_IMAGE_AUTHORIZATION: 0x21523F21 | + ## Checksum For every command except for FW_LOAD, the request and response feature a checksum. This diff --git a/runtime/src/authorize_and_stash.rs b/runtime/src/authorize_and_stash.rs new file mode 100644 index 0000000000..17d1da1639 --- /dev/null +++ b/runtime/src/authorize_and_stash.rs @@ -0,0 +1,93 @@ +/*++ + +Licensed under the Apache-2.0 license. + +File Name: + + authorize_and_stash.rs + +Abstract: + + File contains AUTHORIZE_AND_STASH mailbox command. + +--*/ + +use core::cmp::min; +use core::mem::size_of; + +use crate::{dpe_crypto::DpeCrypto, CptraDpeTypes, DpePlatform, Drivers}; +use caliptra_auth_man_types::{ + AuthManifestImageMetadataCollection, AuthManifestImageMetadataCollectionHeader, + AuthManifestPreamble, AUTH_MANIFEST_MARKER, +}; +use caliptra_cfi_derive_git::cfi_impl_fn; +use caliptra_cfi_lib_git::cfi_launder; +use caliptra_common::mailbox_api::{ + AuthAndStashFlags, AuthorizeAndStashReq, AuthorizeAndStashResp, ImageHashSource, MailboxResp, + MailboxRespHeader, SetAuthManifestReq, +}; +use caliptra_drivers::{ + pcr_log::PCR_ID_STASH_MEASUREMENT, Array4x12, Array4xN, AuthManifestImageMetadataList, + CaliptraError, CaliptraResult, Ecc384, Ecc384PubKey, Ecc384Signature, HashValue, Lms, + PersistentData, RomVerifyConfig, Sha256, Sha384, SocIfc, + AUTH_MANIFEST_IMAGE_METADATA_LIST_MAX_COUNT, +}; +use caliptra_image_types::{ + ImageDigest, ImageEccPubKey, ImageEccSignature, ImageLmsPublicKey, ImageLmsSignature, + ImagePreamble, SHA192_DIGEST_WORD_SIZE, SHA384_DIGEST_BYTE_SIZE, +}; +use crypto::{AlgLen, Crypto}; +use dpe::{ + commands::{CommandExecution, DeriveContextCmd, DeriveContextFlags}, + context::ContextHandle, + dpe_instance::DpeEnv, + response::DpeErrorCode, +}; +use memoffset::offset_of; +use zerocopy::{AsBytes, FromBytes}; + +pub const AUTHORIZE_IMAGE: u32 = 0xDEADC0DE; +pub const DENY_IMAGE_AUTHORIZATION: u32 = 0x21523F21; + +pub struct AuthorizeAndStashCmd; +impl AuthorizeAndStashCmd { + #[cfg_attr(not(feature = "no-cfi"), cfi_impl_fn)] + #[inline(never)] + pub(crate) fn execute(drivers: &mut Drivers, cmd_args: &[u8]) -> CaliptraResult { + if let Some(cmd) = AuthorizeAndStashReq::read_from(cmd_args) { + if ImageHashSource::from(cmd.source) != ImageHashSource::InRequest { + Err(CaliptraError::RUNTIME_AUTH_AND_STASH_UNSUPPORTED_IMAGE_SOURCE)?; + } + + // Check if image hash 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 { + 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; + } + } + + let flags: AuthAndStashFlags = cmd.flags.into(); + if !flags.contains(AuthAndStashFlags::SKIP_STASH) { + // TODO: Stash the image hash + Err(CaliptraError::RUNTIME_UNIMPLEMENTED_COMMAND)?; + } + + Ok(MailboxResp::AuthorizeAndStash(AuthorizeAndStashResp { + hdr: MailboxRespHeader::default(), + auth_req_result: auth_result, + })) + } else { + Err(CaliptraError::RUNTIME_INSUFFICIENT_MEMORY) + } + } +} diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 0ac7f9af88..8fa9d0240f 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -13,6 +13,7 @@ Abstract: --*/ #![cfg_attr(not(feature = "fip-self-test"), allow(unused))] #![no_std] +mod authorize_and_stash; mod capabilities; mod certify_key_extended; pub mod dice; @@ -27,6 +28,7 @@ pub mod info; mod invoke_dpe; mod pcr; mod populate_idev; +mod set_auth_manifest; mod stash_measurement; mod subject_alt_name; mod update; @@ -34,6 +36,7 @@ mod verify; // Used by runtime tests pub mod mailbox; +use authorize_and_stash::AuthorizeAndStashCmd; use caliptra_cfi_lib_git::{cfi_assert, cfi_assert_eq, cfi_assert_ne, cfi_launder, CfiCounter}; use caliptra_registers::soc_ifc::SocIfcReg; pub use drivers::Drivers; @@ -43,6 +46,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 caliptra_common::fips::FipsVersionCmd; pub use dice::{GetFmcAliasCertCmd, GetLdevCertCmd, IDevIdCertCmd}; pub use disable::DisableAttestationCmd; @@ -56,6 +60,7 @@ pub use populate_idev::PopulateIDevIdCertCmd; pub use info::{FwInfoCmd, IDevIdInfoCmd}; pub use invoke_dpe::InvokeDpeCmd; pub use pcr::IncrementPcrResetCounterCmd; +pub use set_auth_manifest::SetAuthManifestCmd; pub use stash_measurement::StashMeasurementCmd; pub use verify::{EcdsaVerifyCmd, LmsVerifyCmd}; pub mod packet; @@ -212,6 +217,8 @@ fn handle_command(drivers: &mut Drivers) -> CaliptraResult { _ => Err(CaliptraError::RUNTIME_SELF_TEST_NOT_STARTED), }, CommandId::SHUTDOWN => FipsShutdownCmd::execute(drivers), + CommandId::SET_AUTH_MANIFEST => SetAuthManifestCmd::execute(drivers, cmd_bytes), + CommandId::AUTHORIZE_AND_STASH => AuthorizeAndStashCmd::execute(drivers, cmd_bytes), _ => Err(CaliptraError::RUNTIME_UNIMPLEMENTED_COMMAND), }?; diff --git a/runtime/src/packet.rs b/runtime/src/packet.rs index 886cbfd870..3072e2e673 100644 --- a/runtime/src/packet.rs +++ b/runtime/src/packet.rs @@ -25,7 +25,7 @@ pub struct Packet { pub len: usize, // Length in bytes } -const MAX_PAYLOAD_SIZE: usize = 512; // in dwords +const MAX_PAYLOAD_SIZE: usize = 2050; // in dwords impl Default for Packet { fn default() -> Self { diff --git a/runtime/src/set_auth_manifest.rs b/runtime/src/set_auth_manifest.rs new file mode 100644 index 0000000000..cf5146fad8 --- /dev/null +++ b/runtime/src/set_auth_manifest.rs @@ -0,0 +1,490 @@ +/*++ + +Licensed under the Apache-2.0 license. + +File Name: + + set_auth_manifest.rs + +Abstract: + + File contains AuthManifest mailbox command. + +--*/ + +use core::cmp::min; +use core::mem::size_of; + +use crate::verify; +use crate::{dpe_crypto::DpeCrypto, CptraDpeTypes, DpePlatform, Drivers}; +use caliptra_auth_man_types::{ + AuthManifestFlags, AuthManifestImageMetadataCollection, + AuthManifestImageMetadataCollectionHeader, AuthManifestPreamble, AUTH_MANIFEST_MARKER, +}; +use caliptra_cfi_derive_git::cfi_impl_fn; +use caliptra_cfi_lib_git::cfi_launder; +use caliptra_common::mailbox_api::{ + MailboxResp, MailboxRespHeader, SetAuthManifestReq, StashMeasurementReq, StashMeasurementResp, +}; +use caliptra_drivers::{ + pcr_log::PCR_ID_STASH_MEASUREMENT, Array4x12, Array4xN, AuthManifestImageMetadataList, + CaliptraError, CaliptraResult, Ecc384, Ecc384PubKey, Ecc384Signature, HashValue, Lms, + PersistentData, RomVerifyConfig, Sha256, Sha384, SocIfc, + AUTH_MANIFEST_IMAGE_METADATA_LIST_MAX_COUNT, +}; +use caliptra_image_types::{ + ImageDigest, ImageEccPubKey, ImageEccSignature, ImageLmsPublicKey, ImageLmsSignature, + ImagePreamble, SHA192_DIGEST_WORD_SIZE, SHA384_DIGEST_BYTE_SIZE, +}; +use crypto::{AlgLen, Crypto}; +use dpe::{ + commands::{CommandExecution, DeriveContextCmd, DeriveContextFlags}, + context::ContextHandle, + dpe_instance::DpeEnv, + response::DpeErrorCode, +}; +use memoffset::offset_of; +use zerocopy::{AsBytes, FromBytes}; + +pub struct SetAuthManifestCmd; +impl SetAuthManifestCmd { + fn sha384_digest( + sha384: &mut Sha384, + manifest: &[u8], + offset: u32, + len: u32, + ) -> CaliptraResult { + let err = CaliptraError::IMAGE_VERIFIER_ERR_DIGEST_OUT_OF_BOUNDS; + let data = manifest + .get(offset as usize..) + .ok_or(err)? + .get(..len as usize) + .ok_or(err)?; + Ok(sha384.digest(data)?.0) + } + + fn ecc384_verify( + ecc384: &mut Ecc384, + digest: &ImageDigest, + pub_key: &ImageEccPubKey, + sig: &ImageEccSignature, + ) -> CaliptraResult> { + let pub_key = Ecc384PubKey { + x: pub_key.x.into(), + y: pub_key.y.into(), + }; + + let digest: Array4x12 = digest.into(); + + let sig = Ecc384Signature { + r: sig.r.into(), + s: sig.s.into(), + }; + + ecc384.verify_r(&pub_key, &digest, &sig) + } + + fn lms_verify_enabled(soc_ifc: &SocIfc) -> bool { + soc_ifc.fuse_bank().lms_verify() == RomVerifyConfig::EcdsaAndLms + } + + fn lms_verify( + sha256: &mut Sha256, + digest: &ImageDigest, + pub_key: &ImageLmsPublicKey, + sig: &ImageLmsSignature, + ) -> CaliptraResult> { + let mut message = [0u8; SHA384_DIGEST_BYTE_SIZE]; + for i in 0..digest.len() { + message[i * 4..][..4].copy_from_slice(&digest[i].to_be_bytes()); + } + Lms::default().verify_lms_signature_cfi(sha256, &message, pub_key, sig) + } + + fn verify_vendor_signed_data( + auth_manifest_preamble: &AuthManifestPreamble, + fw_preamble: &ImagePreamble, + sha384: &mut Sha384, + ecc384: &mut Ecc384, + sha256: &mut Sha256, + soc_ifc: &SocIfc, + ) -> CaliptraResult<()> { + let range = AuthManifestPreamble::vendor_signed_data_range(); + let digest_vendor = Self::sha384_digest( + sha384, + auth_manifest_preamble.as_bytes(), + range.start, + range.len() as u32, + )?; + + // Verify the vendor ECC signature. + let vendor_fw_ecc_key = &fw_preamble + .vendor_pub_keys + .ecc_pub_keys + .get(fw_preamble.vendor_ecc_pub_key_idx as usize) + .ok_or(CaliptraError::RUNTIME_AUTH_MANIFEST_VENDOR_ECC_SIGNATURE_INVALID)?; + + let verify_r = Self::ecc384_verify( + ecc384, + &digest_vendor, + vendor_fw_ecc_key, + &auth_manifest_preamble.vendor_pub_keys_signatures.ecc_sig, + ) + .map_err(|_| CaliptraError::RUNTIME_AUTH_MANIFEST_VENDOR_ECC_SIGNATURE_INVALID)?; + if cfi_launder(verify_r) + != caliptra_drivers::Array4xN( + auth_manifest_preamble.vendor_pub_keys_signatures.ecc_sig.r, + ) + { + Err(CaliptraError::RUNTIME_AUTH_MANIFEST_VENDOR_ECC_SIGNATURE_INVALID)?; + } else { + caliptra_cfi_lib_git::cfi_assert_eq_12_words( + &verify_r.0, + &auth_manifest_preamble.vendor_pub_keys_signatures.ecc_sig.r, + ); + } + + // Verify vendor LMS signature. + if cfi_launder(Self::lms_verify_enabled(soc_ifc)) { + let vendor_fw_lms_key = &fw_preamble + .vendor_pub_keys + .lms_pub_keys + .get(fw_preamble.vendor_lms_pub_key_idx as usize) + .ok_or(CaliptraError::RUNTIME_AUTH_MANIFEST_VENDOR_LMS_SIGNATURE_INVALID)?; + + let candidate_key = Self::lms_verify( + sha256, + &digest_vendor, + vendor_fw_lms_key, + &auth_manifest_preamble.vendor_pub_keys_signatures.lms_sig, + ) + .map_err(|_| CaliptraError::RUNTIME_AUTH_MANIFEST_VENDOR_LMS_SIGNATURE_INVALID)?; + let pub_key_digest = HashValue::from(vendor_fw_lms_key.digest); + if candidate_key != pub_key_digest { + Err(CaliptraError::RUNTIME_AUTH_MANIFEST_VENDOR_LMS_SIGNATURE_INVALID)?; + } else { + caliptra_cfi_lib_git::cfi_assert_eq_6_words(&candidate_key.0, &pub_key_digest.0); + } + } + + Ok(()) + } + + fn verify_owner_pub_keys( + auth_manifest_preamble: &AuthManifestPreamble, + fw_preamble: &ImagePreamble, + sha384: &mut Sha384, + ecc384: &mut Ecc384, + sha256: &mut Sha256, + soc_ifc: &SocIfc, + ) -> CaliptraResult<()> { + let range = AuthManifestPreamble::owner_pub_keys_range(); + let digest_owner = Self::sha384_digest( + sha384, + auth_manifest_preamble.as_bytes(), + range.start, + range.len() as u32, + )?; + + // Verify the owner ECC signature. + let owner_fw_ecc_key = &fw_preamble.owner_pub_keys.ecc_pub_key; + let verify_r = Self::ecc384_verify( + ecc384, + &digest_owner, + owner_fw_ecc_key, + &auth_manifest_preamble.owner_pub_keys_signatures.ecc_sig, + ) + .map_err(|_| CaliptraError::RUNTIME_AUTH_MANIFEST_OWNER_ECC_SIGNATURE_INVALID)?; + if cfi_launder(verify_r) + != caliptra_drivers::Array4xN( + auth_manifest_preamble.owner_pub_keys_signatures.ecc_sig.r, + ) + { + Err(CaliptraError::RUNTIME_AUTH_MANIFEST_OWNER_ECC_SIGNATURE_INVALID)?; + } else { + caliptra_cfi_lib_git::cfi_assert_eq_12_words( + &verify_r.0, + &auth_manifest_preamble.owner_pub_keys_signatures.ecc_sig.r, + ); + } + + // Verify owner LMS signature. + if cfi_launder(Self::lms_verify_enabled(soc_ifc)) { + let owner_fw_lms_key = &fw_preamble.owner_pub_keys.lms_pub_key; + + let candidate_key = Self::lms_verify( + sha256, + &digest_owner, + owner_fw_lms_key, + &auth_manifest_preamble.owner_pub_keys_signatures.lms_sig, + ) + .map_err(|_| CaliptraError::RUNTIME_AUTH_MANIFEST_OWNER_LMS_SIGNATURE_INVALID)?; + let pub_key_digest = HashValue::from(owner_fw_lms_key.digest); + if candidate_key != pub_key_digest { + Err(CaliptraError::RUNTIME_AUTH_MANIFEST_OWNER_LMS_SIGNATURE_INVALID)?; + } else { + caliptra_cfi_lib_git::cfi_assert_eq_6_words(&candidate_key.0, &pub_key_digest.0); + } + } + + Ok(()) + } + + fn verify_vendor_image_metadata_col( + auth_manifest_preamble: &AuthManifestPreamble, + image_metadata_col_digest: &ImageDigest, + sha384: &mut Sha384, + ecc384: &mut Ecc384, + sha256: &mut Sha256, + soc_ifc: &SocIfc, + ) -> CaliptraResult<()> { + let flags = AuthManifestFlags::from(auth_manifest_preamble.flags); + if !flags.contains(AuthManifestFlags::VENDOR_SIGNATURE_REQURIED) { + return Ok(()); + } + // Verify the vendor ECC signature over the image metadata collection. + let verify_r = Self::ecc384_verify( + ecc384, + image_metadata_col_digest, + &auth_manifest_preamble.vendor_pub_keys.ecc_pub_key, + &auth_manifest_preamble + .vendor_image_metdata_signatures + .ecc_sig, + ) + .map_err(|_| CaliptraError::RUNTIME_AUTH_MANIFEST_VENDOR_ECC_SIGNATURE_INVALID)?; + if cfi_launder(verify_r) + != caliptra_drivers::Array4xN( + auth_manifest_preamble + .vendor_image_metdata_signatures + .ecc_sig + .r, + ) + { + Err(CaliptraError::RUNTIME_AUTH_MANIFEST_VENDOR_ECC_SIGNATURE_INVALID)?; + } else { + caliptra_cfi_lib_git::cfi_assert_eq_12_words( + &verify_r.0, + &auth_manifest_preamble + .vendor_image_metdata_signatures + .ecc_sig + .r, + ); + } + + // Verify vendor LMS signature over the image metadata collection. + if cfi_launder(Self::lms_verify_enabled(soc_ifc)) { + let candidate_key = Self::lms_verify( + sha256, + image_metadata_col_digest, + &auth_manifest_preamble.vendor_pub_keys.lms_pub_key, + &auth_manifest_preamble + .vendor_image_metdata_signatures + .lms_sig, + ) + .map_err(|_| CaliptraError::RUNTIME_AUTH_MANIFEST_VENDOR_LMS_SIGNATURE_INVALID)?; + let pub_key_digest = + HashValue::from(auth_manifest_preamble.vendor_pub_keys.lms_pub_key.digest); + if candidate_key != pub_key_digest { + Err(CaliptraError::RUNTIME_AUTH_MANIFEST_VENDOR_LMS_SIGNATURE_INVALID)?; + } else { + caliptra_cfi_lib_git::cfi_assert_eq_6_words(&candidate_key.0, &pub_key_digest.0); + } + } + Ok(()) + } + + fn verify_owner_image_metadata_col( + auth_manifest_preamble: &AuthManifestPreamble, + image_metadata_col_digest: &ImageDigest, + sha384: &mut Sha384, + ecc384: &mut Ecc384, + sha256: &mut Sha256, + soc_ifc: &SocIfc, + ) -> CaliptraResult<()> { + // Verify the owner ECC signature. + let verify_r = Self::ecc384_verify( + ecc384, + image_metadata_col_digest, + &auth_manifest_preamble.owner_pub_keys.ecc_pub_key, + &auth_manifest_preamble + .owner_image_metdata_signatures + .ecc_sig, + ) + .map_err(|_| CaliptraError::RUNTIME_AUTH_MANIFEST_OWNER_ECC_SIGNATURE_INVALID)?; + if cfi_launder(verify_r) + != caliptra_drivers::Array4xN( + auth_manifest_preamble + .owner_image_metdata_signatures + .ecc_sig + .r, + ) + { + Err(CaliptraError::RUNTIME_AUTH_MANIFEST_OWNER_ECC_SIGNATURE_INVALID)?; + } else { + caliptra_cfi_lib_git::cfi_assert_eq_12_words( + &verify_r.0, + &auth_manifest_preamble + .owner_image_metdata_signatures + .ecc_sig + .r, + ); + } + + // Verify owner LMS signature. + if cfi_launder(Self::lms_verify_enabled(soc_ifc)) { + let candidate_key = Self::lms_verify( + sha256, + image_metadata_col_digest, + &auth_manifest_preamble.owner_pub_keys.lms_pub_key, + &auth_manifest_preamble + .owner_image_metdata_signatures + .lms_sig, + ) + .map_err(|_| CaliptraError::RUNTIME_AUTH_MANIFEST_OWNER_LMS_SIGNATURE_INVALID)?; + let pub_key_digest = + HashValue::from(auth_manifest_preamble.owner_pub_keys.lms_pub_key.digest); + if candidate_key != pub_key_digest { + Err(CaliptraError::RUNTIME_AUTH_MANIFEST_OWNER_LMS_SIGNATURE_INVALID)?; + } else { + caliptra_cfi_lib_git::cfi_assert_eq_6_words(&candidate_key.0, &pub_key_digest.0); + } + } + + Ok(()) + } + + fn process_image_metadata_col( + cmd_buf: &[u8], + auth_manifest_preamble: &AuthManifestPreamble, + image_metadata_col: &mut AuthManifestImageMetadataCollection, + sha384: &mut Sha384, + ecc384: &mut Ecc384, + sha256: &mut Sha256, + soc_ifc: &SocIfc, + ) -> CaliptraResult<()> { + if cmd_buf.len() < size_of::() { + Err(CaliptraError::RUNTIME_AUTH_MANIFEST_IMAGE_METADATA_LIST_INVALID_SIZE)?; + } + + let col_size = min( + cmd_buf.len(), + size_of::(), + ); + let buf = cmd_buf + .get(..col_size) + .ok_or(CaliptraError::RUNTIME_AUTH_MANIFEST_IMAGE_METADATA_LIST_INVALID_SIZE)?; + + image_metadata_col.as_bytes_mut()[..col_size].copy_from_slice(buf); + + if image_metadata_col.header.entry_count == 0 + || image_metadata_col.header.entry_count + > AUTH_MANIFEST_IMAGE_METADATA_LIST_MAX_COUNT as u32 + { + Err(CaliptraError::RUNTIME_AUTH_MANIFEST_IMAGE_METADATA_LIST_INVALID_ENTRY_COUNT)?; + } + + let digest_metadata_col = Self::sha384_digest(sha384, buf, 0, col_size as u32)?; + + Self::verify_vendor_image_metadata_col( + auth_manifest_preamble, + &digest_metadata_col, + sha384, + ecc384, + sha256, + soc_ifc, + )?; + + Self::verify_owner_image_metadata_col( + auth_manifest_preamble, + &digest_metadata_col, + sha384, + ecc384, + sha256, + soc_ifc, + )?; + + Ok(()) + } + + #[cfg_attr(not(feature = "no-cfi"), cfi_impl_fn)] + #[inline(never)] + pub(crate) fn execute(drivers: &mut Drivers, cmd_args: &[u8]) -> CaliptraResult { + // Validate cmd length + let manifest_size: usize = { + let err = CaliptraError::RUNTIME_MAILBOX_INVALID_PARAMS; + let offset = offset_of!(SetAuthManifestReq, manifest_size); + u32::from_le_bytes( + cmd_args + .get(offset..offset + 4) + .ok_or(err)? + .try_into() + .map_err(|_| err)?, + ) + .try_into() + .unwrap() + }; + + if manifest_size > SetAuthManifestReq::MAX_MAN_SIZE { + Err(CaliptraError::RUNTIME_MAILBOX_INVALID_PARAMS)?; + } + + let manifest_buf = { + let offset = offset_of!(SetAuthManifestReq, manifest); + cmd_args + .get(offset..offset + manifest_size) + .ok_or(CaliptraError::RUNTIME_MAILBOX_INVALID_PARAMS)? + }; + + let preamble_size = size_of::(); + let auth_manifest_preamble = { + let err = CaliptraError::RUNTIME_AUTH_MANIFEST_PREAMBLE_SIZE_LT_MIN; + AuthManifestPreamble::read_from(manifest_buf.get(..preamble_size).ok_or(err)?) + .ok_or(err)? + }; + + // Check if the preamble has the required marker. + if auth_manifest_preamble.marker != AUTH_MANIFEST_MARKER { + Err(CaliptraError::RUNTIME_INVALID_AUTH_MANIFEST_MARKER)?; + } + + // Check if the manifest size is valid. + if auth_manifest_preamble.size as usize != size_of::() { + Err(CaliptraError::RUNTIME_AUTH_MANIFEST_PREAMBLE_SIZE_MISMATCH)?; + } + + let persistent_data = drivers.persistent_data.get_mut(); + // Verify the vendor signed data (vendor public keys + flags). + Self::verify_vendor_signed_data( + &auth_manifest_preamble, + &persistent_data.manifest1.preamble, + &mut drivers.sha384, + &mut drivers.ecc384, + &mut drivers.sha256, + &drivers.soc_ifc, + )?; + + // Verify the owner public keys. + Self::verify_owner_pub_keys( + &auth_manifest_preamble, + &persistent_data.manifest1.preamble, + &mut drivers.sha384, + &mut drivers.ecc384, + &mut drivers.sha256, + &drivers.soc_ifc, + )?; + + Self::process_image_metadata_col( + manifest_buf + .get(preamble_size..) + .ok_or(CaliptraError::RUNTIME_AUTH_MANIFEST_IMAGE_METADATA_LIST_INVALID_SIZE)?, + &auth_manifest_preamble, + &mut persistent_data.auth_manifest_image_metadata_col, + &mut drivers.sha384, + &mut drivers.ecc384, + &mut drivers.sha256, + &drivers.soc_ifc, + )?; + + Ok(MailboxResp::default()) + } +} diff --git a/runtime/tests/runtime_integration_tests/common.rs b/runtime/tests/runtime_integration_tests/common.rs index b6da89b5f5..4b865be3b7 100644 --- a/runtime/tests/runtime_integration_tests/common.rs +++ b/runtime/tests/runtime_integration_tests/common.rs @@ -9,7 +9,7 @@ use caliptra_common::mailbox_api::{ MailboxReqHeader, }; use caliptra_error::CaliptraError; -use caliptra_hw_model::{BootParams, DefaultHwModel, HwModel, InitParams, ModelError}; +use caliptra_hw_model::{BootParams, DefaultHwModel, Fuses, HwModel, InitParams, ModelError}; use dpe::{ commands::{Command, CommandHdr}, response::{ @@ -39,12 +39,11 @@ pub const TEST_DIGEST: [u8; 48] = [ pub const DEFAULT_FMC_VERSION: u16 = 0xaaaa; pub const DEFAULT_APP_VERSION: u32 = 0xbbbbbbbb; -// Run a test which boots ROM -> FMC -> test_bin. If test_bin_name is None, -// run the production runtime image. -pub fn run_rt_test( +pub fn run_rt_test_lms( test_fwid: Option<&'static FwId>, test_image_options: Option, init_params: Option, + lms_verify: bool, ) -> DefaultHwModel { let default_rt_fwid = if cfg!(feature = "fpga_realtime") { &APP_WITH_UART_FPGA @@ -77,6 +76,10 @@ pub fn run_rt_test( init_params, BootParams { fw_image: Some(&image.to_bytes().unwrap()), + fuses: Fuses { + lms_verify, + ..Default::default() + }, ..Default::default() }, ) @@ -87,6 +90,16 @@ pub fn run_rt_test( model } +// Run a test which boots ROM -> FMC -> test_bin. If test_bin_name is None, +// run the production runtime image. +pub fn run_rt_test( + test_fwid: Option<&'static FwId>, + test_image_options: Option, + init_params: Option, +) -> DefaultHwModel { + run_rt_test_lms(test_fwid, test_image_options, init_params, false) +} + pub fn generate_test_x509_cert(ec_key: PKey) -> X509 { let mut cert_builder = X509Builder::new().unwrap(); cert_builder.set_version(2).unwrap(); diff --git a/runtime/tests/runtime_integration_tests/main.rs b/runtime/tests/runtime_integration_tests/main.rs index d4d3750b26..fb62673997 100644 --- a/runtime/tests/runtime_integration_tests/main.rs +++ b/runtime/tests/runtime_integration_tests/main.rs @@ -1,6 +1,7 @@ // Licensed under the Apache-2.0 license mod common; +mod test_authorize_and_stash; mod test_boot; mod test_certify_key_extended; mod test_certs; @@ -15,6 +16,7 @@ mod test_panic_missing; mod test_pauser_privilege_levels; mod test_pcr; mod test_populate_idev; +mod test_set_auth_manifest; mod test_stash_measurement; mod test_tagging; mod test_update_reset; diff --git a/runtime/tests/runtime_integration_tests/test_authorize_and_stash.rs b/runtime/tests/runtime_integration_tests/test_authorize_and_stash.rs new file mode 100644 index 0000000000..088a375fb0 --- /dev/null +++ b/runtime/tests/runtime_integration_tests/test_authorize_and_stash.rs @@ -0,0 +1,49 @@ +// Licensed under the Apache-2.0 license + +use crate::common::run_rt_test; +use caliptra_common::mailbox_api::{ + AuthorizeAndStashReq, AuthorizeAndStashResp, CommandId, ImageHashSource, MailboxReq, + MailboxReqHeader, +}; +use caliptra_hw_model::HwModel; +use caliptra_runtime::RtBootStatus; +use caliptra_runtime::DENY_IMAGE_AUTHORIZATION; +use zerocopy::FromBytes; + +#[test] +fn test_authorize_and_stash_cmd_deny_authorization() { + let mut model = run_rt_test(None, None, None); + + model.step_until(|m| { + m.soc_ifc().cptra_boot_status().read() == u32::from(RtBootStatus::RtReadyForCommands) + }); + + let 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, + ]; + + let mut authorize_and_stash_cmd = MailboxReq::AuthorizeAndStash(AuthorizeAndStashReq { + hdr: MailboxReqHeader { chksum: 0 }, + measurement: image_digest1, + source: ImageHashSource::InRequest as u32, + ..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, + DENY_IMAGE_AUTHORIZATION + ); +} diff --git a/runtime/tests/runtime_integration_tests/test_set_auth_manifest.rs b/runtime/tests/runtime_integration_tests/test_set_auth_manifest.rs new file mode 100644 index 0000000000..bac7a2eb90 --- /dev/null +++ b/runtime/tests/runtime_integration_tests/test_set_auth_manifest.rs @@ -0,0 +1,332 @@ +// Licensed under the Apache-2.0 license + +use crate::common::{assert_error, run_rt_test_lms}; +use caliptra_auth_man_gen::{ + AuthManifestGenerator, AuthManifestGeneratorConfig, AuthManifestGeneratorKeyConfig, +}; +use caliptra_auth_man_types::{ + AuthManifestFlags, AuthManifestImageMetadata, AuthManifestPrivKeys, AuthManifestPubKeys, + AuthorizationManifest, +}; +use caliptra_common::mailbox_api::{CommandId, MailboxReq, MailboxReqHeader, SetAuthManifestReq}; +use caliptra_error::CaliptraError; +use caliptra_hw_model::HwModel; +use caliptra_image_crypto::OsslCrypto as Crypto; +use caliptra_image_fake_keys::*; +use caliptra_runtime::RtBootStatus; +use zerocopy::AsBytes; + +fn test_auth_manifest() -> 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 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, + ]; + + let image_digest2: [u8; 48] = [ + 0xCB, 0x00, 0x75, 0x3F, 0x45, 0xA3, 0x5E, 0x8B, 0xB5, 0xA0, 0x3D, 0x69, 0x9A, 0xC6, 0x50, + 0x07, 0x27, 0x2C, 0x32, 0xAB, 0x0E, 0xDE, 0xD1, 0x63, 0x1A, 0x8B, 0x60, 0x5A, 0x43, 0xFF, + 0x5B, 0xED, 0x80, 0x86, 0x07, 0x2B, 0xA1, 0xE7, 0xCC, 0x23, 0x58, 0xBA, 0xEC, 0xA1, 0x34, + 0xC8, 0x25, 0xA7, + ]; + + // Generate authorization manifest. + let image_metadata_list: Vec = vec![ + AuthManifestImageMetadata { + image_source: 0, + digest: image_digest1, + }, + AuthManifestImageMetadata { + image_source: 1, + 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: AuthManifestFlags::VENDOR_SIGNATURE_REQURIED, + }; + + let gen = AuthManifestGenerator::new(Crypto::default()); + gen.generate(&gen_config).unwrap() +} + +#[test] +fn test_set_auth_manifest_cmd() { + let mut model = run_rt_test_lms(None, None, None, true); + + 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"); +} + +#[test] +fn test_set_auth_manifest_cmd_invalid_len() { + let mut model = run_rt_test_lms(None, None, None, true); + + model.step_until(|m| { + m.soc_ifc().cptra_boot_status().read() == u32::from(RtBootStatus::RtReadyForCommands) + }); + + let mut set_auth_manifest_cmd = MailboxReq::SetAuthManifest(SetAuthManifestReq { + hdr: MailboxReqHeader { chksum: 0 }, + manifest_size: 0xffff_ffff, + manifest: [0u8; SetAuthManifestReq::MAX_MAN_SIZE], + }); + set_auth_manifest_cmd.populate_chksum().unwrap(); + + let resp = model + .mailbox_execute( + u32::from(CommandId::SET_AUTH_MANIFEST), + set_auth_manifest_cmd.as_bytes().unwrap(), + ) + .unwrap_err(); + + assert_error( + &mut model, + CaliptraError::RUNTIME_MAILBOX_INVALID_PARAMS, + resp, + ); + + let mut set_auth_manifest_cmd = MailboxReq::SetAuthManifest(SetAuthManifestReq { + hdr: MailboxReqHeader { chksum: 0 }, + manifest_size: 1_u32, + manifest: [0u8; SetAuthManifestReq::MAX_MAN_SIZE], + }); + set_auth_manifest_cmd.populate_chksum().unwrap(); + + let resp = model + .mailbox_execute( + u32::from(CommandId::SET_AUTH_MANIFEST), + set_auth_manifest_cmd.as_bytes().unwrap(), + ) + .unwrap_err(); + + assert_error( + &mut model, + CaliptraError::RUNTIME_AUTH_MANIFEST_PREAMBLE_SIZE_LT_MIN, + resp, + ); +} + +fn test_manifest_expect_err(manifest: AuthorizationManifest, expected_err: CaliptraError) { + let mut model = run_rt_test_lms(None, None, None, true); + + model.step_until(|m| { + m.soc_ifc().cptra_boot_status().read() == u32::from(RtBootStatus::RtReadyForCommands) + }); + + let buf = 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(); + + let resp = model + .mailbox_execute( + u32::from(CommandId::SET_AUTH_MANIFEST), + set_auth_manifest_cmd.as_bytes().unwrap(), + ) + .unwrap_err(); + + assert_error(&mut model, expected_err, resp); +} + +#[test] +fn test_set_auth_manifest_invalid_preamble_marker() { + let mut auth_manifest = test_auth_manifest(); + auth_manifest.preamble.marker = Default::default(); + test_manifest_expect_err( + auth_manifest, + CaliptraError::RUNTIME_INVALID_AUTH_MANIFEST_MARKER, + ); +} + +#[test] +fn test_set_auth_manifest_invalid_preamble_size() { + let mut auth_manifest = test_auth_manifest(); + auth_manifest.preamble.size -= 1; + test_manifest_expect_err( + auth_manifest, + CaliptraError::RUNTIME_AUTH_MANIFEST_PREAMBLE_SIZE_MISMATCH, + ); +} + +#[test] +fn test_set_auth_manifest_invalid_vendor_ecc_sig() { + let mut auth_manifest = test_auth_manifest(); + auth_manifest.preamble.vendor_pub_keys_signatures.ecc_sig = Default::default(); + test_manifest_expect_err( + auth_manifest, + CaliptraError::RUNTIME_AUTH_MANIFEST_VENDOR_ECC_SIGNATURE_INVALID, + ); +} + +#[test] +fn test_set_auth_manifest_invalid_vendor_lms_sig() { + let mut auth_manifest = test_auth_manifest(); + auth_manifest.preamble.vendor_pub_keys_signatures.lms_sig = Default::default(); + test_manifest_expect_err( + auth_manifest, + CaliptraError::RUNTIME_AUTH_MANIFEST_VENDOR_LMS_SIGNATURE_INVALID, + ); +} + +#[test] +fn test_set_auth_manifest_invalid_owner_ecc_sig() { + let mut auth_manifest = test_auth_manifest(); + auth_manifest.preamble.owner_pub_keys_signatures.ecc_sig = Default::default(); + test_manifest_expect_err( + auth_manifest, + CaliptraError::RUNTIME_AUTH_MANIFEST_OWNER_ECC_SIGNATURE_INVALID, + ); +} + +#[test] +fn test_set_auth_manifest_invalid_owner_lms_sig() { + let mut auth_manifest = test_auth_manifest(); + auth_manifest.preamble.owner_pub_keys_signatures.lms_sig = Default::default(); + test_manifest_expect_err( + auth_manifest, + CaliptraError::RUNTIME_AUTH_MANIFEST_OWNER_LMS_SIGNATURE_INVALID, + ); +} + +#[test] +fn test_set_auth_manifest_invalid_metadata_list_count() { + let mut auth_manifest = test_auth_manifest(); + auth_manifest.image_metadata_col.header.entry_count = 0; + test_manifest_expect_err( + auth_manifest, + CaliptraError::RUNTIME_AUTH_MANIFEST_IMAGE_METADATA_LIST_INVALID_ENTRY_COUNT, + ); +} + +#[test] +fn test_set_auth_manifest_invalid_vendor_metadata_ecc_sig() { + let mut auth_manifest = test_auth_manifest(); + auth_manifest + .preamble + .vendor_image_metdata_signatures + .ecc_sig = Default::default(); + test_manifest_expect_err( + auth_manifest, + CaliptraError::RUNTIME_AUTH_MANIFEST_VENDOR_ECC_SIGNATURE_INVALID, + ); +} + +#[test] +fn test_set_auth_manifest_invalid_vendor_metadata_lms_sig() { + let mut auth_manifest = test_auth_manifest(); + auth_manifest + .preamble + .vendor_image_metdata_signatures + .lms_sig = Default::default(); + test_manifest_expect_err( + auth_manifest, + CaliptraError::RUNTIME_AUTH_MANIFEST_VENDOR_LMS_SIGNATURE_INVALID, + ); +} + +#[test] +fn test_set_auth_manifest_invalid_owner_metadata_ecc_sig() { + let mut auth_manifest = test_auth_manifest(); + auth_manifest + .preamble + .owner_image_metdata_signatures + .ecc_sig = Default::default(); + test_manifest_expect_err( + auth_manifest, + CaliptraError::RUNTIME_AUTH_MANIFEST_OWNER_ECC_SIGNATURE_INVALID, + ); +} + +#[test] +fn test_set_auth_manifest_invalid_owner_metadata_lms_sig() { + let mut auth_manifest = test_auth_manifest(); + auth_manifest + .preamble + .owner_image_metdata_signatures + .lms_sig = Default::default(); + test_manifest_expect_err( + auth_manifest, + CaliptraError::RUNTIME_AUTH_MANIFEST_OWNER_LMS_SIGNATURE_INVALID, + ); +}