Coverage Report

Created: 2026-07-30 06:46

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}