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}