From c32cc0f5cdbc4a4deb4b60975a4525aef2ef3f5e 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. --- api/src/mailbox.rs | 41 ++++++++++ drivers/src/idevid_csr.rs | 32 ++++++++ drivers/src/lib.rs | 2 + drivers/src/memory_layout.rs | 12 ++- drivers/src/persistent.rs | 12 ++- rom/dev/README.md | 1 + rom/dev/src/flow/cold_reset/fw_processor.rs | 18 ++++- rom/dev/src/flow/cold_reset/idev_id.rs | 48 ++++++++--- rom/dev/src/flow/cold_reset/x509.rs | 23 ------ rom/dev/tests/rom_integration_tests/main.rs | 1 + .../tests_get_idev_csr.rs | 81 +++++++++++++++++++ runtime/README.md | 19 +++++ 12 files changed, 249 insertions(+), 41 deletions(-) create mode 100644 drivers/src/idevid_csr.rs create mode 100644 rom/dev/tests/rom_integration_tests/tests_get_idev_csr.rs diff --git a/api/src/mailbox.rs b/api/src/mailbox.rs index 46508b0d83..0b26282182 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_IDV_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_IDV_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/idevid_csr.rs b/drivers/src/idevid_csr.rs new file mode 100644 index 0000000000..379c7349a5 --- /dev/null +++ b/drivers/src/idevid_csr.rs @@ -0,0 +1,32 @@ +use core::mem::size_of; +use zerocopy::{AsBytes, FromBytes}; +use zeroize::Zeroize; + +use crate::memory_layout; + +pub const MAX_CSR_SIZE: usize = 512; + +#[derive(FromBytes, AsBytes, Zeroize)] +#[repr(C)] +pub struct IDevIDCsr { + pub csr: [u8; MAX_CSR_SIZE], + pub csr_len: u32, +} + +impl Default for IDevIDCsr { + fn default() -> Self { + Self { + csr: [0; MAX_CSR_SIZE], + csr_len: 0, + } + } +} + +impl IDevIDCsr { + /// Get the CSR buffer + pub fn get(&self) -> Option<&[u8]> { + self.csr.get(..self.csr_len as usize) + } +} + +const _: () = assert!(size_of::() < memory_layout::IDEVID_CSR_SIZE as usize); diff --git a/drivers/src/lib.rs b/drivers/src/lib.rs index 23ccd648cd..51c0458804 100644 --- a/drivers/src/lib.rs +++ b/drivers/src/lib.rs @@ -32,6 +32,7 @@ pub mod fuse_log; pub mod hand_off; mod hmac384; mod hmac384_kdf; +mod idevid_csr; mod key_vault; mod kv_access; mod lms; @@ -75,6 +76,7 @@ pub use fuse_bank::{ pub use hand_off::FirmwareHandoffTable; pub use hmac384::{Hmac384, Hmac384Data, Hmac384Key, Hmac384Op, Hmac384Tag}; pub use hmac384_kdf::hmac384_kdf; +pub use idevid_csr::{IDevIDCsr, MAX_CSR_SIZE}; pub use key_vault::{KeyId, KeyUsage, KeyVault}; pub use kv_access::{KeyReadArgs, KeyWriteArgs}; pub use lms::{ 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..96c7ba7f65 100644 --- a/drivers/src/persistent.rs +++ b/drivers/src/persistent.rs @@ -12,6 +12,7 @@ use dpe::{DpeInstance, U8Bool, MAX_HANDLES}; use zerocopy::{AsBytes, FromBytes}; use zeroize::Zeroize; +use crate::IDevIDCsr; use crate::{ fuse_log::FuseLogEntry, memory_layout, @@ -101,7 +102,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 +133,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/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 f9aadebbe0..074f3ecf53 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; @@ -306,6 +306,22 @@ impl FirmwareProcessor { resp.populate_chksum(); txn.send_response(resp.as_bytes())?; } + CommandId::GET_IDV_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(); + + let csr = csr_persistent_mem + .get() + .ok_or(CaliptraError::ROM_IDEVID_INVALID_CSR)?; + resp.data_size = csr_persistent_mem.csr_len as u32; + 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 f16525fc98..a469f1e68c 100644 --- a/rom/dev/src/flow/cold_reset/idev_id.rs +++ b/rom/dev/src/flow/cold_reset/idev_id.rs @@ -27,14 +27,9 @@ 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 {} @@ -264,42 +259,58 @@ 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 dev_id_csr = IDevIDCsr::default(); 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 dev_id_csr.csr) .ok_or(CaliptraError::ROM_IDEVID_CSR_BUILDER_BUILD_FAILURE)?; - if csr_len > csr.len() { + if csr_len > dev_id_csr.csr.len() { return Err(CaliptraError::ROM_IDEVID_CSR_OVERFLOW); } - cprintln!("[idev] CSR = {}", HexBytes(&csr[..csr_len])); + dev_id_csr.csr_len = csr_len as u32; + + cprintln!("[idev] CSR = {}", HexBytes(&dev_id_csr.csr[..csr_len])); report_boot_status(IDevIdMakeCsrComplete.into()); // 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); + } + dev_id_csr.zeroize(); result } + fn write_csr_to_peristent_storage(env: &mut RomEnv, csr: &IDevIDCsr) -> CaliptraResult<()> { + let mut csr_persistent_mem = &mut env.persistent_data.get_mut().idevid_csr; + csr_persistent_mem.zeroize(); + + let csr_buf = csr.get().ok_or(CaliptraError::ROM_IDEVID_INVALID_CSR)?; + + csr_persistent_mem.csr[..csr.csr_len as usize].copy_from_slice(csr_buf); + csr_persistent_mem.csr_len = csr.csr_len; + 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() { // Copy the CSR to mailbox - txn.send_request(0, csr.get().ok_or(CaliptraError::ROM_IDEVID_INVALID_CSR)?)?; + txn.send_request(0, &csr.get().ok_or(CaliptraError::ROM_IDEVID_INVALID_CSR)?)?; // Signal the JTAG/SOC that Initial Device ID CSR is ready env.soc_ifc.flow_status_set_idevid_csr_ready(); @@ -319,3 +330,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 261c1829b8..701c5bef89 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..e51ae4641e --- /dev/null +++ b/rom/dev/tests/rom_integration_tests/tests_get_idev_csr.rs @@ -0,0 +1,81 @@ +// 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::{MAX_CSR_SIZE, MfgFlags}; +use caliptra_hw_model::{Fuses, HwModel}; +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_IDV_CSR), &[]), + }; + + let response = hw + .mailbox_execute(CommandId::GET_IDV_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_IDV_CSR), &[]), + }; + + let response = hw + .mailbox_execute(CommandId::GET_IDV_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!([0; MAX_CSR_SIZE as usize], get_idv_csr_resp.data); + assert_eq!(0, get_idv_csr_resp.data_size); +} diff --git a/runtime/README.md b/runtime/README.md index d2bf0419e6..f2c8d96cc0 100644 --- a/runtime/README.md +++ b/runtime/README.md @@ -794,6 +794,25 @@ 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. | + +**Note**: The `GENERATE_IDEVID_CSR` Manufacturing State Flag **MUST** have been set to generate a CSR. Otherwise this command will return an empty data buffer. + ## Checksum For every command except for FW_LOAD, the request and response feature a checksum. This