rustls/conn/
unbuffered.rs

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