rustls/client/client_conn.rs
1use crate::builder::{ConfigBuilder, WantsCipherSuites};
2use crate::common_state::{CommonState, Protocol, Side};
3use crate::conn::{ConnectionCommon, ConnectionCore};
4use crate::dns_name::{DnsName, DnsNameRef, InvalidDnsNameError};
5use crate::enums::{CipherSuite, ProtocolVersion, SignatureScheme};
6use crate::error::Error;
7use crate::kx::SupportedKxGroup;
8#[cfg(feature = "logging")]
9use crate::log::trace;
10use crate::msgs::enums::NamedGroup;
11use crate::msgs::handshake::ClientExtension;
12use crate::msgs::persist;
13use crate::sign;
14use crate::suites::SupportedCipherSuite;
15use crate::verify;
16use crate::versions;
17#[cfg(feature = "secret_extraction")]
18use crate::ExtractedSecrets;
19use crate::KeyLog;
20
21use super::handy::{ClientSessionMemoryCache, NoClientSessionStorage};
22use super::hs;
23
24use std::marker::PhantomData;
25use std::net::IpAddr;
26use std::ops::{Deref, DerefMut};
27use std::sync::Arc;
28use std::{fmt, io, mem};
29
30/// A trait for the ability to store client session data, so that sessions
31/// can be resumed in future connections.
32///
33/// Generally all data in this interface should be treated as
34/// **highly sensitive**, containing enough key material to break all security
35/// of the corresponding session.
36///
37/// `set_`, `insert_`, `remove_` and `take_` operations are mutating; this isn't
38/// expressed in the type system to allow implementations freedom in
39/// how to achieve interior mutability. `Mutex` is a common choice.
40pub trait ClientSessionStore: Send + Sync {
41 /// Remember what `NamedGroup` the given server chose.
42 fn set_kx_hint(&self, server_name: &ServerName, group: NamedGroup);
43
44 /// This should return the value most recently passed to `set_kx_hint`
45 /// for the given `server_name`.
46 ///
47 /// If `None` is returned, the caller chooses the first configured group,
48 /// and an extra round trip might happen if that choice is unsatisfactory
49 /// to the server.
50 fn kx_hint(&self, server_name: &ServerName) -> Option<NamedGroup>;
51
52 /// Remember a TLS1.2 session.
53 ///
54 /// At most one of these can be remembered at a time, per `server_name`.
55 fn set_tls12_session(&self, server_name: &ServerName, value: persist::Tls12ClientSessionValue);
56
57 /// Get the most recently saved TLS1.2 session for `server_name` provided to `set_tls12_session`.
58 fn tls12_session(&self, server_name: &ServerName) -> Option<persist::Tls12ClientSessionValue>;
59
60 /// Remove and forget any saved TLS1.2 session for `server_name`.
61 fn remove_tls12_session(&self, server_name: &ServerName);
62
63 /// Remember a TLS1.3 ticket that might be retrieved later from `take_tls13_ticket`, allowing
64 /// resumption of this session.
65 ///
66 /// This can be called multiple times for a given session, allowing multiple independent tickets
67 /// to be valid at once. The number of times this is called is controlled by the server, so
68 /// implementations of this trait should apply a reasonable bound of how many items are stored
69 /// simultaneously.
70 fn insert_tls13_ticket(
71 &self,
72 server_name: &ServerName,
73 value: persist::Tls13ClientSessionValue,
74 );
75
76 /// Return a TLS1.3 ticket previously provided to `add_tls13_ticket`.
77 ///
78 /// Implementations of this trait must return each value provided to `add_tls13_ticket` _at most once_.
79 fn take_tls13_ticket(
80 &self,
81 server_name: &ServerName,
82 ) -> Option<persist::Tls13ClientSessionValue>;
83}
84
85/// A trait for the ability to choose a certificate chain and
86/// private key for the purposes of client authentication.
87pub trait ResolvesClientCert: Send + Sync {
88 /// With the server-supplied acceptable issuers in `acceptable_issuers`,
89 /// the server's supported signature schemes in `sigschemes`,
90 /// return a certificate chain and signing key to authenticate.
91 ///
92 /// `acceptable_issuers` is undecoded and unverified by the rustls
93 /// library, but it should be expected to contain a DER encodings
94 /// of X501 NAMEs.
95 ///
96 /// Return None to continue the handshake without any client
97 /// authentication. The server may reject the handshake later
98 /// if it requires authentication.
99 fn resolve(
100 &self,
101 acceptable_issuers: &[&[u8]],
102 sigschemes: &[SignatureScheme],
103 ) -> Option<Arc<sign::CertifiedKey>>;
104
105 /// Return true if any certificates at all are available.
106 fn has_certs(&self) -> bool;
107}
108
109/// Common configuration for (typically) all connections made by a program.
110///
111/// Making one of these is cheap, though one of the inputs may be expensive: gathering trust roots
112/// from the operating system to add to the [`RootCertStore`] passed to `with_root_certificates()`
113/// (the rustls-native-certs crate is often used for this) may take on the order of a few hundred
114/// milliseconds.
115///
116/// These must be created via the [`ClientConfig::builder()`] function.
117///
118/// # Defaults
119///
120/// * [`ClientConfig::max_fragment_size`]: the default is `None`: TLS packets are not fragmented to a specific size.
121/// * [`ClientConfig::resumption`]: supports resumption with up to 256 server names, using session
122/// ids or tickets, with a max of eight tickets per server.
123/// * [`ClientConfig::alpn_protocols`]: the default is empty -- no ALPN protocol is negotiated.
124/// * [`ClientConfig::key_log`]: key material is not logged.
125///
126/// [`RootCertStore`]: crate::RootCertStore
127#[derive(Clone)]
128pub struct ClientConfig {
129 /// List of ciphersuites, in preference order.
130 pub(super) cipher_suites: Vec<SupportedCipherSuite>,
131
132 /// List of supported key exchange algorithms, in preference order -- the
133 /// first element is the highest priority.
134 ///
135 /// The first element in this list is the _default key share algorithm_,
136 /// and in TLS1.3 a key share for it is sent in the client hello.
137 pub(super) kx_groups: Vec<&'static SupportedKxGroup>,
138
139 /// Which ALPN protocols we include in our client hello.
140 /// If empty, no ALPN extension is sent.
141 pub alpn_protocols: Vec<Vec<u8>>,
142
143 /// How and when the client can resume a previous session.
144 pub resumption: Resumption,
145
146 /// The maximum size of TLS message we'll emit. If None, we don't limit TLS
147 /// message lengths except to the 2**16 limit specified in the standard.
148 ///
149 /// rustls enforces an arbitrary minimum of 32 bytes for this field.
150 /// Out of range values are reported as errors from ClientConnection::new.
151 ///
152 /// Setting this value to the TCP MSS may improve latency for stream-y workloads.
153 pub max_fragment_size: Option<usize>,
154
155 /// How to decide what client auth certificate/keys to use.
156 pub client_auth_cert_resolver: Arc<dyn ResolvesClientCert>,
157
158 /// Supported versions, in no particular order. The default
159 /// is all supported versions.
160 pub(super) versions: versions::EnabledVersions,
161
162 /// Whether to send the Server Name Indication (SNI) extension
163 /// during the client handshake.
164 ///
165 /// The default is true.
166 pub enable_sni: bool,
167
168 /// How to verify the server certificate chain.
169 pub(super) verifier: Arc<dyn verify::ServerCertVerifier>,
170
171 /// How to output key material for debugging. The default
172 /// does nothing.
173 pub key_log: Arc<dyn KeyLog>,
174
175 /// Allows traffic secrets to be extracted after the handshake,
176 /// e.g. for kTLS setup.
177 #[cfg(feature = "secret_extraction")]
178 #[cfg_attr(docsrs, doc(cfg(feature = "secret_extraction")))]
179 pub enable_secret_extraction: bool,
180
181 /// Whether to send data on the first flight ("early data") in
182 /// TLS 1.3 handshakes.
183 ///
184 /// The default is false.
185 pub enable_early_data: bool,
186}
187
188/// What mechanisms to support for resuming a TLS 1.2 session.
189#[derive(Clone, Copy, Debug, PartialEq)]
190pub enum Tls12Resumption {
191 /// Disable 1.2 resumption.
192 Disabled,
193 /// Support 1.2 resumption using session ids only.
194 SessionIdOnly,
195 /// Support 1.2 resumption using session ids or RFC 5077 tickets.
196 ///
197 /// See[^1] for why you might like to disable RFC 5077 by instead choosing the `SessionIdOnly`
198 /// option. Note that TLS 1.3 tickets do not have those issues.
199 ///
200 /// [^1]: <https://words.filippo.io/we-need-to-talk-about-session-tickets/>
201 SessionIdOrTickets,
202}
203
204impl fmt::Debug for ClientConfig {
205 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
206 f.debug_struct("ClientConfig")
207 .field("alpn_protocols", &self.alpn_protocols)
208 .field("resumption", &self.resumption)
209 .field("max_fragment_size", &self.max_fragment_size)
210 .field("enable_sni", &self.enable_sni)
211 .field("enable_early_data", &self.enable_early_data)
212 .finish_non_exhaustive()
213 }
214}
215
216impl ClientConfig {
217 /// Create a builder to build up the client configuration.
218 ///
219 /// For more information, see the [`ConfigBuilder`] documentation.
220 pub fn builder() -> ConfigBuilder<Self, WantsCipherSuites> {
221 ConfigBuilder {
222 state: WantsCipherSuites(()),
223 side: PhantomData,
224 }
225 }
226
227 /// We support a given TLS version if it's quoted in the configured
228 /// versions *and* at least one ciphersuite for this version is
229 /// also configured.
230 pub(crate) fn supports_version(&self, v: ProtocolVersion) -> bool {
231 self.versions.contains(v)
232 && self
233 .cipher_suites
234 .iter()
235 .any(|cs| cs.version().version == v)
236 }
237
238 /// Access configuration options whose use is dangerous and requires
239 /// extra care.
240 #[cfg(feature = "dangerous_configuration")]
241 pub fn dangerous(&mut self) -> danger::DangerousClientConfig {
242 danger::DangerousClientConfig { cfg: self }
243 }
244
245 pub(super) fn find_cipher_suite(&self, suite: CipherSuite) -> Option<SupportedCipherSuite> {
246 self.cipher_suites
247 .iter()
248 .copied()
249 .find(|&scs| scs.suite() == suite)
250 }
251}
252
253/// Configuration for how/when a client is allowed to resume a previous session.
254#[derive(Clone)]
255pub struct Resumption {
256 /// How we store session data or tickets. The default is to use an in-memory
257 /// [ClientSessionMemoryCache].
258 pub(super) store: Arc<dyn ClientSessionStore>,
259
260 /// What mechanism is used for resuming a TLS 1.2 session.
261 pub(super) tls12_resumption: Tls12Resumption,
262}
263
264impl Resumption {
265 /// Create a new `Resumption` that stores data for the given number of sessions in memory.
266 ///
267 /// This is the default `Resumption` choice, and enables resuming a TLS 1.2 session with
268 /// a session id or RFC 5077 ticket.
269 pub fn in_memory_sessions(num: usize) -> Self {
270 Self {
271 store: Arc::new(ClientSessionMemoryCache::new(num)),
272 tls12_resumption: Tls12Resumption::SessionIdOrTickets,
273 }
274 }
275
276 /// Use a custom [`ClientSessionStore`] implementation to store sessions.
277 ///
278 /// By default, enables resuming a TLS 1.2 session with a session id or RFC 5077 ticket.
279 pub fn store(store: Arc<dyn ClientSessionStore>) -> Self {
280 Self {
281 store,
282 tls12_resumption: Tls12Resumption::SessionIdOrTickets,
283 }
284 }
285
286 /// Disable all use of session resumption.
287 pub fn disabled() -> Self {
288 Self {
289 store: Arc::new(NoClientSessionStorage),
290 tls12_resumption: Tls12Resumption::Disabled,
291 }
292 }
293
294 /// Configure whether TLS 1.2 sessions may be resumed, and by what mechanism.
295 ///
296 /// This is meaningless if you've disabled resumption entirely.
297 pub fn tls12_resumption(mut self, tls12: Tls12Resumption) -> Self {
298 self.tls12_resumption = tls12;
299 self
300 }
301}
302
303impl fmt::Debug for Resumption {
304 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
305 f.debug_struct("Resumption")
306 .field("tls12_resumption", &self.tls12_resumption)
307 .finish()
308 }
309}
310
311impl Default for Resumption {
312 /// Create an in-memory session store resumption with up to 256 server names, allowing
313 /// a TLS 1.2 session to resume with a session id or RFC 5077 ticket.
314 fn default() -> Self {
315 Self::in_memory_sessions(256)
316 }
317}
318
319/// Encodes ways a client can know the expected name of the server.
320///
321/// This currently covers knowing the DNS name of the server, but
322/// will be extended in the future to supporting privacy-preserving names
323/// for the server ("ECH"). For this reason this enum is `non_exhaustive`.
324///
325/// # Making one
326///
327/// If you have a DNS name as a `&str`, this type implements `TryFrom<&str>`,
328/// so you can do:
329///
330/// ```
331/// # use rustls::ServerName;
332/// ServerName::try_from("example.com").expect("invalid DNS name");
333///
334/// // or, alternatively...
335///
336/// let x = "example.com".try_into().expect("invalid DNS name");
337/// # let _: ServerName = x;
338/// ```
339#[non_exhaustive]
340#[derive(Clone, Eq, Hash, PartialEq)]
341pub enum ServerName {
342 /// The server is identified by a DNS name. The name
343 /// is sent in the TLS Server Name Indication (SNI)
344 /// extension.
345 DnsName(DnsName),
346
347 /// The server is identified by an IP address. SNI is not
348 /// done.
349 IpAddress(IpAddr),
350}
351
352impl fmt::Debug for ServerName {
353 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
354 match self {
355 Self::DnsName(d) => f
356 .debug_tuple("DnsName")
357 .field(&d.as_ref())
358 .finish(),
359 Self::IpAddress(i) => f
360 .debug_tuple("IpAddress")
361 .field(i)
362 .finish(),
363 }
364 }
365}
366
367impl ServerName {
368 /// Return the name that should go in the SNI extension.
369 /// If [`None`] is returned, the SNI extension is not included
370 /// in the handshake.
371 pub(crate) fn for_sni(&self) -> Option<DnsNameRef> {
372 match self {
373 Self::DnsName(dns_name) => Some(dns_name.borrow()),
374 Self::IpAddress(_) => None,
375 }
376 }
377}
378
379/// Attempt to make a ServerName from a string by parsing
380/// it as a DNS name.
381impl TryFrom<&str> for ServerName {
382 type Error = InvalidDnsNameError;
383 fn try_from(s: &str) -> Result<Self, Self::Error> {
384 match DnsNameRef::try_from(s) {
385 Ok(dns) => Ok(Self::DnsName(dns.to_owned())),
386 Err(InvalidDnsNameError) => match s.parse() {
387 Ok(ip) => Ok(Self::IpAddress(ip)),
388 Err(_) => Err(InvalidDnsNameError),
389 },
390 }
391 }
392}
393
394/// Container for unsafe APIs
395#[cfg(feature = "dangerous_configuration")]
396pub(super) mod danger {
397 use std::sync::Arc;
398
399 use super::verify::ServerCertVerifier;
400 use super::ClientConfig;
401
402 /// Accessor for dangerous configuration options.
403 #[derive(Debug)]
404 #[cfg_attr(docsrs, doc(cfg(feature = "dangerous_configuration")))]
405 pub struct DangerousClientConfig<'a> {
406 /// The underlying ClientConfig
407 pub cfg: &'a mut ClientConfig,
408 }
409
410 impl<'a> DangerousClientConfig<'a> {
411 /// Overrides the default `ServerCertVerifier` with something else.
412 pub fn set_certificate_verifier(&mut self, verifier: Arc<dyn ServerCertVerifier>) {
413 self.cfg.verifier = verifier;
414 }
415 }
416}
417
418#[derive(Debug, PartialEq)]
419enum EarlyDataState {
420 Disabled,
421 Ready,
422 Accepted,
423 AcceptedFinished,
424 Rejected,
425}
426
427pub(super) struct EarlyData {
428 state: EarlyDataState,
429 left: usize,
430}
431
432impl EarlyData {
433 fn new() -> Self {
434 Self {
435 left: 0,
436 state: EarlyDataState::Disabled,
437 }
438 }
439
440 pub(super) fn is_enabled(&self) -> bool {
441 matches!(self.state, EarlyDataState::Ready | EarlyDataState::Accepted)
442 }
443
444 fn is_accepted(&self) -> bool {
445 matches!(
446 self.state,
447 EarlyDataState::Accepted | EarlyDataState::AcceptedFinished
448 )
449 }
450
451 pub(super) fn enable(&mut self, max_data: usize) {
452 assert_eq!(self.state, EarlyDataState::Disabled);
453 self.state = EarlyDataState::Ready;
454 self.left = max_data;
455 }
456
457 pub(super) fn rejected(&mut self) {
458 trace!("EarlyData rejected");
459 self.state = EarlyDataState::Rejected;
460 }
461
462 pub(super) fn accepted(&mut self) {
463 trace!("EarlyData accepted");
464 assert_eq!(self.state, EarlyDataState::Ready);
465 self.state = EarlyDataState::Accepted;
466 }
467
468 pub(super) fn finished(&mut self) {
469 trace!("EarlyData finished");
470 self.state = match self.state {
471 EarlyDataState::Accepted => EarlyDataState::AcceptedFinished,
472 _ => panic!("bad EarlyData state"),
473 }
474 }
475
476 fn check_write(&mut self, sz: usize) -> io::Result<usize> {
477 match self.state {
478 EarlyDataState::Disabled => unreachable!(),
479 EarlyDataState::Ready | EarlyDataState::Accepted => {
480 let take = if self.left < sz {
481 mem::replace(&mut self.left, 0)
482 } else {
483 self.left -= sz;
484 sz
485 };
486
487 Ok(take)
488 }
489 EarlyDataState::Rejected | EarlyDataState::AcceptedFinished => {
490 Err(io::Error::from(io::ErrorKind::InvalidInput))
491 }
492 }
493 }
494
495 fn bytes_left(&self) -> usize {
496 self.left
497 }
498}
499
500/// Stub that implements io::Write and dispatches to `write_early_data`.
501pub struct WriteEarlyData<'a> {
502 sess: &'a mut ClientConnection,
503}
504
505impl<'a> WriteEarlyData<'a> {
506 fn new(sess: &'a mut ClientConnection) -> Self {
507 WriteEarlyData { sess }
508 }
509
510 /// How many bytes you may send. Writes will become short
511 /// once this reaches zero.
512 pub fn bytes_left(&self) -> usize {
513 self.sess
514 .inner
515 .core
516 .data
517 .early_data
518 .bytes_left()
519 }
520}
521
522impl<'a> io::Write for WriteEarlyData<'a> {
523 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
524 self.sess.write_early_data(buf)
525 }
526
527 fn flush(&mut self) -> io::Result<()> {
528 Ok(())
529 }
530}
531
532/// This represents a single TLS client connection.
533pub struct ClientConnection {
534 inner: ConnectionCommon<ClientConnectionData>,
535}
536
537impl fmt::Debug for ClientConnection {
538 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
539 f.debug_struct("ClientConnection")
540 .finish()
541 }
542}
543
544impl ClientConnection {
545 /// Make a new ClientConnection. `config` controls how
546 /// we behave in the TLS protocol, `name` is the
547 /// name of the server we want to talk to.
548 pub fn new(config: Arc<ClientConfig>, name: ServerName) -> Result<Self, Error> {
549 Ok(Self {
550 inner: ConnectionCore::for_client(config, name, Vec::new(), Protocol::Tcp)?.into(),
551 })
552 }
553
554 /// Returns an `io::Write` implementer you can write bytes to
555 /// to send TLS1.3 early data (a.k.a. "0-RTT data") to the server.
556 ///
557 /// This returns None in many circumstances when the capability to
558 /// send early data is not available, including but not limited to:
559 ///
560 /// - The server hasn't been talked to previously.
561 /// - The server does not support resumption.
562 /// - The server does not support early data.
563 /// - The resumption data for the server has expired.
564 ///
565 /// The server specifies a maximum amount of early data. You can
566 /// learn this limit through the returned object, and writes through
567 /// it will process only this many bytes.
568 ///
569 /// The server can choose not to accept any sent early data --
570 /// in this case the data is lost but the connection continues. You
571 /// can tell this happened using `is_early_data_accepted`.
572 pub fn early_data(&mut self) -> Option<WriteEarlyData> {
573 if self
574 .inner
575 .core
576 .data
577 .early_data
578 .is_enabled()
579 {
580 Some(WriteEarlyData::new(self))
581 } else {
582 None
583 }
584 }
585
586 /// Returns True if the server signalled it will process early data.
587 ///
588 /// If you sent early data and this returns false at the end of the
589 /// handshake then the server will not process the data. This
590 /// is not an error, but you may wish to resend the data.
591 pub fn is_early_data_accepted(&self) -> bool {
592 self.inner.core.is_early_data_accepted()
593 }
594
595 fn write_early_data(&mut self, data: &[u8]) -> io::Result<usize> {
596 self.inner
597 .core
598 .data
599 .early_data
600 .check_write(data.len())
601 .map(|sz| {
602 self.inner
603 .send_early_plaintext(&data[..sz])
604 })
605 }
606
607 /// Extract secrets, so they can be used when configuring kTLS, for example.
608 #[cfg(feature = "secret_extraction")]
609 #[cfg_attr(docsrs, doc(cfg(feature = "secret_extraction")))]
610 pub fn extract_secrets(self) -> Result<ExtractedSecrets, Error> {
611 self.inner.extract_secrets()
612 }
613}
614
615impl Deref for ClientConnection {
616 type Target = ConnectionCommon<ClientConnectionData>;
617
618 fn deref(&self) -> &Self::Target {
619 &self.inner
620 }
621}
622
623impl DerefMut for ClientConnection {
624 fn deref_mut(&mut self) -> &mut Self::Target {
625 &mut self.inner
626 }
627}
628
629#[doc(hidden)]
630impl<'a> TryFrom<&'a mut crate::Connection> for &'a mut ClientConnection {
631 type Error = ();
632
633 fn try_from(value: &'a mut crate::Connection) -> Result<Self, Self::Error> {
634 use crate::Connection::*;
635 match value {
636 Client(conn) => Ok(conn),
637 Server(_) => Err(()),
638 }
639 }
640}
641
642impl From<ClientConnection> for crate::Connection {
643 fn from(conn: ClientConnection) -> Self {
644 Self::Client(conn)
645 }
646}
647
648impl ConnectionCore<ClientConnectionData> {
649 pub(crate) fn for_client(
650 config: Arc<ClientConfig>,
651 name: ServerName,
652 extra_exts: Vec<ClientExtension>,
653 proto: Protocol,
654 ) -> Result<Self, Error> {
655 let mut common_state = CommonState::new(Side::Client);
656 common_state.set_max_fragment_size(config.max_fragment_size)?;
657 common_state.protocol = proto;
658 #[cfg(feature = "secret_extraction")]
659 {
660 common_state.enable_secret_extraction = config.enable_secret_extraction;
661 }
662 let mut data = ClientConnectionData::new();
663
664 let mut cx = hs::ClientContext {
665 common: &mut common_state,
666 data: &mut data,
667 };
668
669 let state = hs::start_handshake(name, extra_exts, config, &mut cx)?;
670 Ok(Self::new(state, data, common_state))
671 }
672
673 pub(crate) fn is_early_data_accepted(&self) -> bool {
674 self.data.early_data.is_accepted()
675 }
676}
677
678/// State associated with a client connection.
679pub struct ClientConnectionData {
680 pub(super) early_data: EarlyData,
681 pub(super) resumption_ciphersuite: Option<SupportedCipherSuite>,
682}
683
684impl ClientConnectionData {
685 fn new() -> Self {
686 Self {
687 early_data: EarlyData::new(),
688 resumption_ciphersuite: None,
689 }
690 }
691}
692
693impl crate::conn::SideData for ClientConnectionData {}