lit_sev_snp_utils/guest/attestation/
certs.rs

1use std::env;
2use async_std::fs;
3use async_std::fs::{File, OpenOptions};
4use async_std::path::PathBuf;
5use async_std::sync::{Mutex};
6use async_trait::async_trait;
7use bytes::Bytes;
8use log::warn;
9use openssl::ec::EcKey;
10use openssl::pkey::Public;
11use openssl::x509::X509;
12use pem::parse_many;
13use cached::{Cached, SizedCache};
14use cached::once_cell::sync::Lazy;
15
16use crate::{AttestationReport, error};
17use crate::common::cache::{cache_dir_path, cache_file_path};
18use crate::common::cert::{x509_to_ec_key, x509_validate_signature};
19use crate::common::env::{ENV_CACHE_ENTRIES_VCEK_DEFAULT, ENV_CACHE_ENTRIES_VCEK_KEY};
20use crate::common::fetch::fetch_url_cached;
21use crate::common::file::{flock, write_bytes_to_file};
22use crate::common::pem::{der_to_pem, PEM_CERTIFICATE};
23use crate::error::Result as Result;
24
25pub const PRODUCT_NAME_MILAN: &str = "Milan";
26
27pub(crate) const CACHE_PREFIX: &str = "certs";
28
29pub(crate) const FETCH_ATTEMPTS: u8 = 10;
30pub(crate) const FETCH_ATTEMPT_SLEEP_MS: u64 = 4000;
31
32const KDS_CERT_SITE: &str = "https://kdsintf.amd.com";
33#[allow(dead_code)]
34const KDS_DEV_CERT_SITE: &str = "https://kdsintfdev.amd.com";
35#[allow(dead_code)]
36const KDS_CEK: &str = "/cek/id/";
37const KDS_VCEK: &str = "/vcek/v1/";               // KDS_VCEK/{product_name}/{hwid}?{tcb parameter list}
38#[allow(dead_code)]
39const KDS_VCEK_CERT_CHAIN: &str = "cert_chain";   // KDS_VCEK/{product_name}/cert_chain
40#[allow(dead_code)]
41const KDS_VCEK_CRL: &str = "crl";                 // KDS_VCEK/{product_name}/crl"
42
43const ASK_DER_FILENAME: &str = "ask.der";
44const ASK_PEM_FILENAME: &str = "ask.pem";
45const ARK_DER_FILENAME: &str = "ark.der";
46const ARK_PEM_FILENAME: &str = "ark.pem";
47
48const ARK_FETCH_LOCK_FILE: &str = "ark_fetch.lock";
49
50static ARK_CERT_CACHE: Lazy<Mutex<SizedCache<String, (X509, X509)>>> = Lazy::new(||
51    Mutex::new(SizedCache::with_size(10)));
52static VCEK_CERT_CACHE: Lazy<Mutex<SizedCache<String, X509>>> = Lazy::new(|| {
53    let cache_size = env::var(ENV_CACHE_ENTRIES_VCEK_KEY)
54        .unwrap_or(ENV_CACHE_ENTRIES_VCEK_DEFAULT.to_string())
55        .parse::<usize>()
56        .expect(format!("failed to parse env '{}' as usize", ENV_CACHE_ENTRIES_VCEK_KEY).as_str());
57
58    Mutex::new(SizedCache::with_size(cache_size))
59});
60
61#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
62#[allow(unused)]
63pub enum CertFormat {
64    PEM,
65    DER,
66}
67
68pub fn get_kds_vcek_cert_chain_url(product_name: &str) -> String {
69    format!("{}{}{}/{}",
70            KDS_CERT_SITE, KDS_VCEK, product_name, KDS_VCEK_CERT_CHAIN)
71}
72
73fn get_vcek_cache_suffix(product_name: &str) -> String {
74    format!("{}/{}", CACHE_PREFIX, product_name)
75}
76
77pub async fn fetch_kds_vcek_cert_chain_pem(product_name: &str) -> Result<Bytes> {
78    let save_path = format!("{}/{}.pem", get_vcek_cache_suffix(product_name),
79                            KDS_VCEK_CERT_CHAIN
80    );
81    fetch_url_cached(
82        get_kds_vcek_cert_chain_url(product_name).as_str(),
83        save_path.as_str(), FETCH_ATTEMPTS, FETCH_ATTEMPT_SLEEP_MS,
84    ).await
85}
86
87pub async fn get_kds_ark_ask_certs_bytes(product_name: &str, format: CertFormat) -> Result<(Bytes, Bytes)> {
88    let cache_suffix = get_vcek_cache_suffix(product_name);
89    let cache_path = cache_dir_path(&cache_suffix, true).await;
90
91    let mut ask_file_der = cache_path.clone();
92    ask_file_der.push(ASK_DER_FILENAME);
93
94    let mut ask_file_pem = cache_path.clone();
95    ask_file_pem.push(ASK_PEM_FILENAME);
96
97    let mut ark_file_der = cache_path.clone();
98    ark_file_der.push(ARK_DER_FILENAME);
99
100    let mut ark_file_pem = cache_path.clone();
101    ark_file_pem.push(ARK_PEM_FILENAME);
102
103    let ask_want = match format {
104        CertFormat::DER => &ask_file_der,
105        CertFormat::PEM => &ask_file_pem
106    };
107    let ark_want = match format {
108        CertFormat::DER => &ark_file_der,
109        CertFormat::PEM => &ark_file_pem
110    };
111
112    async fn load_files(ask_want: &PathBuf, ark_want: &PathBuf, format: CertFormat) -> Result<(Bytes, Bytes)> {
113        let ask_bytes = fs::read(ask_want).await
114            .map_err(|e| crate::error::io(e, Some(format!("failed to read ASK {:?} file: {}",
115                                                        format, ask_want.to_str().unwrap()))))?;
116        let ark_bytes = fs::read(&ark_want).await
117            .map_err(|e| crate::error::io(e, Some(format!("failed to read ARK {:?} file: {}",
118                                                        format, ark_want.to_str().unwrap()))))?;
119
120        return Ok((Bytes::from(ark_bytes), Bytes::from(ask_bytes)));
121    }
122
123    {
124        // Try read lock first and check if exists.
125        let _guard = ArkFetchLockFile::read().await?;
126
127        if ask_want.exists().await && ark_want.exists().await {
128            return load_files(ask_want, ark_want, format).await;
129        }
130    }
131
132    // Not found, get a write lock.
133    let _guard = ArkFetchLockFile::write().await?;
134
135    // Check one last time.
136    if ask_want.exists().await && ark_want.exists().await {
137        return load_files(ask_want, ark_want, format).await;
138    }
139
140    match fetch_kds_vcek_cert_chain_pem(product_name).await {
141        Ok(body) => {
142            // Extract pems
143            let pems = parse_many(body)
144                .map_err(|e| crate::error::io(e, Some(format!("failed to parse ARK cert chain into PEMs"))))?;
145            if pems.len() != 2 {
146                return Err(crate::error::io(
147                    format!("failed to parse ARK cert chain  - PEM count {} != 2", pems.len()), None,
148                ));
149            }
150
151            // DER
152            let ask_der_bytes = Bytes::from(pems[0].contents.clone());
153            let ark_der_bytes = Bytes::from(pems[1].contents.clone());
154
155            // Write ASK & ARK DER
156            write_bytes_to_file(ask_file_der.as_path(), &ask_der_bytes).await?;
157            write_bytes_to_file(ark_file_der.as_path(), &ark_der_bytes).await?;
158
159            // PEM
160            let ask_pem_bytes = Bytes::from(der_to_pem(ask_der_bytes.as_ref(), PEM_CERTIFICATE));
161            let ark_pem_bytes = Bytes::from(der_to_pem(ark_der_bytes.as_ref(), PEM_CERTIFICATE));
162
163            // Write ASK & ARK PEM
164            write_bytes_to_file(ask_file_pem.as_path(), &ask_pem_bytes).await?;
165            write_bytes_to_file(ark_file_pem.as_path(), &ark_pem_bytes).await?;
166
167            match format {
168                CertFormat::DER => Ok((ark_der_bytes, ask_der_bytes)),
169                CertFormat::PEM => Ok((ark_pem_bytes, ask_pem_bytes))
170            }
171        }
172        Err(e) => Err(e)
173    }
174}
175
176pub async fn get_kds_ark_ask_certs(product_name: &str) -> Result<(X509, X509)> {
177    let cache_key = format!("{}", product_name);
178
179    {
180        // Load from cache
181        let mut cache = ARK_CERT_CACHE.lock().await;
182
183        match cache.cache_get(&cache_key) {
184            Some((ark_cert, ask_cert)) => {
185                return Ok((ark_cert.clone(), ask_cert.clone()));
186            }
187            None => {},
188        }
189    }
190
191    let (ark_bytes, ask_bytes) = get_kds_ark_ask_certs_bytes(product_name,
192                                                             CertFormat::DER).await?;
193
194    let ark_cert = X509::from_der(ark_bytes.as_ref())
195        .map_err(|e| error::cert(Some(format!("failed to parse ARK cert: {:?}", e).to_string())))?;
196    let ask_cert = X509::from_der(ask_bytes.as_ref())
197        .map_err(|e| error::cert(Some(format!("failed to parse ASK cert: {:?}", e).to_string())))?;
198
199    // Store in cache
200    let mut cache = ARK_CERT_CACHE.lock().await;
201
202    cache.cache_set(cache_key, (ark_cert.clone(), ask_cert.clone()));
203
204    Ok((ark_cert, ask_cert))
205}
206
207pub fn validate_ark_ask_vcek_certs(ark_cert: &X509, ask_cert: &X509, vcek_cert: Option<&X509>) -> Result<()> {
208    // Verify ARK self-signed.
209    x509_validate_signature(ark_cert.clone(), None, ark_cert.clone())
210        .map_err(|e| error::cert(Some(format!("failed to verify ARK cert as self-signed: {:?}", e).to_string())))?;
211
212    // Verify ASK signed by ARK.
213    x509_validate_signature(ark_cert.clone(), None, ask_cert.clone())
214        .map_err(|e| error::cert(Some(format!("failed to verify ASK cert signed by ARK: {:?}", e).to_string())))?;
215
216    if let Some(vcek_cert) = vcek_cert {
217        // Verify VCEK signed by ASK.
218        x509_validate_signature(ark_cert.clone(), Some(ask_cert.clone()), vcek_cert.clone())
219            .map_err(|e| error::cert(Some(format!("failed to verify ASK cert signed by ARK: {:?}", e).to_string())))?;
220    }
221
222    Ok(())
223}
224
225pub async fn get_kds_ark_ask_certs_and_validate(product_name: &str) -> Result<(X509, X509)> {
226    let (ark_cert, ask_cert) = get_kds_ark_ask_certs(product_name).await?;
227
228    validate_ark_ask_vcek_certs(&ark_cert, &ask_cert, None)?;
229
230    Ok((ark_cert, ask_cert))
231}
232
233fn get_vcek_chip_cache_suffix(product_name: &str, chip_id: &str) -> String {
234    format!("{}/{}/{}", CACHE_PREFIX, product_name, chip_id)
235}
236
237fn get_vcek_cert_name_prefix(boot_loader: u8, tee: u8, snp: u8, microcode: u8) -> String {
238    format!("{:0>2}{:0>2}{:0>2}{:0>2}", boot_loader, tee, snp, microcode)
239}
240
241pub fn get_kds_vcek_der_url(product_name: &str, chip_id: &str,
242                            boot_loader: u8, tee: u8, snp: u8, microcode: u8) -> String {
243    format!("{}{}{}/{}?blSPL={:0>2}&teeSPL={:0>2}&snpSPL={:0>2}&ucodeSPL={:0>2}",
244            KDS_CERT_SITE, KDS_VCEK, product_name, chip_id, boot_loader, tee, snp, microcode)
245}
246
247pub async fn fetch_kds_vcek_der(product_name: &str, chip_id: &str,
248                                boot_loader: u8, tee: u8, snp: u8, microcode: u8) -> Result<Bytes> {
249    let save_path = format!("{}/{}.der",
250                            get_vcek_chip_cache_suffix(product_name, chip_id),
251                            get_vcek_cert_name_prefix(boot_loader, tee, snp, microcode)
252    );
253    fetch_url_cached(
254        get_kds_vcek_der_url(product_name, chip_id, boot_loader, tee, snp, microcode).as_str(),
255        save_path.as_str(), FETCH_ATTEMPTS, FETCH_ATTEMPT_SLEEP_MS,
256    ).await
257}
258
259pub async fn get_kds_vcek_cert_bytes(product_name: &str, chip_id: &str,
260                                     boot_loader: u8, tee: u8, snp: u8, microcode: u8,
261                                     format: CertFormat) -> Result<Bytes> {
262    let cache_suffix = get_vcek_chip_cache_suffix(product_name, chip_id);
263    let cache_path = cache_dir_path(&cache_suffix, true).await;
264
265    let cert_name = get_vcek_cert_name_prefix(boot_loader, tee, snp, microcode);
266
267    let mut cert_file_der = cache_path.clone();
268    cert_file_der.push(format!("{}.der", cert_name));
269
270    let mut cert_file_pem = cache_path.clone();
271    cert_file_pem.push(format!("{}.pem", cert_name));
272
273    let want_file = match format {
274        CertFormat::DER => &cert_file_der,
275        CertFormat::PEM => &cert_file_pem
276    };
277
278    async fn load_file(want_file: &PathBuf, format: CertFormat) -> Result<Bytes> {
279        let bytes = fs::read(&want_file).await
280            .map_err(|e| error::io(e, Some(format!("failed to read kds vcek {:?} file: {}",
281                                                   format, want_file.to_str().unwrap()))))?;
282        return Ok(Bytes::from(bytes));
283    }
284
285    {
286        // Try read lock first and check if exists.
287        let _guard = ArkFetchLockFile::read().await?;
288
289        if want_file.exists().await {
290            return load_file(want_file, format).await;
291        }
292    }
293
294    // Doesn't exist, get write lock.
295    let _guard = ArkFetchLockFile::write().await?;
296
297    // Check exists one last time.
298    if want_file.exists().await {
299        return load_file(want_file, format).await;
300    }
301
302    match fetch_kds_vcek_der(product_name, chip_id, boot_loader, tee, snp, microcode).await {
303        Ok(body) => {
304            // Extract pem
305            let pem_bytes = Bytes::from(der_to_pem(body.as_ref(), PEM_CERTIFICATE));
306
307            write_bytes_to_file(cert_file_pem.as_path(), &pem_bytes).await?;
308
309            match format {
310                CertFormat::DER => Ok(body),
311                CertFormat::PEM => Ok(pem_bytes)
312            }
313        }
314        Err(e) => Err(e)
315    }
316}
317
318pub async fn get_kds_vcek_cert(product_name: &str, chip_id: &str,
319                              boot_loader: u8, tee: u8, snp: u8, microcode: u8) -> Result<X509> {
320    let cache_key = format!("{}-{}-{:0>2}{:0>2}{:0>2}{:0>2}", product_name, chip_id,
321                            boot_loader, tee, snp, microcode);
322    {
323        // Load from cache
324        let mut cache = VCEK_CERT_CACHE.lock().await;
325
326        match cache.cache_get(&cache_key) {
327            Some(cert) => return Ok(cert.clone()),
328            None => {}
329        }
330    }
331
332    let vcek_bytes = get_kds_vcek_cert_bytes(product_name, chip_id,
333                                             boot_loader, tee, snp, microcode,
334                                             CertFormat::DER).await?;
335    let vcek_cert = X509::from_der(vcek_bytes.as_ref())
336        .map_err(|e| error::cert(Some(format!("failed to parse VCEK cert: {:?}", e).to_string())))?;
337
338    // Store in cache
339    let mut cache = VCEK_CERT_CACHE.lock().await;
340
341    cache.cache_set(cache_key, vcek_cert.clone());
342
343    Ok(vcek_cert)
344}
345
346#[async_trait]
347pub trait KdsCertificates {
348    fn get_kds_vcek_der_url(&self) -> String;
349    async fn get_kds_vcek_cert_bytes(&self, format: CertFormat) -> Result<Bytes>;
350    async fn get_kds_vcek_cert(&self) -> Result<X509>;
351    async fn get_kds_vcek_ec_key(&self) -> Result<EcKey<Public>>;
352    async fn verify_certs(&self) -> Result<()>;
353}
354
355#[async_trait]
356impl KdsCertificates for AttestationReport {
357    fn get_kds_vcek_der_url(&self) -> String {
358        get_kds_vcek_der_url(PRODUCT_NAME_MILAN, self.chip_id_hex().as_str(),
359                             self.platform_version.boot_loader, self.platform_version.tee,
360                             self.platform_version.snp, self.platform_version.microcode)
361    }
362
363    async fn get_kds_vcek_cert_bytes(&self, format: CertFormat) -> Result<Bytes> {
364        get_kds_vcek_cert_bytes(PRODUCT_NAME_MILAN, self.chip_id_hex().as_str(),
365                                self.platform_version.boot_loader, self.platform_version.tee,
366                                self.platform_version.snp, self.platform_version.microcode, format).await
367    }
368
369    async fn get_kds_vcek_cert(&self) -> Result<X509> {
370        get_kds_vcek_cert(PRODUCT_NAME_MILAN, self.chip_id_hex().as_str(),
371                              self.platform_version.boot_loader, self.platform_version.tee,
372                              self.platform_version.snp, self.platform_version.microcode).await
373    }
374
375    async fn get_kds_vcek_ec_key(&self) -> Result<EcKey<Public>> {
376        x509_to_ec_key(
377            self.get_kds_vcek_cert().await?
378        ).map_err(|e|
379            error::cert(Some(format!("failed to extract EC Key from VCEK key: {:?}", e).to_string())))
380    }
381
382    async fn verify_certs(&self) -> Result<()> {
383        let (ask_cert, ark_cert) = get_kds_ark_ask_certs(PRODUCT_NAME_MILAN).await?;
384        let vcek_cert = get_kds_vcek_cert(PRODUCT_NAME_MILAN, self.chip_id_hex().as_str(),
385                                    self.platform_version.boot_loader, self.platform_version.tee,
386                                    self.platform_version.snp, self.platform_version.microcode).await?;
387
388        validate_ark_ask_vcek_certs(&ark_cert, &ask_cert, Some(&vcek_cert))
389    }
390}
391
392// Utils
393
394struct ArkFetchLockFile {
395    file: File,
396}
397
398impl ArkFetchLockFile {
399    pub async fn new(flag: libc::c_int) -> Result<Self> {
400        let filename = cache_file_path(
401            format!("{}/{}", CACHE_PREFIX, ARK_FETCH_LOCK_FILE).as_str(), true).await;
402
403        let file = OpenOptions::new()
404            .write(true)
405            .create(true)
406            .append(true)
407            .open(&filename).await
408            .map_err(error::map_io_err)?;
409
410        flock(&file, flag)?;
411
412        Ok(Self { file })
413    }
414
415    pub async fn write() -> Result<Self> {
416        Self::new(libc::LOCK_EX).await
417    }
418
419    pub async fn read() -> Result<Self> {
420        Self::new(libc::LOCK_SH).await
421    }
422}
423
424impl Drop for ArkFetchLockFile {
425    fn drop(&mut self) {
426        if let Err(err) = flock(&self.file, libc::LOCK_UN) {
427            warn!("failed to unlock: {:?}", err);
428        }
429    }
430}
431
432#[cfg(test)]
433mod tests {
434    use std::env;
435    use std::path::PathBuf;
436
437    use crate::guest::attestation::certs::{CertFormat, fetch_kds_vcek_cert_chain_pem, fetch_kds_vcek_der, get_kds_ark_ask_certs, get_kds_ark_ask_certs_and_validate, get_kds_ark_ask_certs_bytes, get_kds_vcek_cert_chain_url, KdsCertificates, PRODUCT_NAME_MILAN};
438    use crate::guest::attestation::report::AttestationReport;
439
440    const TEST_REPORT_BIN: &str = "resources/test/guest_report.bin";
441
442    const TEST_ARK_MILAN_SUBJECT_STR: &str = "[organizationalUnitName = \"Engineering\", countryName = \"US\", localityName = \"Santa Clara\", stateOrProvinceName = \"CA\", organizationName = \"Advanced Micro Devices\", commonName = \"ARK-Milan\"]";
443    const TEST_SEV_MILAN_SUBJECT_STR: &str = "[organizationalUnitName = \"Engineering\", countryName = \"US\", localityName = \"Santa Clara\", stateOrProvinceName = \"CA\", organizationName = \"Advanced Micro Devices\", commonName = \"SEV-Milan\"]";
444    const TEST_SEV_VCEK_SUBJECT_STR: &str = "[organizationalUnitName = \"Engineering\", countryName = \"US\", localityName = \"Santa Clara\", stateOrProvinceName = \"CA\", organizationName = \"Advanced Micro Devices\", commonName = \"SEV-VCEK\"]";
445
446    #[test]
447    fn get_kds_vcek_cert_chain_url_test() {
448        let url = get_kds_vcek_cert_chain_url(PRODUCT_NAME_MILAN);
449
450        assert_eq!(url, "https://kdsintf.amd.com/vcek/v1/Milan/cert_chain");
451    }
452
453    #[tokio::test]
454    async fn fetch_kds_vcek_cert_chain_test() {
455        let chain = fetch_kds_vcek_cert_chain_pem(PRODUCT_NAME_MILAN).await
456            .expect("failed to call fetch_kds_vcek_cert_chain");
457
458        assert!(chain.len() > 1000);
459    }
460
461    #[tokio::test]
462    async fn get_kds_ark_ask_certs_bytes_test() {
463        let (ark_pem, ask_pem) = get_kds_ark_ask_certs_bytes(
464            PRODUCT_NAME_MILAN, CertFormat::PEM).await
465            .expect("failed to call get_kds_ark_ask_certs_bytes");
466
467        assert!(ark_pem.len() > 1000);
468        assert!(ask_pem.len() > 1000);
469
470        let (ark_der, ask_der) = get_kds_ark_ask_certs_bytes(
471            PRODUCT_NAME_MILAN, CertFormat::DER).await
472            .expect("failed to call get_kds_ark_ask_certs_bytes");
473
474        assert!(ark_der.len() > 1000);
475        assert!(ask_der.len() > 1000);
476    }
477
478    #[tokio::test]
479    async fn get_kds_ark_ask_certs_test() {
480        let (ark_cert, ask_cert) = get_kds_ark_ask_certs(
481            PRODUCT_NAME_MILAN).await
482            .expect("failed to call get_kds_ark_ask_certs");
483
484        assert_eq!(format!("{:?}", ark_cert.subject_name()), TEST_ARK_MILAN_SUBJECT_STR);
485        assert_eq!(format!("{:?}", ask_cert.subject_name()), TEST_SEV_MILAN_SUBJECT_STR);
486    }
487
488    #[tokio::test]
489    async fn get_kds_ark_ask_certs_and_validate_test() {
490        let (ark_cert, ask_cert) = get_kds_ark_ask_certs_and_validate(PRODUCT_NAME_MILAN).await
491            .expect("failed to call get_kds_ark_ask_certs_and_validate");
492
493        assert_eq!(format!("{:?}", ark_cert.subject_name()), TEST_ARK_MILAN_SUBJECT_STR);
494        assert_eq!(format!("{:?}", ask_cert.subject_name()), TEST_SEV_MILAN_SUBJECT_STR);
495    }
496
497    #[test]
498    fn get_kds_vcek_url_test() {
499        let mut test_file = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
500        test_file.push(TEST_REPORT_BIN);
501
502        let report = AttestationReport::from_file(&test_file)
503            .expect("failed to create AttestationReport from_file");
504
505        assert_eq!(report.get_kds_vcek_der_url(), "https://kdsintf.amd.com/vcek/v1/Milan/9e1235cce6f3e507b66a9d3f2199a325cd0be17c6c50fd55c284ceff993dbf6c7e32fa16a76521bf6b78cc9ca482e572bde70e8c9f1bdfcb8267dea8e11ff77e?blSPL=02&teeSPL=00&snpSPL=06&ucodeSPL=115");
506    }
507
508    #[tokio::test]
509    async fn fetch_kds_vcek_test() {
510        let mut test_file = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
511        test_file.push(TEST_REPORT_BIN);
512
513        let report = AttestationReport::from_file(&test_file).unwrap();
514
515        let vcek = fetch_kds_vcek_der(
516            PRODUCT_NAME_MILAN, report.chip_id_hex().as_str(),
517            report.platform_version.boot_loader, report.platform_version.tee,
518            report.platform_version.snp, report.platform_version.microcode).await
519            .expect("failed to call fetch_kds_vcek_der");
520
521        assert!(vcek.len() > 1000);
522    }
523
524    #[tokio::test]
525    async fn get_kds_vcek_cert_bytes_test() {
526        let mut test_file = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
527        test_file.push(TEST_REPORT_BIN);
528
529        let report = AttestationReport::from_file(&test_file).unwrap();
530
531        let pem = report.get_kds_vcek_cert_bytes(CertFormat::PEM).await
532            .expect("failed to call get_kds_vcek");
533
534        assert_ne!(pem, "");
535
536        // Calling a second time should work (cached)
537        let der = report.get_kds_vcek_cert_bytes(CertFormat::DER).await
538            .expect("failed to call get_kds_vcek");
539
540        assert_ne!(der, "");
541    }
542
543    #[tokio::test]
544    async fn get_kds_vcek_test() {
545        let mut test_file = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
546        test_file.push(TEST_REPORT_BIN);
547
548        let report = AttestationReport::from_file(&test_file).unwrap();
549
550        let cert = report.get_kds_vcek_cert().await
551            .expect("failed to call get_kds_vcek_cert");
552
553        assert_eq!(format!("{:?}", cert.subject_name()), TEST_SEV_VCEK_SUBJECT_STR);
554    }
555
556    #[tokio::test]
557    async fn verify_certs_test() {
558        let mut test_file = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
559        test_file.push(TEST_REPORT_BIN);
560
561        let report = AttestationReport::from_file(&test_file).unwrap();
562
563        report.verify_certs().await
564            .expect("failed to call verify_certs");
565    }
566}