Skip to main content

rustls_platform_verifier/verification/
others.rs

1use std::fmt::Debug;
2use std::sync::Arc;
3
4use rustls::client::danger::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier};
5use rustls::client::WebPkiServerVerifier;
6use rustls::pki_types;
7use rustls::{
8    crypto::CryptoProvider, CertificateError, DigitallySignedStruct, Error as TlsError, OtherError,
9    SignatureScheme,
10};
11
12use super::log_server_cert;
13
14/// A TLS certificate verifier that uses the system's root store and WebPKI.
15#[derive(Debug)]
16pub struct Verifier {
17    // We currently keep one set of certificates per-verifier so that
18    // recreating the verifier will pull fresh root certificates from disk,
19    // picking up on any changes that might have been made since.
20    inner: Arc<WebPkiServerVerifier>,
21}
22
23impl Verifier {
24    /// Creates a new verifier whose certificate validation is provided by
25    /// WebPKI, using root certificates provided by the platform.
26    #[cfg_attr(docsrs, doc(cfg(all())))]
27    pub fn new(crypto_provider: Arc<CryptoProvider>) -> Result<Self, TlsError> {
28        Self::new_inner([], None, crypto_provider)
29    }
30
31    /// Creates a new verifier whose certificate validation is provided by
32    /// WebPKI, using root certificates provided by the platform and augmented by
33    /// the provided extra root certificates.
34    #[cfg_attr(docsrs, doc(cfg(not(target_os = "android"))))]
35    pub fn new_with_extra_roots(
36        extra_roots: impl IntoIterator<Item = pki_types::CertificateDer<'static>>,
37        crypto_provider: Arc<CryptoProvider>,
38    ) -> Result<Self, TlsError> {
39        Self::new_inner(extra_roots, None, crypto_provider)
40    }
41
42    /// Creates a test-only TLS certificate verifier which trusts our fake root CA cert.
43    #[cfg(any(test, feature = "ffi-testing", feature = "dbg"))]
44    pub(crate) fn new_with_fake_root(
45        root: pki_types::CertificateDer<'static>,
46        crypto_provider: Arc<CryptoProvider>,
47    ) -> Self {
48        Self::new_inner([], Some(root), crypto_provider)
49            .expect("failed to create verifier with fake root")
50    }
51
52    /// Creates a new verifier whose certificate validation is provided by
53    /// WebPKI, using root certificates provided by the platform and augmented by
54    /// the provided extra root certificates.
55    fn new_inner(
56        extra_roots: impl IntoIterator<Item = pki_types::CertificateDer<'static>>,
57        #[allow(unused)] // test_root is only used in tests
58        test_root: Option<pki_types::CertificateDer<'static>>,
59        crypto_provider: Arc<CryptoProvider>,
60    ) -> Result<Self, TlsError> {
61        let mut root_store = rustls::RootCertStore::empty();
62
63        // For testing only: load fake root cert, instead of native/WebPKI roots
64        #[cfg(any(test, feature = "ffi-testing", feature = "dbg"))]
65        {
66            if let Some(test_root) = test_root {
67                root_store.add(test_root)?;
68                return Ok(Self {
69                    inner: WebPkiServerVerifier::builder_with_provider(
70                        root_store.into(),
71                        crypto_provider.clone(),
72                    )
73                    .build()
74                    .map_err(|e| TlsError::Other(OtherError(Arc::new(e))))?,
75                });
76            }
77        }
78
79        // While we ignore invalid certificates from the system, we forward errors from
80        // parsing the extra roots to the caller.
81        for cert in extra_roots {
82            root_store.add(cert)?;
83        }
84
85        #[cfg(all(
86            unix,
87            not(target_os = "android"),
88            not(target_vendor = "apple"),
89            not(target_arch = "wasm32"),
90        ))]
91        {
92            let result = rustls_native_certs::load_native_certs();
93            let (added, ignored) = root_store.add_parsable_certificates(result.certs);
94            if ignored > 0 {
95                log::warn!("{ignored} platform CA root certificates were ignored due to errors");
96            }
97
98            for error in result.errors {
99                log::warn!("Error loading CA root certificate: {error}");
100            }
101
102            // Don't return an error if this fails when other roots have already been loaded via
103            // `new_with_extra_roots`. It leads to extra failure cases where connections would otherwise still work.
104            if root_store.is_empty() {
105                return Err(rustls::Error::General(
106                    "No CA certificates were loaded from the system".to_owned(),
107                ));
108            } else {
109                log::debug!("Loaded {added} CA root certificates from the system");
110            }
111        }
112
113        #[cfg(target_arch = "wasm32")]
114        {
115            root_store.add_parsable_certificates(
116                webpki_root_certs::TLS_SERVER_ROOT_CERTS.iter().cloned(),
117            );
118        };
119
120        Ok(Self {
121            inner: WebPkiServerVerifier::builder_with_provider(root_store.into(), crypto_provider)
122                .build()
123                .map_err(|e| TlsError::Other(OtherError(Arc::new(e))))?,
124        })
125    }
126}
127
128#[cfg_attr(docsrs, doc(cfg(all())))]
129impl ServerCertVerifier for Verifier {
130    fn verify_server_cert(
131        &self,
132        end_entity: &pki_types::CertificateDer<'_>,
133        intermediates: &[pki_types::CertificateDer<'_>],
134        server_name: &pki_types::ServerName,
135        ocsp_response: &[u8],
136        now: pki_types::UnixTime,
137    ) -> Result<ServerCertVerified, TlsError> {
138        log_server_cert(end_entity);
139
140        self.inner
141            .verify_server_cert(end_entity, intermediates, server_name, ocsp_response, now)
142            .map_err(map_webpki_errors)
143            // This only contains information from the system or other public
144            // bits of the TLS handshake, so it can't leak anything.
145            .map_err(|e| {
146                log::error!("failed to verify TLS certificate: {}", e);
147                e
148            })
149    }
150
151    fn verify_tls12_signature(
152        &self,
153        message: &[u8],
154        cert: &pki_types::CertificateDer<'_>,
155        dss: &DigitallySignedStruct,
156    ) -> Result<HandshakeSignatureValid, TlsError> {
157        self.inner.verify_tls12_signature(message, cert, dss)
158    }
159
160    fn verify_tls13_signature(
161        &self,
162        message: &[u8],
163        cert: &pki_types::CertificateDer<'_>,
164        dss: &DigitallySignedStruct,
165    ) -> Result<HandshakeSignatureValid, TlsError> {
166        self.inner.verify_tls13_signature(message, cert, dss)
167    }
168
169    fn supported_verify_schemes(&self) -> Vec<SignatureScheme> {
170        self.inner.supported_verify_schemes()
171    }
172}
173
174fn map_webpki_errors(err: TlsError) -> TlsError {
175    match &err {
176        TlsError::InvalidCertificate(CertificateError::InvalidPurpose)
177        | TlsError::InvalidCertificate(CertificateError::InvalidPurposeContext { .. }) => {
178            TlsError::InvalidCertificate(CertificateError::Other(OtherError(Arc::new(
179                super::EkuError,
180            ))))
181        }
182        _ => err,
183    }
184}