From 4a114b33c36cc7f11103c186d2fdd408a79e6f0d 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 | 29 ++++++++++----- crypto/src/openssl.rs | 60 +++++++++++++++++++++++++----- crypto/src/rustcrypto.rs | 58 ++++++++++++++++++++++++----- 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, 151 insertions(+), 45 deletions(-) diff --git a/crypto/src/lib.rs b/crypto/src/lib.rs index f74dfe00..8bea1174 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,9 @@ pub enum CryptoError { Size = 0x3, NotImplemented = 0x4, HashError(u32) = 0x5, + InvalidExportedCdiHandle = 0x6, + ExportedCdiHandleDuplicateCdi = 0x7, + ExportedCdiHandleLimitExceeded = 0x8, } impl CryptoError { @@ -66,11 +71,14 @@ 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::InvalidExportedCdiHandle + | CryptoError::ExportedCdiHandleLimitExceeded + | CryptoError::ExportedCdiHandleDuplicateCdi + | CryptoError::NotImplemented => None, } } } @@ -182,7 +190,7 @@ pub trait Crypto { algs: AlgLen, measurement: &Digest, info: &[u8], - ) -> Result; + ) -> Result<[u8; MAX_EXPORTED_CDI_SIZE], CryptoError>; /// CFI wrapper around derive_cdi /// @@ -206,7 +214,7 @@ pub trait Crypto { algs: AlgLen, measurement: &Digest, info: &[u8], - ) -> Result; + ) -> Result<[u8; MAX_EXPORTED_CDI_SIZE], CryptoError>; /// Derives a key pair using a cryptographically secure KDF /// @@ -230,14 +238,15 @@ pub trait Crypto { /// # Arguments /// /// * `algs` - Which length of algorithms to use. - /// * `cdi` - Caller-supplied private key to use in public key derivation + /// * `exported_handle` - The handle associated with an existing CDI. Created by + /// `derive_cdi_exported` /// * `label` - Caller-supplied label to use in asymmetric key derivation /// * `info` - Caller-supplied info string to use in asymmetric key derivation /// fn derive_key_pair_exported( &mut self, algs: AlgLen, - cdi: &Self::Cdi, + exported_handle: &[u8; MAX_EXPORTED_CDI_SIZE], label: &[u8], info: &[u8], ) -> Result<(Self::PrivKey, EcdsaPub), CryptoError>; @@ -263,7 +272,7 @@ pub trait Crypto { fn __cfi_derive_key_pair_exported( &mut self, algs: AlgLen, - cdi: &Self::Cdi, + exported_handle: &[u8; MAX_EXPORTED_CDI_SIZE], label: &[u8], info: &[u8], ) -> Result<(Self::PrivKey, EcdsaPub), CryptoError>; diff --git a/crypto/src/openssl.rs b/crypto/src/openssl.rs index e22f83a3..d200472a 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<(::Cdi, ExportedCdiHandle)>, +} 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(()) } @@ -172,9 +189,23 @@ impl Crypto for OpensslCrypto { algs: AlgLen, measurement: &Digest, info: &[u8], - ) -> Result { + ) -> Result { let cdi = hkdf_derive_cdi(algs, measurement, info)?; - Ok(cdi) + + for (stored_cdi, _) in self.export_cdi_slots.iter() { + if *stored_cdi == cdi { + return Err(CryptoError::ExportedCdiHandleDuplicateCdi); + } + } + + 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, exported_cdi_handle)); + Ok(exported_cdi_handle) } #[cfg_attr(not(feature = "no-cfi"), cfi_impl_fn)] @@ -192,11 +223,20 @@ impl Crypto for OpensslCrypto { fn derive_key_pair_exported( &mut self, algs: AlgLen, - cdi: &Self::Cdi, + exported_handle: &ExportedCdiHandle, label: &[u8], info: &[u8], ) -> Result<(Self::PrivKey, EcdsaPub), CryptoError> { - self.derive_key_pair_inner(algs, cdi, label, info) + let cdi = { + let mut cdi = None; + for (stored_cdi, stored_handle) in self.export_cdi_slots.iter() { + if stored_handle == exported_handle { + cdi = Some(stored_cdi.clone()); + } + } + cdi.ok_or(CryptoError::InvalidExportedCdiHandle) + }?; + self.derive_key_pair_inner(algs, &cdi, label, info) } fn ecdsa_sign_with_alias( diff --git a/crypto/src/rustcrypto.rs b/crypto/src/rustcrypto.rs index 3719393b..eca8855c 100644 --- a/crypto/src/rustcrypto.rs +++ b/crypto/src/rustcrypto.rs @@ -1,6 +1,9 @@ // Licensed under the Apache-2.0 license -use crate::{hkdf::*, AlgLen, Crypto, CryptoBuf, CryptoError, Digest, EcdsaPub, EcdsaSig, Hasher}; +use crate::{ + hkdf::*, AlgLen, Crypto, CryptoBuf, CryptoError, Digest, EcdsaPub, EcdsaSig, Hasher, + MAX_EXPORTED_CDI_SIZE, +}; use core::ops::Deref; use ecdsa::{signature::hazmat::PrehashSigner, Signature}; use p256::NistP256; @@ -54,18 +57,32 @@ 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 { + rng: StdRng::from_entropy(), + 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( @@ -114,7 +131,7 @@ impl Crypto for RustCryptoImpl { } 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(()) } @@ -134,9 +151,23 @@ impl Crypto for RustCryptoImpl { algs: AlgLen, measurement: &Digest, info: &[u8], - ) -> Result { + ) -> Result { let cdi = hkdf_derive_cdi(algs, measurement, info)?; - Ok(cdi) + + for (stored_cdi, _) in self.export_cdi_slots.iter() { + if *stored_cdi == cdi { + return Err(CryptoError::ExportedCdiHandleDuplicateCdi); + } + } + + 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, exported_cdi_handle)); + Ok(exported_cdi_handle) } #[cfg_attr(not(feature = "no-cfi"), cfi_impl_fn)] @@ -154,11 +185,20 @@ impl Crypto for RustCryptoImpl { fn derive_key_pair_exported( &mut self, algs: AlgLen, - cdi: &Self::Cdi, + exported_handle: &ExportedCdiHandle, label: &[u8], info: &[u8], ) -> Result<(Self::PrivKey, EcdsaPub), CryptoError> { - self.derive_key_pair_inner(algs, cdi, label, info) + let cdi = { + let mut cdi = None; + for (stored_cdi, stored_handle) in self.export_cdi_slots.iter() { + if stored_handle == exported_handle { + cdi = Some(stored_cdi.clone()); + } + } + cdi.ok_or(CryptoError::InvalidExportedCdiHandle) + }?; + self.derive_key_pair_inner(algs, &cdi, label, info) } fn ecdsa_sign_with_alias( 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..0cccdaec 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,13 +2393,20 @@ 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 mut exported_cdi_handle = None; + let key_pair = match cert_type { CertificateType::Exported => { - let cdi = env + let exported_handle = env .crypto .derive_exported_cdi(algs, &digest, args.cdi_label)?; - env.crypto - .derive_key_pair_exported(algs, &cdi, args.key_label, args.context) + exported_cdi_handle = Some(exported_handle); + env.crypto.derive_key_pair_exported( + algs, + &exported_handle, + args.key_label, + args.context, + ) } CertificateType::Leaf => { let cdi = env.crypto.derive_cdi(algs, &digest, args.cdi_label)?; @@ -2521,7 +2531,17 @@ fn create_dpe_cert_or_csr( } }; - Ok(CreateDpeCertResult { cert_size, pub_key }) + let exported_cdi_handle = match cert_type { + // If the `CertificateType::Exported` is set then we should have a valid exported_cdi_handle at this point. + CertificateType::Exported => exported_cdi_handle.ok_or(DpeErrorCode::InternalError)?, + _ => [0; MAX_EXPORTED_CDI_SIZE], + }; + + Ok(CreateDpeCertResult { + cert_size, + pub_key, + exported_cdi_handle, + }) } #[cfg(test)]