rustls/webpki/
server_verifier.rs

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