Skip to content

Commit

Permalink
Auto-initialize DPE with the RT Journey PCR
Browse files Browse the repository at this point in the history
  • Loading branch information
sree-revoori1 committed Dec 8, 2023
1 parent dd36931 commit 5dee000
Show file tree
Hide file tree
Showing 9 changed files with 220 additions and 72 deletions.
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::Active
{
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
23 changes: 21 additions & 2 deletions runtime/test-fw/src/mbox_responder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,17 @@
#![no_std]

use caliptra_common::mailbox_api::CommandId;
use caliptra_drivers::{pcr_log::RT_FW_JOURNEY_PCR, Array4x12};
use caliptra_registers::mbox::enums::MboxStatusE;
use caliptra_drivers::{
pcr_log::{PCR_ID_STASH_MEASUREMENT, RT_FW_JOURNEY_PCR},
Array4x12,
};
use caliptra_registers::{mbox::enums::MboxStatusE, soc_ifc::SocIfcReg};
use caliptra_runtime::{ContextState, Drivers};
use caliptra_test_harness::{runtime_handlers, test_suite};
use zerocopy::AsBytes;

const FW_LOAD_CMD_OPCODE: u32 = CommandId::FIRMWARE_LOAD.0;

fn mbox_responder() {
let mut drivers = unsafe { Drivers::new_from_registers().unwrap() };
assert!(drivers.persistent_data.get().fht.is_valid());
Expand Down Expand Up @@ -66,6 +71,20 @@ fn mbox_responder() {
.unwrap();
mbox.set_status(MboxStatusE::DataReady);
}
// Read PCR_ID_STASH_MEASUREMENT
CommandId(0x5000_0000) => {
let stash_measurement_pcr: [u8; 48] =
drivers.pcr_bank.read_pcr(PCR_ID_STASH_MEASUREMENT).into();
mbox.write_response(&stash_measurement_pcr).unwrap();
mbox.set_status(MboxStatusE::DataReady);
}
// Update reset
CommandId(FW_LOAD_CMD_OPCODE) => {
unsafe { SocIfcReg::new() }
.regs_mut()
.internal_fw_update_reset()
.write(|w| w.core_rst(true));
}
_ => {
mbox.set_status(MboxStatusE::CmdFailure);
}
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
116 changes: 115 additions & 1 deletion runtime/tests/runtime_integration_tests/test_stash_measurement.rs
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, APP_WITH_UART, 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,113 @@ 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);
}

#[test]
fn test_pcr31_extended_upon_stash_measurement() {
let mut model = run_rt_test(Some(&firmware::runtime_tests::MBOX), None, None);

// Read PCR_ID_STASH_MEASUREMENT
let pcr_31_resp = model.mailbox_execute(0x5000_0000, &[]).unwrap().unwrap();
let pcr_31: [u8; 48] = pcr_31_resp.as_bytes().try_into().unwrap();

// update reset to the real runtime image
let updated_fw_image = caliptra_builder::build_and_sign_image(
&FMC_WITH_UART,
&APP_WITH_UART,
ImageOptions::default(),
)
.unwrap()
.to_bytes()
.unwrap();
model
.mailbox_execute(u32::from(CommandId::FIRMWARE_LOAD), &updated_fw_image)
.unwrap();

// stash a measurement
let measurement = [2u8; 48];
let mut cmd = MailboxReq::StashMeasurement(StashMeasurementReq {
hdr: MailboxReqHeader { chksum: 0 },
metadata: [0u8; 4],
measurement,
context: [0u8; 48],
svn: 0,
});
cmd.populate_chksum().unwrap();

let _ = model
.mailbox_execute(
u32::from(CommandId::STASH_MEASUREMENT),
cmd.as_bytes().unwrap(),
)
.unwrap()
.expect("We should have received a response");

// update reset back to 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();
model
.mailbox_execute(u32::from(CommandId::FIRMWARE_LOAD), &updated_fw_image)
.unwrap();

let updated_fw_image = caliptra_builder::build_and_sign_image(
&FMC_WITH_UART,
&firmware::runtime_tests::MBOX,
ImageOptions::default(),
)
.unwrap()
.to_bytes()
.unwrap();
model
.mailbox_execute(u32::from(CommandId::FIRMWARE_LOAD), &updated_fw_image)
.unwrap();

// Read extended PCR_ID_STASH_MEASUREMENT
let extended_pcr_31_resp = model.mailbox_execute(0x5000_0000, &[]).unwrap().unwrap();
let extended_pcr_31: [u8; 48] = extended_pcr_31_resp.as_bytes().try_into().unwrap();

let measurements_to_be_hashed = [[0u8; 48], measurement].concat();
let expected_pcr_31 = model
.mailbox_execute(0x4000_0000, &measurements_to_be_hashed.as_bytes())
.unwrap()
.unwrap();

assert_eq!(expected_pcr_31, extended_pcr_31);
}
Loading

0 comments on commit 5dee000

Please sign in to comment.