From 07bf939be220915b32e0c485eb8227a31c0a4652 Mon Sep 17 00:00:00 2001 From: Carl Lundin Date: Wed, 16 Oct 2024 14:46:15 -0700 Subject: [PATCH] Add command to retrieve IDevID CSR from persistent storage. * This resolves https://github.com/chipsalliance/caliptra-sw/issues/1687, for ROM. --- FROZEN_IMAGES.sha384sum | 4 +- api/src/mailbox.rs | 41 +++++++++++ drivers/src/lib.rs | 5 +- drivers/src/memory_layout.rs | 12 ++- drivers/src/persistent.rs | 73 ++++++++++++++++++- error/src/lib.rs | 6 ++ hw-model/src/lib.rs | 9 +++ rom/dev/README.md | 1 + rom/dev/src/flow/cold_reset/fw_processor.rs | 27 ++++++- rom/dev/src/flow/cold_reset/idev_id.rs | 45 +++++++++--- rom/dev/src/flow/cold_reset/x509.rs | 23 ------ rom/dev/tests/rom_integration_tests/main.rs | 1 + .../tests_get_idev_csr.rs | 70 ++++++++++++++++++ runtime/README.md | 27 +++++++ runtime/src/get_idev_csr.rs | 56 ++++++++++++++ runtime/src/lib.rs | 3 + .../tests/runtime_integration_tests/common.rs | 9 +++ .../tests/runtime_integration_tests/main.rs | 1 + .../test_get_idev_csr.rs | 63 ++++++++++++++++ 19 files changed, 432 insertions(+), 44 deletions(-) create mode 100644 rom/dev/tests/rom_integration_tests/tests_get_idev_csr.rs create mode 100644 runtime/src/get_idev_csr.rs create mode 100644 runtime/tests/runtime_integration_tests/test_get_idev_csr.rs diff --git a/FROZEN_IMAGES.sha384sum b/FROZEN_IMAGES.sha384sum index 97e4b9ff3c..317e19ab6e 100644 --- a/FROZEN_IMAGES.sha384sum +++ b/FROZEN_IMAGES.sha384sum @@ -1,3 +1,3 @@ # WARNING: Do not update this file without the approval of the Caliptra TAC -ad1064cba5b190e4f5258c175b7ec7c697ccb188ba0ed18302c6e924f0ea0b10457dc753d6d2963f415fbaf761eace96 caliptra-rom-no-log.bin -2aae08cbe6e13559201fee432a7bbf535d27d94df4e8b4d05adab11c4f27b57c6d8392b71a24ee459af7b9663ce9c31f caliptra-rom-with-log.bin +39297297d917e76c678f0e3ecc67b1585269aa47b41efcd081f5d07c40c1379201e1e287a11321a7ad4e437c741d87b9 caliptra-rom-no-log.bin +bfc1ba46d404bff6496ed9aa04183b89a2e589ff928baf21b882d25e9961f2daccfab7b215c29bdccfd58bd4ae88e79d caliptra-rom-with-log.bin diff --git a/api/src/mailbox.rs b/api/src/mailbox.rs index ed666ea4e0..34a338ef0f 100644 --- a/api/src/mailbox.rs +++ b/api/src/mailbox.rs @@ -51,6 +51,9 @@ impl CommandId { // The authorize and stash command. pub const AUTHORIZE_AND_STASH: Self = Self(0x4154_5348); // "ATSH" + + // The get IDevID CSR command. + pub const GET_IDEV_CSR: Self = Self(0x4944_4352); // "IDCR" } impl From for CommandId { @@ -151,6 +154,7 @@ pub enum MailboxResp { QuotePcrs(QuotePcrsResp), CertifyKeyExtended(CertifyKeyExtendedResp), AuthorizeAndStash(AuthorizeAndStashResp), + GetIdevIdCsr(GetIdevIdCsrResp), } impl MailboxResp { @@ -171,6 +175,7 @@ impl MailboxResp { MailboxResp::QuotePcrs(resp) => Ok(resp.as_bytes()), MailboxResp::CertifyKeyExtended(resp) => Ok(resp.as_bytes()), MailboxResp::AuthorizeAndStash(resp) => Ok(resp.as_bytes()), + MailboxResp::GetIdevIdCsr(resp) => Ok(resp.as_bytes()), } } @@ -191,6 +196,7 @@ impl MailboxResp { MailboxResp::QuotePcrs(resp) => Ok(resp.as_bytes_mut()), MailboxResp::CertifyKeyExtended(resp) => Ok(resp.as_bytes_mut()), MailboxResp::AuthorizeAndStash(resp) => Ok(resp.as_bytes_mut()), + MailboxResp::GetIdevIdCsr(resp) => Ok(resp.as_bytes_mut()), } } @@ -458,6 +464,7 @@ pub struct GetIdevInfoResp { pub struct GetLdevCertReq { header: MailboxReqHeader, } + impl Request for GetLdevCertReq { const ID: CommandId = CommandId::GET_LDEV_CERT; type Resp = GetLdevCertResp; @@ -976,6 +983,40 @@ impl Default for SetAuthManifestReq { } } +// GET_IDEVID_CSR +#[repr(C)] +#[derive(Default, Debug, AsBytes, FromBytes, PartialEq, Eq)] +pub struct GetIdevIdCsrReq { + pub hdr: MailboxReqHeader, +} + +impl Request for GetIdevIdCsrReq { + const ID: CommandId = CommandId::GET_IDEV_CSR; + type Resp = GetIdevIdCsrResp; +} + +#[repr(C)] +#[derive(Debug, AsBytes, FromBytes, PartialEq, Eq)] +pub struct GetIdevIdCsrResp { + pub hdr: MailboxRespHeader, + pub data_size: u32, + pub data: [u8; Self::DATA_MAX_SIZE], +} +impl GetIdevIdCsrResp { + pub const DATA_MAX_SIZE: usize = 512; +} +impl ResponseVarSize for GetIdevIdCsrResp {} + +impl Default for GetIdevIdCsrResp { + fn default() -> Self { + Self { + hdr: MailboxRespHeader::default(), + data_size: 0, + data: [0u8; Self::DATA_MAX_SIZE], + } + } +} + #[repr(u32)] #[derive(Debug, PartialEq, Eq)] pub enum ImageHashSource { diff --git a/drivers/src/lib.rs b/drivers/src/lib.rs index 23ccd648cd..e184a3b42f 100644 --- a/drivers/src/lib.rs +++ b/drivers/src/lib.rs @@ -89,8 +89,9 @@ pub use pcr_reset::PcrResetCounter; #[cfg(feature = "runtime")] pub use persistent::{AuthManifestImageMetadataList, AUTH_MANIFEST_IMAGE_METADATA_LIST_MAX_COUNT}; pub use persistent::{ - FuseLogArray, PcrLogArray, PersistentData, PersistentDataAccessor, StashMeasurementArray, - FUSE_LOG_MAX_COUNT, MEASUREMENT_MAX_COUNT, PCR_LOG_MAX_COUNT, + FuseLogArray, IdevIdCsr, PcrLogArray, PersistentData, PersistentDataAccessor, + StashMeasurementArray, FUSE_LOG_MAX_COUNT, MAX_CSR_SIZE, MEASUREMENT_MAX_COUNT, + PCR_LOG_MAX_COUNT, }; pub use pic::{IntSource, Pic}; pub use sha1::{Sha1, Sha1Digest, Sha1DigestOp}; diff --git a/drivers/src/memory_layout.rs b/drivers/src/memory_layout.rs index 0d4ec9c0c4..669a1f2326 100644 --- a/drivers/src/memory_layout.rs +++ b/drivers/src/memory_layout.rs @@ -40,7 +40,8 @@ pub const FUSE_LOG_ORG: u32 = 0x50005000; pub const DPE_ORG: u32 = 0x50005400; pub const PCR_RESET_COUNTER_ORG: u32 = 0x50006800; pub const AUTH_MAN_IMAGE_METADATA_LIST_ORG: u32 = 0x50006C00; -pub const DATA_ORG: u32 = 0x50007000; +pub const IDEVID_CSR_ORG: u32 = 0x50007000; +pub const DATA_ORG: u32 = 0x50007400; pub const STACK_ORG: u32 = 0x5001A000; pub const ROM_STACK_ORG: u32 = 0x5001C000; @@ -72,7 +73,8 @@ pub const FUSE_LOG_SIZE: u32 = 1024; pub const DPE_SIZE: u32 = 5 * 1024; pub const PCR_RESET_COUNTER_SIZE: u32 = 1024; pub const AUTH_MAN_IMAGE_METADATA_LIST_MAX_SIZE: u32 = 1024; -pub const DATA_SIZE: u32 = 76 * 1024; +pub const IDEVID_CSR_SIZE: u32 = 1024; +pub const DATA_SIZE: u32 = 75 * 1024; pub const STACK_SIZE: u32 = 22 * 1024; pub const ROM_STACK_SIZE: u32 = 14 * 1024; pub const ESTACK_SIZE: u32 = 1024; @@ -153,6 +155,12 @@ fn mem_layout_test_pcr_reset_counter() { ); } +#[test] +#[allow(clippy::assertions_on_constants)] +fn mem_layout_test_idevid_csr() { + assert_eq!((DATA_ORG - IDEVID_CSR_ORG), IDEVID_CSR_SIZE); +} + #[test] #[allow(clippy::assertions_on_constants)] fn mem_layout_test_data() { diff --git a/drivers/src/persistent.rs b/drivers/src/persistent.rs index b4b0ee0d57..c44ba5a2d4 100644 --- a/drivers/src/persistent.rs +++ b/drivers/src/persistent.rs @@ -6,6 +6,8 @@ use core::{marker::PhantomData, mem::size_of, ptr::addr_of}; use caliptra_auth_man_types::AuthManifestImageMetadata; #[cfg(feature = "runtime")] use caliptra_auth_man_types::AuthManifestImageMetadataCollection; +use caliptra_error::CaliptraError; +use caliptra_error::CaliptraResult; use caliptra_image_types::ImageManifest; #[cfg(feature = "runtime")] use dpe::{DpeInstance, U8Bool, MAX_HANDLES}; @@ -22,6 +24,7 @@ use crate::{ #[cfg(feature = "runtime")] use crate::pcr_reset::PcrResetCounter; +pub const MAX_CSR_SIZE: usize = 512; pub const PCR_LOG_MAX_COUNT: usize = 17; pub const FUSE_LOG_MAX_COUNT: usize = 62; pub const MEASUREMENT_MAX_COUNT: usize = 8; @@ -44,6 +47,65 @@ pub type StashMeasurementArray = [MeasurementLogEntry; MEASUREMENT_MAX_COUNT]; pub type AuthManifestImageMetadataList = [AuthManifestImageMetadata; AUTH_MANIFEST_IMAGE_METADATA_LIST_MAX_COUNT]; +#[derive(Clone, FromBytes, AsBytes, Zeroize)] +#[repr(C)] +pub struct IdevIdCsr { + csr_len: u32, + csr: [u8; MAX_CSR_SIZE], +} + +impl Default for IdevIdCsr { + fn default() -> Self { + Self { + csr_len: Self::UNPROVISIONED_CSR, + csr: [0; MAX_CSR_SIZE], + } + } +} + +impl IdevIdCsr { + /// The `csr_len` field is set to this constant when a ROM image supports CSR generation but + /// the CSR generation flag was not enabled. + /// + /// This is used by the runtime to distinguish ROM images that support CSR generation from + /// ones that do not. + /// + /// u32::MAX is too large to be a valid CSR, so we use it to encode this state. + pub const UNPROVISIONED_CSR: u32 = u32::MAX; + + /// Get the CSR buffer + pub fn get(&self) -> Option<&[u8]> { + self.csr.get(..self.csr_len as usize) + } + + /// Create `Self` from a csr slice. `csr_len` MUST be the actual length of the csr. + pub fn new(csr_buf: &[u8], csr_len: usize) -> CaliptraResult { + if csr_len >= MAX_CSR_SIZE { + return Err(CaliptraError::ROM_IDEVID_INVALID_CSR); + } + + let mut _self = Self { + csr_len: csr_len as u32, + csr: [0; MAX_CSR_SIZE], + }; + _self.csr[..csr_len].copy_from_slice(&csr_buf[..csr_len]); + + Ok(_self) + } + + /// Get the length of the CSR in bytes. + pub fn get_csr_len(&self) -> u32 { + self.csr_len + } + + /// Check if the CSR was unprovisioned + pub fn is_unprovisioned(&self) -> bool { + self.csr_len == Self::UNPROVISIONED_CSR + } +} + +const _: () = assert!(size_of::() < memory_layout::IDEVID_CSR_SIZE as usize); + #[derive(FromBytes, AsBytes, Zeroize)] #[repr(C)] pub struct PersistentData { @@ -101,7 +163,11 @@ pub struct PersistentData { #[cfg(not(feature = "runtime"))] pub auth_manifest_image_metadata_col: [u8; memory_layout::AUTH_MAN_IMAGE_METADATA_LIST_MAX_SIZE as usize], + + pub idevid_csr: IdevIdCsr, + reserved10: [u8; memory_layout::IDEVID_CSR_SIZE as usize - size_of::()], } + impl PersistentData { pub fn assert_matches_layout() { const P: *const PersistentData = memory_layout::MAN1_ORG as *const PersistentData; @@ -128,10 +194,13 @@ impl PersistentData { addr_of!((*P).auth_manifest_image_metadata_col) as u32, memory_layout::AUTH_MAN_IMAGE_METADATA_LIST_ORG ); + assert_eq!( + addr_of!((*P).idevid_csr) as u32, + memory_layout::IDEVID_CSR_ORG + ); assert_eq!( P.add(1) as u32, - memory_layout::AUTH_MAN_IMAGE_METADATA_LIST_ORG - + memory_layout::AUTH_MAN_IMAGE_METADATA_LIST_MAX_SIZE + memory_layout::IDEVID_CSR_ORG + memory_layout::IDEVID_CSR_SIZE ); } } diff --git a/error/src/lib.rs b/error/src/lib.rs index 7ef6f8476e..eb106accf1 100644 --- a/error/src/lib.rs +++ b/error/src/lib.rs @@ -443,6 +443,10 @@ impl CaliptraError { pub const RUNTIME_CMD_RESERVED_PAUSER: CaliptraError = CaliptraError::new_const(0x000E004F); pub const RUNTIME_AUTH_AND_STASH_MEASUREMENT_DPE_ERROR: CaliptraError = CaliptraError::new_const(0x000E0050); + pub const RUNTIME_GET_IDEV_ID_UNPROVISIONED: CaliptraError = + CaliptraError::new_const(0x000E0051); + pub const RUNTIME_GET_IDEV_ID_UNSUPPORTED_ROM: CaliptraError = + CaliptraError::new_const(0x000E0052); /// FMC Errors pub const FMC_GLOBAL_NMI: CaliptraError = CaliptraError::new_const(0x000F0001); @@ -499,6 +503,8 @@ impl CaliptraError { pub const FW_PROC_MAILBOX_STASH_MEASUREMENT_MAX_LIMIT: CaliptraError = CaliptraError::new_const(0x01020008); 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); /// FMC Alias Layer : Certificate Verification Failure. pub const FMC_ALIAS_CERT_VERIFY: CaliptraError = CaliptraError::new_const(0x01030001); diff --git a/hw-model/src/lib.rs b/hw-model/src/lib.rs index 1487314d64..2f3fa27fa7 100644 --- a/hw-model/src/lib.rs +++ b/hw-model/src/lib.rs @@ -585,6 +585,15 @@ pub trait HwModel: SocManager { const MAX_WAIT_CYCLES: u32 = 20_000_000; let mut cycles = 0; while !self.ready_for_fw() { + // If GENERATE_IDEVID_CSR was set then we need to clear cptra_dbg_manuf_service_reg + // once the CSR is ready to continue making progress. + // + // Generally the CSR should be read from the mailbox at this point, but to + // accommodate test cases that ignore the CSR mailbox, we will ignore it here. + if self.soc_ifc().cptra_flow_status().read().idevid_csr_ready() { + self.soc_ifc().cptra_dbg_manuf_service_reg().write(|_| 0); + } + self.step(); cycles += 1; if cycles > MAX_WAIT_CYCLES { diff --git a/rom/dev/README.md b/rom/dev/README.md index f35bb9a3bd..7ba76d8de7 100644 --- a/rom/dev/README.md +++ b/rom/dev/README.md @@ -381,6 +381,7 @@ ROM supports the following set of commands before handling the FW_DOWNLOAD comma 4. **SELF_TEST_GET_RESULTS**: This command is used to check if a SELF_TEST command is in progress. [Self Test Get Results command](https://github.com/chipsalliance/caliptra-sw/blob/main/runtime/README.md#self_test_get_results). 5. **SHUTDOWN**: This command is used clear the hardware crypto blocks including the keyvault. [Shutdown command](https://github.com/chipsalliance/caliptra-sw/blob/main/runtime/README.md#shutdown). 6. **CAPABILITIES**: This command is used to query the ROM capabilities. Capabilities is a 128-bit value with individual bits indicating a specific capability. Currently, the only capability supported is ROM_BASE (bit 0). [Capabilities command](https://github.com/chipsalliance/caliptra-sw/blob/main/runtime/README.md#capabilities). +7. **GET_IDEVID_CSR**: This command is used to fetch the IDevID CSR from ROM. [Fetch IDevIDCSR command](https://github.com/chipsalliance/caliptra-sw/blob/main/runtime/README.md#get_idevid_csr). ### Downloading images from mailbox diff --git a/rom/dev/src/flow/cold_reset/fw_processor.rs b/rom/dev/src/flow/cold_reset/fw_processor.rs index 15fb952bbb..9ae3826f02 100644 --- a/rom/dev/src/flow/cold_reset/fw_processor.rs +++ b/rom/dev/src/flow/cold_reset/fw_processor.rs @@ -22,7 +22,7 @@ use caliptra_cfi_lib::CfiCounter; use caliptra_common::capabilities::Capabilities; use caliptra_common::fips::FipsVersionCmd; use caliptra_common::mailbox_api::{ - CapabilitiesResp, CommandId, MailboxReqHeader, MailboxRespHeader, Response, + CapabilitiesResp, CommandId, GetIdevIdCsrResp, MailboxReqHeader, MailboxRespHeader, Response, StashMeasurementReq, StashMeasurementResp, }; use caliptra_common::pcr::PCR_ID_STASH_MEASUREMENT; @@ -304,6 +304,31 @@ impl FirmwareProcessor { resp.populate_chksum(); txn.send_response(resp.as_bytes())?; } + CommandId::GET_IDEV_CSR => { + let mut request = MailboxReqHeader::default(); + Self::copy_req_verify_chksum(&mut txn, request.as_bytes_mut())?; + + let csr_persistent_mem = &persistent_data.idevid_csr; + let mut resp = GetIdevIdCsrResp::default(); + + if csr_persistent_mem.is_unprovisioned() { + // CSR was never written to DCCM. This means the gen_idev_id_csr + // manufacturing flag was not set before booting into ROM. + return Err( + CaliptraError::FW_PROC_MAILBOX_GET_IDEV_CSR_UNPROVISIONED_CSR, + ); + } + + let csr = csr_persistent_mem + .get() + .ok_or(CaliptraError::ROM_IDEVID_INVALID_CSR)?; + + resp.data_size = csr_persistent_mem.get_csr_len(); + resp.data[..resp.data_size as usize].copy_from_slice(csr); + + resp.populate_chksum(); + txn.send_response(resp.as_bytes())?; + } _ => { cprintln!("[fwproc] Invalid command received"); // Don't complete the transaction here; let the fatal diff --git a/rom/dev/src/flow/cold_reset/idev_id.rs b/rom/dev/src/flow/cold_reset/idev_id.rs index 57d307744b..875b5de4a4 100644 --- a/rom/dev/src/flow/cold_reset/idev_id.rs +++ b/rom/dev/src/flow/cold_reset/idev_id.rs @@ -23,18 +23,14 @@ use caliptra_cfi_derive::cfi_impl_fn; use caliptra_cfi_lib::{cfi_assert, cfi_assert_bool, cfi_launder}; use caliptra_common::keyids::{KEY_ID_FE, KEY_ID_IDEVID_PRIV_KEY, KEY_ID_ROM_FMC_CDI, KEY_ID_UDS}; use caliptra_common::RomBootStatus::*; +use caliptra_drivers::MAX_CSR_SIZE; use caliptra_drivers::*; use caliptra_x509::*; use zeroize::Zeroize; -type InitDevIdCsr<'a> = Certificate<'a, { MAX_CSR_SIZE }>; - /// Initialization Vector used by Deobfuscation Engine during UDS / field entropy decryption. const DOE_IV: Array4x4 = Array4xN::<4, 16>([0xfb10365b, 0xa1179741, 0xfba193a1, 0x0f406d7e]); -/// Maximum Certificate Signing Request Size -const MAX_CSR_SIZE: usize = 512; - /// Dice Initial Device Identity (IDEVID) Layer pub enum InitDevIdLayer {} @@ -210,6 +206,8 @@ impl InitDevIdLayer { // // A flag is asserted via JTAG interface to enable the generation of CSR if !env.soc_ifc.mfg_flag_gen_idev_id_csr() { + let dev_id_csr = IdevIdCsr::default(); + Self::write_csr_to_peristent_storage(env, &dev_id_csr)?; return Ok(()); } @@ -264,37 +262,49 @@ impl InitDevIdLayer { cprintln!("[idev] SIG.S = {}", HexBytes(&_sig_s)); // Build the CSR with `To Be Signed` & `Signature` - let mut csr = [0u8; MAX_CSR_SIZE]; + let mut csr_buf = [0; MAX_CSR_SIZE]; let result = Ecdsa384CsrBuilder::new(tbs.tbs(), &sig.to_ecdsa()) .ok_or(CaliptraError::ROM_IDEVID_CSR_BUILDER_INIT_FAILURE); sig.zeroize(); let csr_bldr = result?; let csr_len = csr_bldr - .build(&mut csr) + .build(&mut csr_buf) .ok_or(CaliptraError::ROM_IDEVID_CSR_BUILDER_BUILD_FAILURE)?; - if csr_len > csr.len() { + if csr_len > csr_buf.len() { return Err(CaliptraError::ROM_IDEVID_CSR_OVERFLOW); } - cprintln!("[idev] CSR = {}", HexBytes(&csr[..csr_len])); + cprintln!("[idev] CSR = {}", HexBytes(&csr_buf[..csr_len])); report_boot_status(IDevIdMakeCsrComplete.into()); + let dev_id_csr = IdevIdCsr::new(&csr_buf, csr_len)?; + // Execute Send CSR Flow - let result = Self::send_csr(env, InitDevIdCsr::new(&csr, csr_len)); - csr.zeroize(); + let mut result = Self::send_csr(env, &dev_id_csr); + if result.is_ok() { + result = Self::write_csr_to_peristent_storage(env, &dev_id_csr); + } + csr_buf.zeroize(); result } + fn write_csr_to_peristent_storage(env: &mut RomEnv, csr: &IdevIdCsr) -> CaliptraResult<()> { + let csr_persistent_mem = &mut env.persistent_data.get_mut().idevid_csr; + *csr_persistent_mem = csr.clone(); + + Ok(()) + } + /// Send Initial Device ID CSR to SOC /// /// # Argument /// /// * `env` - ROM Environment /// * `csr` - Certificate Signing Request to send to SOC - fn send_csr(env: &mut RomEnv, csr: InitDevIdCsr) -> CaliptraResult<()> { + fn send_csr(env: &mut RomEnv, csr: &IdevIdCsr) -> CaliptraResult<()> { loop { // Create Mailbox send transaction to send the CSR if let Some(mut txn) = env.mbox.try_start_send_txn() { @@ -319,3 +329,14 @@ impl InitDevIdLayer { } } } + +#[cfg(test)] +mod tests { + use super::*; + use caliptra_drivers::memory_layout::IDEVID_CSR_SIZE; + + #[test] + fn verify_csr_fits_in_dccm() { + assert!(MAX_CSR_SIZE <= IDEVID_CSR_SIZE as usize); + } +} diff --git a/rom/dev/src/flow/cold_reset/x509.rs b/rom/dev/src/flow/cold_reset/x509.rs index aa5b4ecac6..e62dadfdda 100644 --- a/rom/dev/src/flow/cold_reset/x509.rs +++ b/rom/dev/src/flow/cold_reset/x509.rs @@ -16,29 +16,6 @@ use crate::cprintln; use crate::rom_env::RomEnv; use caliptra_drivers::*; -/// Wrapper to hold certificate buffer and length -pub struct Certificate<'a, const LEN: usize> { - buf: &'a [u8; LEN], - len: usize, -} - -impl<'a, const LEN: usize> Certificate<'a, LEN> { - /// Create an instance of `Certificate` - /// - /// # Arguments - /// - /// * `buf` - Buffer - /// * `len` - Buffer length - pub fn new(buf: &'a [u8; LEN], len: usize) -> Self { - Self { buf, len } - } - - /// Get the buffer - pub fn get(&self) -> Option<&[u8]> { - self.buf.get(..self.len) - } -} - /// X509 API pub enum X509 {} diff --git a/rom/dev/tests/rom_integration_tests/main.rs b/rom/dev/tests/rom_integration_tests/main.rs index a193ee2ca0..08fac1516e 100644 --- a/rom/dev/tests/rom_integration_tests/main.rs +++ b/rom/dev/tests/rom_integration_tests/main.rs @@ -20,3 +20,4 @@ mod test_update_reset; mod test_version; mod test_warm_reset; mod test_wdt_activation_and_stoppage; +mod tests_get_idev_csr; diff --git a/rom/dev/tests/rom_integration_tests/tests_get_idev_csr.rs b/rom/dev/tests/rom_integration_tests/tests_get_idev_csr.rs new file mode 100644 index 0000000000..3cb44d1d84 --- /dev/null +++ b/rom/dev/tests/rom_integration_tests/tests_get_idev_csr.rs @@ -0,0 +1,70 @@ +// Licensed under the Apache-2.0 license + +use caliptra_api::SocManager; +use caliptra_builder::ImageOptions; +use caliptra_common::mailbox_api::{CommandId, GetIdevIdCsrResp, MailboxReqHeader}; +use caliptra_drivers::MfgFlags; +use caliptra_error::CaliptraError; +use caliptra_hw_model::{Fuses, HwModel, ModelError}; +use zerocopy::{AsBytes, FromBytes}; + +use crate::helpers; + +#[test] +fn test_get_csr() { + let (mut hw, _) = + helpers::build_hw_model_and_image_bundle(Fuses::default(), ImageOptions::default()); + + let csr_bytes = { + let flags = MfgFlags::GENERATE_IDEVID_CSR; + hw.soc_ifc() + .cptra_dbg_manuf_service_reg() + .write(|_| flags.bits()); + + let downloaded = helpers::get_csr(&mut hw).unwrap(); + + hw.step_until(|m| m.soc_ifc().cptra_flow_status().read().ready_for_fw()); + downloaded + }; + + let payload = MailboxReqHeader { + chksum: caliptra_common::checksum::calc_checksum(u32::from(CommandId::GET_IDEV_CSR), &[]), + }; + + let response = hw + .mailbox_execute(CommandId::GET_IDEV_CSR.into(), payload.as_bytes()) + .unwrap() + .unwrap(); + + let get_idv_csr_resp = GetIdevIdCsrResp::read_from(response.as_bytes()).unwrap(); + + assert!(caliptra_common::checksum::verify_checksum( + get_idv_csr_resp.hdr.chksum, + 0x0, + &get_idv_csr_resp.as_bytes()[core::mem::size_of_val(&get_idv_csr_resp.hdr.chksum)..], + )); + + assert_eq!(csr_bytes.len() as u32, get_idv_csr_resp.data_size); + assert_eq!( + csr_bytes, + get_idv_csr_resp.data[..get_idv_csr_resp.data_size as usize] + ); +} + +#[test] +fn test_get_csr_generate_csr_flag_not_set() { + let (mut hw, _) = + helpers::build_hw_model_and_image_bundle(Fuses::default(), ImageOptions::default()); + hw.step_until(|m| m.soc_ifc().cptra_flow_status().read().ready_for_fw()); + + let payload = MailboxReqHeader { + chksum: caliptra_common::checksum::calc_checksum(u32::from(CommandId::GET_IDEV_CSR), &[]), + }; + + let response = hw.mailbox_execute(CommandId::GET_IDEV_CSR.into(), payload.as_bytes()); + + let expected_error = ModelError::MailboxCmdFailed( + CaliptraError::FW_PROC_MAILBOX_GET_IDEV_CSR_UNPROVISIONED_CSR.into(), + ); + assert_eq!(expected_error, response.unwrap_err()); +} diff --git a/runtime/README.md b/runtime/README.md index d2bf0419e6..13e6ae1306 100644 --- a/runtime/README.md +++ b/runtime/README.md @@ -794,6 +794,33 @@ Command Code: `0x4154_5348` ("ATSH") | fips_status | u32 | Indicates if the command is FIPS approved or an error. | | auth_req_result | u32 | AUTHORIZE_IMAGE: 0xDEADC0DE and DENY_IMAGE_AUTHORIZATION: 0x21523F21 | +### GET\_IDEVID\_CSR + +Command Code: `0x4944_4352` ("IDCR") + +*Table: `GET_IDEVID_CSR` input arguments* + +| **Name** | **Type** | **Description** +| -------- | -------- | --------------- +| chksum | u32 | Checksum over other input arguments, computed by the caller. Little endian. | + +*Table: `GET_IDEVID_CSR` output arguments* +| **Name** | **Type** | **Description** +| -------- | -------- | --------------- +| chksum | u32 | Checksum over other output arguments, computed by Caliptra. Little endian. | +| data\_size | u32 | Length in bytes of the valid data in the data field. | +| data | u8[...] | DER-encoded IDevID certificate signing request. | + +The `mfg_flag_gen_idev_id_csr` manufacturing flag **MUST** have been set to generate a CSR. + +When called from ROM, if the CSR was not previously provisioned this command will return `FW_PROC_MAILBOX_UNPROVISIONED_CSR(0x0102000A)`. + +When called from runtime, if the CSR was not previously provisioned this command will return `RUNTIME_GET_IDEV_ID_UNPROVISIONED(0x000E0051)`. If the ROM did not support CSR generation, this command will return `RUNTIME_GET_IDEV_ID_UNSUPPORTED_ROM(0x000E0052)`. + + + +When the `mfg_flag_gen_idev_id_csr` flag has been set, the SoC **MUST** wait for the `flow_status_set_idevid_csr_ready` bit to be set by Caliptra. Once set, the SoC **MUST** clear the `mfg_flag_gen_idev_id_csr` flag for Caliptra to progress. + ## Checksum For every command except for FW_LOAD, the request and response feature a checksum. This diff --git a/runtime/src/get_idev_csr.rs b/runtime/src/get_idev_csr.rs new file mode 100644 index 0000000000..1ccd6374c2 --- /dev/null +++ b/runtime/src/get_idev_csr.rs @@ -0,0 +1,56 @@ +// Licensed under the Apache-2.0 license + +use crate::Drivers; + +use caliptra_cfi_derive_git::cfi_impl_fn; +use caliptra_cfi_lib_git::cfi_launder; + +use caliptra_common::{ + cprintln, + mailbox_api::{GetIdevIdCsrReq, GetIdevIdCsrResp, MailboxResp, MailboxRespHeader}, +}; +use caliptra_error::{CaliptraError, CaliptraResult}; + +use caliptra_drivers::IdevIdCsr; + +use zerocopy::{AsBytes, FromBytes}; + +pub struct GetIdevIdCsrCmd; +impl GetIdevIdCsrCmd { + #[cfg_attr(not(feature = "no-cfi"), cfi_impl_fn)] + #[inline(never)] + pub(crate) fn execute(drivers: &mut Drivers, cmd_args: &[u8]) -> CaliptraResult { + if let Some(cmd) = GetIdevIdCsrReq::read_from(cmd_args) { + let csr_persistent_mem = &drivers.persistent_data.get().idevid_csr; + + match csr_persistent_mem.get_csr_len() { + IdevIdCsr::UNPROVISIONED_CSR => { + Err(CaliptraError::RUNTIME_GET_IDEV_ID_UNPROVISIONED) + } + 0 => Err(CaliptraError::RUNTIME_GET_IDEV_ID_UNSUPPORTED_ROM), + len => { + let csr = csr_persistent_mem + .get() + .ok_or(CaliptraError::RUNTIME_GET_IDEV_ID_UNPROVISIONED)?; + + let mut resp = GetIdevIdCsrResp { + data_size: len, + ..Default::default() + }; + // NOTE: This code will not panic. + // + // csr is guranteed to be the same size as `len`, and therefore + // `resp.data_size` by the `IDevIDCsr::get` API. + // + // A valid `IDevIDCsr` cannot be larger than `MAX_CSR_SIZE`, which is the max + // size of the buffer in `GetIdevIdCsrResp` + resp.data[..resp.data_size as usize].copy_from_slice(csr); + + Ok(MailboxResp::GetIdevIdCsr(resp)) + } + } + } else { + Err(CaliptraError::RUNTIME_INSUFFICIENT_MEMORY) + } + } +} diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 83959dd920..1930cece9d 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -22,6 +22,7 @@ mod dpe_crypto; mod dpe_platform; mod drivers; pub mod fips; +mod get_idev_csr; pub mod handoff; mod hmac; pub mod info; @@ -57,6 +58,7 @@ pub use fips::FipsShutdownCmd; pub use fips::{fips_self_test_cmd, fips_self_test_cmd::SelfTestStatus}; pub use populate_idev::PopulateIDevIdCertCmd; +pub use get_idev_csr::GetIdevIdCsrCmd; pub use info::{FwInfoCmd, IDevIdInfoCmd}; pub use invoke_dpe::InvokeDpeCmd; pub use pcr::IncrementPcrResetCounterCmd; @@ -226,6 +228,7 @@ fn handle_command(drivers: &mut Drivers) -> CaliptraResult { CommandId::SHUTDOWN => FipsShutdownCmd::execute(drivers), CommandId::SET_AUTH_MANIFEST => SetAuthManifestCmd::execute(drivers, cmd_bytes), CommandId::AUTHORIZE_AND_STASH => AuthorizeAndStashCmd::execute(drivers, cmd_bytes), + CommandId::GET_IDEV_CSR => GetIdevIdCsrCmd::execute(drivers, cmd_bytes), _ => Err(CaliptraError::RUNTIME_UNIMPLEMENTED_COMMAND), }?; diff --git a/runtime/tests/runtime_integration_tests/common.rs b/runtime/tests/runtime_integration_tests/common.rs index d4c4022e71..eb7fce430a 100644 --- a/runtime/tests/runtime_integration_tests/common.rs +++ b/runtime/tests/runtime_integration_tests/common.rs @@ -9,6 +9,7 @@ use caliptra_common::mailbox_api::{ CommandId, GetFmcAliasCertResp, GetRtAliasCertResp, InvokeDpeReq, InvokeDpeResp, MailboxReq, MailboxReqHeader, }; +use caliptra_drivers::MfgFlags; use caliptra_error::CaliptraError; use caliptra_hw_model::{BootParams, DefaultHwModel, Fuses, HwModel, InitParams, ModelError}; use dpe::{ @@ -45,6 +46,7 @@ pub struct RuntimeTestArgs<'a> { pub test_fwid: Option<&'static FwId<'static>>, pub test_image_options: Option, pub init_params: Option>, + pub test_mfg_flags: Option, } pub fn run_rt_test_lms(args: RuntimeTestArgs, lms_verify: bool) -> DefaultHwModel { @@ -75,6 +77,12 @@ pub fn run_rt_test_lms(args: RuntimeTestArgs, lms_verify: bool) -> DefaultHwMode let image = caliptra_builder::build_and_sign_image(&FMC_WITH_UART, runtime_fwid, image_options) .unwrap(); + let boot_flags = if let Some(flags) = args.test_mfg_flags { + flags.bits() + } else { + 0 + }; + let mut model = caliptra_hw_model::new( init_params, BootParams { @@ -83,6 +91,7 @@ pub fn run_rt_test_lms(args: RuntimeTestArgs, lms_verify: bool) -> DefaultHwMode lms_verify, ..Default::default() }, + initial_dbg_manuf_service_reg: boot_flags, ..Default::default() }, ) diff --git a/runtime/tests/runtime_integration_tests/main.rs b/runtime/tests/runtime_integration_tests/main.rs index fb62673997..417946cda3 100644 --- a/runtime/tests/runtime_integration_tests/main.rs +++ b/runtime/tests/runtime_integration_tests/main.rs @@ -8,6 +8,7 @@ mod test_certs; mod test_disable; mod test_ecdsa; mod test_fips; +mod test_get_idev_csr; mod test_info; mod test_invoke_dpe; mod test_lms; diff --git a/runtime/tests/runtime_integration_tests/test_get_idev_csr.rs b/runtime/tests/runtime_integration_tests/test_get_idev_csr.rs new file mode 100644 index 0000000000..6e072f097f --- /dev/null +++ b/runtime/tests/runtime_integration_tests/test_get_idev_csr.rs @@ -0,0 +1,63 @@ +// Licensed under the Apache-2.0 license + +use caliptra_api::SocManager; +use caliptra_common::mailbox_api::{CommandId, GetIdevIdCsrResp, MailboxReqHeader}; +use caliptra_drivers::{IdevIdCsr, MfgFlags}; +use caliptra_error::CaliptraError; +use caliptra_hw_model::{HwModel, ModelError}; +use caliptra_runtime::RtBootStatus; +use openssl::x509::X509Req; +use zerocopy::{AsBytes, FromBytes}; + +use crate::common::{run_rt_test, RuntimeTestArgs}; + +#[test] +fn test_get_csr() { + // `run_rt_test` is responsibly for clearing the CSR bit. + // Caliptra will wait until the CSR bit is cleared during startup. + let args = RuntimeTestArgs { + test_mfg_flags: Some(MfgFlags::GENERATE_IDEVID_CSR), + ..Default::default() + }; + let mut model = run_rt_test(args); + + let payload = MailboxReqHeader { + chksum: caliptra_common::checksum::calc_checksum(u32::from(CommandId::GET_IDEV_CSR), &[]), + }; + + let response = model + .mailbox_execute(CommandId::GET_IDEV_CSR.into(), payload.as_bytes()) + .unwrap() + .unwrap(); + + let get_idv_csr_resp = GetIdevIdCsrResp::read_from(response.as_bytes()).unwrap(); + + assert_ne!(IdevIdCsr::UNPROVISIONED_CSR, get_idv_csr_resp.data_size); + assert_ne!(0, get_idv_csr_resp.data_size); + + let csr_bytes = &get_idv_csr_resp.data[..get_idv_csr_resp.data_size as usize]; + assert_ne!([0; 512], csr_bytes); + + assert!(X509Req::from_der(csr_bytes).is_ok()); +} + +#[test] +fn test_missing_csr() { + let mut model = run_rt_test(RuntimeTestArgs::default()); + + model.step_until(|m| { + m.soc_ifc().cptra_boot_status().read() == u32::from(RtBootStatus::RtReadyForCommands) + }); + + let payload = MailboxReqHeader { + chksum: caliptra_common::checksum::calc_checksum(u32::from(CommandId::GET_IDEV_CSR), &[]), + }; + + let response = model + .mailbox_execute(CommandId::GET_IDEV_CSR.into(), payload.as_bytes()) + .unwrap_err(); + assert_eq!( + response, + ModelError::MailboxCmdFailed(CaliptraError::RUNTIME_GET_IDEV_ID_UNPROVISIONED.into()) + ); +}