Skip to main content

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