Repository: chipsalliance/caliptra-sw
Pull request #1101 (2023-11-21 21:33:36)

Implement GetTaggedTci and TagTCI mailbox commands in RT


Patch:
diff --git a/api/src/mailbox.rs b/api/src/mailbox.rs
index 11177c46..67645b4a 100644
--- a/api/src/mailbox.rs
+++ b/api/src/mailbox.rs
@@ -18,6 +18,8 @@ impl CommandId {
     pub const INVOKE_DPE: Self = Self(0x44504543); // "DPEC"
     pub const DISABLE_ATTESTATION: Self = Self(0x4453424C); // "DSBL"
     pub const FW_INFO: Self = Self(0x494E464F); // "INFO"
+    pub const DPE_TAG_TCI: Self = Self(0x54514754); // "TAGT"
+    pub const DPE_GET_TAGGED_TCI: Self = Self(0x47544744); // "GTGD"
 
     // TODO: Remove this and merge with GET_LDEV_CERT once that is implemented
     pub const TEST_ONLY_GET_LDEV_CERT: Self = Self(0x4345524c); // "CERL"
@@ -132,6 +134,7 @@ pub enum MailboxResp {
     FipsVersion(FipsVersionResp),
     FwInfo(FwInfoResp),
     Capabilities(CapabilitiesResp),
+    GetTaggedTci(GetTaggedTciResp),
 }
 
 impl MailboxResp {
@@ -148,6 +151,7 @@ impl MailboxResp {
             MailboxResp::FipsVersion(resp) => Ok(resp.as_bytes()),
             MailboxResp::FwInfo(resp) => Ok(resp.as_bytes()),
             MailboxResp::Capabilities(resp) => Ok(resp.as_bytes()),
+            MailboxResp::GetTaggedTci(resp) => Ok(resp.as_bytes()),
         }
     }
 
@@ -164,6 +168,7 @@ impl MailboxResp {
             MailboxResp::FipsVersion(resp) => Ok(resp.as_bytes_mut()),
             MailboxResp::FwInfo(resp) => Ok(resp.as_bytes_mut()),
             MailboxResp::Capabilities(resp) => Ok(resp.as_bytes_mut()),
+            MailboxResp::GetTaggedTci(resp) => Ok(resp.as_bytes_mut()),
         }
     }
 
@@ -213,6 +218,8 @@ pub enum MailboxReq {
     FwInfo(MailboxReqHeader),
     PopulateIdevCert(PopulateIdevCertReq),
     GetIdevCert(GetIdevCertReq),
+    TagTci(TagTciReq),
+    GetTaggedTci(GetTaggedTciReq),
 
     #[cfg(feature = "test_only_commands")]
     TestHmacVerify(HmacVerifyReq),
@@ -232,6 +239,8 @@ impl MailboxReq {
             MailboxReq::GetLdevCert(req) => Ok(req.as_bytes()),
             MailboxReq::PopulateIdevCert(req) => req.as_bytes_partial(),
             MailboxReq::GetIdevCert(req) => req.as_bytes_partial(),
+            MailboxReq::TagTci(req) => Ok(req.as_bytes()),
+            MailboxReq::GetTaggedTci(req) => Ok(req.as_bytes()),
 
             #[cfg(feature = "test_only_commands")]
             MailboxReq::TestGetFmcAliasCert(req) => Ok(req.as_bytes()),
@@ -251,6 +260,8 @@ impl MailboxReq {
             MailboxReq::FwInfo(req) => Ok(req.as_bytes_mut()),
             MailboxReq::PopulateIdevCert(req) => req.as_bytes_partial_mut(),
             MailboxReq::GetIdevCert(req) => req.as_bytes_partial_mut(),
+            MailboxReq::TagTci(req) => Ok(req.as_bytes_mut()),
+            MailboxReq::GetTaggedTci(req) => Ok(req.as_bytes_mut()),
 
             #[cfg(feature = "test_only_commands")]
             MailboxReq::TestHmacVerify(req) => Ok(req.as_bytes_mut()),
@@ -270,6 +281,8 @@ impl MailboxReq {
             MailboxReq::FwInfo(_) => CommandId::FW_INFO,
             MailboxReq::PopulateIdevCert(_) => CommandId::POPULATE_IDEV_CERT,
             MailboxReq::GetIdevCert(_) => CommandId::GET_IDEV_CERT,
+            MailboxReq::TagTci(_) => CommandId::DPE_TAG_TCI,
+            MailboxReq::GetTaggedTci(_) => CommandId::DPE_GET_TAGGED_TCI,
 
             #[cfg(feature = "test_only_commands")]
             MailboxReq::TestHmacVerify(_) => CommandId::TEST_ONLY_HMAC384_VERIFY,
@@ -706,6 +719,31 @@ impl Default for PopulateIdevCertReq {
     }
 }
 
+// DPE_TAG_TCI
+// No command-specific output args
+#[repr(C)]
+#[derive(Debug, AsBytes, FromBytes, PartialEq, Eq)]
+pub struct TagTciReq {
+    pub hdr: MailboxReqHeader,
+    pub handle: [u8; 16],
+    pub tag: u32,
+}
+
+// DPE_GET_TAGGED_TCI
+#[repr(C)]
+#[derive(Debug, AsBytes, FromBytes, PartialEq, Eq)]
+pub struct GetTaggedTciReq {
+    pub hdr: MailboxReqHeader,
+    pub tag: u32,
+}
+#[repr(C)]
+#[derive(Debug, AsBytes, FromBytes, PartialEq, Eq)]
+pub struct GetTaggedTciResp {
+    pub hdr: MailboxRespHeader,
+    pub tci_cumulative: [u8; 48],
+    pub tci_current: [u8; 48],
+}
+
 #[cfg(test)]
 mod tests {
     use super::*;
diff --git a/drivers/src/persistent.rs b/drivers/src/persistent.rs
index 27928c9c..fb18c641 100644
--- a/drivers/src/persistent.rs
+++ b/drivers/src/persistent.rs
@@ -4,7 +4,7 @@ use core::{marker::PhantomData, mem::size_of, ptr::addr_of};
 
 use caliptra_image_types::ImageManifest;
 #[cfg(feature = "runtime")]
-use dpe::DpeInstance;
+use dpe::{DpeInstance, U8Bool, MAX_HANDLES};
 use zerocopy::{AsBytes, FromBytes};
 use zeroize::Zeroize;
 
@@ -54,7 +54,14 @@ pub struct PersistentData {
     #[cfg(feature = "runtime")]
     pub dpe: DpeInstance,
     #[cfg(feature = "runtime")]
-    reserved6: [u8; memory_layout::DPE_SIZE as usize - size_of::<DpeInstance>()],
+    pub context_tags: [u32; MAX_HANDLES],
+    #[cfg(feature = "runtime")]
+    pub context_has_tag: [U8Bool; MAX_HANDLES],
+    #[cfg(feature = "runtime")]
+    reserved6: [u8; memory_layout::DPE_SIZE as usize
+        - size_of::<DpeInstance>()
+        - size_of::<u32>() * MAX_HANDLES
+        - size_of::<U8Bool>() * MAX_HANDLES],
     #[cfg(not(feature = "runtime"))]
     dpe: [u8; memory_layout::DPE_SIZE as usize],
 }
diff --git a/error/src/lib.rs b/error/src/lib.rs
index 3e415896..40f1f11c 100644
--- a/error/src/lib.rs
+++ b/error/src/lib.rs
@@ -358,12 +358,15 @@ impl CaliptraError {
         CaliptraError::new_const(0x000E0021);
     pub const RUNTIME_ADD_ROM_MEASUREMENTS_TO_DPE_FAILED: CaliptraError =
         CaliptraError::new_const(0x000E0022);
+    pub const RUNTIME_TAGGING_FAILURE: CaliptraError = CaliptraError::new_const(0x000E0022);
+    pub const RUNTIME_DUPLICATE_TAG: CaliptraError = CaliptraError::new_const(0x000E0023);
+    pub const RUNTIME_CONTEXT_ALREADY_TAGGED: CaliptraError = CaliptraError::new_const(0x000E0024);
     pub const RUNTIME_ADD_VALID_PAUSER_MEASUREMENT_TO_DPE_FAILED: CaliptraError =
-        CaliptraError::new_const(0x000E0023);
+        CaliptraError::new_const(0x000E0025);
     pub const RUNTIME_MAILBOX_API_RESPONSE_DATA_LEN_TOO_LARGE: CaliptraError =
-        CaliptraError::new_const(0x000E0024);
+        CaliptraError::new_const(0x000E0026);
     pub const RUNTIME_MAILBOX_API_REQUEST_DATA_LEN_TOO_LARGE: CaliptraError =
-        CaliptraError::new_const(0x000E0025);
+        CaliptraError::new_const(0x000E0027);
 
     /// FMC Errors
     pub const FMC_GLOBAL_NMI: CaliptraError = CaliptraError::new_const(0x000F0001);
diff --git a/runtime/README.md b/runtime/README.md
index 2a4e26f1..cfe7d7bf 100644
--- a/runtime/README.md
+++ b/runtime/README.md
@@ -426,6 +426,48 @@ Table: `INCREMENT_PCR_RESET_COUNTER` input arguments
 
 `INCREMENT_PCR_RESET_COUNTER` returns no output arguments.
 
+### DPE\_TAG\_TCI
+
+Associates a unique tag with a DPE context
+
+Command Code: `0x5451_4754` ("TAGT")
+
+Table: `DPE_TAG_TCI` input arguments
+
+| **Name**     | **Type**      | **Description**
+| --------     | --------      | ---------------
+| chksum       | u32           | Checksum over other input arguments, computed by the caller. Little endian.
+| handle       | u8[16]        | DPE context handle 
+| tag          | u32           | A unique tag which the handle will be associated with 
+
+Table: `DPE_TAG_TCI` output arguments
+
+| **Name**    | **Type** | **Description**
+| --------    | -------- | ---------------
+| chksum      | u32      | Checksum over other output arguments, computed by Caliptra. Little endian.
+| fips_status | u32      | Indicates if the command is FIPS approved or an error
+
+### DPE\_GET\_TAGGED\_TCI
+
+Retrieves the TCI measurements corresponding to the tagged DPE context 
+
+Command Code: `0x4754_4744` ("GTGD")
+
+Table: `DPE_GET_TAGGED_TCI` input arguments
+
+| **Name**     | **Type**      | **Description**
+| --------     | --------      | ---------------
+| chksum       | u32           | Checksum over other input arguments, computed by the caller. Little endian.
+| tag          | u32           | A unique tag corresponding to a DPE context
+
+Table: `DPE_GET_TAGGED_TCI` output arguments
+
+| **Name**          | **Type**       | **Description**
+| --------          | --------       | ---------------
+| chksum            | u32            | Checksum over other input arguments, computed by the caller. Little endian.
+| tci_cumulative    | u8[48]         | Hash of all the input data provided to the context
+| tci_current       | u8[48]         | Most recent measurement made into the context
+
 ## Checksum
 
 For every command, the request and response feature a checksum. This mitigates
@@ -550,7 +592,6 @@ details specific requirements for the Caliptra DPE implementation.
 | Simulation Context Support | Yes                            | Whether Caliptra implements the optional Simulation Contexts feature
 | Supports ExtendTci         | Yes                            | Whether Caliptra implements the optional ExtendTci command
 | Supports Auto Init         | Yes                            | Whether Caliptra will automatically initialize the default DPE context.
-| Supports Tagging           | Yes                            | Whether Caliptra implements the optional TCI tagging feature.
 | Supports Rotate Context    | Yes                            | Whether Caliptra supports the optional RotateContextHandle command.
 | CertifyKey Alias Key       | Caliptra Runtime Alias Key     | The key that will be used to sign certificates produced by the DPE CertifyKey command.
 
@@ -569,12 +610,10 @@ Caliptra DPE supports the following commands
 * DestroyContext
 * GetCertificateChain
 
-In addition, Caliptra supports the following profile-defined commands:
+In addition, Caliptra supports the following profile-defined command:
 
 * ExtendTci: Extend a TCI measurement made by DeriveChild to provide additional
              measurement data.
-* TagTci: Associate a TCI node with a unique tag
-* GetTaggedTci: Look up the measurements in a TCI node by tag
 
 ### DPE State Atomicity
 
@@ -639,11 +678,10 @@ To derive an asymmetric key for Sign and CertifyKey, RT will
 | Byte Offset | Bits  | Name           | Description
 | ----------- | ----- | ------------   | -------------
 | 0x02        | 15:0  | Parent Index   | Index of the TCI node that is the parent of this node. 0xFF if this node is the root.
-| 0x04        | 31:0  | Node Tag       | Tag of this node provided by the TagTci command.
-| 0x08        | 159:0 | Context Handle | DPE context handle referring to this node
-| 0x1C        | 31    | Internal TCI   | This TCI was measured by Runtime Firmware itself
+| 0x04        | 159:0 | Context Handle | DPE context handle referring to this node
+| 0x18        | 31    | Internal TCI   | This TCI was measured by Runtime Firmware itself
 |             | 30:0  | Reserved       | Reserved flag bits
-| 0x20        | 383:0 | Latest TCI     | The latest `INPUT_DATA` extended into this TCI by ExtendTci or DeriveChild
+| 0x1C        | 383:0 | Latest TCI     | The latest `INPUT_DATA` extended into this TCI by ExtendTci or DeriveChild
 
 Table: `TCI_NODE_DATA` for `DPE_PROFILE_IROT_P384_SHA384`
 
diff --git a/runtime/src/dpe_crypto.rs b/runtime/src/dpe_crypto.rs
index ad6ff121..6e2d2326 100644
--- a/runtime/src/dpe_crypto.rs
+++ b/runtime/src/dpe_crypto.rs
@@ -323,6 +323,7 @@ impl<'a> Crypto for DpeCrypto<'a> {
                         Hmac384Tag::Array4x12(&mut tag),
                     )
                     .map_err(|e| CryptoError::CryptoLibError(u32::from(e)))?;
+                hmac_key.zeroize();
                 HmacSig::new(tag.as_bytes())
             }
         }
diff --git a/runtime/src/invoke_dpe.rs b/runtime/src/invoke_dpe.rs
index d65db3e1..d917d3b7 100644
--- a/runtime/src/invoke_dpe.rs
+++ b/runtime/src/invoke_dpe.rs
@@ -10,7 +10,7 @@ use dpe::{
     },
     context::{Context, ContextState},
     response::{Response, ResponseHdr},
-    DpeInstance,
+    DpeInstance, U8Bool, MAX_HANDLES,
 };
 use zerocopy::{AsBytes, FromBytes};
 
@@ -52,7 +52,10 @@ impl InvokeDpeCmd {
                 .map_err(|_| CaliptraError::RUNTIME_INVOKE_DPE_FAILED)?;
             let flags = pdata.manifest1.header.flags;
 
-            let mut dpe = &mut drivers.persistent_data.get_mut().dpe;
+            let pdata_mut = drivers.persistent_data.get_mut();
+            let mut dpe = &mut pdata_mut.dpe;
+            let mut context_has_tag = &mut pdata_mut.context_has_tag;
+            let mut context_tags = &mut pdata_mut.context_tags;
             let resp = match command {
                 Command::GetProfile => Ok(Response::GetProfile(
                     dpe.get_profile(&mut env.platform)
@@ -87,9 +90,23 @@ impl InvokeDpeCmd {
                     }
                     cmd.execute(dpe, &mut env, locality)
                 }
+                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;
+                        }
+                    });
+                    destroy_ctx_resp
+                }
                 Command::Sign(cmd) => cmd.execute(dpe, &mut env, locality),
                 Command::RotateCtx(cmd) => cmd.execute(dpe, &mut env, locality),
-                Command::DestroyCtx(cmd) => cmd.execute(dpe, &mut env, locality),
                 Command::ExtendTci(cmd) => cmd.execute(dpe, &mut env, locality),
                 Command::GetCertificateChain(cmd) => cmd.execute(dpe, &mut env, locality),
             };
diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs
index e0f621df..1128f6e5 100644
--- a/runtime/src/lib.rs
+++ b/runtime/src/lib.rs
@@ -36,6 +36,8 @@ pub use verify::EcdsaVerifyCmd;
 pub mod packet;
 use caliptra_common::mailbox_api::{CommandId, MailboxResp};
 use packet::Packet;
+pub mod tagging;
+use tagging::{GetTaggedTciCmd, TagTciCmd};
 
 use caliptra_common::cprintln;
 
@@ -137,6 +139,8 @@ fn handle_command(drivers: &mut Drivers) -> CaliptraResult<MboxStatusE> {
         CommandId::STASH_MEASUREMENT => StashMeasurementCmd::execute(drivers, cmd_bytes),
         CommandId::DISABLE_ATTESTATION => DisableAttestationCmd::execute(drivers),
         CommandId::FW_INFO => FwInfoCmd::execute(drivers),
+        CommandId::DPE_TAG_TCI => TagTciCmd::execute(drivers, cmd_bytes),
+        CommandId::DPE_GET_TAGGED_TCI => GetTaggedTciCmd::execute(drivers, cmd_bytes),
         CommandId::POPULATE_IDEV_CERT => PopulateIDevIdCertCmd::execute(drivers, cmd_bytes),
         #[cfg(feature = "test_only_commands")]
         CommandId::TEST_ONLY_GET_LDEV_CERT => GetLdevCertCmd::execute(drivers),
diff --git a/runtime/src/tagging.rs b/runtime/src/tagging.rs
new file mode 100644
index 00000000..3593d3c8
--- /dev/null
+++ b/runtime/src/tagging.rs
@@ -0,0 +1,103 @@
+// Licensed under the Apache-2.0 license
+
+use caliptra_common::mailbox_api::{
+    GetTaggedTciReq, GetTaggedTciResp, MailboxResp, MailboxRespHeader, TagTciReq,
+};
+use caliptra_drivers::cprintln;
+use caliptra_error::{CaliptraError, CaliptraResult};
+use dpe::{
+    context::{ContextHandle, ContextState},
+    dpe_instance::DpeEnv,
+    U8Bool, MAX_HANDLES,
+};
+use zerocopy::FromBytes;
+
+use crate::{dpe_crypto::DpeCrypto, CptraDpeTypes, DpePlatform, Drivers};
+
+pub struct TagTciCmd;
+impl TagTciCmd {
+    pub(crate) fn execute(drivers: &mut Drivers, cmd_args: &[u8]) -> CaliptraResult<MailboxResp> {
+        if let Some(cmd) = TagTciReq::read_from(cmd_args) {
+            let hashed_rt_pub_key = drivers.compute_rt_alias_sn()?;
+            let pdata = drivers.persistent_data.get();
+            let rt_pub_key = pdata.fht.rt_dice_pub_key;
+            let mut crypto = DpeCrypto::new(
+                &mut drivers.sha384,
+                &mut drivers.trng,
+                &mut drivers.ecc384,
+                &mut drivers.hmac384,
+                &mut drivers.key_vault,
+                rt_pub_key,
+            );
+            let mut env = DpeEnv::<CptraDpeTypes> {
+                crypto,
+                platform: DpePlatform::new(
+                    pdata.manifest1.header.pl0_pauser,
+                    hashed_rt_pub_key,
+                    &mut drivers.cert_chain,
+                ),
+            };
+
+            let pdata_mut = drivers.persistent_data.get_mut();
+            let mut dpe = &mut pdata_mut.dpe;
+            let mut context_has_tag = &mut pdata_mut.context_has_tag;
+            let mut context_tags = &mut pdata_mut.context_tags;
+
+            // Make sure the tag isn't used by any other contexts.
+            if (0..MAX_HANDLES).any(|i| {
+                i < context_has_tag.len()
+                    && i < context_tags.len()
+                    && context_has_tag[i].get()
+                    && context_tags[i] == cmd.tag
+            }) {
+                return Err(CaliptraError::RUNTIME_DUPLICATE_TAG);
+            }
+
+            let locality = drivers.mbox.user();
+            let idx = dpe
+                .get_active_context_pos(&ContextHandle(cmd.handle), locality)
+                .map_err(|_| CaliptraError::RUNTIME_TAGGING_FAILURE)?;
+
+            // Make sure the context doesn't already have a tag
+            if context_has_tag[idx].get() {
+                return Err(CaliptraError::RUNTIME_CONTEXT_ALREADY_TAGGED);
+            }
+
+            context_has_tag[idx] = U8Bool::new(true);
+            context_tags[idx] = cmd.tag;
+
+            Ok(MailboxResp::default())
+        } else {
+            Err(CaliptraError::RUNTIME_INSUFFICIENT_MEMORY)
+        }
+    }
+}
+
+pub struct GetTaggedTciCmd;
+impl GetTaggedTciCmd {
+    pub(crate) fn execute(drivers: &Drivers, cmd_args: &[u8]) -> CaliptraResult<MailboxResp> {
+        if let Some(cmd) = GetTaggedTciReq::read_from(cmd_args) {
+            let persistent_data = drivers.persistent_data.get();
+            let idx = (0..MAX_HANDLES)
+                .find(|i| {
+                    *i < persistent_data.context_has_tag.len()
+                        && *i < persistent_data.context_tags.len()
+                        && persistent_data.context_has_tag[*i].get()
+                        && persistent_data.context_tags[*i] == cmd.tag
+                })
+                .ok_or(CaliptraError::RUNTIME_TAGGING_FAILURE)?;
+            if idx >= persistent_data.dpe.contexts.len() {
+                return Err(CaliptraError::RUNTIME_TAGGING_FAILURE);
+            }
+            let context = persistent_data.dpe.contexts[idx];
+
+            Ok(MailboxResp::GetTaggedTci(GetTaggedTciResp {
+                hdr: MailboxRespHeader::default(),
+                tci_cumulative: context.tci.tci_cumulative.0,
+                tci_current: context.tci.tci_current.0,
+            }))
+        } else {
+            Err(CaliptraError::RUNTIME_INSUFFICIENT_MEMORY)
+        }
+    }
+}


Test patch:
diff --git a/runtime/doc/test-coverage.md b/runtime/doc/test-coverage.md
index 7a0a954f..fe24382e 100644
--- a/runtime/doc/test-coverage.md
+++ b/runtime/doc/test-coverage.md
@@ -34,11 +34,12 @@ These tests are implemented in Go and test end-to-end DPE attestation behavior.
 Test Scenario | Test Name | Go Error Code
 ---|---|---
 Calls and tests behavior of the DPE command InitializeContext | **TestInitializeContext** | N/A
-Calls and tests behavior of the DPE command InitializeContext with simulation contexts | **TestInitializeContextSimulation** | N/A
+Calls and tests behavior of the DPE command InitializeContext with simulation contexts | **TestInitializeSimulation** | N/A
 Calls the DPE command CertifyKey, verifies the structure of the resulting certificate by parsing and linting it, and checks that the desired extensions are present | **TestCertifyKey** | N/A
 Calls the DPE command CertifyKey with a simulation context handle, verifies the structure of the resulting certificate by parsing and linting it, and checks that the desired extensions are present | **TestCertifyKey_SimulationMode** | N/A
 Calls the DPE command GetCertificateChain and verifies the structure of each certificate in the chain by parsing and linting them | **TestGetCertificateChain** | N/A
-Calls and tests behavior of the DPE command TagTci | **TestTagTCI** | N/A
+Calls the DPE command ExtendTci and verifies the resulting TCI | **TestExtendTCI** | N/A
+Calls the DPE command ExtendTci with a derived child context and verifies the resulting TCI | **TestExtendTciOnDerivedContexts** | N/A
 Calls the DPE command GetProfile and verifies the DPE profile | **TestGetProfile** | N/A
 Checks whether an error is reported when non-existent handle is passed as input to DPE commands | **TestInvalidHandle** | StatusInvalidHandle
 Checks whether an error is reported when caller from one locality issues DPE commands in another locality | **TestWrongLocality** | StatusInvalidLocality
@@ -59,6 +60,7 @@ Checks that the get_idev_cert mailbox command succeeds and verifies the size of
 Checks that the version mailbox command succeeds and validates the FIPS version response | **test_fips_cmd_api** | RUNTIME_SHUTDOWN
 Check that the error register is cleared when a successful mailbox command runs after a failed mailbox command | **test_error_cleared** | RUNTIME_MAILBOX_INVALID_PARAMS
 Calls the POPULATE_IDEV_CERT mailbox command and checks that the IDevId certificate is able to be parsed from the certificate chain | **test_populate_idev_cert_cmd** | N/A
+Validates the behavior of the dpe_tag_tci and dpe_get_tagged_tci mailbox commands and verifies the contents of the current and cumulative TCI responses | **test_tagging** | N/A
 
 <br><br>
 # **Wycheproof Tests**
diff --git a/runtime/tests/runtime_integration_tests/integration_tests.rs b/runtime/tests/runtime_integration_tests/integration_tests.rs
index 09f4f559..a0308cdd 100644
--- a/runtime/tests/runtime_integration_tests/integration_tests.rs
+++ b/runtime/tests/runtime_integration_tests/integration_tests.rs
@@ -7,8 +7,9 @@ use caliptra_builder::{
 };
 use caliptra_common::mailbox_api::{
     CommandId, EcdsaVerifyReq, FipsVersionResp, FwInfoResp, GetIdevCertReq, GetIdevCertResp,
-    GetIdevInfoResp, InvokeDpeReq, InvokeDpeResp, MailboxReq, MailboxReqHeader, MailboxRespHeader,
-    PopulateIdevCertReq, StashMeasurementReq, StashMeasurementResp,
+    GetIdevInfoResp, GetTaggedTciReq, GetTaggedTciResp, InvokeDpeReq, InvokeDpeResp, MailboxReq,
+    MailboxReqHeader, MailboxRespHeader, PopulateIdevCertReq, StashMeasurementReq,
+    StashMeasurementResp, TagTciReq,
 };
 use caliptra_drivers::{CaliptraError, Ecc384PubKey};
 use caliptra_hw_model::{DefaultHwModel, HwModel, ModelError, ShaAccMode};
@@ -18,7 +19,7 @@ use caliptra_runtime::{
 use dpe::{
     commands::{
         CertifyKeyCmd, CertifyKeyFlags, Command, CommandHdr, DeriveChildCmd, DeriveChildFlags,
-        GetCertificateChainCmd, RotateCtxCmd, RotateCtxFlags, SignCmd, SignFlags,
+        DestroyCtxCmd, GetCertificateChainCmd, RotateCtxCmd, RotateCtxFlags, SignCmd, SignFlags,
     },
     context::ContextHandle,
     response::{
@@ -1300,3 +1301,148 @@ fn test_idev_id_cert() {
         Err(caliptra_drivers::CaliptraError::RUNTIME_MAILBOX_API_REQUEST_DATA_LEN_TOO_LARGE)
     );
 }
+
+#[test]
+fn test_tagging() {
+    const TAG: u32 = 1;
+    const INVALID_TAG: u32 = 2;
+    const DEFAULT_HANDLE: [u8; 16] = [0u8; 16];
+
+    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");
+
+    // get tcis of default 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()
+        .expect("We expected a response");
+    let _ = GetTaggedTciResp::read_from(resp.as_slice()).unwrap();
+
+    // Check that tagging a tagged context fails
+    let mut cmd = MailboxReq::TagTci(TagTciReq {
+        hdr: MailboxReqHeader { chksum: 0 },
+        handle: DEFAULT_HANDLE,
+        tag: INVALID_TAG,
+    });
+    cmd.populate_chksum().unwrap();
+    let resp = model
+        .mailbox_execute(u32::from(CommandId::DPE_TAG_TCI), cmd.as_bytes().unwrap())
+        .unwrap_err();
+    if let ModelError::MailboxCmdFailed(code) = resp {
+        assert_eq!(
+            code,
+            u32::from(caliptra_drivers::CaliptraError::RUNTIME_CONTEXT_ALREADY_TAGGED)
+        );
+    }
+    assert_eq!(
+        model.soc_ifc().cptra_fw_error_non_fatal().read(),
+        u32::from(caliptra_drivers::CaliptraError::RUNTIME_CONTEXT_ALREADY_TAGGED)
+    );
+
+    // Check that adding a duplicate tag fails
+    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();
+    if let ModelError::MailboxCmdFailed(code) = resp {
+        assert_eq!(
+            code,
+            u32::from(caliptra_drivers::CaliptraError::RUNTIME_DUPLICATE_TAG)
+        );
+    }
+    assert_eq!(
+        model.soc_ifc().cptra_fw_error_non_fatal().read(),
+        u32::from(caliptra_drivers::CaliptraError::RUNTIME_DUPLICATE_TAG)
+    );
+
+    // Check that DPE_GET_TAGGED_TCI fails if the tag does not exist
+    let mut cmd = MailboxReq::GetTaggedTci(GetTaggedTciReq {
+        hdr: MailboxReqHeader { chksum: 0 },
+        tag: INVALID_TAG,
+    });
+    cmd.populate_chksum().unwrap();
+    let resp = model
+        .mailbox_execute(
+            u32::from(CommandId::DPE_GET_TAGGED_TCI),
+            cmd.as_bytes().unwrap(),
+        )
+        .unwrap_err();
+    if let ModelError::MailboxCmdFailed(code) = resp {
+        assert_eq!(
+            code,
+            u32::from(caliptra_drivers::CaliptraError::RUNTIME_TAGGING_FAILURE)
+        );
+    }
+    assert_eq!(
+        model.soc_ifc().cptra_fw_error_non_fatal().read(),
+        u32::from(caliptra_drivers::CaliptraError::RUNTIME_TAGGING_FAILURE)
+    );
+
+    // destroy tagged context
+    let mut data = [0u8; InvokeDpeReq::DATA_MAX_SIZE];
+    let destroy_ctx_cmd = DestroyCtxCmd {
+        handle: ContextHandle::default(),
+    };
+    let cmd_hdr = CommandHdr::new_for_test(Command::DESTROY_CONTEXT);
+    let cmd_hdr_buf = cmd_hdr.as_bytes();
+    data[..cmd_hdr_buf.len()].copy_from_slice(cmd_hdr_buf);
+    let dpe_cmd_buf = destroy_ctx_cmd.as_bytes();
+    data[cmd_hdr_buf.len()..cmd_hdr_buf.len() + dpe_cmd_buf.len()].copy_from_slice(dpe_cmd_buf);
+    let mut cmd = MailboxReq::InvokeDpeCommand(InvokeDpeReq {
+        hdr: MailboxReqHeader { chksum: 0 },
+        data,
+        data_size: (cmd_hdr_buf.len() + dpe_cmd_buf.len()) as u32,
+    });
+    cmd.populate_chksum().unwrap();
+    let _ = model
+        .mailbox_execute(u32::from(CommandId::INVOKE_DPE), cmd.as_bytes().unwrap())
+        .unwrap()
+        .expect("We should have received a response");
+
+    // check that we cannot get tagged tci for a destroyed 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();
+    if let ModelError::MailboxCmdFailed(code) = resp {
+        assert_eq!(
+            code,
+            u32::from(caliptra_drivers::CaliptraError::RUNTIME_TAGGING_FAILURE)
+        );
+    }
+    assert_eq!(
+        model.soc_ifc().cptra_fw_error_non_fatal().read(),
+        u32::from(caliptra_drivers::CaliptraError::RUNTIME_TAGGING_FAILURE)
+    );
+}