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::TrustAnchor<'static>>,
58    ) -> Self {
59        Self {
60            inner: OnceCell::new(),
61            extra_roots: roots.into_iter().collect::<Vec<_>>().into(),
62            #[cfg(any(test, feature = "ffi-testing", feature = "dbg"))]
63            test_only_root_ca_override: None,
64            crypto_provider: OnceCell::new(),
65        }
66    }
67
68    /// Creates a test-only TLS certificate verifier which trusts our fake root CA cert.
69    #[cfg(any(test, feature = "ffi-testing", feature = "dbg"))]
70    pub(crate) fn new_with_fake_root(root: &[u8]) -> Self {
71        Self {
72            inner: OnceCell::new(),
73            extra_roots: Vec::new().into(),
74            test_only_root_ca_override: Some(root.into()),
75            crypto_provider: OnceCell::new(),
76        }
77    }
78
79    fn get_or_init_verifier(&self) -> Result<&Arc<WebPkiServerVerifier>, TlsError> {
80        self.inner.get_or_try_init(|| self.init_verifier())
81    }
82
83    // Attempt to load CA root certificates present on system, fallback to WebPKI roots if error
84    fn init_verifier(&self) -> Result<Arc<WebPkiServerVerifier>, TlsError> {
85        let mut root_store = rustls::RootCertStore::empty();
86
87        // For testing only: load fake root cert, instead of native/WebPKI roots
88        #[cfg(any(test, feature = "ffi-testing", feature = "dbg"))]
89        {
90            if let Some(test_root) = &self.test_only_root_ca_override {
91                let (added, ignored) =
92                    root_store.add_parsable_certificates([pki_types::CertificateDer::from(
93                        test_root.as_ref(),
94                    )]);
95                if (added != 1) || (ignored != 0) {
96                    panic!("Failed to insert fake, test-only root trust anchor");
97                }
98                return Ok(WebPkiServerVerifier::builder_with_provider(
99                    root_store.into(),
100                    Arc::clone(self.get_provider()),
101                )
102                .build()
103                .unwrap());
104            }
105        }
106
107        // Safety: There's no way for the mutex to be locked multiple times, so this is
108        // an infallible operation.
109        let mut extra_roots = self.extra_roots.try_lock().unwrap();
110        if !extra_roots.is_empty() {
111            let count = extra_roots.len();
112            root_store.extend(extra_roots.drain(..));
113            log::debug!(
114                "Loaded {count} extra CA certificates in addition to possible system roots",
115            );
116        }
117
118        #[cfg(all(
119            unix,
120            not(target_os = "android"),
121            not(target_os = "macos"),
122            not(target_os = "ios"),
123            not(target_os = "tvos"),
124            not(target_arch = "wasm32"),
125        ))]
126        match rustls_native_certs::load_native_certs() {
127            Ok(certs) => {
128                let (added, ignored) = root_store.add_parsable_certificates(certs);
129
130                if ignored != 0 {
131                    log::warn!("Some CA root certificates were ignored due to errors");
132                }
133
134                if root_store.is_empty() {
135                    log::error!("No CA certificates were loaded from the system");
136                } else {
137                    log::debug!("Loaded {added} CA certificates from the system");
138                }
139            }
140            Err(err) => {
141                // This only contains a path to a system directory:
142                // https://github.com/rustls/rustls-native-certs/blob/bc13b9a6bfc2e1eec881597055ca49accddd972a/src/lib.rs#L91-L94
143                const MSG: &str = "failed to load system root certificates: ";
144
145                // Don't return an error if this fails when other roots have already been loaded via
146                // `new_with_extra_roots`. It leads to extra failure cases where connections would otherwise still work.
147                if root_store.is_empty() {
148                    return Err(rustls::Error::General(format!("{MSG}{err}")));
149                } else {
150                    log::error!("{MSG}{err}");
151                }
152            }
153        };
154
155        #[cfg(target_arch = "wasm32")]
156        {
157            root_store.add_trust_anchors(webpki_roots::TLS_SERVER_ROOTS.iter().map(|root| {
158                rustls::OwnedTrustAnchor::from_subject_spki_name_constraints(
159                    root.subject,
160                    root.spki,
161                    root.name_constraints,
162                )
163            }));
164        };
165
166        WebPkiServerVerifier::builder_with_provider(
167            root_store.into(),
168            Arc::clone(self.get_provider()),
169        )
170        .build()
171        .map_err(|e| TlsError::Other(OtherError(Arc::new(e))))
172    }
173}
174
175impl ServerCertVerifier for Verifier {
176    fn verify_server_cert(
177        &self,
178        end_entity: &pki_types::CertificateDer<'_>,
179        intermediates: &[pki_types::CertificateDer<'_>],
180        server_name: &pki_types::ServerName,
181        ocsp_response: &[u8],
182        now: pki_types::UnixTime,
183    ) -> Result<ServerCertVerified, TlsError> {
184        log_server_cert(end_entity);
185
186        self.get_or_init_verifier()?
187            .verify_server_cert(end_entity, intermediates, server_name, ocsp_response, now)
188            .map_err(map_webpki_errors)
189            // This only contains information from the system or other public
190            // bits of the TLS handshake, so it can't leak anything.
191            .map_err(|e| {
192                log::error!("failed to verify TLS certificate: {}", e);
193                e
194            })
195    }
196
197    fn verify_tls12_signature(
198        &self,
199        message: &[u8],
200        cert: &pki_types::CertificateDer<'_>,
201        dss: &DigitallySignedStruct,
202    ) -> Result<HandshakeSignatureValid, TlsError> {
203        self.get_or_init_verifier()?
204            .verify_tls12_signature(message, cert, dss)
205    }
206
207    fn verify_tls13_signature(
208        &self,
209        message: &[u8],
210        cert: &pki_types::CertificateDer<'_>,
211        dss: &DigitallySignedStruct,
212    ) -> Result<HandshakeSignatureValid, TlsError> {
213        self.get_or_init_verifier()?
214            .verify_tls13_signature(message, cert, dss)
215    }
216
217    fn supported_verify_schemes(&self) -> Vec<SignatureScheme> {
218        match self.get_or_init_verifier() {
219            Ok(v) => v.supported_verify_schemes(),
220            Err(_) => Vec::default(),
221        }
222    }
223}
224
225impl Default for Verifier {
226    fn default() -> Self {
227        Self::new()
228    }
229}
230
231fn map_webpki_errors(err: TlsError) -> TlsError {
232    if let TlsError::InvalidCertificate(CertificateError::Other(other_err)) = &err {
233        if let Some(webpki::Error::RequiredEkuNotFound) =
234            other_err.0.downcast_ref::<webpki::Error>()
235        {
236            return TlsError::InvalidCertificate(CertificateError::Other(OtherError(Arc::new(
237                super::EkuError,
238            ))));
239        }
240    }
241
242    err
243}