1use alloc::{
6 collections::BTreeMap,
7 string::{String, ToString},
8 vec::Vec,
9};
10use anyhow::{bail, Context as _, Result};
11use hex::{encode as hex_encode, FromHexError};
12use serde::{Deserialize, Serialize};
13use sha2::Digest;
14
15#[cfg(feature = "borsh_schema")]
16use borsh::BorshSchema;
17#[cfg(feature = "borsh")]
18use borsh::{BorshDeserialize, BorshSerialize};
19
20use crate::dstack::EventLog;
21
22const INIT_MR: &str = "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000";
23
24#[derive(Debug, Clone, Serialize, Deserialize)]
26#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))]
27#[cfg_attr(feature = "borsh_schema", derive(BorshSchema))]
28pub enum QuoteHashAlgorithm {
29 Sha256,
30 Sha384,
31 Sha512,
32 Sha3_256,
33 Sha3_384,
34 Sha3_512,
35 Keccak256,
36 Keccak384,
37 Keccak512,
38 Raw,
39}
40
41impl QuoteHashAlgorithm {
42 pub fn as_str(&self) -> &'static str {
43 match self {
44 Self::Sha256 => "sha256",
45 Self::Sha384 => "sha384",
46 Self::Sha512 => "sha512",
47 Self::Sha3_256 => "sha3-256",
48 Self::Sha3_384 => "sha3-384",
49 Self::Sha3_512 => "sha3-512",
50 Self::Keccak256 => "keccak256",
51 Self::Keccak384 => "keccak384",
52 Self::Keccak512 => "keccak512",
53 Self::Raw => "raw",
54 }
55 }
56}
57
58fn replay_rtmr(history: Vec<String>) -> Result<String, FromHexError> {
59 if history.is_empty() {
60 return Ok(INIT_MR.to_string());
61 }
62 let mut mr = hex::decode(INIT_MR)?;
63 for content in history {
64 let mut content_bytes = hex::decode(content)?;
65 if content_bytes.len() < 48 {
66 content_bytes.resize(48, 0);
67 }
68 mr.extend_from_slice(&content_bytes);
69 mr = sha2::Sha384::digest(&mr).to_vec();
70 }
71 Ok(hex_encode(mr))
72}
73
74#[derive(Debug, Serialize, Deserialize)]
76#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))]
77#[cfg_attr(feature = "borsh_schema", derive(BorshSchema))]
78pub struct DeriveKeyResponse {
79 pub key: String,
81 pub certificate_chain: Vec<String>,
83}
84
85impl DeriveKeyResponse {
86 pub fn decode_key(&self) -> Result<Vec<u8>, anyhow::Error> {
88 use pkcs8::der::asn1::{Int, OctetString};
89 use pkcs8::der::{Decode, Document, Reader, SliceReader};
90 use pkcs8::PrivateKeyInfo;
91
92 let key_content = self.key.trim();
93
94 let (label, doc) = Document::from_pem(key_content)
96 .map_err(|e| anyhow::anyhow!("Failed to parse PEM: {:?}", e))?;
97
98 if label != "PRIVATE KEY" {
100 bail!("Expected PRIVATE KEY PEM label, got: {}", label);
101 }
102
103 let private_key_info = PrivateKeyInfo::from_der(doc.as_bytes())
105 .map_err(|e| anyhow::anyhow!("Failed to parse PKCS#8 private key: {:?}", e))?;
106
107 let private_key_data = private_key_info.private_key;
110
111 let mut reader = SliceReader::new(private_key_data)
119 .map_err(|e| anyhow::anyhow!("Failed to create reader: {:?}", e))?;
120 let key_bytes = reader
121 .sequence(|reader| {
122 let _version: Int = reader.decode()?;
124 let private_key: OctetString = reader.decode()?;
126 while !reader.is_finished() {
129 let _: pkcs8::der::Any = reader.decode()?;
130 }
131 Ok(private_key.as_bytes().to_vec())
132 })
133 .map_err(|e| anyhow::anyhow!("Failed to parse ECPrivateKey structure: {:?}", e))?;
134
135 if key_bytes.len() != 32 {
136 bail!(
137 "Expected 32-byte ECDSA P-256 private key, got {} bytes",
138 key_bytes.len()
139 );
140 }
141
142 Ok(key_bytes)
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 TdxQuoteResponse {
151 pub quote: String,
153 pub event_log: String,
155 #[serde(default)]
157 pub hash_algorithm: Option<String>,
158 #[serde(default)]
160 pub prefix: Option<String>,
161}
162
163impl TdxQuoteResponse {
164 pub fn decode_quote(&self) -> Result<Vec<u8>, FromHexError> {
165 hex::decode(&self.quote)
166 }
167
168 pub fn decode_event_log(&self) -> Result<Vec<EventLog>, serde_json::Error> {
169 serde_json::from_str(&self.event_log)
170 }
171
172 pub fn replay_rtmrs(&self) -> Result<BTreeMap<u8, String>> {
174 let parsed_event_log: Vec<EventLog> = self.decode_event_log()?;
175 let mut rtmrs = BTreeMap::new();
176 for idx in 0..4 {
177 let mut history = Vec::new();
178 for event in &parsed_event_log {
179 if event.imr == idx {
180 history.push(event.digest.clone());
181 }
182 }
183 rtmrs.insert(
184 idx as u8,
185 replay_rtmr(history)
186 .ok()
187 .context("Invalid digest in event log")?,
188 );
189 }
190 Ok(rtmrs)
191 }
192}
193
194#[derive(Debug, Serialize, Deserialize)]
196#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))]
197#[cfg_attr(feature = "borsh_schema", derive(BorshSchema))]
198pub struct TappdTcbInfo {
199 pub mrtd: String,
201 pub rtmr0: String,
203 pub rtmr1: String,
205 pub rtmr2: String,
207 pub rtmr3: String,
209 pub event_log: Vec<EventLog>,
211 pub app_compose: String,
213}
214
215#[derive(Debug, Serialize, Deserialize)]
217#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))]
218#[cfg_attr(feature = "borsh_schema", derive(BorshSchema))]
219pub struct TappdInfoResponse {
220 pub app_id: String,
222 pub instance_id: String,
224 pub app_cert: String,
226 pub tcb_info: TappdTcbInfo,
228 pub app_name: String,
230}