use alloc::vec::Vec;
use anyhow::{anyhow, bail, Context, Result};
use asn1_der::{
typed::{DerDecodable, Sequence},
DerObject,
};
use webpki::{
self,
types::{TrustAnchor, UnixTime},
BorrowedCertRevocationList,
};
use webpki::{types::CertificateDer, CertRevocationList};
use x509_cert::Certificate;
use crate::{constants::*, oids};
pub fn get_intel_extension(der_encoded: &[u8]) -> Result<Vec<u8>> {
let cert: Certificate =
der::Decode::from_der(der_encoded).context("Failed to decode certificate")?;
let mut extension_iter = cert
.tbs_certificate
.extensions
.as_deref()
.unwrap_or(&[])
.iter()
.filter(|e| e.extn_id == oids::SGX_EXTENSION)
.map(|e| e.extn_value.clone());
let extension = extension_iter.next().context("Intel extension not found")?;
if extension_iter.next().is_some() {
bail!("Intel extension ambiguity");
}
Ok(extension.into_bytes())
}
pub fn find_extension(path: &[&[u8]], raw: &[u8]) -> Result<Vec<u8>> {
let obj = DerObject::decode(raw).context("Failed to decode DER object")?;
let subobj = get_obj(path, obj).context("Failed to get subobject")?;
Ok(subobj.value().to_vec())
}
fn get_obj<'a>(path: &[&[u8]], mut obj: DerObject<'a>) -> Result<DerObject<'a>> {
for oid in path {
let seq = Sequence::load(obj).context("Failed to load sequence")?;
obj = sub_obj(oid, seq).context("Failed to get subobject")?;
}
Ok(obj)
}
fn sub_obj<'a>(oid: &[u8], seq: Sequence<'a>) -> Result<DerObject<'a>> {
for i in 0..seq.len() {
let entry = seq.get(i).context("Failed to get entry")?;
let entry = Sequence::load(entry).context("Failed to load sequence")?;
let name = entry.get(0).context("Failed to get name")?;
let value = entry.get(1).context("Failed to get value")?;
if name.value() == oid {
return Ok(value);
}
}
bail!("Oid is missing");
}
pub fn get_fmspc(extension_section: &[u8]) -> Result<Fmspc> {
let data = find_extension(&[oids::FMSPC.as_bytes()], extension_section)
.context("Failed to find Fmspc")?;
if data.len() != 6 {
bail!("Fmspc length mismatch");
}
data.try_into()
.map_err(|_| anyhow!("Failed to decode Fmspc"))
}
pub fn get_cpu_svn(extension_section: &[u8]) -> Result<CpuSvn> {
let data = find_extension(
&[oids::TCB.as_bytes(), oids::CPUSVN.as_bytes()],
extension_section,
)?;
if data.len() != 16 {
bail!("CpuSvn length mismatch");
}
data.try_into()
.map_err(|_| anyhow!("Failed to decode CpuSvn"))
}
pub fn get_pce_svn(extension_section: &[u8]) -> Result<Svn> {
let data = find_extension(
&[oids::TCB.as_bytes(), oids::PCESVN.as_bytes()],
extension_section,
)
.context("Failed to find PceSvn")?;
match data[..] {
[byte0] => Ok(u16::from(byte0)),
[byte0, byte1] => Ok(u16::from_be_bytes([byte0, byte1])),
_ => bail!("PceSvn length mismatch"),
}
}
pub fn extract_raw_certs(cert_chain: &[u8]) -> Result<Vec<Vec<u8>>> {
Ok(pem::parse_many(cert_chain)
.context("Failed to parse certs")?
.iter()
.map(|i| i.contents().to_vec())
.collect())
}
pub fn extract_certs<'a>(cert_chain: &'a [u8]) -> Result<Vec<CertificateDer<'a>>> {
let mut certs = Vec::<CertificateDer<'a>>::new();
let raw_certs = extract_raw_certs(cert_chain)?;
for raw_cert in raw_certs.iter() {
let cert = webpki::types::CertificateDer::<'a>::from(raw_cert.to_vec());
certs.push(cert);
}
Ok(certs)
}
pub fn encode_as_der(data: &[u8]) -> Result<Vec<u8>> {
let (first, second) = data.split_at_checked(32).context("Invalid key length")?;
let mut sequence = der::asn1::SequenceOf::<der::asn1::UintRef, 2>::new();
let element0 = der::asn1::UintRef::new(first).context("Failed to add first element")?;
sequence
.add(element0)
.context("Failed to add second element")?;
let element1 = der::asn1::UintRef::new(second).context("Failed to add second element")?;
sequence
.add(element1)
.context("Failed to add third element")?;
let mut asn1 = alloc::vec![0u8; 72];
let mut writer = der::SliceWriter::new(&mut asn1);
writer
.encode(&sequence)
.context("Failed to encode sequence")?;
Ok(writer.finish().context("Failed to finish writer")?.to_vec())
}
pub fn verify_certificate_chain(
leaf_cert: &webpki::EndEntityCert,
intermediate_certs: &[CertificateDer],
time: UnixTime,
crl_der: &[&[u8]],
trust_anchor: TrustAnchor<'_>,
) -> Result<()> {
let sig_algs = webpki::ALL_VERIFICATION_ALGS;
let crls: Vec<CertRevocationList> = crl_der
.iter()
.map(|der| BorrowedCertRevocationList::from_der(der).map(|crl| crl.into()))
.collect::<Result<Vec<_>, _>>()
.context("Failed to parse CRL")?;
let crl_slice = crls.iter().collect::<Vec<_>>();
let builder = match webpki::RevocationOptionsBuilder::new(&crl_slice) {
Ok(builder) => builder,
Err(_) => bail!("Failed to create RevocationOptionsBuilder - CRLs required"),
};
let revocation = builder
.with_depth(webpki::RevocationCheckDepth::Chain)
.with_status_policy(webpki::UnknownStatusPolicy::Deny)
.with_expiration_policy(webpki::ExpirationPolicy::Enforce)
.build();
leaf_cert
.verify_for_usage(
sig_algs,
&[trust_anchor],
intermediate_certs,
time,
webpki::KeyUsage::server_auth(),
Some(revocation),
None,
)
.context("Failed to verify certificate chain")?;
Ok(())
}