diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 4b4e4b3d3..8457d1ee5 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -55,6 +55,16 @@ jobs: - run: cargo test --manifest-path fuzz/Cargo.toml if: ${{ matrix.rust }} == "stable" + test-aws-lc-rs: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + - uses: Swatinem/rust-cache@v2 + # Prevent feature unification from selecting *ring* as the crypto provider + - run: RUST_BACKTRACE=1 cargo test --manifest-path quinn-proto/Cargo.toml --no-default-features --features rustls-aws-lc-rs + - run: RUST_BACKTRACE=1 cargo test --manifest-path quinn/Cargo.toml --no-default-features --features rustls-aws-lc-rs,runtime-tokio + msrv: runs-on: ubuntu-latest steps: diff --git a/Cargo.toml b/Cargo.toml index e76ebcc56..d9dc1cde7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,6 +17,7 @@ arbitrary = { version = "1.0.1", features = ["derive"] } async-io = "2" async-std = "1.11" assert_matches = "1.1" +aws-lc-rs = { version = "1.9", default-features = false } bencher = "0.1.5" bytes = "1" clap = { version = "4", features = ["derive"] } @@ -33,7 +34,7 @@ rand = "0.8" rcgen = "0.13" ring = "0.17" rustc-hash = "2" -rustls = { version = "0.23.5", default-features = false, features = ["ring", "std"] } +rustls = { version = "0.23.5", default-features = false, features = ["std"] } rustls-pemfile = "2" rustls-platform-verifier = "0.3" serde = { version = "1.0", features = ["derive"] } diff --git a/deny.toml b/deny.toml index 43038b9ad..8998d6244 100644 --- a/deny.toml +++ b/deny.toml @@ -6,9 +6,9 @@ allow = [ "ISC", "MIT", "MPL-2.0", + "OpenSSL", "Unicode-DFS-2016", ] -exceptions = [{ allow = ["ISC", "MIT", "OpenSSL"], name = "ring" }] private = { ignore = true } [[licenses.clarify]] diff --git a/quinn-proto/Cargo.toml b/quinn-proto/Cargo.toml index 1b757fa72..9a67f02cd 100644 --- a/quinn-proto/Cargo.toml +++ b/quinn-proto/Cargo.toml @@ -14,8 +14,14 @@ workspace = ".." all-features = true [features] -default = ["rustls", "log"] -rustls = ["dep:rustls", "ring"] +default = ["rustls-ring", "log"] +aws-lc-rs = ["dep:aws-lc-rs", "aws-lc-rs/aws-lc-sys", "aws-lc-rs/prebuilt-nasm"] +# For backwards compatibility, `rustls` forwards to `rustls-ring` +rustls = ["rustls-ring"] +# Enable rustls with the `aws-lc-rs` crypto provider +rustls-aws-lc-rs = ["dep:rustls", "rustls/aws-lc-rs", "aws-lc-rs"] +# Enable rustls with the `ring` crypto provider +rustls-ring = ["dep:rustls", "rustls/ring", "ring"] ring = ["dep:ring"] # Enable rustls ring provider and direct ring usage # Provides `ClientConfig::with_platform_verifier()` convenience method @@ -25,6 +31,7 @@ log = ["tracing/log"] [dependencies] arbitrary = { workspace = true, optional = true } +aws-lc-rs = { workspace = true, optional = true } bytes = { workspace = true } rustc-hash = { workspace = true } rand = { workspace = true } @@ -32,7 +39,7 @@ ring = { workspace = true, optional = true } rustls = { workspace = true, optional = true } rustls-platform-verifier = { workspace = true, optional = true } slab = { workspace = true } -thiserror = { workspace= true } +thiserror = { workspace = true } tinyvec = { workspace = true, features = ["alloc"] } tracing = { workspace = true } diff --git a/quinn-proto/src/config.rs b/quinn-proto/src/config.rs index 557ae2cf1..57f38e160 100644 --- a/quinn-proto/src/config.rs +++ b/quinn-proto/src/config.rs @@ -6,16 +6,14 @@ use std::{ time::Duration, }; -#[cfg(feature = "ring")] -use rand::RngCore; -#[cfg(feature = "rustls")] +#[cfg(any(feature = "rustls-aws-lc-rs", feature = "rustls-ring"))] use rustls::client::WebPkiServerVerifier; -#[cfg(feature = "rustls")] +#[cfg(any(feature = "rustls-aws-lc-rs", feature = "rustls-ring"))] use rustls::pki_types::{CertificateDer, PrivateKeyDer}; use thiserror::Error; -#[cfg(feature = "rustls")] -use crate::crypto::rustls::QuicServerConfig; +#[cfg(any(feature = "rustls-aws-lc-rs", feature = "rustls-ring"))] +use crate::crypto::rustls::{configured_provider, QuicServerConfig}; use crate::{ cid_generator::{ConnectionIdGenerator, HashedConnectionIdGenerator}, congestion, @@ -759,16 +757,19 @@ impl fmt::Debug for EndpointConfig { } } -#[cfg(feature = "ring")] +#[cfg(any(feature = "aws-lc-rs", feature = "ring"))] impl Default for EndpointConfig { fn default() -> Self { + #[cfg(all(feature = "aws-lc-rs", not(feature = "ring")))] + use aws_lc_rs::hmac; + use rand::RngCore; + #[cfg(feature = "ring")] + use ring::hmac; + let mut reset_key = [0; 64]; rand::thread_rng().fill_bytes(&mut reset_key); - Self::new(Arc::new(ring::hmac::Key::new( - ring::hmac::HMAC_SHA256, - &reset_key, - ))) + Self::new(Arc::new(hmac::Key::new(hmac::HMAC_SHA256, &reset_key))) } } @@ -919,7 +920,7 @@ impl ServerConfig { } } -#[cfg(feature = "rustls")] +#[cfg(any(feature = "rustls-aws-lc-rs", feature = "rustls-ring"))] impl ServerConfig { /// Create a server config with the given certificate chain to be presented to clients /// @@ -934,16 +935,22 @@ impl ServerConfig { } } -#[cfg(feature = "ring")] +#[cfg(any(feature = "aws-lc-rs", feature = "ring"))] impl ServerConfig { /// Create a server config with the given [`crypto::ServerConfig`] /// /// Uses a randomized handshake token key. pub fn with_crypto(crypto: Arc) -> Self { + #[cfg(all(feature = "aws-lc-rs", not(feature = "ring")))] + use aws_lc_rs::hkdf; + use rand::RngCore; + #[cfg(feature = "ring")] + use ring::hkdf; + let rng = &mut rand::thread_rng(); let mut master_key = [0u8; 64]; rng.fill_bytes(&mut master_key); - let master_key = ring::hkdf::Salt::new(ring::hkdf::HKDF_SHA256, &[]).extract(&master_key); + let master_key = hkdf::Salt::new(hkdf::HKDF_SHA256, &[]).extract(&master_key); Self::new(crypto, Arc::new(master_key)) } @@ -1030,7 +1037,7 @@ impl ClientConfig { } } -#[cfg(feature = "rustls")] +#[cfg(any(feature = "rustls-aws-lc-rs", feature = "rustls-ring"))] impl ClientConfig { /// Create a client configuration that trusts the platform's native roots #[cfg(feature = "platform-verifier")] @@ -1045,7 +1052,7 @@ impl ClientConfig { roots: Arc, ) -> Result { Ok(Self::new(Arc::new(crypto::rustls::QuicClientConfig::new( - WebPkiServerVerifier::builder(roots).build()?, + WebPkiServerVerifier::builder_with_provider(roots, configured_provider()).build()?, )))) } } diff --git a/quinn-proto/src/crypto.rs b/quinn-proto/src/crypto.rs index 17229708a..f15d740c0 100644 --- a/quinn-proto/src/crypto.rs +++ b/quinn-proto/src/crypto.rs @@ -18,10 +18,10 @@ use crate::{ }; /// Cryptography interface based on *ring* -#[cfg(feature = "ring")] -pub(crate) mod ring; +#[cfg(any(feature = "aws-lc-rs", feature = "ring"))] +pub(crate) mod ring_like; /// TLS interface based on rustls -#[cfg(feature = "rustls")] +#[cfg(any(feature = "rustls-aws-lc-rs", feature = "rustls-ring"))] pub mod rustls; /// A cryptographic session (commonly TLS) diff --git a/quinn-proto/src/crypto/ring.rs b/quinn-proto/src/crypto/ring_like.rs similarity index 92% rename from quinn-proto/src/crypto/ring.rs rename to quinn-proto/src/crypto/ring_like.rs index aaeaaafbb..1b5f301bf 100644 --- a/quinn-proto/src/crypto/ring.rs +++ b/quinn-proto/src/crypto/ring_like.rs @@ -1,3 +1,6 @@ +#[cfg(all(feature = "aws-lc-rs", not(feature = "ring")))] +use aws_lc_rs::{aead, error, hkdf, hmac}; +#[cfg(feature = "ring")] use ring::{aead, error, hkdf, hmac}; use crate::crypto::{self, CryptoError}; diff --git a/quinn-proto/src/crypto/rustls.rs b/quinn-proto/src/crypto/rustls.rs index 214ad83b3..b84151811 100644 --- a/quinn-proto/src/crypto/rustls.rs +++ b/quinn-proto/src/crypto/rustls.rs @@ -1,6 +1,9 @@ use std::{any::Any, io, str, sync::Arc}; +#[cfg(all(feature = "aws-lc-rs", not(feature = "ring")))] +use aws_lc_rs::aead; use bytes::BytesMut; +#[cfg(feature = "ring")] use ring::aead; pub use rustls::Error; use rustls::{ @@ -307,14 +310,12 @@ impl QuicClientConfig { } pub(crate) fn inner(verifier: Arc) -> rustls::ClientConfig { - let mut config = rustls::ClientConfig::builder_with_provider( - rustls::crypto::ring::default_provider().into(), - ) - .with_protocol_versions(&[&rustls::version::TLS13]) - .unwrap() // The *ring* default provider supports TLS 1.3 - .dangerous() - .with_custom_certificate_verifier(verifier) - .with_no_client_auth(); + let mut config = rustls::ClientConfig::builder_with_provider(configured_provider()) + .with_protocol_versions(&[&rustls::version::TLS13]) + .unwrap() // The default providers support TLS 1.3 + .dangerous() + .with_custom_certificate_verifier(verifier) + .with_no_client_auth(); config.enable_early_data = true; config @@ -446,13 +447,11 @@ impl QuicServerConfig { cert_chain: Vec>, key: PrivateKeyDer<'static>, ) -> Result { - 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)?; + let mut inner = rustls::ServerConfig::builder_with_provider(configured_provider()) + .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)?; inner.max_early_data_size = u32::MAX; Ok(inner) @@ -549,6 +548,14 @@ pub(crate) fn initial_suite_from_provider( .flatten() } +pub(crate) fn configured_provider() -> Arc { + #[cfg(all(feature = "aws-lc-rs", not(feature = "ring")))] + let provider = rustls::crypto::aws_lc_rs::default_provider(); + #[cfg(feature = "ring")] + let provider = rustls::crypto::ring::default_provider(); + Arc::new(provider) +} + fn to_vec(params: &TransportParameters) -> Vec { let mut bytes = Vec::new(); params.write(&mut bytes); diff --git a/quinn-proto/src/lib.rs b/quinn-proto/src/lib.rs index bb39101b7..02a9d2243 100644 --- a/quinn-proto/src/lib.rs +++ b/quinn-proto/src/lib.rs @@ -32,7 +32,7 @@ mod cid_queue; pub mod coding; mod constant_time; mod range_set; -#[cfg(all(test, feature = "rustls"))] +#[cfg(all(test, any(feature = "rustls-aws-lc-rs", feature = "rustls-ring")))] mod tests; pub mod transport_parameters; mod varint; diff --git a/quinn-proto/src/packet.rs b/quinn-proto/src/packet.rs index ee1babcc7..9135bd65b 100644 --- a/quinn-proto/src/packet.rs +++ b/quinn-proto/src/packet.rs @@ -898,8 +898,6 @@ impl SpaceId { #[cfg(test)] mod tests { use super::*; - #[cfg(feature = "rustls")] - use crate::DEFAULT_SUPPORTED_VERSIONS; use hex_literal::hex; use std::io; @@ -936,7 +934,7 @@ mod tests { } } - #[cfg(feature = "rustls")] + #[cfg(any(feature = "rustls-aws-lc-rs", feature = "rustls-ring"))] #[test] fn header_encoding() { use crate::crypto::rustls::{initial_keys, initial_suite_from_provider}; @@ -955,7 +953,7 @@ mod tests { src_cid: ConnectionId::new(&[]), dst_cid: dcid, token: Bytes::new(), - version: DEFAULT_SUPPORTED_VERSIONS[0], + version: crate::DEFAULT_SUPPORTED_VERSIONS[0], }); let encode = header.encode(&mut buf); let header_len = buf.len(); @@ -979,7 +977,7 @@ mod tests { ); let server = initial_keys(Version::V1, &dcid, Side::Server, &suite); - let supported_versions = DEFAULT_SUPPORTED_VERSIONS.to_vec(); + let supported_versions = crate::DEFAULT_SUPPORTED_VERSIONS.to_vec(); let decode = PartialDecode::new( buf.as_slice().into(), &FixedLengthConnectionIdParser::new(0), diff --git a/quinn-proto/src/tests/mod.rs b/quinn-proto/src/tests/mod.rs index 50bb6ef0e..6e2336b34 100644 --- a/quinn-proto/src/tests/mod.rs +++ b/quinn-proto/src/tests/mod.rs @@ -7,9 +7,12 @@ use std::{ }; use assert_matches::assert_matches; +#[cfg(all(feature = "aws-lc-rs", not(feature = "ring")))] +use aws_lc_rs::hmac; use bytes::{Bytes, BytesMut}; use hex_literal::hex; use rand::RngCore; +#[cfg(feature = "ring")] use ring::hmac; use rustls::{ pki_types::{CertificateDer, PrivateKeyDer, PrivatePkcs8KeyDer}, diff --git a/quinn-proto/src/tests/util.rs b/quinn-proto/src/tests/util.rs index 5c66a263d..b293057ed 100644 --- a/quinn-proto/src/tests/util.rs +++ b/quinn-proto/src/tests/util.rs @@ -21,7 +21,7 @@ use rustls::{ }; use tracing::{info_span, trace}; -use super::crypto::rustls::{QuicClientConfig, QuicServerConfig}; +use super::crypto::rustls::{configured_provider, QuicClientConfig, QuicServerConfig}; use super::*; pub(super) const DEFAULT_MTU: usize = 1452; @@ -640,7 +640,7 @@ fn client_crypto_inner( } let mut inner = QuicClientConfig::inner( - WebPkiServerVerifier::builder(Arc::new(roots)) + WebPkiServerVerifier::builder_with_provider(Arc::new(roots), configured_provider()) .build() .unwrap(), ); diff --git a/quinn-proto/src/token.rs b/quinn-proto/src/token.rs index 8b63d43ea..4fa603295 100644 --- a/quinn-proto/src/token.rs +++ b/quinn-proto/src/token.rs @@ -164,9 +164,13 @@ impl fmt::Display for ResetToken { } } -#[cfg(test)] +#[cfg(all(test, any(feature = "aws-lc-rs", feature = "ring")))] mod test { + #[cfg(all(feature = "aws-lc-rs", not(feature = "ring")))] + use aws_lc_rs::hkdf; #[cfg(feature = "ring")] + use ring::hkdf; + #[test] fn token_sanity() { use super::*; @@ -184,7 +188,7 @@ mod test { let mut master_key = [0; 64]; rng.fill_bytes(&mut master_key); - let prk = ring::hkdf::Salt::new(ring::hkdf::HKDF_SHA256, &[]).extract(&master_key); + let prk = hkdf::Salt::new(hkdf::HKDF_SHA256, &[]).extract(&master_key); let addr = SocketAddr::new(Ipv6Addr::LOCALHOST.into(), 4433); let retry_src_cid = RandomConnectionIdGenerator::new(MAX_CID_SIZE).generate_cid(); @@ -200,7 +204,6 @@ mod test { assert_eq!(token.issued, decoded.issued); } - #[cfg(feature = "ring")] #[test] fn invalid_token_returns_err() { use super::*; @@ -214,7 +217,7 @@ mod test { let mut master_key = [0; 64]; rng.fill_bytes(&mut master_key); - let prk = ring::hkdf::Salt::new(ring::hkdf::HKDF_SHA256, &[]).extract(&master_key); + let prk = hkdf::Salt::new(hkdf::HKDF_SHA256, &[]).extract(&master_key); let addr = SocketAddr::new(Ipv6Addr::LOCALHOST.into(), 4433); let retry_src_cid = RandomConnectionIdGenerator::new(MAX_CID_SIZE).generate_cid(); diff --git a/quinn/Cargo.toml b/quinn/Cargo.toml index e64d1235d..a54ef7b91 100644 --- a/quinn/Cargo.toml +++ b/quinn/Cargo.toml @@ -15,12 +15,19 @@ rust-version.workspace = true all-features = true [features] -default = ["log", "platform-verifier", "ring", "runtime-tokio", "rustls"] +default = ["log", "platform-verifier", "runtime-tokio", "rustls-ring"] +# Enables `Endpoint::client` and `Endpoint::server` conveniences +aws-lc-rs = ["proto/aws-lc-rs"] # Records how long locks are held, and warns if they are held >= 1ms lock_tracking = [] # Provides `ClientConfig::with_platform_verifier()` convenience method platform-verifier = ["proto/platform-verifier"] -rustls = ["dep:rustls", "proto/rustls", "proto/ring"] +# For backwards compatibility, `rustls` forwards to `rustls-ring` +rustls = ["rustls-ring"] +# Enable rustls with the `aws-lc-rs` crypto provider +rustls-aws-lc-rs = ["dep:rustls", "aws-lc-rs", "proto/rustls-aws-lc-rs", "proto/aws-lc-rs"] +# Enable rustls with the `ring` crypto provider +rustls-ring = ["dep:rustls", "ring", "proto/rustls-ring", "proto/ring"] # Enables `Endpoint::client` and `Endpoint::server` conveniences ring = ["proto/ring"] runtime-tokio = ["tokio/time", "tokio/rt", "tokio/net"] diff --git a/quinn/examples/common/mod.rs b/quinn/examples/common/mod.rs index 05e9f37b4..0aa690f8a 100644 --- a/quinn/examples/common/mod.rs +++ b/quinn/examples/common/mod.rs @@ -1,4 +1,4 @@ -#![cfg(feature = "rustls")] +#![cfg(any(feature = "rustls-aws-lc-rs", feature = "rustls-ring"))] //! Commonly used code in most examples. use quinn::{ClientConfig, Endpoint, ServerConfig}; diff --git a/quinn/src/endpoint.rs b/quinn/src/endpoint.rs index 816be0545..7dd203570 100644 --- a/quinn/src/endpoint.rs +++ b/quinn/src/endpoint.rs @@ -12,7 +12,7 @@ use std::{ time::Instant, }; -#[cfg(feature = "ring")] +#[cfg(any(feature = "aws-lc-rs", feature = "ring"))] use crate::runtime::default_runtime; use crate::{ runtime::{AsyncUdpSocket, Runtime}, @@ -25,7 +25,7 @@ use proto::{ EndpointEvent, ServerConfig, }; use rustc_hash::FxHashMap; -#[cfg(feature = "ring")] +#[cfg(any(feature = "aws-lc-rs", feature = "ring"))] use socket2::{Domain, Protocol, Socket, Type}; use tokio::sync::{futures::Notified, mpsc, Notify}; use tracing::{Instrument, Span}; @@ -67,7 +67,7 @@ impl Endpoint { /// /// Some environments may not allow creation of dual-stack sockets, in which case an IPv6 /// client will only be able to connect to IPv6 servers. An IPv4 client is never dual-stack. - #[cfg(feature = "ring")] + #[cfg(any(feature = "aws-lc-rs", feature = "ring"))] // `EndpointConfig::default()` is only available with these pub fn client(addr: SocketAddr) -> io::Result { let socket = Socket::new(Domain::for_address(addr), Type::DGRAM, Some(Protocol::UDP))?; if addr.is_ipv6() { @@ -97,7 +97,7 @@ impl Endpoint { /// IPv6 address on Windows will not by default be able to communicate with IPv4 /// addresses. Portable applications should bind an address that matches the family they wish to /// communicate within. - #[cfg(feature = "ring")] + #[cfg(any(feature = "aws-lc-rs", feature = "ring"))] // `EndpointConfig::default()` is only available with these pub fn server(config: ServerConfig, addr: SocketAddr) -> io::Result { let socket = std::net::UdpSocket::bind(addr)?; let runtime = default_runtime() diff --git a/quinn/src/lib.rs b/quinn/src/lib.rs index 636e4f6b0..e51e275dd 100644 --- a/quinn/src/lib.rs +++ b/quinn/src/lib.rs @@ -66,7 +66,7 @@ pub use proto::{ ConfigError, ConnectError, ConnectionClose, ConnectionError, ConnectionStats, EndpointConfig, IdleTimeout, MtuDiscoveryConfig, ServerConfig, StreamId, Transmit, TransportConfig, VarInt, }; -#[cfg(feature = "rustls")] +#[cfg(any(feature = "rustls-aws-lc-rs", feature = "rustls-ring"))] pub use rustls; pub use udp; diff --git a/quinn/src/tests.rs b/quinn/src/tests.rs index fe83d8342..dca2e111d 100755 --- a/quinn/src/tests.rs +++ b/quinn/src/tests.rs @@ -1,4 +1,4 @@ -#![cfg(feature = "rustls")] +#![cfg(any(feature = "rustls-aws-lc-rs", feature = "rustls-ring"))] use std::{ convert::TryInto, diff --git a/quinn/tests/many_connections.rs b/quinn/tests/many_connections.rs index ff98539ab..b97e58fb1 100644 --- a/quinn/tests/many_connections.rs +++ b/quinn/tests/many_connections.rs @@ -1,4 +1,4 @@ -#![cfg(feature = "rustls")] +#![cfg(any(feature = "rustls-aws-lc-rs", feature = "rustls-ring"))] use std::{ convert::TryInto, sync::{Arc, Mutex},