From bdf97b4c0f771f3ac924548f8e54bdac54c64aea Mon Sep 17 00:00:00 2001 From: Carl Lundin Date: Wed, 15 Jan 2025 13:33:08 -0800 Subject: [PATCH] Exchange CDI for CDI handle via DPE::Crypto trait --- crypto/src/lib.rs | 39 +++++++++++++++--- crypto/src/openssl.rs | 63 +++++++++++++++++++++++++++--- crypto/src/rustcrypto.rs | 53 +++++++++++++++++++++++-- dpe/src/commands/certify_key.rs | 4 +- dpe/src/commands/derive_context.rs | 13 ++---- dpe/src/lib.rs | 2 +- dpe/src/x509.rs | 30 ++++++++++---- 7 files changed, 172 insertions(+), 32 deletions(-) diff --git a/crypto/src/lib.rs b/crypto/src/lib.rs index f74dfe00..8fa995d0 100644 --- a/crypto/src/lib.rs +++ b/crypto/src/lib.rs @@ -25,6 +25,8 @@ pub use rand::*; mod hkdf; mod signer; +pub const MAX_EXPORTED_CDI_SIZE: usize = 32; + #[derive(Debug, Clone, Copy)] #[cfg_attr(test, derive(strum_macros::EnumIter))] pub enum AlgLen { @@ -54,6 +56,7 @@ pub enum CryptoError { Size = 0x3, NotImplemented = 0x4, HashError(u32) = 0x5, + ExportedCdiHandleLimitExceeded = 0x6, } impl CryptoError { @@ -66,11 +69,12 @@ impl CryptoError { pub fn get_error_detail(&self) -> Option { match self { - CryptoError::AbstractionLayer(code) => Some(*code), - CryptoError::CryptoLibError(code) => Some(*code), - CryptoError::Size => None, - CryptoError::NotImplemented => None, - CryptoError::HashError(code) => Some(*code), + CryptoError::AbstractionLayer(code) + | CryptoError::CryptoLibError(code) + | CryptoError::HashError(code) => Some(*code), + CryptoError::Size + | CryptoError::ExportedCdiHandleLimitExceeded + | CryptoError::NotImplemented => None, } } } @@ -208,6 +212,31 @@ pub trait Crypto { info: &[u8], ) -> Result; + /// Retrieve an exported CDI Handle from a CDI. + /// If the CDI has already been exported, the same handle is returned. + /// If the number of CDIs that can be exported has reached its limit, + /// a `CryptoError` is returned. + /// + /// # Arguments + /// + /// * `cdi` - The CDI to request a handle for. + fn get_exported_cdi_handle( + &mut self, + cdi: &Self::Cdi, + ) -> Result<[u8; MAX_EXPORTED_CDI_SIZE], CryptoError>; + + /// Retrieve a CDI from an exported CDI Handle. + /// + /// If the handle is not associated with a CDI, `None` is returned. + /// + /// # Arguments + /// + /// * `exported_cdi_handle` - The handle to the CDI. + fn get_cdi_from_exported_handle( + &mut self, + exported_cdi_handle: &[u8; MAX_EXPORTED_CDI_SIZE], + ) -> Option; + /// Derives a key pair using a cryptographically secure KDF /// /// # Arguments diff --git a/crypto/src/openssl.rs b/crypto/src/openssl.rs index e22f83a3..273f8750 100644 --- a/crypto/src/openssl.rs +++ b/crypto/src/openssl.rs @@ -1,6 +1,9 @@ // Licensed under the Apache-2.0 license -use crate::{hkdf::*, AlgLen, Crypto, CryptoBuf, CryptoError, Digest, EcdsaPub, Hasher}; +use crate::{ + hkdf::*, AlgLen, Crypto, CryptoBuf, CryptoError, Digest, EcdsaPub, Hasher, + MAX_EXPORTED_CDI_SIZE, +}; #[cfg(not(feature = "no-cfi"))] use caliptra_cfi_derive_git::cfi_impl_fn; use openssl::{ @@ -41,23 +44,37 @@ impl Hasher for OpensslHasher { } } +// Currently only supports one CDI handle but in the future we may want to support multiple. +const MAX_CDI_HANDLES: usize = 1; +type ExportedCdiHandle = [u8; MAX_EXPORTED_CDI_SIZE]; + #[cfg(feature = "deterministic_rand")] -pub struct OpensslCrypto(StdRng); +pub struct OpensslCrypto { + rng: StdRng, + export_cdi_slots: Vec<(::Cdi, ExportedCdiHandle)>, +} #[cfg(not(feature = "deterministic_rand"))] -pub struct OpensslCrypto; +pub struct OpensslCrypto { + export_cdi_slots: Vec, +} impl OpensslCrypto { #[cfg(feature = "deterministic_rand")] pub fn new() -> Self { const SEED: [u8; 32] = [1; 32]; let seeded_rng = StdRng::from_seed(SEED); - OpensslCrypto(seeded_rng) + Self { + rng: seeded_rng, + export_cdi_slots: Vec::new(), + } } #[cfg(not(feature = "deterministic_rand"))] pub fn new() -> Self { - Self {} + Self { + export_cdi_slots: Vec::new(), + } } fn get_digest(algs: AlgLen) -> MessageDigest { @@ -141,7 +158,7 @@ impl Crypto for OpensslCrypto { #[cfg(feature = "deterministic_rand")] fn rand_bytes(&mut self, dst: &mut [u8]) -> Result<(), CryptoError> { - StdRng::fill_bytes(&mut self.0, dst); + StdRng::fill_bytes(&mut self.rng, dst); Ok(()) } @@ -177,6 +194,40 @@ impl Crypto for OpensslCrypto { Ok(cdi) } + fn get_exported_cdi_handle( + &mut self, + cdi: &Self::Cdi, + ) -> Result { + for (stored_cdi, stored_handle) in self.export_cdi_slots.iter() { + if stored_cdi == cdi { + return Ok(stored_handle.to_owned()); + } + } + + if self.export_cdi_slots.len() >= MAX_CDI_HANDLES { + return Err(CryptoError::ExportedCdiHandleLimitExceeded); + } + + let mut exported_cdi_handle = [0; MAX_EXPORTED_CDI_SIZE]; + self.rand_bytes(&mut exported_cdi_handle)?; + self.export_cdi_slots + .push((cdi.clone(), exported_cdi_handle)); + + Ok(exported_cdi_handle) + } + + fn get_cdi_from_exported_handle( + &mut self, + exported_cdi_handle: &ExportedCdiHandle, + ) -> Option { + for (cdi, handle) in self.export_cdi_slots.iter() { + if handle == exported_cdi_handle { + return Some(cdi.to_owned()); + } + } + None + } + #[cfg_attr(not(feature = "no-cfi"), cfi_impl_fn)] fn derive_key_pair( &mut self, diff --git a/crypto/src/rustcrypto.rs b/crypto/src/rustcrypto.rs index 3719393b..aeddc5d6 100644 --- a/crypto/src/rustcrypto.rs +++ b/crypto/src/rustcrypto.rs @@ -54,18 +54,31 @@ impl Hasher for RustCryptoHasher { } } -pub struct RustCryptoImpl(StdRng); +// Currently only supports one CDI handle but in the future we may want to support multiple. +const MAX_CDI_HANDLES: usize = 1; +type ExportedCdiHandle = [u8; MAX_EXPORTED_CDI_SIZE]; + +pub struct RustCryptoImpl { + rng: StdRng, + export_cdi_slots: Vec<(::Cdi, ExportedCdiHandle)>, +} + impl RustCryptoImpl { #[cfg(not(feature = "deterministic_rand"))] pub fn new() -> Self { - RustCryptoImpl(StdRng::from_entropy()) + Self { + export_cdi_slots: Vec::new(), + } } #[cfg(feature = "deterministic_rand")] pub fn new() -> Self { const SEED: [u8; 32] = [1; 32]; let seeded_rng = StdRng::from_seed(SEED); - RustCryptoImpl(seeded_rng) + Self { + rng: seeded_rng, + export_cdi_slots: Vec::new(), + } } fn derive_key_pair_inner( @@ -139,6 +152,40 @@ impl Crypto for RustCryptoImpl { Ok(cdi) } + fn get_exported_cdi_handle( + &mut self, + cdi: &Self::Cdi, + ) -> Result { + for (stored_cdi, stored_handle) in self.export_cdi_slots.iter() { + if stored_cdi == cdi { + return Ok(stored_handle.to_owned()); + } + } + + if self.export_cdi_slots.len() >= MAX_CDI_HANDLES { + return Err(CryptoError::ExportedCdiHandleLimitExceeded); + } + + let mut exported_cdi_handle = [0; MAX_EXPORTED_CDI_SIZE]; + self.rand_bytes(&mut exported_cdi_handle)?; + self.export_cdi_slots + .push((cdi.clone(), exported_cdi_handle)); + + Ok(exported_cdi_handle) + } + + fn get_cdi_from_exported_handle( + &mut self, + exported_cdi_handle: &ExportedCdiHandle, + ) -> Option { + for (cdi, handle) in self.export_cdi_slots.iter() { + if handle == exported_cdi_handle { + return Some(cdi.to_owned()); + } + } + None + } + #[cfg_attr(not(feature = "no-cfi"), cfi_impl_fn)] fn derive_key_pair( &mut self, diff --git a/dpe/src/commands/certify_key.rs b/dpe/src/commands/certify_key.rs index b6cb15b5..7ce38b0a 100644 --- a/dpe/src/commands/certify_key.rs +++ b/dpe/src/commands/certify_key.rs @@ -97,7 +97,9 @@ impl CommandExecution for CertifyKeyCmd { }; let mut cert = [0; MAX_CERT_SIZE]; - let CreateDpeCertResult { cert_size, pub_key } = match self.format { + let CreateDpeCertResult { + cert_size, pub_key, .. + } = match self.format { Self::FORMAT_X509 => { cfg_if! { if #[cfg(not(feature = "disable_x509"))] { diff --git a/dpe/src/commands/derive_context.rs b/dpe/src/commands/derive_context.rs index f8f4a96b..43f76cc1 100644 --- a/dpe/src/commands/derive_context.rs +++ b/dpe/src/commands/derive_context.rs @@ -8,7 +8,7 @@ use crate::{ }, tci::TciMeasurement, x509::{create_exported_dpe_cert, CreateDpeCertArgs, CreateDpeCertResult}, - DPE_PROFILE, MAX_CERT_SIZE, MAX_EXPORTED_CDI_SIZE, + DPE_PROFILE, MAX_CERT_SIZE, }; use bitflags::bitflags; #[cfg(not(feature = "no-cfi"))] @@ -16,7 +16,6 @@ use caliptra_cfi_derive_git::cfi_impl_fn; #[cfg(not(feature = "no-cfi"))] use caliptra_cfi_lib_git::{cfi_assert, cfi_assert_eq}; use cfg_if::cfg_if; -use crypto::Crypto; #[repr(C)] #[derive( @@ -300,19 +299,15 @@ impl CommandExecution for DeriveContextCmd { } else if self.creates_certificate() && self.exports_cdi() { cfg_if! { if #[cfg(not(feature = "disable_export_cdi"))] { - let mut exported_cdi_handle = [0; MAX_EXPORTED_CDI_SIZE]; - env.crypto - .rand_bytes(&mut exported_cdi_handle) - .map_err(DpeErrorCode::Crypto)?; let args = CreateDpeCertArgs { handle: &self.handle, locality, cdi_label: b"Exported CDI", key_label: b"Exported ECC", - context: &exported_cdi_handle, + context: b"Exported ECC", }; let mut cert = [0; MAX_CERT_SIZE]; - let CreateDpeCertResult { cert_size, .. } = create_exported_dpe_cert( + let CreateDpeCertResult { cert_size, exported_cdi_handle, .. } = create_exported_dpe_cert( &args, dpe, env, @@ -422,7 +417,7 @@ mod tests { context::ContextType, dpe_instance::tests::{TestTypes, RANDOM_HANDLE, SIMULATION_HANDLE, TEST_LOCALITIES}, support::Support, - DpeProfile, MAX_HANDLES, + DpeProfile, MAX_EXPORTED_CDI_SIZE, MAX_HANDLES, }; use caliptra_cfi_lib_git::CfiCounter; use crypto::{Crypto, Hasher, OpensslCrypto}; diff --git a/dpe/src/lib.rs b/dpe/src/lib.rs index 1182682d..e3378532 100644 --- a/dpe/src/lib.rs +++ b/dpe/src/lib.rs @@ -25,7 +25,7 @@ pub mod x509; use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout}; -const MAX_EXPORTED_CDI_SIZE: usize = 32; +pub use crypto::MAX_EXPORTED_CDI_SIZE; // Max cert size returned by CertifyKey const MAX_CERT_SIZE: usize = 6144; diff --git a/dpe/src/x509.rs b/dpe/src/x509.rs index 3bf45131..60e88878 100644 --- a/dpe/src/x509.rs +++ b/dpe/src/x509.rs @@ -16,7 +16,7 @@ use bitflags::bitflags; use caliptra_cfi_lib_git::cfi_launder; #[cfg(not(feature = "no-cfi"))] use caliptra_cfi_lib_git::{cfi_assert, cfi_assert_eq}; -use crypto::{Crypto, Digest, EcdsaPub, EcdsaSig, Hasher}; +use crypto::{Crypto, Digest, EcdsaPub, EcdsaSig, Hasher, MAX_EXPORTED_CDI_SIZE}; #[cfg(not(feature = "disable_x509"))] use platform::CertValidity; #[cfg(not(feature = "disable_csr"))] @@ -2265,6 +2265,9 @@ pub(crate) struct CreateDpeCertResult { pub cert_size: u32, /// Public key embedded in Cert or CSR. pub pub_key: EcdsaPub, + /// If the cert_type is `CertificateType::Exported` the CDI is exchanged for a handle, and + /// returned via `exported_cdi_handle`. + pub exported_cdi_handle: [u8; MAX_EXPORTED_CDI_SIZE], } fn get_dpe_measurement_digest( @@ -2390,18 +2393,22 @@ fn create_dpe_cert_or_csr( let algs = DPE_PROFILE.alg_len(); let digest = get_dpe_measurement_digest(dpe, env, args.handle, args.locality)?; - let key_pair = match cert_type { + let (key_pair, cdi) = match cert_type { CertificateType::Exported => { let cdi = env .crypto .derive_exported_cdi(algs, &digest, args.cdi_label)?; - env.crypto - .derive_key_pair_exported(algs, &cdi, args.key_label, args.context) + let key_pair = + env.crypto + .derive_key_pair_exported(algs, &cdi, args.key_label, args.context); + (key_pair, cdi) } CertificateType::Leaf => { let cdi = env.crypto.derive_cdi(algs, &digest, args.cdi_label)?; - env.crypto - .derive_key_pair(algs, &cdi, args.key_label, args.context) + let key_pair = env + .crypto + .derive_key_pair(algs, &cdi, args.key_label, args.context); + (key_pair, cdi) } }; if cfi_launder(key_pair.is_ok()) { @@ -2521,7 +2528,16 @@ fn create_dpe_cert_or_csr( } }; - Ok(CreateDpeCertResult { cert_size, pub_key }) + let exported_cdi_handle = match cert_type { + CertificateType::Exported => env.crypto.get_exported_cdi_handle(&cdi)?, + _ => [0; MAX_EXPORTED_CDI_SIZE], + }; + + Ok(CreateDpeCertResult { + cert_size, + pub_key, + exported_cdi_handle, + }) } #[cfg(test)]