#[cfg(feature = "attestation")]
use ear::{Ear, TrustVector};
use serde::{Deserialize, Serialize};
use super::Backend;
#[derive(Debug, Clone, Serialize, Deserialize)]
#[non_exhaustive]
pub struct AttestationResult {
pub backend: Backend,
pub trust_tier: AttestationTrust,
pub measurement: String,
pub signer: Option<String>,
pub verified: bool,
pub summary: String,
#[cfg(feature = "attestation")]
pub ear_token: Option<String>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
#[non_exhaustive]
pub enum AttestationTrust {
Contraindicated,
Warning,
None,
Affirming,
}
impl std::fmt::Display for AttestationTrust {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::None => write!(f, "none"),
Self::Affirming => write!(f, "affirming"),
Self::Warning => write!(f, "warning"),
Self::Contraindicated => write!(f, "contraindicated"),
}
}
}
pub trait Attestable {
fn to_attestation_result(&self) -> AttestationResult;
}
#[cfg(feature = "sgx")]
impl Attestable for super::sgx::SgxAttestationReport {
fn to_attestation_result(&self) -> AttestationResult {
let verified = self.verify();
AttestationResult {
backend: Backend::Sgx,
trust_tier: if verified {
AttestationTrust::Affirming
} else {
AttestationTrust::Contraindicated
},
measurement: self.mrenclave.clone(),
signer: Some(self.mrsigner.clone()),
verified,
summary: format!("SGX enclave ISV_SVN={} verified={}", self.isv_svn, verified),
#[cfg(feature = "attestation")]
ear_token: None,
}
}
}
#[cfg(feature = "sev")]
impl Attestable for super::sev::SevAttestationReport {
fn to_attestation_result(&self) -> AttestationResult {
let verified = self.verify();
AttestationResult {
backend: Backend::Sev,
trust_tier: if verified {
if self.vmpl == 0 {
AttestationTrust::Affirming
} else {
AttestationTrust::Warning
}
} else {
AttestationTrust::Contraindicated
},
measurement: self.measurement.clone(),
signer: Some(self.id_key_digest.clone()),
verified,
summary: format!(
"SEV-SNP VMPL={} guest_svn={} verified={}",
self.vmpl, self.guest_svn, verified
),
#[cfg(feature = "attestation")]
ear_token: None,
}
}
}
#[cfg(feature = "sy-agnos")]
impl Attestable for super::sy_agnos::AttestationReport {
fn to_attestation_result(&self) -> AttestationResult {
let verified = self.verify();
let pcr8 = self
.pcr_values
.get(&8)
.cloned()
.unwrap_or_else(|| "missing".into());
AttestationResult {
backend: Backend::SyAgnos,
trust_tier: if verified {
AttestationTrust::Affirming
} else {
AttestationTrust::Contraindicated
},
measurement: pcr8,
signer: None,
verified,
summary: format!(
"TPM measured boot PCRs={} verified={}",
self.pcr_values.len(),
verified
),
#[cfg(feature = "attestation")]
ear_token: None,
}
}
}
#[cfg(feature = "attestation")]
impl AttestationResult {
#[must_use]
pub fn to_ear(&self) -> Ear {
let mut ear_obj = Ear::default();
let tier_val: i8 = match self.trust_tier {
AttestationTrust::Affirming => 2, AttestationTrust::Warning => 32, AttestationTrust::Contraindicated => 96, AttestationTrust::None => 0, };
let mut tv = TrustVector::default();
tv.instance_identity.set(tier_val);
tv.executables.set(tier_val);
ear_obj.submods.insert(
self.backend.to_string(),
ear::Appraisal {
trust_vector: tv,
..Default::default()
},
);
ear_obj
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn attestation_trust_display() {
assert_eq!(AttestationTrust::None.to_string(), "none");
assert_eq!(AttestationTrust::Affirming.to_string(), "affirming");
assert_eq!(AttestationTrust::Warning.to_string(), "warning");
assert_eq!(
AttestationTrust::Contraindicated.to_string(),
"contraindicated"
);
}
#[test]
fn trust_ordering() {
assert!(AttestationTrust::Contraindicated < AttestationTrust::Warning);
assert!(AttestationTrust::Warning < AttestationTrust::None);
assert!(AttestationTrust::None < AttestationTrust::Affirming);
}
#[test]
fn attestation_result_serde() {
let result = AttestationResult {
backend: Backend::Sgx,
trust_tier: AttestationTrust::Affirming,
measurement: "a".repeat(64),
signer: Some("b".repeat(64)),
verified: true,
summary: "test".into(),
#[cfg(feature = "attestation")]
ear_token: None,
};
let json = serde_json::to_string(&result).unwrap();
let back: AttestationResult = serde_json::from_str(&json).unwrap();
assert_eq!(back.trust_tier, AttestationTrust::Affirming);
assert!(back.verified);
}
#[cfg(feature = "sgx")]
#[test]
fn sgx_attestable() {
let report = super::super::sgx::SgxAttestationReport {
mrenclave: "a".repeat(64),
mrsigner: "b".repeat(64),
isv_prod_id: 1,
isv_svn: 2,
report_data: vec![0; 64],
ias_signature: Some("c".repeat(64)),
timestamp: None,
};
let result = report.to_attestation_result();
assert_eq!(result.backend, Backend::Sgx);
assert_eq!(result.trust_tier, AttestationTrust::Affirming);
assert!(result.verified);
}
#[cfg(feature = "sev")]
#[test]
fn sev_attestable() {
let report = super::super::sev::SevAttestationReport {
report_version: 2,
guest_svn: 1,
policy: 0x30000,
measurement: "a".repeat(96),
host_data: "b".repeat(64),
id_key_digest: "c".repeat(64),
report_id: "d".repeat(64),
vmpl: 0,
signature: vec![0xAB; 96],
};
let result = report.to_attestation_result();
assert_eq!(result.backend, Backend::Sev);
assert_eq!(result.trust_tier, AttestationTrust::Affirming);
}
#[cfg(feature = "attestation")]
#[test]
fn to_ear_conversion() {
let result = AttestationResult {
backend: Backend::Sgx,
trust_tier: AttestationTrust::Affirming,
measurement: "test".into(),
signer: None,
verified: true,
summary: "test".into(),
ear_token: None,
};
let ear = result.to_ear();
assert!(ear.submods.contains_key("sgx"));
}
}