Skip to content
Draft
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
1 change: 1 addition & 0 deletions api/tests/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ xshell.workspace = true
azihsm_api.workspace = true
azihsm_api_tests_macro.workspace = true
azihsm_crypto = { features = ["testvectors"], workspace = true }
azihsm_ddi_mbor_test_helpers.workspace = true
azihsm_ddi_tbor_types.workspace = true
azihsm_resiliency_test_helpers.workspace = true
hex.workspace = true
Expand Down
4 changes: 2 additions & 2 deletions api/tests/src/sd/create_backup_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ fn sd_create_remote_backup_roundtrip() {
let sata_key = CaKey::generate();
let (session, policy, pid_pub) = finalized_backing_session(&sata_key);

let (masked, report) = masked_key_and_report(&session);
let (masked, report) = masked_key_and_report(&session, &policy);
let evidence = build_receiver_evidence(&pid_pub, &sata_key, &report);
let result = evidence
.with_hsm_evidence(|receiver| session.sd_create_remote_backup(&masked, receiver, &policy))
Expand Down Expand Up @@ -72,7 +72,7 @@ fn sd_create_remote_backup_is_one_shot() {
let sata_key = CaKey::generate();
let (session, policy, pid_pub) = finalized_backing_session(&sata_key);

let (masked, report) = masked_key_and_report(&session);
let (masked, report) = masked_key_and_report(&session, &policy);
let evidence = build_receiver_evidence(&pid_pub, &sata_key, &report);

evidence
Expand Down
4 changes: 2 additions & 2 deletions api/tests/src/sd/create_peer_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ fn sd_create_peer_backup_roundtrip() {
let sata_key = CaKey::generate();
let (session, policy, pid_pub) = finalized_backing_session(&sata_key);

let (masked, report) = masked_key_and_report(&session);
let (masked, report) = masked_key_and_report(&session, &policy);
let evidence = build_receiver_evidence(&pid_pub, &sata_key, &report);

// Create the security domain first to obtain the device-local backup
Expand Down Expand Up @@ -60,7 +60,7 @@ fn sd_create_peer_backup_rerandomizes() {
let sata_key = CaKey::generate();
let (session, policy, pid_pub) = finalized_backing_session(&sata_key);

let (masked, report) = masked_key_and_report(&session);
let (masked, report) = masked_key_and_report(&session, &policy);
let evidence = build_receiver_evidence(&pid_pub, &sata_key, &report);

let created = evidence
Expand Down
12 changes: 6 additions & 6 deletions api/tests/src/sd/reseal_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,9 @@ fn sd_reseal_remote_backup_roundtrip() {

// Receiver (unseals the source), sender (sealed the source), and
// destination (the reseal target) SD sealing keys, each attested.
let (masked_rcvr, report_rcvr) = masked_key_and_report(&session);
let (masked_sndr, report_sndr) = masked_key_and_report(&session);
let (_masked_dst, report_dst) = masked_key_and_report(&session);
let (masked_rcvr, report_rcvr) = masked_key_and_report(&session, &policy);
let (masked_sndr, report_sndr) = masked_key_and_report(&session, &policy);
let (_masked_dst, report_dst) = masked_key_and_report(&session, &policy);

let src_backup = create_source_backup(
&session,
Expand Down Expand Up @@ -94,9 +94,9 @@ fn sd_reseal_remote_backup_rerandomizes() {
let sata_key = CaKey::generate();
let (session, policy, pid_pub) = finalized_backing_session(&sata_key);

let (masked_rcvr, report_rcvr) = masked_key_and_report(&session);
let (masked_sndr, report_sndr) = masked_key_and_report(&session);
let (_masked_dst, report_dst) = masked_key_and_report(&session);
let (masked_rcvr, report_rcvr) = masked_key_and_report(&session, &policy);
let (masked_sndr, report_sndr) = masked_key_and_report(&session, &policy);
let (_masked_dst, report_dst) = masked_key_and_report(&session, &policy);

let src_backup = create_source_backup(
&session,
Expand Down
4 changes: 2 additions & 2 deletions api/tests/src/sd/restore_local_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ fn sd_restore_local_backup_roundtrip() {
// Device 1: finalize + create the SD, capturing the device-local backups
// and the local_mk backup needed to restore PartLocalMK after reboot.
let (session1, policy, pid_pub, local_mk) = provision_backing(&sata, &pota, None, None);
let (masked, report) = masked_key_and_report(&session1);
let (masked, report) = masked_key_and_report(&session1, &policy);
let evidence = build_receiver_evidence(&pid_pub, &sata, &report);
let created = evidence
.with_hsm_evidence(|ev| session1.sd_create_remote_backup(&masked, ev, &policy))
Expand Down Expand Up @@ -75,7 +75,7 @@ fn sd_restore_local_backup_is_one_shot() {
let pota = CaKey::generate();

let (session, policy, pid_pub, _local_mk) = provision_backing(&sata, &pota, None, None);
let (masked, report) = masked_key_and_report(&session);
let (masked, report) = masked_key_and_report(&session, &policy);
let evidence = build_receiver_evidence(&pid_pub, &sata, &report);
let created = evidence
.with_hsm_evidence(|ev| session.sd_create_remote_backup(&masked, ev, &policy))
Expand Down
6 changes: 3 additions & 3 deletions api/tests/src/sd/restore_peer_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ fn sd_restore_peer_backup_roundtrip() {
// capturing the peer backup plus the sd_mk / local_mk backups device 2
// needs to restore.
let (session1, policy, pid_pub, local_mk) = provision_backing(&sata, &pota, None, None);
let (masked, report) = masked_key_and_report(&session1);
let (masked, report) = masked_key_and_report(&session1, &policy);
let evidence = build_receiver_evidence(&pid_pub, &sata, &report);
let created = evidence
.with_hsm_evidence(|ev| session1.sd_create_remote_backup(&masked, ev, &policy))
Expand Down Expand Up @@ -93,7 +93,7 @@ fn sd_restore_peer_backup_is_one_shot() {
let pota = CaKey::generate();

let (session, policy, pid_pub, _local_mk) = provision_backing(&sata, &pota, None, None);
let (masked, report) = masked_key_and_report(&session);
let (masked, report) = masked_key_and_report(&session, &policy);
let evidence = build_receiver_evidence(&pid_pub, &sata, &report);
let created = evidence
.with_hsm_evidence(|ev| session.sd_create_remote_backup(&masked, ev, &policy))
Expand Down Expand Up @@ -132,7 +132,7 @@ fn sd_restore_peer_backup_rejects_without_peer_cloning() {

let (session, policy, pid_pub, _local_mk) =
provision_backing_ex(&sata, &pota, None, None, false);
let (masked, report) = masked_key_and_report(&session);
let (masked, report) = masked_key_and_report(&session, &policy);
let evidence = build_receiver_evidence(&pid_pub, &sata, &report);

let pok_peer_backup = [0u8; POK_REMOTE_BACKUP_LEN];
Expand Down
4 changes: 2 additions & 2 deletions api/tests/src/sd/restore_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ fn sd_restore_remote_backup_roundtrip() {
// Device 1: finalize + create, capturing the remote backup and the
// local_mk backup needed to restore PartLocalMK after reboot.
let (session1, policy, pid_pub, local_mk) = provision_backing(&sata, &pota, None, None);
let (masked, report) = masked_key_and_report(&session1);
let (masked, report) = masked_key_and_report(&session1, &policy);
let evidence = build_receiver_evidence(&pid_pub, &sata, &report);
let created = evidence
.with_hsm_evidence(|ev| session1.sd_create_remote_backup(&masked, ev, &policy))
Expand Down Expand Up @@ -83,7 +83,7 @@ fn sd_restore_remote_backup_is_one_shot() {
let pota = CaKey::generate();

let (session, policy, pid_pub, _local_mk) = provision_backing(&sata, &pota, None, None);
let (masked, report) = masked_key_and_report(&session);
let (masked, report) = masked_key_and_report(&session, &policy);
let evidence = build_receiver_evidence(&pid_pub, &sata, &report);
let created = evidence
.with_hsm_evidence(|ev| session.sd_create_remote_backup(&masked, ev, &policy))
Expand Down
39 changes: 36 additions & 3 deletions api/tests/src/utils/sd_provision.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,13 @@ use azihsm_api::*;
use azihsm_crypto::EccCurve;
use azihsm_crypto::EccKeyOp;
use azihsm_crypto::EccPrivateKey;
#[cfg(not(feature = "emu"))]
use azihsm_crypto::EccPublicKey;
use azihsm_crypto::EcdsaAlgo;
use azihsm_crypto::HashAlgo;
use azihsm_crypto::HashOp;
#[cfg(not(feature = "emu"))]
use azihsm_crypto::ImportableKey;
use azihsm_crypto::SignOp;
use azihsm_crypto::x509_builder::cert_builder;
use azihsm_crypto::x509_builder::cert_builder::CN_LEN;
Expand All @@ -29,6 +33,9 @@ use azihsm_crypto::x509_builder::cert_builder::SN_LEN;
use azihsm_crypto::x509_builder::intermediate_cert;
use azihsm_crypto::x509_builder::leaf_cert;
use azihsm_crypto::x509_builder::root_cert;
#[cfg(not(feature = "emu"))]
use azihsm_ddi_mbor_test_helpers::fake_manticore_key_report_bytes;
#[cfg(feature = "emu")]
use azihsm_ddi_tbor_types::KEY_REPORT_DATA_LEN;
use azihsm_ddi_tbor_types::MACH_SEED_LEN;
use azihsm_ddi_tbor_types::PART_POLICY_LEN;
Expand Down Expand Up @@ -759,24 +766,50 @@ pub(crate) fn sealing_props() -> HsmKeyProps {
.expect("build sealing props")
}

/// Mint an SD sealing key on `session` and return its masked blob and a
/// COSE_Sign1 `KeyReport` attesting it (signed by the PID key).
pub(crate) fn masked_key_and_report(session: &HsmSession) -> (Vec<u8>, Vec<u8>) {
/// Mint an SD sealing key and return its masked blob and attestation report.
///
/// Emulator runs request a signed KeyReport. Hardware runs synthesize the
/// policy-bound v2 report until Manticore implements `TborKeyReport`.
pub(crate) fn masked_key_and_report(
session: &HsmSession,
policy: &[u8; PART_POLICY_LEN],
) -> (Vec<u8>, Vec<u8>) {
#[cfg(feature = "emu")]
let _ = policy;
let mut algo = HsmSealingKeyGenAlgo::default();
let key = HsmKeyManager::generate_key(session, &mut algo, sealing_props())
.expect("generate sealing key");

let masked = key.masked_key_vec().expect("masked key");

#[cfg(feature = "emu")]
let report_data = [0u8; KEY_REPORT_DATA_LEN];
#[cfg(feature = "emu")]
let report_len = key
.generate_key_report(&report_data, None)
.expect("key report size");
#[cfg(feature = "emu")]
let mut report = vec![0u8; report_len];
#[cfg(feature = "emu")]
let written = key
.generate_key_report(&report_data, Some(&mut report))
.expect("key report");
#[cfg(feature = "emu")]
report.truncate(written);

#[cfg(not(feature = "emu"))]
let report = {
let der = key.pub_key_der_vec().expect("sealing public key");
let public_key = EccPublicKey::from_bytes(&der).expect("P-384 public key DER");
let (x, y) = public_key.coord_vec().expect("P-384 coordinates");
assert_eq!(x.len(), 48, "P-384 X coordinate");
assert_eq!(y.len(), 48, "P-384 Y coordinate");
let mut sec1 = [0u8; SEC1_PUB_LEN];
sec1[0] = 0x04;
sec1[1..49].copy_from_slice(&x);
sec1[49..].copy_from_slice(&y);
fake_manticore_key_report_bytes(&sec1, policy)
};

(masked, report)
}
106 changes: 106 additions & 0 deletions ddi/mbor/test_helpers/src/report.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

//! Key Attestation Report Format in COSE_Sign1.

use azihsm_crypto::HashAlgo;
use azihsm_crypto::HashOp;
use bitfield_struct::bitfield;
use minicbor::CborLen;
use minicbor::Decode;
Expand Down Expand Up @@ -362,6 +364,110 @@ pub fn encode_ecc_public(
Ok(out_len - encoder.writer().len())
}

const MANTICORE_REPORT_VERSION_V2: u16 = 2;
const MANTICORE_PARTITION_ID_SIZE: usize = 16;
const MANTICORE_PID_PUB_KEY_SIZE: usize = 97;
const MANTICORE_POTA_PUB_KEY_SIZE: usize = 97;
const MANTICORE_POLICY_DIGEST_SIZE: usize = 48;
const P384_SEC1_PUB_LEN: usize = 97;
const P384_FE_LEN: usize = 48;
const COSE_ELLIPTIC_CURVES_P_384: i8 = 2;

#[derive(Encode, CborLen)]
#[cbor(map)]
struct ManticoreKeyAttestationReportPayloadV2 {
#[n(0)]
version: u16,
#[n(1)]
#[cbor(with = "minicbor::bytes")]
public_key: [u8; PUBLIC_KEY_MAX_SIZE],
#[n(2)]
public_key_size: u16,
#[n(3)]
flags: u32,
#[n(4)]
#[cbor(with = "minicbor::bytes")]
app_uuid: [u8; APP_UUID_SIZE],
#[n(5)]
#[cbor(with = "minicbor::bytes")]
report_data: [u8; REPORT_DATA_SIZE],
#[n(6)]
#[cbor(with = "minicbor::bytes")]
vm_launch_id: [u8; VM_LAUNCH_ID_SIZE],
#[n(7)]
key_scope: u8,
#[n(8)]
#[cbor(with = "minicbor::bytes")]
partition_id: [u8; MANTICORE_PARTITION_ID_SIZE],
#[n(9)]
#[cbor(with = "minicbor::bytes")]
pid_pub_key: [u8; MANTICORE_PID_PUB_KEY_SIZE],
#[n(10)]
#[cbor(with = "minicbor::bytes")]
pota_pub_key: [u8; MANTICORE_POTA_PUB_KEY_SIZE],
#[n(11)]
#[cbor(with = "minicbor::bytes")]
policy_digest: [u8; MANTICORE_POLICY_DIGEST_SIZE],
}

/// Build an unsigned Manticore v2 key report for hardware tests.
///
/// The report carries a real P-384 public key and the SHA-384 digest of
/// `policy`; all other payload fields and the ES384 signature are zeroed.
pub fn fake_manticore_key_report_bytes(
public_key_sec1: &[u8; P384_SEC1_PUB_LEN],
policy: &[u8],
) -> Vec<u8> {
assert_eq!(public_key_sec1[0], 0x04, "expected uncompressed P-384 key");
let mut public_key = [0u8; PUBLIC_KEY_MAX_SIZE];
let public_key_size = encode_ecc_public(
COSE_ELLIPTIC_CURVES_P_384,
&public_key_sec1[1..1 + P384_FE_LEN],
&public_key_sec1[1 + P384_FE_LEN..],
&mut public_key,
)
.expect("encode P-384 COSE key");
let mut policy_digest = [0u8; MANTICORE_POLICY_DIGEST_SIZE];
HashAlgo::sha384()
.hash(policy, Some(&mut policy_digest))
.expect("SHA-384 policy digest");
let payload = ManticoreKeyAttestationReportPayloadV2 {
version: MANTICORE_REPORT_VERSION_V2,
public_key,
public_key_size: u16::try_from(public_key_size).expect("COSE key length"),
flags: 0,
app_uuid: [0u8; APP_UUID_SIZE],
report_data: [0u8; REPORT_DATA_SIZE],
vm_launch_id: [0u8; VM_LAUNCH_ID_SIZE],
key_scope: 0,
partition_id: [0u8; MANTICORE_PARTITION_ID_SIZE],
pid_pub_key: [0u8; MANTICORE_PID_PUB_KEY_SIZE],
pota_pub_key: [0u8; MANTICORE_POTA_PUB_KEY_SIZE],
policy_digest,
};
let mut payload_bytes = vec![0u8; minicbor::len(&payload)];
minicbor::encode(&payload, payload_bytes.as_mut_slice()).expect("encode Manticore v2 report");
let cose = CoseSign1Object {
protected_header: PROTECTED_HEADER,
unprotected_header: UnprotectedHeader {},
payload: &payload_bytes,
signature: [0u8; SIGNATURE_SIZE],
};
let mut output = vec![
0u8;
COSE_SIGN1_TAG_SIZE
+ COSE_SIGN1_ENCODING_BYTES
+ PROTECTED_HEADER_SIZE
+ payload_bytes.len()
+ SIGNATURE_SIZE
];
let written = cose
.encode(&mut output)
.expect("encode Manticore COSE_Sign1");
output.truncate(written);
output
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
Loading