Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reconstruct IDevId cert at runtime #709

Merged
merged 3 commits into from
Sep 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions common/src/mailbox_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -50,7 +52,9 @@ impl From<CommandId> for u32 {
#[allow(clippy::large_enum_variant)]
pub enum MailboxResp {
Header(MailboxRespHeader),
GetIdevCert(GetIdevCertResp),
GetIdevCsr(GetIdevCsrResp),
GetIdevInfo(GetIdevInfoResp),
GetLdevCert(GetLdevCertResp),
StashMeasurement(StashMeasurementResp),
InvokeDpeCommand(InvokeDpeResp),
Expand All @@ -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(),
Expand All @@ -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(),
Expand Down Expand Up @@ -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 {
jhand2 marked this conversation as resolved.
Show resolved Hide resolved
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)]
Expand Down
1 change: 1 addition & 0 deletions error/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
46 changes: 46 additions & 0 deletions runtime/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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.
Expand Down
62 changes: 60 additions & 2 deletions runtime/src/info.rs
Original file line number Diff line number Diff line change
@@ -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 {
Expand All @@ -27,3 +31,57 @@ impl FwInfoCmd {
}))
}
}

pub struct IDevIdInfoCmd;
impl IDevIdInfoCmd {
pub(crate) fn execute(drivers: &Drivers) -> CaliptraResult<MailboxResp> {
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<MailboxResp> {
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)
}
}
}
4 changes: 3 additions & 1 deletion runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -229,7 +229,9 @@ fn handle_command(drivers: &mut Drivers) -> CaliptraResult<MboxStatusE> {
// 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),
Expand Down
56 changes: 54 additions & 2 deletions runtime/tests/integration_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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);
}
2 changes: 1 addition & 1 deletion x509/src/cert_bldr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down