1use std::borrow::Cow;
14use std::time::Duration;
15
16use pcs::{
17 CpuSvn, DcapArtifactIssuer, EncPpid, Fmspc, PceId, PceIsvsvn, PckCert, PckCrl, QeId, QeIdentitySigned, TcbInfo,
18 Unverified,
19};
20use rustc_serialize::hex::{FromHex, ToHex};
21
22use super::common::*;
23use super::{
24 Client, ClientBuilder, Fetcher, PckCertIn, PckCertService, PckCrlIn, PckCrlService, PcsVersion,
25 ProvisioningServiceApi, QeIdIn, QeIdService, StatusCode, TcbInfoIn, TcbInfoService,
26};
27use super::intel::TcbEvaluationDataNumbersApi;
28use crate::Error;
29
30pub struct PccsProvisioningClientBuilder {
31 base_url: Cow<'static, str>,
32 api_version: PcsVersion,
33 client_builder: ClientBuilder,
34}
35
36impl PccsProvisioningClientBuilder {
37 pub fn new<T: Into<Cow<'static, str>>>(api_version: PcsVersion, base_url: T) -> Self {
38 Self {
39 base_url: base_url.into(),
40 api_version,
41 client_builder: ClientBuilder::new(),
42 }
43 }
44
45 pub fn set_retry_timeout(mut self, retry_timeout: Duration) -> Self {
46 self.client_builder = self.client_builder.set_retry_timeout(retry_timeout);
47 self
48 }
49
50 pub fn build<F: for<'a> Fetcher<'a>>(self, fetcher: F) -> Client<F> {
51 let pck_certs = PckCertsApiNotSupported;
52 let pck_cert = PckCertApi::new(self.base_url.clone(), self.api_version);
53 let pck_crl = PckCrlApi::new(self.base_url.clone(), self.api_version);
54 let qeid = QeIdApi::new(self.base_url.clone(), self.api_version);
55 let tcbinfo = TcbInfoApi::new(self.base_url.clone(), self.api_version);
56 let evaluation_data_numbers = TcbEvaluationDataNumbersApi::new(self.base_url.clone());
57 self.client_builder
58 .build(pck_certs, pck_cert, pck_crl, qeid, tcbinfo, evaluation_data_numbers, fetcher)
59 }
60}
61
62pub struct PckCertApi {
63 base_url: Cow<'static, str>,
64 api_version: PcsVersion,
65}
66
67impl PckCertApi {
68 pub(crate) fn new(base_url: Cow<'static, str>, api_version: PcsVersion) -> PckCertApi {
69 PckCertApi {
70 base_url,
71 api_version,
72 }
73 }
74}
75
76impl<'inp> PckCertService<'inp> for PckCertApi {
77 fn build_input(
78 &'inp self,
79 encrypted_ppid: Option<&'inp EncPpid>,
80 pce_id: &'inp PceId,
81 cpu_svn: &'inp CpuSvn,
82 pce_isvsvn: PceIsvsvn,
83 qe_id: Option<&'inp QeId>,
84 ) -> <Self as ProvisioningServiceApi<'inp>>::Input {
85 PckCertIn {
86 encrypted_ppid,
87 pce_id,
88 cpu_svn,
89 pce_isvsvn,
90 qe_id,
91 api_key: &None,
92 api_version: self.api_version,
93 }
94 }
95}
96
97impl<'inp> ProvisioningServiceApi<'inp> for PckCertApi {
101 type Input = PckCertIn<'inp>;
102 type Output = PckCert<Unverified>;
103
104 fn build_request(&self, input: &Self::Input) -> Result<(String, Vec<(String, String)>), Error> {
105 let api_version = input.api_version as u8;
106 let encrypted_ppid = input
107 .encrypted_ppid
108 .ok_or(Error::NoEncPPID)
109 .map(|e_ppid| e_ppid.to_hex())?;
110
111 let cpu_svn = input.cpu_svn.to_hex();
112 let pce_isvsvn = input.pce_isvsvn.to_le_bytes().to_hex();
113 let pce_id = input.pce_id.to_le_bytes().to_hex();
114 let qe_id = input
115 .qe_id
116 .ok_or(Error::NoQeID)
117 .map(|qe_id| qe_id.to_hex())?;
118
119 let url = format!(
120 "{}/sgx/certification/v{}/pckcert?encrypted_ppid={}&cpusvn={}&pcesvn={}&pceid={}&qeid={}",
121 self.base_url, api_version, encrypted_ppid, cpu_svn, pce_isvsvn, pce_id, qe_id,
122 );
123 let headers = Vec::new();
124 Ok((url, headers))
125 }
126
127 fn validate_response(&self, status_code: StatusCode) -> Result<(), Error> {
128 match status_code {
129 StatusCode::Ok => Ok(()),
130 StatusCode::BadRequest => {
131 Err(Error::PCSError(status_code, "Invalid request parameters"))
132 }
133 StatusCode::NotFound => Err(Error::PCSError(
134 status_code,
135 "No cache data for this platform",
136 )),
137 StatusCode::NonStandard461 => Err(Error::PCSError(
138 status_code,
139 "The platform was not found in the cache",
140 )),
141 StatusCode::NonStandard462 => Err(Error::PCSError(
142 status_code,
143 "Certificates are not available for certain TCBs",
144 )),
145 StatusCode::InternalServerError => Err(Error::PCSError(
146 status_code,
147 "PCCS suffered from an internal server error",
148 )),
149 StatusCode::BadGateway => Err(Error::PCSError(
150 status_code,
151 "Unable to retrieve the collateral from the Intel SGX PCS",
152 )),
153 _ => Err(Error::PCSError(
154 status_code,
155 "Unexpected response from PCCS server",
156 )),
157 }
158 }
159
160 fn parse_response(
161 &self,
162 response_body: String,
163 response_headers: Vec<(String, String)>,
164 _api_version: PcsVersion,
165 ) -> Result<Self::Output, Error> {
166 let ca_chain = parse_issuer_header(&response_headers, PCK_CERTIFICATE_ISSUER_CHAIN_HEADER)?;
167 Ok(PckCert::new(response_body, ca_chain))
168 }
169}
170
171pub struct PckCrlApi {
172 base_url: Cow<'static, str>,
173 api_version: PcsVersion,
174}
175
176impl PckCrlApi {
177 pub fn new(base_url: Cow<'static, str>, api_version: PcsVersion) -> Self {
178 PckCrlApi {
179 base_url,
180 api_version,
181 }
182 }
183}
184
185impl<'inp> PckCrlService<'inp> for PckCrlApi {
186 fn build_input(&'inp self, ca: DcapArtifactIssuer) -> <Self as ProvisioningServiceApi<'inp>>::Input {
187 PckCrlIn {
188 api_version: self.api_version,
189 ca,
190 }
191 }
192}
193
194impl<'inp> ProvisioningServiceApi<'inp> for PckCrlApi {
198 type Input = PckCrlIn;
199 type Output = PckCrl<Unverified>;
200
201 fn build_request(&self, input: &Self::Input) -> Result<(String, Vec<(String, String)>), Error> {
202 let ca = match input.ca {
203 DcapArtifactIssuer::PCKProcessorCA => "processor",
204 DcapArtifactIssuer::PCKPlatformCA => "platform",
205 DcapArtifactIssuer::SGXRootCA => {
206 return Err(Error::PCSError(StatusCode::BadRequest, "Invalid ca parameter"));
207 },
208 };
209 let url = format!(
210 "{}/sgx/certification/v{}/pckcrl?ca={}",
211 self.base_url, input.api_version as u8, ca
212 );
213 Ok((url, Vec::new()))
214 }
215
216 fn validate_response(&self, status_code: StatusCode) -> Result<(), Error> {
217 match &status_code {
218 StatusCode::Ok => Ok(()),
219 StatusCode::BadRequest => {
220 Err(Error::PCSError(status_code, "Invalid request parameters"))
221 }
222 StatusCode::NotFound => Err(Error::PCSError(status_code, "PCK CRL cannot be found")),
223 StatusCode::InternalServerError => Err(Error::PCSError(
224 status_code,
225 "PCCS suffered from an internal server error",
226 )),
227 StatusCode::BadGateway => Err(Error::PCSError(
228 status_code,
229 "Unable to retrieve the collateral from the Intel SGX PCS",
230 )),
231 _ => Err(Error::PCSError(
232 status_code,
233 "Unexpected response from PCCS server",
234 )),
235 }
236 }
237
238 fn parse_response(
239 &self,
240 response_body: String,
241 response_headers: Vec<(String, String)>,
242 _api_version: PcsVersion,
243 ) -> Result<Self::Output, Error> {
244 let ca_chain = parse_issuer_header(&response_headers, PCK_CRL_ISSUER_CHAIN_HEADER)?;
245 let pem_crl = pkix::pem::der_to_pem(
246 &response_body.from_hex().map_err(|e| {
247 Error::ReadResponseError(
248 format!("failed to parse response body as hex-encoded DER: {}", e).into(),
249 )
250 })?,
251 pkix::pem::PEM_CRL,
252 );
253 Ok(PckCrl::new(pem_crl, ca_chain)?)
254 }
255}
256
257pub struct TcbInfoApi {
258 base_url: Cow<'static, str>,
259 api_version: PcsVersion,
260}
261
262impl TcbInfoApi {
263 pub fn new(base_url: Cow<'static, str>, api_version: PcsVersion) -> Self {
264 TcbInfoApi {
265 base_url,
266 api_version,
267 }
268 }
269}
270
271impl<'inp> TcbInfoService<'inp> for TcbInfoApi {
272 fn build_input(
273 &'inp self,
274 fmspc: &'inp Fmspc,
275 tcb_evaluation_data_number: Option<u16>,
276 ) -> <Self as ProvisioningServiceApi<'inp>>::Input {
277 TcbInfoIn {
278 api_version: self.api_version,
279 fmspc,
280 tcb_evaluation_data_number,
281 }
282 }
283}
284
285impl<'inp> ProvisioningServiceApi<'inp> for TcbInfoApi {
289 type Input = TcbInfoIn<'inp>;
290 type Output = TcbInfo;
291
292 fn build_request(&self, input: &Self::Input) -> Result<(String, Vec<(String, String)>), Error> {
293 let api_version = input.api_version as u8;
294 let fmspc = input.fmspc.as_bytes().to_hex();
295 let url = if let Some(evaluation_data_number) = input.tcb_evaluation_data_number {
296 format!(
297 "{}/sgx/certification/v{}/tcb?fmspc={}&tcbEvaluationDataNumber={}",
298 self.base_url, api_version, fmspc, evaluation_data_number)
299 } else {
300 format!(
301 "{}/sgx/certification/v{}/tcb?fmspc={}&update=early",
302 self.base_url, api_version, fmspc,
303 )
304 };
305 Ok((url, Vec::new()))
306 }
307
308 fn validate_response(&self, status_code: StatusCode) -> Result<(), Error> {
309 match &status_code {
310 StatusCode::Ok => Ok(()),
311 StatusCode::BadRequest => {
312 Err(Error::PCSError(status_code, "Invalid request parameters"))
313 }
314 StatusCode::NotFound => Err(Error::PCSError(
315 status_code,
316 "TCB information for provided FMSPC cannot be found",
317 )),
318 StatusCode::InternalServerError => Err(Error::PCSError(
319 status_code,
320 "PCCS suffered from an internal server error",
321 )),
322 StatusCode::BadGateway => Err(Error::PCSError(
323 status_code,
324 "Unable to retrieve the collateral from the Intel SGX PCS",
325 )),
326 _ => Err(Error::PCSError(
327 status_code,
328 "Unexpected response from PCCS server",
329 )),
330 }
331 }
332
333 fn parse_response(
334 &self,
335 response_body: String,
336 response_headers: Vec<(String, String)>,
337 api_version: PcsVersion,
338 ) -> Result<Self::Output, Error> {
339 let key = match api_version {
340 PcsVersion::V3 => TCB_INFO_ISSUER_CHAIN_HEADER_V3,
341 PcsVersion::V4 => TCB_INFO_ISSUER_CHAIN_HEADER_V4,
342 };
343 let ca_chain = parse_issuer_header(&response_headers, key)?;
344 Ok(TcbInfo::parse(&response_body, ca_chain)?)
345 }
346}
347
348pub struct QeIdApi {
349 base_url: Cow<'static, str>,
350 api_version: PcsVersion,
351}
352
353impl QeIdApi {
354 pub fn new(base_url: Cow<'static, str>, api_version: PcsVersion) -> Self {
355 QeIdApi {
356 base_url,
357 api_version,
358 }
359 }
360}
361
362impl<'inp> QeIdService<'inp> for QeIdApi {
363 fn build_input(&'inp self, tcb_evaluation_data_number: Option<u16>) -> <Self as ProvisioningServiceApi<'inp>>::Input {
364 QeIdIn {
365 api_version: self.api_version,
366 tcb_evaluation_data_number,
367 }
368 }
369}
370
371impl<'inp> ProvisioningServiceApi<'inp> for QeIdApi {
375 type Input = QeIdIn;
376 type Output = QeIdentitySigned;
377
378 fn build_request(&self, input: &Self::Input) -> Result<(String, Vec<(String, String)>), Error> {
379 let api_version = input.api_version as u8;
380 let url = if let Some(tcb_evaluation_data_number) = input.tcb_evaluation_data_number {
381 format!(
382 "{}/sgx/certification/v{}/qe/identity?tcbEvaluationDataNumber={}",
383 self.base_url, api_version, tcb_evaluation_data_number
384 )
385 } else {
386 format!(
387 "{}/sgx/certification/v{}/qe/identity?update=early",
388 self.base_url, api_version,
389 )
390 };
391 Ok((url, Vec::new()))
392 }
393
394 fn validate_response(&self, status_code: StatusCode) -> Result<(), Error> {
395 match &status_code {
396 StatusCode::Ok => Ok(()),
397 StatusCode::NotFound => Err(Error::PCSError(
398 status_code,
399 "QE identity information cannot be found",
400 )),
401 StatusCode::InternalServerError => Err(Error::PCSError(
402 status_code,
403 "PCCS suffered from an internal server error",
404 )),
405 StatusCode::BadGateway => Err(Error::PCSError(
406 status_code,
407 "Unable to retrieve the collateral from the Intel SGX PCS",
408 )),
409 _ => Err(Error::PCSError(
410 status_code,
411 "Unexpected response from PCCS server",
412 )),
413 }
414 }
415
416 fn parse_response(
417 &self,
418 response_body: String,
419 response_headers: Vec<(String, String)>,
420 _api_version: PcsVersion,
421 ) -> Result<Self::Output, Error> {
422 let ca_chain = parse_issuer_header(&response_headers, ENCLAVE_ID_ISSUER_CHAIN_HEADER)?;
423 let id = QeIdentitySigned::parse(&response_body, ca_chain)?;
424 Ok(id)
425 }
426}
427
428#[cfg(all(test, feature = "reqwest"))]
429mod tests {
430 use std::convert::TryFrom;
431 use std::hash::{DefaultHasher, Hash, Hasher};
432 use std::path::PathBuf;
433 use std::sync::OnceLock;
434 use std::time::Duration;
435
436 use pcs::{
437 EnclaveIdentity, Fmspc, PckID, Platform, RawTcbEvaluationDataNumbers,
438 TcbEvaluationDataNumbers,
439 };
440
441 use super::Client;
442 use crate::provisioning_client::{
443 test_helpers, DcapArtifactIssuer, PccsProvisioningClientBuilder, PcsVersion,
444 ProvisioningClient,
445 };
446 use crate::{reqwest_client_insecure_tls, ReqwestClient};
447
448 const PCKID_TEST_FILE: &str = "./tests/data/pckid_retrieval.csv";
449 const OUTPUT_TEST_DIR: &str = "./tests/data/";
450 const TIME_RETRY_TIMEOUT: Duration = Duration::from_secs(180);
451
452 static PCCS_URL: OnceLock<String> = OnceLock::new();
453
454 fn pccs_url_from_env() -> String {
455 let url = std::env::var("PCCS_URL").unwrap_or(String::from("https://pccs.fortanix.com"));
456 assert!(
457 !url.is_empty(),
458 "Empty string in environment variable: PCCS_URL"
459 );
460 url
461 }
462
463 fn make_client(api_version: PcsVersion) -> Client<ReqwestClient> {
464 let url = &*PCCS_URL.get_or_init(pccs_url_from_env);
465 PccsProvisioningClientBuilder::new(api_version, url)
466 .set_retry_timeout(TIME_RETRY_TIMEOUT)
467 .build(reqwest_client_insecure_tls())
468 }
469
470 #[test]
471 pub fn pck() {
472 for api_version in [PcsVersion::V3, PcsVersion::V4] {
473 let client = make_client(api_version);
474
475 for pckid in PckID::parse_file(&PathBuf::from(PCKID_TEST_FILE).as_path())
476 .unwrap()
477 .iter()
478 {
479 let pck = client
480 .pckcert(
481 Some(&pckid.enc_ppid),
482 &pckid.pce_id,
483 &pckid.cpu_svn,
484 pckid.pce_isvsvn,
485 Some(&pckid.qe_id),
486 )
487 .unwrap();
488
489 assert_eq!(
490 test_helpers::get_cert_subject(pck.ca_chain().last().unwrap()),
491 "Intel SGX Root CA"
492 );
493 }
494 }
495 }
496
497 #[test]
498 pub fn pck_cached() {
499 let root_ca = include_bytes!("../../tests/data/root_SGX_CA_der.cert");
500 let root_cas = [&root_ca[..]];
501 for api_version in [PcsVersion::V3, PcsVersion::V4] {
502 let client = make_client(api_version);
503
504 for pckid in PckID::parse_file(&PathBuf::from(PCKID_TEST_FILE).as_path())
505 .unwrap()
506 .iter()
507 {
508 let pck = client
509 .pckcert(
510 Some(&pckid.enc_ppid),
511 &pckid.pce_id,
512 &pckid.cpu_svn,
513 pckid.pce_isvsvn,
514 Some(&pckid.qe_id),
515 )
516 .unwrap();
517
518 let pck = pck.verify(&root_cas, None).unwrap();
519
520 {
522 let mut cache = client.pckcert_service.cache.lock().unwrap();
523
524 assert!(cache.len() > 0);
525
526 let (cached_pck, _) = {
527 let mut hasher = DefaultHasher::new();
528 let input = client.pckcert_service.pcs_service().build_input(
529 Some(&pckid.enc_ppid),
530 &pckid.pce_id,
531 &pckid.cpu_svn,
532 pckid.pce_isvsvn,
533 Some(&pckid.qe_id),
534 );
535 input.hash(&mut hasher);
536
537 cache
538 .get_mut(&hasher.finish())
539 .expect("Can't find key in cache")
540 .to_owned()
541 };
542
543 assert_eq!(
544 pck.fmspc().unwrap(),
545 cached_pck
546 .clone()
547 .verify(&root_cas, None)
548 .unwrap()
549 .fmspc()
550 .unwrap()
551 );
552 assert_eq!(pck.ca_chain(), cached_pck.ca_chain());
553 }
554
555 let pck_from_service = client
557 .pckcert(
558 Some(&pckid.enc_ppid),
559 &pckid.pce_id,
560 &pckid.cpu_svn,
561 pckid.pce_isvsvn,
562 Some(&pckid.qe_id),
563 )
564 .unwrap();
565
566 assert_eq!(
567 pck.fmspc().unwrap(),
568 pck_from_service
569 .clone()
570 .verify(&root_cas, None)
571 .unwrap()
572 .fmspc()
573 .unwrap()
574 );
575 assert_eq!(pck.ca_chain(), pck_from_service.ca_chain());
576 }
577 }
578 }
579
580 #[test]
581 pub fn test_pckcerts_with_fallback() {
582 for api_version in [PcsVersion::V3, PcsVersion::V4] {
583 let client = make_client(api_version);
584
585 for pckid in PckID::parse_file(&PathBuf::from(PCKID_TEST_FILE).as_path())
586 .unwrap()
587 .iter()
588 {
589 let pckcerts = client.pckcerts_with_fallback(&pckid).unwrap();
590 println!("Found {} PCK certs.", pckcerts.as_pck_certs().len());
591
592 let tcb_info = client.tcbinfo(&pckcerts.fmspc().unwrap(), None).unwrap();
593 let tcb_data = tcb_info.data().unwrap();
594
595 let selected = pckcerts
596 .select_pck(&tcb_data, &pckid.cpu_svn, pckid.pce_isvsvn, pckid.pce_id)
597 .unwrap();
598
599 let pck = client
600 .pckcert(
601 Some(&pckid.enc_ppid),
602 &pckid.pce_id,
603 &pckid.cpu_svn,
604 pckid.pce_isvsvn,
605 Some(&pckid.qe_id),
606 )
607 .unwrap();
608
609 assert_eq!(
610 format!("{:?}", selected.sgx_extension().unwrap()),
611 format!("{:?}", pck.sgx_extension().unwrap())
612 );
613 }
614 }
615 }
616
617 #[test]
618 pub fn tcb_info() {
619 for api_version in [PcsVersion::V3, PcsVersion::V4] {
620 let client = make_client(api_version);
621
622 for pckid in PckID::parse_file(&PathBuf::from(PCKID_TEST_FILE).as_path())
623 .unwrap()
624 .iter()
625 {
626 let pckcerts = client.pckcerts_with_fallback(&pckid).unwrap();
627
628 assert!(client
629 .tcbinfo(&pckcerts.fmspc().unwrap(), None)
630 .and_then(|tcb| { Ok(tcb.store(OUTPUT_TEST_DIR).unwrap()) })
631 .is_ok());
632 }
633 }
634 }
635
636 #[test]
637 pub fn tcb_info_with_evaluation_data_number() {
638 let client = make_client(PcsVersion::V4);
639 for pckid in PckID::parse_file(&PathBuf::from(PCKID_TEST_FILE).as_path())
640 .unwrap()
641 .iter()
642 {
643 let pckcerts = client
644 .pckcerts_with_fallback(&pckid)
645 .unwrap();
646
647 let fmspc = pckcerts.fmspc().unwrap();
648
649 let evaluation_data_numbers = client
650 .tcb_evaluation_data_numbers()
651 .unwrap()
652 .evaluation_data_numbers()
653 .unwrap();
654
655 for number in evaluation_data_numbers.numbers() {
656 if number.number() < 17 {
663 continue;
664 }
665 let tcb = match client.tcbinfo(&fmspc, Some(number.number())) {
666 Ok(tcb) => tcb,
667 Err(super::Error::PCSError(status_code, _)) if status_code == super::StatusCode::Gone => continue,
671 res @Err(_) => res.unwrap(),
672 };
673 tcb.store(OUTPUT_TEST_DIR).unwrap();
674 }
675 }
676 }
677
678 #[test]
679 pub fn tcb_info_cached() {
680 for api_version in [PcsVersion::V3, PcsVersion::V4] {
681 let client = make_client(api_version);
682
683 for pckid in PckID::parse_file(&PathBuf::from(PCKID_TEST_FILE).as_path())
684 .unwrap()
685 .iter()
686 {
687 let pckcerts = client.pckcerts_with_fallback(&pckid).unwrap();
688 let fmspc = pckcerts.fmspc().unwrap();
689 let tcb_info = client.tcbinfo(&fmspc, None).unwrap();
690
691 {
693 let mut cache = client.tcbinfo_service.cache.lock().unwrap();
694
695 assert!(cache.len() > 0);
696
697 let (cached_tcb_info, _) = {
698 let mut hasher = DefaultHasher::new();
699 let input = client
700 .tcbinfo_service
701 .pcs_service()
702 .build_input(&fmspc, None);
703 input.hash(&mut hasher);
704
705 cache
706 .get_mut(&hasher.finish())
707 .expect("Can't find key in cache")
708 .to_owned()
709 };
710
711 assert_eq!(tcb_info, cached_tcb_info);
712 }
713
714 let tcb_info_from_service = client.tcbinfo(&fmspc, None).unwrap();
716
717 assert_eq!(tcb_info, tcb_info_from_service);
718 }
719 }
720 }
721
722 #[test]
723 pub fn pckcrl() {
724 for api_version in [PcsVersion::V3, PcsVersion::V4] {
725 let client = make_client(api_version);
726 assert!(client
727 .pckcrl(DcapArtifactIssuer::PCKProcessorCA)
728 .and_then(|crl| Ok(crl.write_to_file(OUTPUT_TEST_DIR).unwrap()))
729 .is_ok());
730 assert!(client
731 .pckcrl(DcapArtifactIssuer::PCKPlatformCA)
732 .and_then(|crl| Ok(crl.write_to_file(OUTPUT_TEST_DIR).unwrap()))
733 .is_ok());
734 }
735 }
736
737 #[test]
738 pub fn pckcrl_cached() {
739 for ca in [
740 DcapArtifactIssuer::PCKProcessorCA,
741 DcapArtifactIssuer::PCKPlatformCA,
742 ] {
743 for api_version in [PcsVersion::V3, PcsVersion::V4] {
744 let client = make_client(api_version);
745 let pckcrl = client.pckcrl(ca).unwrap();
746
747 {
749 let mut cache = client.pckcrl_service.cache.lock().unwrap();
750
751 assert!(cache.len() > 0);
752
753 let (cached_pckcrl, _) = {
754 let mut hasher = DefaultHasher::new();
755 let input = client.pckcrl_service.pcs_service().build_input(ca);
756 input.hash(&mut hasher);
757
758 cache
759 .get_mut(&hasher.finish())
760 .expect("Can't find key in cache")
761 .to_owned()
762 };
763
764 assert_eq!(pckcrl, cached_pckcrl);
765 }
766
767 let pckcrl_from_service = client.pckcrl(ca).unwrap();
769
770 assert_eq!(pckcrl, pckcrl_from_service);
771 }
772 }
773 }
774
775 #[test]
776 pub fn qe_identity() {
777 for api_version in [PcsVersion::V3, PcsVersion::V4] {
778 let client = make_client(api_version);
779 let qe_id = client.qe_identity(None);
780 assert!(qe_id.is_ok());
781 assert!(qe_id.unwrap().write_to_file(OUTPUT_TEST_DIR).is_ok());
782 }
783 }
784
785 #[test]
786 pub fn qe_identity_cached() {
787 for api_version in [PcsVersion::V3, PcsVersion::V4] {
788 let client = make_client(api_version);
789 let qe_id = client.qe_identity(None).unwrap();
790
791 {
793 let mut cache = client.qeid_service.cache.lock().unwrap();
794
795 assert!(cache.len() > 0);
796
797 let (cached_qeid, _) = {
798 let mut hasher = DefaultHasher::new();
799 let input = client.qeid_service.pcs_service().build_input(None);
800 input.hash(&mut hasher);
801
802 cache
803 .get_mut(&hasher.finish())
804 .expect("Can't find key in cache")
805 .to_owned()
806 };
807
808 assert_eq!(qe_id, cached_qeid);
809 }
810
811 let qeid_from_service = client.qe_identity(None).unwrap();
813
814 assert_eq!(qe_id, qeid_from_service);
815 }
816 }
817
818 #[test]
819 pub fn tcb_evaluation_data_numbers() {
820 let root_ca = include_bytes!("../../tests/data/root_SGX_CA_der.cert");
821 let root_cas = [&root_ca[..]];
822 let client = make_client(PcsVersion::V4);
823 let eval_numbers = client.tcb_evaluation_data_numbers().unwrap();
824
825 let eval_numbers2 = serde_json::ser::to_vec(&eval_numbers)
826 .and_then(|v| serde_json::from_slice::<RawTcbEvaluationDataNumbers>(&v))
827 .unwrap();
828 assert_eq!(eval_numbers, eval_numbers2);
829
830 let fmspc = Fmspc::try_from("90806f000000").unwrap();
831 let eval_numbers: TcbEvaluationDataNumbers =
832 eval_numbers.verify(&root_cas, Platform::SGX).unwrap();
833 for number in eval_numbers.numbers().map(|n| n.number()) {
834 if number < 17 {
841 continue;
842 }
843 let qe_identity = match client.qe_identity(Some(number)) {
844 Ok(id) => id,
845 Err(super::Error::PCSError(status_code, _)) if status_code == super::StatusCode::Gone => continue,
849 res @Err(_) => res.unwrap(),
850 };
851 let verified_qe_id = qe_identity
852 .verify(&root_cas, EnclaveIdentity::QE)
853 .unwrap();
854 assert_eq!(verified_qe_id.tcb_evaluation_data_number(), u64::from(number));
855
856 let tcb_info = client
857 .tcbinfo(&fmspc, Some(number))
858 .unwrap()
859 .verify(&root_cas, Platform::SGX, 2)
860 .unwrap();
861 assert_eq!(tcb_info.tcb_evaluation_data_number(), u64::from(number));
862 }
863 }
864}