From d70dbb92264651421a7218acd27b9e532abfe7fc Mon Sep 17 00:00:00 2001 From: Christopher Patton Date: Tue, 8 Aug 2023 16:43:36 -0700 Subject: [PATCH] Use features to set default key exchange preferences Overwrite boringSSL's default key exchange preferences with safe defaults using feature flags: * "kx-pq-supported" enables support for PQ key exchange algorithms by default. Classical key exchange is still preferred, but will be upgraded to PQ if requested. * "kx-pq-preferred" enables preference for PQ key exchange by default, with fallback to classical key exchange if requested. * "kx-fips-required" disables non-FIPS-compliant algorithms by default. --- Cargo.toml | 1 + boring-sys/Cargo.toml | 6 ++++- boring/Cargo.toml | 21 ++++++++++++++- boring/src/ssl/mod.rs | 61 ++++++++++++++++++++++++++++++++++--------- 4 files changed, 75 insertions(+), 14 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 2784e6c8b..8d2b5c3d3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,6 +18,7 @@ boring = { version = "3", path = "./boring" } tokio-boring = { version = "3", path = "./tokio-boring" } bindgen = { version = "0.66.1", default-features = false, features = ["runtime"] } +cfg-if = "1.0.0" cmake = "0.1" fs_extra = "1.3.0" fslock = "0.2" diff --git a/boring-sys/Cargo.toml b/boring-sys/Cargo.toml index 1b938fd24..53e38f949 100644 --- a/boring-sys/Cargo.toml +++ b/boring-sys/Cargo.toml @@ -56,7 +56,11 @@ fips-link-precompiled = ["fips"] # Enables Raw public key API (https://datatracker.ietf.org/doc/html/rfc7250) rpk = [] -# Enables experimental post-quantum crypto (https://blog.cloudflare.com/post-quantum-for-all/) +# Applies a patch (`patches/boring-pq.patch`) to the boringSSL source code that +# enables support for PQ key exchange. This feature is necessary in order to +# compile the bindings for the default branch of boringSSL (`deps/boringssl`). +# Alternatively, a version of boringSSL that implements the same feature set +# can be provided by setting `BORING_BSSL_SOURCE_PATH`. pq-experimental = [] [build-dependencies] diff --git a/boring/Cargo.toml b/boring/Cargo.toml index 568a53ad8..50f5df887 100644 --- a/boring/Cargo.toml +++ b/boring/Cargo.toml @@ -25,11 +25,30 @@ fips-link-precompiled = ["fips", "boring-sys/fips-link-precompiled"] # Enables Raw public key API (https://datatracker.ietf.org/doc/html/rfc7250) rpk = ["boring-sys/rpk"] -# Enables experimental post-quantum crypto (https://blog.cloudflare.com/post-quantum-for-all/) +# Applies a patch to the boringSSL source code that enables support for PQ key +# exchange. This feature is necessary in order to compile the bindings for the +# default branch of boringSSL. Alternatively, a version of boringSSL that +# implements the same feature set can be provided by setting +# `BORING_BSSL_SOURCE_PATH`. pq-experimental = ["boring-sys/pq-experimental"] +# Support PQ key exchange by default. The client will prefer classical key +# exchange, but will upgrade to PQ key exchange if requested by the server. +# This is the safest option if you don't know if the peer supports PQ key +# exchange. +kx-pq-supported = [] + +# Prefer PQ key exchange by default. The client will prefer PQ exchange, but +# fallback to classical key exchange if requested by the server. This is the +# best option if you know the peer supports PQ key exchange. +kx-pq-preferred = ["kx-pq-supported"] + +# Disable non-FIPS key exchange algorithms by default. +kx-fips-required = [] + [dependencies] bitflags = { workspace = true } +cfg-if = { workspace = true } foreign-types = { workspace = true } once_cell = { workspace = true } libc = { workspace = true } diff --git a/boring/src/ssl/mod.rs b/boring/src/ssl/mod.rs index 67e16b181..656e54273 100644 --- a/boring/src/ssl/mod.rs +++ b/boring/src/ssl/mod.rs @@ -633,13 +633,13 @@ impl SslCurve { #[cfg(not(feature = "fips"))] pub const X25519_KYBER768_DRAFT00: SslCurve = SslCurve(ffi::NID_X25519Kyber768Draft00); - #[cfg(feature = "pq-experimental")] + #[cfg(feature = "kx-pq-supported")] pub const X25519_KYBER768_DRAFT00_OLD: SslCurve = SslCurve(ffi::NID_X25519Kyber768Draft00Old); - #[cfg(feature = "pq-experimental")] + #[cfg(feature = "kx-pq-supported")] pub const X25519_KYBER512_DRAFT00: SslCurve = SslCurve(ffi::NID_X25519Kyber512Draft00); - #[cfg(feature = "pq-experimental")] + #[cfg(feature = "kx-pq-supported")] pub const P256_KYBER768_DRAFT00: SslCurve = SslCurve(ffi::NID_P256Kyber768Draft00); } @@ -696,16 +696,20 @@ pub struct SslContextBuilder { impl SslContextBuilder { /// Creates a new `SslContextBuilder` to be used with Raw Public Key. /// - /// This corresponds to [`SSL_CTX_new`]. + /// This corresponds to calling [`SSL_CTX_new`] then overwriting the default key exchange and + /// cipher preferences. The defaults are only overwritten if certain feature flags are set. /// /// [`SSL_CTX_new`]: https://www.openssl.org/docs/manmaster/man3/SSL_CTX_new.html pub fn new_rpk() -> Result { - unsafe { + let mut ctx = unsafe { init(); let ctx = cvt_p(ffi::SSL_CTX_new(SslMethod::tls_with_buffer().as_ptr()))?; - Ok(SslContextBuilder::from_ptr(ctx, true)) - } + SslContextBuilder::from_ptr(ctx, true) + }; + + ctx.set_safe_defaults()?; + Ok(ctx) } /// Sets raw public key certificate in DER format. @@ -739,24 +743,28 @@ impl SslContextBuilder { impl SslContextBuilder { /// Creates a new `SslContextBuilder`. /// - /// This corresponds to [`SSL_CTX_new`]. + /// This corresponds to calling [`SSL_CTX_new`] then overwriting the default key exchange and + /// cipher preferences. The defaults are only overwritten if certain feature flags are set. /// /// [`SSL_CTX_new`]: https://www.openssl.org/docs/manmaster/man3/SSL_CTX_new.html pub fn new(method: SslMethod) -> Result { - unsafe { + let mut ctx = unsafe { init(); let ctx = cvt_p(ffi::SSL_CTX_new(method.as_ptr()))?; #[cfg(feature = "rpk")] { - Ok(SslContextBuilder::from_ptr(ctx, false)) + SslContextBuilder::from_ptr(ctx, false) } #[cfg(not(feature = "rpk"))] { - Ok(SslContextBuilder::from_ptr(ctx)) + SslContextBuilder::from_ptr(ctx) } - } + }; + + ctx.set_safe_defaults()?; + Ok(ctx) } /// Creates an `SslContextBuilder` from a pointer to a raw OpenSSL value. @@ -1707,6 +1715,35 @@ impl SslContextBuilder { } } + fn set_safe_defaults(&mut self) -> Result<(), ErrorStack> { + // Set default key exchange preferences. + cfg_if::cfg_if! { + if #[cfg(feature = "kx-pq-preferred")] { + self.set_curves(&[ + #[cfg(not(feature = "kx-fips-required"))] + SslCurve::X25519_KYBER512_DRAFT00, + SslCurve::P256_KYBER768_DRAFT00, + #[cfg(not(feature = "kx-fips-required"))] + SslCurve::X25519, + SslCurve::SECP256R1, + SslCurve::SECP384R1, + ])?; + } else if #[cfg(feature = "kx-pq-supported")] { + self.set_curves(&[ + #[cfg(not(feature = "kx-fips-required"))] + SslCurve::X25519, + SslCurve::SECP256R1, + SslCurve::SECP384R1, + #[cfg(not(feature = "kx-fips-required"))] + SslCurve::X25519_KYBER512_DRAFT00, + SslCurve::P256_KYBER768_DRAFT00, + ])?; + } + } + + Ok(()) + } + /// Consumes the builder, returning a new `SslContext`. pub fn build(self) -> SslContext { self.ctx