Skip to content

Commit

Permalink
Add a special crypto session to perf binaries to disable packet
Browse files Browse the repository at this point in the history
encryption / decryption. It can be enabled using --null-cipher
command line option. This can be use when doing performance
tests to reveal other hotspots than packet encryption and
decryption.
  • Loading branch information
stormshield-damiend committed Jan 24, 2023
1 parent af4c4ca commit 19839bd
Show file tree
Hide file tree
Showing 5 changed files with 213 additions and 4 deletions.
1 change: 1 addition & 0 deletions perf/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ json-output = ["serde", "serde_json"]
anyhow = "1.0.22"
hdrhistogram = { version = "7.2", default-features = false }
quinn = { path = "../quinn" }
quinn-proto = { path = "../quinn-proto" }
rcgen = "0.10.0"
rustls = { version = "0.20", default-features = false, features = ["dangerous_configuration"] }
rustls-pemfile = "1.0.0"
Expand Down
11 changes: 9 additions & 2 deletions perf/src/bin/perf_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ use quinn::TokioRuntime;
use tokio::sync::Semaphore;
use tracing::{debug, error, info};

use perf::bind_socket;
use perf::stats::{OpenStreamStats, Stats};
use perf::{bind_socket, null::NullCiperClientConfig};
#[cfg(feature = "json-output")]
use std::path::PathBuf;

Expand Down Expand Up @@ -66,6 +66,9 @@ struct Opt {
/// UDP payload size that the network must be capable of carrying
#[clap(long, default_value = "1200")]
initial_max_udp_payload_size: u16,
/// Disable packet encryption/decryption (for debugging purpose)
#[clap(long = "null-cipher")]
nullcipher: bool,
}

#[tokio::main(flavor = "current_thread")]
Expand Down Expand Up @@ -128,7 +131,11 @@ async fn run(opt: Opt) -> Result<()> {
let mut transport = quinn::TransportConfig::default();
transport.initial_max_udp_payload_size(opt.initial_max_udp_payload_size);

let mut cfg = quinn::ClientConfig::new(Arc::new(crypto));
let mut cfg = if opt.nullcipher {
quinn::ClientConfig::new(Arc::new(NullCiperClientConfig::new(Arc::new(crypto))))
} else {
quinn::ClientConfig::new(Arc::new(crypto))
};
cfg.transport_config(Arc::new(transport));

let stream_stats = OpenStreamStats::default();
Expand Down
12 changes: 10 additions & 2 deletions perf/src/bin/perf_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use clap::Parser;
use quinn::TokioRuntime;
use tracing::{debug, error, info};

use perf::bind_socket;
use perf::{bind_socket, null::NullCiperServerConfig};

#[derive(Parser)]
#[clap(name = "server")]
Expand Down Expand Up @@ -35,6 +35,9 @@ struct Opt {
/// UDP payload size that the network must be capable of carrying
#[clap(long, default_value = "1200")]
initial_max_udp_payload_size: u16,
/// Disable packet encryption/decryption (for debugging purpose)
#[clap(long = "null-cipher")]
nullcipher: bool,
}

#[tokio::main(flavor = "current_thread")]
Expand Down Expand Up @@ -87,7 +90,12 @@ async fn run(opt: Opt) -> Result<()> {
let mut transport = quinn::TransportConfig::default();
transport.initial_max_udp_payload_size(opt.initial_max_udp_payload_size);

let mut server_config = quinn::ServerConfig::with_crypto(Arc::new(crypto));
// FIXME add command line option
let mut server_config = if opt.nullcipher {
quinn::ServerConfig::with_crypto(Arc::new(NullCiperServerConfig::new(Arc::new(crypto))))
} else {
quinn::ServerConfig::with_crypto(Arc::new(crypto))
};
server_config.transport_config(Arc::new(transport));

let socket = bind_socket(opt.listen, opt.send_buffer_size, opt.recv_buffer_size)?;
Expand Down
2 changes: 2 additions & 0 deletions perf/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ use tracing::warn;
#[cfg_attr(not(feature = "json-output"), allow(dead_code))]
pub mod stats;

pub mod null;

pub fn bind_socket(
addr: SocketAddr,
send_buffer_size: usize,
Expand Down
191 changes: 191 additions & 0 deletions perf/src/null.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
use std::sync::Arc;

use bytes::BytesMut;

use quinn_proto::{
crypto::{self, CryptoError},
transport_parameters, ConnectionId, Side, TransportError,
};

/// A rustls TLS session which does not perform packet encryption/decryption (for debugging purpose)
struct NullCipherSession {
inner: Box<dyn crypto::Session>,
}

impl NullCipherSession {
fn new(tls: Box<dyn crypto::Session>) -> Self {
Self { inner: tls }
}
}

struct NullCipherPacketKey {
inner: Box<dyn crypto::PacketKey>,
}

impl NullCipherPacketKey {
fn new(key: Box<dyn crypto::PacketKey>) -> Self {
Self { inner: key }
}
}

pub struct NullCiperClientConfig {
inner: Arc<rustls::ClientConfig>,
}

impl NullCiperClientConfig {
pub fn new(config: Arc<rustls::ClientConfig>) -> Self {
Self { inner: config }
}
}

pub struct NullCiperServerConfig {
inner: Arc<rustls::ServerConfig>,
}

impl NullCiperServerConfig {
pub fn new(config: Arc<rustls::ServerConfig>) -> Self {
Self { inner: config }
}
}

// forward all calls to inner except those related to packet encryption/decryption
impl crypto::Session for NullCipherSession {
fn initial_keys(&self, dst_cid: &ConnectionId, side: Side) -> crypto::Keys {
self.inner.initial_keys(dst_cid, side)
}

fn handshake_data(&self) -> Option<Box<dyn std::any::Any>> {
self.inner.handshake_data()
}

fn peer_identity(&self) -> Option<Box<dyn std::any::Any>> {
self.inner.peer_identity()
}

fn early_crypto(&self) -> Option<(Box<dyn crypto::HeaderKey>, Box<dyn crypto::PacketKey>)> {
let (hkey, pkey) = self.inner.early_crypto()?;

// use wrapper type to disable packet encryption/decryption
Some((hkey, Box::new(NullCipherPacketKey::new(pkey))))
}

fn early_data_accepted(&self) -> Option<bool> {
self.inner.early_data_accepted()
}

fn is_handshaking(&self) -> bool {
self.inner.is_handshaking()
}

fn read_handshake(&mut self, buf: &[u8]) -> Result<bool, TransportError> {
self.inner.read_handshake(buf)
}

fn transport_parameters(
&self,
) -> Result<Option<transport_parameters::TransportParameters>, TransportError> {
self.inner.transport_parameters()
}

fn write_handshake(&mut self, buf: &mut Vec<u8>) -> Option<crypto::Keys> {
self.inner.write_handshake(buf)
}

fn next_1rtt_keys(&mut self) -> Option<crypto::KeyPair<Box<dyn crypto::PacketKey>>> {
let keys = self.inner.next_1rtt_keys()?;

// use wrapper type to disable packet encryption/decryption
Some(crypto::KeyPair {
local: Box::new(NullCipherPacketKey::new(keys.local)),
remote: Box::new(NullCipherPacketKey::new(keys.remote)),
})
}

fn is_valid_retry(&self, orig_dst_cid: &ConnectionId, header: &[u8], payload: &[u8]) -> bool {
self.inner.is_valid_retry(orig_dst_cid, header, payload)
}

fn export_keying_material(
&self,
output: &mut [u8],
label: &[u8],
context: &[u8],
) -> Result<(), crypto::ExportKeyingMaterialError> {
self.inner.export_keying_material(output, label, context)
}
}

impl crypto::ClientConfig for NullCiperClientConfig {
fn start_session(
self: std::sync::Arc<Self>,
version: u32,
server_name: &str,
params: &transport_parameters::TransportParameters,
) -> Result<Box<dyn crypto::Session>, quinn::ConnectError> {
let tls = self
.inner
.clone()
.start_session(version, server_name, params)?;

Ok(Box::new(NullCipherSession::new(tls)))
}
}

impl crypto::ServerConfig for NullCiperServerConfig {
fn initial_keys(
&self,
version: u32,
dst_cid: &ConnectionId,
side: Side,
) -> Result<crypto::Keys, crypto::UnsupportedVersion> {
self.inner.initial_keys(version, dst_cid, side)
}

fn retry_tag(&self, version: u32, orig_dst_cid: &ConnectionId, packet: &[u8]) -> [u8; 16] {
self.inner.retry_tag(version, orig_dst_cid, packet)
}

fn start_session(
self: Arc<Self>,
version: u32,
params: &transport_parameters::TransportParameters,
) -> Box<dyn crypto::Session> {
let tls = self.inner.clone().start_session(version, params);

Box::new(NullCipherSession::new(tls))
}
}

// forward all calls to inner except those related to packet encryption/decryption
impl crypto::PacketKey for NullCipherPacketKey {
fn encrypt(&self, _packet: u64, buf: &mut [u8], header_len: usize) {
let (_header, payload_tag) = buf.split_at_mut(header_len);
let (_payload, tag_storage) =
payload_tag.split_at_mut(payload_tag.len() - self.inner.tag_len());
// packet = identity(packet)
tag_storage.fill(42);
}

fn decrypt(
&self,
_packet: u64,
_header: &[u8],
payload: &mut BytesMut,
) -> Result<(), CryptoError> {
let plain_len = payload.len() - self.inner.tag_len();
payload.truncate(plain_len);
Ok(())
}

fn tag_len(&self) -> usize {
self.inner.tag_len()
}

fn confidentiality_limit(&self) -> u64 {
self.inner.confidentiality_limit()
}

fn integrity_limit(&self) -> u64 {
self.inner.integrity_limit()
}
}

0 comments on commit 19839bd

Please sign in to comment.