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