Skip to main content

rustls/crypto/cipher/
messages.rs

1use alloc::vec::Vec;
2use core::fmt;
3use core::ops::{Deref, DerefMut, Range};
4
5use crate::crypto::cipher::EncryptionState;
6use crate::enums::{ContentType, ProtocolVersion};
7use crate::error::{Error, InvalidMessage, PeerMisbehaved};
8use crate::msgs::{Codec, HEADER_SIZE, MAX_FRAGMENT_LEN, Reader, hex, read_opaque_message_header};
9
10/// A TLS message with encoded (but not necessarily encrypted) payload.
11#[expect(clippy::exhaustive_structs)]
12#[derive(Clone, Debug)]
13pub struct EncodedMessage<P> {
14    /// The content type of this message.
15    pub typ: ContentType,
16    /// The protocol version of this message.
17    pub version: ProtocolVersion,
18    /// The payload of this message.
19    pub payload: P,
20}
21
22impl<P> EncodedMessage<P> {
23    /// Create a new `EncodedMessage` with the given fields.
24    pub fn new(typ: ContentType, version: ProtocolVersion, payload: P) -> Self {
25        Self {
26            typ,
27            version,
28            payload,
29        }
30    }
31}
32
33impl<'a> EncodedMessage<Payload<'a>> {
34    /// Construct by decoding from a [`Reader`].
35    ///
36    /// `MessageError` allows callers to distinguish between valid prefixes (might
37    /// become valid if we read more data) and invalid data.
38    pub(crate) fn read(r: &mut Reader<'a>) -> Result<Self, MessageError> {
39        let (typ, version, len) = read_opaque_message_header(r)?;
40
41        let content = r
42            .take(len as usize)
43            .ok_or(MessageError::TooShortForLength)?;
44
45        Ok(Self {
46            typ,
47            version,
48            payload: Payload::Borrowed(content),
49        })
50    }
51
52    /// Convert into an unencrypted [`EncodedMessage<OutboundOpaque>`] (without decrypting).
53    pub fn into_unencrypted_opaque(self) -> EncodedMessage<OutboundOpaque> {
54        EncodedMessage {
55            typ: self.typ,
56            version: self.version,
57            payload: OutboundOpaque::from(self.payload.bytes()),
58        }
59    }
60
61    /// Borrow as an [`EncodedMessage<OutboundPlain<'a>>`].
62    pub fn borrow_outbound(&'a self) -> EncodedMessage<OutboundPlain<'a>> {
63        EncodedMessage {
64            typ: self.typ,
65            version: self.version,
66            payload: self.payload.bytes().into(),
67        }
68    }
69
70    /// Convert into an owned `EncodedMessage<Plain<'static>>`.
71    pub fn into_owned(self) -> Self {
72        Self {
73            typ: self.typ,
74            version: self.version,
75            payload: self.payload.into_owned(),
76        }
77    }
78}
79
80impl EncodedMessage<&'_ [u8]> {
81    /// Returns true if the payload is a CCS message.
82    ///
83    /// We passthrough ChangeCipherSpec messages in the deframer without decrypting them.
84    /// Note: this is prior to the record layer, so is unencrypted. See
85    /// third paragraph of section 5 in RFC8446.
86    pub(crate) fn is_valid_ccs(&self) -> bool {
87        self.typ == ContentType::ChangeCipherSpec && self.payload == [0x01]
88    }
89}
90
91impl<'a> EncodedMessage<InboundOpaque<'a>> {
92    /// For TLS1.3 (only), checks the length msg.payload is valid and removes the padding.
93    ///
94    /// Returns an error if the message (pre-unpadding) is too long, or the padding is invalid,
95    /// or the message (post-unpadding) is too long.
96    pub fn into_tls13_unpadded_message(mut self) -> Result<EncodedMessage<&'a [u8]>, Error> {
97        let payload = &mut self.payload;
98
99        if payload.len() > MAX_FRAGMENT_LEN + 1 {
100            return Err(Error::PeerSentOversizedRecord);
101        }
102
103        self.typ = unpad_tls13_payload(payload);
104        if self.typ == ContentType(0) {
105            return Err(PeerMisbehaved::IllegalTlsInnerPlaintext.into());
106        }
107
108        if payload.len() > MAX_FRAGMENT_LEN {
109            return Err(Error::PeerSentOversizedRecord);
110        }
111
112        self.version = ProtocolVersion::TLSv1_3;
113        Ok(self.into_plain_message())
114    }
115
116    /// Force conversion into a plaintext message.
117    ///
118    /// `range` restricts the resulting message: this function panics if it is out of range for
119    /// the underlying message payload.
120    ///
121    /// This should only be used for messages that are known to be in plaintext. Otherwise, the
122    /// [`EncodedMessage<InboundOpaque<'_>>`] should be decrypted into an
123    /// `EncodedMessage<&'_ [u8]>` using a `MessageDecrypter`.
124    pub fn into_plain_message_range(self, range: Range<usize>) -> EncodedMessage<&'a [u8]> {
125        EncodedMessage {
126            typ: self.typ,
127            version: self.version,
128            payload: &self.payload.into_inner()[range],
129        }
130    }
131
132    /// Force conversion into a plaintext message.
133    ///
134    /// This should only be used for messages that are known to be in plaintext. Otherwise, the
135    /// [`EncodedMessage<InboundOpaque<'a>>`] should be decrypted into a
136    /// `EncodedMessage<&'a [u8]>` using a `MessageDecrypter`.
137    pub fn into_plain_message(self) -> EncodedMessage<&'a [u8]> {
138        EncodedMessage {
139            typ: self.typ,
140            version: self.version,
141            payload: self.payload.into_inner(),
142        }
143    }
144}
145
146impl EncodedMessage<OutboundPlain<'_>> {
147    pub(crate) fn to_unencrypted_opaque(&self) -> EncodedMessage<OutboundOpaque> {
148        let mut payload = OutboundOpaque::with_capacity(self.payload.len());
149        payload.extend_from_chunks(&self.payload);
150        EncodedMessage {
151            typ: self.typ,
152            version: self.version,
153            payload,
154        }
155    }
156
157    #[expect(dead_code)]
158    pub(crate) fn encoded_len(&self, record_layer: &EncryptionState) -> usize {
159        HEADER_SIZE + record_layer.encrypted_len(self.payload.len())
160    }
161}
162
163impl EncodedMessage<OutboundOpaque> {
164    /// Encode this message to a vector of bytes.
165    pub fn encode(self) -> Vec<u8> {
166        let length = self.payload.len() as u16;
167        let mut encoded_payload = self.payload.0;
168        encoded_payload[0] = self.typ.into();
169        encoded_payload[1..3].copy_from_slice(&self.version.to_array());
170        encoded_payload[3..5].copy_from_slice(&(length).to_be_bytes());
171        encoded_payload
172    }
173}
174
175/// A collection of borrowed plaintext slices.
176///
177/// Warning: OutboundChunks does not guarantee that the simplest variant is used.
178/// Multiple can hold non fragmented or empty payloads.
179#[non_exhaustive]
180#[derive(Debug, Clone)]
181pub enum OutboundPlain<'a> {
182    /// A single byte slice.
183    ///
184    /// Contrary to `Multiple`, this uses a single pointer indirection
185    Single(&'a [u8]),
186    /// A collection of chunks (byte slices).
187    Multiple {
188        /// A collection of byte slices that hold the buffered data.
189        chunks: &'a [&'a [u8]],
190        /// The start cursor into the first chunk.
191        start: usize,
192        /// The end cursor into the last chunk.
193        end: usize,
194    },
195}
196
197impl<'a> OutboundPlain<'a> {
198    /// Create a payload from a slice of byte slices.
199    /// If fragmented the cursors are added by default: start = 0, end = length
200    pub fn new(chunks: &'a [&'a [u8]]) -> Self {
201        if chunks.len() == 1 {
202            Self::Single(chunks[0])
203        } else {
204            Self::Multiple {
205                chunks,
206                start: 0,
207                end: chunks
208                    .iter()
209                    .map(|chunk| chunk.len())
210                    .sum(),
211            }
212        }
213    }
214
215    /// Create a payload with a single empty slice
216    pub fn new_empty() -> Self {
217        Self::Single(&[])
218    }
219
220    /// Flatten the slice of byte slices to an owned vector of bytes
221    pub fn to_vec(&self) -> Vec<u8> {
222        let mut vec = Vec::with_capacity(self.len());
223        self.copy_to_vec(&mut vec);
224        vec
225    }
226
227    /// Append all bytes to a vector
228    pub fn copy_to_vec(&self, vec: &mut Vec<u8>) {
229        match *self {
230            Self::Single(chunk) => vec.extend_from_slice(chunk),
231            Self::Multiple { chunks, start, end } => {
232                let mut size = 0;
233                for chunk in chunks.iter() {
234                    let psize = size;
235                    let len = chunk.len();
236                    size += len;
237                    if size <= start || psize >= end {
238                        continue;
239                    }
240                    let start = start.saturating_sub(psize);
241                    let end = if end - psize < len { end - psize } else { len };
242                    vec.extend_from_slice(&chunk[start..end]);
243                }
244            }
245        }
246    }
247
248    /// Split self in two, around an index
249    /// Works similarly to `split_at` in the core library, except it doesn't panic if out of bound
250    pub(crate) fn split_at(&self, mid: usize) -> (Self, Self) {
251        match *self {
252            Self::Single(chunk) => {
253                let mid = Ord::min(mid, chunk.len());
254                (Self::Single(&chunk[..mid]), Self::Single(&chunk[mid..]))
255            }
256            Self::Multiple { chunks, start, end } => {
257                let mid = Ord::min(start + mid, end);
258                (
259                    Self::Multiple {
260                        chunks,
261                        start,
262                        end: mid,
263                    },
264                    Self::Multiple {
265                        chunks,
266                        start: mid,
267                        end,
268                    },
269                )
270            }
271        }
272    }
273
274    /// Returns true if the payload is empty
275    pub(crate) fn is_empty(&self) -> bool {
276        self.len() == 0
277    }
278
279    /// Returns the cumulative length of all chunks
280    #[expect(clippy::len_without_is_empty)]
281    pub fn len(&self) -> usize {
282        match self {
283            Self::Single(chunk) => chunk.len(),
284            Self::Multiple { start, end, .. } => end - start,
285        }
286    }
287}
288
289impl<'a> From<&'a [u8]> for OutboundPlain<'a> {
290    fn from(payload: &'a [u8]) -> Self {
291        Self::Single(payload)
292    }
293}
294
295/// A payload buffer with space reserved at the front for a TLS message header.
296///
297/// `EncodedMessage<OutboundOpaque>` is named `TLSPlaintext` in the standard.
298///
299/// This outbound type owns all memory for its interior parts.
300/// It results from encryption and is used for io write.
301#[derive(Clone, Debug)]
302pub struct OutboundOpaque(Vec<u8>);
303
304impl OutboundOpaque {
305    /// Create a new value with the given payload capacity.
306    ///
307    /// (The actual capacity of the returned value will be at least `HEADER_SIZE + capacity`.)
308    pub fn with_capacity(capacity: usize) -> Self {
309        let mut prefixed_payload = Vec::with_capacity(HEADER_SIZE + capacity);
310        prefixed_payload.resize(HEADER_SIZE, 0);
311        Self(prefixed_payload)
312    }
313
314    /// Append bytes from a slice.
315    pub fn extend_from_slice(&mut self, slice: &[u8]) {
316        self.0.extend_from_slice(slice)
317    }
318
319    /// Append bytes from an `OutboundChunks`.
320    pub fn extend_from_chunks(&mut self, chunks: &OutboundPlain<'_>) {
321        chunks.copy_to_vec(&mut self.0)
322    }
323
324    /// Truncate the payload to the given length (plus header).
325    pub fn truncate(&mut self, len: usize) {
326        self.0.truncate(len + HEADER_SIZE)
327    }
328
329    fn len(&self) -> usize {
330        self.0.len() - HEADER_SIZE
331    }
332}
333
334impl AsRef<[u8]> for OutboundOpaque {
335    fn as_ref(&self) -> &[u8] {
336        &self.0[HEADER_SIZE..]
337    }
338}
339
340impl AsMut<[u8]> for OutboundOpaque {
341    fn as_mut(&mut self) -> &mut [u8] {
342        &mut self.0[HEADER_SIZE..]
343    }
344}
345
346impl<'a> Extend<&'a u8> for OutboundOpaque {
347    fn extend<T: IntoIterator<Item = &'a u8>>(&mut self, iter: T) {
348        self.0.extend(iter)
349    }
350}
351
352impl From<&[u8]> for OutboundOpaque {
353    fn from(content: &[u8]) -> Self {
354        let mut payload = Vec::with_capacity(HEADER_SIZE + content.len());
355        payload.extend(&[0u8; HEADER_SIZE]);
356        payload.extend(content);
357        Self(payload)
358    }
359}
360
361impl<const N: usize> From<&[u8; N]> for OutboundOpaque {
362    fn from(content: &[u8; N]) -> Self {
363        Self::from(&content[..])
364    }
365}
366
367/// An externally length'd payload
368///
369/// When encountered in an [`EncodedMessage`], it represents a plaintext payload. It can be
370/// decrypted from an [`InboundOpaque`] or encrypted into an [`OutboundOpaque`],
371/// and it is also used for joining and fragmenting.
372#[non_exhaustive]
373#[derive(Clone, Eq, PartialEq)]
374pub enum Payload<'a> {
375    /// Borrowed payload
376    Borrowed(&'a [u8]),
377    /// Owned payload
378    Owned(Vec<u8>),
379}
380
381impl<'a> Payload<'a> {
382    /// A reference to the payload's bytes
383    pub fn bytes(&'a self) -> &'a [u8] {
384        match self {
385            Self::Borrowed(bytes) => bytes,
386            Self::Owned(bytes) => bytes,
387        }
388    }
389
390    pub(crate) fn into_owned(self) -> Payload<'static> {
391        Payload::Owned(self.into_vec())
392    }
393
394    pub(crate) fn into_vec(self) -> Vec<u8> {
395        match self {
396            Self::Borrowed(bytes) => bytes.to_vec(),
397            Self::Owned(bytes) => bytes,
398        }
399    }
400
401    pub(crate) fn read(r: &mut Reader<'a>) -> Self {
402        Self::Borrowed(r.rest())
403    }
404}
405
406impl Payload<'static> {
407    /// Create a new owned payload from the given `bytes`.
408    pub fn new(bytes: impl Into<Vec<u8>>) -> Self {
409        Self::Owned(bytes.into())
410    }
411}
412
413impl<'a> Codec<'a> for Payload<'a> {
414    fn encode(&self, bytes: &mut Vec<u8>) {
415        bytes.extend_from_slice(self.bytes());
416    }
417
418    fn read(r: &mut Reader<'a>) -> Result<Self, InvalidMessage> {
419        Ok(Self::read(r))
420    }
421}
422
423impl fmt::Debug for Payload<'_> {
424    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
425        hex(f, self.bytes())
426    }
427}
428
429/// A borrowed payload buffer.
430#[expect(clippy::exhaustive_structs)]
431pub struct InboundOpaque<'a>(pub &'a mut [u8]);
432
433impl<'a> InboundOpaque<'a> {
434    /// Truncate the payload to `len` bytes.
435    pub fn truncate(&mut self, len: usize) {
436        if len >= self.len() {
437            return;
438        }
439
440        self.0 = core::mem::take(&mut self.0)
441            .split_at_mut(len)
442            .0;
443    }
444
445    pub(crate) fn into_inner(self) -> &'a mut [u8] {
446        self.0
447    }
448
449    pub(crate) fn pop(&mut self) -> Option<u8> {
450        if self.is_empty() {
451            return None;
452        }
453
454        let len = self.len();
455        let last = self[len - 1];
456        self.truncate(len - 1);
457        Some(last)
458    }
459}
460
461impl Deref for InboundOpaque<'_> {
462    type Target = [u8];
463
464    fn deref(&self) -> &Self::Target {
465        self.0
466    }
467}
468
469impl DerefMut for InboundOpaque<'_> {
470    fn deref_mut(&mut self) -> &mut Self::Target {
471        self.0
472    }
473}
474
475/// Decode a TLS1.3 `TLSInnerPlaintext` encoding.
476///
477/// `p` is a message payload, immediately post-decryption.  This function
478/// removes zero padding bytes, until a non-zero byte is encountered which is
479/// the content type, which is returned.  See RFC8446 s5.2.
480///
481/// ContentType(0) is returned if the message payload is empty or all zeroes.
482fn unpad_tls13_payload(p: &mut InboundOpaque<'_>) -> ContentType {
483    loop {
484        match p.pop() {
485            Some(0) => {}
486            Some(content_type) => return ContentType::from(content_type),
487            None => return ContentType(0),
488        }
489    }
490}
491
492/// Errors from trying to parse a TLS message.
493#[expect(missing_docs)]
494#[non_exhaustive]
495#[derive(Debug)]
496pub enum MessageError {
497    TooShortForHeader,
498    TooShortForLength,
499    InvalidEmptyPayload,
500    MessageTooLarge,
501    InvalidContentType,
502    UnknownProtocolVersion,
503}
504
505#[cfg(test)]
506mod tests {
507    use std::{println, vec};
508
509    use super::*;
510
511    #[test]
512    fn split_at_with_single_slice() {
513        let owner: &[u8] = &[0, 1, 2, 3, 4, 5, 6, 7];
514        let borrowed_payload = OutboundPlain::Single(owner);
515
516        let (before, after) = borrowed_payload.split_at(6);
517        println!("before:{before:?}\nafter:{after:?}");
518        assert_eq!(before.to_vec(), &[0, 1, 2, 3, 4, 5]);
519        assert_eq!(after.to_vec(), &[6, 7]);
520    }
521
522    #[test]
523    fn split_at_with_multiple_slices() {
524        let owner: Vec<&[u8]> = vec![&[0, 1, 2, 3], &[4, 5], &[6, 7, 8], &[9, 10, 11, 12]];
525        let borrowed_payload = OutboundPlain::new(&owner);
526
527        let (before, after) = borrowed_payload.split_at(3);
528        println!("before:{before:?}\nafter:{after:?}");
529        assert_eq!(before.to_vec(), &[0, 1, 2]);
530        assert_eq!(after.to_vec(), &[3, 4, 5, 6, 7, 8, 9, 10, 11, 12]);
531
532        let (before, after) = borrowed_payload.split_at(8);
533        println!("before:{before:?}\nafter:{after:?}");
534        assert_eq!(before.to_vec(), &[0, 1, 2, 3, 4, 5, 6, 7]);
535        assert_eq!(after.to_vec(), &[8, 9, 10, 11, 12]);
536
537        let (before, after) = borrowed_payload.split_at(11);
538        println!("before:{before:?}\nafter:{after:?}");
539        assert_eq!(before.to_vec(), &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
540        assert_eq!(after.to_vec(), &[11, 12]);
541    }
542
543    #[test]
544    fn split_out_of_bounds() {
545        let owner: Vec<&[u8]> = vec![&[0, 1, 2, 3], &[4, 5], &[6, 7, 8], &[9, 10, 11, 12]];
546
547        let single_payload = OutboundPlain::Single(owner[0]);
548        let (before, after) = single_payload.split_at(17);
549        println!("before:{before:?}\nafter:{after:?}");
550        assert_eq!(before.to_vec(), &[0, 1, 2, 3]);
551        assert!(after.is_empty());
552
553        let multiple_payload = OutboundPlain::new(&owner);
554        let (before, after) = multiple_payload.split_at(17);
555        println!("before:{before:?}\nafter:{after:?}");
556        assert_eq!(before.to_vec(), &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]);
557        assert!(after.is_empty());
558
559        let empty_payload = OutboundPlain::new_empty();
560        let (before, after) = empty_payload.split_at(17);
561        println!("before:{before:?}\nafter:{after:?}");
562        assert!(before.is_empty());
563        assert!(after.is_empty());
564    }
565
566    #[test]
567    fn empty_slices_mixed() {
568        let owner: Vec<&[u8]> = vec![&[], &[], &[0], &[], &[1, 2], &[], &[3], &[4], &[], &[]];
569        let mut borrowed_payload = OutboundPlain::new(&owner);
570        let mut fragment_count = 0;
571        let mut fragment;
572        let expected_fragments: &[&[u8]] = &[&[0, 1], &[2, 3], &[4]];
573
574        while !borrowed_payload.is_empty() {
575            (fragment, borrowed_payload) = borrowed_payload.split_at(2);
576            println!("{fragment:?}");
577            assert_eq!(&expected_fragments[fragment_count], &fragment.to_vec());
578            fragment_count += 1;
579        }
580        assert_eq!(fragment_count, expected_fragments.len());
581    }
582
583    #[test]
584    fn exhaustive_splitting() {
585        let owner: Vec<u8> = (0..127).collect();
586        let slices = (0..7)
587            .map(|i| &owner[((1 << i) - 1)..((1 << (i + 1)) - 1)])
588            .collect::<Vec<_>>();
589        let payload = OutboundPlain::new(&slices);
590
591        assert_eq!(payload.to_vec(), owner);
592        println!("{payload:#?}");
593
594        for start in 0..128 {
595            for end in start..128 {
596                for mid in 0..(end - start) {
597                    let witness = owner[start..end].split_at(mid);
598                    let split_payload = payload
599                        .split_at(end)
600                        .0
601                        .split_at(start)
602                        .1
603                        .split_at(mid);
604                    assert_eq!(
605                        witness.0,
606                        split_payload.0.to_vec(),
607                        "start: {start}, mid:{mid}, end:{end}"
608                    );
609                    assert_eq!(
610                        witness.1,
611                        split_payload.1.to_vec(),
612                        "start: {start}, mid:{mid}, end:{end}"
613                    );
614                }
615            }
616        }
617    }
618}