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(
122                root_store.into(),
123                crypto_provider.clone(),
124            )
125            .build()
126            .map_err(|e| TlsError::Other(OtherError(Arc::new(e))))?,
127        })
128    }
129}
130
131#[cfg_attr(docsrs, doc(cfg(all())))]
132impl ServerCertVerifier for Verifier {
133    fn verify_server_cert(
134        &self,
135        end_entity: &pki_types::CertificateDer<'_>,
136        intermediates: &[pki_types::CertificateDer<'_>],
137        server_name: &pki_types::ServerName,
138        ocsp_response: &[u8],
139        now: pki_types::UnixTime,
140    ) -> Result<ServerCertVerified, TlsError> {
141        log_server_cert(end_entity);
142
143        self.inner
144            .verify_server_cert(end_entity, intermediates, server_name, ocsp_response, now)
145            .map_err(map_webpki_errors)
146            // This only contains information from the system or other public
147            // bits of the TLS handshake, so it can't leak anything.
148            .map_err(|e| {
149                log::error!("failed to verify TLS certificate: {}", e);
150                e
151            })
152    }
153
154    fn verify_tls12_signature(
155        &self,
156        message: &[u8],
157        cert: &pki_types::CertificateDer<'_>,
158        dss: &DigitallySignedStruct,
159    ) -> Result<HandshakeSignatureValid, TlsError> {
160        self.inner.verify_tls12_signature(message, cert, dss)
161    }
162
163    fn verify_tls13_signature(
164        &self,
165        message: &[u8],
166        cert: &pki_types::CertificateDer<'_>,
167        dss: &DigitallySignedStruct,
168    ) -> Result<HandshakeSignatureValid, TlsError> {
169        self.inner.verify_tls13_signature(message, cert, dss)
170    }
171
172    fn supported_verify_schemes(&self) -> Vec<SignatureScheme> {
173        self.inner.supported_verify_schemes()
174    }
175}
176
177fn map_webpki_errors(err: TlsError) -> TlsError {
178    match &err {
179        TlsError::InvalidCertificate(CertificateError::InvalidPurpose)
180        | TlsError::InvalidCertificate(CertificateError::InvalidPurposeContext { .. }) => {
181            TlsError::InvalidCertificate(CertificateError::Other(OtherError(Arc::new(
182                super::EkuError,
183            ))))
184        }
185        _ => err,
186    }
187}