1use base64::Engine;
5use serde::{Deserialize, Serialize};
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
9pub enum TeeType {
10 #[serde(rename = "intel-sgx")]
11 IntelSGX,
12 #[serde(rename = "amd-sev-snp")]
13 AmdSevSnp,
14}
15
16#[derive(Debug, Clone, Serialize, Deserialize)]
18pub struct TeeEvidence {
19 pub attestation_type: TeeType,
21
22 pub quote: String,
24
25 pub certificates: Option<String>,
27
28 pub report_data: Option<String>,
30
31 pub mr_enclave: Option<String>,
33
34 pub mr_signer: Option<String>,
36
37 pub product_id: Option<u16>,
39
40 pub min_svn: Option<u16>,
42}
43
44#[derive(Debug, Clone, Serialize, Deserialize)]
46pub struct TeeReport {
47 pub valid: bool,
49
50 pub tee_type: TeeType,
52
53 pub mr_enclave: String,
55
56 pub mr_signer: String,
58
59 pub product_id: u16,
61
62 pub svn: u16,
64
65 pub report_data_match: bool,
67
68 pub errors: Vec<String>,
70
71 pub info: Vec<String>,
73}
74
75pub fn verify_tee_evidence(evidence: &TeeEvidence) -> Result<TeeReport, String> {
80 match evidence.attestation_type {
81 TeeType::IntelSGX => verify_sgx_evidence(evidence),
82 TeeType::AmdSevSnp => verify_sev_snp_evidence(evidence),
83 }
84}
85
86fn verify_sgx_evidence(evidence: &TeeEvidence) -> Result<TeeReport, String> {
88 let mut errors = Vec::new();
89 let mut info = Vec::new();
90 let mut valid = true;
91
92 use base64::engine::general_purpose::STANDARD;
94 let quote_bytes = STANDARD
95 .decode(&evidence.quote)
96 .map_err(|e| format!("Failed to decode quote: {}", e))?;
97
98 info.push(format!("Quote decoded: {} bytes", quote_bytes.len()));
99
100 if quote_bytes.len() < 200 {
102 errors.push("Quote too short to be valid SGX quote".to_string());
103 valid = false;
104 }
105
106 let mr_enclave = evidence
112 .mr_enclave
113 .clone()
114 .unwrap_or_else(|| "not_provided".to_string());
115 let mr_signer = evidence
116 .mr_signer
117 .clone()
118 .unwrap_or_else(|| "not_provided".to_string());
119 let product_id = evidence.product_id.unwrap_or(0);
120 let svn = evidence.min_svn.unwrap_or(0);
121
122 let report_data_match = if let Some(expected_report_data) = &evidence.report_data {
124 expected_report_data.len() >= 32
127 } else {
128 true };
130
131 if !report_data_match {
132 errors.push("Report data does not match expected value".to_string());
133 valid = false;
134 }
135
136 if let Some(expected_mrenclave) = &evidence.mr_enclave {
138 info.push(format!(
139 "Checking MRENCLAVE: {} == {}",
140 mr_enclave, expected_mrenclave
141 ));
142 }
143
144 if let Some(expected_product_id) = evidence.product_id {
145 if product_id != expected_product_id {
146 errors.push(format!(
147 "Product ID mismatch: expected {}, got {}",
148 expected_product_id, product_id
149 ));
150 valid = false;
151 }
152 }
153
154 if let Some(min_svn) = evidence.min_svn {
155 if svn < min_svn {
156 errors.push(format!("SVN too old: minimum {}, got {}", min_svn, svn));
157 valid = false;
158 }
159 }
160
161 info.push("Intel SGX quote structure validated (basic checks)".to_string());
162 info.push("Note: Full quote verification requires mc-sgx-dcap-quoteverify".to_string());
163
164 Ok(TeeReport {
165 valid,
166 tee_type: TeeType::IntelSGX,
167 mr_enclave,
168 mr_signer,
169 product_id,
170 svn,
171 report_data_match,
172 errors,
173 info,
174 })
175}
176
177fn verify_sev_snp_evidence(_evidence: &TeeEvidence) -> Result<TeeReport, String> {
179 let mut errors = Vec::new();
180 let info = Vec::new();
181
182 errors.push("AMD SEV-SNP verification not yet implemented".to_string());
183
184 Ok(TeeReport {
185 valid: false,
186 tee_type: TeeType::AmdSevSnp,
187 mr_enclave: "not_implemented".to_string(),
188 mr_signer: "not_implemented".to_string(),
189 product_id: 0,
190 svn: 0,
191 report_data_match: false,
192 errors,
193 info,
194 })
195}
196
197#[cfg(test)]
198mod tests {
199 use super::*;
200
201 #[test]
202 fn test_verify_sgx_basic() {
203 let evidence = TeeEvidence {
204 attestation_type: TeeType::IntelSGX,
205 quote: "SGVsbG8gV29ybGQhSGVsbG8gV29ybGQhSGVsbG8gV29ybGQhSGVsbG8gV29ybGQhSGVsbG8gV29ybGQhSGVsbG8gV29ybGQhSGVsbG8gV29ybGQhSGVsbG8gV29ybGQhSGVsbG8gV29ybGQhSGVsbG8gV29ybGQhSGVsbG8gV29ybGQhSGVsbG8gV29ybGQhSGVsbG8gV29ybGQhSGVsbG8gV29ybGQhSGVsbG8gV29ybGQhSGVsbG8gV29ybGQhSGVsbG8gV29ybGQhSGVsbG8gV29ybGQhSGVsbG8gV29ybGQhSGVsbG8gV29ybGQhSGVsbG8gV29ybGQhSGVsbG8gV29ybGQhSGVsbG8gV29ybGQhSGVsbG8gV29ybGQhSGVsbG8gV29ybGQhSGVsbG8gV29ybGQhSGVsbG8gV29ybGQhSGVsbG8gV29ybGQhSGVsbG8gV29ybGQhSGVsbG8gV29ybGQhSGVsbG8gV29ybGQhSGVsbG8gV29ybGQ=".to_string(),
207 certificates: None,
208 report_data: Some("0".repeat(64)),
209 mr_enclave: Some("0".repeat(64)),
210 mr_signer: Some("0".repeat(64)),
211 product_id: Some(0),
212 min_svn: Some(0),
213 };
214
215 let report = verify_tee_evidence(&evidence).unwrap();
216 assert!(!report.valid || report.info.iter().any(|i| i.contains("decoded")));
217 }
218
219 #[test]
220 fn test_verify_invalid_base64() {
221 let evidence = TeeEvidence {
222 attestation_type: TeeType::IntelSGX,
223 quote: "invalid!base64!!!".to_string(),
224 certificates: None,
225 report_data: None,
226 mr_enclave: None,
227 mr_signer: None,
228 product_id: None,
229 min_svn: None,
230 };
231
232 let result = verify_tee_evidence(&evidence);
233 assert!(result.is_err());
234 assert!(result.unwrap_err().contains("Failed to decode"));
235 }
236
237 #[test]
238 fn test_amd_sev_not_implemented() {
239 let evidence = TeeEvidence {
240 attestation_type: TeeType::AmdSevSnp,
241 quote: "somequote".to_string(),
242 certificates: None,
243 report_data: None,
244 mr_enclave: None,
245 mr_signer: None,
246 product_id: None,
247 min_svn: None,
248 };
249
250 let report = verify_tee_evidence(&evidence).unwrap();
251 assert!(!report.valid);
252 assert!(report
253 .errors
254 .iter()
255 .any(|e| e.contains("not yet implemented")));
256 }
257}