1use alloc::{
6 collections::BTreeMap,
7 string::{String, ToString},
8 vec::Vec,
9};
10use anyhow::{Context as _, Result};
11use hex::{encode as hex_encode, FromHexError};
12use serde::{Deserialize, Serialize};
13use serde_json::{from_str, Value};
14use sha2::Digest;
15
16#[cfg(feature = "borsh_schema")]
17use borsh::BorshSchema;
18#[cfg(feature = "borsh")]
19use borsh::{BorshDeserialize, BorshSerialize};
20
21const INIT_MR: &str = "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000";
22
23fn replay_rtmr(history: Vec<String>) -> Result<String, FromHexError> {
24 if history.is_empty() {
25 return Ok(INIT_MR.to_string());
26 }
27 let mut mr = hex::decode(INIT_MR)?;
28 for content in history {
29 let mut content_bytes = hex::decode(content)?;
30 if content_bytes.len() < 48 {
31 content_bytes.resize(48, 0);
32 }
33 mr.extend_from_slice(&content_bytes);
34 mr = sha2::Sha384::digest(&mr).to_vec();
35 }
36 Ok(hex_encode(mr))
37}
38
39#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Serialize, Deserialize)]
41#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))]
42#[cfg_attr(feature = "borsh_schema", derive(BorshSchema))]
43pub struct EventLog {
44 pub imr: u32,
46 pub event_type: u32,
48 pub digest: String,
50 pub event: String,
52 pub event_payload: String,
54}
55
56#[derive(Debug, bon::Builder, Serialize, Deserialize)]
58#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))]
59#[cfg_attr(feature = "borsh_schema", derive(BorshSchema))]
60pub struct TlsKeyConfig {
61 #[builder(into, default = String::new())]
63 pub subject: String,
64 #[builder(default = Vec::new())]
66 pub alt_names: Vec<String>,
67 #[builder(default = false)]
69 pub usage_ra_tls: bool,
70 #[builder(default = true)]
72 pub usage_server_auth: bool,
73 #[builder(default = false)]
75 pub usage_client_auth: bool,
76}
77
78#[derive(Debug, Serialize, Deserialize)]
80#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))]
81#[cfg_attr(feature = "borsh_schema", derive(BorshSchema))]
82pub struct GetKeyResponse {
83 pub key: String,
85 pub signature_chain: Vec<String>,
87}
88
89impl GetKeyResponse {
90 pub fn decode_key(&self) -> Result<Vec<u8>, FromHexError> {
91 hex::decode(&self.key)
92 }
93
94 pub fn decode_signature_chain(&self) -> Result<Vec<Vec<u8>>, FromHexError> {
95 self.signature_chain.iter().map(hex::decode).collect()
96 }
97}
98
99#[derive(Debug, Serialize, Deserialize)]
101#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))]
102#[cfg_attr(feature = "borsh_schema", derive(BorshSchema))]
103pub struct GetQuoteResponse {
104 pub quote: String,
106 pub event_log: String,
108 #[serde(default)]
110 pub report_data: String,
111 #[serde(default)]
113 pub vm_config: String,
114}
115
116impl GetQuoteResponse {
117 pub fn decode_quote(&self) -> Result<Vec<u8>, FromHexError> {
118 hex::decode(&self.quote)
119 }
120
121 pub fn decode_event_log(&self) -> Result<Vec<EventLog>, serde_json::Error> {
122 serde_json::from_str(&self.event_log)
123 }
124
125 pub fn replay_rtmrs(&self) -> Result<BTreeMap<u8, String>> {
126 let parsed_event_log: Vec<EventLog> = self.decode_event_log()?;
127 let mut rtmrs = BTreeMap::new();
128 for idx in 0..4 {
129 let mut history = Vec::new();
130 for event in &parsed_event_log {
131 if event.imr == idx {
132 history.push(event.digest.clone());
133 }
134 }
135 rtmrs.insert(
136 idx as u8,
137 replay_rtmr(history)
138 .ok()
139 .context("Invalid digest in event log")?,
140 );
141 }
142 Ok(rtmrs)
143 }
144}
145
146#[derive(Debug, Serialize, Deserialize)]
148#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))]
149#[cfg_attr(feature = "borsh_schema", derive(BorshSchema))]
150pub struct InfoResponse {
151 pub app_id: String,
153 pub instance_id: String,
155 pub app_cert: String,
157 pub tcb_info: TcbInfo,
159 pub app_name: String,
161 pub device_id: String,
163 #[serde(default)]
165 pub mr_aggregated: String,
166 #[serde(default)]
169 pub os_image_hash: String,
170 pub key_provider_info: String,
172 pub compose_hash: String,
174 #[serde(default)]
176 pub vm_config: String,
177}
178
179impl InfoResponse {
180 pub fn validated_from_value(mut obj: Value) -> Result<Self, serde_json::Error> {
181 if let Some(tcb_info_str) = obj.get("tcb_info").and_then(Value::as_str) {
182 let parsed_tcb_info: TcbInfo = from_str(tcb_info_str)?;
183 obj["tcb_info"] = serde_json::to_value(parsed_tcb_info)?;
184 }
185 serde_json::from_value(obj)
186 }
187}
188
189#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Serialize, Deserialize)]
191#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))]
192#[cfg_attr(feature = "borsh_schema", derive(BorshSchema))]
193pub struct TcbInfo {
194 pub mrtd: String,
196 pub rtmr0: String,
198 pub rtmr1: String,
200 pub rtmr2: String,
202 pub rtmr3: String,
204 #[serde(default)]
206 pub os_image_hash: String,
207 pub compose_hash: String,
209 pub device_id: String,
211 pub app_compose: String,
213 pub event_log: Vec<EventLog>,
215}
216
217#[derive(Debug, Serialize, Deserialize)]
219#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))]
220#[cfg_attr(feature = "borsh_schema", derive(BorshSchema))]
221pub struct GetTlsKeyResponse {
222 pub key: String,
224 pub certificate_chain: Vec<String>,
226}