// Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. // Encoding and decoding packets off the wire. use std::{ cmp::min, fmt, ops::{Deref, DerefMut, Range}, time::Instant, }; use enum_map::Enum; use log::debug; use neqo_common::{Buffer, Decoder, Encoder, hex, hex_with_len, qtrace, qwarn}; use neqo_crypto::{AeadTrait as _, random}; use strum::{EnumIter, FromRepr}; use crate::{ Error, Res, cid::{ConnectionId, ConnectionIdDecoder, ConnectionIdRef}, crypto::{CryptoDxState, CryptoStates, Epoch}, frame::{FrameEncoder as _, FrameType}, version::{self, Version}, }; /// `MIN_INITIAL_PACKET_SIZE` is the smallest packet that can be used to establish /// a new connection across all QUIC versions this server supports. pub const MIN_INITIAL_PACKET_SIZE: usize = 1200; pub const BIT_LONG: u8 = 0x80; const BIT_SHORT: u8 = 0x00; const BIT_FIXED_QUIC: u8 = 0x40; const BIT_SPIN: u8 = 0x20; const BIT_KEY_PHASE: u8 = 0x04; const HP_MASK_LONG: u8 = 0x0f; const HP_MASK_SHORT: u8 = 0x1f; const SAMPLE_SIZE: usize = 16; const SAMPLE_OFFSET: usize = 4; const MAX_PACKET_NUMBER_LEN: usize = 4; /// The length of a long packet length field. const LONG_PACKET_LENGTH_LEN: usize = 2; pub mod metadata; mod retry; pub use metadata::MetaData; pub type Number = u64; #[derive(Debug, Clone, Copy, PartialEq, Eq, Enum, EnumIter, FromRepr, Hash)] #[repr(u8)] pub enum Type { Initial = 0, ZeroRtt = 1, Handshake = 2, Retry = 3, Short, OtherVersion, VersionNegotiation, } impl Type { #[must_use] fn from_byte(t: u8, v: Version) -> Self { // Version2 adds one to the type, modulo 4 Self::from_repr(t.wrapping_sub(u8::from(v == Version::Version2)) & 3) .expect("packet type in range") } #[must_use] fn to_byte(self, v: Version) -> u8 { assert!(self.is_long(), "is a long header packet type"); // Version2 adds one to the type, modulo 4 (self as u8 + u8::from(v == Version::Version2)) & 3 } #[must_use] pub const fn is_long(self) -> bool { matches!( self, Self::Initial | Self::ZeroRtt | Self::Handshake | Self::Retry ) } } impl TryFrom for Epoch { type Error = Error; fn try_from(v: Type) -> Res { match v { Type::Initial => Ok(Self::Initial), Type::ZeroRtt => Ok(Self::ZeroRtt), Type::Handshake => Ok(Self::Handshake), Type::Short => Ok(Self::ApplicationData), _ => Err(Error::InvalidPacket), } } } impl From for Type { fn from(cs: Epoch) -> Self { match cs { Epoch::Initial => Self::Initial, Epoch::ZeroRtt => Self::ZeroRtt, Epoch::Handshake => Self::Handshake, Epoch::ApplicationData => Self::Short, } } } struct BuilderOffsets { /// The bits of the first octet that need masking. first_byte_mask: u8, /// The offset of the length field. len: usize, /// The location of the packet number field. pn: Range, } /// A packet builder that can be used to produce short packets and long packets. /// This does not produce Retry or Version Negotiation. pub struct Builder { encoder: Encoder, pn: Number, header: Range, offsets: BuilderOffsets, limit: usize, /// Whether to pad the packet before construction. padding: bool, } impl Builder> { /// The minimum useful frame size. If space is less than this, we will claim to be full. pub const MINIMUM_FRAME_SIZE: usize = 2; /// Make a retry packet. /// As this is a simple packet, this is just an associated function. /// As Retry is odd (it has to be constructed with leading bytes), /// this returns a [`Vec`] rather than building on an encoder. /// /// # Errors /// /// This will return an error if AEAD encrypt fails. pub fn retry( version: Version, dcid: &[u8], scid: &[u8], token: &[u8], odcid: &[u8], ) -> Res> { let mut encoder = Encoder::default(); encoder.encode_vec(1, odcid); let start = encoder.len(); encoder.encode_byte( BIT_LONG | BIT_FIXED_QUIC | (Type::Retry.to_byte(version) << 4) | (random::<1>()[0] & 0xf), ); encoder.encode_uint(4, version.wire_version()); encoder.encode_vec(1, dcid); encoder.encode_vec(1, scid); debug_assert_ne!(token.len(), 0); encoder.encode(token); let tag = retry::use_aead(version, |aead| { let mut buf = vec![0; aead.expansion()]; Ok(aead.encrypt(0, encoder.as_ref(), &[], &mut buf)?.to_vec()) })?; encoder.encode(&tag); let mut complete: Vec = encoder.into(); Ok(complete.split_off(start)) } /// Make a Version Negotiation packet. #[must_use] pub fn version_negotiation( dcid: &[u8], scid: &[u8], client_version: u32, versions: &[Version], ) -> Vec { let mut encoder = Encoder::default(); let mut grease = random::<4>(); // This will not include the "QUIC bit" sometimes. Intentionally. encoder.encode_byte(BIT_LONG | (grease[3] & 0x7f)); encoder.encode([0; 4]); // Zero version == VN. encoder.encode_vec(1, dcid); encoder.encode_vec(1, scid); for v in versions { encoder.encode_uint(4, v.wire_version()); } // Add a greased version, using the randomness already generated. for g in &mut grease[..3] { *g = *g & 0xf0 | 0x0a; } // Ensure our greased version does not collide with the client version // by making the last byte differ from the client initial. grease[3] = (client_version.wrapping_add(0x10) & 0xf0) as u8 | 0x0a; encoder.encode(&grease[..4]); Vec::from(encoder) } } impl Builder { /// Start building a short header packet. /// /// This doesn't fail if there isn't enough space; instead it returns a builder that /// has no available space left. This allows the caller to extract the encoder /// and any packets that might have been added before as adding a packet header is /// only likely to fail if there are other packets already written. /// /// If, after calling this method, `remaining()` returns 0, then call `abort()` to get /// the encoder back. pub fn short>( mut encoder: Encoder, key_phase: bool, dcid: Option, limit: usize, ) -> Self { let mut limit = limit; let header_start = encoder.len(); // Check that there is enough space for the header. // 5 = 1 (first byte) + 4 (packet number) if limit > encoder.len() && 5 + dcid.as_ref().map_or(0, |d| d.as_ref().len()) < limit - encoder.len() { encoder.encode_byte(BIT_SHORT | BIT_FIXED_QUIC | (u8::from(key_phase) << 2)); if let Some(dcid) = dcid { encoder.encode(dcid.as_ref()); } } else { limit = 0; } Self { encoder, pn: u64::MAX, header: header_start..header_start, offsets: BuilderOffsets { first_byte_mask: HP_MASK_SHORT, pn: 0..0, len: 0, }, limit, padding: false, } } /// Start building a long header packet. /// For an Initial packet you will need to call `initial_token()`, /// even if the token is empty. /// /// See `short()` for more on how to handle this in cases where there is no space. pub fn long, A1: AsRef<[u8]>>( mut encoder: Encoder, pt: Type, version: Version, mut dcid: Option, mut scid: Option, limit: usize, ) -> Self { let mut limit = limit; let header_start = encoder.len(); // Check that there is enough space for the header. // 11 = 1 (first byte) + 4 (version) + 2 (dcid+scid length) + 4 (packet number) if limit > encoder.len() && 11 + dcid.as_ref().map_or(0, |d| d.as_ref().len()) + scid.as_ref().map_or(0, |d| d.as_ref().len()) < limit - encoder.len() { encoder.encode_byte(BIT_LONG | BIT_FIXED_QUIC | (pt.to_byte(version) << 4)); encoder.encode_uint(4, version.wire_version()); encoder.encode_vec(1, dcid.take().as_ref().map_or(&[], AsRef::as_ref)); encoder.encode_vec(1, scid.take().as_ref().map_or(&[], AsRef::as_ref)); } else { limit = 0; } Self { encoder, pn: u64::MAX, header: header_start..header_start, offsets: BuilderOffsets { first_byte_mask: HP_MASK_LONG, pn: 0..0, len: 0, }, limit, padding: false, } } fn is_long(&self) -> bool { self.as_ref()[self.header.start] & 0x80 == BIT_LONG } /// This stores a value that can be used as a limit. This does not cause /// this limit to be enforced until encryption occurs. Prior to that, it /// is only used voluntarily by users of the builder, through `remaining()`. pub const fn set_limit(&mut self, limit: usize) { self.limit = limit; } /// Get the current limit. #[must_use] pub const fn limit(&self) -> usize { self.limit } /// How many bytes remain against the size limit for the builder. #[must_use] pub fn remaining(&self) -> usize { self.limit.saturating_sub(self.len()) } /// Returns true if the packet has no more space for frames. #[must_use] pub fn is_full(&self) -> bool { // No useful frame is smaller than 2 bytes long. self.limit < self.len() + Builder::MINIMUM_FRAME_SIZE } /// Adjust the limit to ensure that no more data is added. pub fn mark_full(&mut self) { self.limit = self.len(); } /// Mark the packet as needing padding (or not). pub const fn enable_padding(&mut self, needs_padding: bool) { self.padding = needs_padding; } /// Maybe pad with "PADDING" frames. /// Only does so if padding was needed and this is a short packet. /// Returns true if padding was added. /// /// # Panics /// /// Cannot happen. pub fn pad(&mut self) -> bool { if self.padding && !self.is_long() { self.encoder.pad_to(self.limit, FrameType::Padding.into()); true } else { false } } /// Add unpredictable values for unprotected parts of the packet. pub fn scramble(&mut self, quic_bit: bool) { debug_assert!(self.len() > self.header.start); let mask = if quic_bit { BIT_FIXED_QUIC } else { 0 } | if self.is_long() { 0 } else { BIT_SPIN }; let first = self.header.start; self.encoder.as_mut()[first] ^= random::<1>()[0] & mask; } /// For an Initial packet, encode the token. /// If you fail to do this, then you will not get a valid packet. pub fn initial_token(&mut self, token: &[u8]) { if Encoder::vvec_len(token.len()) < self.remaining() { self.encoder.encode_vvec(token); } else { self.limit = 0; } } /// Add a packet number of the given size. /// For a long header packet, this also inserts a dummy length. /// The length is filled in after calling `build`. /// Does nothing if there isn't 4 bytes available other than render this builder /// unusable; if `remaining()` returns 0 at any point, call `abort()`. /// /// # Panics /// /// This will panic if the packet number length is too large. pub fn pn(&mut self, pn: Number, pn_len: usize) { if self.remaining() < MAX_PACKET_NUMBER_LEN { self.limit = 0; return; } // Reserve space for a length in long headers. if self.is_long() { if self.remaining() < LONG_PACKET_LENGTH_LEN + MAX_PACKET_NUMBER_LEN { self.limit = 0; return; } self.offsets.len = self.encoder.len(); self.encoder.encode([0; LONG_PACKET_LENGTH_LEN]); } // This allows the input to be >4, which is absurd, but we can eat that. let pn_len = min(MAX_PACKET_NUMBER_LEN, pn_len); debug_assert_ne!(pn_len, 0); // Encode the packet number and save its offset. let pn_offset = self.encoder.len(); self.encoder.encode_uint(pn_len, pn); self.offsets.pn = pn_offset..self.encoder.len(); // Now encode the packet number length and save the header length. self.encoder.as_mut()[self.header.start] |= u8::try_from(pn_len - 1).expect("packet number length fits in u8"); self.header.end = self.encoder.len(); self.pn = pn; } #[expect(clippy::cast_possible_truncation, reason = "AND'ing makes this safe.")] fn write_len(&mut self, expansion: usize) { let len = self.encoder.len() - (self.offsets.len + LONG_PACKET_LENGTH_LEN) + expansion; self.encoder.as_mut()[self.offsets.len] = 0x40 | ((len >> 8) & 0x3f) as u8; self.encoder.as_mut()[self.offsets.len + 1] = (len & 0xff) as u8; } fn pad_for_crypto(&mut self, crypto: &CryptoDxState) { // Make sure that there is enough data in the packet. // The length of the packet number plus the payload length needs to // be at least 4 (MAX_PACKET_NUMBER_LEN) plus any amount by which // the header protection sample exceeds the AEAD expansion. // // > To ensure that sufficient data is available for sampling, packets // > are padded so that the combined lengths of the encoded packet number // > and protected payload is at least 4 bytes longer than the sample // > required for header protection. // // let crypto_pad = crypto.extra_padding(); self.encoder.pad_to( self.offsets.pn.start + MAX_PACKET_NUMBER_LEN + crypto_pad, 0, ); } /// A lot of frames here are just a collection of varints. /// This helper functions writes a frame like that safely, returning `true` if /// a frame was written. pub fn write_varint_frame(&mut self, values: &[u64]) -> bool { let write = self.remaining() >= values .iter() .map(|&v| Encoder::varint_len(v)) .sum::(); if write { if let Some((frame_type, rest)) = values.split_first() { self.encode_frame(*frame_type, |enc| { for v in rest { enc.encode_varint(*v); } }); } debug_assert!(self.len() <= self.limit()); } write } /// Build the packet and return the encoder. /// /// # Errors /// /// This will return an error if the packet is too large. pub fn build(mut self, crypto: &mut CryptoDxState) -> Res> { if self.len() > self.limit { qwarn!("Packet contents are more than the limit"); debug_assert!( false, "Builder length ({}) is larger than limit ({}).", self.len(), self.limit ); return Err(Error::Internal); } self.pad_for_crypto(crypto); if self.offsets.len > 0 { self.write_len(crypto.expansion()); } qtrace!( "Packet build pn={} hdr={} body={}", self.pn, hex(&self.encoder.as_ref()[self.header.clone()]), hex(&self.encoder.as_ref()[self.header.end..]) ); // Add space for crypto expansion. let data_end = self.encoder.len(); self.pad_to(data_end + crypto.expansion(), 0); // Calculate the mask. crypto.encrypt(self.pn, self.header.clone(), self.encoder.as_mut())?; // `decode()` already checked that `decoder.remaining() >= SAMPLE_OFFSET + SAMPLE_SIZE`. let sample_start = self.header.end + SAMPLE_OFFSET - self.offsets.pn.len(); let sample = self.encoder.as_ref()[sample_start..sample_start + SAMPLE_SIZE] .try_into() .map_err(|_| Error::Internal)?; let mask = crypto.compute_mask(sample)?; // Apply the mask. self.encoder.as_mut()[self.header.start] ^= mask[0] & self.offsets.first_byte_mask; for (i, j) in (1..=self.offsets.pn.len()).zip(self.offsets.pn) { self.encoder.as_mut()[j] ^= mask[i]; } qtrace!("Packet built {}", hex(&self.encoder)); Ok(self.encoder) } /// Abort writing of this packet and return the encoder. #[must_use] pub fn abort(mut self) -> Encoder { self.encoder.truncate(self.header.start); self.encoder } /// Work out if nothing was added after the header. #[must_use] pub fn packet_empty(&self) -> bool { self.encoder.len() == self.header.end } pub fn len(&self) -> usize { self.encoder.len() } pub fn is_empty(&self) -> bool { self.len() == 0 } } impl Deref for Builder { type Target = Encoder; fn deref(&self) -> &Self::Target { &self.encoder } } impl DerefMut for Builder { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.encoder } } impl From> for Encoder { fn from(v: Builder) -> Self { v.encoder } } /// `Public` holds information from packets that is public only. This allows for /// processing of packets prior to decryption. pub struct Public<'a> { /// The packet type. packet_type: Type, /// The recovered destination connection ID. dcid: ConnectionId, /// The source connection ID, if this is a long header packet. scid: Option, /// Any token that is included in the packet (Retry always has a token; Initial sometimes /// does). This is empty when there is no token. token: Vec, /// The size of the header, not including the packet number. header_len: usize, /// Protocol version, if present in header. version: Option, /// A reference to the entire packet, including the header. data: &'a mut [u8], } impl<'a> Public<'a> { fn opt(v: Option) -> Res { v.map_or_else(|| Err(Error::NoMoreData), |v| Ok(v)) } /// Decode the type-specific portions of a long header. /// This includes reading the length and the remainder of the packet. /// Returns a tuple of any token and the length of the header. fn decode_long( decoder: &mut Decoder<'a>, packet_type: Type, version: Version, ) -> Res<(&'a [u8], usize)> { if packet_type == Type::Retry { let header_len = decoder.offset(); let expansion = retry::expansion(version); let token = decoder .remaining() .checked_sub(expansion) .map_or(Err(Error::InvalidPacket), |v| Self::opt(decoder.decode(v)))?; if token.is_empty() { return Err(Error::InvalidPacket); } Self::opt(decoder.decode(expansion))?; return Ok((token, header_len)); } let token = if packet_type == Type::Initial { Self::opt(decoder.decode_vvec())? } else { &[] }; let len = Self::opt(decoder.decode_varint())?; let header_len = decoder.offset(); let _body = Self::opt(decoder.decode(usize::try_from(len)?))?; Ok((token, header_len)) } /// Decode the common parts of a packet. This provides minimal parsing and validation. /// Returns a tuple of a `Public` and a slice with any remainder from the datagram. /// /// # Errors /// /// This will return an error if the packet could not be decoded. pub fn decode( data: &'a mut [u8], dcid_decoder: &dyn ConnectionIdDecoder, ) -> Res<(Self, &'a mut [u8])> { Self::decode_inner(data, dcid_decoder, false) } /// Like `decode()`, but allow unknown versions. /// /// # Errors /// /// This will return an error if the packet could not be decoded. pub fn decode_server( data: &'a mut [u8], dcid_decoder: &dyn ConnectionIdDecoder, ) -> Res<(Self, &'a mut [u8])> { Self::decode_inner(data, dcid_decoder, true) } /// Decode the common parts of a packet. This provides minimal parsing and validation. /// Returns a tuple of a `Public` and a slice with any remainder from the datagram. /// /// # Errors /// /// This will return an error if the packet could not be decoded. fn decode_inner( mut data: &'a mut [u8], dcid_decoder: &dyn ConnectionIdDecoder, accept_other_version: bool, ) -> Res<(Self, &'a mut [u8])> { loop { let mut decoder = Decoder::new(data); let first = Self::opt(decoder.decode_uint::())?; if first & 0x80 == BIT_SHORT { // Conveniently, this also guarantees that there is enough space // for a connection ID of any size. if decoder.remaining() < SAMPLE_OFFSET + SAMPLE_SIZE { return Err(Error::InvalidPacket); } let dcid = Self::opt(dcid_decoder.decode_cid(&mut decoder))?.into(); if decoder.remaining() < SAMPLE_OFFSET + SAMPLE_SIZE { return Err(Error::InvalidPacket); } let header_len = decoder.offset(); return Ok(( Self { packet_type: Type::Short, dcid, scid: None, token: vec![], header_len, version: None, data, }, &mut [], )); } // Generic long header. let version = Self::opt(decoder.decode_uint())?; let dcid = ConnectionIdRef::from(Self::opt(decoder.decode_vec(1))?); let scid = ConnectionIdRef::from(Self::opt(decoder.decode_vec(1))?); // Version negotiation. match version { 0 => { return Ok(( Self { packet_type: Type::VersionNegotiation, dcid: ConnectionId::from(dcid), scid: Some(ConnectionId::from(scid)), token: vec![], header_len: decoder.offset(), version: None, data, }, &mut [], )); } Version::SCONE1 | Version::SCONE2 => { // Note: this outright ignores SCONE packets. // It does not even validate that the connection ID is correct. debug!( "Received SCONE indication {i}", i = u8::try_from((version >> 25) & 0x40)? | (first & 0x3f) ); let offset = decoder.offset(); (_, data) = std::mem::take(&mut data).split_at_mut(offset); continue; } _ => {} } // Check that this is a long header from a supported version. let Ok(version) = Version::try_from(version) else { return if accept_other_version { Ok(( Self { packet_type: Type::OtherVersion, dcid: ConnectionId::from(dcid), scid: Some(ConnectionId::from(scid)), token: vec![], header_len: decoder.offset(), version: Some(version), data, }, &mut [], )) } else { Err(Error::InvalidPacket) }; }; if dcid.len() > ConnectionId::MAX_LEN || scid.len() > ConnectionId::MAX_LEN { return Err(Error::InvalidPacket); } let packet_type = Type::from_byte((first >> 4) & 3, version); // The type-specific code includes a token. This consumes the remainder of the packet. let (token, header_len) = Public::decode_long(&mut decoder, packet_type, version)?; let token = token.to_vec(); let dcid = ConnectionId::from(dcid); let scid = Some(ConnectionId::from(scid)); let (data, remainder) = data.split_at_mut(decoder.offset()); return Ok(( Self { packet_type, dcid, scid, token, header_len, version: Some(version.wire_version()), data, }, remainder, )); } // end loop } /// Validate the given packet as though it were a retry. #[must_use] pub fn is_valid_retry(&self, odcid: &ConnectionId) -> bool { if self.packet_type != Type::Retry { return false; } let Some(version) = self.version() else { return false; }; let expansion = retry::expansion(version); if self.data.len() <= expansion { return false; } let (header, tag) = self.data.split_at(self.data.len() - expansion); let mut encoder = Encoder::with_capacity(self.data.len()); encoder.encode_vec(1, odcid); encoder.encode(header); retry::use_aead(version, |aead| { let mut buf = vec![0; expansion]; Ok(aead.decrypt(0, encoder.as_ref(), tag, &mut buf)?.is_empty()) }) .unwrap_or(false) } #[must_use] pub fn is_valid_initial(&self) -> bool { // Packet has to be an initial, with a DCID of 8 bytes, or a token. // Note: the Server class validates the token and checks the length. self.packet_type == Type::Initial && (self.dcid().len() >= 8 || !self.token.is_empty()) } #[must_use] pub const fn packet_type(&self) -> Type { self.packet_type } #[must_use] pub fn dcid(&self) -> ConnectionIdRef<'_> { self.dcid.as_cid_ref() } /// # Panics /// /// This will panic if called for a short header packet. #[must_use] pub fn scid(&self) -> ConnectionIdRef<'_> { self.scid .as_ref() .expect("should only be called for long header packets") .as_cid_ref() } #[must_use] pub fn token(&self) -> &[u8] { &self.token } #[must_use] pub fn version(&self) -> Option { Version::try_from(self.version?).ok() } #[must_use] pub fn wire_version(&self) -> version::Wire { debug_assert!(self.version.is_some()); self.version.unwrap_or(0) } #[allow( clippy::allow_attributes, clippy::len_without_is_empty, reason = "Is OK here." )] #[must_use] pub const fn len(&self) -> usize { self.data.len() } #[cfg(feature = "build-fuzzing-corpus")] #[must_use] pub const fn data(&self) -> &[u8] { self.data } const fn decode_pn(expected: Number, pn: u64, w: usize) -> Number { let window = 1_u64 << (w * 8); let candidate = (expected & !(window - 1)) | pn; if candidate + (window / 2) <= expected { candidate + window } else if candidate > expected + (window / 2) { match candidate.checked_sub(window) { Some(pn_sub) => pn_sub, None => candidate, } } else { candidate } } /// Decrypt the header of the packet. fn decrypt_header(&mut self, crypto: &CryptoDxState) -> Res<(bool, Number, Range)> { debug_assert_ne!(self.packet_type, Type::Retry); debug_assert_ne!(self.packet_type, Type::VersionNegotiation); let sample_offset = self.header_len + SAMPLE_OFFSET; let sample = self .data .get(sample_offset..(sample_offset + SAMPLE_SIZE)) .ok_or(Error::NoMoreData)?; let sample: &[u8; SAMPLE_SIZE] = sample.try_into()?; qtrace!( "{:?} unmask hdr={}", crypto.version(), hex(&self.data[..sample_offset]) ); let mask = crypto.compute_mask(sample)?; // Un-mask the leading byte. let bits = if self.packet_type == Type::Short { HP_MASK_SHORT } else { HP_MASK_LONG }; assert!(!self.data.is_empty()); let first_byte = self.data[0] ^ (mask[0] & bits); let mut hdrbytes = 0..self.header_len + 4; self.data[0] = first_byte; // Unmask the PN. let mut pn_encoded: u64 = 0; let mut pn_bytes = self.data[self.header_len..self.header_len + MAX_PACKET_NUMBER_LEN].to_vec(); for i in 0..MAX_PACKET_NUMBER_LEN { pn_bytes[i] ^= mask[1 + i]; pn_encoded <<= 8; pn_encoded += u64::from(pn_bytes[i]); } // Now decode the packet number length and apply it, hopefully in constant time. let pn_len = usize::from((first_byte & 0x3) + 1); self.data[self.header_len..self.header_len + pn_len].copy_from_slice(&pn_bytes[..pn_len]); hdrbytes.end = self.header_len + pn_len; pn_encoded >>= 8 * (MAX_PACKET_NUMBER_LEN - pn_len); qtrace!("unmasked hdr={}", hex(&self.data[hdrbytes.clone()])); let key_phase = self.packet_type == Type::Short && (first_byte & BIT_KEY_PHASE) == BIT_KEY_PHASE; let pn = Self::decode_pn(crypto.next_pn(), pn_encoded, pn_len); Ok((key_phase, pn, hdrbytes)) } /// # Errors /// /// This will return an error if the packet cannot be decrypted. pub fn decrypt( mut self, crypto: &mut CryptoStates, release_at: Instant, ) -> Result, DecryptionError<'a>> { let epoch = match self.packet_type.try_into() { Ok(e) => e, Err(e) => return Err((self, e).into()), }; // When we don't have a version, the crypto code doesn't need a version // for lookup, so use the default, but fix it up if decryption succeeds. let version = self.version().unwrap_or_default(); // This has to work in two stages because we need to remove header protection // before picking the keys to use. let Some(rx) = crypto.rx_hp(version, epoch) else { if crypto.rx_pending(epoch) { return Err((self, Error::KeysPending(epoch)).into()); } qtrace!("keys for {epoch:?} already discarded"); return Err((self, Error::KeysDiscarded(epoch)).into()); }; // Note that this will dump early, which creates a side-channel. // This is OK in this case because we the only reason this can // fail is if the cryptographic module is bad or the packet is // too small (which is public information). let (key_phase, pn, header) = match self.decrypt_header(rx) { Ok(v) => v, Err(e) => return Err((self, e).into()), }; let Some(rx) = crypto.rx(version, epoch, key_phase) else { return Err((self, Error::Decrypt).into()); }; let version = rx.version(); // Version fixup; see above. let header_end = header.end; let payload_len = match rx.decrypt(pn, header, self.data) { Ok(v) => v, Err(e) => return Err((self, e).into()), }; let data = &self.data[header_end..header_end + payload_len]; // Helper for late errors where `self` is partially borrowed. let make_err = |error| DecryptionError { error, data: self.data, dcid: self.dcid.clone(), packet_type: self.packet_type, }; // If this is the first packet ever successfully decrypted // using `rx`, make sure to initiate a key update. if rx.needs_update() { crypto.key_update_received(release_at).map_err(make_err)?; } crypto.check_pn_overlap().map_err(make_err)?; Ok(Decrypted { version, pt: self.packet_type, pn, dcid: self.dcid, scid: self.scid, data, }) } /// # Errors /// /// This will return an error if the packet is not a version negotiation packet /// or if the versions cannot be decoded. pub fn supported_versions(&self) -> Res> { if self.packet_type != Type::VersionNegotiation { return Err(Error::InvalidPacket); } let mut decoder = Decoder::new(&self.data[self.header_len..]); let mut res = Vec::new(); while decoder.remaining() > 0 { let version = Self::opt(decoder.decode_uint::())?; res.push(version); } Ok(res) } } impl fmt::Debug for Public<'_> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!( f, "{:?}: {} {}", self.packet_type(), hex_with_len(&self.data[..self.header_len]), hex_with_len(&self.data[self.header_len..]) ) } } /// Error information from a failed decryption attempt. /// Contains minimal packet information needed for error handling. #[derive(Debug)] pub struct DecryptionError<'a> { /// The error that occurred. pub error: Error, /// The original packet data (unchanged since decryption failed). pub data: &'a [u8], /// The destination connection ID. pub dcid: ConnectionId, /// The packet type. pub packet_type: Type, } impl<'a> From<(Public<'a>, Error)> for DecryptionError<'a> { fn from((packet, error): (Public<'a>, Error)) -> Self { Self { error, data: packet.data, dcid: packet.dcid, packet_type: packet.packet_type, } } } impl DecryptionError<'_> { #[must_use] pub const fn len(&self) -> usize { self.data.len() } // The packet module is made public when the `bench` feature is enabled or we're fuzzing, which // triggers the `clippy::len_without_is_empty` lint without this. #[cfg(any(fuzzing, feature = "bench"))] #[must_use] pub const fn is_empty(&self) -> bool { self.data.is_empty() } #[must_use] pub const fn packet_type(&self) -> Type { self.packet_type } } pub struct Decrypted<'a> { version: Version, pt: Type, pn: Number, data: &'a [u8], dcid: ConnectionId, scid: Option, } impl Decrypted<'_> { #[must_use] pub const fn version(&self) -> Version { self.version } #[must_use] pub const fn packet_type(&self) -> Type { self.pt } #[must_use] pub const fn pn(&self) -> Number { self.pn } #[must_use] pub fn dcid(&self) -> ConnectionIdRef<'_> { self.dcid.as_cid_ref() } /// # Panics /// /// This will panic if called for a short header packet. #[must_use] pub fn scid(&self) -> ConnectionIdRef<'_> { self.scid .as_ref() .expect("should only be called for long header packets") .as_cid_ref() } } impl Deref for Decrypted<'_> { type Target = [u8]; fn deref(&self) -> &Self::Target { self.data } } #[cfg(test)] pub const LIMIT: usize = 2048; #[cfg(all(test, not(feature = "disable-encryption")))] #[cfg(test)] #[cfg_attr(coverage_nightly, coverage(off))] mod tests { use neqo_common::Encoder; use test_fixture::{fixture_init, now}; use crate::{ ConnectionId, EmptyConnectionIdGenerator, Error, RandomConnectionIdGenerator, Version, crypto::{CryptoDxState, CryptoStates}, packet::{self, Builder, Public, Type}, }; const CLIENT_CID: &[u8] = &[0x83, 0x94, 0xc8, 0xf0, 0x3e, 0x51, 0x57, 0x08]; const SERVER_CID: &[u8] = &[0xf0, 0x67, 0xa5, 0x50, 0x2a, 0x42, 0x62, 0xb5]; /// This is a connection ID manager, which is only used for decoding short header packets. const fn cid_mgr() -> RandomConnectionIdGenerator { RandomConnectionIdGenerator::new(SERVER_CID.len()) } const SAMPLE_INITIAL_PAYLOAD: &[u8] = &[ 0x02, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x40, 0x5a, 0x02, 0x00, 0x00, 0x56, 0x03, 0x03, 0xee, 0xfc, 0xe7, 0xf7, 0xb3, 0x7b, 0xa1, 0xd1, 0x63, 0x2e, 0x96, 0x67, 0x78, 0x25, 0xdd, 0xf7, 0x39, 0x88, 0xcf, 0xc7, 0x98, 0x25, 0xdf, 0x56, 0x6d, 0xc5, 0x43, 0x0b, 0x9a, 0x04, 0x5a, 0x12, 0x00, 0x13, 0x01, 0x00, 0x00, 0x2e, 0x00, 0x33, 0x00, 0x24, 0x00, 0x1d, 0x00, 0x20, 0x9d, 0x3c, 0x94, 0x0d, 0x89, 0x69, 0x0b, 0x84, 0xd0, 0x8a, 0x60, 0x99, 0x3c, 0x14, 0x4e, 0xca, 0x68, 0x4d, 0x10, 0x81, 0x28, 0x7c, 0x83, 0x4d, 0x53, 0x11, 0xbc, 0xf3, 0x2b, 0xb9, 0xda, 0x1a, 0x00, 0x2b, 0x00, 0x02, 0x03, 0x04, ]; const SAMPLE_INITIAL: &[u8] = &[ 0xcf, 0x00, 0x00, 0x00, 0x01, 0x00, 0x08, 0xf0, 0x67, 0xa5, 0x50, 0x2a, 0x42, 0x62, 0xb5, 0x00, 0x40, 0x75, 0xc0, 0xd9, 0x5a, 0x48, 0x2c, 0xd0, 0x99, 0x1c, 0xd2, 0x5b, 0x0a, 0xac, 0x40, 0x6a, 0x58, 0x16, 0xb6, 0x39, 0x41, 0x00, 0xf3, 0x7a, 0x1c, 0x69, 0x79, 0x75, 0x54, 0x78, 0x0b, 0xb3, 0x8c, 0xc5, 0xa9, 0x9f, 0x5e, 0xde, 0x4c, 0xf7, 0x3c, 0x3e, 0xc2, 0x49, 0x3a, 0x18, 0x39, 0xb3, 0xdb, 0xcb, 0xa3, 0xf6, 0xea, 0x46, 0xc5, 0xb7, 0x68, 0x4d, 0xf3, 0x54, 0x8e, 0x7d, 0xde, 0xb9, 0xc3, 0xbf, 0x9c, 0x73, 0xcc, 0x3f, 0x3b, 0xde, 0xd7, 0x4b, 0x56, 0x2b, 0xfb, 0x19, 0xfb, 0x84, 0x02, 0x2f, 0x8e, 0xf4, 0xcd, 0xd9, 0x37, 0x95, 0xd7, 0x7d, 0x06, 0xed, 0xbb, 0x7a, 0xaf, 0x2f, 0x58, 0x89, 0x18, 0x50, 0xab, 0xbd, 0xca, 0x3d, 0x20, 0x39, 0x8c, 0x27, 0x64, 0x56, 0xcb, 0xc4, 0x21, 0x58, 0x40, 0x7d, 0xd0, 0x74, 0xee, ]; #[test] fn sample_server_initial() { fixture_init(); let mut prot = CryptoDxState::test_default(); // The spec uses PN=1, but our crypto refuses to skip packet numbers. // So burn an encryption: let mut burn = [0; 16]; prot.encrypt(0, 0..0, &mut burn).expect("burn OK"); assert_eq!(burn.len(), prot.expansion()); let mut builder = Builder::long( Encoder::default(), Type::Initial, Version::default(), None::<&[u8]>, Some(ConnectionId::from(SERVER_CID)), packet::LIMIT, ); builder.initial_token(&[]); builder.pn(1, 2); builder.encode(SAMPLE_INITIAL_PAYLOAD); let packet = builder.build(&mut prot).expect("build"); assert_eq!(packet.as_ref(), SAMPLE_INITIAL); } #[test] fn decrypt_initial() { const EXTRA: &[u8] = &[0xce; 33]; fixture_init(); let mut padded = SAMPLE_INITIAL.to_vec(); padded.extend_from_slice(EXTRA); let (packet, remainder) = Public::decode(&mut padded, &cid_mgr()).unwrap(); assert_eq!(packet.packet_type(), Type::Initial); assert_eq!(&packet.dcid()[..], &[] as &[u8]); assert_eq!(&packet.scid()[..], SERVER_CID); assert!(packet.token().is_empty()); assert_eq!(remainder, EXTRA); let decrypted = packet .decrypt(&mut CryptoStates::test_default(), now()) .unwrap(); assert_eq!(decrypted.pn(), 1); } #[test] fn disallow_long_dcid() { let mut enc = Encoder::default(); enc.encode_byte(packet::BIT_LONG | packet::BIT_FIXED_QUIC); enc.encode_uint(4, Version::default().wire_version()); enc.encode_vec(1, &[0x00; ConnectionId::MAX_LEN + 1]); enc.encode_vec(1, &[]); enc.encode([0xff; 40]); // junk assert!(Public::decode(enc.as_mut(), &cid_mgr()).is_err()); } #[test] fn disallow_long_scid() { let mut enc = Encoder::default(); enc.encode_byte(packet::BIT_LONG | packet::BIT_FIXED_QUIC); enc.encode_uint(4, Version::default().wire_version()); enc.encode_vec(1, &[]); enc.encode_vec(1, &[0x00; ConnectionId::MAX_LEN + 2]); enc.encode([0xff; 40]); // junk assert!(Public::decode(enc.as_mut(), &cid_mgr()).is_err()); } const SAMPLE_SHORT: &[u8] = &[ 0x40, 0xf0, 0x67, 0xa5, 0x50, 0x2a, 0x42, 0x62, 0xb5, 0xf4, 0xa8, 0x30, 0x39, 0xc4, 0x7d, 0x99, 0xe3, 0x94, 0x1c, 0x9b, 0xb9, 0x7a, 0x30, 0x1d, 0xd5, 0x8f, 0xf3, 0xdd, 0xa9, ]; const SAMPLE_SHORT_PAYLOAD: &[u8] = &[0; 3]; #[test] fn build_short() { fixture_init(); assert!(!Type::Short.is_long()); let mut builder = Builder::short( Encoder::default(), true, Some(ConnectionId::from(SERVER_CID)), packet::LIMIT, ); assert!(!builder.is_empty()); builder.pn(0, 1); builder.encode(SAMPLE_SHORT_PAYLOAD); // Enough payload for sampling. let packet = builder .build(&mut CryptoDxState::test_default()) .expect("build"); assert_eq!(packet.as_ref(), SAMPLE_SHORT); } #[test] fn scramble_short() { fixture_init(); let mut firsts = Vec::new(); for _ in 0..64 { let mut builder = Builder::short( Encoder::default(), true, Some(ConnectionId::from(SERVER_CID)), packet::LIMIT, ); builder.scramble(true); builder.pn(0, 1); firsts.push(builder.as_ref()[0]); } let is_set = |bit| move |v| v & bit == bit; // There should be at least one value with the QUIC bit set: assert!(firsts.iter().any(is_set(packet::BIT_FIXED_QUIC))); // ... but not all: assert!(!firsts.iter().all(is_set(packet::BIT_FIXED_QUIC))); // There should be at least one value with the spin bit set: assert!(firsts.iter().any(is_set(packet::BIT_SPIN))); // ... but not all: assert!(!firsts.iter().all(is_set(packet::BIT_SPIN))); } fn decode_sample_short(packet: &[u8]) { fixture_init(); let mut sample_short = packet.to_vec(); let (packet, remainder) = Public::decode(&mut sample_short, &cid_mgr()).unwrap(); assert_eq!(packet.packet_type(), Type::Short); assert!(remainder.is_empty()); let decrypted = packet .decrypt(&mut CryptoStates::test_default(), now()) .unwrap(); assert_eq!(&decrypted[..], SAMPLE_SHORT_PAYLOAD); } #[test] fn decode_short() { decode_sample_short(SAMPLE_SHORT); } /// By telling the decoder that the connection ID is shorter than it really is, we get a /// decryption error. #[test] fn decode_short_bad_cid() { fixture_init(); let mut sample_short = SAMPLE_SHORT.to_vec(); let (packet, remainder) = Public::decode( &mut sample_short, &RandomConnectionIdGenerator::new(SERVER_CID.len() - 1), ) .unwrap(); assert_eq!(packet.packet_type(), Type::Short); assert!(remainder.is_empty()); assert!( packet .decrypt(&mut CryptoStates::test_default(), now()) .is_err() ); } /// Saying that the connection ID is longer causes the initial decode to fail. #[test] fn decode_short_long_cid() { let mut sample_short = SAMPLE_SHORT.to_vec(); assert!( Public::decode( &mut sample_short, &RandomConnectionIdGenerator::new(SERVER_CID.len() + 1) ) .is_err() ); } #[test] fn build_two() { fixture_init(); let mut prot = CryptoDxState::test_default(); let mut builder = Builder::long( Encoder::default(), Type::Handshake, Version::default(), Some(ConnectionId::from(SERVER_CID)), Some(ConnectionId::from(CLIENT_CID)), packet::LIMIT, ); builder.pn(0, 1); builder.encode([0; 3]); let encoder = builder.build(&mut prot).expect("build"); assert_eq!(encoder.len(), 45); let first = encoder.clone(); let mut builder = Builder::short( encoder, false, Some(ConnectionId::from(SERVER_CID)), packet::LIMIT, ); builder.pn(1, 3); builder.encode([0]); // Minimal size (packet number is big enough). let encoder = builder.build(&mut prot).expect("build"); assert_eq!( first.as_ref(), &encoder.as_ref()[..first.len()], "the first packet should be a prefix" ); assert_eq!(encoder.len(), 45 + 29); } #[test] fn build_long() { const EXPECTED: &[u8] = &[ 0xe4, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x40, 0x14, 0xfb, 0xa9, 0x32, 0x3a, 0xf8, 0xbb, 0x18, 0x63, 0xc6, 0xbd, 0x78, 0x0e, 0xba, 0x0c, 0x98, 0x65, 0x58, 0xc9, 0x62, 0x31, ]; fixture_init(); let mut builder = Builder::long( Encoder::default(), Type::Handshake, Version::default(), None::<&[u8]>, None::<&[u8]>, packet::LIMIT, ); builder.pn(0, 1); builder.encode([1, 2, 3]); let packet = builder.build(&mut CryptoDxState::test_default()).unwrap(); assert_eq!(packet.as_ref(), EXPECTED); } #[test] fn scramble_long() { fixture_init(); let mut found_unset = false; let mut found_set = false; for _ in 1..64 { let mut builder = Builder::long( Encoder::default(), Type::Handshake, Version::default(), None::<&[u8]>, None::<&[u8]>, packet::LIMIT, ); builder.pn(0, 1); builder.scramble(true); if (builder.as_ref()[0] & packet::BIT_FIXED_QUIC) == 0 { found_unset = true; } else { found_set = true; } } assert!(found_unset); assert!(found_set); } #[test] fn build_abort() { let mut builder = Builder::long( Encoder::default(), Type::Initial, Version::default(), None::<&[u8]>, Some(ConnectionId::from(SERVER_CID)), packet::LIMIT, ); assert_ne!(builder.remaining(), 0); builder.initial_token(&[]); assert_ne!(builder.remaining(), 0); builder.pn(1, 2); assert_ne!(builder.remaining(), 0); let encoder = builder.abort(); assert!(encoder.is_empty()); } #[test] fn build_insufficient_space() { const LIMIT: usize = 100; // Pad first short packet, but not up to the full limit. Leave enough // space for the AEAD expansion and some extra of the second long // packet, but not for an entire long header. const LIMIT_FIRST: usize = LIMIT - 25; fixture_init(); let mut builder = Builder::short( Encoder::default(), true, Some(ConnectionId::from(SERVER_CID)), LIMIT_FIRST, ); builder.pn(0, 1); builder.enable_padding(true); assert!(builder.pad()); let encoder = builder.build(&mut CryptoDxState::test_default()).unwrap(); let encoder_copy = encoder.clone(); let limit_second = LIMIT - encoder.len(); let builder = Builder::long( encoder, Type::Initial, Version::default(), Some(ConnectionId::from(SERVER_CID)), Some(ConnectionId::from(SERVER_CID)), limit_second, ); assert_eq!(builder.remaining(), 0); assert_eq!(builder.abort(), encoder_copy); } /// Given an encoder that already contains some QUIC packet(s), i.e. is /// filled close to the MTU, attempt to use the remaining insufficient space /// for another QUIC packet. /// /// Details in . #[test] fn build_insufficient_space_for_dummy_length_and_pn() { const MTU: usize = 1280; const FIRST_QUIC_PACKET: usize = 1236; fixture_init(); let crypto = CryptoDxState::test_default(); let mut encoder = Encoder::default(); encoder.pad_to(FIRST_QUIC_PACKET, 0); // Builder::long should add 1 (first byte) + 4 (version) + 2 // (dcid+scid length) + 8 (dcid) + 8 (scid) = 23 bytes. let mut builder = Builder::long( encoder, Type::Initial, Version::default(), Some(SERVER_CID), Some(CLIENT_CID), MTU - crypto.expansion(), ); assert_eq!(builder.len() - FIRST_QUIC_PACKET, 23); // Given the FIRST_QUIC_PACKET and the partial header from // Builder::long, the builder should have 5 bytes remaining. assert_eq!(builder.remaining(), 5); // Builder::pn needs 2 bytes for the dummy packet length and 4 bytes for // the maximum packet number, but only 5 bytes remain. The builder // should now be full and needs to be aborted. builder.pn(0, 1); assert!(builder.is_full()); } #[test] #[cfg_attr( debug_assertions, should_panic(expected = "Builder length (30) is larger than limit (20)") )] fn build_insufficient_space_error() { const SMALL_LIMIT: usize = 20; fixture_init(); // Set up a builder with a very small limit let mut builder = Builder::short( Encoder::default(), false, Some(ConnectionId::from(SERVER_CID)), SMALL_LIMIT, ); builder.pn(0, 1); // Add more data than the limit allows. This will exceed the limit when // combined with header. let large_payload = vec![0u8; SMALL_LIMIT]; builder.encode(&large_payload); // Verify that the length exceeds the limit. assert!(builder.is_full()); // Building should trigger the debug_assert in debug mode, returning // internal error in release mode. assert_eq!( builder.build(&mut CryptoDxState::test_default()), Err(Error::Internal) ); } const SAMPLE_RETRY_V2: &[u8] = &[ 0xcf, 0x6b, 0x33, 0x43, 0xcf, 0x00, 0x08, 0xf0, 0x67, 0xa5, 0x50, 0x2a, 0x42, 0x62, 0xb5, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0xc8, 0x64, 0x6c, 0xe8, 0xbf, 0xe3, 0x39, 0x52, 0xd9, 0x55, 0x54, 0x36, 0x65, 0xdc, 0xc7, 0xb6, ]; const SAMPLE_RETRY_V1: &[u8] = &[ 0xff, 0x00, 0x00, 0x00, 0x01, 0x00, 0x08, 0xf0, 0x67, 0xa5, 0x50, 0x2a, 0x42, 0x62, 0xb5, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x04, 0xa2, 0x65, 0xba, 0x2e, 0xff, 0x4d, 0x82, 0x90, 0x58, 0xfb, 0x3f, 0x0f, 0x24, 0x96, 0xba, ]; const SAMPLE_RETRY_29: &[u8] = &[ 0xff, 0xff, 0x00, 0x00, 0x1d, 0x00, 0x08, 0xf0, 0x67, 0xa5, 0x50, 0x2a, 0x42, 0x62, 0xb5, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0xd1, 0x69, 0x26, 0xd8, 0x1f, 0x6f, 0x9c, 0xa2, 0x95, 0x3a, 0x8a, 0xa4, 0x57, 0x5e, 0x1e, 0x49, ]; const RETRY_TOKEN: &[u8] = b"token"; #[cfg(test)] fn build_retry_single(version: Version, sample_retry: &[u8]) { fixture_init(); let mut retry = Builder::retry(version, &[], SERVER_CID, RETRY_TOKEN, CLIENT_CID).unwrap(); let (packet, remainder) = Public::decode(&mut retry, &cid_mgr()).unwrap(); assert!(packet.is_valid_retry(&ConnectionId::from(CLIENT_CID))); assert!(remainder.is_empty()); // The builder adds randomness, which makes expectations hard. // So only do a full check when that randomness matches up. if retry[0] == sample_retry[0] { assert_eq!(&retry, &sample_retry); } else { // Otherwise, just check that the header is OK. assert_eq!(retry[0] & 0xf0, 0xc0 | (Type::Retry.to_byte(version) << 4)); let header_range = 1..retry.len() - 16; assert_eq!(&retry[header_range.clone()], &sample_retry[header_range]); } } #[test] fn build_retry_v2() { build_retry_single(Version::Version2, SAMPLE_RETRY_V2); } #[test] fn build_retry_v1() { build_retry_single(Version::Version1, SAMPLE_RETRY_V1); } #[test] fn build_retry_29() { build_retry_single(Version::Draft29, SAMPLE_RETRY_29); } #[test] fn build_retry_multiple() { // Run the build_retry test a few times. // Odds are approximately 1 in 8 that the full comparison doesn't happen // for a given version. for _ in 0..32 { build_retry_v2(); build_retry_v1(); build_retry_29(); } } fn decode_retry(version: Version, sample_retry: &mut [u8]) { fixture_init(); let (packet, remainder) = Public::decode(sample_retry, &RandomConnectionIdGenerator::new(5)).unwrap(); assert!(packet.is_valid_retry(&ConnectionId::from(CLIENT_CID))); assert_eq!(Some(version), packet.version()); assert!(packet.dcid().is_empty()); assert_eq!(&packet.scid()[..], SERVER_CID); assert_eq!(packet.token(), RETRY_TOKEN); assert!(remainder.is_empty()); } #[test] fn decode_retry_v2() { let mut sample_retry_v2 = SAMPLE_RETRY_V2.to_vec(); decode_retry(Version::Version2, &mut sample_retry_v2); } #[test] fn decode_retry_v1() { let mut sample_retry_v1 = SAMPLE_RETRY_V1.to_vec(); decode_retry(Version::Version1, &mut sample_retry_v1); } #[test] fn decode_retry_29() { let mut sample_retry_29 = SAMPLE_RETRY_29.to_vec(); decode_retry(Version::Draft29, &mut sample_retry_29); } /// Check some packets that are clearly not valid Retry packets. #[test] fn invalid_retry() { fixture_init(); let cid_mgr = RandomConnectionIdGenerator::new(5); let odcid = ConnectionId::from(CLIENT_CID); assert!(Public::decode(&mut [], &cid_mgr).is_err()); let mut sample_retry_v1 = SAMPLE_RETRY_V1.to_vec(); let (packet, remainder) = Public::decode(&mut sample_retry_v1, &cid_mgr).unwrap(); assert!(remainder.is_empty()); assert!(packet.is_valid_retry(&odcid)); let mut damaged_retry = SAMPLE_RETRY_V1.to_vec(); let last = damaged_retry.len() - 1; damaged_retry[last] ^= 0b100_0010; // 66 let (packet, remainder) = Public::decode(&mut damaged_retry, &cid_mgr).unwrap(); assert!(remainder.is_empty()); assert!(!packet.is_valid_retry(&odcid)); damaged_retry.truncate(last); let (packet, remainder) = Public::decode(&mut damaged_retry, &cid_mgr).unwrap(); assert!(remainder.is_empty()); assert!(!packet.is_valid_retry(&odcid)); // An invalid token should be rejected sooner. damaged_retry.truncate(last - 4); assert!(Public::decode(&mut damaged_retry, &cid_mgr).is_err()); damaged_retry.truncate(last - 1); assert!(Public::decode(&mut damaged_retry, &cid_mgr).is_err()); } const SAMPLE_VN: &[u8] = &[ 0x80, 0x00, 0x00, 0x00, 0x00, 0x08, 0xf0, 0x67, 0xa5, 0x50, 0x2a, 0x42, 0x62, 0xb5, 0x08, 0x83, 0x94, 0xc8, 0xf0, 0x3e, 0x51, 0x57, 0x08, 0x6b, 0x33, 0x43, 0xcf, 0x00, 0x00, 0x00, 0x01, 0xff, 0x00, 0x00, 0x1d, 0x0a, 0x0a, 0x0a, 0x0a, ]; #[test] fn build_vn() { fixture_init(); let mut vn = Builder::version_negotiation(SERVER_CID, CLIENT_CID, 0x0a0a_0a0a, &Version::all()); // Erase randomness from greasing... assert_eq!(vn.len(), SAMPLE_VN.len()); vn[0] &= 0x80; for v in vn.iter_mut().skip(SAMPLE_VN.len() - 4) { *v &= 0x0f; } assert_eq!(&vn, &SAMPLE_VN); } #[test] fn vn_do_not_repeat_client_grease() { fixture_init(); let vn = Builder::version_negotiation(SERVER_CID, CLIENT_CID, 0x0a0a_0a0a, &Version::all()); assert_ne!(&vn[SAMPLE_VN.len() - 4..], &[0x0a, 0x0a, 0x0a, 0x0a]); } #[test] fn parse_vn() { let mut sample_vn = SAMPLE_VN.to_vec(); let (packet, remainder) = Public::decode(&mut sample_vn, &EmptyConnectionIdGenerator::default()).unwrap(); assert!(remainder.is_empty()); assert_eq!(&packet.dcid[..], SERVER_CID); assert!(packet.scid.is_some()); assert_eq!(&packet.scid.unwrap()[..], CLIENT_CID); } /// A Version Negotiation packet can have a long connection ID. #[test] fn parse_vn_big_cid() { const BIG_DCID: &[u8] = &[0x44; ConnectionId::MAX_LEN + 1]; const BIG_SCID: &[u8] = &[0xee; 255]; let mut enc = Encoder::from(&[0xff, 0x00, 0x00, 0x00, 0x00][..]); enc.encode_vec(1, BIG_DCID); enc.encode_vec(1, BIG_SCID); enc.encode_uint(4, 0x1a2a_3a4a_u64); enc.encode_uint(4, Version::default().wire_version()); enc.encode_uint(4, 0x5a6a_7a8a_u64); let (packet, remainder) = Public::decode(enc.as_mut(), &EmptyConnectionIdGenerator::default()).unwrap(); assert!(remainder.is_empty()); assert_eq!(&packet.dcid[..], BIG_DCID); assert!(packet.scid.is_some()); assert_eq!(&packet.scid.unwrap()[..], BIG_SCID); } #[test] fn decode_pn() { // When the expected value is low, the value doesn't go negative. assert_eq!(Public::decode_pn(0, 0, 1), 0); assert_eq!(Public::decode_pn(0, 0xff, 1), 0xff); assert_eq!(Public::decode_pn(10, 0, 1), 0); assert_eq!(Public::decode_pn(0x7f, 0, 1), 0); assert_eq!(Public::decode_pn(0x80, 0, 1), 0x100); assert_eq!(Public::decode_pn(0x80, 2, 1), 2); assert_eq!(Public::decode_pn(0x80, 0xff, 1), 0xff); assert_eq!(Public::decode_pn(0x7ff, 0xfe, 1), 0x7fe); // This is invalid by spec, as we are expected to check for overflow around 2^62-1, // but we don't need to worry about overflow // and hitting this is basically impossible in practice. assert_eq!( Public::decode_pn(0x3fff_ffff_ffff_ffff, 2, 4), 0x4000_0000_0000_0002 ); } #[test] fn chacha20_sample() { const PACKET: &[u8] = &[ 0x4c, 0xfe, 0x41, 0x89, 0x65, 0x5e, 0x5c, 0xd5, 0x5c, 0x41, 0xf6, 0x90, 0x80, 0x57, 0x5d, 0x79, 0x99, 0xc2, 0x5a, 0x5b, 0xfb, ]; fixture_init(); let mut packet = PACKET.to_vec(); let (packet, slice) = Public::decode(&mut packet, &EmptyConnectionIdGenerator::default()).unwrap(); assert!(slice.is_empty()); let decrypted = packet .decrypt(&mut CryptoStates::test_chacha(), now()) .unwrap(); assert_eq!(decrypted.packet_type(), Type::Short); assert_eq!(decrypted.pn(), 654_360_564); assert_eq!(&decrypted[..], &[0x01]); } #[test] fn decode_empty() { neqo_crypto::init().unwrap(); let res = Public::decode(&mut [], &EmptyConnectionIdGenerator::default()); assert!(res.is_err()); } #[test] fn decode_too_short() { neqo_crypto::init().unwrap(); let mut data = [179, 255, 0, 0, 29, 0, 0]; let res = Public::decode(&mut data, &EmptyConnectionIdGenerator::default()); assert!(res.is_err()); } #[test] fn ignore_scone() { /// A minimal SCONE packet. const SCONE1: &[u8] = &[0xff, 0x6f, 0x7d, 0xc0, 0xfd, 0x00, 0x00]; /// A generous SCONE packet, including a connection ID that is too long for QUIC v1/v2. const SCONE2: &[u8] = &[ 0xff, 0xef, 0x7d, 0xc0, 0xfd, 0x01, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]; let mut scone1 = SCONE1.to_vec(); scone1.extend_from_slice(SAMPLE_SHORT); decode_sample_short(&scone1); let mut scone2 = SCONE2.to_vec(); scone2.extend_from_slice(SAMPLE_SHORT); decode_sample_short(&scone2); // Add several SCONE packets. let mut scone3 = SCONE1.to_vec(); scone3.extend_from_slice(SCONE1); scone3.extend_from_slice(SCONE2); scone3.extend_from_slice(SCONE1); scone3.extend_from_slice(SAMPLE_SHORT); decode_sample_short(&scone3); // A SCONE-only packet is an error. let mut scone_only = SCONE1.to_vec(); let res = Public::decode(&mut scone_only, &cid_mgr()); assert!(matches!(res, Err(Error::NoMoreData))); } #[test] fn many_scones() { // A long chain of valid SCONE1 long-header packets with empty CIDs. const SCONE_PACKET: [u8; 7] = [0xff, 0x6f, 0x7d, 0xc0, 0xfd, 0x00, 0x00]; let mut data: Vec = SCONE_PACKET .iter() .copied() .cycle() .take(SCONE_PACKET.len() * 20_000) .collect(); assert!(matches!( Public::decode(&mut data, &cid_mgr()), Err(Error::NoMoreData) )); } }