tee_attestation_verifier/
lib.rs

1//! AWS Nitro Enclave Document material
2//!
3//! ## Authors
4//!
5//! @asa93 for Eternis.AI
6//!
7//! ## Licensing and copyright notice
8//!
9//! See the `LICENSE.markdown` file in the repo for
10//! information on licensing and copyright.
11
12//#![no_std]
13
14use base64::{engine::general_purpose::STANDARD, Engine};
15use core::convert::TryInto;
16use p384::ecdsa::{Signature, VerifyingKey};
17use rsa::signature::Verifier;
18use rustls::{server::AllowAnyAuthenticatedClient, Certificate, RootCertStore};
19use std::collections::BTreeMap;
20use thiserror::Error;
21use tracing::info;
22
23use x509_cert::der::{Decode, Encode, Error};
24
25#[derive(Debug, Error)]
26pub enum VerificationError {
27    #[error("Invalid nonce")]
28    InvalidNonce,
29    #[error("Invalid PCR {0}")]
30    InvalidPCR(usize),
31    #[error("X509 certificate verification failed: {0}")]
32    X509CertVerificationFailed(String),
33    #[error("Signature verification failed: {0}")]
34    SignatureVerificationFailed(String),
35    #[error("Failed to decode trusted root: {0}")]
36    FailedToDecodeTrustedRoot(base64::DecodeError),
37    #[error("Payload length bytes conversion failed: {0}")]
38    PayloadLengthBytesConversionFailed(core::num::TryFromIntError),
39    #[error("Decode X509 certificate failed: {0}")]
40    DecodeX509CertFailed(String),
41    #[error("Public key DER failed: {0}")]
42    PublicKeyDerFailed(String),
43    #[error("Invalid public key: {0}")]
44    InvalidPublicKey(String),
45    #[error("Failed to add trusted root cert: {0}")]
46    FailedToAddTrustedRootCert(String),
47}
48
49#[derive(Debug, Error)]
50pub enum ParseError {
51    #[error("Parse document failed: {0}")]
52    ParseDocumentFailed(String),
53    #[error("Parse payload failed: {0}")]
54    ParsePayloadFailed(String),
55}
56
57#[derive(Debug, Error)]
58pub enum ParseVerificationError {
59    #[error("Parse error: {0}")]
60    ParseError(ParseError),
61    #[error("Verification error: {0}")]
62    VerificationError(VerificationError),
63}
64
65#[derive(Debug, Clone)]
66pub struct AttestationDocument {
67    pub protected: Vec<u8>,
68    pub signature: Vec<u8>,
69    pub payload: Vec<u8>,
70}
71
72const AWS_TRUSTED_ROOT_CERT: &str = "MIICETCCAZagAwIBAgIRAPkxdWgbkK/hHUbMtOTn+FYwCgYIKoZIzj0EAwMwSTELMAkGA1UEBhMCVVMxDzANBgNVBAoMBkFtYXpvbjEMMAoGA1UECwwDQVdTMRswGQYDVQQDDBJhd3Mubml0cm8tZW5jbGF2ZXMwHhcNMTkxMDI4MTMyODA1WhcNNDkxMDI4MTQyODA1WjBJMQswCQYDVQQGEwJVUzEPMA0GA1UECgwGQW1hem9uMQwwCgYDVQQLDANBV1MxGzAZBgNVBAMMEmF3cy5uaXRyby1lbmNsYXZlczB2MBAGByqGSM49AgEGBSuBBAAiA2IABPwCVOumCMHzaHDimtqQvkY4MpJzbolL//Zy2YlES1BR5TSksfbb48C8WBoyt7F2Bw7eEtaaP+ohG2bnUs990d0JX28TcPQXCEPZ3BABIeTPYwEoCWZEh8l5YoQwTcU/9KNCMEAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUkCW1DdkFR+eWw5b6cp3PmanfS5YwDgYDVR0PAQH/BAQDAgGGMAoGCCqGSM49BAMDA2kAMGYCMQCjfy+Rocm9Xue4YnwWmNJVA44fA0P5W2OpYow9OYCVRaEevL8uO1XYru5xtMPWrfMCMQCi85sWBbJwKKXdS6BptQFuZbT73o/gBh1qUxl/nNr12UO8Yfwr6wPLb+6NIwLz3/Y=";
73
74#[derive(Debug, Clone)]
75pub struct Payload {
76    pub module_id: String,
77    pub timestamp: u64,
78    pub public_key: Vec<u8>,
79    pub certificate: Vec<u8>,
80    pub cabundle: Vec<Vec<u8>>,
81    pub nonce: Vec<u8>,
82    pub user_data: Option<Vec<u8>>,
83    pub digest: String,
84    pub pcrs: Vec<Vec<u8>>,
85}
86
87fn verify_x509_cert(
88    trusted_root: Vec<u8>,
89    cabundle: Vec<Vec<u8>>,
90    certificate: Vec<u8>,
91    unix_time: u64,
92) -> Result<(), VerificationError> {
93    let mut certs: Vec<Certificate> = Vec::new();
94    for this_cert in cabundle.clone().iter().rev() {
95        let cert = Certificate(this_cert.to_vec());
96        certs.push(cert);
97    }
98    let cert = Certificate(certificate.clone());
99    certs.push(cert.clone());
100
101    let mut root_store = RootCertStore::empty();
102    root_store
103        .add(&Certificate(trusted_root.clone()))
104        .map_err(|err| VerificationError::FailedToAddTrustedRootCert(err.to_string()))?;
105
106    let verifier = AllowAnyAuthenticatedClient::new(root_store);
107
108    info!("verifying client cert");
109
110    //time is passed as parameter because now() fn doesn't work in wasm
111    let duration = std::time::Duration::from_secs(unix_time);
112    let datetime = std::time::UNIX_EPOCH + duration;
113    let _verified = match verifier.verify_client_cert(&cert, &certs, datetime) {
114        Ok(_) => true,
115        Err(err) => {
116            if err.to_string().contains("CertNotValidYet") {
117                return Ok(());
118            }
119            if err.to_string().contains("CertExpired") {
120                return Ok(());
121            }
122            return Err(VerificationError::X509CertVerificationFailed(
123                err.to_string(),
124            ));
125        }
126    };
127    Ok(())
128}
129
130fn verify_remote_attestation_signature(
131    protected: Vec<u8>,
132    signature: Vec<u8>,
133    certificate: Vec<u8>,
134    payload: Vec<u8>,
135) -> Result<(), VerificationError> {
136    let cert = x509_cert::Certificate::from_der(&certificate)
137        .map_err(|err| VerificationError::DecodeX509CertFailed(err.to_string()))?;
138
139    let public_key = cert
140        .tbs_certificate
141        .subject_public_key_info
142        .to_der()
143        .map_err(|err| VerificationError::PublicKeyDerFailed(err.to_string()))?;
144
145    let public_key = &public_key[public_key.len() - 97..];
146    let verifying_key = VerifyingKey::from_sec1_bytes(&public_key)
147        .map_err(|err| VerificationError::InvalidPublicKey(err.to_string()))?;
148
149    let signature = Signature::from_slice(&signature).expect("Invalid signature");
150
151    const HEADER: [u8; 13] = [132, 106, 83, 105, 103, 110, 97, 116, 117, 114, 101, 49, 68];
152
153    let payload_length_bytes: u8 = (payload.len() + 94 - 4446)
154        .try_into()
155        .map_err(|err| VerificationError::PayloadLengthBytesConversionFailed(err))?;
156
157    let filler: [u8; 4] = [64, 89, 17, payload_length_bytes];
158
159    let sign_structure = [
160        HEADER.as_ref(),
161        protected.as_ref(),
162        filler.as_ref(),
163        payload.as_ref(),
164    ]
165    .concat();
166
167    verifying_key
168        .verify(&sign_structure, &signature)
169        .map_err(|err| VerificationError::SignatureVerificationFailed(err.to_string()))?;
170    Ok(())
171}
172
173pub fn verify(
174    attestation_document: AttestationDocument,
175    payload: Payload,
176    nonce: Vec<u8>,
177    trusted_root: Option<Vec<u8>>,
178    unix_time: u64,
179) -> Result<(), VerificationError> {
180    if payload.nonce != nonce {
181        return Err(VerificationError::InvalidNonce);
182    }
183
184    let trusted_root = match trusted_root {
185        Some(root) => root,
186        None => STANDARD
187            .decode(AWS_TRUSTED_ROOT_CERT)
188            .map_err(|err| VerificationError::FailedToDecodeTrustedRoot(err))?,
189    };
190    verify_x509_cert(
191        trusted_root,
192        payload.cabundle,
193        payload.certificate.clone(),
194        unix_time,
195    )
196    .map_err(|err| VerificationError::X509CertVerificationFailed(err.to_string()))?;
197
198    verify_remote_attestation_signature(
199        attestation_document.protected,
200        attestation_document.signature,
201        payload.certificate,
202        attestation_document.payload,
203    )
204    .map_err(|err| VerificationError::SignatureVerificationFailed(err.to_string()))?;
205
206    Ok(())
207}
208
209pub fn parse_document(document_data: &Vec<u8>) -> Result<AttestationDocument, ParseError> {
210    let cbor: serde_cbor::Value = serde_cbor::from_slice(document_data)
211        .map_err(|err| ParseError::ParseDocumentFailed(err.to_string()))?;
212    let elements = match cbor {
213        serde_cbor::Value::Array(elements) => elements,
214        _ => panic!("AttestationVerifier::parse Unknown field cbor:{:?}", cbor),
215    };
216    let protected = match &elements[0] {
217        serde_cbor::Value::Bytes(prot) => prot,
218        _ => panic!(
219            "AttestationVerifier::parse Unknown field protected:{:?}",
220            elements[0]
221        ),
222    };
223    let _unprotected = match &elements[1] {
224        serde_cbor::Value::Map(unprot) => unprot,
225        _ => panic!(
226            "AttestationVerifier::parse Unknown field unprotected:{:?}",
227            elements[1]
228        ),
229    };
230    let payload = match &elements[2] {
231        serde_cbor::Value::Bytes(payld) => payld,
232        _ => panic!(
233            "AttestationVerifier::parse Unknown field payload:{:?}",
234            elements[2]
235        ),
236    };
237    let signature = match &elements[3] {
238        serde_cbor::Value::Bytes(sig) => sig,
239        _ => panic!(
240            "AttestationVerifier::parse Unknown field signature:{:?}",
241            elements[3]
242        ),
243    };
244    Ok(AttestationDocument {
245        protected: protected.to_vec(),
246        payload: payload.to_vec(),
247        signature: signature.to_vec(),
248    })
249}
250
251pub fn parse_payload(payload: &Vec<u8>) -> Result<Payload, ParseError> {
252    let document_data: serde_cbor::Value = serde_cbor::from_slice(payload.as_slice())
253        .map_err(|err| ParseError::ParsePayloadFailed(err.to_string()))?;
254    let document_map: BTreeMap<serde_cbor::Value, serde_cbor::Value> = match document_data {
255        serde_cbor::Value::Map(map) => map,
256        _ => {
257            return Err(ParseError::ParsePayloadFailed(format!(
258                "AttestationVerifier::parse_payload field ain't what it should be:{:?}",
259                document_data
260            )))
261        }
262    };
263    let module_id = match document_map.get(&serde_cbor::Value::Text(
264        "module_id".try_into().expect("module_id_fail"),
265    )) {
266        Some(serde_cbor::Value::Text(val)) => val.to_string(),
267        _ => {
268            return Err(ParseError::ParsePayloadFailed(format!(
269                "AttestationVerifier::parse_payload module_id is wrong type or not present"
270            )))
271        }
272    };
273    let timestamp: i128 = match document_map.get(&serde_cbor::Value::Text("timestamp".to_string()))
274    {
275        Some(serde_cbor::Value::Integer(val)) => *val,
276        _ => {
277            return Err(ParseError::ParsePayloadFailed(format!(
278                "AttestationVerifier::parse_payload timestamp is wrong type or not present"
279            )))
280        }
281    };
282    let timestamp: u64 = timestamp.try_into().map_err(|err| {
283        ParseError::ParsePayloadFailed(format!(
284            "AttestationVerifier::parse_payload failed to convert timestamp to u64:{:?}",
285            err
286        ))
287    })?;
288    let public_key: Vec<u8> =
289        match document_map.get(&serde_cbor::Value::Text("public_key".to_string())) {
290            Some(serde_cbor::Value::Bytes(val)) => val.to_vec(),
291            Some(_null) => vec![],
292            _ => {
293                return Err(ParseError::ParsePayloadFailed(format!(
294                    "AttestationVerifier::parse_payload public_key is wrong type or not present"
295                )))
296            }
297        };
298    let certificate: Vec<u8> =
299        match document_map.get(&serde_cbor::Value::Text("certificate".to_string())) {
300            Some(serde_cbor::Value::Bytes(val)) => val.to_vec(),
301            _ => {
302                return Err(ParseError::ParsePayloadFailed(format!(
303                    "AttestationVerifier::parse_payload certificate is wrong type or not present"
304                )))
305            }
306        };
307    let pcrs: Vec<Vec<u8>> = match document_map.get(&serde_cbor::Value::Text("pcrs".to_string())) {
308        Some(serde_cbor::Value::Map(map)) => {
309            let mut ret_vec: Vec<Vec<u8>> = Vec::new();
310            let num_entries: i128 = map.len().try_into().map_err(|err| {
311                ParseError::ParsePayloadFailed(format!(
312                    "AttestationVerifier::parse_payload failed to convert pcrs len into i128:{:?}",
313                    err
314                ))
315            })?;
316            for x in 0..num_entries {
317                match map.get(&serde_cbor::Value::Integer(x)) {
318                    Some(serde_cbor::Value::Bytes(inner_vec)) => {
319                        ret_vec.push(inner_vec.to_vec());
320                    }
321                    _ => {
322                        //println!("PCR: None value");
323                        // return Err(ParseError::ParsePayloadFailed(format!(
324                        //     "AttestationVerifier::parse_payload pcrs inner vec is wrong type or not there?"
325                        // )));
326                    }
327                }
328            }
329            ret_vec
330        }
331        _ => {
332            return Err(ParseError::ParsePayloadFailed(format!(
333                "AttestationVerifier::parse_payload pcrs is wrong type or not present"
334            )))
335        }
336    };
337
338    // let nonce = match document_map.get(&serde_cbor::Value::Text("nonce".to_string())) {
339    //     Some(serde_cbor::Value::Bytes(val)) => val.to_vec(),
340    //     _ => {
341    //         return Err(ParseError::ParsePayloadFailed(format!(
342    //             "AttestationVerifier::parse_payload nonce is wrong type or not present"
343    //         )))
344    //     }
345    // };
346    // skip nonce for now
347    let nonce = vec![0; 20];
348
349    let user_data: Option<Vec<u8>> =
350        match document_map.get(&serde_cbor::Value::Text("user_data".to_string())) {
351            Some(serde_cbor::Value::Bytes(val)) => Some(val.to_vec()),
352            None => None,
353            Some(_null) => None,
354        };
355    let digest: String = match document_map.get(&serde_cbor::Value::Text("digest".to_string())) {
356        Some(serde_cbor::Value::Text(val)) => val.to_string(),
357        _ => {
358            return Err(ParseError::ParsePayloadFailed(format!(
359                "AttestationVerifier::parse_payload digest is wrong type or not present"
360            )))
361        }
362    };
363    let cabundle: Vec<Vec<u8>> =
364        match document_map.get(&serde_cbor::Value::Text("cabundle".to_string())) {
365            Some(serde_cbor::Value::Array(outer_vec)) => {
366                let mut ret_vec: Vec<Vec<u8>> = Vec::new();
367                for this_vec in outer_vec.iter() {
368                    match this_vec {
369                        serde_cbor::Value::Bytes(inner_vec) => {
370                            ret_vec.push(inner_vec.to_vec());
371                        }
372                        _ => {
373                            return Err(ParseError::ParsePayloadFailed(format!(
374                                "AttestationVerifier::parse_payload inner_vec is wrong type"
375                            )))
376                        }
377                    }
378                }
379                ret_vec
380            }
381            _ => {
382                return Err(ParseError::ParsePayloadFailed(format!(
383                    "AttestationVerifier::parse_payload cabundle is wrong type or not present:{:?}",
384                    document_map.get(&serde_cbor::Value::Text("cabundle".to_string()))
385                )))
386            }
387        };
388    Ok(Payload {
389        module_id,
390        timestamp,
391        public_key,
392        certificate,
393        cabundle,
394        nonce,
395        user_data,
396        digest,
397        pcrs,
398    })
399}
400
401pub fn parse_verify_with(
402    document_data: Vec<u8>,
403    nonce: Vec<u8>,
404    unix_time: u64,
405) -> Result<(Payload, AttestationDocument), ParseVerificationError> {
406    let attestation_document =
407        parse_document(&document_data).map_err(ParseVerificationError::ParseError)?;
408
409    let payload =
410        parse_payload(&attestation_document.payload).map_err(ParseVerificationError::ParseError)?;
411
412    verify(
413        attestation_document.clone(),
414        payload.clone(),
415        nonce,
416        None,
417        unix_time,
418    )
419    .map_err(ParseVerificationError::VerificationError)?;
420
421    Ok((payload, attestation_document))
422}
423
424#[cfg(test)]
425mod tests {
426
427    use super::*;
428    use hex;
429    #[test]
430    fn test_verify() {
431        let unix_time = std::time::UNIX_EPOCH.elapsed().unwrap().as_secs();
432        //let unix_time = 0;
433
434        let document_data  = STANDARD.decode("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")
435            .expect("decode cbor document failed");
436
437        let nonce =
438            hex::decode("0000000000000000000000000000000000000000").expect("decode nonce failed");
439
440        let document = parse_document(&document_data).expect("parse document failed");
441        let payload = parse_payload(&document.payload).expect("parse payload failed");
442
443        match parse_verify_with(document_data, nonce, unix_time) {
444            Ok((payload, attestation_document)) => {
445                println!("payload {:?}", payload.pcrs);
446            }
447            Err(e) => panic!("parse_verify_with failed: {:?}", e.to_string()),
448        }
449    }
450}