diff --git a/error/src/lib.rs b/error/src/lib.rs
index e81e0db55e..62393a1f92 100644
--- a/error/src/lib.rs
+++ b/error/src/lib.rs
@@ -369,6 +369,8 @@ impl CaliptraError {
CaliptraError::new_const(0x000E0027);
pub const RUNTIME_LDEVID_CERT_HANDOFF_FAILED: CaliptraError =
CaliptraError::new_const(0x000E0028);
+ pub const RUNTIME_CONTEXT_TAG_VALIDATION_FAILED: CaliptraError =
+ CaliptraError::new_const(0x000E0029);
/// FMC Errors
pub const FMC_GLOBAL_NMI: CaliptraError = CaliptraError::new_const(0x000F0001);
diff --git a/runtime/README.md b/runtime/README.md
index 832764155b..5cd674a5be 100644
--- a/runtime/README.md
+++ b/runtime/README.md
@@ -557,6 +557,7 @@ this case, the new Runtime Firmware must:
Runtime Journey PCR (TYPE = RTJM, “Internal TCI” flag is set) matches the
“Latest” Runtime PCR value from PCRX
1. Ensure `SHA384_HASH(0x00..00, TCI from SRAM) == RT_FW_JOURNEY_PCR`
+1. Check that retired and inactive contexts do not have tags
1. If any validations fail, runtime firmware will execute the
`DISABLE_ATTESTATION` command.
diff --git a/runtime/doc/test-coverage.md b/runtime/doc/test-coverage.md
index 37801eda01..432a7b1410 100644
--- a/runtime/doc/test-coverage.md
+++ b/runtime/doc/test-coverage.md
@@ -100,6 +100,7 @@ Attempts to add a duplicate tag and verifies that it fails | **test_duplicate_ta
Calls the dpe_get_tagged_tci mailbox command with a tag that does not exist and checks that it fails | **test_get_tagged_tci_on_non_existent_tag** | RUNTIME_TAGGING_FAILURE
Attempts to tag an inactive context and verifies that it fails | **test_tagging_inactive_context** | RUNTIME_TAGGING_FAILURE
Tags the default context, destroys the default context, and checks that the dpe_get_tagged_tci mailbox command fails on the default context | **test_tagging_destroyed_context** | RUNTIME_TAGGING_FAILURE
+Tags the default context, retires the default context, and checks that the dpe_get_tagged_tci mailbox command fails on the default context | **test_tagging_retired_context** | RUNTIME_TAGGING_FAILURE
# **DPE Verification Tests**
@@ -147,4 +148,5 @@ Test PL1 pauser active context limits | N/A | N/A
Check that measurements are stored in DPE when StashMeasurement is called | N/A | N/A
Verify that DPE attestation flow fails after DisableAttestation is called | N/A | N/A
Check that mailbox valid pausers are measured into DPE upon RT startup | N/A | N/A
-Check that the RT alias key is different from the key signing DPE certs | N/A | N/A
\ No newline at end of file
+Check that the RT alias key is different from the key signing DPE certs | N/A | N/A
+Test context tag validity upon warm/update reset | N/A | N/A
\ No newline at end of file
diff --git a/runtime/src/drivers.rs b/runtime/src/drivers.rs
index 94967e641c..eaebb2479e 100644
--- a/runtime/src/drivers.rs
+++ b/runtime/src/drivers.rs
@@ -24,6 +24,7 @@ use caliptra_registers::{
};
use dpe::context::{Context, ContextState};
use dpe::tci::TciMeasurement;
+use dpe::MAX_HANDLES;
use dpe::{
commands::{CommandExecution, DeriveChildCmd, DeriveChildFlags},
context::ContextHandle,
@@ -94,10 +95,12 @@ impl Drivers {
}
ResetReason::UpdateReset => {
Self::validate_dpe_structure(&mut drivers)?;
+ Self::validate_context_tags(&mut drivers)?;
Self::update_dpe_rt_journey(&mut drivers)?;
}
ResetReason::WarmReset => {
Self::validate_dpe_structure(&mut drivers)?;
+ Self::validate_context_tags(&mut drivers)?;
Self::check_dpe_rt_journey_unchanged(&mut drivers)?;
}
ResetReason::Unknown => {
@@ -229,6 +232,22 @@ impl Drivers {
Ok(())
}
+ fn validate_context_tags(mut drivers: &mut Drivers) -> CaliptraResult<()> {
+ let pdata = drivers.persistent_data.get();
+ let context_has_tag = pdata.context_has_tag;
+ let context_tags = pdata.context_tags;
+ let dpe = &pdata.dpe;
+
+ for i in (0..MAX_HANDLES) {
+ if dpe.contexts[i].state != ContextState::Active
+ && (context_has_tag[i].get() || context_tags[i] != 0)
+ {
+ return Err(CaliptraError::RUNTIME_CONTEXT_TAG_VALIDATION_FAILED);
+ }
+ }
+ Ok(())
+ }
+
// Caliptra Name serialNumber fields are sha256 digests
pub fn compute_rt_alias_sn(&mut self) -> CaliptraResult {
let key = self.persistent_data.get().fht.rt_dice_pub_key.to_der();
diff --git a/runtime/src/invoke_dpe.rs b/runtime/src/invoke_dpe.rs
index d917d3b758..98aea562d6 100644
--- a/runtime/src/invoke_dpe.rs
+++ b/runtime/src/invoke_dpe.rs
@@ -79,7 +79,10 @@ impl InvokeDpeCmd {
{
return Err(CaliptraError::RUNTIME_INCORRECT_PAUSER_PRIVILEGE_LEVEL);
}
- cmd.execute(dpe, &mut env, locality)
+ let derive_child_resp = cmd.execute(dpe, &mut env, locality);
+ // clear tags for retired contexts
+ Self::clear_tags_for_non_active_contexts(dpe, context_has_tag, context_tags);
+ derive_child_resp
}
Command::CertifyKey(cmd) => {
// PL1 cannot request X509
@@ -93,16 +96,7 @@ impl InvokeDpeCmd {
Command::DestroyCtx(cmd) => {
let destroy_ctx_resp = cmd.execute(dpe, &mut env, locality);
// clear tags for destroyed contexts
- (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;
- }
- });
+ Self::clear_tags_for_non_active_contexts(dpe, context_has_tag, context_tags);
destroy_ctx_resp
}
Command::Sign(cmd) => cmd.execute(dpe, &mut env, locality),
@@ -172,4 +166,21 @@ impl InvokeDpeCmd {
fn is_caller_pl1(pl0_pauser: u32, flags: u32, locality: u32) -> bool {
flags & PL0_PAUSER_FLAG == 0 && locality != pl0_pauser
}
+
+ 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;
+ }
+ });
+ }
}
diff --git a/runtime/tests/runtime_integration_tests/test_tagging.rs b/runtime/tests/runtime_integration_tests/test_tagging.rs
index bd5e4dc0f5..6653ad974c 100644
--- a/runtime/tests/runtime_integration_tests/test_tagging.rs
+++ b/runtime/tests/runtime_integration_tests/test_tagging.rs
@@ -6,9 +6,10 @@ use caliptra_common::mailbox_api::{
};
use caliptra_hw_model::HwModel;
use dpe::{
- commands::{Command, DestroyCtxCmd},
+ commands::{Command, DeriveChildCmd, DeriveChildFlags, DestroyCtxCmd},
context::ContextHandle,
response::Response,
+ DPE_PROFILE,
};
use zerocopy::FromBytes;
@@ -206,3 +207,55 @@ fn test_tagging_destroyed_context() {
resp,
);
}
+
+#[test]
+fn test_tagging_retired_context() {
+ let mut model = run_rt_test(None, None, None);
+
+ // Tag default context
+ let mut cmd = MailboxReq::TagTci(TagTciReq {
+ hdr: MailboxReqHeader { chksum: 0 },
+ handle: DEFAULT_HANDLE,
+ 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
+ let derive_child_cmd = DeriveChildCmd {
+ handle: ContextHandle::default(),
+ data: [0u8; DPE_PROFILE.get_hash_size()],
+ flags: DeriveChildFlags::MAKE_DEFAULT,
+ 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(_)) = resp else {
+ panic!("Wrong response type!");
+ };
+
+ // check that we cannot get tagged tci for a retired context
+ let mut cmd = MailboxReq::GetTaggedTci(GetTaggedTciReq {
+ hdr: MailboxReqHeader { chksum: 0 },
+ tag: TAG,
+ });
+ cmd.populate_chksum().unwrap();
+ let resp = model
+ .mailbox_execute(
+ u32::from(CommandId::DPE_GET_TAGGED_TCI),
+ cmd.as_bytes().unwrap(),
+ )
+ .unwrap_err();
+ assert_error(
+ &mut model,
+ caliptra_drivers::CaliptraError::RUNTIME_TAGGING_FAILURE,
+ resp,
+ );
+}