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::{ApiMisuse, 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(crate) 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/// A fixed-size buffer into which a [`RecordEncrypter`][] writes an encrypted record payload.
396///
397/// This wraps the output buffer passed to [`RecordEncrypter::encrypt()`][], tracking how
398/// much of it has been written as the append methods fill it front-to-back. It writes
399/// into caller-owned memory and cannot grow. [`Self::new()`] checks that the caller's
400/// buffer can hold the `len` bytes the encrypter declared, and the append methods then
401/// panic if the writes exceed that length.
402///
403/// Such a panic always indicates a bug in the `RecordEncrypter` implementation, not a
404/// runtime condition the caller can handle. The same implementation declares the total
405/// length up front (via [`RecordEncrypter::encrypted_payload_len()`]) and performs the
406/// writes, so overflowing the buffer means the two disagree. The record layer also
407/// relies on that declared length for framing, so there is no way to recover from the
408/// mismatch after the fact.
409///
410/// [`RecordEncrypter`]: crate::crypto::cipher::RecordEncrypter
411/// [`RecordEncrypter::encrypt()`]: crate::crypto::cipher::RecordEncrypter::encrypt()
412/// [`RecordEncrypter::encrypted_payload_len()`]: crate::crypto::cipher::RecordEncrypter::encrypted_payload_len()
413pub struct EncryptBuffer<'a> {
414    buf: &'a mut [u8],
415    used: usize,
416}
417
418impl<'a> EncryptBuffer<'a> {
419    /// Wrap the first `len` bytes of `out`, all of which are as yet unwritten.
420    ///
421    /// Returns [`ApiMisuse::EncryptBufferTooSmall`] if `out` is shorter than `len` bytes.
422    pub fn new(out: &'a mut [u8], len: usize) -> Result<Self, Error> {
423        let provided = out.len();
424        match out.get_mut(..len) {
425            Some(buf) => Ok(Self { buf, used: 0 }),
426            None => Err(ApiMisuse::EncryptBufferTooSmall {
427                required: len,
428                provided,
429            }
430            .into()),
431        }
432    }
433
434    /// Append bytes from an [`OutboundPlain`]'s chunks.
435    ///
436    /// Panics if the write would extend beyond the `len` given to [`Self::new()`],
437    /// which indicates a bug in the calling `RecordEncrypter` implementation (see
438    /// the type-level documentation).
439    pub fn extend_from_chunks(&mut self, chunks: &OutboundPlain<'_>) {
440        match chunks {
441            // for the common case with a single chunk we want to avoid iteration overhead.
442            OutboundPlain::Single(chunk) => self.extend_from_slice(chunk),
443            chunks => {
444                for chunk in chunks.chunks() {
445                    self.extend_from_slice(chunk);
446                }
447            }
448        }
449    }
450
451    /// Append bytes from a slice.
452    ///
453    /// Panics if the write would extend beyond the `len` given to [`Self::new()`],
454    /// which indicates a bug in the calling `RecordEncrypter` implementation (see
455    /// the type-level documentation).
456    pub fn extend_from_slice(&mut self, slice: &[u8]) {
457        self.buf[self.used..self.used + slice.len()].copy_from_slice(slice);
458        self.used += slice.len();
459    }
460
461    /// Consume this value, returning the written prefix of the wrapped buffer.
462    pub fn into_written(self) -> &'a [u8] {
463        &self.buf[..self.used]
464    }
465}
466
467impl AsMut<[u8]> for EncryptBuffer<'_> {
468    fn as_mut(&mut self) -> &mut [u8] {
469        &mut self.buf[..self.used]
470    }
471}
472
473/// An externally length'd payload
474///
475/// When encountered in an [`Record`], it represents a plaintext payload. It can be
476/// decrypted from an [`InboundOpaque`] or encrypted by a
477/// [`RecordEncrypter`](crate::crypto::cipher::RecordEncrypter), and it is also used for
478/// joining and fragmenting.
479#[non_exhaustive]
480#[derive(Clone, Eq, PartialEq)]
481pub enum Payload<'a> {
482    /// Borrowed payload
483    Borrowed(&'a [u8]),
484    /// Owned payload
485    Owned(Vec<u8>),
486}
487
488impl<'a> Payload<'a> {
489    /// A reference to the payload's bytes
490    pub fn bytes(&'a self) -> &'a [u8] {
491        match self {
492            Self::Borrowed(bytes) => bytes,
493            Self::Owned(bytes) => bytes,
494        }
495    }
496
497    pub(crate) fn into_owned(self) -> Payload<'static> {
498        Payload::Owned(self.into_vec())
499    }
500
501    pub(crate) fn into_vec(self) -> Vec<u8> {
502        match self {
503            Self::Borrowed(bytes) => bytes.to_vec(),
504            Self::Owned(bytes) => bytes,
505        }
506    }
507
508    pub(crate) fn read(r: &mut Reader<'a>) -> Self {
509        Self::Borrowed(r.rest())
510    }
511}
512
513impl Payload<'static> {
514    /// Create a new owned payload from the given `bytes`.
515    pub fn new(bytes: impl Into<Vec<u8>>) -> Self {
516        Self::Owned(bytes.into())
517    }
518}
519
520impl<'a> Codec<'a> for Payload<'a> {
521    fn encode(&self, bytes: &mut Vec<u8>) {
522        bytes.extend_from_slice(self.bytes());
523    }
524
525    fn read(r: &mut Reader<'a>) -> Result<Self, InvalidMessage> {
526        Ok(Self::read(r))
527    }
528}
529
530impl fmt::Debug for Payload<'_> {
531    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
532        hex(f, self.bytes())
533    }
534}
535
536/// A borrowed payload buffer.
537#[expect(clippy::exhaustive_structs)]
538pub struct InboundOpaque<'a>(pub &'a mut [u8]);
539
540impl<'a> InboundOpaque<'a> {
541    /// Truncate the payload to `len` bytes.
542    pub fn truncate(&mut self, len: usize) {
543        if len >= self.len() {
544            return;
545        }
546
547        self.0 = core::mem::take(&mut self.0)
548            .split_at_mut(len)
549            .0;
550    }
551
552    pub(crate) fn into_inner(self) -> &'a mut [u8] {
553        self.0
554    }
555
556    pub(crate) fn pop(&mut self) -> Option<u8> {
557        if self.is_empty() {
558            return None;
559        }
560
561        let len = self.len();
562        let last = self[len - 1];
563        self.truncate(len - 1);
564        Some(last)
565    }
566}
567
568impl Deref for InboundOpaque<'_> {
569    type Target = [u8];
570
571    fn deref(&self) -> &Self::Target {
572        self.0
573    }
574}
575
576impl DerefMut for InboundOpaque<'_> {
577    fn deref_mut(&mut self) -> &mut Self::Target {
578        self.0
579    }
580}
581
582/// A protocol version that can be encoded.
583#[derive(Clone, Copy, Debug, PartialEq, Eq)]
584#[non_exhaustive]
585pub enum EncodableVersion {
586    /// Encode the version in legacy, backward-compatible form.
587    ///
588    /// > MUST be set to 0x0303 for all records generated by a TLS 1.3
589    /// > implementation...
590    ///
591    /// <https://www.rfc-editor.org/info/rfc9846/#section-5.1>
592    Legacy(ProtocolVersion),
593    /// Encode the version as an initial client hello, not a retry.
594    ///
595    /// > ...other than an initial ClientHello (i.e., one not generated after a HelloRetryRequest),
596    /// > where it MAY also be 0x0301 for compatibility purposes.
597    ///
598    /// <https://www.rfc-editor.org/info/rfc9846/#section-5.1>
599    InitialClientHello(Protocol),
600}
601
602impl EncodableVersion {
603    /// Encode the protocol version.
604    pub fn encode(&self) -> ProtocolVersion {
605        match self {
606            Self::Legacy(_) => ProtocolVersion::TLSv1_2,
607            Self::InitialClientHello(Protocol::Tcp | Protocol::Quic(_)) => ProtocolVersion::TLSv1_0,
608        }
609    }
610
611    /// The actual protocol version in use.
612    pub fn version(&self) -> ProtocolVersion {
613        match self {
614            Self::Legacy(v) => *v,
615            Self::InitialClientHello(Protocol::Tcp | Protocol::Quic(_)) => ProtocolVersion::TLSv1_2,
616        }
617    }
618}
619
620/// Decode a TLS1.3 `TLSInnerPlaintext` encoding.
621///
622/// `p` is a record payload, immediately post-decryption.  This function
623/// removes zero padding bytes, until a non-zero byte is encountered which is
624/// the content type, which is returned.  See RFC 9846 s5.2.
625///
626/// ContentType(0) is returned if the record payload is empty or all zeroes.
627fn unpad_tls13_payload(p: &mut InboundOpaque<'_>) -> ContentType {
628    loop {
629        match p.pop() {
630            Some(0) => {}
631            Some(content_type) => return ContentType::from(content_type),
632            None => return ContentType(0),
633        }
634    }
635}
636
637/// Errors from trying to parse a TLS record.
638#[expect(missing_docs)]
639#[non_exhaustive]
640#[derive(Debug)]
641pub enum RecordError {
642    TooShortForHeader,
643    TooShortForLength,
644    InvalidEmptyPayload,
645    MessageTooLarge,
646    InvalidContentType,
647    UnknownProtocolVersion,
648}
649
650#[cfg(test)]
651mod tests {
652    use std::{println, vec};
653
654    use super::*;
655    use crate::quic;
656
657    #[test]
658    fn single_chunk_with_multiple_slices() {
659        let chunks: &[&[u8]] = &[&[], &[1, 2, 3], &[], &[4, 5, 6, 7], &[]];
660        let expected = [1, 2, 3, 4, 5, 6, 7];
661        for start in 0..=7 {
662            for end in start..=7 {
663                let (_, tail) = OutboundPlain::new(chunks).split_at(start);
664                let (plain, _) = tail.split_at(end - start);
665                let contiguous = start == end || end <= 3 || start >= 3;
666                let single_chunk = plain.single_chunk();
667                assert_eq!(single_chunk.is_some(), contiguous);
668                let Some(slice) = single_chunk else {
669                    continue;
670                };
671                assert_eq!(slice, &expected[start..end]);
672                if !slice.is_empty() {
673                    let source = if end <= 3 { chunks[1] } else { chunks[3] };
674                    let offset = if end <= 3 { start } else { start - 3 };
675                    assert_eq!(slice.as_ptr(), source[offset..].as_ptr());
676                }
677            }
678        }
679    }
680
681    #[test]
682    fn single_chunk_with_out_of_bounds_range() {
683        for start in [3, 5, 6] {
684            let plain = OutboundPlain::Multiple {
685                chunks: &[&[1, 2, 3], &[], &[4, 5], &[]],
686                start,
687                end: 7,
688            };
689            assert!(plain.single_chunk().is_none());
690        }
691    }
692
693    #[test]
694    fn single_chunk_borrows_payload() {
695        let data = [1, 2, 3];
696        let chunks: &[&[u8]] = &[&[], &data, &[]];
697        let slice = {
698            let plain = OutboundPlain::Multiple {
699                chunks,
700                start: 1,
701                end: 3,
702            };
703            plain.single_chunk().unwrap()
704        };
705        assert_eq!(slice, &data[1..]);
706        assert_eq!(slice.as_ptr(), data[1..].as_ptr());
707    }
708
709    #[test]
710    fn single_chunk_does_not_join_adjacent_slices() {
711        let owner = [1, 2, 3, 4];
712        let adjacent = [&owner[..2], &owner[2..]];
713        assert!(
714            OutboundPlain::new(&adjacent)
715                .single_chunk()
716                .is_none()
717        );
718    }
719
720    #[test]
721    fn single_chunk_empty_and_single_payloads() {
722        for plain in [
723            OutboundPlain::new_empty(),
724            OutboundPlain::new(&[]),
725            OutboundPlain::new(&[&[], &[]]),
726        ] {
727            assert_eq!(plain.single_chunk(), Some(&[][..]));
728        }
729        let data = [1, 2, 3];
730        for plain in [
731            OutboundPlain::from(data.as_slice()),
732            OutboundPlain::new(&[&data]),
733        ] {
734            let slice = plain.single_chunk().unwrap();
735            assert_eq!(slice, data);
736            assert_eq!(slice.as_ptr(), data.as_ptr());
737        }
738    }
739
740    #[test]
741    fn encrypt_buffer_appends() {
742        let mut space = [0u8; 8];
743        let mut buf = EncryptBuffer::new(&mut space[..], 6).unwrap();
744        buf.extend_from_slice(&[1, 2]);
745        buf.extend_from_chunks(&OutboundPlain::new(&[&[3u8, 4][..], &[5][..]]));
746        buf.extend_from_slice(&[6]);
747        assert_eq!(buf.as_mut(), &mut [1, 2, 3, 4, 5, 6]);
748        assert_eq!(buf.into_written(), &[1, 2, 3, 4, 5, 6]);
749        assert_eq!(space, [1, 2, 3, 4, 5, 6, 0, 0]);
750    }
751
752    #[test]
753    fn encrypt_buffer_rejects_short_buffer() {
754        let mut space = [0u8; 4];
755        assert!(matches!(
756            EncryptBuffer::new(&mut space[..], 5),
757            Err(Error::ApiMisuse(ApiMisuse::EncryptBufferTooSmall {
758                required: 5,
759                provided: 4,
760            }))
761        ));
762    }
763
764    #[test]
765    fn chunks_iteration() {
766        // `Single` yields its chunk, unless empty
767        assert_eq!(
768            OutboundPlain::Single(&[1, 2, 3])
769                .chunks()
770                .collect::<Vec<_>>(),
771            [&[1u8, 2, 3][..]],
772        );
773        assert_eq!(
774            OutboundPlain::new_empty()
775                .chunks()
776                .count(),
777            0
778        );
779
780        // `Multiple` yields the in-window part of each chunk, skipping
781        // empty chunks
782        let owner: Vec<&[u8]> = vec![&[], &[1, 2, 3], &[], &[4, 5], &[], &[6, 7], &[]];
783        let (_, tail) = OutboundPlain::new(&owner).split_at(1);
784        let (window, _) = tail.split_at(5);
785        assert_eq!(
786            window.chunks().collect::<Vec<_>>(),
787            [&[2u8, 3][..], &[4, 5][..], &[6][..]],
788        );
789    }
790
791    #[test]
792    fn split_at_with_single_slice() {
793        let owner: &[u8] = &[0, 1, 2, 3, 4, 5, 6, 7];
794        let borrowed_payload = OutboundPlain::Single(owner);
795
796        let (before, after) = borrowed_payload.split_at(6);
797        println!("before:{before:?}\nafter:{after:?}");
798        assert_eq!(before.to_vec(), &[0, 1, 2, 3, 4, 5]);
799        assert_eq!(after.to_vec(), &[6, 7]);
800    }
801
802    #[test]
803    fn split_at_with_multiple_slices() {
804        let owner: Vec<&[u8]> = vec![&[0, 1, 2, 3], &[4, 5], &[6, 7, 8], &[9, 10, 11, 12]];
805        let borrowed_payload = OutboundPlain::new(&owner);
806
807        let (before, after) = borrowed_payload.split_at(3);
808        println!("before:{before:?}\nafter:{after:?}");
809        assert_eq!(before.to_vec(), &[0, 1, 2]);
810        assert_eq!(after.to_vec(), &[3, 4, 5, 6, 7, 8, 9, 10, 11, 12]);
811
812        let (before, after) = borrowed_payload.split_at(8);
813        println!("before:{before:?}\nafter:{after:?}");
814        assert_eq!(before.to_vec(), &[0, 1, 2, 3, 4, 5, 6, 7]);
815        assert_eq!(after.to_vec(), &[8, 9, 10, 11, 12]);
816
817        let (before, after) = borrowed_payload.split_at(11);
818        println!("before:{before:?}\nafter:{after:?}");
819        assert_eq!(before.to_vec(), &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
820        assert_eq!(after.to_vec(), &[11, 12]);
821    }
822
823    #[test]
824    fn split_at_drops_consumed_chunks() {
825        let owner = vec![&[0, 1, 2][..], &[], &[3, 4], &[5], &[]];
826        let payload = OutboundPlain::new(&owner);
827
828        let (before, after) = payload.split_at(4);
829        assert_eq!(before.to_vec(), &[0, 1, 2, 3]);
830        assert_eq!(after.to_vec(), &[4, 5]);
831        let OutboundPlain::Multiple { chunks, start, end } = after else {
832            panic!("expected multiple chunks");
833        };
834        assert_eq!(chunks, &owner[2..]);
835        assert_eq!((start, end), (1, 3));
836
837        let (before, after) = after.split_at(1);
838        assert_eq!(before.to_vec(), &[4]);
839        assert_eq!(after.to_vec(), &[5]);
840        let OutboundPlain::Multiple { chunks, start, end } = after else {
841            panic!("expected multiple chunks");
842        };
843        assert_eq!(chunks, &owner[3..]);
844        assert_eq!((start, end), (0, 1));
845
846        let (before, after) = after.split_at(1);
847        assert_eq!(before.to_vec(), &[5]);
848        assert!(after.is_empty());
849        let OutboundPlain::Multiple { chunks, start, end } = after else {
850            panic!("expected multiple chunks");
851        };
852        assert!(chunks.is_empty());
853        assert_eq!((start, end), (0, 0));
854    }
855
856    #[test]
857    fn split_out_of_bounds() {
858        let owner: Vec<&[u8]> = vec![&[0, 1, 2, 3], &[4, 5], &[6, 7, 8], &[9, 10, 11, 12]];
859
860        let single_payload = OutboundPlain::Single(owner[0]);
861        let (before, after) = single_payload.split_at(17);
862        println!("before:{before:?}\nafter:{after:?}");
863        assert_eq!(before.to_vec(), &[0, 1, 2, 3]);
864        assert!(after.is_empty());
865
866        let multiple_payload = OutboundPlain::new(&owner);
867        let (before, after) = multiple_payload.split_at(17);
868        println!("before:{before:?}\nafter:{after:?}");
869        assert_eq!(before.to_vec(), &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]);
870        assert!(after.is_empty());
871
872        let empty_payload = OutboundPlain::new_empty();
873        let (before, after) = empty_payload.split_at(17);
874        println!("before:{before:?}\nafter:{after:?}");
875        assert!(before.is_empty());
876        assert!(after.is_empty());
877    }
878
879    #[test]
880    fn empty_slices_mixed() {
881        let owner: Vec<&[u8]> = vec![&[], &[], &[0], &[], &[1, 2], &[], &[3], &[4], &[], &[]];
882        let mut borrowed_payload = OutboundPlain::new(&owner);
883        let mut fragment_count = 0;
884        let mut fragment;
885        let expected_fragments: &[&[u8]] = &[&[0, 1], &[2, 3], &[4]];
886
887        while !borrowed_payload.is_empty() {
888            (fragment, borrowed_payload) = borrowed_payload.split_at(2);
889            println!("{fragment:?}");
890            assert_eq!(&expected_fragments[fragment_count], &fragment.to_vec());
891            fragment_count += 1;
892        }
893        assert_eq!(fragment_count, expected_fragments.len());
894    }
895
896    #[test]
897    fn exhaustive_splitting() {
898        let owner: Vec<u8> = (0..127).collect();
899        let slices = (0..7)
900            .map(|i| &owner[((1 << i) - 1)..((1 << (i + 1)) - 1)])
901            .collect::<Vec<_>>();
902        let payload = OutboundPlain::new(&slices);
903
904        assert_eq!(payload.to_vec(), owner);
905        println!("{payload:#?}");
906
907        for start in 0..128 {
908            for end in start..128 {
909                for mid in 0..(end - start) {
910                    let witness = owner[start..end].split_at(mid);
911                    let split_payload = payload
912                        .split_at(end)
913                        .0
914                        .split_at(start)
915                        .1
916                        .split_at(mid);
917                    assert_eq!(
918                        witness.0,
919                        split_payload.0.to_vec(),
920                        "start: {start}, mid:{mid}, end:{end}"
921                    );
922                    assert_eq!(
923                        witness.1,
924                        split_payload.1.to_vec(),
925                        "start: {start}, mid:{mid}, end:{end}"
926                    );
927                }
928            }
929        }
930    }
931
932    #[test]
933    fn tls13_unpadded_record_rejects_protected_change_cipher_spec() {
934        let mut payload = [0x01u8, ContentType::ChangeCipherSpec.into(), 0x00];
935        let record = Record {
936            typ: ContentType::ApplicationData,
937            version: EncodableVersion::Legacy(ProtocolVersion::TLSv1_2),
938            payload: InboundOpaque(&mut payload),
939        };
940        assert_eq!(
941            record
942                .into_tls13_unpadded_record()
943                .unwrap_err(),
944            Error::PeerMisbehaved(PeerMisbehaved::IllegalMiddleboxChangeCipherSpec)
945        );
946    }
947
948    #[test]
949    fn record_encoding_version() {
950        for (version, expect) in [
951            (
952                EncodableVersion::InitialClientHello(Protocol::Tcp),
953                ProtocolVersion::TLSv1_0,
954            ),
955            (
956                EncodableVersion::InitialClientHello(Protocol::Quic(quic::Version::V2)),
957                ProtocolVersion::TLSv1_0,
958            ),
959            (
960                EncodableVersion::Legacy(ProtocolVersion::TLSv1_3),
961                ProtocolVersion::TLSv1_2,
962            ),
963            (
964                EncodableVersion::Legacy(ProtocolVersion::TLSv1_2),
965                ProtocolVersion::TLSv1_2,
966            ),
967        ] {
968            let record = Record {
969                typ: ContentType::Handshake,
970                version,
971                payload: OutboundPlain::Single(&[0, 1, 2, 3, 4]),
972            };
973            let encoded = record.to_unencrypted_bytes();
974            let decoded = Record::<Payload<'_>>::read(&mut Reader::new(&encoded)).unwrap();
975            assert_eq!(decoded.version.version(), expect);
976        }
977    }
978}