use alloc::vec::Vec;
use core::ops::{Deref, DerefMut, Range};
use core::{fmt, slice};
use crate::crypto::cipher::EncryptionState;
use crate::enums::{ContentType, ProtocolVersion};
use crate::error::{ApiMisuse, Error, InvalidMessage, PeerMisbehaved};
use crate::msgs::{Codec, HEADER_SIZE, MAX_FRAGMENT_LEN, Reader, hex, read_opaque_message_header};
#[expect(clippy::exhaustive_structs)]
#[derive(Clone, Debug)]
pub struct EncodedMessage<P> {
pub typ: ContentType,
pub version: ProtocolVersion,
pub payload: P,
}
impl<P> EncodedMessage<P> {
pub fn new(typ: ContentType, version: ProtocolVersion, payload: P) -> Self {
Self {
typ,
version,
payload,
}
}
}
impl<'a> EncodedMessage<Payload<'a>> {
pub(crate) fn read(r: &mut Reader<'a>) -> Result<Self, MessageError> {
let (typ, version, len) = read_opaque_message_header(r)?;
let content = r
.take(len as usize)
.ok_or(MessageError::TooShortForLength)?;
Ok(Self {
typ,
version,
payload: Payload::Borrowed(content),
})
}
pub fn borrow_outbound(&'a self) -> EncodedMessage<OutboundPlain<'a>> {
EncodedMessage {
typ: self.typ,
version: self.version,
payload: self.payload.bytes().into(),
}
}
pub fn into_owned(self) -> Self {
Self {
typ: self.typ,
version: self.version,
payload: self.payload.into_owned(),
}
}
}
impl EncodedMessage<&'_ [u8]> {
pub(crate) fn is_valid_ccs(&self) -> bool {
self.typ == ContentType::ChangeCipherSpec && self.payload == [0x01]
}
}
impl<'a> EncodedMessage<InboundOpaque<'a>> {
pub fn into_tls13_unpadded_message(mut self) -> Result<EncodedMessage<&'a [u8]>, Error> {
let payload = &mut self.payload;
if payload.len() > MAX_FRAGMENT_LEN + 1 {
return Err(Error::PeerSentOversizedRecord);
}
self.typ = unpad_tls13_payload(payload);
if self.typ == ContentType(0) {
return Err(PeerMisbehaved::IllegalTlsInnerPlaintext.into());
}
if payload.len() > MAX_FRAGMENT_LEN {
return Err(Error::PeerSentOversizedRecord);
}
self.version = ProtocolVersion::TLSv1_3;
Ok(self.into_plain_message())
}
pub fn into_plain_message_range(self, range: Range<usize>) -> EncodedMessage<&'a [u8]> {
EncodedMessage {
typ: self.typ,
version: self.version,
payload: &self.payload.into_inner()[range],
}
}
pub fn into_plain_message(self) -> EncodedMessage<&'a [u8]> {
EncodedMessage {
typ: self.typ,
version: self.version,
payload: self.payload.into_inner(),
}
}
}
impl EncodedMessage<OutboundPlain<'_>> {
pub(crate) fn to_unencrypted_bytes(&self) -> Vec<u8> {
let len = self.payload.len();
debug_assert!(len <= usize::from(u16::MAX));
let mut buf = Vec::with_capacity(HEADER_SIZE + len);
buf.extend_from_slice(&encode_record_header(self.typ, self.version, len as u16));
self.payload.copy_to_vec(&mut buf);
buf
}
#[expect(dead_code)]
pub(crate) fn encoded_len(&self, record_layer: &EncryptionState) -> usize {
HEADER_SIZE + record_layer.encrypted_len(self.payload.len())
}
}
pub(crate) fn encode_record_header(
typ: ContentType,
version: ProtocolVersion,
len: u16,
) -> [u8; HEADER_SIZE] {
let [version_hi, version_lo] = version.to_array();
let [len_hi, len_lo] = len.to_be_bytes();
[typ.into(), version_hi, version_lo, len_hi, len_lo]
}
#[non_exhaustive]
#[derive(Debug, Clone)]
pub enum OutboundPlain<'a> {
Single(&'a [u8]),
Multiple {
chunks: &'a [&'a [u8]],
start: usize,
end: usize,
},
}
impl<'a> OutboundPlain<'a> {
pub fn new(chunks: &'a [&'a [u8]]) -> Self {
if chunks.len() == 1 {
Self::Single(chunks[0])
} else {
Self::Multiple {
chunks,
start: 0,
end: chunks
.iter()
.map(|chunk| chunk.len())
.sum(),
}
}
}
pub fn new_empty() -> Self {
Self::Single(&[])
}
pub fn to_vec(&self) -> Vec<u8> {
let mut vec = Vec::with_capacity(self.len());
self.copy_to_vec(&mut vec);
vec
}
pub fn copy_to_vec(&self, vec: &mut Vec<u8>) {
for chunk in self.chunks() {
vec.extend_from_slice(chunk);
}
}
pub fn chunks(&self) -> impl Iterator<Item = &[u8]> + '_ {
match self {
Self::Single(chunk) => Chunks::Single((!chunk.is_empty()).then_some(*chunk)),
Self::Multiple { chunks, start, end } => Chunks::Multiple {
chunks: chunks.iter(),
skip: *start,
remaining: end - start,
},
}
}
pub(crate) fn split_at(&self, mid: usize) -> (Self, Self) {
match *self {
Self::Single(chunk) => {
let mid = Ord::min(mid, chunk.len());
(Self::Single(&chunk[..mid]), Self::Single(&chunk[mid..]))
}
Self::Multiple { chunks, start, end } => {
let mid = Ord::min(start + mid, end);
(
Self::Multiple {
chunks,
start,
end: mid,
},
Self::Multiple {
chunks,
start: mid,
end,
},
)
}
}
}
pub(crate) fn is_empty(&self) -> bool {
self.len() == 0
}
#[expect(clippy::len_without_is_empty)]
pub fn len(&self) -> usize {
match self {
Self::Single(chunk) => chunk.len(),
Self::Multiple { start, end, .. } => end - start,
}
}
}
enum Chunks<'a> {
Single(Option<&'a [u8]>),
Multiple {
chunks: slice::Iter<'a, &'a [u8]>,
skip: usize,
remaining: usize,
},
}
impl<'a> Iterator for Chunks<'a> {
type Item = &'a [u8];
fn next(&mut self) -> Option<Self::Item> {
let (chunks, skip, remaining) = match self {
Self::Single(chunk) => return chunk.take(),
Self::Multiple {
chunks,
skip,
remaining,
} => (chunks, skip, remaining),
};
loop {
if *remaining == 0 {
return None;
}
let chunk = chunks.next()?;
let Some((_, chunk)) = chunk.split_at_checked(*skip) else {
*skip -= chunk.len();
continue;
};
*skip = 0;
if chunk.is_empty() {
continue;
}
let take = Ord::min(chunk.len(), *remaining);
*remaining -= take;
return Some(&chunk[..take]);
}
}
}
impl<'a> From<&'a [u8]> for OutboundPlain<'a> {
fn from(payload: &'a [u8]) -> Self {
Self::Single(payload)
}
}
pub struct EncryptBuffer<'a> {
buf: &'a mut [u8],
used: usize,
}
impl<'a> EncryptBuffer<'a> {
pub fn new(out: &'a mut [u8], len: usize) -> Result<Self, Error> {
let provided = out.len();
match out.get_mut(..len) {
Some(buf) => Ok(Self { buf, used: 0 }),
None => Err(ApiMisuse::EncryptBufferTooSmall {
required: len,
provided,
}
.into()),
}
}
pub fn extend_from_chunks(&mut self, chunks: &OutboundPlain<'_>) {
match chunks {
OutboundPlain::Single(chunk) => self.extend_from_slice(chunk),
chunks => {
for chunk in chunks.chunks() {
self.extend_from_slice(chunk);
}
}
}
}
pub fn extend_from_slice(&mut self, slice: &[u8]) {
self.buf[self.used..self.used + slice.len()].copy_from_slice(slice);
self.used += slice.len();
}
pub fn into_written(self) -> &'a [u8] {
&self.buf[..self.used]
}
}
impl AsMut<[u8]> for EncryptBuffer<'_> {
fn as_mut(&mut self) -> &mut [u8] {
&mut self.buf[..self.used]
}
}
#[non_exhaustive]
#[derive(Clone, Eq, PartialEq)]
pub enum Payload<'a> {
Borrowed(&'a [u8]),
Owned(Vec<u8>),
}
impl<'a> Payload<'a> {
pub fn bytes(&'a self) -> &'a [u8] {
match self {
Self::Borrowed(bytes) => bytes,
Self::Owned(bytes) => bytes,
}
}
pub(crate) fn into_owned(self) -> Payload<'static> {
Payload::Owned(self.into_vec())
}
pub(crate) fn into_vec(self) -> Vec<u8> {
match self {
Self::Borrowed(bytes) => bytes.to_vec(),
Self::Owned(bytes) => bytes,
}
}
pub(crate) fn read(r: &mut Reader<'a>) -> Self {
Self::Borrowed(r.rest())
}
}
impl Payload<'static> {
pub fn new(bytes: impl Into<Vec<u8>>) -> Self {
Self::Owned(bytes.into())
}
}
impl<'a> Codec<'a> for Payload<'a> {
fn encode(&self, bytes: &mut Vec<u8>) {
bytes.extend_from_slice(self.bytes());
}
fn read(r: &mut Reader<'a>) -> Result<Self, InvalidMessage> {
Ok(Self::read(r))
}
}
impl fmt::Debug for Payload<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
hex(f, self.bytes())
}
}
#[expect(clippy::exhaustive_structs)]
pub struct InboundOpaque<'a>(pub &'a mut [u8]);
impl<'a> InboundOpaque<'a> {
pub fn truncate(&mut self, len: usize) {
if len >= self.len() {
return;
}
self.0 = core::mem::take(&mut self.0)
.split_at_mut(len)
.0;
}
pub(crate) fn into_inner(self) -> &'a mut [u8] {
self.0
}
pub(crate) fn pop(&mut self) -> Option<u8> {
if self.is_empty() {
return None;
}
let len = self.len();
let last = self[len - 1];
self.truncate(len - 1);
Some(last)
}
}
impl Deref for InboundOpaque<'_> {
type Target = [u8];
fn deref(&self) -> &Self::Target {
self.0
}
}
impl DerefMut for InboundOpaque<'_> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.0
}
}
fn unpad_tls13_payload(p: &mut InboundOpaque<'_>) -> ContentType {
loop {
match p.pop() {
Some(0) => {}
Some(content_type) => return ContentType::from(content_type),
None => return ContentType(0),
}
}
}
#[expect(missing_docs)]
#[non_exhaustive]
#[derive(Debug)]
pub enum MessageError {
TooShortForHeader,
TooShortForLength,
InvalidEmptyPayload,
MessageTooLarge,
InvalidContentType,
UnknownProtocolVersion,
}
#[cfg(test)]
mod tests {
use std::{println, vec};
use super::*;
#[test]
fn encrypt_buffer_appends() {
let mut space = [0u8; 8];
let mut buf = EncryptBuffer::new(&mut space[..], 6).unwrap();
buf.extend_from_slice(&[1, 2]);
buf.extend_from_chunks(&OutboundPlain::new(&[&[3u8, 4][..], &[5][..]]));
buf.extend_from_slice(&[6]);
assert_eq!(buf.as_mut(), &mut [1, 2, 3, 4, 5, 6]);
assert_eq!(buf.into_written(), &[1, 2, 3, 4, 5, 6]);
assert_eq!(space, [1, 2, 3, 4, 5, 6, 0, 0]);
}
#[test]
fn encrypt_buffer_rejects_short_buffer() {
let mut space = [0u8; 4];
assert!(matches!(
EncryptBuffer::new(&mut space[..], 5),
Err(Error::ApiMisuse(ApiMisuse::EncryptBufferTooSmall {
required: 5,
provided: 4,
}))
));
}
#[test]
fn chunks_iteration() {
assert_eq!(
OutboundPlain::Single(&[1, 2, 3])
.chunks()
.collect::<Vec<_>>(),
[&[1u8, 2, 3][..]],
);
assert_eq!(
OutboundPlain::new_empty()
.chunks()
.count(),
0
);
let owner: Vec<&[u8]> = vec![&[], &[1, 2, 3], &[], &[4, 5], &[], &[6, 7], &[]];
let (_, tail) = OutboundPlain::new(&owner).split_at(1);
let (window, _) = tail.split_at(5);
assert_eq!(
window.chunks().collect::<Vec<_>>(),
[&[2u8, 3][..], &[4, 5][..], &[6][..]],
);
}
#[test]
fn split_at_with_single_slice() {
let owner: &[u8] = &[0, 1, 2, 3, 4, 5, 6, 7];
let borrowed_payload = OutboundPlain::Single(owner);
let (before, after) = borrowed_payload.split_at(6);
println!("before:{before:?}\nafter:{after:?}");
assert_eq!(before.to_vec(), &[0, 1, 2, 3, 4, 5]);
assert_eq!(after.to_vec(), &[6, 7]);
}
#[test]
fn split_at_with_multiple_slices() {
let owner: Vec<&[u8]> = vec![&[0, 1, 2, 3], &[4, 5], &[6, 7, 8], &[9, 10, 11, 12]];
let borrowed_payload = OutboundPlain::new(&owner);
let (before, after) = borrowed_payload.split_at(3);
println!("before:{before:?}\nafter:{after:?}");
assert_eq!(before.to_vec(), &[0, 1, 2]);
assert_eq!(after.to_vec(), &[3, 4, 5, 6, 7, 8, 9, 10, 11, 12]);
let (before, after) = borrowed_payload.split_at(8);
println!("before:{before:?}\nafter:{after:?}");
assert_eq!(before.to_vec(), &[0, 1, 2, 3, 4, 5, 6, 7]);
assert_eq!(after.to_vec(), &[8, 9, 10, 11, 12]);
let (before, after) = borrowed_payload.split_at(11);
println!("before:{before:?}\nafter:{after:?}");
assert_eq!(before.to_vec(), &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
assert_eq!(after.to_vec(), &[11, 12]);
}
#[test]
fn split_out_of_bounds() {
let owner: Vec<&[u8]> = vec![&[0, 1, 2, 3], &[4, 5], &[6, 7, 8], &[9, 10, 11, 12]];
let single_payload = OutboundPlain::Single(owner[0]);
let (before, after) = single_payload.split_at(17);
println!("before:{before:?}\nafter:{after:?}");
assert_eq!(before.to_vec(), &[0, 1, 2, 3]);
assert!(after.is_empty());
let multiple_payload = OutboundPlain::new(&owner);
let (before, after) = multiple_payload.split_at(17);
println!("before:{before:?}\nafter:{after:?}");
assert_eq!(before.to_vec(), &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]);
assert!(after.is_empty());
let empty_payload = OutboundPlain::new_empty();
let (before, after) = empty_payload.split_at(17);
println!("before:{before:?}\nafter:{after:?}");
assert!(before.is_empty());
assert!(after.is_empty());
}
#[test]
fn empty_slices_mixed() {
let owner: Vec<&[u8]> = vec![&[], &[], &[0], &[], &[1, 2], &[], &[3], &[4], &[], &[]];
let mut borrowed_payload = OutboundPlain::new(&owner);
let mut fragment_count = 0;
let mut fragment;
let expected_fragments: &[&[u8]] = &[&[0, 1], &[2, 3], &[4]];
while !borrowed_payload.is_empty() {
(fragment, borrowed_payload) = borrowed_payload.split_at(2);
println!("{fragment:?}");
assert_eq!(&expected_fragments[fragment_count], &fragment.to_vec());
fragment_count += 1;
}
assert_eq!(fragment_count, expected_fragments.len());
}
#[test]
fn exhaustive_splitting() {
let owner: Vec<u8> = (0..127).collect();
let slices = (0..7)
.map(|i| &owner[((1 << i) - 1)..((1 << (i + 1)) - 1)])
.collect::<Vec<_>>();
let payload = OutboundPlain::new(&slices);
assert_eq!(payload.to_vec(), owner);
println!("{payload:#?}");
for start in 0..128 {
for end in start..128 {
for mid in 0..(end - start) {
let witness = owner[start..end].split_at(mid);
let split_payload = payload
.split_at(end)
.0
.split_at(start)
.1
.split_at(mid);
assert_eq!(
witness.0,
split_payload.0.to_vec(),
"start: {start}, mid:{mid}, end:{end}"
);
assert_eq!(
witness.1,
split_payload.1.to_vec(),
"start: {start}, mid:{mid}, end:{end}"
);
}
}
}
}
}