/rust/registry/src/index.crates.io-1949cf8c6b5b557f/ureq-3.3.0/src/tls/mod.rs
Line | Count | Source |
1 | | //! TLS for handling `https`. |
2 | | |
3 | | use std::collections::hash_map::DefaultHasher; |
4 | | use std::fmt; |
5 | | use std::hash::{Hash, Hasher}; |
6 | | use std::sync::Arc; |
7 | | |
8 | | mod cert; |
9 | | pub use cert::{Certificate, PemItem, PrivateKey, parse_pem}; |
10 | | |
11 | | #[cfg(feature = "_rustls")] |
12 | | pub(crate) mod rustls; |
13 | | |
14 | | #[cfg(feature = "native-tls")] |
15 | | pub(crate) mod native_tls; |
16 | | |
17 | | /// Setting for which TLS provider to use. |
18 | | /// |
19 | | /// Defaults to [`Rustls`][Self::Rustls] because this has the highest chance |
20 | | /// to compile and "just work" straight out of the box without installing additional |
21 | | /// development dependencies. |
22 | | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)] |
23 | | #[non_exhaustive] |
24 | | pub enum TlsProvider { |
25 | | /// [Rustls](https://crates.io/crates/rustls) with the |
26 | | /// [process-wide default cryptographic backend](https://docs.rs/rustls/latest/rustls/crypto/struct.CryptoProvider.html#method.install_default), |
27 | | /// or [Ring](https://crates.io/crates/ring) if no process-wide default is set. |
28 | | /// |
29 | | /// Requires the feature flag **rustls**. |
30 | | /// |
31 | | /// This is the default. |
32 | | #[default] |
33 | | Rustls, |
34 | | |
35 | | /// [Native-TLS](https://crates.io/crates/native-tls) for cases where it's important to |
36 | | /// use the TLS libraries installed on the host running ureq. |
37 | | /// |
38 | | /// Requires the feature flag **native-tls** and that using an [`Agent`](crate::Agent) with |
39 | | /// this config option set in the [`TlsConfig`]. |
40 | | /// |
41 | | /// The setting is never picked up automatically. |
42 | | NativeTls, |
43 | | } |
44 | | |
45 | | impl TlsProvider { |
46 | 0 | pub(crate) fn is_feature_enabled(&self) -> bool { |
47 | 0 | match self { |
48 | | TlsProvider::Rustls => { |
49 | 0 | cfg!(feature = "_rustls") |
50 | | } |
51 | | TlsProvider::NativeTls => { |
52 | 0 | cfg!(feature = "native-tls") |
53 | | } |
54 | | } |
55 | 0 | } |
56 | | |
57 | 0 | pub(crate) fn feature_name(&self) -> &'static str { |
58 | 0 | match self { |
59 | 0 | TlsProvider::Rustls => "rustls", |
60 | 0 | TlsProvider::NativeTls => "native-tls", |
61 | | } |
62 | 0 | } |
63 | | } |
64 | | |
65 | | /// Configuration of TLS. |
66 | | /// |
67 | | /// This configuration is in common for both the different TLS mechanisms (available through |
68 | | /// feature flags **rustls** and **native-tls**). |
69 | | #[derive(Clone)] |
70 | | pub struct TlsConfig { |
71 | | provider: TlsProvider, |
72 | | client_cert: Option<ClientCert>, |
73 | | root_certs: RootCerts, |
74 | | use_sni: bool, |
75 | | disable_verification: bool, |
76 | | #[cfg(feature = "_rustls")] |
77 | | rustls_crypto_provider: Option<Arc<::rustls::crypto::CryptoProvider>>, |
78 | | } |
79 | | |
80 | | impl TlsConfig { |
81 | | /// Builder to make a bespoke config. |
82 | 0 | pub fn builder() -> TlsConfigBuilder { |
83 | 0 | TlsConfigBuilder { |
84 | 0 | config: TlsConfig::default(), |
85 | 0 | } |
86 | 0 | } |
87 | | |
88 | 0 | pub(crate) fn hash_value(&self) -> u64 { |
89 | 0 | let mut hasher = DefaultHasher::new(); |
90 | 0 | self.hash(&mut hasher); |
91 | 0 | hasher.finish() |
92 | 0 | } |
93 | | } |
94 | | |
95 | | impl TlsConfig { |
96 | | /// The provider to use. |
97 | | /// |
98 | | /// Defaults to [`TlsProvider::Rustls`]. |
99 | 0 | pub fn provider(&self) -> TlsProvider { |
100 | 0 | self.provider |
101 | 0 | } |
102 | | |
103 | | /// Client certificate chain with corresponding private key. |
104 | | /// |
105 | | /// Defaults to `None`. |
106 | 0 | pub fn client_cert(&self) -> Option<&ClientCert> { |
107 | 0 | self.client_cert.as_ref() |
108 | 0 | } |
109 | | |
110 | | /// The set of trusted root certificates to use to validate server certificates. |
111 | | /// |
112 | | /// Defaults to `WebPki`. |
113 | 0 | pub fn root_certs(&self) -> &RootCerts { |
114 | 0 | &self.root_certs |
115 | 0 | } |
116 | | |
117 | | /// Whether to send SNI (Server Name Indication) to the remote server. |
118 | | /// |
119 | | /// This is used by the server to determine which domain/certificate we are connecting |
120 | | /// to for servers where multiple domains/sites are hosted on the same IP. |
121 | | /// |
122 | | /// Defaults to `true`. |
123 | 0 | pub fn use_sni(&self) -> bool { |
124 | 0 | self.use_sni |
125 | 0 | } |
126 | | |
127 | | /// **WARNING** Disable all server certificate verification. |
128 | | /// |
129 | | /// This breaks encryption and leaks secrets. Must never be enabled for code where |
130 | | /// any level of security is required. |
131 | 0 | pub fn disable_verification(&self) -> bool { |
132 | 0 | self.disable_verification |
133 | 0 | } |
134 | | |
135 | | /// Specific `CryptoProvider` to use for `rustls`. |
136 | | /// |
137 | | /// # UNSTABLE API |
138 | | /// |
139 | | /// **NOTE: This API is not guaranteed for semver.** |
140 | | /// |
141 | | /// `rustls` is not (yet) semver 1.x and ureq can't promise that this API is upheld. |
142 | | /// If `rustls` makes a breaking change regarding `CryptoProvider` their configuration, |
143 | | /// or incompatible data types between rustls versions, ureq will _NOT_ bump a major version. |
144 | | /// |
145 | | /// ureq will update to the latest `rustls` minor version using ureq minor versions. |
146 | | #[cfg(feature = "_rustls")] |
147 | 0 | pub fn unversioned_rustls_crypto_provider( |
148 | 0 | &self, |
149 | 0 | ) -> &Option<Arc<::rustls::crypto::CryptoProvider>> { |
150 | 0 | &self.rustls_crypto_provider |
151 | 0 | } |
152 | | } |
153 | | |
154 | | /// Builder of [`TlsConfig`] |
155 | | pub struct TlsConfigBuilder { |
156 | | config: TlsConfig, |
157 | | } |
158 | | |
159 | | impl TlsConfigBuilder { |
160 | | /// The provider to use. |
161 | | /// |
162 | | /// Defaults to [`TlsProvider::Rustls`]. |
163 | 0 | pub fn provider(mut self, v: TlsProvider) -> Self { |
164 | 0 | self.config.provider = v; |
165 | 0 | self |
166 | 0 | } |
167 | | |
168 | | /// Client certificate chain with corresponding private key. |
169 | | /// |
170 | | /// Defaults to `None`. |
171 | 0 | pub fn client_cert(mut self, v: Option<ClientCert>) -> Self { |
172 | 0 | self.config.client_cert = v; |
173 | 0 | self |
174 | 0 | } |
175 | | |
176 | | /// The set of trusted root certificates to use to validate server certificates. |
177 | | /// |
178 | | /// Defaults to `WebPki`. |
179 | 0 | pub fn root_certs(mut self, v: RootCerts) -> Self { |
180 | 0 | self.config.root_certs = v; |
181 | 0 | self |
182 | 0 | } |
183 | | |
184 | | /// Whether to send SNI (Server Name Indication) to the remote server. |
185 | | /// |
186 | | /// This is used by the server to determine which domain/certificate we are connecting |
187 | | /// to for servers where multiple domains/sites are hosted on the same IP. |
188 | | /// |
189 | | /// Defaults to `true`. |
190 | 0 | pub fn use_sni(mut self, v: bool) -> Self { |
191 | 0 | self.config.use_sni = v; |
192 | 0 | self |
193 | 0 | } |
194 | | |
195 | | /// **WARNING** Disable all server certificate verification. |
196 | | /// |
197 | | /// This breaks encryption and leaks secrets. Must never be enabled for code where |
198 | | /// any level of security is required. |
199 | 0 | pub fn disable_verification(mut self, v: bool) -> Self { |
200 | 0 | self.config.disable_verification = v; |
201 | 0 | self |
202 | 0 | } |
203 | | |
204 | | /// Specific `CryptoProvider` to use for `rustls`. |
205 | | /// |
206 | | /// # UNSTABLE API |
207 | | /// |
208 | | /// **NOTE: This API is not guaranteed for semver.** |
209 | | /// |
210 | | /// `rustls` is not (yet) semver 1.x and ureq can't promise that this API is upheld. |
211 | | /// If `rustls` makes a breaking change regarding `CryptoProvider` their configuration, |
212 | | /// or incompatible data types between rustls versions, ureq will _NOT_ bump a major version. |
213 | | /// |
214 | | /// ureq will update to the latest `rustls` minor version using ureq minor versions. |
215 | | /// |
216 | | /// # Feature flags |
217 | | /// |
218 | | /// This requires either feature **rustls** or **rustls-no-provider**, you probably |
219 | | /// want the latter when configuring an explicit crypto provider since |
220 | | /// **rustls** compiles with `ring`, while **rustls-no-provider** does not. |
221 | | /// |
222 | | /// # Example |
223 | | /// |
224 | | /// This example uses `aws-lc-rs` for the [`Agent`][crate::Agent]. The following |
225 | | /// depdendencies would compile ureq without `ring` and only aws-lc-rs. |
226 | | /// |
227 | | /// * `Cargo.toml` |
228 | | /// |
229 | | /// ```text |
230 | | /// ureq = { version = "3", default-features = false, features = ["rustls-no-provider"] } |
231 | | /// rustls = { version = "0.23", features = ["aws-lc-rs"] } |
232 | | /// ``` |
233 | | /// |
234 | | /// * Agent |
235 | | /// |
236 | | /// ``` |
237 | | /// use std::sync::Arc; |
238 | | /// use ureq::{Agent}; |
239 | | /// use ureq::tls::{TlsConfig, TlsProvider}; |
240 | | /// use rustls::crypto; |
241 | | /// |
242 | | /// let crypto = Arc::new(crypto::aws_lc_rs::default_provider()); |
243 | | /// |
244 | | /// let agent = Agent::config_builder() |
245 | | /// .tls_config( |
246 | | /// TlsConfig::builder() |
247 | | /// .provider(TlsProvider::Rustls) |
248 | | /// // requires rustls or rustls-no-provider feature |
249 | | /// .unversioned_rustls_crypto_provider(crypto) |
250 | | /// .build() |
251 | | /// ) |
252 | | /// .build() |
253 | | /// .new_agent(); |
254 | | /// ``` |
255 | | #[cfg(feature = "_rustls")] |
256 | 0 | pub fn unversioned_rustls_crypto_provider( |
257 | 0 | mut self, |
258 | 0 | v: Arc<::rustls::crypto::CryptoProvider>, |
259 | 0 | ) -> Self { |
260 | 0 | self.config.rustls_crypto_provider = Some(v); |
261 | 0 | self |
262 | 0 | } |
263 | | |
264 | | /// Finalize the config |
265 | 0 | pub fn build(self) -> TlsConfig { |
266 | 0 | self.config |
267 | 0 | } |
268 | | } |
269 | | |
270 | | /// A client certificate. |
271 | | #[derive(Debug, Clone, Hash)] |
272 | | pub struct ClientCert(Arc<(Vec<Certificate<'static>>, PrivateKey<'static>)>); |
273 | | |
274 | | impl ClientCert { |
275 | | /// Creates a new client certificate from a chain and a private key. |
276 | 0 | pub fn new_with_certs(chain: &[Certificate<'static>], key: PrivateKey<'static>) -> Self { |
277 | 0 | Self(Arc::new((chain.to_vec(), key))) |
278 | 0 | } |
279 | | |
280 | | /// Client certificate chain. |
281 | 0 | pub fn certs(&self) -> &[Certificate<'static>] { |
282 | 0 | &self.0.0 |
283 | 0 | } |
284 | | |
285 | | /// Client certificate private key. |
286 | 0 | pub fn private_key(&self) -> &PrivateKey<'static> { |
287 | 0 | &self.0.1 |
288 | 0 | } |
289 | | } |
290 | | |
291 | | /// Configuration setting for root certs. |
292 | | #[derive(Debug, Clone, Hash)] |
293 | | #[non_exhaustive] |
294 | | pub enum RootCerts { |
295 | | /// Use these specific certificates as root certs. |
296 | | Specific(Arc<Vec<Certificate<'static>>>), |
297 | | |
298 | | /// Use the platform's verifier. |
299 | | /// |
300 | | /// * For **rustls**, this uses the `rustls-platform-verifier` crate. It requires |
301 | | /// the feature **platform-verifier**. |
302 | | /// * For **native-tls**, this uses the roots that native-tls loads by default. |
303 | | PlatformVerifier, |
304 | | |
305 | | /// Use Mozilla's root certificates instead of the platform. |
306 | | /// |
307 | | /// This is useful when you can't trust the system roots, such as in |
308 | | /// environments where TLS is intercepted and decrypted by a proxy (MITM attack). |
309 | | /// |
310 | | /// This is the default value. |
311 | | WebPki, |
312 | | } |
313 | | |
314 | | impl RootCerts { |
315 | | /// Use these specific root certificates |
316 | 0 | pub fn new_with_certs(certs: &[Certificate<'static>]) -> Self { |
317 | 0 | certs.iter().cloned().into() |
318 | 0 | } |
319 | | } |
320 | | |
321 | | impl<I: IntoIterator<Item = Certificate<'static>>> From<I> for RootCerts { |
322 | 0 | fn from(value: I) -> Self { |
323 | 0 | RootCerts::Specific(Arc::new(value.into_iter().collect())) |
324 | 0 | } |
325 | | } |
326 | | |
327 | | impl Default for TlsConfig { |
328 | 0 | fn default() -> Self { |
329 | 0 | let provider = TlsProvider::default(); |
330 | 0 | Self { |
331 | 0 | provider, |
332 | 0 | client_cert: None, |
333 | 0 | root_certs: RootCerts::WebPki, |
334 | 0 | use_sni: true, |
335 | 0 | disable_verification: false, |
336 | 0 | #[cfg(feature = "_rustls")] |
337 | 0 | rustls_crypto_provider: None, |
338 | 0 | } |
339 | 0 | } |
340 | | } |
341 | | |
342 | | impl fmt::Debug for TlsConfig { |
343 | 0 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
344 | 0 | f.debug_struct("TlsConfig") |
345 | 0 | .field("provider", &self.provider) |
346 | 0 | .field("client_cert", &self.client_cert) |
347 | 0 | .field("root_certs", &self.root_certs) |
348 | 0 | .field("use_sni", &self.use_sni) |
349 | 0 | .field("disable_verification", &self.disable_verification) |
350 | 0 | .finish() |
351 | 0 | } |
352 | | } |
353 | | |
354 | | impl Hash for TlsConfig { |
355 | 0 | fn hash<H: std::hash::Hasher>(&self, state: &mut H) { |
356 | 0 | self.provider.hash(state); |
357 | 0 | self.client_cert.hash(state); |
358 | 0 | self.root_certs.hash(state); |
359 | 0 | self.use_sni.hash(state); |
360 | 0 | self.disable_verification.hash(state); |
361 | | |
362 | | #[cfg(feature = "_rustls")] |
363 | 0 | if let Some(arc) = &self.rustls_crypto_provider { |
364 | 0 | (Arc::as_ptr(arc) as usize).hash(state); |
365 | 0 | } |
366 | 0 | } |
367 | | } |
368 | | |
369 | | #[cfg(test)] |
370 | | mod test { |
371 | | use super::*; |
372 | | use assert_no_alloc::*; |
373 | | |
374 | | #[test] |
375 | | fn tls_config_clone_does_not_allocate() { |
376 | | let c = TlsConfig::default(); |
377 | | assert_no_alloc(|| c.clone()); |
378 | | } |
379 | | } |