rustls/webpki/
server_verifier.rs

1use alloc::vec::Vec;
2
3use pki_types::{CertificateDer, CertificateRevocationListDer, ServerName, UnixTime};
4use webpki::{CertRevocationList, ExpirationPolicy, RevocationCheckDepth, UnknownStatusPolicy};
5
6use crate::crypto::{CryptoProvider, WebPkiSupportedAlgorithms};
7use crate::sync::Arc;
8use crate::verify::{
9    DigitallySignedStruct, HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier,
10};
11use crate::webpki::verify::{
12    ParsedCertificate, verify_server_cert_signed_by_trust_anchor_impl, verify_tls12_signature,
13    verify_tls13_signature,
14};
15use crate::webpki::{VerifierBuilderError, parse_crls, verify_server_name};
16#[cfg(doc)]
17use crate::{ConfigBuilder, ServerConfig, crypto};
18use crate::{Error, RootCertStore, SignatureScheme};
19
20/// A builder for configuring a `webpki` server certificate verifier.
21///
22/// For more information, see the [`WebPkiServerVerifier`] documentation.
23#[derive(Debug, Clone)]
24pub struct ServerCertVerifierBuilder {
25    roots: Arc<RootCertStore>,
26    crls: Vec<CertificateRevocationListDer<'static>>,
27    revocation_check_depth: RevocationCheckDepth,
28    unknown_revocation_policy: UnknownStatusPolicy,
29    revocation_expiration_policy: ExpirationPolicy,
30    supported_algs: WebPkiSupportedAlgorithms,
31}
32
33impl ServerCertVerifierBuilder {
34    pub(crate) fn new(
35        roots: Arc<RootCertStore>,
36        supported_algs: WebPkiSupportedAlgorithms,
37    ) -> Self {
38        Self {
39            roots,
40            crls: Vec::new(),
41            revocation_check_depth: RevocationCheckDepth::Chain,
42            unknown_revocation_policy: UnknownStatusPolicy::Deny,
43            revocation_expiration_policy: ExpirationPolicy::Ignore,
44            supported_algs,
45        }
46    }
47
48    /// Verify the revocation state of presented client certificates against the provided
49    /// certificate revocation lists (CRLs). Calling `with_crls` multiple times appends the
50    /// given CRLs to the existing collection.
51    pub fn with_crls(
52        mut self,
53        crls: impl IntoIterator<Item = CertificateRevocationListDer<'static>>,
54    ) -> Self {
55        self.crls.extend(crls);
56        self
57    }
58
59    /// Only check the end entity certificate revocation status when using CRLs.
60    ///
61    /// If CRLs are provided using [`with_crls`][Self::with_crls] only check the end entity
62    /// certificate's revocation status. Overrides the default behavior of checking revocation
63    /// status for each certificate in the verified chain built to a trust anchor
64    /// (excluding the trust anchor itself).
65    ///
66    /// If no CRLs are provided then this setting has no effect. Neither the end entity certificate
67    /// or any intermediates will have revocation status checked.
68    pub fn only_check_end_entity_revocation(mut self) -> Self {
69        self.revocation_check_depth = RevocationCheckDepth::EndEntity;
70        self
71    }
72
73    /// Allow unknown certificate revocation status when using CRLs.
74    ///
75    /// If CRLs are provided with [`with_crls`][Self::with_crls] and it isn't possible to
76    /// determine the revocation status of a certificate, do not treat it as an error condition.
77    /// Overrides the default behavior where unknown revocation status is considered an error.
78    ///
79    /// If no CRLs are provided then this setting has no effect as revocation status checks
80    /// are not performed.
81    pub fn allow_unknown_revocation_status(mut self) -> Self {
82        self.unknown_revocation_policy = UnknownStatusPolicy::Allow;
83        self
84    }
85
86    /// Enforce the CRL nextUpdate field (i.e. expiration)
87    ///
88    /// If CRLs are provided with [`with_crls`][Self::with_crls] and the verification time is
89    /// beyond the time in the CRL nextUpdate field, it is expired and treated as an error condition.
90    /// Overrides the default behavior where expired CRLs are not treated as an error condition.
91    ///
92    /// If no CRLs are provided then this setting has no effect as revocation status checks
93    /// are not performed.
94    pub fn enforce_revocation_expiration(mut self) -> Self {
95        self.revocation_expiration_policy = ExpirationPolicy::Enforce;
96        self
97    }
98
99    /// Build a server certificate verifier, allowing control over the root certificates to use as
100    /// trust anchors, and to control how server certificate revocation checking is performed.
101    ///
102    /// If `with_signature_verification_algorithms` was not called on the builder, a default set of
103    /// signature verification algorithms is used, controlled by the selected [`crypto::CryptoProvider`].
104    ///
105    /// Once built, the provided `Arc<dyn ServerCertVerifier>` can be used with a Rustls
106    /// [`ServerConfig`] to configure client certificate validation using
107    /// [`with_client_cert_verifier`][ConfigBuilder<ClientConfig, WantsVerifier>::with_client_cert_verifier].
108    ///
109    /// # Errors
110    /// This function will return a [`VerifierBuilderError`] if:
111    /// 1. No trust anchors have been provided.
112    /// 2. DER encoded CRLs have been provided that can not be parsed successfully.
113    pub fn build(self) -> Result<Arc<WebPkiServerVerifier>, VerifierBuilderError> {
114        if self.roots.is_empty() {
115            return Err(VerifierBuilderError::NoRootAnchors);
116        }
117
118        Ok(WebPkiServerVerifier::new(
119            self.roots,
120            parse_crls(self.crls)?,
121            self.revocation_check_depth,
122            self.unknown_revocation_policy,
123            self.revocation_expiration_policy,
124            self.supported_algs,
125        )
126        .into())
127    }
128}
129
130/// Default `ServerCertVerifier`, see the trait impl for more information.
131#[allow(unreachable_pub)]
132#[derive(Debug)]
133pub struct WebPkiServerVerifier {
134    roots: Arc<RootCertStore>,
135    crls: Vec<CertRevocationList<'static>>,
136    revocation_check_depth: RevocationCheckDepth,
137    unknown_revocation_policy: UnknownStatusPolicy,
138    revocation_expiration_policy: ExpirationPolicy,
139    supported: WebPkiSupportedAlgorithms,
140}
141
142#[allow(unreachable_pub)]
143impl WebPkiServerVerifier {
144    /// Create a builder for the `webpki` server certificate verifier configuration using
145    /// the [process-default `CryptoProvider`][CryptoProvider#using-the-per-process-default-cryptoprovider].
146    ///
147    /// Server certificates will be verified using the trust anchors found in the provided `roots`.
148    ///
149    /// Use [`Self::builder_with_provider`] if you wish to specify an explicit provider.
150    ///
151    /// For more information, see the [`ServerCertVerifierBuilder`] documentation.
152    pub fn builder(roots: Arc<RootCertStore>) -> ServerCertVerifierBuilder {
153        Self::builder_with_provider(
154            roots,
155            CryptoProvider::get_default_or_install_from_crate_features().clone(),
156        )
157    }
158
159    /// Create a builder for the `webpki` server certificate verifier configuration using
160    /// a specified [`CryptoProvider`].
161    ///
162    /// Server certificates will be verified using the trust anchors found in the provided `roots`.
163    ///
164    /// The cryptography used comes from the specified [`CryptoProvider`].
165    ///
166    /// For more information, see the [`ServerCertVerifierBuilder`] documentation.
167    pub fn builder_with_provider(
168        roots: Arc<RootCertStore>,
169        provider: Arc<CryptoProvider>,
170    ) -> ServerCertVerifierBuilder {
171        ServerCertVerifierBuilder::new(roots, provider.signature_verification_algorithms)
172    }
173
174    /// Short-cut for creating a `WebPkiServerVerifier` that does not perform certificate revocation
175    /// checking, avoiding the need to use a builder.
176    pub(crate) fn new_without_revocation(
177        roots: impl Into<Arc<RootCertStore>>,
178        supported_algs: WebPkiSupportedAlgorithms,
179    ) -> Self {
180        Self::new(
181            roots,
182            Vec::default(),
183            RevocationCheckDepth::Chain,
184            UnknownStatusPolicy::Allow,
185            ExpirationPolicy::Ignore,
186            supported_algs,
187        )
188    }
189
190    /// Constructs a new `WebPkiServerVerifier`.
191    ///
192    /// * `roots` is the set of trust anchors to trust for issuing server certs.
193    /// * `crls` are a vec of owned certificate revocation lists (CRLs) to use for
194    ///   client certificate validation.
195    /// * `revocation_check_depth` controls which certificates have their revocation status checked
196    ///   when `crls` are provided.
197    /// * `unknown_revocation_policy` controls how certificates with an unknown revocation status
198    ///   are handled when `crls` are provided.
199    /// * `supported` is the set of supported algorithms that will be used for
200    ///   certificate verification and TLS handshake signature verification.
201    pub(crate) fn new(
202        roots: impl Into<Arc<RootCertStore>>,
203        crls: Vec<CertRevocationList<'static>>,
204        revocation_check_depth: RevocationCheckDepth,
205        unknown_revocation_policy: UnknownStatusPolicy,
206        revocation_expiration_policy: ExpirationPolicy,
207        supported: WebPkiSupportedAlgorithms,
208    ) -> Self {
209        Self {
210            roots: roots.into(),
211            crls,
212            revocation_check_depth,
213            unknown_revocation_policy,
214            revocation_expiration_policy,
215            supported,
216        }
217    }
218}
219
220impl ServerCertVerifier for WebPkiServerVerifier {
221    /// Will verify the certificate is valid in the following ways:
222    /// - Signed by a trusted `RootCertStore` CA
223    /// - Not Expired
224    /// - Valid for DNS entry
225    /// - Valid revocation status (if applicable).
226    ///
227    /// Depending on the verifier's configuration revocation status checking may be performed for
228    /// each certificate in the chain to a root CA (excluding the root itself), or only the
229    /// end entity certificate. Similarly, unknown revocation status may be treated as an error
230    /// or allowed based on configuration.
231    fn verify_server_cert(
232        &self,
233        end_entity: &CertificateDer<'_>,
234        intermediates: &[CertificateDer<'_>],
235        server_name: &ServerName<'_>,
236        _ocsp_response: &[u8],
237        now: UnixTime,
238    ) -> Result<ServerCertVerified, Error> {
239        let cert = ParsedCertificate::try_from(end_entity)?;
240
241        let crl_refs = self.crls.iter().collect::<Vec<_>>();
242
243        let revocation = if self.crls.is_empty() {
244            None
245        } else {
246            // Note: unwrap here is safe because RevocationOptionsBuilder only errors when given
247            //       empty CRLs.
248            Some(
249                webpki::RevocationOptionsBuilder::new(crl_refs.as_slice())
250                    // Note: safe to unwrap here - new is only fallible if no CRLs are provided
251                    //       and we verify this above.
252                    .unwrap()
253                    .with_depth(self.revocation_check_depth)
254                    .with_status_policy(self.unknown_revocation_policy)
255                    .with_expiration_policy(self.revocation_expiration_policy)
256                    .build(),
257            )
258        };
259
260        // Note: we use the crate-internal `_impl` fn here in order to provide revocation
261        // checking information, if applicable.
262        verify_server_cert_signed_by_trust_anchor_impl(
263            &cert,
264            &self.roots,
265            intermediates,
266            revocation,
267            now,
268            self.supported.all,
269        )?;
270
271        verify_server_name(&cert, server_name)?;
272        Ok(ServerCertVerified::assertion())
273    }
274
275    fn verify_tls12_signature(
276        &self,
277        message: &[u8],
278        cert: &CertificateDer<'_>,
279        dss: &DigitallySignedStruct,
280    ) -> Result<HandshakeSignatureValid, Error> {
281        verify_tls12_signature(message, cert, dss, &self.supported)
282    }
283
284    fn verify_tls13_signature(
285        &self,
286        message: &[u8],
287        cert: &CertificateDer<'_>,
288        dss: &DigitallySignedStruct,
289    ) -> Result<HandshakeSignatureValid, Error> {
290        verify_tls13_signature(message, cert, dss, &self.supported)
291    }
292
293    fn supported_verify_schemes(&self) -> Vec<SignatureScheme> {
294        self.supported.supported_schemes()
295    }
296
297    fn request_ocsp_response(&self) -> bool {
298        false
299    }
300}
301
302#[cfg(test)]
303#[macro_rules_attribute::apply(test_for_each_provider)]
304mod tests {
305    use std::prelude::v1::*;
306    use std::{println, vec};
307
308    use pki_types::pem::PemObject;
309    use pki_types::{CertificateDer, CertificateRevocationListDer};
310
311    use super::{VerifierBuilderError, WebPkiServerVerifier, provider};
312    use crate::RootCertStore;
313    use crate::sync::Arc;
314
315    fn load_crls(crls_der: &[&[u8]]) -> Vec<CertificateRevocationListDer<'static>> {
316        crls_der
317            .iter()
318            .map(|pem_bytes| CertificateRevocationListDer::from_pem_slice(pem_bytes).unwrap())
319            .collect()
320    }
321
322    fn test_crls() -> Vec<CertificateRevocationListDer<'static>> {
323        load_crls(&[
324            include_bytes!("../../../test-ca/ecdsa-p256/client.revoked.crl.pem").as_slice(),
325            include_bytes!("../../../test-ca/rsa-2048/client.revoked.crl.pem").as_slice(),
326        ])
327    }
328
329    fn load_roots(roots_der: &[&[u8]]) -> Arc<RootCertStore> {
330        let mut roots = RootCertStore::empty();
331        roots_der.iter().for_each(|der| {
332            roots
333                .add(CertificateDer::from(der.to_vec()))
334                .unwrap()
335        });
336        roots.into()
337    }
338
339    fn test_roots() -> Arc<RootCertStore> {
340        load_roots(&[
341            include_bytes!("../../../test-ca/ecdsa-p256/ca.der").as_slice(),
342            include_bytes!("../../../test-ca/rsa-2048/ca.der").as_slice(),
343        ])
344    }
345
346    #[test]
347    fn test_with_invalid_crls() {
348        // Trying to build a server verifier with invalid CRLs should error at build time.
349        let result = WebPkiServerVerifier::builder_with_provider(
350            test_roots(),
351            provider::default_provider().into(),
352        )
353        .with_crls(vec![CertificateRevocationListDer::from(vec![0xFF])])
354        .build();
355        assert!(matches!(result, Err(VerifierBuilderError::InvalidCrl(_))));
356    }
357
358    #[test]
359    fn test_with_crls_multiple_calls() {
360        // We should be able to call `with_crls` on a server verifier multiple times.
361        let initial_crls = test_crls();
362        let extra_crls =
363            load_crls(&[
364                include_bytes!("../../../test-ca/eddsa/client.revoked.crl.pem").as_slice(),
365            ]);
366
367        let builder = WebPkiServerVerifier::builder_with_provider(
368            test_roots(),
369            provider::default_provider().into(),
370        )
371        .with_crls(initial_crls.clone())
372        .with_crls(extra_crls.clone());
373
374        // There should be the expected number of crls.
375        assert_eq!(builder.crls.len(), initial_crls.len() + extra_crls.len());
376        // The builder should be Debug.
377        println!("{builder:?}");
378        builder.build().unwrap();
379    }
380
381    #[test]
382    fn test_builder_no_roots() {
383        // Trying to create a server verifier builder with no trust anchors should fail at build time
384        let result = WebPkiServerVerifier::builder_with_provider(
385            RootCertStore::empty().into(),
386            provider::default_provider().into(),
387        )
388        .build();
389        assert!(matches!(result, Err(VerifierBuilderError::NoRootAnchors)));
390    }
391
392    #[test]
393    fn test_server_verifier_ee_only() {
394        // We should be able to build a server cert. verifier that only checks the EE cert.
395        let builder = WebPkiServerVerifier::builder_with_provider(
396            test_roots(),
397            provider::default_provider().into(),
398        )
399        .only_check_end_entity_revocation();
400        // The builder should be Debug.
401        println!("{builder:?}");
402        builder.build().unwrap();
403    }
404
405    #[test]
406    fn test_server_verifier_allow_unknown() {
407        // We should be able to build a server cert. verifier that allows unknown revocation
408        // status.
409        let builder = WebPkiServerVerifier::builder_with_provider(
410            test_roots(),
411            provider::default_provider().into(),
412        )
413        .allow_unknown_revocation_status();
414        // The builder should be Debug.
415        println!("{builder:?}");
416        builder.build().unwrap();
417    }
418
419    #[test]
420    fn test_server_verifier_allow_unknown_ee_only() {
421        // We should be able to build a server cert. verifier that allows unknown revocation
422        // status and only checks the EE cert.
423        let builder = WebPkiServerVerifier::builder_with_provider(
424            test_roots(),
425            provider::default_provider().into(),
426        )
427        .allow_unknown_revocation_status()
428        .only_check_end_entity_revocation();
429        // The builder should be Debug.
430        println!("{builder:?}");
431        builder.build().unwrap();
432    }
433
434    #[test]
435    fn test_server_verifier_enforce_expiration() {
436        // We should be able to build a server cert. verifier that allows unknown revocation
437        // status.
438        let builder = WebPkiServerVerifier::builder_with_provider(
439            test_roots(),
440            provider::default_provider().into(),
441        )
442        .enforce_revocation_expiration();
443        // The builder should be Debug.
444        println!("{builder:?}");
445        builder.build().unwrap();
446    }
447}