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/"; #[allow(dead_code)]
39const KDS_VCEK_CERT_CHAIN: &str = "cert_chain"; #[allow(dead_code)]
41const KDS_VCEK_CRL: &str = "crl"; const 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 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 let _guard = ArkFetchLockFile::write().await?;
134
135 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 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 let ask_der_bytes = Bytes::from(pems[0].contents.clone());
153 let ark_der_bytes = Bytes::from(pems[1].contents.clone());
154
155 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 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_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 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 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 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 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 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 let _guard = ArkFetchLockFile::read().await?;
288
289 if want_file.exists().await {
290 return load_file(want_file, format).await;
291 }
292 }
293
294 let _guard = ArkFetchLockFile::write().await?;
296
297 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 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 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 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
392struct 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 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}