rustls_platform_verifier/verification/
others.rs

1use super::log_server_cert;
2use once_cell::sync::OnceCell;
3use rustls::client::danger::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier};
4use rustls::client::WebPkiServerVerifier;
5use rustls::pki_types;
6use rustls::{
7    crypto::CryptoProvider, CertificateError, DigitallySignedStruct, Error as TlsError, OtherError,
8    SignatureScheme,
9};
10use std::fmt::Debug;
11use std::sync::{Arc, Mutex};
12
13/// A TLS certificate verifier that uses the system's root store and WebPKI.
14#[derive(Debug)]
15pub struct Verifier {
16    // We use a `OnceCell` so we only need
17    // to try loading native root certs once per verifier.
18    //
19    // We currently keep one set of certificates per-verifier so that
20    // locking and unlocking the application will pull fresh root
21    // certificates from disk, picking up on any changes
22    // that might have been made since.
23    inner: OnceCell<Arc<WebPkiServerVerifier>>,
24
25    // Extra trust anchors to add to the verifier above and beyond those provided by the
26    // platform via rustls-native-certs.
27    extra_roots: Mutex<Vec<pki_types::TrustAnchor<'static>>>,
28
29    /// Testing only: an additional root CA certificate to trust.
30    #[cfg(any(test, feature = "ffi-testing", feature = "dbg"))]
31    test_only_root_ca_override: Option<Vec<u8>>,
32
33    pub(super) crypto_provider: OnceCell<Arc<CryptoProvider>>,
34}
35
36impl Verifier {
37    /// Creates a new verifier whose certificate validation is provided by
38    /// WebPKI, using root certificates provided by the platform.
39    ///
40    /// A [`CryptoProvider`] must be set with
41    /// [`set_provider`][Verifier::set_provider]/[`with_provider`][Verifier::with_provider] or
42    /// [`CryptoProvider::install_default`] before the verifier can be used.
43    pub fn new() -> Self {
44        Self {
45            inner: OnceCell::new(),
46            extra_roots: Vec::new().into(),
47            #[cfg(any(test, feature = "ffi-testing", feature = "dbg"))]
48            test_only_root_ca_override: None,
49            crypto_provider: OnceCell::new(),
50        }
51    }
52
53    /// Creates a new verifier whose certificate validation is provided by
54    /// WebPKI, using root certificates provided by the platform and augmented by
55    /// the provided extra root certificates.
56    pub fn new_with_extra_roots(
57        roots: impl IntoIterator<Item = pki_types::CertificateDer<'static>>,
58    ) -> Result<Self, TlsError> {
59        Ok(Self {
60            inner: OnceCell::new(),
61            extra_roots: roots
62                .into_iter()
63                .flat_map(|root| {
64                    webpki::anchor_from_trusted_cert(&root).map(|anchor| anchor.to_owned())
65                })
66                .collect::<Vec<_>>()
67                .into(),
68            #[cfg(any(test, feature = "ffi-testing", feature = "dbg"))]
69            test_only_root_ca_override: None,
70            crypto_provider: OnceCell::new(),
71        })
72    }
73
74    /// Creates a test-only TLS certificate verifier which trusts our fake root CA cert.
75    #[cfg(any(test, feature = "ffi-testing", feature = "dbg"))]
76    pub(crate) fn new_with_fake_root(root: &[u8]) -> Self {
77        Self {
78            inner: OnceCell::new(),
79            extra_roots: Vec::new().into(),
80            test_only_root_ca_override: Some(root.into()),
81            crypto_provider: OnceCell::new(),
82        }
83    }
84
85    fn get_or_init_verifier(&self) -> Result<&Arc<WebPkiServerVerifier>, TlsError> {
86        self.inner.get_or_try_init(|| self.init_verifier())
87    }
88
89    // Attempt to load CA root certificates present on system, fallback to WebPKI roots if error
90    fn init_verifier(&self) -> Result<Arc<WebPkiServerVerifier>, TlsError> {
91        let mut root_store = rustls::RootCertStore::empty();
92
93        // For testing only: load fake root cert, instead of native/WebPKI roots
94        #[cfg(any(test, feature = "ffi-testing", feature = "dbg"))]
95        {
96            if let Some(test_root) = &self.test_only_root_ca_override {
97                let (added, ignored) =
98                    root_store.add_parsable_certificates([pki_types::CertificateDer::from(
99                        test_root.as_ref(),
100                    )]);
101                if (added != 1) || (ignored != 0) {
102                    panic!("Failed to insert fake, test-only root trust anchor");
103                }
104                return Ok(WebPkiServerVerifier::builder_with_provider(
105                    root_store.into(),
106                    Arc::clone(self.get_provider()),
107                )
108                .build()
109                .unwrap());
110            }
111        }
112
113        // Safety: There's no way for the mutex to be locked multiple times, so this is
114        // an infallible operation.
115        let mut extra_roots = self.extra_roots.try_lock().unwrap();
116        if !extra_roots.is_empty() {
117            let count = extra_roots.len();
118            root_store.extend(extra_roots.drain(..));
119            log::debug!(
120                "Loaded {count} extra CA certificates in addition to possible system roots",
121            );
122        }
123
124        #[cfg(all(
125            unix,
126            not(target_os = "android"),
127            not(target_vendor = "apple"),
128            not(target_arch = "wasm32"),
129        ))]
130        {
131            let result = rustls_native_certs::load_native_certs();
132            let (added, ignored) = root_store.add_parsable_certificates(result.certs);
133            if ignored != 0 {
134                log::warn!("Some CA root certificates were ignored due to errors");
135            }
136
137            for error in result.errors {
138                log::warn!("Error loading CA root certificate: {error}");
139            }
140
141            // Don't return an error if this fails when other roots have already been loaded via
142            // `new_with_extra_roots`. It leads to extra failure cases where connections would otherwise still work.
143            if root_store.is_empty() {
144                return Err(rustls::Error::General(
145                    "No CA certificates were loaded from the system".to_owned(),
146                ));
147            } else {
148                log::debug!("Loaded {added} CA certificates from the system");
149            }
150        }
151
152        #[cfg(target_arch = "wasm32")]
153        {
154            root_store.add_parsable_certificates(
155                webpki_root_certs::TLS_SERVER_ROOT_CERTS.iter().cloned(),
156            );
157        };
158
159        WebPkiServerVerifier::builder_with_provider(
160            root_store.into(),
161            Arc::clone(self.get_provider()),
162        )
163        .build()
164        .map_err(|e| TlsError::Other(OtherError(Arc::new(e))))
165    }
166}
167
168impl ServerCertVerifier for Verifier {
169    fn verify_server_cert(
170        &self,
171        end_entity: &pki_types::CertificateDer<'_>,
172        intermediates: &[pki_types::CertificateDer<'_>],
173        server_name: &pki_types::ServerName,
174        ocsp_response: &[u8],
175        now: pki_types::UnixTime,
176    ) -> Result<ServerCertVerified, TlsError> {
177        log_server_cert(end_entity);
178
179        self.get_or_init_verifier()?
180            .verify_server_cert(end_entity, intermediates, server_name, ocsp_response, now)
181            .map_err(map_webpki_errors)
182            // This only contains information from the system or other public
183            // bits of the TLS handshake, so it can't leak anything.
184            .map_err(|e| {
185                log::error!("failed to verify TLS certificate: {}", e);
186                e
187            })
188    }
189
190    fn verify_tls12_signature(
191        &self,
192        message: &[u8],
193        cert: &pki_types::CertificateDer<'_>,
194        dss: &DigitallySignedStruct,
195    ) -> Result<HandshakeSignatureValid, TlsError> {
196        self.get_or_init_verifier()?
197            .verify_tls12_signature(message, cert, dss)
198    }
199
200    fn verify_tls13_signature(
201        &self,
202        message: &[u8],
203        cert: &pki_types::CertificateDer<'_>,
204        dss: &DigitallySignedStruct,
205    ) -> Result<HandshakeSignatureValid, TlsError> {
206        self.get_or_init_verifier()?
207            .verify_tls13_signature(message, cert, dss)
208    }
209
210    fn supported_verify_schemes(&self) -> Vec<SignatureScheme> {
211        // XXX: Don't go through `self.verifier` here: It introduces extra failure
212        // cases and is strictly unneeded because `get_provider` is the same provider and
213        // set of algorithms passed into the wrapped `WebPkiServerVerifier`. Given this,
214        // the list of schemes are identical.
215        self.get_provider()
216            .signature_verification_algorithms
217            .supported_schemes()
218    }
219}
220
221impl Default for Verifier {
222    fn default() -> Self {
223        Self::new()
224    }
225}
226
227fn map_webpki_errors(err: TlsError) -> TlsError {
228    if let TlsError::InvalidCertificate(CertificateError::InvalidPurpose) = &err {
229        return TlsError::InvalidCertificate(CertificateError::Other(OtherError(Arc::new(
230            super::EkuError,
231        ))));
232    }
233
234    err
235}