diff --git a/Cargo.lock b/Cargo.lock index d84d660b74..931202f06b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4589,6 +4589,7 @@ dependencies = [ "oid-registry", "once_cell", "openssl", + "openssl-sys", "pin-project-lite", "pingora-pool", "ppp", diff --git a/Cargo.toml b/Cargo.toml index 5f89e6e9cb..762b9a4057 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,7 @@ jemalloc = ["dep:tikv-jemallocator", "dep:jemalloc_pprof"] tls-boring = ["dep:boring", "dep:boring-sys", "boring-rustls-provider/fips-only"] tls-ring = ["dep:ring", "rustls/ring", "tokio-rustls/ring", "hyper-rustls/ring", "dep:rcgen"] tls-aws-lc = ["dep:ring", "rustls/aws_lc_rs", "tokio-rustls/aws_lc_rs", "hyper-rustls/aws-lc-rs", "dep:rcgen", "rcgen/aws_lc_rs"] -tls-openssl = ["dep:rustls-openssl", "dep:openssl" ] +tls-openssl = ["dep:rustls-openssl", "dep:openssl", "dep:openssl-sys"] testing = ["dep:rcgen", "rcgen/x509-parser", "dep:tempfile"] # Enables utilities supporting tests. [lib] @@ -42,6 +42,7 @@ ring = { version = "0.17", optional = true } # Enabled with 'tls-openssl' rustls-openssl = { version = "0.3", optional = true, features = ["tls12"]} openssl = { version = "0.10", optional = true } +openssl-sys = { version = "0.9", optional = true } anyhow = "1.0" async-stream = "0.3" diff --git a/build.rs b/build.rs index 6776f02db7..ec06e13f62 100644 --- a/build.rs +++ b/build.rs @@ -21,6 +21,17 @@ fn main() -> Result<(), anyhow::Error> { // Fuzzing uses custom cfg (https://rust-fuzz.github.io/book/cargo-fuzz/guide.html) // Tell cargo to expect this (https://doc.rust-lang.org/nightly/rustc/check-cfg/cargo-specifics.html). println!("cargo::rustc-check-cfg=cfg(fuzzing)"); + + // OpenSSL version detection (https://docs.rs/openssl/latest/openssl/#feature-detection) + println!("cargo:rustc-check-cfg=cfg(ossl350)"); + if let Ok(v) = env::var("DEP_OPENSSL_VERSION_NUMBER") { + let version = u64::from_str_radix(&v, 16).unwrap(); + #[allow(clippy::unusual_byte_groupings)] + if version >= 0x3_05_00_00_0 { + println!("cargo:rustc-cfg=ossl350"); + } + } + let proto_files = [ "proto/xds.proto", "proto/workload.proto", diff --git a/src/tls/lib.rs b/src/tls/lib.rs index dddd631bd1..54b7bb6da2 100644 --- a/src/tls/lib.rs +++ b/src/tls/lib.rs @@ -131,11 +131,27 @@ pub(super) fn provider() -> Arc { ]); } - // Use only FIPS-compliant key exchange groups - let kx_groups: Vec<&'static dyn rustls::crypto::SupportedKxGroup> = vec![ - rustls_openssl::kx_group::SECP256R1, - rustls_openssl::kx_group::SECP384R1, - ]; + let kx_groups: Vec<&'static dyn rustls::crypto::SupportedKxGroup> = if *PQC_ENABLED { + // To use PQC with OpenSSL provider the binary needs to be + // both compiled and used with OpenSSL >= 3.5.0. + #[cfg(ossl350)] + { + if openssl::version::number() >= 0x30500000 { + vec![rustls_openssl::kx_group::X25519MLKEM768] + } else { + panic!("COMPLIANCE_POLICY=pqc requires OpenSSL >=3.5.0"); + } + } + #[cfg(not(ossl350))] + { + panic!("COMPLIANCE_POLICY=pqc requires compilation with OpenSSL >=3.5.0"); + } + } else { + vec![ + rustls_openssl::kx_group::SECP256R1, + rustls_openssl::kx_group::SECP384R1, + ] + }; Arc::new(CryptoProvider { cipher_suites, @@ -253,4 +269,39 @@ pub mod tests { assert!(!future_certs.is_expired()); assert_eq!(future_certs.get_duration_until_refresh(), zero_dur); } + + #[test] + #[cfg(feature = "tls-openssl")] + fn test_openssl_provider_created_successfully() { + // Test that provider can be created without panicking + let provider = super::provider(); + assert!( + !provider.kx_groups.is_empty(), + "kx_groups should not be empty" + ); + } + + #[test] + #[cfg(feature = "tls-openssl")] + fn test_openssl_provider_kx_groups_valid() { + // Provider must have valid key exchange groups regardless of PQC state + let provider = super::provider(); + let expected_len = if *crate::PQC_ENABLED { 1 } else { 2 }; + assert_eq!( + provider.kx_groups.len(), + expected_len, + "PQC={} should have {} kx groups", + *crate::PQC_ENABLED, + expected_len + ); + } + + #[test] + #[cfg(all(feature = "tls-openssl", not(ossl350)))] + fn test_pqc_panic_expected_without_ossl350() { + // Without ossl350 cfg, PQC cannot be enabled (would panic in provider()) + if *crate::PQC_ENABLED { + panic!("PQC_ENABLED=true without ossl350 cfg - provider() will panic"); + } + } }