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

Auto-initialize DPE with the RT Journey PCR #1167

Merged
merged 2 commits into from
Dec 8, 2023
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
2 changes: 1 addition & 1 deletion dpe
Submodule dpe updated 1 files
+58 −0 dpe/src/dpe_instance.rs
44 changes: 10 additions & 34 deletions runtime/src/drivers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,14 @@ use caliptra_registers::{
};
use dpe::context::{Context, ContextState};
use dpe::tci::TciMeasurement;
use dpe::MAX_HANDLES;
use dpe::{
commands::{CommandExecution, DeriveChildCmd, DeriveChildFlags},
context::ContextHandle,
dpe_instance::{DpeEnv, DpeInstance, DpeTypes},
support::Support,
DPE_PROFILE,
};
use dpe::{U8Bool, MAX_HANDLES};

use crypto::{AlgLen, Crypto, CryptoBuf, Hasher};
use zerocopy::AsBytes;
Expand Down Expand Up @@ -240,7 +240,7 @@ impl Drivers {
let dpe = &pdata.dpe;

for i in (0..MAX_HANDLES) {
if dpe.contexts[i].state != ContextState::Active
if dpe.contexts[i].state == ContextState::Inactive
&& (context_has_tag[i].get() || context_tags[i] != 0)
{
return Err(CaliptraError::RUNTIME_CONTEXT_TAG_VALIDATION_FAILED);
Expand Down Expand Up @@ -295,24 +295,17 @@ impl Drivers {
&mut drivers.cert_chain,
),
};
let mut dpe = DpeInstance::new(&mut env, DPE_SUPPORT)
.map_err(|_| CaliptraError::RUNTIME_INITIALIZE_DPE_FAILED)?;

let data = <[u8; DPE_PROFILE.get_hash_size()]>::from(
// Initialize DPE with the RT journey PCR
let rt_journey_measurement = <[u8; DPE_PROFILE.get_hash_size()]>::from(
&drivers.pcr_bank.read_pcr(RT_FW_JOURNEY_PCR),
);
// Call DeriveChild to create root context.
DeriveChildCmd {
handle: ContextHandle::default(),
data,
flags: DeriveChildFlags::MAKE_DEFAULT
| DeriveChildFlags::CHANGE_LOCALITY
| DeriveChildFlags::INPUT_ALLOW_CA
| DeriveChildFlags::INPUT_ALLOW_X509,
tci_type: u32::from_be_bytes(*b"RTJM"),
target_locality: caliptra_locality,
}
.execute(&mut dpe, &mut env, caliptra_locality)
let mut dpe = DpeInstance::new_auto_init(
&mut env,
DPE_SUPPORT,
u32::from_be_bytes(*b"RTJM"),
rt_journey_measurement,
)
.map_err(|_| CaliptraError::RUNTIME_INITIALIZE_DPE_FAILED)?;

// Call DeriveChild to create a measurement for the mailbox valid pausers and change locality to the pl0 pauser locality
Expand Down Expand Up @@ -460,21 +453,4 @@ impl Drivers {
pub fn is_caller_pl1(pl0_pauser: u32, flags: u32, locality: u32) -> bool {
flags & PL0_PAUSER_FLAG == 0 && locality != pl0_pauser
}

pub fn clear_tags_for_non_active_contexts(
dpe: &mut DpeInstance,
context_has_tag: &mut [U8Bool; MAX_HANDLES],
context_tags: &mut [u32; MAX_HANDLES],
) {
(0..MAX_HANDLES).for_each(|i| {
if i < dpe.contexts.len()
&& i < context_has_tag.len()
&& i < context_tags.len()
&& dpe.contexts[i].state != ContextState::Active
{
context_has_tag[i] = U8Bool::new(false);
context_tags[i] = 0;
}
});
}
}
26 changes: 20 additions & 6 deletions runtime/src/invoke_dpe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use dpe::{
},
context::{Context, ContextState},
response::{Response, ResponseHdr},
DpeInstance, MAX_HANDLES,
DpeInstance, U8Bool, MAX_HANDLES,
};
use zerocopy::{AsBytes, FromBytes};

Expand Down Expand Up @@ -78,10 +78,7 @@ impl InvokeDpeCmd {
{
return Err(CaliptraError::RUNTIME_INCORRECT_PAUSER_PRIVILEGE_LEVEL);
}
let derive_child_resp = cmd.execute(dpe, &mut env, locality);
// clear tags for retired contexts
Drivers::clear_tags_for_non_active_contexts(dpe, context_has_tag, context_tags);
derive_child_resp
cmd.execute(dpe, &mut env, locality)
}
Command::CertifyKey(cmd) => {
// PL1 cannot request X509
Expand All @@ -95,7 +92,7 @@ impl InvokeDpeCmd {
Command::DestroyCtx(cmd) => {
let destroy_ctx_resp = cmd.execute(dpe, &mut env, locality);
// clear tags for destroyed contexts
Drivers::clear_tags_for_non_active_contexts(dpe, context_has_tag, context_tags);
Self::clear_tags_for_inactive_contexts(dpe, context_has_tag, context_tags);
destroy_ctx_resp
}
Command::Sign(cmd) => cmd.execute(dpe, &mut env, locality),
Expand Down Expand Up @@ -131,4 +128,21 @@ impl InvokeDpeCmd {
Err(CaliptraError::RUNTIME_INSUFFICIENT_MEMORY)
}
}

pub fn clear_tags_for_inactive_contexts(
dpe: &mut DpeInstance,
context_has_tag: &mut [U8Bool; MAX_HANDLES],
context_tags: &mut [u32; MAX_HANDLES],
) {
(0..MAX_HANDLES).for_each(|i| {
if i < dpe.contexts.len()
&& i < context_has_tag.len()
&& i < context_tags.len()
&& dpe.contexts[i].state == ContextState::Inactive
{
context_has_tag[i] = U8Bool::new(false);
context_tags[i] = 0;
}
});
}
}
6 changes: 0 additions & 6 deletions runtime/src/stash_measurement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,6 @@ impl StashMeasurementCmd {
target_locality: locality,
}
.execute(&mut pdata_mut.dpe, &mut env, locality);
// clear tags for retired contexts
Drivers::clear_tags_for_non_active_contexts(
&mut pdata_mut.dpe,
&mut pdata_mut.context_has_tag,
&mut pdata_mut.context_tags,
);

match derive_child_resp {
Ok(_) => DpeErrorCode::NoError,
Expand Down
5 changes: 2 additions & 3 deletions runtime/tests/runtime_integration_tests/test_boot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ fn test_boot_tci_data() {
let valid_pauser_hash: [u8; 48] = valid_pauser_hash_resp.as_bytes().try_into().unwrap();

// hash expected DPE measurements in order
let measurements_to_be_hashed = [[0u8; 48], rt_journey_pcr, valid_pauser_hash].concat();
let measurements_to_be_hashed = [rt_journey_pcr, valid_pauser_hash].concat();
let expected_measurement_hash = model
.mailbox_execute(0x4000_0000, measurements_to_be_hashed.as_bytes())
.unwrap()
Expand Down Expand Up @@ -151,8 +151,7 @@ fn test_measurement_in_measurement_log_added_to_dpe() {
let valid_pauser_hash: [u8; 48] = valid_pauser_hash_resp.as_bytes().try_into().unwrap();

// hash expected DPE measurements in order
let measurements_to_be_hashed =
[[0u8; 48], rt_journey_pcr, valid_pauser_hash, measurement].concat();
let measurements_to_be_hashed = [rt_journey_pcr, valid_pauser_hash, measurement].concat();
let expected_measurement_hash = model
.mailbox_execute(0x4000_0000, measurements_to_be_hashed.as_bytes())
.unwrap()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,11 +112,11 @@ fn test_pl1_derive_child_dpe_context_thresholds() {
let mut handle = init_ctx_resp.handle;

// Call DeriveChild with PL1 enough times to breach the threshold on the last iteration.
// Note that this loop runs exactly PL1_DPE_ACTIVE_CONTEXT_THRESHOLD - 2 times. When we initialize
// DPE, we measure the RT journey PCR and the mailbox valid pausers in Caliptra's locality: 0xFFFFFFFF,
// which counts as a PL1 locality. Then, we initialize a simulation context in locality 1. Thus, we can call derive child
// from PL1 exactly 16 - 3 = 13 times, and the last iteration of this loop, is expected to throw a threshold breached error.
let num_iterations = InvokeDpeCmd::PL1_DPE_ACTIVE_CONTEXT_THRESHOLD - 2;
// Note that this loop runs exactly PL1_DPE_ACTIVE_CONTEXT_THRESHOLD - 1 times. When we initialize
// DPE, we measure the RT journey PCR in Caliptra's locality: 0xFFFFFFFF, which counts as a PL1 locality.
// Then, we initialize a simulation context in locality 1. Thus, we can call derive child
// from PL1 exactly 16 - 2 = 14 times, and the last iteration of this loop, is expected to throw a threshold breached error.
let num_iterations = InvokeDpeCmd::PL1_DPE_ACTIVE_CONTEXT_THRESHOLD - 1;
for i in 0..num_iterations {
let derive_child_cmd = DeriveChildCmd {
handle,
Expand Down Expand Up @@ -194,12 +194,13 @@ fn test_pl1_init_ctx_dpe_context_thresholds() {
m.soc_ifc().cptra_boot_status().read() == u32::from(RtBootStatus::RtReadyForCommands)
});

let num_iterations = InvokeDpeCmd::PL1_DPE_ACTIVE_CONTEXT_THRESHOLD - 1;
let num_iterations = InvokeDpeCmd::PL1_DPE_ACTIVE_CONTEXT_THRESHOLD;
for i in 0..num_iterations {
let init_ctx_cmd = InitCtxCmd::new_simulation();

// InitCtx should fail on the PL1_DPE_ACTIVE_CONTEXT_THRESHOLD - 2nd iteration since
// RT initialization creates two contexts in Caliptra's locality, which is PL1.
// InitCtx should fail on the PL1_DPE_ACTIVE_CONTEXT_THRESHOLD - 1st iteration since
// RT initialization creates the RT journey measurement context in Caliptra's locality,
// which is PL1.
if i == num_iterations - 1 {
let resp = execute_dpe_cmd(
&mut model,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
// Licensed under the Apache-2.0 license

use caliptra_builder::{
firmware::{self, FMC_WITH_UART},
ImageOptions,
};
use caliptra_common::mailbox_api::{
CommandId, MailboxReq, MailboxReqHeader, StashMeasurementReq, StashMeasurementResp,
};
Expand All @@ -17,10 +21,11 @@ fn test_stash_measurement() {
m.soc_ifc().cptra_boot_status().read() == u32::from(RtBootStatus::RtReadyForCommands)
});

let measurement = [1u8; 48];
let mut cmd = MailboxReq::StashMeasurement(StashMeasurementReq {
hdr: MailboxReqHeader { chksum: 0 },
metadata: [0u8; 4],
measurement: [0u8; 48],
measurement,
context: [0u8; 48],
svn: 0,
});
Expand All @@ -40,4 +45,35 @@ fn test_stash_measurement() {
.into_ref();

assert_eq!(resp_hdr.dpe_result, 0);

// create a new fw image with the runtime replaced by the mbox responder
let updated_fw_image = caliptra_builder::build_and_sign_image(
&FMC_WITH_UART,
&firmware::runtime_tests::MBOX,
ImageOptions::default(),
)
.unwrap()
.to_bytes()
.unwrap();

// trigger an update reset so we can use commands in mbox responder
model
.mailbox_execute(u32::from(CommandId::FIRMWARE_LOAD), &updated_fw_image)
.unwrap();

let rt_journey_pcr_resp = model.mailbox_execute(0x1000_0000, &[]).unwrap().unwrap();
let rt_journey_pcr: [u8; 48] = rt_journey_pcr_resp.as_bytes().try_into().unwrap();

let valid_pauser_hash_resp = model.mailbox_execute(0x2000_0000, &[]).unwrap().unwrap();
let valid_pauser_hash: [u8; 48] = valid_pauser_hash_resp.as_bytes().try_into().unwrap();

// hash expected DPE measurements in order to check that stashed measurement was added to DPE
let measurements_to_be_hashed = [rt_journey_pcr, valid_pauser_hash, measurement].concat();
let expected_measurement_hash = model
.mailbox_execute(0x4000_0000, measurements_to_be_hashed.as_bytes())
.unwrap()
.unwrap();

let dpe_measurement_hash = model.mailbox_execute(0x3000_0000, &[]).unwrap().unwrap();
assert_eq!(expected_measurement_hash, dpe_measurement_hash);
}
53 changes: 42 additions & 11 deletions runtime/tests/runtime_integration_tests/test_tagging.rs
Original file line number Diff line number Diff line change
Expand Up @@ -212,23 +212,57 @@ fn test_tagging_destroyed_context() {
fn test_tagging_retired_context() {
let mut model = run_rt_test(None, None, None);

// Tag default context
// retire context via DeriveChild
let derive_child_cmd = DeriveChildCmd {
handle: ContextHandle::default(),
data: [0u8; DPE_PROFILE.get_hash_size()],
flags: DeriveChildFlags::empty(),
tci_type: 0,
target_locality: 0,
};
let resp = execute_dpe_cmd(
&mut model,
&mut Command::DeriveChild(derive_child_cmd),
DpeResult::Success,
);
let Some(Response::DeriveChild(derive_child_resp)) = resp else {
panic!("Wrong response type!");
};
let new_handle = derive_child_resp.handle;

// check that we cannot tag retired context
let mut cmd = MailboxReq::TagTci(TagTciReq {
hdr: MailboxReqHeader { chksum: 0 },
handle: DEFAULT_HANDLE,
tag: TAG,
});
cmd.populate_chksum().unwrap();
let resp = model
.mailbox_execute(u32::from(CommandId::DPE_TAG_TCI), cmd.as_bytes().unwrap())
.unwrap_err();
assert_error(
&mut model,
caliptra_drivers::CaliptraError::RUNTIME_TAGGING_FAILURE,
resp,
);

// tag new context
let mut cmd = MailboxReq::TagTci(TagTciReq {
hdr: MailboxReqHeader { chksum: 0 },
handle: new_handle.0,
tag: TAG,
});
cmd.populate_chksum().unwrap();
let _ = model
.mailbox_execute(u32::from(CommandId::DPE_TAG_TCI), cmd.as_bytes().unwrap())
.unwrap()
.expect("We expected a response");

// retire tagged context via DeriveChild
// retire tagged context via derive child
let derive_child_cmd = DeriveChildCmd {
handle: ContextHandle::default(),
handle: new_handle,
data: [0u8; DPE_PROFILE.get_hash_size()],
flags: DeriveChildFlags::MAKE_DEFAULT,
flags: DeriveChildFlags::empty(),
tci_type: 0,
target_locality: 0,
};
Expand All @@ -241,7 +275,7 @@ fn test_tagging_retired_context() {
panic!("Wrong response type!");
};

// check that we cannot get tagged tci for a retired context
// check that we can get tagged tci for a retired context
let mut cmd = MailboxReq::GetTaggedTci(GetTaggedTciReq {
hdr: MailboxReqHeader { chksum: 0 },
tag: TAG,
Expand All @@ -252,10 +286,7 @@ fn test_tagging_retired_context() {
u32::from(CommandId::DPE_GET_TAGGED_TCI),
cmd.as_bytes().unwrap(),
)
.unwrap_err();
assert_error(
&mut model,
caliptra_drivers::CaliptraError::RUNTIME_TAGGING_FAILURE,
resp,
);
.unwrap()
.expect("We expected a response");
let _ = GetTaggedTciResp::read_from(resp.as_slice()).unwrap();
}
Loading