Skip to main content

rustls/crypto/cipher/
messages.rs

1use alloc::vec::Vec;
2use core::ops::{Deref, DerefMut, Range};
3use core::{fmt, slice};
4
5use crate::Protocol;
6use crate::enums::{ContentType, ProtocolVersion};
7use crate::error::{Error, InvalidMessage, PeerMisbehaved};
8use crate::msgs::{Codec, HEADER_SIZE, MAX_FRAGMENT_LEN, Reader, hex, read_record_header};
9
10/// A TLS record with encoded (but not necessarily encrypted) payload.
11#[expect(clippy::exhaustive_structs)]
12#[derive(Clone, Debug)]
13pub struct Record<P> {
14    /// The content type of this record.
15    pub typ: ContentType,
16    /// The protocol version of this record.
17    pub version: EncodableVersion,
18    /// The payload of this record.
19    pub payload: P,
20}
21
22impl<P> Record<P> {
23    /// Create a new `Record` with the given fields.
24    pub fn new(typ: ContentType, version: EncodableVersion, payload: P) -> Self {
25        Self {
26            typ,
27            version,
28            payload,
29        }
30    }
31}
32
33impl<'a> Record<Payload<'a>> {
34    /// Construct by decoding from a [`Reader`].
35    ///
36    /// `RecordError` 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, RecordError> {
39        let (typ, version, len) = read_record_header(r)?;
40
41        let content = r
42            .take(len as usize)
43            .ok_or(RecordError::TooShortForLength)?;
44
45        Ok(Self {
46            typ,
47            version: EncodableVersion::Legacy(version),
48            payload: Payload::Borrowed(content),
49        })
50    }
51
52    /// Borrow as an [`Record<OutboundPlain<'a>>`].
53    pub fn borrow_outbound(&'a self) -> Record<OutboundPlain<'a>> {
54        Record {
55            typ: self.typ,
56            version: self.version,
57            payload: self.payload.bytes().into(),
58        }
59    }
60
61    /// Convert into an owned `Record<Plain<'static>>`.
62    pub fn into_owned(self) -> Self {
63        Self {
64            typ: self.typ,
65            version: self.version,
66            payload: self.payload.into_owned(),
67        }
68    }
69}
70
71impl Record<&'_ [u8]> {
72    /// Returns true if the payload is a CCS message.
73    ///
74    /// We passthrough ChangeCipherSpec messages in the deframer without decrypting them.
75    /// Note: this is prior to the record layer, so is unencrypted. See
76    /// third paragraph of section 5 in RFC 9846.
77    pub(crate) fn is_valid_ccs(&self) -> bool {
78        self.typ == ContentType::ChangeCipherSpec && self.payload == [0x01]
79    }
80}
81
82impl<'a> Record<InboundOpaque<'a>> {
83    /// For TLS1.3 (only), checks the length record.payload is valid and removes the padding.
84    ///
85    /// Returns an error if the record payload (pre-unpadding) is too long, or the padding is invalid,
86    /// or the inner content type is `change_cipher_spec`, or the record payload (post-unpadding)
87    /// is too long.
88    pub fn into_tls13_unpadded_record(mut self) -> Result<Record<&'a [u8]>, Error> {
89        let payload = &mut self.payload;
90
91        if self.typ != ContentType::ApplicationData {
92            return Err(PeerMisbehaved::IllegalTls13ContentType.into());
93        }
94
95        if payload.len() > MAX_FRAGMENT_LEN.get() + 1 {
96            return Err(Error::PeerSentOversizedRecord);
97        }
98
99        self.typ = unpad_tls13_payload(payload);
100        if self.typ == ContentType(0) {
101            return Err(PeerMisbehaved::IllegalTlsInnerPlaintext.into());
102        }
103
104        // > An implementation which receives any other change_cipher_spec value or
105        // > which receives a protected change_cipher_spec record MUST abort the
106        // > handshake with an "unexpected_message" alert.
107        // <https://www.rfc-editor.org/rfc/rfc9846#section-5>
108        if self.typ == ContentType::ChangeCipherSpec {
109            return Err(PeerMisbehaved::IllegalMiddleboxChangeCipherSpec.into());
110        }
111
112        if payload.len() > MAX_FRAGMENT_LEN.get() {
113            return Err(Error::PeerSentOversizedRecord);
114        }
115
116        self.version = EncodableVersion::Legacy(ProtocolVersion::TLSv1_3);
117        Ok(self.into_plain_record())
118    }
119
120    /// Force conversion into a plaintext record.
121    ///
122    /// `range` restricts the resulting record: this function panics if it is out of range for
123    /// the underlying record payload.
124    ///
125    /// This should only be used for records that are known to be in plaintext. Otherwise, the
126    /// [`Record<InboundOpaque<'_>>`] should be decrypted into an
127    /// `Record<&'_ [u8]>` using a `RecordDecrypter`.
128    pub fn into_plain_record_range(self, range: Range<usize>) -> Record<&'a [u8]> {
129        Record {
130            typ: self.typ,
131            version: self.version,
132            payload: &self.payload.into_inner()[range],
133        }
134    }
135
136    /// Force conversion into a plaintext record.
137    ///
138    /// This should only be used for records that are known to be in plaintext. Otherwise, the
139    /// [`Record<InboundOpaque<'a>>`] should be decrypted into a
140    /// `Record<&'a [u8]>` using a `RecordDecrypter`.
141    pub fn into_plain_record(self) -> Record<&'a [u8]> {
142        Record {
143            typ: self.typ,
144            version: self.version,
145            payload: self.payload.into_inner(),
146        }
147    }
148}
149
150impl Record<OutboundPlain<'_>> {
151    /// Encode this record into its unencrypted wire representation, including
152    /// its record header.
153    pub(crate) fn to_unencrypted_bytes(&self) -> Vec<u8> {
154        let len = self.payload.len();
155        debug_assert!(len <= usize::from(u16::MAX));
156        let mut buf = Vec::with_capacity(HEADER_SIZE + len);
157        self.encode_unencrypted(&mut buf);
158        buf
159    }
160
161    pub(crate) fn encode_unencrypted(&self, buf: &mut Vec<u8>) {
162        let len = self.payload.len();
163        debug_assert!(len <= usize::from(u16::MAX));
164        buf.extend_from_slice(&encode_record_header(self.typ, self.version, len as u16));
165        self.payload.copy_to_vec(buf);
166    }
167}
168
169/// Encode a TLS record header.
170///
171/// `typ`, `version` and `len` describe the record's payload.
172pub fn encode_record_header(
173    typ: ContentType,
174    version: EncodableVersion,
175    len: u16,
176) -> [u8; HEADER_SIZE] {
177    let [version_hi, version_lo] = version.encode().to_array();
178    let [len_hi, len_lo] = len.to_be_bytes();
179    [typ.into(), version_hi, version_lo, len_hi, len_lo]
180}
181
182/// A collection of borrowed plaintext slices.
183///
184/// Warning: OutboundPlain does not guarantee that the simplest variant is used.
185/// Multiple can hold non fragmented or empty payloads.
186#[non_exhaustive]
187#[derive(Debug, Clone)]
188pub enum OutboundPlain<'a> {
189    /// A single byte slice.
190    ///
191    /// Contrary to `Multiple`, this uses a single pointer indirection
192    Single(&'a [u8]),
193    /// A collection of chunks (byte slices).
194    Multiple {
195        /// A collection of byte slices that hold the buffered data.
196        chunks: &'a [&'a [u8]],
197        /// Offset of the payload's first byte within the logical
198        /// concatenation of all `chunks`.
199        ///
200        /// This may point beyond the first chunk.
201        start: usize,
202        /// Offset one past the payload's last byte within the logical
203        /// concatenation of all `chunks`, so `end - start` is the payload's
204        /// length in bytes.
205        end: usize,
206    },
207}
208
209impl<'a> OutboundPlain<'a> {
210    /// Create a payload from a slice of byte slices.
211    /// If fragmented the cursors are added by default: start = 0, end = length
212    pub fn new(chunks: &'a [&'a [u8]]) -> Self {
213        if chunks.len() == 1 {
214            Self::Single(chunks[0])
215        } else {
216            Self::Multiple {
217                chunks,
218                start: 0,
219                end: chunks
220                    .iter()
221                    .map(|chunk| chunk.len())
222                    .sum(),
223            }
224        }
225    }
226
227    /// Create a payload with a single empty slice
228    pub fn new_empty() -> Self {
229        Self::Single(&[])
230    }
231
232    /// Flatten the slice of byte slices to an owned vector of bytes
233    pub fn to_vec(&self) -> Vec<u8> {
234        let mut vec = Vec::with_capacity(self.len());
235        self.copy_to_vec(&mut vec);
236        vec
237    }
238
239    /// Append all bytes to a vector
240    pub fn copy_to_vec(&self, vec: &mut Vec<u8>) {
241        for chunk in self.chunks() {
242            vec.extend_from_slice(chunk);
243        }
244    }
245
246    /// Iterate over the payload's chunks of bytes, in order.
247    ///
248    /// Empty chunks are not yielded.
249    pub fn chunks(&self) -> impl Iterator<Item = &'a [u8]> + '_ {
250        match self {
251            Self::Single(chunk) => Chunks::Single((!chunk.is_empty()).then_some(*chunk)),
252            Self::Multiple { chunks, start, end } => Chunks::Multiple {
253                chunks: chunks.iter(),
254                skip: *start,
255                remaining: end - start,
256            },
257        }
258    }
259
260    /// The payload's single contiguous chunk, or `None` if it is fragmented.
261    ///
262    /// An empty payload is treated as a single empty chunk. A [`Self::Multiple`]
263    /// payload is contiguous when its selected range lies within one chunk.
264    /// Distinct chunks are never joined, even if their addresses are adjacent.
265    pub fn single_chunk(&self) -> Option<&'a [u8]> {
266        if let Self::Single(chunk) = self {
267            return Some(chunk);
268        }
269        if self.is_empty() {
270            return Some(&[]);
271        }
272        let mut chunks = self.chunks();
273        let chunk = chunks.next()?;
274        (chunk.len() == self.len() && chunks.next().is_none()).then_some(chunk)
275    }
276
277    /// Split self in two, around an index
278    /// Works similarly to `split_at` in the core library, except it doesn't panic if out of bound
279    pub(crate) fn split_at(&self, mid: usize) -> (Self, Self) {
280        let (chunks, start, end) = match *self {
281            Self::Multiple { chunks, start, end } => (chunks, start, end),
282            Self::Single(chunk) => {
283                let mid = Ord::min(mid, chunk.len());
284                return (Self::Single(&chunk[..mid]), Self::Single(&chunk[mid..]));
285            }
286        };
287
288        let mid = Ord::min(start + mid, end);
289        let mut skip = 0;
290        let mut consumed = 0;
291        for chunk in chunks {
292            if consumed + chunk.len() > mid {
293                break;
294            }
295            consumed += chunk.len();
296            skip += 1;
297        }
298
299        (
300            Self::Multiple {
301                chunks,
302                start,
303                end: mid,
304            },
305            Self::Multiple {
306                chunks: &chunks[skip..],
307                start: mid - consumed,
308                end: end - consumed,
309            },
310        )
311    }
312
313    /// Returns true if the payload is empty
314    pub(crate) fn is_empty(&self) -> bool {
315        self.len() == 0
316    }
317
318    /// Returns the cumulative length of all chunks
319    #[expect(clippy::len_without_is_empty)]
320    pub fn len(&self) -> usize {
321        match self {
322            Self::Single(chunk) => chunk.len(),
323            Self::Multiple { start, end, .. } => end - start,
324        }
325    }
326}
327
328/// Iterator over an [`OutboundPlain`]'s chunks, returned by [`OutboundPlain::chunks()`].
329enum Chunks<'a> {
330    Single(Option<&'a [u8]>),
331    Multiple {
332        /// Chunks not yet visited, including any leading ones `skip` covers.
333        chunks: slice::Iter<'a, &'a [u8]>,
334        /// How many leading bytes remain to be skipped.
335        skip: usize,
336        /// How many bytes remain to be yielded.
337        remaining: usize,
338    },
339}
340
341impl<'a> Iterator for Chunks<'a> {
342    type Item = &'a [u8];
343
344    fn next(&mut self) -> Option<Self::Item> {
345        let (chunks, skip, remaining) = match self {
346            Self::Single(chunk) => return chunk.take(),
347            Self::Multiple {
348                chunks,
349                skip,
350                remaining,
351            } => (chunks, skip, remaining),
352        };
353
354        loop {
355            if *remaining == 0 {
356                return None;
357            }
358
359            let chunk = chunks.next()?;
360            let Some((_, chunk)) = chunk.split_at_checked(*skip) else {
361                *skip -= chunk.len();
362                continue;
363            };
364
365            *skip = 0;
366            if chunk.is_empty() {
367                continue;
368            }
369
370            let take = Ord::min(chunk.len(), *remaining);
371            *remaining -= take;
372            return Some(&chunk[..take]);
373        }
374    }
375}
376
377impl<'a> From<&'a [u8]> for OutboundPlain<'a> {
378    fn from(payload: &'a [u8]) -> Self {
379        Self::Single(payload)
380    }
381}
382
383impl<'a, const N: usize> From<&'a [u8; N]> for OutboundPlain<'a> {
384    fn from(payload: &'a [u8; N]) -> Self {
385        Self::Single(payload)
386    }
387}
388
389impl<'a> From<&'a Vec<u8>> for OutboundPlain<'a> {
390    fn from(payload: &'a Vec<u8>) -> Self {
391        Self::Single(payload)
392    }
393}
394
395/// An externally length'd payload
396///
397/// When encountered in an [`Record`], it represents a plaintext payload. It can be
398/// decrypted from an [`InboundOpaque`] or encrypted by a
399/// [`RecordEncrypter`](crate::crypto::cipher::RecordEncrypter), and it is also used for
400/// joining and fragmenting.
401#[non_exhaustive]
402#[derive(Clone, Eq, PartialEq)]
403pub enum Payload<'a> {
404    /// Borrowed payload
405    Borrowed(&'a [u8]),
406    /// Owned payload
407    Owned(Vec<u8>),
408}
409
410impl<'a> Payload<'a> {
411    /// A reference to the payload's bytes
412    pub fn bytes(&'a self) -> &'a [u8] {
413        match self {
414            Self::Borrowed(bytes) => bytes,
415            Self::Owned(bytes) => bytes,
416        }
417    }
418
419    pub(crate) fn into_owned(self) -> Payload<'static> {
420        Payload::Owned(self.into_vec())
421    }
422
423    pub(crate) fn into_vec(self) -> Vec<u8> {
424        match self {
425            Self::Borrowed(bytes) => bytes.to_vec(),
426            Self::Owned(bytes) => bytes,
427        }
428    }
429
430    pub(crate) fn read(r: &mut Reader<'a>) -> Self {
431        Self::Borrowed(r.rest())
432    }
433}
434
435impl Payload<'static> {
436    /// Create a new owned payload from the given `bytes`.
437    pub fn new(bytes: impl Into<Vec<u8>>) -> Self {
438        Self::Owned(bytes.into())
439    }
440}
441
442impl<'a> Codec<'a> for Payload<'a> {
443    fn encode(&self, bytes: &mut Vec<u8>) {
444        bytes.extend_from_slice(self.bytes());
445    }
446
447    fn read(r: &mut Reader<'a>) -> Result<Self, InvalidMessage> {
448        Ok(Self::read(r))
449    }
450}
451
452impl fmt::Debug for Payload<'_> {
453    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
454        hex(f, self.bytes())
455    }
456}
457
458/// A borrowed payload buffer.
459#[expect(clippy::exhaustive_structs)]
460pub struct InboundOpaque<'a>(pub &'a mut [u8]);
461
462impl<'a> InboundOpaque<'a> {
463    /// Truncate the payload to `len` bytes.
464    pub fn truncate(&mut self, len: usize) {
465        if len >= self.len() {
466            return;
467        }
468
469        self.0 = core::mem::take(&mut self.0)
470            .split_at_mut(len)
471            .0;
472    }
473
474    pub(crate) fn into_inner(self) -> &'a mut [u8] {
475        self.0
476    }
477
478    pub(crate) fn pop(&mut self) -> Option<u8> {
479        if self.is_empty() {
480            return None;
481        }
482
483        let len = self.len();
484        let last = self[len - 1];
485        self.truncate(len - 1);
486        Some(last)
487    }
488}
489
490impl Deref for InboundOpaque<'_> {
491    type Target = [u8];
492
493    fn deref(&self) -> &Self::Target {
494        self.0
495    }
496}
497
498impl DerefMut for InboundOpaque<'_> {
499    fn deref_mut(&mut self) -> &mut Self::Target {
500        self.0
501    }
502}
503
504/// A protocol version that can be encoded.
505#[derive(Clone, Copy, Debug, PartialEq, Eq)]
506#[non_exhaustive]
507pub enum EncodableVersion {
508    /// Encode the version in legacy, backward-compatible form.
509    ///
510    /// > MUST be set to 0x0303 for all records generated by a TLS 1.3
511    /// > implementation...
512    ///
513    /// <https://www.rfc-editor.org/info/rfc9846/#section-5.1>
514    Legacy(ProtocolVersion),
515    /// Encode the version as an initial client hello, not a retry.
516    ///
517    /// > ...other than an initial ClientHello (i.e., one not generated after a HelloRetryRequest),
518    /// > where it MAY also be 0x0301 for compatibility purposes.
519    ///
520    /// <https://www.rfc-editor.org/info/rfc9846/#section-5.1>
521    InitialClientHello(Protocol),
522}
523
524impl EncodableVersion {
525    /// Encode the protocol version.
526    pub fn encode(&self) -> ProtocolVersion {
527        match self {
528            Self::Legacy(_) => ProtocolVersion::TLSv1_2,
529            Self::InitialClientHello(Protocol::Tcp | Protocol::Quic(_)) => ProtocolVersion::TLSv1_0,
530        }
531    }
532
533    /// The actual protocol version in use.
534    pub fn version(&self) -> ProtocolVersion {
535        match self {
536            Self::Legacy(v) => *v,
537            Self::InitialClientHello(Protocol::Tcp | Protocol::Quic(_)) => ProtocolVersion::TLSv1_2,
538        }
539    }
540}
541
542/// Decode a TLS1.3 `TLSInnerPlaintext` encoding.
543///
544/// `p` is a record payload, immediately post-decryption.  This function
545/// removes zero padding bytes, until a non-zero byte is encountered which is
546/// the content type, which is returned.  See RFC 9846 s5.2.
547///
548/// ContentType(0) is returned if the record payload is empty or all zeroes.
549fn unpad_tls13_payload(p: &mut InboundOpaque<'_>) -> ContentType {
550    loop {
551        match p.pop() {
552            Some(0) => {}
553            Some(content_type) => return ContentType::from(content_type),
554            None => return ContentType(0),
555        }
556    }
557}
558
559/// Errors from trying to parse a TLS record.
560#[expect(missing_docs)]
561#[non_exhaustive]
562#[derive(Debug)]
563pub enum RecordError {
564    TooShortForHeader,
565    TooShortForLength,
566    InvalidEmptyPayload,
567    MessageTooLarge,
568    InvalidContentType,
569    UnknownProtocolVersion,
570}
571
572#[cfg(test)]
573mod tests {
574    use std::{println, vec};
575
576    use super::*;
577    use crate::quic;
578
579    #[test]
580    fn single_chunk_with_multiple_slices() {
581        let chunks: &[&[u8]] = &[&[], &[1, 2, 3], &[], &[4, 5, 6, 7], &[]];
582        let expected = [1, 2, 3, 4, 5, 6, 7];
583        for start in 0..=7 {
584            for end in start..=7 {
585                let (_, tail) = OutboundPlain::new(chunks).split_at(start);
586                let (plain, _) = tail.split_at(end - start);
587                let contiguous = start == end || end <= 3 || start >= 3;
588                let single_chunk = plain.single_chunk();
589                assert_eq!(single_chunk.is_some(), contiguous);
590                let Some(slice) = single_chunk else {
591                    continue;
592                };
593                assert_eq!(slice, &expected[start..end]);
594                if !slice.is_empty() {
595                    let source = if end <= 3 { chunks[1] } else { chunks[3] };
596                    let offset = if end <= 3 { start } else { start - 3 };
597                    assert_eq!(slice.as_ptr(), source[offset..].as_ptr());
598                }
599            }
600        }
601    }
602
603    #[test]
604    fn single_chunk_with_out_of_bounds_range() {
605        for start in [3, 5, 6] {
606            let plain = OutboundPlain::Multiple {
607                chunks: &[&[1, 2, 3], &[], &[4, 5], &[]],
608                start,
609                end: 7,
610            };
611            assert!(plain.single_chunk().is_none());
612        }
613    }
614
615    #[test]
616    fn single_chunk_borrows_payload() {
617        let data = [1, 2, 3];
618        let chunks: &[&[u8]] = &[&[], &data, &[]];
619        let slice = {
620            let plain = OutboundPlain::Multiple {
621                chunks,
622                start: 1,
623                end: 3,
624            };
625            plain.single_chunk().unwrap()
626        };
627        assert_eq!(slice, &data[1..]);
628        assert_eq!(slice.as_ptr(), data[1..].as_ptr());
629    }
630
631    #[test]
632    fn single_chunk_does_not_join_adjacent_slices() {
633        let owner = [1, 2, 3, 4];
634        let adjacent = [&owner[..2], &owner[2..]];
635        assert!(
636            OutboundPlain::new(&adjacent)
637                .single_chunk()
638                .is_none()
639        );
640    }
641
642    #[test]
643    fn single_chunk_empty_and_single_payloads() {
644        for plain in [
645            OutboundPlain::new_empty(),
646            OutboundPlain::new(&[]),
647            OutboundPlain::new(&[&[], &[]]),
648        ] {
649            assert_eq!(plain.single_chunk(), Some(&[][..]));
650        }
651        let data = [1, 2, 3];
652        for plain in [
653            OutboundPlain::from(data.as_slice()),
654            OutboundPlain::new(&[&data]),
655        ] {
656            let slice = plain.single_chunk().unwrap();
657            assert_eq!(slice, data);
658            assert_eq!(slice.as_ptr(), data.as_ptr());
659        }
660    }
661
662    #[test]
663    fn chunks_iteration() {
664        // `Single` yields its chunk, unless empty
665        assert_eq!(
666            OutboundPlain::Single(&[1, 2, 3])
667                .chunks()
668                .collect::<Vec<_>>(),
669            [&[1u8, 2, 3][..]],
670        );
671        assert_eq!(
672            OutboundPlain::new_empty()
673                .chunks()
674                .count(),
675            0
676        );
677
678        // `Multiple` yields the in-window part of each chunk, skipping
679        // empty chunks
680        let owner: Vec<&[u8]> = vec![&[], &[1, 2, 3], &[], &[4, 5], &[], &[6, 7], &[]];
681        let (_, tail) = OutboundPlain::new(&owner).split_at(1);
682        let (window, _) = tail.split_at(5);
683        assert_eq!(
684            window.chunks().collect::<Vec<_>>(),
685            [&[2u8, 3][..], &[4, 5][..], &[6][..]],
686        );
687    }
688
689    #[test]
690    fn split_at_with_single_slice() {
691        let owner: &[u8] = &[0, 1, 2, 3, 4, 5, 6, 7];
692        let borrowed_payload = OutboundPlain::Single(owner);
693
694        let (before, after) = borrowed_payload.split_at(6);
695        println!("before:{before:?}\nafter:{after:?}");
696        assert_eq!(before.to_vec(), &[0, 1, 2, 3, 4, 5]);
697        assert_eq!(after.to_vec(), &[6, 7]);
698    }
699
700    #[test]
701    fn split_at_with_multiple_slices() {
702        let owner: Vec<&[u8]> = vec![&[0, 1, 2, 3], &[4, 5], &[6, 7, 8], &[9, 10, 11, 12]];
703        let borrowed_payload = OutboundPlain::new(&owner);
704
705        let (before, after) = borrowed_payload.split_at(3);
706        println!("before:{before:?}\nafter:{after:?}");
707        assert_eq!(before.to_vec(), &[0, 1, 2]);
708        assert_eq!(after.to_vec(), &[3, 4, 5, 6, 7, 8, 9, 10, 11, 12]);
709
710        let (before, after) = borrowed_payload.split_at(8);
711        println!("before:{before:?}\nafter:{after:?}");
712        assert_eq!(before.to_vec(), &[0, 1, 2, 3, 4, 5, 6, 7]);
713        assert_eq!(after.to_vec(), &[8, 9, 10, 11, 12]);
714
715        let (before, after) = borrowed_payload.split_at(11);
716        println!("before:{before:?}\nafter:{after:?}");
717        assert_eq!(before.to_vec(), &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
718        assert_eq!(after.to_vec(), &[11, 12]);
719    }
720
721    #[test]
722    fn split_at_drops_consumed_chunks() {
723        let owner = vec![&[0, 1, 2][..], &[], &[3, 4], &[5], &[]];
724        let payload = OutboundPlain::new(&owner);
725
726        let (before, after) = payload.split_at(4);
727        assert_eq!(before.to_vec(), &[0, 1, 2, 3]);
728        assert_eq!(after.to_vec(), &[4, 5]);
729        let OutboundPlain::Multiple { chunks, start, end } = after else {
730            panic!("expected multiple chunks");
731        };
732        assert_eq!(chunks, &owner[2..]);
733        assert_eq!((start, end), (1, 3));
734
735        let (before, after) = after.split_at(1);
736        assert_eq!(before.to_vec(), &[4]);
737        assert_eq!(after.to_vec(), &[5]);
738        let OutboundPlain::Multiple { chunks, start, end } = after else {
739            panic!("expected multiple chunks");
740        };
741        assert_eq!(chunks, &owner[3..]);
742        assert_eq!((start, end), (0, 1));
743
744        let (before, after) = after.split_at(1);
745        assert_eq!(before.to_vec(), &[5]);
746        assert!(after.is_empty());
747        let OutboundPlain::Multiple { chunks, start, end } = after else {
748            panic!("expected multiple chunks");
749        };
750        assert!(chunks.is_empty());
751        assert_eq!((start, end), (0, 0));
752    }
753
754    #[test]
755    fn split_out_of_bounds() {
756        let owner: Vec<&[u8]> = vec![&[0, 1, 2, 3], &[4, 5], &[6, 7, 8], &[9, 10, 11, 12]];
757
758        let single_payload = OutboundPlain::Single(owner[0]);
759        let (before, after) = single_payload.split_at(17);
760        println!("before:{before:?}\nafter:{after:?}");
761        assert_eq!(before.to_vec(), &[0, 1, 2, 3]);
762        assert!(after.is_empty());
763
764        let multiple_payload = OutboundPlain::new(&owner);
765        let (before, after) = multiple_payload.split_at(17);
766        println!("before:{before:?}\nafter:{after:?}");
767        assert_eq!(before.to_vec(), &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]);
768        assert!(after.is_empty());
769
770        let empty_payload = OutboundPlain::new_empty();
771        let (before, after) = empty_payload.split_at(17);
772        println!("before:{before:?}\nafter:{after:?}");
773        assert!(before.is_empty());
774        assert!(after.is_empty());
775    }
776
777    #[test]
778    fn empty_slices_mixed() {
779        let owner: Vec<&[u8]> = vec![&[], &[], &[0], &[], &[1, 2], &[], &[3], &[4], &[], &[]];
780        let mut borrowed_payload = OutboundPlain::new(&owner);
781        let mut fragment_count = 0;
782        let mut fragment;
783        let expected_fragments: &[&[u8]] = &[&[0, 1], &[2, 3], &[4]];
784
785        while !borrowed_payload.is_empty() {
786            (fragment, borrowed_payload) = borrowed_payload.split_at(2);
787            println!("{fragment:?}");
788            assert_eq!(&expected_fragments[fragment_count], &fragment.to_vec());
789            fragment_count += 1;
790        }
791        assert_eq!(fragment_count, expected_fragments.len());
792    }
793
794    #[test]
795    fn exhaustive_splitting() {
796        let owner: Vec<u8> = (0..127).collect();
797        let slices = (0..7)
798            .map(|i| &owner[((1 << i) - 1)..((1 << (i + 1)) - 1)])
799            .collect::<Vec<_>>();
800        let payload = OutboundPlain::new(&slices);
801
802        assert_eq!(payload.to_vec(), owner);
803        println!("{payload:#?}");
804
805        for start in 0..128 {
806            for end in start..128 {
807                for mid in 0..(end - start) {
808                    let witness = owner[start..end].split_at(mid);
809                    let split_payload = payload
810                        .split_at(end)
811                        .0
812                        .split_at(start)
813                        .1
814                        .split_at(mid);
815                    assert_eq!(
816                        witness.0,
817                        split_payload.0.to_vec(),
818                        "start: {start}, mid:{mid}, end:{end}"
819                    );
820                    assert_eq!(
821                        witness.1,
822                        split_payload.1.to_vec(),
823                        "start: {start}, mid:{mid}, end:{end}"
824                    );
825                }
826            }
827        }
828    }
829
830    #[test]
831    fn tls13_unpadded_record_rejects_protected_change_cipher_spec() {
832        let mut payload = [0x01u8, ContentType::ChangeCipherSpec.into(), 0x00];
833        let record = Record {
834            typ: ContentType::ApplicationData,
835            version: EncodableVersion::Legacy(ProtocolVersion::TLSv1_2),
836            payload: InboundOpaque(&mut payload),
837        };
838        assert_eq!(
839            record
840                .into_tls13_unpadded_record()
841                .unwrap_err(),
842            Error::PeerMisbehaved(PeerMisbehaved::IllegalMiddleboxChangeCipherSpec)
843        );
844    }
845
846    #[test]
847    fn record_encoding_version() {
848        for (version, expect) in [
849            (
850                EncodableVersion::InitialClientHello(Protocol::Tcp),
851                ProtocolVersion::TLSv1_0,
852            ),
853            (
854                EncodableVersion::InitialClientHello(Protocol::Quic(quic::Version::V2)),
855                ProtocolVersion::TLSv1_0,
856            ),
857            (
858                EncodableVersion::Legacy(ProtocolVersion::TLSv1_3),
859                ProtocolVersion::TLSv1_2,
860            ),
861            (
862                EncodableVersion::Legacy(ProtocolVersion::TLSv1_2),
863                ProtocolVersion::TLSv1_2,
864            ),
865        ] {
866            let record = Record {
867                typ: ContentType::Handshake,
868                version,
869                payload: OutboundPlain::Single(&[0, 1, 2, 3, 4]),
870            };
871            let encoded = record.to_unencrypted_bytes();
872            let decoded = Record::<Payload<'_>>::read(&mut Reader::new(&encoded)).unwrap();
873            assert_eq!(decoded.version.version(), expect);
874        }
875    }
876}