iroh_relay/
tls.rs

1//! TLS verification configuration for iroh and iroh-relay.
2
3use std::{io, sync::Arc};
4
5use rustls::{
6    client::{ClientConfig, WebPkiServerVerifier, danger::ServerCertVerifier},
7    crypto::CryptoProvider,
8};
9use webpki_types::CertificateDer;
10
11/// Configures the trusted CA root certificates for non-iroh TLS connections.
12///
13/// These roots are used whenever iroh establishes standard TLS connections to
14/// external services, such as iroh relays, pkarr servers, or DNS-over-HTTPS
15/// resolvers.
16///
17/// The configured Certificate Authority (CA) roots are only used for verifying
18/// the validity of TLS certificates presented by those external services. These
19/// CAs don't need to be trusted for the integrity or authenticity of native
20/// iroh connections, which rely on iroh's own cryptographic authentication mechanisms.
21#[derive(Debug, Clone)]
22pub struct CaRootsConfig {
23    mode: Mode,
24    extra_roots: Vec<CertificateDer<'static>>,
25}
26
27#[derive(Debug, Clone)]
28enum Mode {
29    /// Use a compiled-in copy of the root certificates trusted by Mozilla.
30    ///
31    /// See [`webpki_roots`].
32    EmbeddedWebPki,
33    /// Use the operating system's certificate facilities for verifying the validity of TLS certificates.
34    ///
35    /// See [`rustls_platform_verifier`] for details how roots are retrieved on different platforms.
36    #[cfg(feature = "platform-verifier")]
37    System,
38    /// Only trust explicitly set root certificates.
39    ExtraRootsOnly,
40    /// INSECURE: Do not verify server certificates at all.
41    ///
42    /// May only be used in tests or local development setups.
43    #[cfg(any(test, feature = "test-utils"))]
44    InsecureSkipVerify,
45}
46
47impl Default for CaRootsConfig {
48    fn default() -> Self {
49        Self {
50            mode: Mode::EmbeddedWebPki,
51            extra_roots: vec![],
52        }
53    }
54}
55
56impl CaRootsConfig {
57    /// Use the operating system's certificate facilities for verifying the validity of TLS certificates.
58    ///
59    /// See [`rustls_platform_verifier`] for details how trust anchors are retrieved on different platforms.
60    ///
61    /// Note: Additional certificates added via [`Self::with_extra_roots`] will be ignored on Android due to
62    /// missing support in [`rustls`].
63    #[cfg(feature = "platform-verifier")]
64    pub fn system() -> Self {
65        Self {
66            mode: Mode::System,
67            extra_roots: Vec::new(),
68        }
69    }
70
71    /// Use a compiled-in copy of the root certificates trusted by Mozilla.
72    ///
73    /// See [`webpki_roots`] for details.
74    pub fn embedded() -> Self {
75        Self {
76            mode: Mode::EmbeddedWebPki,
77            extra_roots: Vec::new(),
78        }
79    }
80
81    /// INSECURE: Do not verify server certificates at all.
82    ///
83    /// May only be used in tests or local development setups.
84    #[cfg(any(test, feature = "test-utils"))]
85    pub fn insecure_skip_verify() -> Self {
86        Self {
87            mode: Mode::InsecureSkipVerify,
88            extra_roots: Vec::new(),
89        }
90    }
91
92    /// Only trust the explicitly set root certificates.
93    pub fn custom(roots: impl IntoIterator<Item = CertificateDer<'static>>) -> Self {
94        Self {
95            mode: Mode::ExtraRootsOnly,
96            extra_roots: roots.into_iter().collect(),
97        }
98    }
99
100    /// Add additional root certificates to the list of trusted certificates.
101    pub fn with_extra_roots(
102        mut self,
103        extra_roots: impl IntoIterator<Item = CertificateDer<'static>>,
104    ) -> Self {
105        self.extra_roots.extend(extra_roots);
106        self
107    }
108
109    /// Builds a [`ServerCertVerifier`] from this config.
110    pub fn server_cert_verifier(
111        &self,
112        crypto_provider: Arc<CryptoProvider>,
113    ) -> io::Result<Arc<dyn ServerCertVerifier>> {
114        Ok(match self.mode {
115            #[cfg(feature = "platform-verifier")]
116            Mode::System => {
117                #[cfg(not(target_os = "android"))]
118                let verifier = rustls_platform_verifier::Verifier::new_with_extra_roots(
119                    self.extra_roots.clone(),
120                    crypto_provider,
121                );
122                #[cfg(target_os = "android")]
123                let verifier = rustls_platform_verifier::Verifier::new(crypto_provider);
124                Arc::new(verifier.map_err(io::Error::other)?)
125            }
126
127            Mode::EmbeddedWebPki => {
128                let mut root_store = rustls::RootCertStore {
129                    roots: webpki_roots::TLS_SERVER_ROOTS.to_vec(),
130                };
131                root_store.add_parsable_certificates(self.extra_roots.clone());
132                WebPkiServerVerifier::builder_with_provider(Arc::new(root_store), crypto_provider)
133                    .build()
134                    .map_err(io::Error::other)?
135            }
136            Mode::ExtraRootsOnly => {
137                let mut root_store = rustls::RootCertStore { roots: vec![] };
138                root_store.add_parsable_certificates(self.extra_roots.clone());
139                WebPkiServerVerifier::builder_with_provider(Arc::new(root_store), crypto_provider)
140                    .build()
141                    .map_err(io::Error::other)?
142            }
143            #[cfg(any(test, feature = "test-utils"))]
144            Mode::InsecureSkipVerify => Arc::new(self::no_cert_verifier::NoCertVerifier),
145        })
146    }
147
148    /// Build a [`ClientConfig`] from this config.
149    pub fn client_config(&self, crypto_provider: Arc<CryptoProvider>) -> io::Result<ClientConfig> {
150        let verifier = self.server_cert_verifier(crypto_provider.clone())?;
151        let config = ClientConfig::builder_with_provider(crypto_provider)
152            .with_safe_default_protocol_versions()
153            .expect("protocols supported by ring")
154            .dangerous()
155            .with_custom_certificate_verifier(verifier)
156            .with_no_client_auth();
157        Ok(config)
158    }
159}
160
161/// Returns iroh's default crypto provider.
162///
163/// Currently, this is [`rustls::crypto::ring`].
164pub fn default_provider() -> Arc<CryptoProvider> {
165    Arc::new(rustls::crypto::ring::default_provider())
166}
167
168#[cfg(any(test, feature = "test-utils"))]
169mod no_cert_verifier {
170    use rustls::{
171        client::danger::{ServerCertVerified, ServerCertVerifier},
172        pki_types::{CertificateDer, ServerName},
173    };
174
175    /// Used to allow self signed certificates in tests
176    #[derive(Debug)]
177    pub(super) struct NoCertVerifier;
178
179    impl ServerCertVerifier for NoCertVerifier {
180        fn verify_server_cert(
181            &self,
182            _end_entity: &CertificateDer,
183            _intermediates: &[CertificateDer],
184            _server_name: &ServerName,
185            _ocsp_response: &[u8],
186            _now: rustls::pki_types::UnixTime,
187        ) -> Result<ServerCertVerified, rustls::Error> {
188            Ok(ServerCertVerified::assertion())
189        }
190        fn verify_tls12_signature(
191            &self,
192            _message: &[u8],
193            _cert: &CertificateDer<'_>,
194            _dss: &rustls::DigitallySignedStruct,
195        ) -> Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
196            Ok(rustls::client::danger::HandshakeSignatureValid::assertion())
197        }
198
199        fn verify_tls13_signature(
200            &self,
201            _message: &[u8],
202            _cert: &CertificateDer<'_>,
203            _dss: &rustls::DigitallySignedStruct,
204        ) -> Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
205            Ok(rustls::client::danger::HandshakeSignatureValid::assertion())
206        }
207
208        fn supported_verify_schemes(&self) -> Vec<rustls::SignatureScheme> {
209            super::default_provider()
210                .signature_verification_algorithms
211                .supported_schemes()
212        }
213    }
214}