Skip to content

Commit

Permalink
Support disable_1rtt_encryption transport parameter (#393)
Browse files Browse the repository at this point in the history
  • Loading branch information
iyangsj authored Oct 9, 2024
1 parent f38c6f3 commit 65365a0
Show file tree
Hide file tree
Showing 9 changed files with 158 additions and 25 deletions.
8 changes: 8 additions & 0 deletions include/tquic.h
Original file line number Diff line number Diff line change
Expand Up @@ -753,6 +753,14 @@ void quic_config_set_zerortt_buffer_size(struct quic_config_t *config, uint16_t
*/
void quic_config_set_max_undecryptable_packets(struct quic_config_t *config, uint16_t v);

/**
* Enable or disable encryption on 1-RTT packets. (Experimental)
* The default value is true.
* WARN: The The disable_1rtt_encryption extension is not meant to be used
* for any practical application protocol on the open internet.
*/
void quic_config_enable_encryption(struct quic_config_t *config, bool v);

/**
* Create a new TlsConfig.
* The caller is responsible for the memory of the TlsConfig and should properly
Expand Down
79 changes: 66 additions & 13 deletions src/connection/connection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -527,7 +527,9 @@ impl Connection {
return Ok(read + length);
}
};
packet::decrypt_header(buf, pkt_num_offset, &mut hdr, key).map_err(|_| Error::Done)?;
let is_encryption_disabled = self.is_encryption_disabled(hdr.pkt_type);
packet::decrypt_header(buf, pkt_num_offset, &mut hdr, key, is_encryption_disabled)
.map_err(|_| Error::Done)?;

// Decode packet sequence number
let handshake_confirmed = self.is_confirmed();
Expand Down Expand Up @@ -564,9 +566,12 @@ impl Connection {
&hdr,
space,
)?;
let mut payload =
let mut payload = if !is_encryption_disabled {
packet::decrypt_payload(buf, payload_offset, payload_len, cid_seq, pkt_num, key)
.map_err(|_| Error::Done)?;
.map_err(|_| Error::Done)?
} else {
bytes::Bytes::copy_from_slice(&buf[payload_offset..payload_offset + payload_len])
};
if payload.is_empty() {
// An endpoint MUST treat receipt of a packet containing no frames as a connection error
// of type PROTOCOL_VIOLATION.
Expand Down Expand Up @@ -1187,6 +1192,15 @@ impl Connection {
self.events.add(Event::ResetTokenAdvertised(reset_token));
}

// The connection enters disable_1rtt_encryption mode
if peer_params.disable_encryption && self.local_transport_params.disable_encryption {
self.flags.insert(DisableEncryption);
debug!(
"{} encryption on 1-RTT packets has been negotiated to be disabled",
self.trace_id
);
}

self.set_peer_trans_params(peer_params)?;
self.flags.insert(AppliedPeerTransportParams);

Expand Down Expand Up @@ -1729,16 +1743,20 @@ impl Connection {
cid_seq = Some(dcid_seq as u32);
}

let written = packet::encrypt_packet(
out,
cid_seq,
pkt_num,
pkt_num_len,
payload_len,
payload_offset,
None,
key,
)?;
let written = if !self.is_encryption_disabled(hdr.pkt_type) {
packet::encrypt_packet(
out,
cid_seq,
pkt_num,
pkt_num_len,
payload_len,
payload_offset,
None,
key,
)?
} else {
payload_offset + payload_len
};

let sent_pkt = space::SentPacket {
pkt_type,
Expand Down Expand Up @@ -3305,6 +3323,11 @@ impl Connection {
Some(idle_timeout)
}

/// Whether encryption on the specified packet type should be disabled
fn is_encryption_disabled(&self, pkt_type: PacketType) -> bool {
pkt_type == PacketType::OneRTT && self.flags.contains(DisableEncryption)
}

/// Check whether the connection is a server connection.
pub fn is_server(&self) -> bool {
self.is_server
Expand Down Expand Up @@ -4372,6 +4395,9 @@ enum ConnectionFlags {

/// The multipath extension is successfully negotiated.
EnableMultipath = 1 << 20,

/// The disable_1rtt_encryption is successfully negotiated.
DisableEncryption = 1 << 21,
}

/// Statistics about a QUIC connection.
Expand Down Expand Up @@ -5674,6 +5700,33 @@ pub(crate) mod tests {
Ok(())
}

#[test]
fn handshake_with_disable_encryption_negotiated() -> Result<()> {
let cases = [
// The items in each case are as following:
// - client disable_encryption
// - server disable_encryption
// - disable_encryption negotiation result
//(true, false, false),
//(false,false, false),
//(false, true, false),
(true, true, true),
];
for case in cases {
let mut client_config = TestPair::new_test_config(false)?;
client_config.enable_encryption(!case.0);
let mut server_config = TestPair::new_test_config(true)?;
server_config.enable_encryption(!case.1);

let mut test_pair = TestPair::new(&mut client_config, &mut server_config)?;
assert_eq!(test_pair.handshake(), Ok(()));
assert_eq!(test_pair.client.flags.contains(DisableEncryption), case.2);
assert_eq!(test_pair.server.flags.contains(DisableEncryption), case.2);
}

Ok(())
}

#[test]
fn max_datagram_size() -> Result<()> {
let mut client_config = TestPair::new_test_config(false)?;
Expand Down
20 changes: 20 additions & 0 deletions src/endpoint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2719,6 +2719,26 @@ mod tests {
Ok(())
}

#[test]
fn transfer_single_stream_disable_encryption() -> Result<()> {
let mut t = TestPair::new();

let mut cli_conf = TestPair::new_test_config(false)?;
cli_conf.enable_encryption(false);
let mut srv_conf = TestPair::new_test_config(true)?;
srv_conf.enable_encryption(false);

let mut case_conf = CaseConf::default();
case_conf.session = Some(TestPair::new_test_session_state());
case_conf.client_0rtt_expected = true;
case_conf.resumption_expected = true;
case_conf.request_num = 1;
case_conf.request_size = 1024 * 16;

t.run(cli_conf, srv_conf, case_conf)?;
Ok(())
}

#[test]
fn transfer_multi_stream_normal() -> Result<()> {
let mut t = TestPair::new();
Expand Down
9 changes: 9 additions & 0 deletions src/ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -553,6 +553,15 @@ pub extern "C" fn quic_config_set_max_undecryptable_packets(config: &mut Config,
config.set_max_undecryptable_packets(v as usize);
}

/// Enable or disable encryption on 1-RTT packets. (Experimental)
/// The default value is true.
/// WARN: The The disable_1rtt_encryption extension is not meant to be used
/// for any practical application protocol on the open internet.
#[no_mangle]
pub extern "C" fn quic_config_enable_encryption(config: &mut Config, v: bool) {
config.enable_encryption(v);
}

/// Create a new TlsConfig.
/// The caller is responsible for the memory of the TlsConfig and should properly
/// destroy it by calling `quic_tls_config_free`.
Expand Down
8 changes: 8 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -735,6 +735,14 @@ impl Config {
}
}

/// Enable or disable encryption on 1-RTT packets. (Experimental)
/// The default value is true.
/// WARN: The The disable_1rtt_encryption extension is not meant to be used
/// for any practical application protocol on the open internet.
pub fn enable_encryption(&mut self, v: bool) {
self.local_transport_params.disable_encryption = !v;
}

/// Set TLS config.
pub fn set_tls_config(&mut self, tls_config: tls::TlsConfig) {
self.set_tls_config_selector(Arc::new(tls::DefaultTlsConfigSelector {
Expand Down
31 changes: 19 additions & 12 deletions src/packet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -533,11 +533,13 @@ pub(crate) fn decrypt_payload(
/// The `pkt_buf` is the raw data of a QUIC packet.
/// The `pkt_num_offset` is the offset of Packet Number field in `pkt_buf`.
/// The `hdr` is the partially parsed header return by PacketHeader::from().
/// The `plaintext_mode` is used for the `disable_1rtt_encryption` extension.
pub(crate) fn decrypt_header(
pkt_buf: &mut [u8],
pkt_num_offset: usize,
hdr: &mut PacketHeader,
aead: &Open,
plaintext_mode: bool,
) -> Result<()> {
if pkt_buf.len() < pkt_num_offset + MAX_PKT_NUM_LEN + SAMPLE_LEN {
return Err(Error::BufferTooShort);
Expand All @@ -546,20 +548,25 @@ pub(crate) fn decrypt_header(
let mut first = pkt_buf[0];
let sample_start = pkt_num_offset + MAX_PKT_NUM_LEN;
let sample = &pkt_buf[sample_start..sample_start + SAMPLE_LEN];
let mask = aead.new_mask(sample)?;

// Remove protection of bits in the first byte
let mask = aead.new_mask(sample)?;
if PacketHeader::long_header(first) {
first ^= mask[0] & 0x0f;
} else {
first ^= mask[0] & 0x1f;
if !plaintext_mode {
if PacketHeader::long_header(first) {
first ^= mask[0] & 0x0f;
} else {
first ^= mask[0] & 0x1f;
}
}

// Remove protection of packet number field
let pkt_num_len = usize::from((first & PKT_NUM_LEN_MASK) + 1);
let pkt_num_buf = &mut pkt_buf[pkt_num_offset..pkt_num_offset + pkt_num_len];
for i in 0..pkt_num_len {
pkt_num_buf[i] ^= mask[i + 1];

// Remove protection of packet number field
if !plaintext_mode {
for i in 0..pkt_num_len {
pkt_num_buf[i] ^= mask[i + 1];
}
}

// Extract packet number corresponding to the length.
Expand Down Expand Up @@ -1339,7 +1346,7 @@ mod tests {

// Decrypt QUIC packet header on the server
let (open, _) = tls::derive_initial_secrets(&hdr.dcid, hdr.version, true)?;
decrypt_header(&mut pkt[..], pkt_num_off, &mut hdr, &open)?;
decrypt_header(&mut pkt[..], pkt_num_off, &mut hdr, &open, false)?;
assert_eq!(hdr.pkt_num_len, 4);

hdr.pkt_num = decode_packet_num(0, hdr.pkt_num, hdr.pkt_num_len);
Expand Down Expand Up @@ -1406,7 +1413,7 @@ mod tests {

// Decrypt QUIC packet header on the client
let (open, _) = tls::derive_initial_secrets(&odcid, hdr.version, false)?;
decrypt_header(&mut pkt[..], pkt_num_off, &mut hdr, &open)?;
decrypt_header(&mut pkt[..], pkt_num_off, &mut hdr, &open, false)?;
assert_eq!(hdr.pkt_num_len, 2);

hdr.pkt_num = decode_packet_num(0, hdr.pkt_num, hdr.pkt_num_len);
Expand Down Expand Up @@ -1516,7 +1523,7 @@ mod tests {
assert_eq!(hdr.key_phase, pkt_hdr.key_phase);

let open = Open::new_with_secret(tls::Algorithm::ChaCha20Poly1305, secret.to_vec())?;
decrypt_header(&mut out, read, &mut hdr, &open)?;
decrypt_header(&mut out, read, &mut hdr, &open, false)?;
assert_eq!(hdr.pkt_num_len, pkt_hdr.pkt_num_len);
assert_eq!(hdr.pkt_num, pkt_hdr.pkt_num);

Expand Down Expand Up @@ -1557,7 +1564,7 @@ mod tests {
key_phase: false,
};
assert_eq!(
decrypt_header(bw, 10, &mut hdr, &open),
decrypt_header(bw, 10, &mut hdr, &open, false),
Err(Error::BufferTooShort)
);
assert_eq!(
Expand Down
18 changes: 18 additions & 0 deletions src/trans_param.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,12 @@ pub struct TransportParams {
/// This parameter has a zero-length value.
/// See draft-ietf-quic-multipath-05.
pub enable_multipath: bool,

/// The parameter is used to negotiate the disablement of encryption on 1-RTT
/// packets. It is only meant to be used in environments where both endpoints
/// completely trust the path between themselves.
/// See draft-banks-quic-disable-encryption-00.
pub disable_encryption: bool,
}

impl TransportParams {
Expand Down Expand Up @@ -254,6 +260,10 @@ impl TransportParams {
tp.enable_multipath = true;
}

0xbaad => {
tp.disable_encryption = true;
}

// Ignore unknown parameters.
_ => (),
}
Expand Down Expand Up @@ -387,6 +397,11 @@ impl TransportParams {
buf.write_varint(0)?;
}

if tp.disable_encryption {
buf.write_varint(0xbaad)?;
buf.write_varint(0)?;
}

Ok(len - buf.len())
}

Expand Down Expand Up @@ -464,6 +479,7 @@ impl Default for TransportParams {
retry_source_connection_id: None,

enable_multipath: false,
disable_encryption: false,
}
}
}
Expand Down Expand Up @@ -563,6 +579,7 @@ mod tests {
initial_source_connection_id: Some(ConnectionId::random()),
retry_source_connection_id: None,
enable_multipath: true,
disable_encryption: false,
};

// encode on the client side
Expand Down Expand Up @@ -606,6 +623,7 @@ mod tests {
initial_source_connection_id: Some(ConnectionId::random()),
retry_source_connection_id: Some(ConnectionId::random()),
enable_multipath: false,
disable_encryption: true,
};

// encode on the server side
Expand Down
5 changes: 5 additions & 0 deletions tools/src/bin/tquic_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,10 @@ pub struct ClientOpt {
#[clap(long, default_value = "1", value_name = "NUM", help_heading = "Misc")]
pub send_batch_size: usize,

/// Disable encryption on 1-RTT packets.
#[clap(long, help_heading = "Misc")]
pub disable_encryption: bool,

/// Number of max samples per thread used for request time statistics.
#[clap(
long,
Expand Down Expand Up @@ -542,6 +546,7 @@ impl Worker {
config.enable_multipath(option.enable_multipath);
config.set_multipath_algorithm(option.multipath_algor);
config.set_active_connection_id_limit(option.active_cid_limit);
config.enable_encryption(!option.disable_encryption);
let tls_config = TlsConfig::new_client_config(
ApplicationProto::convert_to_vec(&option.alpn),
option.enable_early_data,
Expand Down
5 changes: 5 additions & 0 deletions tools/src/bin/tquic_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,10 @@ pub struct ServerOpt {
help_heading = "Misc"
)]
pub zerortt_buffer_size: usize,

/// Disable encryption on 1-RTT packets.
#[clap(long, help_heading = "Misc")]
pub disable_encryption: bool,
}

const MAX_BUF_SIZE: usize = 65536;
Expand Down Expand Up @@ -286,6 +290,7 @@ impl Server {
config.enable_multipath(option.enable_multipath);
config.set_multipath_algorithm(option.multipath_algor);
config.set_active_connection_id_limit(option.active_cid_limit);
config.enable_encryption(!option.disable_encryption);

if let Some(address_token_key) = &option.address_token_key {
let address_token_key = convert_address_token_key(address_token_key);
Expand Down

0 comments on commit 65365a0

Please sign in to comment.