From c75bb3d2e4efb927b808f7c51f4fafe0130f42ac Mon Sep 17 00:00:00 2001 From: Jordan Hand Date: Tue, 8 Oct 2024 12:46:23 -0700 Subject: [PATCH] User iterator instead of temporary buffer for CertifyKey TCB When encoding TcbInfos, do not make a temporary buffer to copy all the TCI measurements. This adds multiple KiB of stack usage. Instead, add a function to allow the X.509 library to create an iterator for the TCB of the node. This allows the main DPE context array to be used as the backing memory when encoding TcbInfos instead of using extra stack space. --- dpe/src/commands/certify_key.rs | 13 +---- dpe/src/context.rs | 93 +++++++++++++++++++++++++++++++++ dpe/src/dpe_instance.rs | 42 +++------------ dpe/src/x509.rs | 69 +++++++++++------------- 4 files changed, 132 insertions(+), 85 deletions(-) diff --git a/dpe/src/commands/certify_key.rs b/dpe/src/commands/certify_key.rs index a2c23e76..6111d869 100644 --- a/dpe/src/commands/certify_key.rs +++ b/dpe/src/commands/certify_key.rs @@ -4,9 +4,8 @@ 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, + DPE_PROFILE, MAX_CERT_SIZE, }; use bitflags::bitflags; #[cfg(not(feature = "no-cfi"))] @@ -120,14 +119,6 @@ impl CommandExecution for CertifyKeyCmd { 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())?; @@ -153,7 +144,7 @@ impl CommandExecution for CertifyKeyCmd { let measurements = MeasurementData { label: &self.label, - tci_nodes: &nodes[..tcb_count], + tcb: dpe.get_tcb(idx), is_ca: self.uses_is_ca(), supports_recursive: dpe.support.recursive(), subject_key_identifier, diff --git a/dpe/src/context.rs b/dpe/src/context.rs index f70b6584..096970d1 100644 --- a/dpe/src/context.rs +++ b/dpe/src/context.rs @@ -214,6 +214,10 @@ impl ChildToRootIter<'_> { count: 0, } } + + fn curr_idx(&self) -> usize { + self.idx + } } impl<'a> Iterator for ChildToRootIter<'a> { @@ -251,6 +255,95 @@ impl<'a> Iterator for ChildToRootIter<'a> { } } +pub(crate) struct RootToChildIter<'a> { + contexts: &'a [Context], + path: [u8; MAX_HANDLES], + path_size: usize, + done: bool, + curr: usize, +} + +impl<'a> RootToChildIter<'a> { + /// Create a new iterator that will start at the leaf and go to the root node. + pub fn new(leaf_idx: usize, contexts: &'a [Context]) -> Result { + let mut iter = ChildToRootIter::new(leaf_idx, contexts); + let mut path = [0u8; MAX_HANDLES]; + let mut path_size = 0; + + // Use while loop to avoid consuming iter so curr_idx can be read + // inside the loop + let mut curr = iter.curr_idx(); + while let Some(_) = iter.next() { + if path_size > contexts.len() { + return Err(DpeErrorCode::InternalError); + } + + // Guaranteed to fit into u8 since Contexts struct is of size + // MAX_HANDLES. + path[path_size] = curr as u8; + path_size += 1; + curr = iter.curr_idx(); + } + + Ok(Self { + contexts, + path, + path_size, + done: false, + curr: path_size - 1, + }) + } +} + +impl<'a> Iterator for RootToChildIter<'a> { + type Item = Result<&'a Context, DpeErrorCode>; + + fn next(&mut self) -> Option> { + if self.done { + return None; + } + if self.curr >= self.contexts.len() || self.curr >= self.path_size { + return Some(Err(DpeErrorCode::InternalError)); + } + + let context = &self.contexts[self.path[self.curr] as usize]; + + if self.curr == 0 { + self.done = true + } else { + self.curr -= 1; + } + Some(Ok(context)) + } +} + +pub(crate) struct ContextTcb<'a> { + idx: usize, + contexts: &'a [Context], +} + +impl<'a> ContextTcb<'a> { + /// Create a new iterator that will start at the leaf and go to the root node. + pub(crate) fn new(leaf_idx: usize, contexts: &'a [Context]) -> Self { + ContextTcb { + idx: leaf_idx, + contexts, + } + } + + pub fn rev_iter(&self) -> Result, DpeErrorCode> { + RootToChildIter::new(self.idx, self.contexts) + } + + pub fn iter(&self) -> ChildToRootIter<'a> { + ChildToRootIter::new(self.idx, self.contexts) + } + + pub fn count(&self) -> usize { + self.iter().count() + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/dpe/src/dpe_instance.rs b/dpe/src/dpe_instance.rs index 2fae1684..bc5573e8 100644 --- a/dpe/src/dpe_instance.rs +++ b/dpe/src/dpe_instance.rs @@ -8,10 +8,10 @@ Abstract: use crate::INTERNAL_INPUT_INFO_SIZE; use crate::{ commands::{Command, CommandExecution, InitCtxCmd}, - context::{ChildToRootIter, Context, ContextHandle, ContextState}, + context::{ChildToRootIter, Context, ContextHandle, ContextState, ContextTcb}, response::{DpeErrorCode, GetProfileResp, Response, ResponseHdr}, support::Support, - tci::{TciMeasurement, TciNodeData}, + tci::TciMeasurement, U8Bool, DPE_PROFILE, MAX_HANDLES, }; #[cfg(not(feature = "no-cfi"))] @@ -312,40 +312,10 @@ impl DpeInstance { Ok(()) } - /// Get the TCI nodes from the context at `start_idx` to the root node following parent - /// links. These are the nodes that should contribute to CDI and key - /// derivation for the context at `start_idx`. - /// - /// # Arguments - /// - /// * `start_idx` - Index into context array - /// * `nodes` - Array to write TCI nodes to - /// - /// Returns the number of TCIs written to `nodes` - #[cfg_attr(not(feature = "no-cfi"), cfi_impl_fn)] - pub(crate) fn get_tcb_nodes( - &self, - start_idx: usize, - nodes: &mut [TciNodeData], - ) -> Result { - let mut out_idx = 0; - - for status in ChildToRootIter::new(start_idx, &self.contexts) { - let curr = status?; - if out_idx >= nodes.len() { - return Err(DpeErrorCode::InternalError); - } - - nodes[out_idx] = curr.tci; - out_idx += 1; - } - - if out_idx > nodes.len() { - return Err(DpeErrorCode::InternalError); - } - nodes[..out_idx].reverse(); - - Ok(out_idx) + // Get an iterator that collects all the contexts in the path from + // `start_idx` to the root + pub(crate) fn get_tcb(&self, start_idx: usize) -> ContextTcb { + ContextTcb::new(start_idx, &self.contexts) } /// Adds `measurement` to `context`. The current TCI is the measurement and diff --git a/dpe/src/x509.rs b/dpe/src/x509.rs index 3cde5053..baf9551c 100644 --- a/dpe/src/x509.rs +++ b/dpe/src/x509.rs @@ -6,6 +6,7 @@ //! this functionality for a no_std environment. use crate::{ + context::ContextTcb, response::DpeErrorCode, tci::{TciMeasurement, TciNodeData}, DpeProfile, DPE_PROFILE, @@ -51,7 +52,7 @@ pub struct Name<'a> { pub struct MeasurementData<'a> { pub label: &'a [u8], - pub tci_nodes: &'a [TciNodeData], + pub(crate) tcb: ContextTcb<'a>, pub is_ca: bool, pub supports_recursive: bool, pub subject_key_identifier: [u8; MAX_KEY_IDENTIFIER_SIZE], @@ -348,9 +349,9 @@ impl CertWriter<'_> { } /// Get the size of a DICE FWID structure - fn get_fwid_size(digest: &[u8], tagged: bool) -> Result { + fn get_fwid_size(tagged: bool) -> Result { let size = Self::get_structure_size(Self::HASH_OID.len(), /*tagged=*/ true)? - + Self::get_structure_size(digest.len(), /*tagged=*/ true)?; + + Self::get_structure_size(DPE_PROFILE.get_tci_size(), /*tagged=*/ true)?; Self::get_structure_size(size, tagged) } @@ -358,14 +359,10 @@ impl CertWriter<'_> { /// Get the size of a tcg-dice-TcbInfo structure. For DPE, this is only used /// as part of a MultiTcbInfo. For this reason, do not include the standard /// extension fields. Only include the size of the structure itself. - fn get_tcb_info_size( - node: &TciNodeData, - supports_recursive: bool, - tagged: bool, - ) -> Result { - let fwid0_size = Self::get_fwid_size(&node.tci_current.0, /*tagged=*/ true)?; + fn get_tcb_info_size(supports_recursive: bool, tagged: bool) -> Result { + let fwid0_size = Self::get_fwid_size(/*tagged=*/ true)?; let fwid1_size = if supports_recursive { - Self::get_fwid_size(&node.tci_cumulative.0, /*tagged=*/ true)? + Self::get_fwid_size(/*tagged=*/ true)? } else { 0 }; @@ -383,17 +380,15 @@ impl CertWriter<'_> { measurements: &MeasurementData, tagged: bool, ) -> Result { - if measurements.tci_nodes.is_empty() { + let tci_count = measurements.tcb.count(); + if tci_count == 0 { return Err(DpeErrorCode::InternalError); } // Size of concatenated tcb infos - let tcb_infos_size = measurements.tci_nodes.len() - * Self::get_tcb_info_size( - &measurements.tci_nodes[0], - measurements.supports_recursive, - /*tagged=*/ true, - )?; + // PANIC FREE: Count already checked to be non-zero + let tcb_infos_size = tci_count + * Self::get_tcb_info_size(measurements.supports_recursive, /*tagged=*/ true)?; // Size of tcb infos including SEQUENCE OF tag/size let multi_tcb_info_size = Self::get_structure_size(tcb_infos_size, /*tagged=*/ true)?; @@ -1178,8 +1173,7 @@ impl CertWriter<'_> { fn encode_fwid(&mut self, tci: &TciMeasurement) -> Result { let mut bytes_written = self.encode_byte(Self::SEQUENCE_TAG)?; - bytes_written += - self.encode_size_field(Self::get_fwid_size(&tci.0, /*tagged=*/ false)?)?; + bytes_written += self.encode_size_field(Self::get_fwid_size(/*tagged=*/ false)?)?; // hashAlg OID bytes_written += self.encode_byte(Self::OID_TAG)?; @@ -1211,15 +1205,14 @@ impl CertWriter<'_> { node: &TciNodeData, supports_recursive: bool, ) -> Result { - let tcb_info_size = - Self::get_tcb_info_size(node, supports_recursive, /*tagged=*/ false)?; + let tcb_info_size = Self::get_tcb_info_size(supports_recursive, /*tagged=*/ false)?; // TcbInfo sequence let mut bytes_written = self.encode_byte(Self::SEQUENCE_TAG)?; bytes_written += self.encode_size_field(tcb_info_size)?; // fwids SEQUENCE OF // IMPLICIT [6] Constructed - let fwid_size = Self::get_fwid_size(&node.tci_current.0, /*tagged=*/ true)?; + let fwid_size = Self::get_fwid_size(/*tagged=*/ true)?; bytes_written += self.encode_byte(Self::CONTEXT_SPECIFIC | Self::CONSTRUCTED | 0x06)?; if supports_recursive { bytes_written += self.encode_size_field(fwid_size * 2)?; @@ -1259,6 +1252,7 @@ impl CertWriter<'_> { &mut self, measurements: &MeasurementData, ) -> Result { + let tci_count = measurements.tcb.count(); let multi_tcb_info_size = Self::get_multi_tcb_info_size(measurements, /*tagged=*/ false)?; @@ -1272,12 +1266,8 @@ impl CertWriter<'_> { bytes_written += self.encode_size_field(Self::BOOL_SIZE)?; bytes_written += self.encode_byte(crit)?; - let tcb_infos_size = if !measurements.tci_nodes.is_empty() { - Self::get_tcb_info_size( - &measurements.tci_nodes[0], - measurements.supports_recursive, - /*tagged=*/ true, - )? * measurements.tci_nodes.len() + let tcb_infos_size = if tci_count != 0 { + Self::get_tcb_info_size(measurements.supports_recursive, /*tagged=*/ true)? * tci_count } else { 0 }; @@ -1292,8 +1282,8 @@ impl CertWriter<'_> { bytes_written += self.encode_size_field(tcb_infos_size)?; // Encode multiple tcg-dice-TcbInfos - for node in measurements.tci_nodes { - bytes_written += self.encode_tcb_info(node, measurements.supports_recursive)?; + for node in measurements.tcb.rev_iter()? { + bytes_written += self.encode_tcb_info(&node?.tci, measurements.supports_recursive)?; } Ok(bytes_written) @@ -1582,7 +1572,7 @@ impl CertWriter<'_> { /// AuthorityKeyIdentifier ::= SEQUENCE { /// keyIdentifier [0] KeyIdentifier OPTIONAL, /// authorityCertIssuer [1] GeneralNames OPTIONAL, - /// authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL + /// authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL /// } fn encode_authority_key_identifier_extension( &mut self, @@ -2229,6 +2219,7 @@ impl CertWriter<'_> { #[cfg(test)] pub(crate) mod tests { + use crate::context::{Context, ContextTcb}; use crate::tci::{TciMeasurement, TciNodeData}; use crate::x509::{CertWriter, DirectoryString, MeasurementData, Name}; use crate::{DpeProfile, DPE_PROFILE}; @@ -2388,7 +2379,7 @@ pub(crate) mod tests { assert_eq!( bytes_written, - CertWriter::get_tcb_info_size(&node, supports_recursive, true).unwrap() + CertWriter::get_tcb_info_size(supports_recursive, true).unwrap() ); // FWIDs @@ -2413,7 +2404,7 @@ pub(crate) mod tests { assert_eq!( bytes_written, - CertWriter::get_tcb_info_size(&node, supports_recursive, true).unwrap() + CertWriter::get_tcb_info_size(supports_recursive, true).unwrap() ); // Check that only FWID[0] is present @@ -2469,11 +2460,11 @@ pub(crate) mod tests { y: CryptoBuf::new(&[0xBB; ECC_INT_SIZE]).unwrap(), }; - let node = TciNodeData::new(); - + let node = Context::new(); + let contexts = [node]; let measurements = MeasurementData { label: &[0xCC; DPE_PROFILE.get_hash_size()], - tci_nodes: &[node], + tcb: ContextTcb::new(0, &contexts), is_ca: false, supports_recursive: true, subject_key_identifier: [0u8; MAX_KEY_IDENTIFIER_SIZE], @@ -2549,7 +2540,7 @@ pub(crate) mod tests { y: CryptoBuf::new(&[0xBB; ECC_INT_SIZE]).unwrap(), }; - let node = TciNodeData::new(); + let node = Context::new(); let mut hasher = match DPE_PROFILE { DpeProfile::P256Sha256 => Hasher::new(MessageDigest::sha256()).unwrap(), @@ -2569,9 +2560,11 @@ pub(crate) mod tests { oid: DEFAULT_OTHER_NAME_OID, other_name, }); + + let contexts = [node]; let measurements = MeasurementData { label: &[0; DPE_PROFILE.get_hash_size()], - tci_nodes: &[node], + tcb: ContextTcb::new(0, &contexts), is_ca, supports_recursive: true, subject_key_identifier,