Skip to content

Commit

Permalink
Add CFI instrumentation to Runtime
Browse files Browse the repository at this point in the history
  • Loading branch information
sree-revoori1 authored and jhand2 committed Feb 5, 2024
1 parent 3d3c5b1 commit ec2617a
Show file tree
Hide file tree
Showing 18 changed files with 246 additions and 21 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

52 changes: 52 additions & 0 deletions cfi/lib/src/cfi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,58 @@ pub fn cfi_assert_eq_12_words(a: &[u32; 12], b: &[u32; 12]) {
}
}

#[cfg(not(any(target_arch = "riscv32", target_arch = "riscv64")))]
pub fn cfi_assert_eq_8_words(a: &[u32; 8], b: &[u32; 8]) {
if a != b {
cfi_panic(CfiPanicInfo::AssertEqFail)
}
}

/// Unrolled comparison of 8 words
///
/// Written in assembly so the trampoline is above the comparisons rather than
/// below
#[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))]
#[inline(always)]
pub fn cfi_assert_eq_8_words(a: &[u32; 8], b: &[u32; 8]) {
unsafe {
core::arch::asm!(
"j 3f",
"2:",
"li a0, 0x01040055",
"j cfi_panic_handler",
"3:",
"lw {tmp0}, 0(a4)",
"lw {tmp1}, 0(a5)",
"bne {tmp0}, {tmp1}, 2b",
"lw {tmp0}, 4(a4)",
"lw {tmp1}, 4(a5)",
"bne {tmp0}, {tmp1}, 2b",
"lw {tmp0}, 8(a4)",
"lw {tmp1}, 8(a5)",
"bne {tmp0}, {tmp1}, 2b",
"lw {tmp0}, 12(a4)",
"lw {tmp1}, 12(a5)",
"bne {tmp0}, {tmp1}, 2b",
"lw {tmp0}, 16(a4)",
"lw {tmp1}, 16(a5)",
"bne {tmp0}, {tmp1}, 2b",
"lw {tmp0}, 20(a4)",
"lw {tmp1}, 20(a5)",
"bne {tmp0}, {tmp1}, 2b",
"lw {tmp0}, 24(a4)",
"lw {tmp1}, 24(a5)",
"bne {tmp0}, {tmp1}, 2b",
"lw {tmp0}, 28(a4)",
"lw {tmp1}, 28(a5)",
"bne {tmp0}, {tmp1}, 2b",
in("a4") a.as_ptr(),
in("a5") b.as_ptr(),
tmp0 = out(reg) _,
tmp1 = out(reg) _);
}
}

#[cfg(not(any(target_arch = "riscv32", target_arch = "riscv64")))]
pub fn cfi_assert_eq_6_words(a: &[u32; 6], b: &[u32; 6]) {
if a != b {
Expand Down
35 changes: 35 additions & 0 deletions cfi/lib/tests/test_asm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,41 @@ pub fn test_assert_eq_12words_failure() {
// Leak thread in infinite loop...
}

#[test]
pub fn test_assert_eq_8words_success() {
CFI_PANIC_CALLED.with(|c| c.borrow_mut().store(0, Relaxed));
use caliptra_cfi_lib::cfi_assert_eq_8_words;
let a = [0, 1, 2, 3, 4, 5, 6, 7];
let b = [0, 1, 2, 3, 4, 5, 6, 7];
// Make sure these are separate memory addresses
assert_ne!(a.as_ptr(), b.as_ptr());
cfi_assert_eq_8_words(&a, &b);
assert_eq!(CFI_PANIC_CALLED.with(|c| c.borrow_mut().load(Relaxed)), 0);
}

#[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))]
#[test]
pub fn test_assert_eq_8words_failure() {
use caliptra_cfi_lib::cfi_assert_eq_8_words;

let cfi_panic_called = Arc::new(AtomicU32::new(0));
let cfi_panic_called2 = cfi_panic_called.clone();

std::thread::spawn(|| {
CFI_PANIC_CALLED.with(|c| c.replace(cfi_panic_called2));
cfi_assert_eq_8_words(&[0, 1, 2, 3, 4, 5, 6, 7], &[0, 1, 2, 3, 4, 5, 6, 8]);
});
let val = loop {
let val = cfi_panic_called.load(Relaxed);
if val != 0 {
break val;
}
};
assert_eq!(val, CaliptraError::ROM_CFI_PANIC_ASSERT_EQ_FAILURE.into());

// Leak thread in infinite loop...
}

#[test]
pub fn test_assert_eq_6words_success() {
CFI_PANIC_CALLED.with(|c| c.borrow_mut().store(0, Relaxed));
Expand Down
7 changes: 5 additions & 2 deletions runtime/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@ version = "0.1.0"
edition = "2021"

[dependencies]
caliptra-cfi-lib = { workspace = true, default-features = false, features = ["cfi", "cfi-counter" ] }
caliptra-cfi-derive.workspace = true
caliptra_common = { workspace = true, default-features = false, features = ["runtime"] }
caliptra-cpu.workspace = true
caliptra-drivers = { workspace = true, features = ["runtime", "no-cfi"] }
caliptra-drivers = { workspace = true, features = ["runtime"] }
caliptra-error = { workspace = true, default-features = false }
caliptra-image-types = { workspace = true, default-features = false }
caliptra-kat.workspace = true
Expand All @@ -20,7 +22,7 @@ platform.workspace = true
ufmt.workspace = true
zerocopy.workspace = true
arrayvec.workspace = true
caliptra-image-verify = { workspace = true, default-features = false, features = ["no-cfi"] }
caliptra-image-verify = { workspace = true, default-features = false }
zeroize.workspace = true

[build-dependencies]
Expand Down Expand Up @@ -52,4 +54,5 @@ test_only_commands = ["caliptra_common/test_only_commands"]
slow_tests = []
verilator = ["caliptra-hw-model/verilator"]
fips_self_test=[]
no-cfi = ["caliptra-image-verify/no-cfi", "caliptra-drivers/no-cfi"]
fpga_realtime = ["caliptra-drivers/fpga_realtime"]
24 changes: 19 additions & 5 deletions runtime/src/disable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ Abstract:
--*/

use crate::Drivers;
use caliptra_cfi_derive::cfi_impl_fn;
use caliptra_common::mailbox_api::MailboxResp;
use caliptra_drivers::{
hmac384_kdf, Array4x12, CaliptraError, CaliptraResult, Ecc384Seed, Hmac384Key, KeyReadArgs,
Expand All @@ -22,23 +23,35 @@ use dpe::U8Bool;

pub struct DisableAttestationCmd;
impl DisableAttestationCmd {
#[cfg_attr(not(feature = "no-cfi"), cfi_impl_fn)]
#[inline(never)]
pub(crate) fn execute(drivers: &mut Drivers) -> CaliptraResult<MailboxResp> {
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)?;
drivers.key_vault.erase_key(key_id_rt_cdi)?;
drivers.key_vault.erase_key(key_id_rt_priv_key)?;

Self::erase_keys(drivers)?;
Self::zero_rt_cdi(drivers)?;
Self::generate_dice_key(drivers)?;
drivers.persistent_data.get_mut().attestation_disabled = U8Bool::new(true);
Ok(MailboxResp::default())
}

/// Erase the RT CDI and RT Private Key from the key vault
///
/// # Arguments
///
/// * `drivers` - Drivers
#[cfg_attr(not(feature = "no-cfi"), cfi_impl_fn)]
fn erase_keys(drivers: &mut Drivers) -> CaliptraResult<()> {
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)?;
drivers.key_vault.erase_key(key_id_rt_cdi)?;
drivers.key_vault.erase_key(key_id_rt_priv_key)
}

/// Set CDI key vault slot to a KDF of a buffer of 0s.
///
/// # Arguments
///
/// * `drivers` - Drivers
#[cfg_attr(not(feature = "no-cfi"), cfi_impl_fn)]
fn zero_rt_cdi(drivers: &mut Drivers) -> CaliptraResult<()> {
let key_id_rt_cdi = Drivers::get_key_id_rt_cdi(drivers)?;
hmac384_kdf(
Expand Down Expand Up @@ -66,6 +79,7 @@ impl DisableAttestationCmd {
/// # Arguments
///
/// * `drivers` - Drivers
#[cfg_attr(not(feature = "no-cfi"), cfi_impl_fn)]
fn generate_dice_key(drivers: &mut Drivers) -> CaliptraResult<()> {
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
9 changes: 8 additions & 1 deletion runtime/src/dpe_crypto.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ Abstract:

use core::cmp::min;

use caliptra_cfi_lib::{cfi_assert, cfi_assert_eq, cfi_launder};
use caliptra_common::keyids::{KEY_ID_DPE_CDI, KEY_ID_DPE_PRIV_KEY, KEY_ID_TMP};
use caliptra_drivers::{
cprintln, hmac384_kdf, Array4x12, Ecc384, Ecc384PrivKeyIn, Ecc384PubKey, Ecc384Scalar,
Expand Down Expand Up @@ -303,7 +304,13 @@ impl<'a> Crypto for DpeCrypto<'a> {
// note: the output point must be kept secret since it is derived from the private key,
// so as long as that output is kept secret and not released outside of Caliptra,
// it is safe to use it as key material.
let (_, hmac_seed) = Self::derive_key_pair(self, algs, cdi, label, info)?;
let key_pair = Self::derive_key_pair(self, algs, cdi, label, info);
if cfi_launder(key_pair.is_ok()) {
cfi_assert!(key_pair.is_ok());
} else {
cfi_assert!(key_pair.is_err());
}
let (_, hmac_seed) = key_pair?;

// create ikm to the hmac kdf by hashing the seed entropy from the pub key
// this is more secure than directly using the pub key components in the hmac
Expand Down
44 changes: 40 additions & 4 deletions runtime/src/drivers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ use crate::{
};

use arrayvec::ArrayVec;
use caliptra_cfi_derive::{cfi_impl_fn, cfi_mod_fn};
use caliptra_cfi_lib::{cfi_assert, cfi_assert_eq, cfi_assert_eq_12_words, cfi_launder};
use caliptra_drivers::KeyId;
use caliptra_drivers::{
cprint, cprintln, pcr_log::RT_FW_JOURNEY_PCR, Array4x12, CaliptraError, CaliptraResult,
Expand Down Expand Up @@ -115,19 +117,23 @@ impl Drivers {
let reset_reason = drivers.soc_ifc.reset_reason();
match reset_reason {
ResetReason::ColdReset => {
cfi_assert_eq(drivers.soc_ifc.reset_reason(), ResetReason::ColdReset);
Self::initialize_dpe(&mut drivers)?;
}
ResetReason::UpdateReset => {
cfi_assert_eq(drivers.soc_ifc.reset_reason(), ResetReason::UpdateReset);
Self::validate_dpe_structure(&mut drivers)?;
Self::validate_context_tags(&mut drivers)?;
Self::update_dpe_rt_journey(&mut drivers)?;
}
ResetReason::WarmReset => {
cfi_assert_eq(drivers.soc_ifc.reset_reason(), ResetReason::WarmReset);
Self::validate_dpe_structure(&mut drivers)?;
Self::validate_context_tags(&mut drivers)?;
Self::check_dpe_rt_journey_unchanged(&mut drivers)?;
}
ResetReason::Unknown => {
cfi_assert_eq(drivers.soc_ifc.reset_reason(), ResetReason::Unknown);
return Err(CaliptraError::RUNTIME_UNKNOWN_RESET_FLOW);
}
}
Expand Down Expand Up @@ -206,6 +212,11 @@ impl Drivers {
if let Err(e) = validation_result {
// If SRAM Dpe Instance validation fails, disable attestation
let mut result = DisableAttestationCmd::execute(drivers);
if cfi_launder(result.is_ok()) {
cfi_assert!(result.is_ok());
} else {
cfi_assert!(result.is_err());
}
match result {
Ok(_) => {
cprintln!("Disabled attestation due to DPE validation failure");
Expand All @@ -225,14 +236,25 @@ impl Drivers {
let flags = drivers.persistent_data.get().manifest1.header.flags;
let locality = drivers.mbox.user();
// check that DPE used context limits are not exceeded
if let Err(e) = Self::is_dpe_context_threshold_exceeded(
let dpe_context_threshold_exceeded = Self::is_dpe_context_threshold_exceeded(
pl0_pauser,
flags,
locality,
&drivers.persistent_data.get().dpe,
true,
) {
);
if cfi_launder(dpe_context_threshold_exceeded.is_ok()) {
cfi_assert!(dpe_context_threshold_exceeded.is_ok());
} else {
cfi_assert!(dpe_context_threshold_exceeded.is_err());
}
if let Err(e) = dpe_context_threshold_exceeded {
let mut result = DisableAttestationCmd::execute(drivers);
if cfi_launder(result.is_ok()) {
cfi_assert!(result.is_ok());
} else {
cfi_assert!(result.is_err());
}
match result {
Ok(_) => {
cprintln!(
Expand All @@ -252,6 +274,7 @@ impl Drivers {
}

/// Update DPE root context's TCI measurement with RT_FW_JOURNEY_PCR
#[cfg_attr(not(feature = "no-cfi"), cfi_impl_fn)]
fn update_dpe_rt_journey(drivers: &mut Drivers) -> CaliptraResult<()> {
let dpe = &mut drivers.persistent_data.get_mut().dpe;
let root_idx = Self::get_dpe_root_context_idx(dpe)?;
Expand All @@ -266,13 +289,18 @@ impl Drivers {
fn check_dpe_rt_journey_unchanged(mut drivers: &mut Drivers) -> CaliptraResult<()> {
let dpe = &drivers.persistent_data.get().dpe;
let root_idx = Self::get_dpe_root_context_idx(dpe)?;
let latest_tci = dpe.contexts[root_idx].tci.tci_current;
let latest_tci = Array4x12::from(&dpe.contexts[root_idx].tci.tci_current.0);
let latest_pcr = drivers.pcr_bank.read_pcr(RT_FW_JOURNEY_PCR);

// Ensure TCI from SRAM == RT_FW_JOURNEY_PCR
if latest_pcr != Array4x12::from(&latest_tci.0) {
if latest_pcr != latest_tci {
// If latest pcr validation fails, disable attestation
let mut result = DisableAttestationCmd::execute(drivers);
if cfi_launder(result.is_ok()) {
cfi_assert!(result.is_ok());
} else {
cfi_assert!(result.is_err());
}
match result {
Ok(_) => {
cprintln!("Disabled attestation due to latest TCI of the node containing the runtime journey PCR not matching the runtime PCR");
Expand All @@ -285,6 +313,11 @@ impl Drivers {
return Err(CaliptraError::RUNTIME_GLOBAL_EXCEPTION);
}
}
} else {
cfi_assert_eq_12_words(
&<[u32; 12]>::from(latest_tci),
&<[u32; 12]>::from(latest_pcr),
)
}

Ok(())
Expand All @@ -310,6 +343,7 @@ impl Drivers {
}

/// Compute the Caliptra Name SerialNumber by Sha256 hashing the RT Alias public key
#[cfg_attr(not(feature = "no-cfi"), cfi_impl_fn)]
pub fn compute_rt_alias_sn(&mut self) -> CaliptraResult<CryptoBuf> {
let key = self.persistent_data.get().fht.rt_dice_pub_key.to_der();

Expand All @@ -321,6 +355,7 @@ impl Drivers {
}

/// Initialize DPE with measurements and store in Drivers
#[cfg_attr(not(feature = "no-cfi"), cfi_impl_fn)]
fn initialize_dpe(drivers: &mut Drivers) -> CaliptraResult<()> {
let caliptra_locality = 0xFFFFFFFF;
let pl0_pauser_locality = drivers.persistent_data.get().manifest1.header.pl0_pauser;
Expand Down Expand Up @@ -442,6 +477,7 @@ impl Drivers {
}

/// Create certificate chain and store in Drivers
#[cfg_attr(not(feature = "no-cfi"), cfi_impl_fn)]
fn create_cert_chain(drivers: &mut Drivers) -> CaliptraResult<()> {
let data_vault = &drivers.data_vault;
let persistent_data = &drivers.persistent_data;
Expand Down
Loading

0 comments on commit ec2617a

Please sign in to comment.