Skip to content

Commit

Permalink
Use features to set key exchange preferences
Browse files Browse the repository at this point in the history
Overwrite boringSSL's default key exchange preferences with safe
defaults using feature flags:

* "kx-pq-supported" enables support for PQ key exchange algorithms.
  Classical key exchange is still preferred, but will be upgraded to PQ
  if requested.

* "kx-pq-preferred" enables preference for PQ key exchange,
  with fallback to classical key exchange if requested.

* "kx-fips-required" disables non-FIPS-compliant algorithms.

If any of these "kx-*" features are enabled, then don't compile bindings
for `SSL_CTX_set1_curves()`. This is to prevent the feature flags from
silently overriding curve preferences chosen by the user.

Ideally we'd allow both: that is, use "kx-*" to set defaults, but still
allow the user to manually override them. However, this doesn't work
because by the time the `SSL_CTX` is constructed, we don't yet know
whether we're the client or server. (The "kx-*" features set different
preferences for each.)
  • Loading branch information
cjpatton committed Aug 17, 2023
1 parent a6e35a4 commit f083a08
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 3 deletions.
6 changes: 5 additions & 1 deletion boring-sys/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
19 changes: 18 additions & 1 deletion boring/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,26 @@ 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. 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. 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 on the client side.
kx-fips-required = []

[dependencies]
bitflags = { workspace = true }
foreign-types = { workspace = true }
Expand Down
73 changes: 72 additions & 1 deletion boring/src/ssl/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -630,7 +630,7 @@ impl SslCurve {

pub const X25519: SslCurve = SslCurve(ffi::NID_X25519);

#[cfg(not(feature = "fips"))]
#[cfg(all(feature = "pq-experimental", not(feature = "fips")))]
pub const X25519_KYBER768_DRAFT00: SslCurve = SslCurve(ffi::NID_X25519Kyber768Draft00);

#[cfg(feature = "pq-experimental")]
Expand Down Expand Up @@ -1696,6 +1696,15 @@ impl SslContextBuilder {
/// This corresponds to [`SSL_CTX_set1_curves`]
///
/// [`SSL_CTX_set1_curves`]: https://commondatastorage.googleapis.com/chromium-boringssl-docs/ssl.h.html#SSL_CTX_set1_curves
//
// If the "kx-*" flags are used to set key exchange preference, then don't allow the user to
// set them here. This ensures we don't override the user's preference without telling them:
// when the flags are used, the preferences are set just before connecting or accepting.
#[cfg(not(any(
feature = "kx-pq-preferred",
feature = "kx-pq-supported",
feature = "kx-fips-required"
)))]
pub fn set_curves(&mut self, curves: &[SslCurve]) -> Result<(), ErrorStack> {
unsafe {
cvt_0i(ffi::SSL_CTX_set1_curves(
Expand Down Expand Up @@ -2393,6 +2402,22 @@ impl SslRef {
unsafe { ErrorCode::from_raw(ffi::SSL_get_error(self.as_ptr(), ret)) }
}

#[cfg(any(
feature = "kx-pq-preferred",
feature = "kx-pq-supported",
feature = "kx-fips-required"
))]
fn set_curves_list(&mut self, curves: &str) -> Result<(), ErrorStack> {
let curves = CString::new(curves).unwrap();
unsafe {
cvt(ffi::SSL_set1_curves_list(
self.as_ptr(),
curves.as_ptr() as *const _,
))
.map(|_| ())
}
}

/// Like [`SslContextBuilder::set_verify`].
///
/// This corresponds to [`SSL_set_verify`].
Expand Down Expand Up @@ -3466,6 +3491,38 @@ where
/// See `Ssl::connect`
pub fn connect(self) -> Result<SslStream<S>, HandshakeError<S>> {
let mut stream = self.inner;

// Set key exchange preferences for the connection from the "kx-*" feature flags.
#[cfg(any(
feature = "kx-pq-preferred",
feature = "kx-pq-supported",
feature = "kx-fips-required"
))]
{
// Decide which algorithms are supported and choose a preference order.
let curves = if cfg!(feature = "kx-pq-preferred") {
if cfg!(feature = "kx-fips-required") {
Some("P256Kyber768Draft00:P-256:P-384")
} else {
Some("X25519Kyber768Draft00:P256Kyber768Draft00:X25519:P-256:P-384")
}
} else if cfg!(feature = "kx-pq-supported") {
if cfg!(feature = "kx-fips-required") {
Some("P-256:P-384:P256Kyber768Draft00")
} else {
Some("X25519:P-256:P-384:X25519Kyber768Draft00:P256Kyber768Draft00")
}
} else if cfg!(feature = "kx-fips-required") {
Some("P-256:P-384")
} else {
None
};

if let Some(curves) = curves {
stream.ssl.set_curves_list(curves)?;
}
}

let ret = unsafe { ffi::SSL_connect(stream.ssl.as_ptr()) };
if ret > 0 {
Ok(stream)
Expand All @@ -3489,6 +3546,20 @@ where
/// See `Ssl::accept`
pub fn accept(self) -> Result<SslStream<S>, HandshakeError<S>> {
let mut stream = self.inner;

// Set key exchange preferences for the connection from the "kx-*" feature flags.
#[cfg(any(
feature = "kx-pq-preferred",
feature = "kx-pq-supported",
feature = "kx-fips-required"
))]
{
// The server prefers PQ key exchange but supports classical as well.
stream
.ssl
.set_curves_list("X25519Kyber768Draft00:P256Kyber768Draft00:X25519:P-256:P-384")?;
}

let ret = unsafe { ffi::SSL_accept(stream.ssl.as_ptr()) };
if ret > 0 {
Ok(stream)
Expand Down

0 comments on commit f083a08

Please sign in to comment.