Skip to content

Commit

Permalink
proto: validate ServerConfig crypto provider
Browse files Browse the repository at this point in the history
Signed-off-by: gabrik <gabriele.baldoni@gmail.com>
  • Loading branch information
gabrik authored and djc committed Apr 22, 2024
1 parent e6d4897 commit ce13559
Show file tree
Hide file tree
Showing 7 changed files with 95 additions and 44 deletions.
4 changes: 2 additions & 2 deletions perf/src/bin/perf_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::{fs, net::SocketAddr, path::PathBuf, sync::Arc, time::Duration};
use anyhow::{Context, Result};
use bytes::Bytes;
use clap::Parser;
use quinn::TokioRuntime;
use quinn::{crypto::rustls::QuicServerConfig, TokioRuntime};
use rustls::pki_types::{CertificateDer, PrivatePkcs8KeyDer};
use tracing::{debug, error, info};

Expand Down Expand Up @@ -94,7 +94,7 @@ async fn run(opt: Opt) -> Result<()> {
let mut transport = quinn::TransportConfig::default();
transport.initial_mtu(opt.initial_mtu);

let crypto = Arc::new(crypto);
let crypto = Arc::new(QuicServerConfig::try_from(crypto)?);
let mut config = quinn::ServerConfig::with_crypto(match opt.no_protection {
true => Arc::new(NoProtectionServerConfig::new(crypto)),
false => crypto,
Expand Down
10 changes: 7 additions & 3 deletions perf/src/noprotection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@ use std::sync::Arc;
use bytes::BytesMut;

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

Expand Down Expand Up @@ -49,11 +53,11 @@ impl NoProtectionClientConfig {
}

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

impl NoProtectionServerConfig {
pub fn new(config: Arc<rustls::ServerConfig>) -> Self {
pub fn new(config: Arc<QuicServerConfig>) -> Self {
Self { inner: config }
}
}
Expand Down
7 changes: 4 additions & 3 deletions quinn-proto/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use thiserror::Error;
use crate::{
cid_generator::{ConnectionIdGenerator, HashedConnectionIdGenerator},
congestion,
crypto::{self, HandshakeTokenKey, HmacKey},
crypto::{self, rustls::QuicServerConfig, HandshakeTokenKey, HmacKey},
VarInt, VarIntBoundsExceeded, DEFAULT_SUPPORTED_VERSIONS, INITIAL_MTU, MAX_UDP_PAYLOAD,
};

Expand Down Expand Up @@ -849,8 +849,9 @@ impl ServerConfig {
cert_chain: Vec<CertificateDer<'static>>,
key: PrivateKeyDer<'static>,
) -> Result<Self, rustls::Error> {
let crypto = crypto::rustls::server_config(cert_chain, key)?;
Ok(Self::with_crypto(Arc::new(crypto)))
Ok(Self::with_crypto(Arc::new(QuicServerConfig::new(
cert_chain, key,
))))
}
}

Expand Down
98 changes: 70 additions & 28 deletions quinn-proto/src/crypto/rustls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -353,24 +353,88 @@ impl std::fmt::Display for NoInitialCipherSuite {

impl std::error::Error for NoInitialCipherSuite {}

impl crypto::ServerConfig for rustls::ServerConfig {
/// A QUIC-compatible TLS server configuration
///
/// Can be constructed via [`ServerConfig::with_single_cert()`][single] or by using the
/// [`TryFrom`] implementation with a custom [`rustls::ServerConfig`]. A pre-existing
/// `ServerConfig` must have TLS 1.3 support enabled for this to work. 0-RTT support is
/// available to clients if `max_early_data_size` is set to `u32::MAX`.
///
/// [single]: crate::config::ServerConfig::with_single_cert()
pub struct QuicServerConfig {
inner: Arc<rustls::ServerConfig>,
initial: Suite,
}

impl QuicServerConfig {
pub(crate) fn new(
cert_chain: Vec<CertificateDer<'static>>,
key: PrivateKeyDer<'static>,
) -> Self {
let inner = Self::inner(cert_chain, key);
Self {
// We're confident that the *ring* default provider contains TLS13_AES_128_GCM_SHA256
initial: initial_suite_from_provider(inner.crypto_provider())
.expect("no initial cipher suite found"),
inner: Arc::new(inner),
}
}

/// Initialize a sane QUIC-compatible TLS server configuration
///
/// QUIC requires that TLS 1.3 be enabled, and that the maximum early data size is either 0 or
/// `u32::MAX`. Advanced users can use any [`rustls::ServerConfig`] that satisfies these
/// requirements.
pub(crate) fn inner(
cert_chain: Vec<CertificateDer<'static>>,
key: PrivateKeyDer<'static>,
) -> rustls::ServerConfig {
let mut inner = rustls::ServerConfig::builder_with_provider(
rustls::crypto::ring::default_provider().into(),
)
.with_protocol_versions(&[&rustls::version::TLS13])
.unwrap() // The *ring* default provider supports TLS 1.3
.with_no_client_auth()
.with_single_cert(cert_chain, key)
.unwrap();

inner.max_early_data_size = u32::MAX;
inner
}
}

impl TryFrom<rustls::ServerConfig> for QuicServerConfig {
type Error = NoInitialCipherSuite;

fn try_from(inner: rustls::ServerConfig) -> Result<Self, Self::Error> {
Ok(Self {
initial: initial_suite_from_provider(inner.crypto_provider())
.ok_or(NoInitialCipherSuite(()))?,
inner: Arc::new(inner),
})
}
}

impl crypto::ServerConfig for QuicServerConfig {
fn start_session(
self: Arc<Self>,
version: u32,
params: &TransportParameters,
) -> Box<dyn crypto::Session> {
// Safe: `start_session()` is never called if `initial_keys()` rejected `version`
let version = interpret_version(version).unwrap();
// TODO: validate the configuration before this point
let suite = initial_suite_from_provider(self.crypto_provider()).unwrap();
Box::new(TlsSession {
version,
got_handshake_data: false,
next_secrets: None,
inner: rustls::quic::Connection::Server(
rustls::quic::ServerConnection::new(self, version, to_vec(params)).unwrap(),
rustls::quic::ServerConnection::new(self.inner.clone(), version, to_vec(params))
.unwrap(),
),
suite,
suite: Suite {
suite: self.initial.suite,
quic: self.initial.quic,
},
})
}

Expand All @@ -380,9 +444,7 @@ impl crypto::ServerConfig for rustls::ServerConfig {
dst_cid: &ConnectionId,
) -> Result<Keys, UnsupportedVersion> {
let version = interpret_version(version)?;
// TODO: validate the configuration before this point
let suite = initial_suite_from_provider(self.crypto_provider()).unwrap();
Ok(initial_keys(version, dst_cid, Side::Server, &suite))
Ok(initial_keys(version, dst_cid, Side::Server, &self.initial))
}

fn retry_tag(&self, version: u32, orig_dst_cid: &ConnectionId, packet: &[u8]) -> [u8; 16] {
Expand Down Expand Up @@ -486,26 +548,6 @@ impl crypto::PacketKey for Box<dyn PacketKey> {
}
}

/// Initialize a sane QUIC-compatible TLS server configuration
///
/// QUIC requires that TLS 1.3 be enabled, and that the maximum early data size is either 0 or
/// `u32::MAX`. Advanced users can use any [`rustls::ServerConfig`] that satisfies these
/// requirements.
pub(crate) fn server_config(
cert_chain: Vec<CertificateDer<'static>>,
key: PrivateKeyDer<'static>,
) -> Result<rustls::ServerConfig, Error> {
let mut cfg = rustls::ServerConfig::builder_with_provider(
rustls::crypto::ring::default_provider().into(),
)
.with_protocol_versions(&[&rustls::version::TLS13])
.unwrap() // The *ring* default provider supports TLS 1.3
.with_no_client_auth()
.with_single_cert(cert_chain, key)?;
cfg.max_early_data_size = u32::MAX;
Ok(cfg)
}

fn interpret_version(version: u32) -> Result<Version, UnsupportedVersion> {
match version {
0xff00_001d..=0xff00_0020 => Ok(Version::V1Draft),
Expand Down
2 changes: 2 additions & 0 deletions quinn-proto/src/tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ use tracing::info;
use super::*;
use crate::{
cid_generator::{ConnectionIdGenerator, RandomConnectionIdGenerator},
crypto::rustls::QuicServerConfig,
frame::FrameStruct,
transport_parameters::TransportParameters,
};
Expand Down Expand Up @@ -455,6 +456,7 @@ fn reject_missing_client_cert() {
)
.with_single_cert(vec![cert], PrivateKeyDer::from(key))
.unwrap();
let config = QuicServerConfig::try_from(config).unwrap();

let mut pair = Pair::new(
Default::default(),
Expand Down
14 changes: 7 additions & 7 deletions quinn-proto/src/tests/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use rustls::{
};
use tracing::{info_span, trace};

use super::crypto::rustls::QuicClientConfig;
use super::crypto::rustls::{QuicClientConfig, QuicServerConfig};
use super::*;

pub(super) const DEFAULT_MTU: usize = 1452;
Expand Down Expand Up @@ -561,38 +561,38 @@ pub(super) fn server_config_with_cert(
ServerConfig::with_crypto(Arc::new(server_crypto_with_cert(cert, key)))
}

pub(super) fn server_crypto() -> rustls::ServerConfig {
pub(super) fn server_crypto() -> QuicServerConfig {
server_crypto_inner(None, None)
}

pub(super) fn server_crypto_with_alpn(alpn: Vec<Vec<u8>>) -> rustls::ServerConfig {
pub(super) fn server_crypto_with_alpn(alpn: Vec<Vec<u8>>) -> QuicServerConfig {
server_crypto_inner(None, Some(alpn))
}

pub(super) fn server_crypto_with_cert(
cert: CertificateDer<'static>,
key: PrivateKeyDer<'static>,
) -> rustls::ServerConfig {
) -> QuicServerConfig {
server_crypto_inner(Some((cert, key)), None)
}

fn server_crypto_inner(
identity: Option<(CertificateDer<'static>, PrivateKeyDer<'static>)>,
alpn: Option<Vec<Vec<u8>>>,
) -> rustls::ServerConfig {
) -> QuicServerConfig {
let (cert, key) = identity.unwrap_or_else(|| {
(
CERTIFIED_KEY.cert.der().clone(),
PrivateKeyDer::Pkcs8(CERTIFIED_KEY.key_pair.serialize_der().into()),
)
});

let mut config = crate::crypto::rustls::server_config(vec![cert], key).unwrap();
let mut config = QuicServerConfig::inner(vec![cert], key);
if let Some(alpn) = alpn {
config.alpn_protocols = alpn;
}

config
config.try_into().unwrap()
}

pub(super) fn client_config() -> ClientConfig {
Expand Down
4 changes: 3 additions & 1 deletion quinn/examples/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use std::{

use anyhow::{anyhow, bail, Context, Result};
use clap::Parser;
use proto::crypto::rustls::QuicServerConfig;
use rustls::pki_types::{CertificateDer, PrivateKeyDer, PrivatePkcs8KeyDer};
use tracing::{error, info, info_span};
use tracing_futures::Instrument as _;
Expand Down Expand Up @@ -123,7 +124,8 @@ async fn run(options: Opt) -> Result<()> {
server_crypto.key_log = Arc::new(rustls::KeyLogFile::new());
}

let mut server_config = quinn::ServerConfig::with_crypto(Arc::new(server_crypto));
let mut server_config =
quinn::ServerConfig::with_crypto(Arc::new(QuicServerConfig::try_from(server_crypto)?));
let transport_config = Arc::get_mut(&mut server_config.transport).unwrap();
transport_config.max_concurrent_uni_streams(0_u8.into());

Expand Down

0 comments on commit ce13559

Please sign in to comment.