Skip to main content

rustls/client/
connection.rs

1use alloc::vec::Vec;
2use core::fmt;
3use core::ops::Deref;
4
5use pki_types::{FipsStatus, ServerName};
6
7use super::config::ClientConfig;
8use super::hs::ClientHelloInput;
9use crate::TlsInputBuffer;
10use crate::client::EchStatus;
11use crate::common_state::{CommonState, ConnectionOutputs, EarlyDataEvent, Event, Protocol, Side};
12use crate::conn::private::SideOutput;
13use crate::conn::split::SplitConnection;
14use crate::conn::{
15    Connection, ConnectionCommon, KeyingMaterialExporter, MessageHandler, SideCommonOutput,
16    SideData,
17};
18#[cfg(doc)]
19use crate::crypto;
20use crate::crypto::cipher::OutboundPlain;
21use crate::enums::ApplicationProtocol;
22use crate::error::Error;
23use crate::msgs::ClientExtensionsInput;
24use crate::quic::QuicOutput;
25use crate::suites::ExtractedSecrets;
26use crate::sync::Arc;
27use crate::tracing::trace;
28
29/// This represents a single TLS client connection.
30pub struct ClientConnection {
31    inner: ConnectionCommon<ClientSide>,
32}
33
34impl fmt::Debug for ClientConnection {
35    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
36        f.debug_struct("ClientConnection")
37            .finish_non_exhaustive()
38    }
39}
40
41impl ClientConnection {
42    /// Split a post-handshake connection into a [`SplitConnection`].
43    ///
44    /// This allows the two directions (transmit and receive) of the connection to be progressed
45    /// separately (including by different threads, which would allow dedicating a CPU core for each
46    /// direction rather than one per connection; this can dramatically improve performance for
47    /// full-duplex protocols).
48    ///
49    /// It also separates out the [`ConnectionOutputs`] which gives the application direct control
50    /// of how long this is kept.
51    ///
52    /// This fails if:
53    ///
54    /// - the handshake is not complete. Check with [`Connection::is_handshaking()`].
55    /// - there is any buffered TLS data to send.  Obtain it first with [`Connection::write_tls()`].
56    pub fn split(self) -> Result<SplitConnection<ClientSide>, Error> {
57        self.inner.split()
58    }
59
60    /// Returns an `io::Write` implementer you can write bytes to
61    /// to send TLS1.3 early data (a.k.a. "0-RTT data") to the server.
62    ///
63    /// This returns None in many circumstances when the capability to
64    /// send early data is not available, including but not limited to:
65    ///
66    /// - The server hasn't been talked to previously.
67    /// - The server does not support resumption.
68    /// - The server does not support early data.
69    /// - The resumption data for the server has expired.
70    ///
71    /// The server specifies a maximum amount of early data.  You can
72    /// learn this limit through the returned object, and writes through
73    /// it will process only this many bytes.
74    ///
75    /// The server can choose not to accept any sent early data --
76    /// in this case the data is lost but the connection continues.  You
77    /// can tell this happened using `is_early_data_accepted`.
78    pub fn early_data(&mut self) -> Option<WriteEarlyData<'_>> {
79        let ConnectionCommon { side, common, .. } = &mut self.inner;
80        let early_data = side.early_data.as_mut()?;
81        match early_data.state {
82            EarlyDataState::Ready | EarlyDataState::Sending | EarlyDataState::Accepted => {
83                Some(WriteEarlyData { early_data, common })
84            }
85            _ => None,
86        }
87    }
88
89    /// Returns True if the server signalled it will process early data.
90    ///
91    /// If you sent early data and this returns false at the end of the
92    /// handshake then the server will not process the data.  This
93    /// is not an error, but you may wish to resend the data.
94    pub fn is_early_data_accepted(&self) -> bool {
95        self.inner.is_early_data_accepted()
96    }
97
98    /// Return the connection's Encrypted Client Hello (ECH) status.
99    pub fn ech_status(&self) -> EchStatus {
100        self.inner.side.ech_status
101    }
102
103    /// Returns the number of TLS1.3 tickets that have been received.
104    pub fn tls13_tickets_received(&self) -> u32 {
105        self.inner
106            .common
107            .recv
108            .tls13_tickets_received
109    }
110}
111
112impl Connection for ClientConnection {
113    type Side = ClientSide;
114
115    fn write_tls(&mut self, plaintext: OutboundPlain<'_>, tls: &mut Vec<u8>) -> Result<(), Error> {
116        self.inner.write_tls(plaintext, tls)
117    }
118
119    fn wants_read(&self) -> bool {
120        self.inner.wants_read()
121    }
122
123    fn process_new_packets<'a, 'm>(
124        &'a mut self,
125        input: &'m mut dyn TlsInputBuffer,
126        tls: &'a mut Vec<u8>,
127    ) -> MessageHandler<'a, 'm, ClientSide> {
128        self.inner
129            .process_new_packets(input, tls)
130    }
131
132    fn exporter(&mut self) -> Result<KeyingMaterialExporter, Error> {
133        self.inner.exporter()
134    }
135
136    fn dangerous_extract_secrets(self) -> Result<ExtractedSecrets, Error> {
137        self.inner.dangerous_extract_secrets()
138    }
139
140    fn refresh_traffic_keys(&mut self, tls: &mut Vec<u8>) -> Result<(), Error> {
141        self.inner.refresh_traffic_keys(tls)
142    }
143
144    fn send_close_notify(&mut self, tls: &mut Vec<u8>) {
145        self.inner.send_close_notify(tls);
146    }
147
148    fn is_handshaking(&self) -> bool {
149        self.inner.is_handshaking()
150    }
151
152    fn fips(&self) -> FipsStatus {
153        self.inner.fips
154    }
155}
156
157impl Deref for ClientConnection {
158    type Target = ConnectionOutputs;
159
160    fn deref(&self) -> &Self::Target {
161        &self.inner
162    }
163}
164
165/// Builder for [`ClientConnection`] values.
166///
167/// Create one with [`ClientConfig::connect()`].
168pub struct ClientConnectionBuilder {
169    pub(crate) config: Arc<ClientConfig>,
170    pub(crate) name: ServerName<'static>,
171    pub(crate) alpn_protocols: Option<Vec<ApplicationProtocol<'static>>>,
172}
173
174impl ClientConnectionBuilder {
175    /// Specify the ALPN protocols to use for this connection.
176    pub fn with_alpn(mut self, alpn_protocols: Vec<ApplicationProtocol<'static>>) -> Self {
177        self.alpn_protocols = Some(alpn_protocols);
178        self
179    }
180
181    /// Finalize the builder and create the `ClientConnection`.
182    pub fn build(self, tls: &mut Vec<u8>) -> Result<ClientConnection, Error> {
183        let Self {
184            config,
185            name,
186            alpn_protocols,
187        } = self;
188
189        let alpn_protocols = alpn_protocols.unwrap_or_else(|| config.alpn_protocols.clone());
190        Ok(ClientConnection {
191            inner: ConnectionCommon::for_client(
192                config,
193                name,
194                ClientExtensionsInput::from_alpn(alpn_protocols),
195                None,
196                Protocol::Tcp,
197                tls,
198            )?,
199        })
200    }
201}
202
203/// Allows writing of early data in resumed TLS 1.3 connections.
204///
205/// "Early data" is also known as "0-RTT data".
206///
207/// Use [`Self::write_tls()`] to encrypt early data into TLS records.
208pub struct WriteEarlyData<'a> {
209    early_data: &'a mut EarlyData,
210    common: &'a mut CommonState,
211}
212
213impl<'a> WriteEarlyData<'a> {
214    /// Encrypt early data as TLS records and encode them into `tls`.
215    ///
216    /// Yields the number of bytes of `plaintext` that were consumed.  This may be less than
217    /// the length of `plaintext` if the server has limited the amount of early data that
218    /// may be sent.
219    pub fn write_tls(&mut self, plaintext: OutboundPlain<'_>, tls: &mut Vec<u8>) -> usize {
220        let state = &mut self.early_data;
221        let plaintext = match state.state {
222            EarlyDataState::Ready | EarlyDataState::Sending | EarlyDataState::Accepted => {
223                let take = Ord::min(plaintext.len(), state.left);
224                state.left -= take;
225                plaintext.split_at(take).0
226            }
227            EarlyDataState::AcceptedFinished => return 0,
228        };
229
230        self.common
231            .send
232            .send_appdata_encrypt(plaintext, tls)
233    }
234
235    /// How many bytes you may send.  Writes will become short
236    /// once this reaches zero.
237    pub fn bytes_left(&self) -> usize {
238        self.early_data.left
239    }
240
241    /// Returns the "early" exporter that can derive key material for use in early data
242    ///
243    /// See [RFC 5705][] for general details on what exporters are, and [RFC 9846 S7.5][] for
244    /// specific details on the "early" exporter.
245    ///
246    /// **Beware** that the early exporter requires care, as it is subject to the same
247    /// potential for replay as early data itself.  See [RFC 9846 appendix F.5.1][] for
248    /// more detail.
249    ///
250    /// This function can be called at most once per connection. This function will error:
251    /// if called more than once per connection.
252    ///
253    /// If you are looking for the normal exporter, this is available from
254    /// [`Connection::exporter()`].
255    ///
256    /// [RFC 5705]: https://datatracker.ietf.org/doc/html/rfc5705
257    /// [RFC 9846 S7.5]: https://datatracker.ietf.org/doc/html/rfc9846#section-7.5
258    /// [RFC 9846 appendix F.5.1]: https://datatracker.ietf.org/doc/html/rfc9846#appendix-F.5.1
259    /// [`Connection::exporter()`]: crate::conn::Connection::exporter()
260    pub fn exporter(&mut self) -> Result<KeyingMaterialExporter, Error> {
261        self.common.early_exporter()
262    }
263}
264
265impl ConnectionCommon<ClientSide> {
266    pub(crate) fn for_client(
267        config: Arc<ClientConfig>,
268        name: ServerName<'static>,
269        extra_exts: ClientExtensionsInput,
270        quic: Option<&mut dyn QuicOutput>,
271        protocol: Protocol,
272        tls: &mut Vec<u8>,
273    ) -> Result<Self, Error> {
274        let mut common_state = CommonState::new(Side::Client, config.fips());
275        common_state
276            .send
277            .set_max_fragment_size(config.max_fragment_size)?;
278        let mut data = ClientConnectionData::default();
279
280        let mut output = SideCommonOutput {
281            side: &mut data,
282            quic,
283            common: &mut common_state,
284            tls,
285        };
286
287        let input = ClientHelloInput::new(name, &extra_exts, protocol, &mut output, config)?;
288        let state = input.start_handshake(extra_exts, &mut output)?;
289
290        Ok(Self::new(state, data, common_state))
291    }
292
293    pub(crate) fn is_early_data_accepted(&self) -> bool {
294        matches!(
295            &self.side.early_data,
296            Some(EarlyData {
297                state: EarlyDataState::Accepted | EarlyDataState::AcceptedFinished,
298                ..
299            })
300        )
301    }
302}
303
304/// State associated with a client connection.
305#[expect(clippy::exhaustive_structs)]
306#[derive(Debug)]
307pub struct ClientSide;
308
309impl SideData for ClientSide {}
310
311impl crate::conn::private::Side for ClientSide {
312    type Data = ClientConnectionData;
313    type State = super::hs::ClientState;
314}
315
316impl SideOutput for ClientConnectionData {
317    fn emit(&mut self, ev: Event<'_>) {
318        match ev {
319            Event::EchStatus(ech) => self.ech_status = ech,
320            Event::EarlyData(event) => match (event, &mut self.early_data) {
321                (EarlyDataEvent::Enable(sz), None) => self.early_data = Some(EarlyData::new(sz)),
322                (EarlyDataEvent::Start, Some(early_data)) => {
323                    assert_eq!(early_data.state, EarlyDataState::Ready);
324                    early_data.state = EarlyDataState::Sending;
325                }
326                (EarlyDataEvent::Accepted, Some(early_data)) => {
327                    trace!("EarlyData accepted");
328                    assert_eq!(early_data.state, EarlyDataState::Sending);
329                    early_data.state = EarlyDataState::Accepted;
330                }
331                (EarlyDataEvent::Rejected, _) => self.early_data = None,
332                (EarlyDataEvent::Finished, Some(early_data)) => {
333                    trace!("EarlyData finished");
334                    early_data.state = match early_data.state {
335                        EarlyDataState::Accepted => EarlyDataState::AcceptedFinished,
336                        _ => panic!("bad EarlyData state"),
337                    }
338                }
339                _ => unreachable!(),
340            },
341            _ => unreachable!(),
342        }
343    }
344}
345
346#[derive(Default)]
347pub(crate) struct ClientConnectionData {
348    early_data: Option<EarlyData>,
349    ech_status: EchStatus,
350}
351
352pub(super) struct EarlyData {
353    state: EarlyDataState,
354    left: usize,
355}
356
357impl EarlyData {
358    fn new(left: usize) -> Self {
359        Self {
360            state: EarlyDataState::Ready,
361            left,
362        }
363    }
364}
365
366#[derive(Debug, PartialEq)]
367enum EarlyDataState {
368    Ready,
369    Sending,
370    Accepted,
371    AcceptedFinished,
372}