Skip to content

Commit 24205a8

Browse files
authored
[feat] Add Stable Owner Key derivation from HEK seed (#3625)
* [feat] Add Stable Owner Key derivation from HEK seed ROM derives a Stable Owner Root Key from HEK seed via HKDF into KV15 during cold boot. CM_DERIVE_STABLE_KEY gains OwnerKey (0x3) which uses CMAC-KDF + HMAC-KDF with label "Stable Owner Key" to produce child keys as encrypted CMKs. Both ROM firmware processor and runtime handle the new key type with identical derivation logic. Adds integration tests for all three key types (IDevId, LDevId, OwnerKey) and a separate test verifying different personalization seeds produce different keys. * Addressing feedback + updating ROM hash * Addressing PR feedback * Addressing PR feedback * Additional tests * Addressing feedback * Adding derivation documentation * Updating ROM hash * Filtering tests for FPGA
1 parent d703860 commit 24205a8

16 files changed

Lines changed: 969 additions & 102 deletions

File tree

FROZEN_IMAGES.sha384sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
# WARNING: Do not update this file without the approval of the Caliptra TAC
2-
015d6f71f1fff828e2152a6c245574f01e8be137e0a379f1ec95aa03b5abdee3e72a7524cae7a710cfb6b4c3f0683c8d caliptra-rom-no-log.bin
3-
cc1e28b0ab4ca2fe636f5a88f31aff803285472feefbeceb261754591f47552ae1fc008ff11a3da405874f6ab232de46 caliptra-rom-with-log.bin
2+
3befdf9287ced84ee193c4f960841919cc37a21647143671f61dbebcc252355dc643340edb456a69d83a6ad01c314fef caliptra-rom-no-log.bin
3+
bbbc22da3331d2c8af8961f5db5123cdcd4dd7c1ee9d152bb296ad1effb0ea4e4f9f531e2923e268a943dd4cac69cae8 caliptra-rom-with-log.bin

api/src/mailbox.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4934,13 +4934,15 @@ pub enum CmStableKeyType {
49344934
Reserved = 0,
49354935
IDevId,
49364936
LDevId,
4937+
OwnerKey,
49374938
}
49384939

49394940
impl From<u32> for CmStableKeyType {
49404941
fn from(val: u32) -> Self {
49414942
match val {
49424943
1_u32 => CmStableKeyType::IDevId,
49434944
2_u32 => CmStableKeyType::LDevId,
4945+
3_u32 => CmStableKeyType::OwnerKey,
49444946
_ => CmStableKeyType::Reserved,
49454947
}
49464948
}
@@ -4951,6 +4953,7 @@ impl From<CmStableKeyType> for u32 {
49514953
match val {
49524954
CmStableKeyType::IDevId => 1,
49534955
CmStableKeyType::LDevId => 2,
4956+
CmStableKeyType::OwnerKey => 3,
49544957
CmStableKeyType::Reserved => 0,
49554958
}
49564959
}

common/src/boot_status.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ pub enum RomBootStatus {
3535
IDevIdMakeCsrEnvelopeComplete = IDEVID_BOOT_STATUS_BASE + 7,
3636
IDevIdSendCsrEnvelopeComplete = IDEVID_BOOT_STATUS_BASE + 8,
3737
IDevIdDerivationComplete = IDEVID_BOOT_STATUS_BASE + 9,
38+
IDevIdDecryptHekSeedComplete = IDEVID_BOOT_STATUS_BASE + 10,
39+
IDevIdStableOwnerRootKeyDerivationComplete = IDEVID_BOOT_STATUS_BASE + 11,
3840

3941
// Ldevid Statuses
4042
LDevIdCdiDerivationComplete = LDEVID_BOOT_STATUS_BASE,

common/src/keyids.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,13 @@ pub const KEY_ID_DPE_PRIV_KEY: KeyId = KeyId::KeyId11;
4747
pub const KEY_ID_EXPORTED_DPE_CDI: KeyId = KeyId::KeyId12;
4848
pub const KEY_ID_STABLE_IDEV: KeyId = KeyId::KeyId0;
4949
pub const KEY_ID_STABLE_LDEV: KeyId = KeyId::KeyId1;
50+
pub const KEY_ID_STABLE_OWNER: KeyId = KeyId::KeyId15;
5051

5152
pub const KEY_ID_TMP: KeyId = KeyId::KeyId3;
5253

54+
#[cfg(feature = "rom")]
55+
pub const KEY_ID_HEK_SEED: KeyId = KeyId::KeyId14;
56+
5357
pub mod ocp_lock {
5458
use super::KeyId;
5559

drivers/src/soc_ifc.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -644,6 +644,24 @@ impl SocIfc {
644644
)
645645
}
646646

647+
/// Check if the Stable Owner Key feature is enabled via SS_STRAP_GENERIC[3] bit 0.
648+
pub fn stable_owner_key_enabled(&self) -> bool {
649+
self.soc_ifc.regs().ss_strap_generic().at(3).read() & 1 != 0
650+
}
651+
652+
/// Check if the Stable Owner Key feature is available.
653+
/// Requires subsystem mode, the strap bit to be set, and OCP LOCK
654+
/// to be disabled (both consume fuse_hek_seed via DOE and are
655+
/// mutually exclusive).
656+
///
657+
/// TODO(2.2): When a dedicated DOE path and fuse register are added for
658+
/// the stable owner key seed, the OCP LOCK check here should be relaxed.
659+
/// The two features will only conflict when the stable owner key is
660+
/// derived from the shared HEK seed (2.1 path), not from a dedicated seed.
661+
pub fn stable_owner_key_available(&self) -> bool {
662+
self.subsystem_mode() && self.stable_owner_key_enabled() && !self.ocp_lock_enabled()
663+
}
664+
647665
pub fn otp_dai_idle_bit_num(&self) -> u32 {
648666
(self.soc_ifc.regs().ss_strap_generic().at(0).read() >> 16) & 0xFFFF
649667
}

error/src/lib.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2724,6 +2724,11 @@ impl CaliptraError {
27242724
0xa005_5000,
27252725
"DOT Error: Invalid key type"
27262726
),
2727+
(
2728+
CMB_STABLE_OWNER_KEY_NOT_AVAILABLE,
2729+
0xa005_5001,
2730+
"Crypto Mailbox Error: Stable Owner Key is not available when OCP LOCK is enabled"
2731+
),
27272732
(
27282733
CMB_HMAC_INVALID_ENC_CMK,
27292734
0xa005_5020,

hw-model/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,8 @@ pub struct InitParams<'a> {
251251

252252
pub ocp_lock_en: bool,
253253

254+
pub stable_owner_key_en: bool,
255+
254256
pub uds_fuse_row_granularity_64: bool,
255257

256258
pub otp_dai_idle_bit_offset: u32,
@@ -343,6 +345,7 @@ impl Default for InitParams<'_> {
343345
dbg_manuf_service: Default::default(),
344346
subsystem_mode: false,
345347
ocp_lock_en: cfg!(feature = "ocp-lock"),
348+
stable_owner_key_en: false,
346349
uds_fuse_row_granularity_64: true,
347350
otp_dai_idle_bit_offset: 30,
348351
otp_direct_access_cmd_reg_offset: 0x80,

hw-model/src/model_emulated.rs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -242,9 +242,17 @@ impl HwModel for ModelEmulated {
242242

243243
let ss_strap_generic_reg_0 = params.otp_dai_idle_bit_offset << 16;
244244
let ss_strap_generic_reg_1 = params.otp_direct_access_cmd_reg_offset;
245-
root_bus
246-
.soc_reg
247-
.set_strap_generic(&[ss_strap_generic_reg_0, ss_strap_generic_reg_1, 0, 0]);
245+
let ss_strap_generic_reg_3 = if params.stable_owner_key_en {
246+
1u32
247+
} else {
248+
0u32
249+
};
250+
root_bus.soc_reg.set_strap_generic(&[
251+
ss_strap_generic_reg_0,
252+
ss_strap_generic_reg_1,
253+
0,
254+
ss_strap_generic_reg_3,
255+
]);
248256

249257
{
250258
let mut iccm_ram = root_bus.iccm.ram().borrow_mut();

rom/dev/README.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,23 @@ The ROM configures the entropy source (CSRNG) during initialization using the fo
9595
- In debug mode (`debug_locked == false`), entropy source configuration registers remain unlocked for characterization.
9696
- In production mode, ROM locks the entropy source configuration after programming to prevent modification.
9797

98+
### Stable Owner Key Root Derivation
99+
100+
The Stable Owner Key feature is only available in subsystem mode when OCP LOCK is disabled and the following subsystem strap is set:
101+
102+
| Register | Field/Bits | Description |
103+
| :------------------------------- | :--------- | :------------------------------------------------------ |
104+
| SS_STRAP_GENERIC[3] | [0] | Stable Owner Key enable. When set to 1, ROM derives the Stable Owner Root Key from the HEK seed and allows `CM_DERIVE_STABLE_KEY` with `key_type = OwnerKey` when the other availability requirements are met. When clear, Stable Owner Key derivation is disabled. |
105+
106+
When the feature is available, ROM derives the Stable Owner Root Key during the IDevID stage before clearing DOE secrets:
107+
108+
1. DOE decrypts the obfuscated HEK seed into `KEY_ID_HEK_SEED` (`KeyId14`) with HMAC block usage.
109+
2. HKDF-Extract uses HMAC-SHA512 with salt `stable_owner_root_key`, zero-padded to 64 bytes, and reads `KEY_ID_HEK_SEED` as HMAC block data. The resulting PRK overwrites `KEY_ID_HEK_SEED` with HMAC key usage.
110+
3. HKDF-Expand uses HMAC-SHA512 with the PRK and label `stable_owner_root_key` to populate `KEY_ID_STABLE_OWNER` (`KeyId15`) with AES key usage.
111+
4. ROM write-locks `KEY_ID_STABLE_OWNER` and erases the temporary `KEY_ID_HEK_SEED` slot.
112+
113+
If subsystem mode is not active, the strap is clear, or OCP LOCK is enabled, ROM skips this derivation and `CM_DERIVE_STABLE_KEY` with `key_type = OwnerKey` is unavailable.
114+
98115
For a comprehensive overview of the SOC interface registers, please refer to the following link::
99116
https://chipsalliance.github.io/caliptra-rtl/main/external-regs/?p=caliptra_top_reg.generic_and_fuse_reg
100117

rom/dev/src/flow/cold_reset/fw_processor/mod.rs

Lines changed: 56 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -424,14 +424,25 @@ impl FirmwareProcessor {
424424
CommandId::GET_IDEV_MLDSA87_CSR => {
425425
GetIdevMldsa87CsrCmd::execute(cmd_bytes, persistent_data, resp)?
426426
}
427-
CommandId::CM_DERIVE_STABLE_KEY => CmDeriveStableKeyCmd::execute(
428-
cmd_bytes,
429-
env.aes_gcm,
430-
env.hmac,
431-
env.trng,
432-
persistent_data,
433-
resp,
434-
)?,
427+
CommandId::CM_DERIVE_STABLE_KEY => {
428+
// Reject OwnerKey if the Stable Owner Key feature is not available.
429+
let req = CmDeriveStableKeyReq::ref_from_bytes(cmd_bytes)
430+
.map_err(|_| CaliptraError::FW_PROC_MAILBOX_INVALID_REQUEST_LENGTH)?;
431+
let key_type: CmStableKeyType = req.key_type.into();
432+
if key_type == CmStableKeyType::OwnerKey
433+
&& !soc_ifc.stable_owner_key_available()
434+
{
435+
Err(CaliptraError::CMB_STABLE_OWNER_KEY_NOT_AVAILABLE)?;
436+
}
437+
CmDeriveStableKeyCmd::execute(
438+
cmd_bytes,
439+
env.aes_gcm,
440+
env.hmac,
441+
env.trng,
442+
persistent_data,
443+
resp,
444+
)?
445+
}
435446
CommandId::CM_RANDOM_GENERATE => {
436447
CmRandomGenerateCmd::execute(cmd_bytes, env.trng, resp)?
437448
}
@@ -1124,26 +1135,48 @@ impl FirmwareProcessor {
11241135
CmStableKeyType::LDevId => AesKey::KV(KeyReadArgs::new(
11251136
caliptra_common::keyids::KEY_ID_STABLE_LDEV,
11261137
)),
1138+
CmStableKeyType::OwnerKey => AesKey::KV(KeyReadArgs::new(
1139+
caliptra_common::keyids::KEY_ID_STABLE_OWNER,
1140+
)),
11271141
CmStableKeyType::Reserved => Err(CaliptraError::DOT_INVALID_KEY_TYPE)?,
11281142
};
11291143
let k0 = cmac_kdf(aes, aes_key, &request.info, None, 4)?;
11301144

1131-
// Prepend "DOT Final" to info and use as label for HMAC KDF
1132-
const PREFIX: &[u8] = b"DOT Final";
1133-
let mut data = [0u8; CM_STABLE_KEY_INFO_SIZE_BYTES + PREFIX.len()];
1134-
data[..PREFIX.len()].copy_from_slice(PREFIX);
1135-
data[PREFIX.len()..].copy_from_slice(&request.info);
1136-
1145+
// Prepend a domain-separation prefix to info and use as label for HMAC KDF
1146+
const DOT_PREFIX: &[u8] = b"DOT Final";
1147+
const OWNER_PREFIX: &[u8] = b"Stable Owner Key";
11371148
let mut tag: Array4x16 = Array4x16::default();
1138-
hmac_kdf(
1139-
hmac,
1140-
HmacKey::Array4x16(&Array4x16::from(k0)),
1141-
&data[..],
1142-
None,
1143-
trng,
1144-
HmacTag::Array4x16(&mut tag),
1145-
HmacMode::Hmac512,
1146-
)?;
1149+
match key_type {
1150+
CmStableKeyType::OwnerKey => {
1151+
let mut data = [0u8; CM_STABLE_KEY_INFO_SIZE_BYTES + OWNER_PREFIX.len()];
1152+
data[..OWNER_PREFIX.len()].copy_from_slice(OWNER_PREFIX);
1153+
data[OWNER_PREFIX.len()..].copy_from_slice(&request.info);
1154+
hmac_kdf(
1155+
hmac,
1156+
HmacKey::Array4x16(&Array4x16::from(k0)),
1157+
&data,
1158+
None,
1159+
trng,
1160+
HmacTag::Array4x16(&mut tag),
1161+
HmacMode::Hmac512,
1162+
)?;
1163+
}
1164+
CmStableKeyType::IDevId | CmStableKeyType::LDevId => {
1165+
let mut data = [0u8; CM_STABLE_KEY_INFO_SIZE_BYTES + DOT_PREFIX.len()];
1166+
data[..DOT_PREFIX.len()].copy_from_slice(DOT_PREFIX);
1167+
data[DOT_PREFIX.len()..].copy_from_slice(&request.info);
1168+
hmac_kdf(
1169+
hmac,
1170+
HmacKey::Array4x16(&Array4x16::from(k0)),
1171+
&data,
1172+
None,
1173+
trng,
1174+
HmacTag::Array4x16(&mut tag),
1175+
HmacMode::Hmac512,
1176+
)?;
1177+
}
1178+
CmStableKeyType::Reserved => Err(CaliptraError::DOT_INVALID_KEY_TYPE)?,
1179+
}
11471180

11481181
let mut key_material = [0u8; 64];
11491182
for (i, word) in tag.0.iter().enumerate() {

0 commit comments

Comments
 (0)