#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[repr(u8)]
pub enum TeePlatform {
Sgx = 0,
SevSnp = 1,
Tdx = 2,
ArmCca = 3,
SoftwareTee = 0xFE,
}
impl TryFrom<u8> for TeePlatform {
type Error = u8;
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
0 => Ok(Self::Sgx),
1 => Ok(Self::SevSnp),
2 => Ok(Self::Tdx),
3 => Ok(Self::ArmCca),
0xFE => Ok(Self::SoftwareTee),
other => Err(other),
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[repr(u8)]
pub enum AttestationWitnessType {
PlatformAttestation = 0x05,
KeyBinding = 0x06,
ComputationProof = 0x07,
DataProvenance = 0x08,
}
impl TryFrom<u8> for AttestationWitnessType {
type Error = u8;
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
0x05 => Ok(Self::PlatformAttestation),
0x06 => Ok(Self::KeyBinding),
0x07 => Ok(Self::ComputationProof),
0x08 => Ok(Self::DataProvenance),
other => Err(other),
}
}
}
pub const KEY_TYPE_TEE_BOUND: u8 = 4;
#[derive(Clone, Copy, Debug)]
#[repr(C)]
pub struct AttestationHeader {
pub platform: u8,
pub attestation_type: u8,
pub quote_length: u16,
pub reserved_0: u32,
pub measurement: [u8; 32],
pub signer_id: [u8; 32],
pub timestamp_ns: u64,
pub nonce: [u8; 16],
pub svn: u16,
pub sig_algo: u16,
pub flags: u8,
pub reserved_1: [u8; 3],
pub report_data_len: u64,
}
const _: () = assert!(core::mem::size_of::<AttestationHeader>() == 112);
impl AttestationHeader {
pub const FLAG_DEBUGGABLE: u8 = 0x01;
pub const FLAG_HAS_REPORT_DATA: u8 = 0x02;
pub const FLAG_MULTI_PLATFORM: u8 = 0x04;
pub const fn new(platform: u8, attestation_type: u8) -> Self {
Self {
platform,
attestation_type,
quote_length: 0,
reserved_0: 0,
measurement: [0u8; 32],
signer_id: [0u8; 32],
timestamp_ns: 0,
nonce: [0u8; 16],
svn: 0,
sig_algo: 0,
flags: 0,
reserved_1: [0u8; 3],
report_data_len: 0,
}
}
pub const fn is_debuggable(&self) -> bool {
self.flags & Self::FLAG_DEBUGGABLE != 0
}
pub const fn has_report_data(&self) -> bool {
self.flags & Self::FLAG_HAS_REPORT_DATA != 0
}
pub const fn total_record_length(&self) -> u64 {
112 + self.report_data_len + self.quote_length as u64
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn tee_platform_round_trip() {
let variants: &[(u8, TeePlatform)] = &[
(0, TeePlatform::Sgx),
(1, TeePlatform::SevSnp),
(2, TeePlatform::Tdx),
(3, TeePlatform::ArmCca),
(0xFE, TeePlatform::SoftwareTee),
];
for &(raw, expected) in variants {
let parsed = TeePlatform::try_from(raw).unwrap();
assert_eq!(parsed, expected);
assert_eq!(parsed as u8, raw);
}
}
#[test]
fn tee_platform_invalid() {
assert_eq!(TeePlatform::try_from(4), Err(4));
assert_eq!(TeePlatform::try_from(0xFF), Err(0xFF));
assert_eq!(TeePlatform::try_from(0x80), Err(0x80));
}
#[test]
fn attestation_witness_type_round_trip() {
let variants: &[(u8, AttestationWitnessType)] = &[
(0x05, AttestationWitnessType::PlatformAttestation),
(0x06, AttestationWitnessType::KeyBinding),
(0x07, AttestationWitnessType::ComputationProof),
(0x08, AttestationWitnessType::DataProvenance),
];
for &(raw, expected) in variants {
let parsed = AttestationWitnessType::try_from(raw).unwrap();
assert_eq!(parsed, expected);
assert_eq!(parsed as u8, raw);
}
}
#[test]
fn attestation_witness_type_invalid() {
assert_eq!(AttestationWitnessType::try_from(0x00), Err(0x00));
assert_eq!(AttestationWitnessType::try_from(0x04), Err(0x04));
assert_eq!(AttestationWitnessType::try_from(0x09), Err(0x09));
assert_eq!(AttestationWitnessType::try_from(0xFF), Err(0xFF));
}
#[test]
fn attestation_header_size() {
assert_eq!(core::mem::size_of::<AttestationHeader>(), 112);
}
#[test]
fn attestation_header_new() {
let hdr = AttestationHeader::new(
TeePlatform::SevSnp as u8,
AttestationWitnessType::PlatformAttestation as u8,
);
assert_eq!(hdr.platform, 1);
assert_eq!(hdr.attestation_type, 0x05);
assert_eq!(hdr.quote_length, 0);
assert_eq!(hdr.reserved_0, 0);
assert_eq!(hdr.measurement, [0u8; 32]);
assert_eq!(hdr.signer_id, [0u8; 32]);
assert_eq!(hdr.timestamp_ns, 0);
assert_eq!(hdr.nonce, [0u8; 16]);
assert_eq!(hdr.svn, 0);
assert_eq!(hdr.sig_algo, 0);
assert_eq!(hdr.flags, 0);
assert_eq!(hdr.reserved_1, [0u8; 3]);
assert_eq!(hdr.report_data_len, 0);
}
#[test]
fn flag_is_debuggable() {
let mut hdr = AttestationHeader::new(0, 0);
assert!(!hdr.is_debuggable());
hdr.flags = AttestationHeader::FLAG_DEBUGGABLE;
assert!(hdr.is_debuggable());
hdr.flags = AttestationHeader::FLAG_DEBUGGABLE | AttestationHeader::FLAG_HAS_REPORT_DATA;
assert!(hdr.is_debuggable());
}
#[test]
fn flag_has_report_data() {
let mut hdr = AttestationHeader::new(0, 0);
assert!(!hdr.has_report_data());
hdr.flags = AttestationHeader::FLAG_HAS_REPORT_DATA;
assert!(hdr.has_report_data());
}
#[test]
fn total_record_length() {
let mut hdr = AttestationHeader::new(0, 0);
assert_eq!(hdr.total_record_length(), 112);
hdr.quote_length = 256;
assert_eq!(hdr.total_record_length(), 112 + 256);
hdr.report_data_len = 64;
assert_eq!(hdr.total_record_length(), 112 + 64 + 256);
}
#[test]
fn key_type_tee_bound_value() {
assert_eq!(KEY_TYPE_TEE_BOUND, 4);
}
}