From 79efcc965f12b30f8fcedb11a1b9074f873b493d Mon Sep 17 00:00:00 2001 From: Tom Fay Date: Wed, 29 Apr 2026 15:18:38 +0100 Subject: [PATCH] filter crypto runtime availability Add runtime availability helpers: - kx_group::available_default_groups - kx_group::available_groups - available_cipher_suites Change default_provider to use runtime-available cipher suites, key exchange groups, and signature algorithms. Keep compile-time algorithm lists as supersets and filter at runtime instead of relying on compile-time chacha/fips gating. --- Cargo.toml | 2 +- build.rs | 16 +++---- src/aead.rs | 25 +++++++++-- src/kx_group/ec.rs | 6 ++- src/kx_group/mod.rs | 56 ++++++++++++++++------- src/lib.rs | 89 ++++++++++++++++++++++++------------- src/openssl_internal/mod.rs | 4 +- src/quic.rs | 21 +++++++-- src/signer.rs | 3 +- src/tls12.rs | 11 ----- src/tls13.rs | 3 -- src/verify.rs | 60 +++++++++++++++++++++++-- tests/it.rs | 35 ++++++++++----- 13 files changed, 233 insertions(+), 98 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index bbfb288..3b0982a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,7 @@ readme = "README.md" [dependencies] foreign-types = "0.3.1" once_cell = "1.8.0" -openssl = "0.10.68" +openssl = "0.10.79" openssl-sys = "0.9.104" rustls = { version = "0.23.20", default-features = false } zeroize = "1.8.1" diff --git a/build.rs b/build.rs index 238dedb..3eeb68d 100644 --- a/build.rs +++ b/build.rs @@ -2,11 +2,9 @@ use std::env; -const OPENSSL_NO_CHACHA: &str = "OPENSSL_NO_CHACHA"; - fn main() { - println!("cargo:rustc-check-cfg=cfg(chacha)"); println!("cargo:rustc-check-cfg=cfg(fips_module)"); + println!("cargo:rustc-check-cfg=cfg(ossl300)"); println!("cargo:rustc-check-cfg=cfg(ossl320)"); println!("cargo:rustc-check-cfg=cfg(ossl350)"); // Determine whether to work around https://github.com/openssl/openssl/issues/23448 @@ -23,6 +21,10 @@ fn main() { println!("cargo:rustc-cfg=fips_module"); } + if version >= 0x3_00_00_00_0 { + println!("cargo:rustc-cfg=ossl300"); + } + if version >= 0x3_02_00_00_0 { println!("cargo:rustc-cfg=ossl320"); } @@ -31,12 +33,4 @@ fn main() { println!("cargo:rustc-cfg=ossl350"); } } - - // Enable the `chacha` cfg if the `OPENSSL_NO_CHACHA` OpenSSL config is not set. - if std::env::var("DEP_OPENSSL_CONF") - .map(|conf_string| !conf_string.split(',').any(|conf| conf == OPENSSL_NO_CHACHA)) - .unwrap_or(true) - { - println!("cargo:rustc-cfg=chacha"); - } } diff --git a/src/aead.rs b/src/aead.rs index 06b002c..40b840c 100644 --- a/src/aead.rs +++ b/src/aead.rs @@ -7,7 +7,6 @@ use rustls::crypto::cipher::NONCE_LEN; pub(crate) enum Algorithm { Aes128Gcm, Aes256Gcm, - #[cfg(all(chacha, not(feature = "fips")))] ChaCha20Poly1305, } @@ -19,7 +18,6 @@ impl Algorithm { match self { Self::Aes128Gcm => Cipher::aes_128_gcm(), Self::Aes256Gcm => Cipher::aes_256_gcm(), - #[cfg(all(chacha, not(feature = "fips")))] Self::ChaCha20Poly1305 => Cipher::chacha20_poly1305(), } } @@ -28,6 +26,18 @@ impl Algorithm { self.openssl_cipher().key_length() } + /// Returns `true` when OpenSSL can initialize this AEAD at runtime. + pub(crate) fn is_available(self) -> bool { + let key = vec![0u8; self.key_size()]; + let nonce = [0u8; NONCE_LEN]; + + CipherCtx::new() + .and_then(|mut ctx| { + ctx.encrypt_init(Some(self.openssl_cipher()), Some(&key), Some(&nonce)) + }) + .is_ok() + } + /// Encrypts data in place and returns the tag. pub(crate) fn encrypt_in_place( self, @@ -95,7 +105,6 @@ mod test { super::Algorithm::Aes128Gcm | super::Algorithm::Aes256Gcm => { wycheproof::aead::TestName::AesGcm } - #[cfg(all(chacha, not(feature = "fips")))] super::Algorithm::ChaCha20Poly1305 => wycheproof::aead::TestName::ChaCha20Poly1305, }; let test_set = wycheproof::aead::TestSet::load(test_name).unwrap(); @@ -178,9 +187,17 @@ mod test { test_aead(super::Algorithm::Aes256Gcm); } - #[cfg(all(chacha, not(feature = "fips")))] + #[test] + fn test_aes_available() { + assert!(super::Algorithm::Aes128Gcm.is_available()); + assert!(super::Algorithm::Aes256Gcm.is_available()); + } + #[test] fn test_chacha() { + if !super::Algorithm::ChaCha20Poly1305.is_available() { + return; + } test_aead(super::Algorithm::ChaCha20Poly1305); } } diff --git a/src/kx_group/ec.rs b/src/kx_group/ec.rs index db36c15..1e79675 100644 --- a/src/kx_group/ec.rs +++ b/src/kx_group/ec.rs @@ -119,14 +119,16 @@ mod test { #[case::secp384r1(TestName::EcdhSecp384r1, NamedGroup::secp384r1, Nid::SECP384R1)] fn test_ec_kx(#[case] test_name: TestName, #[case] rustls_group: NamedGroup, #[case] nid: Nid) { let test_set = wycheproof::ecdh::TestSet::load(test_name).unwrap(); - let ctx = openssl::bn::BigNumContext::new().unwrap(); + let mut ctx = openssl::bn::BigNumContext::new().unwrap(); for test_group in &test_set.test_groups { for test in &test_group.tests { let group = EcGroup::from_curve_name(nid).unwrap(); let private_num = BigNum::from_slice(&test.private_key).unwrap(); let mut point = EcPoint::new(&group).unwrap(); - point.mul_generator(&group, &private_num, &ctx).unwrap(); + point + .mul_generator2(&group, &private_num, &mut ctx) + .unwrap(); let ec_key = EcKey::from_private_components(&group, &private_num, &point).unwrap(); let kx = EcKeyExchange { diff --git a/src/kx_group/mod.rs b/src/kx_group/mod.rs index fc78a0a..111bcbf 100644 --- a/src/kx_group/mod.rs +++ b/src/kx_group/mod.rs @@ -4,38 +4,47 @@ use rustls::crypto::SupportedKxGroup; mod ec; pub use ec::{SECP256R1, SECP384R1}; -#[cfg(not(feature = "fips"))] mod x25519; -#[cfg(not(feature = "fips"))] pub use x25519::X25519; -#[cfg(ossl350)] +#[cfg(ossl300)] mod kem; -#[cfg(ossl350)] +#[cfg(ossl300)] pub use kem::{MLKEM768, X25519MLKEM768}; -/// Key exchanges enabled by default by this provider: +/// Key exchanges enabled by default by this provider. +/// +/// This compile-time list is filtered at runtime based on OpenSSL algorithm +/// availability. +/// Use [available_default_groups()] for runtime-available defaults. +/// +/// Compile-time set: /// * [X25519MLKEM768] (OpenSSL 3.5+) -/// * [X25519] (if fips feature not enabled) +/// * [X25519] /// * [SECP384R1] /// * [SECP256R1] /// /// If the `prefer-post-quantum` feature is enabled, X25519MLKEM768 will /// be the first group offered, otherwise it will be the last. pub static DEFAULT_KX_GROUPS: &[&dyn SupportedKxGroup] = &[ - #[cfg(all(ossl350, feature = "prefer-post-quantum"))] + #[cfg(all(ossl300, feature = "prefer-post-quantum"))] X25519MLKEM768, - #[cfg(not(feature = "fips"))] X25519, SECP256R1, SECP384R1, - #[cfg(all(ossl350, not(feature = "prefer-post-quantum")))] + #[cfg(all(ossl300, not(feature = "prefer-post-quantum")))] X25519MLKEM768, ]; -/// All key exchanges supported by this provider: +/// All key exchanges supported by this provider. +/// +/// This compile-time list is filtered at runtime based on OpenSSL algorithm +/// availability. +/// Use [available_groups()] for runtime-available groups. +/// +/// Compile-time set: /// * [X25519MLKEM768] (OpenSSL 3.5+) -/// * [X25519] (if fips feature not enabled) +/// * [X25519] /// * [SECP384R1] /// * [SECP256R1] /// * [MLKEM768] (OpenSSL 3.5+) @@ -43,14 +52,31 @@ pub static DEFAULT_KX_GROUPS: &[&dyn SupportedKxGroup] = &[ /// If the `prefer-post-quantum` feature is enabled, X25519MLKEM768 will /// be the first group offered, otherwise it will be the last. pub static ALL_KX_GROUPS: &[&dyn SupportedKxGroup] = &[ - #[cfg(all(ossl350, feature = "prefer-post-quantum"))] + #[cfg(all(ossl300, feature = "prefer-post-quantum"))] X25519MLKEM768, - #[cfg(not(feature = "fips"))] X25519, SECP256R1, SECP384R1, - #[cfg(all(ossl350, not(feature = "prefer-post-quantum")))] + #[cfg(all(ossl300, not(feature = "prefer-post-quantum")))] X25519MLKEM768, - #[cfg(ossl350)] + #[cfg(ossl300)] MLKEM768, ]; + +/// Returns the algorithms from [DEFAULT_KX_GROUPS] that are available at runtime. +pub fn available_default_groups() -> Vec<&'static dyn SupportedKxGroup> { + DEFAULT_KX_GROUPS + .iter() + .copied() + .filter(|group| group.start().is_ok()) + .collect() +} + +/// Returns the algorithms from [ALL_KX_GROUPS] that are available at runtime. +pub fn available_groups() -> Vec<&'static dyn SupportedKxGroup> { + ALL_KX_GROUPS + .iter() + .copied() + .filter(|group| group.start().is_ok()) + .collect() +} diff --git a/src/lib.rs b/src/lib.rs index 9d1d868..5a06a82 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,9 +6,10 @@ //! //! Supported cipher suites are listed below, in descending order of preference. //! -//! If OpenSSL is compiled with the `OPENSSL_NO_CHACHA` option, or the `fips` feature is enabled, -//! then the suites using ChaCha20-Poly1305 will not be available. -//! If the `tls12` feature is disabled then the TLS 1.2 cipher suites will not be available. +//! The default provider includes all of these cipher suites, filtered based on runtime availability +//! of the encryption algorithm, i.e when running against OpenSSL compiled without ChaCha20-Poly1305 support, the ChaCha20-Poly1305 cipher suites will be filtered out. +//! If the `tls12` feature is disabled, then the TLS 1.2 cipher suites will not be available. +//! Use [available_cipher_suites()] to get the runtime-available set of these cipher suites. //! //! ### TLS 1.3 //! @@ -29,16 +30,19 @@ //! //! In descending order of preference: //! -//! * X25519MLKEM768 (OpenSSL 3.5+) +//! * X25519MLKEM768 //! * SECP384R1 //! * SECP256R1 //! * X25519 -//! * MLKEM768 (OpenSSL 3.5+) +//! * MLKEM768 //! -//! If the `fips` feature is enabled then X25519 will not be available. //! If the `prefer-post-quantum` feature is enabled, X25519MLKEM768 will be the first group offered, otherwise it will be the last. //! MLKEM768 is not offered by default, but can be used by specifying it in the `custom_provider()` function. //! +//! The default provider includes all of these key exchange groups, filtered based on runtime availability of the algorithm. +//! Use [kx_group::available_default_groups()] to get the runtime-available set of default key exchange groups, +//! and [kx_group::available_groups()] for the runtime-available set of all key exchange groups. +//! //! ## Usage //! //! Add `rustls-openssl` to your `Cargo.toml`: @@ -57,8 +61,8 @@ //! # Features //! - `tls12`: Enables TLS 1.2 cipher suites. Enabled by default. //! - `prefer-post-quantum`: Enables X25519MLKEM768 as the first key exchange group. Enabled by default. -//! - `fips`: Enabling this feature removes non-FIPS-approved cipher suites and key exchanges. Disabled by default. See [fips]. //! - `vendored`: Enables vendored OpenSSL. Disabled by default. +//! - `fips`: No longer used. #![warn(missing_docs)] use openssl::rand::rand_priv_bytes; use rustls::SupportedCipherSuite; @@ -84,21 +88,18 @@ pub mod cipher_suite { #[cfg(feature = "tls12")] pub use super::tls12::{ TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, - TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, }; - #[cfg(all(feature = "tls12", chacha, not(feature = "fips")))] - pub use super::tls12::{ - TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + pub use super::tls13::{ + TLS13_AES_128_GCM_SHA256, TLS13_AES_256_GCM_SHA384, TLS13_CHACHA20_POLY1305_SHA256, }; - #[cfg(all(chacha, not(feature = "fips")))] - pub use super::tls13::TLS13_CHACHA20_POLY1305_SHA256; - pub use super::tls13::{TLS13_AES_128_GCM_SHA256, TLS13_AES_256_GCM_SHA384}; } pub use signer::KeyProvider; pub use verify::SUPPORTED_SIG_ALGS; -/// Returns an OpenSSL-based [CryptoProvider] using default available cipher suites ([ALL_CIPHER_SUITES]) and key exchange groups ([ALL_KX_GROUPS]). +/// Returns an OpenSSL-based [CryptoProvider] using default available cipher suites ([available_cipher_suites()]) and key exchange groups ([kx_group::available_default_groups()]). /// /// Sample usage: /// ```rust @@ -121,20 +122,51 @@ pub use verify::SUPPORTED_SIG_ALGS; /// ``` pub fn default_provider() -> CryptoProvider { CryptoProvider { - cipher_suites: ALL_CIPHER_SUITES.to_vec(), - kx_groups: kx_group::DEFAULT_KX_GROUPS.to_vec(), - signature_verification_algorithms: SUPPORTED_SIG_ALGS, + cipher_suites: available_cipher_suites(), + kx_groups: kx_group::available_default_groups(), + signature_verification_algorithms: *verify::available_supported_sig_algs(), secure_random: &SecureRandom, key_provider: &KeyProvider, } } +/// Returns the cipher suites from [ALL_CIPHER_SUITES] that are available at runtime. +pub fn available_cipher_suites() -> Vec { + ALL_CIPHER_SUITES + .iter() + .copied() + .filter(cipher_suite_available) + .collect() +} + +fn cipher_suite_available(cipher_suite: &SupportedCipherSuite) -> bool { + match cipher_suite.suite() { + rustls::CipherSuite::TLS13_AES_128_GCM_SHA256 + | rustls::CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 + | rustls::CipherSuite::TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 => { + aead::Algorithm::Aes128Gcm.is_available() + } + rustls::CipherSuite::TLS13_AES_256_GCM_SHA384 + | rustls::CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 + | rustls::CipherSuite::TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 => { + aead::Algorithm::Aes256Gcm.is_available() + } + rustls::CipherSuite::TLS13_CHACHA20_POLY1305_SHA256 + | rustls::CipherSuite::TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 + | rustls::CipherSuite::TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 => { + aead::Algorithm::ChaCha20Poly1305.is_available() + } + _ => true, + } +} + /// Create a [CryptoProvider] with specific cipher suites and key exchange groups /// /// The specified cipher suites and key exchange groups should be defined in descending order of preference. /// i.e the first elements have the highest priority during negotiation. /// -/// If the `fips` feature is enabled then fips mode will be enabled for OpenSSL, and this function will panic if this fails. +/// If OpenSSL is running in FIPS mode, non-approved algorithms may be filtered +/// out by runtime availability checks. /// /// Sample usage: /// ```rust @@ -170,7 +202,7 @@ pub fn custom_provider( CryptoProvider { cipher_suites, kx_groups, - signature_verification_algorithms: SUPPORTED_SIG_ALGS, + signature_verification_algorithms: *verify::available_supported_sig_algs(), secure_random: &SecureRandom, key_provider: &KeyProvider, } @@ -187,24 +219,23 @@ pub fn custom_provider( /// * TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 /// * TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 /// -/// If the non-default `fips` feature is enabled, or OpenSSL is compiled with the `OPENSSL_NO_CHACHA` option, then the ChaCha20-Poly1305 cipher suites will not be included. +/// ChaCha20-Poly1305 suites are runtime-filtered by [available_cipher_suites()]. /// If the default `tls12` feature is disabled then the TLS 1.2 cipher suites will not be included. pub static ALL_CIPHER_SUITES: &[SupportedCipherSuite] = &[ tls13::TLS13_AES_256_GCM_SHA384, tls13::TLS13_AES_128_GCM_SHA256, - #[cfg(all(chacha, not(feature = "fips")))] tls13::TLS13_CHACHA20_POLY1305_SHA256, #[cfg(feature = "tls12")] tls12::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, #[cfg(feature = "tls12")] tls12::TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, - #[cfg(all(feature = "tls12", chacha, not(feature = "fips")))] + #[cfg(feature = "tls12")] tls12::TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, #[cfg(feature = "tls12")] tls12::TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, #[cfg(feature = "tls12")] tls12::TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, - #[cfg(all(feature = "tls12", chacha, not(feature = "fips")))] + #[cfg(feature = "tls12")] tls12::TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, ]; @@ -227,19 +258,15 @@ pub mod fips { //! //! To use rustls with OpenSSL in FIPS mode, perform the following actions. //! - //! ## 1. Enable the `fips` feature - //! - //! This removes non-FIPS-approved cipher suites and key exchanges. - //! - //! ## 2. Specify `require_ems` when constructing [rustls::ClientConfig] or [rustls::ServerConfig] + //! ## 1. Specify `require_ems` when constructing [rustls::ClientConfig] or [rustls::ServerConfig] //! //! See [rustls documentation](https://docs.rs/rustls/latest/rustls/client/struct.ClientConfig.html#structfield.require_ems) for rationale. //! - //! ## 3. Enable FIPS mode for OpenSSL + //! ## 2. Enable FIPS mode for OpenSSL //! //! See [enable()]. //! - //! ## 4. Validate the FIPS status of your ClientConfig or ServerConfig at runtime + //! ## 3. Validate the FIPS status of your ClientConfig or ServerConfig at runtime //! See [rustls documenation on FIPS](https://docs.rs/rustls/latest/rustls/manual/_06_fips/index.html#3-validate-the-fips-status-of-your-clientconfigserverconfig-at-run-time). /// Returns `true` if OpenSSL is running in FIPS mode. diff --git a/src/openssl_internal/mod.rs b/src/openssl_internal/mod.rs index 765619a..fb45ac2 100644 --- a/src/openssl_internal/mod.rs +++ b/src/openssl_internal/mod.rs @@ -4,7 +4,7 @@ use openssl_sys::c_int; #[cfg(ossl320)] mod hpke; -#[cfg(ossl350)] +#[cfg(ossl300)] pub(crate) mod kem; #[cfg(feature = "tls12")] pub(crate) mod prf; @@ -18,8 +18,8 @@ pub(crate) fn cvt(r: c_int) -> Result { } } -#[cfg(ossl320)] #[inline] +#[cfg(ossl300)] fn cvt_p(r: *mut T) -> Result<*mut T, ErrorStack> { if r.is_null() { Err(ErrorStack::get()) diff --git a/src/quic.rs b/src/quic.rs index cb1da4f..9956928 100644 --- a/src/quic.rs +++ b/src/quic.rs @@ -27,7 +27,6 @@ struct PacketKey { pub(crate) enum HeaderProtectionAlgorithm { Aes128, Aes256, - #[cfg(all(chacha, not(feature = "fips")))] ChaCha20, } @@ -183,7 +182,6 @@ impl HeaderProtectionAlgorithm { match self { HeaderProtectionAlgorithm::Aes128 => Cipher::aes_128_ecb(), HeaderProtectionAlgorithm::Aes256 => Cipher::aes_256_ecb(), - #[cfg(all(chacha, not(feature = "fips")))] HeaderProtectionAlgorithm::ChaCha20 => Cipher::chacha20(), } } @@ -199,7 +197,6 @@ impl HeaderProtectionKey { .map_err(|e| Error::General(format!("OpenSSL error: {e}")))?; mask.copy_from_slice(&block[..5]); } - #[cfg(all(chacha, not(feature = "fips")))] // https://datatracker.ietf.org/doc/html/rfc9001#section-5.4.4 HeaderProtectionAlgorithm::ChaCha20 => { let block = encrypt( @@ -218,6 +215,7 @@ impl HeaderProtectionKey { #[cfg(test)] mod test { + use openssl::symm::encrypt; use rustls::{ Side, quic::{Keys, Version}, @@ -225,6 +223,18 @@ mod test { use super::super::tls13::TLS13_AES_128_GCM_SHA256_INTERNAL; + fn chacha20_is_available() -> bool { + let key = [0u8; 32]; + let iv = [0u8; 16]; + encrypt( + super::HeaderProtectionAlgorithm::ChaCha20.openssl_cipher(), + &key, + Some(&iv), + &[0u8; 5], + ) + .is_ok() + } + // Taken from rustls: Copyright (c) 2016 Joseph Birr-Pixton #[test] fn initial_test_vector_v2() { @@ -285,9 +295,12 @@ mod test { assert_eq!(server_packet[..], expected_server_packet[..]); } - #[cfg(all(chacha, not(feature = "fips")))] #[test] fn test_short_packet_length() { + if !chacha20_is_available() { + return; + } + use rustls::crypto::cipher::AeadKey; let sample = [ 0x5e, 0x5c, 0xd5, 0x5c, 0x41, 0xf6, 0x90, 0x80, 0x57, 0x5d, 0x79, 0x99, 0xc2, 0x5a, diff --git a/src/signer.rs b/src/signer.rs index 01138c9..4654146 100644 --- a/src/signer.rs +++ b/src/signer.rs @@ -108,7 +108,8 @@ impl SigningKey for PKey { .map(|scheme| Box::new(self.signer(*scheme)) as Box), SignatureAlgorithm::ED25519 => { - if offered.contains(&SignatureScheme::ED25519) { + if crate::verify::ed25519_available() && offered.contains(&SignatureScheme::ED25519) + { Some(Box::new(Signer { key: Arc::clone(&self.0), scheme: SignatureScheme::ED25519, diff --git a/src/tls12.rs b/src/tls12.rs index 96d771e..d99f398 100644 --- a/src/tls12.rs +++ b/src/tls12.rs @@ -17,7 +17,6 @@ const GCM_EXPLICIT_NONCE_LENGTH: usize = 8; const GCM_IMPLICIT_NONCE_LENGTH: usize = 4; static ECDSA_SCHEMES: &[SignatureScheme] = &[ - #[cfg(not(feature = "fips"))] SignatureScheme::ED25519, SignatureScheme::ECDSA_NISTP521_SHA512, SignatureScheme::ECDSA_NISTP384_SHA384, @@ -25,7 +24,6 @@ static ECDSA_SCHEMES: &[SignatureScheme] = &[ ]; /// The TLS1.2 ciphersuite `TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256`. -#[cfg(all(chacha, not(feature = "fips")))] pub static TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256: SupportedCipherSuite = SupportedCipherSuite::Tls12(&Tls12CipherSuite { common: CipherSuiteCommon { @@ -40,7 +38,6 @@ pub static TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256: SupportedCipherSuite = }); /// The TLS1.2 ciphersuite `TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256` -#[cfg(all(chacha, not(feature = "fips")))] pub static TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256: SupportedCipherSuite = SupportedCipherSuite::Tls12(&Tls12CipherSuite { common: CipherSuiteCommon { @@ -122,7 +119,6 @@ struct AesGcmEncrypter { full_iv: Iv, } -#[cfg(all(chacha, not(feature = "fips")))] pub(crate) struct ChaCha20Poly1305Crypter { key: AeadKey, iv: Iv, @@ -141,7 +137,6 @@ impl Tls12AeadAlgorithm for aead::Algorithm { full_iv: Iv::new(full_iv), }) } - #[cfg(all(chacha, not(feature = "fips")))] aead::Algorithm::ChaCha20Poly1305 => Box::new(ChaCha20Poly1305Crypter { key, iv: Iv::copy(iv), @@ -161,7 +156,6 @@ impl Tls12AeadAlgorithm for aead::Algorithm { implicit_iv, }) } - #[cfg(all(chacha, not(feature = "fips")))] aead::Algorithm::ChaCha20Poly1305 => Box::new(ChaCha20Poly1305Crypter { key, iv: Iv::copy(iv), @@ -176,7 +170,6 @@ impl Tls12AeadAlgorithm for aead::Algorithm { fixed_iv_len: GCM_IMPLICIT_NONCE_LENGTH, explicit_nonce_len: GCM_EXPLICIT_NONCE_LENGTH, }, - #[cfg(all(chacha, not(feature = "fips")))] aead::Algorithm::ChaCha20Poly1305 => KeyBlockShape { enc_key_len: self.key_size(), fixed_iv_len: NONCE_LEN, @@ -210,7 +203,6 @@ impl Tls12AeadAlgorithm for aead::Algorithm { iv: Iv::new(gcm_iv), }) } - #[cfg(all(chacha, not(feature = "fips")))] aead::Algorithm::ChaCha20Poly1305 => Ok(ConnectionTrafficSecrets::Chacha20Poly1305 { key, iv: Iv::new(iv[..].try_into().map_err(|_| UnsupportedOperationError)?), @@ -221,7 +213,6 @@ impl Tls12AeadAlgorithm for aead::Algorithm { fn fips(&self) -> bool { match self { aead::Algorithm::Aes128Gcm | aead::Algorithm::Aes256Gcm => crate::fips::enabled(), - #[cfg(all(chacha, not(feature = "fips")))] aead::Algorithm::ChaCha20Poly1305 => false, } } @@ -298,7 +289,6 @@ impl MessageDecrypter for AesGcmDecrypter { } } -#[cfg(all(chacha, not(feature = "fips")))] impl MessageEncrypter for ChaCha20Poly1305Crypter { fn encrypt( &mut self, @@ -327,7 +317,6 @@ impl MessageEncrypter for ChaCha20Poly1305Crypter { } } -#[cfg(all(chacha, not(feature = "fips")))] impl MessageDecrypter for ChaCha20Poly1305Crypter { fn decrypt<'a>( &mut self, diff --git a/src/tls13.rs b/src/tls13.rs index a90fb65..fbe1e17 100644 --- a/src/tls13.rs +++ b/src/tls13.rs @@ -13,11 +13,9 @@ use rustls::{ }; /// The TLS1.3 ciphersuite `TLS_CHACHA20_POLY1305_SHA256` -#[cfg(all(chacha, not(feature = "fips")))] pub static TLS13_CHACHA20_POLY1305_SHA256: SupportedCipherSuite = SupportedCipherSuite::Tls13(TLS13_CHACHA20_POLY1305_SHA256_INTERNAL); -#[cfg(all(chacha, not(feature = "fips")))] pub static TLS13_CHACHA20_POLY1305_SHA256_INTERNAL: &Tls13CipherSuite = &Tls13CipherSuite { common: CipherSuiteCommon { suite: CipherSuite::TLS13_CHACHA20_POLY1305_SHA256, @@ -113,7 +111,6 @@ impl Tls13AeadAlgorithm for aead::Algorithm { Ok(match self { aead::Algorithm::Aes128Gcm => ConnectionTrafficSecrets::Aes128Gcm { key, iv }, aead::Algorithm::Aes256Gcm => ConnectionTrafficSecrets::Aes256Gcm { key, iv }, - #[cfg(all(chacha, not(feature = "fips")))] aead::Algorithm::ChaCha20Poly1305 => { ConnectionTrafficSecrets::Chacha20Poly1305 { key, iv } } diff --git a/src/verify.rs b/src/verify.rs index a540872..0ec4130 100644 --- a/src/verify.rs +++ b/src/verify.rs @@ -1,4 +1,5 @@ use core::fmt; +use once_cell::sync::Lazy; use openssl::{ bn::BigNumContext, ec::{EcGroup, EcKey, EcPoint}, @@ -25,7 +26,6 @@ pub static SUPPORTED_SIG_ALGS: WebPkiSupportedAlgorithms = WebPkiSupportedAlgori ECDSA_P521_SHA256, ECDSA_P521_SHA384, ECDSA_P521_SHA512, - #[cfg(not(feature = "fips"))] ED25519, RSA_PSS_SHA512, RSA_PSS_SHA384, @@ -45,7 +45,6 @@ pub static SUPPORTED_SIG_ALGS: WebPkiSupportedAlgorithms = WebPkiSupportedAlgori &[ECDSA_P256_SHA256, ECDSA_P384_SHA256, ECDSA_P521_SHA256], ), (SignatureScheme::ECDSA_NISTP521_SHA512, &[ECDSA_P521_SHA512]), - #[cfg(not(feature = "fips"))] (SignatureScheme::ED25519, &[ED25519]), (SignatureScheme::RSA_PSS_SHA512, &[RSA_PSS_SHA512]), (SignatureScheme::RSA_PSS_SHA384, &[RSA_PSS_SHA384]), @@ -56,6 +55,62 @@ pub static SUPPORTED_SIG_ALGS: WebPkiSupportedAlgorithms = WebPkiSupportedAlgori ], }; +/// A [WebPkiSupportedAlgorithms] value defining the supported signature algorithms, +/// excluding ED25519 which is not available on fips enabled OpenSSL < 3.4. +static SUPPORTED_SIG_ALGS_NO_ED25519: WebPkiSupportedAlgorithms = WebPkiSupportedAlgorithms { + all: &[ + ECDSA_P256_SHA256, + ECDSA_P256_SHA384, + ECDSA_P384_SHA256, + ECDSA_P384_SHA384, + ECDSA_P521_SHA256, + ECDSA_P521_SHA384, + ECDSA_P521_SHA512, + RSA_PSS_SHA512, + RSA_PSS_SHA384, + RSA_PSS_SHA256, + RSA_PKCS1_SHA512, + RSA_PKCS1_SHA384, + RSA_PKCS1_SHA256, + ], + mapping: &[ + ( + SignatureScheme::ECDSA_NISTP384_SHA384, + &[ECDSA_P384_SHA384, ECDSA_P256_SHA384, ECDSA_P521_SHA384], + ), + ( + SignatureScheme::ECDSA_NISTP256_SHA256, + &[ECDSA_P256_SHA256, ECDSA_P384_SHA256, ECDSA_P521_SHA256], + ), + (SignatureScheme::ECDSA_NISTP521_SHA512, &[ECDSA_P521_SHA512]), + (SignatureScheme::RSA_PSS_SHA512, &[RSA_PSS_SHA512]), + (SignatureScheme::RSA_PSS_SHA384, &[RSA_PSS_SHA384]), + (SignatureScheme::RSA_PSS_SHA256, &[RSA_PSS_SHA256]), + (SignatureScheme::RSA_PKCS1_SHA512, &[RSA_PKCS1_SHA512]), + (SignatureScheme::RSA_PKCS1_SHA384, &[RSA_PKCS1_SHA384]), + (SignatureScheme::RSA_PKCS1_SHA256, &[RSA_PKCS1_SHA256]), + ], +}; + +static AVAILABLE_SIG_ALGS: Lazy<&'static WebPkiSupportedAlgorithms> = Lazy::new(|| { + // ED25519 won't be available on fips enabled OpenSSL < 3.4. + if ed25519_available() { + &SUPPORTED_SIG_ALGS + } else { + &SUPPORTED_SIG_ALGS_NO_ED25519 + } +}); + +static ED25519_AVAILABLE: Lazy = Lazy::new(|| PKey::generate_ed25519().is_ok()); + +pub(crate) fn available_supported_sig_algs() -> &'static WebPkiSupportedAlgorithms { + *AVAILABLE_SIG_ALGS +} + +pub(crate) fn ed25519_available() -> bool { + *ED25519_AVAILABLE +} + /// RSA PKCS#1 1.5 signatures using SHA-256. pub(crate) static RSA_PKCS1_SHA256: &dyn SignatureVerificationAlgorithm = &OpenSslAlgorithm { display_name: "RSA_PKCS1_SHA256", @@ -98,7 +153,6 @@ pub(crate) static RSA_PSS_SHA512: &dyn SignatureVerificationAlgorithm = &OpenSsl signature_alg_id: alg_id::RSA_PSS_SHA512, }; -#[cfg(not(feature = "fips"))] /// ED25519 signatures according to RFC 8410 pub(crate) static ED25519: &dyn SignatureVerificationAlgorithm = &OpenSslAlgorithm { display_name: "ED25519", diff --git a/tests/it.rs b/tests/it.rs index c7ea4c0..dad3b50 100644 --- a/tests/it.rs +++ b/tests/it.rs @@ -18,6 +18,12 @@ use std::sync::Arc; pub mod server; +fn suite_is_available(suite: CipherSuite) -> bool { + rustls_openssl::available_cipher_suites() + .iter() + .any(|supported| supported.suite() == suite) +} + fn test_with_provider( provider: CryptoProvider, port: u16, @@ -89,14 +95,11 @@ fn test_with_provider( server::Alg::PKCS_ECDSA_P256_SHA256, CipherSuite::TLS13_AES_256_GCM_SHA384 )] -#[cfg_attr( - all(chacha, not(feature = "fips")), - case::tls13_chacha20_poly1305_sha256( - rustls_openssl::cipher_suite::TLS13_CHACHA20_POLY1305_SHA256, - rustls_openssl::kx_group::SECP256R1, - server::Alg::PKCS_ECDSA_P256_SHA256, - CipherSuite::TLS13_CHACHA20_POLY1305_SHA256 - ) +#[case::tls13_chacha20_poly1305_sha256( + rustls_openssl::cipher_suite::TLS13_CHACHA20_POLY1305_SHA256, + rustls_openssl::kx_group::SECP256R1, + server::Alg::PKCS_ECDSA_P256_SHA256, + CipherSuite::TLS13_CHACHA20_POLY1305_SHA256 )] #[cfg_attr( feature = "tls12", @@ -132,7 +135,7 @@ fn test_with_provider( CipherSuite::TLS13_AES_256_GCM_SHA384 )] #[cfg_attr( - all(feature = "tls12", chacha, not(feature = "fips")), + feature = "tls12", case::tls_ecdhe_rsa_with_chacha20_poly1305_sha256( rustls_openssl::cipher_suite::TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, rustls_openssl::kx_group::SECP256R1, @@ -179,6 +182,10 @@ fn test_client_and_server( #[case] alg: server::Alg, #[case] expected: CipherSuite, ) { + if !suite_is_available(suite.suite()) { + return; + } + // Run against a server using our default provider let (port, certificate) = start_server(alg, None); let provider = custom_provider(vec![suite], vec![group]); @@ -189,6 +196,10 @@ fn test_client_and_server( #[cfg(ossl350)] #[test] fn test_classical_completion() { + if rustls_openssl::kx_group::X25519.start().is_err() { + return; + } + // Run against a server that only supports the classical component let provider = custom_provider( rustls_openssl::ALL_CIPHER_SUITES.to_vec(), @@ -210,7 +221,7 @@ fn test_classical_completion() { #[rstest] #[cfg_attr( - all(feature = "tls12", chacha, not(feature = "fips")), + feature = "tls12", case( rustls_openssl::cipher_suite::TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, rustls_openssl::kx_group::SECP384R1, @@ -227,6 +238,10 @@ fn test_to_internet( #[case] group: &'static dyn SupportedKxGroup, #[case] expected: CipherSuite, ) { + if !suite_is_available(suite.suite()) { + return; + } + #[cfg(feature = "fips")] { rustls_openssl::fips::enable();