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