Skip to main content

rustls/client/
connection.rs

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