diff --git a/Cargo.toml b/Cargo.toml index 85bfcf6778..ca1edb616a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -40,7 +40,7 @@ boring-sys = { version = "4", optional = true } ring = { version = "0.17", optional = true } # Enabled with 'tls-openssl' -rustls-openssl = { version = "0.3", optional = true } +rustls-openssl = { version = "0.3", optional = true, features = ["tls12"]} openssl = { version = "0.10", optional = true } anyhow = "1.0" @@ -83,7 +83,7 @@ prost = { version = "0.14", default-features = false } prost-types = { version = "0.14", default-features = false } rand = { version = "0.9" , features = ["small_rng"]} rcgen = { version = "0.14", optional = true, features = ["pem"] } -rustls = { version = "0.23", default-features = false } +rustls = { version = "0.23", features = ["tls12"], default-features = false } rustls-native-certs = "0.8" rustls-pemfile = "2.2" serde = { version = "1.0", features = ["derive", "rc"] } diff --git a/src/lib.rs b/src/lib.rs index 3da9d4d233..e963d20377 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -49,3 +49,7 @@ pub mod test_helpers; #[allow(dead_code)] static PQC_ENABLED: Lazy = Lazy::new(|| env::var("COMPLIANCE_POLICY").unwrap_or_default() == "pqc"); + +#[allow(dead_code)] +static TLS12_ENABLED: Lazy = + Lazy::new(|| env::var("TLS12_ENABLED").unwrap_or_default() == "true"); diff --git a/src/tls/certificate.rs b/src/tls/certificate.rs index 9d565e52ce..af95bfdcf2 100644 --- a/src/tls/certificate.rs +++ b/src/tls/certificate.rs @@ -326,7 +326,7 @@ impl WorkloadCertificate { let client_cert_verifier = crate::tls::workload::TrustDomainVerifier::new(raw_client_cert_verifier, td); let mut sc = ServerConfig::builder_with_provider(crate::tls::lib::provider()) - .with_protocol_versions(tls::TLS_VERSIONS) + .with_protocol_versions(tls::tls_versions()) .expect("server config must be valid") .with_client_cert_verifier(client_cert_verifier) .with_single_cert( @@ -343,7 +343,7 @@ impl WorkloadCertificate { let roots = self.root_store.clone(); let verifier = IdentityVerifier { roots, identity }; let mut cc = ClientConfig::builder_with_provider(crate::tls::lib::provider()) - .with_protocol_versions(tls::TLS_VERSIONS) + .with_protocol_versions(tls::tls_versions()) .expect("client config must be valid") .dangerous() // Customer verifier is requires "dangerous" opt-in .with_custom_certificate_verifier(Arc::new(verifier)) diff --git a/src/tls/control.rs b/src/tls/control.rs index 490676dbaa..36df68ef79 100644 --- a/src/tls/control.rs +++ b/src/tls/control.rs @@ -182,7 +182,7 @@ async fn control_plane_client_config( ) -> Result { let roots = root_to_store(root_cert).await?; let c = ClientConfig::builder_with_provider(provider()) - .with_protocol_versions(crate::tls::TLS_VERSIONS)?; + .with_protocol_versions(crate::tls::tls_versions())?; if let Some(alt_hostname) = alt_hostname { debug!("using alternate hostname {alt_hostname} for TLS verification"); Ok(c.dangerous() diff --git a/src/tls/lib.rs b/src/tls/lib.rs index f6b73a755d..2825163dac 100644 --- a/src/tls/lib.rs +++ b/src/tls/lib.rs @@ -16,6 +16,7 @@ use super::Error; #[allow(unused_imports)] use crate::PQC_ENABLED; +use crate::TLS12_ENABLED; use crate::identity::{self, Identity}; use std::fmt::Debug; @@ -38,7 +39,17 @@ pub trait ServerCertProvider: Send + Sync + Clone { async fn fetch_cert(&mut self) -> Result, TlsError>; } -pub(super) static TLS_VERSIONS: &[&rustls::SupportedProtocolVersion] = &[&rustls::version::TLS13]; +static TLS_VERSIONS_13_ONLY: &[&rustls::SupportedProtocolVersion] = &[&rustls::version::TLS13]; +static TLS_VERSIONS_12_AND_13: &[&rustls::SupportedProtocolVersion] = + &[&rustls::version::TLS13, &rustls::version::TLS12]; + +pub fn tls_versions() -> &'static [&'static rustls::SupportedProtocolVersion] { + if *TLS12_ENABLED { + TLS_VERSIONS_12_AND_13 + } else { + TLS_VERSIONS_13_ONLY + } +} #[cfg(feature = "tls-aws-lc")] pub static CRYPTO_PROVIDER: &str = "tls-aws-lc"; @@ -59,30 +70,47 @@ pub static CRYPTO_PROVIDER: &str = "tls-openssl"; pub(super) fn provider() -> Arc { // Due to 'fips-only' feature on the boring provider, this will use only AES_256_GCM_SHA384 // and AES_128_GCM_SHA256 - // In later code we select to only use TLS 1.3 Arc::new(boring_rustls_provider::provider()) } #[cfg(feature = "tls-ring")] pub(super) fn provider() -> Arc { + let mut cipher_suites = vec![ + rustls::crypto::ring::cipher_suite::TLS13_AES_256_GCM_SHA384, + rustls::crypto::ring::cipher_suite::TLS13_AES_128_GCM_SHA256, + ]; + if *TLS12_ENABLED { + // Add TLS 1.2 FIPS-compatible cipher suites + cipher_suites.extend([ + rustls::crypto::ring::cipher_suite::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + rustls::crypto::ring::cipher_suite::TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + rustls::crypto::ring::cipher_suite::TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + rustls::crypto::ring::cipher_suite::TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + ]); + } Arc::new(CryptoProvider { - // Limit to only the subset of ciphers that are FIPS compatible - cipher_suites: vec![ - rustls::crypto::ring::cipher_suite::TLS13_AES_256_GCM_SHA384, - rustls::crypto::ring::cipher_suite::TLS13_AES_128_GCM_SHA256, - ], + cipher_suites, ..rustls::crypto::ring::default_provider() }) } #[cfg(feature = "tls-aws-lc")] pub(super) fn provider() -> Arc { + let mut cipher_suites = vec![ + rustls::crypto::aws_lc_rs::cipher_suite::TLS13_AES_256_GCM_SHA384, + rustls::crypto::aws_lc_rs::cipher_suite::TLS13_AES_128_GCM_SHA256, + ]; + if *TLS12_ENABLED { + // Add TLS 1.2 FIPS-compatible cipher suites + cipher_suites.extend([ + rustls::crypto::aws_lc_rs::cipher_suite::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + rustls::crypto::aws_lc_rs::cipher_suite::TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + rustls::crypto::aws_lc_rs::cipher_suite::TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + rustls::crypto::aws_lc_rs::cipher_suite::TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + ]); + } let mut provider = CryptoProvider { - // Limit to only the subset of ciphers that are FIPS compatible - cipher_suites: vec![ - rustls::crypto::aws_lc_rs::cipher_suite::TLS13_AES_256_GCM_SHA384, - rustls::crypto::aws_lc_rs::cipher_suite::TLS13_AES_128_GCM_SHA256, - ], + cipher_suites, ..rustls::crypto::aws_lc_rs::default_provider() }; @@ -95,12 +123,29 @@ pub(super) fn provider() -> Arc { #[cfg(feature = "tls-openssl")] pub(super) fn provider() -> Arc { + let mut cipher_suites = vec![ + rustls_openssl::cipher_suite::TLS13_AES_256_GCM_SHA384, + rustls_openssl::cipher_suite::TLS13_AES_128_GCM_SHA256, + ]; + if *TLS12_ENABLED { + // Add TLS 1.2 FIPS-compatible cipher suites + cipher_suites.extend([ + rustls_openssl::cipher_suite::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + rustls_openssl::cipher_suite::TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + rustls_openssl::cipher_suite::TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + rustls_openssl::cipher_suite::TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + ]); + } + + // 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, + ]; + Arc::new(CryptoProvider { - // Limit to only the subset of ciphers that are FIPS compatible - cipher_suites: vec![ - rustls_openssl::cipher_suite::TLS13_AES_256_GCM_SHA384, - rustls_openssl::cipher_suite::TLS13_AES_128_GCM_SHA256, - ], + cipher_suites, + kx_groups, ..rustls_openssl::default_provider() }) } diff --git a/src/tls/mock.rs b/src/tls/mock.rs index 16a663336c..f52a0e704e 100644 --- a/src/tls/mock.rs +++ b/src/tls/mock.rs @@ -22,7 +22,7 @@ use std::net::IpAddr; use std::sync::Arc; use std::time::{Duration, SystemTime}; -use crate::tls::TLS_VERSIONS; +use crate::tls::tls_versions; use rustls::ServerConfig; use super::{ServerCertProvider, TlsError, WorkloadCertificate}; @@ -184,7 +184,7 @@ impl MockServerCertProvider { impl ServerCertProvider for MockServerCertProvider { async fn fetch_cert(&mut self) -> Result, TlsError> { let mut sc = ServerConfig::builder_with_provider(crate::tls::lib::provider()) - .with_protocol_versions(TLS_VERSIONS) + .with_protocol_versions(tls_versions()) .expect("server config must be valid") .with_no_client_auth() .with_single_cert(