From c26d480faa15023ea8fce24475d09afb6055a1c9 Mon Sep 17 00:00:00 2001 From: Carl Lundin <108372512+clundin25@users.noreply.github.com> Date: Wed, 9 Oct 2024 17:19:06 -0700 Subject: [PATCH] Limit STASH_MEASUREMENT command to PL0 callers. (#1704) Limit STASH_MEASUREMENT command to PL0 callers. Fixes https://github.com/chipsalliance/caliptra-sw/issues/1678. --- runtime/doc/test-coverage.md | 1 + runtime/src/certify_key_extended.rs | 19 ++-- runtime/src/drivers.rs | 89 ++++++++++--------- runtime/src/invoke_dpe.rs | 20 +++-- runtime/src/lib.rs | 2 +- runtime/src/stash_measurement.rs | 23 +++-- .../test_pauser_privilege_levels.rs | 27 ++++++ 7 files changed, 114 insertions(+), 67 deletions(-) diff --git a/runtime/doc/test-coverage.md b/runtime/doc/test-coverage.md index b7132d5c85..dce57f7641 100644 --- a/runtime/doc/test-coverage.md +++ b/runtime/doc/test-coverage.md @@ -116,6 +116,7 @@ Checks the limit on the number of active DPE contexts belonging to PL1 by callin Checks the limit on the number of active DPE contexts belonging to PL0 by calling initialize_context via the invoke_dpe mailbox command with the SIMULATION flag set | **test_pl0_init_ctx_dpe_context_thresholds** | RUNTIME_PL0_USED_DPE_CONTEXT_THRESHOLD_EXCEEDED Checks the limit on the number of active DPE contexts belonging to PL1 by calling initialize_context via the invoke_dpe mailbox command with the SIMULATION flag set | **test_pl1_init_ctx_dpe_context_thresholds** | RUNTIME_PL1_USED_DPE_CONTEXT_THRESHOLD_EXCEEDED Checks that PopulateIdevIdCert cannot be called from PL1 | **test_populate_idev_cannot_be_called_from_pl1** | RUNTIME_INCORRECT_PAUSER_PRIVILEGE_LEVEL ++Checks that StashMeasurement cannot be called from PL1 | **test_stash_measurement_cannot_be_called_from_pl1** | RUNTIME_INCORRECT_PAUSER_PRIVILEGE_LEVEL Checks that CertifyKeyExtended cannot be called from PL1 | **test_certify_key_extended_cannot_be_called_from_pl1** | RUNTIME_INCORRECT_PAUSER_PRIVILEGE_LEVEL Checks that InvokeDpe::DeriveContext cannot be called from PL1 if it attempts to change locality to P0 | **test_derive_context_cannot_be_called_from_pl1_if_changes_locality_to_pl0** | RUNTIME_INCORRECT_PAUSER_PRIVILEGE_LEVEL Checks that InvokeDpe::CertifyKey cannot be called from PL1 if it requests X509 | **test_certify_key_x509_cannot_be_called_from_pl1** | RUNTIME_INCORRECT_PAUSER_PRIVILEGE_LEVEL diff --git a/runtime/src/certify_key_extended.rs b/runtime/src/certify_key_extended.rs index 6904bc7648..6aee6fa4cd 100644 --- a/runtime/src/certify_key_extended.rs +++ b/runtime/src/certify_key_extended.rs @@ -28,7 +28,8 @@ use dpe::{ use zerocopy::{AsBytes, FromBytes}; use crate::{ - CptraDpeTypes, DpeCrypto, DpeEnv, DpePlatform, Drivers, MAX_CERT_CHAIN_SIZE, PL0_PAUSER_FLAG, + CptraDpeTypes, DpeCrypto, DpeEnv, DpePlatform, Drivers, PauserPrivileges, MAX_CERT_CHAIN_SIZE, + PL0_PAUSER_FLAG, }; pub struct CertifyKeyExtendedCmd; @@ -36,6 +37,15 @@ impl CertifyKeyExtendedCmd { pub(crate) fn execute(drivers: &mut Drivers, cmd_args: &[u8]) -> CaliptraResult { let cmd = CertifyKeyExtendedReq::read_from(cmd_args) .ok_or(CaliptraError::RUNTIME_INSUFFICIENT_MEMORY)?; + + match drivers.caller_privilege_level() { + // CERTIFY_KEY_EXTENDED MUST only be called from PL0 + PauserPrivileges::PL0 => (), + PauserPrivileges::PL1 => { + return Err(CaliptraError::RUNTIME_INCORRECT_PAUSER_PRIVILEGE_LEVEL); + } + } + let hashed_rt_pub_key = drivers.compute_rt_alias_sn()?; let key_id_rt_cdi = Drivers::get_key_id_rt_cdi(drivers)?; let key_id_rt_priv_key = Drivers::get_key_id_rt_priv_key(drivers)?; @@ -73,15 +83,10 @@ impl CertifyKeyExtendedCmd { ), }; - let locality = drivers.mbox.user(); - // Cannot call CERTIFY_KEY_EXTENDED from PL1 - if Drivers::is_caller_pl1(pl0_pauser, pdata.manifest1.header.flags, locality) { - return Err(CaliptraError::RUNTIME_INCORRECT_PAUSER_PRIVILEGE_LEVEL); - } - let mut dpe = &mut pdata.dpe; let certify_key_cmd = CertifyKeyCmd::read_from(&cmd.certify_key_req[..]) .ok_or(CaliptraError::RUNTIME_DPE_COMMAND_DESERIALIZATION_FAILED)?; + let locality = drivers.mbox.user(); let resp = certify_key_cmd.execute(dpe, &mut env, locality); let certify_key_resp = match resp { diff --git a/runtime/src/drivers.rs b/runtime/src/drivers.rs index 4b1ef53883..edc4f81cfb 100644 --- a/runtime/src/drivers.rs +++ b/runtime/src/drivers.rs @@ -61,6 +61,12 @@ use core::cmp::Ordering::{Equal, Greater}; use crypto::{AlgLen, Crypto, CryptoBuf, Hasher}; use zerocopy::AsBytes; +#[derive(PartialEq, Clone)] +pub enum PauserPrivileges { + PL0, + PL1, +} + pub struct Drivers { pub mbox: Mailbox, pub sha_acc: Sha512AccCsr, @@ -237,16 +243,8 @@ impl Drivers { } } else { let pl0_pauser = drivers.persistent_data.get().manifest1.header.pl0_pauser; - let flags = drivers.persistent_data.get().manifest1.header.flags; - let locality = drivers.mbox.user(); // check that DPE used context limits are not exceeded - let dpe_context_threshold_exceeded = Self::is_dpe_context_threshold_exceeded( - pl0_pauser, - flags, - locality, - &drivers.persistent_data.get().dpe, - true, - ); + let dpe_context_threshold_exceeded = drivers.is_dpe_context_threshold_exceeded(); if cfi_launder(dpe_context_threshold_exceeded.is_ok()) { cfi_assert!(dpe_context_threshold_exceeded.is_ok()); } else { @@ -364,6 +362,7 @@ impl Drivers { let caliptra_locality = 0xFFFFFFFF; let pl0_pauser_locality = drivers.persistent_data.get().manifest1.header.pl0_pauser; let hashed_rt_pub_key = drivers.compute_rt_alias_sn()?; + let privilege_level = drivers.caller_privilege_level(); // create a hash of all the mailbox valid pausers const PAUSER_COUNT: usize = 5; @@ -446,13 +445,12 @@ impl Drivers { for measurement_log_entry in measurement_log.iter().take(num_measurements) { // Check that adding this measurement to DPE doesn't cause // the PL0 context threshold to be exceeded. - let flags = pdata.manifest1.header.flags; - Self::is_dpe_context_threshold_exceeded( - pl0_pauser_locality, - flags, + // + // Use the helper method here because the DPE instance holds a mutable reference to driver + Self::is_dpe_context_threshold_exceeded_helper( pl0_pauser_locality, + privilege_level.clone(), &dpe, - false, )?; let measurement_data = measurement_log_entry.pcr_entry.measured_data(); @@ -537,20 +535,18 @@ impl Drivers { /// Counts the number of non-inactive DPE contexts and returns an error /// if this number is greater than or equal to the active context threshold /// corresponding to the privilege level of the caller. - /// - /// # Arguments - /// - /// * `pl0_pauser` - Value of PL0 PAuser - /// * `flags` - Flags from manifest header - /// * `locality` - Caller's locality - /// * `dpe` - DpeInstance - /// * `check_already_exceeded` - If true, checks that the active context threshold is already exceeded. - pub fn is_dpe_context_threshold_exceeded( + pub fn is_dpe_context_threshold_exceeded(&self) -> CaliptraResult<()> { + Self::is_dpe_context_threshold_exceeded_helper( + self.persistent_data.get().manifest1.header.pl0_pauser, + self.caller_privilege_level(), + &self.persistent_data.get().dpe, + ) + } + + fn is_dpe_context_threshold_exceeded_helper( pl0_pauser: u32, - flags: u32, - locality: u32, + caller_privilege_level: PauserPrivileges, dpe: &DpeInstance, - check_already_exceeded: bool, ) -> CaliptraResult<()> { let used_pl0_dpe_context_count = dpe .count_contexts(|c: &Context| { @@ -566,32 +562,43 @@ impl Drivers { - used_pl0_dpe_context_count; match ( - Self::is_caller_pl1(pl0_pauser, flags, locality), + caller_privilege_level, used_pl1_dpe_context_count.cmp(&PL1_DPE_ACTIVE_CONTEXT_THRESHOLD), used_pl0_dpe_context_count.cmp(&PL0_DPE_ACTIVE_CONTEXT_THRESHOLD), ) { - (true, Equal, _) => Err(CaliptraError::RUNTIME_PL1_USED_DPE_CONTEXT_THRESHOLD_REACHED), - (true, Greater, _) => { + (PauserPrivileges::PL1, Equal, _) => { + Err(CaliptraError::RUNTIME_PL1_USED_DPE_CONTEXT_THRESHOLD_REACHED) + } + (PauserPrivileges::PL1, Greater, _) => { Err(CaliptraError::RUNTIME_PL1_USED_DPE_CONTEXT_THRESHOLD_EXCEEDED) } - (false, _, Equal) => Err(CaliptraError::RUNTIME_PL0_USED_DPE_CONTEXT_THRESHOLD_REACHED), - (false, _, Greater) => { + (PauserPrivileges::PL0, _, Equal) => { + Err(CaliptraError::RUNTIME_PL0_USED_DPE_CONTEXT_THRESHOLD_REACHED) + } + (PauserPrivileges::PL0, _, Greater) => { Err(CaliptraError::RUNTIME_PL0_USED_DPE_CONTEXT_THRESHOLD_EXCEEDED) } _ => Ok(()), } } - /// Checks if the caller is privilege level 1 - /// - /// # Arguments - /// - /// * `pl0_pauser` - Value of PL0 PAuser - /// * `flags` - Flags from manifest header - /// * `locality` - Caller's locality - pub fn is_caller_pl1(pl0_pauser: u32, flags: u32, locality: u32) -> bool { - (flags & PL0_PAUSER_FLAG == 0) // There is no PL0 PAUSER - || (locality != pl0_pauser) // There is a PL0 PAUSER, but it's not the current user + /// Retrieves the caller permission level + pub fn caller_privilege_level(&self) -> PauserPrivileges { + let manifest_header = self.persistent_data.get().manifest1.header; + let pl0_pauser = manifest_header.pl0_pauser; + let flags = manifest_header.flags; + let locality = self.mbox.user(); + + // When the PL0_PAUSER_FLAG bit is not set there can be no PL0 PAUSER. + if (flags & PL0_PAUSER_FLAG == 0) { + return PauserPrivileges::PL1; + } + + if locality == pl0_pauser { + PauserPrivileges::PL0 + } else { + PauserPrivileges::PL1 + } } /// Get the KeyId for the RT Alias CDI diff --git a/runtime/src/invoke_dpe.rs b/runtime/src/invoke_dpe.rs index d59a2975c9..dfb709ffdc 100644 --- a/runtime/src/invoke_dpe.rs +++ b/runtime/src/invoke_dpe.rs @@ -12,7 +12,9 @@ Abstract: --*/ -use crate::{CptraDpeTypes, DpeCrypto, DpeEnv, DpePlatform, Drivers, PL0_PAUSER_FLAG}; +use crate::{ + CptraDpeTypes, DpeCrypto, DpeEnv, DpePlatform, Drivers, PauserPrivileges, PL0_PAUSER_FLAG, +}; use caliptra_cfi_derive_git::cfi_impl_fn; use caliptra_common::mailbox_api::{InvokeDpeReq, InvokeDpeResp, MailboxResp, MailboxRespHeader}; use caliptra_drivers::{CaliptraError, CaliptraResult}; @@ -43,6 +45,10 @@ impl InvokeDpeCmd { let hashed_rt_pub_key = drivers.compute_rt_alias_sn()?; let key_id_rt_cdi = Drivers::get_key_id_rt_cdi(drivers)?; let key_id_rt_priv_key = Drivers::get_key_id_rt_priv_key(drivers)?; + + let caller_privilege_level = drivers.caller_privilege_level(); + let dpe_context_threshold_err = drivers.is_dpe_context_threshold_exceeded(); + let pdata = drivers.persistent_data.get_mut(); let crypto = DpeCrypto::new( &mut drivers.sha384, @@ -84,9 +90,7 @@ impl InvokeDpeCmd { Command::InitCtx(cmd) => { // InitCtx can only create new contexts if they are simulation contexts. if InitCtxCmd::flag_is_simulation(&cmd) { - Drivers::is_dpe_context_threshold_exceeded( - pl0_pauser, flags, locality, dpe, false, - )?; + dpe_context_threshold_err?; } cmd.execute(dpe, &mut env, locality) } @@ -95,13 +99,11 @@ impl InvokeDpeCmd { // If recursive _is_ set, it will extend the existing one, which will not count // against the context threshold. if !DeriveContextCmd::is_recursive(&cmd) { - Drivers::is_dpe_context_threshold_exceeded( - pl0_pauser, flags, locality, dpe, false, - )?; + dpe_context_threshold_err?; } if DeriveContextCmd::changes_locality(&cmd) && cmd.target_locality == pl0_pauser - && Drivers::is_caller_pl1(pl0_pauser, flags, locality) + && caller_privilege_level != PauserPrivileges::PL0 { return Err(CaliptraError::RUNTIME_INCORRECT_PAUSER_PRIVILEGE_LEVEL); } @@ -110,7 +112,7 @@ impl InvokeDpeCmd { Command::CertifyKey(cmd) => { // PL1 cannot request X509 if cmd.format == CertifyKeyCmd::FORMAT_X509 - && Drivers::is_caller_pl1(pl0_pauser, flags, locality) + && caller_privilege_level != PauserPrivileges::PL0 { return Err(CaliptraError::RUNTIME_INCORRECT_PAUSER_PRIVILEGE_LEVEL); } diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 8fa9d0240f..6d4dce49cb 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -39,7 +39,7 @@ pub mod mailbox; use authorize_and_stash::AuthorizeAndStashCmd; use caliptra_cfi_lib_git::{cfi_assert, cfi_assert_eq, cfi_assert_ne, cfi_launder, CfiCounter}; use caliptra_registers::soc_ifc::SocIfcReg; -pub use drivers::Drivers; +pub use drivers::{Drivers, PauserPrivileges}; use mailbox::Mailbox; use crate::capabilities::CapabilitiesCmd; diff --git a/runtime/src/stash_measurement.rs b/runtime/src/stash_measurement.rs index f5e6334bb5..02a9da0b79 100644 --- a/runtime/src/stash_measurement.rs +++ b/runtime/src/stash_measurement.rs @@ -12,7 +12,7 @@ Abstract: --*/ -use crate::{dpe_crypto::DpeCrypto, CptraDpeTypes, DpePlatform, Drivers}; +use crate::{dpe_crypto::DpeCrypto, CptraDpeTypes, DpePlatform, Drivers, PauserPrivileges}; use caliptra_cfi_derive_git::cfi_impl_fn; use caliptra_common::mailbox_api::{ MailboxResp, MailboxRespHeader, StashMeasurementReq, StashMeasurementResp, @@ -35,6 +35,18 @@ impl StashMeasurementCmd { let cmd = StashMeasurementReq::read_from(cmd_args) .ok_or(CaliptraError::RUNTIME_INSUFFICIENT_MEMORY)?; let dpe_result = { + match drivers.caller_privilege_level() { + // Only PL0 can call STASH_MEASUREMENT + PauserPrivileges::PL0 => (), + PauserPrivileges::PL1 => { + return Err(CaliptraError::RUNTIME_INCORRECT_PAUSER_PRIVILEGE_LEVEL); + } + } + + // Check that adding this measurement to DPE doesn't cause + // the PL0 context threshold to be exceeded. + drivers.is_dpe_context_threshold_exceeded()?; + let hashed_rt_pub_key = drivers.compute_rt_alias_sn()?; let key_id_rt_cdi = Drivers::get_key_id_rt_cdi(drivers)?; let key_id_rt_priv_key = Drivers::get_key_id_rt_priv_key(drivers)?; @@ -62,15 +74,8 @@ impl StashMeasurementCmd { ), }; - let pl0_pauser = pdata.manifest1.header.pl0_pauser; - let flags = pdata.manifest1.header.flags; let locality = drivers.mbox.user(); - // Check that adding this measurement to DPE doesn't cause - // the PL0 context threshold to be exceeded. - Drivers::is_dpe_context_threshold_exceeded( - pl0_pauser, flags, locality, &pdata.dpe, false, - )?; - // let pdata_mut = drivers.persistent_data.get_mut(); + let derive_context_resp = DeriveContextCmd { handle: ContextHandle::default(), data: cmd.measurement, diff --git a/runtime/tests/runtime_integration_tests/test_pauser_privilege_levels.rs b/runtime/tests/runtime_integration_tests/test_pauser_privilege_levels.rs index f2bf0eda35..975c77698d 100644 --- a/runtime/tests/runtime_integration_tests/test_pauser_privilege_levels.rs +++ b/runtime/tests/runtime_integration_tests/test_pauser_privilege_levels.rs @@ -266,6 +266,33 @@ fn test_populate_idev_cannot_be_called_from_pl1() { ); } +#[test] +fn test_stash_measurement_cannot_be_called_from_pl1() { + let mut image_opts = ImageOptions::default(); + image_opts.vendor_config.pl0_pauser = None; + + let mut model = run_rt_test(None, Some(image_opts), None); + + model.step_until(|m| { + m.soc_ifc().cptra_boot_status().read() == u32::from(RtBootStatus::RtReadyForCommands) + }); + + let mut cmd = MailboxReq::StashMeasurement(StashMeasurementReq::default()); + cmd.populate_chksum().unwrap(); + + let resp = model + .mailbox_execute( + u32::from(CommandId::STASH_MEASUREMENT), + cmd.as_bytes().unwrap(), + ) + .unwrap_err(); + assert_error( + &mut model, + CaliptraError::RUNTIME_INCORRECT_PAUSER_PRIVILEGE_LEVEL, + resp, + ); +} + #[test] fn test_certify_key_x509_cannot_be_called_from_pl1() { let mut image_opts = ImageOptions::default();