diff --git a/Cargo.lock b/Cargo.lock index 9b50b0d3b0..c43f9813be 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -747,6 +747,7 @@ dependencies = [ "caliptra-emu-cpu", "caliptra-error", "caliptra-hw-model", + "caliptra-hw-model-types", "caliptra-image-crypto", "caliptra-image-elf", "caliptra-image-fake-keys", diff --git a/api/src/mailbox.rs b/api/src/mailbox.rs index c786820420..846c25f67c 100644 --- a/api/src/mailbox.rs +++ b/api/src/mailbox.rs @@ -54,6 +54,11 @@ impl CommandId { // The get IDevID ECC CSR command. pub const GET_IDEV_ECC_CSR: Self = Self(0x4944_4352); // "IDCR" + + // Debug unlock commands + pub const MANUF_DEBUG_UNLOCK_REQ_TOKEN: Self = Self(0x4d445554); // "MDUT" + pub const PRODUCTION_AUTH_DEBUG_UNLOCK_REQ: Self = Self(0x50445552); // "PDUR" + pub const PRODUCTION_AUTH_DEBUG_UNLOCK_TOKEN: Self = Self(0x50445554); // "PDUT" } impl From for CommandId { @@ -1092,6 +1097,110 @@ pub struct AuthorizeAndStashResp { } impl Response for AuthorizeAndStashResp {} +// MANUF_DEBUG_UNLOCK_REQ_TOKEN +#[repr(C)] +#[derive(Debug, AsBytes, FromBytes, PartialEq, Eq, Default)] +pub struct ManufDebugUnlockTokenReq { + pub hdr: MailboxReqHeader, + pub token: [u8; 16], +} +impl Request for ManufDebugUnlockTokenReq { + const ID: CommandId = CommandId::MANUF_DEBUG_UNLOCK_REQ_TOKEN; + type Resp = MailboxRespHeader; +} + +// PRODUCTION_AUTH_DEBUG_UNLOCK_REQ +#[repr(C)] +#[derive(Debug, AsBytes, FromBytes, PartialEq, Eq, Default)] +pub struct ProductionAuthDebugUnlockReq { + pub hdr: MailboxReqHeader, + pub vendor_id: u16, // Vendor ID (2 bytes) + pub object_data_type: u8, // Object Data Type (1 byte) + pub _reserved_1: u8, // Reserved (1 byte) + pub length: [u8; 3], // Length (3 bytes, should be ensured as 3 DWORDs) + pub _reserved_2: u8, // Reserved (1 byte) + pub unlock_category: [u8; 3], // Unlock Category (3 bytes, Bits[0:3] - Debug unlock Level) + pub _reserved_3: u8, // Reserved (1 byte) +} + +impl Request for ProductionAuthDebugUnlockReq { + const ID: CommandId = CommandId::PRODUCTION_AUTH_DEBUG_UNLOCK_REQ; + type Resp = ProductionAuthDebugUnlockChallenge; +} + +// PRODUCTION_AUTH_DEBUG_UNLOCK_CHALLENGE +#[repr(C)] +#[derive(Debug, AsBytes, FromBytes, PartialEq, Eq)] +pub struct ProductionAuthDebugUnlockChallenge { + pub hdr: MailboxRespHeader, + pub vendor_id: u16, // Vendor ID (2 bytes) + pub object_data_type: u8, // Object Data Type (1 byte) + pub _reserved_1: u8, // Reserved (1 byte) + pub length: [u8; 3], // Length (3 bytes, should be ensured as 8 (TODO?) DWORDs) + pub _reserved_2: u8, // Reserved (1 byte) + pub unique_device_identifier: [u8; 32], // Device identifier of the Caliptra Device + pub challenge: [u8; 48], // Random number +} +impl Default for ProductionAuthDebugUnlockChallenge { + fn default() -> Self { + Self { + hdr: Default::default(), + vendor_id: Default::default(), + object_data_type: Default::default(), + _reserved_1: Default::default(), + length: Default::default(), + _reserved_2: Default::default(), + unique_device_identifier: Default::default(), + challenge: [0; 48], + } + } +} +impl Response for ProductionAuthDebugUnlockChallenge {} + +// PRODUCTION_AUTH_DEBUG_UNLOCK_TOKEN +#[repr(C)] +#[derive(Debug, AsBytes, FromBytes, PartialEq, Eq)] +pub struct ProductionAuthDebugUnlockToken { + pub hdr: MailboxReqHeader, + pub vendor_id: u16, // Vendor ID (2 bytes) + pub object_data_type: u8, // Object Data Type (1 byte) + pub _reserved_1: u8, // Reserved (1 byte) + pub length: [u8; 3], // Length (3 bytes, should be ensured as 0x754) + pub _reserved_2: u8, // Reserved (1 byte) + pub unique_device_identifier: [u8; 32], // Device identifier of the Caliptra Device + pub unlock_category: [u8; 3], // Unlock Category (3 bytes, Bits[0:3] - Debug unlock Level) + pub _reserved_3: u8, // Reserved (1 byte) + pub challenge: [u8; 48], // Random number + pub ecc_public_key: [u8; 96], // ECC public key + pub mldsa_public_key: [u8; 2592], // MLDSA public key + pub ecc_signature: [u8; 96], // ECC P-384 signature of the Message hashed using SHA2-384. R-Coordinate: Random Point (48 bytes) S-Coordinate: Proof (48 bytes) + pub mldsa_signature: [u8; 4628], // MLDSA signature of the Message hashed using SHA2-512. (4627 bytes + 1 Reserved byte). +} +impl Default for ProductionAuthDebugUnlockToken { + fn default() -> Self { + Self { + hdr: Default::default(), + vendor_id: Default::default(), + object_data_type: Default::default(), + _reserved_1: Default::default(), + length: Default::default(), + _reserved_2: Default::default(), + unique_device_identifier: Default::default(), + unlock_category: Default::default(), + _reserved_3: Default::default(), + challenge: [0; 48], + ecc_public_key: [0; 96], + mldsa_public_key: [0; 2592], + ecc_signature: [0; 96], + mldsa_signature: [0; 4628], + } + } +} +impl Request for ProductionAuthDebugUnlockToken { + const ID: CommandId = CommandId::PRODUCTION_AUTH_DEBUG_UNLOCK_TOKEN; // TODO + type Resp = MailboxRespHeader; // TODO Check +} + /// Retrieves dlen bytes from the mailbox. pub fn mbox_read_response( mbox: mbox::RegisterBlock, diff --git a/api/src/soc_mgr.rs b/api/src/soc_mgr.rs index 7c7aedf85c..ec69e9f7a2 100644 --- a/api/src/soc_mgr.rs +++ b/api/src/soc_mgr.rs @@ -161,6 +161,9 @@ pub trait SocManager { self.soc_ifc() .fuse_soc_stepping_id() .write(|w| w.soc_stepping_id(fuses.soc_stepping_id.into())); + self.soc_ifc() + .fuse_manuf_dbg_unlock_token() + .write(&fuses.manuf_dbg_unlock_token); self.soc_ifc().cptra_fuse_wr_done().write(|w| w.done(true)); diff --git a/api/types/src/lib.rs b/api/types/src/lib.rs index 7d6fb712df..ed8069fff1 100644 --- a/api/types/src/lib.rs +++ b/api/types/src/lib.rs @@ -19,6 +19,9 @@ pub const DEFAULT_CPTRA_OBF_KEY: [u32; 8] = [ 0xa0a1a2a3, 0xb0b1b2b3, 0xc0c1c2c3, 0xd0d1d2d3, 0xe0e1e2e3, 0xf0f1f2f3, 0xa4a5a6a7, 0xb4b5b6b7, ]; +pub const DEFAULT_MANUF_DEBUG_UNLOCK_TOKEN: [u32; 4] = + [0xcfcecdcc, 0xcbcac9c8, 0xc7c6c5c4, 0xc3c2c1c0]; + // Based on device_lifecycle_e from RTL #[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] pub enum DeviceLifecycle { @@ -82,6 +85,49 @@ impl SecurityState { } } +#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] +pub struct DbgManufServiceRegReq(u32); +impl From for DbgManufServiceRegReq { + fn from(value: u32) -> Self { + Self(value) + } +} +impl From for u32 { + fn from(value: DbgManufServiceRegReq) -> Self { + value.0 + } +} + +impl DbgManufServiceRegReq { + pub fn set_manuf_dbg_unlock_req(&mut self, val: bool) -> &mut Self { + let mask = 1 << 0; + if val { + self.0 |= mask; + } else { + self.0 &= !mask + }; + self + } + pub fn set_prod_dbg_unlock_req(&mut self, val: bool) -> &mut Self { + let mask = 1 << 1; + if val { + self.0 |= mask; + } else { + self.0 &= !mask + }; + self + } + pub fn set_uds_program_req(&mut self, val: bool) -> &mut Self { + let mask = 1 << 2; + if val { + self.0 |= mask; + } else { + self.0 &= !mask + }; + self + } +} + #[derive(Clone, Copy, PartialEq, Eq, Debug, Default)] pub enum U4 { #[default] @@ -168,6 +214,7 @@ pub struct Fuses { pub fuse_lms_revocation: u32, pub fuse_mldsa_revocation: u32, pub soc_stepping_id: u16, + pub manuf_dbg_unlock_token: [u32; 4], } impl Default for Fuses { fn default() -> Self { @@ -186,6 +233,7 @@ impl Default for Fuses { fuse_lms_revocation: Default::default(), fuse_mldsa_revocation: Default::default(), soc_stepping_id: Default::default(), + manuf_dbg_unlock_token: DEFAULT_MANUF_DEBUG_UNLOCK_TOKEN, } } } diff --git a/drivers/src/dma.rs b/drivers/src/dma.rs index 6c15e8ef28..bab3d91ded 100644 --- a/drivers/src/dma.rs +++ b/drivers/src/dma.rs @@ -17,6 +17,7 @@ use caliptra_registers::axi_dma::{ enums::{RdRouteE, WrRouteE}, AxiDmaReg, }; +use core::ops::Add; use zerocopy::AsBytes; pub enum DmaReadTarget { @@ -39,6 +40,22 @@ impl From for AxiAddr { } } } +impl From for u64 { + fn from(addr: AxiAddr) -> Self { + (addr.hi as u64) << 32 | (addr.lo as u64) + } +} + +impl Add for AxiAddr { + type Output = Self; + + fn add(self, rhs: Self) -> Self { + let self_u64: u64 = self.into(); + let rhs_u64: u64 = rhs.into(); + let sum = self_u64 + rhs_u64; + sum.into() + } +} pub struct DmaReadTransaction { pub read_addr: AxiAddr, @@ -230,20 +247,34 @@ impl Dma { /// * `CaliptraResult` - Read value or error code pub fn read_dword(&mut self, read_addr: AxiAddr) -> CaliptraResult { let mut read_val: u32 = 0; + self.read_buffer(read_addr, read_val.as_bytes_mut())?; + Ok(read_val) + } + /// Read an arbitrary length buffer to fifo and read back the fifo into the provided buffer + /// + /// # Arguments + /// + /// * `read_addr` - Address to read from + /// * `buffer` - Target location to read to + /// + /// # Returns + /// + /// * CaliptraResult<()> - Success or failure + pub fn read_buffer(&mut self, read_addr: AxiAddr, buffer: &mut [u8]) -> CaliptraResult<()> { self.flush(); let read_transaction = DmaReadTransaction { read_addr, fixed_addr: false, - length: core::mem::size_of::() as u32, + length: buffer.len() as u32, target: DmaReadTarget::AhbFifo, }; self.setup_dma_read(read_transaction); self.do_transaction()?; - self.dma_read_fifo(read_val.as_bytes_mut())?; - Ok(read_val) + self.dma_read_fifo(buffer)?; + Ok(()) } /// Write a 32-bit word to the specified address diff --git a/drivers/src/fuse_bank.rs b/drivers/src/fuse_bank.rs index 1664f9b8c7..182febcc79 100644 --- a/drivers/src/fuse_bank.rs +++ b/drivers/src/fuse_bank.rs @@ -12,7 +12,7 @@ Abstract: --*/ -use crate::Array4x12; +use crate::{Array4x12, Array4x4}; use caliptra_cfi_derive::Launder; use caliptra_registers::soc_ifc::SocIfcReg; use zerocopy::AsBytes; @@ -342,6 +342,19 @@ impl FuseBank<'_> { let soc_ifc_regs = self.soc_ifc.regs(); soc_ifc_regs.fuse_lms_revocation().read() } + + /// Get the manufactoring debug unlock token + /// + /// # Arguments + /// * None + /// + /// # Returns + /// manufactoring debug unlock token + /// + pub fn manuf_dbg_unlock_token(&self) -> Array4x4 { + let soc_ifc_regs = self.soc_ifc.regs(); + Array4x4::read_from_reg(soc_ifc_regs.fuse_manuf_dbg_unlock_token()) + } } #[cfg(test)] diff --git a/drivers/src/lib.rs b/drivers/src/lib.rs index 38ec3414a9..941b7c8571 100644 --- a/drivers/src/lib.rs +++ b/drivers/src/lib.rs @@ -48,7 +48,7 @@ pub mod pic; pub mod printer; mod sha1; mod sha256; -mod sha2_512_384; +pub mod sha2_512_384; mod sha2_512_384acc; mod soc_ifc; mod trng; diff --git a/drivers/src/mailbox.rs b/drivers/src/mailbox.rs index d1a4224cf2..f331660647 100644 --- a/drivers/src/mailbox.rs +++ b/drivers/src/mailbox.rs @@ -510,6 +510,14 @@ impl MailboxRecvTxn<'_> { self.state = MailboxOpState::Idle; Ok(()) } + + /// + /// Set UC TAP unlock + /// + pub fn set_uc_tap_unlock(&mut self, enable: bool) { + let mbox = self.mbox.regs_mut(); + mbox.tap_mode().modify(|w| w.enabled(enable)) + } } impl Drop for MailboxRecvTxn<'_> { diff --git a/drivers/src/sha2_512_384.rs b/drivers/src/sha2_512_384.rs index 8e62763b90..1249dad847 100644 --- a/drivers/src/sha2_512_384.rs +++ b/drivers/src/sha2_512_384.rs @@ -59,13 +59,32 @@ impl Sha2_512_384 { /// # Returns /// /// * `Sha2DigestOp` - Object representing the digest operation - pub fn sha384_digest_init(&mut self) -> CaliptraResult> { + pub fn sha384_digest_init(&mut self) -> CaliptraResult> { let op = Sha2DigestOp { sha: self, state: Sha2DigestState::Init, buf: [0u8; SHA512_BLOCK_BYTE_SIZE], buf_idx: 0, data_size: 0, + _phantom: core::marker::PhantomData, + }; + + Ok(op) + } + + /// Initialize multi step digest operation + /// + /// # Returns + /// + /// * `Sha2DigestOp` - Object representing the digest operation + pub fn sha512_digest_init(&mut self) -> CaliptraResult> { + let op = Sha2DigestOp { + sha: self, + state: Sha2DigestState::Init, + buf: [0u8; SHA512_BLOCK_BYTE_SIZE], + buf_idx: 0, + data_size: 0, + _phantom: core::marker::PhantomData, }; Ok(op) @@ -201,6 +220,19 @@ impl Sha2_512_384 { Array4x12::read_from_reg(sha.digest().truncate::<12>()) } + /// Copy digest to buffer + /// + /// # Arguments + /// + /// * `buf` - Digest buffer + fn sha512_read_digest(&mut self) -> Array4x16 { + let sha = self.sha512.regs(); + // digest_block() only waits until the peripheral is ready for the next + // command; the result register may not be valid yet + wait::until(|| sha.status().read().valid()); + Array4x16::read_from_reg(sha.digest()) + } + /// Generate digest over PCRs + nonce /// /// # Arguments @@ -387,7 +419,7 @@ enum Sha2DigestState { } /// Multi step SHA-384 digest operation -pub struct Sha2DigestOp<'a, const HASH_SIZE: u16> { +pub struct Sha2DigestOp<'a, V> { /// SHA-384 Engine sha: &'a mut Sha2_512_384, @@ -402,9 +434,12 @@ pub struct Sha2DigestOp<'a, const HASH_SIZE: u16> { /// Data size data_size: usize, + + /// Phantom data to use the type parameter + _phantom: core::marker::PhantomData, } -impl<'a, const HASH_SIZE: u16> Sha2DigestOp<'a, HASH_SIZE> { +impl<'a, V: Sha2Variant> Sha2DigestOp<'a, V> { /// Check if this the first digest operation fn is_first(&self) -> bool { self.state == Sha2DigestState::Init @@ -418,70 +453,121 @@ impl<'a, const HASH_SIZE: u16> Sha2DigestOp<'a, HASH_SIZE> { } } -impl<'a> Sha2DigestOp<'a, 384> { +impl<'a> Sha2DigestOpTrait<'a, Sha384> for Sha2DigestOp<'a, Sha384> { + fn as_digest_op(&mut self) -> &mut Sha2DigestOp<'a, Sha384> { + self + } +} + +impl<'a> Sha2DigestOpTrait<'a, Sha512> for Sha2DigestOp<'a, Sha512> { + fn as_digest_op(&mut self) -> &mut Sha2DigestOp<'a, Sha512> { + self + } +} + +/// Trait for SHA-2 digest operations +pub trait Sha2DigestOpTrait<'a, V: Sha2Variant>: Sized { + /// Get mutable reference to internal state + fn as_digest_op(&mut self) -> &mut Sha2DigestOp<'a, V>; + /// Update the digest with data - /// - /// # Arguments - /// - /// * `data` - Data to used to update the digest - pub fn update(&mut self, data: &[u8]) -> CaliptraResult<()> { - if self.state == Sha2DigestState::Final { + fn update(&mut self, data: &[u8]) -> CaliptraResult<()> { + let this = self.as_digest_op(); + if this.state == Sha2DigestState::Final { return Err(CaliptraError::DRIVER_SHA384_INVALID_STATE_ERR); } - if self.data_size + data.len() > SHA512_MAX_DATA_SIZE { + if this.data_size + data.len() > SHA512_MAX_DATA_SIZE { return Err(CaliptraError::DRIVER_SHA384_MAX_DATA_ERR); } for byte in data { - self.data_size += 1; + this.data_size += 1; - // PANIC-FREE: Following check optimizes the out of bounds - // panic in indexing the `buf` - if self.buf_idx >= self.buf.len() { + if this.buf_idx >= this.buf.len() { return Err(CaliptraError::DRIVER_SHA384_INDEX_OUT_OF_BOUNDS); } - // Copy the data to the buffer - self.buf[self.buf_idx] = *byte; - self.buf_idx += 1; + this.buf[this.buf_idx] = *byte; + this.buf_idx += 1; - // If the buffer is full calculate the digest of accumulated data - if self.buf_idx == self.buf.len() { - self.sha - .digest_block(ShaMode::Sha384, &self.buf, self.is_first(), false)?; - self.reset_buf_state(); + if this.buf_idx == this.buf.len() { + this.sha + .digest_block(V::sha_mode(), &this.buf, this.is_first(), false)?; + this.reset_buf_state(); } } Ok(()) } - /// Finalize the digest operations - pub fn finalize(mut self, digest: &mut Array4x12) -> CaliptraResult<()> { - if self.state == Sha2DigestState::Final { + /// Finalize the digest operation + fn finalize(mut self, digest: &mut V::DigestType) -> CaliptraResult<()> + where + Self: Sized, + { + let this = self.as_digest_op(); + if this.state == Sha2DigestState::Final { return Err(CaliptraError::DRIVER_SHA384_INVALID_STATE_ERR); } - if self.buf_idx > self.buf.len() { + if this.buf_idx > this.buf.len() { return Err(CaliptraError::DRIVER_SHA2_INVALID_SLICE); } - // Calculate the digest of the final block - let buf = &self.buf[..self.buf_idx]; - self.sha - .digest_partial_block(ShaMode::Sha384, buf, self.is_first(), self.data_size)?; + let buf = &this.buf[..this.buf_idx]; + this.sha + .digest_partial_block(V::sha_mode(), buf, this.is_first(), this.data_size)?; - // Set the state of the operation to final - self.state = Sha2DigestState::Final; - - // Copy digest - *digest = self.sha.sha384_read_digest(); + this.state = Sha2DigestState::Final; + *digest = V::read_digest(this.sha); Ok(()) } } +/// Trait for SHA-2 variants defining their specific behaviors +pub trait Sha2Variant { + /// The digest type for this SHA-2 variant + type DigestType; + + /// Get the SHA mode for this variant + fn sha_mode() -> ShaMode; + + /// Read the digest from hardware + fn read_digest(sha: &mut Sha2_512_384) -> Self::DigestType; +} + +/// SHA-384 variant implementation +pub struct Sha384; + +impl Sha2Variant for Sha384 { + type DigestType = Array4x12; + + fn sha_mode() -> ShaMode { + ShaMode::Sha384 + } + + fn read_digest(sha: &mut Sha2_512_384) -> Self::DigestType { + sha.sha384_read_digest() + } +} + +/// SHA-512 variant implementation +pub struct Sha512; + +impl Sha2Variant for Sha512 { + type DigestType = Array4x16; + + fn sha_mode() -> ShaMode { + ShaMode::Sha512 + } + + fn read_digest(sha: &mut Sha2_512_384) -> Self::DigestType { + sha.sha512_read_digest() + } +} + /// SHA-384 key access error trait trait Sha384KeyAccessErr { /// Convert to read data operation error diff --git a/drivers/src/soc_ifc.rs b/drivers/src/soc_ifc.rs index 24e641fbb4..49b7a856a6 100644 --- a/drivers/src/soc_ifc.rs +++ b/drivers/src/soc_ifc.rs @@ -12,6 +12,8 @@ Abstract: --*/ +use crate::{Array4x16, AxiAddr}; +use bitfield::size_of; use caliptra_cfi_derive::Launder; use caliptra_error::{CaliptraError, CaliptraResult}; use caliptra_registers::soc_ifc::enums::DeviceLifecycleE; @@ -75,6 +77,116 @@ impl SocIfc { soc_ifc_regs.cptra_security_state().read().debug_locked() } + /// Subsystem debug intent + fn ss_debug_intent(&self) -> bool { + let soc_ifc_regs = self.soc_ifc.regs(); + soc_ifc_regs.ss_debug_intent().read().debug_intent() + } + + /// Subsystem debug unlock requested + pub fn ss_debug_unlock_req(&self) -> CaliptraResult { + if !self.ss_debug_intent() { + return Ok(false); + } + + let soc_ifc_regs = self.soc_ifc.regs(); + let lifecycle = self.lifecycle(); + let dbg_req = soc_ifc_regs.ss_dbg_manuf_service_reg_req().read(); + let (manuf, prod) = ( + dbg_req.manuf_dbg_unlock_req(), + dbg_req.prod_dbg_unlock_req(), + ); + + match (manuf, prod, lifecycle) { + (true, false, Lifecycle::Manufacturing) | (false, true, Lifecycle::Production) => { + Ok(true) + } + (true, true, _) | (true, false, _) | (false, true, _) => { + Err(CaliptraError::ROM_SS_DBG_UNLOCK_INVALID_REQ_REG_VALUE) + } + (false, false, _) => Ok(false), + } + } + + /// Set debug unlock in progress + pub fn set_ss_dbg_unlock_in_progress(&mut self, process: bool) { + let lifecycle = self.lifecycle(); + let soc_ifc_regs = self.soc_ifc.regs_mut(); + match lifecycle { + Lifecycle::Manufacturing => soc_ifc_regs + .ss_dbg_manuf_service_reg_rsp() + .write(|w| w.manuf_dbg_unlock_in_progress(process)), + DeviceLifecycleE::Production => soc_ifc_regs + .ss_dbg_manuf_service_reg_rsp() + .write(|w| w.prod_dbg_unlock_in_progress(process)), + _ => (), + } + } + + /// Set debug unlock as finished with either failure or success + pub fn finish_ss_dbg_unluck(&mut self, success: bool) { + let lifecycle = self.lifecycle(); + let soc_ifc_regs = self.soc_ifc.regs_mut(); + if success { + match lifecycle { + Lifecycle::Manufacturing => { + soc_ifc_regs.ss_dbg_manuf_service_reg_rsp().write(|w| { + w.manuf_dbg_unlock_in_progress(false) + .manuf_dbg_unlock_success(true) + }) + } + DeviceLifecycleE::Production => { + soc_ifc_regs.ss_dbg_manuf_service_reg_rsp().write(|w| { + w.prod_dbg_unlock_in_progress(false) + .prod_dbg_unlock_success(true) + }) + } + _ => (), + } + } else { + match lifecycle { + Lifecycle::Manufacturing => { + soc_ifc_regs.ss_dbg_manuf_service_reg_rsp().write(|w| { + w.manuf_dbg_unlock_in_progress(false) + .manuf_dbg_unlock_fail(true) + }) + } + DeviceLifecycleE::Production => { + soc_ifc_regs.ss_dbg_manuf_service_reg_rsp().write(|w| { + w.prod_dbg_unlock_in_progress(false) + .prod_dbg_unlock_fail(true) + }) + } + _ => (), + } + } + } + + /// MCI AXI base address + pub fn ss_mci_axi_base(&self) -> AxiAddr { + let soc_ifc_regs = self.soc_ifc.regs(); + AxiAddr { + lo: soc_ifc_regs.ss_caliptra_base_addr_l().read(), + hi: soc_ifc_regs.ss_caliptra_base_addr_h().read(), + } + } + + /// Debug unlock memory offset + pub fn debug_unlock_pk_hash_offset(&self, level: usize) -> CaliptraResult { + let soc_ifc_regs = self.soc_ifc.regs(); + let fusebank_offset = soc_ifc_regs + .ss_prod_debug_unlock_auth_pk_hash_reg_bank_offset() + .read() as usize; + let num_of_debug_pk_hashes = soc_ifc_regs + .ss_num_of_prod_debug_unlock_auth_pk_hashes() + .read(); + if level as u32 >= num_of_debug_pk_hashes { + Err(CaliptraError::ROM_SS_DBG_UNLOCK_PROD_INVALID_LEVEL)? + } + // DEBUG_AUTH_PK_HASH_REG_BANK_OFFSET register value + ( Debug Unlock Level * SHA2-512 hash size (64 bytes) ) + Ok(fusebank_offset + size_of::() * level) + } + pub fn mbox_valid_pauser(&self) -> [u32; 5] { let soc_ifc_regs = self.soc_ifc.regs(); soc_ifc_regs.cptra_mbox_valid_axi_user().read() diff --git a/drivers/test-fw/src/bin/sha384_tests.rs b/drivers/test-fw/src/bin/sha384_tests.rs index ead03d3f4f..7f97ece65e 100644 --- a/drivers/test-fw/src/bin/sha384_tests.rs +++ b/drivers/test-fw/src/bin/sha384_tests.rs @@ -16,6 +16,7 @@ Abstract: #![no_main] use caliptra_cfi_lib::CfiCounter; +use caliptra_drivers::sha2_512_384::Sha2DigestOpTrait; use caliptra_drivers::{Array4x12, PcrBank, PcrId, Sha2_512_384}; use caliptra_kat::Sha384Kat; use caliptra_registers::{pv::PvReg, sha512::Sha512Reg}; diff --git a/error/src/lib.rs b/error/src/lib.rs index e8cc104724..50a5759b34 100644 --- a/error/src/lib.rs +++ b/error/src/lib.rs @@ -683,6 +683,24 @@ impl CaliptraError { // TODO: What base value is right for this? // FIPS Hooks pub const FIPS_HOOKS_INJECTED_ERROR: CaliptraError = CaliptraError::new_const(0x90100000); + + // Debug unlock errors + pub const ROM_SS_DBG_UNLOCK_INVALID_REQ_REG_VALUE: CaliptraError = + CaliptraError::new_const(0xa0000000); + pub const ROM_SS_DBG_UNLOCK_MANUF_INVALID_MBOX_CMD: CaliptraError = + CaliptraError::new_const(0xa0000001); + pub const ROM_SS_DBG_UNLOCK_MANUF_INVALID_TOKEN: CaliptraError = + CaliptraError::new_const(0xa0000002); + pub const ROM_SS_DBG_UNLOCK_PROD_INVALID_REQ_MBOX_CMD: CaliptraError = + CaliptraError::new_const(0xa0000003); + pub const ROM_SS_DBG_UNLOCK_PROD_INVALID_REQ: CaliptraError = + CaliptraError::new_const(0xa0000003); + pub const ROM_SS_DBG_UNLOCK_PROD_INVALID_LEVEL: CaliptraError = + CaliptraError::new_const(0xa0000004); + pub const ROM_SS_DBG_UNLOCK_PROD_INVALID_TOKEN_MBOX_CMD: CaliptraError = + CaliptraError::new_const(0xa0000005); + pub const ROM_SS_DBG_UNLOCK_PROD_INVALID_TOKEN: CaliptraError = + CaliptraError::new_const(0xa0000006); } impl From for crate::CaliptraError { diff --git a/hw-model/src/lib.rs b/hw-model/src/lib.rs index 7467662b42..10c9568aa1 100644 --- a/hw-model/src/lib.rs +++ b/hw-model/src/lib.rs @@ -43,7 +43,7 @@ mod output; mod rv32_builder; pub use api::mailbox::mbox_write_fifo; -pub use api_types::{DeviceLifecycle, Fuses, SecurityState, U4}; +pub use api_types::{DbgManufServiceRegReq, DeviceLifecycle, Fuses, SecurityState, U4}; pub use caliptra_emu_bus::BusMmio; pub use caliptra_emu_cpu::{CodeRange, ImageInfo, StackInfo, StackRange}; use output::ExitStatus; @@ -155,6 +155,10 @@ pub struct InitParams<'a> { pub security_state: SecurityState, + pub dbg_manuf_service: DbgManufServiceRegReq, + + pub debug_intent: bool, + // The silicon obfuscation key passed to caliptra_top. pub cptra_obf_key: [u32; 8], @@ -205,6 +209,8 @@ impl<'a> Default for InitParams<'a> { log_writer: Box::new(stdout()), security_state: *SecurityState::default() .set_device_lifecycle(DeviceLifecycle::Unprovisioned), + dbg_manuf_service: Default::default(), + debug_intent: false, cptra_obf_key: DEFAULT_CPTRA_OBF_KEY, itrng_nibbles, etrng_responses, diff --git a/hw-model/src/model_emulated.rs b/hw-model/src/model_emulated.rs index f94c22e4f4..6541e9dbf3 100644 --- a/hw-model/src/model_emulated.rs +++ b/hw-model/src/model_emulated.rs @@ -160,6 +160,8 @@ impl HwModel for ModelEmulated { cpu_enabled_cloned.set(true); }), security_state: params.security_state, + dbg_manuf_service_req: params.dbg_manuf_service, + debug_intent: params.debug_intent, cptra_obf_key: params.cptra_obf_key, itrng_nibbles: Some(params.itrng_nibbles), diff --git a/hw-model/types/src/lib.rs b/hw-model/types/src/lib.rs index 2e054442e1..ce1e72f874 100644 --- a/hw-model/types/src/lib.rs +++ b/hw-model/types/src/lib.rs @@ -27,6 +27,9 @@ pub const DEFAULT_CPTRA_OBF_KEY: [u32; 8] = [ 0xa0a1a2a3, 0xb0b1b2b3, 0xc0c1c2c3, 0xd0d1d2d3, 0xe0e1e2e3, 0xf0f1f2f3, 0xa4a5a6a7, 0xb4b5b6b7, ]; +pub const DEFAULT_MANUF_DEBUG_UNLOCK_TOKEN: [u32; 4] = + [0xcfcecdcc, 0xcbcac9c8, 0xc7c6c5c4, 0xc3c2c1c0]; + struct SecurityStateWrapper(SecurityState); impl std::fmt::Debug for SecurityStateWrapper { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { diff --git a/rom/dev/Cargo.toml b/rom/dev/Cargo.toml index 68f35e844b..b6acd50011 100644 --- a/rom/dev/Cargo.toml +++ b/rom/dev/Cargo.toml @@ -7,6 +7,7 @@ edition = "2021" rust-version = "1.70" [dependencies] +caliptra-api.workspace = true caliptra-cfi-lib = { workspace = true, default-features = false, features = [ "cfi", "cfi-counter", @@ -38,6 +39,7 @@ caliptra-builder.workspace = true caliptra-emu-cpu.workspace = true caliptra-hw-model.workspace = true +caliptra-hw-model-types.workspace = true caliptra-image-elf.workspace = true caliptra-image-fake-keys.workspace = true caliptra-image-gen.workspace = true diff --git a/rom/dev/src/flow/cold_reset/fmc_alias.rs b/rom/dev/src/flow/cold_reset/fmc_alias.rs index 2479d72393..221c4e87f3 100644 --- a/rom/dev/src/flow/cold_reset/fmc_alias.rs +++ b/rom/dev/src/flow/cold_reset/fmc_alias.rs @@ -31,7 +31,8 @@ use caliptra_common::pcr::PCR_ID_FMC_CURRENT; use caliptra_common::RomBootStatus::*; use caliptra_common::{dice, x509}; use caliptra_drivers::{ - okmutref, report_boot_status, Array4x12, CaliptraResult, HmacMode, KeyId, Lifecycle, + okmutref, report_boot_status, sha2_512_384::Sha2DigestOpTrait, Array4x12, CaliptraResult, + HmacMode, KeyId, Lifecycle, }; use caliptra_x509::{ FmcAliasCertTbsEcc384, FmcAliasCertTbsEcc384Params, FmcAliasCertTbsMlDsa87, diff --git a/rom/dev/src/flow/cold_reset/fw_processor.rs b/rom/dev/src/flow/cold_reset/fw_processor.rs index 42b5dc3bb3..ebb41d1d9f 100644 --- a/rom/dev/src/flow/cold_reset/fw_processor.rs +++ b/rom/dev/src/flow/cold_reset/fw_processor.rs @@ -616,7 +616,7 @@ impl FirmwareProcessor { /// # Returns /// * `()` - Ok /// Error code on failure. - fn copy_req_verify_chksum(txn: &mut MailboxRecvTxn, data: &mut [u8]) -> CaliptraResult<()> { + pub fn copy_req_verify_chksum(txn: &mut MailboxRecvTxn, data: &mut [u8]) -> CaliptraResult<()> { // NOTE: Currently ROM only supports commands with a fixed request size // This check will need to be updated if any commands are added with a variable request size if txn.dlen() as usize != data.len() { diff --git a/rom/dev/src/flow/cold_reset/mod.rs b/rom/dev/src/flow/cold_reset/mod.rs index e0ef18e281..a81835ee0e 100644 --- a/rom/dev/src/flow/cold_reset/mod.rs +++ b/rom/dev/src/flow/cold_reset/mod.rs @@ -14,7 +14,7 @@ Abstract: mod dice; mod fmc_alias; -mod fw_processor; +pub mod fw_processor; mod idev_id; mod ldev_id; mod x509; diff --git a/rom/dev/src/flow/debug_unlock.rs b/rom/dev/src/flow/debug_unlock.rs new file mode 100644 index 0000000000..9c7ad4d016 --- /dev/null +++ b/rom/dev/src/flow/debug_unlock.rs @@ -0,0 +1,343 @@ +/*++ + +Licensed under the Apache-2.0 license. + +File Name: + + debug_unlock.rs + +Abstract: + + File contains the code to handle debug unlock + +--*/ + +use core::mem::size_of; + +use crate::flow::cold_reset::fw_processor::FirmwareProcessor; +use crate::CaliptraResult; +use caliptra_api::mailbox::{ + MailboxReqHeader, MailboxRespHeader, ManufDebugUnlockTokenReq, + ProductionAuthDebugUnlockChallenge, ProductionAuthDebugUnlockReq, + ProductionAuthDebugUnlockToken, +}; +use caliptra_cfi_lib::{cfi_launder, CfiCounter}; +use caliptra_common::mailbox_api::CommandId; +use caliptra_drivers::{ + sha2_512_384::Sha2DigestOpTrait, Array4x12, Array4x16, Ecc384PubKey, Ecc384Result, + Ecc384Scalar, Ecc384Signature, Lifecycle, Mldsa87PubKey, Mldsa87Result, Mldsa87Signature, +}; +use caliptra_error::CaliptraError; +use zerocopy::AsBytes; + +use crate::rom_env::RomEnv; + +/// Debug Unlock Flow +/// +/// # Arguments +/// +/// * `env` - ROM Environment +pub fn debug_unlock(env: &mut RomEnv) -> CaliptraResult<()> { + // Always false on passive mode + if !env.soc_ifc.ss_debug_unlock_req()? { + return Ok(()); + } + + crate::cprintln!("[state] debug unlock requested"); + + // [TODO][CAP2] how to handle early failures like wrong CMDs? Should failure bits be set or just return failure? + let lifecycle = env.soc_ifc.lifecycle(); + match lifecycle { + Lifecycle::Production => handle_production(env), + Lifecycle::Manufacturing => handle_manufacturing(env), + _ => Ok(()), + } +} + +fn handle_manufacturing(env: &mut RomEnv) -> CaliptraResult<()> { + let mbox = &mut env.mbox; + let txn = loop { + // Random delay for CFI glitch protection. + CfiCounter::delay(); + + match mbox.peek_recv() { + Some(txn) => break txn, + None => continue, + } + }; + + if CommandId::from(txn.cmd()) != CommandId::MANUF_DEBUG_UNLOCK_REQ_TOKEN { + Err(CaliptraError::ROM_SS_DBG_UNLOCK_MANUF_INVALID_MBOX_CMD)? + } + + let mut txn = txn.start_txn(); + let mut request = ManufDebugUnlockTokenReq::default(); + FirmwareProcessor::copy_req_verify_chksum(&mut txn, request.as_bytes_mut())?; + + env.soc_ifc.set_ss_dbg_unlock_in_progress(true); + + let result: CaliptraResult<()> = (|| { + let nonce: [u8; 32] = env.trng.generate()?.as_bytes()[..32].try_into().unwrap(); + // The ROM then appends a 256-bit random nonce to the token and performs a SHA-512 operation to generate the expected token. + let input_token = { + let mut token: [u8; 64] = [0; 64]; + token[8..][..16].copy_from_slice(&request.token); + token[32..].copy_from_slice(&nonce); + env.sha2_512_384.sha512_digest(&token)? + }; + + // Same transformation as mbox input + let fuse_token = { + let mut token: [u8; 64] = [0; 64]; + let fuse = env.soc_ifc.fuse_bank().manuf_dbg_unlock_token(); + token[8..][..16].copy_from_slice(fuse.as_bytes()); + token[32..].copy_from_slice(&nonce); + env.sha2_512_384.sha512_digest(&token)? + }; + + if cfi_launder(input_token) != fuse_token { + return Err(CaliptraError::ROM_SS_DBG_UNLOCK_MANUF_INVALID_TOKEN); + } else { + caliptra_cfi_lib::cfi_assert_eq_12_words( + &input_token.0[..12].try_into().unwrap(), + &fuse_token.0[..12].try_into().unwrap(), + ); + } + Ok(()) + })(); + + env.soc_ifc.set_ss_dbg_unlock_in_progress(false); + match result { + Ok(()) => { + env.soc_ifc.finish_ss_dbg_unluck(true); + txn.set_uc_tap_unlock(true); + let resp = MailboxRespHeader::default(); + txn.send_response(resp.as_bytes())?; + } + Err(_) => { + env.soc_ifc.finish_ss_dbg_unluck(false); + txn.set_uc_tap_unlock(false); + } + } + result +} + +fn payload_length(length: [u8; 3]) -> usize { + let mut len: usize = 0; + len |= length[0] as usize; + len |= (length[1] as usize) << 8; + len |= (length[2] as usize) << 16; + len * size_of::() +} + +fn handle_production_request( + env: &mut RomEnv, +) -> CaliptraResult<( + ProductionAuthDebugUnlockReq, + ProductionAuthDebugUnlockChallenge, +)> { + let mbox = &mut env.mbox; + let txn = loop { + // Random delay for CFI glitch protection. + CfiCounter::delay(); + + match mbox.peek_recv() { + Some(txn) => break txn, + None => continue, + } + }; + + if CommandId::from(txn.cmd()) != CommandId::PRODUCTION_AUTH_DEBUG_UNLOCK_REQ { + Err(CaliptraError::ROM_SS_DBG_UNLOCK_PROD_INVALID_REQ_MBOX_CMD)? + } + + let mut txn = txn.start_txn(); + let mut request = ProductionAuthDebugUnlockReq::default(); + FirmwareProcessor::copy_req_verify_chksum(&mut txn, request.as_bytes_mut())?; + + let payload_length = |length: [u8; 3]| { + let mut len: usize = 0; + len |= length[0] as usize; + len |= (length[1] as usize) << 8; + len |= (length[2] as usize) << 16; + len * size_of::() + }; + + // Validate payload + if payload_length(request.length) + != size_of::() - size_of::() + { + return Err(CaliptraError::ROM_SS_DBG_UNLOCK_PROD_INVALID_REQ); + } + // [TODO][CAP2] what do these 3 bytes mean when only 4 bits are active? + // Debug level + let dbg_level = payload_length(request.unlock_category); + if dbg_level & 0xf != dbg_level { + return Err(CaliptraError::ROM_SS_DBG_UNLOCK_PROD_INVALID_REQ); + } + + let length = (size_of::() - size_of::()) + / size_of::(); + let challenge = env.trng.generate()?.as_bytes().try_into().unwrap(); + let challenge_resp = ProductionAuthDebugUnlockChallenge { + vendor_id: request.vendor_id, + object_data_type: request.object_data_type, + length: length.to_ne_bytes()[..3].try_into().unwrap(), + unique_device_identifier: { + let mut id = [0u8; 32]; + id[..17].copy_from_slice(&env.soc_ifc.fuse_bank().ueid()); + id + }, + challenge, + ..Default::default() + }; + txn.send_response(challenge_resp.as_bytes())?; + Ok((request, challenge_resp)) +} + +fn handle_production_token( + env: &mut RomEnv, + request: &ProductionAuthDebugUnlockReq, + challenge: &ProductionAuthDebugUnlockChallenge, +) -> CaliptraResult<()> { + let mbox = &mut env.mbox; + let txn = loop { + // Random delay for CFI glitch protection. + CfiCounter::delay(); + + match mbox.peek_recv() { + Some(txn) => break txn, + None => continue, + } + }; + + if CommandId::from(txn.cmd()) != CommandId::PRODUCTION_AUTH_DEBUG_UNLOCK_TOKEN { + return Err(CaliptraError::ROM_SS_DBG_UNLOCK_PROD_INVALID_TOKEN_MBOX_CMD); + } + + env.soc_ifc.set_ss_dbg_unlock_in_progress(true); + + let mut txn = txn.start_txn(); + let mut token = ProductionAuthDebugUnlockToken::default(); + FirmwareProcessor::copy_req_verify_chksum(&mut txn, token.as_bytes_mut())?; + + // Validate payload + if payload_length(token.length) + != size_of::() - size_of::() + { + Err(CaliptraError::ROM_SS_DBG_UNLOCK_PROD_INVALID_TOKEN)? + } + // Debug level + if payload_length(token.unlock_category) != payload_length(request.unlock_category) { + return Err(CaliptraError::ROM_SS_DBG_UNLOCK_PROD_INVALID_TOKEN); + } + if cfi_launder(token.challenge) != challenge.challenge { + return Err(CaliptraError::ROM_SS_DBG_UNLOCK_PROD_INVALID_TOKEN); + } else { + caliptra_cfi_lib::cfi_assert_eq_12_words( + &Array4x12::from(token.challenge).0, + &Array4x12::from(challenge.challenge).0, + ); + } + + let debug_auth_pk_offset = + env.soc_ifc + .debug_unlock_pk_hash_offset(payload_length(token.unlock_category))? as u64; + let mci_base = env.soc_ifc.ss_mci_axi_base(); + let debug_auth_pk_hash_base = mci_base + debug_auth_pk_offset.into(); + + let dma = &mut env.dma; + let mut fuse_digest = Array4x16::default(); + dma.read_buffer(debug_auth_pk_hash_base, fuse_digest.as_bytes_mut())?; + + let mut digest_op = env.sha2_512_384.sha512_digest_init()?; + digest_op.update(&token.ecc_public_key)?; + digest_op.update(&token.mldsa_public_key)?; + let mut request_digest = Array4x16::default(); + digest_op.finalize(&mut request_digest)?; + + // Verify that digest of keys match + if cfi_launder(request_digest) != fuse_digest { + env.soc_ifc.finish_ss_dbg_unluck(false); + txn.set_uc_tap_unlock(false); + return Err(CaliptraError::ROM_SS_DBG_UNLOCK_MANUF_INVALID_TOKEN); + } else { + caliptra_cfi_lib::cfi_assert_eq_12_words( + &request_digest.0[..12].try_into().unwrap(), + &fuse_digest.0[..12].try_into().unwrap(), + ); + } + + // Verify that the challenge is properly signed by the keys + let pubkey = Ecc384PubKey { + x: Ecc384Scalar::from(<[u8; 48]>::try_from(&token.ecc_public_key[..48]).unwrap()), + y: Ecc384Scalar::from(<[u8; 48]>::try_from(&token.ecc_public_key[48..]).unwrap()), + }; + let signature = Ecc384Signature { + r: Ecc384Scalar::from(<[u8; 48]>::try_from(&token.ecc_signature[..48]).unwrap()), + s: Ecc384Scalar::from(<[u8; 48]>::try_from(&token.ecc_signature[48..]).unwrap()), + }; + let mut digest_op = env.sha2_512_384.sha384_digest_init()?; + digest_op.update(&token.challenge)?; + digest_op.update(&token.unique_device_identifier)?; + digest_op.update(&token.unlock_category)?; + let mut ecc_msg = Array4x12::default(); + digest_op.finalize(&mut ecc_msg)?; + let result = env.ecc384.verify(&pubkey, &ecc_msg, &signature)?; + if result == Ecc384Result::SigVerifyFailed { + env.soc_ifc.finish_ss_dbg_unluck(false); + txn.set_uc_tap_unlock(false); + Err(CaliptraError::ROM_SS_DBG_UNLOCK_MANUF_INVALID_TOKEN)?; + } + + let mut digest_op = env.sha2_512_384.sha512_digest_init()?; + digest_op.update(&token.challenge)?; + digest_op.update(&token.unique_device_identifier)?; + digest_op.update(&token.unlock_category)?; + let mut mldsa_msg = Array4x16::default(); + digest_op.finalize(&mut mldsa_msg)?; + + let result = env.mldsa87.verify( + &Mldsa87PubKey::from(&token.mldsa_public_key), + &mldsa_msg, + &Mldsa87Signature::from(&token.mldsa_signature), + )?; + + if result == Mldsa87Result::SigVerifyFailed { + return Err(CaliptraError::ROM_SS_DBG_UNLOCK_MANUF_INVALID_TOKEN); + } + Ok(()) +} + +fn handle_production(env: &mut RomEnv) -> CaliptraResult<()> { + let (request, challenge) = handle_production_request(env)?; + let result = handle_production_token(env, &request, &challenge); + + env.soc_ifc.set_ss_dbg_unlock_in_progress(false); + + let mbox = &mut env.mbox; + let txn = loop { + // Random delay for CFI glitch protection. + CfiCounter::delay(); + + match mbox.peek_recv() { + Some(txn) => break txn, + None => continue, + } + }; + let mut txn = txn.start_txn(); + + match result { + Ok(()) => { + env.soc_ifc.finish_ss_dbg_unluck(true); + txn.set_uc_tap_unlock(true); + let resp = MailboxRespHeader::default(); + txn.send_response(resp.as_bytes())?; + } + Err(_) => { + env.soc_ifc.finish_ss_dbg_unluck(false); + txn.set_uc_tap_unlock(false); + } + } + result +} diff --git a/rom/dev/src/flow/mod.rs b/rom/dev/src/flow/mod.rs index 0d0d41a8f5..f935637832 100644 --- a/rom/dev/src/flow/mod.rs +++ b/rom/dev/src/flow/mod.rs @@ -13,6 +13,7 @@ Abstract: --*/ mod cold_reset; +pub mod debug_unlock; #[cfg(feature = "fake-rom")] mod fake; pub(crate) mod uds_programming; diff --git a/rom/dev/src/main.rs b/rom/dev/src/main.rs index fa121603a6..25ae3b4de7 100644 --- a/rom/dev/src/main.rs +++ b/rom/dev/src/main.rs @@ -88,13 +88,17 @@ pub extern "C" fn rom_entry() -> ! { report_boot_status(RomBootStatus::CfiInitialized.into()); - let _lifecyle = match env.soc_ifc.lifecycle() { + let lifecyle = match env.soc_ifc.lifecycle() { caliptra_drivers::Lifecycle::Unprovisioned => "Unprovisioned", caliptra_drivers::Lifecycle::Manufacturing => "Manufacturing", caliptra_drivers::Lifecycle::Production => "Production", caliptra_drivers::Lifecycle::Reserved2 => "Unknown", }; - cprintln!("[state] LifecycleState = {}", _lifecyle); + cprintln!("[state] LifecycleState = {}", lifecyle); + + if let Err(err) = crate::flow::debug_unlock::debug_unlock(&mut env) { + handle_fatal_error(err.into()); + } // UDS programming. if let Err(err) = crate::flow::UdsProgrammingFlow::program_uds(&mut env) { diff --git a/rom/dev/tests/rom_integration_tests/main.rs b/rom/dev/tests/rom_integration_tests/main.rs index 73e7b76021..d18fa13121 100644 --- a/rom/dev/tests/rom_integration_tests/main.rs +++ b/rom/dev/tests/rom_integration_tests/main.rs @@ -6,6 +6,7 @@ mod rv32_unit_tests; mod test_capabilities; mod test_cfi; mod test_cpu_fault; +mod test_debug_unlock; mod test_dice_derivations; mod test_fake_rom; mod test_fips_hooks; diff --git a/rom/dev/tests/rom_integration_tests/test_debug_unlock.rs b/rom/dev/tests/rom_integration_tests/test_debug_unlock.rs new file mode 100644 index 0000000000..7f747fb4af --- /dev/null +++ b/rom/dev/tests/rom_integration_tests/test_debug_unlock.rs @@ -0,0 +1,58 @@ +// Licensed under the Apache-2.0 license + +use caliptra_api::mailbox::{CommandId, MailboxReqHeader, ManufDebugUnlockTokenReq}; +use caliptra_api::SocManager; +use caliptra_builder::firmware::ROM_WITH_UART; +use caliptra_hw_model::{DbgManufServiceRegReq, DeviceLifecycle, HwModel, SecurityState}; +use zerocopy::AsBytes; + +#[test] +fn test_dbg_unlock_manuf() { + let security_state = *SecurityState::default() + .set_debug_locked(true) + .set_device_lifecycle(DeviceLifecycle::Manufacturing); + + let dbg_manuf_service = *DbgManufServiceRegReq::default().set_manuf_dbg_unlock_req(true); + + let rom = caliptra_builder::build_firmware_rom(&ROM_WITH_UART).unwrap(); + + let mut hw = caliptra_hw_model::new( + caliptra_hw_model::InitParams { + rom: &rom, + security_state, + dbg_manuf_service, + debug_intent: true, + ..Default::default() + }, + caliptra_hw_model::BootParams::default(), + ) + .unwrap(); + + let token = ManufDebugUnlockTokenReq { + token: caliptra_hw_model_types::DEFAULT_MANUF_DEBUG_UNLOCK_TOKEN + .as_bytes() + .try_into() + .unwrap(), + ..Default::default() + }; + let checksum = caliptra_common::checksum::calc_checksum( + u32::from(CommandId::MANUF_DEBUG_UNLOCK_REQ_TOKEN), + &token.as_bytes()[4..], + ); + let token = ManufDebugUnlockTokenReq { + hdr: MailboxReqHeader { chksum: checksum }, + ..token + }; + hw.mailbox_execute( + CommandId::MANUF_DEBUG_UNLOCK_REQ_TOKEN.into(), + token.as_bytes(), + ) + .unwrap(); + + hw.step_until(|m| { + let resp = m.soc_ifc().ss_dbg_manuf_service_reg_rsp().read(); + resp.manuf_dbg_unlock_success() + }); +} + +// [TODO][CAP2] write unit test for production diff --git a/runtime/src/dpe_crypto.rs b/runtime/src/dpe_crypto.rs index b8ba9cc78d..04a7070f9f 100644 --- a/runtime/src/dpe_crypto.rs +++ b/runtime/src/dpe_crypto.rs @@ -18,9 +18,11 @@ use caliptra_cfi_derive_git::cfi_impl_fn; use caliptra_cfi_lib_git::{cfi_assert, cfi_assert_eq, cfi_launder}; use caliptra_common::keyids::{KEY_ID_DPE_CDI, KEY_ID_DPE_PRIV_KEY, KEY_ID_TMP}; use caliptra_drivers::{ - cprintln, hmac_kdf, Array4x12, Ecc384, Ecc384PrivKeyIn, Ecc384PubKey, Ecc384Scalar, Ecc384Seed, - Hmac, HmacData, HmacKey, HmacMode, HmacTag, KeyId, KeyReadArgs, KeyUsage, KeyVault, - KeyWriteArgs, Sha2DigestOp, Sha2_512_384, Trng, + cprintln, hmac_kdf, + sha2_512_384::{Sha2DigestOpTrait, Sha384}, + Array4x12, Ecc384, Ecc384PrivKeyIn, Ecc384PubKey, Ecc384Scalar, Ecc384Seed, Hmac, HmacData, + HmacKey, HmacMode, HmacTag, KeyId, KeyReadArgs, KeyUsage, KeyVault, KeyWriteArgs, Sha2DigestOp, + Sha2_512_384, Trng, }; use crypto::{AlgLen, Crypto, CryptoBuf, CryptoError, Digest, EcdsaPub, EcdsaSig, Hasher, HmacSig}; use zerocopy::AsBytes; @@ -71,11 +73,11 @@ impl Drop for DpeCrypto<'_> { } pub struct DpeHasher<'a> { - op: Sha2DigestOp<'a, 384>, + op: Sha2DigestOp<'a, Sha384>, } impl<'a> DpeHasher<'a> { - pub fn new(op: Sha2DigestOp<'a, 384>) -> Self { + pub fn new(op: Sha2DigestOp<'a, Sha384>) -> Self { Self { op } } } diff --git a/runtime/src/drivers.rs b/runtime/src/drivers.rs index 2c018319a6..38251b6849 100644 --- a/runtime/src/drivers.rs +++ b/runtime/src/drivers.rs @@ -28,10 +28,10 @@ use caliptra_cfi_derive_git::{cfi_impl_fn, cfi_mod_fn}; use caliptra_cfi_lib_git::{cfi_assert, cfi_assert_eq, cfi_assert_eq_12_words, cfi_launder}; use caliptra_common::mailbox_api::AddSubjectAltNameReq; use caliptra_drivers::{ - cprint, cprintln, hand_off::DataStore, pcr_log::RT_FW_JOURNEY_PCR, Array4x12, CaliptraError, - CaliptraResult, DataVault, Ecc384, Ecc384PubKey, Hmac, KeyId, KeyVault, Lms, Mldsa87, PcrBank, - PcrId, PersistentDataAccessor, Pic, ResetReason, Sha1, Sha256, Sha256Alg, Sha2_512_384, - Sha2_512_384Acc, SocIfc, Trng, + cprint, cprintln, hand_off::DataStore, pcr_log::RT_FW_JOURNEY_PCR, + sha2_512_384::Sha2DigestOpTrait, Array4x12, CaliptraError, CaliptraResult, DataVault, Ecc384, + Ecc384PubKey, Hmac, KeyId, KeyVault, Lms, Mldsa87, PcrBank, PcrId, PersistentDataAccessor, Pic, + ResetReason, Sha1, Sha256, Sha256Alg, Sha2_512_384, Sha2_512_384Acc, SocIfc, Trng, }; use caliptra_image_types::ImageManifest; use caliptra_registers::{ diff --git a/runtime/src/hmac.rs b/runtime/src/hmac.rs index d7fb561b34..74961ed632 100644 --- a/runtime/src/hmac.rs +++ b/runtime/src/hmac.rs @@ -16,8 +16,8 @@ use caliptra_cfi_derive_git::{cfi_impl_fn, cfi_mod_fn}; use caliptra_cfi_lib_git::{cfi_assert, cfi_assert_eq, cfi_launder}; use caliptra_common::{crypto::Ecc384KeyPair, keyids::KEY_ID_TMP}; use caliptra_drivers::{ - hmac_kdf, Array4x12, Ecc384PrivKeyOut, Ecc384PubKey, HmacData, HmacKey, HmacMode, HmacTag, - KeyId, KeyReadArgs, KeyUsage, KeyWriteArgs, + hmac_kdf, sha2_512_384::Sha2DigestOpTrait, Array4x12, Ecc384PrivKeyOut, Ecc384PubKey, HmacData, + HmacKey, HmacMode, HmacTag, KeyId, KeyReadArgs, KeyUsage, KeyWriteArgs, }; use caliptra_error::CaliptraResult; use zerocopy::AsBytes; diff --git a/runtime/test-fw/src/mbox_responder.rs b/runtime/test-fw/src/mbox_responder.rs index 09b08fa391..3ca5046bd0 100644 --- a/runtime/test-fw/src/mbox_responder.rs +++ b/runtime/test-fw/src/mbox_responder.rs @@ -9,6 +9,7 @@ use caliptra_common::{handle_fatal_error, mailbox_api::CommandId}; use caliptra_drivers::{ cprintln, pcr_log::{PCR_ID_STASH_MEASUREMENT, RT_FW_JOURNEY_PCR}, + sha2_512_384::Sha2DigestOpTrait, Array4x12, CaliptraError, CaliptraResult, }; use caliptra_registers::{mbox::enums::MboxStatusE, soc_ifc::SocIfcReg}; diff --git a/sw-emulator/lib/periph/src/root_bus.rs b/sw-emulator/lib/periph/src/root_bus.rs index 1864c0d714..1dd6e5b5c0 100644 --- a/sw-emulator/lib/periph/src/root_bus.rs +++ b/sw-emulator/lib/periph/src/root_bus.rs @@ -22,7 +22,7 @@ use crate::{ AsymEcc384, Csrng, Doe, EmuCtrl, HashSha256, HashSha512, HmacSha, KeyVault, MailboxExternal, MailboxInternal, MailboxRam, Sha512Accelerator, SocRegistersInternal, Uart, }; -use caliptra_api_types::SecurityState; +use caliptra_api_types::{DbgManufServiceRegReq, SecurityState}; use caliptra_emu_bus::{Clock, Ram, Rom}; use caliptra_emu_cpu::{Pic, PicMmioRegisters}; use caliptra_emu_derive::Bus; @@ -213,6 +213,8 @@ pub struct CaliptraRootBusArgs { pub log_dir: PathBuf, // The security state wires provided to caliptra_top pub security_state: SecurityState, + pub dbg_manuf_service_req: DbgManufServiceRegReq, + pub debug_intent: bool, /// Callback to customize application behavior when /// a write to the tb-services register write is performed. @@ -234,6 +236,8 @@ impl Default for CaliptraRootBusArgs { rom: Default::default(), log_dir: Default::default(), security_state: Default::default(), + dbg_manuf_service_req: Default::default(), + debug_intent: false, tb_services_cb: Default::default(), ready_for_fw_cb: Default::default(), upload_update_fw: Default::default(), diff --git a/sw-emulator/lib/periph/src/soc_reg.rs b/sw-emulator/lib/periph/src/soc_reg.rs index 6a3e878ad8..99bffa573d 100644 --- a/sw-emulator/lib/periph/src/soc_reg.rs +++ b/sw-emulator/lib/periph/src/soc_reg.rs @@ -300,6 +300,11 @@ register_bitfields! [ RSVD OFFSET(6) NUMBITS(26) [], ], + /// Debug intent + SsDebugIntent [ + DEBUG_INTENT OFFSET(0) NUMBITS(1) [], + ], + /// SubSytem Debug Manufacturing Service Request Register SsDbgManufServiceRegReq [ MANUF_DBG_UNLOCK_REQ OFFSET(0) NUMBITS(1) [], @@ -708,6 +713,9 @@ struct SocRegistersImpl { #[register(offset = 0x524)] ss_uds_seed_base_addr_h: ReadOnlyRegister, + #[register(offset = 0x530)] + ss_debug_intent: ReadOnlyRegister, + #[register(offset = 0x5c0)] ss_dbg_manuf_service_reg_req: ReadWriteRegister, @@ -906,6 +914,9 @@ impl SocRegistersImpl { fuse_mldsa_revocation: Default::default(), fuse_soc_stepping_id: ReadWriteRegister::new(0), fuse_manuf_dbg_unlock_token: [0; 4], + ss_debug_intent: ReadOnlyRegister::new(if args.debug_intent { 1 } else { 0 }), + ss_dbg_manuf_service_reg_req: ReadWriteRegister::new(args.dbg_manuf_service_req.into()), + ss_dbg_manuf_service_reg_rsp: ReadWriteRegister::new(0), internal_obf_key: args.cptra_obf_key, internal_iccm_lock: ReadWriteRegister::new(0), internal_fw_update_reset: ReadWriteRegister::new(0), @@ -941,8 +952,6 @@ impl SocRegistersImpl { etrng_responses: args.etrng_responses, pending_etrng_response: None, op_pending_etrng_response_action: None, - ss_dbg_manuf_service_reg_req: ReadWriteRegister::new(0), - ss_dbg_manuf_service_reg_rsp: ReadWriteRegister::new(0), ss_uds_seed_base_addr_l: ReadOnlyRegister::new(0), // [TODO][CAP2] Program this ss_uds_seed_base_addr_h: ReadOnlyRegister::new(0), // [TODO][CAP2] Program this };