Skip to content

Commit

Permalink
Implement full DPE validation
Browse files Browse the repository at this point in the history
DPE validation does two things:
1. Checks for illegal state in DPE
2. Checks that the DPE context tree is well-formed

2 was done in a previous commit, but this commit improves upon that
implementation in a few ways:

1. Previously we were returning a bool from the context tree validation
function. Now we return an error code so the caller knows exactly what
is wrong with the shape of the DPE context tree
2. We weren't considering simulation contexts before. Simulation contexts
forming a different connected component is not an error.
  • Loading branch information
sree-revoori1 committed Dec 12, 2023
1 parent d849694 commit e8f2660
Show file tree
Hide file tree
Showing 5 changed files with 313 additions and 83 deletions.
2 changes: 2 additions & 0 deletions dpe/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,8 @@ impl Context {
self.state = ContextState::Inactive;
self.uses_internal_input_info = false.into();
self.uses_internal_input_dice = false.into();
self.allow_ca = false.into();
self.allow_x509 = false.into();
self.parent_idx = Self::ROOT_INDEX;
}

Expand Down
81 changes: 0 additions & 81 deletions dpe/src/dpe_instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -394,49 +394,6 @@ impl DpeInstance {
Ok(hasher.finish()?)
}

/// Determines if the context array represents a valid tree by checking that
/// there is only 1 connected component and that all nodes lead up to
/// the root node.
///
/// # Arguments
///
/// * `root_idx` - The index of the root context
pub fn validate_context_tree(&self, root_idx: usize) -> bool {
let mut seen = [false; MAX_HANDLES];

// dfs from the root node and try to discover invalid subtrees
if self.detect_invalid_subtree(root_idx, &mut seen) {
return false;
}

for (i, node_visited) in seen.iter().enumerate().take(MAX_HANDLES) {
// If a node was not seen when doing a dfs from the root, there must be multiple
// connected components or the root is not actually the root
if i != root_idx && self.contexts[i].state != ContextState::Inactive && !node_visited {
return false;
}
}
true
}

fn detect_invalid_subtree(&self, curr_idx: usize, seen: &mut [bool; MAX_HANDLES]) -> bool {
// if the current node was already visited we have a cycle
if curr_idx >= MAX_HANDLES
|| self.contexts[curr_idx].state == ContextState::Inactive
|| seen[curr_idx]
{
return true;
}
seen[curr_idx] = true;
// dfs on all child nodes
for child_idx in flags_iter(self.contexts[curr_idx].children, MAX_HANDLES) {
if child_idx >= MAX_HANDLES || self.detect_invalid_subtree(child_idx, seen) {
return true;
}
}
false
}

/// Count number of contexts satisfying some predicate
///
/// # Arguments
Expand Down Expand Up @@ -861,44 +818,6 @@ pub mod tests {
assert_eq!(answer, cdi_with_internal_input_dice)
}

#[test]
fn test_validate_context_tree() {
let mut env = DpeEnv::<TestTypes> {
crypto: OpensslCrypto::new(),
platform: DefaultPlatform,
};
let mut dpe = DpeInstance::new(&mut env, SUPPORT).unwrap();

dpe.contexts[0].state = ContextState::Active;
dpe.contexts[0].children = 0b100;
dpe.contexts[1].state = ContextState::Active;
dpe.contexts[1].children = 0b100;
dpe.contexts[2].state = ContextState::Active;
// validation fails on graph where child has multiple parents
assert_eq!(dpe.validate_context_tree(0), false);

dpe.contexts[0].children = 0b10;
// validation passes on a tree in the shape of a linked-list
assert_eq!(dpe.validate_context_tree(0), true);

dpe.contexts[2].children = 0b1;
// validation fails on circle graph
assert_eq!(dpe.validate_context_tree(0), false);

dpe.contexts[0].children |= 0b100;
dpe.contexts[1].children = 0;
dpe.contexts[2].children = 0;
// validation passes on a complete binary tree of size 2
assert_eq!(dpe.validate_context_tree(0), true);

dpe.contexts[10].state = ContextState::Active;
dpe.contexts[10].children = 1 << 11 | 1 << 12;
dpe.contexts[11].state = ContextState::Active;
dpe.contexts[12].state = ContextState::Active;
// validation fails on a graph with multiple connected components
assert_eq!(dpe.validate_context_tree(0), false);
}

#[test]
fn test_new_auto_init() {
let mut env = DpeEnv::<TestTypes> {
Expand Down
1 change: 1 addition & 0 deletions dpe/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ pub mod context;
pub mod dpe_instance;
pub mod response;
pub mod support;
pub mod validation;

use core::mem::size_of;
use response::GetProfileResp;
Expand Down
6 changes: 4 additions & 2 deletions dpe/src/response.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ Abstract:
DPE reponses and serialization.
--*/
use crate::{
context::ContextHandle, CURRENT_PROFILE_MAJOR_VERSION, CURRENT_PROFILE_MINOR_VERSION,
DPE_PROFILE, MAX_CERT_SIZE, MAX_HANDLES,
context::ContextHandle, validation::ValidationError, CURRENT_PROFILE_MAJOR_VERSION,
CURRENT_PROFILE_MINOR_VERSION, DPE_PROFILE, MAX_CERT_SIZE, MAX_HANDLES,
};
use crypto::CryptoError;
use platform::PlatformError;
Expand Down Expand Up @@ -152,6 +152,7 @@ pub enum DpeErrorCode {
MaxTcis = 0x1003,
Platform(PlatformError) = 0x01000000,
Crypto(CryptoError) = 0x02000000,
Validation(ValidationError) = 0x03000000,
}

impl From<PlatformError> for DpeErrorCode {
Expand Down Expand Up @@ -181,6 +182,7 @@ impl DpeErrorCode {
match self {
DpeErrorCode::Platform(e) => self.discriminant() | e.discriminant() as u32,
DpeErrorCode::Crypto(e) => self.discriminant() | e.discriminant() as u32,
DpeErrorCode::Validation(e) => self.discriminant() | e.discriminant() as u32,
_ => self.discriminant(),
}
}
Expand Down
Loading

0 comments on commit e8f2660

Please sign in to comment.