diff --git a/.codecov.yml b/.codecov.yml index 1633a5dc59..09bf45df8f 100644 --- a/.codecov.yml +++ b/.codecov.yml @@ -1,5 +1,6 @@ -# neqo has no test coverage for its example client and server +# neqo has no test coverage for its example client and server, and for the fuzzing code. ignore: + - "fuzz" - "neqo-bin" - "test-fixture" diff --git a/Cargo.toml b/Cargo.toml index 6f040a6cef..017918b88c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,6 @@ [workspace] members = [ + "fuzz", "neqo-bin", "neqo-common", "neqo-crypto", diff --git a/fuzz/.gitignore b/fuzz/.gitignore new file mode 100644 index 0000000000..1a45eee776 --- /dev/null +++ b/fuzz/.gitignore @@ -0,0 +1,4 @@ +target +corpus +artifacts +coverage diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml new file mode 100644 index 0000000000..6fd0941867 --- /dev/null +++ b/fuzz/Cargo.toml @@ -0,0 +1,52 @@ +[package] +name = "fuzz" +authors.workspace = true +homepage.workspace = true +repository.workspace = true +version.workspace = true +edition.workspace = true +rust-version.workspace = true +license.workspace = true + +[package.metadata] +cargo-fuzz = true + +[dependencies] +neqo-common = { path = "../neqo-common" } +neqo-crypto = { path = "../neqo-crypto" } +neqo-transport = { path = "../neqo-transport" } +test-fixture = { path = "../test-fixture" } + +[target.'cfg(not(windows))'.dependencies] +libfuzzer-sys = { version = "0.4" } + +[lints] +workspace = true + +[[bin]] +name = "packet" +path = "fuzz_targets/packet.rs" +test = false +doc = false +bench = false + +[[bin]] +name = "frame" +path = "fuzz_targets/frame.rs" +test = false +doc = false +bench = false + +[[bin]] +name = "client_initial" +path = "fuzz_targets/client_initial.rs" +test = false +doc = false +bench = false + +[[bin]] +name = "server_initial" +path = "fuzz_targets/server_initial.rs" +test = false +doc = false +bench = false diff --git a/fuzz/fuzz_targets/client_initial.rs b/fuzz/fuzz_targets/client_initial.rs new file mode 100644 index 0000000000..f018298961 --- /dev/null +++ b/fuzz/fuzz_targets/client_initial.rs @@ -0,0 +1,73 @@ +#![cfg_attr(all(fuzzing, not(windows)), no_main)] + +#[cfg(all(fuzzing, not(windows)))] +use libfuzzer_sys::fuzz_target; + +#[cfg(all(fuzzing, not(windows)))] +fuzz_target!(|data: &[u8]| { + use neqo_common::{Datagram, Encoder, Role}; + use neqo_transport::Version; + use test_fixture::{ + default_client, default_server, + header_protection::{ + apply_header_protection, decode_initial_header, initial_aead_and_hp, + remove_header_protection, + }, + now, + }; + + let mut client = default_client(); + let ci = client.process(None, now()).dgram().expect("a datagram"); + let Some((header, d_cid, s_cid, payload)) = decode_initial_header(&ci, Role::Client) else { + return; + }; + let (aead, hp) = initial_aead_and_hp(d_cid, Role::Client); + let (_, pn) = remove_header_protection(&hp, header, payload); + + let mut payload_enc = Encoder::with_capacity(1200); + payload_enc.encode(data); // Add fuzzed data. + + // Make a new header with a 1 byte packet number length. + let mut header_enc = Encoder::new(); + header_enc + .encode_byte(0xc0) // Initial with 1 byte packet number. + .encode_uint(4, Version::default().wire_version()) + .encode_vec(1, d_cid) + .encode_vec(1, s_cid) + .encode_vvec(&[]) + .encode_varint(u64::try_from(payload_enc.len() + aead.expansion() + 1).unwrap()) + .encode_byte(u8::try_from(pn).unwrap()); + + let mut ciphertext = header_enc.as_ref().to_vec(); + ciphertext.resize(header_enc.len() + payload_enc.len() + aead.expansion(), 0); + let v = aead + .encrypt( + pn, + header_enc.as_ref(), + payload_enc.as_ref(), + &mut ciphertext[header_enc.len()..], + ) + .unwrap(); + assert_eq!(header_enc.len() + v.len(), ciphertext.len()); + // Pad with zero to get up to 1200. + ciphertext.resize(1200, 0); + + apply_header_protection( + &hp, + &mut ciphertext, + (header_enc.len() - 1)..header_enc.len(), + ); + let fuzzed_ci = Datagram::new( + ci.source(), + ci.destination(), + ci.tos(), + ci.ttl(), + ciphertext, + ); + + let mut server = default_server(); + let _response = server.process(Some(&fuzzed_ci), now()); +}); + +#[cfg(any(not(fuzzing), windows))] +fn main() {} diff --git a/fuzz/fuzz_targets/frame.rs b/fuzz/fuzz_targets/frame.rs new file mode 100644 index 0000000000..56f733a783 --- /dev/null +++ b/fuzz/fuzz_targets/frame.rs @@ -0,0 +1,17 @@ +#![cfg_attr(all(fuzzing, not(windows)), no_main)] + +#[cfg(all(fuzzing, not(windows)))] +use libfuzzer_sys::fuzz_target; + +#[cfg(all(fuzzing, not(windows)))] +fuzz_target!(|data: &[u8]| { + use neqo_common::Decoder; + use neqo_transport::frame::Frame; + + // Run the fuzzer + let mut decoder = Decoder::new(data); + let _ = Frame::decode(&mut decoder); +}); + +#[cfg(any(not(fuzzing), windows))] +fn main() {} diff --git a/fuzz/fuzz_targets/packet.rs b/fuzz/fuzz_targets/packet.rs new file mode 100644 index 0000000000..3080a75e70 --- /dev/null +++ b/fuzz/fuzz_targets/packet.rs @@ -0,0 +1,21 @@ +#![cfg_attr(all(fuzzing, not(windows)), no_main)] + +#[cfg(all(fuzzing, not(windows)))] +use libfuzzer_sys::fuzz_target; + +#[cfg(all(fuzzing, not(windows)))] +fuzz_target!(|data: &[u8]| { + use std::sync::OnceLock; + + use neqo_transport::{packet::PublicPacket, RandomConnectionIdGenerator}; + + static DECODER: OnceLock = OnceLock::new(); + let decoder = DECODER.get_or_init(|| RandomConnectionIdGenerator::new(20)); + neqo_crypto::init().unwrap(); + + // Run the fuzzer + let _ = PublicPacket::decode(data, decoder); +}); + +#[cfg(any(not(fuzzing), windows))] +fn main() {} diff --git a/fuzz/fuzz_targets/server_initial.rs b/fuzz/fuzz_targets/server_initial.rs new file mode 100644 index 0000000000..f4bd5ec0b9 --- /dev/null +++ b/fuzz/fuzz_targets/server_initial.rs @@ -0,0 +1,77 @@ +#![cfg_attr(all(fuzzing, not(windows)), no_main)] + +#[cfg(all(fuzzing, not(windows)))] +use libfuzzer_sys::fuzz_target; + +#[cfg(all(fuzzing, not(windows)))] +fuzz_target!(|data: &[u8]| { + use neqo_common::{Datagram, Encoder, Role}; + use neqo_transport::Version; + use test_fixture::{ + default_client, default_server, + header_protection::{ + apply_header_protection, decode_initial_header, initial_aead_and_hp, + remove_header_protection, + }, + now, + }; + + let mut client = default_client(); + let ci = client.process(None, now()).dgram().expect("a datagram"); + let mut server = default_server(); + let si = server + .process(Some(&ci), now()) + .dgram() + .expect("a datagram"); + + let Some((header, d_cid, s_cid, payload)) = decode_initial_header(&si, Role::Server) else { + return; + }; + let (aead, hp) = initial_aead_and_hp(d_cid, Role::Server); + let (_, pn) = remove_header_protection(&hp, header, payload); + + let mut payload_enc = Encoder::with_capacity(1200); + payload_enc.encode(data); // Add fuzzed data. + + // Make a new header with a 1 byte packet number length. + let mut header_enc = Encoder::new(); + header_enc + .encode_byte(0xc0) // Initial with 1 byte packet number. + .encode_uint(4, Version::default().wire_version()) + .encode_vec(1, d_cid) + .encode_vec(1, s_cid) + .encode_vvec(&[]) + .encode_varint(u64::try_from(payload_enc.len() + aead.expansion() + 1).unwrap()) + .encode_byte(u8::try_from(pn).unwrap()); + + let mut ciphertext = header_enc.as_ref().to_vec(); + ciphertext.resize(header_enc.len() + payload_enc.len() + aead.expansion(), 0); + let v = aead + .encrypt( + pn, + header_enc.as_ref(), + payload_enc.as_ref(), + &mut ciphertext[header_enc.len()..], + ) + .unwrap(); + assert_eq!(header_enc.len() + v.len(), ciphertext.len()); + // Pad with zero to get up to 1200. + ciphertext.resize(1200, 0); + + apply_header_protection( + &hp, + &mut ciphertext, + (header_enc.len() - 1)..header_enc.len(), + ); + let fuzzed_si = Datagram::new( + si.source(), + si.destination(), + si.tos(), + si.ttl(), + ciphertext, + ); + let _response = client.process(Some(&fuzzed_si), now()); +}); + +#[cfg(any(not(fuzzing), windows))] +fn main() {} diff --git a/neqo-common/Cargo.toml b/neqo-common/Cargo.toml index 5d8383614f..a70b3738d5 100644 --- a/neqo-common/Cargo.toml +++ b/neqo-common/Cargo.toml @@ -16,6 +16,7 @@ workspace = true # Sync with https://searchfox.org/mozilla-central/source/Cargo.lock 2024-02-08 enum-map = { version = "2.7", default-features = false } env_logger = { version = "0.10", default-features = false } +hex = { version = "0.4", default-features = false, features = ["alloc"], optional = true } log = { workspace = true } qlog = { workspace = true } time = { version = "0.3", default-features = false, features = ["formatting"] } @@ -26,6 +27,7 @@ test-fixture = { path = "../test-fixture" } [features] ci = [] +build-fuzzing-corpus = ["hex"] [target."cfg(windows)".dependencies.winapi] version = "0.3" diff --git a/neqo-common/src/fuzz.rs b/neqo-common/src/fuzz.rs new file mode 100644 index 0000000000..d0a35a49ae --- /dev/null +++ b/neqo-common/src/fuzz.rs @@ -0,0 +1,43 @@ +// 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. + +use std::{ + collections::hash_map::DefaultHasher, + fs::File, + hash::{Hash, Hasher}, + io::Write, + path::Path, +}; + +/// Write a data item `data` for the fuzzing target `target` to the fuzzing corpus. The caller needs +/// to make sure that `target` is the correct fuzzing target name for the data written. +/// +/// # Panics +/// +/// Panics if the corpus directory does not exist or if the corpus item cannot be written. +pub fn write_item_to_fuzzing_corpus(target: &str, data: &[u8]) { + // This bakes in the assumption that we're executing in the root of the neqo workspace. + // Unfortunately, `cargo fuzz` doesn't provide a way to learn the location of the corpus + // directory. + let corpus = Path::new("../fuzz/corpus").join(target); + if !corpus.exists() { + std::fs::create_dir_all(&corpus).expect("failed to create corpus directory"); + } + + // Hash the data to get a unique name for the corpus item. + let mut hasher = DefaultHasher::new(); + data.hash(&mut hasher); + let item_name = hex::encode(hasher.finish().to_be_bytes()); + let item_path = corpus.join(item_name); + if item_path.exists() { + // Don't overwrite existing corpus items. + return; + } + + // Write the data to the corpus item. + let mut file = File::create(item_path).expect("failed to create corpus item"); + Write::write_all(&mut file, data).expect("failed to write to corpus item"); +} diff --git a/neqo-common/src/lib.rs b/neqo-common/src/lib.rs index e988c6071d..3a72425e44 100644 --- a/neqo-common/src/lib.rs +++ b/neqo-common/src/lib.rs @@ -9,6 +9,8 @@ mod codec; mod datagram; pub mod event; +#[cfg(feature = "build-fuzzing-corpus")] +mod fuzz; pub mod header; pub mod hrtime; mod incrdecoder; @@ -21,6 +23,8 @@ use std::fmt::Write; use enum_map::Enum; +#[cfg(feature = "build-fuzzing-corpus")] +pub use self::fuzz::write_item_to_fuzzing_corpus; pub use self::{ codec::{Decoder, Encoder}, datagram::Datagram, diff --git a/neqo-crypto/Cargo.toml b/neqo-crypto/Cargo.toml index aae7c19644..85b2630404 100644 --- a/neqo-crypto/Cargo.toml +++ b/neqo-crypto/Cargo.toml @@ -30,8 +30,9 @@ toml = { version = "0.5", default-features = false } test-fixture = { path = "../test-fixture" } [features] -gecko = ["mozbuild"] disable-encryption = [] +disable-random = [] +gecko = ["mozbuild"] [lib] # See https://github.com/bheisler/criterion.rs/blob/master/book/src/faq.md#cargo-bench-gives-unrecognized-option-errors-for-valid-command-line-options diff --git a/neqo-crypto/src/p11.rs b/neqo-crypto/src/p11.rs index 5552882e2e..c235bb869c 100644 --- a/neqo-crypto/src/p11.rs +++ b/neqo-crypto/src/p11.rs @@ -13,7 +13,7 @@ use std::{ cell::RefCell, mem, ops::{Deref, DerefMut}, - os::raw::{c_int, c_uint}, + os::raw::c_uint, ptr::null_mut, }; @@ -290,14 +290,31 @@ impl Item { } } +#[cfg(feature = "disable-random")] +thread_local! { + static CURRENT_VALUE: std::cell::Cell = const { std::cell::Cell::new(0) }; +} + +#[cfg(feature = "disable-random")] +/// Fill a buffer with a predictable sequence of bytes. +pub fn randomize>(mut buf: B) -> B { + let m_buf = buf.as_mut(); + for v in m_buf.iter_mut() { + *v = CURRENT_VALUE.get(); + CURRENT_VALUE.set(v.wrapping_add(1)); + } + buf +} + /// Fill a buffer with randomness. /// /// # Panics /// /// When `size` is too large or NSS fails. +#[cfg(not(feature = "disable-random"))] pub fn randomize>(mut buf: B) -> B { let m_buf = buf.as_mut(); - let len = c_int::try_from(m_buf.len()).unwrap(); + let len = std::os::raw::c_int::try_from(m_buf.len()).unwrap(); secstatus_to_res(unsafe { PK11_GenerateRandom(m_buf.as_mut_ptr(), len) }).unwrap(); buf } @@ -359,10 +376,13 @@ mod test { use test_fixture::fixture_init; use super::RandomCache; - use crate::{random, randomize}; + use crate::random; + #[cfg(not(feature = "disable-random"))] #[test] fn randomness() { + use crate::randomize; + fixture_init(); // If any of these ever fail, there is either a bug, or it's time to buy a lottery ticket. assert_ne!(random::<16>(), randomize([0; 16])); diff --git a/neqo-transport/Cargo.toml b/neqo-transport/Cargo.toml index f57e8e2219..6095f3ac92 100644 --- a/neqo-transport/Cargo.toml +++ b/neqo-transport/Cargo.toml @@ -27,6 +27,12 @@ test-fixture = { path = "../test-fixture" } [features] bench = [] +build-fuzzing-corpus = [ + "neqo-common/build-fuzzing-corpus", + "neqo-crypto/disable-encryption", + "neqo-crypto/disable-random", + "test-fixture/disable-random", +] disable-encryption = ["neqo-crypto/disable-encryption"] [lib] diff --git a/neqo-transport/src/connection/mod.rs b/neqo-transport/src/connection/mod.rs index d6d12fe80c..b8f598e4fc 100644 --- a/neqo-transport/src/connection/mod.rs +++ b/neqo-transport/src/connection/mod.rs @@ -1113,7 +1113,15 @@ impl Connection { self.input(d, now, now); self.process_saved(now); } - self.process_output(now) + #[allow(clippy::let_and_return)] + let output = self.process_output(now); + #[cfg(all(feature = "build-fuzzing-corpus", test))] + if self.test_frame_writer.is_none() { + if let Some(d) = output.clone().dgram() { + neqo_common::write_item_to_fuzzing_corpus("packet", &d); + } + } + output } fn handle_retry(&mut self, packet: &PublicPacket, now: Instant) { @@ -1517,6 +1525,16 @@ impl Connection { d.tos(), ); + #[cfg(feature = "build-fuzzing-corpus")] + if packet.packet_type() == PacketType::Initial { + let target = if self.role == Role::Client { + "server_initial" + } else { + "client_initial" + }; + neqo_common::write_item_to_fuzzing_corpus(target, &payload[..]); + } + qlog::packet_received(&mut self.qlog, &packet, &payload); let space = PacketNumberSpace::from(payload.packet_type()); if self.acks.get_mut(space).unwrap().is_duplicate(payload.pn()) { @@ -1588,7 +1606,11 @@ impl Connection { let mut probing = true; let mut d = Decoder::from(&packet[..]); while d.remaining() > 0 { + #[cfg(feature = "build-fuzzing-corpus")] + let pos = d.offset(); let f = Frame::decode(&mut d)?; + #[cfg(feature = "build-fuzzing-corpus")] + neqo_common::write_item_to_fuzzing_corpus("frame", &packet[pos..d.offset()]); ack_eliciting |= f.ack_eliciting(); probing &= f.path_probing(); let t = f.get_type(); diff --git a/neqo-transport/src/lib.rs b/neqo-transport/src/lib.rs index 57515fe3e1..53af334e27 100644 --- a/neqo-transport/src/lib.rs +++ b/neqo-transport/src/lib.rs @@ -18,8 +18,14 @@ mod crypto; mod ecn; mod events; mod fc; +#[cfg(fuzzing)] +pub mod frame; +#[cfg(not(fuzzing))] mod frame; mod pace; +#[cfg(fuzzing)] +pub mod packet; +#[cfg(not(fuzzing))] mod packet; mod path; mod qlog; diff --git a/neqo-transport/src/packet/mod.rs b/neqo-transport/src/packet/mod.rs index ce611a9664..10d9b13208 100644 --- a/neqo-transport/src/packet/mod.rs +++ b/neqo-transport/src/packet/mod.rs @@ -740,6 +740,7 @@ impl<'a> PublicPacket<'a> { } #[must_use] + #[allow(clippy::len_without_is_empty)] // is_empty() would always return false in this case pub fn len(&self) -> usize { self.data.len() } diff --git a/neqo-transport/tests/connection.rs b/neqo-transport/tests/connection.rs index 6cac320a53..d08d946cf8 100644 --- a/neqo-transport/tests/connection.rs +++ b/neqo-transport/tests/connection.rs @@ -72,12 +72,13 @@ fn reorder_server_initial() { let client_initial = client.process_output(now()); let (_, client_dcid, _, _) = - decode_initial_header(client_initial.as_dgram_ref().unwrap(), Role::Client); + decode_initial_header(client_initial.as_dgram_ref().unwrap(), Role::Client).unwrap(); let client_dcid = client_dcid.to_owned(); let server_packet = server.process(client_initial.as_dgram_ref(), now()).dgram(); let (server_initial, server_hs) = split_datagram(server_packet.as_ref().unwrap()); - let (protected_header, _, _, payload) = decode_initial_header(&server_initial, Role::Server); + let (protected_header, _, _, payload) = + decode_initial_header(&server_initial, Role::Server).unwrap(); // Now decrypt the packet. let (aead, hp) = initial_aead_and_hp(&client_dcid, Role::Server); @@ -134,7 +135,7 @@ fn reorder_server_initial() { fn set_payload(server_packet: &Option, client_dcid: &[u8], payload: &[u8]) -> Datagram { let (server_initial, _server_hs) = split_datagram(server_packet.as_ref().unwrap()); let (protected_header, _, _, orig_payload) = - decode_initial_header(&server_initial, Role::Server); + decode_initial_header(&server_initial, Role::Server).unwrap(); // Now decrypt the packet. let (aead, hp) = initial_aead_and_hp(client_dcid, Role::Server); @@ -172,7 +173,7 @@ fn packet_without_frames() { let client_initial = client.process_output(now()); let (_, client_dcid, _, _) = - decode_initial_header(client_initial.as_dgram_ref().unwrap(), Role::Client); + decode_initial_header(client_initial.as_dgram_ref().unwrap(), Role::Client).unwrap(); let server_packet = server.process(client_initial.as_dgram_ref(), now()).dgram(); let modified = set_payload(&server_packet, client_dcid, &[]); @@ -193,7 +194,7 @@ fn packet_with_only_padding() { let client_initial = client.process_output(now()); let (_, client_dcid, _, _) = - decode_initial_header(client_initial.as_dgram_ref().unwrap(), Role::Client); + decode_initial_header(client_initial.as_dgram_ref().unwrap(), Role::Client).unwrap(); let server_packet = server.process(client_initial.as_dgram_ref(), now()).dgram(); let modified = set_payload(&server_packet, client_dcid, &[0]); @@ -212,7 +213,7 @@ fn overflow_crypto() { let client_initial = client.process_output(now()).dgram(); let (_, client_dcid, _, _) = - decode_initial_header(client_initial.as_ref().unwrap(), Role::Client); + decode_initial_header(client_initial.as_ref().unwrap(), Role::Client).unwrap(); let client_dcid = client_dcid.to_owned(); let server_packet = server.process(client_initial.as_ref(), now()).dgram(); @@ -221,7 +222,8 @@ fn overflow_crypto() { // Now decrypt the server packet to get AEAD and HP instances. // We won't be using the packet, but making new ones. let (aead, hp) = initial_aead_and_hp(&client_dcid, Role::Server); - let (_, server_dcid, server_scid, _) = decode_initial_header(&server_initial, Role::Server); + let (_, server_dcid, server_scid, _) = + decode_initial_header(&server_initial, Role::Server).unwrap(); // Send in 100 packets, each with 1000 bytes of crypto frame data each, // eventually this will overrun the buffer we keep for crypto data. diff --git a/neqo-transport/tests/retry.rs b/neqo-transport/tests/retry.rs index 00cadaf1b4..0cfc48f051 100644 --- a/neqo-transport/tests/retry.rs +++ b/neqo-transport/tests/retry.rs @@ -404,7 +404,7 @@ fn mitm_retry() { // rewriting the header to remove the token, and then re-encrypting. let client_initial2 = client_initial2.unwrap(); let (protected_header, d_cid, s_cid, payload) = - decode_initial_header(&client_initial2, Role::Client); + decode_initial_header(&client_initial2, Role::Client).unwrap(); // Now we have enough information to make keys. let (aead, hp) = initial_aead_and_hp(d_cid, Role::Client); diff --git a/neqo-transport/tests/server.rs b/neqo-transport/tests/server.rs index 8fa1c5878f..3c43a9105d 100644 --- a/neqo-transport/tests/server.rs +++ b/neqo-transport/tests/server.rs @@ -390,7 +390,7 @@ fn bad_client_initial() { let mut server = default_server(); let dgram = client.process(None, now()).dgram().expect("a datagram"); - let (header, d_cid, s_cid, payload) = decode_initial_header(&dgram, Role::Client); + let (header, d_cid, s_cid, payload) = decode_initial_header(&dgram, Role::Client).unwrap(); let (aead, hp) = initial_aead_and_hp(d_cid, Role::Client); let (fixed_header, pn) = remove_header_protection(&hp, header, payload); let payload = &payload[(fixed_header.len() - header.len())..]; @@ -484,7 +484,7 @@ fn bad_client_initial_connection_close() { let mut server = default_server(); let dgram = client.process(None, now()).dgram().expect("a datagram"); - let (header, d_cid, s_cid, payload) = decode_initial_header(&dgram, Role::Client); + let (header, d_cid, s_cid, payload) = decode_initial_header(&dgram, Role::Client).unwrap(); let (aead, hp) = initial_aead_and_hp(d_cid, Role::Client); let (_, pn) = remove_header_protection(&hp, header, payload); diff --git a/test-fixture/Cargo.toml b/test-fixture/Cargo.toml index fe1976f0ed..3e5e797e4e 100644 --- a/test-fixture/Cargo.toml +++ b/test-fixture/Cargo.toml @@ -23,6 +23,7 @@ qlog = { workspace = true } [features] bench = [] +disable-random = [] [lib] # See https://github.com/bheisler/criterion.rs/blob/master/book/src/faq.md#cargo-bench-gives-unrecognized-option-errors-for-valid-command-line-options diff --git a/test-fixture/src/header_protection.rs b/test-fixture/src/header_protection.rs index 3e2e43a3e8..031193367b 100644 --- a/test-fixture/src/header_protection.rs +++ b/test-fixture/src/header_protection.rs @@ -30,13 +30,22 @@ pub use crate::{default_client, now, CountingConnectionIdGenerator}; // Any token is thrown away. #[must_use] #[allow(clippy::missing_panics_doc)] -pub fn decode_initial_header(dgram: &Datagram, role: Role) -> (&[u8], &[u8], &[u8], &[u8]) { +#[allow(clippy::type_complexity)] +pub fn decode_initial_header(dgram: &Datagram, role: Role) -> Option<(&[u8], &[u8], &[u8], &[u8])> { let mut dec = Decoder::new(&dgram[..]); let type_and_ver = dec.decode(5).unwrap().to_vec(); // The client sets the QUIC bit, the server might not. match role { - Role::Client => assert_eq!(type_and_ver[0] & 0xf0, 0xc0), - Role::Server => assert_eq!(type_and_ver[0] & 0xb0, 0x80), + Role::Client => { + if type_and_ver[0] & 0xf0 != 0xc0 { + return None; + } + } + Role::Server => { + if type_and_ver[0] & 0xb0 != 0x80 { + return None; + } + } } let dest_cid = dec.decode_vec(1).unwrap(); let src_cid = dec.decode_vec(1).unwrap(); @@ -45,12 +54,12 @@ pub fn decode_initial_header(dgram: &Datagram, role: Role) -> (&[u8], &[u8], &[u // Need to read of the length separately so that we can find the packet number. let payload_len = usize::try_from(dec.decode_varint().unwrap()).unwrap(); let pn_offset = dgram.len() - dec.remaining(); - ( + Some(( &dgram[..pn_offset], dest_cid, src_cid, dec.decode(payload_len).unwrap(), - ) + )) } /// Generate an AEAD and header protection object for a client Initial. diff --git a/test-fixture/src/sim/rng.rs b/test-fixture/src/sim/rng.rs index 913d7eae7a..14b2240b9e 100644 --- a/test-fixture/src/sim/rng.rs +++ b/test-fixture/src/sim/rng.rs @@ -77,7 +77,14 @@ impl Random { } impl Default for Random { + #[cfg(not(feature = "disable-random"))] fn default() -> Self { Random::new(&neqo_crypto::random::<32>()) } + + #[cfg(feature = "disable-random")] + // Use a fixed seed for a deterministic sequence of numbers. + fn default() -> Self { + Random::new(&[1; 32]) + } }