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