Skip to content

Commit

Permalink
Started work on sign with exported runtime command.
Browse files Browse the repository at this point in the history
  • Loading branch information
clundin25 committed Jan 16, 2025
1 parent f4dd360 commit f7d2543
Show file tree
Hide file tree
Showing 10 changed files with 236 additions and 27 deletions.
9 changes: 4 additions & 5 deletions api/src/mailbox.rs
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,7 @@ pub enum MailboxReq {
CertifyKeyExtended(CertifyKeyExtendedReq),
SetAuthManifest(SetAuthManifestReq),
AuthorizeAndStash(AuthorizeAndStashReq),
SignWithExported(SignWithExportedResp),
SignWithExported(SignWithExportedReq),
}

impl MailboxReq {
Expand Down Expand Up @@ -709,7 +709,7 @@ pub struct InvokeDpeResp {
pub data: [u8; InvokeDpeResp::DATA_MAX_SIZE], // variable length
}
impl InvokeDpeResp {
pub const DATA_MAX_SIZE: usize = 2200;
pub const DATA_MAX_SIZE: usize = 7000; // Temporarily avoid DPE buffer overrun.
}
impl ResponseVarSize for InvokeDpeResp {}

Expand Down Expand Up @@ -1039,9 +1039,8 @@ impl Default for SignWithExportedReq {
}

impl SignWithExportedReq {
pub const EXPORTED_CDI_MAX_SIZE: usize = 512;
pub const MAX_DIGEST_SIZE: usize = 64; // TODO(clundin): Is this a reasonable max size? This accommodates
// SHA-512 but DPE only supports SHA-384.
pub const EXPORTED_CDI_MAX_SIZE: usize = 32;
pub const MAX_DIGEST_SIZE: usize = 48; // TODO(clundin): Is this a reasonable max size? DPE only supports up to SHA-384.
}

impl Request for SignWithExportedReq {
Expand Down
53 changes: 49 additions & 4 deletions runtime/src/dpe_crypto.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ Abstract:

use core::cmp::min;

use arrayvec::ArrayVec;
use caliptra_cfi_derive_git::cfi_impl_fn;
use caliptra_cfi_lib_git::{cfi_assert, cfi_assert_eq, cfi_launder};
use caliptra_common::keyids::{
Expand All @@ -25,10 +26,16 @@ use caliptra_drivers::{
KeyVault, KeyWriteArgs, Sha384, Sha384DigestOp, Trng,
};
use crypto::{AlgLen, Crypto, CryptoBuf, CryptoError, Digest, EcdsaPub, EcdsaSig, Hasher};
use dpe::x509::MeasurementData;
use dpe::{x509::MeasurementData, MAX_EXPORTED_CDI_SIZE};
use zerocopy::IntoBytes;
use zeroize::Zeroize;

// Currently only can export CDI once, but in the future we may want to support multiple exported
// CDI handles at the cost of using more KeyVault slots.
pub const EXPORTED_HANDLES_NUM: usize = 1;
pub type ExportedCdiHandle = [u8; MAX_EXPORTED_CDI_SIZE];
pub type ExportedCdiHandles = ArrayVec<(KeyId, ExportedCdiHandle), EXPORTED_HANDLES_NUM>;

pub struct DpeCrypto<'a> {
sha384: &'a mut Sha384,
trng: &'a mut Trng,
Expand All @@ -38,6 +45,7 @@ pub struct DpeCrypto<'a> {
rt_pub_key: &'a mut Ecc384PubKey,
key_id_rt_cdi: KeyId,
key_id_rt_priv_key: KeyId,
exported_cdi_slots: Option<&'a mut ExportedCdiHandles>,
}

impl<'a> DpeCrypto<'a> {
Expand All @@ -61,9 +69,14 @@ impl<'a> DpeCrypto<'a> {
rt_pub_key,
key_id_rt_cdi,
key_id_rt_priv_key,
exported_cdi_slots: None,
}
}

pub fn with_exported_cdi_slots(&mut self, slots: &'a mut ExportedCdiHandles) {
self.exported_cdi_slots = Some(slots);
}

fn derive_cdi_inner(
&mut self,
algs: AlgLen,
Expand Down Expand Up @@ -227,9 +240,41 @@ impl<'a> Crypto for DpeCrypto<'a> {
self.derive_cdi_inner(algs, measurement, info, KEY_ID_DPE_CDI)
}

#[cfg_attr(not(feature = "no-cfi"), cfi_impl_fn)]
fn get_exported_cdi(&mut self) -> Result<Self::Cdi, CryptoError> {
Ok(KEY_ID_EXPORTED_DPE_CDI)
//TODO(clundin): Wrap this with CFI?
fn get_exported_cdi_handle(
&mut self,
cdi: &Self::Cdi,
) -> Result<[u8; MAX_EXPORTED_CDI_SIZE], CryptoError> {
let mut exported_cdi_handle = [0; MAX_EXPORTED_CDI_SIZE];
self.rand_bytes(&mut exported_cdi_handle)?;

// TODO(clundin): Add a real error.
let Some(ref mut export_cdi_slots) = self.exported_cdi_slots else { Err(CryptoError::ExportedCdiHandleLimitExceeded)? };
if export_cdi_slots.is_full() {
return Err(CryptoError::ExportedCdiHandleLimitExceeded);
}

for slot in export_cdi_slots.iter() {
if slot.0 == *cdi {
return Err(CryptoError::ExportedCdiHandleLimitExceeded);
}
}
export_cdi_slots.push((cdi.clone(), exported_cdi_handle));
Ok(exported_cdi_handle)
}

//TODO(clundin): Wrap this with CFI?
fn get_cdi_from_exported_handle(
&mut self,
exported_cdi_handle: &[u8; MAX_EXPORTED_CDI_SIZE],
) -> Option<Self::Cdi> {
let Some(ref export_cdi_slots) = self.exported_cdi_slots else { return None };
for (cdi, handle) in export_cdi_slots.iter() {
if handle == exported_cdi_handle {
return Some(cdi.clone());
}
}
None
}

#[cfg_attr(not(feature = "no-cfi"), cfi_impl_fn)]
Expand Down
5 changes: 4 additions & 1 deletion runtime/src/drivers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ use crate::{
PL1_DPE_ACTIVE_CONTEXT_THRESHOLD,
};

use crate::dpe_crypto::ExportedCdiHandles;
use arrayvec::ArrayVec;
use caliptra_cfi_derive_git::{cfi_impl_fn, cfi_mod_fn};
use caliptra_cfi_lib_git::{cfi_assert, cfi_assert_eq, cfi_assert_eq_12_words, cfi_launder};
Expand Down Expand Up @@ -58,7 +59,7 @@ use dpe::{
};

use core::cmp::Ordering::{Equal, Greater};
use crypto::{AlgLen, Crypto, CryptoBuf, Hasher};
use crypto::{AlgLen, Crypto, CryptoBuf, Hasher, MAX_EXPORTED_CDI_SIZE};
use zerocopy::IntoBytes;

#[derive(PartialEq, Clone)]
Expand Down Expand Up @@ -108,6 +109,7 @@ pub struct Drivers {
pub is_shutdown: bool,

pub dmtf_device_info: Option<ArrayVec<u8, { AddSubjectAltNameReq::MAX_DEVICE_INFO_LEN }>>,
pub exported_cdi_slots: ExportedCdiHandles,
}

impl Drivers {
Expand Down Expand Up @@ -146,6 +148,7 @@ impl Drivers {
cert_chain: ArrayVec::new(),
is_shutdown: false,
dmtf_device_info: None,
exported_cdi_slots: ArrayVec::new(),
})
}

Expand Down
7 changes: 6 additions & 1 deletion runtime/src/invoke_dpe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,12 @@ impl InvokeDpeCmd {
{
return Err(CaliptraError::RUNTIME_INCORRECT_PAUSER_PRIVILEGE_LEVEL);
}
// TODO(clundin): Document this better; TL;DR if the export CDI flag is set we
// need to pass a persistent back end for it.
// Also this should only be needed when the export CDI flag is set, but it's
// okay to always do it.
env.crypto
.with_exported_cdi_slots(&mut drivers.exported_cdi_slots);
cmd.execute(dpe, &mut env, locality)
}
Command::CertifyKey(cmd) => {
Expand All @@ -131,7 +137,6 @@ impl InvokeDpeCmd {
destroy_ctx_resp
}
Command::Sign(cmd) => cmd.execute(dpe, &mut env, locality),
Command::SignWithExported(cmd) => cmd.execute(dpe, &mut env, locality),
Command::RotateCtx(cmd) => cmd.execute(dpe, &mut env, locality),
Command::GetCertificateChain(cmd) => cmd.execute(dpe, &mut env, locality),
};
Expand Down
3 changes: 3 additions & 0 deletions runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ mod invoke_dpe;
mod pcr;
mod populate_idev;
mod set_auth_manifest;
mod sign_with_exported;
mod stash_measurement;
mod subject_alt_name;
mod update;
Expand All @@ -46,6 +47,7 @@ use mailbox::Mailbox;
use crate::capabilities::CapabilitiesCmd;
pub use crate::certify_key_extended::CertifyKeyExtendedCmd;
pub use crate::hmac::Hmac;
use crate::sign_with_exported::SignWithExportedCmd;
pub use crate::subject_alt_name::AddSubjectAltNameCmd;
pub use authorize_and_stash::{IMAGE_AUTHORIZED, IMAGE_HASH_MISMATCH, IMAGE_NOT_AUTHORIZED};
pub use caliptra_common::fips::FipsVersionCmd;
Expand Down Expand Up @@ -229,6 +231,7 @@ fn handle_command(drivers: &mut Drivers) -> CaliptraResult<MboxStatusE> {
CommandId::SET_AUTH_MANIFEST => SetAuthManifestCmd::execute(drivers, cmd_bytes),
CommandId::AUTHORIZE_AND_STASH => AuthorizeAndStashCmd::execute(drivers, cmd_bytes),
CommandId::GET_IDEV_CSR => GetIdevCsrCmd::execute(drivers, cmd_bytes),
CommandId::SIGN_WITH_EXPORTED => SignWithExportedCmd::execute(drivers, cmd_bytes),
_ => Err(CaliptraError::RUNTIME_UNIMPLEMENTED_COMMAND),
}?;

Expand Down
91 changes: 91 additions & 0 deletions runtime/src/sign_with_exported.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
// Licensed under the Apache-2.0 license

use crate::{dpe_crypto::DpeCrypto, Drivers};

use caliptra_cfi_derive_git::cfi_impl_fn;
use caliptra_cfi_lib_git::{cfi_assert, cfi_assert_eq, cfi_launder};

use caliptra_common::mailbox_api::{
MailboxResp, MailboxRespHeader, SignWithExportedReq, SignWithExportedResp,
};
use caliptra_error::{CaliptraError, CaliptraResult};

use crypto::{Crypto, Digest, EcdsaSig};
use dpe::{DPE_PROFILE, MAX_EXPORTED_CDI_SIZE};
use zerocopy::{FromBytes, IntoBytes};

pub struct SignWithExportedCmd;
impl SignWithExportedCmd {
/// SignWithExported signs a `digest` using an ECDSA keypair derived from a exported_cdi
/// handle and the CDI stored in DPE.
///
/// # Arguments
///
/// * `env` - DPE environment containing Crypto and Platform implementations
/// * `digest` - The data to be signed
#[cfg_attr(not(feature = "no-cfi"), cfi_impl_fn)]
fn ecdsa_sign(
env: &mut DpeCrypto,
digest: &Digest,
exported_cdi_handle: &[u8; MAX_EXPORTED_CDI_SIZE],
) -> CaliptraResult<EcdsaSig> {
let algs = DPE_PROFILE.alg_len();
// TODO(clundin): Add unique error codes.
let cdi = env.get_cdi_from_exported_handle(exported_cdi_handle);
// TODO(clundin): Actually handle this.
let cdi = cdi.unwrap();

let key_label = b"Exported ECC";
let key_pair = env.derive_key_pair_exported(algs, &cdi, key_label, b"Exported Handle");

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.map_err(|_| CaliptraError::RUNTIME_INITIALIZE_DPE_FAILED)?;

let sig = env
.ecdsa_sign_with_derived(algs, digest, &priv_key, &pub_key)
.map_err(|_| CaliptraError::RUNTIME_INITIALIZE_DPE_FAILED)?;

Ok(sig)
}

#[cfg_attr(not(feature = "no-cfi"), cfi_impl_fn)]
#[inline(never)]
pub(crate) fn execute(drivers: &mut Drivers, cmd_args: &[u8]) -> CaliptraResult<MailboxResp> {
let cmd = SignWithExportedReq::ref_from_bytes(cmd_args)
.map_err(|_| CaliptraError::RUNTIME_MAILBOX_INVALID_PARAMS)?;
let key_id_rt_cdi = Drivers::get_key_id_rt_cdi(drivers)?;
let key_id_rt_priv_key = Drivers::get_key_id_rt_priv_key(drivers)?;
let mut crypto = DpeCrypto::new(
&mut drivers.sha384,
&mut drivers.trng,
&mut drivers.ecc384,
&mut drivers.hmac384,
&mut drivers.key_vault,
&mut drivers.persistent_data.get_mut().fht.rt_dice_pub_key,
key_id_rt_cdi,
key_id_rt_priv_key,
);
crypto.with_exported_cdi_slots(&mut drivers.exported_cdi_slots);

// TODO(clundin): Update error code.
let digest =
Digest::new(&cmd.digest).map_err(|_| CaliptraError::RUNTIME_INVALID_CHECKSUM)?;
// TODO(clundin): Can we / should we make these assumptions for the signature type?
// I think we need to expose more metadata.
let EcdsaSig { r, s } = Self::ecdsa_sign(&mut crypto, &digest, &cmd.exported_cdi)?;

let mut resp = SignWithExportedResp::default();
resp.signature[..r.bytes().len()].copy_from_slice(r.bytes());
resp.signature[r.bytes().len()..s.bytes().len() + r.bytes().len()]
.copy_from_slice(s.bytes());
resp.signature_size = (s.bytes().len() + r.bytes().len()) as u32;
Ok(MailboxResp::SignWithExported(resp))
}
}
17 changes: 7 additions & 10 deletions runtime/tests/runtime_integration_tests/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ use caliptra_hw_model::{
use dpe::{
commands::{Command, CommandHdr},
response::{
CertifyKeyResp, DeriveContextResp, GetCertificateChainResp, GetProfileResp, NewHandleResp,
Response, ResponseHdr, SignResp, SignWithExportedResp,
CertifyKeyResp, DeriveContextExportedCdiResp, DeriveContextResp, GetCertificateChainResp,
GetProfileResp, NewHandleResp, Response, ResponseHdr, SignResp,
},
};
use openssl::{
Expand Down Expand Up @@ -161,7 +161,6 @@ fn get_cmd_id(dpe_cmd: &mut Command) -> u32 {
Command::DeriveContext(_) => Command::DERIVE_CONTEXT,
Command::CertifyKey(_) => Command::CERTIFY_KEY,
Command::Sign(_) => Command::SIGN,
Command::SignWithExported(_) => Command::SIGN_WITH_EXPORTED,
Command::RotateCtx(_) => Command::ROTATE_CONTEXT_HANDLE,
Command::DestroyCtx(_) => Command::DESTROY_CONTEXT,
Command::GetCertificateChain(_) => Command::GET_CERTIFICATE_CHAIN,
Expand All @@ -178,7 +177,6 @@ fn as_bytes<'a>(dpe_cmd: &'a mut Command) -> &'a [u8] {
Command::InitCtx(cmd) => cmd.as_bytes(),
Command::RotateCtx(cmd) => cmd.as_bytes(),
Command::Sign(cmd) => cmd.as_bytes(),
Command::SignWithExported(cmd) => cmd.as_bytes(),
}
}

Expand All @@ -187,9 +185,11 @@ fn parse_dpe_response(dpe_cmd: &mut Command, resp_bytes: &[u8]) -> Response {
Command::CertifyKey(_) => {
Response::CertifyKey(CertifyKeyResp::read_from_bytes(resp_bytes).unwrap())
}
Command::DeriveContext(_) => {
Response::DeriveContext(DeriveContextResp::read_from_bytes(resp_bytes).unwrap())
}
Command::DeriveContext(_) => Response::DeriveContextExportedCdi(
// TODO(clundin): We need to infer the return type by the flags passed into the cmd.
// This type is based off the EXPORT_CDI flag.
DeriveContextExportedCdiResp::read_from_bytes(resp_bytes).unwrap(),
),
Command::GetCertificateChain(_) => Response::GetCertificateChain(
GetCertificateChainResp::read_from_bytes(resp_bytes).unwrap(),
),
Expand All @@ -206,9 +206,6 @@ fn parse_dpe_response(dpe_cmd: &mut Command, resp_bytes: &[u8]) -> Response {
Response::RotateCtx(NewHandleResp::read_from_bytes(resp_bytes).unwrap())
}
Command::Sign(_) => Response::Sign(SignResp::read_from_bytes(resp_bytes).unwrap()),
Command::SignWithExported(_) => {
Response::SignWithExported(SignWithExportedResp::read_from_bytes(resp_bytes).unwrap())
}
}
}

Expand Down
1 change: 1 addition & 0 deletions runtime/tests/runtime_integration_tests/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ mod test_pauser_privilege_levels;
mod test_pcr;
mod test_populate_idev;
mod test_set_auth_manifest;
mod test_sign_with_export;
mod test_stash_measurement;
mod test_tagging;
mod test_update_reset;
Expand Down
Loading

0 comments on commit f7d2543

Please sign in to comment.