rustls/client/config.rs
1use alloc::vec::Vec;
2use core::any::Any;
3use core::fmt;
4use core::hash::{Hash, Hasher};
5use core::marker::PhantomData;
6
7#[cfg(feature = "webpki")]
8use pki_types::PrivateKeyDer;
9use pki_types::{FipsStatus, ServerName, UnixTime};
10
11use super::ech::EchMode;
12#[cfg(feature = "std")]
13use super::handy::ClientSessionMemoryCache;
14use super::handy::{FailResolveClientCert, NoClientSessionStorage};
15use super::{Tls12Session, Tls13Session};
16use crate::builder::{ConfigBuilder, WantsVerifier};
17use crate::client::connection::ClientConnectionBuilder;
18use crate::common_state::Protocol;
19#[cfg(doc)]
20use crate::crypto;
21use crate::crypto::kx::NamedGroup;
22use crate::crypto::{CipherSuite, CryptoProvider, SelectedCredential, SignatureScheme, hash};
23#[cfg(feature = "webpki")]
24use crate::crypto::{Credentials, Identity, SingleCredential};
25use crate::enums::{ApplicationProtocol, CertificateType, ProtocolVersion};
26use crate::error::{ApiMisuse, Error};
27use crate::key_log::NoKeyLog;
28use crate::suites::SupportedCipherSuite;
29use crate::sync::Arc;
30#[cfg(feature = "std")]
31use crate::time_provider::DefaultTimeProvider;
32use crate::time_provider::TimeProvider;
33use crate::verify::ServerVerifier;
34#[cfg(feature = "webpki")]
35use crate::webpki::{self, WebPkiServerVerifier};
36use crate::{DistinguishedName, DynHasher, KeyLog, compress};
37
38/// Common configuration for (typically) all connections made by a program.
39///
40/// Making one of these is cheap, though one of the inputs may be expensive: gathering trust roots
41/// from the operating system to add to the [`RootCertStore`] passed to `with_root_certificates()`
42/// (the rustls-native-certs crate is often used for this) may take on the order of a few hundred
43/// milliseconds.
44///
45/// These must be created via the [`ClientConfig::builder()`] or [`ClientConfig::builder_with_details()`]
46/// function.
47///
48/// Note that using [`ConfigBuilder<ClientConfig, WantsVersions>::with_ech()`] will produce a common
49/// configuration specific to the provided [`crate::client::EchConfig`] that may not be appropriate
50/// for all connections made by the program. In this case the configuration should only be shared
51/// by connections intended for domains that offer the provided [`crate::client::EchConfig`] in
52/// their DNS zone.
53///
54/// # Defaults
55///
56/// * [`ClientConfig::max_fragment_size`]: the default is `None` (meaning 16kB).
57/// * [`ClientConfig::resumption`]: supports resumption with up to 256 server names, using session
58/// ids or tickets, with a max of eight tickets per server.
59/// * [`ClientConfig::alpn_protocols`]: the default is empty -- no ALPN protocol is negotiated.
60/// * [`ClientConfig::key_log`]: key material is not logged.
61/// * [`ClientConfig::cert_decompressors`]: depends on the crate features, see [`compress::default_cert_decompressors()`].
62/// * [`ClientConfig::cert_compressors`]: depends on the crate features, see [`compress::default_cert_compressors()`].
63/// * [`ClientConfig::cert_compression_cache`]: caches the most recently used 4 compressions
64///
65/// [`RootCertStore`]: crate::RootCertStore
66#[derive(Clone, Debug)]
67pub struct ClientConfig {
68 /// Which ALPN protocols we include in our client hello.
69 /// If empty, no ALPN extension is sent.
70 pub alpn_protocols: Vec<ApplicationProtocol<'static>>,
71
72 /// Whether to check the selected ALPN was offered.
73 ///
74 /// The default is true.
75 pub check_selected_alpn: bool,
76
77 /// How and when the client can resume a previous session.
78 ///
79 /// # Sharing `resumption` between `ClientConfig`s
80 /// In a program using many `ClientConfig`s it may improve resumption rates
81 /// (which has a significant impact on connection performance) if those
82 /// configs share a single [`Resumption`].
83 ///
84 /// However, resumption is only allowed between two `ClientConfig`s if their
85 /// `client_auth_cert_resolver` (ie, potential client authentication credentials),
86 /// `verifier` (ie, server certificate verification settings), and `time_provider`
87 /// (ie, verification time source):
88 ///
89 /// - are the same type (determined by hashing their `TypeId`), and
90 /// - input the same data into [`ServerVerifier::hash_config()`] and
91 /// [`ClientCredentialResolver::hash_config()`] (note that time providers are
92 /// assumed to have no state and do not have a `hash_config()`.)
93 ///
94 /// To illustrate, imagine two `ClientConfig`s `A` and `B`. `A` fully validates
95 /// the server certificate, `B` does not. If `A` and `B` shared a resumption store,
96 /// it would be possible for a session originated by `B` to be inserted into the
97 /// store, and then resumed by `A`. This would give a false impression to the user
98 /// of `A` that the server certificate is fully validated.
99 ///
100 /// [`ServerVerifier::hash_config()`]: crate::verify::ServerVerifier::hash_config()
101 pub resumption: Resumption,
102
103 /// The maximum size of plaintext input to be emitted in a single TLS record.
104 /// A value of None is equivalent to the [TLS maximum] of 16 kB.
105 ///
106 /// rustls enforces an arbitrary minimum of 32 bytes for this field.
107 /// Out of range values are reported as errors when initializing a connection.
108 ///
109 /// Setting this value to a little less than the TCP MSS may improve latency
110 /// for stream-y workloads.
111 ///
112 /// [TLS maximum]: https://datatracker.ietf.org/doc/html/rfc9846#section-5.1
113 pub max_fragment_size: Option<usize>,
114
115 /// Whether to send the Server Name Indication (SNI) extension
116 /// during the client handshake.
117 ///
118 /// The default is true.
119 pub enable_sni: bool,
120
121 /// How to output key material for debugging.
122 ///
123 /// The default does nothing.
124 ///
125 /// See [RFC 9850](https://datatracker.ietf.org/doc/html/rfc9850) for background.
126 pub key_log: Arc<dyn KeyLog>,
127
128 /// Allows traffic secrets to be extracted after the handshake,
129 /// e.g. for kTLS setup.
130 pub enable_secret_extraction: bool,
131
132 /// Whether to send data on the first flight ("early data") in
133 /// TLS 1.3 handshakes.
134 ///
135 /// The default is false.
136 pub enable_early_data: bool,
137
138 /// If set to `true`, requires the server to support the extended
139 /// master secret extraction method defined in [RFC 7627].
140 ///
141 /// The default is `true` if the configured [`CryptoProvider`] is FIPS-compliant,
142 /// false otherwise.
143 ///
144 /// It must be set to `true` to meet FIPS requirement mentioned in section
145 /// **D.Q Transition of the TLS 1.2 KDF to Support the Extended Master
146 /// Secret** from [FIPS 140-3 IG.pdf].
147 ///
148 /// [RFC 7627]: https://datatracker.ietf.org/doc/html/rfc7627
149 /// [FIPS 140-3 IG.pdf]: https://csrc.nist.gov/csrc/media/Projects/cryptographic-module-validation-program/documents/fips%20140-3/FIPS%20140-3%20IG.pdf
150 pub require_ems: bool,
151
152 /// Request a specific number of TLS 1.3 session tickets via [RFC 9149].
153 ///
154 /// Set to `None` to disable sending the extension (the default).
155 ///
156 /// [RFC 9149]: https://datatracker.ietf.org/doc/html/rfc9149
157 pub send_ticket_request: Option<TicketRequest>,
158
159 /// Items that affect the fundamental security properties of a connection.
160 pub(super) domain: SecurityDomain,
161
162 /// How to decompress the server's certificate chain.
163 ///
164 /// If this is non-empty, the [RFC 8779] certificate compression
165 /// extension is offered, and any compressed certificates are
166 /// transparently decompressed during the handshake.
167 ///
168 /// This only applies to TLS1.3 connections. It is ignored for
169 /// TLS1.2 connections.
170 ///
171 /// [RFC 8779]: https://datatracker.ietf.org/doc/rfc8879/
172 pub cert_decompressors: Vec<&'static dyn compress::CertDecompressor>,
173
174 /// How to compress the client's certificate chain.
175 ///
176 /// If a server supports this extension, and advertises support
177 /// for one of the compression algorithms included here, the
178 /// client certificate will be compressed according to [RFC 8779].
179 ///
180 /// This only applies to TLS1.3 connections. It is ignored for
181 /// TLS1.2 connections.
182 ///
183 /// [RFC 8779]: https://datatracker.ietf.org/doc/rfc8879/
184 pub cert_compressors: Vec<&'static dyn compress::CertCompressor>,
185
186 /// Caching for compressed certificates.
187 ///
188 /// This is optional: [`compress::CompressionCache::Disabled`] gives
189 /// a cache that does no caching.
190 pub cert_compression_cache: Arc<compress::CompressionCache>,
191
192 /// How to offer Encrypted Client Hello (ECH). The default is to not offer ECH.
193 pub(super) ech_mode: Option<EchMode>,
194}
195
196impl ClientConfig {
197 /// Create a builder for a client configuration with a specific [`CryptoProvider`].
198 ///
199 /// This will use the provider's configured ciphersuites.
200 ///
201 /// For more information, see the [`ConfigBuilder`] documentation.
202 #[cfg(feature = "std")]
203 pub fn builder(provider: Arc<CryptoProvider>) -> ConfigBuilder<Self, WantsVerifier> {
204 Self::builder_with_details(provider, Arc::new(DefaultTimeProvider))
205 }
206
207 /// Create a builder for a client configuration with no default implementation details.
208 ///
209 /// This API must be used by `no_std` users.
210 ///
211 /// You must provide a specific [`TimeProvider`].
212 ///
213 /// You must provide a specific [`CryptoProvider`].
214 ///
215 /// For more information, see the [`ConfigBuilder`] documentation.
216 pub fn builder_with_details(
217 provider: Arc<CryptoProvider>,
218 time_provider: Arc<dyn TimeProvider>,
219 ) -> ConfigBuilder<Self, WantsVerifier> {
220 ConfigBuilder {
221 state: WantsVerifier {
222 client_ech_mode: None,
223 },
224 provider,
225 time_provider,
226 side: PhantomData,
227 }
228 }
229
230 /// Create a new client connection builder for the given server name.
231 ///
232 /// The `ClientConfig` controls how the client behaves;
233 /// `name` is the name of server we want to talk to.
234 pub fn connect(self: &Arc<Self>, server_name: ServerName<'static>) -> ClientConnectionBuilder {
235 ClientConnectionBuilder {
236 config: self.clone(),
237 name: server_name,
238 alpn_protocols: None,
239 }
240 }
241
242 /// Access configuration options whose use is dangerous and requires
243 /// extra care.
244 pub fn dangerous(&mut self) -> danger::DangerousClientConfig<'_> {
245 danger::DangerousClientConfig { cfg: self }
246 }
247
248 /// Return the FIPS validation status for connections made with this configuration.
249 ///
250 /// This is different from [`CryptoProvider::fips()`]: [`CryptoProvider::fips()`]
251 /// is concerned only with cryptography, whereas this _also_ covers TLS-level
252 /// configuration that NIST recommends, as well as ECH HPKE suites if applicable.
253 pub fn fips(&self) -> FipsStatus {
254 if !self.require_ems {
255 return FipsStatus::Unvalidated;
256 }
257
258 let status = self.domain.provider.fips();
259 match &self.ech_mode {
260 Some(ech) => Ord::min(status, ech.fips()),
261 None => status,
262 }
263 }
264
265 /// Return the crypto provider used to construct this client configuration.
266 pub fn provider(&self) -> &Arc<CryptoProvider> {
267 &self.domain.provider
268 }
269
270 /// Return the resolver for this client configuration.
271 ///
272 /// This is the object that determines which credentials to use for client
273 /// authentication.
274 pub fn resolver(&self) -> &Arc<dyn ClientCredentialResolver> {
275 &self.domain.client_auth_cert_resolver
276 }
277
278 /// Return the verifier for this client configuration.
279 ///
280 /// This is the object that determines how server certificates are verified.
281 pub fn verifier(&self) -> &Arc<dyn ServerVerifier> {
282 &self.domain.verifier
283 }
284
285 pub(crate) fn supports_version(&self, v: ProtocolVersion, protocol: Protocol) -> bool {
286 self.domain.provider.supports_version(v) && protocol.supports_version(v)
287 }
288
289 pub(super) fn find_cipher_suite(&self, suite: CipherSuite) -> Option<SupportedCipherSuite> {
290 self.domain
291 .provider
292 .iter_cipher_suites()
293 .find(|&scs| scs.suite() == suite)
294 }
295
296 pub(super) fn current_time(&self) -> Result<UnixTime, Error> {
297 self.domain
298 .time_provider
299 .current_time()
300 .ok_or(Error::FailedToGetCurrentTime)
301 }
302
303 /// A hash which partitions this config's use of the [`Self::resumption`] store.
304 pub(super) fn config_hash(&self) -> [u8; 32] {
305 self.domain.config_hash
306 }
307}
308
309struct HashAdapter<'a>(&'a mut dyn hash::Context);
310
311impl Hasher for HashAdapter<'_> {
312 fn finish(&self) -> u64 {
313 // SAFETY: this is private to `SecurityDomain::new`, which guarantees `hash::Output`
314 // is at least 32 bytes.
315 u64::from_be_bytes(
316 self.0.fork_finish().as_ref()[..8]
317 .try_into()
318 .unwrap(),
319 )
320 }
321
322 fn write(&mut self, bytes: &[u8]) {
323 self.0.update(bytes)
324 }
325}
326
327/// Client session data store for possible future resumption.
328///
329/// All data in this interface should be treated as **highly sensitive**, containing enough key
330/// material to break all security of the corresponding session.
331///
332/// `set_`, `insert_`, `remove_` and `take_` operations are mutating; this isn't
333/// expressed in the type system to allow implementations freedom in
334/// how to achieve interior mutability. `Mutex` is a common choice.
335pub trait ClientSessionStore: fmt::Debug + Send + Sync {
336 /// Remember what `NamedGroup` the given server chose.
337 fn set_kx_hint(&self, key: ClientSessionKey<'static>, group: NamedGroup);
338
339 /// Value most recently passed to `set_kx_hint` for the given `key`.
340 ///
341 /// If `None` is returned, the caller chooses the first configured group, and an extra round
342 /// trip might happen if that choice is unsatisfactory to the server.
343 fn kx_hint(&self, key: &ClientSessionKey<'_>) -> Option<NamedGroup>;
344
345 /// Remember a TLS1.2 session, allowing resumption of this connection in the future.
346 ///
347 /// At most one of these per session key can be remembered at a time.
348 fn set_tls12_session(&self, key: ClientSessionKey<'static>, value: Tls12Session);
349
350 /// Get the most recently saved TLS1.2 session for `key` provided to `set_tls12_session`.
351 fn tls12_session(&self, key: &ClientSessionKey<'_>) -> Option<Tls12Session>;
352
353 /// Remove and forget any saved TLS1.2 session for `key`.
354 fn remove_tls12_session(&self, key: &ClientSessionKey<'static>);
355
356 /// Remember a TLS1.3 ticket, allowing resumption of this connection in the future.
357 ///
358 /// This can be called multiple times for a given session, allowing multiple independent tickets
359 /// to be valid at once. The number of times this is called is controlled by the server, so
360 /// implementations of this trait should apply a reasonable bound of how many items are stored
361 /// simultaneously.
362 fn insert_tls13_ticket(&self, key: ClientSessionKey<'static>, value: Tls13Session);
363
364 /// Return a TLS1.3 ticket previously provided to `insert_tls13_ticket()`.
365 ///
366 /// Implementations of this trait must return each value provided to `insert_tls13_ticket()` _at most once_.
367 fn take_tls13_ticket(&self, key: &ClientSessionKey<'static>) -> Option<Tls13Session>;
368}
369
370/// Identifies a security context and server in the [`ClientSessionStore`] interface.
371#[derive(Clone, Debug, Eq, Hash, PartialEq)]
372#[non_exhaustive]
373pub struct ClientSessionKey<'a> {
374 /// A hash to partition the client storage between different security domains.
375 pub config_hash: [u8; 32],
376
377 /// Transport-level identity of the server.
378 pub server_name: ServerName<'a>,
379}
380
381impl ClientSessionKey<'_> {
382 /// Copy the value to own its contents.
383 pub fn to_owned(&self) -> ClientSessionKey<'static> {
384 let Self {
385 config_hash,
386 server_name,
387 } = self;
388 ClientSessionKey {
389 config_hash: *config_hash,
390 server_name: server_name.to_owned(),
391 }
392 }
393}
394
395/// A trait for the ability to choose a certificate chain and
396/// private key for the purposes of client authentication.
397pub trait ClientCredentialResolver: fmt::Debug + Send + Sync + Any {
398 /// Resolve a client certificate chain/private key to use as the client's identity.
399 ///
400 /// The [`SelectedCredential`] returned from this method contains an identity and a
401 /// one-time-use [`Signer`] wrapping the private key. This is usually obtained via a
402 /// [`Credentials`], on which an implementation can call [`Credentials::signer()`].
403 /// An implementation can either store long-lived [`Credentials`] values, or instantiate
404 /// them as needed using one of its constructors.
405 ///
406 /// Return `None` to continue the handshake without any client authentication. The server
407 /// may reject the handshake later if it requires authentication.
408 ///
409 /// [RFC 5280 A.1]: https://www.rfc-editor.org/rfc/rfc5280#appendix-A.1
410 ///
411 /// [`Credentials`]: crate::crypto::Credentials
412 /// [`Credentials::signer()`]: crate::crypto::Credentials::signer
413 /// [`Signer`]: crate::crypto::Signer
414 fn resolve(&self, request: &CredentialRequest<'_>) -> Option<SelectedCredential>;
415
416 /// Returns which [`CertificateType`]s this resolver supports.
417 ///
418 /// Should return the empty slice if the resolver does not have any credentials to send.
419 /// Implementations should return the same value every time.
420 ///
421 /// See [RFC 7250](https://tools.ietf.org/html/rfc7250) for more information.
422 fn supported_certificate_types(&self) -> &'static [CertificateType];
423
424 /// Instance configuration should be input to `h`.
425 fn hash_config(&self, h: &mut dyn Hasher);
426}
427
428/// Context from the server to inform client credential selection.
429pub struct CredentialRequest<'a> {
430 pub(super) negotiated_type: CertificateType,
431 pub(super) root_hint_subjects: &'a [DistinguishedName],
432 pub(super) signature_schemes: &'a [SignatureScheme],
433}
434
435impl CredentialRequest<'_> {
436 /// List of certificate authority subject distinguished names provided by the server.
437 ///
438 /// If the list is empty, the client should send whatever certificate it has. The hints
439 /// are expected to be DER-encoded X.500 distinguished names, per [RFC 5280 A.1]. Note that
440 /// the encoding comes from the server and has not been validated by rustls.
441 ///
442 /// See [`DistinguishedName`] for more information on decoding with external crates like
443 /// `x509-parser`.
444 ///
445 /// [`DistinguishedName`]: crate::DistinguishedName
446 pub fn root_hint_subjects(&self) -> &[DistinguishedName] {
447 self.root_hint_subjects
448 }
449
450 /// Get the compatible signature schemes.
451 pub fn signature_schemes(&self) -> &[SignatureScheme] {
452 self.signature_schemes
453 }
454
455 /// The negotiated certificate type.
456 ///
457 /// If the server does not support [RFC 7250], this will be [`CertificateType::X509`].
458 ///
459 /// [RFC 7250]: https://tools.ietf.org/html/rfc7250
460 pub fn negotiated_type(&self) -> CertificateType {
461 self.negotiated_type
462 }
463}
464
465/// Items that affect the fundamental security properties of a connection.
466///
467/// This is its own type because `config_hash` depends on the other fields:
468/// fields therefore should not be mutated, but an entire object created
469/// through [`Self::new`] for any edits.
470#[derive(Clone, Debug)]
471pub(super) struct SecurityDomain {
472 /// Provides the current system time
473 time_provider: Arc<dyn TimeProvider>,
474
475 /// Source of randomness and other crypto.
476 provider: Arc<CryptoProvider>,
477
478 /// How to verify the server certificate chain.
479 verifier: Arc<dyn ServerVerifier>,
480
481 /// How to decide what client auth certificate/keys to use.
482 client_auth_cert_resolver: Arc<dyn ClientCredentialResolver>,
483
484 config_hash: [u8; 32],
485}
486
487impl SecurityDomain {
488 pub(crate) fn new(
489 provider: Arc<CryptoProvider>,
490 client_auth_cert_resolver: Arc<dyn ClientCredentialResolver + 'static>,
491 verifier: Arc<dyn ServerVerifier + 'static>,
492 time_provider: Arc<dyn TimeProvider + 'static>,
493 ) -> Self {
494 // Use a hash function that outputs at least 32 bytes.
495 let hash = provider
496 .iter_cipher_suites()
497 .map(|cs| cs.hash_provider())
498 .find(|h| h.output_len() >= 32)
499 .expect("no suitable cipher suite available (with |H| >= 32)"); // this is -- in practice -- all cipher suites
500
501 let mut h = hash.start();
502 let mut adapter = HashAdapter(h.as_mut());
503
504 // Include TypeId of impl, so two different types with different non-configured
505 // behavior do not collide even if their `hash_config()`s are the same.
506 Any::type_id(client_auth_cert_resolver.as_ref()).hash(&mut DynHasher(&mut adapter));
507 client_auth_cert_resolver.hash_config(&mut adapter);
508
509 Any::type_id(verifier.as_ref()).hash(&mut DynHasher(&mut adapter));
510 verifier.hash_config(&mut adapter);
511
512 Any::type_id(time_provider.as_ref()).hash(&mut DynHasher(&mut adapter));
513
514 let config_hash = h.finish().as_ref()[..32]
515 .try_into()
516 .unwrap();
517
518 Self {
519 time_provider,
520 provider,
521 verifier,
522 client_auth_cert_resolver,
523 config_hash,
524 }
525 }
526
527 fn with_verifier(&self, verifier: Arc<dyn ServerVerifier + 'static>) -> Self {
528 let Self {
529 time_provider,
530 provider,
531 verifier: _,
532 client_auth_cert_resolver,
533 config_hash: _,
534 } = self;
535 Self::new(
536 provider.clone(),
537 client_auth_cert_resolver.clone(),
538 verifier,
539 time_provider.clone(),
540 )
541 }
542}
543
544/// Configuration for how/when a client is allowed to resume a previous session.
545#[derive(Clone, Debug)]
546pub struct Resumption {
547 /// How we store session data or tickets. The default is to use an in-memory
548 /// [super::handy::ClientSessionMemoryCache].
549 pub(super) store: Arc<dyn ClientSessionStore>,
550
551 /// What mechanism is used for resuming a TLS 1.2 session.
552 pub(super) tls12_resumption: Tls12Resumption,
553}
554
555impl Resumption {
556 /// Create a new `Resumption` that stores data for the given number of sessions in memory.
557 ///
558 /// This is the default `Resumption` choice, and enables resuming a TLS 1.2 session with
559 /// a session id or RFC 5077 ticket.
560 #[cfg(feature = "std")]
561 pub fn in_memory_sessions(num: usize) -> Self {
562 Self {
563 store: Arc::new(ClientSessionMemoryCache::new(num)),
564 tls12_resumption: Tls12Resumption::SessionIdOrTickets,
565 }
566 }
567
568 /// Use a custom [`ClientSessionStore`] implementation to store sessions.
569 ///
570 /// By default, enables resuming a TLS 1.2 session with a session id or RFC 5077 ticket.
571 pub fn store(store: Arc<dyn ClientSessionStore>) -> Self {
572 Self {
573 store,
574 tls12_resumption: Tls12Resumption::SessionIdOrTickets,
575 }
576 }
577
578 /// Disable all use of session resumption.
579 pub fn disabled() -> Self {
580 Self {
581 store: Arc::new(NoClientSessionStorage),
582 tls12_resumption: Tls12Resumption::Disabled,
583 }
584 }
585
586 /// Configure whether TLS 1.2 sessions may be resumed, and by what mechanism.
587 ///
588 /// This is meaningless if you've disabled resumption entirely, which is the case in `no-std`
589 /// contexts.
590 pub fn tls12_resumption(mut self, tls12: Tls12Resumption) -> Self {
591 self.tls12_resumption = tls12;
592 self
593 }
594}
595
596impl Default for Resumption {
597 /// Create an in-memory session store resumption with up to 256 server names, allowing
598 /// a TLS 1.2 session to resume with a session id or RFC 5077 ticket.
599 fn default() -> Self {
600 #[cfg(feature = "std")]
601 {
602 Self::in_memory_sessions(256)
603 }
604 #[cfg(not(feature = "std"))]
605 {
606 Self::disabled()
607 }
608 }
609}
610
611/// What mechanisms to support for resuming a TLS 1.2 session.
612#[non_exhaustive]
613#[derive(Clone, Copy, Debug, PartialEq)]
614pub enum Tls12Resumption {
615 /// Disable 1.2 resumption.
616 Disabled,
617 /// Support 1.2 resumption using session ids only.
618 SessionIdOnly,
619 /// Support 1.2 resumption using session ids or RFC 5077 tickets.
620 ///
621 /// See[^1] for why you might like to disable RFC 5077 by instead choosing the `SessionIdOnly`
622 /// option. Note that TLS 1.3 tickets do not have those issues.
623 ///
624 /// [^1]: <https://words.filippo.io/we-need-to-talk-about-session-tickets/>
625 SessionIdOrTickets,
626}
627
628/// Number of TLS 1.3 session tickets to request via the [RFC 9149]
629/// `ticket_request` extension.
630///
631/// [RFC 9149]: https://datatracker.ietf.org/doc/html/rfc9149
632#[expect(clippy::exhaustive_structs)]
633#[derive(Clone, Copy, Debug, PartialEq)]
634pub struct TicketRequest {
635 /// Tickets desired when the server negotiates a new connection.
636 ///
637 /// RFC 9149 recommends setting this to the desired number of tickets
638 /// and `resumption_count` to 0 for initial connections.
639 pub new_session_count: u8,
640
641 /// Tickets desired when the server resumes using a presented ticket.
642 ///
643 /// A value of 1 is a good default for primed caches. Clients racing
644 /// multiple connections may want a higher value.
645 pub resumption_count: u8,
646}
647
648impl ConfigBuilder<ClientConfig, WantsVerifier> {
649 /// Choose how to verify server certificates.
650 ///
651 /// Using this function does not configure revocation. If you wish to
652 /// configure revocation, instead use:
653 ///
654 /// ```diff
655 /// - .with_root_certificates(root_store)
656 /// + .with_webpki_verifier(
657 /// + WebPkiServerVerifier::builder(root_store, crypto_provider)
658 /// + .with_crls(...)
659 /// + .build()?
660 /// + )
661 /// ```
662 #[cfg(feature = "webpki")]
663 pub fn with_root_certificates(
664 self,
665 root_store: impl Into<Arc<webpki::RootCertStore>>,
666 ) -> ConfigBuilder<ClientConfig, WantsClientCert> {
667 let algorithms = self
668 .provider
669 .signature_verification_algorithms;
670 self.with_webpki_verifier(
671 WebPkiServerVerifier::new_without_revocation(root_store, algorithms).into(),
672 )
673 }
674
675 /// Choose how to verify server certificates using a webpki verifier.
676 ///
677 /// See [`webpki::WebPkiServerVerifier::builder`] for more information.
678 #[cfg(feature = "webpki")]
679 pub fn with_webpki_verifier(
680 self,
681 verifier: Arc<WebPkiServerVerifier>,
682 ) -> ConfigBuilder<ClientConfig, WantsClientCert> {
683 ConfigBuilder {
684 state: WantsClientCert {
685 verifier,
686 client_ech_mode: self.state.client_ech_mode,
687 },
688 provider: self.provider,
689 time_provider: self.time_provider,
690 side: PhantomData,
691 }
692 }
693
694 /// Enable Encrypted Client Hello (ECH) in the given mode.
695 ///
696 /// This requires TLS 1.3 as the only supported protocol version to meet the requirement
697 /// to support ECH. At the end, the config building process will return an error if either
698 /// TLS1.3 _is not_ supported by the provider, or TLS1.2 _is_ supported.
699 ///
700 /// The `ClientConfig` that will be produced by this builder will be specific to the provided
701 /// [`crate::client::EchConfig`] and may not be appropriate for all connections made by the program.
702 /// In this case the configuration should only be shared by connections intended for domains
703 /// that offer the provided [`crate::client::EchConfig`] in their DNS zone.
704 pub fn with_ech(mut self, mode: EchMode) -> Self {
705 self.state.client_ech_mode = Some(mode);
706 self
707 }
708
709 /// Access configuration options whose use is dangerous and requires
710 /// extra care.
711 pub fn dangerous(self) -> danger::DangerousClientConfigBuilder {
712 danger::DangerousClientConfigBuilder { cfg: self }
713 }
714}
715
716/// A config builder state where the caller needs to supply whether and how to provide a client
717/// certificate.
718///
719/// For more information, see the [`ConfigBuilder`] documentation.
720#[derive(Clone)]
721pub struct WantsClientCert {
722 verifier: Arc<dyn ServerVerifier>,
723 client_ech_mode: Option<EchMode>,
724}
725
726impl ConfigBuilder<ClientConfig, WantsClientCert> {
727 /// Sets a single identity and matching private key for use in client authentication.
728 ///
729 /// - `identity` is the [`Identity`], typically containing a certificate chain.
730 /// - `key_der` is a DER-encoded private key as PKCS#1, PKCS#8, or SEC1. Supported key
731 /// formats and types depends on the configured provider.
732 ///
733 /// This function fails if `key_der` is invalid, or if the `SubjectPublicKeyInfo` from
734 /// the private key does not match the public key from the `identity`.
735 #[cfg(feature = "webpki")]
736 pub fn with_client_auth_cert(
737 self,
738 identity: Arc<Identity<'static>>,
739 key_der: PrivateKeyDer<'static>,
740 ) -> Result<ClientConfig, Error> {
741 let credentials = Credentials::from_der(identity, key_der, &self.provider)?;
742 self.with_client_credential_resolver(Arc::new(SingleCredential::from(credentials)))
743 }
744
745 /// Do not support client auth.
746 pub fn with_no_client_auth(self) -> Result<ClientConfig, Error> {
747 self.with_client_credential_resolver(Arc::new(FailResolveClientCert {}))
748 }
749
750 /// Sets a custom [`ClientCredentialResolver`].
751 pub fn with_client_credential_resolver(
752 self,
753 client_auth_cert_resolver: Arc<dyn ClientCredentialResolver>,
754 ) -> Result<ClientConfig, Error> {
755 self.provider.consistency_check()?;
756
757 if self.state.client_ech_mode.is_some() {
758 match (
759 self.provider
760 .tls12_cipher_suites
761 .is_empty(),
762 self.provider
763 .tls13_cipher_suites
764 .is_empty(),
765 ) {
766 (_, true) => return Err(ApiMisuse::EchRequiresTls13Support.into()),
767 (false, _) => return Err(ApiMisuse::EchForbidsTls12Support.into()),
768 (true, false) => {}
769 };
770 }
771
772 let require_ems = !matches!(self.provider.fips(), FipsStatus::Unvalidated);
773 Ok(ClientConfig {
774 alpn_protocols: Vec::new(),
775 check_selected_alpn: true,
776 resumption: Resumption::default(),
777 max_fragment_size: None,
778 enable_sni: true,
779 key_log: Arc::new(NoKeyLog {}),
780 enable_secret_extraction: false,
781 enable_early_data: false,
782 require_ems,
783 send_ticket_request: None,
784 domain: SecurityDomain::new(
785 self.provider,
786 client_auth_cert_resolver,
787 self.state.verifier,
788 self.time_provider,
789 ),
790 cert_decompressors: compress::default_cert_decompressors().to_vec(),
791 cert_compressors: compress::default_cert_compressors().to_vec(),
792 cert_compression_cache: Arc::new(compress::CompressionCache::default()),
793 ech_mode: self.state.client_ech_mode,
794 })
795 }
796}
797
798/// Container for unsafe APIs
799pub(super) mod danger {
800 use core::marker::PhantomData;
801
802 use crate::client::WantsClientCert;
803 use crate::client::config::ClientConfig;
804 use crate::sync::Arc;
805 use crate::verify::ServerVerifier;
806 use crate::{ConfigBuilder, WantsVerifier};
807
808 /// Accessor for dangerous configuration options.
809 #[derive(Debug)]
810 pub struct DangerousClientConfig<'a> {
811 /// The underlying ClientConfig
812 pub(super) cfg: &'a mut ClientConfig,
813 }
814
815 impl DangerousClientConfig<'_> {
816 /// Overrides the default `ServerVerifier` with something else.
817 pub fn set_certificate_verifier(&mut self, verifier: Arc<dyn ServerVerifier>) {
818 self.cfg.domain = self.cfg.domain.with_verifier(verifier);
819 }
820 }
821
822 /// Accessor for dangerous configuration options.
823 #[derive(Debug)]
824 pub struct DangerousClientConfigBuilder {
825 /// The underlying ClientConfigBuilder
826 pub(super) cfg: ConfigBuilder<ClientConfig, WantsVerifier>,
827 }
828
829 impl DangerousClientConfigBuilder {
830 /// Set a custom certificate verifier.
831 pub fn with_custom_certificate_verifier(
832 self,
833 verifier: Arc<dyn ServerVerifier>,
834 ) -> ConfigBuilder<ClientConfig, WantsClientCert> {
835 ConfigBuilder {
836 state: WantsClientCert {
837 verifier,
838 client_ech_mode: self.cfg.state.client_ech_mode,
839 },
840 provider: self.cfg.provider,
841 time_provider: self.cfg.time_provider,
842 side: PhantomData,
843 }
844 }
845 }
846}