dcap_artifact_retrieval/provisioning_client/
mod.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
8use std::collections::BTreeMap;
9use std::convert::{TryFrom, TryInto};
10use std::hash::{DefaultHasher, Hash, Hasher};
11use std::io::Read;
12use std::sync::Mutex;
13use std::time::{Duration, SystemTime};
14
15use lru_cache::LruCache;
16use num_enum::TryFromPrimitive;
17use pcs::{
18    CpuSvn, DcapArtifactIssuer, EncPpid, Fmspc, PceId, PceIsvsvn, PckCert, PckCerts, PckCrl, PckID, QeId,
19    QeIdentitySigned, TcbComponent, TcbInfo, RawTcbEvaluationDataNumbers, Unverified,
20};
21#[cfg(feature = "reqwest")]
22use reqwest::blocking::{Client as ReqwestClient, Response as ReqwestResponse};
23
24use crate::Error;
25
26pub mod azure;
27pub(self) mod common;
28pub mod intel;
29pub mod pccs;
30
31pub use self::azure::AzureProvisioningClientBuilder;
32pub use self::intel::IntelProvisioningClientBuilder;
33pub use self::pccs::PccsProvisioningClientBuilder;
34
35// Taken from https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml
36#[derive(Clone, Debug, Eq, PartialEq, TryFromPrimitive)]
37#[repr(u16)]
38pub enum StatusCode {
39    Continue = 100,
40    SwitchingProtocols = 101,
41    Processing = 102,
42    EarlyHints = 103,
43    //104-199	Unassigned
44    Ok = 200,
45    Created = 201,
46    Accepted = 202,
47    NonAuthoritativeInformation = 203,
48    NoContent = 204,
49    ResetContent = 205,
50    PartialContent = 206,
51    MultiStatus = 207,
52    AlreadyReported = 208,
53    //209-225	Unassigned
54    ImUsed = 226,
55    //227-299	Unassigned
56    MultipleChoices = 300,
57    MovedPermanently = 301,
58    Found = 302,
59    SeeOther = 303,
60    NotModified = 304,
61    UseProxy = 305,
62    #[num_enum(alternatives = [418])]
63    Unused = 306,
64    TemporaryRedirect = 307,
65    PermanentRedirect = 308,
66    // 309-399	Unassigned
67    BadRequest = 400,
68    Unauthorized = 401,
69    PaymentRequired = 402,
70    Forbidden = 403,
71    NotFound = 404,
72    MethodNotAllowed = 405,
73    NotAcceptable = 406,
74    ProxyAuthenticationRequired = 407,
75    RequestTimeout = 408,
76    Conflict = 409,
77    Gone = 410,
78    LengthRequired = 411,
79    PreconditionFailed = 412,
80    ContentTooLarge = 413,
81    UriTooLong = 414,
82    UnsupportedMediaType = 415,
83    RangeNotSatisfiable = 416,
84    ExpectationFailed = 417,
85    //418 Unused
86    //419-420	Unassigned,
87    MisdirectedRequest = 421,
88    UnprocessableContent = 422,
89    Locked = 423,
90    FailedDependency = 424,
91    TooEarly = 425,
92    UpgradeRequired = 426,
93    // 427 Unassigned
94    PreconditionRequired = 428,
95    TooManyRequests = 429,
96    //430 Unassigned
97    RequestHeaderFieldsTooLarge = 431,
98    //432-450	Unassigned,
99    UnavailableForLegalReasons = 451,
100    //452-460	Unassigned,
101    NonStandard461 = 461, // used by PCCS
102    NonStandard462 = 462, // used by PCCS
103    //463-499	Unassigned,
104    InternalServerError = 500,
105    NotImplemented = 501,
106    BadGateway = 502,
107    ServiceUnavailable = 503,
108    GatewayTimeout = 504,
109    HttpVersionNotSupported = 505,
110    VariantAlsoNegotiates = 506,
111    InsufficientStorage = 507,
112    LoopDetected = 508,
113    //509 Unassigned
114    NotExtended = 510, // OBSOLETED
115    NetworkAuthenticationRequired = 511,
116    //512-599	Unassigned
117    #[num_enum(alternatives = [104..=199, 209..=225, 227..=299, 309..=399, 419, 420, 427, 430, 432..=450, 452..=460, 463..=499, 509, 512..=598])]
118    Unassigned = 599,
119}
120
121#[derive(Copy, Clone, PartialEq, Eq, Hash)]
122pub enum PcsVersion {
123    V3 = 3,
124    V4 = 4,
125}
126
127pub trait WithApiVersion {
128    fn api_version(&self) -> PcsVersion;
129}
130
131impl WithApiVersion for PcsVersion {
132    fn api_version(&self) -> PcsVersion {
133        *self
134    }
135}
136
137#[derive(Hash)]
138pub struct PckCertsIn<'a> {
139    enc_ppid: &'a EncPpid,
140    pce_id: PceId,
141    api_key: &'a Option<String>,
142    api_version: PcsVersion,
143}
144
145impl WithApiVersion for PckCertsIn<'_> {
146    fn api_version(&self) -> PcsVersion {
147        self.api_version
148    }
149}
150
151pub trait PckCertsService<'inp>:
152    ProvisioningServiceApi<'inp, Input = PckCertsIn<'inp>, Output = PckCerts>
153{
154    fn build_input(
155        &'inp self,
156        enc_ppid: &'inp EncPpid,
157        pce_id: PceId,
158    ) -> <Self as ProvisioningServiceApi<'inp>>::Input;
159}
160
161#[derive(Hash)]
162pub struct PckCertIn<'a> {
163    encrypted_ppid: Option<&'a EncPpid>,
164    pce_id: &'a PceId,
165    cpu_svn: &'a CpuSvn,
166    pce_isvsvn: PceIsvsvn,
167    qe_id: Option<&'a QeId>,
168    api_version: PcsVersion,
169    api_key: &'a Option<String>,
170}
171
172impl WithApiVersion for PckCertIn<'_> {
173    fn api_version(&self) -> PcsVersion {
174        self.api_version
175    }
176}
177
178pub trait PckCertService<'inp>:
179    ProvisioningServiceApi<'inp, Input = PckCertIn<'inp>, Output = PckCert<Unverified>>
180{
181    fn build_input(
182        &'inp self,
183        encrypted_ppid: Option<&'inp EncPpid>,
184        pce_id: &'inp PceId,
185        cpu_svn: &'inp CpuSvn,
186        pce_isvsvn: PceIsvsvn,
187        qe_id: Option<&'inp QeId>,
188    ) -> <Self as ProvisioningServiceApi<'inp>>::Input;
189}
190
191#[derive(Hash)]
192pub struct PckCrlIn {
193    api_version: PcsVersion,
194    ca: DcapArtifactIssuer,
195}
196
197impl WithApiVersion for PckCrlIn {
198    fn api_version(&self) -> PcsVersion {
199        self.api_version
200    }
201}
202
203pub trait PckCrlService<'inp>:
204    ProvisioningServiceApi<'inp, Input = PckCrlIn, Output = PckCrl<Unverified>>
205{
206    fn build_input(&'inp self, ca: DcapArtifactIssuer) -> <Self as ProvisioningServiceApi<'inp>>::Input;
207}
208
209#[derive(Hash)]
210pub struct QeIdIn {
211    pub api_version: PcsVersion,
212    pub tcb_evaluation_data_number: Option<u16>,
213}
214
215impl WithApiVersion for QeIdIn {
216    fn api_version(&self) -> PcsVersion {
217        self.api_version
218    }
219}
220
221pub trait QeIdService<'inp>:
222    ProvisioningServiceApi<'inp, Input = QeIdIn, Output = QeIdentitySigned>
223{
224    fn build_input(&'inp self, tcb_evaluation_data_number: Option<u16>) -> <Self as ProvisioningServiceApi<'inp>>::Input;
225}
226
227#[derive(Hash)]
228pub struct TcbInfoIn<'i> {
229    pub(crate) api_version: PcsVersion,
230    pub(crate) fmspc: &'i Fmspc,
231    pub(crate) tcb_evaluation_data_number: Option<u16>,
232}
233
234impl WithApiVersion for TcbInfoIn<'_> {
235    fn api_version(&self) -> PcsVersion {
236        self.api_version
237    }
238}
239
240pub trait TcbInfoService<'inp>:
241    ProvisioningServiceApi<'inp, Input = TcbInfoIn<'inp>, Output = TcbInfo>
242{
243    fn build_input(&'inp self, fmspc: &'inp Fmspc, tcb_evaluation_data_number: Option<u16>)
244        -> <Self as ProvisioningServiceApi<'inp>>::Input;
245}
246
247#[derive(Hash)]
248pub struct TcbEvaluationDataNumbersIn;
249
250impl WithApiVersion for TcbEvaluationDataNumbersIn {
251    fn api_version(&self) -> PcsVersion {
252        PcsVersion::V4
253    }
254}
255
256pub trait TcbEvaluationDataNumbersService<'inp>:
257    ProvisioningServiceApi<'inp, Input = TcbEvaluationDataNumbersIn, Output = RawTcbEvaluationDataNumbers>
258{
259    fn build_input(&self)
260        -> <Self as ProvisioningServiceApi<'inp>>::Input;
261}
262
263pub struct ClientBuilder {
264    retry_timeout: Option<Duration>,
265    cache_capacity: usize,
266    cache_shelf_time: Duration,
267}
268
269impl Default for ClientBuilder {
270    fn default() -> Self {
271        ClientBuilder {
272            retry_timeout: None,
273            cache_capacity: 10,
274            cache_shelf_time: Duration::from_secs(60),
275        }
276    }
277}
278
279impl ClientBuilder {
280    pub(crate) fn new() -> Self {
281        ClientBuilder::default()
282    }
283
284    pub(crate) fn set_retry_timeout(mut self, retry_timeout: Duration) -> Self {
285        self.retry_timeout = Some(retry_timeout);
286        self
287    }
288
289    pub(crate) fn build<PSS, PS, PC, QS, TS, ES, F>(
290        self,
291        pckcerts_service: PSS,
292        pckcert_service: PS,
293        pckcrl_service: PC,
294        qeid_service: QS,
295        tcbinfo_service: TS,
296        tcb_evaluation_data_numbers_service: ES,
297        fetcher: F,
298    ) -> Client<F>
299    where
300        PSS: for<'a> PckCertsService<'a> + Sync + Send + 'static,
301        PS: for<'a> PckCertService<'a> + Sync + Send + 'static,
302        PC: for<'a> PckCrlService<'a> + Sync + Send + 'static,
303        QS: for<'a> QeIdService<'a> + Sync + Send + 'static,
304        TS: for<'a> TcbInfoService<'a> + Sync + Send + 'static,
305        ES: for<'a> TcbEvaluationDataNumbersService<'a> + Sync + Send + 'static,
306        F: for<'a> Fetcher<'a>,
307    {
308        Client::new(
309            pckcerts_service,
310            pckcert_service,
311            pckcrl_service,
312            qeid_service,
313            tcbinfo_service,
314            tcb_evaluation_data_numbers_service,
315            fetcher,
316            self.retry_timeout,
317            self.cache_capacity,
318            self.cache_shelf_time,
319        )
320    }
321}
322
323struct PcsService<T: for<'a> ProvisioningServiceApi<'a> + Sync + ?Sized> {
324    service: Box<T>,
325}
326
327impl<T: for<'a> ProvisioningServiceApi<'a> + Sync + ?Sized> PcsService<T> {
328    pub fn new(service: Box<T>) -> Self {
329        Self { service }
330    }
331}
332
333impl<T: for<'a> ProvisioningServiceApi<'a> + Sync + ?Sized> PcsService<T> {
334    pub(crate) fn pcs_service(&self) -> &T {
335        &self.service
336    }
337
338    fn call_service<'a, F: Fetcher<'a>>(
339        &'a self,
340        fetcher: &'a F,
341        input: &<T as ProvisioningServiceApi<'a>>::Input,
342    ) -> Result<<T as ProvisioningServiceApi<'a>>::Output, Error> {
343        let (url, headers) =
344            <T as ProvisioningServiceApi<'a>>::build_request(&self.pcs_service(), input)?;
345        let req = fetcher.build_request(&url, headers)?;
346        let api_version = input.api_version();
347
348        let (status_code, resp) = fetcher.send(req)?;
349        <T as ProvisioningServiceApi<'a>>::validate_response(self.pcs_service(), status_code)?;
350        let (response_body, response_headers) = fetcher.parse_response(resp)?;
351        <T as ProvisioningServiceApi<'a>>::parse_response(
352            self.pcs_service(),
353            response_body,
354            response_headers,
355            api_version,
356        )
357    }
358}
359
360struct CachedService<O: Clone, T: for<'a> ProvisioningServiceApi<'a, Output = O> + Sync + ?Sized> {
361    service: BackoffService<T>,
362    cache: Mutex<LruCache<u64, (O, SystemTime)>>,
363    cache_shelf_time: Duration,
364}
365
366impl<O: Clone, T: for<'a> ProvisioningServiceApi<'a, Output = O> + Sync + ?Sized>
367    CachedService<O, T>
368{
369    pub fn new(service: BackoffService<T>, capacity: usize, cache_shelf_time: Duration) -> Self {
370        Self {
371            service,
372            cache: Mutex::new(LruCache::new(capacity)),
373            cache_shelf_time,
374        }
375    }
376}
377
378impl<O: Clone, T: for<'a> ProvisioningServiceApi<'a, Output = O> + Sync + ?Sized>
379    CachedService<O, T>
380{
381    pub(crate) fn pcs_service(&self) -> &T {
382        &self.service.pcs_service()
383    }
384
385    pub fn call_service<'a, F: Fetcher<'a>>(
386        &'a self,
387        fetcher: &'a F,
388        input: &<T as ProvisioningServiceApi<'a>>::Input,
389    ) -> Result<<T as ProvisioningServiceApi<'a>>::Output, Error> {
390        let key = {
391            let mut hasher = DefaultHasher::new();
392            input.hash(&mut hasher);
393            hasher.finish()
394        };
395
396        let mut cache = self.cache.lock().unwrap();
397        if let Some((value, time)) = cache.get_mut(&key) {
398            if self.cache_shelf_time < time.elapsed().unwrap_or(Duration::MAX) {
399                cache.remove(&key);
400            } else {
401                return Ok(value.to_owned());
402            }
403        }
404        let value = self.service.call_service::<F>(fetcher, input)?;
405        cache.insert(key, (value.clone(), SystemTime::now()));
406        Ok(value)
407    }
408}
409
410struct BackoffService<T: for<'a> ProvisioningServiceApi<'a> + Sync + ?Sized> {
411    service: PcsService<T>,
412    retry_timeout: Option<Duration>,
413}
414
415impl<T: for<'a> ProvisioningServiceApi<'a> + Sync + ?Sized> BackoffService<T> {
416    pub fn new(service: PcsService<T>, retry_timeout: Option<Duration>) -> Self {
417        Self {
418            service,
419            retry_timeout,
420        }
421    }
422}
423
424impl<T: for<'a> ProvisioningServiceApi<'a> + Sync + ?Sized> BackoffService<T> {
425    const RETRY_INITIAL_INTERVAL: Duration = Duration::from_secs(2);
426    const RETRY_INTERVAL_MULTIPLIER: f64 = 2.0;
427
428    pub(crate) fn pcs_service(&self) -> &T {
429        &self.service.pcs_service()
430    }
431
432    pub fn call_service<'a, F: Fetcher<'a>>(
433        &'a self,
434        fetcher: &'a F,
435        input: &<T as ProvisioningServiceApi<'a>>::Input,
436    ) -> Result<<T as ProvisioningServiceApi<'a>>::Output, Error> {
437        if let Some(retry_timeout) = self.retry_timeout {
438            let op = || match self.service.call_service::<F>(fetcher, input) {
439                Ok(output) => Ok(output),
440                Err(err) => match err {
441                    Error::PCSError(status_code, msg) => {
442                        if status_code.clone() as u16 >= 500 {
443                            Err(backoff::Error::transient(Error::PCSError(status_code, msg)))
444                        } else {
445                            Err(backoff::Error::permanent(Error::PCSError(status_code, msg)))
446                        }
447                    }
448                    err @ _ => Err(backoff::Error::Permanent(err)),
449                },
450            };
451
452            let backoff = backoff::ExponentialBackoffBuilder::default()
453                .with_initial_interval(Self::RETRY_INITIAL_INTERVAL)
454                .with_multiplier(Self::RETRY_INTERVAL_MULTIPLIER)
455                .with_max_elapsed_time(Some(retry_timeout))
456                .build();
457            backoff::retry(backoff, op).map_err(|backoff_err| match backoff_err {
458                backoff::Error::Permanent(err) => err,
459                backoff::Error::Transient { err, .. } => err,
460            })
461        } else {
462            self.service.call_service::<F>(fetcher, input)
463        }
464    }
465}
466
467pub struct Client<F: for<'a> Fetcher<'a>> {
468    pckcerts_service: CachedService<PckCerts, dyn for<'a> PckCertsService<'a> + Sync + Send>,
469    pckcert_service:
470        CachedService<PckCert<Unverified>, dyn for<'a> PckCertService<'a> + Sync + Send>,
471    pckcrl_service: CachedService<PckCrl<Unverified>, dyn for<'a> PckCrlService<'a> + Sync + Send>,
472    qeid_service: CachedService<QeIdentitySigned, dyn for<'a> QeIdService<'a> + Sync + Send>,
473    tcbinfo_service: CachedService<TcbInfo, dyn for<'a> TcbInfoService<'a> + Sync + Send>,
474    tcb_evaluation_data_numbers_service: CachedService<RawTcbEvaluationDataNumbers, dyn for<'a> TcbEvaluationDataNumbersService<'a> + Sync + Send>,
475    fetcher: F,
476}
477
478impl<F: for<'a> Fetcher<'a>> Client<F> {
479    fn new<PSS, PS, PC, QS, TS, ES>(
480        pckcerts_service: PSS,
481        pckcert_service: PS,
482        pckcrl_service: PC,
483        qeid_service: QS,
484        tcbinfo_service: TS,
485        tcb_evaluation_data_numbers_service: ES,
486        fetcher: F,
487        retry_timeout: Option<Duration>,
488        cache_capacity: usize,
489        cache_shelf_time: Duration,
490    ) -> Client<F>
491    where
492        PSS: for<'a> PckCertsService<'a> + Sync + Send + 'static,
493        PS: for<'a> PckCertService<'a> + Sync + Send + 'static,
494        PC: for<'a> PckCrlService<'a> + Sync + Send + 'static,
495        QS: for<'a> QeIdService<'a> + Sync + Send + 'static,
496        TS: for<'a> TcbInfoService<'a> + Sync + Send + 'static,
497        ES: for<'a> TcbEvaluationDataNumbersService<'a> + Sync + Send + 'static,
498    {
499        Client {
500            pckcerts_service: CachedService::new(
501                BackoffService::new(
502                    PcsService::new(Box::new(pckcerts_service)),
503                    retry_timeout.clone(),
504                ),
505                cache_capacity,
506                cache_shelf_time,
507            ),
508            pckcert_service: CachedService::new(
509                BackoffService::new(
510                    PcsService::new(Box::new(pckcert_service)),
511                    retry_timeout.clone(),
512                ),
513                cache_capacity,
514                cache_shelf_time,
515            ),
516            pckcrl_service: CachedService::new(
517                BackoffService::new(
518                    PcsService::new(Box::new(pckcrl_service)),
519                    retry_timeout.clone(),
520                ),
521                cache_capacity,
522                cache_shelf_time,
523            ),
524            qeid_service: CachedService::new(
525                BackoffService::new(
526                    PcsService::new(Box::new(qeid_service)),
527                    retry_timeout.clone(),
528                ),
529                cache_capacity,
530                cache_shelf_time,
531            ),
532            tcbinfo_service: CachedService::new(
533                BackoffService::new(
534                    PcsService::new(Box::new(tcbinfo_service)),
535                    retry_timeout.clone(),
536                ),
537                cache_capacity,
538                cache_shelf_time,
539            ),
540            tcb_evaluation_data_numbers_service: CachedService::new(
541                BackoffService::new(
542                    PcsService::new(Box::new(tcb_evaluation_data_numbers_service)),
543                    retry_timeout.clone(),
544                ),
545                cache_capacity,
546                cache_shelf_time,
547            ),
548            fetcher,
549        }
550    }
551}
552
553pub trait ProvisioningClient {
554    fn pckcerts(&self, enc_ppid: &EncPpid, pce_id: PceId) -> Result<PckCerts, Error>;
555
556    fn pckcert(
557        &self,
558        encrypted_ppid: Option<&EncPpid>,
559        pce_id: &PceId,
560        cpu_svn: &CpuSvn,
561        pce_isvsvn: PceIsvsvn,
562        qe_id: Option<&QeId>,
563    ) -> Result<PckCert<Unverified>, Error>;
564
565    fn tcbinfo(&self, fmspc: &Fmspc, evaluation_data_number: Option<u16>) -> Result<TcbInfo, Error>;
566
567    fn pckcrl(&self, ca: DcapArtifactIssuer) -> Result<PckCrl<Unverified>, Error>;
568
569    fn qe_identity(&self, evaluation_data_number: Option<u16>) -> Result<QeIdentitySigned, Error>;
570
571    /// Retrieve PCK certificates using `pckcerts()` and fallback to the
572    /// following method if that's not supported:
573    /// 1. Call `pckcert()` with PCK ID to get best available PCK cert.
574    /// 2. Try to call `pckcert()` with PCK ID but with CPUSVN all 1's.
575    /// 3. Using the FMSPC value from PCK cert in step 1, call `tcbinfo()` to
576    ///    get TCB info.
577    /// 4. For each TCB level in the result of previous call:
578    ///     - Call `pckcert()` to get the best available PCK cert for that TCB
579    ///       level.
580    ///     - When late microcode value is higher than the early microcode
581    ///       value, also try to get PCK cert with TCB level where the early
582    ///       microcode value is set to the late microcode value.
583    /// 
584    /// Note that PCK certs for some TCB levels may be missing.
585    fn pckcerts_with_fallback(&self, pck_id: &PckID) -> Result<PckCerts, Error> {
586        let get_and_collect = |collection: &mut BTreeMap<([u8; 16], u16), PckCert<Unverified>>, cpu_svn: &[u8; 16], pce_svn: u16| -> Result<PckCert<Unverified>, Error> {
587            let pck_cert = self.pckcert(
588                Some(&pck_id.enc_ppid),
589                &pck_id.pce_id,
590                cpu_svn,
591                pce_svn,
592                Some(&pck_id.qe_id),
593            )?;
594
595            // Getting PCK cert using CPUSVN from PCKID
596            let ptcb = pck_cert.platform_tcb()?;
597            collection.insert((ptcb.cpusvn, ptcb.tcb_components.pce_svn()), pck_cert.clone());
598            Ok(pck_cert)
599        };
600
601        match self.pckcerts(&pck_id.enc_ppid, pck_id.pce_id) {
602            Ok(pck_certs) => return Ok(pck_certs),
603            Err(Error::RequestNotSupported) => {} // fallback below
604            Err(e) => return Err(e),
605        }
606        // fallback:
607
608        // Use BTreeMap to have an ordered PckCerts at the end
609        let mut pckcerts_map = BTreeMap::new();
610
611        // 1. Use PCK ID to get best available PCK Cert
612        let pck_cert = get_and_collect(&mut pckcerts_map, &pck_id.cpu_svn, pck_id.pce_isvsvn)?;
613
614        // 2. Getting PCK cert using CPUSVN all 1's
615        let _ign_err = get_and_collect(&mut pckcerts_map, &[u8::MAX; 16], pck_id.pce_isvsvn);
616
617        let fmspc = pck_cert.sgx_extension()?.fmspc;
618        let tcb_info = self.tcbinfo(&fmspc, None)?;
619        let tcb_data = tcb_info.data()?;
620        for (cpu_svn, pce_isvsvn) in tcb_data.iter_tcb_components() {
621            // 3. Get PCK based on TCB levels
622            let _ = get_and_collect(&mut pckcerts_map, &cpu_svn, pce_isvsvn)?;
623
624            // 4. If late loaded microcode version is higher than early loaded microcode,
625            //    also try with highest microcode version of both components. We found cases where
626            //    fetching the PCK Cert that exactly matched the TCB level, did not result in a PCK
627            //    Cert for that level
628            const EARLY_UCODE_IDX: usize = 0;
629            const LATE_UCODE_IDX: usize = 1;
630            // Unfortunately the TCB Info does not populate the component type (e.g., curl -v -X GET
631            // "https://api.trustedservices.intel.com/sgx/certification/v4/tcb?fmspc=00906ED50000&tcbEvaluationDataNumber=20"
632            // ). We pick a default as backup, and ensure errors fetching these PckCerts are
633            // ignored.
634            let eary_ucode_idx = tcb_data.tcb_component_index(TcbComponent::EarlyMicrocodeUpdate).unwrap_or(EARLY_UCODE_IDX);
635            let late_ucode_idx = tcb_data.tcb_component_index(TcbComponent::LateMicrocodeUpdate).unwrap_or(LATE_UCODE_IDX);
636            let early_ucode = cpu_svn[eary_ucode_idx];
637            let late_ucode = cpu_svn[late_ucode_idx];
638            if early_ucode < late_ucode {
639                let mut cpu_svn = cpu_svn.clone();
640                cpu_svn[eary_ucode_idx] = late_ucode;
641                let _ign_err = get_and_collect(&mut pckcerts_map, &cpu_svn, pce_isvsvn);
642            }
643        }
644
645        // BTreeMap by default is Ascending
646        let pck_certs: Vec<_> = pckcerts_map.into_iter().rev().map(|(_, v)| v).collect();
647        pck_certs
648            .try_into()
649            .map_err(|e| Error::PCSDecodeError(format!("{}", e).into()))
650    }
651
652    fn tcb_evaluation_data_numbers(&self) -> Result<RawTcbEvaluationDataNumbers, Error>;
653}
654
655impl<F: for<'a> Fetcher<'a>> ProvisioningClient for Client<F> {
656    fn pckcerts(&self, encrypted_ppid: &EncPpid, pce_id: PceId) -> Result<PckCerts, Error> {
657        let input = self
658            .pckcerts_service
659            .pcs_service()
660            .build_input(encrypted_ppid, pce_id);
661        self.pckcerts_service.call_service(&self.fetcher, &input)
662    }
663
664    fn pckcert(
665        &self,
666        encrypted_ppid: Option<&EncPpid>,
667        pce_id: &PceId,
668        cpu_svn: &CpuSvn,
669        pce_isvsvn: PceIsvsvn,
670        qe_id: Option<&QeId>,
671    ) -> Result<PckCert<Unverified>, Error> {
672        let input = self.pckcert_service.pcs_service().build_input(
673            encrypted_ppid,
674            pce_id,
675            cpu_svn,
676            pce_isvsvn,
677            qe_id,
678        );
679        self.pckcert_service.call_service(&self.fetcher, &input)
680    }
681
682    fn tcbinfo(&self, fmspc: &Fmspc, tcb_evaluation_data_number: Option<u16>) -> Result<TcbInfo, Error> {
683        let input = self.tcbinfo_service.pcs_service().build_input(fmspc, tcb_evaluation_data_number);
684        self.tcbinfo_service.call_service(&self.fetcher, &input)
685    }
686
687    fn pckcrl(&self, ca: DcapArtifactIssuer) -> Result<PckCrl<Unverified>, Error> {
688        let input = self.pckcrl_service.pcs_service().build_input(ca);
689        self.pckcrl_service.call_service(&self.fetcher, &input)
690    }
691
692    fn qe_identity(&self, tcb_evaluation_data_number: Option<u16>) -> Result<QeIdentitySigned, Error> {
693        let input = self.qeid_service.pcs_service().build_input(tcb_evaluation_data_number);
694        self.qeid_service.call_service(&self.fetcher, &input)
695    }
696
697    fn tcb_evaluation_data_numbers(&self) -> Result<RawTcbEvaluationDataNumbers, Error> {
698        let input = self.tcb_evaluation_data_numbers_service.pcs_service().build_input();
699        self.tcb_evaluation_data_numbers_service.call_service(&self.fetcher, &input)
700    }
701}
702
703pub trait Fetcher<'req> {
704    type Request;
705    type Response;
706
707    fn build_request(
708        &'req self,
709        url: &String,
710        headers: Vec<(String, String)>,
711    ) -> Result<Self::Request, Error>;
712
713    fn send(&'req self, request: Self::Request) -> Result<(StatusCode, Self::Response), Error>;
714
715    fn parse_response(
716        &'req self,
717        response: Self::Response,
718    ) -> Result<(String, Vec<(String, String)>), Error>;
719}
720
721#[cfg(feature = "reqwest")]
722impl<'req> Fetcher<'req> for ReqwestClient {
723    type Request = reqwest::blocking::RequestBuilder;
724    type Response = ReqwestResponse;
725
726    fn build_request(
727        &'req self,
728        url: &String,
729        headers: Vec<(String, String)>,
730    ) -> Result<Self::Request, Error> {
731        let url = reqwest::Url::parse(url).map_err(|e| e.to_string())?;
732
733        let mut result = self.get(url);
734
735        for (name, value) in headers {
736            result = result.header(name, value);
737        }
738
739        Ok(result)
740    }
741
742    fn send(&'req self, request: Self::Request) -> Result<(StatusCode, Self::Response), Error> {
743        use std::fmt::Write;
744        // Reqwest does not provide enough info about error
745        fn report(mut err: &(dyn std::error::Error + 'static)) -> String {
746            let mut s = format!("{}", err);
747            while let Some(src) = err.source() {
748                let _ = write!(s, "\n  Caused by: {}", src);
749                err = src;
750            }
751            s
752        }
753        let response = request.send().map_err(|e| report(&e))?;
754        let status_code =
755            StatusCode::try_from(response.status().as_u16()).map_err(|e| e.to_string())?;
756
757        Ok((status_code, response))
758    }
759
760    fn parse_response(
761        &'req self,
762        mut response: Self::Response,
763    ) -> Result<(String, Vec<(String, String)>), Error> {
764        let mut body = Vec::new();
765        response.read_to_end(&mut body).map_err(|e| {
766            Error::ReadResponseError(
767                format!("Error while trying to read response body. Error: {}", e).into(),
768            )
769        })?;
770
771        let body = String::from_utf8(body)
772            .map_err(|e| Error::ReadResponseError(format!("{}", e).into()))?;
773
774        let headers: Vec<(String, String)> = response
775            .headers()
776            .iter()
777            .map(|(header, value)| (header.to_string(), value.to_str().unwrap_or("").to_string()))
778            .collect();
779        Ok((body, headers))
780    }
781}
782
783pub trait ProvisioningServiceApi<'inp> {
784    type Input: 'inp + WithApiVersion + Hash;
785    type Output: Clone;
786
787    fn build_request(
788        &'inp self,
789        input: &Self::Input,
790    ) -> Result<(String, Vec<(String, String)>), Error>;
791
792    fn validate_response(&'inp self, code: StatusCode) -> Result<(), Error>;
793
794    fn parse_response(
795        &'inp self,
796        response_body: String,
797        response_headers: Vec<(String, String)>,
798        api_version: PcsVersion,
799    ) -> Result<Self::Output, Error>;
800}
801
802#[cfg(test)]
803mod test_helpers {
804    use pkix::FromBer;
805
806    pub fn get_cert_subject(cert: &str) -> String {
807        let cert = &pkix::pem::pem_to_der(cert.trim(), Some(pkix::pem::PEM_CERTIFICATE))
808            .ok_or(yasna::ASN1Error::new(yasna::ASN1ErrorKind::Invalid))
809            .unwrap();
810        let cert = pkix::x509::GenericCertificate::from_ber(&cert).unwrap();
811        let name = cert.tbscert.subject.get(&*pkix::oid::commonName).unwrap();
812        String::from_utf8_lossy(&name.value()).to_string()
813    }
814}
815
816#[cfg(test)]
817mod tests {
818    use super::*;
819    use std::string::String;
820
821    struct MockService;
822
823    #[derive(Hash, Clone, PartialEq, Eq, Debug)]
824    struct MockInput(u64);
825
826    impl WithApiVersion for MockInput {
827        fn api_version(&self) -> PcsVersion {
828            PcsVersion::V3
829        }
830    }
831
832    #[derive(Clone, PartialEq, Eq, Debug)]
833    struct MockOutput(String);
834
835    impl<'a> ProvisioningServiceApi<'a> for MockService {
836        type Input = MockInput;
837        type Output = MockOutput;
838
839        fn build_request(
840            &'a self,
841            _input: &Self::Input,
842        ) -> Result<(String, Vec<(String, String)>), Error> {
843            Ok((_input.0.to_string(), vec![]))
844        }
845
846        fn validate_response(&'a self, _code: StatusCode) -> Result<(), Error> {
847            Ok(())
848        }
849
850        fn parse_response(
851            &'a self,
852            response_body: String,
853            _response_headers: Vec<(std::string::String, std::string::String)>,
854            _api_version: PcsVersion,
855        ) -> Result<Self::Output, Error> {
856            Ok(MockOutput(format!("response to: {}", response_body)))
857        }
858    }
859
860    struct MockFetcher;
861
862    impl<'req> Fetcher<'req> for MockFetcher {
863        type Request = String;
864        type Response = String;
865
866        fn build_request(
867            &'req self,
868            url: &String,
869            _headers: Vec<(String, String)>,
870        ) -> Result<Self::Request, Error> {
871            Ok(url.clone())
872        }
873
874        fn send(&'req self, request: Self::Request) -> Result<(StatusCode, Self::Response), Error> {
875            Ok((StatusCode::Ok, request))
876        }
877
878        fn parse_response(
879            &'req self,
880            response: Self::Response,
881        ) -> Result<(String, Vec<(String, String)>), Error> {
882            Ok((response, vec![]))
883        }
884    }
885
886    #[test]
887    fn test_call_service_cache_miss() {
888        let service = PcsService {
889            service: Box::new(MockService),
890        };
891        let service = BackoffService::new(service, None);
892        let cached_service = CachedService::new(service, 5, Duration::from_secs(120));
893        let fetcher = MockFetcher;
894        let input_a = MockInput(42);
895        let input_b = MockInput(420);
896
897        // Initial call to populate the cache for `input_a`
898        cached_service.call_service(&fetcher, &input_a).unwrap();
899
900        // input_b should provoke cache miss and add new key to the cache
901        let result = cached_service.call_service(&fetcher, &input_b).unwrap();
902
903        let (cached_value, _) = {
904            let mut cache = cached_service.cache.lock().unwrap();
905            cache.get_mut(&calculate_key(&input_b)).unwrap().to_owned()
906        };
907
908        assert_eq!(result, cached_value);
909    }
910
911    #[test]
912    fn test_call_service_cache_hit() {
913        let service = PcsService {
914            service: Box::new(MockService),
915        };
916        let service = BackoffService::new(service, None);
917        let cached_service = CachedService::new(service, 5, Duration::from_secs(120));
918        let fetcher = MockFetcher;
919        let input = MockInput(42);
920
921        // Initial call to populate the cache
922        let _ = cached_service.call_service(&fetcher, &input).unwrap();
923
924        // Now the service should not be called, and the cached result should be returned
925        let (cached_value, _) = {
926            let mut cache = cached_service.cache.lock().unwrap();
927            cache.get_mut(&calculate_key(&input)).unwrap().to_owned()
928        };
929
930        let result = cached_service.call_service(&fetcher, &input).unwrap();
931        assert_eq!(result, cached_value);
932    }
933
934    #[test]
935    fn test_cache_capacity_eviction() {
936        let service = PcsService {
937            service: Box::new(MockService),
938        };
939        let service = BackoffService::new(service, None);
940        let cached_service = CachedService::new(service, 2, Duration::from_secs(120));
941        let fetcher = MockFetcher;
942
943        // Insert entries into the cache, exceeding its capacity
944        for i in 0..3 {
945            let input = MockInput(i);
946            let _ = cached_service.call_service(&fetcher, &input).unwrap();
947        }
948
949        // At this point, the cache should have evicted the first inserted entry (MockInput(0))
950        let mut cache = cached_service.cache.lock().unwrap();
951
952        // The cache should only have 2 items (capacity is 2)
953        assert_eq!(cache.len(), 2);
954
955        // The first inserted key (MockInput(0)) should be evicted
956        let key_first = calculate_key(&MockInput(0));
957        assert!(!cache.contains_key(&key_first));
958
959        // The last inserted key (MockInput(2)) should be present
960        let key_last = calculate_key(&MockInput(2));
961        assert!(cache.contains_key(&key_last));
962    }
963
964    // Helper function to calculate the cache key based on the input
965    fn calculate_key(input: &MockInput) -> u64 {
966        let mut hasher = DefaultHasher::new();
967        input.hash(&mut hasher);
968        hasher.finish()
969    }
970}