dcap_artifact_retrieval/provisioning_client/
intel.rs

1/* Copyright (c) Fortanix, Inc.
2 *
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 */
7
8//! Interface to the Intel DCAP attestation API.
9//!
10//! Origins:
11//! - <https://api.portal.trustedservices.intel.com/provisioning-certification>
12//! - <https://download.01.org/intel-sgx/dcap-1.1/linux/docs/Intel_SGX_PCK_Certificate_CRL_Spec-1.1.pdf>
13
14use pcs::{
15    CpuSvn, DcapArtifactIssuer, EncPpid, Fmspc, PceId, PceIsvsvn, PckCert, PckCerts, PckCrl, QeId, QeIdentitySigned,
16    TcbInfo, RawTcbEvaluationDataNumbers, Unverified,
17};
18use rustc_serialize::hex::ToHex;
19use std::borrow::Cow;
20use std::time::Duration;
21
22use super::common::*;
23use super::{
24    Client, ClientBuilder, Fetcher, PckCertIn, PckCertService, PckCertsIn, PckCertsService,
25    PckCrlIn, PckCrlService, PcsVersion, ProvisioningServiceApi, QeIdIn, QeIdService, StatusCode,
26    TcbEvaluationDataNumbersIn, TcbEvaluationDataNumbersService, TcbInfoIn, TcbInfoService, WithApiVersion,
27};
28use crate::Error;
29
30pub(crate) const INTEL_BASE_URL: &'static str = "https://api.trustedservices.intel.com";
31const SUBSCRIPTION_KEY_HEADER: &'static str = "Ocp-Apim-Subscription-Key";
32
33pub struct IntelProvisioningClientBuilder {
34    api_key: Option<String>,
35    api_version: PcsVersion,
36    client_builder: ClientBuilder,
37}
38
39impl IntelProvisioningClientBuilder {
40    pub fn new(api_version: PcsVersion) -> Self {
41        Self {
42            api_version,
43            api_key: None,
44            client_builder: ClientBuilder::new(),
45        }
46    }
47
48    pub fn set_api_key(&mut self, api_key: String) -> &mut Self {
49        self.api_key = Some(api_key);
50        self
51    }
52
53    pub fn set_retry_timeout(mut self, retry_timeout: Duration) -> Self {
54        self.client_builder = self.client_builder.set_retry_timeout(retry_timeout);
55        self
56    }
57
58    pub fn build<F: for<'a> Fetcher<'a>>(self, fetcher: F) -> Client<F> {
59        let pck_certs = PckCertsApi::new(self.api_version.clone(), self.api_key.clone());
60        let pck_cert = PckCertApi::new(self.api_version.clone(), self.api_key.clone());
61        let pck_crl = PckCrlApi::new(self.api_version.clone());
62        let qeid = QeIdApi::new(self.api_version.clone());
63        let tcbinfo = TcbInfoApi::new(self.api_version.clone());
64        let evaluation_data_numbers = TcbEvaluationDataNumbersApi::new(INTEL_BASE_URL.into());
65        self.client_builder
66            .build(pck_certs, pck_cert, pck_crl, qeid, tcbinfo, evaluation_data_numbers, fetcher)
67    }
68}
69
70pub struct PckCertsApi {
71    api_key: Option<String>,
72    api_version: PcsVersion,
73}
74
75impl PckCertsApi {
76    pub(crate) fn new(api_version: PcsVersion, api_key: Option<String>) -> PckCertsApi {
77        PckCertsApi {
78            api_version,
79            api_key,
80        }
81    }
82}
83
84impl<'inp> PckCertsService<'inp> for PckCertsApi {
85    fn build_input(
86        &'inp self,
87        enc_ppid: &'inp EncPpid,
88        pce_id: PceId,
89    ) -> <Self as ProvisioningServiceApi<'inp>>::Input {
90        PckCertsIn {
91            enc_ppid,
92            pce_id,
93            api_key: &self.api_key,
94            api_version: self.api_version.clone(),
95        }
96    }
97}
98
99impl<'inp> PckCertService<'inp> for PckCertApi {
100    fn build_input(
101        &'inp self,
102        encrypted_ppid: Option<&'inp EncPpid>,
103        pce_id: &'inp PceId,
104        cpu_svn: &'inp CpuSvn,
105        pce_isvsvn: PceIsvsvn,
106        qe_id: Option<&'inp QeId>,
107    ) -> <Self as ProvisioningServiceApi<'inp>>::Input {
108        PckCertIn {
109            encrypted_ppid,
110            pce_id,
111            cpu_svn,
112            pce_isvsvn,
113            qe_id,
114            api_key: &self.api_key,
115            api_version: self.api_version,
116        }
117    }
118}
119
120/// Implementation of pckcerts
121/// <https://api.portal.trustedservices.intel.com/documentation#pcs-certificates-v4>
122impl<'inp> ProvisioningServiceApi<'inp> for PckCertsApi {
123    type Input = PckCertsIn<'inp>;
124    type Output = PckCerts;
125
126    fn build_request(&self, input: &Self::Input) -> Result<(String, Vec<(String, String)>), Error> {
127        let api_version = input.api_version as u8;
128        let encrypted_ppid = input.enc_ppid.to_hex();
129        let pce_id = input.pce_id.to_le_bytes().to_hex();
130        let url = format!(
131            "{}/sgx/certification/v{}/pckcerts?encrypted_ppid={}&pceid={}",
132            INTEL_BASE_URL, api_version, encrypted_ppid, pce_id,
133        );
134        let headers = if let Some(api_key) = &input.api_key {
135            vec![(SUBSCRIPTION_KEY_HEADER.to_owned(), api_key.to_string())]
136        } else {
137            Vec::new()
138        };
139        Ok((url, headers))
140    }
141
142    fn validate_response(&self, status_code: StatusCode) -> Result<(), Error> {
143        match status_code {
144            StatusCode::Ok => Ok(()),
145            StatusCode::BadRequest => Err(Error::PCSError(status_code, "Invalid parameter")),
146            StatusCode::Unauthorized => Err(Error::PCSError(
147                status_code,
148                "Failed to authenticate or authorize the request (check your PCS key)",
149            )),
150            StatusCode::NotFound => Err(Error::PCSError(
151                status_code,
152                "Cannot find the requested certificate",
153            )),
154            StatusCode::TooManyRequests => Err(Error::PCSError(status_code, "Too many requests")),
155            StatusCode::InternalServerError => Err(Error::PCSError(
156                status_code,
157                "PCS suffered from an internal server error",
158            )),
159            StatusCode::ServiceUnavailable => Err(Error::PCSError(
160                status_code,
161                "PCS is temporarily unavailable",
162            )),
163            _ => Err(Error::PCSError(
164                status_code,
165                "Unexpected response from PCS server",
166            )),
167        }
168    }
169
170    fn parse_response(
171        &self,
172        response_body: String,
173        response_headers: Vec<(String, String)>,
174        _api_version: PcsVersion,
175    ) -> Result<Self::Output, Error> {
176        let ca_chain = parse_issuer_header(&response_headers, PCK_CERTIFICATE_ISSUER_CHAIN_HEADER)?;
177        PckCerts::parse(&response_body, ca_chain).map_err(|e| Error::OfflineAttestationError(e))
178    }
179}
180
181pub struct PckCertApi {
182    api_key: Option<String>,
183    api_version: PcsVersion,
184}
185
186impl PckCertApi {
187    pub(crate) fn new(api_version: PcsVersion, api_key: Option<String>) -> PckCertApi {
188        PckCertApi {
189            api_version,
190            api_key,
191        }
192    }
193}
194
195/// Implementation of pckcert
196/// <https://api.portal.trustedservices.intel.com/content/documentation.html#pcs-certificate-v4>
197impl<'inp> ProvisioningServiceApi<'inp> for PckCertApi {
198    type Input = PckCertIn<'inp>;
199    type Output = PckCert<Unverified>;
200
201    fn build_request(&self, input: &Self::Input) -> Result<(String, Vec<(String, String)>), Error> {
202        let api_version = input.api_version as u8;
203        let encrypted_ppid = input
204            .encrypted_ppid
205            .ok_or(Error::NoEncPPID)
206            .map(|e_ppid| e_ppid.to_hex())?;
207        let cpusvn = input.cpu_svn.to_hex();
208        let pce_isvsvn = input.pce_isvsvn.to_le_bytes().to_hex();
209        let pce_id = input.pce_id.to_le_bytes().to_hex();
210        let url = format!(
211            "{}/sgx/certification/v{}/pckcert?encrypted_ppid={}&cpusvn={}&pcesvn={}&pceid={}",
212            INTEL_BASE_URL, api_version, encrypted_ppid, cpusvn, pce_isvsvn, pce_id,
213        );
214        let headers = if let Some(api_key) = input.api_key {
215            vec![(SUBSCRIPTION_KEY_HEADER.to_owned(), api_key.to_string())]
216        } else {
217            Vec::new()
218        };
219        Ok((url, headers))
220    }
221
222    fn validate_response(&self, status_code: StatusCode) -> Result<(), Error> {
223        match status_code {
224            StatusCode::Ok => Ok(()),
225            StatusCode::BadRequest => Err(Error::PCSError(status_code, "Invalid parameter")),
226            StatusCode::Unauthorized => Err(Error::PCSError(
227                status_code,
228                "Failed to authenticate or authorize the request (check your PCS key)",
229            )),
230            StatusCode::NotFound => Err(Error::PCSError(
231                status_code,
232                "Cannot find the requested certificate",
233            )),
234            StatusCode::TooManyRequests => Err(Error::PCSError(status_code, "Too many requests")),
235            StatusCode::InternalServerError => Err(Error::PCSError(
236                status_code,
237                "PCS suffered from an internal server error",
238            )),
239            StatusCode::ServiceUnavailable => Err(Error::PCSError(
240                status_code,
241                "PCS is temporarily unavailable",
242            )),
243            _ => Err(Error::PCSError(
244                status_code,
245                "Unexpected response from PCS server",
246            )),
247        }
248    }
249
250    fn parse_response(
251        &self,
252        response_body: String,
253        response_headers: Vec<(String, String)>,
254        _api_version: PcsVersion,
255    ) -> Result<Self::Output, Error> {
256        let ca_chain = parse_issuer_header(&response_headers, PCK_CERTIFICATE_ISSUER_CHAIN_HEADER)?;
257        Ok(PckCert::new(response_body, ca_chain))
258    }
259}
260
261pub struct PckCrlApi {
262    api_version: PcsVersion,
263}
264
265impl PckCrlApi {
266    pub fn new(api_version: PcsVersion) -> Self {
267        PckCrlApi { api_version }
268    }
269}
270
271impl<'inp> PckCrlService<'inp> for PckCrlApi {
272    fn build_input(&'inp self, ca: DcapArtifactIssuer) -> <Self as ProvisioningServiceApi<'inp>>::Input {
273        PckCrlIn {
274            api_version: self.api_version.clone(),
275            ca,
276        }
277    }
278}
279
280/// Implementation of pckcrl
281/// See: <https://api.portal.trustedservices.intel.com/documentation#pcs-revocation-v4>
282impl<'inp> ProvisioningServiceApi<'inp> for PckCrlApi {
283    type Input = PckCrlIn;
284    type Output = PckCrl<Unverified>;
285
286    fn build_request(&self, input: &Self::Input) -> Result<(String, Vec<(String, String)>), Error> {
287        let ca = match input.ca {
288            DcapArtifactIssuer::PCKProcessorCA => "processor",
289            DcapArtifactIssuer::PCKPlatformCA => "platform",
290            DcapArtifactIssuer::SGXRootCA => {
291                return Err(Error::PCSError(StatusCode::BadRequest, "Invalid ca parameter"));
292            },
293        };
294        let url = format!(
295            "{}/sgx/certification/v{}/pckcrl?ca={}&encoding=pem",
296            INTEL_BASE_URL, input.api_version as u8, ca,
297        );
298        Ok((url, Vec::new()))
299    }
300
301    fn validate_response(&self, status_code: StatusCode) -> Result<(), Error> {
302        match &status_code {
303            StatusCode::Ok => Ok(()),
304            StatusCode::BadRequest => Err(Error::PCSError(status_code, "Invalid parameter")),
305            StatusCode::Unauthorized => Err(Error::PCSError(
306                status_code,
307                "Failed to authenticate or authorize the request (check your PCS key)",
308            )),
309            StatusCode::InternalServerError => Err(Error::PCSError(
310                status_code,
311                "PCS suffered from an internal server error",
312            )),
313            StatusCode::ServiceUnavailable => Err(Error::PCSError(
314                status_code,
315                "PCS is temporarily unavailable",
316            )),
317            __ => Err(Error::PCSError(
318                status_code,
319                "Unexpected response from PCS server",
320            )),
321        }
322    }
323
324    fn parse_response(
325        &self,
326        response_body: String,
327        response_headers: Vec<(String, String)>,
328        _api_version: PcsVersion,
329    ) -> Result<Self::Output, Error> {
330        let ca_chain = parse_issuer_header(&response_headers, PCK_CRL_ISSUER_CHAIN_HEADER)?;
331        let crl = PckCrl::new(response_body, ca_chain)?;
332        Ok(crl)
333    }
334}
335
336pub struct TcbInfoApi {
337    api_version: PcsVersion,
338}
339
340impl TcbInfoApi {
341    pub fn new(api_version: PcsVersion) -> Self {
342        TcbInfoApi { api_version }
343    }
344}
345
346impl<'inp> TcbInfoService<'inp> for TcbInfoApi {
347    fn build_input(
348        &'inp self,
349        fmspc: &'inp Fmspc,
350        tcb_evaluation_data_number: Option<u16>,
351    ) -> <Self as ProvisioningServiceApi<'inp>>::Input {
352        TcbInfoIn {
353            api_version: self.api_version.clone(),
354            fmspc,
355            tcb_evaluation_data_number,
356        }
357    }
358}
359
360// Implementation of Get TCB Info
361// <https://api.portal.trustedservices.intel.com/documentation#pcs-tcb-info-v4>>
362impl<'inp> ProvisioningServiceApi<'inp> for TcbInfoApi {
363    type Input = TcbInfoIn<'inp>;
364    type Output = TcbInfo;
365
366    fn build_request(&self, input: &Self::Input) -> Result<(String, Vec<(String, String)>), Error> {
367        let api_version = input.api_version as u8;
368        let fmspc = input.fmspc.as_bytes().to_hex();
369        let url = if let Some(evaluation_data_number) = input.tcb_evaluation_data_number {
370            format!(
371                "{}/sgx/certification/v{}/tcb?fmspc={}&tcbEvaluationDataNumber={}",
372                INTEL_BASE_URL, api_version, fmspc, evaluation_data_number)
373        } else {
374            format!(
375                "{}/sgx/certification/v{}/tcb?fmspc={}&update=early",
376                INTEL_BASE_URL, api_version, fmspc,
377            )
378        };
379        Ok((url, Vec::new()))
380    }
381
382    fn validate_response(&self, status_code: StatusCode) -> Result<(), Error> {
383        match &status_code {
384            StatusCode::Ok => Ok(()),
385            StatusCode::BadRequest => Err(Error::PCSError(status_code, "Invalid parameter")),
386            StatusCode::Unauthorized => Err(Error::PCSError(
387                status_code,
388                "Failed to authenticate or authorize the request (check your PCS key)",
389            )),
390            StatusCode::NotFound => {
391                Err(Error::PCSError(status_code, "TCB info cannot be found"))
392            }
393            StatusCode::Gone => {
394                Err(Error::PCSError(status_code, "TCB info no longer available"))
395            }
396            StatusCode::InternalServerError => Err(Error::PCSError(
397                status_code,
398                "PCS suffered from an internal server error",
399            )),
400            StatusCode::ServiceUnavailable => Err(Error::PCSError(
401                status_code,
402                "PCS is temporarily unavailable",
403            )),
404            __ => Err(Error::PCSError(
405                status_code,
406                "Unexpected response from PCS server",
407            )),
408        }
409    }
410
411    fn parse_response(
412        &self,
413        response_body: String,
414        response_headers: Vec<(String, String)>,
415        api_version: PcsVersion,
416    ) -> Result<Self::Output, Error> {
417        let key = match api_version {
418            PcsVersion::V3 => TCB_INFO_ISSUER_CHAIN_HEADER_V3,
419            PcsVersion::V4 => TCB_INFO_ISSUER_CHAIN_HEADER_V4,
420        };
421        let ca_chain = parse_issuer_header(&response_headers, key)?;
422        let tcb_info = TcbInfo::parse(&response_body, ca_chain)?;
423        Ok(tcb_info)
424    }
425}
426
427pub struct QeIdApi {
428    api_version: PcsVersion,
429}
430
431impl QeIdApi {
432    pub fn new(api_version: PcsVersion) -> Self {
433        QeIdApi { api_version }
434    }
435}
436
437impl<'inp> QeIdService<'inp> for QeIdApi {
438    fn build_input(&'inp self, tcb_evaluation_data_number: Option<u16>) -> <Self as ProvisioningServiceApi<'inp>>::Input {
439        QeIdIn {
440            api_version: self.api_version.clone(),
441            tcb_evaluation_data_number,
442        }
443    }
444}
445
446/// Implementation of qe/identity
447/// <https://api.portal.trustedservices.intel.com/documentation#pcs-certificates-v://api.portal.trustedservices.intel.com/documentation#pcs-qe-identity-v4>
448impl<'inp> ProvisioningServiceApi<'inp> for QeIdApi {
449    type Input = QeIdIn;
450    type Output = QeIdentitySigned;
451
452    fn build_request(&self, input: &Self::Input) -> Result<(String, Vec<(String, String)>), Error> {
453        let api_version = input.api_version as u8;
454        let url = if let Some(tcb_evaluation_data_number) = input.tcb_evaluation_data_number {
455            format!(
456                "{}/sgx/certification/v{}/qe/identity?tcbEvaluationDataNumber={}",
457                INTEL_BASE_URL, api_version, tcb_evaluation_data_number
458            )
459        } else {
460            format!(
461                "{}/sgx/certification/v{}/qe/identity?update=early",
462                INTEL_BASE_URL, api_version,
463            )
464        };
465        Ok((url, Vec::new()))
466    }
467
468    fn validate_response(&self, status_code: StatusCode) -> Result<(), Error> {
469        match &status_code {
470            StatusCode::Ok => Ok(()),
471            StatusCode::BadRequest => Err(Error::PCSError(status_code, "Invalid parameter")),
472            StatusCode::Unauthorized => Err(Error::PCSError(
473                status_code,
474                "Failed to authenticate or authorize the request (check your PCS key)",
475            )),
476            StatusCode::NotFound => {
477                Err(Error::PCSError(status_code, "QE identity Cannot be found"))
478            }
479            StatusCode::InternalServerError => Err(Error::PCSError(
480                status_code,
481                "PCS suffered from an internal server error",
482            )),
483            StatusCode::ServiceUnavailable => Err(Error::PCSError(
484                status_code,
485                "PCS is temporarily unavailable",
486            )),
487            __ => Err(Error::PCSError(
488                status_code,
489                "Unexpected response from PCS server",
490            )),
491        }
492    }
493
494    fn parse_response(
495        &self,
496        response_body: String,
497        response_headers: Vec<(String, String)>,
498        _api_version: PcsVersion,
499    ) -> Result<Self::Output, Error> {
500        let ca_chain = parse_issuer_header(&response_headers, ENCLAVE_ID_ISSUER_CHAIN_HEADER)?;
501        let id = QeIdentitySigned::parse(&response_body, ca_chain)?;
502        Ok(id)
503    }
504}
505
506pub struct TcbEvaluationDataNumbersApi {
507    base_url: Cow<'static, str>,
508}
509
510impl TcbEvaluationDataNumbersApi {
511    pub fn new(base_url: Cow<'static, str>) -> Self {
512        TcbEvaluationDataNumbersApi {
513            base_url,
514        }
515    }
516}
517
518impl<'inp> TcbEvaluationDataNumbersService<'inp> for TcbEvaluationDataNumbersApi {
519    fn build_input(&self)
520        -> <Self as ProvisioningServiceApi<'inp>>::Input {
521        TcbEvaluationDataNumbersIn
522    }
523}
524
525/// Implementation of TCB Evaluation Data Numbers endpoint
526/// <https://api.portal.trustedservices.intel.com/content/documentation.html#pcs-retrieve-tcbevalnumbers-v4>
527impl<'inp> ProvisioningServiceApi<'inp> for TcbEvaluationDataNumbersApi {
528    type Input = TcbEvaluationDataNumbersIn;
529    type Output = RawTcbEvaluationDataNumbers;
530
531    fn build_request(&self, input: &Self::Input) -> Result<(String, Vec<(String, String)>), Error> {
532        let url = format!(
533            "{}/sgx/certification/v{}/tcbevaluationdatanumbers",
534            self.base_url, input.api_version() as u8,
535        );
536        Ok((url, Vec::new()))
537    }
538
539    fn validate_response(&self, status_code: StatusCode) -> Result<(), Error> {
540        match &status_code {
541            StatusCode::Ok => Ok(()),
542            StatusCode::InternalServerError => Err(Error::PCSError(
543                status_code,
544                "PCS suffered from an internal server error",
545            )),
546            StatusCode::ServiceUnavailable => Err(Error::PCSError(
547                status_code,
548                "PCS is temporarily unavailable",
549            )),
550            __ => Err(Error::PCSError(
551                status_code,
552                "Unexpected response from PCS server",
553            )),
554        }
555    }
556
557    fn parse_response(
558        &self,
559        response_body: String,
560        response_headers: Vec<(String, String)>,
561        _api_version: PcsVersion,
562    ) -> Result<Self::Output, Error> {
563        let ca_chain = parse_issuer_header(&response_headers, TCB_EVALUATION_DATA_NUMBERS_ISSUER_CHAIN)?;
564        RawTcbEvaluationDataNumbers::parse(&response_body, ca_chain).map_err(|e| e.into())
565    }
566}
567
568#[cfg(all(test, feature = "reqwest"))]
569mod tests {
570    use std::convert::TryFrom;
571    use std::hash::Hash;
572    use std::hash::Hasher;
573    use std::path::PathBuf;
574    use std::time::Duration;
575
576    use pcs::{
577        DcapArtifactIssuer, EnclaveIdentity, Fmspc, PckID, Platform, RawTcbEvaluationDataNumbers,
578        TcbEvaluationDataNumbers,
579    };
580
581    use crate::provisioning_client::{
582        test_helpers, IntelProvisioningClientBuilder, PcsVersion, ProvisioningClient,
583    };
584    use crate::reqwest_client;
585    use std::hash::DefaultHasher;
586
587    const PCKID_TEST_FILE: &str = "./tests/data/pckid_retrieval.csv";
588    const OUTPUT_TEST_DIR: &str = "./tests/data/";
589    const TIME_RETRY_TIMEOUT: Duration = Duration::from_secs(180);
590
591    fn pcs_api_key() -> Option<String> {
592        let api_key_option = std::env::var("PCS_API_KEY").ok();
593        if let Some(api_key) = api_key_option.as_ref() {
594        assert!(!api_key.is_empty(), "Empty string in PCS_API_KEY");
595        }
596        api_key_option
597    }
598
599    #[test]
600    pub fn pcks() {
601        for api_version in [PcsVersion::V3, PcsVersion::V4] {
602            let mut intel_builder = IntelProvisioningClientBuilder::new(api_version)
603                .set_retry_timeout(TIME_RETRY_TIMEOUT);
604            if api_version == PcsVersion::V3 {
605                if let Some(pcs_api_key) = pcs_api_key() {
606                    intel_builder.set_api_key(pcs_api_key);
607                } else {
608                    // Intel SGX PCS version 3 is scheduled to end of life not later than October 31, 2025.
609                    // So we no longer force to test it.
610                    continue;
611                }
612            }
613            let client = intel_builder.build(reqwest_client());
614
615            for pckid in PckID::parse_file(&PathBuf::from(PCKID_TEST_FILE).as_path())
616                .unwrap()
617                .iter()
618            {
619                let pcks = client
620                    .pckcerts(&pckid.enc_ppid, pckid.pce_id.clone())
621                    .unwrap();
622                assert_eq!(
623                    test_helpers::get_cert_subject(pcks.ca_chain().last().unwrap()),
624                    "Intel SGX Root CA"
625                );
626                pcks.fmspc().unwrap();
627                pcks.store(OUTPUT_TEST_DIR, pckid.qe_id.as_slice()).unwrap();
628            }
629        }
630    }
631
632    #[test]
633    pub fn pcks_cached() {
634        for api_version in [PcsVersion::V3, PcsVersion::V4] {
635            let mut intel_builder = IntelProvisioningClientBuilder::new(api_version)
636                .set_retry_timeout(TIME_RETRY_TIMEOUT);
637            if api_version == PcsVersion::V3 {
638                if let Some(pcs_api_key) = pcs_api_key() {
639                    intel_builder.set_api_key(pcs_api_key);
640                } else {
641                    // Intel SGX PCS version 3 is scheduled to end of life not later than October 31, 2025.
642                    // So we no longer force to test it.
643                    continue;
644                }
645            }
646            let client = intel_builder.build(reqwest_client());
647
648            for pckid in PckID::parse_file(&PathBuf::from(PCKID_TEST_FILE).as_path())
649                .unwrap()
650                .iter()
651            {
652                let pcks = client
653                    .pckcerts(&pckid.enc_ppid, pckid.pce_id.clone())
654                    .unwrap();
655
656                // The cache should be populated after initial service call
657                {
658                    let mut cache = client.pckcerts_service.cache.lock().unwrap();
659
660                    assert!(cache.len() > 0);
661
662                    let (cached_pcks, _) = {
663                        let mut hasher = DefaultHasher::new();
664                        let input = client
665                            .pckcerts_service
666                            .pcs_service()
667                            .build_input(&pckid.enc_ppid, pckid.pce_id.clone());
668                        input.hash(&mut hasher);
669
670                        cache
671                            .get_mut(&hasher.finish())
672                            .expect("Can't find key in cache")
673                            .to_owned()
674                    };
675
676                    assert_eq!(pcks.fmspc().unwrap(), cached_pcks.fmspc().unwrap());
677                    assert_eq!(pcks, cached_pcks);
678                }
679
680                // Second service call should return value from cache
681                let pcks_from_service = client
682                    .pckcerts(&pckid.enc_ppid, pckid.pce_id.clone())
683                    .unwrap();
684
685                assert_eq!(pcks, pcks_from_service);
686                assert_eq!(pcks.fmspc().unwrap(), pcks_from_service.fmspc().unwrap());
687            }
688        }
689    }
690
691    #[test]
692    pub fn pck() {
693        for api_version in [PcsVersion::V3, PcsVersion::V4] {
694            let mut intel_builder = IntelProvisioningClientBuilder::new(api_version)
695                .set_retry_timeout(TIME_RETRY_TIMEOUT);
696            if api_version == PcsVersion::V3 {
697                if let Some(pcs_api_key) = pcs_api_key() {
698                    intel_builder.set_api_key(pcs_api_key);
699                } else {
700                    // Intel SGX PCS version 3 is scheduled to end of life not later than October 31, 2025.
701                    // So we no longer force to test it.
702                    continue;
703                }
704            }
705            let client = intel_builder.build(reqwest_client());
706            for pckid in PckID::parse_file(&PathBuf::from(PCKID_TEST_FILE).as_path())
707                .unwrap()
708                .iter()
709            {
710                let pck = client
711                    .pckcert(
712                        Some(&pckid.enc_ppid),
713                        &pckid.pce_id,
714                        &pckid.cpu_svn,
715                        pckid.pce_isvsvn,
716                        None,
717                    )
718                    .unwrap();
719                assert_eq!(
720                    test_helpers::get_cert_subject(pck.ca_chain().last().unwrap()),
721                    "Intel SGX Root CA"
722                );
723            }
724        }
725    }
726
727    #[test]
728    pub fn pck_cached() {
729        for api_version in [PcsVersion::V3, PcsVersion::V4] {
730            let root_ca = include_bytes!("../../tests/data/root_SGX_CA_der.cert");
731            let root_cas = [&root_ca[..]];
732            let mut intel_builder = IntelProvisioningClientBuilder::new(api_version)
733                .set_retry_timeout(TIME_RETRY_TIMEOUT);
734            if api_version == PcsVersion::V3 {
735                if let Some(pcs_api_key) = pcs_api_key() {
736                    intel_builder.set_api_key(pcs_api_key);
737                } else {
738                    // Intel SGX PCS version 3 is scheduled to end of life not later than October 31, 2025.
739                    // So we no longer force to test it.
740                    continue;
741                }
742            }
743            let client = intel_builder.build(reqwest_client());
744            let crl_processor = client
745                .pckcrl(DcapArtifactIssuer::PCKProcessorCA)
746                .unwrap()
747                .crl_as_pem()
748                .to_owned();
749            let crl_platform = client
750                .pckcrl(DcapArtifactIssuer::PCKPlatformCA)
751                .unwrap()
752                .crl_as_pem()
753                .to_owned();
754            for pckid in PckID::parse_file(&PathBuf::from(PCKID_TEST_FILE).as_path())
755                .unwrap()
756                .iter()
757            {
758                let pck = client
759                    .pckcert(
760                        Some(&pckid.enc_ppid),
761                        &pckid.pce_id,
762                        &pckid.cpu_svn,
763                        pckid.pce_isvsvn,
764                        None,
765                    )
766                    .unwrap();
767                let pck = pck
768                    .clone()
769                    .verify(&root_cas, Some(&crl_processor))
770                    .or(pck.clone().verify(&root_cas, Some(&crl_platform)))
771                    .unwrap();
772
773                // The cache should be populated after initial service call
774                {
775                    let mut cache = client.pckcert_service.cache.lock().unwrap();
776
777                    assert!(cache.len() > 0);
778
779                    let (cached_pck, _) = {
780                        let mut hasher = DefaultHasher::new();
781                        let input = client.pckcert_service.pcs_service().build_input(
782                            Some(&pckid.enc_ppid),
783                            &pckid.pce_id,
784                            &pckid.cpu_svn,
785                            pckid.pce_isvsvn,
786                            None,
787                        );
788                        input.hash(&mut hasher);
789
790                        cache
791                            .get_mut(&hasher.finish())
792                            .expect("Can't find key in cache")
793                            .to_owned()
794                    };
795
796                    assert_eq!(
797                        pck.fmspc().unwrap(),
798                        cached_pck
799                            .clone()
800                            .verify(&root_cas, None)
801                            .unwrap()
802                            .fmspc()
803                            .unwrap()
804                    );
805                    assert_eq!(pck.ca_chain(), cached_pck.ca_chain());
806                }
807
808                // Second service call should return value from cache
809                let pck_from_service = client
810                    .pckcert(
811                        Some(&pckid.enc_ppid),
812                        &pckid.pce_id,
813                        &pckid.cpu_svn,
814                        pckid.pce_isvsvn,
815                        None,
816                    )
817                    .unwrap();
818
819                assert_eq!(
820                    pck.fmspc().unwrap(),
821                    pck_from_service
822                        .clone()
823                        .verify(&root_cas, None)
824                        .unwrap()
825                        .fmspc()
826                        .unwrap()
827                );
828                assert_eq!(pck.ca_chain(), pck_from_service.ca_chain());
829            }
830        }
831    }
832
833    #[test]
834    pub fn tcb_info() {
835        for api_version in [PcsVersion::V3, PcsVersion::V4] {
836            let mut intel_builder = IntelProvisioningClientBuilder::new(api_version)
837                .set_retry_timeout(TIME_RETRY_TIMEOUT);
838            if api_version == PcsVersion::V3 {
839                if let Some(pcs_api_key) = pcs_api_key() {
840                    intel_builder.set_api_key(pcs_api_key);
841                } else {
842                    // Intel SGX PCS version 3 is scheduled to end of life not later than October 31, 2025.
843                    // So we no longer force to test it.
844                    continue;
845                }
846            }
847            let client = intel_builder.build(reqwest_client());
848            for pckid in PckID::parse_file(&PathBuf::from(PCKID_TEST_FILE).as_path())
849                .unwrap()
850                .iter()
851            {
852                let pckcerts = client
853                    .pckcerts(&pckid.enc_ppid, pckid.pce_id.clone())
854                    .unwrap();
855                assert!(client
856                    .tcbinfo(&pckcerts.fmspc().unwrap(), None)
857                    .and_then(|tcb| { Ok(tcb.store(OUTPUT_TEST_DIR).unwrap()) })
858                    .is_ok());
859            }
860        }
861    }
862
863    #[test]
864    pub fn tcb_info_with_evaluation_data_number() {
865        let intel_builder = IntelProvisioningClientBuilder::new(PcsVersion::V4)
866            .set_retry_timeout(TIME_RETRY_TIMEOUT);
867        let client = intel_builder.build(reqwest_client());
868        for pckid in PckID::parse_file(&PathBuf::from(PCKID_TEST_FILE).as_path())
869            .unwrap()
870            .iter()
871        {
872            let pckcerts = client
873                .pckcerts(&pckid.enc_ppid, pckid.pce_id.clone())
874                .unwrap();
875            let fmspc = pckcerts.fmspc().unwrap();
876
877            let evaluation_data_numbers = client
878                .tcb_evaluation_data_numbers()
879                .unwrap()
880                .evaluation_data_numbers()
881                .unwrap();
882
883            for number in evaluation_data_numbers.numbers() {
884                let tcb = match client.tcbinfo(&fmspc, Some(number.number())) {
885                    Ok(tcb) => tcb,
886                    // API query with update="standard" will return QE Identity with TCB Evaluation Data Number M.
887                    // A 410 Gone response is returned when the inputted TCB Evaluation Data Number is < M,
888                    // so we ignore these TCB Evaluation Data Numbers.
889                    Err(super::Error::PCSError(status_code, _)) if status_code == super::StatusCode::Gone => continue,
890                    res @Err(_) => res.unwrap(),
891                };
892                tcb.store(OUTPUT_TEST_DIR).unwrap();
893            }
894        }
895    }
896
897    #[test]
898    pub fn tcb_info_cached() {
899        for api_version in [PcsVersion::V3, PcsVersion::V4] {
900            let mut intel_builder = IntelProvisioningClientBuilder::new(api_version)
901                .set_retry_timeout(TIME_RETRY_TIMEOUT);
902            if api_version == PcsVersion::V3 {
903                if let Some(pcs_api_key) = pcs_api_key() {
904                    intel_builder.set_api_key(pcs_api_key);
905                } else {
906                    // Intel SGX PCS version 3 is scheduled to end of life not later than October 31, 2025.
907                    // So we no longer force to test it.
908                    continue;
909                }
910            }
911            let client = intel_builder.build(reqwest_client());
912            for pckid in PckID::parse_file(&PathBuf::from(PCKID_TEST_FILE).as_path())
913                .unwrap()
914                .iter()
915            {
916                let pckcerts = client
917                    .pckcerts(&pckid.enc_ppid, pckid.pce_id.clone())
918                    .unwrap();
919                let fmspc = pckcerts.fmspc().unwrap();
920                let tcb_info = client.tcbinfo(&fmspc, None).unwrap();
921
922                // The cache should be populated after initial service call
923                {
924                    let mut cache = client.tcbinfo_service.cache.lock().unwrap();
925
926                    assert!(cache.len() > 0);
927
928                    let (cached_tcb_info, _) = {
929                        let mut hasher = DefaultHasher::new();
930                        let input = client
931                            .tcbinfo_service
932                            .pcs_service()
933                            .build_input(&fmspc, None);
934                        input.hash(&mut hasher);
935
936                        cache
937                            .get_mut(&hasher.finish())
938                            .expect("Can't find key in cache")
939                            .to_owned()
940                    };
941
942                    assert_eq!(tcb_info, cached_tcb_info);
943                }
944
945                // Second service call should return value from cache
946                let tcb_info_from_service = client.tcbinfo(&fmspc, None).unwrap();
947
948                assert_eq!(tcb_info, tcb_info_from_service);
949            }
950        }
951    }
952
953    #[test]
954    pub fn pckcrl() {
955        for ca in [
956            DcapArtifactIssuer::PCKProcessorCA,
957            DcapArtifactIssuer::PCKPlatformCA,
958        ] {
959            for api_version in [PcsVersion::V3, PcsVersion::V4] {
960                let mut intel_builder = IntelProvisioningClientBuilder::new(api_version)
961                    .set_retry_timeout(TIME_RETRY_TIMEOUT);
962                if api_version == PcsVersion::V3 {
963                    if let Some(pcs_api_key) = pcs_api_key() {
964                        intel_builder.set_api_key(pcs_api_key);
965                    } else {
966                        // Intel SGX PCS version 3 is scheduled to end of life not later than October 31, 2025.
967                        // So we no longer force to test it.
968                        continue;
969                    }
970                }
971                let client = intel_builder.build(reqwest_client());
972                assert!(client
973                    .pckcrl(ca)
974                    .and_then(|crl| { Ok(crl.write_to_file(OUTPUT_TEST_DIR).unwrap()) })
975                    .is_ok());
976            }
977        }
978    }
979
980    #[test]
981    pub fn pckcrl_cached() {
982        for ca in [
983            DcapArtifactIssuer::PCKProcessorCA,
984            DcapArtifactIssuer::PCKPlatformCA,
985        ] {
986            for api_version in [PcsVersion::V3, PcsVersion::V4] {
987                let mut intel_builder = IntelProvisioningClientBuilder::new(api_version)
988                    .set_retry_timeout(TIME_RETRY_TIMEOUT);
989                if api_version == PcsVersion::V3 {
990                    if let Some(pcs_api_key) = pcs_api_key() {
991                        intel_builder.set_api_key(pcs_api_key);
992                    } else {
993                        // Intel SGX PCS version 3 is scheduled to end of life not later than October 31, 2025.
994                        // So we no longer force to test it.
995                        continue;
996                    }
997                }
998                let client = intel_builder.build(reqwest_client());
999                let pckcrl = client.pckcrl(ca).unwrap();
1000
1001                // The cache should be populated after initial service call
1002                {
1003                    let mut cache = client.pckcrl_service.cache.lock().unwrap();
1004
1005                    assert!(cache.len() > 0);
1006
1007                    let (cached_pckcrl, _) = {
1008                        let mut hasher = DefaultHasher::new();
1009                        let input = client.pckcrl_service.pcs_service().build_input(ca);
1010                        input.hash(&mut hasher);
1011
1012                        cache
1013                            .get_mut(&hasher.finish())
1014                            .expect("Can't find key in cache")
1015                            .to_owned()
1016                    };
1017
1018                    assert_eq!(pckcrl, cached_pckcrl);
1019                }
1020
1021                // Second service call should return value from cache
1022                let pckcrl_from_service = client.pckcrl(ca).unwrap();
1023
1024                assert_eq!(pckcrl, pckcrl_from_service);
1025            }
1026        }
1027    }
1028
1029    #[test]
1030    pub fn qe_identity() {
1031        for api_version in [PcsVersion::V3, PcsVersion::V4] {
1032            let mut intel_builder = IntelProvisioningClientBuilder::new(api_version)
1033                .set_retry_timeout(TIME_RETRY_TIMEOUT);
1034            if api_version == PcsVersion::V3 {
1035                if let Some(pcs_api_key) = pcs_api_key() {
1036                    intel_builder.set_api_key(pcs_api_key);
1037                } else {
1038                    // Intel SGX PCS version 3 is scheduled to end of life not later than October 31, 2025.
1039                    // So we no longer force to test it.
1040                    continue;
1041                }
1042            }
1043            let client = intel_builder.build(reqwest_client());
1044            let qe_id = client.qe_identity(None);
1045            assert!(qe_id.is_ok());
1046            assert!(qe_id.unwrap().write_to_file(OUTPUT_TEST_DIR).is_ok());
1047        }
1048    }
1049
1050    #[test]
1051    pub fn qe_identity_cached() {
1052        for api_version in [PcsVersion::V3, PcsVersion::V4] {
1053            let mut intel_builder = IntelProvisioningClientBuilder::new(api_version)
1054                .set_retry_timeout(TIME_RETRY_TIMEOUT);
1055            if api_version == PcsVersion::V3 {
1056                if let Some(pcs_api_key) = pcs_api_key() {
1057                    intel_builder.set_api_key(pcs_api_key);
1058                } else {
1059                    // Intel SGX PCS version 3 is scheduled to end of life not later than October 31, 2025.
1060                    // So we no longer force to test it.
1061                    continue;
1062                }
1063            }
1064            let client = intel_builder.build(reqwest_client());
1065            let qe_id = client.qe_identity(None).unwrap();
1066
1067            // The cache should be populated after initial service call
1068            {
1069                let mut cache = client.qeid_service.cache.lock().unwrap();
1070
1071                assert!(cache.len() > 0);
1072
1073                let (cached_qeid, _) = {
1074                    let mut hasher = DefaultHasher::new();
1075                    let input = client.qeid_service.pcs_service().build_input(None);
1076                    input.hash(&mut hasher);
1077
1078                    cache
1079                        .get_mut(&hasher.finish())
1080                        .expect("Can't find key in cache")
1081                        .to_owned()
1082                };
1083
1084                assert_eq!(qe_id, cached_qeid);
1085            }
1086
1087            // Second service call should return value from cache
1088            let qeid_from_service = client.qe_identity(None).unwrap();
1089
1090            assert_eq!(qe_id, qeid_from_service);
1091        }
1092    }
1093
1094    #[test]
1095    pub fn tcb_evaluation_data_numbers() {
1096        let root_ca = include_bytes!("../../tests/data/root_SGX_CA_der.cert");
1097        let root_cas = [&root_ca[..]];
1098        let intel_builder = IntelProvisioningClientBuilder::new(PcsVersion::V4)
1099            .set_retry_timeout(TIME_RETRY_TIMEOUT);
1100        let client = intel_builder.build(reqwest_client());
1101        let eval_numbers = client.tcb_evaluation_data_numbers().unwrap();
1102
1103        let eval_numbers2 = serde_json::ser::to_vec(&eval_numbers)
1104            .and_then(|v| serde_json::from_slice::<RawTcbEvaluationDataNumbers>(&v))
1105            .unwrap();
1106        assert_eq!(eval_numbers, eval_numbers2);
1107
1108        let fmspc = Fmspc::try_from("90806f000000").unwrap();
1109        let eval_numbers: TcbEvaluationDataNumbers =
1110            eval_numbers.verify(&root_cas, Platform::SGX).unwrap();
1111        for number in eval_numbers.numbers().map(|n| n.number()) {
1112            let qe_identity = match client.qe_identity(Some(number)) {
1113                Ok(id) => id,
1114                // API query with update="standard" will return QE Identity with TCB Evaluation Data Number M.
1115                // A 410 Gone response is returned when the inputted TCB Evaluation Data Number is < M,
1116                // so we ignore these TCB Evaluation Data Numbers.
1117                Err(super::Error::PCSError(status_code, _)) if status_code == super::StatusCode::Gone => continue,
1118                res @Err(_) => res.unwrap(),
1119            };
1120            let verified_qe_id = qe_identity
1121                .verify(&root_cas, EnclaveIdentity::QE)
1122                .unwrap();
1123            assert_eq!(verified_qe_id.tcb_evaluation_data_number(), u64::from(number));
1124
1125            let tcb_info = client
1126                    .tcbinfo(&fmspc, Some(number))
1127                    .unwrap()
1128                    .verify(&root_cas, Platform::SGX, 2)
1129                    .unwrap();
1130            assert_eq!(tcb_info.tcb_evaluation_data_number(), u64::from(number));
1131        }
1132    }
1133}