Skip to content

Adding robustness checks to the QUIC handshake #2613

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion neqo-bin/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,10 @@ pub struct QuicParameters {
/// Whether to slice the SNI.
pub no_sni_slicing: bool,

#[arg(long)]
/// Whether to send random noise packets before client Initial packets.
pub no_pre_init_noise: bool,

#[arg(name = "preferred-address-v4", long)]
/// An IPv4 address for the server preferred address.
pub preferred_address_v4: Option<String>,
Expand All @@ -148,6 +152,7 @@ impl Default for QuicParameters {
preferred_address_v4: None,
preferred_address_v6: None,
no_sni_slicing: false,
no_pre_init_noise: false,
}
}
}
Expand Down Expand Up @@ -221,7 +226,8 @@ impl QuicParameters {
.cc_algorithm(self.congestion_control)
.pacing(!self.no_pacing)
.pmtud(!self.no_pmtud)
.sni_slicing(!self.no_sni_slicing);
.sni_slicing(!self.no_sni_slicing)
.pre_init_noise(!self.no_pre_init_noise);
params = if let Some(pa) = self.preferred_address() {
params.preferred_address(pa)
} else {
Expand Down
37 changes: 36 additions & 1 deletion neqo-transport/src/connection/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ use neqo_common::{
use neqo_crypto::{
agent::CertificateInfo, Agent, AntiReplay, AuthenticationStatus, Cipher, Client, Group,
HandshakeState, PrivateKey, PublicKey, ResumptionToken, SecretAgentInfo, SecretAgentPreInfo,
Server, ZeroRttChecker,
Server, ZeroRttChecker, random, randomize,
};
use smallvec::SmallVec;
use strum::IntoEnumIterator as _;
Expand Down Expand Up @@ -88,6 +88,11 @@ pub use crate::send_stream::{RetransmissionPriority, SendStreamStats, Transmissi
/// handshake. This is a hack, but a useful one.
const EXTRA_INITIALS: usize = 4;

/// Maximum number of noise datagrams to send before Client Initial packets.
const MAX_PRE_INIT_NOISE_PKTS: usize = 1;
/// Payload size range (in bytes) for pre-Initial noise datagrams: to mimic real Initial packets.
const PRE_INIT_NOISE_LEN_RANGE: RangeInclusive<usize> = RangeInclusive::new(1200, 1400);

#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum ZeroRttState {
Init,
Expand Down Expand Up @@ -291,6 +296,7 @@ pub struct Connection {
release_resumption_token_timer: Option<Instant>,
conn_params: ConnectionParameters,
hrtime: hrtime::Handle,
pre_initial_noise_pkts: usize,

/// For testing purposes it is sometimes necessary to inject frames that wouldn't
/// otherwise be sent, just to see how a connection handles them. Inserting them
Expand Down Expand Up @@ -351,6 +357,12 @@ impl Connection {
&mut c.stats.borrow_mut(),
);
c.setup_handshake_path(&Rc::new(RefCell::new(path)), now);

if c.conn_params.pre_init_noise_enabled() {
// Generate a random number between 1 and the maximum number of pre-initial packets
c.pre_initial_noise_pkts = (usize::from(random::<1>()[0]) % MAX_PRE_INIT_NOISE_PKTS) + 1;
}

Ok(c)
}

Expand Down Expand Up @@ -437,6 +449,7 @@ impl Connection {
conn_params,
hrtime: hrtime::Time::get(Self::LOOSE_TIMER_RESOLUTION),
quic_datagrams,
pre_initial_noise_pkts: 0,
#[cfg(test)]
test_frame_writer: None,
};
Expand Down Expand Up @@ -1142,6 +1155,28 @@ impl Connection {

match (&self.state, self.role) {
(State::Init, Role::Client) => {
// Before starting the client, send some noise
if self.pre_initial_noise_pkts > 0 {
if let Some(path) = self.paths.primary() {
// Choose a random payload size between the noise payload length range.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Choose a random payload size between the noise payload length range.
// Choose a random payload size in the noise payload length range.

let span = PRE_INIT_NOISE_LEN_RANGE.end() - PRE_INIT_NOISE_LEN_RANGE.start() + 1;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
let span = PRE_INIT_NOISE_LEN_RANGE.end() - PRE_INIT_NOISE_LEN_RANGE.start() + 1;
let span = PRE_INIT_NOISE_LEN_RANGE.len() + 1;

let v = usize::from(random::<1>()[0]);
let len = PRE_INIT_NOISE_LEN_RANGE.start() + (v % span);

let mut dummy_buf= vec![0u8; len];
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
let mut dummy_buf= vec![0u8; len];
let mut noise = vec![0u8; len];

randomize(&mut dummy_buf);

let dg = Datagram::new(
path.borrow().local_address(),
path.borrow().remote_address(),
IpTos::default(),
dummy_buf,
);
self.pre_initial_noise_pkts -= 1;
return Output::Datagram(dg);
}
}

let res = self.client_start(now);
self.absorb_error(now, res);
}
Expand Down
14 changes: 14 additions & 0 deletions neqo-transport/src/connection/params.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,8 @@ pub struct ConnectionParameters {
pmtud_iface_mtu: bool,
/// Whether the connection should use SNI slicing.
sni_slicing: bool,
/// Whether the connection should enable pre-Initial noise.
pre_init_noise: bool,
/// Whether to enable mlkem768nistp256-sha256.
mlkem: bool,
}
Expand Down Expand Up @@ -128,6 +130,7 @@ impl Default for ConnectionParameters {
pmtud_iface_mtu: true,
sni_slicing: true,
mlkem: true,
pre_init_noise: true,
}
}
}
Expand Down Expand Up @@ -410,6 +413,17 @@ impl ConnectionParameters {
self
}

#[must_use]
pub const fn pre_init_noise_enabled(&self) -> bool {
self.pre_init_noise
}

#[must_use]
pub const fn pre_init_noise(mut self, pre_init_noise: bool) -> Self {
self.pre_init_noise = pre_init_noise;
self
}

#[must_use]
pub const fn mlkem_enabled(&self) -> bool {
self.mlkem
Expand Down
Loading