use std::{borrow::Cow, fmt};
use anyhow::{Context, anyhow, ensure};
use asn1_rs::FromDer;
use lexe_crypto::ed25519;
use lexe_enclave::enclave;
use lexe_hex::hex;
use x509_parser::certificate::X509Certificate;
use yasna::{ASN1Error, ASN1ErrorKind, models::ObjectIdentifier};
use crate::attest_client::quote::ReportData;
#[derive(Eq, PartialEq)]
pub struct SgxAttestationExtension<'quote> {
pub quote: Cow<'quote, [u8]>,
}
impl SgxAttestationExtension<'_> {
pub const OID: &'static [u64] = &[1, 2, 840, 113741, 1337, 7];
pub const OID_DER: &'static [u8] = &[
0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF8, 0x4D, 0x8A, 0x39, 0x07,
];
pub fn oid_yasna() -> ObjectIdentifier {
ObjectIdentifier::from_slice(Self::OID)
}
#[rustfmt::skip]
pub const fn oid_asn1_rs() -> asn1_rs::Oid<'static> {
asn1_rs::oid!(1.2.840.113741.1337.7)
}
pub const fn is_critical() -> bool {
false
}
pub fn to_der_bytes(&self) -> Vec<u8> {
yasna::construct_der(|writer| {
writer.write_sequence(|writer| {
writer.next().write_bytes(&self.quote);
})
})
}
pub fn to_cert_extension(&self) -> rcgen::CustomExtension {
let mut ext = rcgen::CustomExtension::from_oid_content(
Self::OID,
self.to_der_bytes(),
);
ext.set_criticality(Self::is_critical());
ext
}
}
impl SgxAttestationExtension<'static> {
pub fn dummy(cert_pk: &ed25519::PublicKey) -> Self {
let mut report = enclave::report().clone();
let report_data = ReportData::from_cert_pk(cert_pk);
report.reportdata = report_data.into_inner();
Self {
quote: Cow::Owned(AsRef::<[u8]>::as_ref(&report).to_owned()),
}
}
pub fn from_der_bytes(buf: &[u8]) -> yasna::ASN1Result<Self> {
yasna::parse_der(buf, |reader| {
reader.read_sequence(|reader| {
let quote = reader.next().read_bytes()?;
Ok(Self {
quote: Cow::Owned(quote),
})
})
})
}
}
impl fmt::Debug for SgxAttestationExtension<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("SgxAttestationExtension")
.field("quote", &hex::display(&self.quote))
.finish()
}
}
#[derive(Debug)]
#[cfg_attr(test, derive(Eq, PartialEq))]
pub struct SgxPckExtensions {
pub(crate) cpu_fmspc: CpuFmspc,
#[allow(dead_code)] pub(crate) pce_svn: u16,
#[allow(dead_code)] pub(crate) sgx_tcb_comp_svns: [u8; 16],
}
impl SgxPckExtensions {
#[rustfmt::skip]
const OID: asn1_rs::Oid<'static> = asn1_rs::oid!(1.2.840.113741.1.13.1);
const OID_TCB: &[u64] = &[1, 2, 840, 113741, 1, 13, 1, 2];
const OID_FMSPC: &[u64] = &[1, 2, 840, 113741, 1, 13, 1, 4];
pub(crate) const DUMMY: Self = Self {
cpu_fmspc: CpuFmspc(hex::decode_const(b"00606a000000")),
pce_svn: 123,
sgx_tcb_comp_svns: [42u8; 16],
};
pub(crate) fn from_cert_der(cert_der: &[u8]) -> anyhow::Result<Self> {
let (unparsed_data, cert) =
X509Certificate::from_der(cert_der).context("Invalid PCK cert")?;
ensure!(unparsed_data.is_empty(), "leftover unparsed PCK cert data");
let cert_ext = cert
.get_extension_unique(&Self::OID)
.context("Invalid SGX PCK cert extension")?
.context("Missing SGX PCK cert extension")?;
Self::from_der_bytes(cert_ext.value)
.map_err(|err| anyhow!("Invalid SGX PCK cert extension: {err:#}"))
}
fn from_der_bytes(buf: &[u8]) -> yasna::ASN1Result<Self> {
let mut fmspc = None;
let mut tcb = None;
yasna::parse_der(buf, |reader| {
reader.read_sequence_of(|reader| {
reader.read_sequence(|reader| {
let oid = reader.next().read_oid()?;
if oid.as_ref() == Self::OID_FMSPC {
let bytes = reader.next().read_bytes()?;
let bytes = <[u8; 6]>::try_from(bytes.as_slice())
.map_err(|_| Self::invalid_asn1())?;
if fmspc.replace(bytes).is_some() {
return Err(Self::invalid_asn1());
}
} else if oid.as_ref() == Self::OID_TCB {
let new_tcb = Self::read_tcb(reader)?;
if tcb.replace(new_tcb).is_some() {
return Err(Self::invalid_asn1());
}
} else {
let _ = reader.next().read_der()?;
}
Ok(())
})
})?;
let cpu_fmspc = CpuFmspc(fmspc.ok_or_else(Self::invalid_asn1)?);
let (sgx_tcb_comp_svns, pce_svn) =
tcb.ok_or_else(Self::invalid_asn1)?;
Ok(Self {
cpu_fmspc,
pce_svn,
sgx_tcb_comp_svns,
})
})
}
fn read_tcb(
reader: &mut yasna::BERReaderSeq<'_, '_>,
) -> yasna::ASN1Result<([u8; 16], u16)> {
let mut pce_svn = None;
let mut sgx_tcb_comp_svns = [None; 16];
reader.next().read_sequence_of(|reader| {
reader.read_sequence(|reader| {
let oid = reader.next().read_oid()?;
let (idx, prefix) =
oid.as_ref().split_last().ok_or_else(Self::invalid_asn1)?;
if prefix != Self::OID_TCB {
return Err(Self::invalid_asn1());
}
match *idx {
1..=16 => {
let idx = (idx - 1) as usize;
let svn = reader.next().read_u8()?;
if sgx_tcb_comp_svns[idx].replace(svn).is_some() {
return Err(Self::invalid_asn1());
}
}
17 => {
let svn = reader.next().read_u16()?;
if pce_svn.replace(svn).is_some() {
return Err(Self::invalid_asn1());
}
}
_ => {
let _ = reader.next().read_der()?;
}
}
Ok(())
})
})?;
let pce_svn = pce_svn.ok_or_else(Self::invalid_asn1)?;
let mut svns = [0; 16];
for (svn, parsed_svn) in svns.iter_mut().zip(sgx_tcb_comp_svns) {
*svn = parsed_svn.ok_or_else(Self::invalid_asn1)?;
}
Ok((svns, pce_svn))
}
fn invalid_asn1() -> ASN1Error {
ASN1Error::new(ASN1ErrorKind::Invalid)
}
}
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
pub(crate) struct CpuFmspc(pub [u8; 6]);
lexe_byte_array::impl_byte_array!(CpuFmspc, 6);
lexe_byte_array::impl_debug_display_as_hex!(CpuFmspc);
#[cfg(test)]
mod test {
use std::fs;
use lexe_common::ByteArray;
use rustls::pki_types::pem::PemObject;
use super::*;
#[test]
fn test_sgx_attestation_ext_oid_der_bytes() {
let oid = SgxAttestationExtension::oid_yasna();
let oid_der = yasna::encode_der(&oid);
assert_eq!(SgxAttestationExtension::OID_DER, &oid_der);
}
#[test]
fn test_sgx_attestation_ext_serde() {
let ext = SgxAttestationExtension {
quote: b"test".as_slice().into(),
};
let der_bytes = ext.to_der_bytes();
let ext2 = SgxAttestationExtension::from_der_bytes(&der_bytes).unwrap();
assert_eq!(ext, ext2);
let der_bytes2 = ext2.to_der_bytes();
assert_eq!(der_bytes, der_bytes2);
assert_eq!(b"test".as_slice(), ext2.quote.as_ref());
}
#[test]
fn test_sgx_pck_extensions_parse() {
let cert_pem = fs::read_to_string("test_data/pck_cert.pem").unwrap();
let cert_der = rustls::pki_types::CertificateDer::from_pem_slice(
cert_pem.as_bytes(),
)
.unwrap();
let actual = SgxPckExtensions::from_cert_der(&cert_der).unwrap();
let expected = SgxPckExtensions {
cpu_fmspc: CpuFmspc::from_hex("00606a000000").unwrap(),
pce_svn: 13,
sgx_tcb_comp_svns: [
14, 14, 3, 3, 255, 255, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
],
};
assert_eq!(actual, expected)
}
}