rustls/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};
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::prelude::v1::*;
417    use std::sync::Arc;
418    use std::{format, println, vec};
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(test_roots());
467        // The builder should be Debug.
468        println!("{:?}", builder);
469        builder.build().unwrap();
470    }
471
472    #[test]
473    fn test_client_verifier_optional_auth() {
474        // We should be able to build a verifier that allows client authentication, and anonymous
475        // access, and does no revocation checking.
476        let builder = WebPkiClientVerifier::builder(test_roots()).allow_unauthenticated();
477        // The builder should be Debug.
478        println!("{:?}", builder);
479        builder.build().unwrap();
480    }
481
482    #[test]
483    fn test_client_verifier_without_crls_required_auth() {
484        // We should be able to build a verifier that requires client authentication, and does
485        // no revocation checking, that hasn't been configured to determine how to handle
486        // unauthenticated clients yet.
487        let builder = WebPkiClientVerifier::builder(test_roots());
488        // The builder should be Debug.
489        println!("{:?}", builder);
490        builder.build().unwrap();
491    }
492
493    #[test]
494    fn test_client_verifier_without_crls_opptional_auth() {
495        // We should be able to build a verifier that allows client authentication,
496        // and anonymous access, that does no revocation checking.
497        let builder = WebPkiClientVerifier::builder(test_roots()).allow_unauthenticated();
498        // The builder should be Debug.
499        println!("{:?}", builder);
500        builder.build().unwrap();
501    }
502
503    #[test]
504    fn test_with_invalid_crls() {
505        // Trying to build a client verifier with invalid CRLs should error at build time.
506        let result = WebPkiClientVerifier::builder(test_roots())
507            .with_crls(vec![CertificateRevocationListDer::from(vec![0xFF])])
508            .build();
509        assert!(matches!(result, Err(VerifierBuilderError::InvalidCrl(_))));
510    }
511
512    #[test]
513    fn test_with_crls_multiple_calls() {
514        // We should be able to call `with_crls` on a client verifier multiple times.
515        let initial_crls = test_crls();
516        let extra_crls =
517            load_crls(&[
518                include_bytes!("../../../test-ca/eddsa/client.revoked.crl.pem").as_slice(),
519            ]);
520        let builder = WebPkiClientVerifier::builder(test_roots())
521            .with_crls(initial_crls.clone())
522            .with_crls(extra_crls.clone());
523
524        // There should be the expected number of crls.
525        assert_eq!(builder.crls.len(), initial_crls.len() + extra_crls.len());
526        // The builder should be Debug.
527        println!("{:?}", builder);
528        builder.build().unwrap();
529    }
530
531    #[test]
532    fn test_client_verifier_with_crls_required_auth_implicit() {
533        // We should be able to build a verifier that requires client authentication, and that does
534        // revocation checking with CRLs, and that does not allow any anonymous access.
535        let builder = WebPkiClientVerifier::builder(test_roots()).with_crls(test_crls());
536        // The builder should be Debug.
537        println!("{:?}", builder);
538        builder.build().unwrap();
539    }
540
541    #[test]
542    fn test_client_verifier_with_crls_optional_auth() {
543        // We should be able to build a verifier that supports client authentication, that does
544        // revocation checking with CRLs, and that allows anonymous access.
545        let builder = WebPkiClientVerifier::builder(test_roots())
546            .with_crls(test_crls())
547            .allow_unauthenticated();
548        // The builder should be Debug.
549        println!("{:?}", builder);
550        builder.build().unwrap();
551    }
552
553    #[test]
554    fn test_client_verifier_ee_only() {
555        // We should be able to build a client verifier that only checks EE revocation status.
556        let builder = WebPkiClientVerifier::builder(test_roots())
557            .with_crls(test_crls())
558            .only_check_end_entity_revocation();
559        // The builder should be Debug.
560        println!("{:?}", builder);
561        builder.build().unwrap();
562    }
563
564    #[test]
565    fn test_client_verifier_allow_unknown() {
566        // We should be able to build a client verifier that allows unknown revocation status
567        let builder = WebPkiClientVerifier::builder(test_roots())
568            .with_crls(test_crls())
569            .allow_unknown_revocation_status();
570        // The builder should be Debug.
571        println!("{:?}", builder);
572        builder.build().unwrap();
573    }
574
575    #[test]
576    fn test_builder_no_roots() {
577        // Trying to create a client verifier builder with no trust anchors should fail at build time
578        let result = WebPkiClientVerifier::builder(RootCertStore::empty().into()).build();
579        assert!(matches!(result, Err(VerifierBuilderError::NoRootAnchors)));
580    }
581
582    #[test]
583    fn smoke() {
584        let all = vec![
585            VerifierBuilderError::NoRootAnchors,
586            VerifierBuilderError::InvalidCrl(crate::CertRevocationListError::ParseError),
587        ];
588
589        for err in all {
590            let _ = format!("{:?}", err);
591            let _ = format!("{}", err);
592        }
593    }
594}