use crate::{
crypto::{packet_protection, scatter, HeaderKey, HeaderProtectionMask, Key, ProtectedPayload},
packet::number::{PacketNumber, PacketNumberSpace},
varint::VarInt,
};
use bolero::{check, generator::*};
use core::mem::size_of;
use s2n_codec::{DecoderBufferMut, EncoderBuffer};
use std::convert::TryInto;
#[test]
#[cfg_attr(miri, ignore)] fn round_trip() {
check!()
.with_generator((
produce()
.map_gen(VarInt::from_u32)
.map_gen(|value| PacketNumberSpace::Initial.new_packet_number(value)),
produce::<Vec<u8>>(),
))
.for_each(|(largest_packet_number, input)| {
let mut buffer = input.clone();
if let Ok((packet_number, header_len)) =
fuzz_unprotect(&mut buffer, *largest_packet_number)
{
fuzz_protect(
&mut buffer,
header_len,
*largest_packet_number,
packet_number,
)
.expect("protection should always work");
assert_eq!(input, &buffer);
}
});
}
fn fuzz_unprotect(
input: &mut [u8],
largest_packet_number: PacketNumber,
) -> Result<(PacketNumber, usize), packet_protection::Error> {
let buffer = DecoderBufferMut::new(input);
let header_len = {
let peek = buffer.peek();
let original_len = peek.len();
let peek = peek.skip(1)?; let peek = peek.skip_with_len_prefix::<u8>()?; original_len - peek.len()
};
let (payload, _) = buffer.decode::<DecoderBufferMut>()?;
let payload = ProtectedPayload::new(header_len, payload.into_less_safe_slice());
let (truncated_packet_number, payload) =
crate::crypto::unprotect(&FuzzCrypto, largest_packet_number.space(), payload)?;
let packet_number = truncated_packet_number.expand(largest_packet_number);
packet_number
.truncate(largest_packet_number)
.filter(|actual| truncated_packet_number.eq(actual))
.ok_or(packet_protection::Error::DECODE_ERROR)?;
let (_header, _payload) = crate::crypto::decrypt(&FuzzCrypto, packet_number, payload)?;
Ok((packet_number, header_len))
}
fn fuzz_protect(
input: &mut [u8],
header_len: usize,
largest_packet_number: PacketNumber,
packet_number: PacketNumber,
) -> Result<(), packet_protection::Error> {
let payload_len = input.len();
let mut payload = EncoderBuffer::new(input);
payload.set_position(payload_len);
let payload = scatter::Buffer::new(payload);
let truncated_packet_number = packet_number.truncate(largest_packet_number).unwrap();
let packet_number_len = truncated_packet_number.len();
let (payload, _remaining) = crate::crypto::encrypt(
&mut FuzzCrypto,
packet_number,
packet_number_len,
header_len,
payload,
)?;
let _payload = crate::crypto::protect(&FuzzCrypto, payload)?;
Ok(())
}
struct FuzzCrypto;
impl Key for FuzzCrypto {
fn decrypt(
&self,
packet_number: u64,
_header: &[u8],
payload: &mut [u8],
) -> Result<(), packet_protection::Error> {
let mask = packet_number as u8;
for byte in payload.iter_mut() {
*byte ^= mask;
}
Ok(())
}
fn encrypt<'a>(
&mut self,
packet_number: u64,
_header: &[u8],
payload: &mut scatter::Buffer,
) -> Result<(), packet_protection::Error> {
let payload = payload.flatten();
let (payload, _) = payload.split_mut();
let mask = packet_number as u8;
for byte in payload.iter_mut() {
*byte ^= mask;
}
Ok(())
}
fn tag_len(&self) -> usize {
0
}
fn aead_confidentiality_limit(&self) -> u64 {
0
}
fn aead_integrity_limit(&self) -> u64 {
0
}
fn cipher_suite(&self) -> crate::crypto::tls::CipherSuite {
crate::crypto::tls::CipherSuite::Unknown
}
}
impl HeaderKey for FuzzCrypto {
fn opening_header_protection_mask(&self, buffer: &[u8]) -> HeaderProtectionMask {
buffer.try_into().unwrap()
}
fn opening_sample_len(&self) -> usize {
size_of::<HeaderProtectionMask>()
}
fn sealing_header_protection_mask(&self, buffer: &[u8]) -> HeaderProtectionMask {
buffer.try_into().unwrap()
}
fn sealing_sample_len(&self) -> usize {
size_of::<HeaderProtectionMask>()
}
}