Skip to content

Commit

Permalink
Self Signed FMC Alias Csr (#1863)
Browse files Browse the repository at this point in the history
* Self Signed FMC Alias Csr

- FMC modfied to generate a  self signed FMC Alias CSR test upon cold boot.
- Persistent driver modified to add persistent memory for the FMC Alias CSR
- Runtime modified to expose an API to retrieve it.
- Test case created to verify the self signed FMC Alias CSR.
- Test case created to verify the RT Alias Certificate with the pub key of the FMC Alias CSR.

* Code review feedback.

* Remove fmc-alias-csr feature.

* NIT changes.

* Remove unused test_data

* Revert changes to strings.

* Fix RESERVED_MEMORY size.

* Remove dead code
  • Loading branch information
rusty1968 authored Jan 30, 2025
1 parent 77e61bd commit fa6b1a9
Show file tree
Hide file tree
Showing 25 changed files with 765 additions and 51 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.

41 changes: 41 additions & 0 deletions api/src/mailbox.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ impl CommandId {

// The get IDevID CSR command.
pub const GET_IDEV_CSR: Self = Self(0x4944_4352); // "IDCR"

// The get FMC Alias CSR command.
pub const GET_FMC_ALIAS_CSR: Self = Self(0x464D_4352); // "FMCR"
}

impl From<u32> for CommandId {
Expand Down Expand Up @@ -153,6 +156,7 @@ pub enum MailboxResp {
CertifyKeyExtended(CertifyKeyExtendedResp),
AuthorizeAndStash(AuthorizeAndStashResp),
GetIdevCsr(GetIdevCsrResp),
GetFmcAliasCsr(GetFmcAliasCsrResp),
}

impl MailboxResp {
Expand All @@ -174,6 +178,7 @@ impl MailboxResp {
MailboxResp::CertifyKeyExtended(resp) => Ok(resp.as_bytes()),
MailboxResp::AuthorizeAndStash(resp) => Ok(resp.as_bytes()),
MailboxResp::GetIdevCsr(resp) => Ok(resp.as_bytes()),
MailboxResp::GetFmcAliasCsr(resp) => Ok(resp.as_bytes()),
}
}

Expand All @@ -195,6 +200,7 @@ impl MailboxResp {
MailboxResp::CertifyKeyExtended(resp) => Ok(resp.as_mut_bytes()),
MailboxResp::AuthorizeAndStash(resp) => Ok(resp.as_mut_bytes()),
MailboxResp::GetIdevCsr(resp) => Ok(resp.as_mut_bytes()),
MailboxResp::GetFmcAliasCsr(resp) => Ok(resp.as_mut_bytes()),
}
}

Expand Down Expand Up @@ -1010,6 +1016,41 @@ impl Default for GetIdevCsrResp {
}
}

// GET_FMC_ALIAS_CSR
#[repr(C)]
#[derive(Default, Debug, IntoBytes, FromBytes, KnownLayout, Immutable, PartialEq, Eq)]
pub struct GetFmcAliasCsrReq {
pub hdr: MailboxReqHeader,
}

impl Request for GetFmcAliasCsrReq {
const ID: CommandId = CommandId::GET_FMC_ALIAS_CSR;
type Resp = GetFmcAliasCsrResp;
}

#[repr(C)]
#[derive(Debug, IntoBytes, FromBytes, KnownLayout, Immutable, PartialEq, Eq)]
pub struct GetFmcAliasCsrResp {
pub hdr: MailboxRespHeader,
pub data_size: u32,
pub data: [u8; Self::DATA_MAX_SIZE],
}

impl Default for GetFmcAliasCsrResp {
fn default() -> Self {
Self {
hdr: MailboxRespHeader::default(),
data_size: 0,
data: [0u8; Self::DATA_MAX_SIZE],
}
}
}

impl GetFmcAliasCsrResp {
pub const DATA_MAX_SIZE: usize = 512;
}
impl ResponseVarSize for GetFmcAliasCsrResp {}

#[repr(u32)]
#[derive(Debug, PartialEq, Eq)]
pub enum ImageHashSource {
Expand Down
4 changes: 2 additions & 2 deletions common/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,9 @@ pub use fuse::{FuseLogEntry, FuseLogEntryId};
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 FMC_SIZE: u32 = 21 * 1024;
pub const RUNTIME_ORG: u32 = FMC_ORG + FMC_SIZE;
pub const RUNTIME_SIZE: u32 = 97 * 1024;
pub const RUNTIME_SIZE: u32 = 96 * 1024;

pub use memory_layout::{DATA_ORG, PERSISTENT_DATA_ORG};
pub use wdt::{restart_wdt, start_wdt, stop_wdt, WdtTimeout};
2 changes: 2 additions & 0 deletions drivers/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,10 @@ pub use okref::okmutref;
pub use okref::okref;
pub use pcr_bank::{PcrBank, PcrId};
pub use pcr_reset::PcrResetCounter;
pub use persistent::fmc_alias_csr::FmcAliasCsr;
#[cfg(feature = "runtime")]
pub use persistent::AuthManifestImageMetadataList;

pub use persistent::{
FuseLogArray, IdevIdCsr, PcrLogArray, PersistentData, PersistentDataAccessor,
StashMeasurementArray, FUSE_LOG_MAX_COUNT, MAX_CSR_SIZE, MEASUREMENT_MAX_COUNT,
Expand Down
79 changes: 78 additions & 1 deletion drivers/src/persistent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ use crate::{
FirmwareHandoffTable,
};

use crate::FmcAliasCsr;

#[cfg(feature = "runtime")]
use crate::pcr_reset::PcrResetCounter;

Expand All @@ -38,7 +40,8 @@ pub const DPE_SIZE: u32 = 5 * 1024;
pub const PCR_RESET_COUNTER_SIZE: u32 = 1024;
pub const AUTH_MAN_IMAGE_METADATA_MAX_SIZE: u32 = 7 * 1024;
pub const IDEVID_CSR_SIZE: u32 = 1024;
pub const RESERVED_MEMORY_SIZE: u32 = 4 * 1024;
pub const FMC_ALIAS_CSR_SIZE: u32 = 1024;
pub const RESERVED_MEMORY_SIZE: u32 = 3 * 1024;

pub const PCR_LOG_MAX_COUNT: usize = 17;
pub const FUSE_LOG_MAX_COUNT: usize = 62;
Expand All @@ -61,6 +64,7 @@ pub type AuthManifestImageMetadataList =
[AuthManifestImageMetadata; AUTH_MANIFEST_IMAGE_METADATA_MAX_COUNT];

const _: () = assert!(MAX_CSR_SIZE < IDEVID_CSR_SIZE as usize);
const _: () = assert!(MAX_CSR_SIZE < FMC_ALIAS_CSR_SIZE as usize);

#[derive(Clone, TryFromBytes, IntoBytes, Zeroize)]
#[repr(C)]
Expand All @@ -69,6 +73,69 @@ pub struct IdevIdCsr {
csr: [u8; MAX_CSR_SIZE],
}

pub mod fmc_alias_csr {
use super::*;

const _: () = assert!(size_of::<FmcAliasCsr>() < FMC_ALIAS_CSR_SIZE as usize);

#[derive(Clone, TryFromBytes, IntoBytes, Zeroize)]
#[repr(C)]
pub struct FmcAliasCsr {
csr_len: u32,
csr: [u8; MAX_CSR_SIZE],
}

impl Default for FmcAliasCsr {
fn default() -> Self {
Self {
csr_len: Self::UNPROVISIONED_CSR,
csr: [0; MAX_CSR_SIZE],
}
}
}

impl FmcAliasCsr {
/// The `csr_len` field is set to this constant when a ROM image supports CSR generation but
/// the CSR generation flag was not enabled.
///
/// This is used by the runtime to distinguish ROM images that support CSR generation from
/// ones that do not.
///
/// u32::MAX is too large to be a valid CSR, so we use it to encode this state.
pub const UNPROVISIONED_CSR: u32 = u32::MAX;

/// Get the CSR buffer
pub fn get(&self) -> Option<&[u8]> {
self.csr.get(..self.csr_len as usize)
}

/// Create `Self` from a csr slice. `csr_len` MUST be the actual length of the csr.
pub fn new(csr_buf: &[u8], csr_len: usize) -> CaliptraResult<Self> {
if csr_len >= MAX_CSR_SIZE {
return Err(CaliptraError::FMC_ALIAS_INVALID_CSR);
}

let mut _self = Self {
csr_len: csr_len as u32,
csr: [0; MAX_CSR_SIZE],
};
_self.csr[..csr_len].copy_from_slice(&csr_buf[..csr_len]);

Ok(_self)
}

/// Get the length of the CSR in bytes.
pub fn get_csr_len(&self) -> u32 {
self.csr_len
}

/// Check if the CSR was unprovisioned
pub fn is_unprovisioned(&self) -> bool {
self.csr_len == Self::UNPROVISIONED_CSR
}
}
}

impl Default for IdevIdCsr {
fn default() -> Self {
Self {
Expand Down Expand Up @@ -193,6 +260,10 @@ pub struct PersistentData {
pub idevid_csr: IdevIdCsr,
reserved10: [u8; IDEVID_CSR_SIZE as usize - size_of::<IdevIdCsr>()],

pub fmc_alias_csr: FmcAliasCsr,

reserved11: [u8; FMC_ALIAS_CSR_SIZE as usize - size_of::<FmcAliasCsr>()],

// Reserved memory for future objects.
// New objects should always source memory from this range.
// Taking memory from this reserve does NOT break hitless updates.
Expand Down Expand Up @@ -282,6 +353,12 @@ impl PersistentData {
);

persistent_data_offset += IDEVID_CSR_SIZE;
assert_eq!(
addr_of!((*P).fmc_alias_csr) as u32,
memory_layout::PERSISTENT_DATA_ORG + persistent_data_offset
);

persistent_data_offset += FMC_ALIAS_CSR_SIZE;
assert_eq!(
addr_of!((*P).reserved_memory) as u32,
memory_layout::PERSISTENT_DATA_ORG + persistent_data_offset
Expand Down
16 changes: 16 additions & 0 deletions error/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -450,6 +450,12 @@ impl CaliptraError {
pub const RUNTIME_AUTH_MANIFEST_IMAGE_METADATA_LIST_DUPLICATE_FIRMWARE_ID: CaliptraError =
CaliptraError::new_const(0x000E0053);

pub const RUNTIME_GET_FMC_CSR_UNPROVISIONED: CaliptraError =
CaliptraError::new_const(0x000E0054);

pub const RUNTIME_GET_FMC_CSR_UNSUPPORTED_FMC: CaliptraError =
CaliptraError::new_const(0x000E0055);

/// FMC Errors
pub const FMC_GLOBAL_NMI: CaliptraError = CaliptraError::new_const(0x000F0001);
pub const FMC_GLOBAL_EXCEPTION: CaliptraError = CaliptraError::new_const(0x000F0002);
Expand All @@ -466,6 +472,16 @@ impl CaliptraError {
pub const FMC_GLOBAL_WDT_EXPIRED: CaliptraError = CaliptraError::new_const(0x000F000D);
pub const FMC_UNKNOWN_RESET: CaliptraError = CaliptraError::new_const(0x000F000E);

/// FMC Alias CSR Errors
pub const FMC_ALIAS_CSR_BUILDER_INIT_FAILURE: CaliptraError =
CaliptraError::new_const(0x000F000F);
pub const FMC_ALIAS_CSR_BUILDER_BUILD_FAILURE: CaliptraError =
CaliptraError::new_const(0x000F0010);
pub const FMC_ALIAS_INVALID_CSR: CaliptraError = CaliptraError::new_const(0x000F0011);
pub const FMC_ALIAS_CSR_VERIFICATION_FAILURE: CaliptraError =
CaliptraError::new_const(0x000F0012);
pub const FMC_ALIAS_CSR_OVERFLOW: CaliptraError = CaliptraError::new_const(0x000F0013);

/// TRNG_EXT Errors
pub const DRIVER_TRNG_EXT_TIMEOUT: CaliptraError = CaliptraError::new_const(0x00100001);

Expand Down
1 change: 1 addition & 0 deletions fmc/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ ufmt.workspace = true
zerocopy.workspace = true
caliptra-cfi-lib = { workspace = true, default-features = false, features = ["cfi", "cfi-counter" ] }
caliptra-cfi-derive.workspace = true
zeroize.workspace = true


[build-dependencies]
Expand Down
52 changes: 52 additions & 0 deletions fmc/src/flow/crypto.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,12 @@ File Name:
Abstract:
Crypto helper routines
--*/
use caliptra_x509::Ecdsa384Signature;

use crate::fmc_env::FmcEnv;
use caliptra_drivers::okmutref;
use zeroize::Zeroize;

use caliptra_cfi_derive::cfi_impl_fn;
use caliptra_common::{crypto::Ecc384KeyPair, keyids::KEY_ID_TMP};
use caliptra_drivers::{
Expand All @@ -14,6 +19,21 @@ use caliptra_drivers::{
KeyWriteArgs, Sha256Alg,
};

pub trait Ecdsa384SignatureAdapter {
/// Convert to ECDSA Signature
fn to_ecdsa(&self) -> Ecdsa384Signature;
}

impl Ecdsa384SignatureAdapter for Ecc384Signature {
/// Convert to ECDSA Signatuure
fn to_ecdsa(&self) -> Ecdsa384Signature {
Ecdsa384Signature {
r: (&self.r).into(),
s: (&self.s).into(),
}
}
}

pub enum Crypto {}

impl Crypto {
Expand Down Expand Up @@ -187,4 +207,36 @@ impl Crypto {
let digest = okref(&digest)?;
env.ecc384.verify(pub_key, digest, sig)
}

/// Sign the data using ECC Private Key.
/// Verify the signature using the ECC Public Key.
///
/// This routine calculates the digest of the `data`, signs the hash and returns the signature.
/// This routine also verifies the signature using the public key.
///
/// # Arguments
///
/// * `env` - FMC Environment
/// * `priv_key` - Key slot to retrieve the private key
/// * `pub_key` - Public key to verify with
/// * `data` - Input data to hash
///
/// # Returns
///
/// * `Ecc384Signature` - Signature
#[inline(always)]
pub fn ecdsa384_sign_and_verify(
env: &mut FmcEnv,
priv_key: KeyId,
pub_key: &Ecc384PubKey,
data: &[u8],
) -> CaliptraResult<Ecc384Signature> {
let mut digest = Self::sha384_digest(env, data);
let digest = okmutref(&mut digest)?;
let priv_key_args = KeyReadArgs::new(priv_key);
let priv_key = Ecc384PrivKeyIn::Key(priv_key_args);
let result = env.ecc384.sign(&priv_key, pub_key, digest, &mut env.trng);
digest.0.zeroize();
result
}
}
Loading

0 comments on commit fa6b1a9

Please sign in to comment.