Skip to content

Commit

Permalink
hw-model: High-level API for making mailbox requests.
Browse files Browse the repository at this point in the history
  • Loading branch information
korran committed Nov 14, 2023
1 parent ec9c7f9 commit 6a9bb90
Show file tree
Hide file tree
Showing 3 changed files with 137 additions and 56 deletions.
51 changes: 51 additions & 0 deletions api/src/mailbox.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,13 @@ impl From<CommandId> for u32 {
}
}

/// A trait implemented by request types. Describes the associated command ID
/// and response type.
pub trait Request: AsBytes + FromBytes {
const ID: CommandId;
type Resp: FromBytes;
}

// Contains all the possible mailbox response structs
#[cfg_attr(test, derive(PartialEq, Debug, Eq))]
#[allow(clippy::large_enum_variant)]
Expand Down Expand Up @@ -292,6 +299,16 @@ pub struct GetIdevInfoResp {
pub idev_pub_y: [u8; 48],
}

#[repr(C)]
#[derive(Default, Debug, AsBytes, FromBytes, PartialEq, Eq)]
pub struct TestOnlyGetLdevCertReq {
header: MailboxReqHeader,
}
impl Request for TestOnlyGetLdevCertReq {
const ID: CommandId = CommandId::TEST_ONLY_GET_LDEV_CERT;
type Resp = GetLdevCertResp;
}

// GET_LDEV_CERT
// No command-specific input args
#[repr(C)]
Expand All @@ -303,6 +320,10 @@ pub struct GetLdevCertResp {
}
impl GetLdevCertResp {
pub const DATA_MAX_SIZE: usize = 1024;

pub fn data(&self) -> Option<&[u8]> {
self.data.get(..self.data_size as usize)
}
}

// ECDSA384_SIGNATURE_VERIFY
Expand All @@ -315,6 +336,10 @@ pub struct EcdsaVerifyReq {
pub signature_r: [u8; 48],
pub signature_s: [u8; 48],
}
impl Request for EcdsaVerifyReq {
const ID: CommandId = CommandId::ECDSA384_VERIFY;
type Resp = MailboxRespHeader;
}
// No command-specific output args

// TEST_ONLY_HMAC384_SIGNATURE_VERIFY
Expand All @@ -327,6 +352,10 @@ pub struct HmacVerifyReq {
pub len: u32,
pub msg: [u8; 256],
}
impl Request for HmacVerifyReq {
const ID: CommandId = CommandId::TEST_ONLY_HMAC384_VERIFY;
type Resp = MailboxRespHeader;
}
// No command-specific output args

// STASH_MEASUREMENT
Expand All @@ -350,6 +379,10 @@ impl Default for StashMeasurementReq {
}
}
}
impl Request for StashMeasurementReq {
const ID: CommandId = CommandId::STASH_MEASUREMENT;
type Resp = StashMeasurementResp;
}

#[repr(C)]
#[derive(Debug, AsBytes, FromBytes, PartialEq, Eq)]
Expand Down Expand Up @@ -384,6 +417,10 @@ impl Default for InvokeDpeReq {
}
}
}
impl Request for InvokeDpeReq {
const ID: CommandId = CommandId::INVOKE_DPE;
type Resp = InvokeDpeResp;
}

#[repr(C)]
#[derive(Debug, AsBytes, FromBytes, PartialEq, Eq)]
Expand Down Expand Up @@ -417,6 +454,16 @@ impl Default for InvokeDpeResp {
}
}

#[repr(C)]
#[derive(Debug, Default, AsBytes, FromBytes, PartialEq, Eq)]
pub struct TestOnlyGetFmcAliasCertReq {
header: MailboxReqHeader,
}
impl Request for TestOnlyGetFmcAliasCertReq {
const ID: CommandId = CommandId::TEST_ONLY_GET_FMC_ALIAS_CERT;
type Resp = GetLdevCertResp;
}

// TEST_ONLY_GET_FMC_ALIAS_CERT
// No command-specific input args
#[repr(C)]
Expand All @@ -428,6 +475,10 @@ pub struct TestGetFmcAliasCertResp {
}
impl TestGetFmcAliasCertResp {
pub const DATA_MAX_SIZE: usize = 1024;

pub fn data(&self) -> Option<&[u8]> {
self.data.get(..self.data_size as usize)
}
}

// FIPS_SELF_TEST
Expand Down
80 changes: 80 additions & 0 deletions hw-model/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// Licensed under the Apache-2.0 license

use std::mem;
use std::path::PathBuf;
use std::str::FromStr;
use std::{
Expand All @@ -8,6 +9,7 @@ use std::{
io::{stdout, ErrorKind, Write},
};

use api::mailbox::{MailboxReqHeader, MailboxRespHeader};
use caliptra_api as api;
use caliptra_emu_bus::Bus;
use caliptra_hw_model_types::{
Expand Down Expand Up @@ -237,6 +239,12 @@ pub enum ModelError {
UnableToLockSha512Acc,
UploadMeasurementResponseError,
UnableToReadMailbox,
MailboxNoResponseData,
MailboxReqTypeTooSmall,
MailboxRespTypeTooSmall,
MailboxUnexpectedResponseLen { expected: u32, actual: u32 },
MailboxRespInvalidChecksum,
MailboxRespInvalidFipsStatus(u32),
}
impl Error for ModelError {}
impl Display for ModelError {
Expand Down Expand Up @@ -268,6 +276,30 @@ impl Display for ModelError {
write!(f, "Error in response after uploading measurement")
}
ModelError::UnableToReadMailbox => write!(f, "Unable to read mailbox regs"),
ModelError::MailboxNoResponseData => {
write!(f, "Expected response data but none was found")
}
ModelError::MailboxReqTypeTooSmall => {
write!(f, "Mailbox request type too small to contain header")
}
ModelError::MailboxRespTypeTooSmall => {
write!(f, "Mailbox response type too small to contain header")
}
ModelError::MailboxUnexpectedResponseLen { expected, actual } => {
write!(
f,
"Expected mailbox response lenth of {expected}, was {actual}"
)
}
ModelError::MailboxRespInvalidChecksum => {
write!(f, "Mailbox response had invalid checksum")
}
ModelError::MailboxRespInvalidFipsStatus(status) => {
write!(
f,
"Mailbox response had non-success FIPS status: 0x{status:x}"
)
}
}
}
}
Expand Down Expand Up @@ -732,6 +764,54 @@ pub trait HwModel {

fn set_apb_pauser(&mut self, pauser: u32);

/// Executes a typed request and (if success), returns the typed response.
/// The checksum field of the request is calculated, and the checksum of the
/// response is validated.
fn mailbox_execute_req<R: api::mailbox::Request>(
&mut self,
mut req: R,
) -> std::result::Result<R::Resp, ModelError> {
if mem::size_of::<R>() < mem::size_of::<MailboxReqHeader>() {
return Err(ModelError::MailboxReqTypeTooSmall);
}
if mem::size_of::<R::Resp>() < mem::size_of::<MailboxRespHeader>() {
return Err(ModelError::MailboxRespTypeTooSmall);
}
let (header_bytes, payload_bytes) = req
.as_bytes_mut()
.split_at_mut(mem::size_of::<MailboxReqHeader>());

let mut header = MailboxReqHeader::read_from(header_bytes as &[u8]).unwrap();
header.chksum = api::calc_checksum(R::ID.into(), payload_bytes);
header_bytes.copy_from_slice(header.as_bytes());

let Some(response_bytes) = self.mailbox_execute(R::ID.into(), req.as_bytes())? else {
return Err(ModelError::MailboxNoResponseData);
};
let response = match R::Resp::read_from(response_bytes.as_slice()) {
Some(response) => response,
None => {
return Err(ModelError::MailboxUnexpectedResponseLen {
expected: mem::size_of::<R::Resp>() as u32,
actual: response_bytes.len() as u32,
})
}
};
let (header_bytes, payload_bytes) =
response_bytes.split_at(mem::size_of::<MailboxRespHeader>());
let response_header = MailboxRespHeader::read_from(header_bytes).unwrap();

if !api::verify_checksum(response_header.chksum, 0, payload_bytes) {
return Err(ModelError::MailboxRespInvalidChecksum);
}
if response_header.fips_status != MailboxRespHeader::FIPS_STATUS_APPROVED {
return Err(ModelError::MailboxRespInvalidFipsStatus(
response_header.fips_status,
));
}
Ok(response)
}

/// Executes `cmd` with request data `buf`. Returns `Ok(Some(_))` if
/// the uC responded with data, `Ok(None)` if the uC indicated success
/// without data, Err(ModelError::MailboxCmdFailed) if the microcontroller
Expand Down
62 changes: 6 additions & 56 deletions test/tests/caliptra_integration_tests/smoke_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
use caliptra_builder::{firmware, ImageOptions};
use caliptra_common::fips::FipsVersionCmd;
use caliptra_common::mailbox_api::{
CommandId, FipsVersionResp, GetLdevCertResp, MailboxReqHeader, MailboxRespHeader,
TestGetFmcAliasCertResp,
CommandId, FipsVersionResp, MailboxReqHeader, MailboxRespHeader, TestOnlyGetFmcAliasCertReq,
TestOnlyGetLdevCertReq,
};
use caliptra_hw_model::{BootParams, HwModel, InitParams, ModelError, SecurityState};
use caliptra_hw_model_types::{DeviceLifecycle, Fuses};
Expand Down Expand Up @@ -191,37 +191,12 @@ fn smoke_test() {
\____\__,_|_|_| .__/ \__|_| \__,_| |_| \_\|_|"#,
);

let payload = MailboxReqHeader {
chksum: caliptra_common::checksum::calc_checksum(
u32::from(CommandId::TEST_ONLY_GET_LDEV_CERT),
&[],
),
};

// Execute the command
let ldev_cert_resp = hw
.mailbox_execute(
u32::from(CommandId::TEST_ONLY_GET_LDEV_CERT),
payload.as_bytes(),
)
.unwrap()
.mailbox_execute_req(TestOnlyGetLdevCertReq::default())
.unwrap();

let ldev_cert_resp = GetLdevCertResp::read_from(ldev_cert_resp.as_bytes()).unwrap();

// Verify checksum and FIPS approval
assert!(caliptra_common::checksum::verify_checksum(
ldev_cert_resp.hdr.chksum,
0x0,
&ldev_cert_resp.as_bytes()[core::mem::size_of_val(&ldev_cert_resp.hdr.chksum)..],
));
assert_eq!(
ldev_cert_resp.hdr.fips_status,
MailboxRespHeader::FIPS_STATUS_APPROVED
);

// Extract the certificate from the response
let ldev_cert_der = &ldev_cert_resp.data[..(ldev_cert_resp.data_size as usize)];
let ldev_cert_der = ldev_cert_resp.data().unwrap();
let ldev_cert = openssl::x509::X509::from_der(ldev_cert_der).unwrap();
let ldev_cert_txt = String::from_utf8(ldev_cert.to_text().unwrap()).unwrap();

Expand Down Expand Up @@ -258,38 +233,13 @@ fn smoke_test() {

println!("ldev-cert: {}", ldev_cert_txt);

let payload = MailboxReqHeader {
chksum: caliptra_common::checksum::calc_checksum(
u32::from(CommandId::TEST_ONLY_GET_FMC_ALIAS_CERT),
&[],
),
};

// Execute command
let fmc_alias_cert_resp = hw
.mailbox_execute(
u32::from(CommandId::TEST_ONLY_GET_FMC_ALIAS_CERT),
payload.as_bytes(),
)
.unwrap()
.mailbox_execute_req(TestOnlyGetFmcAliasCertReq::default())
.unwrap();

let fmc_alias_cert_resp =
TestGetFmcAliasCertResp::read_from(fmc_alias_cert_resp.as_bytes()).unwrap();

// Verify checksum and FIPS approval
assert!(caliptra_common::checksum::verify_checksum(
fmc_alias_cert_resp.hdr.chksum,
0x0,
&fmc_alias_cert_resp.as_bytes()[core::mem::size_of_val(&fmc_alias_cert_resp.hdr.chksum)..],
));
assert_eq!(
fmc_alias_cert_resp.hdr.fips_status,
MailboxRespHeader::FIPS_STATUS_APPROVED
);

// Extract the certificate from the response
let fmc_alias_cert_der = &fmc_alias_cert_resp.data[..(fmc_alias_cert_resp.data_size as usize)];
let fmc_alias_cert_der = fmc_alias_cert_resp.data().unwrap();
let fmc_alias_cert = openssl::x509::X509::from_der(fmc_alias_cert_der).unwrap();

println!(
Expand Down

0 comments on commit 6a9bb90

Please sign in to comment.