Skip to content

Commit

Permalink
[ROM] Only inspect RT FW's SVN during image validation.
Browse files Browse the repository at this point in the history
This is done as part of Stable Identity, which requires Caliptra
to have a single SVN. As part of this work, FMC's SVN is being
deprecated. Instead, the runtime SVN now describes the security
state of the entire firmware image. This is safe because the
FMC and runtime images are always signed, validated, and updated
in tandem.

Future changes will update build tooling to prevent callers from
setting FMC's SVN.
  • Loading branch information
bluegate010 authored and jhand2 committed Nov 21, 2024
1 parent e0beffe commit ce4af67
Show file tree
Hide file tree
Showing 25 changed files with 142 additions and 302 deletions.
5 changes: 0 additions & 5 deletions common/src/verifier.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,11 +128,6 @@ impl<'a, 'b> ImageVerificationEnv for &mut FirmwareImageVerificationEnv<'a, 'b>
self.data_vault.fmc_tci().into()
}

// Get Fuse FMC Key Manifest SVN
fn fmc_fuse_svn(&self) -> u32 {
self.soc_ifc.fuse_bank().fmc_fuse_svn()
}

// Get Runtime fuse SVN
fn runtime_fuse_svn(&self) -> u32 {
self.soc_ifc.fuse_bank().runtime_fuse_svn()
Expand Down
22 changes: 12 additions & 10 deletions drivers/src/fuse_log.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,26 +19,28 @@ use zeroize::Zeroize;

pub enum FuseLogEntryId {
Invalid = 0,
VendorEccPubKeyIndex = 1, // 4 bytes (From Manifest)
VendorEccPubKeyRevocation = 2, // 4 bytes (From Fuse)
ManifestFmcSvn = 3, // 4 bytes
ManifestReserved0 = 4, // 4 bytes
FuseFmcSvn = 5, // 4 bytes
ManifestRtSvn = 6, // 4 bytes
ManifestReserved1 = 7, // 4 bytes
FuseRtSvn = 8, // 4 bytes
VendorLmsPubKeyIndex = 9, // 4 bytes (From Manifest)
VendorEccPubKeyIndex = 1, // 4 bytes (From Manifest)
VendorEccPubKeyRevocation = 2, // 4 bytes (From Fuse)
ManifestFmcSvn = 3, // 4 bytes
ManifestReserved0 = 4, // 4 bytes
#[deprecated]
_DeprecatedFuseFmcSvn = 5, // 4 bytes
ManifestRtSvn = 6, // 4 bytes
ManifestReserved1 = 7, // 4 bytes
FuseRtSvn = 8, // 4 bytes
VendorLmsPubKeyIndex = 9, // 4 bytes (From Manifest)
VendorLmsPubKeyRevocation = 10, // 4 bytes (From Fuse)
}

impl From<u32> for FuseLogEntryId {
#[allow(deprecated)]
fn from(id: u32) -> FuseLogEntryId {
match id {
1 => FuseLogEntryId::VendorEccPubKeyIndex,
2 => FuseLogEntryId::VendorEccPubKeyRevocation,
3 => FuseLogEntryId::ManifestFmcSvn,
4 => FuseLogEntryId::ManifestReserved0,
5 => FuseLogEntryId::FuseFmcSvn,
5 => FuseLogEntryId::_DeprecatedFuseFmcSvn,
6 => FuseLogEntryId::ManifestRtSvn,
7 => FuseLogEntryId::ManifestReserved1,
8 => FuseLogEntryId::FuseRtSvn,
Expand Down
12 changes: 4 additions & 8 deletions error/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -218,12 +218,9 @@ impl CaliptraError {
CaliptraError::new_const(0x000b0023);
pub const IMAGE_VERIFIER_ERR_FMC_ENTRY_POINT_UNALIGNED: CaliptraError =
CaliptraError::new_const(0x000b0024);
pub const IMAGE_VERIFIER_ERR_FMC_SVN_GREATER_THAN_MAX_SUPPORTED: CaliptraError =
CaliptraError::new_const(0x000b0025);
pub const IMAGE_VERIFIER_ERR_FMC_SVN_LESS_THAN_MIN_SUPPORTED: CaliptraError =
CaliptraError::new_const(0x000b0026);
pub const IMAGE_VERIFIER_ERR_FMC_SVN_LESS_THAN_FUSE: CaliptraError =
CaliptraError::new_const(0x000b0027);
// 0x000b0025 was IMAGE_VERIFIER_ERR_FMC_SVN_GREATER_THAN_MAX_SUPPORTED
// 0x000b0026 was IMAGE_VERIFIER_ERR_FMC_SVN_LESS_THAN_MIN_SUPPORTED
// 0x000b0027 was IMAGE_VERIFIER_ERR_FMC_SVN_LESS_THAN_FUSE
pub const IMAGE_VERIFIER_ERR_RUNTIME_LOAD_ADDR_INVALID: CaliptraError =
CaliptraError::new_const(0x000b0028);
pub const IMAGE_VERIFIER_ERR_RUNTIME_LOAD_ADDR_UNALIGNED: CaliptraError =
Expand All @@ -234,8 +231,7 @@ impl CaliptraError {
CaliptraError::new_const(0x000b002b);
pub const IMAGE_VERIFIER_ERR_RUNTIME_SVN_GREATER_THAN_MAX_SUPPORTED: CaliptraError =
CaliptraError::new_const(0x000b002c);
pub const IMAGE_VERIFIER_ERR_RUNTIME_SVN_LESS_THAN_MIN_SUPPORTED: CaliptraError =
CaliptraError::new_const(0x000b002d);
// 0x000b002d was IMAGE_VERIFIER_ERR_RUNTIME_SVN_LESS_THAN_MIN_SUPPORTED
pub const IMAGE_VERIFIER_ERR_RUNTIME_SVN_LESS_THAN_FUSE: CaliptraError =
CaliptraError::new_const(0x000b002e);
pub const IMAGE_VERIFIER_ERR_IMAGE_LEN_MORE_THAN_BUNDLE_SIZE: CaliptraError =
Expand Down
1 change: 1 addition & 0 deletions image/types/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -479,6 +479,7 @@ pub struct ImageTocEntry {
pub version: u32,

/// Security Version Number
/// Only read for Runtime entries. Not read for FMC.
pub svn: u32,

/// Reserved field
Expand Down
8 changes: 0 additions & 8 deletions image/verify/fuzz/src/fuzz_target_common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,14 +121,6 @@ impl ImageVerificationEnv for TestEnv {
self.fmc_digest
}

fn fmc_fuse_svn(&self) -> u32 {
0
}

fn runtime_fuse_svn(&self) -> u32 {
0
}

fn iccm_range(&self) -> Range<u32> {
Range {
start: ICCM_ORG,
Expand Down
24 changes: 9 additions & 15 deletions image/verify/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ pub const MAX_RUNTIME_SVN: u32 = 128;

/// Image Verifification Executable Info
#[derive(Default, Debug)]
pub struct ImageSvnLogInfo {
pub struct FirmwareSvnLogInfo {
pub manifest_svn: u32,
pub reserved: u32,
pub fuse_svn: u32,
Expand All @@ -43,12 +43,6 @@ pub struct ImageVerificationExeInfo {
/// Entry Point
pub entry_point: u32,

/// Security version number
pub svn: u32,

/// The effective fuse SVN for this image
pub effective_fuse_svn: u32,

/// Digest of the image
pub digest: ImageDigest,
}
Expand All @@ -68,11 +62,8 @@ pub struct ImageVerificationLogInfo {
/// Vendor LMS Public Key Revocation Fuse
pub fuse_vendor_lms_pub_key_revocation: u32,

/// First Mutable code's logging information
pub fmc_log_info: ImageSvnLogInfo,

/// Runtime Mutable code's logging information
pub rt_log_info: ImageSvnLogInfo,
/// Firmware's SVN logging information
pub fw_log_info: FirmwareSvnLogInfo,
}

/// Verified image information
Expand All @@ -93,6 +84,12 @@ pub struct ImageVerificationInfo {
/// Whether `owner_pub_keys_digest` was in fuses
pub owner_pub_keys_digest_in_fuses: bool,

/// The SVN for this firmware bundle
pub fw_svn: u32,

/// The effective fuse SVN for this firmware bundle
pub effective_fuse_svn: u32,

/// First mutable code
pub fmc: ImageVerificationExeInfo,

Expand Down Expand Up @@ -154,9 +151,6 @@ pub trait ImageVerificationEnv {
// Save the fmc digest in the data vault on cold boot
fn get_fmc_digest_dv(&self) -> ImageDigest;

// Get Fuse FMC Key Manifest SVN
fn fmc_fuse_svn(&self) -> u32;

// Get Runtime fuse SVN
fn runtime_fuse_svn(&self) -> u32;

Expand Down
132 changes: 54 additions & 78 deletions image/verify/src/verifier.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,10 +112,18 @@ impl<Env: ImageVerificationEnv> ImageVerifier<Env> {
let image_info = okref(&image_info)?;

// Verify FMC
let (fmc_info, fmc_log_info) = self.verify_fmc(image_info.fmc, reason)?;
let fmc_info = self.verify_fmc(image_info.fmc, reason)?;

// Verify Runtime
let (runtime_info, rt_log_info) = self.verify_runtime(image_info.runtime)?;
let runtime_info = self.verify_runtime(image_info.runtime)?;

// The FMC and RT FW images within the bundle each include an SVN, for
// backwards compatibility with 1.x. ROM only inspects the RT FW SVN.
let fw_svn = image_info.runtime.svn;

self.verify_svn(fw_svn)?;

let effective_fuse_svn = self.effective_fuse_svn();

let info = ImageVerificationInfo {
vendor_ecc_pub_key_idx: header_info.vendor_ecc_pub_key_idx,
Expand All @@ -124,20 +132,56 @@ impl<Env: ImageVerificationEnv> ImageVerifier<Env> {
owner_pub_keys_digest_in_fuses: header_info.owner_pub_keys_digest_in_fuses,
fmc: fmc_info,
runtime: runtime_info,
fw_svn,
effective_fuse_svn,
log_info: ImageVerificationLogInfo {
vendor_ecc_pub_key_idx: header_info.vendor_ecc_pub_key_idx,
fuse_vendor_ecc_pub_key_revocation: header_info.vendor_ecc_pub_key_revocation,
fmc_log_info,
rt_log_info,
fuse_vendor_lms_pub_key_revocation: header_info.vendor_lms_pub_key_revocation,
vendor_lms_pub_key_idx: header_info.vendor_lms_pub_key_idx,
fw_log_info: FirmwareSvnLogInfo {
manifest_svn: fw_svn,
reserved: 0,
fuse_svn: self.env.runtime_fuse_svn(),
},
},
pqc_verify_config: manifest.fw_image_type.into(),
};

Ok(info)
}

/// If an SVN check is required, verifies that the given SVN is greater than
/// or equal to the fuse SVN.
fn verify_svn(&mut self, fw_svn: u32) -> CaliptraResult<()> {
if self.svn_check_required() {
if fw_svn > MAX_RUNTIME_SVN {
Err(CaliptraError::IMAGE_VERIFIER_ERR_RUNTIME_SVN_GREATER_THAN_MAX_SUPPORTED)?;
}

if cfi_launder(fw_svn) < self.env.runtime_fuse_svn() {
Err(CaliptraError::IMAGE_VERIFIER_ERR_RUNTIME_SVN_LESS_THAN_FUSE)?;
} else {
cfi_assert_ge(fw_svn, self.env.runtime_fuse_svn());
}
}
Ok(())
}

/// Calculates the effective fuse SVN.
///
/// If anti-rollback is disabled, the effective fuse-SVN is zero.
/// Otherwise, it is the value in fuses.
fn effective_fuse_svn(&mut self) -> u32 {
if cfi_launder(self.env.anti_rollback_disable()) {
cfi_assert!(self.env.anti_rollback_disable());
0_u32
} else {
cfi_assert!(!self.env.anti_rollback_disable());
self.env.runtime_fuse_svn()
}
}

/// Verify Preamble
#[cfg_attr(all(not(test), not(feature = "no-cfi")), cfi_impl_fn)]
fn verify_preamble<'a>(
Expand Down Expand Up @@ -761,7 +805,7 @@ impl<Env: ImageVerificationEnv> ImageVerifier<Env> {
&mut self,
verify_info: &ImageTocEntry,
reason: ResetReason,
) -> CaliptraResult<(ImageVerificationExeInfo, ImageSvnLogInfo)> {
) -> CaliptraResult<ImageVerificationExeInfo> {
let range = verify_info.image_range()?;

#[cfg(feature = "fips-test-hooks")]
Expand Down Expand Up @@ -806,21 +850,6 @@ impl<Env: ImageVerificationEnv> ImageVerifier<Env> {
Err(CaliptraError::IMAGE_VERIFIER_ERR_FMC_ENTRY_POINT_UNALIGNED)?;
}

if self.svn_check_required() {
if verify_info.svn > 32 {
Err(CaliptraError::IMAGE_VERIFIER_ERR_FMC_SVN_GREATER_THAN_MAX_SUPPORTED)?;
}

if cfi_launder(verify_info.svn) < self.env.fmc_fuse_svn() {
Err(CaliptraError::IMAGE_VERIFIER_ERR_FMC_SVN_LESS_THAN_FUSE)?;
} else {
cfi_assert_ge(verify_info.svn, self.env.fmc_fuse_svn());
}
}

let effective_fuse_svn =
Self::effective_fuse_svn(self.env.fmc_fuse_svn(), self.env.anti_rollback_disable());

if cfi_launder(reason) == ResetReason::UpdateReset {
if cfi_launder(actual) != self.env.get_fmc_digest_dv() {
Err(CaliptraError::IMAGE_VERIFIER_ERR_UPDATE_RESET_FMC_DIGEST_MISMATCH)?;
Expand All @@ -834,27 +863,19 @@ impl<Env: ImageVerificationEnv> ImageVerifier<Env> {
let info = ImageVerificationExeInfo {
load_addr: verify_info.load_addr,
entry_point: verify_info.entry_point,
svn: verify_info.svn,
effective_fuse_svn,
digest: verify_info.digest,
size: verify_info.size,
};

let log_info: ImageSvnLogInfo = ImageSvnLogInfo {
manifest_svn: verify_info.svn,
reserved: verify_info.reserved,
fuse_svn: self.env.fmc_fuse_svn(),
};

Ok((info, log_info))
Ok(info)
}

/// Verify Runtime
#[cfg_attr(all(not(test), not(feature = "no-cfi")), cfi_impl_fn)]
fn verify_runtime(
&mut self,
verify_info: &ImageTocEntry,
) -> CaliptraResult<(ImageVerificationExeInfo, ImageSvnLogInfo)> {
) -> CaliptraResult<ImageVerificationExeInfo> {
let range = verify_info.image_range()?;

#[cfg(feature = "fips-test-hooks")]
Expand Down Expand Up @@ -898,53 +919,14 @@ impl<Env: ImageVerificationEnv> ImageVerifier<Env> {
Err(CaliptraError::IMAGE_VERIFIER_ERR_RUNTIME_ENTRY_POINT_UNALIGNED)?;
}

if self.svn_check_required() {
if verify_info.svn > MAX_RUNTIME_SVN {
Err(CaliptraError::IMAGE_VERIFIER_ERR_RUNTIME_SVN_GREATER_THAN_MAX_SUPPORTED)?;
}

if cfi_launder(verify_info.svn) < self.env.runtime_fuse_svn() {
Err(CaliptraError::IMAGE_VERIFIER_ERR_RUNTIME_SVN_LESS_THAN_FUSE)?;
} else {
cfi_assert_ge(verify_info.svn, self.env.runtime_fuse_svn());
}
}

let effective_fuse_svn = Self::effective_fuse_svn(
self.env.runtime_fuse_svn(),
self.env.anti_rollback_disable(),
);

let info = ImageVerificationExeInfo {
load_addr: verify_info.load_addr,
entry_point: verify_info.entry_point,
svn: verify_info.svn,
effective_fuse_svn,
digest: verify_info.digest,
size: verify_info.size,
};

let log_info: ImageSvnLogInfo = ImageSvnLogInfo {
manifest_svn: verify_info.svn,
reserved: verify_info.reserved,
fuse_svn: self.env.runtime_fuse_svn(),
};

Ok((info, log_info))
}

/// Calculates the effective fuse SVN.
///
/// If anti-rollback is disabled, the effective fuse-SVN is zero.
/// Otherwise, it is SVN-fuses.
fn effective_fuse_svn(fuse_svn: u32, anti_rollback_disable: bool) -> u32 {
if cfi_launder(anti_rollback_disable) {
cfi_assert!(anti_rollback_disable);
0_u32
} else {
cfi_assert!(!anti_rollback_disable);
fuse_svn
}
Ok(info)
}
}

Expand Down Expand Up @@ -1953,10 +1935,9 @@ mod tests {

let result = verifier.verify_fmc(&verify_info, ResetReason::ColdReset);
assert!(result.is_ok());
let (info, _log_info) = result.unwrap();
let info = result.unwrap();
assert_eq!(info.load_addr, ICCM_ORG);
assert_eq!(info.entry_point, ICCM_ORG);
assert_eq!(info.svn, 1);
assert_eq!(info.size, 100);
}

Expand Down Expand Up @@ -2016,10 +1997,9 @@ mod tests {
};
let result = verifier.verify_runtime(&verify_info);
assert!(result.is_ok());
let (info, _log_info) = result.unwrap();
let info = result.unwrap();
assert_eq!(info.load_addr, ICCM_ORG);
assert_eq!(info.entry_point, ICCM_ORG);
assert_eq!(info.svn, 1);
assert_eq!(info.size, 100);
}

Expand Down Expand Up @@ -2122,10 +2102,6 @@ mod tests {
self.fmc_digest
}

fn fmc_fuse_svn(&self) -> u32 {
0
}

fn runtime_fuse_svn(&self) -> u32 {
0
}
Expand Down
Loading

0 comments on commit ce4af67

Please sign in to comment.