use arrayvec::ArrayVec;
use crate::types::{ContentType, Sequence};
#[cfg(test)]
pub(crate) const DTLS_EXPLICIT_NONCE_LEN: usize = 8;
#[cfg(test)]
pub(crate) const GCM_TAG_LEN: usize = 16;
#[cfg(test)]
pub(crate) const DTLS_AEAD_OVERHEAD: usize = DTLS_EXPLICIT_NONCE_LEN + GCM_TAG_LEN;
#[inline]
#[cfg(test)]
pub fn aad_len_from_plaintext_len(plaintext_len: u16) -> u16 {
plaintext_len
}
#[inline]
#[cfg(test)]
pub fn fragment_len_from_plaintext_len(plaintext_len: usize) -> usize {
DTLS_EXPLICIT_NONCE_LEN + plaintext_len + GCM_TAG_LEN
}
#[inline]
#[cfg(test)]
pub fn plaintext_len_from_fragment_len(fragment_len: usize) -> Option<usize> {
fragment_len.checked_sub(DTLS_AEAD_OVERHEAD)
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) struct Iv {
bytes: [u8; 12],
len: u8,
}
impl Iv {
pub(crate) fn new(iv: &[u8]) -> Self {
assert!(
iv.len() <= 12,
"invalid IV length: expected <= 12, got {}",
iv.len()
);
let mut bytes = [0u8; 12];
bytes[..iv.len()].copy_from_slice(iv);
Self {
bytes,
len: iv.len() as u8,
}
}
pub(crate) fn len(&self) -> usize {
self.len as usize
}
pub(crate) fn as_slice(&self) -> &[u8] {
&self.bytes[..self.len()]
}
pub(crate) fn as_12_bytes(&self) -> &[u8; 12] {
assert_eq!(
self.len(),
12,
"as_12_bytes called on {}-byte IV",
self.len()
);
&self.bytes
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Nonce(pub [u8; 12]);
impl Nonce {
pub(crate) fn new(iv: Iv, explicit_nonce: &[u8]) -> Self {
assert_eq!(
iv.len() + explicit_nonce.len(),
12,
"invalid DTLS 1.2 nonce parts: iv_len={}, explicit_nonce_len={}",
iv.len(),
explicit_nonce.len()
);
let mut nonce = [0u8; 12];
let iv_len = iv.len();
nonce[..iv_len].copy_from_slice(iv.as_slice());
nonce[iv_len..].copy_from_slice(explicit_nonce);
Self(nonce)
}
pub(crate) fn xor(iv: &[u8; 12], seq: u64) -> Self {
let mut nonce = *iv;
let seq_bytes = seq.to_be_bytes(); for i in 0..8 {
nonce[4 + i] ^= seq_bytes[i];
}
Self(nonce)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Aad(pub ArrayVec<u8, 13>);
impl Aad {
pub(crate) fn new_dtls12(content_type: ContentType, sequence: Sequence, length: u16) -> Self {
let mut aad = ArrayVec::new();
let seq_bytes = sequence.sequence_number.to_be_bytes();
aad.try_extend_from_slice(&seq_bytes).unwrap();
let epoch_bytes = sequence.epoch.to_be_bytes();
aad[0] = epoch_bytes[0];
aad[1] = epoch_bytes[1];
aad.push(content_type.as_u8());
aad.push(0xfe); aad.push(0xfd);
aad.try_extend_from_slice(&length.to_be_bytes()).unwrap();
Aad(aad)
}
pub(crate) fn new_dtls13(header_bytes: &[u8]) -> Self {
let mut aad = ArrayVec::new();
aad.try_extend_from_slice(header_bytes).unwrap();
Aad(aad)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn aead_constants_and_length_helpers() {
assert_eq!(DTLS_EXPLICIT_NONCE_LEN, 8);
assert_eq!(GCM_TAG_LEN, 16);
assert_eq!(DTLS_AEAD_OVERHEAD, 24);
for &pt_len in &[0usize, 1, 37, 512, 1350, 16384] {
let aad_len = aad_len_from_plaintext_len(pt_len as u16);
assert_eq!(aad_len as usize, pt_len);
let frag_len = fragment_len_from_plaintext_len(pt_len);
assert_eq!(frag_len, DTLS_EXPLICIT_NONCE_LEN + pt_len + GCM_TAG_LEN);
let roundtrip =
plaintext_len_from_fragment_len(frag_len).expect("frag_len >= overhead");
assert_eq!(roundtrip, pt_len);
}
assert!(plaintext_len_from_fragment_len(0).is_none());
assert!(plaintext_len_from_fragment_len(3).is_none());
assert!(plaintext_len_from_fragment_len(DTLS_AEAD_OVERHEAD - 1).is_none());
}
}