Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[update] Manifest Based Authorization feature update. #1828

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.

4 changes: 2 additions & 2 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 metadata: [u8; 4], // Firmware Id.
pub measurement: [u8; 48], // Image digest.
pub context: [u8; 48],
pub svn: u32,
pub flags: u32,
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
78 changes: 66 additions & 12 deletions runtime/src/authorize_and_stash.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,13 @@ Abstract:

--*/

use core::cmp::min;
use core::cmp::{self, min};
use core::mem::size_of;

use crate::{dpe_crypto::DpeCrypto, CptraDpeTypes, DpePlatform, Drivers, StashMeasurementCmd};
use caliptra_auth_man_types::{
AuthManifestImageMetadataCollection, AuthManifestPreamble, AUTH_MANIFEST_MARKER,
AuthManifestImageMetadata, AuthManifestImageMetadataCollection, AuthManifestPreamble,
ImageMetadataFlags, AUTH_MANIFEST_MARKER,
};
use caliptra_cfi_derive_git::cfi_impl_fn;
use caliptra_cfi_lib_git::cfi_launder;
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,25 +59,35 @@ impl AuthorizeAndStashCmd {
Err(CaliptraError::RUNTIME_AUTH_AND_STASH_UNSUPPORTED_IMAGE_SOURCE)?;
}

// Check if image hash is present in the image metadata entry collection.
// Check if firmware id is present in the image metadata entry collection.
let persistent_data = drivers.persistent_data.get();
let auth_manifest_image_metadata_col =
&persistent_data.auth_manifest_image_metadata_col;

let mut auth_result = DENY_IMAGE_AUTHORIZATION;
for metadata_entry in auth_manifest_image_metadata_col.image_metadata_list.iter() {
if cfi_launder(metadata_entry.digest) == cmd.measurement {
let auth_result = if let Some(metadata_entry) =
Self::find_metadata_entry(auth_manifest_image_metadata_col, &cmd.metadata)
{
// If 'ignore_auth_check' is set, then skip the image digest comparison and authorize the image.
let flags = ImageMetadataFlags(metadata_entry.flags);
let ignore_auth_check = flags.ignore_auth_check();

if flags.ignore_auth_check() == cfi_launder(ignore_auth_check) {
IMAGE_AUTHORIZED
} else if cfi_launder(metadata_entry.digest) == cmd.measurement {
caliptra_cfi_lib_git::cfi_assert_eq_12_words(
&Array4x12::from(metadata_entry.digest).0,
&Array4x12::from(cmd.measurement).0,
);
auth_result = AUTHORIZE_IMAGE;
break;
IMAGE_AUTHORIZED
} else {
IMAGE_HASH_MISMATCH
}
}
} else {
IMAGE_NOT_AUTHORIZED
};

// Stash the measurement if the image is authorized.
if auth_result == AUTHORIZE_IMAGE {
if auth_result == IMAGE_AUTHORIZED {
let flags: AuthAndStashFlags = cmd.flags.into();
if !flags.contains(AuthAndStashFlags::SKIP_STASH) {
let dpe_result = StashMeasurementCmd::stash_measurement(
Expand All @@ -100,4 +112,46 @@ impl AuthorizeAndStashCmd {
Err(CaliptraError::RUNTIME_INSUFFICIENT_MEMORY)
}
}

/// Search for a metadata entry in the sorted `AuthManifestImageMetadataCollection` that matches the firmware ID.
///
/// This function performs a binary search on the `image_metadata_list` of the provided `AuthManifestImageMetadataCollection`.
/// It compares the firmware ID (`fw_id`) of each metadata entry with the provided `cmd_fw_id_bytes`.
///
/// # Arguments
///
/// * `auth_manifest_image_metadata_col` - A reference to the `AuthManifestImageMetadataCollection` containing the metadata entries.
/// * `cmd_fw_id_bytes` - A reference to a `[u8; 4]` array representing the firmware ID from the command to search for.
///
/// # Returns
///
/// * `Option<&'a AuthManifestImageMetadata>` - Returns `Some(&AuthManifestImageMetadata)` if a matching entry is found,
/// otherwise returns `None`.
///
#[inline(never)]
fn find_metadata_entry<'a>(
auth_manifest_image_metadata_col: &'a AuthManifestImageMetadataCollection,
cmd_fw_id_bytes: &[u8; 4],
) -> Option<&'a AuthManifestImageMetadata> {
let mut left = 0;
let mut right = auth_manifest_image_metadata_col.entry_count as usize;

while left < right {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we use auth_manifest_image_metadata_col.image_metadata_list.binary_search_by_key()?

Either way, we should add unit tests for this function.

let mid = (left + right) / 2;
// This check is needed to avoid out of bounds panic.
if mid >= auth_manifest_image_metadata_col.image_metadata_list.len() {
break;
}
let metadata_entry = &auth_manifest_image_metadata_col.image_metadata_list[mid];
let entry_fw_id_bytes = metadata_entry.fw_id.to_le_bytes();

match cmd_fw_id_bytes.cmp(&entry_fw_id_bytes) {
cmp::Ordering::Less => right = mid,
cmp::Ordering::Greater => left = mid + 1,
cmp::Ordering::Equal => return Some(metadata_entry),
}
}

None
}
}
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_NOT_AUTHORIZED};
pub use caliptra_common::fips::FipsVersionCmd;
pub use dice::{GetFmcAliasCertCmd, GetLdevCertCmd, IDevIdCertCmd};
pub use disable::DisableAttestationCmd;
Expand Down
Loading
Loading