rustls/conn/
unbuffered.rs

1//! Unbuffered connection API
2
3use alloc::vec::Vec;
4use core::num::NonZeroUsize;
5use core::{fmt, mem};
6#[cfg(feature = "std")]
7use std::error::Error as StdError;
8
9use super::UnbufferedConnectionCommon;
10use crate::Error;
11use crate::client::ClientConnectionData;
12use crate::msgs::deframer::buffers::DeframerSliceBuffer;
13use crate::server::ServerConnectionData;
14
15impl UnbufferedConnectionCommon<ClientConnectionData> {
16    /// Processes the TLS records in `incoming_tls` buffer until a new [`UnbufferedStatus`] is
17    /// reached.
18    pub fn process_tls_records<'c, 'i>(
19        &'c mut self,
20        incoming_tls: &'i mut [u8],
21    ) -> UnbufferedStatus<'c, 'i, ClientConnectionData> {
22        self.process_tls_records_common(incoming_tls, |_| false, |_, _| unreachable!())
23    }
24}
25
26impl UnbufferedConnectionCommon<ServerConnectionData> {
27    /// Processes the TLS records in `incoming_tls` buffer until a new [`UnbufferedStatus`] is
28    /// reached.
29    pub fn process_tls_records<'c, 'i>(
30        &'c mut self,
31        incoming_tls: &'i mut [u8],
32    ) -> UnbufferedStatus<'c, 'i, ServerConnectionData> {
33        self.process_tls_records_common(
34            incoming_tls,
35            |conn| conn.peek_early_data().is_some(),
36            |conn, incoming_tls| ReadEarlyData::new(conn, incoming_tls).into(),
37        )
38    }
39}
40
41impl<Data> UnbufferedConnectionCommon<Data> {
42    fn process_tls_records_common<'c, 'i>(
43        &'c mut self,
44        incoming_tls: &'i mut [u8],
45        mut early_data_available: impl FnMut(&mut Self) -> bool,
46        early_data_state: impl FnOnce(&'c mut Self, &'i mut [u8]) -> ConnectionState<'c, 'i, Data>,
47    ) -> UnbufferedStatus<'c, 'i, Data> {
48        let mut buffer = DeframerSliceBuffer::new(incoming_tls);
49        let mut buffer_progress = self.core.hs_deframer.progress();
50
51        let (discard, state) = loop {
52            if early_data_available(self) {
53                break (
54                    buffer.pending_discard(),
55                    early_data_state(self, incoming_tls),
56                );
57            }
58
59            if !self
60                .core
61                .common_state
62                .received_plaintext
63                .is_empty()
64            {
65                break (
66                    buffer.pending_discard(),
67                    ReadTraffic::new(self, incoming_tls).into(),
68                );
69            }
70
71            if let Some(chunk) = self
72                .core
73                .common_state
74                .sendable_tls
75                .pop()
76            {
77                break (
78                    buffer.pending_discard(),
79                    EncodeTlsData::new(self, chunk).into(),
80                );
81            }
82
83            let deframer_output = if self
84                .core
85                .common_state
86                .has_received_close_notify
87            {
88                None
89            } else {
90                match self
91                    .core
92                    .deframe(None, buffer.filled_mut(), &mut buffer_progress)
93                {
94                    Err(err) => {
95                        buffer.queue_discard(buffer_progress.take_discard());
96                        return UnbufferedStatus {
97                            discard: buffer.pending_discard(),
98                            state: Err(err),
99                        };
100                    }
101                    Ok(r) => r,
102                }
103            };
104
105            if let Some(msg) = deframer_output {
106                let mut state =
107                    match mem::replace(&mut self.core.state, Err(Error::HandshakeNotComplete)) {
108                        Ok(state) => state,
109                        Err(e) => {
110                            buffer.queue_discard(buffer_progress.take_discard());
111                            self.core.state = Err(e.clone());
112                            return UnbufferedStatus {
113                                discard: buffer.pending_discard(),
114                                state: Err(e),
115                            };
116                        }
117                    };
118
119                match self.core.process_msg(msg, state, None) {
120                    Ok(new) => state = new,
121
122                    Err(e) => {
123                        buffer.queue_discard(buffer_progress.take_discard());
124                        self.core.state = Err(e.clone());
125                        return UnbufferedStatus {
126                            discard: buffer.pending_discard(),
127                            state: Err(e),
128                        };
129                    }
130                }
131
132                buffer.queue_discard(buffer_progress.take_discard());
133
134                self.core.state = Ok(state);
135            } else if self.wants_write {
136                break (
137                    buffer.pending_discard(),
138                    TransmitTlsData { conn: self }.into(),
139                );
140            } else if self
141                .core
142                .common_state
143                .has_received_close_notify
144                && !self.emitted_peer_closed_state
145            {
146                self.emitted_peer_closed_state = true;
147                break (buffer.pending_discard(), ConnectionState::PeerClosed);
148            } else if self
149                .core
150                .common_state
151                .has_received_close_notify
152                && self
153                    .core
154                    .common_state
155                    .has_sent_close_notify
156            {
157                break (buffer.pending_discard(), ConnectionState::Closed);
158            } else if self
159                .core
160                .common_state
161                .may_send_application_data
162            {
163                break (
164                    buffer.pending_discard(),
165                    ConnectionState::WriteTraffic(WriteTraffic { conn: self }),
166                );
167            } else {
168                break (buffer.pending_discard(), ConnectionState::BlockedHandshake);
169            }
170        };
171
172        UnbufferedStatus {
173            discard,
174            state: Ok(state),
175        }
176    }
177}
178
179/// The current status of the `UnbufferedConnection*`
180#[non_exhaustive]
181#[must_use]
182#[derive(Debug)]
183pub struct UnbufferedStatus<'c, 'i, Data> {
184    /// Number of bytes to discard
185    ///
186    /// After the `state` field of this object has been handled, `discard` bytes must be
187    /// removed from the *front* of the `incoming_tls` buffer that was passed to
188    /// the [`UnbufferedConnectionCommon::process_tls_records`] call that returned this object.
189    ///
190    /// This discard operation MUST happen *before*
191    /// [`UnbufferedConnectionCommon::process_tls_records`] is called again.
192    pub discard: usize,
193
194    /// The current state of the handshake process
195    ///
196    /// This value MUST be handled prior to calling
197    /// [`UnbufferedConnectionCommon::process_tls_records`] again. See the documentation on the
198    /// variants of [`ConnectionState`] for more details.
199    pub state: Result<ConnectionState<'c, 'i, Data>, Error>,
200}
201
202/// The state of the [`UnbufferedConnectionCommon`] object
203#[non_exhaustive] // for forwards compatibility; to support caller-side certificate verification
204pub enum ConnectionState<'c, 'i, Data> {
205    /// One, or more, application data records are available
206    ///
207    /// See [`ReadTraffic`] for more details on how to use the enclosed object to access
208    /// the received data.
209    ReadTraffic(ReadTraffic<'c, 'i, Data>),
210
211    /// Connection has been cleanly closed by the peer.
212    ///
213    /// This state is encountered at most once by each connection -- it is
214    /// "edge" triggered, rather than "level" triggered.
215    ///
216    /// It delimits the data received from the peer, meaning you can be sure you
217    /// have received all the data the peer sent.
218    ///
219    /// No further application data will be received from the peer, so no further
220    /// `ReadTraffic` states will be produced.
221    ///
222    /// However, it is possible to _send_ further application data via `WriteTraffic`
223    /// states, or close the connection cleanly by calling
224    /// [`WriteTraffic::queue_close_notify()`].
225    PeerClosed,
226
227    /// Connection has been cleanly closed by both us and the peer.
228    ///
229    /// This is a terminal state.  No other states will be produced for this
230    /// connection.
231    Closed,
232
233    /// One, or more, early (RTT-0) data records are available
234    ReadEarlyData(ReadEarlyData<'c, 'i, Data>),
235
236    /// A Handshake record is ready for encoding
237    ///
238    /// Call [`EncodeTlsData::encode`] on the enclosed object, providing an `outgoing_tls`
239    /// buffer to store the encoding
240    EncodeTlsData(EncodeTlsData<'c, Data>),
241
242    /// Previously encoded handshake records need to be transmitted
243    ///
244    /// Transmit the contents of the `outgoing_tls` buffer that was passed to previous
245    /// [`EncodeTlsData::encode`] calls to the peer.
246    ///
247    /// After transmitting the contents, call [`TransmitTlsData::done`] on the enclosed object.
248    /// The transmitted contents MUST not be sent to the peer more than once so they SHOULD be
249    /// discarded at this point.
250    ///
251    /// At some stages of the handshake process, it's possible to send application-data alongside
252    /// handshake records. Call [`TransmitTlsData::may_encrypt_app_data`] on the enclosed
253    /// object to probe if that's allowed.
254    TransmitTlsData(TransmitTlsData<'c, Data>),
255
256    /// More TLS data is needed to continue with the handshake
257    ///
258    /// Request more data from the peer and append the contents to the `incoming_tls` buffer that
259    /// was passed to [`UnbufferedConnectionCommon::process_tls_records`].
260    BlockedHandshake,
261
262    /// The handshake process has been completed.
263    ///
264    /// [`WriteTraffic::encrypt`] can be called on the enclosed object to encrypt application
265    /// data into an `outgoing_tls` buffer. Similarly, [`WriteTraffic::queue_close_notify`] can
266    /// be used to encrypt a close_notify alert message into a buffer to signal the peer that the
267    /// connection is being closed. Data written into `outgoing_buffer` by either method MAY be
268    /// transmitted to the peer during this state.
269    ///
270    /// Once this state has been reached, data MAY be requested from the peer and appended to an
271    /// `incoming_tls` buffer that will be passed to a future
272    /// [`UnbufferedConnectionCommon::process_tls_records`] invocation. When enough data has been
273    /// appended to `incoming_tls`, [`UnbufferedConnectionCommon::process_tls_records`] will yield
274    /// the [`ConnectionState::ReadTraffic`] state.
275    WriteTraffic(WriteTraffic<'c, Data>),
276}
277
278impl<'c, 'i, Data> From<ReadTraffic<'c, 'i, Data>> for ConnectionState<'c, 'i, Data> {
279    fn from(v: ReadTraffic<'c, 'i, Data>) -> Self {
280        Self::ReadTraffic(v)
281    }
282}
283
284impl<'c, 'i, Data> From<ReadEarlyData<'c, 'i, Data>> for ConnectionState<'c, 'i, Data> {
285    fn from(v: ReadEarlyData<'c, 'i, Data>) -> Self {
286        Self::ReadEarlyData(v)
287    }
288}
289
290impl<'c, Data> From<EncodeTlsData<'c, Data>> for ConnectionState<'c, '_, Data> {
291    fn from(v: EncodeTlsData<'c, Data>) -> Self {
292        Self::EncodeTlsData(v)
293    }
294}
295
296impl<'c, Data> From<TransmitTlsData<'c, Data>> for ConnectionState<'c, '_, Data> {
297    fn from(v: TransmitTlsData<'c, Data>) -> Self {
298        Self::TransmitTlsData(v)
299    }
300}
301
302impl<Data> fmt::Debug for ConnectionState<'_, '_, Data> {
303    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
304        match self {
305            Self::ReadTraffic(..) => f.debug_tuple("ReadTraffic").finish(),
306
307            Self::PeerClosed => write!(f, "PeerClosed"),
308
309            Self::Closed => write!(f, "Closed"),
310
311            Self::ReadEarlyData(..) => f.debug_tuple("ReadEarlyData").finish(),
312
313            Self::EncodeTlsData(..) => f.debug_tuple("EncodeTlsData").finish(),
314
315            Self::TransmitTlsData(..) => f
316                .debug_tuple("TransmitTlsData")
317                .finish(),
318
319            Self::BlockedHandshake => f
320                .debug_tuple("BlockedHandshake")
321                .finish(),
322
323            Self::WriteTraffic(..) => f.debug_tuple("WriteTraffic").finish(),
324        }
325    }
326}
327
328/// Application data is available
329pub struct ReadTraffic<'c, 'i, Data> {
330    conn: &'c mut UnbufferedConnectionCommon<Data>,
331    // for forwards compatibility; to support in-place decryption in the future
332    _incoming_tls: &'i mut [u8],
333
334    // owner of the latest chunk obtained in `next_record`, as borrowed by
335    // `AppDataRecord`
336    chunk: Option<Vec<u8>>,
337}
338
339impl<'c, 'i, Data> ReadTraffic<'c, 'i, Data> {
340    fn new(conn: &'c mut UnbufferedConnectionCommon<Data>, _incoming_tls: &'i mut [u8]) -> Self {
341        Self {
342            conn,
343            _incoming_tls,
344            chunk: None,
345        }
346    }
347
348    /// Decrypts and returns the next available app-data record
349    // TODO deprecate in favor of `Iterator` implementation, which requires in-place decryption
350    pub fn next_record(&mut self) -> Option<Result<AppDataRecord<'_>, Error>> {
351        self.chunk = self
352            .conn
353            .core
354            .common_state
355            .received_plaintext
356            .pop();
357        self.chunk.as_ref().map(|chunk| {
358            Ok(AppDataRecord {
359                discard: 0,
360                payload: chunk,
361            })
362        })
363    }
364
365    /// Returns the payload size of the next app-data record *without* decrypting it
366    ///
367    /// Returns `None` if there are no more app-data records
368    pub fn peek_len(&self) -> Option<NonZeroUsize> {
369        self.conn
370            .core
371            .common_state
372            .received_plaintext
373            .peek()
374            .and_then(|ch| NonZeroUsize::new(ch.len()))
375    }
376}
377
378/// Early application-data is available.
379pub struct ReadEarlyData<'c, 'i, Data> {
380    conn: &'c mut UnbufferedConnectionCommon<Data>,
381
382    // for forwards compatibility; to support in-place decryption in the future
383    _incoming_tls: &'i mut [u8],
384
385    // owner of the latest chunk obtained in `next_record`, as borrowed by
386    // `AppDataRecord`
387    chunk: Option<Vec<u8>>,
388}
389
390impl<'c, 'i> ReadEarlyData<'c, 'i, ServerConnectionData> {
391    fn new(
392        conn: &'c mut UnbufferedConnectionCommon<ServerConnectionData>,
393        _incoming_tls: &'i mut [u8],
394    ) -> Self {
395        Self {
396            conn,
397            _incoming_tls,
398            chunk: None,
399        }
400    }
401
402    /// decrypts and returns the next available app-data record
403    // TODO deprecate in favor of `Iterator` implementation, which requires in-place decryption
404    pub fn next_record(&mut self) -> Option<Result<AppDataRecord<'_>, Error>> {
405        self.chunk = self.conn.pop_early_data();
406        self.chunk.as_ref().map(|chunk| {
407            Ok(AppDataRecord {
408                discard: 0,
409                payload: chunk,
410            })
411        })
412    }
413
414    /// returns the payload size of the next app-data record *without* decrypting it
415    ///
416    /// returns `None` if there are no more app-data records
417    pub fn peek_len(&self) -> Option<NonZeroUsize> {
418        self.conn
419            .peek_early_data()
420            .and_then(|ch| NonZeroUsize::new(ch.len()))
421    }
422}
423
424/// A decrypted application-data record
425#[non_exhaustive]
426pub struct AppDataRecord<'i> {
427    /// Number of additional bytes to discard
428    ///
429    /// This number MUST be added to the value of [`UnbufferedStatus::discard`] *prior* to the
430    /// discard operation. See [`UnbufferedStatus::discard`] for more details
431    pub discard: usize,
432
433    /// The payload of the app-data record
434    pub payload: &'i [u8],
435}
436
437/// Allows encrypting app-data
438pub struct WriteTraffic<'c, Data> {
439    conn: &'c mut UnbufferedConnectionCommon<Data>,
440}
441
442impl<Data> WriteTraffic<'_, Data> {
443    /// Encrypts `application_data` into the `outgoing_tls` buffer
444    ///
445    /// Returns the number of bytes that were written into `outgoing_tls`, or an error if
446    /// the provided buffer is too small. In the error case, `outgoing_tls` is not modified
447    pub fn encrypt(
448        &mut self,
449        application_data: &[u8],
450        outgoing_tls: &mut [u8],
451    ) -> Result<usize, EncryptError> {
452        self.conn
453            .core
454            .maybe_refresh_traffic_keys();
455        self.conn
456            .core
457            .common_state
458            .write_plaintext(application_data.into(), outgoing_tls)
459    }
460
461    /// Encrypts a close_notify warning alert in `outgoing_tls`
462    ///
463    /// Returns the number of bytes that were written into `outgoing_tls`, or an error if
464    /// the provided buffer is too small. In the error case, `outgoing_tls` is not modified
465    pub fn queue_close_notify(&mut self, outgoing_tls: &mut [u8]) -> Result<usize, EncryptError> {
466        self.conn
467            .core
468            .common_state
469            .eager_send_close_notify(outgoing_tls)
470    }
471
472    /// Arranges for a TLS1.3 `key_update` to be sent.
473    ///
474    /// This consumes the `WriteTraffic` state:  to actually send the message,
475    /// call [`UnbufferedConnectionCommon::process_tls_records`] again which will
476    /// return a `ConnectionState::EncodeTlsData` that emits the `key_update`
477    /// message.
478    ///
479    /// See [`ConnectionCommon::refresh_traffic_keys()`] for full documentation,
480    /// including why you might call this and in what circumstances it will fail.
481    ///
482    /// [`ConnectionCommon::refresh_traffic_keys()`]: crate::ConnectionCommon::refresh_traffic_keys
483    pub fn refresh_traffic_keys(self) -> Result<(), Error> {
484        self.conn.core.refresh_traffic_keys()
485    }
486}
487
488/// A handshake record must be encoded
489pub struct EncodeTlsData<'c, Data> {
490    conn: &'c mut UnbufferedConnectionCommon<Data>,
491    chunk: Option<Vec<u8>>,
492}
493
494impl<'c, Data> EncodeTlsData<'c, Data> {
495    fn new(conn: &'c mut UnbufferedConnectionCommon<Data>, chunk: Vec<u8>) -> Self {
496        Self {
497            conn,
498            chunk: Some(chunk),
499        }
500    }
501
502    /// Encodes a handshake record into the `outgoing_tls` buffer
503    ///
504    /// Returns the number of bytes that were written into `outgoing_tls`, or an error if
505    /// the provided buffer is too small. In the error case, `outgoing_tls` is not modified
506    pub fn encode(&mut self, outgoing_tls: &mut [u8]) -> Result<usize, EncodeError> {
507        let Some(chunk) = self.chunk.take() else {
508            return Err(EncodeError::AlreadyEncoded);
509        };
510
511        let required_size = chunk.len();
512
513        if required_size > outgoing_tls.len() {
514            self.chunk = Some(chunk);
515            Err(InsufficientSizeError { required_size }.into())
516        } else {
517            let written = chunk.len();
518            outgoing_tls[..written].copy_from_slice(&chunk);
519
520            self.conn.wants_write = true;
521
522            Ok(written)
523        }
524    }
525}
526
527/// Previously encoded TLS data must be transmitted
528pub struct TransmitTlsData<'c, Data> {
529    pub(crate) conn: &'c mut UnbufferedConnectionCommon<Data>,
530}
531
532impl<Data> TransmitTlsData<'_, Data> {
533    /// Signals that the previously encoded TLS data has been transmitted
534    pub fn done(self) {
535        self.conn.wants_write = false;
536    }
537
538    /// Returns an adapter that allows encrypting application data
539    ///
540    /// If allowed at this stage of the handshake process
541    pub fn may_encrypt_app_data(&mut self) -> Option<WriteTraffic<'_, Data>> {
542        if self
543            .conn
544            .core
545            .common_state
546            .may_send_application_data
547        {
548            Some(WriteTraffic { conn: self.conn })
549        } else {
550            None
551        }
552    }
553}
554
555/// Errors that may arise when encoding a handshake record
556#[non_exhaustive]
557#[derive(Debug)]
558pub enum EncodeError {
559    /// Provided buffer was too small
560    InsufficientSize(InsufficientSizeError),
561
562    /// The handshake record has already been encoded; do not call `encode` again
563    AlreadyEncoded,
564}
565
566impl From<InsufficientSizeError> for EncodeError {
567    fn from(v: InsufficientSizeError) -> Self {
568        Self::InsufficientSize(v)
569    }
570}
571
572impl fmt::Display for EncodeError {
573    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
574        match self {
575            Self::InsufficientSize(InsufficientSizeError { required_size }) => write!(
576                f,
577                "cannot encode due to insufficient size, {required_size} bytes are required"
578            ),
579            Self::AlreadyEncoded => "cannot encode, data has already been encoded".fmt(f),
580        }
581    }
582}
583
584#[cfg(feature = "std")]
585impl StdError for EncodeError {}
586
587/// Errors that may arise when encrypting application data
588#[non_exhaustive]
589#[derive(Debug)]
590pub enum EncryptError {
591    /// Provided buffer was too small
592    InsufficientSize(InsufficientSizeError),
593
594    /// Encrypter has been exhausted
595    EncryptExhausted,
596}
597
598impl From<InsufficientSizeError> for EncryptError {
599    fn from(v: InsufficientSizeError) -> Self {
600        Self::InsufficientSize(v)
601    }
602}
603
604impl fmt::Display for EncryptError {
605    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
606        match self {
607            Self::InsufficientSize(InsufficientSizeError { required_size }) => write!(
608                f,
609                "cannot encrypt due to insufficient size, {required_size} bytes are required"
610            ),
611            Self::EncryptExhausted => f.write_str("encrypter has been exhausted"),
612        }
613    }
614}
615
616#[cfg(feature = "std")]
617impl StdError for EncryptError {}
618
619/// Provided buffer was too small
620#[non_exhaustive]
621#[derive(Clone, Copy, Debug)]
622pub struct InsufficientSizeError {
623    /// buffer must be at least this size
624    pub required_size: usize,
625}