use crate::{
credentials::Credentials,
crypto,
packet::{
control::decoder::ControlFramesMut,
stream::{self, RelativeRetransmissionOffset, Tag},
WireVersion,
},
};
use core::{fmt, mem::size_of};
use s2n_codec::{
decoder_invariant, CheckedRange, DecoderBufferMut, DecoderBufferMutResult as R, DecoderError,
};
use s2n_quic_core::{assume, ensure, varint::VarInt};
type PacketNumber = VarInt;
pub trait Validator {
fn validate_tag(&mut self, tag: Tag) -> Result<(), DecoderError>;
}
impl Validator for () {
#[inline]
fn validate_tag(&mut self, _tag: Tag) -> Result<(), DecoderError> {
Ok(())
}
}
impl Validator for Tag {
#[inline]
fn validate_tag(&mut self, actual: Tag) -> Result<(), DecoderError> {
decoder_invariant!(*self == actual, "unexpected packet type");
Ok(())
}
}
impl<A, B> Validator for (A, B)
where
A: Validator,
B: Validator,
{
#[inline]
fn validate_tag(&mut self, tag: Tag) -> Result<(), DecoderError> {
self.0.validate_tag(tag)?;
self.1.validate_tag(tag)?;
Ok(())
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Owned {
pub tag: Tag,
pub wire_version: WireVersion,
pub credentials: Credentials,
pub source_queue_id: Option<VarInt>,
pub stream_id: stream::Id,
pub original_packet_number: PacketNumber,
pub packet_number: PacketNumber,
pub retransmission_packet_number_offset: u8,
pub next_expected_control_packet: PacketNumber,
pub stream_offset: VarInt,
pub final_offset: Option<VarInt>,
pub application_header: Vec<u8>,
pub control_data: Vec<u8>,
pub payload: Vec<u8>,
pub auth_tag: Vec<u8>,
}
impl<'a> From<Packet<'a>> for Owned {
fn from(packet: Packet<'a>) -> Self {
let application_header = packet.application_header().to_vec();
let control_data = packet.control_data().to_vec();
Self {
tag: packet.tag,
wire_version: packet.wire_version,
credentials: packet.credentials,
source_queue_id: packet.source_queue_id,
stream_id: packet.stream_id,
original_packet_number: packet.original_packet_number,
packet_number: packet.packet_number,
retransmission_packet_number_offset: packet.retransmission_packet_number_offset,
next_expected_control_packet: packet.next_expected_control_packet,
stream_offset: packet.stream_offset,
final_offset: packet.final_offset,
application_header,
control_data,
payload: packet.payload.to_vec(),
auth_tag: packet.auth_tag.to_vec(),
}
}
}
pub struct Packet<'a> {
tag: Tag,
wire_version: WireVersion,
credentials: Credentials,
source_queue_id: Option<VarInt>,
stream_id: stream::Id,
original_packet_number: PacketNumber,
packet_number: PacketNumber,
retransmission_packet_number_offset: u8,
next_expected_control_packet: PacketNumber,
stream_offset: VarInt,
final_offset: Option<VarInt>,
header: &'a mut [u8],
application_header: CheckedRange,
control_data: CheckedRange,
payload: &'a mut [u8],
auth_tag: &'a mut [u8],
}
impl fmt::Debug for Packet<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("stream::Packet")
.field("tag", &self.tag)
.field("wire_version", &self.wire_version)
.field("credentials", &self.credentials)
.field("source_queue_id", &self.source_queue_id)
.field("stream_id", &self.stream_id)
.field("packet_number", &self.packet_number())
.field("stream_offset", &self.stream_offset)
.field("final_offset", &self.final_offset)
.field("header_len", &self.header.len())
.field("payload_len", &self.payload.len())
.field("auth_tag_len", &self.auth_tag.len())
.finish()
}
}
impl Packet<'_> {
#[inline]
pub fn tag(&self) -> Tag {
self.tag
}
#[inline]
pub fn wire_version(&self) -> WireVersion {
self.wire_version
}
#[inline]
pub fn credentials(&self) -> &Credentials {
&self.credentials
}
#[inline]
pub fn source_queue_id(&self) -> Option<VarInt> {
self.source_queue_id
}
#[inline]
pub fn stream_id(&self) -> &stream::Id {
&self.stream_id
}
#[inline]
pub fn packet_number(&self) -> PacketNumber {
self.packet_number
}
#[inline]
pub fn is_retransmission(&self) -> bool {
self.packet_number != self.original_packet_number
}
#[inline]
pub fn next_expected_control_packet(&self) -> PacketNumber {
self.next_expected_control_packet
}
#[inline]
pub fn stream_offset(&self) -> VarInt {
self.stream_offset
}
#[inline]
pub fn final_offset(&self) -> Option<VarInt> {
self.final_offset
}
#[inline]
pub fn is_fin(&self) -> bool {
self.final_offset()
.and_then(|offset| offset.checked_sub(self.stream_offset))
.and_then(|offset| {
let len = VarInt::try_from(self.payload.len()).ok()?;
offset.checked_sub(len)
})
.is_some_and(|v| *v == 0)
}
#[inline]
pub fn application_header(&self) -> &[u8] {
self.application_header.get(self.header)
}
#[inline]
pub fn control_data(&self) -> &[u8] {
self.control_data.get(self.header)
}
#[inline]
pub fn control_frames_mut(&mut self) -> ControlFramesMut<'_> {
ControlFramesMut::new(self.control_data.get_mut(self.header))
}
#[inline]
pub fn header(&self) -> &[u8] {
self.header
}
#[inline]
pub fn payload(&self) -> &[u8] {
self.payload
}
#[inline]
pub fn auth_tag(&self) -> &[u8] {
self.auth_tag
}
#[inline]
pub fn payload_mut(&mut self) -> &mut [u8] {
self.payload
}
#[inline]
pub fn total_len(&self) -> usize {
self.header.len() + self.payload.len() + self.auth_tag.len()
}
#[inline]
pub fn decrypt<D, C>(
&mut self,
d: &D,
c: &C,
payload_out: &mut crypto::UninitSlice,
) -> Result<(), crypto::open::Error>
where
D: crypto::open::Application,
C: crypto::open::control::Stream,
{
let key_phase = self.tag.key_phase();
let space = self.remove_retransmit(c)?;
let nonce = self.original_packet_number.as_u64();
let header = &self.header;
let payload = &self.payload;
let auth_tag = &self.auth_tag;
match space {
stream::PacketSpace::Stream => {
d.decrypt(key_phase, nonce, header, payload, auth_tag, payload_out)?;
}
stream::PacketSpace::Recovery => {
ensure!(payload.is_empty(), Err(crypto::open::Error::MacOnly));
c.verify(header, auth_tag)?;
}
}
Ok(())
}
#[inline]
pub fn decrypt_in_place<D, C>(&mut self, d: &D, c: &C) -> Result<(), crypto::open::Error>
where
D: crypto::open::Application,
C: crypto::open::control::Stream,
{
let key_phase = self.tag.key_phase();
let space = self.remove_retransmit(c)?;
let nonce = self.original_packet_number.as_u64();
let header = &self.header;
match space {
stream::PacketSpace::Stream => {
let payload_len = self.payload.len();
let payload_ptr = self.payload.as_mut_ptr();
let tag_len = self.auth_tag.len();
let tag_ptr = self.auth_tag.as_mut_ptr();
let payload_and_tag = unsafe {
debug_assert_eq!(payload_ptr.add(payload_len), tag_ptr);
core::slice::from_raw_parts_mut(payload_ptr, payload_len + tag_len)
};
d.decrypt_in_place(key_phase, nonce, header, payload_and_tag)?;
}
stream::PacketSpace::Recovery => {
ensure!(self.payload.is_empty(), Err(crypto::open::Error::MacOnly));
c.verify(header, self.auth_tag)?;
}
}
Ok(())
}
#[inline]
fn remove_retransmit<C>(&mut self, c: &C) -> Result<stream::PacketSpace, crypto::open::Error>
where
C: crypto::open::control::Stream,
{
let space = self.tag.packet_space();
let original_packet_number = self.original_packet_number;
let retransmission_packet_number = self.packet_number;
if original_packet_number != retransmission_packet_number {
c.retransmission_tag(
original_packet_number.as_u64(),
retransmission_packet_number.as_u64(),
self.auth_tag,
)?;
self.header[0] &= !super::Tag::IS_RECOVERY_PACKET;
let offset = self.retransmission_packet_number_offset as usize;
let range = offset..offset + size_of::<RelativeRetransmissionOffset>();
self.header[range].copy_from_slice(&[0; size_of::<RelativeRetransmissionOffset>()]);
Ok(stream::PacketSpace::Stream)
} else {
Ok(space)
}
}
#[inline]
#[cfg(debug_assertions)]
pub fn retransmit<K>(
buffer: DecoderBufferMut,
space: stream::PacketSpace,
retransmission_packet_number: VarInt,
key: &K,
) -> Result<(), DecoderError>
where
K: crypto::seal::control::Stream,
{
let buffer = buffer.into_less_safe_slice();
let mut before = Self::snapshot(buffer, key.tag_len());
before.tag.set_packet_space(space);
before.auth_tag.clear();
Self::retransmit_impl(
DecoderBufferMut::new(buffer),
space,
retransmission_packet_number,
key,
)?;
let mut after = Self::snapshot(buffer, key.tag_len());
assert_eq!(after.packet_number, retransmission_packet_number);
after.packet_number = before.packet_number;
after.auth_tag.clear();
assert_eq!(before, after);
Ok(())
}
#[inline]
#[cfg(not(debug_assertions))]
pub fn retransmit<K>(
buffer: DecoderBufferMut,
space: stream::PacketSpace,
retransmission_packet_number: VarInt,
key: &K,
) -> Result<(), DecoderError>
where
K: crypto::seal::control::Stream,
{
Self::retransmit_impl(buffer, space, retransmission_packet_number, key)
}
#[inline]
#[cfg(debug_assertions)]
fn snapshot(buffer: &mut [u8], crypto_tag_len: usize) -> Owned {
let buffer = DecoderBufferMut::new(buffer);
let (packet, _buffer) = Self::decode(buffer, (), crypto_tag_len).unwrap();
packet.into()
}
#[inline(always)]
fn retransmit_impl<K>(
buffer: DecoderBufferMut,
space: stream::PacketSpace,
retransmission_packet_number: VarInt,
key: &K,
) -> Result<(), DecoderError>
where
K: crypto::seal::control::Stream,
{
unsafe {
assume!(key.tag_len() >= 16, "tag len needs to be at least 16 bytes");
}
let (tag_slice, buffer) = buffer.decode_slice(1)?;
let tag: super::Tag = {
let tag_slice = tag_slice.into_less_safe_slice();
match space {
stream::PacketSpace::Stream => {
tag_slice[0] &= !super::Tag::IS_RECOVERY_PACKET;
}
stream::PacketSpace::Recovery => {
tag_slice[0] |= super::Tag::IS_RECOVERY_PACKET;
}
}
let tag_slice = DecoderBufferMut::new(tag_slice);
let (tag, _) = tag_slice.decode()?;
tag
};
let (_credentials, buffer) = buffer.decode::<Credentials>()?;
let (_wire_version, buffer) = buffer.decode::<WireVersion>()?;
let (_source_control_port, buffer) = buffer.decode::<u16>()?;
let (stream_id, buffer) = buffer.decode::<stream::Id>()?;
decoder_invariant!(
stream_id.is_reliable,
"only reliable streams can be retransmitted"
);
let (_source_queue_id, buffer) = if tag.has_source_queue_id() {
let (v, buffer) = buffer.decode::<VarInt>()?;
(Some(v), buffer)
} else {
(None, buffer)
};
let (original_packet_number, buffer) = buffer.decode::<VarInt>()?;
let (retransmission_packet_number_buffer, buffer) =
buffer.decode_slice(size_of::<RelativeRetransmissionOffset>())?;
let retransmission_packet_number_buffer =
retransmission_packet_number_buffer.into_less_safe_slice();
let retransmission_packet_number_buffer: &mut [u8;
size_of::<RelativeRetransmissionOffset>(
)] = retransmission_packet_number_buffer.try_into().unwrap();
let (_next_expected_control_packet, buffer) = buffer.decode::<VarInt>()?;
let (_stream_offset, buffer) = buffer.decode::<VarInt>()?;
let auth_tag_offset = buffer
.len()
.checked_sub(key.tag_len())
.ok_or(DecoderError::InvariantViolation("missing auth tag"))?;
let buffer = buffer.skip(auth_tag_offset)?;
let auth_tag = buffer.into_less_safe_slice();
let relative = retransmission_packet_number
.checked_sub(original_packet_number)
.ok_or(DecoderError::InvariantViolation(
"invalid retransmission packet number",
))?;
let relative: RelativeRetransmissionOffset = relative
.as_u64()
.try_into()
.map_err(|_| DecoderError::InvariantViolation("packet is too old"))?;
let prev_value =
RelativeRetransmissionOffset::from_be_bytes(*retransmission_packet_number_buffer);
if prev_value != 0 {
let retransmission_packet_number =
original_packet_number + VarInt::from_u32(prev_value);
key.retransmission_tag(
original_packet_number.as_u64(),
retransmission_packet_number.as_u64(),
auth_tag,
);
}
retransmission_packet_number_buffer.copy_from_slice(&relative.to_be_bytes());
key.retransmission_tag(
original_packet_number.as_u64(),
retransmission_packet_number.as_u64(),
auth_tag,
);
Ok(())
}
#[inline(always)]
pub fn decode<V: Validator>(
buffer: DecoderBufferMut,
mut validator: V,
crypto_tag_len: usize,
) -> R<Packet> {
let (
tag,
wire_version,
credentials,
source_queue_id,
stream_id,
original_packet_number,
packet_number,
retransmission_packet_number_offset,
next_expected_control_packet,
stream_offset,
final_offset,
header_len,
total_header_len,
application_header_len,
control_data_len,
payload_len,
) = {
let buffer = buffer.peek();
unsafe {
assume!(
crypto_tag_len >= 16,
"tag len needs to be at least 16 bytes"
);
}
let start_len = buffer.len();
let (tag, buffer) = buffer.decode()?;
validator.validate_tag(tag)?;
let (credentials, buffer) = buffer.decode()?;
let (wire_version, buffer) = buffer.decode()?;
let (_source_control_port, buffer) = buffer.decode::<u16>()?;
let (stream_id, buffer) = buffer.decode::<stream::Id>()?;
let (source_queue_id, buffer) = if tag.has_source_queue_id() {
let (v, buffer) = buffer.decode()?;
(Some(v), buffer)
} else {
(None, buffer)
};
let (original_packet_number, buffer) = buffer.decode::<VarInt>()?;
let retransmission_packet_number_offset = (start_len - buffer.len()) as u8;
let (packet_number, buffer) = if stream_id.is_reliable {
let (rel, buffer) = buffer.decode::<RelativeRetransmissionOffset>()?;
let rel = VarInt::from_u32(rel);
let pn = original_packet_number.checked_add(rel).ok_or(
DecoderError::InvariantViolation("retransmission packet number overflow"),
)?;
(pn, buffer)
} else {
(original_packet_number, buffer)
};
let (next_expected_control_packet, buffer) = buffer.decode()?;
let (stream_offset, buffer) = buffer.decode()?;
let (final_offset, buffer) = if tag.has_final_offset() {
let (final_offset, buffer) = buffer.decode()?;
(Some(final_offset), buffer)
} else {
(None, buffer)
};
let (control_data_len, buffer) = if tag.has_control_data() {
buffer.decode()?
} else {
(VarInt::ZERO, buffer)
};
let (payload_len, buffer) = buffer.decode::<VarInt>()?;
let (application_header_len, buffer) = if tag.has_application_header() {
let (application_header_len, buffer) = buffer.decode::<VarInt>()?;
((*application_header_len) as usize, buffer)
} else {
(0, buffer)
};
let header_len = start_len - buffer.len();
let buffer = buffer.skip(application_header_len)?;
let buffer = buffer.skip(*control_data_len as _)?;
let total_header_len = start_len - buffer.len();
let buffer = buffer.skip(*payload_len as _)?;
let buffer = buffer.skip(crypto_tag_len)?;
let _ = buffer;
(
tag,
wire_version,
credentials,
source_queue_id,
stream_id,
original_packet_number,
packet_number,
retransmission_packet_number_offset,
next_expected_control_packet,
stream_offset,
final_offset,
header_len,
total_header_len,
application_header_len,
control_data_len,
payload_len,
)
};
unsafe {
assume!(buffer.len() >= total_header_len);
}
let (header, buffer) = buffer.decode_slice(total_header_len)?;
let (application_header, control_data) = {
let buffer = header.peek();
unsafe {
assume!(buffer.len() >= header_len);
}
let buffer = buffer.skip(header_len)?;
unsafe {
assume!(buffer.len() >= application_header_len);
}
let (application_header, buffer) =
buffer.skip_into_range(application_header_len, &header)?;
unsafe {
assume!(buffer.len() >= *control_data_len as usize);
}
let (control_data, _) = buffer.skip_into_range(*control_data_len as usize, &header)?;
(application_header, control_data)
};
let header = header.into_less_safe_slice();
let (payload, buffer) = buffer.decode_slice(*payload_len as usize)?;
let payload = payload.into_less_safe_slice();
let (auth_tag, buffer) = buffer.decode_slice(crypto_tag_len)?;
let auth_tag = auth_tag.into_less_safe_slice();
let packet = Packet {
tag,
wire_version,
credentials,
source_queue_id,
stream_id,
original_packet_number,
packet_number,
retransmission_packet_number_offset,
next_expected_control_packet,
stream_offset,
final_offset,
header,
application_header,
control_data,
payload,
auth_tag,
};
Ok((packet, buffer))
}
}