1use 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#[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 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 ImUsed = 226,
55 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 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 MisdirectedRequest = 421,
88 UnprocessableContent = 422,
89 Locked = 423,
90 FailedDependency = 424,
91 TooEarly = 425,
92 UpgradeRequired = 426,
93 PreconditionRequired = 428,
95 TooManyRequests = 429,
96 RequestHeaderFieldsTooLarge = 431,
98 UnavailableForLegalReasons = 451,
100 NonStandard461 = 461, NonStandard462 = 462, 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 NotExtended = 510, NetworkAuthenticationRequired = 511,
116 #[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 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 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) => {} Err(e) => return Err(e),
605 }
606 let mut pckcerts_map = BTreeMap::new();
610
611 let pck_cert = get_and_collect(&mut pckcerts_map, &pck_id.cpu_svn, pck_id.pce_isvsvn)?;
613
614 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 let _ = get_and_collect(&mut pckcerts_map, &cpu_svn, pce_isvsvn)?;
623
624 const EARLY_UCODE_IDX: usize = 0;
629 const LATE_UCODE_IDX: usize = 1;
630 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 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 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 cached_service.call_service(&fetcher, &input_a).unwrap();
899
900 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 let _ = cached_service.call_service(&fetcher, &input).unwrap();
923
924 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 for i in 0..3 {
945 let input = MockInput(i);
946 let _ = cached_service.call_service(&fetcher, &input).unwrap();
947 }
948
949 let mut cache = cached_service.cache.lock().unwrap();
951
952 assert_eq!(cache.len(), 2);
954
955 let key_first = calculate_key(&MockInput(0));
957 assert!(!cache.contains_key(&key_first));
958
959 let key_last = calculate_key(&MockInput(2));
961 assert!(cache.contains_key(&key_last));
962 }
963
964 fn calculate_key(input: &MockInput) -> u64 {
966 let mut hasher = DefaultHasher::new();
967 input.hash(&mut hasher);
968 hasher.finish()
969 }
970}