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