Skip to main content

kya_validator/
tee.rs

1// Trusted Execution Environment (TEE) Evidence Validation
2// Supports Intel SGX and AMD SEV-SNP remote attestation
3
4use base64::Engine;
5use serde::{Deserialize, Serialize};
6
7/// TEE attestation types supported
8#[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/// TEE evidence submitted with a manifest
17#[derive(Debug, Clone, Serialize, Deserialize)]
18pub struct TeeEvidence {
19    /// Type of TEE attestation
20    pub attestation_type: TeeType,
21
22    /// Raw attestation quote
23    pub quote: String,
24
25    /// Optional certificate chain for quote verification
26    pub certificates: Option<String>,
27
28    /// Report data expected to be in the quote
29    pub report_data: Option<String>,
30
31    /// Expected MR Enclave value
32    pub mr_enclave: Option<String>,
33
34    /// Expected MR Signer value
35    pub mr_signer: Option<String>,
36
37    /// Expected Product ID
38    pub product_id: Option<u16>,
39
40    /// Expected minimum SVN
41    pub min_svn: Option<u16>,
42}
43
44/// Result of TEE evidence verification
45#[derive(Debug, Clone, Serialize, Deserialize)]
46pub struct TeeReport {
47    /// Whether the evidence is valid
48    pub valid: bool,
49
50    /// TEE type
51    pub tee_type: TeeType,
52
53    /// MRENCLAVE value from quote
54    pub mr_enclave: String,
55
56    /// MRSIGNER value from quote
57    pub mr_signer: String,
58
59    /// Product ID from quote
60    pub product_id: u16,
61
62    /// Security Version Number from quote
63    pub svn: u16,
64
65    /// Whether report data matches expected
66    pub report_data_match: bool,
67
68    /// Verification errors (if any)
69    pub errors: Vec<String>,
70
71    /// Additional information
72    pub info: Vec<String>,
73}
74
75/// Verify TEE evidence
76///
77/// This function validates the attestation quote and verifies it against
78/// the expected values in the evidence.
79pub 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
86/// Verify Intel SGX quote
87fn 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    // Decode base64 quote (use STANDARD to handle padding properly)
93    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    // Check quote minimum length (SGX quote is at least several hundred bytes)
101    if quote_bytes.len() < 200 {
102        errors.push("Quote too short to be valid SGX quote".to_string());
103        valid = false;
104    }
105
106    // In production, we would use mc-sgx-dcap-quoteverify crate
107    // For now, we do basic validation and report what we extracted
108
109    // Simulate extracting values from quote
110    // In real implementation, this would parse the SGX quote structure
111    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    // Verify report data if provided
123    let report_data_match = if let Some(expected_report_data) = &evidence.report_data {
124        // In production, extract actual report data from quote and compare
125        // For now, just check format
126        expected_report_data.len() >= 32
127    } else {
128        true // No expected report data, so pass
129    };
130
131    if !report_data_match {
132        errors.push("Report data does not match expected value".to_string());
133        valid = false;
134    }
135
136    // Validate expected values if provided
137    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
177/// Verify AMD SEV-SNP attestation
178fn 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            // Create valid base64 that decodes to 250+ bytes
206            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}