Skip to content

Commit

Permalink
Implement key ladder in ROM and RT FW.
Browse files Browse the repository at this point in the history
The key ladder is initialized in cold-boot from LDevID CDI. It is lengthened during
update-reset flows to match min-SVN-since-cold-boot.

This PR does not include a smoke test, because the production application firmware
doesn't yet include any APIs for exercising the key ladder.

The additional cycles from this feature depends on the firmware's SVN, and is
roughly 2.7k cycles * (128 - fw_svn). In the worst case, when fw_svn = 0, the
cycle count is ~350k.
  • Loading branch information
bluegate010 committed Jan 7, 2025
1 parent 44c8242 commit 7370ef0
Show file tree
Hide file tree
Showing 20 changed files with 631 additions and 34 deletions.
8 changes: 8 additions & 0 deletions builder/src/firmware.rs
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,13 @@ pub mod runtime_tests {
..RUNTIME_TEST_FWID_BASE
};

// Used to test updates between RT FW images.
pub const MBOX_WITHOUT_UART: FwId = FwId {
bin_name: "mbox",
features: &["riscv", "runtime"],
..RUNTIME_TEST_FWID_BASE
};

pub const PERSISTENT_RT: FwId = FwId {
bin_name: "persistent_rt",
..RUNTIME_TEST_FWID_BASE
Expand Down Expand Up @@ -453,6 +460,7 @@ pub const REGISTERED_FW: &[&FwId] = &[
&rom_tests::TEST_PMP_TESTS,
&runtime_tests::BOOT,
&runtime_tests::MBOX,
&runtime_tests::MBOX_WITHOUT_UART,
&runtime_tests::PERSISTENT_RT,
&runtime_tests::MOCK_RT_INTERACTIVE,
];
12 changes: 7 additions & 5 deletions common/src/boot_status.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@ pub enum RomBootStatus {
FwProcessorExtendPcrComplete = FWPROCESSOR_BOOT_STATUS_BASE + 4,
FwProcessorLoadImageComplete = FWPROCESSOR_BOOT_STATUS_BASE + 5,
FwProcessorFirmwareDownloadTxComplete = FWPROCESSOR_BOOT_STATUS_BASE + 6,
FwProcessorComplete = FWPROCESSOR_BOOT_STATUS_BASE + 7,
FwProcessorCalculateKeyLadderComplete = FWPROCESSOR_BOOT_STATUS_BASE + 7,
FwProcessorComplete = FWPROCESSOR_BOOT_STATUS_BASE + 8,

// FmcAlias Statuses
FmcAliasDeriveCdiComplete = FMCALIAS_BOOT_STATUS_BASE,
Expand All @@ -71,10 +72,11 @@ pub enum RomBootStatus {
UpdateResetLoadManifestComplete = UPDATE_RESET_BOOT_STATUS_BASE + 1,
UpdateResetImageVerificationComplete = UPDATE_RESET_BOOT_STATUS_BASE + 2,
UpdateResetPopulateDataVaultComplete = UPDATE_RESET_BOOT_STATUS_BASE + 3,
UpdateResetExtendPcrComplete = UPDATE_RESET_BOOT_STATUS_BASE + 4,
UpdateResetLoadImageComplete = UPDATE_RESET_BOOT_STATUS_BASE + 5,
UpdateResetOverwriteManifestComplete = UPDATE_RESET_BOOT_STATUS_BASE + 6,
UpdateResetComplete = UPDATE_RESET_BOOT_STATUS_BASE + 7,
UpdateResetExtendKeyLadderComplete = UPDATE_RESET_BOOT_STATUS_BASE + 4,
UpdateResetExtendPcrComplete = UPDATE_RESET_BOOT_STATUS_BASE + 5,
UpdateResetLoadImageComplete = UPDATE_RESET_BOOT_STATUS_BASE + 6,
UpdateResetOverwriteManifestComplete = UPDATE_RESET_BOOT_STATUS_BASE + 7,
UpdateResetComplete = UPDATE_RESET_BOOT_STATUS_BASE + 8,

// ROM Global Boot Statues
CfiInitialized = ROM_GLOBAL_BOOT_STATUS_BASE,
Expand Down
2 changes: 2 additions & 0 deletions common/src/keyids.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ pub const KEY_ID_ROM_FMC_CDI: KeyId = KeyId::KeyId6;
pub const KEY_ID_FMC_ECDSA_PRIV_KEY: KeyId = KeyId::KeyId7;
#[cfg(feature = "rom")]
pub const KEY_ID_FMC_MLDSA_KEYPAIR_SEED: KeyId = KeyId::KeyId8;
#[cfg(any(feature = "rom"))]
pub const KEY_ID_FW_KEY_LADDER: KeyId = KeyId::KeyId2;
#[cfg(feature = "fmc")]
pub const KEY_ID_RT_CDI: KeyId = KeyId::KeyId4;
#[cfg(feature = "fmc")]
Expand Down
10 changes: 5 additions & 5 deletions drivers/src/hand_off.rs
Original file line number Diff line number Diff line change
Expand Up @@ -216,10 +216,10 @@ pub struct FirmwareHandoffTable {
pub rtalias_tbs_size: u16,

/// Maximum value RT FW SVN can take.
pub rt_hash_chain_max_svn: u16,
pub rt_key_ladder_max_svn: u16,

/// Index of RT hash chain value in the Key Vault.
pub rt_hash_chain_kv_hdl: HandOffDataHandle,
/// Index of RT key ladder value in the Key Vault.
pub rt_key_ladder_kv_hdl: HandOffDataHandle,

/// Reserved for future use.
pub reserved: [u8; FHT_RESERVED_SIZE],
Expand Down Expand Up @@ -257,8 +257,8 @@ impl Default for FirmwareHandoffTable {
idev_dice_mldsa_pub_key_load_addr: 0,
rom_info_addr: RomAddr::new(FHT_INVALID_ADDRESS),
rtalias_tbs_size: 0,
rt_hash_chain_max_svn: 0,
rt_hash_chain_kv_hdl: HandOffDataHandle(0),
rt_key_ladder_max_svn: 0,
rt_key_ladder_kv_hdl: HandOffDataHandle(0),
reserved: [0u8; FHT_RESERVED_SIZE],
}
}
Expand Down
5 changes: 4 additions & 1 deletion error/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -459,7 +459,7 @@ impl CaliptraError {
CaliptraError::new_const(0x000E0039);
pub const RUNTIME_PRIV_KEY_KV_HDL_HANDOFF_FAILED: CaliptraError =
CaliptraError::new_const(0x000E003A);
pub const RUNTIME_HASH_CHAIN_HANDOFF_FAILED: CaliptraError =
pub const RUNTIME_KEY_LADDER_HANDOFF_FAILED: CaliptraError =
CaliptraError::new_const(0x000E003B);
/// PCR Runtime Errors
pub const RUNTIME_PCR_RESERVED: CaliptraError = CaliptraError::new_const(0x000E003C);
Expand Down Expand Up @@ -510,6 +510,8 @@ impl CaliptraError {
CaliptraError::new_const(0x000E0054);
pub const RUNTIME_AUTH_MANIFEST_LMS_OWNER_PUB_KEY_INVALID: CaliptraError =
CaliptraError::new_const(0x000E0055);
pub const RUNTIME_KEY_LADDER_TARGET_SVN_TOO_LARGE: CaliptraError =
CaliptraError::new_const(0x000E0056);

/// FMC Errors
pub const FMC_GLOBAL_NMI: CaliptraError = CaliptraError::new_const(0x000F0001);
Expand Down Expand Up @@ -568,6 +570,7 @@ impl CaliptraError {
pub const FW_PROC_MAILBOX_RESERVED_PAUSER: CaliptraError = CaliptraError::new_const(0x01020009);
pub const FW_PROC_MAILBOX_GET_IDEV_CSR_UNPROVISIONED_CSR: CaliptraError =
CaliptraError::new_const(0x0102000A);
pub const FW_PROC_SVN_TOO_LARGE: CaliptraError = CaliptraError::new_const(0x0102000B);

/// FMC Alias Layer : Certificate Verification Failure.
pub const FMC_ALIAS_CERT_VERIFY: CaliptraError = CaliptraError::new_const(0x01030001);
Expand Down
8 changes: 4 additions & 4 deletions fmc/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -306,14 +306,14 @@ This field provides the size of the *To Be Signed* portion of the Runtime Alias

This field provides the size of the *To Be Signed* portion of the Runtime Alias MLDSA certificate.

### rt_hash_chain_max_svn
### rt_key_ladder_max_svn

This field informs firmware of the maximum RT SVN, which value was used
to determine the length of RT FW's hash chain.
to determine the length of RT FW's key ladder.

### rt_hash_chain_kv_hdl
### rt_key_ladder_kv_hdl

This field provides the Handle into the Key Vault where RT's hash chain is stored.
This field provides the Handle into the Key Vault where RT's key ladder is stored.

### reserved

Expand Down
3 changes: 0 additions & 3 deletions fmc/src/boot_status.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,6 @@ pub enum FmcBootStatus {
RtAliasSubjKeyIdGenerationComplete = RTALIAS_BOOT_STATUS_BASE + 4,
RtAliasCertSigGenerationComplete = RTALIAS_BOOT_STATUS_BASE + 5,
RtAliasDerivationComplete = RTALIAS_BOOT_STATUS_BASE + 6,

// Hash chain statuses
RtHashChainComplete = RTALIAS_BOOT_STATUS_BASE + 7,
}

impl From<FmcBootStatus> for u32 {
Expand Down
12 changes: 11 additions & 1 deletion rom/dev/src/fht.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,14 @@ use crate::{rom_env::RomEnv, CALIPTRA_ROM_INFO};
#[cfg(not(feature = "no-cfi"))]
use caliptra_cfi_derive::cfi_mod_fn;
use caliptra_common::{
keyids::{KEY_ID_FMC_ECDSA_PRIV_KEY, KEY_ID_FMC_MLDSA_KEYPAIR_SEED, KEY_ID_ROM_FMC_CDI},
keyids::{
KEY_ID_FMC_ECDSA_PRIV_KEY, KEY_ID_FMC_MLDSA_KEYPAIR_SEED, KEY_ID_FW_KEY_LADDER,
KEY_ID_ROM_FMC_CDI,
},
FirmwareHandoffTable, HandOffDataHandle, Vault, FHT_INVALID_HANDLE, FHT_MARKER,
};
use caliptra_drivers::{cprintln, RomAddr};
use caliptra_image_verify::MAX_RUNTIME_SVN;

const FHT_MAJOR_VERSION: u16 = 1;
const FHT_MINOR_VERSION: u16 = 0;
Expand All @@ -40,6 +44,10 @@ impl FhtDataStore {
pub const fn fmc_mldsa_keypair_seed_store() -> HandOffDataHandle {
HandOffDataHandle(((Vault::KeyVault as u32) << 12) | KEY_ID_FMC_MLDSA_KEYPAIR_SEED as u32)
}
/// The firmware key ladder is stored in a KeyVault slot.
pub const fn fw_key_ladder_store() -> HandOffDataHandle {
HandOffDataHandle(((Vault::KeyVault as u32) << 12) | KEY_ID_FW_KEY_LADDER as u32)
}
}

#[cfg_attr(not(feature = "no-cfi"), cfi_mod_fn)]
Expand All @@ -65,6 +73,8 @@ pub fn initialize_fht(env: &mut RomEnv) {
pcr_log_addr: &pdata.pcr_log as *const _ as u32,
meas_log_addr: &pdata.measurement_log as *const _ as u32,
fuse_log_addr: &pdata.fuse_log as *const _ as u32,
rt_key_ladder_max_svn: MAX_RUNTIME_SVN as u16,
rt_key_ladder_kv_hdl: FhtDataStore::fw_key_ladder_store(),
..Default::default()
};
}
34 changes: 34 additions & 0 deletions rom/dev/src/flow/cold_reset/fw_processor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,11 @@ Abstract:
File contains the code to download and validate the firmware.
--*/

#[cfg(feature = "fake-rom")]
use crate::flow::fake::FakeRomImageVerificationEnv;
use crate::fuse::log_fuse_data;
use crate::key_ladder;
use crate::pcr;
use crate::rom_env::RomEnv;
use crate::run_fips_tests;
Expand All @@ -30,8 +32,10 @@ use caliptra_common::{
pcr::PCR_ID_STASH_MEASUREMENT, verifier::FirmwareImageVerificationEnv, FuseLogEntryId,
PcrLogEntry, PcrLogEntryId, RomBootStatus::*,
};
use caliptra_drivers::printer::HexBytes;
use caliptra_drivers::{pcr_log::MeasurementLogEntry, *};
use caliptra_image_types::{ImageManifest, IMAGE_BYTE_SIZE};
use caliptra_image_verify::MAX_RUNTIME_SVN;
use caliptra_image_verify::{ImageVerificationInfo, ImageVerificationLogInfo, ImageVerifier};
use caliptra_kat::KatsEnv;
use caliptra_x509::{NotAfter, NotBefore};
Expand Down Expand Up @@ -151,6 +155,8 @@ impl FirmwareProcessor {
// Get the certificate validity info
let (nb, nf) = Self::get_cert_validity_info(manifest);

Self::populate_fw_key_ladder(env)?;

report_boot_status(FwProcessorComplete.into());
Ok(FwProcInfo {
fmc_cert_valid_not_before: nb,
Expand Down Expand Up @@ -573,6 +579,34 @@ impl FirmwareProcessor {
report_boot_status(FwProcessorPopulateDataVaultComplete.into());
}

#[cfg_attr(not(feature = "no-cfi"), cfi_impl_fn)]
fn populate_fw_key_ladder(env: &mut RomEnv) -> CaliptraResult<()> {
let epoch = env.persistent_data.get().manifest1.header.owner_data.epoch;
let svn = env.persistent_data.get().data_vault.rt_svn();

if svn > MAX_RUNTIME_SVN {
// If this occurs it is an internal programming error.
Err(CaliptraError::FW_PROC_SVN_TOO_LARGE)?;
}

let chain_len = MAX_RUNTIME_SVN - svn;

cprintln!(
"[fwproc] Initializing chain with epoch {}, length {} (max {})",
HexBytes(&epoch),
chain_len,
MAX_RUNTIME_SVN
);

key_ladder::initialize_key_ladder(env, epoch, chain_len)?;

cprintln!("[fwproc] Chain initialized");

report_boot_status(FwProcessorCalculateKeyLadderComplete.into());

Ok(())
}

/// Process the certificate validity info
///
/// # Arguments
Expand Down
2 changes: 1 addition & 1 deletion rom/dev/src/flow/fake.rs
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ impl FakeRomFlow {
}
}

// Used to derive the firmware's hash chain.
// Used to derive the firmware's key ladder.
fn initialize_fake_ldevid_cdi(env: &mut RomEnv) -> CaliptraResult<()> {
env.hmac.hmac(
&HmacKey::Array4x12(&Array4x12::default()),
Expand Down
27 changes: 22 additions & 5 deletions rom/dev/src/flow/update_reset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,15 @@ Abstract:
--*/
#[cfg(feature = "fake-rom")]
use crate::flow::fake::FakeRomImageVerificationEnv;
use crate::key_ladder;
use crate::{cprintln, pcr, rom_env::RomEnv};
#[cfg(not(feature = "no-cfi"))]
use caliptra_cfi_derive::cfi_impl_fn;
use caliptra_common::mailbox_api::CommandId;
use caliptra_common::verifier::FirmwareImageVerificationEnv;
use caliptra_common::RomBootStatus::*;
use caliptra_drivers::report_fw_error_non_fatal;
use caliptra_drivers::{okref, report_boot_status, MailboxRecvTxn, ResetReason};
use caliptra_drivers::{report_fw_error_non_fatal, Hmac, Trng};
use caliptra_drivers::{DataVault, PersistentData};
use caliptra_error::{CaliptraError, CaliptraResult};
use caliptra_image_types::ImageManifest;
Expand Down Expand Up @@ -82,7 +83,7 @@ impl UpdateResetFlow {

// Populate data vault
let data_vault = &mut env.persistent_data.get_mut().data_vault;
Self::populate_data_vault(data_vault, info);
Self::populate_data_vault(data_vault, info, &mut env.hmac, &mut env.trng)?;

// Extend PCR0 and PCR1
pcr::extend_pcrs(
Expand Down Expand Up @@ -216,16 +217,32 @@ impl UpdateResetFlow {
///
/// * `env` - ROM Environment
/// * `info` - Image Verification Info
fn populate_data_vault(data_vault: &mut DataVault, info: &ImageVerificationInfo) {
/// * `hmac` - HMAC helper
/// * `trng` - TRNG helper
fn populate_data_vault(
data_vault: &mut DataVault,
info: &ImageVerificationInfo,
hmac: &mut Hmac,
trng: &mut Trng,
) -> CaliptraResult<()> {
data_vault.set_rt_tci(&info.runtime.digest.into());

let cur_min_svn = data_vault.rt_min_svn();
let new_min_svn = core::cmp::min(cur_min_svn, info.fw_svn);
let old_min_svn = data_vault.rt_min_svn();
let new_min_svn = core::cmp::min(old_min_svn, info.fw_svn);

data_vault.set_rt_svn(info.fw_svn);
data_vault.set_rt_min_svn(new_min_svn);
data_vault.set_rt_entry_point(info.runtime.entry_point);

report_boot_status(UpdateResetPopulateDataVaultComplete.into());

// Extend the key ladder if the min-SVN is being decremented.
let decrement_by = old_min_svn - new_min_svn;
cprintln!("[update-reset] Extending key ladder by {}", decrement_by);

key_ladder::extend_key_ladder(hmac, trng, decrement_by)?;
report_boot_status(UpdateResetExtendKeyLadderComplete.into());

Ok(())
}
}
Loading

0 comments on commit 7370ef0

Please sign in to comment.