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