use std::collections::BTreeMap;
use aws_nitro_enclaves_nsm_api::api::{AttestationDoc as NsmAttestationDoc, Digest as NsmDigest};
use coset::{CborSerializable, CoseSign1};
#[derive(Debug, thiserror::Error)]
pub enum NitroParseError {
#[error("COSE_Sign1 decode failed: {0}")]
CoseMalformed(String),
#[error("COSE_Sign1 payload missing")]
PayloadMissing,
#[error("attestation-document CBOR decode failed: {0}")]
CborMalformed(String),
}
#[derive(Debug, Clone)]
pub struct ParsedNitroQuote {
pub module_id: String,
pub timestamp_ms: u64,
pub digest: NsmDigest,
pub pcrs: BTreeMap<usize, Vec<u8>>,
pub certificate_der: Vec<u8>,
pub cabundle_der: Vec<Vec<u8>>,
pub public_key: Option<Vec<u8>>,
pub user_data: Option<Vec<u8>>,
pub nonce: Option<Vec<u8>>,
pub(crate) cose: CoseSign1,
}
pub fn parse_nitro_quote(bytes: &[u8]) -> Result<ParsedNitroQuote, NitroParseError> {
let cose = CoseSign1::from_slice(bytes)
.map_err(|e| NitroParseError::CoseMalformed(format!("{e:?}")))?;
let payload = cose
.payload
.as_deref()
.ok_or(NitroParseError::PayloadMissing)?;
let doc = NsmAttestationDoc::from_binary(payload)
.map_err(|e| NitroParseError::CborMalformed(format!("{e:?}")))?;
Ok(ParsedNitroQuote {
module_id: doc.module_id,
timestamp_ms: doc.timestamp,
digest: doc.digest,
pcrs: doc
.pcrs
.into_iter()
.map(|(i, v)| (i, v.into_vec()))
.collect(),
certificate_der: doc.certificate.into_vec(),
cabundle_der: doc.cabundle.into_iter().map(|c| c.into_vec()).collect(),
public_key: doc.public_key.map(|b| b.into_vec()),
user_data: doc.user_data.map(|b| b.into_vec()),
nonce: doc.nonce.map(|b| b.into_vec()),
cose,
})
}