Skip to content

Commit

Permalink
Add support for export-cdi in DeriveContext
Browse files Browse the repository at this point in the history
  • Loading branch information
clundin25 committed Jan 29, 2025
1 parent 44a2307 commit 39762ab
Show file tree
Hide file tree
Showing 29 changed files with 1,220 additions and 814 deletions.
1 change: 0 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 1 addition & 2 deletions crypto/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 = []

Expand All @@ -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}
Expand Down
94 changes: 71 additions & 23 deletions crypto/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -54,6 +57,9 @@ pub enum CryptoError {
Size = 0x3,
NotImplemented = 0x4,
HashError(u32) = 0x5,
InvalidExportedCdiHandle = 0x6,
ExportedCdiHandleDuplicateCdi = 0x7,
ExportedCdiHandleLimitExceeded = 0x8,
}

impl CryptoError {
Expand All @@ -66,11 +72,14 @@ impl CryptoError {

pub fn get_error_detail(&self) -> Option<u32> {
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,
}
}
}
Expand Down Expand Up @@ -170,6 +179,20 @@ pub trait Crypto {
info: &[u8],
) -> Result<Self::Cdi, CryptoError>;

/// 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<ExportedCdiHandle, CryptoError>;

/// CFI wrapper around derive_cdi
///
/// To implement this function, you need to add the
Expand All @@ -182,6 +205,18 @@ pub trait Crypto {
info: &[u8],
) -> Result<Self::Cdi, CryptoError>;

/// 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<ExportedCdiHandle, CryptoError>;

/// Derives a key pair using a cryptographically secure KDF
///
/// # Arguments
Expand All @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -240,24 +306,6 @@ pub trait Crypto {
priv_key: &Self::PrivKey,
pub_key: &EcdsaPub,
) -> Result<EcdsaSig, CryptoError>;

/// 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<HmacSig, CryptoError>;
}
#[cfg(test)]
mod tests {
Expand Down
146 changes: 99 additions & 47 deletions crypto/src/openssl.rs
Original file line number Diff line number Diff line change
@@ -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::{
Expand All @@ -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};
Expand Down Expand Up @@ -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<(<OpensslCrypto as Crypto>::Cdi, ExportedCdiHandle)>,
}

#[cfg(not(feature = "deterministic_rand"))]
pub struct OpensslCrypto;
pub struct OpensslCrypto {
export_cdi_slots: Vec<(<OpensslCrypto as Crypto>::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 {
Expand Down Expand Up @@ -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: &<OpensslCrypto as Crypto>::Cdi,
label: &[u8],
info: &[u8],
) -> Result<(<OpensslCrypto as Crypto>::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 {
Expand All @@ -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(())
}

Expand All @@ -134,37 +178,64 @@ impl Crypto for OpensslCrypto {
measurement: &Digest,
info: &[u8],
) -> Result<Self::Cdi, CryptoError> {
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<ExportedCdiHandle, CryptoError> {
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(
Expand Down Expand Up @@ -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<HmacSig, CryptoError> {
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())
}
}
Loading

0 comments on commit 39762ab

Please sign in to comment.