diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ca12abc..a97bcc2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,10 +24,10 @@ jobs: - name: Cache build artifacts uses: Swatinem/rust-cache@v2 - name: cargo test - run: cargo test --all-features + run: cargo test # https://github.com/rust-lang/cargo/issues/6669 - name: cargo test --doc - run: cargo test --doc --all-features + run: cargo test --doc lint: name: Lint runs-on: ubuntu-latest @@ -44,4 +44,23 @@ jobs: - name: cargo fmt (check) run: cargo fmt -- --check -l - name: cargo clippy (warnings) - run: cargo clippy --all-targets --all-features -- -D warnings + run: cargo clippy --all-targets -- -D warnings + + test-fips: + name: Test using FIPS openssl + runs-on: ubuntu-latest + container: + image: registry.access.redhat.com/ubi8/ubi:latest + steps: + - name: Install dependencies + run: dnf install -y gcc openssl-devel openssl + - name: Check out repository + uses: actions/checkout@v4 + - name: Install toolchain + uses: dtolnay/rust-toolchain@v1 + with: + toolchain: stable + - name: Cache build artifacts + uses: Swatinem/rust-cache@v2 + - name: Run cargo test --features fips + run: cargo test --features fips diff --git a/Cargo.toml b/Cargo.toml index 35b4a80..3eb14cb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,8 +17,8 @@ rustls = { version = "0.23.0", default-features = false } rustls-webpki = { version = "0.102.2", default-features = false } [features] -default = ["tls12", "x25519"] -x25519 = [] +default = ["tls12"] +fips = [] tls12 = ["rustls/tls12", "foreign-types-shared"] [dev-dependencies] diff --git a/README.md b/README.md index dfe32db..8f47c14 100644 --- a/README.md +++ b/README.md @@ -1,46 +1,7 @@ # rustls-openssl A [rustls Crypto Provider](https://docs.rs/rustls/latest/rustls/crypto/struct.CryptoProvider.html) that uses OpenSSL for cryptographic operations. -## Status -**Early in development.** - -## Usage -The main entry points are the `rustls_openssl::default_provider` and `rustls_openssl::custom_provider` functions. -See the [rustls documentation]((https://docs.rs/rustls/latest/rustls/crypto/struct.CryptoProvider.html)) for how to use them. - -## Supported Ciphers - -Supported cipher suites are listed below, in descending order of preference. -If OpenSSL is compiled with the `OPENSSL_NO_CHACHA` option, the ChaCha20-Poly1305 ciphers will not be available. - -### TLS 1.3 - -The following cipher suites are supported for TLS 1.3. These support QUIC. - -``` -TLS13_AES_256_GCM_SHA384 -TLS13_AES_128_GCM_SHA256 -TLS13_CHACHA20_POLY1305_SHA256 -``` - -### TLS 1.2 -*Requires the `tls12` feature, which is a default feature.* - -``` -TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 -TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 -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 -``` -## Supported Key Exchanges - -Key exchanges, in descending order ofpreference: - -``` -SECP384R1 -SECP256R1 -X25519 // Requires the `x25519` feature -``` +[Documentation](https://docs.rs/rustls-openssl) +## Status +**In development.** \ No newline at end of file diff --git a/build.rs b/build.rs index 87ba935..0136243 100644 --- a/build.rs +++ b/build.rs @@ -6,6 +6,7 @@ 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)"); // Determine whether to work around https://github.com/openssl/openssl/issues/23448 // according to the OpenSSL version println!("cargo:rustc-check-cfg=cfg(bugged_add_hkdf_info)"); @@ -15,6 +16,10 @@ fn main() { if (0x3_00_00_00_0..0x3_04_00_00_0).contains(&version) { println!("cargo:rustc-cfg=bugged_add_hkdf_info"); } + + if version < 0x3_00_00_00_0 { + println!("cargo:rustc-cfg=fips_module"); + } } // Enable the `chacha` cfg if the `OPENSSL_NO_CHACHA` OpenSSL config is not set. diff --git a/src/aead.rs b/src/aead.rs index c9104b4..8f09ddf 100644 --- a/src/aead.rs +++ b/src/aead.rs @@ -7,7 +7,7 @@ use rustls::Error; pub(crate) enum Algorithm { Aes128Gcm, Aes256Gcm, - #[cfg(chacha)] + #[cfg(all(chacha, not(feature = "fips")))] ChaCha20Poly1305, } @@ -19,7 +19,7 @@ impl Algorithm { match self { Self::Aes128Gcm => Cipher::aes_128_gcm(), Self::Aes256Gcm => Cipher::aes_256_gcm(), - #[cfg(chacha)] + #[cfg(all(chacha, not(feature = "fips")))] Self::ChaCha20Poly1305 => Cipher::chacha20_poly1305(), } } diff --git a/src/hash.rs b/src/hash.rs index fd17a6d..9410ca6 100644 --- a/src/hash.rs +++ b/src/hash.rs @@ -62,6 +62,10 @@ impl rustls::crypto::hash::Hash for Algorithm { Algorithm::SHA384 => rustls::crypto::hash::HashAlgorithm::SHA384, } } + + fn fips(&self) -> bool { + crate::fips() + } } impl Context { diff --git a/src/hkdf.rs b/src/hkdf.rs index b3e631f..045815e 100644 --- a/src/hkdf.rs +++ b/src/hkdf.rs @@ -71,6 +71,10 @@ impl RustlsHkdf for Hkdf { fn hmac_sign(&self, key: &OkmBlock, message: &[u8]) -> Tag { Hmac(self.0).with_key(key.as_ref()).sign(&[message]) } + + fn fips(&self) -> bool { + crate::fips() + } } impl RustlsHkdfExpander for HkdfExpander { diff --git a/src/hmac.rs b/src/hmac.rs index ac446be..059da55 100644 --- a/src/hmac.rs +++ b/src/hmac.rs @@ -21,6 +21,10 @@ impl rustls::crypto::hmac::Hmac for Hmac { fn hash_output_len(&self) -> usize { self.0.output_len() } + + fn fips(&self) -> bool { + crate::fips() + } } impl Key for HmacKey { diff --git a/src/kx.rs b/src/kx.rs index 3931064..6390f61 100644 --- a/src/kx.rs +++ b/src/kx.rs @@ -3,22 +3,22 @@ use openssl::derive::Deriver; use openssl::ec::{EcGroup, EcKey, EcPoint, PointConversionForm}; use openssl::error::ErrorStack; use openssl::nid::Nid; -#[cfg(feature = "x25519")] +#[cfg(not(feature = "fips"))] use openssl::pkey::Id; use openssl::pkey::{PKey, Private, Public}; use rustls::crypto::{ActiveKeyExchange, SharedSecret, SupportedKxGroup}; use rustls::{Error, NamedGroup}; -/// Supported `KeyExchange` groups. -/// ```ignore -/// SECP384R1 -/// SECP256R1 -/// X25519 // Enabled with the `x25519` feature -/// ``` +/// [Supported KeyExchange groups](SupportedKxGroup). +/// * [SECP384R1] +/// * [SECP256R1] +/// * [X25519] +/// +/// If the `fips` feature is enabled, only [SECP384R1] and [SECP256R1] are available. pub const ALL_KX_GROUPS: &[&dyn SupportedKxGroup] = &[ SECP256R1, SECP384R1, - #[cfg(feature = "x25519")] + #[cfg(not(feature = "fips"))] X25519, ]; @@ -36,26 +36,27 @@ struct EcKeyExchange { pub_key: Vec, } -#[cfg(feature = "x25519")] +#[cfg(not(feature = "fips"))] /// KXGroup for X25519 #[derive(Debug)] struct X25519KxGroup {} -#[cfg(feature = "x25519")] +#[cfg(not(feature = "fips"))] #[derive(Debug)] struct X25519KeyExchange { private_key: PKey, public_key: Vec, } -#[cfg(feature = "x25519")] +#[cfg(not(feature = "fips"))] +/// X25519 key exchange group as registered with [IANA](https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-8). pub const X25519: &dyn SupportedKxGroup = &X25519KxGroup {}; - +/// secp256r1 key exchange group as registered with [IANA](https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-8) pub const SECP256R1: &dyn SupportedKxGroup = &EcKxGroup { name: NamedGroup::secp256r1, nid: Nid::X9_62_PRIME256V1, }; - +/// secp384r1 key exchange group as registered with [IANA](https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-8) pub const SECP384R1: &dyn SupportedKxGroup = &EcKxGroup { name: NamedGroup::secp384r1, nid: Nid::SECP384R1, @@ -85,6 +86,10 @@ impl SupportedKxGroup for EcKxGroup { fn name(&self) -> NamedGroup { self.name } + + fn fips(&self) -> bool { + crate::fips() + } } impl EcKeyExchange { @@ -120,7 +125,7 @@ impl ActiveKeyExchange for EcKeyExchange { } } -#[cfg(feature = "x25519")] +#[cfg(not(feature = "fips"))] impl SupportedKxGroup for X25519KxGroup { fn start(&self) -> Result, Error> { PKey::generate_x25519() @@ -139,7 +144,7 @@ impl SupportedKxGroup for X25519KxGroup { } } -#[cfg(feature = "x25519")] +#[cfg(not(feature = "fips"))] impl ActiveKeyExchange for X25519KeyExchange { fn complete(self: Box, peer_pub_key: &[u8]) -> Result { PKey::public_key_from_raw_bytes(peer_pub_key, Id::X25519) diff --git a/src/lib.rs b/src/lib.rs index bb2040f..7db622e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,39 +1,39 @@ -//! # OpenSSL Provider for Rustls +//! # rustls-openssl //! -//! A Rustls crypto provider that uses `OpenSSL` for crypto. +//! A [rustls crypto provider](https://docs.rs/rustls/latest/rustls/crypto/struct.CryptoProvider.html) that uses OpenSSL for crypto. //! //! ## Supported Ciphers //! -//! Supported cipher suites are listed below, ordered by preference. IE: The default configuration prioritizes `TLS13_AES_256_GCM_SHA384` over `TLS13_AES_128_GCM_SHA256`. -//! If OpenSSL is compiled with the `OPENSSL_NO_CHACHA` option, the ChaCha20-Poly1305 ciphers will not be available. +//! 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. //! //! ### TLS 1.3 //! -//! ```ignore -//! TLS13_AES_256_GCM_SHA384 -//! TLS13_AES_128_GCM_SHA256 -//! TLS13_CHACHA20_POLY1305_SHA256 -//! ``` +//! * TLS13_AES_256_GCM_SHA384 +//! * TLS13_AES_128_GCM_SHA256 +//! * TLS13_CHACHA20_POLY1305_SHA256 //! //! ### TLS 1.2 //! -//! ```ignore -//! TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 -//! TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 -//! 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 -//! ``` +//! * TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 +//! * TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 +//! * 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 +//! //! ## Supported Key Exchanges //! -//! Key exchanges are listed below, ordered by preference. IE: `SECP384R1` is preferred over `SECP256R1`. +//! In descending order of preference: //! -//! ```ignore -//! SECP384R1 -//! SECP256R1 -//! X25519 // Requires the `x25519` feature -//! ``` +//! * SECP384R1 +//! * SECP256R1 +//! * X25519 +//! +//! If the `fips` feature is enabled then X25519 will not be available. //! //! ## Usage //! @@ -45,63 +45,38 @@ //! rustls_openssl = "0.1.0" //! ``` //! -//! ### Default Configuration +//! ### Configuration //! -//! Use `default_provider()` for a `ClientConfig` that utilizes the default cipher suites and key exchange groups listed above: +//! Use [default_provider()] to create a provider using cipher suites and key exchange groups listed above. +//! Use [custom_provider()] to specify custom cipher suites and key exchange groups. //! -//! ```rust -//! use rustls::{ClientConfig, RootCertStore}; -//! use rustls_openssl::default_provider; -//! use std::sync::Arc; -//! use webpki_roots; +//! # Features +//! - `tls12`: Enables TLS 1.2 cipher suites. Enabled by default. +//! - `fips`: Enabling this feature removes non-FIPS-approved cipher suites and key exchanges. Disabled by default. //! -//! let mut root_store = RootCertStore { -//! roots: webpki_roots::TLS_SERVER_ROOTS.iter().cloned().collect(), -//! }; +//! # FIPS //! -//! let mut config = -//! ClientConfig::builder_with_provider(Arc::new(default_provider())) -//! .with_safe_default_protocol_versions() -//! .unwrap() -//! .with_root_certificates(root_store) -//! .with_no_client_auth(); -//! ``` +//! To use rustls with OpenSSL in FIPS mode, perform the following actions. //! -//! ### Custom Configuration +//! ## 1. Enable the FIPS feature //! -//! To modify or change the order of negotiated cipher suites for `ClientConfig`, use `custom_provider()`. +//! This removes non-FIPS-approved cipher suites and key exchanges. //! -//! ```rust -//! use rustls::{ClientConfig, RootCertStore}; -//! use rustls_openssl::custom_provider; -//! use rustls_openssl::cipher_suite::TLS13_AES_128_GCM_SHA256; -//! use rustls_openssl::kx_group::SECP256R1; -//! use std::sync::Arc; -//! use webpki_roots; +//! ## 2. Specify `require_ems` when constructing [rustls::ClientConfig] or [rustls::ServerConfig] //! -//! let mut root_store = RootCertStore { -//! roots: webpki_roots::TLS_SERVER_ROOTS.iter().cloned().collect(), -//! }; -//! -//! // Set custom config of cipher suites that have been imported from rustls_openssl. -//! let cipher_suites = vec![TLS13_AES_128_GCM_SHA256]; -//! let kx_group = vec![SECP256R1]; +//! See [rustls documentation](https://docs.rs/rustls/latest/rustls/client/struct.ClientConfig.html#structfield.require_ems) for rationale. //! -//! let mut config = -//! ClientConfig::builder_with_provider(Arc::new(custom_provider( -//! Some(cipher_suites), Some(kx_group)))) -//! .with_safe_default_protocol_versions() -//! .unwrap() -//! .with_root_certificates(root_store) -//! .with_no_client_auth(); -//! ``` +//! ## 3. Enable FIPS mode for OpenSSL //! -//! # Features -//! The following non-default features are available: -//! - `x25519`: Enables X25519 key exchange group. +//! [enable_fips()] can be used to enable FIPS mode for OpenSSL without users needing to depend on the [openssl] crate directly. +//! This calls [openssl::fips::enable](https://github.com/sfackler/rust-openssl/blob/538a5cb737e8d83085553cac01643820dc7ff205/openssl/src/fips.rs#L12), panicking if that fails. + +//! ## 4. 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). +#![warn(missing_docs)] use openssl::rand::rand_bytes; -use rustls::crypto::{CryptoProvider, GetRandomFailed, SecureRandom, SupportedKxGroup}; +use rustls::crypto::{CryptoProvider, GetRandomFailed, SupportedKxGroup}; use rustls::SupportedCipherSuite; mod aead; @@ -121,34 +96,33 @@ mod tls13; mod verify; pub mod cipher_suite { - //! All supported cipher suites. + //! Supported cipher suites. #[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, }; - #[cfg(all(feature = "tls12", chacha))] + #[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, }; - #[cfg(chacha)] + #[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 kx::ALL_KX_GROUPS; -/// All supported key exchange groups are exported via the `kx_group` module. pub mod kx_group { - #[cfg(feature = "x25519")] + //! Supported key exchange groups. + #[cfg(not(feature = "fips"))] pub use super::kx::X25519; pub use super::kx::{SECP256R1, SECP384R1}; } - +pub use signer::KeyProvider; pub use verify::SUPPORTED_SIG_ALGS; -/// `default_provider` returns a `CryptoProvider` using default and cipher suites. -/// For cipher suites see[`DEFAULT_CIPHER_SUITES`]. +/// Returns an OpenSSL-based [CryptoProvider] using all available cipher suites ([ALL_CIPHER_SUITES]) and key exchange groups ([ALL_KX_GROUPS]). /// /// Sample usage: /// ```rust @@ -171,25 +145,20 @@ pub use verify::SUPPORTED_SIG_ALGS; /// ``` pub fn default_provider() -> CryptoProvider { CryptoProvider { - cipher_suites: DEFAULT_CIPHER_SUITES.to_vec(), + cipher_suites: ALL_CIPHER_SUITES.to_vec(), kx_groups: ALL_KX_GROUPS.to_vec(), signature_verification_algorithms: SUPPORTED_SIG_ALGS, - secure_random: &Provider, - key_provider: &Provider, + secure_random: &SecureRandom, + key_provider: &KeyProvider, } } -/// Create a `CryptoProvider` with specific cipher suites and key exchange groups during setup. +/// Create a [CryptoProvider] with specific cipher suites and key exchange groups /// -/// `provided_cipher_suites` takes in an optional `Vec<>` of `SupportedCipherSuites` -/// The supplied arguments for `provided_cipher_suite` will be used when when negotiating the TLS cipher suite; -/// and should be placed in preference order, where the first element has highest priority. -/// If `None` or an empty `Vec<>` is provided the [`DEFAULT_CIPHER_SUITES`] will be used instead. +/// 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. /// -/// `provided_kx_group` takes in an optional `Vec<>` of `SupportedKxGroup` -/// The supplied arguments for `provided_kx_group` will be used when when negotiating the TLS key exchange; -/// and should be placed in preference order, where the first element has highest priority. -/// If `None` or an empty `Vec<>` is provided the default will be used instead. +/// If the `fips` feature is enabled then fips mode will be enabled for OpenSSL, and this function will panic if this fails. /// /// Sample usage: /// ```rust @@ -210,7 +179,7 @@ pub fn default_provider() -> CryptoProvider { /// /// let mut config = /// ClientConfig::builder_with_provider(Arc::new(custom_provider( -/// Some(cipher_suites), Some(kx_group)))) +/// cipher_suites, kx_group))) /// .with_safe_default_protocol_versions() /// .unwrap() /// .with_root_certificates(root_store) @@ -219,71 +188,78 @@ pub fn default_provider() -> CryptoProvider { /// /// ``` pub fn custom_provider( - provided_cipher_suites: Option>, - provided_kx_group: Option>, + cipher_suites: Vec, + kx_groups: Vec<&'static dyn SupportedKxGroup>, ) -> CryptoProvider { - let cipher_suites = match provided_cipher_suites { - Some(suites) if !suites.is_empty() => suites, - _ => DEFAULT_CIPHER_SUITES.to_vec(), - }; - - let kx_group = match provided_kx_group { - Some(groups) if !groups.is_empty() => groups, - _ => ALL_KX_GROUPS.to_vec(), - }; - CryptoProvider { cipher_suites, - kx_groups: kx_group, + kx_groups, signature_verification_algorithms: SUPPORTED_SIG_ALGS, - secure_random: &Provider, - key_provider: &Provider, + secure_random: &SecureRandom, + key_provider: &KeyProvider, } } -/// List of supported cipher suites in a preference order. -/// The first element has highest priority when negotiating cipher suites. -/// ```ignore -/// // TLS 1.3 suites -/// TLS13_AES_256_GCM_SHA384 -/// TLS13_AES_128_GCM_SHA256 -/// TLS13_CHACHA20_POLY1305_SHA256 // Enabled with the `chacha` feature -/// // TLS 1.2 suites, enabled with the `tls12` feature -/// TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 -/// TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 -/// TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 // Enabled with the `chacha` feature -/// TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 -/// TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 -/// TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 // Enabled with the `chacha` feature -/// ``` -pub static DEFAULT_CIPHER_SUITES: &[SupportedCipherSuite] = ALL_CIPHER_SUITES; - +/// All supported cipher suites in descending order of preference: +/// * [tls13::TLS13_AES_256_GCM_SHA384] +/// * TLS13_AES_128_GCM_SHA256 +/// * TLS13_CHACHA20_POLY1305_SHA256 +/// * TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 +/// * TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 +/// * 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 +/// +/// 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. +/// 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(chacha)] + #[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))] + #[cfg(all(feature = "tls12", chacha, not(feature = "fips")))] 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))] + #[cfg(all(feature = "tls12", chacha, not(feature = "fips")))] tls12::TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, ]; -/// Rustls Openssl crypto provider. -/// Implements `SecureRandom` and `KeyProvider` traits. +/// A struct that implements [rustls::crypto::SecureRandom]. #[derive(Debug)] -pub struct Provider; +pub struct SecureRandom; -impl SecureRandom for Provider { +impl rustls::crypto::SecureRandom for SecureRandom { fn fill(&self, buf: &mut [u8]) -> Result<(), GetRandomFailed> { rand_bytes(buf).map_err(|_| GetRandomFailed) } + + fn fips(&self) -> bool { + fips() + } +} + +/// Returns `true` if OpenSSL is running in FIPS mode. +#[cfg(fips_module)] +pub(crate) fn fips() -> bool { + openssl::fips::enabled() +} +#[cfg(not(fips_module))] +pub(crate) fn fips() -> bool { + false +} + +/// Enable FIPS mode for OpenSSL. +/// +/// Panics if FIPS mode cannot be enabled. +#[cfg(all(fips_module, feature = "fips"))] +pub fn enable_fips() { + openssl::fips::enable(true).expect("Failed to enable FIPS mode."); } diff --git a/src/prf.rs b/src/prf.rs index 903bc66..a42f8ed 100644 --- a/src/prf.rs +++ b/src/prf.rs @@ -43,6 +43,10 @@ impl rustls::crypto::tls12::Prf for Prf { }) .expect("HDKF-Extract failed"); } + + fn fips(&self) -> bool { + crate::fips() + } } // rust-openssl doesn't expose tls1_prf function yet: https://github.com/sfackler/rust-openssl/pull/2329 diff --git a/src/quic.rs b/src/quic.rs index c16cc36..86d6483 100644 --- a/src/quic.rs +++ b/src/quic.rs @@ -26,7 +26,7 @@ struct PacketKey { pub(crate) enum HeaderProtectionAlgorithm { Aes128, Aes256, - #[cfg(chacha)] + #[cfg(all(chacha, not(feature = "fips")))] ChaCha20, } @@ -59,6 +59,10 @@ impl quic::Algorithm for KeyBuilder { fn aead_key_len(&self) -> usize { self.packet_algo.key_size() } + + fn fips(&self) -> bool { + crate::fips() + } } impl quic::PacketKey for PacketKey { @@ -178,7 +182,7 @@ impl HeaderProtectionAlgorithm { match self { HeaderProtectionAlgorithm::Aes128 => Cipher::aes_128_ecb(), HeaderProtectionAlgorithm::Aes256 => Cipher::aes_256_ecb(), - #[cfg(chacha)] + #[cfg(all(chacha, not(feature = "fips")))] HeaderProtectionAlgorithm::ChaCha20 => Cipher::chacha20(), } } @@ -194,7 +198,7 @@ impl HeaderProtectionKey { .map_err(|e| Error::General(format!("OpenSSL error: {e}")))?; mask.copy_from_slice(&block[..5]); } - #[cfg(chacha)] + #[cfg(all(chacha, not(feature = "fips")))] // https://datatracker.ietf.org/doc/html/rfc9001#section-5.4.4 HeaderProtectionAlgorithm::ChaCha20 => { let block = encrypt( @@ -280,7 +284,7 @@ mod test { assert_eq!(server_packet[..], expected_server_packet[..]); } - #[cfg(chacha)] + #[cfg(all(chacha, not(feature = "fips")))] #[test] fn test_short_packet_length() { use rustls::crypto::cipher::AeadKey; diff --git a/src/signer.rs b/src/signer.rs index 81eee3e..94aa40a 100644 --- a/src/signer.rs +++ b/src/signer.rs @@ -1,14 +1,16 @@ -use crate::Provider; use openssl::hash::MessageDigest; use openssl::pkey::{Id, Private}; use openssl::rsa::Padding; use openssl::sign::RsaPssSaltlen; -use rustls::crypto::KeyProvider; use rustls::pki_types::PrivateKeyDer; use rustls::sign::SigningKey; use rustls::{Error, SignatureAlgorithm, SignatureScheme}; use std::sync::Arc; +/// A struct that implements [rustls::crypto::KeyProvider]. +#[derive(Debug)] +pub struct KeyProvider; + /// RSA schemes in descending order of preference pub(crate) static RSA_SCHEMES: &[SignatureScheme] = &[ SignatureScheme::RSA_PSS_SHA512, @@ -89,7 +91,7 @@ impl PKey { } } -impl KeyProvider for Provider { +impl rustls::crypto::KeyProvider for KeyProvider { fn load_private_key( &self, key_der: PrivateKeyDer<'static>, @@ -98,6 +100,10 @@ impl KeyProvider for Provider { .map_err(|e| Error::General(format!("OpenSSL error: {e}")))?; Ok(Arc::new(PKey(Arc::new(pkey)))) } + + fn fips(&self) -> bool { + crate::fips() + } } impl SigningKey for PKey { diff --git a/src/tls12.rs b/src/tls12.rs index 31b9f5f..91cb8ea 100644 --- a/src/tls12.rs +++ b/src/tls12.rs @@ -2,12 +2,10 @@ use crate::aead::{self, TAG_LEN}; use crate::hash::{SHA256, SHA384}; use crate::prf::Prf; use crate::signer::{ECDSA_SCHEMES, RSA_SCHEMES}; -#[cfg(chacha)] -use rustls::crypto::cipher::NONCE_LEN; use rustls::crypto::cipher::{ make_tls12_aad, AeadKey, InboundOpaqueMessage, InboundPlainMessage, Iv, KeyBlockShape, MessageDecrypter, MessageEncrypter, Nonce, OutboundOpaqueMessage, OutboundPlainMessage, - PrefixedPayload, Tls12AeadAlgorithm, UnsupportedOperationError, + PrefixedPayload, Tls12AeadAlgorithm, UnsupportedOperationError, NONCE_LEN, }; use rustls::{ crypto::KeyExchangeAlgorithm, CipherSuite, CipherSuiteCommon, SupportedCipherSuite, @@ -15,12 +13,11 @@ use rustls::{ }; use rustls::{ConnectionTrafficSecrets, Error}; -const GCM_FULL_NONCE_LENGTH: usize = 12; const GCM_EXPLICIT_NONCE_LENGTH: usize = 8; const GCM_IMPLICIT_NONCE_LENGTH: usize = 4; /// The TLS1.2 ciphersuite `TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256`. -#[cfg(chacha)] +#[cfg(all(chacha, not(feature = "fips")))] pub static TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256: SupportedCipherSuite = SupportedCipherSuite::Tls12(&Tls12CipherSuite { common: CipherSuiteCommon { @@ -35,7 +32,7 @@ pub static TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256: SupportedCipherSuite = }); /// The TLS1.2 ciphersuite `TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256` -#[cfg(chacha)] +#[cfg(all(chacha, not(feature = "fips")))] pub static TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256: SupportedCipherSuite = SupportedCipherSuite::Tls12(&Tls12CipherSuite { common: CipherSuiteCommon { @@ -117,7 +114,7 @@ struct AesGcmEncrypter { full_iv: Iv, } -#[cfg(chacha)] +#[cfg(all(chacha, not(feature = "fips")))] pub(crate) struct ChaCha20Poly1305Crypter { key: AeadKey, iv: Iv, @@ -127,7 +124,7 @@ impl Tls12AeadAlgorithm for aead::Algorithm { fn encrypter(&self, key: AeadKey, iv: &[u8], extra: &[u8]) -> Box { match self { aead::Algorithm::Aes128Gcm | aead::Algorithm::Aes256Gcm => { - let mut full_iv = [0u8; GCM_FULL_NONCE_LENGTH]; + let mut full_iv = [0u8; NONCE_LEN]; full_iv[..GCM_IMPLICIT_NONCE_LENGTH].copy_from_slice(iv); full_iv[GCM_IMPLICIT_NONCE_LENGTH..].copy_from_slice(extra); Box::new(AesGcmEncrypter { @@ -136,7 +133,7 @@ impl Tls12AeadAlgorithm for aead::Algorithm { full_iv: Iv::new(full_iv), }) } - #[cfg(chacha)] + #[cfg(all(chacha, not(feature = "fips")))] aead::Algorithm::ChaCha20Poly1305 => Box::new(ChaCha20Poly1305Crypter { key, iv: Iv::copy(iv), @@ -156,7 +153,7 @@ impl Tls12AeadAlgorithm for aead::Algorithm { implicit_iv, }) } - #[cfg(chacha)] + #[cfg(all(chacha, not(feature = "fips")))] aead::Algorithm::ChaCha20Poly1305 => Box::new(ChaCha20Poly1305Crypter { key, iv: Iv::copy(iv), @@ -171,7 +168,7 @@ impl Tls12AeadAlgorithm for aead::Algorithm { fixed_iv_len: GCM_IMPLICIT_NONCE_LENGTH, explicit_nonce_len: GCM_EXPLICIT_NONCE_LENGTH, }, - #[cfg(chacha)] + #[cfg(all(chacha, not(feature = "fips")))] aead::Algorithm::ChaCha20Poly1305 => KeyBlockShape { enc_key_len: self.key_size(), fixed_iv_len: NONCE_LEN, @@ -188,7 +185,7 @@ impl Tls12AeadAlgorithm for aead::Algorithm { ) -> Result { match self { aead::Algorithm::Aes128Gcm => { - let mut gcm_iv = [0; GCM_FULL_NONCE_LENGTH]; + let mut gcm_iv = [0; NONCE_LEN]; gcm_iv[..GCM_IMPLICIT_NONCE_LENGTH].copy_from_slice(iv); gcm_iv[GCM_IMPLICIT_NONCE_LENGTH..].copy_from_slice(explicit); Ok(ConnectionTrafficSecrets::Aes128Gcm { @@ -197,7 +194,7 @@ impl Tls12AeadAlgorithm for aead::Algorithm { }) } aead::Algorithm::Aes256Gcm => { - let mut gcm_iv = [0; GCM_FULL_NONCE_LENGTH]; + let mut gcm_iv = [0; NONCE_LEN]; gcm_iv[..GCM_IMPLICIT_NONCE_LENGTH].copy_from_slice(iv); gcm_iv[GCM_IMPLICIT_NONCE_LENGTH..].copy_from_slice(explicit); Ok(ConnectionTrafficSecrets::Aes256Gcm { @@ -205,13 +202,21 @@ impl Tls12AeadAlgorithm for aead::Algorithm { iv: Iv::new(gcm_iv), }) } - #[cfg(chacha)] + #[cfg(all(chacha, not(feature = "fips")))] aead::Algorithm::ChaCha20Poly1305 => Ok(ConnectionTrafficSecrets::Chacha20Poly1305 { key, iv: Iv::new(iv[..].try_into().map_err(|_| UnsupportedOperationError)?), }), } } + + fn fips(&self) -> bool { + match self { + aead::Algorithm::Aes128Gcm | aead::Algorithm::Aes256Gcm => crate::fips(), + #[cfg(all(chacha, not(feature = "fips")))] + aead::Algorithm::ChaCha20Poly1305 => false, + } + } } impl MessageEncrypter for AesGcmEncrypter { @@ -257,7 +262,7 @@ impl MessageDecrypter for AesGcmDecrypter { return Err(Error::DecryptError); } - let mut nonce = [0u8; GCM_FULL_NONCE_LENGTH]; + let mut nonce = [0u8; NONCE_LEN]; nonce[..GCM_IMPLICIT_NONCE_LENGTH].copy_from_slice(&self.implicit_iv); nonce[GCM_IMPLICIT_NONCE_LENGTH..].copy_from_slice(&payload[..GCM_EXPLICIT_NONCE_LENGTH]); @@ -285,7 +290,7 @@ impl MessageDecrypter for AesGcmDecrypter { } } -#[cfg(chacha)] +#[cfg(all(chacha, not(feature = "fips")))] impl MessageEncrypter for ChaCha20Poly1305Crypter { fn encrypt( &mut self, @@ -314,7 +319,7 @@ impl MessageEncrypter for ChaCha20Poly1305Crypter { } } -#[cfg(chacha)] +#[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 3e8fbba..a89538e 100644 --- a/src/tls13.rs +++ b/src/tls13.rs @@ -13,11 +13,11 @@ use rustls::{ }; /// The TLS1.3 ciphersuite `TLS_CHACHA20_POLY1305_SHA256` -#[cfg(chacha)] +#[cfg(all(chacha, not(feature = "fips")))] pub static TLS13_CHACHA20_POLY1305_SHA256: SupportedCipherSuite = SupportedCipherSuite::Tls13(TLS13_CHACHA20_POLY1305_SHA256_INTERNAL); -#[cfg(chacha)] +#[cfg(all(chacha, not(feature = "fips")))] pub static TLS13_CHACHA20_POLY1305_SHA256_INTERNAL: &Tls13CipherSuite = &Tls13CipherSuite { common: CipherSuiteCommon { suite: CipherSuite::TLS13_CHACHA20_POLY1305_SHA256, @@ -113,12 +113,16 @@ impl Tls13AeadAlgorithm for aead::Algorithm { Ok(match self { aead::Algorithm::Aes128Gcm => ConnectionTrafficSecrets::Aes128Gcm { key, iv }, aead::Algorithm::Aes256Gcm => ConnectionTrafficSecrets::Aes256Gcm { key, iv }, - #[cfg(chacha)] + #[cfg(all(chacha, not(feature = "fips")))] aead::Algorithm::ChaCha20Poly1305 => { ConnectionTrafficSecrets::Chacha20Poly1305 { key, iv } } }) } + + fn fips(&self) -> bool { + crate::fips() + } } impl MessageEncrypter for Tls13Crypter { diff --git a/src/verify.rs b/src/verify.rs index 2ed5ddd..8dc3630 100644 --- a/src/verify.rs +++ b/src/verify.rs @@ -15,6 +15,7 @@ use rustls::{ }; use webpki::alg_id; +/// A [WebPkiSupportedAlgorithms] value defining the supported signature algorithms. pub static SUPPORTED_SIG_ALGS: WebPkiSupportedAlgorithms = WebPkiSupportedAlgorithms { all: &[ ECDSA_P256_SHA256, @@ -24,6 +25,7 @@ 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, @@ -43,6 +45,7 @@ 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]), @@ -53,48 +56,43 @@ pub static SUPPORTED_SIG_ALGS: WebPkiSupportedAlgorithms = WebPkiSupportedAlgori ], }; -/// RSA PKCS#1 1.5 signatures using SHA-256 for keys of 2048-8192 bits. +/// RSA PKCS#1 1.5 signatures using SHA-256. pub(crate) static RSA_PKCS1_SHA256: &dyn SignatureVerificationAlgorithm = &OpenSslAlgorithm { public_key_alg_id: alg_id::RSA_ENCRYPTION, signature_alg_id: alg_id::RSA_PKCS1_SHA256, }; -/// RSA PKCS#1 1.5 signatures using SHA-384 for keys of 2048-8192 bits. +/// RSA PKCS#1 1.5 signatures using SHA-384. pub(crate) static RSA_PKCS1_SHA384: &dyn SignatureVerificationAlgorithm = &OpenSslAlgorithm { public_key_alg_id: alg_id::RSA_ENCRYPTION, signature_alg_id: alg_id::RSA_PKCS1_SHA384, }; -/// RSA PKCS#1 1.5 signatures using SHA-512 for keys of 2048-8192 bits. +/// RSA PKCS#1 1.5 signatures using SHA-512. pub(crate) static RSA_PKCS1_SHA512: &dyn SignatureVerificationAlgorithm = &OpenSslAlgorithm { public_key_alg_id: alg_id::RSA_ENCRYPTION, signature_alg_id: alg_id::RSA_PKCS1_SHA512, }; -/// RSA PSS signatures using SHA-256 -/// -/// [RFC 4055 Section 1.2]: https://tools.ietf.org/html/rfc4055#section-1.2 +/// RSA PSS signatures using SHA-256. pub(crate) static RSA_PSS_SHA256: &dyn SignatureVerificationAlgorithm = &OpenSslAlgorithm { public_key_alg_id: alg_id::RSA_ENCRYPTION, signature_alg_id: alg_id::RSA_PSS_SHA256, }; -/// RSA PSS signatures using SHA-384 -/// -/// [RFC 4055 Section 1.2]: https://tools.ietf.org/html/rfc4055#section-1.2 +/// RSA PSS signatures using SHA-384. pub(crate) static RSA_PSS_SHA384: &dyn SignatureVerificationAlgorithm = &OpenSslAlgorithm { public_key_alg_id: alg_id::RSA_ENCRYPTION, signature_alg_id: alg_id::RSA_PSS_SHA384, }; -/// RSA PSS signatures using SHA-512 -/// -/// [RFC 4055 Section 1.2]: https://tools.ietf.org/html/rfc4055#section-1.2 +/// RSA PSS signatures using SHA-512. pub(crate) static RSA_PSS_SHA512: &dyn SignatureVerificationAlgorithm = &OpenSslAlgorithm { public_key_alg_id: alg_id::RSA_ENCRYPTION, 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 { public_key_alg_id: alg_id::ED25519, @@ -284,7 +282,14 @@ impl SignatureVerificationAlgorithm for OpenSslAlgorithm { Verifier::new_without_digest(&pkey) .and_then(|mut verifier| verifier.verify_oneshot(signature, message)) } - .map_err(|_| InvalidSignature) + .map_err(|e| { + std::dbg!(e); + InvalidSignature + }) .and_then(|valid| if valid { Ok(()) } else { Err(InvalidSignature) }) } + + fn fips(&self) -> bool { + crate::fips() + } } diff --git a/tests/it.rs b/tests/it.rs index c385ce3..46cb770 100644 --- a/tests/it.rs +++ b/tests/it.rs @@ -2,6 +2,7 @@ use openssl::bn::BigNumContext; use openssl::ec::{EcGroup, EcKey, PointConversionForm}; use openssl::nid::Nid; +#[cfg(not(feature = "fips"))] use openssl::pkey::PKey; use openssl::rsa::Rsa; use rstest::rstest; @@ -74,6 +75,11 @@ fn test_with_config( suite: SupportedCipherSuite, group: &'static dyn SupportedKxGroup, ) -> CipherSuite { + #[cfg(feature = "fips")] + { + rustls_openssl::enable_fips(); + } + let cipher_suites = vec![suite]; let kx_group = vec![group]; @@ -94,15 +100,22 @@ fn test_with_config( root_store.add_parsable_certificates(certs); - let config = rustls::ClientConfig::builder_with_provider(Arc::new(custom_provider( - Some(cipher_suites), - Some(kx_group), + #[allow(unused_mut)] + let mut config = rustls::ClientConfig::builder_with_provider(Arc::new(custom_provider( + cipher_suites, + kx_group, ))) .with_safe_default_protocol_versions() .unwrap() .with_root_certificates(root_store) .with_no_client_auth(); + #[cfg(feature = "fips")] + { + config.require_ems = true; + assert!(config.fips()); + } + let server_name = "localhost".try_into().unwrap(); let mut sock = TcpStream::connect(format!("localhost:{PORT}")).unwrap(); @@ -135,6 +148,11 @@ fn test_with_custom_config_to_internet( suite: SupportedCipherSuite, group: &'static dyn SupportedKxGroup, ) -> CipherSuite { + #[cfg(feature = "fips")] + { + rustls_openssl::enable_fips(); + } + let cipher_suites = vec![suite]; let kx_group = vec![group]; @@ -143,15 +161,22 @@ fn test_with_custom_config_to_internet( roots: webpki_roots::TLS_SERVER_ROOTS.to_vec(), }; - let config = rustls::ClientConfig::builder_with_provider(Arc::new(custom_provider( - Some(cipher_suites), - Some(kx_group), + #[allow(unused_mut)] + let mut config = rustls::ClientConfig::builder_with_provider(Arc::new(custom_provider( + cipher_suites, + kx_group, ))) .with_safe_default_protocol_versions() .unwrap() .with_root_certificates(root_store) .with_no_client_auth(); + #[cfg(feature = "fips")] + { + config.require_ems = true; + assert!(config.fips()); + } + let server_name = "index.crates.io".try_into().unwrap(); let mut sock = TcpStream::connect("index.crates.io:443").unwrap(); @@ -196,7 +221,7 @@ fn test_with_custom_config_to_internet( CipherSuite::TLS13_AES_256_GCM_SHA384 )] #[cfg_attr( - chacha, + all(chacha, not(feature = "fips")), case::tls13_chacha20_poly1305_sha256( rustls_openssl::cipher_suite::TLS13_CHACHA20_POLY1305_SHA256, rustls_openssl::kx_group::SECP256R1, @@ -220,7 +245,7 @@ fn test_with_custom_config_to_internet( ) )] #[cfg_attr( - feature = "x25519", + not(feature = "fips"), case::tls13_aes_256_gcm_sha384_x25519( rustls_openssl::cipher_suite::TLS13_AES_256_GCM_SHA384, rustls_openssl::kx_group::X25519, @@ -233,7 +258,7 @@ fn test_with_custom_config_to_internet( CipherSuite::TLS13_AES_256_GCM_SHA384 )] #[cfg_attr( - all(feature = "tls12", chacha), + all(feature = "tls12", chacha, not(feature = "fips")), case::tls_ecdhe_rsa_with_chacha20_poly1305_sha256( rustls_openssl::cipher_suite::TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, rustls_openssl::kx_group::SECP256R1, @@ -264,7 +289,7 @@ fn test_tls( #[rstest] #[cfg_attr( - chacha, + all(chacha, not(feature = "fips")), case( rustls_openssl::cipher_suite::TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, rustls_openssl::kx_group::SECP384R1, @@ -288,6 +313,11 @@ fn test_to_internet( #[test] fn test_default_client() { let lock = OPENSSL_SERVER_PROCESS.lock(); + #[cfg(feature = "fips")] + { + openssl::fips::enable(true).unwrap(); + } + // Add default webpki roots to the root store let mut root_store = rustls::RootCertStore { roots: webpki_roots::TLS_SERVER_ROOTS.to_vec(), @@ -305,12 +335,19 @@ fn test_default_client() { root_store.add_parsable_certificates(certs); - let config = rustls::ClientConfig::builder_with_provider(Arc::new(default_provider())) + #[allow(unused_mut)] + let mut config = rustls::ClientConfig::builder_with_provider(Arc::new(default_provider())) .with_safe_default_protocol_versions() .unwrap() .with_root_certificates(root_store) .with_no_client_auth(); + #[cfg(feature = "fips")] + { + config.require_ems = true; + assert!(config.fips()); + } + let server_name = "localhost".try_into().unwrap(); let mut sock = TcpStream::connect(format!("localhost:{PORT}")).unwrap(); @@ -419,6 +456,7 @@ fn test_ec_sign_and_verify(#[case] scheme: SignatureScheme, #[case] curve: Nid) ); } +#[cfg(not(feature = "fips"))] #[test] fn test_ed25119_sign_and_verify() { let ours = rustls_openssl::default_provider(); @@ -479,3 +517,11 @@ fn sign_and_verify( .iter() .any(|alg| { alg.verify_signature(pub_key, data, &signature).is_ok() })); } + +#[cfg(feature = "fips")] +#[test] +fn provider_is_fips() { + rustls_openssl::enable_fips(); + let provider = rustls_openssl::default_provider(); + assert!(provider.fips()); +}