Skip to main content

rustls/client/
connection.rs

1use alloc::vec::Vec;
2use core::ops::{Deref, DerefMut};
3use core::{fmt, mem};
4use std::io;
5
6use pki_types::{FipsStatus, ServerName};
7
8use super::config::ClientConfig;
9use super::hs::ClientHelloInput;
10use crate::client::EchStatus;
11use crate::common_state::{
12    CommonState, ConnectionOutputs, EarlyDataEvent, Event, Output, Protocol, Side,
13};
14use crate::conn::unbuffered::EncryptError;
15use crate::conn::{
16    Connection, ConnectionCommon, ConnectionCore, IoState, KeyingMaterialExporter, Reader,
17    SideCommonOutput, Writer,
18};
19#[cfg(doc)]
20use crate::crypto;
21use crate::enums::ApplicationProtocol;
22use crate::error::Error;
23use crate::log::trace;
24use crate::msgs::ClientExtensionsInput;
25use crate::suites::ExtractedSecrets;
26use crate::sync::Arc;
27
28/// This represents a single TLS client connection.
29pub struct ClientConnection {
30    inner: ConnectionCommon<ClientSide>,
31}
32
33impl fmt::Debug for ClientConnection {
34    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
35        f.debug_struct("ClientConnection")
36            .finish_non_exhaustive()
37    }
38}
39
40impl ClientConnection {
41    /// Returns an `io::Write` implementer you can write bytes to
42    /// to send TLS1.3 early data (a.k.a. "0-RTT data") to the server.
43    ///
44    /// This returns None in many circumstances when the capability to
45    /// send early data is not available, including but not limited to:
46    ///
47    /// - The server hasn't been talked to previously.
48    /// - The server does not support resumption.
49    /// - The server does not support early data.
50    /// - The resumption data for the server has expired.
51    ///
52    /// The server specifies a maximum amount of early data.  You can
53    /// learn this limit through the returned object, and writes through
54    /// it will process only this many bytes.
55    ///
56    /// The server can choose not to accept any sent early data --
57    /// in this case the data is lost but the connection continues.  You
58    /// can tell this happened using `is_early_data_accepted`.
59    pub fn early_data(&mut self) -> Option<WriteEarlyData<'_>> {
60        if self
61            .inner
62            .core
63            .side
64            .early_data
65            .is_enabled()
66        {
67            Some(WriteEarlyData::new(self))
68        } else {
69            None
70        }
71    }
72
73    /// Returns True if the server signalled it will process early data.
74    ///
75    /// If you sent early data and this returns false at the end of the
76    /// handshake then the server will not process the data.  This
77    /// is not an error, but you may wish to resend the data.
78    pub fn is_early_data_accepted(&self) -> bool {
79        self.inner.core.is_early_data_accepted()
80    }
81
82    /// Extract secrets, so they can be used when configuring kTLS, for example.
83    /// Should be used with care as it exposes secret key material.
84    pub fn dangerous_extract_secrets(self) -> Result<ExtractedSecrets, Error> {
85        self.inner.dangerous_extract_secrets()
86    }
87
88    /// Return the connection's Encrypted Client Hello (ECH) status.
89    pub fn ech_status(&self) -> EchStatus {
90        self.inner.core.side.ech_status
91    }
92
93    fn write_early_data(&mut self, data: &[u8]) -> io::Result<usize> {
94        self.inner
95            .core
96            .side
97            .early_data
98            .check_write(data.len())
99            .map(|sz| {
100                self.inner
101                    .send
102                    .send_early_plaintext(&data[..sz])
103            })
104    }
105
106    /// Returns the number of TLS1.3 tickets that have been received.
107    pub fn tls13_tickets_received(&self) -> u32 {
108        self.inner
109            .core
110            .common
111            .recv
112            .tls13_tickets_received
113    }
114}
115
116impl Connection for ClientConnection {
117    fn read_tls(&mut self, rd: &mut dyn io::Read) -> Result<usize, io::Error> {
118        self.inner.read_tls(rd)
119    }
120
121    fn write_tls(&mut self, wr: &mut dyn io::Write) -> Result<usize, io::Error> {
122        self.inner.write_tls(wr)
123    }
124
125    fn wants_read(&self) -> bool {
126        self.inner.wants_read()
127    }
128
129    fn wants_write(&self) -> bool {
130        self.inner.wants_write()
131    }
132
133    fn reader(&mut self) -> Reader<'_> {
134        self.inner.reader()
135    }
136
137    fn writer(&mut self) -> Writer<'_> {
138        self.inner.writer()
139    }
140
141    fn process_new_packets(&mut self) -> Result<IoState, Error> {
142        self.inner.process_new_packets()
143    }
144
145    fn exporter(&mut self) -> Result<KeyingMaterialExporter, Error> {
146        self.inner.exporter()
147    }
148
149    fn dangerous_extract_secrets(self) -> Result<ExtractedSecrets, Error> {
150        self.inner.dangerous_extract_secrets()
151    }
152
153    fn set_buffer_limit(&mut self, limit: Option<usize>) {
154        self.inner.set_buffer_limit(limit)
155    }
156
157    fn set_plaintext_buffer_limit(&mut self, limit: Option<usize>) {
158        self.inner
159            .set_plaintext_buffer_limit(limit)
160    }
161
162    fn refresh_traffic_keys(&mut self) -> Result<(), Error> {
163        self.inner.refresh_traffic_keys()
164    }
165
166    fn send_close_notify(&mut self) {
167        self.inner.send_close_notify();
168    }
169
170    fn is_handshaking(&self) -> bool {
171        self.inner.is_handshaking()
172    }
173
174    fn fips(&self) -> FipsStatus {
175        self.inner.fips
176    }
177}
178
179impl Deref for ClientConnection {
180    type Target = ConnectionOutputs;
181
182    fn deref(&self) -> &Self::Target {
183        &self.inner
184    }
185}
186
187impl DerefMut for ClientConnection {
188    fn deref_mut(&mut self) -> &mut Self::Target {
189        &mut self.inner
190    }
191}
192
193/// Builder for [`ClientConnection`] values.
194///
195/// Create one with [`ClientConfig::connect()`].
196pub struct ClientConnectionBuilder {
197    pub(crate) config: Arc<ClientConfig>,
198    pub(crate) name: ServerName<'static>,
199    pub(crate) alpn_protocols: Option<Vec<ApplicationProtocol<'static>>>,
200}
201
202impl ClientConnectionBuilder {
203    /// Specify the ALPN protocols to use for this connection.
204    pub fn with_alpn(mut self, alpn_protocols: Vec<ApplicationProtocol<'static>>) -> Self {
205        self.alpn_protocols = Some(alpn_protocols);
206        self
207    }
208
209    /// Finalize the builder and create the `ClientConnection`.
210    pub fn build(self) -> Result<ClientConnection, Error> {
211        let Self {
212            config,
213            name,
214            alpn_protocols,
215        } = self;
216
217        let alpn_protocols = alpn_protocols.unwrap_or_else(|| config.alpn_protocols.clone());
218        let fips = config.fips();
219        Ok(ClientConnection {
220            inner: ConnectionCommon::new(
221                ConnectionCore::for_client(
222                    config,
223                    name,
224                    ClientExtensionsInput::from_alpn(alpn_protocols),
225                    Protocol::Tcp,
226                )?,
227                fips,
228            ),
229        })
230    }
231}
232
233/// Allows writing of early data in resumed TLS 1.3 connections.
234///
235/// "Early data" is also known as "0-RTT data".
236///
237/// This type implements [`io::Write`].
238pub struct WriteEarlyData<'a> {
239    sess: &'a mut ClientConnection,
240}
241
242impl<'a> WriteEarlyData<'a> {
243    fn new(sess: &'a mut ClientConnection) -> Self {
244        WriteEarlyData { sess }
245    }
246
247    /// How many bytes you may send.  Writes will become short
248    /// once this reaches zero.
249    pub fn bytes_left(&self) -> usize {
250        self.sess
251            .inner
252            .core
253            .side
254            .early_data
255            .bytes_left()
256    }
257
258    /// Returns the "early" exporter that can derive key material for use in early data
259    ///
260    /// See [RFC5705][] for general details on what exporters are, and [RFC8446 S7.5][] for
261    /// specific details on the "early" exporter.
262    ///
263    /// **Beware** that the early exporter requires care, as it is subject to the same
264    /// potential for replay as early data itself.  See [RFC8446 appendix E.5.1][] for
265    /// more detail.
266    ///
267    /// This function can be called at most once per connection. This function will error:
268    /// if called more than once per connection.
269    ///
270    /// If you are looking for the normal exporter, this is available from
271    /// [`Connection::exporter()`].
272    ///
273    /// [RFC5705]: https://datatracker.ietf.org/doc/html/rfc5705
274    /// [RFC8446 S7.5]: https://datatracker.ietf.org/doc/html/rfc8446#section-7.5
275    /// [RFC8446 appendix E.5.1]: https://datatracker.ietf.org/doc/html/rfc8446#appendix-E.5.1
276    /// [`Connection::exporter()`]: crate::conn::Connection::exporter()
277    pub fn exporter(&mut self) -> Result<KeyingMaterialExporter, Error> {
278        self.sess.inner.core.early_exporter()
279    }
280}
281
282impl io::Write for WriteEarlyData<'_> {
283    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
284        self.sess.write_early_data(buf)
285    }
286
287    fn flush(&mut self) -> io::Result<()> {
288        Ok(())
289    }
290}
291
292impl ConnectionCore<ClientSide> {
293    pub(crate) fn for_client(
294        config: Arc<ClientConfig>,
295        name: ServerName<'static>,
296        extra_exts: ClientExtensionsInput,
297        proto: Protocol,
298    ) -> Result<Self, Error> {
299        let mut common_state = CommonState::new(Side::Client, proto);
300        common_state
301            .send
302            .set_max_fragment_size(config.max_fragment_size)?;
303        let mut data = ClientConnectionData::new();
304
305        let mut output = SideCommonOutput {
306            side: &mut data,
307            common: &mut common_state,
308        };
309
310        let input = ClientHelloInput::new(name, &extra_exts, proto, &mut output, config)?;
311        let state = input.start_handshake(extra_exts, &mut output)?;
312
313        Ok(Self::new(state, data, common_state))
314    }
315
316    pub(crate) fn is_early_data_accepted(&self) -> bool {
317        self.side.early_data.is_accepted()
318    }
319}
320
321#[derive(Debug)]
322pub(super) struct EarlyData {
323    state: EarlyDataState,
324    left: usize,
325}
326
327impl EarlyData {
328    fn new() -> Self {
329        Self {
330            state: EarlyDataState::Disabled,
331            left: 0,
332        }
333    }
334
335    fn is_enabled(&self) -> bool {
336        matches!(
337            self.state,
338            EarlyDataState::Ready | EarlyDataState::Sending | EarlyDataState::Accepted
339        )
340    }
341
342    fn is_accepted(&self) -> bool {
343        matches!(
344            self.state,
345            EarlyDataState::Accepted | EarlyDataState::AcceptedFinished
346        )
347    }
348
349    fn enable(&mut self, max_data: usize) {
350        assert_eq!(self.state, EarlyDataState::Disabled);
351        self.state = EarlyDataState::Ready;
352        self.left = max_data;
353    }
354
355    fn start(&mut self) {
356        assert_eq!(self.state, EarlyDataState::Ready);
357        self.state = EarlyDataState::Sending;
358    }
359
360    fn rejected(&mut self) {
361        trace!("EarlyData rejected");
362        self.state = EarlyDataState::Rejected;
363    }
364
365    fn accepted(&mut self) {
366        trace!("EarlyData accepted");
367        assert_eq!(self.state, EarlyDataState::Sending);
368        self.state = EarlyDataState::Accepted;
369    }
370
371    pub(super) fn finished(&mut self) {
372        trace!("EarlyData finished");
373        self.state = match self.state {
374            EarlyDataState::Accepted => EarlyDataState::AcceptedFinished,
375            _ => panic!("bad EarlyData state"),
376        }
377    }
378
379    fn check_write(&mut self, sz: usize) -> io::Result<usize> {
380        self.check_write_opt(sz)
381            .ok_or_else(|| io::Error::from(io::ErrorKind::InvalidInput))
382    }
383
384    fn check_write_opt(&mut self, sz: usize) -> Option<usize> {
385        match self.state {
386            EarlyDataState::Disabled => unreachable!(),
387            EarlyDataState::Ready | EarlyDataState::Sending | EarlyDataState::Accepted => {
388                let take = if self.left < sz {
389                    mem::replace(&mut self.left, 0)
390                } else {
391                    self.left -= sz;
392                    sz
393                };
394
395                Some(take)
396            }
397            EarlyDataState::Rejected | EarlyDataState::AcceptedFinished => None,
398        }
399    }
400
401    fn bytes_left(&self) -> usize {
402        self.left
403    }
404}
405
406#[derive(Debug, PartialEq)]
407enum EarlyDataState {
408    Disabled,
409    Ready,
410    Sending,
411    Accepted,
412    AcceptedFinished,
413    Rejected,
414}
415
416/// Errors that may arise when encrypting early (RTT-0) data
417#[non_exhaustive]
418#[derive(Debug)]
419pub enum EarlyDataError {
420    /// Cannot encrypt more early data due to imposed limits
421    ExceededAllowedEarlyData,
422    /// Encryption error
423    Encrypt(EncryptError),
424}
425
426impl From<EncryptError> for EarlyDataError {
427    fn from(v: EncryptError) -> Self {
428        Self::Encrypt(v)
429    }
430}
431
432impl fmt::Display for EarlyDataError {
433    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
434        match self {
435            Self::ExceededAllowedEarlyData => f.write_str("cannot send any more early data"),
436            Self::Encrypt(e) => fmt::Display::fmt(e, f),
437        }
438    }
439}
440
441impl core::error::Error for EarlyDataError {}
442
443#[derive(Debug)]
444pub(crate) struct ClientConnectionData {
445    early_data: EarlyData,
446    ech_status: EchStatus,
447}
448
449impl ClientConnectionData {
450    fn new() -> Self {
451        Self {
452            early_data: EarlyData::new(),
453            ech_status: EchStatus::default(),
454        }
455    }
456}
457
458impl Output for ClientConnectionData {
459    fn emit(&mut self, ev: Event<'_>) {
460        match ev {
461            Event::EchStatus(ech) => self.ech_status = ech,
462            Event::EarlyData(EarlyDataEvent::Accepted) => self.early_data.accepted(),
463            Event::EarlyData(EarlyDataEvent::Enable(sz)) => self.early_data.enable(sz),
464            Event::EarlyData(EarlyDataEvent::Finished) => self.early_data.finished(),
465            Event::EarlyData(EarlyDataEvent::Start) => self.early_data.start(),
466            Event::EarlyData(EarlyDataEvent::Rejected) => self.early_data.rejected(),
467            _ => unreachable!(),
468        }
469    }
470}
471
472/// State associated with a client connection.
473#[expect(clippy::exhaustive_structs)]
474#[derive(Debug)]
475pub struct ClientSide;
476
477impl crate::conn::SideData for ClientSide {}
478
479impl crate::conn::private::Side for ClientSide {
480    type Data = ClientConnectionData;
481    type State = super::hs::ClientState;
482}