Skip to content

Commit

Permalink
Implement hash chain derivation in FMC.
Browse files Browse the repository at this point in the history
  • Loading branch information
bluegate010 committed Mar 12, 2024
1 parent 2dfcf87 commit 75ed0da
Show file tree
Hide file tree
Showing 12 changed files with 289 additions and 15 deletions.
2 changes: 2 additions & 0 deletions common/src/keyids.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ pub const KEY_ID_FMC_PRIV_KEY: KeyId = KeyId::KeyId7;
pub const KEY_ID_RT_CDI: KeyId = KeyId::KeyId4;
#[cfg(feature = "fmc")]
pub const KEY_ID_RT_PRIV_KEY: KeyId = KeyId::KeyId5;
#[cfg(feature = "fmc")]
pub const KEY_ID_RT_HASH_CHAIN: KeyId = KeyId::KeyId2;
#[cfg(feature = "runtime")]
pub const KEY_ID_DPE_CDI: KeyId = KeyId::KeyId8;
#[cfg(feature = "runtime")]
Expand Down
2 changes: 1 addition & 1 deletion common/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ pub use fuse::{FuseLogEntry, FuseLogEntryId};
pub use pcr::{PcrLogEntry, PcrLogEntryId, RT_FW_CURRENT_PCR, RT_FW_JOURNEY_PCR};

pub const FMC_ORG: u32 = 0x40000000;
pub const FMC_SIZE: u32 = 20 * 1024;
pub const FMC_SIZE: u32 = 22 * 1024;
pub const RUNTIME_ORG: u32 = FMC_ORG + FMC_SIZE;
pub const RUNTIME_SIZE: u32 = 92 * 1024;

Expand Down
1 change: 1 addition & 0 deletions error/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,7 @@ impl CaliptraError {
pub const FMC_HANDOFF_NOT_READY_FOR_RT: CaliptraError = CaliptraError::new_const(0x000F000C);
pub const FMC_GLOBAL_WDT_EXPIRED: CaliptraError = CaliptraError::new_const(0x000F000D);
pub const FMC_UNKNOWN_RESET: CaliptraError = CaliptraError::new_const(0x000F000E);
pub const RT_SVN_EXCEEDS_MAX: CaliptraError = CaliptraError::new_const(0x000F000F);

/// TRNG_EXT Errors
pub const DRIVER_TRNG_EXT_TIMEOUT: CaliptraError = CaliptraError::new_const(0x00100001);
Expand Down
33 changes: 31 additions & 2 deletions fmc/src/flow/crypto.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ use caliptra_cfi_derive::cfi_impl_fn;
use caliptra_common::{crypto::Ecc384KeyPair, keyids::KEY_ID_TMP};
use caliptra_drivers::{
hmac384_kdf, okref, Array4x12, Array4x5, Array4x8, CaliptraResult, Ecc384PrivKeyIn,
Ecc384PrivKeyOut, Ecc384PubKey, Ecc384Result, Ecc384Signature, KeyId, KeyReadArgs, KeyUsage,
KeyWriteArgs, Sha256Alg,
Ecc384PrivKeyOut, Ecc384PubKey, Ecc384Result, Ecc384Signature, Hmac384Data, KeyId, KeyReadArgs,
KeyUsage, KeyWriteArgs, Sha256Alg,
};

pub enum Crypto {}
Expand Down Expand Up @@ -95,6 +95,35 @@ impl Crypto {
)
}

/// Calculate HMAC-348
///
/// # Arguments
///
/// * `env` - ROM Environment
/// * `key` - HMAC384 key slot
/// * `data` - Input data to hash
/// * `tag` - Key slot to store the tag
#[inline(always)]
pub fn hmac384_mac(
env: &mut FmcEnv,
key: KeyId,
data: &Hmac384Data,
tag: KeyId,
) -> CaliptraResult<()> {
env.hmac384.hmac(
&KeyReadArgs::new(key).into(),
data,
&mut env.trng,
KeyWriteArgs::new(
tag,
KeyUsage::default()
.set_hmac_key_en()
.set_ecc_key_gen_seed_en(),
)
.into(),
)
}

/// Generate ECC Key Pair
///
/// # Arguments
Expand Down
90 changes: 90 additions & 0 deletions fmc/src/flow/hash_chain.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/*++
Licensed under the Apache-2.0 license.
File Name:
hash_chain.rs
Abstract:
File contains execution routines for deriving a hash chain based on RT FW's SVN.
Environment:
FMC
--*/

use caliptra_cfi_derive::cfi_impl_fn;
use caliptra_cfi_lib::{cfi_assert_eq, cfi_assert_le};

use caliptra_common::keyids::KEY_ID_RT_HASH_CHAIN;
use caliptra_drivers::{cprintln, report_boot_status, Hmac384Data, KeyId};
use caliptra_error::{CaliptraError, CaliptraResult};

use crate::{flow::crypto::Crypto, fmc_env::FmcEnv, hand_off::HandOff, FmcBootStatus};

pub const MAX_RT_SVN: u32 = 128;

pub struct HashChain {}

impl HashChain {
#[cfg_attr(not(feature = "no-cfi"), cfi_impl_fn)]
pub fn derive(env: &mut FmcEnv) -> CaliptraResult<()> {
HandOff::set_rt_hash_chain_max_svn(env, MAX_RT_SVN as u16);

cprintln!("[hash chain rt] Deriving hash chain");

let fw_epoch = env.persistent_data.get().manifest1.header.owner_data.epoch;
Self::compute_chain(env, &fw_epoch)?;

cprintln!("[hash chain rt] Derivation complete");
report_boot_status(FmcBootStatus::RtHashChainComplete as u32);
Ok(())
}

fn compute_chain(env: &mut FmcEnv, fw_epoch: &[u8]) -> CaliptraResult<()> {
let rt_svn = HandOff::rt_svn(env);
if rt_svn > MAX_RT_SVN {
return Err(CaliptraError::RT_SVN_EXCEEDS_MAX);
} else {
cfi_assert_le(rt_svn, MAX_RT_SVN);
}

let chain_len: u32 = MAX_RT_SVN - rt_svn;
cprintln!(
"[hash chain rt] SVN = {}, max = {}, chain_len = {}",
rt_svn,
MAX_RT_SVN,
chain_len
);

let fmc_cdi = HandOff::fmc_cdi(env);

Crypto::hmac384_kdf(
env,
fmc_cdi,
b"rt_hash_chain",
Some(fw_epoch),
KEY_ID_RT_HASH_CHAIN,
)?;

let mut num_iters: u32 = 0;

for _ in 0..chain_len {
num_iters += 1;
Self::hmac_hash(env, KEY_ID_RT_HASH_CHAIN)?;
}

cfi_assert_eq(num_iters, chain_len);

HandOff::set_rt_hash_chain_kv_hdl(env, KEY_ID_RT_HASH_CHAIN);
Ok(())
}

// "Hashes" a value in-place in KV by HMACing it with empty data.
fn hmac_hash(env: &mut FmcEnv, key_id: KeyId) -> CaliptraResult<()> {
Crypto::hmac384_mac(env, key_id, &Hmac384Data::Slice(&[]), key_id)
}
}
10 changes: 9 additions & 1 deletion fmc/src/flow/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,17 @@ Abstract:

mod crypto;
pub mod dice;
mod hash_chain;
mod pcr;
mod rt_alias;
mod tci;
mod x509;

use crate::flow::hash_chain::HashChain;
use crate::flow::rt_alias::RtAliasLayer;

use crate::fmc_env::FmcEnv;
use crate::hand_off::HandOff;
use caliptra_drivers::CaliptraResult;

/// Execute FMC Flows based on reset resason
Expand All @@ -30,5 +33,10 @@ use caliptra_drivers::CaliptraResult;
///
/// * `env` - FMC Environment
pub fn run(env: &mut FmcEnv) -> CaliptraResult<()> {
RtAliasLayer::run(env)
RtAliasLayer::run(env)?;
HashChain::derive(env)?;

env.key_vault.set_key_use_lock(HandOff::fmc_cdi(env));

Ok(())
}
4 changes: 2 additions & 2 deletions fmc/src/flow/rt_alias.rs
Original file line number Diff line number Diff line change
Expand Up @@ -345,9 +345,9 @@ impl RtAliasLayer {
"[alias rt] Erasing AUTHORITY.KEYID = {}",
auth_priv_key as u8
);
// FMC ensures that CDIFMC and PrivateKeyFMC are locked to block further usage until the next boot.
// FMC ensures that PrivateKeyFMC is locked to block further usage until the next boot.
// CDI is locked later, after it is used in hash chain derivation.
env.key_vault.set_key_use_lock(auth_priv_key);
env.key_vault.set_key_use_lock(input.cdi);

let _pub_x: [u8; 48] = (&pub_key.x).into();
let _pub_y: [u8; 48] = (&pub_key.y).into();
Expand Down
4 changes: 3 additions & 1 deletion fmc/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use core::hint::black_box;
use caliptra_cfi_lib::{cfi_assert_eq, CfiCounter};
use caliptra_common::{
cprintln, handle_fatal_error,
keyids::{KEY_ID_RT_CDI, KEY_ID_RT_PRIV_KEY},
keyids::{KEY_ID_RT_CDI, KEY_ID_RT_HASH_CHAIN, KEY_ID_RT_PRIV_KEY},
};
use caliptra_cpu::{log_trap_record, TrapRecord};

Expand Down Expand Up @@ -80,6 +80,8 @@ pub extern "C" fn entry_point() -> ! {
HandOffDataHandle::from(DataStore::KeyVaultSlot(KEY_ID_RT_CDI));
env.persistent_data.get_mut().fht.rt_priv_key_kv_hdl =
HandOffDataHandle::from(DataStore::KeyVaultSlot(KEY_ID_RT_PRIV_KEY));
env.persistent_data.get_mut().fht.rt_hash_chain_kv_hdl =
HandOffDataHandle::from(DataStore::KeyVaultSlot(KEY_ID_RT_HASH_CHAIN));
HandOff::to_rt(&env);
}
match flow::run(&mut env) {
Expand Down
2 changes: 2 additions & 0 deletions fmc/tests/fmc_integration_tests/test_rtalias.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ const RT_ALIAS_SUBJ_ID_SN_GENERATION_COMPLETE: u32 = 0x403;
const RT_ALIAS_SUBJ_KEY_ID_GENERATION_COMPLETE: u32 = 0x404;
const RT_ALIAS_CERT_SIG_GENERATION_COMPLETE: u32 = 0x405;
const RT_ALIAS_DERIVATION_COMPLETE: u32 = 0x406;
const RT_HASH_CHAIN_COMPLETE: u32 = 0x407;

const PCR_COUNT: usize = 32;
const PCR_ENTRY_SIZE: usize = core::mem::size_of::<PcrLogEntry>();
Expand Down Expand Up @@ -62,6 +63,7 @@ fn test_boot_status_reporting() {
hw.step_until_boot_status(RT_ALIAS_SUBJ_KEY_ID_GENERATION_COMPLETE, true);
hw.step_until_boot_status(RT_ALIAS_CERT_SIG_GENERATION_COMPLETE, true);
hw.step_until_boot_status(RT_ALIAS_DERIVATION_COMPLETE, true);
hw.step_until_boot_status(RT_HASH_CHAIN_COMPLETE, true);
}

#[test]
Expand Down
46 changes: 43 additions & 3 deletions runtime/test-fw/src/mbox_responder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,16 @@

use core::mem::size_of;

use caliptra_common::{handle_fatal_error, mailbox_api::CommandId};
use caliptra_common::{handle_fatal_error, keyids::KEY_ID_TMP, mailbox_api::CommandId};
use caliptra_drivers::{
cprintln,
pcr_log::{PCR_ID_STASH_MEASUREMENT, RT_FW_JOURNEY_PCR},
Array4x12, CaliptraError, CaliptraResult,
};
use caliptra_registers::{mbox::enums::MboxStatusE, soc_ifc::SocIfcReg};
use caliptra_runtime::{
mailbox::Mailbox, ContextState, DpeInstance, Drivers, RtBootStatus, TciMeasurement, U8Bool,
MAX_HANDLES,
handoff::RtHandoff, mailbox::Mailbox, ContextState, DpeInstance, Drivers, Hmac, RtBootStatus,
TciMeasurement, U8Bool, MAX_HANDLES,
};
use caliptra_test_harness::{runtime_handlers, test_suite};
use zerocopy::{AsBytes, FromBytes};
Expand All @@ -31,6 +31,8 @@ const OPCODE_READ_DPE_INSTANCE: u32 = 0xA000_0000;
const OPCODE_CORRUPT_DPE_INSTANCE: u32 = 0xB000_0000;
const OPCODE_READ_PCR_RESET_COUNTER: u32 = 0xC000_0000;
const OPCODE_CORRUPT_DPE_ROOT_TCI: u32 = 0xD000_0000;
const OPCODE_READ_HASH_CHAIN_MAX_SVN: u32 = 0xE000_0000;
const OPCODE_READ_HASH_CHAIN_DIGEST: u32 = 0xF000_0000;
const OPCODE_FW_LOAD: u32 = CommandId::FIRMWARE_LOAD.0;

fn read_request(mbox: &Mailbox) -> &[u8] {
Expand Down Expand Up @@ -210,6 +212,44 @@ pub fn handle_command(drivers: &mut Drivers) -> CaliptraResult<MboxStatusE> {
.tci_current = TciMeasurement(input_bytes.try_into().unwrap());
write_response(&mut drivers.mbox, &[]);
}
CommandId(OPCODE_READ_HASH_CHAIN_MAX_SVN) => {
write_response(
&mut drivers.mbox,
&drivers
.persistent_data
.get()
.fht
.rt_hash_chain_max_svn
.to_le_bytes(),
);
}
// Ratchet the chain forwards a given number of times, then
// compute a digest from the chain.
CommandId(OPCODE_READ_HASH_CHAIN_DIGEST) => {
let handoff = RtHandoff {
data_vault: &drivers.data_vault,
fht: &drivers.persistent_data.get().fht,
};

let hash_chain_kv = handoff.rt_hash_chain().unwrap();
let num_hashes = u32::read_from(read_request(&drivers.mbox)).unwrap();

let hmac_input = if num_hashes == 0 {
hash_chain_kv
} else {
Hmac::hmac384_hash(drivers, hash_chain_kv, KEY_ID_TMP).unwrap();
for _ in 1..num_hashes {
Hmac::hmac384_hash(drivers, KEY_ID_TMP, KEY_ID_TMP).unwrap();
}
KEY_ID_TMP
};

let hmac_output =
Hmac::ecc384_hmac(drivers, hmac_input, b"hmac_label", b"hmac_data").unwrap();

drivers.key_vault.erase_key(KEY_ID_TMP).unwrap();
write_response(&mut drivers.mbox, hmac_output.as_bytes());
}
CommandId(OPCODE_FW_LOAD) => {
unsafe { SocIfcReg::new() }
.regs_mut()
Expand Down
Loading

0 comments on commit 75ed0da

Please sign in to comment.