From 7c755ef76d9467c52c7260eefb6841988ab4d6f2 Mon Sep 17 00:00:00 2001 From: Harald Gutmann Date: Wed, 9 Apr 2025 17:34:51 +0200 Subject: [PATCH 01/14] tls & crypto: move existing files to submodule --- quiche/src/crypto/{ => boringssl_openssl}/boringssl.rs | 0 quiche/src/crypto/{ => boringssl_openssl}/mod.rs | 0 quiche/src/crypto/{ => boringssl_openssl}/openssl_quictls.rs | 0 quiche/src/tls/{ => boringssl_openssl}/boringssl.rs | 0 quiche/src/tls/{ => boringssl_openssl}/mod.rs | 0 quiche/src/tls/{ => boringssl_openssl}/openssl_quictls.rs | 0 6 files changed, 0 insertions(+), 0 deletions(-) rename quiche/src/crypto/{ => boringssl_openssl}/boringssl.rs (100%) rename quiche/src/crypto/{ => boringssl_openssl}/mod.rs (100%) rename quiche/src/crypto/{ => boringssl_openssl}/openssl_quictls.rs (100%) rename quiche/src/tls/{ => boringssl_openssl}/boringssl.rs (100%) rename quiche/src/tls/{ => boringssl_openssl}/mod.rs (100%) rename quiche/src/tls/{ => boringssl_openssl}/openssl_quictls.rs (100%) diff --git a/quiche/src/crypto/boringssl.rs b/quiche/src/crypto/boringssl_openssl/boringssl.rs similarity index 100% rename from quiche/src/crypto/boringssl.rs rename to quiche/src/crypto/boringssl_openssl/boringssl.rs diff --git a/quiche/src/crypto/mod.rs b/quiche/src/crypto/boringssl_openssl/mod.rs similarity index 100% rename from quiche/src/crypto/mod.rs rename to quiche/src/crypto/boringssl_openssl/mod.rs diff --git a/quiche/src/crypto/openssl_quictls.rs b/quiche/src/crypto/boringssl_openssl/openssl_quictls.rs similarity index 100% rename from quiche/src/crypto/openssl_quictls.rs rename to quiche/src/crypto/boringssl_openssl/openssl_quictls.rs diff --git a/quiche/src/tls/boringssl.rs b/quiche/src/tls/boringssl_openssl/boringssl.rs similarity index 100% rename from quiche/src/tls/boringssl.rs rename to quiche/src/tls/boringssl_openssl/boringssl.rs diff --git a/quiche/src/tls/mod.rs b/quiche/src/tls/boringssl_openssl/mod.rs similarity index 100% rename from quiche/src/tls/mod.rs rename to quiche/src/tls/boringssl_openssl/mod.rs diff --git a/quiche/src/tls/openssl_quictls.rs b/quiche/src/tls/boringssl_openssl/openssl_quictls.rs similarity index 100% rename from quiche/src/tls/openssl_quictls.rs rename to quiche/src/tls/boringssl_openssl/openssl_quictls.rs From a9735a331f375741690357b6337e9a633df83d08 Mon Sep 17 00:00:00 2001 From: Harald Gutmann Date: Mon, 28 Apr 2025 15:17:21 +0200 Subject: [PATCH 02/14] tls & crypto: update tls & crypto mod, fix build --- quiche/src/crypto/mod.rs | 38 +++++++++++++++++++++++++ quiche/src/tls/boringssl_openssl/mod.rs | 2 +- quiche/src/tls/mod.rs | 38 +++++++++++++++++++++++++ 3 files changed, 77 insertions(+), 1 deletion(-) create mode 100644 quiche/src/crypto/mod.rs create mode 100644 quiche/src/tls/mod.rs diff --git a/quiche/src/crypto/mod.rs b/quiche/src/crypto/mod.rs new file mode 100644 index 0000000000..2dcdb959dd --- /dev/null +++ b/quiche/src/crypto/mod.rs @@ -0,0 +1,38 @@ +// Copyright (C) 2018-2025, Cloudflare, Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#[cfg(any( + feature = "boringssl-vendored", + feature = "boringssl-boring-crate", + feature = "openssl" +))] +mod boringssl_openssl; +#[cfg(any( + feature = "boringssl-vendored", + feature = "boringssl-boring-crate", + feature = "openssl" +))] +pub use boringssl_openssl::*; diff --git a/quiche/src/tls/boringssl_openssl/mod.rs b/quiche/src/tls/boringssl_openssl/mod.rs index f72038dd2a..90d9e4848e 100644 --- a/quiche/src/tls/boringssl_openssl/mod.rs +++ b/quiche/src/tls/boringssl_openssl/mod.rs @@ -687,7 +687,7 @@ pub struct ExData<'a> { pub session: &'a mut Option>, - pub local_error: &'a mut Option, + pub local_error: &'a mut Option, pub keylog: Option<&'a mut Box>, diff --git a/quiche/src/tls/mod.rs b/quiche/src/tls/mod.rs new file mode 100644 index 0000000000..2dcdb959dd --- /dev/null +++ b/quiche/src/tls/mod.rs @@ -0,0 +1,38 @@ +// Copyright (C) 2018-2025, Cloudflare, Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#[cfg(any( + feature = "boringssl-vendored", + feature = "boringssl-boring-crate", + feature = "openssl" +))] +mod boringssl_openssl; +#[cfg(any( + feature = "boringssl-vendored", + feature = "boringssl-boring-crate", + feature = "openssl" +))] +pub use boringssl_openssl::*; From cc6e35b421acf0c0f136b9237c974a205303fc48 Mon Sep 17 00:00:00 2001 From: Harald Gutmann Date: Mon, 5 May 2025 08:52:17 +0200 Subject: [PATCH 03/14] tls: move tls::ExData to tls/mod.rs --- quiche/src/tls/boringssl_openssl/mod.rs | 22 ++-------------------- quiche/src/tls/mod.rs | 23 +++++++++++++++++++++++ 2 files changed, 25 insertions(+), 20 deletions(-) diff --git a/quiche/src/tls/boringssl_openssl/mod.rs b/quiche/src/tls/boringssl_openssl/mod.rs index 90d9e4848e..bdd880077d 100644 --- a/quiche/src/tls/boringssl_openssl/mod.rs +++ b/quiche/src/tls/boringssl_openssl/mod.rs @@ -46,6 +46,8 @@ use crate::ConnectionError; use crate::crypto; use crate::packet; +use crate::tls::ExData; + const TLS1_3_VERSION: u16 = 0x0304; const TLS_ALERT_ERROR: u64 = 0x100; const INTERNAL_ERROR: u64 = 0x01; @@ -680,26 +682,6 @@ impl Drop for Handshake { } } -pub struct ExData<'a> { - pub application_protos: &'a Vec>, - - pub crypto_ctx: &'a mut [packet::CryptoContext; packet::Epoch::count()], - - pub session: &'a mut Option>, - - pub local_error: &'a mut Option, - - pub keylog: Option<&'a mut Box>, - - pub trace_id: &'a str, - - pub recovery_config: crate::recovery::RecoveryConfig, - - pub tx_cap_factor: f64, - - pub is_server: bool, -} - impl<'a> ExData<'a> { fn from_ssl_ptr(ptr: *const SSL) -> Option<&'a mut Self> { get_ex_data_from_ptr::(ptr, *QUICHE_EX_DATA_INDEX) diff --git a/quiche/src/tls/mod.rs b/quiche/src/tls/mod.rs index 2dcdb959dd..845ea13843 100644 --- a/quiche/src/tls/mod.rs +++ b/quiche/src/tls/mod.rs @@ -36,3 +36,26 @@ mod boringssl_openssl; feature = "openssl" ))] pub use boringssl_openssl::*; + +use crate::packet; +use crate::ConnectionError; + +pub struct ExData<'a> { + pub application_protos: &'a Vec>, + + pub crypto_ctx: &'a mut [packet::CryptoContext; packet::Epoch::count()], + + pub session: &'a mut Option>, + + pub local_error: &'a mut Option, + + pub keylog: Option<&'a mut Box>, + + pub trace_id: &'a str, + + pub recovery_config: crate::recovery::RecoveryConfig, + + pub tx_cap_factor: f64, + + pub is_server: bool, +} From be19225092f64b8baf3668e54af16c7c4cae9c4e Mon Sep 17 00:00:00 2001 From: Harald Gutmann Date: Fri, 11 Apr 2025 11:46:58 +0200 Subject: [PATCH 04/14] crypto: move crypto::Level to crypto/mod.rs --- quiche/src/crypto/boringssl_openssl/mod.rs | 23 ---------------------- quiche/src/crypto/mod.rs | 23 ++++++++++++++++++++++ 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/quiche/src/crypto/boringssl_openssl/mod.rs b/quiche/src/crypto/boringssl_openssl/mod.rs index 1e1b62a0f1..f8fb06b10a 100644 --- a/quiche/src/crypto/boringssl_openssl/mod.rs +++ b/quiche/src/crypto/boringssl_openssl/mod.rs @@ -30,35 +30,12 @@ use libc::c_void; use crate::Error; use crate::Result; -use crate::packet; - // All the AEAD algorithms we support use 96-bit nonces. pub const MAX_NONCE_LEN: usize = 12; // Length of header protection mask. pub const HP_MASK_LEN: usize = 5; -#[repr(C)] -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub enum Level { - Initial = 0, - ZeroRTT = 1, - Handshake = 2, - OneRTT = 3, -} - -impl Level { - pub fn from_epoch(e: packet::Epoch) -> Level { - match e { - packet::Epoch::Initial => Level::Initial, - - packet::Epoch::Handshake => Level::Handshake, - - packet::Epoch::Application => Level::OneRTT, - } - } -} - #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum Algorithm { #[allow(non_camel_case_types)] diff --git a/quiche/src/crypto/mod.rs b/quiche/src/crypto/mod.rs index 2dcdb959dd..4e855eab62 100644 --- a/quiche/src/crypto/mod.rs +++ b/quiche/src/crypto/mod.rs @@ -36,3 +36,26 @@ mod boringssl_openssl; feature = "openssl" ))] pub use boringssl_openssl::*; + +use crate::packet; + +#[repr(C)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum Level { + Initial = 0, + ZeroRTT = 1, + Handshake = 2, + OneRTT = 3, +} + +impl Level { + pub fn from_epoch(e: packet::Epoch) -> Level { + match e { + packet::Epoch::Initial => Level::Initial, + + packet::Epoch::Handshake => Level::Handshake, + + packet::Epoch::Application => Level::OneRTT, + } + } +} From 0ae059128e2314571a6291d9596082fcd372f635 Mon Sep 17 00:00:00 2001 From: Harald Gutmann Date: Mon, 28 Apr 2025 15:33:23 +0200 Subject: [PATCH 05/14] crypto: move crypto::Algorithm and methods to crypto/mod.rs --- quiche/src/crypto/boringssl_openssl/mod.rs | 34 ++-------------------- quiche/src/crypto/mod.rs | 34 ++++++++++++++++++++++ 2 files changed, 36 insertions(+), 32 deletions(-) diff --git a/quiche/src/crypto/boringssl_openssl/mod.rs b/quiche/src/crypto/boringssl_openssl/mod.rs index f8fb06b10a..f87d181378 100644 --- a/quiche/src/crypto/boringssl_openssl/mod.rs +++ b/quiche/src/crypto/boringssl_openssl/mod.rs @@ -30,24 +30,14 @@ use libc::c_void; use crate::Error; use crate::Result; +use crate::crypto::Algorithm; + // All the AEAD algorithms we support use 96-bit nonces. pub const MAX_NONCE_LEN: usize = 12; // Length of header protection mask. pub const HP_MASK_LEN: usize = 5; -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub enum Algorithm { - #[allow(non_camel_case_types)] - AES128_GCM, - - #[allow(non_camel_case_types)] - AES256_GCM, - - #[allow(non_camel_case_types)] - ChaCha20_Poly1305, -} - // Note: some vendor-specific methods are implemented by each vendor's submodule // (openssl-quictls / boringssl). impl Algorithm { @@ -59,26 +49,6 @@ impl Algorithm { } } - pub const fn key_len(self) -> usize { - match self { - Algorithm::AES128_GCM => 16, - Algorithm::AES256_GCM => 32, - Algorithm::ChaCha20_Poly1305 => 32, - } - } - - pub const fn tag_len(self) -> usize { - if cfg!(feature = "fuzzing") { - return 0; - } - - match self { - Algorithm::AES128_GCM => 16, - Algorithm::AES256_GCM => 16, - Algorithm::ChaCha20_Poly1305 => 16, - } - } - pub const fn nonce_len(self) -> usize { match self { Algorithm::AES128_GCM => 12, diff --git a/quiche/src/crypto/mod.rs b/quiche/src/crypto/mod.rs index 4e855eab62..5ca21aaaba 100644 --- a/quiche/src/crypto/mod.rs +++ b/quiche/src/crypto/mod.rs @@ -59,3 +59,37 @@ impl Level { } } } + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum Algorithm { + #[allow(non_camel_case_types)] + AES128_GCM, + + #[allow(non_camel_case_types)] + AES256_GCM, + + #[allow(non_camel_case_types)] + ChaCha20_Poly1305, +} + +impl Algorithm { + pub const fn key_len(self) -> usize { + match self { + Algorithm::AES128_GCM => 16, + Algorithm::AES256_GCM => 32, + Algorithm::ChaCha20_Poly1305 => 32, + } + } + + pub const fn tag_len(self) -> usize { + if cfg!(feature = "fuzzing") { + return 0; + } + + match self { + Algorithm::AES128_GCM => 16, + Algorithm::AES256_GCM => 16, + Algorithm::ChaCha20_Poly1305 => 16, + } + } +} From b063ab92f943aa9a6f1c237334c5e71780d0535d Mon Sep 17 00:00:00 2001 From: Harald Gutmann Date: Tue, 6 May 2025 14:45:44 +0200 Subject: [PATCH 06/14] crypto: move make_nonce to crypto/mod.rs --- quiche/src/crypto/boringssl_openssl/mod.rs | 17 +---------------- quiche/src/crypto/mod.rs | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/quiche/src/crypto/boringssl_openssl/mod.rs b/quiche/src/crypto/boringssl_openssl/mod.rs index f87d181378..dff45686d8 100644 --- a/quiche/src/crypto/boringssl_openssl/mod.rs +++ b/quiche/src/crypto/boringssl_openssl/mod.rs @@ -30,11 +30,9 @@ use libc::c_void; use crate::Error; use crate::Result; +use crate::crypto::make_nonce; use crate::crypto::Algorithm; -// All the AEAD algorithms we support use 96-bit nonces. -pub const MAX_NONCE_LEN: usize = 12; - // Length of header protection mask. pub const HP_MASK_LEN: usize = 5; @@ -423,19 +421,6 @@ fn hkdf_expand_label( Ok(()) } -fn make_nonce(iv: &[u8], counter: u64) -> [u8; MAX_NONCE_LEN] { - let mut nonce = [0; MAX_NONCE_LEN]; - nonce.copy_from_slice(iv); - - // XOR the last bytes of the IV with the counter. This is equivalent to - // left-padding the counter with zero bytes. - for (a, b) in nonce[4..].iter_mut().zip(counter.to_be_bytes().iter()) { - *a ^= b; - } - - nonce -} - pub fn verify_slices_are_equal(a: &[u8], b: &[u8]) -> Result<()> { if a.len() != b.len() { return Err(Error::CryptoFail); diff --git a/quiche/src/crypto/mod.rs b/quiche/src/crypto/mod.rs index 5ca21aaaba..6565b28ad4 100644 --- a/quiche/src/crypto/mod.rs +++ b/quiche/src/crypto/mod.rs @@ -39,6 +39,9 @@ pub use boringssl_openssl::*; use crate::packet; +// All the AEAD algorithms we support use 96-bit nonces. +pub const MAX_NONCE_LEN: usize = 12; + #[repr(C)] #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum Level { @@ -93,3 +96,16 @@ impl Algorithm { } } } + +fn make_nonce(iv: &[u8], counter: u64) -> [u8; MAX_NONCE_LEN] { + let mut nonce = [0; MAX_NONCE_LEN]; + nonce.copy_from_slice(iv); + + // XOR the last bytes of the IV with the counter. This is equivalent to + // left-padding the counter with zero bytes. + for (a, b) in nonce[4..].iter_mut().zip(counter.to_be_bytes().iter()) { + *a ^= b; + } + + nonce +} From 5ae655d649be8618656494c62e4e5d00fb3fe1ec Mon Sep 17 00:00:00 2001 From: Harald Gutmann Date: Mon, 28 Apr 2025 15:35:15 +0200 Subject: [PATCH 07/14] tls & crypto: add rustls cargo feature add base structs and methods/functions required to compile rustls feature --- apps/Cargo.toml | 2 + quiche/Cargo.toml | 4 + quiche/src/crypto/mod.rs | 17 ++-- quiche/src/crypto/rustls/mod.rs | 115 ++++++++++++++++++++++ quiche/src/tls/mod.rs | 17 ++-- quiche/src/tls/rustls/mod.rs | 169 ++++++++++++++++++++++++++++++++ 6 files changed, 304 insertions(+), 20 deletions(-) create mode 100644 quiche/src/crypto/rustls/mod.rs create mode 100644 quiche/src/tls/rustls/mod.rs diff --git a/apps/Cargo.toml b/apps/Cargo.toml index 48e7da4e08..3b6890cb23 100644 --- a/apps/Cargo.toml +++ b/apps/Cargo.toml @@ -19,6 +19,8 @@ qlog = ["quiche/qlog"] # Use BoringSSL provided by the boring crate. boringssl-boring-crate = ["quiche/boringssl-boring-crate"] +rustls = ["quiche/rustls"] + # Enable sfv support. sfv = ["quiche/sfv"] diff --git a/quiche/Cargo.toml b/quiche/Cargo.toml index 7b59c60e58..752a9f0806 100644 --- a/quiche/Cargo.toml +++ b/quiche/Cargo.toml @@ -39,6 +39,9 @@ boringssl-boring-crate = ["boring", "foreign-types-shared"] # Build quiche against OpenSSL instead of BoringSSL. openssl = ["pkg-config"] +# Build quiche using rustls +rustls = ["dep:rustls"] + # Generate pkg-config metadata file for libquiche. pkg-config-meta = [] @@ -83,6 +86,7 @@ sfv = { version = "0.9", optional = true } slab = "0.4" smallvec = { workspace = true, features = ["union"] } enum_dispatch = "0.3" +rustls = { version = "0.23.26", optional = true } [target."cfg(windows)".dependencies] windows-sys = { version = "0.59", features = [ diff --git a/quiche/src/crypto/mod.rs b/quiche/src/crypto/mod.rs index 6565b28ad4..b19c49205d 100644 --- a/quiche/src/crypto/mod.rs +++ b/quiche/src/crypto/mod.rs @@ -24,19 +24,16 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -#[cfg(any( - feature = "boringssl-vendored", - feature = "boringssl-boring-crate", - feature = "openssl" -))] +#[cfg(not(feature = "rustls"))] mod boringssl_openssl; -#[cfg(any( - feature = "boringssl-vendored", - feature = "boringssl-boring-crate", - feature = "openssl" -))] +#[cfg(not(feature = "rustls"))] pub use boringssl_openssl::*; +#[cfg(feature = "rustls")] +mod rustls; +#[cfg(feature = "rustls")] +pub use rustls::*; + use crate::packet; // All the AEAD algorithms we support use 96-bit nonces. diff --git a/quiche/src/crypto/rustls/mod.rs b/quiche/src/crypto/rustls/mod.rs new file mode 100644 index 0000000000..bdef457363 --- /dev/null +++ b/quiche/src/crypto/rustls/mod.rs @@ -0,0 +1,115 @@ +// Copyright (C) 2025, Cloudflare, Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::crypto::Algorithm; +use crate::Error; +use crate::Result; + +pub struct PacketKey {} + +impl PacketKey { + pub fn new( + alg: Algorithm, key: Vec, iv: Vec, _enc: u32, + ) -> Result { + todo!() + } + + pub fn seal_with_u64_counter( + &self, counter: u64, ad: &[u8], buf: &mut [u8], in_len: usize, + extra_in: Option<&[u8]>, + ) -> Result { + todo!() + } +} + +pub struct Open {} + +impl Open { + pub fn new_mask(&self, sample: &[u8]) -> Result<[u8; 5]> { + todo!() + } + + pub fn open_with_u64_counter( + &self, counter: u64, ad: &[u8], buf: &mut [u8], + ) -> Result { + todo!() + } + + pub fn alg(&self) -> Algorithm { + // self.alg + todo!() + } + + pub fn derive_next_packet_key(&self) -> Result { + todo!() + } +} + +pub struct Seal {} + +impl Seal { + pub const ENCRYPT: u32 = 1; + + pub fn new_mask(&self, sample: &[u8]) -> Result<[u8; 5]> { + todo!() + } + + pub fn seal_with_u64_counter( + &self, counter: u64, ad: &[u8], buf: &mut [u8], in_len: usize, + extra_in: Option<&[u8]>, + ) -> Result { + todo!() + } + + pub fn alg(&self) -> Algorithm { + // self.alg + todo!() + } + + pub fn derive_next_packet_key(&self) -> Result { + todo!() + } +} + +pub fn derive_initial_key_material( + cid: &[u8], version: u32, is_server: bool, did_reset: bool, +) -> Result<(Open, Seal)> { + let open = Open {}; + let seal = Seal {}; + + Ok((open, seal)) +} + +pub fn verify_slices_are_equal(a: &[u8], b: &[u8]) -> Result<()> { + if a.len() != b.len() { + return Err(Error::CryptoFail); + } + + match a == b { + true => Ok(()), + false => Err(Error::CryptoFail), + } +} diff --git a/quiche/src/tls/mod.rs b/quiche/src/tls/mod.rs index 845ea13843..cf853d8b06 100644 --- a/quiche/src/tls/mod.rs +++ b/quiche/src/tls/mod.rs @@ -24,19 +24,16 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -#[cfg(any( - feature = "boringssl-vendored", - feature = "boringssl-boring-crate", - feature = "openssl" -))] +#[cfg(not(feature = "rustls"))] mod boringssl_openssl; -#[cfg(any( - feature = "boringssl-vendored", - feature = "boringssl-boring-crate", - feature = "openssl" -))] +#[cfg(not(feature = "rustls"))] pub use boringssl_openssl::*; +#[cfg(feature = "rustls")] +mod rustls; +#[cfg(feature = "rustls")] +pub use rustls::*; + use crate::packet; use crate::ConnectionError; diff --git a/quiche/src/tls/rustls/mod.rs b/quiche/src/tls/rustls/mod.rs new file mode 100644 index 0000000000..d159298e8c --- /dev/null +++ b/quiche/src/tls/rustls/mod.rs @@ -0,0 +1,169 @@ +// Copyright (C) 2025, Cloudflare, Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::crypto; +use crate::tls::ExData; +use crate::Result; + +pub struct Context {} + +impl Context { + pub fn new() -> Result { + todo!() + } + + pub fn new_handshake(&mut self) -> Result { + todo!() + } + + pub fn load_verify_locations_from_file(&mut self, file: &str) -> Result<()> { + todo!() + } + + pub fn load_verify_locations_from_directory( + &mut self, path: &str, + ) -> Result<()> { + todo!() + } + + pub fn use_certificate_chain_file(&mut self, file: &str) -> Result<()> { + todo!() + } + + pub fn use_privkey_file(&mut self, file: &str) -> Result<()> { + todo!() + } + + pub fn set_verify(&mut self, verify: bool) { + todo!() + } + + pub fn enable_keylog(&mut self) { + todo!() + } + + pub fn set_alpn(&mut self, v: &[&[u8]]) -> Result<()> { + todo!() + } + + pub fn set_ticket_key(&mut self, key: &[u8]) -> Result<()> { + todo!() + } + + pub fn set_early_data_enabled(&mut self, enabled: bool) { + todo!() + } +} + +pub struct Handshake {} + +impl Handshake { + pub fn init(&mut self, is_server: bool) -> Result<()> { + todo!() + } + + pub fn use_legacy_codepoint(&mut self, use_legacy: bool) { + todo!() + } + + pub fn set_host_name(&mut self, name: &str) -> Result<()> { + todo!() + } + + pub fn set_quic_transport_params(&mut self, buf: &[u8]) -> Result<()> { + todo!() + } + + pub fn quic_transport_params(&self) -> &[u8] { + todo!() + } + + pub fn alpn_protocol(&self) -> &[u8] { + todo!() + } + + pub fn server_name(&self) -> Option<&str> { + todo!() + } + + pub fn provide_data( + &mut self, level: crypto::Level, buf: &[u8], + ) -> Result<()> { + todo!() + } + + pub fn do_handshake(&mut self, ex_data: &mut ExData) -> Result<()> { + todo!() + } + + pub fn process_post_handshake(&mut self, ex_data: &mut ExData) -> Result<()> { + todo!() + } + + pub fn write_level(&self) -> crypto::Level { + todo!() + } + + pub fn cipher(&self) -> Option { + todo!() + } + + pub fn is_completed(&self) -> bool { + todo!() + } + + pub fn is_resumed(&self) -> bool { + todo!() + } + + pub fn clear(&mut self) -> Result<()> { + todo!() + } + + pub fn set_session(&mut self, session: &[u8]) -> Result<()> { + todo!() + } + + pub fn curve(&self) -> Option { + todo!() + } + + pub fn sigalg(&self) -> Option { + todo!() + } + + pub fn peer_cert_chain(&self) -> Option> { + todo!() + } + + pub fn peer_cert(&self) -> Option<&[u8]> { + todo!() + } + + pub fn is_in_early_data(&self) -> bool { + todo!() + } +} From 83f461e440a1fbc55d42b742a2ca2c0e20d20dfa Mon Sep 17 00:00:00 2001 From: Harald Gutmann Date: Mon, 28 Apr 2025 18:34:41 +0200 Subject: [PATCH 08/14] rand: use random generator from rustls --- quiche/src/rand.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/quiche/src/rand.rs b/quiche/src/rand.rs index 40ea086347..bb07c9b68e 100644 --- a/quiche/src/rand.rs +++ b/quiche/src/rand.rs @@ -24,12 +24,22 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#[cfg(feature = "rustls")] +use crate::crypto::init_crypto_provider; + +#[cfg(not(feature = "rustls"))] pub fn rand_bytes(buf: &mut [u8]) { unsafe { RAND_bytes(buf.as_mut_ptr(), buf.len()); } } +#[cfg(feature = "rustls")] +pub fn rand_bytes(buf: &mut [u8]) { + let provider = init_crypto_provider(); + provider.secure_random.fill(buf).unwrap() +} + pub fn rand_u8() -> u8 { let mut buf = [0; 1]; @@ -59,6 +69,7 @@ pub fn rand_u64_uniform(max: u64) -> u64 { r / chunk_size } +#[cfg(not(feature = "rustls"))] extern "C" { fn RAND_bytes(buf: *mut u8, len: libc::size_t) -> libc::c_int; } From 47e90cfbf749196b8b736c2aed3eb683a2b0c516 Mon Sep 17 00:00:00 2001 From: Harald Gutmann Date: Mon, 5 May 2025 14:59:21 +0200 Subject: [PATCH 09/14] certs: add certificates for tests with rustls feature rustls does not support v1 (needs v3) certificates --- quiche/examples/cert-big_rustls.crt | 100 ++++++++++++++++++++++++++++ quiche/examples/cert_rustls.crt | 20 ++++++ quiche/examples/gen-certs.sh | 17 +++++ quiche/examples/rootca_rustls.crt | 21 ++++++ 4 files changed, 158 insertions(+) create mode 100644 quiche/examples/cert-big_rustls.crt create mode 100644 quiche/examples/cert_rustls.crt create mode 100644 quiche/examples/rootca_rustls.crt diff --git a/quiche/examples/cert-big_rustls.crt b/quiche/examples/cert-big_rustls.crt new file mode 100644 index 0000000000..2b0b6961c1 --- /dev/null +++ b/quiche/examples/cert-big_rustls.crt @@ -0,0 +1,100 @@ +-----BEGIN CERTIFICATE----- +MIIDSzCCAjOgAwIBAgIUTZGqZKKEu0WNBBxhYLNpSOOwz3AwDQYJKoZIhvcNAQEL +BQAwQjELMAkGA1UEBhMCWFgxFTATBgNVBAcMDERlZmF1bHQgQ2l0eTEcMBoGA1UE +CgwTRGVmYXVsdCBDb21wYW55IEx0ZDAgFw0yNTA1MDUxMjQzMDdaGA8yMDUyMDky +MDEyNDMwN1owITELMAkGA1UEBhMCR0IxEjAQBgNVBAMMCXF1aWMudGVjaDCCASIw +DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAM+Wzi+yw/ZIiGoHFa2amdd2XEdC +oTLs7G66lC22SsqgvrgUYCDcWCeyz5yeCz2Lgrf5wGkOwi13yoMwDBbvHiCulBL2 +QOvu29tBOK7zqT20wf0+CW2GTUCsjPQBMK+o9D+rxYK1k+2gc4ZJoqhHub7/+2Th +Bm/gG/nLzHrM3wW5Pd58synTWlLofece/6dDsN9eZ6dj0bqE9EEJwPcxP/XmdyWo +d3DWZzbkOgQx5ZHoM01eSYWGG5UaP6ZrdhPRNIWvjVrES392SVGIZGEd1KjJbRCr +i4JQjlVgWz5ineCGVDSKLaSjFUElW/WoDQRePYR/YgYTd3wZux6fCgaYyS8CAwEA +AaNYMFYwFAYDVR0RBA0wC4IJcXVpYy50ZWNoMB0GA1UdDgQWBBSHabqAgfnBIkPF +Fjt3Xi3wZHQnNzAfBgNVHSMEGDAWgBQjyE843kJpz9KXoQnMi3Qq+Bcl+TANBgkq +hkiG9w0BAQsFAAOCAQEAeWVWygPY+iwlxEfo9uBhwo0OIfcy55QPzgwI5aGB1sPR +Ien+uxziOokXzyrtJWkY3eqzF2eYywH89GJoRv5hdcFKs2NZ/DWvGZHE8PoT0FBF +L47VjPg7k16IIhnOZlN5DeY0WfkeXFvANzPJGbdGYArg+ZQMCQ+xJu/UKs+vPF4+ +mxTQ31iCwHyGdLeXSk2RRkI1m5UhLWd4v5Pan37m9UujvOMwEIGRxlwr1Zhpb/QJ +w90iH5gMVzZ0iQxPYbYug0Ns7ekg+gaCckCt/CKpKmVOF26XmtKkXuCYhDi42CvO +o8rtE95MBjSXOreqH7ns2GdKGllvzGzzCvpDVmn3Cg== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDSzCCAjOgAwIBAgIUTZGqZKKEu0WNBBxhYLNpSOOwz3AwDQYJKoZIhvcNAQEL +BQAwQjELMAkGA1UEBhMCWFgxFTATBgNVBAcMDERlZmF1bHQgQ2l0eTEcMBoGA1UE +CgwTRGVmYXVsdCBDb21wYW55IEx0ZDAgFw0yNTA1MDUxMjQzMDdaGA8yMDUyMDky +MDEyNDMwN1owITELMAkGA1UEBhMCR0IxEjAQBgNVBAMMCXF1aWMudGVjaDCCASIw +DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAM+Wzi+yw/ZIiGoHFa2amdd2XEdC +oTLs7G66lC22SsqgvrgUYCDcWCeyz5yeCz2Lgrf5wGkOwi13yoMwDBbvHiCulBL2 +QOvu29tBOK7zqT20wf0+CW2GTUCsjPQBMK+o9D+rxYK1k+2gc4ZJoqhHub7/+2Th +Bm/gG/nLzHrM3wW5Pd58synTWlLofece/6dDsN9eZ6dj0bqE9EEJwPcxP/XmdyWo +d3DWZzbkOgQx5ZHoM01eSYWGG5UaP6ZrdhPRNIWvjVrES392SVGIZGEd1KjJbRCr +i4JQjlVgWz5ineCGVDSKLaSjFUElW/WoDQRePYR/YgYTd3wZux6fCgaYyS8CAwEA +AaNYMFYwFAYDVR0RBA0wC4IJcXVpYy50ZWNoMB0GA1UdDgQWBBSHabqAgfnBIkPF +Fjt3Xi3wZHQnNzAfBgNVHSMEGDAWgBQjyE843kJpz9KXoQnMi3Qq+Bcl+TANBgkq +hkiG9w0BAQsFAAOCAQEAeWVWygPY+iwlxEfo9uBhwo0OIfcy55QPzgwI5aGB1sPR +Ien+uxziOokXzyrtJWkY3eqzF2eYywH89GJoRv5hdcFKs2NZ/DWvGZHE8PoT0FBF +L47VjPg7k16IIhnOZlN5DeY0WfkeXFvANzPJGbdGYArg+ZQMCQ+xJu/UKs+vPF4+ +mxTQ31iCwHyGdLeXSk2RRkI1m5UhLWd4v5Pan37m9UujvOMwEIGRxlwr1Zhpb/QJ +w90iH5gMVzZ0iQxPYbYug0Ns7ekg+gaCckCt/CKpKmVOF26XmtKkXuCYhDi42CvO +o8rtE95MBjSXOreqH7ns2GdKGllvzGzzCvpDVmn3Cg== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDSzCCAjOgAwIBAgIUTZGqZKKEu0WNBBxhYLNpSOOwz3AwDQYJKoZIhvcNAQEL +BQAwQjELMAkGA1UEBhMCWFgxFTATBgNVBAcMDERlZmF1bHQgQ2l0eTEcMBoGA1UE +CgwTRGVmYXVsdCBDb21wYW55IEx0ZDAgFw0yNTA1MDUxMjQzMDdaGA8yMDUyMDky +MDEyNDMwN1owITELMAkGA1UEBhMCR0IxEjAQBgNVBAMMCXF1aWMudGVjaDCCASIw +DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAM+Wzi+yw/ZIiGoHFa2amdd2XEdC +oTLs7G66lC22SsqgvrgUYCDcWCeyz5yeCz2Lgrf5wGkOwi13yoMwDBbvHiCulBL2 +QOvu29tBOK7zqT20wf0+CW2GTUCsjPQBMK+o9D+rxYK1k+2gc4ZJoqhHub7/+2Th +Bm/gG/nLzHrM3wW5Pd58synTWlLofece/6dDsN9eZ6dj0bqE9EEJwPcxP/XmdyWo +d3DWZzbkOgQx5ZHoM01eSYWGG5UaP6ZrdhPRNIWvjVrES392SVGIZGEd1KjJbRCr +i4JQjlVgWz5ineCGVDSKLaSjFUElW/WoDQRePYR/YgYTd3wZux6fCgaYyS8CAwEA +AaNYMFYwFAYDVR0RBA0wC4IJcXVpYy50ZWNoMB0GA1UdDgQWBBSHabqAgfnBIkPF +Fjt3Xi3wZHQnNzAfBgNVHSMEGDAWgBQjyE843kJpz9KXoQnMi3Qq+Bcl+TANBgkq +hkiG9w0BAQsFAAOCAQEAeWVWygPY+iwlxEfo9uBhwo0OIfcy55QPzgwI5aGB1sPR +Ien+uxziOokXzyrtJWkY3eqzF2eYywH89GJoRv5hdcFKs2NZ/DWvGZHE8PoT0FBF +L47VjPg7k16IIhnOZlN5DeY0WfkeXFvANzPJGbdGYArg+ZQMCQ+xJu/UKs+vPF4+ +mxTQ31iCwHyGdLeXSk2RRkI1m5UhLWd4v5Pan37m9UujvOMwEIGRxlwr1Zhpb/QJ +w90iH5gMVzZ0iQxPYbYug0Ns7ekg+gaCckCt/CKpKmVOF26XmtKkXuCYhDi42CvO +o8rtE95MBjSXOreqH7ns2GdKGllvzGzzCvpDVmn3Cg== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDSzCCAjOgAwIBAgIUTZGqZKKEu0WNBBxhYLNpSOOwz3AwDQYJKoZIhvcNAQEL +BQAwQjELMAkGA1UEBhMCWFgxFTATBgNVBAcMDERlZmF1bHQgQ2l0eTEcMBoGA1UE +CgwTRGVmYXVsdCBDb21wYW55IEx0ZDAgFw0yNTA1MDUxMjQzMDdaGA8yMDUyMDky +MDEyNDMwN1owITELMAkGA1UEBhMCR0IxEjAQBgNVBAMMCXF1aWMudGVjaDCCASIw +DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAM+Wzi+yw/ZIiGoHFa2amdd2XEdC +oTLs7G66lC22SsqgvrgUYCDcWCeyz5yeCz2Lgrf5wGkOwi13yoMwDBbvHiCulBL2 +QOvu29tBOK7zqT20wf0+CW2GTUCsjPQBMK+o9D+rxYK1k+2gc4ZJoqhHub7/+2Th +Bm/gG/nLzHrM3wW5Pd58synTWlLofece/6dDsN9eZ6dj0bqE9EEJwPcxP/XmdyWo +d3DWZzbkOgQx5ZHoM01eSYWGG5UaP6ZrdhPRNIWvjVrES392SVGIZGEd1KjJbRCr +i4JQjlVgWz5ineCGVDSKLaSjFUElW/WoDQRePYR/YgYTd3wZux6fCgaYyS8CAwEA +AaNYMFYwFAYDVR0RBA0wC4IJcXVpYy50ZWNoMB0GA1UdDgQWBBSHabqAgfnBIkPF +Fjt3Xi3wZHQnNzAfBgNVHSMEGDAWgBQjyE843kJpz9KXoQnMi3Qq+Bcl+TANBgkq +hkiG9w0BAQsFAAOCAQEAeWVWygPY+iwlxEfo9uBhwo0OIfcy55QPzgwI5aGB1sPR +Ien+uxziOokXzyrtJWkY3eqzF2eYywH89GJoRv5hdcFKs2NZ/DWvGZHE8PoT0FBF +L47VjPg7k16IIhnOZlN5DeY0WfkeXFvANzPJGbdGYArg+ZQMCQ+xJu/UKs+vPF4+ +mxTQ31iCwHyGdLeXSk2RRkI1m5UhLWd4v5Pan37m9UujvOMwEIGRxlwr1Zhpb/QJ +w90iH5gMVzZ0iQxPYbYug0Ns7ekg+gaCckCt/CKpKmVOF26XmtKkXuCYhDi42CvO +o8rtE95MBjSXOreqH7ns2GdKGllvzGzzCvpDVmn3Cg== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDSzCCAjOgAwIBAgIUTZGqZKKEu0WNBBxhYLNpSOOwz3AwDQYJKoZIhvcNAQEL +BQAwQjELMAkGA1UEBhMCWFgxFTATBgNVBAcMDERlZmF1bHQgQ2l0eTEcMBoGA1UE +CgwTRGVmYXVsdCBDb21wYW55IEx0ZDAgFw0yNTA1MDUxMjQzMDdaGA8yMDUyMDky +MDEyNDMwN1owITELMAkGA1UEBhMCR0IxEjAQBgNVBAMMCXF1aWMudGVjaDCCASIw +DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAM+Wzi+yw/ZIiGoHFa2amdd2XEdC +oTLs7G66lC22SsqgvrgUYCDcWCeyz5yeCz2Lgrf5wGkOwi13yoMwDBbvHiCulBL2 +QOvu29tBOK7zqT20wf0+CW2GTUCsjPQBMK+o9D+rxYK1k+2gc4ZJoqhHub7/+2Th +Bm/gG/nLzHrM3wW5Pd58synTWlLofece/6dDsN9eZ6dj0bqE9EEJwPcxP/XmdyWo +d3DWZzbkOgQx5ZHoM01eSYWGG5UaP6ZrdhPRNIWvjVrES392SVGIZGEd1KjJbRCr +i4JQjlVgWz5ineCGVDSKLaSjFUElW/WoDQRePYR/YgYTd3wZux6fCgaYyS8CAwEA +AaNYMFYwFAYDVR0RBA0wC4IJcXVpYy50ZWNoMB0GA1UdDgQWBBSHabqAgfnBIkPF +Fjt3Xi3wZHQnNzAfBgNVHSMEGDAWgBQjyE843kJpz9KXoQnMi3Qq+Bcl+TANBgkq +hkiG9w0BAQsFAAOCAQEAeWVWygPY+iwlxEfo9uBhwo0OIfcy55QPzgwI5aGB1sPR +Ien+uxziOokXzyrtJWkY3eqzF2eYywH89GJoRv5hdcFKs2NZ/DWvGZHE8PoT0FBF +L47VjPg7k16IIhnOZlN5DeY0WfkeXFvANzPJGbdGYArg+ZQMCQ+xJu/UKs+vPF4+ +mxTQ31iCwHyGdLeXSk2RRkI1m5UhLWd4v5Pan37m9UujvOMwEIGRxlwr1Zhpb/QJ +w90iH5gMVzZ0iQxPYbYug0Ns7ekg+gaCckCt/CKpKmVOF26XmtKkXuCYhDi42CvO +o8rtE95MBjSXOreqH7ns2GdKGllvzGzzCvpDVmn3Cg== +-----END CERTIFICATE----- diff --git a/quiche/examples/cert_rustls.crt b/quiche/examples/cert_rustls.crt new file mode 100644 index 0000000000..62532406a3 --- /dev/null +++ b/quiche/examples/cert_rustls.crt @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDSzCCAjOgAwIBAgIUTZGqZKKEu0WNBBxhYLNpSOOwz3AwDQYJKoZIhvcNAQEL +BQAwQjELMAkGA1UEBhMCWFgxFTATBgNVBAcMDERlZmF1bHQgQ2l0eTEcMBoGA1UE +CgwTRGVmYXVsdCBDb21wYW55IEx0ZDAgFw0yNTA1MDUxMjQzMDdaGA8yMDUyMDky +MDEyNDMwN1owITELMAkGA1UEBhMCR0IxEjAQBgNVBAMMCXF1aWMudGVjaDCCASIw +DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAM+Wzi+yw/ZIiGoHFa2amdd2XEdC +oTLs7G66lC22SsqgvrgUYCDcWCeyz5yeCz2Lgrf5wGkOwi13yoMwDBbvHiCulBL2 +QOvu29tBOK7zqT20wf0+CW2GTUCsjPQBMK+o9D+rxYK1k+2gc4ZJoqhHub7/+2Th +Bm/gG/nLzHrM3wW5Pd58synTWlLofece/6dDsN9eZ6dj0bqE9EEJwPcxP/XmdyWo +d3DWZzbkOgQx5ZHoM01eSYWGG5UaP6ZrdhPRNIWvjVrES392SVGIZGEd1KjJbRCr +i4JQjlVgWz5ineCGVDSKLaSjFUElW/WoDQRePYR/YgYTd3wZux6fCgaYyS8CAwEA +AaNYMFYwFAYDVR0RBA0wC4IJcXVpYy50ZWNoMB0GA1UdDgQWBBSHabqAgfnBIkPF +Fjt3Xi3wZHQnNzAfBgNVHSMEGDAWgBQjyE843kJpz9KXoQnMi3Qq+Bcl+TANBgkq +hkiG9w0BAQsFAAOCAQEAeWVWygPY+iwlxEfo9uBhwo0OIfcy55QPzgwI5aGB1sPR +Ien+uxziOokXzyrtJWkY3eqzF2eYywH89GJoRv5hdcFKs2NZ/DWvGZHE8PoT0FBF +L47VjPg7k16IIhnOZlN5DeY0WfkeXFvANzPJGbdGYArg+ZQMCQ+xJu/UKs+vPF4+ +mxTQ31iCwHyGdLeXSk2RRkI1m5UhLWd4v5Pan37m9UujvOMwEIGRxlwr1Zhpb/QJ +w90iH5gMVzZ0iQxPYbYug0Ns7ekg+gaCckCt/CKpKmVOF26XmtKkXuCYhDi42CvO +o8rtE95MBjSXOreqH7ns2GdKGllvzGzzCvpDVmn3Cg== +-----END CERTIFICATE----- diff --git a/quiche/examples/gen-certs.sh b/quiche/examples/gen-certs.sh index 3df415b326..4c14c03990 100755 --- a/quiche/examples/gen-certs.sh +++ b/quiche/examples/gen-certs.sh @@ -13,3 +13,20 @@ cat cert.crt >> cert-big.crt rm cert.csr rm rootca.key rm rootca.srl + +# required as rustls does not support v1 certificates +# rustls also needs the subjectAltName to successfully verify the certificates +echo '[v3_req]' > openssl.cnf +openssl req -new -x509 -batch -nodes -days 10000 -keyout rootca_rustls.key -out rootca_rustls.crt + +openssl req -new -batch -nodes -sha256 -key cert.key -out cert_rustls.csr \ + -subj '/C=GB/CN=quic.tech' -addext "subjectAltName=DNS:quic.tech" +openssl x509 -req -days 10000 -CAcreateserial -CA rootca_rustls.crt -CAkey rootca_rustls.key \ + -in cert_rustls.csr -out cert_rustls.crt -copy_extensions copyall +openssl verify -CAfile rootca_rustls.crt cert_rustls.crt +cat cert_rustls.crt cert_rustls.crt cert_rustls.crt cert_rustls.crt cert_rustls.crt > cert-big_rustls.crt + +rm openssl.cnf +rm cert_rustls.csr +rm rootca_rustls.key +rm rootca_rustls.srl diff --git a/quiche/examples/rootca_rustls.crt b/quiche/examples/rootca_rustls.crt new file mode 100644 index 0000000000..75432ad504 --- /dev/null +++ b/quiche/examples/rootca_rustls.crt @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDZzCCAk+gAwIBAgIUYhjE4OLyZdy528n3RT0KN2xro+owDQYJKoZIhvcNAQEL +BQAwQjELMAkGA1UEBhMCWFgxFTATBgNVBAcMDERlZmF1bHQgQ2l0eTEcMBoGA1UE +CgwTRGVmYXVsdCBDb21wYW55IEx0ZDAgFw0yNTA1MDUxMjQzMDdaGA8yMDUyMDky +MDEyNDMwN1owQjELMAkGA1UEBhMCWFgxFTATBgNVBAcMDERlZmF1bHQgQ2l0eTEc +MBoGA1UECgwTRGVmYXVsdCBDb21wYW55IEx0ZDCCASIwDQYJKoZIhvcNAQEBBQAD +ggEPADCCAQoCggEBAM2fTBsAiEQv58w//PQPJPECwXQ0pZ4oMP5babMl8EfT9W1j +cC8pEzV6T6Ao+YSRphLdI2oLNhCtqLggveaE08hMEFqUz4k7L+GMKv8H0jvKvC08 +3z0Xe6xG3BfghE+MFg+fEosWHPM36pg8gZTb/zj7ucWeeVBivxdtqTnLGO06yRRO +V4oT/xBlJKrvAzsIS8ZwQC7glldcQegStmofO/PBK4kSW2GTld3pAOsNxIh1haNY +g7moQiQQn3MsAigXNX3Y3p8Hg7iYip+09WT73cUXn1fKtbJjTu7U1DGwVv1mpxeg +1hSviTh5oeXUsbE4I1LEY5HHcq5RxUsbYoBMjv0CAwEAAaNTMFEwHQYDVR0OBBYE +FCPITzjeQmnP0pehCcyLdCr4FyX5MB8GA1UdIwQYMBaAFCPITzjeQmnP0pehCcyL +dCr4FyX5MA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAJRsINU5 +ZYE76j775n8vd9pS0auZzVa5vIWY9292rWAny4H3Pub5DTg7h6JlPmg3/rUjluIl +kK0RgFsRfDRCakOjdTfhBBCtfNARQSeoH3Y7gmELH8D0SojMUq1x4ttPyGOUXM39 +MngYOQemQMKk/OxB87rAJN4av46jv0NYK8unMLM5HKtmBK4nWG3T8KRZRfa5JeFl +mKrvn7C8O8iW8zlLHW43Upx4L+HLvvGZT3eFjfYOUghzWnbjIVmhy6toJcpdhIvD +kCDY1/CGBrgYFZskg3uOATMLAYIUjS6rWqSeKtGOwnTU1AqQ4rM/cjMGrjSsfYYs +TsXIILlIsXPy7/w= +-----END CERTIFICATE----- From 9f492b6ddd3eedf902ff9f54f21f90536d1893e2 Mon Sep 17 00:00:00 2001 From: Harald Gutmann Date: Tue, 6 May 2025 15:03:22 +0200 Subject: [PATCH 10/14] tests: update for boringssl & rustls usage --- quiche/src/h3/mod.rs | 148 +++-------- quiche/src/lib.rs | 620 ++++++++++++++----------------------------- 2 files changed, 247 insertions(+), 521 deletions(-) diff --git a/quiche/src/h3/mod.rs b/quiche/src/h3/mod.rs index 4eaf0f78e6..1b828a1c7c 100644 --- a/quiche/src/h3/mod.rs +++ b/quiche/src/h3/mod.rs @@ -3225,6 +3225,14 @@ pub mod testing { use crate::testing; + pub(super) static KEY: &str = "examples/cert.key"; + + #[cfg(not(feature = "rustls"))] + pub(super) static CERT: &str = "examples/cert.crt"; + + #[cfg(feature = "rustls")] + pub(super) static CERT: &str = "examples/cert_rustls.crt"; + /// Session is an HTTP/3 test helper structure. It holds a client, server /// and pipe that allows them to communicate. /// @@ -3258,10 +3266,10 @@ pub mod testing { let mut config = crate::Config::new(crate::PROTOCOL_VERSION)?; config.load_cert_chain_from_pem_file( - &path_relative_to_manifest_dir("examples/cert.crt"), + &path_relative_to_manifest_dir(CERT), )?; config.load_priv_key_from_pem_file( - &path_relative_to_manifest_dir("examples/cert.key"), + &path_relative_to_manifest_dir(KEY), )?; config.set_application_protos(&[b"h3"])?; config.set_initial_max_data(1500); @@ -3594,12 +3602,8 @@ mod tests { let mut buf = [0; 65535]; let mut config = crate::Config::new(crate::PROTOCOL_VERSION).unwrap(); - config - .load_cert_chain_from_pem_file("examples/cert.crt") - .unwrap(); - config - .load_priv_key_from_pem_file("examples/cert.key") - .unwrap(); + config.load_cert_chain_from_pem_file(CERT).unwrap(); + config.load_priv_key_from_pem_file(KEY).unwrap(); config .set_application_protos(&[b"proto1", b"proto2"]) .unwrap(); @@ -5553,12 +5557,8 @@ mod tests { /// Tests that the max header list size setting is enforced. fn request_max_header_size_limit() { let mut config = crate::Config::new(crate::PROTOCOL_VERSION).unwrap(); - config - .load_cert_chain_from_pem_file("examples/cert.crt") - .unwrap(); - config - .load_priv_key_from_pem_file("examples/cert.key") - .unwrap(); + config.load_cert_chain_from_pem_file(CERT).unwrap(); + config.load_priv_key_from_pem_file(KEY).unwrap(); config.set_application_protos(&[b"h3"]).unwrap(); config.set_initial_max_data(1500); config.set_initial_max_stream_data_bidi_local(150); @@ -5690,12 +5690,8 @@ mod tests { /// Tests that we limit sending HEADERS based on the stream capacity. fn headers_blocked() { let mut config = crate::Config::new(crate::PROTOCOL_VERSION).unwrap(); - config - .load_cert_chain_from_pem_file("examples/cert.crt") - .unwrap(); - config - .load_priv_key_from_pem_file("examples/cert.key") - .unwrap(); + config.load_cert_chain_from_pem_file(CERT).unwrap(); + config.load_priv_key_from_pem_file(KEY).unwrap(); config.set_application_protos(&[b"h3"]).unwrap(); config.set_initial_max_data(70); config.set_initial_max_stream_data_bidi_local(150); @@ -5743,12 +5739,8 @@ mod tests { /// Ensure StreamBlocked when connection flow control prevents headers. fn headers_blocked_on_conn() { let mut config = crate::Config::new(crate::PROTOCOL_VERSION).unwrap(); - config - .load_cert_chain_from_pem_file("examples/cert.crt") - .unwrap(); - config - .load_priv_key_from_pem_file("examples/cert.key") - .unwrap(); + config.load_cert_chain_from_pem_file(CERT).unwrap(); + config.load_priv_key_from_pem_file(KEY).unwrap(); config.set_application_protos(&[b"h3"]).unwrap(); config.set_initial_max_data(70); config.set_initial_max_stream_data_bidi_local(150); @@ -5804,12 +5796,8 @@ mod tests { use crate::testing::decode_pkt; let mut config = crate::Config::new(crate::PROTOCOL_VERSION).unwrap(); - config - .load_cert_chain_from_pem_file("examples/cert.crt") - .unwrap(); - config - .load_priv_key_from_pem_file("examples/cert.key") - .unwrap(); + config.load_cert_chain_from_pem_file(CERT).unwrap(); + config.load_priv_key_from_pem_file(KEY).unwrap(); config.set_application_protos(&[b"h3"]).unwrap(); config.set_initial_max_data(10000); // large connection-level flow control config.set_initial_max_stream_data_bidi_local(80); @@ -5938,12 +5926,8 @@ mod tests { /// Ensure stream doesn't hang due to small cwnd. fn send_body_stream_blocked_by_small_cwnd() { let mut config = crate::Config::new(crate::PROTOCOL_VERSION).unwrap(); - config - .load_cert_chain_from_pem_file("examples/cert.crt") - .unwrap(); - config - .load_priv_key_from_pem_file("examples/cert.key") - .unwrap(); + config.load_cert_chain_from_pem_file(CERT).unwrap(); + config.load_priv_key_from_pem_file(KEY).unwrap(); config.set_application_protos(&[b"h3"]).unwrap(); config.set_initial_max_data(100000); // large connection-level flow control config.set_initial_max_stream_data_bidi_local(100000); @@ -6010,12 +5994,8 @@ mod tests { /// Ensure stream doesn't hang due to small cwnd. fn send_body_stream_blocked_zero_length() { let mut config = crate::Config::new(crate::PROTOCOL_VERSION).unwrap(); - config - .load_cert_chain_from_pem_file("examples/cert.crt") - .unwrap(); - config - .load_priv_key_from_pem_file("examples/cert.key") - .unwrap(); + config.load_cert_chain_from_pem_file(CERT).unwrap(); + config.load_priv_key_from_pem_file(KEY).unwrap(); config.set_application_protos(&[b"h3"]).unwrap(); config.set_initial_max_data(100000); // large connection-level flow control config.set_initial_max_stream_data_bidi_local(100000); @@ -6143,12 +6123,8 @@ mod tests { /// Tests that blocked 0-length DATA writes are reported correctly. fn zero_length_data_blocked() { let mut config = crate::Config::new(crate::PROTOCOL_VERSION).unwrap(); - config - .load_cert_chain_from_pem_file("examples/cert.crt") - .unwrap(); - config - .load_priv_key_from_pem_file("examples/cert.key") - .unwrap(); + config.load_cert_chain_from_pem_file(CERT).unwrap(); + config.load_priv_key_from_pem_file(KEY).unwrap(); config.set_application_protos(&[b"h3"]).unwrap(); config.set_initial_max_data(69); config.set_initial_max_stream_data_bidi_local(150); @@ -6198,12 +6174,8 @@ mod tests { /// Tests that receiving an empty SETTINGS frame is handled and reported. fn empty_settings() { let mut config = crate::Config::new(crate::PROTOCOL_VERSION).unwrap(); - config - .load_cert_chain_from_pem_file("examples/cert.crt") - .unwrap(); - config - .load_priv_key_from_pem_file("examples/cert.key") - .unwrap(); + config.load_cert_chain_from_pem_file(CERT).unwrap(); + config.load_priv_key_from_pem_file(KEY).unwrap(); config.set_application_protos(&[b"h3"]).unwrap(); config.set_initial_max_data(1500); config.set_initial_max_stream_data_bidi_local(150); @@ -6228,12 +6200,8 @@ mod tests { /// Tests that receiving a H3_DATAGRAM setting is ok. fn dgram_setting() { let mut config = crate::Config::new(crate::PROTOCOL_VERSION).unwrap(); - config - .load_cert_chain_from_pem_file("examples/cert.crt") - .unwrap(); - config - .load_priv_key_from_pem_file("examples/cert.key") - .unwrap(); + config.load_cert_chain_from_pem_file(CERT).unwrap(); + config.load_priv_key_from_pem_file(KEY).unwrap(); config.set_application_protos(&[b"h3"]).unwrap(); config.set_initial_max_data(70); config.set_initial_max_stream_data_bidi_local(150); @@ -6273,12 +6241,8 @@ mod tests { /// an error. fn dgram_setting_no_tp() { let mut config = crate::Config::new(crate::PROTOCOL_VERSION).unwrap(); - config - .load_cert_chain_from_pem_file("examples/cert.crt") - .unwrap(); - config - .load_priv_key_from_pem_file("examples/cert.key") - .unwrap(); + config.load_cert_chain_from_pem_file(CERT).unwrap(); + config.load_priv_key_from_pem_file(KEY).unwrap(); config.set_application_protos(&[b"h3"]).unwrap(); config.set_initial_max_data(70); config.set_initial_max_stream_data_bidi_local(150); @@ -6325,12 +6289,8 @@ mod tests { /// Tests that receiving SETTINGS with prohibited values generates an error. fn settings_h2_prohibited() { let mut config = crate::Config::new(crate::PROTOCOL_VERSION).unwrap(); - config - .load_cert_chain_from_pem_file("examples/cert.crt") - .unwrap(); - config - .load_priv_key_from_pem_file("examples/cert.key") - .unwrap(); + config.load_cert_chain_from_pem_file(CERT).unwrap(); + config.load_priv_key_from_pem_file(KEY).unwrap(); config.set_application_protos(&[b"h3"]).unwrap(); config.set_initial_max_data(70); config.set_initial_max_stream_data_bidi_local(150); @@ -6435,12 +6395,8 @@ mod tests { /// Tests additional settings are actually exchanged by the peers. fn set_additional_settings() { let mut config = crate::Config::new(crate::PROTOCOL_VERSION).unwrap(); - config - .load_cert_chain_from_pem_file("examples/cert.crt") - .unwrap(); - config - .load_priv_key_from_pem_file("examples/cert.key") - .unwrap(); + config.load_cert_chain_from_pem_file(CERT).unwrap(); + config.load_priv_key_from_pem_file(KEY).unwrap(); config.set_application_protos(&[b"h3"]).unwrap(); config.set_initial_max_data(70); config.set_initial_max_stream_data_bidi_local(150); @@ -6559,12 +6515,8 @@ mod tests { /// Send a single DATAGRAM and request. fn poll_datagram_cycling_no_read() { let mut config = crate::Config::new(crate::PROTOCOL_VERSION).unwrap(); - config - .load_cert_chain_from_pem_file("examples/cert.crt") - .unwrap(); - config - .load_priv_key_from_pem_file("examples/cert.key") - .unwrap(); + config.load_cert_chain_from_pem_file(CERT).unwrap(); + config.load_priv_key_from_pem_file(KEY).unwrap(); config.set_application_protos(&[b"h3"]).unwrap(); config.set_initial_max_data(1500); config.set_initial_max_stream_data_bidi_local(150); @@ -6603,12 +6555,8 @@ mod tests { let mut buf = [0; 65535]; let mut config = crate::Config::new(crate::PROTOCOL_VERSION).unwrap(); - config - .load_cert_chain_from_pem_file("examples/cert.crt") - .unwrap(); - config - .load_priv_key_from_pem_file("examples/cert.key") - .unwrap(); + config.load_cert_chain_from_pem_file(CERT).unwrap(); + config.load_priv_key_from_pem_file(KEY).unwrap(); config.set_application_protos(&[b"h3"]).unwrap(); config.set_initial_max_data(1500); config.set_initial_max_stream_data_bidi_local(150); @@ -6688,12 +6636,8 @@ mod tests { let mut buf = [0; 65535]; let mut config = crate::Config::new(crate::PROTOCOL_VERSION).unwrap(); - config - .load_cert_chain_from_pem_file("examples/cert.crt") - .unwrap(); - config - .load_priv_key_from_pem_file("examples/cert.key") - .unwrap(); + config.load_cert_chain_from_pem_file(CERT).unwrap(); + config.load_priv_key_from_pem_file(KEY).unwrap(); config.set_application_protos(&[b"h3"]).unwrap(); config.set_initial_max_data(1500); config.set_initial_max_stream_data_bidi_local(150); @@ -7063,12 +7007,8 @@ mod tests { let mut buf = [0; 65535]; let mut config = crate::Config::new(crate::PROTOCOL_VERSION).unwrap(); - config - .load_cert_chain_from_pem_file("examples/cert.crt") - .unwrap(); - config - .load_priv_key_from_pem_file("examples/cert.key") - .unwrap(); + config.load_cert_chain_from_pem_file(CERT).unwrap(); + config.load_priv_key_from_pem_file(KEY).unwrap(); config.set_application_protos(&[b"h3"]).unwrap(); config.set_initial_max_data(1500); config.set_initial_max_stream_data_bidi_local(150); diff --git a/quiche/src/lib.rs b/quiche/src/lib.rs index 8578877043..bbf0f74666 100644 --- a/quiche/src/lib.rs +++ b/quiche/src/lib.rs @@ -9033,6 +9033,13 @@ impl TransportParams { pub mod testing { use super::*; + pub(super) static KEY: &str = "examples/cert.key"; + + #[cfg(not(feature = "rustls"))] + pub(super) static CERT: &str = "examples/cert.crt"; + #[cfg(feature = "rustls")] + pub(super) static CERT: &str = "examples/cert_rustls.crt"; + pub struct Pipe { pub client: Connection, pub server: Connection, @@ -9042,8 +9049,8 @@ pub mod testing { pub fn new(cc_algorithm_name: &str) -> Result { let mut config = Config::new(crate::PROTOCOL_VERSION)?; assert_eq!(config.set_cc_algorithm_name(cc_algorithm_name), Ok(())); - config.load_cert_chain_from_pem_file("examples/cert.crt")?; - config.load_priv_key_from_pem_file("examples/cert.key")?; + config.load_cert_chain_from_pem_file(CERT)?; + config.load_priv_key_from_pem_file(KEY)?; config.set_application_protos(&[b"proto1", b"proto2"])?; config.set_initial_max_data(30); config.set_initial_max_stream_data_bidi_local(15); @@ -9138,8 +9145,8 @@ pub mod testing { let server_addr = Pipe::server_addr(); let mut config = Config::new(crate::PROTOCOL_VERSION)?; - config.load_cert_chain_from_pem_file("examples/cert.crt")?; - config.load_priv_key_from_pem_file("examples/cert.key")?; + config.load_cert_chain_from_pem_file(CERT)?; + config.load_priv_key_from_pem_file(KEY)?; config.set_application_protos(&[b"proto1", b"proto2"])?; config.set_initial_max_data(30); config.set_initial_max_stream_data_bidi_local(15); @@ -9548,8 +9555,19 @@ mod tests { use crate::range_buf::RangeBuf; use rstest::rstest; + use super::testing::*; use super::*; + #[cfg(not(feature = "rustls"))] + pub(super) static CERT_BIG: &str = "examples/cert-big.crt"; + #[cfg(not(feature = "rustls"))] + pub(super) static ROOTCA: &str = "examples/rootca.crt"; + + #[cfg(feature = "rustls")] + pub(super) static CERT_BIG: &str = "examples/cert-big_rustls.crt"; + #[cfg(feature = "rustls")] + pub(super) static ROOTCA: &str = "examples/rootca_rustls.crt"; + #[test] fn transport_params() { // Server encodes, client decodes. @@ -9790,9 +9808,7 @@ mod tests { fn verify_custom_root() { let mut config = Config::new(PROTOCOL_VERSION).unwrap(); config.verify_peer(true); - config - .load_verify_locations_from_file("examples/rootca.crt") - .unwrap(); + config.load_verify_locations_from_file(ROOTCA).unwrap(); config .set_application_protos(&[b"proto1", b"proto2"]) .unwrap(); @@ -9807,12 +9823,8 @@ mod tests { #[test] fn verify_client_invalid() { let mut server_config = Config::new(crate::PROTOCOL_VERSION).unwrap(); - server_config - .load_cert_chain_from_pem_file("examples/cert.crt") - .unwrap(); - server_config - .load_priv_key_from_pem_file("examples/cert.key") - .unwrap(); + server_config.load_cert_chain_from_pem_file(CERT).unwrap(); + server_config.load_priv_key_from_pem_file(KEY).unwrap(); server_config .set_application_protos(&[b"proto1", b"proto2"]) .unwrap(); @@ -9826,12 +9838,8 @@ mod tests { server_config.verify_peer(true); let mut client_config = Config::new(crate::PROTOCOL_VERSION).unwrap(); - client_config - .load_cert_chain_from_pem_file("examples/cert.crt") - .unwrap(); - client_config - .load_priv_key_from_pem_file("examples/cert.key") - .unwrap(); + client_config.load_cert_chain_from_pem_file(CERT).unwrap(); + client_config.load_priv_key_from_pem_file(KEY).unwrap(); client_config .set_application_protos(&[b"proto1", b"proto2"]) .unwrap(); @@ -9843,7 +9851,7 @@ mod tests { // The client is able to verify the server's certificate with the // appropriate CA. client_config - .load_verify_locations_from_file("examples/rootca.crt") + .load_verify_locations_from_file(ROOTCA) .unwrap(); client_config.verify_peer(true); @@ -9854,19 +9862,19 @@ mod tests { .unwrap(); assert_eq!(pipe.handshake(), Err(Error::TlsFail)); + #[cfg(not(feature = "rustls"))] // Client did send a certificate. assert!(pipe.server.peer_cert().is_some()); + #[cfg(feature = "rustls")] + // rustls does not provide the peer certificate when verification failed + assert!(pipe.server.peer_cert().is_none()); } #[test] fn verify_client_anonymous() { let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap(); - config - .load_cert_chain_from_pem_file("examples/cert.crt") - .unwrap(); - config - .load_priv_key_from_pem_file("examples/cert.key") - .unwrap(); + config.load_cert_chain_from_pem_file(CERT).unwrap(); + config.load_priv_key_from_pem_file(KEY).unwrap(); config .set_application_protos(&[b"proto1", b"proto2"]) .unwrap(); @@ -9993,6 +10001,7 @@ mod tests { // Disable session tickets on the server (SSL_OP_NO_TICKET) to avoid // triggering 1-RTT packet send with a CRYPTO frame. + #[cfg(not(feature = "rustls"))] pipe.server.handshake.set_options(0x0000_4000); assert_eq!(pipe.handshake(), Ok(())); @@ -10062,6 +10071,7 @@ mod tests { } #[rstest] + #[cfg(not(feature = "rustls"))] fn handshake_resumption( #[values("cubic", "bbr2", "bbr2_gcongestion")] cc_algorithm_name: &str, ) { @@ -10077,12 +10087,8 @@ mod tests { let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap(); assert_eq!(config.set_cc_algorithm_name(cc_algorithm_name), Ok(())); - config - .load_cert_chain_from_pem_file("examples/cert.crt") - .unwrap(); - config - .load_priv_key_from_pem_file("examples/cert.key") - .unwrap(); + config.load_cert_chain_from_pem_file(CERT).unwrap(); + config.load_priv_key_from_pem_file(KEY).unwrap(); config .set_application_protos(&[b"proto1", b"proto2"]) .unwrap(); @@ -10107,12 +10113,8 @@ mod tests { // Configure session on new connection and perform handshake. let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap(); - config - .load_cert_chain_from_pem_file("examples/cert.crt") - .unwrap(); - config - .load_priv_key_from_pem_file("examples/cert.key") - .unwrap(); + config.load_cert_chain_from_pem_file(CERT).unwrap(); + config.load_priv_key_from_pem_file(KEY).unwrap(); config .set_application_protos(&[b"proto1", b"proto2"]) .unwrap(); @@ -10170,12 +10172,8 @@ mod tests { let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap(); assert_eq!(config.set_cc_algorithm_name(cc_algorithm_name), Ok(())); - config - .load_cert_chain_from_pem_file("examples/cert.crt") - .unwrap(); - config - .load_priv_key_from_pem_file("examples/cert.key") - .unwrap(); + config.load_cert_chain_from_pem_file(CERT).unwrap(); + config.load_priv_key_from_pem_file(KEY).unwrap(); config .set_application_protos(&[b"proto1", b"proto2"]) .unwrap(); @@ -10235,12 +10233,8 @@ mod tests { let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap(); assert_eq!(config.set_cc_algorithm_name(cc_algorithm_name), Ok(())); - config - .load_cert_chain_from_pem_file("examples/cert.crt") - .unwrap(); - config - .load_priv_key_from_pem_file("examples/cert.key") - .unwrap(); + config.load_cert_chain_from_pem_file(CERT).unwrap(); + config.load_priv_key_from_pem_file(KEY).unwrap(); config .set_application_protos(&[b"proto1", b"proto2"]) .unwrap(); @@ -10310,12 +10304,8 @@ mod tests { let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap(); assert_eq!(config.set_cc_algorithm_name(cc_algorithm_name), Ok(())); - config - .load_cert_chain_from_pem_file("examples/cert.crt") - .unwrap(); - config - .load_priv_key_from_pem_file("examples/cert.key") - .unwrap(); + config.load_cert_chain_from_pem_file(CERT).unwrap(); + config.load_priv_key_from_pem_file(KEY).unwrap(); config .set_application_protos(&[b"proto1", b"proto2"]) .unwrap(); @@ -10371,12 +10361,8 @@ mod tests { let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap(); assert_eq!(config.set_cc_algorithm_name(cc_algorithm_name), Ok(())); - config - .load_cert_chain_from_pem_file("examples/cert.crt") - .unwrap(); - config - .load_priv_key_from_pem_file("examples/cert.key") - .unwrap(); + config.load_cert_chain_from_pem_file(CERT).unwrap(); + config.load_priv_key_from_pem_file(KEY).unwrap(); config .set_application_protos(&[b"proto1", b"proto2"]) .unwrap(); @@ -10440,12 +10426,8 @@ mod tests { ) { let mut config = Config::new(PROTOCOL_VERSION).unwrap(); assert_eq!(config.set_cc_algorithm_name(cc_algorithm_name), Ok(())); - config - .load_cert_chain_from_pem_file("examples/cert-big.crt") - .unwrap(); - config - .load_priv_key_from_pem_file("examples/cert.key") - .unwrap(); + config.load_cert_chain_from_pem_file(CERT_BIG).unwrap(); + config.load_priv_key_from_pem_file(KEY).unwrap(); config .set_application_protos(&[b"proto1", b"proto2"]) .unwrap(); @@ -10470,12 +10452,8 @@ mod tests { let mut config = Config::new(PROTOCOL_VERSION).unwrap(); assert_eq!(config.set_cc_algorithm_name(cc_algorithm_name), Ok(())); - config - .load_cert_chain_from_pem_file("examples/cert-big.crt") - .unwrap(); - config - .load_priv_key_from_pem_file("examples/cert.key") - .unwrap(); + config.load_cert_chain_from_pem_file(CERT_BIG).unwrap(); + config.load_priv_key_from_pem_file(KEY).unwrap(); config .set_application_protos(&[b"proto1", b"proto2"]) .unwrap(); @@ -10525,12 +10503,8 @@ mod tests { let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap(); assert_eq!(config.set_cc_algorithm_name(cc_algorithm_name), Ok(())); - config - .load_cert_chain_from_pem_file("examples/cert.crt") - .unwrap(); - config - .load_priv_key_from_pem_file("examples/cert.key") - .unwrap(); + config.load_cert_chain_from_pem_file(CERT).unwrap(); + config.load_priv_key_from_pem_file(KEY).unwrap(); config .set_application_protos(&[b"proto1", b"proto2"]) .unwrap(); @@ -10586,12 +10560,8 @@ mod tests { ) { let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap(); assert_eq!(config.set_cc_algorithm_name(cc_algorithm_name), Ok(())); - config - .load_cert_chain_from_pem_file("examples/cert.crt") - .unwrap(); - config - .load_priv_key_from_pem_file("examples/cert.key") - .unwrap(); + config.load_cert_chain_from_pem_file(CERT).unwrap(); + config.load_priv_key_from_pem_file(KEY).unwrap(); config .set_application_protos(&[b"proto1", b"proto2"]) .unwrap(); @@ -12137,12 +12107,8 @@ mod tests { let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap(); assert_eq!(config.set_cc_algorithm_name(cc_algorithm_name), Ok(())); - config - .load_cert_chain_from_pem_file("examples/cert.crt") - .unwrap(); - config - .load_priv_key_from_pem_file("examples/cert.key") - .unwrap(); + config.load_cert_chain_from_pem_file(CERT).unwrap(); + config.load_priv_key_from_pem_file(KEY).unwrap(); config .set_application_protos(&[b"proto1", b"proto2"]) .unwrap(); @@ -12342,12 +12308,8 @@ mod tests { let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap(); assert_eq!(config.set_cc_algorithm_name(cc_algorithm_name), Ok(())); - config - .load_cert_chain_from_pem_file("examples/cert.crt") - .unwrap(); - config - .load_priv_key_from_pem_file("examples/cert.key") - .unwrap(); + config.load_cert_chain_from_pem_file(CERT).unwrap(); + config.load_priv_key_from_pem_file(KEY).unwrap(); config .set_application_protos(&[b"proto1", b"proto2"]) .unwrap(); @@ -12511,12 +12473,8 @@ mod tests { ) { let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap(); assert_eq!(config.set_cc_algorithm_name(cc_algorithm_name), Ok(())); - config - .load_cert_chain_from_pem_file("examples/cert.crt") - .unwrap(); - config - .load_priv_key_from_pem_file("examples/cert.key") - .unwrap(); + config.load_cert_chain_from_pem_file(CERT).unwrap(); + config.load_priv_key_from_pem_file(KEY).unwrap(); config .set_application_protos(&[b"proto1", b"proto2"]) .unwrap(); @@ -12789,12 +12747,8 @@ mod tests { ) { let mut config = crate::Config::new(crate::PROTOCOL_VERSION).unwrap(); assert_eq!(config.set_cc_algorithm_name(cc_algorithm_name), Ok(())); - config - .load_cert_chain_from_pem_file("examples/cert.crt") - .unwrap(); - config - .load_priv_key_from_pem_file("examples/cert.key") - .unwrap(); + config.load_cert_chain_from_pem_file(CERT).unwrap(); + config.load_priv_key_from_pem_file(KEY).unwrap(); config.set_application_protos(&[b"h3"]).unwrap(); config.set_initial_max_data(70); config.set_initial_max_stream_data_bidi_local(150000); @@ -13164,12 +13118,8 @@ mod tests { let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap(); assert_eq!(config.set_cc_algorithm_name(cc_algorithm_name), Ok(())); - config - .load_cert_chain_from_pem_file("examples/cert.crt") - .unwrap(); - config - .load_priv_key_from_pem_file("examples/cert.key") - .unwrap(); + config.load_cert_chain_from_pem_file(CERT).unwrap(); + config.load_priv_key_from_pem_file(KEY).unwrap(); config .set_application_protos(&[b"proto1", b"proto2"]) .unwrap(); @@ -13226,12 +13176,8 @@ mod tests { ) { let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap(); assert_eq!(config.set_cc_algorithm_name(cc_algorithm_name), Ok(())); - config - .load_cert_chain_from_pem_file("examples/cert.crt") - .unwrap(); - config - .load_priv_key_from_pem_file("examples/cert.key") - .unwrap(); + config.load_cert_chain_from_pem_file(CERT).unwrap(); + config.load_priv_key_from_pem_file(KEY).unwrap(); config .set_application_protos(&[b"proto1", b"proto2"]) .unwrap(); @@ -13305,12 +13251,8 @@ mod tests { ) { let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap(); assert_eq!(config.set_cc_algorithm_name(cc_algorithm_name), Ok(())); - config - .load_cert_chain_from_pem_file("examples/cert.crt") - .unwrap(); - config - .load_priv_key_from_pem_file("examples/cert.key") - .unwrap(); + config.load_cert_chain_from_pem_file(CERT).unwrap(); + config.load_priv_key_from_pem_file(KEY).unwrap(); config .set_application_protos(&[b"proto1", b"proto2"]) .unwrap(); @@ -13571,7 +13513,12 @@ mod tests { assert_eq!(pipe.handshake(), Ok(())); match pipe.client.peer_cert() { - Some(c) => assert_eq!(c.len(), 753), + Some(c) => { + #[cfg(not(feature = "rustls"))] + assert_eq!(c.len(), 753); + #[cfg(feature = "rustls")] + assert_eq!(c.len(), 847); + }, None => panic!("missing server certificate"), } @@ -13583,12 +13530,8 @@ mod tests { ) { let mut config = Config::new(PROTOCOL_VERSION).unwrap(); assert_eq!(config.set_cc_algorithm_name(cc_algorithm_name), Ok(())); - config - .load_cert_chain_from_pem_file("examples/cert-big.crt") - .unwrap(); - config - .load_priv_key_from_pem_file("examples/cert.key") - .unwrap(); + config.load_cert_chain_from_pem_file(CERT_BIG).unwrap(); + config.load_priv_key_from_pem_file(KEY).unwrap(); config .set_application_protos(&[b"proto1", b"proto2"]) .unwrap(); @@ -13611,12 +13554,8 @@ mod tests { let mut config = Config::new(PROTOCOL_VERSION).unwrap(); assert_eq!(config.set_cc_algorithm_name(cc_algorithm_name), Ok(())); - config - .load_cert_chain_from_pem_file("examples/cert.crt") - .unwrap(); - config - .load_priv_key_from_pem_file("examples/cert.key") - .unwrap(); + config.load_cert_chain_from_pem_file(CERT).unwrap(); + config.load_priv_key_from_pem_file(KEY).unwrap(); config .set_application_protos(&[b"proto1", b"proto2"]) .unwrap(); @@ -13680,12 +13619,8 @@ mod tests { let mut config = Config::new(PROTOCOL_VERSION).unwrap(); assert_eq!(config.set_cc_algorithm_name(cc_algorithm_name), Ok(())); - config - .load_cert_chain_from_pem_file("examples/cert.crt") - .unwrap(); - config - .load_priv_key_from_pem_file("examples/cert.key") - .unwrap(); + config.load_cert_chain_from_pem_file(CERT).unwrap(); + config.load_priv_key_from_pem_file(KEY).unwrap(); config .set_application_protos(&[b"proto1", b"proto2"]) .unwrap(); @@ -13754,12 +13689,8 @@ mod tests { let mut config = Config::new(PROTOCOL_VERSION).unwrap(); assert_eq!(config.set_cc_algorithm_name(cc_algorithm_name), Ok(())); - config - .load_cert_chain_from_pem_file("examples/cert.crt") - .unwrap(); - config - .load_priv_key_from_pem_file("examples/cert.key") - .unwrap(); + config.load_cert_chain_from_pem_file(CERT).unwrap(); + config.load_priv_key_from_pem_file(KEY).unwrap(); config .set_application_protos(&[b"proto1", b"proto2"]) .unwrap(); @@ -13817,12 +13748,8 @@ mod tests { let mut config = Config::new(PROTOCOL_VERSION).unwrap(); assert_eq!(config.set_cc_algorithm_name(cc_algorithm_name), Ok(())); - config - .load_cert_chain_from_pem_file("examples/cert.crt") - .unwrap(); - config - .load_priv_key_from_pem_file("examples/cert.key") - .unwrap(); + config.load_cert_chain_from_pem_file(CERT).unwrap(); + config.load_priv_key_from_pem_file(KEY).unwrap(); config .set_application_protos(&[b"proto1", b"proto2"]) .unwrap(); @@ -14246,12 +14173,8 @@ mod tests { config .set_application_protos(&[b"proto1", b"proto2"]) .unwrap(); - config - .load_cert_chain_from_pem_file("examples/cert.crt") - .unwrap(); - config - .load_priv_key_from_pem_file("examples/cert.key") - .unwrap(); + config.load_cert_chain_from_pem_file(CERT).unwrap(); + config.load_priv_key_from_pem_file(KEY).unwrap(); config.set_initial_max_data(50000); config.set_initial_max_stream_data_bidi_local(12000); config.set_initial_max_stream_data_bidi_remote(12000); @@ -14298,12 +14221,8 @@ mod tests { ) { let mut config = Config::new(PROTOCOL_VERSION).unwrap(); assert_eq!(config.set_cc_algorithm_name(cc_algorithm_name), Ok(())); - config - .load_cert_chain_from_pem_file("examples/cert.crt") - .unwrap(); - config - .load_priv_key_from_pem_file("examples/cert.key") - .unwrap(); + config.load_cert_chain_from_pem_file(CERT).unwrap(); + config.load_priv_key_from_pem_file(KEY).unwrap(); config .set_application_protos(&[b"proto1", b"proto2"]) .unwrap(); @@ -14328,6 +14247,8 @@ mod tests { } else { if cfg!(feature = "openssl") { Ok(12345) + } else if cfg!(feature = "rustls") { + Ok(12324) } else { Ok(12299) } @@ -14377,12 +14298,8 @@ mod tests { ) { let mut config = Config::new(PROTOCOL_VERSION).unwrap(); assert_eq!(config.set_cc_algorithm_name(cc_algorithm_name), Ok(())); - config - .load_cert_chain_from_pem_file("examples/cert.crt") - .unwrap(); - config - .load_priv_key_from_pem_file("examples/cert.key") - .unwrap(); + config.load_cert_chain_from_pem_file(CERT).unwrap(); + config.load_priv_key_from_pem_file(KEY).unwrap(); config .set_application_protos(&[b"proto1", b"proto2"]) .unwrap(); @@ -14407,6 +14324,8 @@ mod tests { } else { if cfg!(feature = "openssl") { Ok(12345) + } else if cfg!(feature = "rustls") { + Ok(12324) } else { Ok(12299) } @@ -14663,12 +14582,8 @@ mod tests { let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap(); assert_eq!(config.set_cc_algorithm_name(cc_algorithm_name), Ok(())); - config - .load_cert_chain_from_pem_file("examples/cert.crt") - .unwrap(); - config - .load_priv_key_from_pem_file("examples/cert.key") - .unwrap(); + config.load_cert_chain_from_pem_file(CERT).unwrap(); + config.load_priv_key_from_pem_file(KEY).unwrap(); config .set_application_protos(&[b"proto1", b"proto2"]) .unwrap(); @@ -14889,12 +14804,8 @@ mod tests { let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap(); assert_eq!(config.set_cc_algorithm_name(cc_algorithm_name), Ok(())); - config - .load_cert_chain_from_pem_file("examples/cert.crt") - .unwrap(); - config - .load_priv_key_from_pem_file("examples/cert.key") - .unwrap(); + config.load_cert_chain_from_pem_file(CERT).unwrap(); + config.load_priv_key_from_pem_file(KEY).unwrap(); config .set_application_protos(&[b"proto1", b"proto2"]) .unwrap(); @@ -15012,12 +14923,8 @@ mod tests { let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap(); assert_eq!(config.set_cc_algorithm_name(cc_algorithm_name), Ok(())); - config - .load_cert_chain_from_pem_file("examples/cert.crt") - .unwrap(); - config - .load_priv_key_from_pem_file("examples/cert.key") - .unwrap(); + config.load_cert_chain_from_pem_file(CERT).unwrap(); + config.load_priv_key_from_pem_file(KEY).unwrap(); config .set_application_protos(&[b"proto1", b"proto2"]) .unwrap(); @@ -15333,12 +15240,8 @@ mod tests { let mut config = Config::new(PROTOCOL_VERSION).unwrap(); assert_eq!(config.set_cc_algorithm_name(cc_algorithm_name), Ok(())); - config - .load_cert_chain_from_pem_file("examples/cert-big.crt") - .unwrap(); - config - .load_priv_key_from_pem_file("examples/cert.key") - .unwrap(); + config.load_cert_chain_from_pem_file(CERT_BIG).unwrap(); + config.load_priv_key_from_pem_file(KEY).unwrap(); config .set_application_protos(&[b"proto1", b"proto2"]) .unwrap(); @@ -15448,12 +15351,8 @@ mod tests { let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap(); assert_eq!(config.set_cc_algorithm_name(cc_algorithm_name), Ok(())); - config - .load_cert_chain_from_pem_file("examples/cert.crt") - .unwrap(); - config - .load_priv_key_from_pem_file("examples/cert.key") - .unwrap(); + config.load_cert_chain_from_pem_file(CERT).unwrap(); + config.load_priv_key_from_pem_file(KEY).unwrap(); config .set_application_protos(&[b"proto1", b"proto2"]) .unwrap(); @@ -15534,12 +15433,8 @@ mod tests { let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap(); assert_eq!(config.set_cc_algorithm_name(cc_algorithm_name), Ok(())); - config - .load_cert_chain_from_pem_file("examples/cert.crt") - .unwrap(); - config - .load_priv_key_from_pem_file("examples/cert.key") - .unwrap(); + config.load_cert_chain_from_pem_file(CERT).unwrap(); + config.load_priv_key_from_pem_file(KEY).unwrap(); config .set_application_protos(&[b"proto1", b"proto2"]) .unwrap(); @@ -15574,12 +15469,8 @@ mod tests { let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap(); assert_eq!(config.set_cc_algorithm_name(cc_algorithm_name), Ok(())); - config - .load_cert_chain_from_pem_file("examples/cert.crt") - .unwrap(); - config - .load_priv_key_from_pem_file("examples/cert.key") - .unwrap(); + config.load_cert_chain_from_pem_file(CERT).unwrap(); + config.load_priv_key_from_pem_file(KEY).unwrap(); config .set_application_protos(&[b"proto1", b"proto2"]) .unwrap(); @@ -15651,12 +15542,8 @@ mod tests { let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap(); assert_eq!(config.set_cc_algorithm_name(cc_algorithm_name), Ok(())); - config - .load_cert_chain_from_pem_file("examples/cert.crt") - .unwrap(); - config - .load_priv_key_from_pem_file("examples/cert.key") - .unwrap(); + config.load_cert_chain_from_pem_file(CERT).unwrap(); + config.load_priv_key_from_pem_file(KEY).unwrap(); config .set_application_protos(&[b"proto1", b"proto2"]) .unwrap(); @@ -15700,12 +15587,8 @@ mod tests { let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap(); assert_eq!(config.set_cc_algorithm_name(cc_algorithm_name), Ok(())); - config - .load_cert_chain_from_pem_file("examples/cert.crt") - .unwrap(); - config - .load_priv_key_from_pem_file("examples/cert.key") - .unwrap(); + config.load_cert_chain_from_pem_file(CERT).unwrap(); + config.load_priv_key_from_pem_file(KEY).unwrap(); config .set_application_protos(&[b"proto1", b"proto2"]) .unwrap(); @@ -15750,12 +15633,8 @@ mod tests { let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap(); assert_eq!(config.set_cc_algorithm_name(cc_algorithm_name), Ok(())); - config - .load_cert_chain_from_pem_file("examples/cert.crt") - .unwrap(); - config - .load_priv_key_from_pem_file("examples/cert.key") - .unwrap(); + config.load_cert_chain_from_pem_file(CERT).unwrap(); + config.load_priv_key_from_pem_file(KEY).unwrap(); config .set_application_protos(&[b"proto1", b"proto2"]) .unwrap(); @@ -15804,12 +15683,8 @@ mod tests { let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap(); assert_eq!(config.set_cc_algorithm_name(cc_algorithm_name), Ok(())); - config - .load_cert_chain_from_pem_file("examples/cert.crt") - .unwrap(); - config - .load_priv_key_from_pem_file("examples/cert.key") - .unwrap(); + config.load_cert_chain_from_pem_file(CERT).unwrap(); + config.load_priv_key_from_pem_file(KEY).unwrap(); config .set_application_protos(&[b"proto1", b"proto2"]) .unwrap(); @@ -15944,6 +15819,7 @@ mod tests { // OpenSSL does not provide a straightforward interface to deal with custom // off-load key signing. #[cfg(not(feature = "openssl"))] + #[cfg(not(feature = "rustls"))] #[rstest] fn app_close_by_server_during_handshake_private_key_failure( #[values("cubic", "bbr2", "bbr2_gcongestion")] cc_algorithm_name: &str, @@ -16245,12 +16121,8 @@ mod tests { server_config.set_cc_algorithm_name(cc_algorithm_name), Ok(()) ); - server_config - .load_cert_chain_from_pem_file("examples/cert.crt") - .unwrap(); - server_config - .load_priv_key_from_pem_file("examples/cert.key") - .unwrap(); + server_config.load_cert_chain_from_pem_file(CERT).unwrap(); + server_config.load_priv_key_from_pem_file(KEY).unwrap(); server_config .set_application_protos(&[b"proto1", b"proto2"]) .unwrap(); @@ -16316,6 +16188,8 @@ mod tests { } else { if cfg!(feature = "openssl") { 13437 + } else if cfg!(feature = "rustls") { + 13535 } else { 13421 } @@ -16333,12 +16207,8 @@ mod tests { let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap(); assert_eq!(config.set_cc_algorithm_name(cc_algorithm_name), Ok(())); - config - .load_cert_chain_from_pem_file("examples/cert.crt") - .unwrap(); - config - .load_priv_key_from_pem_file("examples/cert.key") - .unwrap(); + config.load_cert_chain_from_pem_file(CERT).unwrap(); + config.load_priv_key_from_pem_file(KEY).unwrap(); config .set_application_protos(&[b"proto1", b"proto2"]) .unwrap(); @@ -16382,6 +16252,8 @@ mod tests { } else { if cfg!(feature = "openssl") { 13959 + } else if cfg!(feature = "rustls") { + 13753 } else { 13873 } @@ -16397,6 +16269,8 @@ mod tests { } else { if cfg!(feature = "openssl") { Ok(3959) + } else if cfg!(feature = "rustls") { + Ok(3753) } else { Ok(3873) } @@ -16423,13 +16297,10 @@ mod tests { boring::ssl::SslContextBuilder::new(boring::ssl::SslMethod::tls()) .unwrap(); server_tls_ctx_builder - .set_certificate_chain_file("examples/cert.crt") + .set_certificate_chain_file(CERT) .unwrap(); server_tls_ctx_builder - .set_private_key_file( - "examples/cert.key", - boring::ssl::SslFiletype::PEM, - ) + .set_private_key_file(KEY, boring::ssl::SslFiletype::PEM) .unwrap(); let mut server_config = Config::with_boring_ssl_ctx_builder( @@ -16441,8 +16312,8 @@ mod tests { client_config.set_cc_algorithm_name(cc_algorithm_name), Ok(()) ); - client_config.load_cert_chain_from_pem_file("examples/cert.crt")?; - client_config.load_priv_key_from_pem_file("examples/cert.key")?; + client_config.load_cert_chain_from_pem_file(CERT)?; + client_config.load_priv_key_from_pem_file(KEY)?; for config in [&mut client_config, &mut server_config] { config.set_application_protos(&[b"proto1", b"proto2"])?; @@ -16481,13 +16352,10 @@ mod tests { boring::ssl::SslContextBuilder::new(boring::ssl::SslMethod::tls()) .unwrap(); server_tls_ctx_builder - .set_certificate_chain_file("examples/cert.crt") + .set_certificate_chain_file(CERT) .unwrap(); server_tls_ctx_builder - .set_private_key_file( - "examples/cert.key", - boring::ssl::SslFiletype::PEM, - ) + .set_private_key_file(KEY, boring::ssl::SslFiletype::PEM) .unwrap(); server_tls_ctx_builder.set_select_certificate_callback(|mut hello| { ::set_initial_congestion_window_packets_in_handshake( @@ -16509,8 +16377,8 @@ mod tests { ); let mut client_config = Config::new(crate::PROTOCOL_VERSION)?; - client_config.load_cert_chain_from_pem_file("examples/cert.crt")?; - client_config.load_priv_key_from_pem_file("examples/cert.key")?; + client_config.load_cert_chain_from_pem_file(CERT)?; + client_config.load_priv_key_from_pem_file(KEY)?; for config in [&mut client_config, &mut server_config] { config.set_application_protos(&[b"proto1", b"proto2"])?; @@ -16564,8 +16432,8 @@ mod tests { CUSTOM_INITIAL_CONGESTION_WINDOW_PACKETS, ); // From Pipe::new() - config.load_cert_chain_from_pem_file("examples/cert.crt")?; - config.load_priv_key_from_pem_file("examples/cert.key")?; + config.load_cert_chain_from_pem_file(CERT)?; + config.load_priv_key_from_pem_file(KEY)?; config.set_application_protos(&[b"proto1", b"proto2"])?; config.set_initial_max_data(1000000); config.set_initial_max_stream_data_bidi_local(15); @@ -16591,6 +16459,8 @@ mod tests { let expected = CUSTOM_INITIAL_CONGESTION_WINDOW_PACKETS * 1200 + if cfg!(feature = "openssl") { 1463 + } else if cfg!(feature = "rustls") { + 1561 } else { 1447 }; @@ -16684,12 +16554,8 @@ mod tests { ) { let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap(); assert_eq!(config.set_cc_algorithm_name(cc_algorithm_name), Ok(())); - config - .load_cert_chain_from_pem_file("examples/cert.crt") - .unwrap(); - config - .load_priv_key_from_pem_file("examples/cert.key") - .unwrap(); + config.load_cert_chain_from_pem_file(CERT).unwrap(); + config.load_priv_key_from_pem_file(KEY).unwrap(); config .set_application_protos(&[b"proto1", b"proto2"]) .unwrap(); @@ -16747,12 +16613,8 @@ mod tests { let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap(); assert_eq!(config.set_cc_algorithm_name(cc_algorithm_name), Ok(())); - config - .load_cert_chain_from_pem_file("examples/cert.crt") - .unwrap(); - config - .load_priv_key_from_pem_file("examples/cert.key") - .unwrap(); + config.load_cert_chain_from_pem_file(CERT).unwrap(); + config.load_priv_key_from_pem_file(KEY).unwrap(); config .set_application_protos(&[b"proto1", b"proto2"]) .unwrap(); @@ -16820,12 +16682,8 @@ mod tests { let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap(); assert_eq!(config.set_cc_algorithm_name(cc_algorithm_name), Ok(())); - config - .load_cert_chain_from_pem_file("examples/cert.crt") - .unwrap(); - config - .load_priv_key_from_pem_file("examples/cert.key") - .unwrap(); + config.load_cert_chain_from_pem_file(CERT).unwrap(); + config.load_priv_key_from_pem_file(KEY).unwrap(); config .set_application_protos(&[b"proto1", b"proto2"]) .unwrap(); @@ -16893,12 +16751,8 @@ mod tests { ) { let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap(); assert_eq!(config.set_cc_algorithm_name(cc_algorithm_name), Ok(())); - config - .load_cert_chain_from_pem_file("examples/cert.crt") - .unwrap(); - config - .load_priv_key_from_pem_file("examples/cert.key") - .unwrap(); + config.load_cert_chain_from_pem_file(CERT).unwrap(); + config.load_priv_key_from_pem_file(KEY).unwrap(); config .set_application_protos(&[b"proto1", b"proto2"]) .unwrap(); @@ -16981,12 +16835,8 @@ mod tests { ) { let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap(); assert_eq!(config.set_cc_algorithm_name(cc_algorithm_name), Ok(())); - config - .load_cert_chain_from_pem_file("examples/cert.crt") - .unwrap(); - config - .load_priv_key_from_pem_file("examples/cert.key") - .unwrap(); + config.load_cert_chain_from_pem_file(CERT).unwrap(); + config.load_priv_key_from_pem_file(KEY).unwrap(); config .set_application_protos(&[b"proto1", b"proto2"]) .unwrap(); @@ -17041,12 +16891,8 @@ mod tests { ) { let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap(); assert_eq!(config.set_cc_algorithm_name(cc_algorithm_name), Ok(())); - config - .load_cert_chain_from_pem_file("examples/cert.crt") - .unwrap(); - config - .load_priv_key_from_pem_file("examples/cert.key") - .unwrap(); + config.load_cert_chain_from_pem_file(CERT).unwrap(); + config.load_priv_key_from_pem_file(KEY).unwrap(); config .set_application_protos(&[b"proto1", b"proto2"]) .unwrap(); @@ -17091,12 +16937,8 @@ mod tests { let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap(); assert_eq!(config.set_cc_algorithm_name(cc_algorithm_name), Ok(())); - config - .load_cert_chain_from_pem_file("examples/cert.crt") - .unwrap(); - config - .load_priv_key_from_pem_file("examples/cert.key") - .unwrap(); + config.load_cert_chain_from_pem_file(CERT).unwrap(); + config.load_priv_key_from_pem_file(KEY).unwrap(); config .set_application_protos(&[b"proto1", b"proto2"]) .unwrap(); @@ -17240,12 +17082,8 @@ mod tests { ) { let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap(); assert_eq!(config.set_cc_algorithm_name(cc_algorithm_name), Ok(())); - config - .load_cert_chain_from_pem_file("examples/cert.crt") - .unwrap(); - config - .load_priv_key_from_pem_file("examples/cert.key") - .unwrap(); + config.load_cert_chain_from_pem_file(CERT).unwrap(); + config.load_priv_key_from_pem_file(KEY).unwrap(); config .set_application_protos(&[b"proto1", b"proto2"]) .unwrap(); @@ -17328,12 +17166,8 @@ mod tests { ) { let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap(); assert_eq!(config.set_cc_algorithm_name(cc_algorithm_name), Ok(())); - config - .load_cert_chain_from_pem_file("examples/cert.crt") - .unwrap(); - config - .load_priv_key_from_pem_file("examples/cert.key") - .unwrap(); + config.load_cert_chain_from_pem_file(CERT).unwrap(); + config.load_priv_key_from_pem_file(KEY).unwrap(); config .set_application_protos(&[b"proto1", b"proto2"]) .unwrap(); @@ -17396,12 +17230,8 @@ mod tests { ) { let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap(); assert_eq!(config.set_cc_algorithm_name(cc_algorithm_name), Ok(())); - config - .load_cert_chain_from_pem_file("examples/cert.crt") - .unwrap(); - config - .load_priv_key_from_pem_file("examples/cert.key") - .unwrap(); + config.load_cert_chain_from_pem_file(CERT).unwrap(); + config.load_priv_key_from_pem_file(KEY).unwrap(); config .set_application_protos(&[b"proto1", b"proto2"]) .unwrap(); @@ -17451,12 +17281,8 @@ mod tests { ) { let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap(); assert_eq!(config.set_cc_algorithm_name(cc_algorithm_name), Ok(())); - config - .load_cert_chain_from_pem_file("examples/cert.crt") - .unwrap(); - config - .load_priv_key_from_pem_file("examples/cert.key") - .unwrap(); + config.load_cert_chain_from_pem_file(CERT).unwrap(); + config.load_priv_key_from_pem_file(KEY).unwrap(); config .set_application_protos(&[b"proto1", b"proto2"]) .unwrap(); @@ -17487,12 +17313,8 @@ mod tests { ) { let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap(); assert_eq!(config.set_cc_algorithm_name(cc_algorithm_name), Ok(())); - config - .load_cert_chain_from_pem_file("examples/cert.crt") - .unwrap(); - config - .load_priv_key_from_pem_file("examples/cert.key") - .unwrap(); + config.load_cert_chain_from_pem_file(CERT).unwrap(); + config.load_priv_key_from_pem_file(KEY).unwrap(); config .set_application_protos(&[b"proto1", b"proto2"]) .unwrap(); @@ -17543,12 +17365,8 @@ mod tests { ) { let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap(); assert_eq!(config.set_cc_algorithm_name(cc_algorithm_name), Ok(())); - config - .load_cert_chain_from_pem_file("examples/cert.crt") - .unwrap(); - config - .load_priv_key_from_pem_file("examples/cert.key") - .unwrap(); + config.load_cert_chain_from_pem_file(CERT).unwrap(); + config.load_priv_key_from_pem_file(KEY).unwrap(); config .set_application_protos(&[b"proto1", b"proto2"]) .unwrap(); @@ -17612,12 +17430,8 @@ mod tests { ) { let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap(); assert_eq!(config.set_cc_algorithm_name(cc_algorithm_name), Ok(())); - config - .load_cert_chain_from_pem_file("examples/cert.crt") - .unwrap(); - config - .load_priv_key_from_pem_file("examples/cert.key") - .unwrap(); + config.load_cert_chain_from_pem_file(CERT).unwrap(); + config.load_priv_key_from_pem_file(KEY).unwrap(); config .set_application_protos(&[b"proto1", b"proto2"]) .unwrap(); @@ -17638,12 +17452,8 @@ mod tests { ) { let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap(); assert_eq!(config.set_cc_algorithm_name(cc_algorithm_name), Ok(())); - config - .load_cert_chain_from_pem_file("examples/cert.crt") - .unwrap(); - config - .load_priv_key_from_pem_file("examples/cert.key") - .unwrap(); + config.load_cert_chain_from_pem_file(CERT).unwrap(); + config.load_priv_key_from_pem_file(KEY).unwrap(); config .set_application_protos(&[b"proto1", b"proto2"]) .unwrap(); @@ -17829,12 +17639,8 @@ mod tests { ) { let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap(); assert_eq!(config.set_cc_algorithm_name(cc_algorithm_name), Ok(())); - config - .load_cert_chain_from_pem_file("examples/cert.crt") - .unwrap(); - config - .load_priv_key_from_pem_file("examples/cert.key") - .unwrap(); + config.load_cert_chain_from_pem_file(CERT).unwrap(); + config.load_priv_key_from_pem_file(KEY).unwrap(); config .set_application_protos(&[b"proto1", b"proto2"]) .unwrap(); @@ -18045,12 +17851,8 @@ mod tests { ) { let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap(); assert_eq!(config.set_cc_algorithm_name(cc_algorithm_name), Ok(())); - config - .load_cert_chain_from_pem_file("examples/cert.crt") - .unwrap(); - config - .load_priv_key_from_pem_file("examples/cert.key") - .unwrap(); + config.load_cert_chain_from_pem_file(CERT).unwrap(); + config.load_priv_key_from_pem_file(KEY).unwrap(); config .set_application_protos(&[b"proto1", b"proto2"]) .unwrap(); @@ -18125,12 +17927,8 @@ mod tests { ) { let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap(); assert_eq!(config.set_cc_algorithm_name(cc_algorithm_name), Ok(())); - config - .load_cert_chain_from_pem_file("examples/cert.crt") - .unwrap(); - config - .load_priv_key_from_pem_file("examples/cert.key") - .unwrap(); + config.load_cert_chain_from_pem_file(CERT).unwrap(); + config.load_priv_key_from_pem_file(KEY).unwrap(); config .set_application_protos(&[b"proto1", b"proto2"]) .unwrap(); @@ -18195,12 +17993,8 @@ mod tests { ) { let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap(); assert_eq!(config.set_cc_algorithm_name(cc_algorithm_name), Ok(())); - config - .load_cert_chain_from_pem_file("examples/cert.crt") - .unwrap(); - config - .load_priv_key_from_pem_file("examples/cert.key") - .unwrap(); + config.load_cert_chain_from_pem_file(CERT).unwrap(); + config.load_priv_key_from_pem_file(KEY).unwrap(); config .set_application_protos(&[b"proto1", b"proto2"]) .unwrap(); @@ -18222,14 +18016,22 @@ mod tests { let mut recv_buf = [0; DATA_BYTES]; let send1_bytes = pipe.server.stream_send(1, &buf, true).unwrap(); assert_eq!(send1_bytes, match cc_algorithm_name { - #[cfg(feature = "openssl")] - "bbr2" => 14041, - #[cfg(not(feature = "openssl"))] - "bbr2" => 13955, - #[cfg(feature = "openssl")] - "bbr2_gcongestion" => 13966, - #[cfg(not(feature = "openssl"))] - "bbr2_gcongestion" => 13880, + "bbr2" => + if cfg!(feature = "openssl") { + 14041 + } else if cfg!(feature = "rustls") { + 13835 + } else { + 13955 + }, + "bbr2_gcongestion" => + if cfg!(feature = "openssl") { + 13966 + } else if cfg!(feature = "rustls") { + 13760 + } else { + 13880 + }, _ => 12000, }); assert_eq!( @@ -18445,12 +18247,8 @@ mod tests { let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap(); assert_eq!(config.set_cc_algorithm_name(cc_algorithm_name), Ok(())); - config - .load_cert_chain_from_pem_file("examples/cert.crt") - .unwrap(); - config - .load_priv_key_from_pem_file("examples/cert.key") - .unwrap(); + config.load_cert_chain_from_pem_file(CERT).unwrap(); + config.load_priv_key_from_pem_file(KEY).unwrap(); config .set_application_protos(&[b"proto1", b"proto2"]) .unwrap(); @@ -18631,12 +18429,8 @@ mod tests { ) { let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap(); assert_eq!(config.set_cc_algorithm_name(cc_algorithm_name), Ok(())); - config - .load_cert_chain_from_pem_file("examples/cert.crt") - .unwrap(); - config - .load_priv_key_from_pem_file("examples/cert.key") - .unwrap(); + config.load_cert_chain_from_pem_file(CERT).unwrap(); + config.load_priv_key_from_pem_file(KEY).unwrap(); config .set_application_protos(&[b"proto1", b"proto2"]) .unwrap(); @@ -18733,12 +18527,8 @@ mod tests { ) { let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap(); assert_eq!(config.set_cc_algorithm_name(cc_algorithm_name), Ok(())); - config - .load_cert_chain_from_pem_file("examples/cert.crt") - .unwrap(); - config - .load_priv_key_from_pem_file("examples/cert.key") - .unwrap(); + config.load_cert_chain_from_pem_file(CERT).unwrap(); + config.load_priv_key_from_pem_file(KEY).unwrap(); config .set_application_protos(&[b"proto1", b"proto2"]) .unwrap(); @@ -18782,12 +18572,8 @@ mod tests { ) { let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap(); assert_eq!(config.set_cc_algorithm_name(cc_algorithm_name), Ok(())); - config - .load_cert_chain_from_pem_file("examples/cert.crt") - .unwrap(); - config - .load_priv_key_from_pem_file("examples/cert.key") - .unwrap(); + config.load_cert_chain_from_pem_file(CERT).unwrap(); + config.load_priv_key_from_pem_file(KEY).unwrap(); config .set_application_protos(&[b"proto1", b"proto2"]) .unwrap(); From 8757613de21cb57d3043021ba364ec5196558b02 Mon Sep 17 00:00:00 2001 From: Harald Gutmann Date: Mon, 28 Apr 2025 16:47:24 +0200 Subject: [PATCH 11/14] crypto: add rustls support --- quiche/Cargo.toml | 3 +- quiche/src/crypto/rustls/mod.rs | 386 ++++++++++++++++++++++++++++++-- quiche/src/packet.rs | 62 +++-- 3 files changed, 408 insertions(+), 43 deletions(-) diff --git a/quiche/Cargo.toml b/quiche/Cargo.toml index 752a9f0806..ff4f83cd89 100644 --- a/quiche/Cargo.toml +++ b/quiche/Cargo.toml @@ -40,7 +40,7 @@ boringssl-boring-crate = ["boring", "foreign-types-shared"] openssl = ["pkg-config"] # Build quiche using rustls -rustls = ["dep:rustls"] +rustls = ["dep:rustls", "dep:ring"] # Generate pkg-config metadata file for libquiche. pkg-config-meta = [] @@ -87,6 +87,7 @@ slab = "0.4" smallvec = { workspace = true, features = ["union"] } enum_dispatch = "0.3" rustls = { version = "0.23.26", optional = true } +ring = { workspace = true, optional = true } [target."cfg(windows)".dependencies] windows-sys = { version = "0.59", features = [ diff --git a/quiche/src/crypto/rustls/mod.rs b/quiche/src/crypto/rustls/mod.rs index bdef457363..a748a870b2 100644 --- a/quiche/src/crypto/rustls/mod.rs +++ b/quiche/src/crypto/rustls/mod.rs @@ -24,85 +24,429 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use crate::crypto::make_nonce; use crate::crypto::Algorithm; use crate::Error; use crate::Result; +use ring::aead::Aad; +use ring::aead::LessSafeKey; +use ring::aead::Nonce; +use ring::aead::UnboundKey; +use ring::aead::AES_128_GCM; +use ring::aead::AES_256_GCM; +use ring::aead::CHACHA20_POLY1305; +use ring::aead::MAX_TAG_LEN; +use rustls::crypto::CryptoProvider; +use rustls::quic::DirectionalKeys; +use rustls::quic::HeaderProtectionKey; +use rustls::quic::Keys; +use rustls::quic::PacketKey as RustlsPacketKey; +use rustls::quic::Secrets; +use rustls::quic::Suite; +use rustls::quic::Version; +use rustls::CipherSuite; +use rustls::Side; +use std::sync::Arc; +use std::sync::Mutex; +use std::sync::MutexGuard; -pub struct PacketKey {} +// TODO: check if rustls re-exports the dependencies +//#[cfg(all(feature = "aws-lc-rs", not(feature = "ring")))] +// use aws_lc_rs::aead; + +pub struct PacketKey { + key: LessSafeKey, + nonce: Vec, +} impl PacketKey { pub fn new( alg: Algorithm, key: Vec, iv: Vec, _enc: u32, ) -> Result { - todo!() + let key = match alg { + Algorithm::AES128_GCM => LessSafeKey::new( + UnboundKey::new(&AES_128_GCM, &key) + .map_err(|_| Error::CryptoFail)?, + ), + Algorithm::AES256_GCM => LessSafeKey::new( + UnboundKey::new(&AES_256_GCM, &key) + .map_err(|_| Error::CryptoFail)?, + ), + Algorithm::ChaCha20_Poly1305 => LessSafeKey::new( + UnboundKey::new(&CHACHA20_POLY1305, &key) + .map_err(|_| Error::CryptoFail)?, + ), + }; + + Ok(Self { key, nonce: iv }) } pub fn seal_with_u64_counter( &self, counter: u64, ad: &[u8], buf: &mut [u8], in_len: usize, extra_in: Option<&[u8]>, ) -> Result { - todo!() + if let Some(_extra_in) = extra_in { + error!("extra_in is not supported when using rustls"); + return Err(Error::CryptoFail); + }; + + let nonce = + Nonce::assume_unique_for_key(make_nonce(&self.nonce, counter)); + + let tag = self + .key + .seal_in_place_separate_tag(nonce, Aad::from(ad), &mut buf[..in_len]) + .map_err(|e| { + error!("failed to seal with packet key: {}", e); + Error::CryptoFail + })?; + + buf.copy_from_slice(tag.as_ref()); + Ok(MAX_TAG_LEN) } } -pub struct Open {} +pub struct Open { + packet_key: Box, + header_protection_key: Arc, + algorithm: Algorithm, + secrets: Option>, +} impl Open { - pub fn new_mask(&self, sample: &[u8]) -> Result<[u8; 5]> { - todo!() + pub(crate) fn from(keys: DirectionalKeys) -> Self { + Self { + packet_key: keys.packet, + header_protection_key: Arc::from(keys.header), + algorithm: Algorithm::AES128_GCM, + secrets: None, + } + } + + pub fn decrypt_hdr( + &self, sample: &[u8], first: &mut u8, packet_number: &mut [u8], + ) -> Result<()> { + self.header_protection_key + .decrypt_in_place(sample, first, packet_number) + .map_err(|e| { + debug!("failed to decrypt packet header: {:?}", e); + Error::CryptoFail + }) } pub fn open_with_u64_counter( - &self, counter: u64, ad: &[u8], buf: &mut [u8], + &self, packet_number: u64, header: &[u8], payload: &mut [u8], ) -> Result { - todo!() + let decrypted = self + .packet_key + .decrypt_in_place(packet_number, header, payload) + .map_err(|e| { + debug!("failed to decrypt packet: {:?}", e); + Error::CryptoFail + })?; + + Ok(decrypted.len()) } pub fn alg(&self) -> Algorithm { - // self.alg - todo!() + self.algorithm } pub fn derive_next_packet_key(&self) -> Result { - todo!() + let Some(secrets) = &self.secrets else { + error!("no secrets present for next packet key"); + return Err(Error::CryptoFail); + }; + + let Some(remote_key) = secrets.next_remote_key()? else { + error!("no remote key available for next packet key, previous local key was not consumed"); + return Err(Error::CryptoFail); + }; + + Ok(Open { + packet_key: remote_key, + header_protection_key: self.header_protection_key.clone(), + algorithm: Algorithm::AES128_GCM, + secrets: Some(secrets.clone()), + }) + } + + pub fn return_next_key(self) -> Result<()> { + let Some(secrets) = &self.secrets else { + error!("no secrets present to return packet key"); + return Err(Error::CryptoFail); + }; + + secrets.return_next_remote_key(self.packet_key) } } -pub struct Seal {} +pub struct Seal { + packet_key: Box, + header_protection_key: Arc, + algorithm: Algorithm, + secrets: Option>, +} impl Seal { pub const ENCRYPT: u32 = 1; - pub fn new_mask(&self, sample: &[u8]) -> Result<[u8; 5]> { - todo!() + pub(crate) fn from(keys: DirectionalKeys) -> Self { + Self { + packet_key: keys.packet, + header_protection_key: Arc::from(keys.header), + algorithm: Algorithm::AES128_GCM, + secrets: None, + } + } + + pub fn encrypt_hdr( + &self, sample: &[u8], first: &mut u8, packet_number: &mut [u8], + ) -> Result<()> { + self.header_protection_key + .encrypt_in_place(sample, first, packet_number) + .map_err(|e| { + error!("failed to encrypt packet header: {:?}", e); + Error::CryptoFail + }) } pub fn seal_with_u64_counter( &self, counter: u64, ad: &[u8], buf: &mut [u8], in_len: usize, extra_in: Option<&[u8]>, ) -> Result { - todo!() + if let Some(_extra_in) = extra_in { + error!("extra_in is not supported when using rustls"); + return Err(Error::CryptoFail); + } + + if (in_len + self.packet_key.tag_len()) > buf.len() { + error!("provided buffer size not sufficient for data and tag"); + return Err(Error::CryptoFail); + } + + let tag = self + .packet_key + .encrypt_in_place(counter, ad, &mut buf[..in_len]) + .map_err(|e| { + error!("failed to encrypt packet: {:?}", e); + Error::CryptoFail + })?; + + let tag_len = tag.as_ref().len(); + let tag = tag.as_ref(); + for ti in 0..tag_len { + buf[in_len + ti] = tag[ti]; + } + + Ok(in_len + tag_len) } pub fn alg(&self) -> Algorithm { - // self.alg - todo!() + self.algorithm } pub fn derive_next_packet_key(&self) -> Result { - todo!() + let Some(secrets) = &self.secrets else { + error!("no secrets present for next packet key"); + return Err(Error::CryptoFail); + }; + + let Some(local_key) = secrets.next_local_key()? else { + error!("no local key available for next packet key, previous remote key was not consumed"); + return Err(Error::CryptoFail); + }; + + Ok(Seal { + packet_key: local_key, + header_protection_key: self.header_protection_key.clone(), + algorithm: Algorithm::AES128_GCM, + secrets: Some(secrets.clone()), + }) } + + pub fn return_next_key(self) -> Result<()> { + let Some(secrets) = &self.secrets else { + error!("no secrets present to return packet key"); + return Err(Error::CryptoFail); + }; + + secrets.return_next_local_key(self.packet_key) + } +} + +pub struct SecretsNextKeys { + inner: Mutex, +} + +impl SecretsNextKeys { + fn from(next_secrets: Secrets) -> Self { + Self { + inner: Mutex::new(SecretsNextKeysInner { + secrets: next_secrets, + local_key: None, + remote_key: None, + }), + } + } + + fn lock(&self) -> Result> { + self.inner.lock().map_err(|e| { + error!("failed to acquire mutex: {:?}", e); + Error::CryptoFail + }) + } + + fn next_local_key(&self) -> Result>> { + let mut me = self.lock()?; + me.maybe_update_keys(); + Ok(me.local_key.take()) + } + + fn next_remote_key(&self) -> Result>> { + let mut me = self.lock()?; + me.maybe_update_keys(); + Ok(me.remote_key.take()) + } + + fn return_next_local_key( + &self, local_key: Box, + ) -> Result<()> { + let mut me = self.lock()?; + me.local_key = Some(local_key); + debug!("returned local key"); + Ok(()) + } + + fn return_next_remote_key( + &self, remote_key: Box, + ) -> Result<()> { + let mut me = self.lock()?; + me.remote_key = Some(remote_key); + debug!("returned remote key"); + Ok(()) + } +} + +pub struct SecretsNextKeysInner { + secrets: Secrets, + local_key: Option>, + remote_key: Option>, +} + +impl SecretsNextKeysInner { + fn maybe_update_keys(&mut self) { + if self.local_key.is_none() && self.remote_key.is_none() { + let keys = self.secrets.next_packet_keys(); + self.local_key = Some(keys.local); + self.remote_key = Some(keys.remote); + }; + } +} + +pub(crate) fn key_material_from_keys( + keys: Keys, next: Option, +) -> Result<(Open, Seal)> { + let next_secrets = if let Some(next) = next { + debug!("creating key material from keys with secrets"); + Some(Arc::new(SecretsNextKeys::from(next))) + } else { + None + }; + + let open = Open { + packet_key: keys.remote.packet, + header_protection_key: Arc::from(keys.remote.header), + algorithm: Algorithm::AES128_GCM, + secrets: next_secrets.clone(), + }; + let seal = Seal { + packet_key: keys.local.packet, + header_protection_key: Arc::from(keys.local.header), + algorithm: Algorithm::AES128_GCM, + secrets: next_secrets, + }; + + Ok((open, seal)) } pub fn derive_initial_key_material( - cid: &[u8], version: u32, is_server: bool, did_reset: bool, + cid: &[u8], version: u32, is_server: bool, _did_reset: bool, ) -> Result<(Open, Seal)> { - let open = Open {}; - let seal = Seal {}; + let quic_suite = quic_suite_from_algorithm(Algorithm::AES128_GCM)?; + + let side = if is_server { + Side::Server + } else { + Side::Client + }; + + let version = match version { + 1 => Version::V1, + _ => Version::V1, + }; + + let keys = + Keys::initial(version, quic_suite.suite, quic_suite.quic, cid, side); + + let open = Open { + packet_key: keys.remote.packet, + header_protection_key: Arc::from(keys.remote.header), + algorithm: Algorithm::AES128_GCM, + secrets: None, + }; + let seal = Seal { + packet_key: keys.local.packet, + header_protection_key: Arc::from(keys.local.header), + algorithm: Algorithm::AES128_GCM, + secrets: None, + }; Ok((open, seal)) } +fn quic_suite_from_algorithm(algo: Algorithm) -> Result { + let provider = init_crypto_provider(); + + let cipher_suite = match algo { + Algorithm::AES128_GCM => CipherSuite::TLS13_AES_128_GCM_SHA256, + Algorithm::AES256_GCM => CipherSuite::TLS13_AES_256_GCM_SHA384, + Algorithm::ChaCha20_Poly1305 => + CipherSuite::TLS13_CHACHA20_POLY1305_SHA256, + }; + + let suite = provider + .cipher_suites + .iter() + .find(|s| s.suite() == cipher_suite) + .ok_or_else(|| { + error!("default crypto suite not available"); + Error::CryptoFail + })?; + + let tls_13_suite = suite.tls13().ok_or_else(|| { + error!("crypto suite not a TLS 1.3 suite"); + Error::CryptoFail + })?; + + let quic_suite = tls_13_suite.quic_suite().ok_or_else(|| { + error!("crypto suite not a TLS 1.3 suite"); + Error::CryptoFail + })?; + + Ok(quic_suite) +} + +pub fn init_crypto_provider() -> &'static Arc { + let mut provider = CryptoProvider::get_default(); + if provider.is_none() { + let _ = rustls::crypto::aws_lc_rs::default_provider().install_default(); + provider = CryptoProvider::get_default(); + }; + + provider.expect("failed to init crypto provider") +} + pub fn verify_slices_are_equal(a: &[u8], b: &[u8]) -> Result<()> { if a.len() != b.len() { return Err(Error::CryptoFail); diff --git a/quiche/src/packet.rs b/quiche/src/packet.rs index d4399f9f37..97fb4f0541 100644 --- a/quiche/src/packet.rs +++ b/quiche/src/packet.rs @@ -579,21 +579,32 @@ pub fn decrypt_hdr( let ciphertext = ciphertext.as_mut(); - let mask = aead.new_mask(sample.as_ref())?; + #[cfg(not(feature = "rustls"))] + let pn_len = { + let mask = aead.new_mask(sample.as_ref())?; - if Header::is_long(first) { - first ^= mask[0] & 0x0f; - } else { - first ^= mask[0] & 0x1f; - } + if Header::is_long(first) { + first ^= mask[0] & 0x0f; + } else { + first ^= mask[0] & 0x1f; + } - let pn_len = usize::from((first & PKT_NUM_MASK) + 1); + let pn_len = usize::from((first & PKT_NUM_MASK) + 1); - let ciphertext = &mut ciphertext[..pn_len]; + let ciphertext = &mut ciphertext[..pn_len]; - for i in 0..pn_len { - ciphertext[i] ^= mask[i + 1]; - } + for i in 0..pn_len { + ciphertext[i] ^= mask[i + 1]; + } + + pn_len + }; + + #[cfg(feature = "rustls")] + let pn_len = { + aead.decrypt_hdr(sample.as_ref(), &mut first, ciphertext)?; + usize::from((first & PKT_NUM_MASK) + 1) + }; // Extract packet number corresponding to the decoded length. let pn = match pn_len { @@ -668,23 +679,30 @@ pub fn encrypt_hdr( let sample = &payload [MAX_PKT_NUM_LEN - pn_len..SAMPLE_LEN + (MAX_PKT_NUM_LEN - pn_len)]; - let mask = aead.new_mask(sample)?; - let (mut first, mut rest) = b.split_at(1)?; let first = first.as_mut(); - if Header::is_long(first[0]) { - first[0] ^= mask[0] & 0x0f; - } else { - first[0] ^= mask[0] & 0x1f; - } - let pn_buf = rest.slice_last(pn_len)?; - for i in 0..pn_len { - pn_buf[i] ^= mask[i + 1]; + + #[cfg(not(feature = "rustls"))] + { + let mask = aead.new_mask(sample)?; + + if Header::is_long(first[0]) { + first[0] ^= mask[0] & 0x0f; + } else { + first[0] ^= mask[0] & 0x1f; + } + + for i in 0..pn_len { + pn_buf[i] ^= mask[i + 1]; + } } + #[cfg(feature = "rustls")] + aead.encrypt_hdr(sample, &mut first[0], pn_buf)?; + Ok(()) } @@ -1527,6 +1545,7 @@ mod tests { } #[test] + #[cfg(not(feature = "rustls"))] fn decrypt_chacha20() { let secret = [ 0x9a, 0xc3, 0x12, 0xa7, 0xf8, 0x77, 0x46, 0x8e, 0xbe, 0x69, 0x42, @@ -1889,6 +1908,7 @@ mod tests { } #[test] + #[cfg(not(feature = "rustls"))] fn encrypt_chacha20() { let secret = [ 0x9a, 0xc3, 0x12, 0xa7, 0xf8, 0x77, 0x46, 0x8e, 0xbe, 0x69, 0x42, From 97d4243c9b87d2e45ed10ffb548811557ae2d755 Mon Sep 17 00:00:00 2001 From: Harald Gutmann Date: Tue, 6 May 2025 14:31:50 +0200 Subject: [PATCH 12/14] tls: add rustls support --- quiche/Cargo.toml | 3 +- quiche/src/lib.rs | 26 +- quiche/src/tls/mod.rs | 4 + quiche/src/tls/rustls/mod.rs | 787 +++++++++++++++++++++++++++-- quiche/src/tls/rustls/verifiers.rs | 157 ++++++ 5 files changed, 928 insertions(+), 49 deletions(-) create mode 100644 quiche/src/tls/rustls/verifiers.rs diff --git a/quiche/Cargo.toml b/quiche/Cargo.toml index ff4f83cd89..ba35275140 100644 --- a/quiche/Cargo.toml +++ b/quiche/Cargo.toml @@ -40,7 +40,7 @@ boringssl-boring-crate = ["boring", "foreign-types-shared"] openssl = ["pkg-config"] # Build quiche using rustls -rustls = ["dep:rustls", "dep:ring"] +rustls = ["dep:rustls", "dep:rustls-native-certs", "dep:ring"] # Generate pkg-config metadata file for libquiche. pkg-config-meta = [] @@ -87,6 +87,7 @@ slab = "0.4" smallvec = { workspace = true, features = ["union"] } enum_dispatch = "0.3" rustls = { version = "0.23.26", optional = true } +rustls-native-certs = { version = "0.8.1", optional = true } ring = { workspace = true, optional = true } [target."cfg(windows)".dependencies] diff --git a/quiche/src/lib.rs b/quiche/src/lib.rs index bbf0f74666..51b37934a3 100644 --- a/quiche/src/lib.rs +++ b/quiche/src/lib.rs @@ -1023,6 +1023,7 @@ impl Config { /// specific key (e.g. in order to support resumption across multiple /// servers), in which case the application is also responsible for /// rotating the key to provide forward secrecy. + #[cfg(not(feature = "rustls"))] pub fn set_ticket_key(&mut self, key: &[u8]) -> Result<()> { self.tls_ctx.set_ticket_key(key) } @@ -1462,6 +1463,7 @@ where path_challenge_rx_count: u64, /// List of supported application protocols. + #[cfg(not(feature = "rustls"))] application_protos: Vec>, /// Total number of received packets. @@ -2019,6 +2021,7 @@ impl Connection { .path_challenge_recv_max_queue_len, path_challenge_rx_count: 0, + #[cfg(not(feature = "rustls"))] application_protos: config.application_protos.clone(), recv_count: 0, @@ -3059,7 +3062,7 @@ impl Connection { } } - let mut payload = packet::decrypt_pkt( + let payload_res = packet::decrypt_pkt( &mut b, pn, pn_len, @@ -3068,7 +3071,22 @@ impl Connection { ) .map_err(|e| { drop_pkt_on_err(e, self.recv_count, self.is_server, &self.trace_id) - })?; + }); + + let mut payload = match payload_res { + Ok(payload) => payload, + Err(e) => { + #[cfg(feature = "rustls")] + // rustls updates the secrets when deriving the next packet keys + // therefore needed to return the keys in case they are not + // verified successfully + if let Some((open, seal)) = aead_next { + let _ = open.return_next_key(); + let _ = seal.return_next_key(); + } + return Err(e); + }, + }; if self.pkt_num_spaces[epoch].recv_pkt_num.contains(pn) { trace!("{} ignored duplicate packet {}", self.trace_id, pn); @@ -7147,6 +7165,7 @@ impl Connection { /// If the connection is already established, it does nothing. fn do_handshake(&mut self, now: time::Instant) -> Result<()> { let mut ex_data = tls::ExData { + #[cfg(not(feature = "rustls"))] application_protos: &self.application_protos, crypto_ctx: &mut self.crypto_ctx, @@ -7155,14 +7174,17 @@ impl Connection { local_error: &mut self.local_error, + #[cfg(not(feature = "rustls"))] keylog: self.keylog.as_mut(), + #[cfg(not(feature = "rustls"))] trace_id: &self.trace_id, recovery_config: self.recovery_config, tx_cap_factor: self.tx_cap_factor, + #[cfg(not(feature = "rustls"))] is_server: self.is_server, }; diff --git a/quiche/src/tls/mod.rs b/quiche/src/tls/mod.rs index cf853d8b06..7d624bdcaf 100644 --- a/quiche/src/tls/mod.rs +++ b/quiche/src/tls/mod.rs @@ -38,6 +38,7 @@ use crate::packet; use crate::ConnectionError; pub struct ExData<'a> { + #[cfg(not(feature = "rustls"))] pub application_protos: &'a Vec>, pub crypto_ctx: &'a mut [packet::CryptoContext; packet::Epoch::count()], @@ -46,13 +47,16 @@ pub struct ExData<'a> { pub local_error: &'a mut Option, + #[cfg(not(feature = "rustls"))] pub keylog: Option<&'a mut Box>, + #[cfg(not(feature = "rustls"))] pub trace_id: &'a str, pub recovery_config: crate::recovery::RecoveryConfig, pub tx_cap_factor: f64, + #[cfg(not(feature = "rustls"))] pub is_server: bool, } diff --git a/quiche/src/tls/rustls/mod.rs b/quiche/src/tls/rustls/mod.rs index d159298e8c..74d68207af 100644 --- a/quiche/src/tls/rustls/mod.rs +++ b/quiche/src/tls/rustls/mod.rs @@ -24,146 +24,841 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::crypto; +mod verifiers; + +use crate::crypto::init_crypto_provider; +use crate::crypto::key_material_from_keys; +use crate::crypto::Algorithm; +use crate::crypto::Level; +use crate::crypto::Open; +use crate::crypto::Seal; +use crate::packet; +use crate::tls::rustls::verifiers::DisabledServerCertVerifier; +use crate::tls::rustls::verifiers::RejectedClientCertAllowedAnonymousVerifier; use crate::tls::ExData; +use crate::ConnectionError; +use crate::Error; use crate::Result; - -pub struct Context {} +use rustls::client::ClientSessionMemoryCache; +use rustls::client::Resumption; +use rustls::client::WebPkiServerVerifier; +use rustls::pki_types::pem::PemObject; +use rustls::pki_types::CertificateDer; +use rustls::pki_types::PrivateKeyDer; +use rustls::pki_types::ServerName; +use rustls::quic::ClientConnection; +use rustls::quic::Connection; +use rustls::quic::KeyChange; +use rustls::quic::Keys; +use rustls::quic::Secrets; +use rustls::quic::ServerConnection; +use rustls::quic::Version; +use rustls::server::WebPkiClientVerifier; +use rustls::version::TLS13; +use rustls::CipherSuite; +use rustls::ClientConfig; +use rustls::HandshakeKind; +use rustls::KeyLogFile; +use rustls::RootCertStore; +use rustls::ServerConfig; +use rustls::Side; +use std::fs::DirEntry; +use std::sync::Arc; + +const INTERNAL_ERROR: u64 = 0x01; + +pub struct Context { + client_config: Option>, + server_config: Option>, + // required to build the above configs + // are consumed during configs building + private_key_client: Option>, + private_key_server: Option>, + ca_certificates: Option>>, + verify_ca_certificates_store: Option, + system_default_cert_store: Option, + alpns: Vec>, + enable_verify_ca_certificates: bool, + enable_keylog: bool, + enable_early_data: bool, + quic_version: Version, + client_resumption_store: Arc, +} impl Context { pub fn new() -> Result { - todo!() + let _ = init_crypto_provider(); + + Ok(Self { + client_config: None, + server_config: None, + private_key_client: None, + private_key_server: None, + ca_certificates: None, + enable_verify_ca_certificates: false, + verify_ca_certificates_store: None, + system_default_cert_store: None, + enable_keylog: false, + enable_early_data: false, + quic_version: Default::default(), + alpns: vec![], + client_resumption_store: Arc::new(ClientSessionMemoryCache::new(256)), + }) } pub fn new_handshake(&mut self) -> Result { - todo!() + // user supplied verification store when enabled and available + // used for server mTLS validation on server or + // used for server certificate validation on client + let verify_store = if self.enable_verify_ca_certificates { + self.verify_ca_certificates_store.clone().take() + } else { + None + }; + + if self.server_config.is_none() && + self.private_key_server.is_some() && + self.ca_certificates.is_some() + { + let builder = ServerConfig::builder_with_protocol_versions(&[&TLS13]); + // setup user supplied and enabled CA certificates for mTLS auth + let builder = if let Some(verify_store) = verify_store.clone() { + let client_verifier = + WebPkiClientVerifier::builder(verify_store.into()) + .allow_unauthenticated() + .build() + .map_err(|e| { + error!("client_verifier: failed to build {}", e); + Error::TlsFail + })?; + + builder.with_client_cert_verifier(client_verifier) + } else { + if self.enable_verify_ca_certificates { + // in case no store is present fail on mTLS authentication + // user warning is issued during Handshake.do_handshake() + builder.with_client_cert_verifier(Arc::new( + RejectedClientCertAllowedAnonymousVerifier::new()?, + )) + } else { + builder.with_no_client_auth() + } + }; + + let (Some(certs), Some(key)) = + (self.ca_certificates.clone(), self.private_key_server.take()) + else { + error!( + "server without certificate and key config is not supported" + ); + return Err(Error::TlsFail); + }; + + let mut config = + builder.with_single_cert(certs, key).map_err(|e| { + error!("loading certificate or key failed: {}", e); + Error::TlsFail + })?; + + if self.enable_keylog { + config.key_log = Arc::new(KeyLogFile::new()); + } + + if self.enable_early_data { + // rustls currently only allows 0 or 2^32-1 for max early data + // size + config.max_early_data_size = u32::MAX; + } + + if self.alpns.len() > 0 { + config.alpn_protocols = self.alpns.clone(); + } + + self.server_config = Some(Arc::new(config)); + }; + + if self.client_config.is_none() { + let builder = ClientConfig::builder_with_protocol_versions(&[&TLS13]); + + // setup user supplied and enabled CA certificates for server + // certificate validation + let builder = if let Some(verify_store) = verify_store.clone() { + let server_verifier = + WebPkiServerVerifier::builder(verify_store.into()) + .build() + .map_err(|e| { + error!("failed to build server verifier: {}", e); + Error::TlsFail + })?; + + builder.with_webpki_verifier(server_verifier) + } else { + // in case enabled but no CA certificates provided use system + // default CAs + if self.enable_verify_ca_certificates { + // default to env variables or system store + let store = self.load_system_default_certificate_store(); + builder.with_root_certificates(store) + } else { + // completely deactivate validation + let builder = builder.dangerous(); + let disabled_server_verification = + Arc::new(DisabledServerCertVerifier::new()?); + builder.with_custom_certificate_verifier( + disabled_server_verification, + ) + } + }; + + let mut config = if let (Some(certs), Some(key)) = + (self.ca_certificates.take(), self.private_key_client.take()) + { + builder.with_client_auth_cert(certs, key).map_err(|e| { + error!("failed to set client auth: {}", e); + Error::TlsFail + })? + } else { + builder.with_no_client_auth() + }; + + if self.enable_keylog { + config.key_log = Arc::new(KeyLogFile::new()); + } + + if self.enable_early_data { + config.enable_early_data = true; + } + + if self.alpns.len() > 0 { + config.alpn_protocols = self.alpns.clone(); + } + + // required for 0-rtt, the resumption store is persisted within the + // Context and propagated to all connections on the + // ClientConfig + config.resumption = + Resumption::store(self.client_resumption_store.clone()); + + self.client_config = Some(Arc::new(config)) + } + + Ok(Handshake { + client_config: self.client_config.clone().ok_or_else(|| { + error!("no client config available"); + Error::TlsFail + })?, + server_config: self.server_config.clone(), + quic_version: self.quic_version.clone(), + connection: None, + side: Side::Client, // dummy value, correctly set during init() + enable_verify_ca_certificates: self.enable_verify_ca_certificates, + quic_transport_params: None, + provided_data: None, + highest_level: Level::Initial, + hostname: None, + one_rtt_keys_secrets: None, + }) + } + + fn load_system_default_certificate_store(&mut self) -> RootCertStore { + // loading the files is an expensive operation, ensure it's only done once + // system default cert store is used in some areas + if let Some(store) = &self.system_default_cert_store { + return store.clone(); + }; + + let mut system_default_certificate_store = RootCertStore::empty(); + let certificates_result = rustls_native_certs::load_native_certs(); + system_default_certificate_store + .add_parsable_certificates(certificates_result.certs); + + self.system_default_cert_store = Some(system_default_certificate_store); + self.system_default_cert_store.clone().unwrap() } pub fn load_verify_locations_from_file(&mut self, file: &str) -> Result<()> { - todo!() + let verify_certificates = Self::load_ca_certificates_from_file(file)?; + self.extend_verify_ca_certificates(verify_certificates); + Ok(()) } pub fn load_verify_locations_from_directory( &mut self, path: &str, ) -> Result<()> { - todo!() + let files: Result> = std::fs::read_dir(path) + .map_err(|e| { + error!("failed to load verify locations from directory: {:?}", e); + Error::TlsFail + })? + .into_iter() + .map(|rd| { + rd.map_err(|e| { + error!( + "failed to load verify locations from directory: {:?}", + e + ); + Error::TlsFail + }) + }) + .collect(); + + let verify_certificates: Vec = files? + .into_iter() + .flat_map(|f| Self::load_ca_certificates_from_file(f.path())) + .flatten() + .collect(); + + self.extend_verify_ca_certificates(verify_certificates); + Ok(()) } pub fn use_certificate_chain_file(&mut self, file: &str) -> Result<()> { - todo!() + self.ca_certificates = Some(Self::load_ca_certificates_from_file(file)?); + Ok(()) + } + + fn load_ca_certificates_from_file( + file: impl AsRef, + ) -> Result>> { + let certificates: Result> = + CertificateDer::pem_file_iter(file) + .map_err(|e| { + error!("failed to load ca certificates from pem file: {}", e); + Error::TlsFail + })? + .map(|r| { + r.map_err(|e| { + error!("failed to load pem certificate: {}", e); + Error::TlsFail + }) + }) + .collect(); + Ok(certificates?) + } + + fn extend_verify_ca_certificates( + &mut self, verify_certificates: Vec>, + ) { + if let Some(cert_store) = &mut self.verify_ca_certificates_store { + cert_store.add_parsable_certificates(verify_certificates); + } else { + let mut store = RootCertStore::empty(); + store.add_parsable_certificates(verify_certificates); + self.verify_ca_certificates_store = Some(store); + } } pub fn use_privkey_file(&mut self, file: &str) -> Result<()> { - todo!() + let private_key_client = + PrivateKeyDer::from_pem_file(file).map_err(|e| { + error!("failed to load private key from pem: {}", e); + Error::TlsFail + })?; + let private_key_server = + PrivateKeyDer::from_pem_file(file).map_err(|e| { + error!("failed to load private key from pem: {}", e); + Error::TlsFail + })?; + + // NOTE: storing it twice as PrivateKeyDer cannot be copied/cloned + // ClientConfig & ServerConfig are built in new_handshake() + self.private_key_client = Some(private_key_client); + self.private_key_server = Some(private_key_server); + Ok(()) } pub fn set_verify(&mut self, verify: bool) { - todo!() + self.enable_verify_ca_certificates = verify; } + /// uses env variable SSLKEYLOGFILE pub fn enable_keylog(&mut self) { - todo!() + self.enable_keylog = true; } pub fn set_alpn(&mut self, v: &[&[u8]]) -> Result<()> { - todo!() + let alpns: Vec> = v.iter().map(|a| a.to_vec()).collect(); + self.alpns = alpns; + Ok(()) } - pub fn set_ticket_key(&mut self, key: &[u8]) -> Result<()> { - todo!() - } + // not supported in rustls + // pub fn set_ticket_key(&mut self, _key: &[u8]) -> Result<()> {} pub fn set_early_data_enabled(&mut self, enabled: bool) { - todo!() + self.enable_early_data = enabled; } } -pub struct Handshake {} +pub struct Handshake { + client_config: Arc, + server_config: Option>, + quic_version: Version, + + side: Side, + enable_verify_ca_certificates: bool, + quic_transport_params: Option>, + hostname: Option>, + connection: Option, + + highest_level: Level, + provided_data: Option>, + one_rtt_keys_secrets: Option<(Keys, Secrets)>, +} impl Handshake { pub fn init(&mut self, is_server: bool) -> Result<()> { - todo!() + self.side = match is_server { + true => Side::Server, + false => Side::Client, + }; + + Ok(()) } - pub fn use_legacy_codepoint(&mut self, use_legacy: bool) { - todo!() + pub fn use_legacy_codepoint(&mut self, _use_legacy: bool) { + () // noop for rustls } pub fn set_host_name(&mut self, name: &str) -> Result<()> { - todo!() + let hostname = ServerName::try_from(name) + .map_err(|e| { + error!("failed to convert hostname: {}", e); + Error::TlsFail + })? + .to_owned(); + + self.hostname = Some(hostname); + Ok(()) } pub fn set_quic_transport_params(&mut self, buf: &[u8]) -> Result<()> { - todo!() + self.quic_transport_params = Some(buf.to_vec()); + Ok(()) } pub fn quic_transport_params(&self) -> &[u8] { - todo!() + // peer/remote transport parameters + if let Some(conn) = &self.connection { + // when resuming a tls session returning the transport parameters + // leads to errors as during decoding they are checked + // against the new connection ids which are not yet + // fully established and the previous connection ids would be present + if self.is_in_early_data() { + return &[]; + }; + + return if let Some(params) = conn.quic_transport_parameters() { + params + } else { + &[] + }; + } + + debug_assert!(false, "connection not available {:?}", self.side); + &[] } pub fn alpn_protocol(&self) -> &[u8] { - todo!() + if let Some(conn) = &self.connection { + if let Some(alpns) = conn.alpn_protocol() { + return alpns; + } + } + + &[] } pub fn server_name(&self) -> Option<&str> { - todo!() + self.connection.as_ref().and_then(|c| match c { + Connection::Client(_) => None, + Connection::Server(sc) => sc.server_name(), + }) } - pub fn provide_data( - &mut self, level: crypto::Level, buf: &[u8], - ) -> Result<()> { - todo!() + // peer/receive Crypto frame data + pub fn provide_data(&mut self, _level: Level, buf: &[u8]) -> Result<()> { + debug!( + "provide_data side={:?} level={:?}", + self.side, self.highest_level + ); + + let Some(conn) = &mut self.connection else { + trace!("storing data as no connection present side={:?}", self.side); + self.provided_data = Some(buf.to_vec()); + return Ok(()); + }; + + conn.read_hs(&mut buf.to_vec()).map_err(|e| { + if let Some(alert) = conn.alert() { + error!("alert description: {:?}", alert) + } + error!("failed to read handshake data: {:?} {:?}", self.side, e); + Error::TlsFail + })?; + + Ok(()) } + // local/send Crypto frame data pub fn do_handshake(&mut self, ex_data: &mut ExData) -> Result<()> { - todo!() + if self.connection.is_none() { + debug!("no connection present side={:?}", self.side); + + let Some(params) = self.quic_transport_params.clone() else { + error!("missing transport parameters {:?}", self.side); + return Err(Error::TlsFail); + }; + + match self.side { + Side::Client => { + let Some(hostname) = self.hostname.clone() else { + error!("hostname not present"); + return Err(Error::TlsFail); + }; + + // NOTE: generates ClientHello + let client_conn = ClientConnection::new( + self.client_config.clone(), + self.quic_version.clone(), + hostname.to_owned(), + params, + ) + .map_err(|e| { + error!("failed to create client config {}", e); + Error::TlsFail + })?; + + self.connection = Some(client_conn.into()) + }, + Side::Server => { + let Some(server_config) = self.server_config.clone() else { + error!("server config not present for server side"); + return Err(Error::TlsFail); + }; + + if self.enable_verify_ca_certificates { + warn!("verify_peer: enabled but no CA store for mTLS verification configured."); + } + + let mut server_conn = ServerConnection::new( + server_config, + self.quic_version.clone(), + params.clone(), + ) + .map_err(|e| { + error!("failed to create server connection {}", e); + Error::TlsFail + })?; + + if let Some(crypto_data) = self.provided_data.take() { + match server_conn.read_hs(&crypto_data) { + Ok(()) => { /* continue */ }, + Err(e) => { + if ex_data.local_error.is_none() { + *ex_data.local_error = Some(ConnectionError { + is_app: false, + error_code: INTERNAL_ERROR, + reason: e.to_string().as_bytes().to_vec(), + }) + }; + error!("failed to read handshake data: {:?}", e); + return Err(Error::TlsFail); + }, + } + } + + self.connection = Some(server_conn.into()); + }, + } + }; + + loop { + let current_level = self.highest_level.clone(); + + let mut buf = Vec::new(); + let mut key_change = + self.connection.as_mut().unwrap().write_hs(&mut buf); + + let mut level_upgraded = false; + if let Some(key_change) = key_change.take() { + level_upgraded = self.process_key_change(ex_data, key_change)?; + } + + if !level_upgraded && self.one_rtt_keys_secrets.is_some() { + let keys_secrets = self.one_rtt_keys_secrets.take().unwrap(); + self.handle_application_keys(ex_data, keys_secrets)?; + } + + if buf.is_empty() { + break; + } else { + self.write_crypto_stream(current_level, ex_data, buf.as_slice())?; + } + } + + let Some(conn) = self.connection.as_mut() else { + return Err(Error::TlsFail); + }; + + if let Some(zero_rtt_keys) = conn.zero_rtt_keys() { + let space = &mut ex_data.crypto_ctx[packet::Epoch::Application]; + match self.side { + Side::Client => { + if space.crypto_seal.is_some() { + error!("client zero_rtt_keys are already present"); + }; + + space.crypto_seal = Some(Seal::from(zero_rtt_keys)); + }, + Side::Server => { + if space.crypto_0rtt_open.is_some() { + error!("server zero_rtt_keys are already present"); + }; + + space.crypto_0rtt_open = Some(Open::from(zero_rtt_keys)); + }, + } + self.highest_level = Level::ZeroRTT; + } + + trace!( + "handshake status side={:?}, kind={:?}, ongoing={:?}, alpn={:?}", + self.side, + conn.handshake_kind(), + conn.is_handshaking(), + match conn.alpn_protocol() { + None => "", + Some(alpn) => { + str::from_utf8(alpn).unwrap() + }, + } + ); + + // setting a session value to allow for tls resumption / zero-rtt + if matches!(self.side, Side::Client) && + !conn.is_handshaking() && + ex_data.session.is_none() + { + let mut session = Vec::new(); + + let local_params = + self.quic_transport_params.as_ref().unwrap().as_slice(); + let local_params_len: [u8; 8] = + (local_params.len() as u64).to_be_bytes(); + session.extend_from_slice(&local_params_len); + session.extend_from_slice(local_params); + + let peer_params = self.quic_transport_params(); + let peer_params_len: [u8; 8] = + (peer_params.len() as u64).to_be_bytes(); + session.extend_from_slice(&peer_params_len); + session.extend_from_slice(peer_params); + + *ex_data.session = Some(session); + } + + Ok(()) + } + + fn process_key_change( + &mut self, ex_data: &mut ExData, key_change: KeyChange, + ) -> Result { + match key_change { + KeyChange::Handshake { keys } => match self.highest_level { + Level::Initial => { + let next_space = + &mut ex_data.crypto_ctx[packet::Epoch::Handshake]; + + if next_space.crypto_seal.is_some() || + next_space.crypto_open.is_some() + { + debug_assert!( + false, + "keys are already present for Handshake" + ); + }; + + self.highest_level = Level::Handshake; + let (open, seal) = key_material_from_keys(keys, None)?; + next_space.crypto_open = Some(open); + next_space.crypto_seal = Some(seal); + + self.highest_level = Level::Handshake; + Ok(true) + }, + Level::ZeroRTT | Level::Handshake | Level::OneRTT => { + debug_assert!(false, "required to handle handshake keys"); + Ok(false) + }, + }, + + KeyChange::OneRtt { keys, next } => + self.handle_application_keys(ex_data, (keys, next)), + } + } + + fn handle_application_keys( + &mut self, ex_data: &mut ExData, keys_secrets: (Keys, Secrets), + ) -> Result { + self.highest_level = Level::OneRTT; + + if !self.is_completed() { + // avoid accepts of 1 RTT data before handshake is finished + // temporarily storing keys/secrets in handshake + // populate in space once handshake is compeleted + self.one_rtt_keys_secrets = Some((keys_secrets.0, keys_secrets.1)); + + Ok(false) + } else { + let next_space = &mut ex_data.crypto_ctx[packet::Epoch::Application]; + + let (open, seal) = + key_material_from_keys(keys_secrets.0, Some(keys_secrets.1))?; + next_space.crypto_open = Some(open); + next_space.crypto_seal = Some(seal); + + Ok(true) + } + } + + fn write_crypto_stream( + &self, level: Level, ex_data: &mut ExData, data: &[u8], + ) -> Result<()> { + let pkt_num_space = match level { + Level::Initial => &mut ex_data.crypto_ctx[packet::Epoch::Initial], + Level::ZeroRTT => unreachable!(), + Level::Handshake => &mut ex_data.crypto_ctx[packet::Epoch::Handshake], + Level::OneRTT => &mut ex_data.crypto_ctx[packet::Epoch::Application], + }; + + pkt_num_space.crypto_stream.send.write(data, false)?; + + debug!( + "handshake crypto data written side={:?}, level={:?}, sent={}", + self.side, + self.highest_level, + data.len() + ); + Ok(()) + } + + pub fn process_post_handshake( + &mut self, _ex_data: &mut ExData, + ) -> Result<()> { + // no-op + Ok(()) } - pub fn process_post_handshake(&mut self, ex_data: &mut ExData) -> Result<()> { - todo!() + pub fn write_level(&self) -> Level { + self.highest_level } - pub fn write_level(&self) -> crypto::Level { - todo!() - } + pub fn cipher(&self) -> Option { + let suite = self + .connection + .as_ref() + .and_then(|c| c.negotiated_cipher_suite()); + let Some(suite) = suite else { return None }; - pub fn cipher(&self) -> Option { - todo!() + match suite.suite() { + CipherSuite::TLS13_AES_128_GCM_SHA256 => Some(Algorithm::AES128_GCM), + CipherSuite::TLS13_AES_256_GCM_SHA384 => Some(Algorithm::AES256_GCM), + CipherSuite::TLS13_CHACHA20_POLY1305_SHA256 => + Some(Algorithm::ChaCha20_Poly1305), + _ => None, + } } pub fn is_completed(&self) -> bool { - todo!() + if let Some(conn) = &self.connection { + return !conn.is_handshaking(); + } + + false } pub fn is_resumed(&self) -> bool { - todo!() + if let Some(conn) = &self.connection { + if let Some(kind) = conn.handshake_kind() { + return matches!(kind, HandshakeKind::Resumed); + } + } + + false } pub fn clear(&mut self) -> Result<()> { - todo!() + self.connection = None; + Ok(()) } pub fn set_session(&mut self, session: &[u8]) -> Result<()> { - todo!() + match self.side { + // peer transport parameters are part of the resumption_store + Side::Client => { + self.set_quic_transport_params(session)?; + Ok(()) + }, + Side::Server => { + error!("set session is a client only operation"); + Err(Error::TlsFail) + }, + } } pub fn curve(&self) -> Option { - todo!() + let Some(conn) = &self.connection else { + return None; + }; + + let Some(kx_group) = conn.negotiated_key_exchange_group() else { + return None; + }; + + Some(format!("{:?}", kx_group.name())) } pub fn sigalg(&self) -> Option { - todo!() + // this information is not available through the connection + // only handled internally within rustls during handshake + // loggable with level trace + None } pub fn peer_cert_chain(&self) -> Option> { - todo!() + let Some(conn) = &self.connection else { + return None; + }; + + if let Some(certs) = conn.peer_certificates() { + let out: Vec<&[u8]> = certs.iter().map(|c| c.as_ref()).collect(); + if out.len() > 0 { + Some(out) + } else { + None + } + } else { + None + } } pub fn peer_cert(&self) -> Option<&[u8]> { - todo!() + let Some(conn) = &self.connection else { + return None; + }; + + if let Some(certs) = conn.peer_certificates() { + certs.first().map(|c| c.as_ref()) + } else { + None + } } pub fn is_in_early_data(&self) -> bool { - todo!() + let Some(conn) = &self.connection else { + return false; + }; + conn.zero_rtt_keys().is_some() } } diff --git a/quiche/src/tls/rustls/verifiers.rs b/quiche/src/tls/rustls/verifiers.rs new file mode 100644 index 0000000000..67b73bdfaf --- /dev/null +++ b/quiche/src/tls/rustls/verifiers.rs @@ -0,0 +1,157 @@ +// Copyright (C) 2025, Cloudflare, Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::crypto::crypto_provider; +use rustls::client::danger::HandshakeSignatureValid; +use rustls::client::danger::ServerCertVerified; +use rustls::client::danger::ServerCertVerifier; +use rustls::crypto::WebPkiSupportedAlgorithms; +use rustls::pki_types::CertificateDer; +use rustls::pki_types::ServerName; +use rustls::pki_types::UnixTime; +use rustls::server::danger::ClientCertVerified; +use rustls::server::danger::ClientCertVerifier; +use rustls::DigitallySignedStruct; +use rustls::DistinguishedName; +use rustls::Error; +use rustls::InvalidMessage; +use rustls::SignatureScheme; +use std::fmt::Debug; + +#[derive(Debug)] +pub(super) struct DisabledServerCertVerifier { + supported_algorithms: WebPkiSupportedAlgorithms, +} + +impl DisabledServerCertVerifier { + pub(super) fn new() -> crate::Result { + let provider = crypto_provider(); + Ok(Self { + supported_algorithms: provider.signature_verification_algorithms, + }) + } +} + +impl ServerCertVerifier for DisabledServerCertVerifier { + fn verify_server_cert( + &self, _end_entity: &CertificateDer<'_>, + _intermediates: &[CertificateDer<'_>], _server_name: &ServerName<'_>, + _ocsp_response: &[u8], _now: UnixTime, + ) -> Result { + warn!( + "Server certificate validation is disabled! \ + Use quiche::Config.verify_peer(true) to enable certificate validation." + ); + Ok(ServerCertVerified::assertion()) + } + + fn verify_tls12_signature( + &self, _message: &[u8], _cert: &CertificateDer<'_>, + _dss: &DigitallySignedStruct, + ) -> Result { + error!("TLS 1.2 is not supported within the Quic protocol."); + Err(Error::InvalidMessage( + InvalidMessage::UnknownProtocolVersion, + )) + } + + fn verify_tls13_signature( + &self, _message: &[u8], _cert: &CertificateDer<'_>, + _dss: &DigitallySignedStruct, + ) -> Result { + warn!( + "Server certificate validation is disabled! \ + Use quiche::Config.verify_peer(true) to enable certificate validation." + ); + Ok(HandshakeSignatureValid::assertion()) + } + + fn supported_verify_schemes(&self) -> Vec { + self.supported_algorithms + .mapping + .iter() + .map(|item| item.0) + .collect() + } +} + +#[derive(Debug)] +pub(super) struct RejectedClientCertAllowedAnonymousVerifier { + supported_algorithms: WebPkiSupportedAlgorithms, + client_auth_mandatory: bool, +} + +impl RejectedClientCertAllowedAnonymousVerifier { + pub(super) fn new() -> crate::Result { + let provider = crypto_provider(); + Ok(Self { + supported_algorithms: provider.signature_verification_algorithms, + client_auth_mandatory: false, + }) + } +} + +impl ClientCertVerifier for RejectedClientCertAllowedAnonymousVerifier { + fn client_auth_mandatory(&self) -> bool { + self.client_auth_mandatory + } + + fn root_hint_subjects(&self) -> &[DistinguishedName] { + &[] + } + + fn verify_client_cert( + &self, _end_entity: &CertificateDer<'_>, + _intermediates: &[CertificateDer<'_>], _now: UnixTime, + ) -> Result { + Err(Error::General("no CA certificates configured to verify presented client auth certificate".to_string())) + } + + fn verify_tls12_signature( + &self, _message: &[u8], _cert: &CertificateDer<'_>, + _dss: &DigitallySignedStruct, + ) -> Result { + error!("TLS 1.2 is not supported within the Quic protocol."); + Err(Error::InvalidMessage( + InvalidMessage::UnknownProtocolVersion, + )) + } + + fn verify_tls13_signature( + &self, _message: &[u8], _cert: &CertificateDer<'_>, + _dss: &DigitallySignedStruct, + ) -> Result { + Err(Error::General("no CA certificates configured to verify presented client auth certificate".to_string())) + } + + fn supported_verify_schemes(&self) -> Vec { + self.supported_algorithms + .mapping + .iter() + .map(|item| item.0) + .collect() + } +} From ddce61b44bc709d23beee85a2ce3313add598a6a Mon Sep 17 00:00:00 2001 From: Harald Gutmann Date: Mon, 5 May 2025 17:39:22 +0200 Subject: [PATCH 13/14] crypto & tls: add support for rustls ring & aws-lc-rs --- apps/Cargo.toml | 3 +- quiche/Cargo.toml | 7 +++- quiche/src/crypto/mod.rs | 8 ++-- quiche/src/crypto/rustls/mod.rs | 67 ++++++++++++++++++++++----------- quiche/src/h3/mod.rs | 4 +- quiche/src/lib.rs | 62 ++++++++++++++++-------------- quiche/src/packet.rs | 12 +++--- quiche/src/rand.rs | 12 +++--- quiche/src/tls/mod.rs | 16 ++++---- quiche/src/tls/rustls/mod.rs | 20 ++++++++-- 10 files changed, 126 insertions(+), 85 deletions(-) diff --git a/apps/Cargo.toml b/apps/Cargo.toml index 3b6890cb23..a4200c46fb 100644 --- a/apps/Cargo.toml +++ b/apps/Cargo.toml @@ -19,7 +19,8 @@ qlog = ["quiche/qlog"] # Use BoringSSL provided by the boring crate. boringssl-boring-crate = ["quiche/boringssl-boring-crate"] -rustls = ["quiche/rustls"] +rustls-aws-lc-rs = ["quiche/rustls-aws-lc-rs"] +rustls-ring = ["quiche/rustls-ring"] # Enable sfv support. sfv = ["quiche/sfv"] diff --git a/quiche/Cargo.toml b/quiche/Cargo.toml index ba35275140..8c947d0e95 100644 --- a/quiche/Cargo.toml +++ b/quiche/Cargo.toml @@ -40,7 +40,9 @@ boringssl-boring-crate = ["boring", "foreign-types-shared"] openssl = ["pkg-config"] # Build quiche using rustls -rustls = ["dep:rustls", "dep:rustls-native-certs", "dep:ring"] +rustls-aws-lc-rs = ["rustls/aws-lc-rs", "dep:aws-lc-rs", "dep:rustls", "dep:rustls-native-certs", "__rustls"] +rustls-ring = ["rustls/ring", "dep:rustls", "dep:ring", "dep:rustls-native-certs", "__rustls"] +__rustls = [] # Generate pkg-config metadata file for libquiche. pkg-config-meta = [] @@ -86,9 +88,10 @@ sfv = { version = "0.9", optional = true } slab = "0.4" smallvec = { workspace = true, features = ["union"] } enum_dispatch = "0.3" -rustls = { version = "0.23.26", optional = true } +rustls = { version = "0.23.26", default-features = false, features = ["std", "logging"], optional = true } rustls-native-certs = { version = "0.8.1", optional = true } ring = { workspace = true, optional = true } +aws-lc-rs = { version = "1.13.0", default-features = false, optional = true } [target."cfg(windows)".dependencies] windows-sys = { version = "0.59", features = [ diff --git a/quiche/src/crypto/mod.rs b/quiche/src/crypto/mod.rs index b19c49205d..acd9ca911c 100644 --- a/quiche/src/crypto/mod.rs +++ b/quiche/src/crypto/mod.rs @@ -24,14 +24,14 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -#[cfg(not(feature = "rustls"))] +#[cfg(not(feature = "__rustls"))] mod boringssl_openssl; -#[cfg(not(feature = "rustls"))] +#[cfg(not(feature = "__rustls"))] pub use boringssl_openssl::*; -#[cfg(feature = "rustls")] +#[cfg(feature = "__rustls")] mod rustls; -#[cfg(feature = "rustls")] +#[cfg(feature = "__rustls")] pub use rustls::*; use crate::packet; diff --git a/quiche/src/crypto/rustls/mod.rs b/quiche/src/crypto/rustls/mod.rs index a748a870b2..fef581bf53 100644 --- a/quiche/src/crypto/rustls/mod.rs +++ b/quiche/src/crypto/rustls/mod.rs @@ -24,18 +24,38 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#[cfg(feature = "rustls-aws-lc-rs")] +mod aws_lc_rs { + pub(super) use aws_lc_rs::aead::Aad; + pub(super) use aws_lc_rs::aead::LessSafeKey; + pub(super) use aws_lc_rs::aead::Nonce; + pub(super) use aws_lc_rs::aead::UnboundKey; + pub(super) use aws_lc_rs::aead::AES_128_GCM; + pub(super) use aws_lc_rs::aead::AES_256_GCM; + pub(super) use aws_lc_rs::aead::CHACHA20_POLY1305; + pub(super) use aws_lc_rs::aead::MAX_TAG_LEN; +} +#[cfg(feature = "rustls-ring")] +mod ring { + pub(super) use ring::aead::Aad; + pub(super) use ring::aead::LessSafeKey; + pub(super) use ring::aead::Nonce; + pub(super) use ring::aead::UnboundKey; + pub(super) use ring::aead::AES_128_GCM; + pub(super) use ring::aead::AES_256_GCM; + pub(super) use ring::aead::CHACHA20_POLY1305; + pub(super) use ring::aead::MAX_TAG_LEN; +} + +#[cfg(feature = "rustls-aws-lc-rs")] +use crate::crypto::rustls::aws_lc_rs::*; +#[cfg(feature = "rustls-ring")] +use crate::crypto::rustls::ring::*; + use crate::crypto::make_nonce; use crate::crypto::Algorithm; use crate::Error; use crate::Result; -use ring::aead::Aad; -use ring::aead::LessSafeKey; -use ring::aead::Nonce; -use ring::aead::UnboundKey; -use ring::aead::AES_128_GCM; -use ring::aead::AES_256_GCM; -use ring::aead::CHACHA20_POLY1305; -use ring::aead::MAX_TAG_LEN; use rustls::crypto::CryptoProvider; use rustls::quic::DirectionalKeys; use rustls::quic::HeaderProtectionKey; @@ -50,10 +70,6 @@ use std::sync::Arc; use std::sync::Mutex; use std::sync::MutexGuard; -// TODO: check if rustls re-exports the dependencies -//#[cfg(all(feature = "aws-lc-rs", not(feature = "ring")))] -// use aws_lc_rs::aead; - pub struct PacketKey { key: LessSafeKey, nonce: Vec, @@ -406,7 +422,7 @@ pub fn derive_initial_key_material( } fn quic_suite_from_algorithm(algo: Algorithm) -> Result { - let provider = init_crypto_provider(); + let provider = crypto_provider(); let cipher_suite = match algo { Algorithm::AES128_GCM => CipherSuite::TLS13_AES_128_GCM_SHA256, @@ -437,16 +453,6 @@ fn quic_suite_from_algorithm(algo: Algorithm) -> Result { Ok(quic_suite) } -pub fn init_crypto_provider() -> &'static Arc { - let mut provider = CryptoProvider::get_default(); - if provider.is_none() { - let _ = rustls::crypto::aws_lc_rs::default_provider().install_default(); - provider = CryptoProvider::get_default(); - }; - - provider.expect("failed to init crypto provider") -} - pub fn verify_slices_are_equal(a: &[u8], b: &[u8]) -> Result<()> { if a.len() != b.len() { return Err(Error::CryptoFail); @@ -457,3 +463,18 @@ pub fn verify_slices_are_equal(a: &[u8], b: &[u8]) -> Result<()> { false => Err(Error::CryptoFail), } } + +// early setup is required as the crypto provider is used before ServerConfig / +// ClientConfig builders which typically set up the crypto providers +pub fn crypto_provider() -> &'static Arc { + if let Some(provider) = CryptoProvider::get_default() { + provider + } else { + #[cfg(feature = "rustls-aws-lc-rs")] + let _ = rustls::crypto::aws_lc_rs::default_provider().install_default(); + #[cfg(feature = "rustls-ring")] + let _ = rustls::crypto::ring::default_provider().install_default(); + + CryptoProvider::get_default().unwrap() + } +} diff --git a/quiche/src/h3/mod.rs b/quiche/src/h3/mod.rs index 1b828a1c7c..a3ce948ff5 100644 --- a/quiche/src/h3/mod.rs +++ b/quiche/src/h3/mod.rs @@ -3227,10 +3227,10 @@ pub mod testing { pub(super) static KEY: &str = "examples/cert.key"; - #[cfg(not(feature = "rustls"))] + #[cfg(not(feature = "__rustls"))] pub(super) static CERT: &str = "examples/cert.crt"; - #[cfg(feature = "rustls")] + #[cfg(feature = "__rustls")] pub(super) static CERT: &str = "examples/cert_rustls.crt"; /// Session is an HTTP/3 test helper structure. It holds a client, server diff --git a/quiche/src/lib.rs b/quiche/src/lib.rs index 51b37934a3..2c7312d674 100644 --- a/quiche/src/lib.rs +++ b/quiche/src/lib.rs @@ -1023,7 +1023,7 @@ impl Config { /// specific key (e.g. in order to support resumption across multiple /// servers), in which case the application is also responsible for /// rotating the key to provide forward secrecy. - #[cfg(not(feature = "rustls"))] + #[cfg(not(feature = "__rustls"))] pub fn set_ticket_key(&mut self, key: &[u8]) -> Result<()> { self.tls_ctx.set_ticket_key(key) } @@ -1463,7 +1463,7 @@ where path_challenge_rx_count: u64, /// List of supported application protocols. - #[cfg(not(feature = "rustls"))] + #[cfg(not(feature = "__rustls"))] application_protos: Vec>, /// Total number of received packets. @@ -2021,7 +2021,7 @@ impl Connection { .path_challenge_recv_max_queue_len, path_challenge_rx_count: 0, - #[cfg(not(feature = "rustls"))] + #[cfg(not(feature = "__rustls"))] application_protos: config.application_protos.clone(), recv_count: 0, @@ -3076,7 +3076,7 @@ impl Connection { let mut payload = match payload_res { Ok(payload) => payload, Err(e) => { - #[cfg(feature = "rustls")] + #[cfg(feature = "__rustls")] // rustls updates the secrets when deriving the next packet keys // therefore needed to return the keys in case they are not // verified successfully @@ -7165,7 +7165,7 @@ impl Connection { /// If the connection is already established, it does nothing. fn do_handshake(&mut self, now: time::Instant) -> Result<()> { let mut ex_data = tls::ExData { - #[cfg(not(feature = "rustls"))] + #[cfg(not(feature = "__rustls"))] application_protos: &self.application_protos, crypto_ctx: &mut self.crypto_ctx, @@ -7174,17 +7174,17 @@ impl Connection { local_error: &mut self.local_error, - #[cfg(not(feature = "rustls"))] + #[cfg(not(feature = "__rustls"))] keylog: self.keylog.as_mut(), - #[cfg(not(feature = "rustls"))] + #[cfg(not(feature = "__rustls"))] trace_id: &self.trace_id, recovery_config: self.recovery_config, tx_cap_factor: self.tx_cap_factor, - #[cfg(not(feature = "rustls"))] + #[cfg(not(feature = "__rustls"))] is_server: self.is_server, }; @@ -9057,9 +9057,9 @@ pub mod testing { pub(super) static KEY: &str = "examples/cert.key"; - #[cfg(not(feature = "rustls"))] + #[cfg(not(feature = "__rustls"))] pub(super) static CERT: &str = "examples/cert.crt"; - #[cfg(feature = "rustls")] + #[cfg(feature = "__rustls")] pub(super) static CERT: &str = "examples/cert_rustls.crt"; pub struct Pipe { @@ -9580,14 +9580,14 @@ mod tests { use super::testing::*; use super::*; - #[cfg(not(feature = "rustls"))] + #[cfg(not(feature = "__rustls"))] pub(super) static CERT_BIG: &str = "examples/cert-big.crt"; - #[cfg(not(feature = "rustls"))] + #[cfg(not(feature = "__rustls"))] pub(super) static ROOTCA: &str = "examples/rootca.crt"; - #[cfg(feature = "rustls")] + #[cfg(feature = "__rustls")] pub(super) static CERT_BIG: &str = "examples/cert-big_rustls.crt"; - #[cfg(feature = "rustls")] + #[cfg(feature = "__rustls")] pub(super) static ROOTCA: &str = "examples/rootca_rustls.crt"; #[test] @@ -9884,10 +9884,10 @@ mod tests { .unwrap(); assert_eq!(pipe.handshake(), Err(Error::TlsFail)); - #[cfg(not(feature = "rustls"))] + #[cfg(not(feature = "__rustls"))] // Client did send a certificate. assert!(pipe.server.peer_cert().is_some()); - #[cfg(feature = "rustls")] + #[cfg(feature = "__rustls")] // rustls does not provide the peer certificate when verification failed assert!(pipe.server.peer_cert().is_none()); } @@ -10023,7 +10023,7 @@ mod tests { // Disable session tickets on the server (SSL_OP_NO_TICKET) to avoid // triggering 1-RTT packet send with a CRYPTO frame. - #[cfg(not(feature = "rustls"))] + #[cfg(not(feature = "__rustls"))] pipe.server.handshake.set_options(0x0000_4000); assert_eq!(pipe.handshake(), Ok(())); @@ -10093,7 +10093,7 @@ mod tests { } #[rstest] - #[cfg(not(feature = "rustls"))] + #[cfg(not(feature = "__rustls"))] fn handshake_resumption( #[values("cubic", "bbr2", "bbr2_gcongestion")] cc_algorithm_name: &str, ) { @@ -13536,9 +13536,9 @@ mod tests { match pipe.client.peer_cert() { Some(c) => { - #[cfg(not(feature = "rustls"))] + #[cfg(not(feature = "__rustls"))] assert_eq!(c.len(), 753); - #[cfg(feature = "rustls")] + #[cfg(feature = "__rustls")] assert_eq!(c.len(), 847); }, @@ -14269,7 +14269,9 @@ mod tests { } else { if cfg!(feature = "openssl") { Ok(12345) - } else if cfg!(feature = "rustls") { + } else if cfg!(feature = "rustls-ring") { + Ok(12320) + } else if cfg!(feature = "rustls-aws-lc-rs") { Ok(12324) } else { Ok(12299) @@ -14346,7 +14348,9 @@ mod tests { } else { if cfg!(feature = "openssl") { Ok(12345) - } else if cfg!(feature = "rustls") { + } else if cfg!(feature = "rustls-ring") { + Ok(12320) + } else if cfg!(feature = "rustls-aws-lc-rs") { Ok(12324) } else { Ok(12299) @@ -15841,7 +15845,7 @@ mod tests { // OpenSSL does not provide a straightforward interface to deal with custom // off-load key signing. #[cfg(not(feature = "openssl"))] - #[cfg(not(feature = "rustls"))] + #[cfg(not(feature = "__rustls"))] #[rstest] fn app_close_by_server_during_handshake_private_key_failure( #[values("cubic", "bbr2", "bbr2_gcongestion")] cc_algorithm_name: &str, @@ -16210,7 +16214,7 @@ mod tests { } else { if cfg!(feature = "openssl") { 13437 - } else if cfg!(feature = "rustls") { + } else if cfg!(feature = "__rustls") { 13535 } else { 13421 @@ -16274,7 +16278,7 @@ mod tests { } else { if cfg!(feature = "openssl") { 13959 - } else if cfg!(feature = "rustls") { + } else if cfg!(feature = "__rustls") { 13753 } else { 13873 @@ -16291,7 +16295,7 @@ mod tests { } else { if cfg!(feature = "openssl") { Ok(3959) - } else if cfg!(feature = "rustls") { + } else if cfg!(feature = "__rustls") { Ok(3753) } else { Ok(3873) @@ -16481,7 +16485,7 @@ mod tests { let expected = CUSTOM_INITIAL_CONGESTION_WINDOW_PACKETS * 1200 + if cfg!(feature = "openssl") { 1463 - } else if cfg!(feature = "rustls") { + } else if cfg!(feature = "__rustls") { 1561 } else { 1447 @@ -18041,7 +18045,7 @@ mod tests { "bbr2" => if cfg!(feature = "openssl") { 14041 - } else if cfg!(feature = "rustls") { + } else if cfg!(feature = "__rustls") { 13835 } else { 13955 @@ -18049,7 +18053,7 @@ mod tests { "bbr2_gcongestion" => if cfg!(feature = "openssl") { 13966 - } else if cfg!(feature = "rustls") { + } else if cfg!(feature = "__rustls") { 13760 } else { 13880 diff --git a/quiche/src/packet.rs b/quiche/src/packet.rs index 97fb4f0541..28b0fae901 100644 --- a/quiche/src/packet.rs +++ b/quiche/src/packet.rs @@ -579,7 +579,7 @@ pub fn decrypt_hdr( let ciphertext = ciphertext.as_mut(); - #[cfg(not(feature = "rustls"))] + #[cfg(not(feature = "__rustls"))] let pn_len = { let mask = aead.new_mask(sample.as_ref())?; @@ -600,7 +600,7 @@ pub fn decrypt_hdr( pn_len }; - #[cfg(feature = "rustls")] + #[cfg(feature = "__rustls")] let pn_len = { aead.decrypt_hdr(sample.as_ref(), &mut first, ciphertext)?; usize::from((first & PKT_NUM_MASK) + 1) @@ -685,7 +685,7 @@ pub fn encrypt_hdr( let pn_buf = rest.slice_last(pn_len)?; - #[cfg(not(feature = "rustls"))] + #[cfg(not(feature = "__rustls"))] { let mask = aead.new_mask(sample)?; @@ -700,7 +700,7 @@ pub fn encrypt_hdr( } } - #[cfg(feature = "rustls")] + #[cfg(feature = "__rustls")] aead.encrypt_hdr(sample, &mut first[0], pn_buf)?; Ok(()) @@ -1545,7 +1545,7 @@ mod tests { } #[test] - #[cfg(not(feature = "rustls"))] + #[cfg(not(feature = "__rustls"))] fn decrypt_chacha20() { let secret = [ 0x9a, 0xc3, 0x12, 0xa7, 0xf8, 0x77, 0x46, 0x8e, 0xbe, 0x69, 0x42, @@ -1908,7 +1908,7 @@ mod tests { } #[test] - #[cfg(not(feature = "rustls"))] + #[cfg(not(feature = "__rustls"))] fn encrypt_chacha20() { let secret = [ 0x9a, 0xc3, 0x12, 0xa7, 0xf8, 0x77, 0x46, 0x8e, 0xbe, 0x69, 0x42, diff --git a/quiche/src/rand.rs b/quiche/src/rand.rs index bb07c9b68e..0b2d3575da 100644 --- a/quiche/src/rand.rs +++ b/quiche/src/rand.rs @@ -24,19 +24,19 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -#[cfg(feature = "rustls")] -use crate::crypto::init_crypto_provider; +#[cfg(feature = "__rustls")] +use crate::crypto::crypto_provider; -#[cfg(not(feature = "rustls"))] +#[cfg(not(feature = "__rustls"))] pub fn rand_bytes(buf: &mut [u8]) { unsafe { RAND_bytes(buf.as_mut_ptr(), buf.len()); } } -#[cfg(feature = "rustls")] +#[cfg(feature = "__rustls")] pub fn rand_bytes(buf: &mut [u8]) { - let provider = init_crypto_provider(); + let provider = crypto_provider(); provider.secure_random.fill(buf).unwrap() } @@ -69,7 +69,7 @@ pub fn rand_u64_uniform(max: u64) -> u64 { r / chunk_size } -#[cfg(not(feature = "rustls"))] +#[cfg(not(feature = "__rustls"))] extern "C" { fn RAND_bytes(buf: *mut u8, len: libc::size_t) -> libc::c_int; } diff --git a/quiche/src/tls/mod.rs b/quiche/src/tls/mod.rs index 7d624bdcaf..abe58d9d1b 100644 --- a/quiche/src/tls/mod.rs +++ b/quiche/src/tls/mod.rs @@ -24,21 +24,21 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -#[cfg(not(feature = "rustls"))] +#[cfg(not(feature = "__rustls"))] mod boringssl_openssl; -#[cfg(not(feature = "rustls"))] +#[cfg(not(feature = "__rustls"))] pub use boringssl_openssl::*; -#[cfg(feature = "rustls")] +#[cfg(feature = "__rustls")] mod rustls; -#[cfg(feature = "rustls")] +#[cfg(feature = "__rustls")] pub use rustls::*; use crate::packet; use crate::ConnectionError; pub struct ExData<'a> { - #[cfg(not(feature = "rustls"))] + #[cfg(not(feature = "__rustls"))] pub application_protos: &'a Vec>, pub crypto_ctx: &'a mut [packet::CryptoContext; packet::Epoch::count()], @@ -47,16 +47,16 @@ pub struct ExData<'a> { pub local_error: &'a mut Option, - #[cfg(not(feature = "rustls"))] + #[cfg(not(feature = "__rustls"))] pub keylog: Option<&'a mut Box>, - #[cfg(not(feature = "rustls"))] + #[cfg(not(feature = "__rustls"))] pub trace_id: &'a str, pub recovery_config: crate::recovery::RecoveryConfig, pub tx_cap_factor: f64, - #[cfg(not(feature = "rustls"))] + #[cfg(not(feature = "__rustls"))] pub is_server: bool, } diff --git a/quiche/src/tls/rustls/mod.rs b/quiche/src/tls/rustls/mod.rs index 74d68207af..9a5da0a34c 100644 --- a/quiche/src/tls/rustls/mod.rs +++ b/quiche/src/tls/rustls/mod.rs @@ -26,7 +26,7 @@ mod verifiers; -use crate::crypto::init_crypto_provider; +use crate::crypto::crypto_provider; use crate::crypto::key_material_from_keys; use crate::crypto::Algorithm; use crate::crypto::Level; @@ -87,7 +87,7 @@ pub struct Context { impl Context { pub fn new() -> Result { - let _ = init_crypto_provider(); + let _ = crypto_provider(); Ok(Self { client_config: None, @@ -120,7 +120,13 @@ impl Context { self.private_key_server.is_some() && self.ca_certificates.is_some() { - let builder = ServerConfig::builder_with_protocol_versions(&[&TLS13]); + let builder = + ServerConfig::builder_with_provider(crypto_provider().clone()) + .with_protocol_versions(&[&TLS13]) + .map_err(|e| { + error!("failed to set protocol version for server config builder: {}", e); + Error::TlsFail + })?; // setup user supplied and enabled CA certificates for mTLS auth let builder = if let Some(verify_store) = verify_store.clone() { let client_verifier = @@ -178,7 +184,13 @@ impl Context { }; if self.client_config.is_none() { - let builder = ClientConfig::builder_with_protocol_versions(&[&TLS13]); + let builder = + ClientConfig::builder_with_provider(crypto_provider().clone()) + .with_protocol_versions(&[&TLS13]) + .map_err(|e| { + error!("failed to set protocol version for client config builder: {}", e); + Error::TlsFail + })?; // setup user supplied and enabled CA certificates for server // certificate validation From c6928a9b2ca704a64dec410386eb87f00257978f Mon Sep 17 00:00:00 2001 From: Harald Gutmann Date: Tue, 6 May 2025 14:27:44 +0200 Subject: [PATCH 14/14] tokio-quiche: add rustls support --- tokio-quiche/Cargo.toml | 9 +++++++- tokio-quiche/src/quic/connection/mod.rs | 2 ++ tokio-quiche/src/quic/hooks.rs | 3 +++ tokio-quiche/src/quic/io/worker.rs | 3 +++ tokio-quiche/src/quic/router/mod.rs | 7 ++++++ tokio-quiche/src/settings/config.rs | 5 ++++ tokio-quiche/tests/fixtures/mod.rs | 8 +++++++ tokio-quiche/tests/integration_tests/mod.rs | 10 ++++++++ .../tests/integration_tests/timeouts.rs | 23 ++++++++++++------- 9 files changed, 61 insertions(+), 9 deletions(-) diff --git a/tokio-quiche/Cargo.toml b/tokio-quiche/Cargo.toml index 9b6d530cba..d9cef92cf7 100644 --- a/tokio-quiche/Cargo.toml +++ b/tokio-quiche/Cargo.toml @@ -15,6 +15,12 @@ readme = "README.md" fuzzing = ["quiche/fuzzing"] quiche_internal = ["quiche/internal"] +default = ["quiche/boringssl-boring-crate"] + +rustls-aws-lc-rs = ["quiche/rustls-aws-lc-rs", "__rustls"] +rustls-ring = ["quiche/rustls-ring", "__rustls"] +__rustls = [] + # Enable extra timing instrumentation for QUIC handshakes, including protocol # overhead and network delays. perf-quic-listener-metrics = [] @@ -52,7 +58,7 @@ ipnetwork = { workspace = true } log = { workspace = true } octets = { workspace = true } pin-project = { workspace = true } -quiche = { workspace = true, features = ["boringssl-boring-crate", "qlog"] } +quiche = { workspace = true, features = ["qlog"] } serde = { workspace = true, features = ["derive", "rc"] } serde_with = { workspace = true } slog-scope = { workspace = true } @@ -71,6 +77,7 @@ tokio-util = { workspace = true, features = [ ] } triomphe = { workspace = true } url = { workspace = true } +env_logger = "0.10.2" [dev-dependencies] h3i = { workspace = true } diff --git a/tokio-quiche/src/quic/connection/mod.rs b/tokio-quiche/src/quic/connection/mod.rs index c9fa83d977..85b0220d0d 100644 --- a/tokio-quiche/src/quic/connection/mod.rs +++ b/tokio-quiche/src/quic/connection/mod.rs @@ -33,6 +33,7 @@ pub use self::id::ConnectionIdGenerator; pub use self::id::SimpleConnectionIdGenerator; pub(crate) use self::map::ConnectionMap; +#[cfg(not(feature = "__rustls"))] use boring::ssl::SslRef; use datagram_socket::AsSocketStats; use datagram_socket::DatagramSocketSend; @@ -230,6 +231,7 @@ where /// [boring]'s SSL object for this connection. #[doc(hidden)] + #[cfg(not(feature = "__rustls"))] pub fn ssl_mut(&mut self) -> &mut SslRef { self.params.quiche_conn.as_mut() } diff --git a/tokio-quiche/src/quic/hooks.rs b/tokio-quiche/src/quic/hooks.rs index 316c90e4d2..ed7bae8da1 100644 --- a/tokio-quiche/src/quic/hooks.rs +++ b/tokio-quiche/src/quic/hooks.rs @@ -24,7 +24,9 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#[cfg(not(feature = "__rustls"))] use crate::settings::TlsCertificatePaths; +#[cfg(not(feature = "__rustls"))] use boring::ssl::SslContextBuilder; /// A set of hooks executed at the level of a [quiche::Connection]. @@ -38,6 +40,7 @@ pub trait ConnectionHook { /// /// Only called if both the hook and [`TlsCertificatePaths`] are set in /// [`ConnectionParams`](crate::ConnectionParams). + #[cfg(not(feature = "__rustls"))] fn create_custom_ssl_context_builder( &self, settings: TlsCertificatePaths<'_>, ) -> Option; diff --git a/tokio-quiche/src/quic/io/worker.rs b/tokio-quiche/src/quic/io/worker.rs index dce1d9a52b..8af757b92b 100644 --- a/tokio-quiche/src/quic/io/worker.rs +++ b/tokio-quiche/src/quic/io/worker.rs @@ -51,6 +51,7 @@ use crate::quic::router::ConnectionMapCommand; use crate::quic::QuicheConnection; use crate::QuicResult; +#[cfg(not(feature = "__rustls"))] use boring::ssl::SslRef; use datagram_socket::DatagramSocketSend; use datagram_socket::DatagramSocketSendExt; @@ -630,6 +631,7 @@ pub struct Running { } impl Running { + #[cfg(not(feature = "__rustls"))] pub fn ssl(&mut self) -> &mut SslRef { self.qconn.as_mut() } @@ -663,6 +665,7 @@ where // async callback from this task accross a channel, for example, will // cause issues as this waker will then be stale and attempt to // wake the wrong task. + #[cfg(not(feature = "__rustls"))] std::future::poll_fn(|cx| { let ssl = qconn.as_mut(); ssl.set_task_waker(Some(cx.waker().clone())); diff --git a/tokio-quiche/src/quic/router/mod.rs b/tokio-quiche/src/quic/router/mod.rs index 1ab8102c3d..e3f9b2c17e 100644 --- a/tokio-quiche/src/quic/router/mod.rs +++ b/tokio-quiche/src/quic/router/mod.rs @@ -788,11 +788,18 @@ mod tests { use tokio::net::UdpSocket; use tokio::time; + #[cfg(not(feature = "__rustls"))] const TEST_CERT_FILE: &str = concat!( env!("CARGO_MANIFEST_DIR"), "/", "../quiche/examples/cert.crt" ); + #[cfg(feature = "__rustls")] + const TEST_CERT_FILE: &str = concat!( + env!("CARGO_MANIFEST_DIR"), + "/", + "../quiche/examples/cert_rustls.crt" + ); const TEST_KEY_FILE: &str = concat!( env!("CARGO_MANIFEST_DIR"), "/", diff --git a/tokio-quiche/src/settings/config.rs b/tokio-quiche/src/settings/config.rs index da0d0f1c7a..6a09703a45 100644 --- a/tokio-quiche/src/settings/config.rs +++ b/tokio-quiche/src/settings/config.rs @@ -111,6 +111,7 @@ impl Config { fn make_quiche_config( params: &ConnectionParams, should_log_keys: bool, ) -> QuicResult { + #[cfg(not(feature = "__rustls"))] let ssl_ctx_builder = params .hooks .connection_hook @@ -118,6 +119,7 @@ fn make_quiche_config( .zip(params.tls_cert) .and_then(|(hook, tls)| hook.create_custom_ssl_context_builder(tls)); + #[cfg(not(feature = "__rustls"))] let mut config = if let Some(builder) = ssl_ctx_builder { quiche::Config::with_boring_ssl_ctx_builder( quiche::PROTOCOL_VERSION, @@ -127,6 +129,9 @@ fn make_quiche_config( quiche_config_with_tls(params.tls_cert)? }; + #[cfg(feature = "__rustls")] + let mut config = quiche_config_with_tls(params.tls_cert)?; + let quic_settings = ¶ms.settings; let alpns: Vec<&[u8]> = diff --git a/tokio-quiche/tests/fixtures/mod.rs b/tokio-quiche/tests/fixtures/mod.rs index 4a1a6f7862..1bda44ab16 100644 --- a/tokio-quiche/tests/fixtures/mod.rs +++ b/tokio-quiche/tests/fixtures/mod.rs @@ -72,11 +72,18 @@ pub mod h3i_fixtures; use h3i_fixtures::stream_body; +#[cfg(not(feature = "__rustls"))] pub const TEST_CERT_FILE: &str = concat!( env!("CARGO_MANIFEST_DIR"), "/", "../quiche/examples/cert.crt" ); +#[cfg(feature = "__rustls")] +pub const TEST_CERT_FILE: &str = concat!( + env!("CARGO_MANIFEST_DIR"), + "/", + "../quiche/examples/cert_rustls.crt" +); pub const TEST_KEY_FILE: &str = concat!( env!("CARGO_MANIFEST_DIR"), "/", @@ -100,6 +107,7 @@ impl TestConnectionHook { } impl ConnectionHook for TestConnectionHook { + #[cfg(not(feature = "__rustls"))] fn create_custom_ssl_context_builder( &self, _settings: TlsCertificatePaths<'_>, ) -> Option { diff --git a/tokio-quiche/tests/integration_tests/mod.rs b/tokio-quiche/tests/integration_tests/mod.rs index 3265064467..41cb8f766f 100644 --- a/tokio-quiche/tests/integration_tests/mod.rs +++ b/tokio-quiche/tests/integration_tests/mod.rs @@ -41,6 +41,7 @@ use tokio_quiche::settings::TlsCertificatePaths; use tokio_quiche::ConnectionParams; use tokio_quiche::InitialQuicConnection; +#[cfg(not(feature = "__rustls"))] pub mod async_callbacks; pub mod connection_close; pub mod headers; @@ -71,7 +72,10 @@ async fn echo() { assert_eq!(resps.len(), req_count(i)); } + #[cfg(not(feature = "__rustls"))] assert!(hook.was_called()); + #[cfg(feature = "__rustls")] + assert!(!hook.was_called()); } #[tokio::test] @@ -86,7 +90,10 @@ async fn e2e() { let resps = res_map.get(&1).unwrap(); assert_eq!(resps.len(), 1); + #[cfg(not(feature = "__rustls"))] assert!(hook.was_called()); + #[cfg(feature = "__rustls")] + assert!(!hook.was_called()); } #[tokio::test] @@ -115,7 +122,10 @@ async fn e2e_client_ip_validation_disabled() { let resps = res_map.get(&1).unwrap(); assert_eq!(resps.len(), 1); + #[cfg(not(feature = "__rustls"))] assert!(hook.was_called()); + #[cfg(feature = "__rustls")] + assert!(!hook.was_called()); } #[with_test_telemetry(tokio::test)] diff --git a/tokio-quiche/tests/integration_tests/timeouts.rs b/tokio-quiche/tests/integration_tests/timeouts.rs index 1dc153de9c..8119ab349a 100644 --- a/tokio-quiche/tests/integration_tests/timeouts.rs +++ b/tokio-quiche/tests/integration_tests/timeouts.rs @@ -24,17 +24,25 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::sync::atomic::AtomicBool; use std::sync::atomic::AtomicUsize; use std::sync::atomic::Ordering; use std::sync::Arc; use std::time::Duration; -use boring::ssl::BoxSelectCertFinish; -use boring::ssl::ClientHello; -use boring::ssl::SslContextBuilder; -use boring::ssl::SslFiletype; -use boring::ssl::SslMethod; +#[cfg(not(feature = "__rustls"))] +mod boringssl { + pub(super) use boring::ssl::BoxSelectCertFinish; + pub(super) use boring::ssl::ClientHello; + pub(super) use boring::ssl::SslContextBuilder; + pub(super) use boring::ssl::SslFiletype; + pub(super) use boring::ssl::SslMethod; + pub(super) use std::sync::atomic::AtomicBool; + pub(super) use tokio_quiche::quic::ConnectionHook; + pub(super) use tokio_quiche::settings::TlsCertificatePaths; +} +#[cfg(not(feature = "__rustls"))] +use self::boringssl::*; + use h3i::actions::h3::send_headers_frame; use h3i::actions::h3::Action; use h3i::actions::h3::WaitType; @@ -45,8 +53,6 @@ use h3i::quiche::{ use tokio::net::UdpSocket; use tokio::time::timeout; use tokio_quiche::http3::driver::H3ConnectionError; -use tokio_quiche::quic::ConnectionHook; -use tokio_quiche::settings::TlsCertificatePaths; use url::Url; use crate::fixtures::h3i_fixtures::*; @@ -54,6 +60,7 @@ use crate::fixtures::*; // TODO(erittenhouse): figure out a way to avoid all of this duplication #[tokio::test] +#[cfg(not(feature = "__rustls"))] async fn test_handshake_duration_ioworker() { use h3i::client::ClientError;