use std::fmt::Debug;
use crate::{AlertDescription, TlsVersion};
#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum ContentType {
ChangeCipherSpec = 20,
Alert = 21,
Handshake = 22,
ApplicationData = 23,
}
impl From<ContentType> for u8 {
#[inline]
fn from(content_type: ContentType) -> Self {
content_type as u8
}
}
impl TryFrom<u8> for ContentType {
type Error = u8;
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
x if x == (Self::ChangeCipherSpec as u8) => Ok(Self::ChangeCipherSpec),
x if x == (Self::Alert as u8) => Ok(Self::Alert),
x if x == (Self::Handshake as u8) => Ok(Self::Handshake),
x if x == (Self::ApplicationData as u8) => Ok(Self::ApplicationData),
_ => Err(value),
}
}
}
impl ContentType {
pub fn min_length(&self) -> u16 {
match self {
ContentType::ChangeCipherSpec => 1,
ContentType::Alert => 2,
ContentType::Handshake => 1,
ContentType::ApplicationData => 0,
}
}
}
pub struct RecordHeader {
buf: [u8; Self::LEN],
}
impl RecordHeader {
pub const LEN: usize = 5;
const MAX_LENGTH: u16 = 1 << 14;
pub fn content_type(&self) -> ContentType {
ContentType::try_from(self.buf[0]).unwrap()
}
pub fn length(&self) -> u16 {
u16::from_be_bytes(self.buf[3..5].try_into().unwrap())
}
pub fn as_bytes(&self) -> &[u8; 5] {
&self.buf
}
pub fn ser(content_type: ContentType, len: usize) -> Result<Self, AlertDescription> {
let len_u16: u16 = u16::try_from(len).unwrap_or(u16::MAX);
if len_u16 > Self::MAX_LENGTH {
log::error!(
"Attempted to create record with length={} greater than maximum of {}",
len,
Self::MAX_LENGTH
);
return Err(AlertDescription::InternalError)?;
}
Ok(Self {
buf: [
content_type.into(),
TlsVersion::V1_2.msb(),
TlsVersion::V1_2.lsb(),
(len_u16 >> 8) as u8,
len_u16 as u8,
],
})
}
pub fn deser(buf: [u8; 5]) -> Result<Self, AlertDescription> {
match ContentType::try_from(buf[0]) {
Ok(content_type) => content_type,
Err(content_type) => {
log::error!(
"Record has invalid ContentType value: 0x{:02X}",
content_type
);
return Err(AlertDescription::DecodeError);
}
};
let ret = Self { buf };
if ret.length() > Self::MAX_LENGTH {
log::error!(
"Record length={} is greater than maximum of {}",
ret.length(),
Self::MAX_LENGTH
);
return Err(AlertDescription::RecordOverflow);
}
Ok(Self { buf })
}
}
impl Debug for RecordHeader {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("RecordHeader")
.field("ContentType", &self.content_type())
.field("Length", &self.length())
.finish()
}
}