diff --git a/crypto/src/lib.rs b/crypto/src/lib.rs index f74dfe00..9aaff240 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,26 @@ pub trait Crypto { info: &[u8], ) -> Result; + /// Retrieve an exported CDI Handle from a CDI. + /// + /// # 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. + /// + /// # 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..3b15ccb4 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 { + if self.export_cdi_slots.len() >= MAX_CDI_HANDLES { + return Err(CryptoError::ExportedCdiHandleLimitExceeded); + } + + for (stored_cdi, stored_handle) in self.export_cdi_slots.iter() { + if stored_cdi == cdi { + return Ok(stored_handle.to_owned()); + } + } + + 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..408ef31b 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 { + if self.export_cdi_slots.len() >= MAX_CDI_HANDLES { + return Err(CryptoError::ExportedCdiHandleLimitExceeded); + } + + for (stored_cdi, stored_handle) in self.export_cdi_slots.iter() { + if stored_cdi == cdi { + return Ok(stored_handle.to_owned()); + } + } + + 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/fuzz/src/fuzz_target_1.rs b/dpe/fuzz/src/fuzz_target_1.rs index 1b1ecf62..2b5d1de3 100644 --- a/dpe/fuzz/src/fuzz_target_1.rs +++ b/dpe/fuzz/src/fuzz_target_1.rs @@ -77,6 +77,7 @@ fn harness(data: &[u8]) { Response::GetProfile(ref res) => res.resp_hdr.status, Response::InitCtx(ref res) => res.resp_hdr.status, Response::DeriveContext(ref res) => res.resp_hdr.status, + Response::DeriveContextExportedCdi(ref res) => res.resp_hdr.status, Response::RotateCtx(ref res) => res.resp_hdr.status, Response::CertifyKey(ref res) => res.resp_hdr.status, Response::Sign(ref res) => res.resp_hdr.status, 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/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 efc498ed..6ebe4ab8 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"))] @@ -2256,6 +2256,11 @@ pub(crate) struct CreateDpeCertArgs<'a> { pub(crate) struct CreateDpeCertResult { pub cert_size: u32, pub pub_key: EcdsaPub, + //TODO(clundin): Remove once https://github.com/chipsalliance/caliptra-dpe/pull/376 is merged. + /// If the cert_type is `CertificateType::Exported` the CDI is exchanged for a handle, and + /// returned via `exported_cdi_handle`. + #[allow(unused)] + pub exported_cdi_handle: [u8; MAX_EXPORTED_CDI_SIZE], } fn get_dpe_measurement_digest( @@ -2381,18 +2386,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()) { @@ -2528,7 +2537,23 @@ fn create_dpe_cert_or_csr( } }; - Ok(CreateDpeCertResult { cert_size, pub_key }) + let result = match cert_type { + CertificateType::Exported => { + let exported_cdi_handle = env.crypto.get_exported_cdi_handle(&cdi)?; + CreateDpeCertResult { + cert_size, + pub_key, + exported_cdi_handle, + } + } + _ => CreateDpeCertResult { + cert_size, + pub_key, + exported_cdi_handle: [0; MAX_EXPORTED_CDI_SIZE], + }, + }; + + Ok(result) } #[cfg(test)]