Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for export-cdi in DeriveContext #388

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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