use alloc::boxed::Box;
use alloc::string::ToString;
use core::{array, fmt};
use pki_types::FipsStatus;
use zeroize::Zeroize;
use crate::enums::{ContentType, ProtocolVersion};
use crate::error::{ApiMisuse, Error};
use crate::msgs::{put_u16, put_u64};
use crate::suites::ConnectionTrafficSecrets;
mod messages;
pub(crate) use messages::encode_record_header;
pub use messages::{
EncodedMessage, EncryptBuffer, InboundOpaque, MessageError, OutboundPlain, Payload,
};
mod record_layer;
pub(crate) use record_layer::{Decrypted, DecryptionState, EncryptionState, PreEncryptAction};
pub trait Tls13AeadAlgorithm: Send + Sync {
fn encrypter(&self, key: AeadKey, iv: Iv) -> Box<dyn MessageEncrypter>;
fn decrypter(&self, key: AeadKey, iv: Iv) -> Box<dyn MessageDecrypter>;
fn key_len(&self) -> usize;
fn iv_len(&self) -> usize {
NONCE_LEN
}
fn extract_keys(
&self,
key: AeadKey,
iv: Iv,
) -> Result<ConnectionTrafficSecrets, UnsupportedOperationError>;
fn fips(&self) -> FipsStatus {
FipsStatus::Unvalidated
}
}
pub trait Tls12AeadAlgorithm: Send + Sync + 'static {
fn encrypter(&self, key: AeadKey, iv: &[u8], extra: &[u8]) -> Box<dyn MessageEncrypter>;
fn decrypter(&self, key: AeadKey, iv: &[u8]) -> Box<dyn MessageDecrypter>;
fn key_block_shape(&self) -> KeyBlockShape;
fn extract_keys(
&self,
key: AeadKey,
iv: &[u8],
explicit: &[u8],
) -> Result<ConnectionTrafficSecrets, UnsupportedOperationError>;
fn fips(&self) -> FipsStatus {
FipsStatus::Unvalidated
}
}
#[expect(clippy::exhaustive_structs)]
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
pub struct UnsupportedOperationError;
impl From<UnsupportedOperationError> for Error {
fn from(value: UnsupportedOperationError) -> Self {
Self::General(value.to_string())
}
}
impl fmt::Display for UnsupportedOperationError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "operation not supported")
}
}
impl core::error::Error for UnsupportedOperationError {}
#[expect(clippy::exhaustive_structs)]
pub struct KeyBlockShape {
pub enc_key_len: usize,
pub fixed_iv_len: usize,
pub explicit_nonce_len: usize,
}
pub trait MessageDecrypter: Send + Sync {
fn decrypt<'a>(
&mut self,
msg: EncodedMessage<InboundOpaque<'a>>,
seq: u64,
) -> Result<EncodedMessage<&'a [u8]>, Error>;
}
pub trait MessageEncrypter: Send + Sync {
fn encrypt<'a>(
&mut self,
msg: EncodedMessage<OutboundPlain<'_>>,
seq: u64,
out: &'a mut [u8],
) -> Result<EncodedMessage<&'a [u8]>, Error>;
fn encrypted_payload_len(&self, payload_len: usize) -> usize;
}
#[derive(Default, Clone)]
pub struct Iv {
buf: [u8; Self::MAX_LEN],
used: usize,
}
impl Iv {
pub fn new(value: &[u8]) -> Result<Self, Error> {
if value.len() > Self::MAX_LEN {
return Err(ApiMisuse::IvLengthExceedsMaximum {
actual: value.len(),
maximum: Self::MAX_LEN,
}
.into());
}
let mut buf = [0u8; Self::MAX_LEN];
buf[..value.len()].copy_from_slice(value);
Ok(Self {
buf,
used: value.len(),
})
}
#[expect(clippy::len_without_is_empty)]
pub fn len(&self) -> usize {
self.used
}
pub const MAX_LEN: usize = 16;
}
impl From<[u8; NONCE_LEN]> for Iv {
fn from(bytes: [u8; NONCE_LEN]) -> Self {
Self::new(&bytes).expect("NONCE_LEN is within MAX_LEN")
}
}
impl AsRef<[u8]> for Iv {
fn as_ref(&self) -> &[u8] {
&self.buf[..self.used]
}
}
pub struct Nonce {
buf: [u8; Iv::MAX_LEN],
len: usize,
}
impl Nonce {
#[inline]
pub fn new(iv: &Iv, seq: u64) -> Self {
Self::new_inner(None, iv, seq)
}
pub fn quic(path_id: Option<u32>, iv: &Iv, pn: u64) -> Self {
Self::new_inner(path_id, iv, pn)
}
#[inline]
fn new_inner(path_id: Option<u32>, iv: &Iv, seq: u64) -> Self {
let iv_len = iv.len();
let mut buf = [0u8; Iv::MAX_LEN];
if iv_len >= 8 {
put_u64(seq, &mut buf[iv_len - 8..iv_len]);
if let Some(path_id) = path_id {
if iv_len >= 12 {
buf[iv_len - 12..iv_len - 8].copy_from_slice(&path_id.to_be_bytes());
}
}
} else {
let seq_bytes = seq.to_be_bytes();
buf[..iv_len].copy_from_slice(&seq_bytes[8 - iv_len..]);
}
buf[..iv_len]
.iter_mut()
.zip(iv.as_ref())
.for_each(|(s, iv)| *s ^= *iv);
Self { buf, len: iv_len }
}
pub fn to_array<const N: usize>(&self) -> Result<[u8; N], Error> {
if self.len != N {
return Err(ApiMisuse::NonceArraySizeMismatch {
expected: N,
actual: self.len,
}
.into());
}
Ok(self.buf[..N]
.try_into()
.expect("nonce buffer conversion failed"))
}
pub fn as_bytes(&self) -> &[u8] {
&self.buf[..self.len]
}
#[expect(clippy::len_without_is_empty)]
pub fn len(&self) -> usize {
self.len
}
}
impl AsRef<[u8]> for Nonce {
fn as_ref(&self) -> &[u8] {
&self.buf[..self.len]
}
}
pub const NONCE_LEN: usize = 12;
#[inline]
pub fn make_tls13_aad(payload_len: usize) -> [u8; 5] {
let version = ProtocolVersion::TLSv1_2.to_array();
[
ContentType::ApplicationData.into(),
version[0],
version[1],
(payload_len >> 8) as u8,
(payload_len & 0xff) as u8,
]
}
#[inline]
pub fn make_tls12_aad(
seq: u64,
typ: ContentType,
vers: ProtocolVersion,
len: usize,
) -> [u8; TLS12_AAD_SIZE] {
let mut out = [0; TLS12_AAD_SIZE];
put_u64(seq, &mut out[0..]);
out[8] = typ.into();
put_u16(vers.into(), &mut out[9..]);
put_u16(len as u16, &mut out[11..]);
out
}
const TLS12_AAD_SIZE: usize = 8 + 1 + 2 + 2;
pub struct AeadKey {
buf: [u8; Self::MAX_LEN],
used: usize,
}
impl AeadKey {
pub(crate) fn new(buf: &[u8]) -> Self {
debug_assert!(buf.len() <= Self::MAX_LEN);
let mut key = Self::from([0u8; Self::MAX_LEN]);
key.buf[..buf.len()].copy_from_slice(buf);
key.used = buf.len();
key
}
pub(crate) fn with_length(self, len: usize) -> Self {
assert!(len <= self.used);
Self {
buf: self.buf,
used: len,
}
}
pub(crate) const MAX_LEN: usize = 32;
}
impl Drop for AeadKey {
#[inline(never)]
fn drop(&mut self) {
self.buf.zeroize();
}
}
impl AsRef<[u8]> for AeadKey {
fn as_ref(&self) -> &[u8] {
&self.buf[..self.used]
}
}
impl From<[u8; Self::MAX_LEN]> for AeadKey {
fn from(bytes: [u8; Self::MAX_LEN]) -> Self {
Self {
buf: bytes,
used: Self::MAX_LEN,
}
}
}
impl From<[u8; 16]> for AeadKey {
fn from(buf: [u8; 16]) -> Self {
Self {
buf: array::from_fn(|i| if i < 16 { buf[i] } else { 0 }),
used: 16,
}
}
}
#[cfg(test)]
pub(crate) struct FakeAead;
#[cfg(test)]
impl Tls12AeadAlgorithm for FakeAead {
fn encrypter(&self, _: AeadKey, _: &[u8], _: &[u8]) -> Box<dyn MessageEncrypter> {
todo!()
}
fn decrypter(&self, _: AeadKey, _: &[u8]) -> Box<dyn MessageDecrypter> {
todo!()
}
fn key_block_shape(&self) -> KeyBlockShape {
todo!()
}
fn extract_keys(
&self,
_: AeadKey,
_: &[u8],
_: &[u8],
) -> Result<ConnectionTrafficSecrets, UnsupportedOperationError> {
Err(UnsupportedOperationError)
}
fn fips(&self) -> FipsStatus {
FipsStatus::Unvalidated
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn multipath_nonce() {
const PATH_ID: u32 = 3;
const PN: u64 = 54321;
const IV: [u8; 16] = 0x6b26114b9cba2b63a9e8dd4fu128.to_be_bytes();
const EXPECTED_NONCE: [u8; 16] = 0x6b2611489cba2b63a9e8097eu128.to_be_bytes();
let nonce = Nonce::quic(Some(PATH_ID), &Iv::new(&IV[4..]).unwrap(), PN);
assert_eq!(&EXPECTED_NONCE[4..], nonce.as_bytes());
}
#[test]
fn iv_len() {
let iv = Iv::new(&[1u8; NONCE_LEN]).unwrap();
assert_eq!(iv.len(), NONCE_LEN);
let short_iv = Iv::new(&[1u8, 2, 3]).unwrap();
assert_eq!(short_iv.len(), 3);
let empty_iv = Iv::new(&[]).unwrap();
assert_eq!(empty_iv.len(), 0);
}
#[test]
fn iv_as_ref() {
let iv_data = [1u8, 2, 3, 4, 5];
let iv = Iv::new(&iv_data).unwrap();
let iv_ref: &[u8] = iv.as_ref();
assert_eq!(iv_ref, &iv_data);
}
#[test]
fn nonce_with_short_iv() {
let short_iv = Iv::new(&[0xAA, 0xBB, 0xCC, 0xDD]).unwrap();
let seq = 0x1122334455667788u64;
let nonce = Nonce::new(&short_iv, seq);
assert_eq!(nonce.len(), 4);
let seq_bytes = seq.to_be_bytes();
let expected = [
0xAA ^ seq_bytes[4],
0xBB ^ seq_bytes[5],
0xCC ^ seq_bytes[6],
0xDD ^ seq_bytes[7],
];
assert_eq!(nonce.as_bytes(), &expected);
}
#[test]
fn nonce_len() {
let iv = Iv::new(&[1u8; NONCE_LEN]).unwrap();
let nonce = Nonce::new(&iv, 42);
assert_eq!(nonce.len(), NONCE_LEN);
let short_iv = Iv::new(&[1u8, 2]).unwrap();
let short_nonce = Nonce::new(&short_iv, 42);
assert_eq!(short_nonce.len(), 2);
}
#[test]
fn nonce_as_ref() {
let iv = Iv::new(&[1u8; NONCE_LEN]).unwrap();
let nonce = Nonce::new(&iv, 42);
let nonce_ref: &[u8] = nonce.as_ref();
assert_eq!(nonce_ref.len(), NONCE_LEN);
}
#[test]
fn nonce_to_array_correct_size() {
let iv = Iv::new(&[1u8; NONCE_LEN]).unwrap();
let nonce = Nonce::new(&iv, 42);
let array: [u8; NONCE_LEN] = nonce.to_array().unwrap();
assert_eq!(array.len(), NONCE_LEN);
}
#[test]
fn nonce_to_array_wrong_size() {
let iv = Iv::new(&[1u8; NONCE_LEN]).unwrap();
let nonce = Nonce::new(&iv, 42);
let result: Result<[u8; 16], Error> = nonce.to_array();
assert!(matches!(
result,
Err(Error::ApiMisuse(ApiMisuse::NonceArraySizeMismatch {
expected: 16,
actual: NONCE_LEN
}))
));
}
#[test]
fn nonce_to_array_variable_length_error() {
let short_iv = Iv::new(&[0xAAu8; 8]).unwrap();
let nonce = Nonce::new(&short_iv, 42);
let result: Result<[u8; NONCE_LEN], Error> = nonce.to_array();
if let Err(Error::ApiMisuse(ApiMisuse::NonceArraySizeMismatch { expected, actual })) =
result
{
assert_eq!(expected, NONCE_LEN);
assert_eq!(actual, 8);
} else {
panic!("Expected Error::ApiMisuse(NonceArraySizeMismatch)");
}
let result_correct: Result<[u8; 8], Error> = nonce.to_array();
assert!(result_correct.is_ok());
}
#[test]
fn nonce_xor_with_iv() {
let iv_data = [0xFFu8; NONCE_LEN];
let iv = Iv::new(&iv_data).unwrap();
let seq = 0x0000000000000001u64;
let nonce = Nonce::new(&iv, seq);
let nonce_bytes = nonce.as_bytes();
assert_eq!(nonce_bytes[NONCE_LEN - 1], 0xFE);
}
#[test]
fn iv_length_exceeds_maximum() {
let too_long_iv = [0xAAu8; Iv::MAX_LEN + 1];
let result = Iv::new(&too_long_iv);
assert!(matches!(
result,
Err(Error::ApiMisuse(ApiMisuse::IvLengthExceedsMaximum {
actual: 17,
maximum: 16
}))
));
}
#[test]
fn aead_key_16_bytes() {
let bytes = [0xABu8; 16];
let key = AeadKey::from(bytes);
assert_eq!(key.as_ref(), &bytes);
}
}