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 nss::{Mode, RecordProtectionOps as _, random}; use strum::{EnumIter, FromRepr};
/// `MIN_INITIAL_PACKET_SIZE` is the smallest packet that can be used to establish /// a new connection across all QUIC versions this server supports. pubconst MIN_INITIAL_PACKET_SIZE: usize = 1200;
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<usize>,
}
/// A packet builder that can be used to produce short packets and long packets. /// This does not produce Retry or Version Negotiation. pubstruct Builder<B> {
encoder: Encoder<B>,
pn: Number,
header: Range<usize>,
offsets: BuilderOffsets,
limit: usize, /// Whether to pad the packet before construction.
padding: bool,
}
impl Builder<Vec<u8>> { /// The minimum useful frame size. If space is less than this, we will claim to be full. pubconst 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<u8>`] rather than building on an encoder. /// /// # Errors /// /// This will return an error if AEAD encrypt fails. pubfn retry(
version: Version,
dcid: &[u8],
scid: &[u8],
token: &[u8],
odcid: &[u8],
) -> Res<Vec<u8>> { letmut 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, Mode::Encrypt, |aead| { letmut buf = vec![0; aead.expansion()];
Ok(aead.encrypt(0, encoder.as_ref(), &[], &mut buf)?.to_vec())
})?;
encoder.encode(&tag); letmut complete: Vec<u8> = encoder.into();
Ok(complete.split_off(start))
}
/// Make a Version Negotiation packet. #[must_use] pubfn version_negotiation(
dcid: &[u8],
scid: &[u8],
client_version: u32,
versions: &[Version],
) -> Vec<u8> { letmut encoder = Encoder::default(); letmut 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<B: Buffer> Builder<B> { /// 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. pubfn short<A: AsRef<[u8]>>( mut encoder: Encoder<B>,
key_phase: bool,
dcid: Option<A>,
limit: usize,
) -> Self { letmut limit = limit;
/// 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. pubfn long<A: AsRef<[u8]>, A1: AsRef<[u8]>>( mut encoder: Encoder<B>,
pt: Type,
version: Version, mut dcid: Option<A>, mut scid: Option<A1>,
limit: usize,
) -> Self { letmut 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;
}
/// 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()`. pubconstfn set_limit(&mutself, limit: usize) { self.limit = limit;
}
/// Get the current limit. #[must_use] pubconstfn limit(&self) -> usize { self.limit
}
/// How many bytes remain against the size limit for the builder. #[must_use] pubfn remaining(&self) -> usize { self.limit.saturating_sub(self.len())
}
/// Returns true if the packet has no more space for frames. #[must_use] pubfn 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. pubfn mark_full(&mutself) { self.limit = self.len();
}
/// Mark the packet as needing padding (or not). pubconstfn enable_padding(&mutself, 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. pubfn pad(&mutself) -> bool { ifself.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. pubfn scramble(&mutself, quic_bit: bool) {
debug_assert!(self.len() > self.header.start); let mask = if quic_bit { BIT_FIXED_QUIC } else { 0 } | ifself.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. pubfn initial_token(&mutself, 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. pubfn pn(&mutself, pn: Number, pn_len: usize) { ifself.remaining() < MAX_PACKET_NUMBER_LEN { self.limit = 0; return;
}
// Reserve space for a length in long headers. ifself.is_long() { ifself.remaining() < LONG_PACKET_LENGTH_LEN + MAX_PACKET_NUMBER_LEN { self.limit = 0; return;
}
// 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(&mutself, 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(&mutself, 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. // // <https://datatracker.ietf.org/doc/html/rfc9001#section-5.4.2>
/// 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. pubfn write_varint_frame(&mutself, values: &[u64]) -> bool { let write = self.remaining()
>= values
.iter()
.map(|&v| Encoder::varint_len(v))
.sum::<usize>(); if write { iflet 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. pubfn build(mutself, crypto: &mut CryptoDxState) -> Res<Encoder<B>> { ifself.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);
}
// 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] pubfn abort(mutself) -> Encoder<B> { self.encoder.truncate(self.header.start); self.encoder
}
/// Work out if nothing was added after the header. #[must_use] pubfn packet_empty(&self) -> bool { self.encoder.len() == self.header.end
}
/// `Public` holds information from packets that is public only. This allows for /// processing of packets prior to decryption. pubstruct 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<ConnectionId>, /// 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<u8>, /// The size of the header, not including the packet number.
header_len: usize, /// Protocol version, if present in header.
version: Option<version::Wire>, /// A reference to the entire packet, including the header.
data: &'a mut [u8], /// SCONE information, if present.
scone: Option<Bitrate>,
}
/// 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. pubfn 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. pubfn 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])> { letmut scone: Option<Bitrate> = None; loop { letmut decoder = Decoder::new(data); let first = Self::opt(decoder.decode_uint::<u8>())?;
if first & 0x80 == BIT_SHORT { 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::new(),
header_len,
version: None,
data,
scone,
},
&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::new(),
header_len: decoder.offset(),
version: None,
data,
scone,
},
&mut [],
));
}
Version::SCONE1 | Version::SCONE2 => { if scone.is_some() { return Err(Error::InvalidPacket);
} let indication = Bitrate::from((first, version));
debug!("Received SCONE indication {indication:x?}"); // Note that this doesn't confirm that the connection ID matches.
scone = Some(indication); let (_scone, remainder) = data.split_at_mut(decoder.offset());
data = remainder; continue;
}
_ => {}
}
// Check that this is a long header from a supported version. let Ok(version) = Version::try_from(version) else { returnif accept_other_version {
Ok(( Self {
packet_type: Type::OtherVersion,
dcid: ConnectionId::from(dcid),
scid: Some(ConnectionId::from(scid)),
token: Vec::new(),
header_len: decoder.offset(),
version: Some(version),
data,
scone,
},
&mut [],
))
} else {
Err(Error::InvalidPacket)
};
};
// 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,
scone,
},
remainder,
));
}
}
/// Validate the given packet as though it were a retry. #[must_use] pubfn is_valid_retry(&self, odcid: &ConnectionId) -> bool { ifself.packet_type != Type::Retry { returnfalse;
} let Some(version) = self.version() else { returnfalse;
}; let expansion = retry::expansion(version); ifself.data.len() <= expansion { returnfalse;
} let (header, tag) = self.data.split_at(self.data.len() - expansion); letmut encoder = Encoder::with_capacity(self.data.len());
encoder.encode_vec(1, odcid);
encoder.encode(header);
retry::use_aead(version, Mode::Decrypt, |aead| { letmut buf = vec![0; expansion];
Ok(aead.decrypt(0, encoder.as_ref(), tag, &mut buf)?.is_empty())
})
.unwrap_or(false)
}
#[must_use] pubfn 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] pubconstfn packet_type(&self) -> Type { self.packet_type
}
/// # Panics /// /// This will panic if called for a short header packet. #[must_use] pubfn scid(&self) -> ConnectionIdRef<'_> { self.scid
.as_ref()
.expect("should only be called for long header packets")
.as_cid_ref()
}
/// # Errors /// /// This will return an error if the packet cannot be decrypted. pubfn decrypt( mutself,
crypto: &mut CryptoStates,
release_at: Instant,
) -> Result<Decrypted<'a>, DecryptionError<'a>> { let epoch = matchself.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) = matchself.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,
scone: self.scone,
})
}
/// # Errors /// /// This will return an error if the packet is not a version negotiation packet /// or if the versions cannot be decoded. pubfn supported_versions(&self) -> Res<Vec<version::Wire>> { ifself.packet_type != Type::VersionNegotiation { return Err(Error::InvalidPacket);
} letmut decoder = Decoder::new(&self.data[self.header_len..]); letmut res = Vec::new(); while decoder.remaining() > 0 { let version = Self::opt(decoder.decode_uint::<version::Wire>())?;
res.push(version);
}
Ok(res)
}
}
/// Error information from a failed decryption attempt. /// Contains minimal packet information needed for error handling. #[derive(Debug)] pubstruct 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,
}
// 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] pubconstfn is_empty(&self) -> bool { self.data.is_empty()
}
#[must_use] pubconstfn packet_type(&self) -> Type { self.packet_type
}
}
/// # Panics /// /// This will panic if called for a short header packet. #[must_use] pubfn 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)] pubconst 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};
/// This is a connection ID manager, which is only used for decoding short header packets. constfn cid_mgr() -> RandomConnectionIdGenerator {
RandomConnectionIdGenerator::new(SERVER_CID.len())
}
#[test] fn scramble_short() {
fixture_init(); letmut firsts = Vec::new(); for _ in0..64 { letmut 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)));
}
/// 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(); letmut 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() { letmut sample_short = SAMPLE_SHORT.to_vec();
assert!(
Public::decode(
&mut sample_short,
&RandomConnectionIdGenerator::new(SERVER_CID.len() + 1)
)
.is_err()
);
}
letmut 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_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();
/// 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 <https://github.com/mozilla/neqo/issues/3046>. #[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_write();
// 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());
}
// Set up a builder with a very small limit letmut 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_write()),
Err(Error::Internal)
);
}
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_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 _ in0..32 {
build_retry_v2();
build_retry_v1();
build_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);
/// 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];
#[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
);
}
// A SCONE-only packet is an error. letmut scone_only = SCONE1.to_vec(); let res = Public::decode(&mut scone_only, &cid_mgr());
assert!(matches!(res, Err(Error::NoMoreData)));
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.