Skip to content

Commit

Permalink
Limit STASH_MEASUREMENT command to PL0 callers. (#1704)
Browse files Browse the repository at this point in the history
Limit STASH_MEASUREMENT command to PL0 callers.

Fixes #1678.
  • Loading branch information
clundin25 authored Oct 10, 2024
1 parent e669aa2 commit c26d480
Show file tree
Hide file tree
Showing 7 changed files with 114 additions and 67 deletions.
1 change: 1 addition & 0 deletions runtime/doc/test-coverage.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
19 changes: 12 additions & 7 deletions runtime/src/certify_key_extended.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,24 @@ 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;
impl CertifyKeyExtendedCmd {
pub(crate) fn execute(drivers: &mut Drivers, cmd_args: &[u8]) -> CaliptraResult<MailboxResp> {
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)?;
Expand Down Expand Up @@ -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 {
Expand Down
89 changes: 48 additions & 41 deletions runtime/src/drivers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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| {
Expand All @@ -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
Expand Down
20 changes: 11 additions & 9 deletions runtime/src/invoke_dpe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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)
}
Expand All @@ -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);
}
Expand All @@ -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);
}
Expand Down
2 changes: 1 addition & 1 deletion runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
23 changes: 14 additions & 9 deletions runtime/src/stash_measurement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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)?;
Expand Down Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down

0 comments on commit c26d480

Please sign in to comment.