diff --git a/Cargo.lock b/Cargo.lock index d565d1df..2a87ab5a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -296,7 +296,6 @@ dependencies = [ "caliptra-cfi-lib-git", "ecdsa", "hkdf", - "hmac", "openssl", "p256", "p384", diff --git a/crypto/Cargo.toml b/crypto/Cargo.toml index e14f71e8..9af37bbc 100644 --- a/crypto/Cargo.toml +++ b/crypto/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" [features] openssl = ["dep:openssl", "dep:hkdf", "dep:sha2"] -rustcrypto = ["dep:hkdf", "dep:hmac", "dep:p256", "dep:p384", "dep:rand", "dep:sha2", "dep:base64ct", "dep:ecdsa", "dep:sec1"] +rustcrypto = ["dep:hkdf", "dep:p256", "dep:p384", "dep:rand", "dep:sha2", "dep:base64ct", "dep:ecdsa", "dep:sec1"] deterministic_rand = ["dep:rand"] no-cfi = [] @@ -17,7 +17,6 @@ caliptra-cfi-lib-git = { workspace = true, default-features = false, features = caliptra-cfi-derive-git.workspace = true ecdsa = { version = "0.16.9", optional = true, features = ["pem"]} hkdf = { version = "0.12.3", optional = true } -hmac = {version="0.12.1", optional = true} openssl = {workspace = true, optional = true} p256 = {version= "0.13.2", optional = true} p384 = {version= "0.13.0", optional = true} diff --git a/crypto/src/lib.rs b/crypto/src/lib.rs index cb9aee03..4e9623f2 100644 --- a/crypto/src/lib.rs +++ b/crypto/src/lib.rs @@ -25,6 +25,9 @@ pub use rand::*; mod hkdf; mod signer; +pub const MAX_EXPORTED_CDI_SIZE: usize = 32; +pub type ExportedCdiHandle = [u8; MAX_EXPORTED_CDI_SIZE]; + #[derive(Debug, Clone, Copy)] #[cfg_attr(test, derive(strum_macros::EnumIter))] pub enum AlgLen { @@ -54,6 +57,9 @@ pub enum CryptoError { Size = 0x3, NotImplemented = 0x4, HashError(u32) = 0x5, + InvalidExportedCdiHandle = 0x6, + ExportedCdiHandleDuplicateCdi = 0x7, + ExportedCdiHandleLimitExceeded = 0x8, } impl CryptoError { @@ -66,11 +72,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, } } } @@ -170,6 +179,20 @@ pub trait Crypto { info: &[u8], ) -> Result; + /// Derive a CDI for an exported private key based on the current base CDI and measurements + /// + /// # Arguments + /// + /// * `algs` - Which length of algorithms to use. + /// * `measurement` - A digest of the measurements which should be used for CDI derivation + /// * `info` - Caller-supplied info string to use in CDI derivation + fn derive_exported_cdi( + &mut self, + algs: AlgLen, + measurement: &Digest, + info: &[u8], + ) -> Result; + /// CFI wrapper around derive_cdi /// /// To implement this function, you need to add the @@ -182,6 +205,18 @@ pub trait Crypto { info: &[u8], ) -> Result; + /// CFI wrapper around derive_cdi_exported + /// + /// To implement this function, you need to add the + /// cfi_impl_fn proc_macro to derive_exported_cdi. + #[cfg(not(feature = "no-cfi"))] + fn __cfi_derive_exported_cdi( + &mut self, + algs: AlgLen, + measurement: &Digest, + info: &[u8], + ) -> Result; + /// Derives a key pair using a cryptographically secure KDF /// /// # Arguments @@ -199,6 +234,24 @@ pub trait Crypto { info: &[u8], ) -> Result<(Self::PrivKey, EcdsaPub), CryptoError>; + /// Derives an exported key pair using a cryptographically secure KDF + /// + /// # Arguments + /// + /// * `algs` - Which length of algorithms to use. + /// * `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, + exported_handle: &ExportedCdiHandle, + label: &[u8], + info: &[u8], + ) -> Result<(Self::PrivKey, EcdsaPub), CryptoError>; + /// CFI wrapper around derive_key_pair /// /// To implement this function, you need to add the @@ -212,6 +265,19 @@ pub trait Crypto { info: &[u8], ) -> Result<(Self::PrivKey, EcdsaPub), CryptoError>; + /// CFI wrapper around derive_key_pair_exported + /// + /// To implement this function, you need to add the + /// cfi_impl_fn proc_macro to derive_key_pair. + #[cfg(not(feature = "no-cfi"))] + fn __cfi_derive_key_pair_exported( + &mut self, + algs: AlgLen, + exported_handle: &ExportedCdiHandle, + label: &[u8], + info: &[u8], + ) -> Result<(Self::PrivKey, EcdsaPub), CryptoError>; + /// Sign `digest` with the platform Alias Key /// /// # Arguments @@ -240,24 +306,6 @@ pub trait Crypto { priv_key: &Self::PrivKey, pub_key: &EcdsaPub, ) -> Result; - - /// Sign `digest` with a derived HMAC key from the CDI. - /// - /// # Arguments - /// - /// * `algs` - Which length of algorithms to use. - /// * `cdi` - CDI from which to derive the signing key - /// * `label` - Caller-supplied label to use in symmetric key derivation - /// * `info` - Caller-supplied info string to use in symmetric key derivation - /// * `digest` - Digest of data to be signed. - fn hmac_sign_with_derived( - &mut self, - algs: AlgLen, - cdi: &Self::Cdi, - label: &[u8], - info: &[u8], - digest: &Digest, - ) -> Result; } #[cfg(test)] mod tests { diff --git a/crypto/src/openssl.rs b/crypto/src/openssl.rs index 888f27bf..05128deb 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, HmacSig}; +use crate::{ + hkdf::*, AlgLen, Crypto, CryptoBuf, CryptoError, Digest, EcdsaPub, ExportedCdiHandle, Hasher, + MAX_EXPORTED_CDI_SIZE, +}; #[cfg(not(feature = "no-cfi"))] use caliptra_cfi_derive_git::cfi_impl_fn; use openssl::{ @@ -10,8 +13,7 @@ use openssl::{ error::ErrorStack, hash::MessageDigest, nid::Nid, - pkey::{PKey, Private}, - sign::Signer, + pkey::Private, }; #[cfg(feature = "deterministic_rand")] use rand::{rngs::StdRng, RngCore, SeedableRng}; @@ -42,23 +44,36 @@ 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; + #[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 { @@ -91,6 +106,35 @@ impl OpensslCrypto { EcKey::from_private_components(&group, priv_key_bn, &pub_point) } + + fn derive_key_pair_inner( + &mut self, + algs: AlgLen, + cdi: &::Cdi, + label: &[u8], + info: &[u8], + ) -> Result<(::PrivKey, EcdsaPub), CryptoError> { + let priv_key = hkdf_get_priv_key(algs, cdi, label, info)?; + + let ec_priv_key = OpensslCrypto::ec_key_from_priv_key(algs, &priv_key)?; + let nid = OpensslCrypto::get_curve(algs); + + let group = EcGroup::from_curve_name(nid).unwrap(); + let mut bn_ctx = BigNumContext::new().unwrap(); + + let mut x = BigNum::new().unwrap(); + let mut y = BigNum::new().unwrap(); + + ec_priv_key + .public_key() + .affine_coordinates(&group, &mut x, &mut y, &mut bn_ctx) + .unwrap(); + + let x = CryptoBuf::new(&x.to_vec_padded(algs.size() as i32).unwrap()).unwrap(); + let y = CryptoBuf::new(&y.to_vec_padded(algs.size() as i32).unwrap()).unwrap(); + + Ok((priv_key, EcdsaPub { x, y })) + } } impl Default for OpensslCrypto { @@ -113,7 +157,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(()) } @@ -134,37 +178,64 @@ impl Crypto for OpensslCrypto { measurement: &Digest, info: &[u8], ) -> Result { - hkdf_derive_cdi(algs, measurement, info) + let cdi = hkdf_derive_cdi(algs, measurement, info)?; + Ok(cdi) } #[cfg_attr(not(feature = "no-cfi"), cfi_impl_fn)] - fn derive_key_pair( + fn derive_exported_cdi( &mut self, algs: AlgLen, - cdi: &Self::Cdi, - label: &[u8], + measurement: &Digest, info: &[u8], - ) -> Result<(Self::PrivKey, EcdsaPub), CryptoError> { - let priv_key = hkdf_get_priv_key(algs, cdi, label, info)?; - - let ec_priv_key = OpensslCrypto::ec_key_from_priv_key(algs, &priv_key)?; - let nid = OpensslCrypto::get_curve(algs); + ) -> Result { + let cdi = hkdf_derive_cdi(algs, measurement, info)?; - let group = EcGroup::from_curve_name(nid).unwrap(); - let mut bn_ctx = BigNumContext::new().unwrap(); + for (stored_cdi, _) in self.export_cdi_slots.iter() { + if *stored_cdi == cdi { + return Err(CryptoError::ExportedCdiHandleDuplicateCdi); + } + } - let mut x = BigNum::new().unwrap(); - let mut y = BigNum::new().unwrap(); + if self.export_cdi_slots.len() >= MAX_CDI_HANDLES { + return Err(CryptoError::ExportedCdiHandleLimitExceeded); + } - ec_priv_key - .public_key() - .affine_coordinates(&group, &mut x, &mut y, &mut bn_ctx) - .unwrap(); + 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) + } - let x = CryptoBuf::new(&x.to_vec_padded(algs.size() as i32).unwrap()).unwrap(); - let y = CryptoBuf::new(&y.to_vec_padded(algs.size() as i32).unwrap()).unwrap(); + #[cfg_attr(not(feature = "no-cfi"), cfi_impl_fn)] + fn derive_key_pair( + &mut self, + algs: AlgLen, + cdi: &Self::Cdi, + label: &[u8], + info: &[u8], + ) -> Result<(Self::PrivKey, EcdsaPub), CryptoError> { + self.derive_key_pair_inner(algs, cdi, label, info) + } - Ok((priv_key, EcdsaPub { x, y })) + #[cfg_attr(not(feature = "no-cfi"), cfi_impl_fn)] + fn derive_key_pair_exported( + &mut self, + algs: AlgLen, + exported_handle: &ExportedCdiHandle, + label: &[u8], + info: &[u8], + ) -> Result<(Self::PrivKey, EcdsaPub), CryptoError> { + 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( @@ -208,23 +279,4 @@ impl Crypto for OpensslCrypto { Ok(super::EcdsaSig { r, s }) } - - fn hmac_sign_with_derived( - &mut self, - algs: AlgLen, - cdi: &Self::Cdi, - label: &[u8], - info: &[u8], - digest: &Digest, - ) -> Result { - let (symmetric_key, _) = self.derive_key_pair(algs, cdi, label, info)?; - let hmac_key = PKey::hmac(symmetric_key.bytes()).unwrap(); - - let sha_size = Self::get_digest(algs); - let mut signer = Signer::new(sha_size, &hmac_key).unwrap(); - signer.update(digest.bytes()).unwrap(); - let hmac = signer.sign_to_vec().unwrap(); - - Ok(HmacSig::new(&hmac).unwrap()) - } } diff --git a/crypto/src/rustcrypto.rs b/crypto/src/rustcrypto.rs index 8ec37553..a4a22672 100644 --- a/crypto/src/rustcrypto.rs +++ b/crypto/src/rustcrypto.rs @@ -1,11 +1,11 @@ // Licensed under the Apache-2.0 license use crate::{ - hkdf::*, AlgLen, Crypto, CryptoBuf, CryptoError, Digest, EcdsaPub, EcdsaSig, Hasher, HmacSig, + hkdf::*, AlgLen, Crypto, CryptoBuf, CryptoError, Digest, EcdsaPub, EcdsaSig, ExportedCdiHandle, + Hasher, MAX_EXPORTED_CDI_SIZE, }; use core::ops::Deref; use ecdsa::{signature::hazmat::PrehashSigner, Signature}; -use hmac::{Hmac, Mac}; use p256::NistP256; use p384::NistP384; use rand::{rngs::StdRng, RngCore, SeedableRng}; @@ -57,18 +57,59 @@ 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; + +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( + &mut self, + algs: AlgLen, + cdi: &::Cdi, + label: &[u8], + info: &[u8], + ) -> Result<(::PrivKey, EcdsaPub), CryptoError> { + let secret = hkdf_get_priv_key(algs, cdi, label, info)?; + match algs { + AlgLen::Bit256 => { + let signing = p256::ecdsa::SigningKey::from_slice(&secret.bytes())?; + let verifying = p256::ecdsa::VerifyingKey::from(&signing); + let point = verifying.to_encoded_point(false); + let x = CryptoBuf::new(point.x().ok_or(RUSTCRYPTO_ECDSA_ERROR)?.as_slice())?; + let y = CryptoBuf::new(point.y().ok_or(RUSTCRYPTO_ECDSA_ERROR)?.as_slice())?; + Ok((secret, EcdsaPub { x, y })) + } + AlgLen::Bit384 => { + let signing = p384::ecdsa::SigningKey::from_slice(&secret.bytes())?; + let verifying = p384::ecdsa::VerifyingKey::from(&signing); + let point = verifying.to_encoded_point(false); + let x = CryptoBuf::new(point.x().ok_or(RUSTCRYPTO_ECDSA_ERROR)?.as_slice())?; + let y = CryptoBuf::new(point.y().ok_or(RUSTCRYPTO_ECDSA_ERROR)?.as_slice())?; + Ok((secret, EcdsaPub { x, y })) + } + } } } @@ -89,10 +130,11 @@ 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(()) } + #[cfg_attr(not(feature = "no-cfi"), cfi_impl_fn)] fn derive_cdi( &mut self, algs: AlgLen, @@ -102,6 +144,32 @@ impl Crypto for RustCryptoImpl { hkdf_derive_cdi(algs, measurement, info) } + #[cfg_attr(not(feature = "no-cfi"), cfi_impl_fn)] + fn derive_exported_cdi( + &mut self, + algs: AlgLen, + measurement: &Digest, + info: &[u8], + ) -> Result { + let cdi = hkdf_derive_cdi(algs, measurement, info)?; + + 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)] fn derive_key_pair( &mut self, algs: AlgLen, @@ -109,25 +177,27 @@ impl Crypto for RustCryptoImpl { label: &[u8], info: &[u8], ) -> Result<(Self::PrivKey, EcdsaPub), CryptoError> { - let secret = hkdf_get_priv_key(algs, cdi, label, info)?; - match algs { - AlgLen::Bit256 => { - let signing = p256::ecdsa::SigningKey::from_slice(&secret.bytes())?; - let verifying = p256::ecdsa::VerifyingKey::from(&signing); - let point = verifying.to_encoded_point(false); - let x = CryptoBuf::new(point.x().ok_or(RUSTCRYPTO_ECDSA_ERROR)?.as_slice())?; - let y = CryptoBuf::new(point.y().ok_or(RUSTCRYPTO_ECDSA_ERROR)?.as_slice())?; - Ok((secret, EcdsaPub { x, y })) - } - AlgLen::Bit384 => { - let signing = p384::ecdsa::SigningKey::from_slice(&secret.bytes())?; - let verifying = p384::ecdsa::VerifyingKey::from(&signing); - let point = verifying.to_encoded_point(false); - let x = CryptoBuf::new(point.x().ok_or(RUSTCRYPTO_ECDSA_ERROR)?.as_slice())?; - let y = CryptoBuf::new(point.y().ok_or(RUSTCRYPTO_ECDSA_ERROR)?.as_slice())?; - Ok((secret, EcdsaPub { x, y })) + self.derive_key_pair_inner(algs, cdi, label, info) + } + + #[cfg_attr(not(feature = "no-cfi"), cfi_impl_fn)] + fn derive_key_pair_exported( + &mut self, + algs: AlgLen, + exported_handle: &ExportedCdiHandle, + label: &[u8], + info: &[u8], + ) -> Result<(Self::PrivKey, EcdsaPub), CryptoError> { + 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( @@ -177,27 +247,4 @@ impl Crypto for RustCryptoImpl { } } } - - fn hmac_sign_with_derived( - &mut self, - algs: AlgLen, - cdi: &Self::Cdi, - label: &[u8], - info: &[u8], - digest: &Digest, - ) -> Result { - let (symmetric_key, _) = self.derive_key_pair(algs, cdi, label, info)?; - match algs { - AlgLen::Bit256 => { - let mut hmac = Hmac::::new_from_slice(symmetric_key.bytes()).unwrap(); - Mac::update(&mut hmac, digest.bytes()); - HmacSig::new(hmac.finalize().into_bytes().as_slice()) - } - AlgLen::Bit384 => { - let mut hmac = Hmac::::new_from_slice(symmetric_key.bytes()).unwrap(); - Mac::update(&mut hmac, digest.bytes()); - HmacSig::new(hmac.finalize().into_bytes().as_slice()) - } - } - } } diff --git a/crypto/src/signer.rs b/crypto/src/signer.rs index 139ad093..c09191c8 100644 --- a/crypto/src/signer.rs +++ b/crypto/src/signer.rs @@ -26,9 +26,6 @@ impl EcdsaPub { } } -/// An HMAC Signature -pub type HmacSig = CryptoBuf; - /// A common base struct that can be used for all digests, signatures, and keys. #[derive(Debug, PartialEq, Eq, ZeroizeOnDrop)] pub struct CryptoBuf(ArrayVec); diff --git a/dpe/Cargo.toml b/dpe/Cargo.toml index 4fad35d0..560cb307 100644 --- a/dpe/Cargo.toml +++ b/dpe/Cargo.toml @@ -17,11 +17,10 @@ disable_auto_init = [] disable_rotate_context = [] disable_x509 = [] disable_csr = [] -disable_is_symmetric = [] disable_internal_info = [] disable_internal_dice = [] -disable_is_ca = [] disable_retain_parent_context = [] +disable_export_cdi = [] no-cfi = ["crypto/no-cfi"] [dependencies] 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 bfd42e07..16042e78 100644 --- a/dpe/src/commands/certify_key.rs +++ b/dpe/src/commands/certify_key.rs @@ -4,22 +4,16 @@ use crate::{ context::ContextHandle, dpe_instance::{DpeEnv, DpeInstance, DpeTypes}, response::{CertifyKeyResp, DpeErrorCode, Response, ResponseHdr}, - tci::TciNodeData, - x509::{CertWriter, DirectoryString, MeasurementData, Name}, - DPE_PROFILE, MAX_CERT_SIZE, MAX_HANDLES, + x509::{create_dpe_cert, create_dpe_csr, CreateDpeCertArgs, CreateDpeCertResult}, + DPE_PROFILE, MAX_CERT_SIZE, }; use bitflags::bitflags; #[cfg(not(feature = "no-cfi"))] use caliptra_cfi_derive_git::cfi_impl_fn; -use caliptra_cfi_lib_git::cfi_launder; #[cfg(not(feature = "no-cfi"))] use caliptra_cfi_lib_git::{cfi_assert, cfi_assert_eq}; use cfg_if::cfg_if; -use crypto::{Crypto, Hasher}; #[cfg(not(feature = "disable_x509"))] -use platform::MAX_ISSUER_NAME_SIZE; -use platform::{Platform, PlatformError, MAX_KEY_IDENTIFIER_SIZE}; - #[repr(C)] #[derive( Debug, @@ -33,9 +27,7 @@ use platform::{Platform, PlatformError, MAX_KEY_IDENTIFIER_SIZE}; pub struct CertifyKeyFlags(u32); bitflags! { - impl CertifyKeyFlags: u32 { - const IS_CA = 1u32 << 30; - } + impl CertifyKeyFlags: u32 {} } #[repr(C)] @@ -58,10 +50,6 @@ pub struct CertifyKeyCmd { impl CertifyKeyCmd { pub const FORMAT_X509: u32 = 0; pub const FORMAT_CSR: u32 = 1; - - const fn uses_is_ca(&self) -> bool { - self.flags.contains(CertifyKeyFlags::IS_CA) - } } impl CommandExecution for CertifyKeyCmd { @@ -75,13 +63,6 @@ impl CommandExecution for CertifyKeyCmd { let idx = dpe.get_active_context_pos(&self.handle, locality)?; let context = &dpe.contexts[idx]; - if self.uses_is_ca() && !dpe.support.is_ca() { - return Err(DpeErrorCode::ArgumentNotSupported); - } - if self.uses_is_ca() && !context.allow_ca() { - return Err(DpeErrorCode::InvalidArgument); - } - if self.format == Self::FORMAT_X509 { if !dpe.support.x509() { return Err(DpeErrorCode::ArgumentNotSupported); @@ -100,8 +81,6 @@ impl CommandExecution for CertifyKeyCmd { cfg_if! { if #[cfg(not(feature = "no-cfi"))] { - cfi_assert!(!self.uses_is_ca() || dpe.support.is_ca()); - cfi_assert!(!self.uses_is_ca() || context.allow_ca()); cfi_assert!(self.format != Self::FORMAT_X509 || dpe.support.x509()); cfi_assert!(self.format != Self::FORMAT_X509 || context.allow_x509()); cfi_assert!(self.format != Self::FORMAT_CSR || dpe.support.csr()); @@ -109,115 +88,27 @@ impl CommandExecution for CertifyKeyCmd { } } - let algs = DPE_PROFILE.alg_len(); - let digest = dpe.compute_measurement_hash(env, idx)?; - let cdi = env - .crypto - .derive_cdi(DPE_PROFILE.alg_len(), &digest, b"DPE")?; - let key_pair = env.crypto.derive_key_pair(algs, &cdi, &self.label, b"ECC"); - if cfi_launder(key_pair.is_ok()) { - #[cfg(not(feature = "no-cfi"))] - cfi_assert!(key_pair.is_ok()); - } else { - #[cfg(not(feature = "no-cfi"))] - cfi_assert!(key_pair.is_err()); - } - #[allow(unused_variables)] - let (priv_key, pub_key) = key_pair?; - - let mut subj_serial = [0u8; DPE_PROFILE.get_hash_size() * 2]; - env.crypto - .get_pubkey_serial(DPE_PROFILE.alg_len(), &pub_key, &mut subj_serial)?; - // The serial number of the subject can be at most 64 bytes - let truncated_subj_serial = &subj_serial[..64]; - - let subject_name = Name { - cn: DirectoryString::PrintableString(b"DPE Leaf"), - serial: DirectoryString::PrintableString(truncated_subj_serial), - }; - - // Get TCI Nodes - const INITIALIZER: TciNodeData = TciNodeData::new(); - let mut nodes = [INITIALIZER; MAX_HANDLES]; - let tcb_count = dpe.get_tcb_nodes(idx, &mut nodes)?; - if tcb_count > MAX_HANDLES { - return Err(DpeErrorCode::InternalError); - } - - let mut subject_key_identifier = [0u8; MAX_KEY_IDENTIFIER_SIZE]; - // compute key identifier as SHA hash of the DER encoded subject public key - let mut hasher = env.crypto.hash_initialize(DPE_PROFILE.alg_len())?; - hasher.update(&[0x04])?; - hasher.update(pub_key.x.bytes())?; - hasher.update(pub_key.y.bytes())?; - let hashed_pub_key = hasher.finish()?; - if hashed_pub_key.len() < MAX_KEY_IDENTIFIER_SIZE { - return Err(DpeErrorCode::InternalError); - } - // truncate key identifier to 20 bytes - subject_key_identifier.copy_from_slice(&hashed_pub_key.bytes()[..MAX_KEY_IDENTIFIER_SIZE]); - - let mut authority_key_identifier = [0u8; MAX_KEY_IDENTIFIER_SIZE]; - env.platform - .get_issuer_key_identifier(&mut authority_key_identifier)?; - - let subject_alt_name = match env.platform.get_subject_alternative_name() { - Ok(subject_alt_name) => Some(subject_alt_name), - Err(PlatformError::NotImplemented) => None, - Err(e) => Err(DpeErrorCode::Platform(e))?, - }; - - let measurements = MeasurementData { - label: &self.label, - tci_nodes: &nodes[..tcb_count], - is_ca: self.uses_is_ca(), - supports_recursive: dpe.support.recursive(), - subject_key_identifier, - authority_key_identifier, - subject_alt_name, + let args = CreateDpeCertArgs { + handle: &self.handle, + locality, + cdi_label: b"DPE", + key_label: &self.label, + context: b"ECC", + ueid: &self.label, }; + let mut cert = [0; MAX_CERT_SIZE]; - let mut cert = [0u8; MAX_CERT_SIZE]; - let cert_size = match self.format { + let CreateDpeCertResult { + cert_size, pub_key, .. + } = match self.format { Self::FORMAT_X509 => { cfg_if! { if #[cfg(not(feature = "disable_x509"))] { - let mut issuer_name = [0u8; MAX_ISSUER_NAME_SIZE]; - let issuer_len = env.platform.get_issuer_name(&mut issuer_name)?; #[cfg(not(feature = "no-cfi"))] cfi_assert_eq(self.format, Self::FORMAT_X509); - let mut tbs_buffer = [0u8; MAX_CERT_SIZE]; - let mut tbs_writer = CertWriter::new(&mut tbs_buffer, true); - if issuer_len > MAX_ISSUER_NAME_SIZE { - return Err(DpeErrorCode::InternalError); - } - let cert_validity = env.platform.get_cert_validity()?; - let mut bytes_written = tbs_writer.encode_ecdsa_tbs( - /*serial=*/ - &subject_name.serial.bytes()[..20], // Serial number must be truncated to 20 bytes - &issuer_name[..issuer_len], - &subject_name, - &pub_key, - &measurements, - &cert_validity, - )?; - if bytes_written > MAX_CERT_SIZE { - return Err(DpeErrorCode::InternalError); - } - - let tbs_digest = env - .crypto - .hash(DPE_PROFILE.alg_len(), &tbs_buffer[..bytes_written])?; - let sig = env - .crypto - .ecdsa_sign_with_alias(DPE_PROFILE.alg_len(), &tbs_digest)?; - - let mut cert_writer = CertWriter::new(&mut cert, true); - bytes_written = - cert_writer.encode_ecdsa_certificate(&tbs_buffer[..bytes_written], &sig)?; - u32::try_from(bytes_written).map_err(|_| DpeErrorCode::InternalError)? + create_dpe_cert(&args, dpe, env, &mut cert) } else { - Err(DpeErrorCode::ArgumentNotSupported)? + Err(DpeErrorCode::ArgumentNotSupported) } } } @@ -226,57 +117,14 @@ impl CommandExecution for CertifyKeyCmd { if #[cfg(not(feature = "disable_csr"))] { #[cfg(not(feature = "no-cfi"))] cfi_assert_eq(self.format, Self::FORMAT_CSR); - let mut cert_req_info_buffer = [0u8; MAX_CERT_SIZE]; - let mut cert_req_info_writer = CertWriter::new(&mut cert_req_info_buffer, true); - let mut bytes_written = cert_req_info_writer - .encode_certification_request_info( - &pub_key, - &subject_name, - &measurements, - )?; - if bytes_written > MAX_CERT_SIZE { - return Err(DpeErrorCode::InternalError); - } - - let cert_req_info_digest = env.crypto.hash( - DPE_PROFILE.alg_len(), - &cert_req_info_buffer[..bytes_written], - )?; - // The PKCS#10 CSR is self-signed so the private key signs it instead of the alias key. - let cert_req_info_sig = env.crypto.ecdsa_sign_with_derived( - DPE_PROFILE.alg_len(), - &cert_req_info_digest, - &priv_key, - &pub_key, - )?; - - let mut csr_buffer = [0u8; MAX_CERT_SIZE]; - let mut csr_writer = CertWriter::new(&mut csr_buffer, true); - bytes_written = csr_writer - .encode_csr(&cert_req_info_buffer[..bytes_written], &cert_req_info_sig)?; - if bytes_written > MAX_CERT_SIZE { - return Err(DpeErrorCode::InternalError); - } - - let csr_digest = env - .crypto - .hash(DPE_PROFILE.alg_len(), &csr_buffer[..bytes_written])?; - let csr_sig = env - .crypto - .ecdsa_sign_with_alias(DPE_PROFILE.alg_len(), &csr_digest)?; - let sid = env.platform.get_signer_identifier()?; - - let mut cms_writer = CertWriter::new(&mut cert, true); - bytes_written = - cms_writer.encode_cms(&csr_buffer[..bytes_written], &csr_sig, &sid)?; - u32::try_from(bytes_written).map_err(|_| DpeErrorCode::InternalError)? + create_dpe_csr(&args, dpe, env, &mut cert) } else { - Err(DpeErrorCode::ArgumentNotSupported)? + Err(DpeErrorCode::ArgumentNotSupported) } } } _ => return Err(DpeErrorCode::InvalidArgument), - }; + }?; let derived_pubkey_x: [u8; DPE_PROFILE.get_ecc_int_size()] = pub_key @@ -312,24 +160,22 @@ mod tests { commands::{Command, CommandHdr, DeriveContextCmd, DeriveContextFlags, InitCtxCmd}, dpe_instance::tests::{TestTypes, SIMULATION_HANDLE, TEST_LOCALITIES}, support::Support, - x509::tests::TcbInfo, - DpeProfile, + x509::{tests::TcbInfo, DirectoryString, Name}, }; use caliptra_cfi_lib_git::CfiCounter; use cms::{ content_info::{CmsVersion, ContentInfo}, signed_data::{SignedData, SignerIdentifier}, }; - use crypto::{AlgLen, CryptoBuf, EcdsaPub, OpensslCrypto}; + use crypto::{AlgLen, Crypto, CryptoBuf, EcdsaPub, OpensslCrypto}; use der::{Decode, Encode}; use openssl::{ bn::BigNum, ec::{EcGroup, EcKey}, ecdsa::EcdsaSig, - hash::{Hasher, MessageDigest}, nid::*, }; - use platform::default::DefaultPlatform; + use platform::{default::DefaultPlatform, Platform}; use spki::ObjectIdentifier; use std::str; use x509_parser::nom::Parser; @@ -401,120 +247,6 @@ mod tests { }; } - #[test] - fn test_is_ca() { - CfiCounter::reset_for_test(); - let mut env = DpeEnv:: { - crypto: OpensslCrypto::new(), - platform: DefaultPlatform, - }; - let mut dpe = DpeInstance::new(&mut env, Support::X509 | Support::IS_CA).unwrap(); - - let init_resp = match InitCtxCmd::new_use_default() - .execute(&mut dpe, &mut env, TEST_LOCALITIES[0]) - .unwrap() - { - Response::InitCtx(resp) => resp, - _ => panic!("Incorrect return type."), - }; - let certify_cmd_ca = CertifyKeyCmd { - handle: init_resp.handle, - flags: CertifyKeyFlags::IS_CA, - label: [0; DPE_PROFILE.get_hash_size()], - format: CertifyKeyCmd::FORMAT_X509, - }; - - let certify_resp_ca = match certify_cmd_ca - .execute(&mut dpe, &mut env, TEST_LOCALITIES[0]) - .unwrap() - { - Response::CertifyKey(resp) => resp, - _ => panic!("Wrong response type."), - }; - - let mut parser = X509CertificateParser::new().with_deep_parse_extensions(true); - match parser.parse(&certify_resp_ca.cert[..certify_resp_ca.cert_size.try_into().unwrap()]) { - Ok((_, cert)) => { - match cert.basic_constraints() { - Ok(Some(basic_constraints)) => { - assert!(basic_constraints.value.ca); - } - Ok(None) => panic!("basic constraints extension not found"), - Err(_) => panic!("multiple basic constraints extensions found"), - } - - let pub_key = &cert.tbs_certificate.subject_pki.subject_public_key.data; - let mut hasher = match DPE_PROFILE { - DpeProfile::P256Sha256 => Hasher::new(MessageDigest::sha256()).unwrap(), - DpeProfile::P384Sha384 => Hasher::new(MessageDigest::sha384()).unwrap(), - }; - hasher.update(pub_key).unwrap(); - let expected_ski: &[u8] = &hasher.finish().unwrap(); - match cert.get_extension_unique(&oid!(2.5.29 .14)) { - Ok(Some(subject_key_identifier_ext)) => { - if let ParsedExtension::SubjectKeyIdentifier(key_identifier) = - subject_key_identifier_ext.parsed_extension() - { - assert_eq!(key_identifier.0, &expected_ski[..MAX_KEY_IDENTIFIER_SIZE]); - } else { - panic!("Extension has wrong type"); - } - } - Ok(None) => panic!("subject key identifier extension not found"), - Err(_) => panic!("multiple subject key identifier extensions found"), - } - - let mut expected_aki = [0u8; MAX_KEY_IDENTIFIER_SIZE]; - env.platform - .get_issuer_key_identifier(&mut expected_aki) - .unwrap(); - match cert.get_extension_unique(&oid!(2.5.29 .35)) { - Ok(Some(extension)) => { - if let ParsedExtension::AuthorityKeyIdentifier(aki) = - extension.parsed_extension() - { - let key_identifier = aki.key_identifier.clone().unwrap(); - assert_eq!(&key_identifier.0, &expected_aki,); - } else { - panic!("Extension has wrong type"); - } - } - Ok(None) => panic!("authority key identifier extension not found"), - Err(_) => panic!("multiple authority key identifier extensions found"), - } - } - Err(e) => panic!("x509 parsing failed: {:?}", e), - }; - - let certify_cmd_non_ca = CertifyKeyCmd { - handle: init_resp.handle, - flags: CertifyKeyFlags::empty(), - label: [0; DPE_PROFILE.get_hash_size()], - format: CertifyKeyCmd::FORMAT_X509, - }; - - let certify_resp_non_ca = match certify_cmd_non_ca - .execute(&mut dpe, &mut env, TEST_LOCALITIES[0]) - .unwrap() - { - Response::CertifyKey(resp) => resp, - _ => panic!("Wrong response type."), - }; - - match parser - .parse(&certify_resp_non_ca.cert[..certify_resp_non_ca.cert_size.try_into().unwrap()]) - { - Ok((_, cert)) => match cert.basic_constraints() { - Ok(Some(basic_constraints)) => { - assert!(!basic_constraints.value.ca); - } - Ok(None) => panic!("basic constraints extension not found"), - Err(_) => panic!("multiple basic constraints extensions found"), - }, - Err(e) => panic!("x509 parsing failed: {:?}", e), - }; - } - #[test] fn test_certify_key_csr() { CfiCounter::reset_for_test(); @@ -710,7 +442,6 @@ mod tests { for extension in &extension_req.extensions { match extension.parsed_extension() { ParsedExtension::BasicConstraints(basic_constraints) => { - // IS_CA not set so this will be false assert!(!basic_constraints.ca); } ParsedExtension::KeyUsage(key_usage) => { diff --git a/dpe/src/commands/derive_context.rs b/dpe/src/commands/derive_context.rs index aff83762..585747f6 100644 --- a/dpe/src/commands/derive_context.rs +++ b/dpe/src/commands/derive_context.rs @@ -1,11 +1,14 @@ // Licensed under the Apache-2.0 license. use super::CommandExecution; use crate::{ - context::{ActiveContextArgs, Context, ContextHandle, ContextState}, + context::{ActiveContextArgs, Context, ContextHandle, ContextState, ContextType}, dpe_instance::{DpeEnv, DpeInstance, DpeTypes}, - response::{DeriveContextResp, DpeErrorCode, Response, ResponseHdr}, + response::{ + DeriveContextExportedCdiResp, DeriveContextResp, DpeErrorCode, Response, ResponseHdr, + }, tci::TciMeasurement, - DPE_PROFILE, + x509::{create_exported_dpe_cert, CreateDpeCertArgs, CreateDpeCertResult}, + DPE_PROFILE, MAX_CERT_SIZE, }; use bitflags::bitflags; #[cfg(not(feature = "no-cfi"))] @@ -14,6 +17,8 @@ use caliptra_cfi_derive_git::cfi_impl_fn; use caliptra_cfi_lib_git::{cfi_assert, cfi_assert_eq}; use cfg_if::cfg_if; +use platform::Platform; + #[repr(C)] #[derive( Debug, @@ -36,6 +41,8 @@ bitflags! { const INPUT_ALLOW_CA = 1u32 << 26; const INPUT_ALLOW_X509 = 1u32 << 25; const RECURSIVE = 1u32 << 24; + const EXPORT_CDI = 1u32 << 23; + const CREATE_CERTIFICATE = 1u32 << 22; } } @@ -91,6 +98,14 @@ impl DeriveContextCmd { self.flags.contains(DeriveContextFlags::RECURSIVE) } + pub const fn exports_cdi(&self) -> bool { + self.flags.contains(DeriveContextFlags::EXPORT_CDI) + } + + pub const fn creates_certificate(&self) -> bool { + self.flags.contains(DeriveContextFlags::CREATE_CERTIFICATE) + } + /// Whether it is okay to make a default context. /// /// When a default context is in a locality, it MUST be the only context in the locality. This @@ -195,8 +210,8 @@ impl CommandExecution for DeriveContextCmd { if (!dpe.support.internal_info() && self.uses_internal_info_input()) || (!dpe.support.internal_dice() && self.uses_internal_dice_input()) || (!dpe.support.retain_parent_context() && self.retains_parent()) - || (!dpe.support.is_ca() && self.allows_ca()) || (!dpe.support.x509() && self.allows_x509()) + || (!dpe.support.cdi_export() && (self.creates_certificate() || self.exports_cdi())) || (!dpe.support.recursive() && self.is_recursive()) { return Err(DpeErrorCode::ArgumentNotSupported); @@ -205,6 +220,12 @@ impl CommandExecution for DeriveContextCmd { let parent_idx = dpe.get_active_context_pos(&self.handle, locality)?; if (!dpe.contexts[parent_idx].allow_ca() && self.allows_ca()) || (!dpe.contexts[parent_idx].allow_x509() && self.allows_x509()) + || (self.exports_cdi() && !self.creates_certificate()) + || (self.exports_cdi() && self.is_recursive()) + || (self.exports_cdi() && self.changes_locality()) + || (self.exports_cdi() && self.retains_parent()) + || (self.exports_cdi() + && dpe.contexts[parent_idx].context_type == ContextType::Simulation) || (self.is_recursive() && self.retains_parent()) { return Err(DpeErrorCode::InvalidArgument); @@ -225,7 +246,6 @@ impl CommandExecution for DeriveContextCmd { cfi_assert!(dpe.support.internal_info() || !self.uses_internal_info_input()); cfi_assert!(dpe.support.internal_dice() || !self.uses_internal_dice_input()); cfi_assert!(dpe.support.retain_parent_context() || !self.retains_parent()); - cfi_assert!(dpe.support.is_ca() || !self.allows_ca()); cfi_assert!(dpe.support.x509() || !self.allows_x509()); cfi_assert!(dpe.contexts[parent_idx].allow_ca() || !self.allows_ca()); cfi_assert!(dpe.contexts[parent_idx].allow_x509() || !self.allows_x509()); @@ -233,6 +253,15 @@ impl CommandExecution for DeriveContextCmd { } } + // Copy the parent context to mutate so that we avoid mutating internal state upon an error. + let mut tmp_parent_context = dpe.contexts[parent_idx]; + if !self.retains_parent() { + #[cfg(not(feature = "no-cfi"))] + cfi_assert!(!self.retains_parent()); + tmp_parent_context.state = ContextState::Retired; + tmp_parent_context.handle = ContextHandle::new_invalid(); + } + if self.is_recursive() { cfg_if! { if #[cfg(not(feature = "disable_recursive"))] { @@ -269,6 +298,39 @@ impl CommandExecution for DeriveContextCmd { Err(DpeErrorCode::ArgumentNotSupported)? } } + } else if self.creates_certificate() && self.exports_cdi() { + cfg_if! { + if #[cfg(not(feature = "disable_export_cdi"))] { + let ueid = &env.platform.get_ueid()?; + let ueid = ueid.get()?; + let args = CreateDpeCertArgs { + handle: &self.handle, + locality, + cdi_label: b"Exported CDI", + key_label: b"Exported ECC", + context: b"Exported ECC", + ueid + }; + let mut cert = [0; MAX_CERT_SIZE]; + let CreateDpeCertResult { cert_size, exported_cdi_handle, .. } = create_exported_dpe_cert( + &args, + dpe, + env, + &mut cert, + )?; + + Ok(Response::DeriveContextExportedCdi(DeriveContextExportedCdiResp { + handle: ContextHandle::new_invalid(), + parent_handle: ContextHandle::new_invalid(), + resp_hdr: ResponseHdr::new(DpeErrorCode::NoError), + exported_cdi: exported_cdi_handle, + certificate_size: cert_size, + new_certificate: cert, + })) + } else { + Err(DpeErrorCode::ArgumentNotSupported)? + } + } } else { let child_idx = dpe .get_next_inactive_context_pos() @@ -318,20 +380,15 @@ impl CommandExecution for DeriveContextCmd { target_locality, )?; - // Copy the parent context to mutate so that we avoid mutating internal state upon an error. - let mut tmp_parent_context = dpe.contexts[parent_idx]; - if !self.retains_parent() { - #[cfg(not(feature = "no-cfi"))] - cfi_assert!(!self.retains_parent()); - tmp_parent_context.state = ContextState::Retired; - tmp_parent_context.handle = ContextHandle([0xff; ContextHandle::SIZE]); - } else if !tmp_parent_context.handle.is_default() { - tmp_parent_context.handle = dpe.generate_new_handle(env)?; - } else { - cfg_if! { - if #[cfg(not(feature = "no-cfi"))] { - cfi_assert!(self.retains_parent()); - cfi_assert!(tmp_parent_context.handle.is_default()); + if self.retains_parent() { + if !tmp_parent_context.handle.is_default() { + tmp_parent_context.handle = dpe.generate_new_handle(env)?; + } else { + cfg_if! { + if #[cfg(not(feature = "no-cfi"))] { + cfi_assert!(self.retains_parent()); + cfi_assert!(tmp_parent_context.handle.is_default()); + } } } } @@ -365,13 +422,18 @@ mod tests { context::ContextType, dpe_instance::tests::{TestTypes, RANDOM_HANDLE, SIMULATION_HANDLE, TEST_LOCALITIES}, support::Support, - MAX_HANDLES, + DpeProfile, MAX_EXPORTED_CDI_SIZE, MAX_HANDLES, }; use caliptra_cfi_lib_git::CfiCounter; use crypto::{Crypto, Hasher, OpensslCrypto}; - use openssl::x509::X509; - use openssl::{bn::BigNum, ecdsa::EcdsaSig}; - use platform::default::DefaultPlatform; + use openssl::{ + bn::BigNum, + ecdsa::EcdsaSig, + hash::{Hasher as OpenSSLHasher, MessageDigest}, + x509::X509, + }; + use platform::{default::DefaultPlatform, Platform, MAX_KEY_IDENTIFIER_SIZE}; + use x509_parser::{nom::Parser, oid_registry::asn1_rs::oid, prelude::*}; use zerocopy::IntoBytes; const TEST_DERIVE_CONTEXT_CMD: DeriveContextCmd = DeriveContextCmd { @@ -680,7 +742,7 @@ mod tests { Ok(Response::Sign(resp)) => ( resp.new_context_handle, EcdsaSig::from_private_components( - BigNum::from_slice(&resp.sig_r_or_hmac).unwrap(), + BigNum::from_slice(&resp.sig_r).unwrap(), BigNum::from_slice(&resp.sig_s).unwrap(), ) .unwrap(), @@ -786,6 +848,7 @@ mod tests { handle, parent_handle, resp_hdr, + .. }) = DeriveContextCmd { handle: dpe.contexts[old_default_idx].handle, data: [0; DPE_PROFILE.get_tci_size()], @@ -998,4 +1061,252 @@ mod tests { let digest = hasher_2.finish().unwrap(); assert_eq!(digest.bytes(), dpe.contexts[child_idx].tci.tci_cumulative.0); } + + #[test] + fn test_cdi_export_flags() { + CfiCounter::reset_for_test(); + let mut env = DpeEnv:: { + crypto: OpensslCrypto::new(), + platform: DefaultPlatform, + }; + let mut dpe = DpeInstance::new( + &mut env, + Support::AUTO_INIT + | Support::CDI_EXPORT + | Support::X509 + | Support::RECURSIVE + | Support::SIMULATION + | Support::RETAIN_PARENT_CONTEXT, + ) + .unwrap(); + + // When `DeriveContextFlags::EXPORT_CDI` is set, `DeriveContextFlags::CREATE_CERTIFICATE` MUST + // also be set, or `DpeErrorCode::InvalidArgument` is raised. + assert_eq!( + Err(DpeErrorCode::InvalidArgument), + DeriveContextCmd { + handle: ContextHandle::default(), + data: [0; DPE_PROFILE.get_tci_size()], + flags: DeriveContextFlags::EXPORT_CDI | DeriveContextFlags::CHANGE_LOCALITY, + tci_type: 0, + target_locality: TEST_LOCALITIES[1] + } + .execute(&mut dpe, &mut env, TEST_LOCALITIES[0]) + ); + assert_eq!( + Err(DpeErrorCode::InvalidArgument), + DeriveContextCmd { + handle: ContextHandle::default(), + data: [0; DPE_PROFILE.get_tci_size()], + flags: DeriveContextFlags::EXPORT_CDI | DeriveContextFlags::RECURSIVE, + tci_type: 0, + target_locality: TEST_LOCALITIES[0] + } + .execute(&mut dpe, &mut env, TEST_LOCALITIES[0]) + ); + + // `DeriveContextFlags::EXPORT_CDI` cannot be set with `DeriveContextFlags::RETAIN_PARENT_CONTEXT` + assert_eq!( + Err(DpeErrorCode::InvalidArgument), + DeriveContextCmd { + handle: ContextHandle::default(), + data: [0; DPE_PROFILE.get_tci_size()], + flags: DeriveContextFlags::EXPORT_CDI | DeriveContextFlags::RETAIN_PARENT_CONTEXT, + tci_type: 0, + target_locality: TEST_LOCALITIES[0] + } + .execute(&mut dpe, &mut env, TEST_LOCALITIES[0]) + ); + + let simulation_handle = match InitCtxCmd::new_simulation() + .execute(&mut dpe, &mut env, TEST_LOCALITIES[0]) + .unwrap() + { + Response::InitCtx(resp) => resp.handle, + _ => panic!("Wrong response type."), + }; + // DPE must return an `DpeErrorCode::InvalidArgument` error if the context-handle refers to a simulation context. + assert_eq!( + Err(DpeErrorCode::InvalidArgument), + DeriveContextCmd { + handle: simulation_handle, + data: [0; DPE_PROFILE.get_tci_size()], + flags: DeriveContextFlags::CREATE_CERTIFICATE | DeriveContextFlags::EXPORT_CDI, + tci_type: 0, + target_locality: TEST_LOCALITIES[0] + } + .execute(&mut dpe, &mut env, TEST_LOCALITIES[0]) + ); + + // DPE must return an `DpeErrorCode::InvalidArgument` if `DeriveContextFlags::EXPORT_CDI` and `DeriveContextFlags::RECURSIVE` are set. + assert_eq!( + Err(DpeErrorCode::InvalidArgument), + DeriveContextCmd { + handle: ContextHandle::default(), + data: [0; DPE_PROFILE.get_tci_size()], + flags: DeriveContextFlags::CREATE_CERTIFICATE + | DeriveContextFlags::EXPORT_CDI + | DeriveContextFlags::RECURSIVE, + tci_type: 0, + target_locality: TEST_LOCALITIES[0] + } + .execute(&mut dpe, &mut env, TEST_LOCALITIES[0]) + ); + + dpe = DpeInstance::new( + &mut env, + Support::AUTO_INIT | Support::CDI_EXPORT | Support::X509, + ) + .unwrap(); + + // Happy case! + let res = DeriveContextCmd { + handle: ContextHandle::default(), + data: [0; DPE_PROFILE.get_tci_size()], + flags: DeriveContextFlags::EXPORT_CDI | DeriveContextFlags::CREATE_CERTIFICATE, + tci_type: 0, + target_locality: TEST_LOCALITIES[0], + } + .execute(&mut dpe, &mut env, TEST_LOCALITIES[0]); + + let res = match res { + Ok(Response::DeriveContextExportedCdi(res)) => res, + _ => panic!("expected to get a valid DeriveContextExportedCdi response."), + }; + assert_eq!(res.parent_handle, ContextHandle::new_invalid()); + assert_eq!(res.handle, ContextHandle::new_invalid()); + assert_ne!(res.certificate_size, 0); + assert_ne!(res.new_certificate, [0; MAX_CERT_SIZE]); + assert_ne!(res.exported_cdi, [0; MAX_EXPORTED_CDI_SIZE]); + + let mut dpe = DpeInstance::new( + &mut env, + Support::AUTO_INIT | Support::INTERNAL_INFO | Support::INTERNAL_DICE | Support::X509, + ) + .unwrap(); + + // `DpeInstance` needs `Support::EXPORT_CDI` to use `DeriveContextFlags::EXPORT_CDI`. + assert_eq!( + Err(DpeErrorCode::ArgumentNotSupported), + DeriveContextCmd { + handle: ContextHandle::default(), + data: [0; DPE_PROFILE.get_tci_size()], + flags: DeriveContextFlags::CREATE_CERTIFICATE | DeriveContextFlags::EXPORT_CDI, + tci_type: 0, + target_locality: TEST_LOCALITIES[0] + } + .execute(&mut dpe, &mut env, TEST_LOCALITIES[0]) + ); + + // `DpeInstance` needs `Support::EXPORT_CDI` to use `DeriveContextFlags::EXPORT_CDI`. + assert_eq!( + Err(DpeErrorCode::ArgumentNotSupported), + DeriveContextCmd { + handle: ContextHandle::default(), + data: [0; DPE_PROFILE.get_tci_size()], + flags: DeriveContextFlags::EXPORT_CDI, + tci_type: 0, + target_locality: TEST_LOCALITIES[0] + } + .execute(&mut dpe, &mut env, TEST_LOCALITIES[0]) + ); + + // `DpeInstance` needs `Support::EXPORT_CDI` to use `DeriveContextFlags::EXPORT_CDI`. + assert_eq!( + Err(DpeErrorCode::ArgumentNotSupported), + DeriveContextCmd { + handle: ContextHandle::default(), + data: [0; DPE_PROFILE.get_tci_size()], + flags: DeriveContextFlags::CREATE_CERTIFICATE, + tci_type: 0, + target_locality: TEST_LOCALITIES[0] + } + .execute(&mut dpe, &mut env, TEST_LOCALITIES[0]) + ); + } + #[test] + fn test_create_ca() { + CfiCounter::reset_for_test(); + let mut env = DpeEnv:: { + crypto: OpensslCrypto::new(), + platform: DefaultPlatform, + }; + let mut dpe = DpeInstance::new(&mut env, Support::X509 | Support::CDI_EXPORT).unwrap(); + let init_resp = match InitCtxCmd::new_use_default() + .execute(&mut dpe, &mut env, TEST_LOCALITIES[0]) + .unwrap() + { + Response::InitCtx(resp) => resp, + _ => panic!("Incorrect return type."), + }; + let derive_cmd = DeriveContextCmd { + handle: init_resp.handle, + flags: DeriveContextFlags::EXPORT_CDI | DeriveContextFlags::CREATE_CERTIFICATE, + data: [0; DPE_PROFILE.get_tci_size()], + tci_type: 0, + target_locality: TEST_LOCALITIES[0], + }; + let derive_resp = match derive_cmd + .execute(&mut dpe, &mut env, TEST_LOCALITIES[0]) + .unwrap() + { + Response::DeriveContextExportedCdi(resp) => resp, + _ => panic!("Wrong response type."), + }; + assert_eq!(ContextHandle::new_invalid(), derive_resp.handle); + assert_eq!(ContextHandle::new_invalid(), derive_resp.parent_handle); + let mut parser = X509CertificateParser::new().with_deep_parse_extensions(true); + match parser + .parse(&derive_resp.new_certificate[..derive_resp.certificate_size.try_into().unwrap()]) + { + Ok((_, cert)) => { + match cert.basic_constraints() { + Ok(Some(basic_constraints)) => { + assert!(basic_constraints.value.ca); + } + Ok(None) => panic!("basic constraints extension not found"), + Err(_) => panic!("multiple basic constraints extensions found"), + } + let pub_key = &cert.tbs_certificate.subject_pki.subject_public_key.data; + let mut hasher = match DPE_PROFILE { + DpeProfile::P256Sha256 => OpenSSLHasher::new(MessageDigest::sha256()).unwrap(), + DpeProfile::P384Sha384 => OpenSSLHasher::new(MessageDigest::sha384()).unwrap(), + }; + hasher.update(pub_key).unwrap(); + let expected_ski: &[u8] = &hasher.finish().unwrap(); + match cert.get_extension_unique(&oid!(2.5.29 .14)) { + Ok(Some(subject_key_identifier_ext)) => { + if let ParsedExtension::SubjectKeyIdentifier(key_identifier) = + subject_key_identifier_ext.parsed_extension() + { + assert_eq!(key_identifier.0, &expected_ski[..MAX_KEY_IDENTIFIER_SIZE]); + } else { + panic!("Extension has wrong type"); + } + } + Ok(None) => panic!("subject key identifier extension not found"), + Err(_) => panic!("multiple subject key identifier extensions found"), + } + let mut expected_aki = [0u8; MAX_KEY_IDENTIFIER_SIZE]; + env.platform + .get_issuer_key_identifier(&mut expected_aki) + .unwrap(); + match cert.get_extension_unique(&oid!(2.5.29 .35)) { + Ok(Some(extension)) => { + if let ParsedExtension::AuthorityKeyIdentifier(aki) = + extension.parsed_extension() + { + let key_identifier = aki.key_identifier.clone().unwrap(); + assert_eq!(&key_identifier.0, &expected_aki,); + } else { + panic!("Extension has wrong type"); + } + } + Ok(None) => panic!("authority key identifier extension not found"), + Err(_) => panic!("multiple authority key identifier extensions found"), + } + } + Err(e) => panic!("x509 parsing failed: {:?}", e), + }; + } } diff --git a/dpe/src/commands/sign.rs b/dpe/src/commands/sign.rs index 574322e0..cca0e2e4 100644 --- a/dpe/src/commands/sign.rs +++ b/dpe/src/commands/sign.rs @@ -14,8 +14,6 @@ use caliptra_cfi_lib_git::cfi_launder; use caliptra_cfi_lib_git::{cfi_assert, cfi_assert_eq, cfi_assert_ne}; use cfg_if::cfg_if; use crypto::{Crypto, Digest, EcdsaSig}; -#[cfg(not(feature = "disable_is_symmetric"))] -use crypto::{CryptoBuf, HmacSig}; #[repr(C)] #[derive( @@ -30,9 +28,7 @@ use crypto::{CryptoBuf, HmacSig}; pub struct SignFlags(u32); bitflags! { - impl SignFlags: u32 { - const IS_SYMMETRIC = 1u32 << 30; - } + impl SignFlags: u32 {} } #[repr(C)] @@ -53,10 +49,6 @@ pub struct SignCmd { } impl SignCmd { - const fn uses_symmetric(&self) -> bool { - self.flags.contains(SignFlags::IS_SYMMETRIC) - } - /// Signs `digest` using ECDSA /// /// # Arguments @@ -94,40 +86,6 @@ impl SignCmd { Ok(sig) } - - /// Signs `digest` using an HMAC - /// - /// # Arguments - /// - /// * `dpe` - DPE instance - /// * `env` - DPE environment containing Crypto and Platform implementations - /// * `idx` - The index of the context where the measurement hash is computed from - /// * `digest` - The data to be signed - #[cfg_attr(not(feature = "no-cfi"), cfi_impl_fn)] - #[cfg(not(feature = "disable_is_symmetric"))] - fn hmac_sign( - &self, - dpe: &mut DpeInstance, - env: &mut DpeEnv, - idx: usize, - digest: &Digest, - ) -> Result { - let algs = DPE_PROFILE.alg_len(); - let cdi_digest = dpe.compute_measurement_hash(env, idx)?; - let cdi = env - .crypto - .derive_cdi(DPE_PROFILE.alg_len(), &cdi_digest, b"DPE"); - if cfi_launder(cdi.is_ok()) { - #[cfg(not(feature = "no-cfi"))] - cfi_assert!(cdi.is_ok()); - } else { - #[cfg(not(feature = "no-cfi"))] - cfi_assert!(cdi.is_err()); - } - Ok(env - .crypto - .hmac_sign_with_derived(algs, &cdi?, &self.label, b"HMAC", digest)?) - } } impl CommandExecution for SignCmd { @@ -138,11 +96,6 @@ impl CommandExecution for SignCmd { env: &mut DpeEnv, locality: u32, ) -> Result { - // Make sure the operation is supported. - if !dpe.support.is_symmetric() && self.uses_symmetric() { - return Err(DpeErrorCode::ArgumentNotSupported); - } - let idx = dpe.get_active_context_pos(&self.handle, locality)?; let context = &dpe.contexts[idx]; @@ -152,29 +105,14 @@ impl CommandExecution for SignCmd { cfg_if! { if #[cfg(not(feature = "no-cfi"))] { - cfi_assert!(dpe.support.is_symmetric() || !self.uses_symmetric()); cfi_assert_ne(context.context_type, ContextType::Simulation); } } let digest = Digest::new(&self.digest)?; - let EcdsaSig { r, s } = if !self.uses_symmetric() { - self.ecdsa_sign(dpe, env, idx, &digest)? - } else { - cfg_if! { - if #[cfg(not(feature = "disable_is_symmetric"))] { - let algs = DPE_PROFILE.alg_len(); - let r = self.hmac_sign(dpe, env, idx, &digest)?; - let s = CryptoBuf::default(algs); - EcdsaSig { r, s } - } - else { - Err(DpeErrorCode::ArgumentNotSupported)? - } - } - }; + let EcdsaSig { r, s } = self.ecdsa_sign(dpe, env, idx, &digest)?; - let sig_r_or_hmac: [u8; DPE_PROFILE.get_ecc_int_size()] = r + let sig_r: [u8; DPE_PROFILE.get_ecc_int_size()] = r .bytes() .try_into() .map_err(|_| DpeErrorCode::InternalError)?; @@ -189,7 +127,7 @@ impl CommandExecution for SignCmd { Ok(Response::Sign(SignResp { new_context_handle: dpe.contexts[idx].handle, - sig_r_or_hmac, + sig_r, sig_s, resp_hdr: ResponseHdr::new(DpeErrorCode::NoError), })) @@ -208,7 +146,7 @@ mod tests { Command, CommandHdr, DeriveContextCmd, InitCtxCmd, }, dpe_instance::tests::{TestTypes, RANDOM_HANDLE, SIMULATION_HANDLE, TEST_LOCALITIES}, - support::{test::SUPPORT, Support}, + support::test::SUPPORT, }; use caliptra_cfi_lib_git::CfiCounter; use crypto::OpensslCrypto; @@ -235,24 +173,6 @@ mod tests { ); } - #[test] - fn test_uses_symmetric() { - CfiCounter::reset_for_test(); - // No flags set. - assert!(!SignCmd { - flags: SignFlags::empty(), - ..TEST_SIGN_CMD - } - .uses_symmetric()); - - // Just is-symmetric flag set. - assert!(SignCmd { - flags: SignFlags::IS_SYMMETRIC, - ..TEST_SIGN_CMD - } - .uses_symmetric()); - } - #[test] fn test_bad_command_inputs() { CfiCounter::reset_for_test(); @@ -262,18 +182,6 @@ mod tests { }; let mut dpe = DpeInstance::new(&mut env, SUPPORT).unwrap(); - // Bad argument - assert_eq!( - Err(DpeErrorCode::ArgumentNotSupported), - SignCmd { - handle: ContextHandle([0xff; ContextHandle::SIZE]), - label: TEST_LABEL, - flags: SignFlags::IS_SYMMETRIC, - digest: TEST_DIGEST - } - .execute(&mut dpe, &mut env, TEST_LOCALITIES[0]) - ); - // Bad handle. assert_eq!( Err(DpeErrorCode::InvalidHandle), @@ -354,7 +262,7 @@ mod tests { }; EcdsaSig::from_private_components( - BigNum::from_slice(&resp.sig_r_or_hmac).unwrap(), + BigNum::from_slice(&resp.sig_r).unwrap(), BigNum::from_slice(&resp.sig_s).unwrap(), ) .unwrap() @@ -379,39 +287,4 @@ mod tests { assert!(sig.verify(&TEST_DIGEST, &ec_pub_key).unwrap()); } - - #[test] - fn test_symmetric() { - CfiCounter::reset_for_test(); - let mut env = DpeEnv:: { - crypto: OpensslCrypto::new(), - platform: DefaultPlatform, - }; - let mut dpe = - DpeInstance::new(&mut env, Support::AUTO_INIT | Support::IS_SYMMETRIC).unwrap(); - - let cmd = SignCmd { - handle: ContextHandle::default(), - label: TEST_LABEL, - flags: SignFlags::IS_SYMMETRIC, - digest: TEST_DIGEST, - }; - let resp = match cmd.execute(&mut dpe, &mut env, TEST_LOCALITIES[0]).unwrap() { - Response::Sign(resp) => resp, - _ => panic!("Incorrect response type"), - }; - - let idx = dpe - .get_active_context_pos(&ContextHandle::default(), TEST_LOCALITIES[0]) - .unwrap(); - // Check that r is equal to the HMAC over the digest - assert_eq!( - resp.sig_r_or_hmac, - cmd.hmac_sign(&mut dpe, &mut env, idx, &Digest::new(&TEST_DIGEST).unwrap(),) - .unwrap() - .bytes() - ); - // Check that s is a buffer of all 0s - assert!(&resp.sig_s.iter().all(|&b| b == 0x0)); - } } diff --git a/dpe/src/context.rs b/dpe/src/context.rs index 76d3d063..14f59a98 100644 --- a/dpe/src/context.rs +++ b/dpe/src/context.rs @@ -122,12 +122,18 @@ pub struct ContextHandle(pub [u8; ContextHandle::SIZE]); impl ContextHandle { pub const SIZE: usize = 16; const DEFAULT: ContextHandle = ContextHandle([0; Self::SIZE]); + const INVALID: ContextHandle = ContextHandle([0xFF; Self::SIZE]); /// Returns the default context handle. pub const fn default() -> ContextHandle { Self::DEFAULT } + /// Returns an invalid context handle. + pub const fn new_invalid() -> ContextHandle { + Self::INVALID + } + /// Whether the handle is the default context handle. pub fn is_default(&self) -> bool { self.equals(&Self::DEFAULT) diff --git a/dpe/src/lib.rs b/dpe/src/lib.rs index 9f56ea04..c3545bf1 100644 --- a/dpe/src/lib.rs +++ b/dpe/src/lib.rs @@ -25,6 +25,8 @@ pub mod x509; use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout}; +pub use crypto::{ExportedCdiHandle, MAX_EXPORTED_CDI_SIZE}; + // Max cert size returned by CertifyKey const MAX_CERT_SIZE: usize = 6144; #[cfg(not(feature = "arbitrary_max_handles"))] diff --git a/dpe/src/response.rs b/dpe/src/response.rs index 22d042ed..d4bb0fef 100644 --- a/dpe/src/response.rs +++ b/dpe/src/response.rs @@ -6,7 +6,7 @@ Abstract: --*/ use crate::{ context::ContextHandle, validation::ValidationError, CURRENT_PROFILE_MAJOR_VERSION, - CURRENT_PROFILE_MINOR_VERSION, DPE_PROFILE, MAX_CERT_SIZE, MAX_HANDLES, + CURRENT_PROFILE_MINOR_VERSION, DPE_PROFILE, MAX_CERT_SIZE, MAX_EXPORTED_CDI_SIZE, MAX_HANDLES, }; use crypto::CryptoError; use platform::{PlatformError, MAX_CHUNK_SIZE}; @@ -18,6 +18,7 @@ pub enum Response { GetProfile(GetProfileResp), InitCtx(NewHandleResp), DeriveContext(DeriveContextResp), + DeriveContextExportedCdi(DeriveContextExportedCdiResp), RotateCtx(NewHandleResp), CertifyKey(CertifyKeyResp), Sign(SignResp), @@ -32,6 +33,7 @@ impl Response { Response::GetProfile(res) => res.as_bytes(), Response::InitCtx(res) => res.as_bytes(), Response::DeriveContext(res) => res.as_bytes(), + Response::DeriveContextExportedCdi(res) => res.as_bytes(), Response::RotateCtx(res) => res.as_bytes(), Response::CertifyKey(res) => res.as_bytes(), Response::Sign(res) => res.as_bytes(), @@ -141,6 +143,25 @@ pub struct DeriveContextResp { pub parent_handle: ContextHandle, } +#[repr(C)] +#[derive( + Debug, + PartialEq, + Eq, + zerocopy::IntoBytes, + zerocopy::FromBytes, + zerocopy::Immutable, + zerocopy::KnownLayout, +)] +pub struct DeriveContextExportedCdiResp { + pub resp_hdr: ResponseHdr, + pub handle: ContextHandle, + pub parent_handle: ContextHandle, + pub exported_cdi: [u8; MAX_EXPORTED_CDI_SIZE], + pub certificate_size: u32, + pub new_certificate: [u8; MAX_CERT_SIZE], +} + #[repr(C)] #[derive( Debug, @@ -173,7 +194,7 @@ pub struct CertifyKeyResp { pub struct SignResp { pub resp_hdr: ResponseHdr, pub new_context_handle: ContextHandle, - pub sig_r_or_hmac: [u8; DPE_PROFILE.get_ecc_int_size()], + pub sig_r: [u8; DPE_PROFILE.get_ecc_int_size()], pub sig_s: [u8; DPE_PROFILE.get_ecc_int_size()], } diff --git a/dpe/src/support.rs b/dpe/src/support.rs index b06e8697..5a31d31f 100644 --- a/dpe/src/support.rs +++ b/dpe/src/support.rs @@ -15,11 +15,10 @@ bitflags! { const ROTATE_CONTEXT = 1u32 << 27; const X509 = 1u32 << 26; const CSR = 1u32 << 25; - const IS_SYMMETRIC = 1u32 << 24; const INTERNAL_INFO = 1u32 << 22; const INTERNAL_DICE = 1u32 << 21; - const IS_CA = 1u32 << 20; const RETAIN_PARENT_CONTEXT = 1u32 << 19; + const CDI_EXPORT = 1u32 << 18; } } @@ -42,21 +41,18 @@ impl Support { pub fn csr(&self) -> bool { self.contains(Support::CSR) } - pub fn is_symmetric(&self) -> bool { - self.contains(Support::IS_SYMMETRIC) - } pub fn internal_info(&self) -> bool { self.contains(Support::INTERNAL_INFO) } pub fn internal_dice(&self) -> bool { self.contains(Support::INTERNAL_DICE) } - pub fn is_ca(&self) -> bool { - self.contains(Support::IS_CA) - } pub fn retain_parent_context(&self) -> bool { self.contains(Support::RETAIN_PARENT_CONTEXT) } + pub fn cdi_export(&self) -> bool { + self.contains(Support::CDI_EXPORT) + } /// Disables supported features based on compilation features pub fn preprocess_support(&self) -> Support { @@ -86,10 +82,6 @@ impl Support { { support.insert(Support::CSR); } - #[cfg(feature = "disable_is_symmetric")] - { - support.insert(Support::IS_SYMMETRIC); - } #[cfg(feature = "disable_internal_info")] { support.insert(Support::INTERNAL_INFO); @@ -98,14 +90,14 @@ impl Support { { support.insert(Support::INTERNAL_DICE); } - #[cfg(feature = "disable_is_ca")] - { - support.insert(Support::IS_CA); - } #[cfg(feature = "disable_retain_parent_context")] { support.insert(Support::RETAIN_PARENT_CONTEXT); } + #[cfg(feature = "disable_export_cdi")] + { + support.insert(Support::CDI_EXPORT); + } self.difference(support) } } @@ -143,20 +135,16 @@ pub mod test { // Supports certify csr. let flags = Support::CSR.bits(); assert_eq!(flags, 1 << 25); - // Supports is symmetric. - let flags = Support::IS_SYMMETRIC.bits(); - assert_eq!(flags, 1 << 24); // Supports internal info. let flags = Support::INTERNAL_INFO.bits(); assert_eq!(flags, 1 << 22); // Supports internal DICE. let flags = Support::INTERNAL_DICE.bits(); assert_eq!(flags, 1 << 21); - // Supports is ca. - let flags = Support::IS_CA.bits(); - assert_eq!(flags, 1 << 20); let flags = Support::RETAIN_PARENT_CONTEXT.bits(); assert_eq!(flags, 1 << 19); + let flags = Support::CDI_EXPORT.bits(); + assert_eq!(flags, 1 << 18); // Supports a couple combos. let flags = (Support::SIMULATION | Support::AUTO_INIT @@ -168,10 +156,8 @@ pub mod test { flags, (1 << 31) | (1 << 29) | (1 << 27) | (1 << 25) | (1 << 21) ); - let flags = - (Support::RECURSIVE | Support::X509 | Support::IS_SYMMETRIC | Support::INTERNAL_INFO) - .bits(); - assert_eq!(flags, (1 << 30) | (1 << 26) | (1 << 24) | (1 << 22)); + let flags = (Support::RECURSIVE | Support::X509 | Support::INTERNAL_INFO).bits(); + assert_eq!(flags, (1 << 30) | (1 << 26) | (1 << 22)); // Supports everything. let flags = Support::all().bits(); assert_eq!( @@ -182,11 +168,10 @@ pub mod test { | (1 << 27) | (1 << 26) | (1 << 25) - | (1 << 24) | (1 << 22) | (1 << 21) - | (1 << 20) | (1 << 19) + | (1 << 18) ); } } diff --git a/dpe/src/x509.rs b/dpe/src/x509.rs index 60aed790..bf6f056c 100644 --- a/dpe/src/x509.rs +++ b/dpe/src/x509.rs @@ -6,17 +6,25 @@ //! this functionality for a no_std environment. use crate::{ + context::ContextHandle, + dpe_instance::{DpeEnv, DpeTypes}, response::DpeErrorCode, tci::{TciMeasurement, TciNodeData}, - DpeProfile, DPE_PROFILE, + DpeInstance, DpeProfile, DPE_PROFILE, MAX_CERT_SIZE, MAX_HANDLES, }; use bitflags::bitflags; -use crypto::{EcdsaPub, EcdsaSig}; +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, MAX_EXPORTED_CDI_SIZE}; #[cfg(not(feature = "disable_x509"))] use platform::CertValidity; #[cfg(not(feature = "disable_csr"))] use platform::SignerIdentifier; -use platform::{OtherName, SubjectAltName, MAX_KEY_IDENTIFIER_SIZE}; +use platform::{ + OtherName, Platform, PlatformError, SubjectAltName, MAX_ISSUER_NAME_SIZE, + MAX_KEY_IDENTIFIER_SIZE, +}; use zerocopy::IntoBytes; pub enum DirectoryString<'a> { @@ -503,7 +511,7 @@ impl CertWriter<'_> { tagged: bool, is_x509: bool, ) -> Result { - if !measurements.is_ca || !is_x509 { + if !is_x509 { return Ok(0); } let aki_size = Self::get_key_identifier_size( @@ -1589,7 +1597,7 @@ impl CertWriter<'_> { measurements: &MeasurementData, is_x509: bool, ) -> Result { - if !measurements.is_ca || !is_x509 { + if !is_x509 { return Ok(0); } @@ -2227,6 +2235,315 @@ impl CertWriter<'_> { } } +enum CertificateFormat { + X509, + Csr, +} + +enum CertificateType { + Leaf, + Exported, +} + +/// Arguments for DPE cert or CSR creation. +pub(crate) struct CreateDpeCertArgs<'a> { + /// Used by DPE to compute measurement digest + pub handle: &'a ContextHandle, + /// The locality of the caller + pub locality: u32, + /// Info string used in the CDI derivation + pub cdi_label: &'a [u8], + /// Label string used in the key derivation + pub key_label: &'a [u8], + /// Additional info string used in the key derivation + pub context: &'a [u8], + /// Ueid extension value + pub ueid: &'a [u8], +} + +/// Results for DPE cert or CSR creation. +pub(crate) struct CreateDpeCertResult { + /// Size of certificate or CSR in bytes. + 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( + dpe: &mut DpeInstance, + env: &mut DpeEnv, + handle: &ContextHandle, + locality: u32, +) -> Result { + let parent_idx = dpe.get_active_context_pos(handle, locality)?; + let digest = dpe.compute_measurement_hash(env, parent_idx)?; + Ok(digest) +} + +fn get_subject_name<'a>( + env: &mut DpeEnv, + pub_key: &'a EcdsaPub, + subj_serial: &'a mut [u8], +) -> Result, DpeErrorCode> { + env.crypto + .get_pubkey_serial(DPE_PROFILE.alg_len(), pub_key, subj_serial)?; + + // The serial number of the subject can be at most 64 bytes + let truncated_subj_serial = &subj_serial[..64]; + + let subject_name = Name { + cn: DirectoryString::PrintableString(b"DPE Leaf"), + serial: DirectoryString::PrintableString(truncated_subj_serial), + }; + Ok(subject_name) +} + +fn get_tci_nodes<'a>( + dpe: &mut DpeInstance, + handle: &ContextHandle, + locality: u32, + nodes: &'a mut [TciNodeData], +) -> Result<&'a mut [TciNodeData], DpeErrorCode> { + let parent_idx = dpe.get_active_context_pos(handle, locality)?; + let tcb_count = dpe.get_tcb_nodes(parent_idx, nodes)?; + if tcb_count > MAX_HANDLES { + return Err(DpeErrorCode::InternalError); + } + Ok(&mut nodes[..tcb_count]) +} + +fn get_subject_key_identifier( + env: &mut DpeEnv, + pub_key: &EcdsaPub, + subject_key_identifier: &mut [u8], +) -> Result<(), DpeErrorCode> { + // compute key identifier as SHA hash of the DER encoded subject public key + let mut hasher = env.crypto.hash_initialize(DPE_PROFILE.alg_len())?; + hasher.update(&[0x04])?; + hasher.update(pub_key.x.bytes())?; + hasher.update(pub_key.y.bytes())?; + let hashed_pub_key = hasher.finish()?; + if hashed_pub_key.len() < MAX_KEY_IDENTIFIER_SIZE { + return Err(DpeErrorCode::InternalError); + } + // truncate key identifier to 20 bytes + subject_key_identifier.copy_from_slice(&hashed_pub_key.bytes()[..MAX_KEY_IDENTIFIER_SIZE]); + Ok(()) +} + +pub(crate) fn create_exported_dpe_cert( + args: &CreateDpeCertArgs, + dpe: &mut DpeInstance, + env: &mut DpeEnv, + cert: &mut [u8], +) -> Result { + create_dpe_cert_or_csr( + args, + dpe, + env, + CertificateFormat::X509, + CertificateType::Exported, + cert, + ) +} + +pub(crate) fn create_dpe_cert( + args: &CreateDpeCertArgs, + dpe: &mut DpeInstance, + env: &mut DpeEnv, + cert: &mut [u8], +) -> Result { + create_dpe_cert_or_csr( + args, + dpe, + env, + CertificateFormat::X509, + CertificateType::Leaf, + cert, + ) +} + +pub(crate) fn create_dpe_csr( + args: &CreateDpeCertArgs, + dpe: &mut DpeInstance, + env: &mut DpeEnv, + csr: &mut [u8], +) -> Result { + create_dpe_cert_or_csr( + args, + dpe, + env, + CertificateFormat::Csr, + CertificateType::Leaf, + csr, + ) +} + +fn create_dpe_cert_or_csr( + args: &CreateDpeCertArgs, + dpe: &mut DpeInstance, + env: &mut DpeEnv, + cert_format: CertificateFormat, + cert_type: CertificateType, + output_cert_or_csr: &mut [u8], +) -> Result { + 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 exported_handle = env + .crypto + .derive_exported_cdi(algs, &digest, args.cdi_label)?; + 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)?; + env.crypto + .derive_key_pair(algs, &cdi, args.key_label, args.context) + } + }; + if cfi_launder(key_pair.is_ok()) { + #[cfg(not(feature = "no-cfi"))] + cfi_assert!(key_pair.is_ok()); + } else { + #[cfg(not(feature = "no-cfi"))] + cfi_assert!(key_pair.is_err()); + } + let (priv_key, pub_key) = key_pair?; + + let mut subj_serial = [0u8; DPE_PROFILE.get_hash_size() * 2]; + let subject_name = get_subject_name(env, &pub_key, &mut subj_serial)?; + + const INITIALIZER: TciNodeData = TciNodeData::new(); + let mut nodes = [INITIALIZER; MAX_HANDLES]; + let tci_nodes = get_tci_nodes(dpe, args.handle, args.locality, &mut nodes)?; + + let mut subject_key_identifier = [0u8; MAX_KEY_IDENTIFIER_SIZE]; + get_subject_key_identifier(env, &pub_key, &mut subject_key_identifier)?; + + let mut authority_key_identifier = [0u8; MAX_KEY_IDENTIFIER_SIZE]; + env.platform + .get_issuer_key_identifier(&mut authority_key_identifier)?; + + let subject_alt_name = match env.platform.get_subject_alternative_name() { + Ok(subject_alt_name) => Some(subject_alt_name), + Err(PlatformError::NotImplemented) => None, + Err(e) => Err(DpeErrorCode::Platform(e))?, + }; + + let is_ca = match cert_type { + CertificateType::Leaf => false, + CertificateType::Exported => true, + }; + + let supports_recursive = match cert_type { + CertificateType::Leaf => dpe.support.recursive(), + CertificateType::Exported => false, + }; + + let measurements = MeasurementData { + label: args.ueid, + tci_nodes, + is_ca, + supports_recursive, + subject_key_identifier, + authority_key_identifier, + subject_alt_name, + }; + let mut scratch_buf = [0u8; MAX_CERT_SIZE]; + let mut scratch_writer = CertWriter::new(&mut scratch_buf, true); + let cert_size = match cert_format { + CertificateFormat::X509 => { + let mut issuer_name = [0u8; MAX_ISSUER_NAME_SIZE]; + let issuer_len = env.platform.get_issuer_name(&mut issuer_name)?; + if issuer_len > MAX_ISSUER_NAME_SIZE { + return Err(DpeErrorCode::InternalError); + } + let cert_validity = env.platform.get_cert_validity()?; + let mut bytes_written = scratch_writer.encode_ecdsa_tbs( + &subject_name.serial.bytes()[..20], // Serial number must be truncated to 20 bytes + &issuer_name[..issuer_len], + &subject_name, + &pub_key, + &measurements, + &cert_validity, + )?; + if bytes_written > MAX_CERT_SIZE { + return Err(DpeErrorCode::InternalError); + } + let tbs_digest = env.crypto.hash(algs, &scratch_buf[..bytes_written])?; + let sig = env + .crypto + .ecdsa_sign_with_alias(DPE_PROFILE.alg_len(), &tbs_digest)?; + let mut cert_writer = CertWriter::new(output_cert_or_csr, true); + bytes_written = + cert_writer.encode_ecdsa_certificate(&scratch_buf[..bytes_written], &sig)?; + u32::try_from(bytes_written).map_err(|_| DpeErrorCode::InternalError)? + } + CertificateFormat::Csr => { + let mut bytes_written = scratch_writer.encode_certification_request_info( + &pub_key, + &subject_name, + &measurements, + )?; + if bytes_written > MAX_CERT_SIZE { + return Err(DpeErrorCode::InternalError); + } + + let cert_req_info_digest = env.crypto.hash(algs, &scratch_buf[..bytes_written])?; + // The PKCS#10 CSR is self-signed so the private key signs it instead of the alias key. + let cert_req_info_sig = env.crypto.ecdsa_sign_with_derived( + algs, + &cert_req_info_digest, + &priv_key, + &pub_key, + )?; + + let mut csr_buffer = [0u8; MAX_CERT_SIZE]; + let mut csr_writer = CertWriter::new(&mut csr_buffer, true); + bytes_written = + csr_writer.encode_csr(&scratch_buf[..bytes_written], &cert_req_info_sig)?; + if bytes_written > MAX_CERT_SIZE { + return Err(DpeErrorCode::InternalError); + } + + let csr_digest = env.crypto.hash(algs, &csr_buffer[..bytes_written])?; + let csr_sig = env + .crypto + .ecdsa_sign_with_alias(DPE_PROFILE.alg_len(), &csr_digest)?; + let sid = env.platform.get_signer_identifier()?; + + let mut cms_writer = CertWriter::new(output_cert_or_csr, true); + bytes_written = cms_writer.encode_cms(&csr_buffer[..bytes_written], &csr_sig, &sid)?; + u32::try_from(bytes_written).map_err(|_| DpeErrorCode::InternalError)? + } + }; + + 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)] pub(crate) mod tests { use crate::tci::{TciMeasurement, TciNodeData}; @@ -2678,9 +2995,6 @@ pub(crate) mod tests { } match cert.get_extension_unique(&oid!(2.5.29 .35)) { - Ok(Some(_)) => { - panic!("authority key identifier extensions found for non CA certificate") - } Err(_) => panic!("multiple authority key identifier extensions found"), _ => (), } diff --git a/platform/src/default.rs b/platform/src/default.rs index 691006e2..3c5c259c 100644 --- a/platform/src/default.rs +++ b/platform/src/default.rs @@ -1,7 +1,7 @@ // Licensed under the Apache-2.0 license use crate::{ - CertValidity, Platform, PlatformError, SignerIdentifier, SubjectAltName, MAX_CHUNK_SIZE, + CertValidity, Platform, PlatformError, SignerIdentifier, SubjectAltName, Ueid, MAX_CHUNK_SIZE, MAX_ISSUER_NAME_SIZE, MAX_KEY_IDENTIFIER_SIZE, }; use arrayvec::ArrayVec; @@ -15,6 +15,7 @@ pub const VENDOR_ID: u32 = 0; pub const VENDOR_SKU: u32 = 0; pub const NOT_BEFORE: &str = "20230227000000Z"; pub const NOT_AFTER: &str = "99991231235959Z"; +pub const TEST_UEID: [u8; 17] = [0xA; 17]; // Run ./generate.sh to generate all test certs and test private keys #[cfg(feature = "dpe_profile_p256_sha256")] @@ -201,4 +202,13 @@ impl Platform for DefaultPlatform { fn get_subject_alternative_name(&mut self) -> Result { Err(PlatformError::NotImplemented) } + fn get_ueid(&mut self) -> Result { + let buf_size = TEST_UEID.len() as u32; + let mut ueid = Ueid::default(); + + ueid.buf[..buf_size as usize].clone_from_slice(&TEST_UEID); + ueid.buf_size = buf_size; + + Ok(ueid) + } } diff --git a/platform/src/lib.rs b/platform/src/lib.rs index 37452458..54cc221e 100644 --- a/platform/src/lib.rs +++ b/platform/src/lib.rs @@ -22,6 +22,32 @@ pub const MAX_SN_SIZE: usize = 20; pub const MAX_KEY_IDENTIFIER_SIZE: usize = 20; pub const MAX_VALIDITY_SIZE: usize = 24; pub const MAX_OTHER_NAME_SIZE: usize = 128; +// Hash size of the SHA-384 DPE profile +pub const MAX_UEID_SIZE: usize = 48; + +pub struct Ueid { + pub buf: [u8; MAX_UEID_SIZE], + pub buf_size: u32, +} + +impl Ueid { + pub fn get(&self) -> Result<&[u8], PlatformError> { + let ueid = self + .buf + .get(..self.buf_size as usize) + .ok_or(PlatformError::InvalidUeidError)?; + Ok(ueid) + } +} + +impl Default for Ueid { + fn default() -> Self { + Self { + buf: [0; MAX_UEID_SIZE], + buf_size: 0, + } + } +} #[derive(Debug, PartialEq, Eq)] pub enum SignerIdentifier { @@ -61,6 +87,8 @@ pub enum PlatformError { CertValidityError(u32) = 0x7, IssuerKeyIdentifierError(u32) = 0x8, SubjectAlternativeNameError(u32) = 0x9, + MissingUeidError = 0xA, + InvalidUeidError = 0xB, } impl PlatformError { @@ -75,6 +103,8 @@ impl PlatformError { match self { PlatformError::CertificateChainError => None, PlatformError::NotImplemented => None, + PlatformError::MissingUeidError => None, + PlatformError::InvalidUeidError => None, PlatformError::IssuerNameError(code) => Some(*code), PlatformError::PrintError(code) => Some(*code), PlatformError::SerialNumberError(code) => Some(*code), @@ -147,4 +177,9 @@ pub trait Platform { /// can be left unimplemented if the SubjectAlternativeName extension is /// not needed in the DPE leaf cert or CSR. fn get_subject_alternative_name(&mut self) -> Result; + + /// Retrieves the device serial number + /// + /// This is encoded into certificates created by DPE. + fn get_ueid(&mut self) -> Result; } diff --git a/simulator/src/main.rs b/simulator/src/main.rs index 919d7d0c..015ef899 100644 --- a/simulator/src/main.rs +++ b/simulator/src/main.rs @@ -52,6 +52,7 @@ fn handle_request(dpe: &mut DpeInstance, env: &mut DpeEnv, stream 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, @@ -100,14 +101,6 @@ struct Args { #[arg(long)] supports_csr: bool, - // Supports the CertifyKey IS_CA flag - #[arg(long)] - supports_is_ca: bool, - - /// Supports symmetric derivation. - #[arg(long)] - supports_is_symmetric: bool, - /// Supports the INTERNAL_INPUT_INFO extension to DeriveContext #[arg(long)] supports_internal_info: bool, @@ -119,6 +112,10 @@ struct Args { /// Supports the RETAIN_PARENT_CONTEXT extension to DeriveContext #[arg(long)] supports_retain_parent_context: bool, + + /// Supports the CDI_EXPORT extension to DeriveContext + #[arg(long)] + supports_cdi_export: bool, } struct SimTypes {} @@ -159,12 +156,11 @@ fn main() -> std::io::Result<()> { support.set(Support::ROTATE_CONTEXT, args.supports_rotate_context); support.set(Support::INTERNAL_DICE, args.supports_internal_dice); support.set(Support::INTERNAL_INFO, args.supports_internal_info); - support.set(Support::IS_CA, args.supports_is_ca); - support.set(Support::IS_SYMMETRIC, args.supports_is_symmetric); support.set( Support::RETAIN_PARENT_CONTEXT, args.supports_retain_parent_context, ); + support.set(Support::CDI_EXPORT, args.supports_cdi_export); let mut env = DpeEnv:: { crypto: ::Crypto::new(), diff --git a/verification/client/abi.go b/verification/client/abi.go index 2707734e..906e8f1c 100644 --- a/verification/client/abi.go +++ b/verification/client/abi.go @@ -35,6 +35,7 @@ type Support struct { InternalDice bool IsCA bool RetainParentContext bool + CdiExport bool } // profileCommandCodes holds command codes for a specific revision of the @@ -125,6 +126,9 @@ const ( // ContextHandle is a DPE context handle type ContextHandle [16]byte +// ExportedCdi is a handle to an exported CDI +type ExportedCdi [32]byte + // DestroyCtxCmd is input parameters to DestroyContext type DestroyCtxCmd struct { handle ContextHandle @@ -149,11 +153,6 @@ type GetProfileResp struct { // CertifyKeyFlags is the input flags to CertifyKey type CertifyKeyFlags uint32 -// Supported flags to CertifyKey -const ( - CertifyAddIsCA CertifyKeyFlags = 1 << 30 -) - // CertifyKeyFormat is the requested output format of the DPE key certification type CertifyKeyFormat uint32 @@ -223,6 +222,8 @@ const ( InputAllowCA DeriveContextFlags = 1 << 26 InputAllowX509 DeriveContextFlags = 1 << 25 Recursive DeriveContextFlags = 1 << 24 + CdiExport DeriveContextFlags = 1 << 23 + CreateCertificate DeriveContextFlags = 1 << 22 ) // DeriveContextReq is the input request to DeriveContext @@ -238,16 +239,14 @@ type DeriveContextReq[Digest DigestAlgorithm] struct { type DeriveContextResp struct { NewContextHandle ContextHandle ParentContextHandle ContextHandle + ExportedCdi ExportedCdi + CertificateSize uint32 + NewCertificate []byte } // SignFlags is the input flags to Sign type SignFlags uint32 -// Supported Sign flags -const ( - IsSymmetric SignFlags = 1 << 30 -) - // SignReq is the input request to Sign type SignReq[Digest DigestAlgorithm] struct { ContextHandle ContextHandle @@ -259,7 +258,7 @@ type SignReq[Digest DigestAlgorithm] struct { // SignResp is the output response from Sign type SignResp[Digest DigestAlgorithm] struct { NewContextHandle ContextHandle - HmacOrSignatureR Digest + SignatureR Digest SignatureS Digest } @@ -517,15 +516,43 @@ func (c *DPEABI[_, _, _]) GetCertificateChainABI() (*GetCertificateChainResp, er } // DeriveContextABI calls DPE DeriveContext command. -func (c *DPEABI[_, Digest, _]) DeriveContextABI(cmd *DeriveContextReq[Digest]) (*DeriveContextResp, error) { - var respStruct DeriveContextResp +func (c *DPEABI[_, Digest, DPECertificate]) DeriveContextABI(cmd *DeriveContextReq[Digest]) (*DeriveContextResp, error) { + // Define an anonymous struct for the response, because the shape changes if exportCdi is set. + if cmd.Flags&CdiExport == CdiExport { + respStruct := struct { + NewContextHandle [16]byte + ParentContextHandle [16]byte + ExportedCdi [32]byte + CertificateSize uint32 + Certificate DPECertificate + }{} + _, err := execCommand(c.transport, c.constants.Codes.DeriveContext, c.Profile, cmd, &respStruct) + if err != nil { + return nil, err + } - _, err := execCommand(c.transport, c.constants.Codes.DeriveContext, c.Profile, cmd, &respStruct) - if err != nil { - return nil, err - } + return &DeriveContextResp{ + NewContextHandle: respStruct.NewContextHandle, + ParentContextHandle: respStruct.ParentContextHandle, + ExportedCdi: respStruct.ExportedCdi, + CertificateSize: respStruct.CertificateSize, + NewCertificate: respStruct.Certificate.Bytes()[:respStruct.CertificateSize], + }, nil + } else { + respStruct := struct { + NewContextHandle [16]byte + ParentContextHandle [16]byte + }{} + _, err := execCommand(c.transport, c.constants.Codes.DeriveContext, c.Profile, cmd, &respStruct) + if err != nil { + return nil, err + } - return &respStruct, err + return &DeriveContextResp{ + NewContextHandle: respStruct.NewContextHandle, + ParentContextHandle: respStruct.ParentContextHandle, + }, nil + } } // RotateContextHandleABI calls DPE RotateContextHandle command. @@ -694,9 +721,9 @@ func (c *DPEABI[_, Digest, _]) Sign(handle *ContextHandle, label []byte, flags S } signedResp := &DPESignedHash{ - Handle: resp.NewContextHandle, - HmacOrSignatureR: resp.HmacOrSignatureR.Bytes(), - SignatureS: resp.SignatureS.Bytes(), + Handle: resp.NewContextHandle, + SignatureR: resp.SignatureR.Bytes(), + SignatureS: resp.SignatureS.Bytes(), } return signedResp, nil @@ -738,5 +765,8 @@ func (s *Support) ToFlags() uint32 { if s.RetainParentContext { flags |= (1 << 19) } + if s.CdiExport { + flags |= (1 << 18) + } return flags } diff --git a/verification/client/client.go b/verification/client/client.go index 260b1fa6..1a4cb617 100644 --- a/verification/client/client.go +++ b/verification/client/client.go @@ -41,9 +41,9 @@ type DPETCI struct { // DPESignedHash is the response from DPE Sign type DPESignedHash struct { - Handle ContextHandle - HmacOrSignatureR []byte - SignatureS []byte + Handle ContextHandle + SignatureR []byte + SignatureS []byte } // DPEClient is a generic interface to a DPE instance diff --git a/verification/sim/transport.go b/verification/sim/transport.go index 938efcd7..3aaf51a5 100644 --- a/verification/sim/transport.go +++ b/verification/sim/transport.go @@ -73,12 +73,6 @@ func (s *DpeSimulator) PowerOn() error { if s.supports.Csr { args = append(args, "--supports-csr") } - if s.supports.IsCA { - args = append(args, "--supports-is-ca") - } - if s.supports.IsSymmetric { - args = append(args, "--supports-is-symmetric") - } if s.supports.InternalInfo { args = append(args, "--supports-internal-info") } @@ -88,6 +82,9 @@ func (s *DpeSimulator) PowerOn() error { if s.supports.RetainParentContext { args = append(args, "--supports-retain-parent-context") } + if s.supports.CdiExport { + args = append(args, "--supports-cdi-export") + } s.cmd = exec.Command(s.exePath, args...) s.cmd.Stdout = os.Stdout diff --git a/verification/testing/certifyKey.go b/verification/testing/certifyKey.go index 7d0a3208..56adb61b 100644 --- a/verification/testing/certifyKey.go +++ b/verification/testing/certifyKey.go @@ -3,7 +3,6 @@ package verification import ( - "bytes" "crypto/ecdsa" "crypto/elliptic" "crypto/sha256" @@ -11,7 +10,6 @@ import ( "crypto/x509" "crypto/x509/pkix" "encoding/asn1" - "encoding/binary" "encoding/pem" "fmt" "hash" @@ -144,7 +142,7 @@ func TestCertifyKeyCsr(d client.TestDPEInstance, c client.DPEClient, t *testing. } digestLen := profile.GetDigestSize() - flags := client.CertifyKeyFlags(client.CertifyAddIsCA) + flags := client.CertifyKeyFlags(0) label := make([]byte, digestLen) // Get DPE leaf certificate from CertifyKey @@ -197,7 +195,7 @@ func TestCertifyKeyCsr(d client.TestDPEInstance, c client.DPEClient, t *testing. } // Check fields and extensions in the CSR - checkCertifyKeyExtensions(t, csr.Extensions, flags, label, certifyKeyResp.Pub, false, certChain[len(certChain)-1].SubjectKeyId) + checkCertificateExtension(t, csr.Extensions, &label, &certifyKeyResp.Pub, false, certChain[len(certChain)-1].SubjectKeyId, false) checkPubKey(t, profile, csr.PublicKey, *certifyKeyResp) // Check that CSR is self-signed @@ -274,9 +272,10 @@ func removeTcgDiceExtendedKeyUsages(t *testing.T, certs []*x509.Certificate) { } // A tcg-dice-Ueid extension MUST be added -// This SHALL be populated by the LABEL input parameter to CertifyKey +// This SHALL be populated by the LABEL input parameter to CertifyKey or retrieved from DPE Platform +// by DeriveContext. // The extension SHOULD be marked as critical -func checkCertifyKeyTcgUeidExtension(t *testing.T, extensions []pkix.Extension, label []byte) { +func checkTcgUeidExtension(t *testing.T, extensions []pkix.Extension, label []byte) { t.Helper() ueid, err := getUeid(extensions) @@ -286,7 +285,7 @@ func checkCertifyKeyTcgUeidExtension(t *testing.T, extensions []pkix.Extension, if !reflect.DeepEqual(ueid.Ueid, label) { // Ueid extn value doen not match the label - t.Errorf("[ERROR]: tcg-dice-Ueid value does not match with the \"Label\" passed in CertifyKeyRequest") + t.Errorf("[ERROR]: tcg-dice-Ueid value does not match with the \"Label\". Got %+v but expect %+v", ueid.Ueid, label) } } @@ -295,7 +294,7 @@ func checkCertifyKeyTcgUeidExtension(t *testing.T, extensions []pkix.Extension, // The ExtendedKeyUsage extension SHOULD be marked as critical // If IsCA = true, the extension SHOULD contain tcg-dice-kp-eca // If IsCA = false, the extension SHOULD contain tcg-dice-kp-attestLoc -func checkCertifyKeyExtendedKeyUsages(t *testing.T, extensions []pkix.Extension, ca bool) { +func checkExtendedKeyUsages(t *testing.T, extensions []pkix.Extension, ca bool) { t.Helper() extKeyUsage, err := getExtendedKeyUsages(extensions) @@ -331,9 +330,10 @@ func checkCertifyKeyExtendedKeyUsages(t *testing.T, extensions []pkix.Extension, } // Checks for KeyUsage Extension as per spec -// If IsCA = true, KeyUsage extension MUST contain DigitalSignature and KeyCertSign +// If IsCA = true, KeyUsage extension MUST contain DigitalSignature and KeyCertSign. BasicConstraints will also be checked that it matches the `IsCA` bool. // If IsCA = false, KeyUsage extension MUST contain only DigitalSignature -func checkCertifyKeyExtensions(t *testing.T, extensions []pkix.Extension, flags client.CertifyKeyFlags, label []byte, pubkey client.DPEPubKey, IsX509 bool, IssuerSki []byte) { +// If `label` is nil then the Ueid extension check is omitted. +func checkCertificateExtension(t *testing.T, extensions []pkix.Extension, label *[]byte, pubkey *client.DPEPubKey, isX509 bool, IssuerSki []byte, isCA bool) { t.Helper() bc, err := getBasicConstraints(extensions) @@ -341,12 +341,14 @@ func checkCertifyKeyExtensions(t *testing.T, extensions []pkix.Extension, flags t.Error(err) } - checkCertifyKeyBasicConstraints(t, extensions, flags) - checkCertifyKeyExtendedKeyUsages(t, extensions, bc.IsCA) - checkCertifyKeyTcgUeidExtension(t, extensions, label) - if IsX509 { - checkCertifyKeySubjectKeyIdentifierExtension(t, extensions, bc.IsCA, pubkey) - checkCertifyKeyAuthorityKeyIdentifierExtension(t, extensions, bc.IsCA, IssuerSki) + checkBasicConstraints(t, extensions, isCA) + checkExtendedKeyUsages(t, extensions, bc.IsCA) + if label != nil { + checkTcgUeidExtension(t, extensions, *label) + } + if isX509 { + checkSubjectKeyIdentifierExtension(t, extensions, bc.IsCA, pubkey) + checkKeyIdentifierExtension(t, extensions, bc.IsCA, IssuerSki) } // Check MultiTcbInfo Extension structure @@ -355,7 +357,7 @@ func checkCertifyKeyExtensions(t *testing.T, extensions []pkix.Extension, flags t.Error(err) } - //Check for keyusage extension + // Check for keyusage extension var allowedKeyUsages x509.KeyUsage if bc.IsCA { @@ -377,11 +379,11 @@ func checkCertifyKeyExtensions(t *testing.T, extensions []pkix.Extension, flags } -// Validates SubjectKeyIdentifier in certificate returned by CertifyKey command +// Validates SubjectKeyIdentifier in certificate returned by CertifyKey or DeriveContext commands // against the isCa flag set in the BasicConstraints extension // The SubjectKeyIdentifier extension MUST be included if isCA is true // and MUST be excluded if isCA is false. -func checkCertifyKeySubjectKeyIdentifierExtension(t *testing.T, extensions []pkix.Extension, ca bool, pubkey client.DPEPubKey) { +func checkSubjectKeyIdentifierExtension(t *testing.T, extensions []pkix.Extension, ca bool, pubkey *client.DPEPubKey) { t.Helper() ski, err := getSubjectKeyIdentifier(extensions) @@ -392,6 +394,9 @@ func checkCertifyKeySubjectKeyIdentifierExtension(t *testing.T, extensions []pki if ski == nil { t.Errorf("[ERROR]: The certificate is a CA but the SubjectKeyIdentifier extension is not present.") } + if pubkey == nil { + return + } var hasher hash.Hash if len(pubkey.X) == 32 { hasher = sha256.New() @@ -410,48 +415,37 @@ func checkCertifyKeySubjectKeyIdentifierExtension(t *testing.T, extensions []pki } } -// Validates AuthorityKeyIdentifier in certificate returned by CertifyKey command +// Validates AuthorityKeyIdentifier in certificate returned by CertifyKey or DeriveContext commands // against the isCa flag set in the BasicConstraints extension // The AuthorityKeyIdentifier extension MUST be included if isCA is true // and MUST be excluded if isCA is false. -func checkCertifyKeyAuthorityKeyIdentifierExtension(t *testing.T, extensions []pkix.Extension, ca bool, IssuerSki []byte) { +func checkKeyIdentifierExtension(t *testing.T, extensions []pkix.Extension, ca bool, IssuerSki []byte) { t.Helper() aki, err := getAuthorityKeyIdentifier(extensions) if err != nil { t.Errorf("[ERROR]: Failed to retrieve AuthorityKeyIdentifier extension: %v", err) } - if ca { - if aki.KeyIdentifier == nil { - t.Fatal("[ERROR]: The certificate is a CA but the AuthorityKeyIdentifier extension is not present.") - } - if !reflect.DeepEqual(aki.KeyIdentifier, IssuerSki) { - t.Errorf("[ERROR]: The value of the authority key identifier %v is not equal to the issuer's subject key identifier %v", aki, IssuerSki) - } - } else if !ca && aki.KeyIdentifier != nil { - t.Errorf("[ERROR]: The certificate is not a CA but the AuthorityKeyIdentifier extension is present.") + if aki.KeyIdentifier == nil { + t.Fatal("[ERROR]: The certificate is a CA but the AuthorityKeyIdentifier extension is not present.") + } + if !reflect.DeepEqual(aki.KeyIdentifier, IssuerSki) { + t.Errorf("[ERROR]: The value of the authority key identifier %v is not equal to the issuer's subject key identifier %v", aki, IssuerSki) } } -// Validates basic constraints in certificate returned by CertifyKey command +// Validates basic constraints in certificate returned by CertifyKey or DeriveContext command // against the flag set for input parameter. // The BasicConstraints extension MUST be included -// If CertifyKey AddIsCA is set, IsCA MUST be set to true. -// If CertifyKey AddIsCA is NOT set, IsCA MUST be set to false -func checkCertifyKeyBasicConstraints(t *testing.T, extensions []pkix.Extension, flags client.CertifyKeyFlags) { +func checkBasicConstraints(t *testing.T, extensions []pkix.Extension, IsCA bool) { t.Helper() - flagsBuf := &bytes.Buffer{} - binary.Write(flagsBuf, binary.LittleEndian, flags) - bc, err := getBasicConstraints(extensions) if err != nil { t.Error(err) } - - flagIsCA := client.CertifyAddIsCA&flags != 0 - if flagIsCA != bc.IsCA { - t.Errorf("[ERROR]: ADD_IS_CA is set to %v but the basic constraint IsCA is set to %v", flagIsCA, bc.IsCA) + if bc.IsCA && !IsCA { + t.Errorf("[ERROR]: basic constraint IsCA is set to %v but expected %v.", bc.IsCA, IsCA) } } @@ -494,6 +488,8 @@ func checkCertificateStructure(t *testing.T, certBytes []byte) *x509.Certificate // We will need to truncate the serial numbers for those certs and // then enable this lint. "e_subject_dn_serial_number_max_length", + // subject key identifiers are optional in leaf certificates. + "w_ext_subject_key_identifier_missing_sub_cert", }, }) if err != nil { @@ -553,8 +549,8 @@ func testCertifyKey(d client.TestDPEInstance, c client.DPEClient, t *testing.T, } certifyKeyParams := []CertifyKeyParams{ - {Label: make([]byte, digestLen), Flags: client.CertifyKeyFlags(client.CertifyAddIsCA)}, - {Label: seqLabel, Flags: client.CertifyKeyFlags(client.CertifyAddIsCA)}, + {Label: make([]byte, digestLen), Flags: client.CertifyKeyFlags(0)}, + {Label: seqLabel, Flags: client.CertifyKeyFlags(0)}, } for _, params := range certifyKeyParams { @@ -583,7 +579,7 @@ func testCertifyKey(d client.TestDPEInstance, c client.DPEClient, t *testing.T, checkPubKey(t, profile, leafCert.PublicKey, *certifyKeyResp) // Check all extensions - checkCertifyKeyExtensions(t, leafCert.Extensions, params.Flags, params.Label, certifyKeyResp.Pub, true, certChain[len(certChain)-1].SubjectKeyId) + checkCertificateExtension(t, leafCert.Extensions, ¶ms.Label, &certifyKeyResp.Pub, true, certChain[len(certChain)-1].SubjectKeyId, false) // Ensure full certificate chain has valid signatures // This also checks certificate lifetime, signatures as part of cert chain validation diff --git a/verification/testing/deriveContext.go b/verification/testing/deriveContext.go index 0623d4e8..b5b84eb1 100644 --- a/verification/testing/deriveContext.go +++ b/verification/testing/deriveContext.go @@ -73,6 +73,58 @@ func TestDeriveContext(d client.TestDPEInstance, c client.DPEClient, t *testing. handle = &resp.NewContextHandle } +func TestDeriveContextCdiExport(d client.TestDPEInstance, c client.DPEClient, t *testing.T) { + var resp *client.DeriveContextResp + + simulation := false + handle := getInitialContextHandle(d, c, t, simulation) + defer func() { + c.DestroyContext(handle) + }() + + profile, err := client.GetTransportProfile(d) + if err != nil { + t.Fatalf("Could not get profile: %v", err) + } + digestLen := profile.GetDigestSize() + resp, err = c.DeriveContext(handle, make([]byte, digestLen), client.CdiExport|client.CreateCertificate, 0, 0) + if err != nil { + t.Fatalf("[ERROR]: Error while exporting CdiExport: %s", err) + } + + if resp.ExportedCdi == client.ExportedCdi(bytes.Repeat([]byte{0x0}, 32)) { + t.Fatalf("[FATAL]: Expected ExportedCdi field to be set but was %v", resp.ExportedCdi) + } + if resp.NewContextHandle != client.ContextHandle(bytes.Repeat([]byte{0xFF}, 16)) { + t.Fatalf("[FATAL]: Expected invalid NewContextHandle field but it was set to %v", resp.NewContextHandle) + } + if resp.ParentContextHandle != client.ContextHandle(bytes.Repeat([]byte{0xFF}, 16)) { + t.Fatalf("[FATAL]: Expected invalid ParentContextHandle field but it was set to %v", resp.ParentContextHandle) + } + if resp.CertificateSize == 0 { + t.Fatalf("[FATAL]: Expected CertificateSize to be set but was set to %v", resp.CertificateSize) + } + + // Check whether certificate is correctly encoded. + if _, err := x509.ParseCertificate(resp.NewCertificate); err != nil { + t.Fatalf("[FATAL]: Could not parse certificate using crypto/x509: %v", err) + } + leafCert := checkCertificateStructure(t, resp.NewCertificate) + + certChainBytes, err := c.GetCertificateChain() + certChain := checkCertificateChain(t, certChainBytes) + if err != nil { + t.Fatalf("[FATAL]: Could not get Certificate Chain: %v", err) + } + + // Check all extensions + checkCertificateExtension(t, leafCert.Extensions, nil, nil, true, certChain[len(certChain)-1].SubjectKeyId, true) + + // Ensure full certificate chain has valid signatures + // This also checks certificate lifetime, signatures as part of cert chain validation + validateLeafCertChain(t, certChain, leafCert) +} + // Validates DerivedChild command with ChangeLocality flag. func TestChangeLocality(d client.TestDPEInstance, c client.DPEClient, t *testing.T) { if !d.HasLocalityControl() { @@ -192,7 +244,7 @@ func TestPrivilegesEscalation(d client.TestDPEInstance, c client.DPEClient, t *t // Similarly, when commands like CertifyKey try to make use of features/flags that are unsupported // by child context, it will fail. - if _, err = c.CertifyKey(handle, make([]byte, digestLen), client.CertifyKeyX509, client.CertifyAddIsCA); err == nil { + if _, err = c.CertifyKey(handle, make([]byte, digestLen), client.CertifyKeyX509, 0); err == nil { t.Errorf("[ERROR]: Should return %q, but returned no error", client.StatusInvalidArgument) } else if !errors.Is(err, client.StatusInvalidArgument) { t.Errorf("[ERROR]: Incorrect error type. Should return %q, but returned %q", client.StatusInvalidArgument, err) diff --git a/verification/testing/negativeCases.go b/verification/testing/negativeCases.go index 08956cc9..245f06ca 100644 --- a/verification/testing/negativeCases.go +++ b/verification/testing/negativeCases.go @@ -146,7 +146,6 @@ func TestUnsupportedCommand(d client.TestDPEInstance, c client.DPEClient, t *tes // IsCA : Allows caller to request the key cert of CA // Csr : Allows caller to request the key cert in CSR format // X509 : Allows caller to request the key cert in X509 format -// IsSymmetric : Allows caller to request for symmetric signing // InternalInfo : Allows caller to derive child context with InternalInfo // InternalDice : Allows caller to derive child context with InternalDice func TestUnsupportedCommandFlag(d client.TestDPEInstance, c client.DPEClient, t *testing.T) { @@ -165,13 +164,6 @@ func TestUnsupportedCommandFlag(d client.TestDPEInstance, c client.DPEClient, t t.Errorf("[ERROR]: Incorrect error type. Simulation is not supported by DPE, InitializeContext supported by DPE, should return %q, but returned %q", client.StatusArgumentNotSupported, err) } - // Check whether error is returned since CA certificate request is unsupported by DPE profile - if _, err := c.CertifyKey(handle, make([]byte, digestLen), client.CertifyKeyX509, client.CertifyAddIsCA); err == nil { - t.Errorf("[ERROR]: IS_CA is not supported by DPE, CertifyKey should return %q, but returned no error", client.StatusArgumentNotSupported) - } else if !errors.Is(err, client.StatusArgumentNotSupported) { - t.Errorf("[ERROR]: Incorrect error type. IS_CA is not supported by DPE, CertifyKey should return %q, but returned %q", client.StatusArgumentNotSupported, err) - } - // Check whether error is returned since CSR format is unsupported by DPE profile if _, err := c.CertifyKey(handle, make([]byte, digestLen), client.CertifyKeyCsr, 0); err == nil { t.Errorf("[ERROR]: CSR format is not supported by DPE, CertifyKey should return %q, but returned no error", client.StatusArgumentNotSupported) @@ -186,13 +178,6 @@ func TestUnsupportedCommandFlag(d client.TestDPEInstance, c client.DPEClient, t t.Errorf("[ERROR]: Incorrect error type. X509 format is not supported by DPE, CertifyKey should return %q, but returned %q", client.StatusArgumentNotSupported, err) } - // Check whether error is returned since symmetric signing is unsupported by DPE profile - if _, err := c.Sign(handle, make([]byte, digestLen), client.SignFlags(client.IsSymmetric), make([]byte, digestLen)); err == nil { - t.Errorf("[ERROR]: Symmetric signing is not supported by DPE, Sign should return %q, but returned no error", client.StatusInvalidArgument) - } else if !errors.Is(err, client.StatusArgumentNotSupported) { - t.Errorf("[ERROR]: Incorrect error type. Symmetric signing is not supported by DPE, Sign should return %q, but returned %q", client.StatusInvalidArgument, err) - } - // Check whether error is returned since InternalInfo usage is unsupported by DPE profile if _, err := c.DeriveContext(handle, make([]byte, digestLen), client.DeriveContextFlags(client.InternalInputInfo), 0, 0); err == nil { t.Errorf("[ERROR]:InternalInfo is not supported by DPE, DeriveContext should return %q, but returned no error", client.StatusArgumentNotSupported) @@ -207,13 +192,6 @@ func TestUnsupportedCommandFlag(d client.TestDPEInstance, c client.DPEClient, t t.Errorf("[ERROR]: Incorrect error type. InternalDice is not supported by DPE, DeriveContext should return %q, but returned %q", client.StatusArgumentNotSupported, err) } - // Check whether error is returned since InternalInfo usage is unsupported by DPE profile - if _, err := c.DeriveContext(handle, make([]byte, digestLen), client.DeriveContextFlags(client.InputAllowCA), 0, 0); err == nil { - t.Errorf("[ERROR]:IS_CA is not supported by DPE, DeriveContext should return %q, but returned no error", client.StatusArgumentNotSupported) - } else if !errors.Is(err, client.StatusArgumentNotSupported) { - t.Errorf("[ERROR]: Incorrect error type. IS_CA is not supported by DPE, DeriveContext should return %q, but returned %q", client.StatusArgumentNotSupported, err) - } - // Check whether error is returned since InternalDice usgae is unsupported by DPE profile if _, err := c.DeriveContext(handle, make([]byte, digestLen), client.DeriveContextFlags(client.InputAllowX509), 0, 0); err == nil { t.Errorf("[ERROR]:X509 is not supported by DPE, DeriveContext should return %q, but returned no error", client.StatusArgumentNotSupported) diff --git a/verification/testing/sign.go b/verification/testing/sign.go index 7f4c0e4d..b4abc041 100644 --- a/verification/testing/sign.go +++ b/verification/testing/sign.go @@ -3,7 +3,6 @@ package verification import ( - "bytes" "crypto/ecdsa" "crypto/elliptic" "crypto/x509" @@ -17,8 +16,6 @@ import ( // TestAsymmetricSigning obtains and validates signature of asymmetric signing. // Check whether the digital signature returned by Sign command can be verified // using public key in signing key certificate returned by CertifyKey command. -// Inspite of the DPE profile supporting symmetric key, for symmetric signing it must be enabled -// explicitly in Sign command flags. Else asymmetric signing is used as default. func TestAsymmetricSigning(d client.TestDPEInstance, c client.DPEClient, t *testing.T) { useSimulation := false handle := getInitialContextHandle(d, c, t, useSimulation) @@ -73,7 +70,7 @@ func TestAsymmetricSigning(d client.TestDPEInstance, c client.DPEClient, t *test publicKey := ecdsa.PublicKey{Curve: ec, X: x, Y: y} // Build Signature from bytes - r := new(big.Int).SetBytes(signResp.HmacOrSignatureR) + r := new(big.Int).SetBytes(signResp.SignatureR) s := new(big.Int).SetBytes(signResp.SignatureS) // Verify Signature @@ -100,73 +97,9 @@ func TestSignSimulation(d client.TestDPEInstance, c client.DPEClient, t *testing digestLen := profile.GetDigestSize() - if _, err := c.Sign(handle, make([]byte, digestLen), client.SignFlags(client.IsSymmetric), make([]byte, digestLen)); err == nil { - t.Fatalf("[FATAL]: Should return %q, but returned no error", client.StatusInvalidArgument) - } else if !errors.Is(err, client.StatusInvalidArgument) { - t.Fatalf("[FATAL]: Incorrect error type. Should return %q, but returned %q", client.StatusInvalidArgument, err) - } - if _, err := c.Sign(handle, make([]byte, digestLen), client.SignFlags(0), make([]byte, digestLen)); err == nil { t.Fatalf("[FATAL]: Should return %q, but returned no error", client.StatusInvalidArgument) } else if !errors.Is(err, client.StatusInvalidArgument) { t.Fatalf("[FATAL]: Incorrect error type. Should return %q, but returned %q", client.StatusInvalidArgument, err) } } - -// TestSymmetricSigning obtains HMAC (symmetric signature) generated and compares for varying label inputs. -// Signature created is deterministic and depends on label passed to command. -// This is because label is used by DPE in symmetric key derivation. -// Invoking Sign command multiple times with same label and same content (TBS) should return same signature -// but it should return different signatures for different labels despite having the same content (To Be Signed content). -func TestSymmetricSigning(d client.TestDPEInstance, c client.DPEClient, t *testing.T) { - useSimulation := false - handle := getInitialContextHandle(d, c, t, useSimulation) - - // Get digest size - profile, err := client.GetTransportProfile(d) - if err != nil { - t.Fatalf("Could not get profile: %v", err) - } - - digestLen := profile.GetDigestSize() - label := make([]byte, digestLen) - for i := range label { - label[i] = byte(i) - } - - tbs := make([]byte, digestLen) - for i := range tbs { - tbs[i] = byte(i) - } - - signedData, err := c.Sign(handle, label, client.SignFlags(client.IsSymmetric), tbs) - if err != nil { - t.Fatalf("[FATAL]: Error while signing %v", err) - } - - // Rerun with same label and compare signature emitted. - signedDataWithSameLabel, err := c.Sign(handle, label, client.SignFlags(client.IsSymmetric), tbs) - if err != nil { - t.Fatalf("[FATAL]: Error while signing %v", err) - } - - // Symmetric sign only populates HmacOrSignatureR. SignatureS is all zeroes. - if !bytes.Equal(signedDataWithSameLabel.HmacOrSignatureR, signedData.HmacOrSignatureR) { - t.Errorf("[ERROR]: Signature varies for same label, want %v but got %v", signedData.HmacOrSignatureR, signedDataWithSameLabel.HmacOrSignatureR) - } - - // Rerun with different label, signature must change this time - newLabel := make([]byte, digestLen) - for i := range newLabel { - newLabel[i] = byte(0) - } - - signedDataWithDiffLabel, err := c.Sign(handle, newLabel, client.SignFlags(client.IsSymmetric), tbs) - if err != nil { - t.Fatalf("[FATAL]: Error while signing %v", err) - } - - if bytes.Equal(signedDataWithDiffLabel.HmacOrSignatureR, signedData.HmacOrSignatureR) { - t.Errorf("[ERROR]: Signature must vary for different label despite having same toBeSigned content, want new signature but got old %v", signedData.HmacOrSignatureR) - } -} diff --git a/verification/testing/simulator.go b/verification/testing/simulator.go index 6282bf92..82b7b02e 100644 --- a/verification/testing/simulator.go +++ b/verification/testing/simulator.go @@ -42,7 +42,7 @@ func GetSimulatorTargets() []TestTarget { }, { "DefaultSupport", - getTestTarget([]string{"AutoInit", "Simulation", "X509", "Csr", "IsCA", "RotateContext", "Recursive", "IsSymmetric", "RetainParentContext"}), + getTestTarget([]string{"AutoInit", "Simulation", "X509", "Csr", "RotateContext", "Recursive", "RetainParentContext"}), AllTestCases, }, { @@ -75,11 +75,6 @@ func GetSimulatorTargets() []TestTarget { getTestTarget([]string{"Csr"}), []TestCase{GetProfileTestCase}, }, - { - "GetProfile_Symmetric", - getTestTarget([]string{"IsSymmetric"}), - []TestCase{GetProfileTestCase}, - }, { "GetProfile_InternalInfo", getTestTarget([]string{"InternalInfo"}), @@ -90,11 +85,6 @@ func GetSimulatorTargets() []TestTarget { getTestTarget([]string{"InternalDice"}), []TestCase{GetProfileTestCase}, }, - { - "GetProfile_IsCA", - getTestTarget([]string{"IsCA"}), - []TestCase{GetProfileTestCase}, - }, { "GetProfile_RetainParentContext", getTestTarget([]string{"RetainParentContext"}), @@ -102,7 +92,7 @@ func GetSimulatorTargets() []TestTarget { }, { "GetProfile_Combo01", - getTestTarget([]string{"Simulation", "AutoInit", "RotateContext", "Csr", "InternalDice", "IsCA"}), + getTestTarget([]string{"Simulation", "AutoInit", "RotateContext", "Csr", "InternalDice"}), []TestCase{GetProfileTestCase}, }, { @@ -112,7 +102,7 @@ func GetSimulatorTargets() []TestTarget { }, { "GetProfile_All", - getTestTarget([]string{"Simulation", "Recursive", "AutoInit", "RotateContext", "X509", "Csr", "IsSymmetric", "InternalInfo", "InternalDice", "IsCA"}), + getTestTarget([]string{"Simulation", "Recursive", "AutoInit", "RotateContext", "X509", "Csr", "InternalInfo", "InternalDice"}), []TestCase{GetProfileTestCase}, }, { @@ -127,17 +117,22 @@ func GetSimulatorTargets() []TestTarget { }, { "DeriveContext", - getTestTarget([]string{"AutoInit", "X509", "IsCA", "RetainParentContext"}), + getTestTarget([]string{"AutoInit", "X509", "RetainParentContext"}), []TestCase{DeriveContextTestCase}, }, + { + "TestDeriveContextCdiExport", + getTestTarget([]string{"AutoInit", "CdiExport"}), + []TestCase{TestDeriveContextCdiExportTestCase}, + }, { "DeriveContext_Simulation", - getTestTarget([]string{"AutoInit", "Simulation", "X509", "IsCA", "RetainParentContext"}), + getTestTarget([]string{"AutoInit", "Simulation", "X509", "RetainParentContext"}), []TestCase{DeriveContextSimulationTestCase}, }, { "DeriveContext_PrivilegeEscalation", - getTestTarget([]string{"AutoInit", "X509", "IsCA"}), + getTestTarget([]string{"AutoInit", "X509"}), []TestCase{DeriveContextPrivilegeEscalationTestCase}, }, { diff --git a/verification/testing/tpm.go b/verification/testing/tpm.go index 323b374a..667a1396 100644 --- a/verification/testing/tpm.go +++ b/verification/testing/tpm.go @@ -130,7 +130,7 @@ func TestTpmPolicySigning(d dpe.TestDPEInstance, c dpe.DPEClient, t *testing.T) pkh := loadPubKey(t, pubKey, tpm, alg, ec) // Get encoded signature from TPM - r := new(big.Int).SetBytes(signResp.HmacOrSignatureR) + r := new(big.Int).SetBytes(signResp.SignatureR) s := new(big.Int).SetBytes(signResp.SignatureS) encodedSignature := getEncodedSignature(t, r, s, alg) diff --git a/verification/testing/verification.go b/verification/testing/verification.go index 1893baee..cedbe9b2 100644 --- a/verification/testing/verification.go +++ b/verification/testing/verification.go @@ -39,17 +39,17 @@ var InitializeContextSimulationTestCase = TestCase{ // CertifyKeyTestCase tests CertifyKey var CertifyKeyTestCase = TestCase{ - "CertifyKey", TestCertifyKey, []string{"AutoInit", "X509", "IsCA"}, + "CertifyKey", TestCertifyKey, []string{"AutoInit", "X509"}, } // client.CertifyKeyCsrTestCase tests CertifyKey with type = CSR var CertifyKeyCsrTestCase = TestCase{ - "CertifyKeyCsr", TestCertifyKeyCsr, []string{"AutoInit", "Csr", "IsCA"}, + "CertifyKeyCsr", TestCertifyKeyCsr, []string{"AutoInit", "Csr"}, } // CertifyKeySimulationTestCase tests CertifyKey on Simulation mode contexts var CertifyKeySimulationTestCase = TestCase{ - "CertifyKeySimulation", TestCertifyKeySimulation, []string{"AutoInit", "Simulation", "X509", "IsCA"}, + "CertifyKeySimulation", TestCertifyKeySimulation, []string{"AutoInit", "Simulation", "X509"}, } // GetCertificateChainTestCase tests GetCertificateChain @@ -97,11 +97,6 @@ var SignAsymmetricTestCase = TestCase{ "Sign", TestAsymmetricSigning, []string{"AutoInit", "X509"}, } -// SignSymmetricTestCase tests Sign with is-symmetric = true -var SignSymmetricTestCase = TestCase{ - "SignSymmetric", TestSymmetricSigning, []string{"AutoInit", "IsSymmetric"}, -} - // SignSimulationTestCase tests Sign with Simulation contexts var SignSimulationTestCase = TestCase{ "SignSimulation", TestSignSimulation, []string{"Simulation"}, @@ -117,6 +112,11 @@ var DeriveContextTestCase = TestCase{ "DeriveContext", TestDeriveContext, []string{"AutoInit", "RetainParentContext"}, } +// TestDeriveContextCdiExport tests DeriveContext +var TestDeriveContextCdiExportTestCase = TestCase{ + "DeriveContextCdiExport", TestDeriveContextCdiExport, []string{"CdiExport"}, +} + // DeriveContextSimulationTestCase tests DeriveContext with Simulation contexts var DeriveContextSimulationTestCase = TestCase{ "DeriveContextSimulation", TestDeriveContextSimulation, []string{"AutoInit", "Simulation", "X509", "InternalDice", "InternalInfo", "RetainParentContext"}, @@ -134,7 +134,7 @@ var DeriveContextLocalityTestCase = TestCase{ // DeriveContextPrivilegeEscalationTestCase tests that commands trying to use features that are unsupported by child context fail. var DeriveContextPrivilegeEscalationTestCase = TestCase{ - "DeriveContext_PrivilegeEscalation", TestPrivilegesEscalation, []string{"AutoInit", "X509", "IsCA"}, + "DeriveContext_PrivilegeEscalation", TestPrivilegesEscalation, []string{"AutoInit", "X509"}, } // DeriveContextInputFlagsTestCase tests DeriveContext with the input flags InternalDiceInfo and InternalInputInfo. @@ -162,7 +162,6 @@ var AllTestCases = []TestCase{ RotateContextTestCase, RotateContextSimulationTestCase, SignAsymmetricTestCase, - SignSymmetricTestCase, SignSimulationTestCase, GetProfileTestCase, InitializeContextTestCase,