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