rustls/webpki/client_verifier.rs
1use alloc::sync::Arc;
2use alloc::vec::Vec;
3
4use pki_types::{CertificateDer, CertificateRevocationListDer, UnixTime};
5use webpki::{CertRevocationList, ExpirationPolicy, RevocationCheckDepth, UnknownStatusPolicy};
6
7use super::{pki_error, VerifierBuilderError};
8#[cfg(doc)]
9use crate::crypto;
10use crate::crypto::{CryptoProvider, WebPkiSupportedAlgorithms};
11#[cfg(doc)]
12use crate::server::ServerConfig;
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 rustls::RootCertStore;
205/// # use rustls::server::WebPkiClientVerifier;
206/// # let roots = RootCertStore::empty();
207/// let client_verifier = WebPkiClientVerifier::builder(roots.into())
208/// .build()
209/// .unwrap();
210/// # }
211/// ```
212///
213/// Or, to allow clients presenting a client certificate authenticated by a trusted CA, or
214/// anonymous clients that present no client certificate:
215/// ```no_run
216/// # #[cfg(any(feature = "ring", feature = "aws_lc_rs"))] {
217/// # use rustls::RootCertStore;
218/// # use rustls::server::WebPkiClientVerifier;
219/// # let roots = RootCertStore::empty();
220/// let client_verifier = WebPkiClientVerifier::builder(roots.into())
221/// .allow_unauthenticated()
222/// .build()
223/// .unwrap();
224/// # }
225/// ```
226///
227/// If you wish to disable advertising client authentication:
228/// ```no_run
229/// # use rustls::RootCertStore;
230/// # use rustls::server::WebPkiClientVerifier;
231/// # let roots = RootCertStore::empty();
232/// let client_verifier = WebPkiClientVerifier::no_client_auth();
233/// ```
234///
235/// You can also configure the client verifier to check for certificate revocation with
236/// client certificate revocation lists (CRLs):
237/// ```no_run
238/// # #[cfg(any(feature = "ring", feature = "aws_lc_rs"))] {
239/// # use rustls::RootCertStore;
240/// # use rustls::server::{WebPkiClientVerifier};
241/// # let roots = RootCertStore::empty();
242/// # let crls = Vec::new();
243/// let client_verifier = WebPkiClientVerifier::builder(roots.into())
244/// .with_crls(crls)
245/// .build()
246/// .unwrap();
247/// # }
248/// ```
249///
250/// [^1]: <https://github.com/rustls/webpki>
251#[derive(Debug)]
252pub struct WebPkiClientVerifier {
253 roots: Arc<RootCertStore>,
254 root_hint_subjects: Vec<DistinguishedName>,
255 crls: Vec<CertRevocationList<'static>>,
256 revocation_check_depth: RevocationCheckDepth,
257 unknown_revocation_policy: UnknownStatusPolicy,
258 revocation_expiration_policy: ExpirationPolicy,
259 anonymous_policy: AnonymousClientPolicy,
260 supported_algs: WebPkiSupportedAlgorithms,
261}
262
263impl WebPkiClientVerifier {
264 /// Create a builder for the `webpki` client certificate verifier configuration using
265 /// the [process-default `CryptoProvider`][CryptoProvider#using-the-per-process-default-cryptoprovider].
266 ///
267 /// Client certificate authentication will be offered by the server, and client certificates
268 /// will be verified using the trust anchors found in the provided `roots`. If you
269 /// wish to disable client authentication use [`WebPkiClientVerifier::no_client_auth()`] instead.
270 ///
271 /// Use [`Self::builder_with_provider`] if you wish to specify an explicit provider.
272 ///
273 /// For more information, see the [`ClientCertVerifierBuilder`] documentation.
274 pub fn builder(roots: Arc<RootCertStore>) -> ClientCertVerifierBuilder {
275 Self::builder_with_provider(
276 roots,
277 Arc::clone(CryptoProvider::get_default_or_install_from_crate_features()),
278 )
279 }
280
281 /// Create a builder for the `webpki` client certificate verifier configuration using
282 /// a specified [`CryptoProvider`].
283 ///
284 /// Client certificate authentication will be offered by the server, and client certificates
285 /// will be verified using the trust anchors found in the provided `roots`. If you
286 /// wish to disable client authentication use [WebPkiClientVerifier::no_client_auth()] instead.
287 ///
288 /// The cryptography used comes from the specified [`CryptoProvider`].
289 ///
290 /// For more information, see the [`ClientCertVerifierBuilder`] documentation.
291 pub fn builder_with_provider(
292 roots: Arc<RootCertStore>,
293 provider: Arc<CryptoProvider>,
294 ) -> ClientCertVerifierBuilder {
295 ClientCertVerifierBuilder::new(roots, provider.signature_verification_algorithms)
296 }
297
298 /// Create a new `WebPkiClientVerifier` that disables client authentication. The server will
299 /// not offer client authentication and anonymous clients will be accepted.
300 ///
301 /// This is in contrast to using `WebPkiClientVerifier::builder().allow_unauthenticated().build()`,
302 /// which will produce a verifier that will offer client authentication, but not require it.
303 pub fn no_client_auth() -> Arc<dyn ClientCertVerifier> {
304 Arc::new(NoClientAuth {})
305 }
306
307 /// Construct a new `WebpkiClientVerifier`.
308 ///
309 /// * `roots` is a list of trust anchors to use for certificate validation.
310 /// * `root_hint_subjects` is a list of distinguished names to use for hinting acceptable
311 /// certificate authority subjects to a client.
312 /// * `crls` is a `Vec` of owned certificate revocation lists (CRLs) to use for
313 /// client certificate validation.
314 /// * `revocation_check_depth` controls which certificates have their revocation status checked
315 /// when `crls` are provided.
316 /// * `unknown_revocation_policy` controls how certificates with an unknown revocation status
317 /// are handled when `crls` are provided.
318 /// * `anonymous_policy` controls whether client authentication is required, or if anonymous
319 /// clients can connect.
320 /// * `supported_algs` specifies which signature verification algorithms should be used.
321 pub(crate) fn new(
322 roots: Arc<RootCertStore>,
323 root_hint_subjects: Vec<DistinguishedName>,
324 crls: Vec<CertRevocationList<'static>>,
325 revocation_check_depth: RevocationCheckDepth,
326 unknown_revocation_policy: UnknownStatusPolicy,
327 revocation_expiration_policy: ExpirationPolicy,
328 anonymous_policy: AnonymousClientPolicy,
329 supported_algs: WebPkiSupportedAlgorithms,
330 ) -> Self {
331 Self {
332 roots,
333 root_hint_subjects,
334 crls,
335 revocation_check_depth,
336 unknown_revocation_policy,
337 revocation_expiration_policy,
338 anonymous_policy,
339 supported_algs,
340 }
341 }
342}
343
344impl ClientCertVerifier for WebPkiClientVerifier {
345 fn offer_client_auth(&self) -> bool {
346 true
347 }
348
349 fn client_auth_mandatory(&self) -> bool {
350 match self.anonymous_policy {
351 AnonymousClientPolicy::Allow => false,
352 AnonymousClientPolicy::Deny => true,
353 }
354 }
355
356 fn root_hint_subjects(&self) -> &[DistinguishedName] {
357 &self.root_hint_subjects
358 }
359
360 fn verify_client_cert(
361 &self,
362 end_entity: &CertificateDer<'_>,
363 intermediates: &[CertificateDer<'_>],
364 now: UnixTime,
365 ) -> Result<ClientCertVerified, Error> {
366 let cert = ParsedCertificate::try_from(end_entity)?;
367
368 let crl_refs = self.crls.iter().collect::<Vec<_>>();
369
370 let revocation = if self.crls.is_empty() {
371 None
372 } else {
373 Some(
374 webpki::RevocationOptionsBuilder::new(&crl_refs)
375 // Note: safe to unwrap here - new is only fallible if no CRLs are provided
376 // and we verify this above.
377 .unwrap()
378 .with_depth(self.revocation_check_depth)
379 .with_status_policy(self.unknown_revocation_policy)
380 .with_expiration_policy(self.revocation_expiration_policy)
381 .build(),
382 )
383 };
384
385 cert.0
386 .verify_for_usage(
387 self.supported_algs.all,
388 &self.roots.roots,
389 intermediates,
390 now,
391 webpki::KeyUsage::client_auth(),
392 revocation,
393 None,
394 )
395 .map_err(pki_error)
396 .map(|_| ClientCertVerified::assertion())
397 }
398
399 fn verify_tls12_signature(
400 &self,
401 message: &[u8],
402 cert: &CertificateDer<'_>,
403 dss: &DigitallySignedStruct,
404 ) -> Result<HandshakeSignatureValid, Error> {
405 verify_tls12_signature(message, cert, dss, &self.supported_algs)
406 }
407
408 fn verify_tls13_signature(
409 &self,
410 message: &[u8],
411 cert: &CertificateDer<'_>,
412 dss: &DigitallySignedStruct,
413 ) -> Result<HandshakeSignatureValid, Error> {
414 verify_tls13_signature(message, cert, dss, &self.supported_algs)
415 }
416
417 fn supported_verify_schemes(&self) -> Vec<SignatureScheme> {
418 self.supported_algs.supported_schemes()
419 }
420}
421
422/// Controls how the [WebPkiClientVerifier] handles anonymous clients.
423#[derive(Debug, Clone, Copy, PartialEq, Eq)]
424pub(crate) enum AnonymousClientPolicy {
425 /// Clients that do not present a client certificate are allowed.
426 Allow,
427 /// Clients that do not present a client certificate are denied.
428 Deny,
429}
430
431#[cfg(test)]
432#[macro_rules_attribute::apply(test_for_each_provider)]
433mod tests {
434 use std::prelude::v1::*;
435 use std::sync::Arc;
436 use std::{format, println, vec};
437
438 use pki_types::pem::PemObject;
439 use pki_types::{CertificateDer, CertificateRevocationListDer};
440
441 use super::{provider, WebPkiClientVerifier};
442 use crate::server::VerifierBuilderError;
443 use crate::RootCertStore;
444
445 fn load_crls(crls_der: &[&[u8]]) -> Vec<CertificateRevocationListDer<'static>> {
446 crls_der
447 .iter()
448 .map(|pem_bytes| CertificateRevocationListDer::from_pem_slice(pem_bytes).unwrap())
449 .collect()
450 }
451
452 fn test_crls() -> Vec<CertificateRevocationListDer<'static>> {
453 load_crls(&[
454 include_bytes!("../../../test-ca/ecdsa-p256/client.revoked.crl.pem").as_slice(),
455 include_bytes!("../../../test-ca/rsa-2048/client.revoked.crl.pem").as_slice(),
456 ])
457 }
458
459 fn load_roots(roots_der: &[&[u8]]) -> Arc<RootCertStore> {
460 let mut roots = RootCertStore::empty();
461 roots_der.iter().for_each(|der| {
462 roots
463 .add(CertificateDer::from(der.to_vec()))
464 .unwrap()
465 });
466 roots.into()
467 }
468
469 fn test_roots() -> Arc<RootCertStore> {
470 load_roots(&[
471 include_bytes!("../../../test-ca/ecdsa-p256/ca.der").as_slice(),
472 include_bytes!("../../../test-ca/rsa-2048/ca.der").as_slice(),
473 ])
474 }
475
476 #[test]
477 fn test_client_verifier_no_auth() {
478 // We should be able to build a verifier that turns off client authentication.
479 WebPkiClientVerifier::no_client_auth();
480 }
481
482 #[test]
483 fn test_client_verifier_required_auth() {
484 // We should be able to build a verifier that requires client authentication, and does
485 // no revocation checking.
486 let builder = WebPkiClientVerifier::builder_with_provider(
487 test_roots(),
488 provider::default_provider().into(),
489 );
490 // The builder should be Debug.
491 println!("{:?}", builder);
492 builder.build().unwrap();
493 }
494
495 #[test]
496 fn test_client_verifier_optional_auth() {
497 // We should be able to build a verifier that allows client authentication, and anonymous
498 // access, and does no revocation checking.
499 let builder = WebPkiClientVerifier::builder_with_provider(
500 test_roots(),
501 provider::default_provider().into(),
502 )
503 .allow_unauthenticated();
504 // The builder should be Debug.
505 println!("{:?}", builder);
506 builder.build().unwrap();
507 }
508
509 #[test]
510 fn test_client_verifier_without_crls_required_auth() {
511 // We should be able to build a verifier that requires client authentication, and does
512 // no revocation checking, that hasn't been configured to determine how to handle
513 // unauthenticated clients yet.
514 let builder = WebPkiClientVerifier::builder_with_provider(
515 test_roots(),
516 provider::default_provider().into(),
517 );
518 // The builder should be Debug.
519 println!("{:?}", builder);
520 builder.build().unwrap();
521 }
522
523 #[test]
524 fn test_client_verifier_without_crls_opptional_auth() {
525 // We should be able to build a verifier that allows client authentication,
526 // and anonymous access, that does no revocation checking.
527 let builder = WebPkiClientVerifier::builder_with_provider(
528 test_roots(),
529 provider::default_provider().into(),
530 )
531 .allow_unauthenticated();
532 // The builder should be Debug.
533 println!("{:?}", builder);
534 builder.build().unwrap();
535 }
536
537 #[test]
538 fn test_with_invalid_crls() {
539 // Trying to build a client verifier with invalid CRLs should error at build time.
540 let result = WebPkiClientVerifier::builder_with_provider(
541 test_roots(),
542 provider::default_provider().into(),
543 )
544 .with_crls(vec![CertificateRevocationListDer::from(vec![0xFF])])
545 .build();
546 assert!(matches!(result, Err(VerifierBuilderError::InvalidCrl(_))));
547 }
548
549 #[test]
550 fn test_with_crls_multiple_calls() {
551 // We should be able to call `with_crls` on a client verifier multiple times.
552 let initial_crls = test_crls();
553 let extra_crls =
554 load_crls(&[
555 include_bytes!("../../../test-ca/eddsa/client.revoked.crl.pem").as_slice(),
556 ]);
557 let builder = WebPkiClientVerifier::builder_with_provider(
558 test_roots(),
559 provider::default_provider().into(),
560 )
561 .with_crls(initial_crls.clone())
562 .with_crls(extra_crls.clone());
563
564 // There should be the expected number of crls.
565 assert_eq!(builder.crls.len(), initial_crls.len() + extra_crls.len());
566 // The builder should be Debug.
567 println!("{:?}", builder);
568 builder.build().unwrap();
569 }
570
571 #[test]
572 fn test_client_verifier_with_crls_required_auth_implicit() {
573 // We should be able to build a verifier that requires client authentication, and that does
574 // revocation checking with CRLs, and that does not allow any anonymous access.
575 let builder = WebPkiClientVerifier::builder_with_provider(
576 test_roots(),
577 provider::default_provider().into(),
578 )
579 .with_crls(test_crls());
580 // The builder should be Debug.
581 println!("{:?}", builder);
582 builder.build().unwrap();
583 }
584
585 #[test]
586 fn test_client_verifier_with_crls_optional_auth() {
587 // We should be able to build a verifier that supports client authentication, that does
588 // revocation checking with CRLs, and that allows anonymous access.
589 let builder = WebPkiClientVerifier::builder_with_provider(
590 test_roots(),
591 provider::default_provider().into(),
592 )
593 .with_crls(test_crls())
594 .allow_unauthenticated();
595 // The builder should be Debug.
596 println!("{:?}", builder);
597 builder.build().unwrap();
598 }
599
600 #[test]
601 fn test_client_verifier_ee_only() {
602 // We should be able to build a client verifier that only checks EE revocation status.
603 let builder = WebPkiClientVerifier::builder_with_provider(
604 test_roots(),
605 provider::default_provider().into(),
606 )
607 .with_crls(test_crls())
608 .only_check_end_entity_revocation();
609 // The builder should be Debug.
610 println!("{:?}", builder);
611 builder.build().unwrap();
612 }
613
614 #[test]
615 fn test_client_verifier_allow_unknown() {
616 // We should be able to build a client verifier that allows unknown revocation status
617 let builder = WebPkiClientVerifier::builder_with_provider(
618 test_roots(),
619 provider::default_provider().into(),
620 )
621 .with_crls(test_crls())
622 .allow_unknown_revocation_status();
623 // The builder should be Debug.
624 println!("{:?}", builder);
625 builder.build().unwrap();
626 }
627
628 #[test]
629 fn test_client_verifier_enforce_expiration() {
630 // We should be able to build a client verifier that allows unknown revocation status
631 let builder = WebPkiClientVerifier::builder_with_provider(
632 test_roots(),
633 provider::default_provider().into(),
634 )
635 .with_crls(test_crls())
636 .enforce_revocation_expiration();
637 // The builder should be Debug.
638 println!("{:?}", builder);
639 builder.build().unwrap();
640 }
641
642 #[test]
643 fn test_builder_no_roots() {
644 // Trying to create a client verifier builder with no trust anchors should fail at build time
645 let result = WebPkiClientVerifier::builder_with_provider(
646 RootCertStore::empty().into(),
647 provider::default_provider().into(),
648 )
649 .build();
650 assert!(matches!(result, Err(VerifierBuilderError::NoRootAnchors)));
651 }
652
653 #[test]
654 fn smoke() {
655 let all = vec![
656 VerifierBuilderError::NoRootAnchors,
657 VerifierBuilderError::InvalidCrl(crate::CertRevocationListError::ParseError),
658 ];
659
660 for err in all {
661 let _ = format!("{:?}", err);
662 let _ = format!("{}", err);
663 }
664 }
665}