Skip to content

Commit

Permalink
[update] Manifest Based Authorization feature update.
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
mhatrevi authored and jhand2 committed Dec 11, 2024
1 parent 2f8f19b commit cbd59bf
Show file tree
Hide file tree
Showing 13 changed files with 878 additions and 100 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions api/src/mailbox.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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(),
Expand Down
6 changes: 6 additions & 0 deletions auth-manifest/app/src/auth-man.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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
21 changes: 18 additions & 3 deletions auth-manifest/app/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")]
Expand All @@ -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
Expand Down Expand Up @@ -119,14 +121,27 @@ pub(crate) fn image_metadata_config_from_file(
config: &Vec<ImageMetadataConfigFromFile>,
) -> anyhow::Result<Vec<AuthManifestImageMetadata>> {
let mut image_metadata_list = Vec::new();
let mut fw_ids: Vec<u32> = 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);
Expand Down
1 change: 1 addition & 0 deletions auth-manifest/types/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
Expand Down
24 changes: 17 additions & 7 deletions auth-manifest/types/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down Expand Up @@ -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,

Expand Down Expand Up @@ -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,
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions error/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -447,6 +447,8 @@ impl CaliptraError {
CaliptraError::new_const(0x000E0051);
pub const RUNTIME_GET_IDEV_ID_UNSUPPORTED_ROM: CaliptraError =
CaliptraError::new_const(0x000E0052);
pub const RUNTIME_AUTH_MANIFEST_IMAGE_METADATA_LIST_DUPLICATE_FIRMWARE_ID: CaliptraError =
CaliptraError::new_const(0x000E0053);

/// FMC Errors
pub const FMC_GLOBAL_NMI: CaliptraError = CaliptraError::new_const(0x000F0001);
Expand Down
35 changes: 22 additions & 13 deletions runtime/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -725,7 +725,7 @@ Command Code: `0x4154_4D4E` ("ATMN")
| preamble\_marker | u32 | Marker needs to be 0x4154_4D4E for the preamble to be valid |
| preamble\_size | u32 | Size of the preamble |
| preamble\_version | u32 | Version of the preamble |
| preamble\_flags | u32 | Preamble flags |
| preamble\_flags | u32 | Manifest flags. See AUTH_MANIFEST_FLAGS below |
| preamble\_vendor\_ecc384\_key | u32[24] | Vendor ECC384 key with X and Y coordinates in that order |
| preamble\_vendor\_lms\_key | u32[6] | Vendor LMS-SHA192-H15 key |
| preamble\_vendor\_ecc384\_sig | u32[24] | Vendor ECC384 signature |
Expand All @@ -739,7 +739,7 @@ Command Code: `0x4154_4D4E` ("ATMN")
| metadata\_owner\_ecc384\_sig | u32[24] | Metadata Owner ECC384 signature |
| metadata\_owner\_LMS\_sig | u32[1344] | Metadata Owner LMOTS-SHA192-W4 signature |
| metadata\_entry\_entry\_count | u32 | number of metadata entries |
| metadata\_entries | MetaData[128] | The max number of metadata entries is 128 but less can be used |
| metadata\_entries | Metadata[127] | The max number of metadata entries is 127 but less can be used |


*Table: `AUTH_MANIFEST_FLAGS` input flags*
Expand All @@ -750,10 +750,19 @@ Command Code: `0x4154_4D4E` ("ATMN")

*Table: `AUTH_MANIFEST_METADATA_ENTRY` digest entries*

| **Name** | **Type** | **Description** |
|---------------|----------|------------------------|
| digest | u32[48] | Digest of the metadata |
| image\_source | u32 | Image source |
| **Name** | **Type** | **Description** |
|---------------|----------|--------------------------------|
| fw\_id | u32 | Id of the image |
| flags | u32 | See METADATA_ENTRY_FLAGS below |
| digest | u32[48] | Digest of the image |


*Table: `METADATA_ENTRY_FLAGS` input flags*

| **Name** | **Size (Bits)** | **Description** |
|---------------------|-----------------|-----------------|
| image\_source | 2 | 1: InRequest |
| ignore\_auth\_check | 1 | If set, the image digest is not compared for the firmware id |

*Table: `SET_AUTH_MANIFEST` output arguments*

Expand All @@ -769,14 +778,14 @@ 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 |
| flags | u32 | See AUTHORIZE_AND_STASH_FLAGS below |
| source | u32 | Enumeration values: { InRequest(1), ShaAcc (2) } |

*Table: `AUTHORIZE_AND_STASH_FLAGS` input flags*
Expand All @@ -790,7 +799,7 @@ Command Code: `0x4154_5348` ("ATSH")
| -------- | -------- | ---------------
| chksum | u32 | Checksum over other output arguments, computed by Caliptra. Little endian. |
| fips_status | u32 | Indicates if the command is FIPS approved or an error. |
| auth_req_result | u32 | AUTHORIZE_IMAGE: 0xDEADC0DE and DENY_IMAGE_AUTHORIZATION: 0x21523F21 |
| auth_req_result | u32 | AUTHORIZE_IMAGE (0xDEADC0DE), IMAGE_NOT_AUTHORIZED (0x21523F21) or IMAGE_HASH_MISMATCH (0x8BFB95CB) |

### GET\_IDEVID\_CSR

Expand Down
67 changes: 53 additions & 14 deletions runtime/src/authorize_and_stash.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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 {
Expand All @@ -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 {
Expand All @@ -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])
}
}
2 changes: 1 addition & 1 deletion runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Loading

0 comments on commit cbd59bf

Please sign in to comment.