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