use crate::{Error, Result, error::buffer_len};
pub const FORMAT_VERSION: u8 = 1;
pub const TAG_LEN: usize = 16;
pub const AES_MAX_BYTES: u64 = 1 << 36;
pub const AES_MAX_FRAMES: u64 = 1 << 32;
pub const AES_MAX_FRAME_LEN: u64 = 1 << 24;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum Framing {
Fixed(u64),
Variable(u64),
}
impl Framing {
pub const fn max_frame_len(self) -> u64 {
match self {
Self::Fixed(size) | Self::Variable(size) => size,
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[repr(u8)]
pub enum Compression {
None = 0,
Lz4 = 1,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[repr(u8)]
pub enum Encryption {
None = 0,
Aes256Gcm = 1,
}
impl Encryption {
pub const fn tag_len(self) -> usize {
match self {
Self::None => 0,
Self::Aes256Gcm => TAG_LEN,
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct Config {
framing: Framing,
compression: Compression,
encryption: Encryption,
}
#[cfg(feature = "serde")]
impl<'de> serde::Deserialize<'de> for Config {
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
#[derive(serde::Deserialize)]
struct Fields {
framing: Framing,
compression: Compression,
encryption: Encryption,
}
let fields = <Fields as serde::Deserialize>::deserialize(deserializer)?;
Self::new(fields.framing, fields.compression, fields.encryption)
.map_err(serde::de::Error::custom)
}
}
impl Config {
pub fn new(framing: Framing, compression: Compression, encryption: Encryption) -> Result<Self> {
let max = framing.max_frame_len();
if max == 0 {
return Err(Error::InvalidFrameSize);
}
buffer_len(
max.checked_add(encryption.tag_len() as u64)
.ok_or(Error::Overflow)?,
)?;
if compression == Compression::Lz4 && max > 0x7e00_0000 {
return Err(Error::InvalidFrameSize);
}
if encryption == Encryption::Aes256Gcm && max > AES_MAX_FRAME_LEN {
return Err(Error::InvalidFrameSize);
}
Ok(Self {
framing,
compression,
encryption,
})
}
pub const fn framing(self) -> Framing {
self.framing
}
pub const fn compression(self) -> Compression {
self.compression
}
pub const fn encryption(self) -> Encryption {
self.encryption
}
pub const fn max_frame_len(self) -> u64 {
self.framing.max_frame_len()
}
pub fn max_frames(self) -> u64 {
match self.encryption {
Encryption::None => u64::MAX,
Encryption::Aes256Gcm => AES_MAX_FRAMES.min(AES_MAX_BYTES / self.max_frame_len()),
}
}
pub(crate) fn validate_frame(self, index: u64, raw: u64, payload: u64) -> Result<()> {
if raw == 0 || raw > self.max_frame_len() {
return Err(Error::InvalidFrameSize);
}
if payload == 0
|| payload > raw
|| (self.compression == Compression::None && payload != raw)
{
return Err(Error::InvalidMetadata);
}
if index >= self.max_frames() {
return Err(Error::UsageLimit);
}
Ok(())
}
}