diff --git a/common/src/mailbox_api.rs b/common/src/mailbox_api.rs index dc27504988..70e2a146d4 100644 --- a/common/src/mailbox_api.rs +++ b/common/src/mailbox_api.rs @@ -9,6 +9,8 @@ pub struct CommandId(pub u32); impl CommandId { pub const FIRMWARE_LOAD: Self = Self(0x46574C44); // "FWLD" pub const GET_IDEV_CSR: Self = Self(0x49444556); // "IDEV" + pub const GET_IDEV_CERT: Self = Self(0x49444543); // IDEC + pub const GET_IDEV_INFO: Self = Self(0x49444549); // IDEI pub const GET_LDEV_CERT: Self = Self(0x4C444556); // "LDEV" pub const ECDSA384_VERIFY: Self = Self(0x53494756); // "SIGV" pub const STASH_MEASUREMENT: Self = Self(0x4D454153); // "MEAS" @@ -50,7 +52,9 @@ impl From for u32 { #[allow(clippy::large_enum_variant)] pub enum MailboxResp { Header(MailboxRespHeader), + GetIdevCert(GetIdevCertResp), GetIdevCsr(GetIdevCsrResp), + GetIdevInfo(GetIdevInfoResp), GetLdevCert(GetLdevCertResp), StashMeasurement(StashMeasurementResp), InvokeDpeCommand(InvokeDpeResp), @@ -63,7 +67,9 @@ impl MailboxResp { pub fn as_bytes(&self) -> &[u8] { match self { MailboxResp::Header(resp) => resp.as_bytes(), + MailboxResp::GetIdevCert(resp) => resp.as_bytes(), MailboxResp::GetIdevCsr(resp) => resp.as_bytes(), + MailboxResp::GetIdevInfo(resp) => resp.as_bytes(), MailboxResp::GetLdevCert(resp) => resp.as_bytes(), MailboxResp::StashMeasurement(resp) => resp.as_bytes(), MailboxResp::InvokeDpeCommand(resp) => resp.as_bytes_partial(), @@ -76,7 +82,9 @@ impl MailboxResp { pub fn as_bytes_mut(&mut self) -> &mut [u8] { match self { MailboxResp::Header(resp) => resp.as_bytes_mut(), + MailboxResp::GetIdevCert(resp) => resp.as_bytes_mut(), MailboxResp::GetIdevCsr(resp) => resp.as_bytes_mut(), + MailboxResp::GetIdevInfo(resp) => resp.as_bytes_mut(), MailboxResp::GetLdevCert(resp) => resp.as_bytes_mut(), MailboxResp::StashMeasurement(resp) => resp.as_bytes_mut(), MailboxResp::InvokeDpeCommand(resp) => resp.as_bytes_partial_mut(), @@ -152,6 +160,41 @@ impl GetIdevCsrResp { pub const DATA_MAX_SIZE: usize = 1024; } +// GET_IDEV_CERT +#[repr(C)] +#[derive(Debug, AsBytes, FromBytes, PartialEq, Eq)] +pub struct GetIdevCertReq { + pub hdr: MailboxReqHeader, + pub tbs_size: u32, + pub signature_r: [u8; 48], + pub signature_s: [u8; 48], + pub tbs: [u8; GetIdevCertReq::DATA_MAX_SIZE], // variable length +} +impl GetIdevCertReq { + pub const DATA_MAX_SIZE: usize = 916; // Req max size = Resp max size - MAX_ECDSA384_SIG_LEN +} + +#[repr(C)] +#[derive(Debug, AsBytes, FromBytes, PartialEq, Eq)] +pub struct GetIdevCertResp { + pub hdr: MailboxRespHeader, + pub cert_size: u32, + pub cert: [u8; GetIdevCertResp::DATA_MAX_SIZE], // variable length +} +impl GetIdevCertResp { + pub const DATA_MAX_SIZE: usize = 1024; +} + +// GET_IDEV_INFO +// No command-specific input args +#[repr(C)] +#[derive(Debug, AsBytes, FromBytes, PartialEq, Eq)] +pub struct GetIdevInfoResp { + pub hdr: MailboxRespHeader, + pub idev_pub_x: [u8; 48], + pub idev_pub_y: [u8; 48], +} + // GET_LDEV_CERT // No command-specific input args #[repr(C)] diff --git a/error/src/lib.rs b/error/src/lib.rs index ec3922edd1..7f4659b423 100644 --- a/error/src/lib.rs +++ b/error/src/lib.rs @@ -303,6 +303,7 @@ impl CaliptraError { pub const RUNTIME_DISABLE_ATTESTATION_FAILED: CaliptraError = CaliptraError::new_const(0x000E0011); pub const RUNTIME_HANDOFF_INVALID_PARM: CaliptraError = CaliptraError::new_const(0x000E0012); + pub const RUNTIME_GET_DEVID_CERT_FAILED: CaliptraError = CaliptraError::new_const(0x000E0013); /// FMC Errors pub const FMC_GLOBAL_NMI: CaliptraError = CaliptraError::new_const(0x000F0001); diff --git a/runtime/README.md b/runtime/README.md index b10b3d50f3..11da72051a 100644 --- a/runtime/README.md +++ b/runtime/README.md @@ -118,6 +118,31 @@ Table: `CALIPTRA_FW_LOAD` output arguments | chksum | u32 | Checksum over other output arguments, computed by Caliptra. Little endian. | fips_status | u32 | Indicates if the command is FIPS approved or an error +### GET\_IDEV\_CERT + +Exposes a command to reconstruct the IDEVID CERT + +Command Code: `0x4944_4543` ("IDEC") + +Table: `GET_IDEV_CERT` input arguments + +| **Name** | **Type** | **Description** +| -------- | -------- | --------------- +| chksum | u32 | Checksum over other input arguments, computed by the caller. Little endian. +| signature_r | u8[48] | R portion of signature of the cert +| signature_s | u8[48] | S portion of signature of the cert +| tbs_size | u32 | Size of the TBS +| tbs | u8[916] | TBS, with a maximum size of 916. Only bytes up to tbs_size are used. + +Table: `GET_IDEV_CERT` output arguments + +| **Name** | **Type** | **Description** +| -------- | -------- | --------------- +| chksum | u32 | Checksum over other output arguments, computed by Caliptra. Little endian. +| fips_status | u32 | Indicates if the command is FIPS approved or an error +| cert_size | u32 | Length in bytes of the cert field in use for the IDevId certificate +| cert | u8[1024] | DER-encoded IDevID CERT + ### GET\_IDEV\_CSR ROM exposes a command to get a self-signed IDEVID CSR. @@ -140,6 +165,27 @@ Table: `GET_IDEV_CSR` output arguments | data_size | u32 | Length in bytes of the valid data in the data field | data | u8[...] | DER-encoded IDevID CSR +### GET\_IDEV\_INFO + +Exposes a command to get a IDEVID public key. + +Command Code: `0x4944_4549` ("IDEI") + +Table: `GET_IDEV_INFO` input arguments + +| **Name** | **Type** | **Description** +| -------- | -------- | --------------- +| chksum | u32 | Checksum over other input arguments, computed by the caller. Little endian. + +Table: `GET_IDEV_INFO` output arguments + +| **Name** | **Type** | **Description** +| -------- | -------- | --------------- +| chksum | u32 | Checksum over other output arguments, computed by Caliptra. Little endian. +| fips_status | u32 | Indicates if the command is FIPS approved or an error +| idev_pub_x | u8[48] | X portion of ECDSA IDevId key +| idev_pub_y | u8[48] | Y portion of ECDSA IDevId key + ### GET\_LDEV\_CERT ROM exposes a command to get a self-signed LDevID Certificate signed by IDevID. diff --git a/runtime/src/info.rs b/runtime/src/info.rs index bb657a4d5f..bb6bbbdab0 100644 --- a/runtime/src/info.rs +++ b/runtime/src/info.rs @@ -1,8 +1,12 @@ // Licensed under the Apache-2.0 license use crate::{handoff::RtHandoff, Drivers}; -use caliptra_common::mailbox_api::{FwInfoResp, MailboxResp, MailboxRespHeader}; -use caliptra_drivers::CaliptraResult; +use caliptra_common::mailbox_api::{ + FwInfoResp, GetIdevCertReq, GetIdevCertResp, GetIdevInfoResp, MailboxResp, MailboxRespHeader, +}; +use caliptra_drivers::{CaliptraError, CaliptraResult}; +use caliptra_x509::{Ecdsa384CertBuilder, Ecdsa384Signature}; +use zerocopy::FromBytes; pub struct FwInfoCmd; impl FwInfoCmd { @@ -27,3 +31,57 @@ impl FwInfoCmd { })) } } + +pub struct IDevIdInfoCmd; +impl IDevIdInfoCmd { + pub(crate) fn execute(drivers: &Drivers) -> CaliptraResult { + let pdata = drivers.persistent_data.get(); + let pub_key = pdata.fht.idev_dice_pub_key; + + Ok(MailboxResp::GetIdevInfo(GetIdevInfoResp { + hdr: MailboxRespHeader::default(), + idev_pub_x: pub_key.x.into(), + idev_pub_y: pub_key.y.into(), + })) + } +} + +pub struct IDevIdCertCmd; +impl IDevIdCertCmd { + pub(crate) fn execute(cmd_args: &[u8]) -> CaliptraResult { + if let Some(cmd) = GetIdevCertReq::read_from(cmd_args) { + // Validate tbs + let Ok(in_len) = usize::try_from(cmd.tbs_size) else { + return Err(CaliptraError::RUNTIME_MAILBOX_INVALID_PARAMS); + }; + if in_len > cmd.tbs.len() { + return Err(CaliptraError::RUNTIME_MAILBOX_INVALID_PARAMS); + } + + let sig = Ecdsa384Signature { + r: cmd.signature_r, + s: cmd.signature_s, + }; + + let Some(builder) = Ecdsa384CertBuilder::new(&cmd.tbs[..in_len], &sig) else { + return Err(CaliptraError::RUNTIME_GET_DEVID_CERT_FAILED); + }; + + let mut cert = [0; GetIdevCertResp::DATA_MAX_SIZE]; + let Some(cert_size) = builder.build(&mut cert) else { + return Err(CaliptraError::RUNTIME_GET_DEVID_CERT_FAILED); + }; + let Ok(cert_size) = u32::try_from(cert_size) else { + return Err(CaliptraError::RUNTIME_GET_DEVID_CERT_FAILED); + }; + + Ok(MailboxResp::GetIdevCert(GetIdevCertResp { + hdr: MailboxRespHeader::default(), + cert_size, + cert, + })) + } else { + Err(CaliptraError::RUNTIME_INSUFFICIENT_MEMORY) + } + } +} diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 5af4661fb0..e885a42505 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -25,7 +25,7 @@ pub use disable::DisableAttestationCmd; use dpe_crypto::DpeCrypto; pub use dpe_platform::{DpePlatform, VENDOR_ID, VENDOR_SKU}; pub use fips::{FipsSelfTestCmd, FipsShutdownCmd, FipsVersionCmd}; -pub use info::FwInfoCmd; +pub use info::{FwInfoCmd, IDevIdCertCmd, IDevIdInfoCmd}; pub use invoke_dpe::InvokeDpeCmd; pub use stash_measurement::StashMeasurementCmd; pub use verify::EcdsaVerifyCmd; @@ -229,7 +229,9 @@ fn handle_command(drivers: &mut Drivers) -> CaliptraResult { // Handle the request and generate the response let mut resp = match CommandId::from(req_packet.cmd) { CommandId::FIRMWARE_LOAD => Err(CaliptraError::RUNTIME_UNIMPLEMENTED_COMMAND), + CommandId::GET_IDEV_CERT => IDevIdCertCmd::execute(cmd_bytes), CommandId::GET_IDEV_CSR => Err(CaliptraError::RUNTIME_UNIMPLEMENTED_COMMAND), + CommandId::GET_IDEV_INFO => IDevIdInfoCmd::execute(drivers), CommandId::GET_LDEV_CERT => Err(CaliptraError::RUNTIME_UNIMPLEMENTED_COMMAND), CommandId::INVOKE_DPE => InvokeDpeCmd::execute(drivers, cmd_bytes), CommandId::ECDSA384_VERIFY => EcdsaVerifyCmd::execute(drivers, cmd_bytes), diff --git a/runtime/tests/integration_tests.rs b/runtime/tests/integration_tests.rs index 1c146e9873..f6d878286f 100644 --- a/runtime/tests/integration_tests.rs +++ b/runtime/tests/integration_tests.rs @@ -4,8 +4,9 @@ pub mod common; use caliptra_builder::{ImageOptions, APP_WITH_UART, FMC_WITH_UART}; use caliptra_common::mailbox_api::{ - CommandId, EcdsaVerifyReq, FipsVersionResp, FwInfoResp, InvokeDpeReq, InvokeDpeResp, - MailboxReqHeader, MailboxRespHeader, StashMeasurementReq, StashMeasurementResp, + CommandId, EcdsaVerifyReq, FipsVersionResp, FwInfoResp, GetIdevCertReq, GetIdevCertResp, + GetIdevInfoResp, InvokeDpeReq, InvokeDpeResp, MailboxReqHeader, MailboxRespHeader, + StashMeasurementReq, StashMeasurementResp, }; use caliptra_drivers::Ecc384PubKey; use caliptra_hw_model::{DefaultHwModel, HwModel, ModelError, ShaAccMode}; @@ -703,3 +704,54 @@ fn test_unimplemented_cmds() { let resp = model.mailbox_execute(INVALID_CMD, payload.as_bytes()); assert_eq!(resp, expected_err); } + +#[test] +fn test_idev_id_info() { + let mut model = run_rt_test(None, None); + + let payload = MailboxReqHeader { + chksum: caliptra_common::checksum::calc_checksum(u32::from(CommandId::GET_IDEV_INFO), &[]), + }; + + let resp = model + .mailbox_execute(u32::from(CommandId::GET_IDEV_INFO), payload.as_bytes()) + .unwrap() + .unwrap(); + + GetIdevInfoResp::read_from(resp.as_slice()).unwrap(); +} + +#[test] +fn test_idev_id_cert() { + let mut model = run_rt_test(None, None); + + let fake_tbs = [0xef, 0xbe, 0xad, 0xde]; + + let mut tbs: [u8; GetIdevCertReq::DATA_MAX_SIZE] = [0; GetIdevCertReq::DATA_MAX_SIZE]; + tbs[..fake_tbs.len()].copy_from_slice(&fake_tbs); + let cmd = GetIdevCertReq { + hdr: MailboxReqHeader { chksum: 0 }, + tbs, + signature_r: [0; 48], + signature_s: [0; 48], + tbs_size: fake_tbs.len().try_into().unwrap(), + }; + + let checksum = caliptra_common::checksum::calc_checksum( + u32::from(CommandId::GET_IDEV_CERT), + &cmd.as_bytes()[4..], + ); + + let cmd = GetIdevCertReq { + hdr: MailboxReqHeader { chksum: checksum }, + ..cmd + }; + + let resp = model + .mailbox_execute(u32::from(CommandId::GET_IDEV_CERT), cmd.as_bytes()) + .unwrap() + .expect("We expected a response"); + + let cert = GetIdevCertResp::read_from(resp.as_slice()).unwrap(); + assert!(cmd.tbs_size < cert.cert_size); +} diff --git a/x509/src/cert_bldr.rs b/x509/src/cert_bldr.rs index 3c4ec03432..04150b8c08 100644 --- a/x509/src/cert_bldr.rs +++ b/x509/src/cert_bldr.rs @@ -49,7 +49,7 @@ impl Default for Ecdsa384Signature { impl Ecdsa384Signature { /// ECDSA Coordinate length - const ECDSA_COORD_LEN: usize = 48; + pub const ECDSA_COORD_LEN: usize = 48; /// Get the length of DER encoded unsigned integer #[inline(never)]