use alloc::{
collections::BTreeMap,
string::{String, ToString},
vec::Vec,
};
use anyhow::{Context as _, Result};
use hex::{encode as hex_encode, FromHexError};
use serde::{Deserialize, Serialize};
use serde_json::{from_str, Value};
use sha2::Digest;
#[cfg(feature = "borsh_schema")]
use borsh::BorshSchema;
#[cfg(feature = "borsh")]
use borsh::{BorshDeserialize, BorshSerialize};
const INIT_MR: &str = "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000";
fn replay_rtmr(history: Vec<String>) -> Result<String, FromHexError> {
if history.is_empty() {
return Ok(INIT_MR.to_string());
}
let mut mr = hex::decode(INIT_MR)?;
for content in history {
let mut content_bytes = hex::decode(content)?;
if content_bytes.len() < 48 {
content_bytes.resize(48, 0);
}
mr.extend_from_slice(&content_bytes);
mr = sha2::Sha384::digest(&mr).to_vec();
}
Ok(hex_encode(mr))
}
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Serialize, Deserialize)]
#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))]
#[cfg_attr(feature = "borsh_schema", derive(BorshSchema))]
pub struct EventLog {
pub imr: u32,
pub event_type: u32,
pub digest: String,
pub event: String,
pub event_payload: String,
}
#[derive(Debug, bon::Builder, Serialize, Deserialize)]
#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))]
#[cfg_attr(feature = "borsh_schema", derive(BorshSchema))]
pub struct TlsKeyConfig {
#[builder(into, default = String::new())]
pub subject: String,
#[builder(default = Vec::new())]
pub alt_names: Vec<String>,
#[builder(default = false)]
pub usage_ra_tls: bool,
#[builder(default = true)]
pub usage_server_auth: bool,
#[builder(default = false)]
pub usage_client_auth: bool,
}
#[derive(Debug, Serialize, Deserialize)]
#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))]
#[cfg_attr(feature = "borsh_schema", derive(BorshSchema))]
pub struct GetKeyResponse {
pub key: String,
pub signature_chain: Vec<String>,
}
impl GetKeyResponse {
pub fn decode_key(&self) -> Result<Vec<u8>, FromHexError> {
hex::decode(&self.key)
}
pub fn decode_signature_chain(&self) -> Result<Vec<Vec<u8>>, FromHexError> {
self.signature_chain.iter().map(hex::decode).collect()
}
}
#[derive(Debug, Serialize, Deserialize)]
#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))]
#[cfg_attr(feature = "borsh_schema", derive(BorshSchema))]
pub struct GetQuoteResponse {
pub quote: String,
pub event_log: String,
#[serde(default)]
pub report_data: String,
#[serde(default)]
pub vm_config: String,
}
impl GetQuoteResponse {
pub fn decode_quote(&self) -> Result<Vec<u8>, FromHexError> {
hex::decode(&self.quote)
}
pub fn decode_event_log(&self) -> Result<Vec<EventLog>, serde_json::Error> {
serde_json::from_str(&self.event_log)
}
pub fn replay_rtmrs(&self) -> Result<BTreeMap<u8, String>> {
let parsed_event_log: Vec<EventLog> = self.decode_event_log()?;
let mut rtmrs = BTreeMap::new();
for idx in 0..4 {
let mut history = Vec::new();
for event in &parsed_event_log {
if event.imr == idx {
history.push(event.digest.clone());
}
}
rtmrs.insert(
idx as u8,
replay_rtmr(history)
.ok()
.context("Invalid digest in event log")?,
);
}
Ok(rtmrs)
}
}
#[derive(Debug, Serialize, Deserialize)]
#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))]
#[cfg_attr(feature = "borsh_schema", derive(BorshSchema))]
pub struct InfoResponse {
pub app_id: String,
pub instance_id: String,
pub app_cert: String,
pub tcb_info: TcbInfo,
pub app_name: String,
pub device_id: String,
#[serde(default)]
pub mr_aggregated: String,
#[serde(default)]
pub os_image_hash: String,
pub key_provider_info: String,
pub compose_hash: String,
#[serde(default)]
pub vm_config: String,
}
impl InfoResponse {
pub fn validated_from_value(mut obj: Value) -> Result<Self, serde_json::Error> {
if let Some(tcb_info_str) = obj.get("tcb_info").and_then(Value::as_str) {
let parsed_tcb_info: TcbInfo = from_str(tcb_info_str)?;
obj["tcb_info"] = serde_json::to_value(parsed_tcb_info)?;
}
serde_json::from_value(obj)
}
}
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Serialize, Deserialize)]
#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))]
#[cfg_attr(feature = "borsh_schema", derive(BorshSchema))]
pub struct TcbInfo {
pub mrtd: String,
pub rtmr0: String,
pub rtmr1: String,
pub rtmr2: String,
pub rtmr3: String,
#[serde(default)]
pub os_image_hash: String,
pub compose_hash: String,
pub device_id: String,
pub app_compose: String,
pub event_log: Vec<EventLog>,
}
#[derive(Debug, Serialize, Deserialize)]
#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))]
#[cfg_attr(feature = "borsh_schema", derive(BorshSchema))]
pub struct GetTlsKeyResponse {
pub key: String,
pub certificate_chain: Vec<String>,
}