portable_rustls/webpki/client_verifier.rs
1use alloc::vec::Vec;
2
3use pki_types::{CertificateDer, CertificateRevocationListDer, UnixTime};
4use webpki::{CertRevocationList, ExpirationPolicy, RevocationCheckDepth, UnknownStatusPolicy};
5
6use super::{pki_error, VerifierBuilderError};
7#[cfg(doc)]
8use crate::crypto;
9use crate::crypto::{CryptoProvider, WebPkiSupportedAlgorithms};
10#[cfg(doc)]
11use crate::server::ServerConfig;
12use crate::sync::Arc;
13use crate::verify::{
14 ClientCertVerified, ClientCertVerifier, DigitallySignedStruct, HandshakeSignatureValid,
15 NoClientAuth,
16};
17use crate::webpki::parse_crls;
18use crate::webpki::verify::{verify_tls12_signature, verify_tls13_signature, ParsedCertificate};
19#[cfg(doc)]
20use crate::ConfigBuilder;
21use crate::{DistinguishedName, Error, RootCertStore, SignatureScheme};
22
23/// A builder for configuring a `webpki` client certificate verifier.
24///
25/// For more information, see the [`WebPkiClientVerifier`] documentation.
26#[derive(Debug, Clone)]
27pub struct ClientCertVerifierBuilder {
28 roots: Arc<RootCertStore>,
29 root_hint_subjects: Vec<DistinguishedName>,
30 crls: Vec<CertificateRevocationListDer<'static>>,
31 revocation_check_depth: RevocationCheckDepth,
32 unknown_revocation_policy: UnknownStatusPolicy,
33 revocation_expiration_policy: ExpirationPolicy,
34 anon_policy: AnonymousClientPolicy,
35 supported_algs: WebPkiSupportedAlgorithms,
36}
37
38impl ClientCertVerifierBuilder {
39 pub(crate) fn new(
40 roots: Arc<RootCertStore>,
41 supported_algs: WebPkiSupportedAlgorithms,
42 ) -> Self {
43 Self {
44 root_hint_subjects: roots.subjects(),
45 roots,
46 crls: Vec::new(),
47 anon_policy: AnonymousClientPolicy::Deny,
48 revocation_check_depth: RevocationCheckDepth::Chain,
49 unknown_revocation_policy: UnknownStatusPolicy::Deny,
50 revocation_expiration_policy: ExpirationPolicy::Ignore,
51 supported_algs,
52 }
53 }
54
55 /// Clear the list of trust anchor hint subjects.
56 ///
57 /// By default, the client cert verifier will use the subjects provided by the root cert
58 /// store configured for client authentication. Calling this function will remove these
59 /// hint subjects, indicating the client should make a free choice of which certificate
60 /// to send.
61 ///
62 /// See [`ClientCertVerifier::root_hint_subjects`] for more information on
63 /// circumstances where you may want to clear the default hint subjects.
64 pub fn clear_root_hint_subjects(mut self) -> Self {
65 self.root_hint_subjects = Vec::default();
66 self
67 }
68
69 /// Add additional [`DistinguishedName`]s to the list of trust anchor hint subjects.
70 ///
71 /// By default, the client cert verifier will use the subjects provided by the root cert
72 /// store configured for client authentication. Calling this function will add to these
73 /// existing hint subjects. Calling this function with empty `subjects` will have no
74 /// effect.
75 ///
76 /// See [`ClientCertVerifier::root_hint_subjects`] for more information on
77 /// circumstances where you may want to override the default hint subjects.
78 pub fn add_root_hint_subjects(
79 mut self,
80 subjects: impl IntoIterator<Item = DistinguishedName>,
81 ) -> Self {
82 self.root_hint_subjects.extend(subjects);
83 self
84 }
85
86 /// Verify the revocation state of presented client certificates against the provided
87 /// certificate revocation lists (CRLs). Calling `with_crls` multiple times appends the
88 /// given CRLs to the existing collection.
89 ///
90 /// By default all certificates in the verified chain built from the presented client
91 /// certificate to a trust anchor will have their revocation status checked. Calling
92 /// [`only_check_end_entity_revocation`][Self::only_check_end_entity_revocation] will
93 /// change this behavior to only check the end entity client certificate.
94 ///
95 /// By default if a certificate's revocation status can not be determined using the
96 /// configured CRLs, it will be treated as an error. Calling
97 /// [`allow_unknown_revocation_status`][Self::allow_unknown_revocation_status] will change
98 /// this behavior to allow unknown revocation status.
99 pub fn with_crls(
100 mut self,
101 crls: impl IntoIterator<Item = CertificateRevocationListDer<'static>>,
102 ) -> Self {
103 self.crls.extend(crls);
104 self
105 }
106
107 /// Only check the end entity certificate revocation status when using CRLs.
108 ///
109 /// If CRLs are provided using [`with_crls`][Self::with_crls] only check the end entity
110 /// certificate's revocation status. Overrides the default behavior of checking revocation
111 /// status for each certificate in the verified chain built to a trust anchor
112 /// (excluding the trust anchor itself).
113 ///
114 /// If no CRLs are provided then this setting has no effect. Neither the end entity certificate
115 /// or any intermediates will have revocation status checked.
116 pub fn only_check_end_entity_revocation(mut self) -> Self {
117 self.revocation_check_depth = RevocationCheckDepth::EndEntity;
118 self
119 }
120
121 /// Allow unauthenticated clients to connect.
122 ///
123 /// Clients that offer a client certificate issued by a trusted root, and clients that offer no
124 /// client certificate will be allowed to connect.
125 pub fn allow_unauthenticated(mut self) -> Self {
126 self.anon_policy = AnonymousClientPolicy::Allow;
127 self
128 }
129
130 /// Allow unknown certificate revocation status when using CRLs.
131 ///
132 /// If CRLs are provided with [`with_crls`][Self::with_crls] and it isn't possible to
133 /// determine the revocation status of a certificate, do not treat it as an error condition.
134 /// Overrides the default behavior where unknown revocation status is considered an error.
135 ///
136 /// If no CRLs are provided then this setting has no effect as revocation status checks
137 /// are not performed.
138 pub fn allow_unknown_revocation_status(mut self) -> Self {
139 self.unknown_revocation_policy = UnknownStatusPolicy::Allow;
140 self
141 }
142
143 /// Enforce the CRL nextUpdate field (i.e. expiration)
144 ///
145 /// If CRLs are provided with [`with_crls`][Self::with_crls] and the verification time is
146 /// beyond the time in the CRL nextUpdate field, it is expired and treated as an error condition.
147 /// Overrides the default behavior where expired CRLs are not treated as an error condition.
148 ///
149 /// If no CRLs are provided then this setting has no effect as revocation status checks
150 /// are not performed.
151 pub fn enforce_revocation_expiration(mut self) -> Self {
152 self.revocation_expiration_policy = ExpirationPolicy::Enforce;
153 self
154 }
155
156 /// Build a client certificate verifier. The built verifier will be used for the server to offer
157 /// client certificate authentication, to control how offered client certificates are validated,
158 /// and to determine what to do with anonymous clients that do not respond to the client
159 /// certificate authentication offer with a client certificate.
160 ///
161 /// If `with_signature_verification_algorithms` was not called on the builder, a default set of
162 /// signature verification algorithms is used, controlled by the selected [`CryptoProvider`].
163 ///
164 /// Once built, the provided `Arc<dyn ClientCertVerifier>` can be used with a Rustls
165 /// [`ServerConfig`] to configure client certificate validation using
166 /// [`with_client_cert_verifier`][ConfigBuilder<ClientConfig, WantsVerifier>::with_client_cert_verifier].
167 ///
168 /// # Errors
169 /// This function will return a [`VerifierBuilderError`] if:
170 /// 1. No trust anchors have been provided.
171 /// 2. DER encoded CRLs have been provided that can not be parsed successfully.
172 pub fn build(self) -> Result<Arc<dyn ClientCertVerifier>, VerifierBuilderError> {
173 if self.roots.is_empty() {
174 return Err(VerifierBuilderError::NoRootAnchors);
175 }
176
177 Ok(Arc::new(WebPkiClientVerifier::new(
178 self.roots,
179 self.root_hint_subjects,
180 parse_crls(self.crls)?,
181 self.revocation_check_depth,
182 self.unknown_revocation_policy,
183 self.revocation_expiration_policy,
184 self.anon_policy,
185 self.supported_algs,
186 )))
187 }
188}
189
190/// A client certificate verifier that uses the `webpki` crate[^1] to perform client certificate
191/// validation.
192///
193/// It must be created via the [`WebPkiClientVerifier::builder()`] or
194/// [`WebPkiClientVerifier::builder_with_provider()`] functions.
195///
196/// Once built, the provided `Arc<dyn ClientCertVerifier>` can be used with a Rustls [`ServerConfig`]
197/// to configure client certificate validation using [`with_client_cert_verifier`][ConfigBuilder<ClientConfig, WantsVerifier>::with_client_cert_verifier].
198///
199/// Example:
200///
201/// To require all clients present a client certificate issued by a trusted CA:
202/// ```no_run
203/// # #[cfg(any(feature = "ring", feature = "aws_lc_rs"))] {
204/// # use portable_rustls as rustls; // DOC IMPORT WORKAROUND for this fork
205/// # use rustls::RootCertStore;
206/// # use rustls::server::WebPkiClientVerifier;
207/// # let roots = RootCertStore::empty();
208/// let client_verifier = WebPkiClientVerifier::builder(roots.into())
209/// .build()
210/// .unwrap();
211/// # }
212/// ```
213///
214/// Or, to allow clients presenting a client certificate authenticated by a trusted CA, or
215/// anonymous clients that present no client certificate:
216/// ```no_run
217/// # #[cfg(any(feature = "ring", feature = "aws_lc_rs"))] {
218/// # use portable_rustls as rustls; // DOC IMPORT WORKAROUND for this fork
219/// # use rustls::RootCertStore;
220/// # use rustls::server::WebPkiClientVerifier;
221/// # let roots = RootCertStore::empty();
222/// let client_verifier = WebPkiClientVerifier::builder(roots.into())
223/// .allow_unauthenticated()
224/// .build()
225/// .unwrap();
226/// # }
227/// ```
228///
229/// If you wish to disable advertising client authentication:
230/// ```no_run
231/// # use portable_rustls as rustls; // DOC IMPORT WORKAROUND for this fork
232/// # use rustls::RootCertStore;
233/// # use rustls::server::WebPkiClientVerifier;
234/// # let roots = RootCertStore::empty();
235/// let client_verifier = WebPkiClientVerifier::no_client_auth();
236/// ```
237///
238/// You can also configure the client verifier to check for certificate revocation with
239/// client certificate revocation lists (CRLs):
240/// ```no_run
241/// # #[cfg(any(feature = "ring", feature = "aws_lc_rs"))] {
242/// # use portable_rustls as rustls; // DOC IMPORT WORKAROUND for this fork
243/// # use rustls::RootCertStore;
244/// # use rustls::server::{WebPkiClientVerifier};
245/// # let roots = RootCertStore::empty();
246/// # let crls = Vec::new();
247/// let client_verifier = WebPkiClientVerifier::builder(roots.into())
248/// .with_crls(crls)
249/// .build()
250/// .unwrap();
251/// # }
252/// ```
253///
254/// [^1]: <https://github.com/rustls/webpki>
255#[derive(Debug)]
256pub struct WebPkiClientVerifier {
257 roots: Arc<RootCertStore>,
258 root_hint_subjects: Vec<DistinguishedName>,
259 crls: Vec<CertRevocationList<'static>>,
260 revocation_check_depth: RevocationCheckDepth,
261 unknown_revocation_policy: UnknownStatusPolicy,
262 revocation_expiration_policy: ExpirationPolicy,
263 anonymous_policy: AnonymousClientPolicy,
264 supported_algs: WebPkiSupportedAlgorithms,
265}
266
267impl WebPkiClientVerifier {
268 /// Create a builder for the `webpki` client certificate verifier configuration using
269 /// the [process-default `CryptoProvider`][CryptoProvider#using-the-per-process-default-cryptoprovider].
270 ///
271 /// Client certificate authentication will be offered by the server, and client certificates
272 /// will be verified using the trust anchors found in the provided `roots`. If you
273 /// wish to disable client authentication use [`WebPkiClientVerifier::no_client_auth()`] instead.
274 ///
275 /// Use [`Self::builder_with_provider`] if you wish to specify an explicit provider.
276 ///
277 /// For more information, see the [`ClientCertVerifierBuilder`] documentation.
278 pub fn builder(roots: Arc<RootCertStore>) -> ClientCertVerifierBuilder {
279 Self::builder_with_provider(
280 roots,
281 Arc::clone(CryptoProvider::get_default_or_install_from_crate_features()),
282 )
283 }
284
285 /// Create a builder for the `webpki` client certificate verifier configuration using
286 /// a specified [`CryptoProvider`].
287 ///
288 /// Client certificate authentication will be offered by the server, and client certificates
289 /// will be verified using the trust anchors found in the provided `roots`. If you
290 /// wish to disable client authentication use [WebPkiClientVerifier::no_client_auth()] instead.
291 ///
292 /// The cryptography used comes from the specified [`CryptoProvider`].
293 ///
294 /// For more information, see the [`ClientCertVerifierBuilder`] documentation.
295 pub fn builder_with_provider(
296 roots: Arc<RootCertStore>,
297 provider: Arc<CryptoProvider>,
298 ) -> ClientCertVerifierBuilder {
299 ClientCertVerifierBuilder::new(roots, provider.signature_verification_algorithms)
300 }
301
302 /// Create a new `WebPkiClientVerifier` that disables client authentication. The server will
303 /// not offer client authentication and anonymous clients will be accepted.
304 ///
305 /// This is in contrast to using `WebPkiClientVerifier::builder().allow_unauthenticated().build()`,
306 /// which will produce a verifier that will offer client authentication, but not require it.
307 pub fn no_client_auth() -> Arc<dyn ClientCertVerifier> {
308 Arc::new(NoClientAuth {})
309 }
310
311 /// Construct a new `WebpkiClientVerifier`.
312 ///
313 /// * `roots` is a list of trust anchors to use for certificate validation.
314 /// * `root_hint_subjects` is a list of distinguished names to use for hinting acceptable
315 /// certificate authority subjects to a client.
316 /// * `crls` is a `Vec` of owned certificate revocation lists (CRLs) to use for
317 /// client certificate validation.
318 /// * `revocation_check_depth` controls which certificates have their revocation status checked
319 /// when `crls` are provided.
320 /// * `unknown_revocation_policy` controls how certificates with an unknown revocation status
321 /// are handled when `crls` are provided.
322 /// * `anonymous_policy` controls whether client authentication is required, or if anonymous
323 /// clients can connect.
324 /// * `supported_algs` specifies which signature verification algorithms should be used.
325 pub(crate) fn new(
326 roots: Arc<RootCertStore>,
327 root_hint_subjects: Vec<DistinguishedName>,
328 crls: Vec<CertRevocationList<'static>>,
329 revocation_check_depth: RevocationCheckDepth,
330 unknown_revocation_policy: UnknownStatusPolicy,
331 revocation_expiration_policy: ExpirationPolicy,
332 anonymous_policy: AnonymousClientPolicy,
333 supported_algs: WebPkiSupportedAlgorithms,
334 ) -> Self {
335 Self {
336 roots,
337 root_hint_subjects,
338 crls,
339 revocation_check_depth,
340 unknown_revocation_policy,
341 revocation_expiration_policy,
342 anonymous_policy,
343 supported_algs,
344 }
345 }
346}
347
348impl ClientCertVerifier for WebPkiClientVerifier {
349 fn offer_client_auth(&self) -> bool {
350 true
351 }
352
353 fn client_auth_mandatory(&self) -> bool {
354 match self.anonymous_policy {
355 AnonymousClientPolicy::Allow => false,
356 AnonymousClientPolicy::Deny => true,
357 }
358 }
359
360 fn root_hint_subjects(&self) -> &[DistinguishedName] {
361 &self.root_hint_subjects
362 }
363
364 fn verify_client_cert(
365 &self,
366 end_entity: &CertificateDer<'_>,
367 intermediates: &[CertificateDer<'_>],
368 now: UnixTime,
369 ) -> Result<ClientCertVerified, Error> {
370 let cert = ParsedCertificate::try_from(end_entity)?;
371
372 let crl_refs = self.crls.iter().collect::<Vec<_>>();
373
374 let revocation = if self.crls.is_empty() {
375 None
376 } else {
377 Some(
378 webpki::RevocationOptionsBuilder::new(&crl_refs)
379 // Note: safe to unwrap here - new is only fallible if no CRLs are provided
380 // and we verify this above.
381 .unwrap()
382 .with_depth(self.revocation_check_depth)
383 .with_status_policy(self.unknown_revocation_policy)
384 .with_expiration_policy(self.revocation_expiration_policy)
385 .build(),
386 )
387 };
388
389 cert.0
390 .verify_for_usage(
391 self.supported_algs.all,
392 &self.roots.roots,
393 intermediates,
394 now,
395 webpki::KeyUsage::client_auth(),
396 revocation,
397 None,
398 )
399 .map_err(pki_error)
400 .map(|_| ClientCertVerified::assertion())
401 }
402
403 fn verify_tls12_signature(
404 &self,
405 message: &[u8],
406 cert: &CertificateDer<'_>,
407 dss: &DigitallySignedStruct,
408 ) -> Result<HandshakeSignatureValid, Error> {
409 verify_tls12_signature(message, cert, dss, &self.supported_algs)
410 }
411
412 fn verify_tls13_signature(
413 &self,
414 message: &[u8],
415 cert: &CertificateDer<'_>,
416 dss: &DigitallySignedStruct,
417 ) -> Result<HandshakeSignatureValid, Error> {
418 verify_tls13_signature(message, cert, dss, &self.supported_algs)
419 }
420
421 fn supported_verify_schemes(&self) -> Vec<SignatureScheme> {
422 self.supported_algs.supported_schemes()
423 }
424}
425
426/// Controls how the [WebPkiClientVerifier] handles anonymous clients.
427#[derive(Debug, Clone, Copy, PartialEq, Eq)]
428pub(crate) enum AnonymousClientPolicy {
429 /// Clients that do not present a client certificate are allowed.
430 Allow,
431 /// Clients that do not present a client certificate are denied.
432 Deny,
433}
434
435#[cfg(test)]
436#[macro_rules_attribute::apply(test_for_each_provider)]
437mod tests {
438 use std::prelude::v1::*;
439 use std::{format, println, vec};
440
441 use pki_types::pem::PemObject;
442 use pki_types::{CertificateDer, CertificateRevocationListDer};
443
444 use super::{provider, WebPkiClientVerifier};
445 use crate::server::VerifierBuilderError;
446 use crate::sync::Arc;
447 use crate::RootCertStore;
448
449 fn load_crls(crls_der: &[&[u8]]) -> Vec<CertificateRevocationListDer<'static>> {
450 crls_der
451 .iter()
452 .map(|pem_bytes| CertificateRevocationListDer::from_pem_slice(pem_bytes).unwrap())
453 .collect()
454 }
455
456 fn test_crls() -> Vec<CertificateRevocationListDer<'static>> {
457 load_crls(&[
458 include_bytes!("../../../test-ca/ecdsa-p256/client.revoked.crl.pem").as_slice(),
459 include_bytes!("../../../test-ca/rsa-2048/client.revoked.crl.pem").as_slice(),
460 ])
461 }
462
463 fn load_roots(roots_der: &[&[u8]]) -> Arc<RootCertStore> {
464 let mut roots = RootCertStore::empty();
465 roots_der.iter().for_each(|der| {
466 roots
467 .add(CertificateDer::from(der.to_vec()))
468 .unwrap()
469 });
470 roots.into()
471 }
472
473 fn test_roots() -> Arc<RootCertStore> {
474 load_roots(&[
475 include_bytes!("../../../test-ca/ecdsa-p256/ca.der").as_slice(),
476 include_bytes!("../../../test-ca/rsa-2048/ca.der").as_slice(),
477 ])
478 }
479
480 #[test]
481 fn test_client_verifier_no_auth() {
482 // We should be able to build a verifier that turns off client authentication.
483 WebPkiClientVerifier::no_client_auth();
484 }
485
486 #[test]
487 fn test_client_verifier_required_auth() {
488 // We should be able to build a verifier that requires client authentication, and does
489 // no revocation checking.
490 let builder = WebPkiClientVerifier::builder_with_provider(
491 test_roots(),
492 provider::default_provider().into(),
493 );
494 // The builder should be Debug.
495 println!("{:?}", builder);
496 builder.build().unwrap();
497 }
498
499 #[test]
500 fn test_client_verifier_optional_auth() {
501 // We should be able to build a verifier that allows client authentication, and anonymous
502 // access, and does no revocation checking.
503 let builder = WebPkiClientVerifier::builder_with_provider(
504 test_roots(),
505 provider::default_provider().into(),
506 )
507 .allow_unauthenticated();
508 // The builder should be Debug.
509 println!("{:?}", builder);
510 builder.build().unwrap();
511 }
512
513 #[test]
514 fn test_client_verifier_without_crls_required_auth() {
515 // We should be able to build a verifier that requires client authentication, and does
516 // no revocation checking, that hasn't been configured to determine how to handle
517 // unauthenticated clients yet.
518 let builder = WebPkiClientVerifier::builder_with_provider(
519 test_roots(),
520 provider::default_provider().into(),
521 );
522 // The builder should be Debug.
523 println!("{:?}", builder);
524 builder.build().unwrap();
525 }
526
527 #[test]
528 fn test_client_verifier_without_crls_opptional_auth() {
529 // We should be able to build a verifier that allows client authentication,
530 // and anonymous access, that does no revocation checking.
531 let builder = WebPkiClientVerifier::builder_with_provider(
532 test_roots(),
533 provider::default_provider().into(),
534 )
535 .allow_unauthenticated();
536 // The builder should be Debug.
537 println!("{:?}", builder);
538 builder.build().unwrap();
539 }
540
541 #[test]
542 fn test_with_invalid_crls() {
543 // Trying to build a client verifier with invalid CRLs should error at build time.
544 let result = WebPkiClientVerifier::builder_with_provider(
545 test_roots(),
546 provider::default_provider().into(),
547 )
548 .with_crls(vec![CertificateRevocationListDer::from(vec![0xFF])])
549 .build();
550 assert!(matches!(result, Err(VerifierBuilderError::InvalidCrl(_))));
551 }
552
553 #[test]
554 fn test_with_crls_multiple_calls() {
555 // We should be able to call `with_crls` on a client verifier multiple times.
556 let initial_crls = test_crls();
557 let extra_crls =
558 load_crls(&[
559 include_bytes!("../../../test-ca/eddsa/client.revoked.crl.pem").as_slice(),
560 ]);
561 let builder = WebPkiClientVerifier::builder_with_provider(
562 test_roots(),
563 provider::default_provider().into(),
564 )
565 .with_crls(initial_crls.clone())
566 .with_crls(extra_crls.clone());
567
568 // There should be the expected number of crls.
569 assert_eq!(builder.crls.len(), initial_crls.len() + extra_crls.len());
570 // The builder should be Debug.
571 println!("{:?}", builder);
572 builder.build().unwrap();
573 }
574
575 #[test]
576 fn test_client_verifier_with_crls_required_auth_implicit() {
577 // We should be able to build a verifier that requires client authentication, and that does
578 // revocation checking with CRLs, and that does not allow any anonymous access.
579 let builder = WebPkiClientVerifier::builder_with_provider(
580 test_roots(),
581 provider::default_provider().into(),
582 )
583 .with_crls(test_crls());
584 // The builder should be Debug.
585 println!("{:?}", builder);
586 builder.build().unwrap();
587 }
588
589 #[test]
590 fn test_client_verifier_with_crls_optional_auth() {
591 // We should be able to build a verifier that supports client authentication, that does
592 // revocation checking with CRLs, and that allows anonymous access.
593 let builder = WebPkiClientVerifier::builder_with_provider(
594 test_roots(),
595 provider::default_provider().into(),
596 )
597 .with_crls(test_crls())
598 .allow_unauthenticated();
599 // The builder should be Debug.
600 println!("{:?}", builder);
601 builder.build().unwrap();
602 }
603
604 #[test]
605 fn test_client_verifier_ee_only() {
606 // We should be able to build a client verifier that only checks EE revocation status.
607 let builder = WebPkiClientVerifier::builder_with_provider(
608 test_roots(),
609 provider::default_provider().into(),
610 )
611 .with_crls(test_crls())
612 .only_check_end_entity_revocation();
613 // The builder should be Debug.
614 println!("{:?}", builder);
615 builder.build().unwrap();
616 }
617
618 #[test]
619 fn test_client_verifier_allow_unknown() {
620 // We should be able to build a client verifier that allows unknown revocation status
621 let builder = WebPkiClientVerifier::builder_with_provider(
622 test_roots(),
623 provider::default_provider().into(),
624 )
625 .with_crls(test_crls())
626 .allow_unknown_revocation_status();
627 // The builder should be Debug.
628 println!("{:?}", builder);
629 builder.build().unwrap();
630 }
631
632 #[test]
633 fn test_client_verifier_enforce_expiration() {
634 // We should be able to build a client verifier that allows unknown revocation status
635 let builder = WebPkiClientVerifier::builder_with_provider(
636 test_roots(),
637 provider::default_provider().into(),
638 )
639 .with_crls(test_crls())
640 .enforce_revocation_expiration();
641 // The builder should be Debug.
642 println!("{:?}", builder);
643 builder.build().unwrap();
644 }
645
646 #[test]
647 fn test_builder_no_roots() {
648 // Trying to create a client verifier builder with no trust anchors should fail at build time
649 let result = WebPkiClientVerifier::builder_with_provider(
650 RootCertStore::empty().into(),
651 provider::default_provider().into(),
652 )
653 .build();
654 assert!(matches!(result, Err(VerifierBuilderError::NoRootAnchors)));
655 }
656
657 #[test]
658 fn smoke() {
659 let all = vec![
660 VerifierBuilderError::NoRootAnchors,
661 VerifierBuilderError::InvalidCrl(crate::CertRevocationListError::ParseError),
662 ];
663
664 for err in all {
665 let _ = format!("{:?}", err);
666 let _ = format!("{}", err);
667 }
668 }
669}