use std::{collections::BTreeMap, fmt};
use serde::{
de::{Deserialize, Visitor},
ser::{Serialize, SerializeMap},
};
use crate::{get_profile, Error, Extensions, KeyAttestation, RawValue, TrustTier, TrustVector};
#[derive(Debug, PartialEq)]
pub struct Appraisal {
pub status: TrustTier,
pub trust_vector: TrustVector,
pub policy_id: Option<String>,
pub annotated_evidence: BTreeMap<String, RawValue>,
pub policy_claims: BTreeMap<String, RawValue>,
pub key_attestation: Option<KeyAttestation>,
pub extensions: Extensions,
}
impl Appraisal {
pub fn new() -> Appraisal {
Appraisal {
status: TrustTier::None,
trust_vector: TrustVector::new(),
policy_id: None,
annotated_evidence: BTreeMap::new(),
policy_claims: BTreeMap::new(),
key_attestation: None,
extensions: Extensions::new(),
}
}
pub fn new_with_profile(profile: &str) -> Result<Appraisal, Error> {
let mut appraisal = Appraisal {
status: TrustTier::None,
trust_vector: TrustVector::new(),
policy_id: None,
annotated_evidence: BTreeMap::new(),
policy_claims: BTreeMap::new(),
key_attestation: None,
extensions: Extensions::new(),
};
match get_profile(profile) {
Some(profile) => {
profile.populate_appraisal_extensions(&mut appraisal)?;
Ok(appraisal)
}
None => Err(Error::ProfileError(format!("{profile} is not registered"))),
}
}
pub fn update_status_from_trust_vector(&mut self) {
for claim in self.trust_vector {
let claim_tier = claim.tier();
if self.status < claim_tier {
self.status = claim_tier
}
}
}
}
impl Default for Appraisal {
fn default() -> Self {
Self::new()
}
}
impl Serialize for Appraisal {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let is_human_readable = serializer.is_human_readable();
let mut map = serializer.serialize_map(None)?;
if is_human_readable {
map.serialize_entry("ear.status", &self.status)?;
if self.trust_vector.any_set() {
map.serialize_entry("ear.trustworthiness-vector", &self.trust_vector)?;
}
if let Some(pid) = &self.policy_id {
map.serialize_entry("ear.appraisal-policy-id", pid.as_str())?
}
if !self.annotated_evidence.is_empty() {
map.serialize_entry("ear.veraison.annotated-evidence", &self.annotated_evidence)?;
}
if !self.policy_claims.is_empty() {
map.serialize_entry("ear.veraison.policy-claims", &self.policy_claims)?;
}
self.extensions.serialize_to_map_by_name(&mut map)?;
} else {
map.serialize_entry(&1000, &self.status)?;
if self.trust_vector.any_set() {
map.serialize_entry(&1001, &self.trust_vector)?;
}
if let Some(pid) = &self.policy_id {
map.serialize_entry(&1003, pid.as_str())?
}
if !self.annotated_evidence.is_empty() {
map.serialize_entry(&-70000, &self.annotated_evidence)?;
}
if !self.policy_claims.is_empty() {
map.serialize_entry(&-70001, &self.policy_claims)?;
}
self.extensions.serialize_to_map_by_key(&mut map)?;
}
map.end()
}
}
impl<'de> Deserialize<'de> for Appraisal {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let is_hr = deserializer.is_human_readable();
deserializer.deserialize_map(AppraisalVisitor {
is_human_readable: is_hr,
})
}
}
struct AppraisalVisitor {
pub is_human_readable: bool,
}
impl<'de> Visitor<'de> for AppraisalVisitor {
type Value = Appraisal;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a CBOR map or JSON object")
}
fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
where
A: serde::de::MapAccess<'de>,
{
let mut appraisal = Appraisal::new();
loop {
if self.is_human_readable {
match map.next_key::<&str>()? {
Some("ear.status") => appraisal.status = map.next_value::<TrustTier>()?,
Some("ear.trustworthiness-vector") => {
appraisal.trust_vector = map.next_value::<TrustVector>()?
}
Some("ear.appraisal-policy-id") => {
appraisal.policy_id = Some(map.next_value::<String>()?)
}
Some("ear.veraison.annotated-evidence") => {
appraisal.annotated_evidence =
map.next_value::<BTreeMap<String, RawValue>>()?
}
Some("ear.veraison.policy-claims") => {
appraisal.policy_claims = map.next_value::<BTreeMap<String, RawValue>>()?
}
Some("ear.veraison.key-attestation") => {
appraisal.key_attestation = Some(map.next_value::<KeyAttestation>()?)
}
Some(name) => appraisal
.extensions
.visit_map_entry_by_name(name, &mut map)?,
None => break,
}
} else {
match map.next_key::<i32>()? {
Some(1000) => appraisal.status = map.next_value::<TrustTier>()?,
Some(1001) => appraisal.trust_vector = map.next_value::<TrustVector>()?,
Some(1003) => appraisal.policy_id = Some(map.next_value::<String>()?),
Some(-70000) => {
appraisal.annotated_evidence =
map.next_value::<BTreeMap<String, RawValue>>()?
}
Some(-70001) => {
appraisal.policy_claims = map.next_value::<BTreeMap<String, RawValue>>()?
}
Some(-70002) => {
appraisal.key_attestation = Some(map.next_value::<KeyAttestation>()?)
}
Some(key) => appraisal.extensions.visit_map_entry_by_key(key, &mut map)?,
None => break,
}
}
}
Ok(appraisal)
}
}
#[cfg(test)]
mod test {
use crate::{claim, Appraisal};
#[test]
fn serde() {
let mut appraisal = Appraisal::new();
let val = serde_json::to_string(&appraisal).unwrap();
assert_eq!(val, r#"{"ear.status":"none"}"#);
appraisal
.trust_vector
.configuration
.set(claim::APPROVED_CONFIG);
let val = serde_json::to_string(&appraisal).unwrap();
assert_eq!(
val,
r#"{"ear.status":"none","ear.trustworthiness-vector":{"configuration":2}}"#
);
let appraisal2: Appraisal = serde_json::from_str(val.as_str()).unwrap();
assert_eq!(appraisal, appraisal2);
}
}