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