Skip to content

Commit

Permalink
FIPS provider integration (#17)
Browse files Browse the repository at this point in the history
* FIPS provider integration

* ensure all integration tests enable fips

* run fips tests with single thread

to avoid exhausting entropy in CI, and issues caused by repeated
enabling of FIPs provider
  • Loading branch information
tofay authored Nov 19, 2024
1 parent 1318c44 commit 12dfd46
Show file tree
Hide file tree
Showing 15 changed files with 191 additions and 139 deletions.
28 changes: 24 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,14 +48,14 @@ jobs:
- name: cargo clippy --no-default-features (warnings)
run: cargo clippy --no-default-features --all-targets -- -D warnings

test-fips:
name: Test using FIPS openssl
test-fips-1-1-1:
name: Test using FIPS openssl 1.1.1
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
run: dnf install -y gcc openssl-devel
- name: Check out repository
uses: actions/checkout@v4
- name: Install toolchain
Expand All @@ -64,5 +64,25 @@ jobs:
toolchain: stable
- name: Cache build artifacts
uses: Swatinem/rust-cache@v2
# Use single thread on FIPS to avoid running out of entropy
- name: Run cargo test --features fips
run: cargo test --features fips
run: cargo test --tests --features fips -- --test-threads=1

test-fips-openssl-3:
name: Test using FIPS openssl 3
runs-on: ubuntu-latest
container:
image: registry.access.redhat.com/ubi9/ubi:latest
steps:
- name: Install dependencies
run: dnf install -y gcc openssl-devel
- 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 --tests --features fips -- --test-threads=1
4 changes: 1 addition & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,15 @@ openssl = "0.10.68"
openssl-sys = "0.9.104"
rustls = { version = "0.23.0", default-features = false }
rustls-webpki = { version = "0.102.2", default-features = false }
once_cell = "1.8.0"

[features]
default = ["tls12"]
fips = []
tls12 = ["rustls/tls12", "foreign-types-shared"]

[dev-dependencies]
antidote = "1.0.0"
hex = "0.4.3"
lazy_static = "1.4.0"
once_cell = "1.8.0"
rcgen = { version = "0.13.1", default-features = false, features = [
"aws_lc_rs",
] }
Expand Down
2 changes: 1 addition & 1 deletion src/hash.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ impl rustls::crypto::hash::Hash for Algorithm {
}

fn fips(&self) -> bool {
crate::fips()
crate::fips::enabled()
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/hkdf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ impl RustlsHkdf for Hkdf {
}

fn fips(&self) -> bool {
crate::fips()
crate::fips::enabled()
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/hmac.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ impl rustls::crypto::hmac::Hmac for Hmac {
}

fn fips(&self) -> bool {
crate::fips()
crate::fips::enabled()
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/kx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ impl SupportedKxGroup for EcKxGroup {
}

fn fips(&self) -> bool {
crate::fips()
crate::fips::enabled()
}
}

Expand Down
120 changes: 81 additions & 39 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,30 +52,11 @@
//!
//! # 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.
//!
//! # 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]
//!
//! See [rustls documentation](https://docs.rs/rustls/latest/rustls/client/struct.ClientConfig.html#structfield.require_ems) for rationale.
//!
//! ## 3. Enable FIPS mode for OpenSSL
//!
//! [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).
//! - `fips`: Enabling this feature removes non-FIPS-approved cipher suites and key exchanges. Disabled by default. See [fips].
#![warn(missing_docs)]

use openssl::rand::rand_bytes;
use openssl::error::ErrorStack;
use openssl::rand::rand_priv_bytes;
use openssl_sys::c_int;
use rustls::crypto::{CryptoProvider, GetRandomFailed, SupportedKxGroup};
use rustls::SupportedCipherSuite;

Expand Down Expand Up @@ -238,28 +219,89 @@ pub struct SecureRandom;

impl rustls::crypto::SecureRandom for SecureRandom {
fn fill(&self, buf: &mut [u8]) -> Result<(), GetRandomFailed> {
rand_bytes(buf).map_err(|_| GetRandomFailed)
rand_priv_bytes(buf).map_err(|_| GetRandomFailed)
}

fn fips(&self) -> bool {
fips()
fips::enabled()
}
}

/// 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
pub(crate) fn cvt(r: c_int) -> Result<i32, ErrorStack> {
if r <= 0 {
Err(ErrorStack::get())
} else {
Ok(r)
}
}

/// 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.");
pub mod fips {
//! # FIPS support
//!
//! 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]
//!
//! See [rustls documentation](https://docs.rs/rustls/latest/rustls/client/struct.ClientConfig.html#structfield.require_ems) for rationale.
//!
//! ## 3. Enable FIPS mode for OpenSSL
//!
//! See [enable()].
//!
//! ## 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).
/// Returns `true` if OpenSSL is running in FIPS mode.
#[cfg(fips_module)]
pub(crate) fn enabled() -> bool {
openssl::fips::enabled()
}
#[cfg(not(fips_module))]
pub(crate) fn enabled() -> bool {
unsafe { openssl_sys::EVP_default_properties_is_fips_enabled(std::ptr::null_mut()) == 1 }
}

/// Enable FIPS mode for OpenSSL.
///
/// This should be called on application startup before the provider is used.
///
/// On OpenSSL 1.1.1 this calls [FIPS_mode_set](https://wiki.openssl.org/index.php/FIPS_mode_set()).
/// On OpenSSL 3 this loads a FIPS provider, which must be available.
///
/// Panics if FIPS cannot be enabled
#[cfg(fips_module)]
pub fn enable() {
openssl::fips::enable(true).expect("Failed to enable FIPS mode.");
}

/// Enable FIPS mode for OpenSSL.
///
/// This should be called on application startup before the provider is used.
///
/// On OpenSSL 1.1.1 this calls [FIPS_mode_set](https://wiki.openssl.org/index.php/FIPS_mode_set()).
/// On OpenSSL 3 this loads a FIPS provider, which must be available.
///
/// Panics if FIPS cannot be enabled
#[cfg(not(fips_module))]
pub fn enable() {
// Use OnceCell to ensure that the provider is only loaded once
use once_cell::sync::OnceCell;
static PROVIDER: OnceCell<openssl::provider::Provider> = OnceCell::new();
PROVIDER.get_or_init(|| {
let provider = openssl::provider::Provider::load(None, "fips")
.expect("Failed to load FIPS provider.");
unsafe {
crate::cvt(openssl_sys::EVP_default_properties_enable_fips(
std::ptr::null_mut(),
1,
))
.expect("Failed to enable FIPS properties.");
}
provider
});
}
}
12 changes: 2 additions & 10 deletions src/prf.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::hash::Algorithm;
use crate::{cvt, hash::Algorithm};
use core::ffi::c_void;
use foreign_types_shared::ForeignTypeRef;
use openssl::{
Expand Down Expand Up @@ -45,19 +45,11 @@ impl rustls::crypto::tls12::Prf for Prf {
}

fn fips(&self) -> bool {
crate::fips()
crate::fips::enabled()
}
}

// rust-openssl doesn't expose tls1_prf function yet: https://github.com/sfackler/rust-openssl/pull/2329
fn cvt(r: c_int) -> Result<i32, ErrorStack> {
if r <= 0 {
Err(ErrorStack::get())
} else {
Ok(r)
}
}

extern "C" {
fn EVP_PKEY_CTX_ctrl(
ctx: *mut EVP_PKEY_CTX,
Expand Down
2 changes: 1 addition & 1 deletion src/quic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ impl quic::Algorithm for KeyBuilder {
}

fn fips(&self) -> bool {
crate::fips()
crate::fips::enabled()
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/signer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ impl rustls::crypto::KeyProvider for KeyProvider {
}

fn fips(&self) -> bool {
crate::fips()
crate::fips::enabled()
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/tls12.rs
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ impl Tls12AeadAlgorithm for aead::Algorithm {

fn fips(&self) -> bool {
match self {
aead::Algorithm::Aes128Gcm | aead::Algorithm::Aes256Gcm => crate::fips(),
aead::Algorithm::Aes128Gcm | aead::Algorithm::Aes256Gcm => crate::fips::enabled(),
#[cfg(all(chacha, not(feature = "fips")))]
aead::Algorithm::ChaCha20Poly1305 => false,
}
Expand Down
2 changes: 1 addition & 1 deletion src/tls13.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ impl Tls13AeadAlgorithm for aead::Algorithm {
}

fn fips(&self) -> bool {
crate::fips()
crate::fips::enabled()
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/verify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,7 @@ impl SignatureVerificationAlgorithm for OpenSslAlgorithm {
}

fn fips(&self) -> bool {
crate::fips()
crate::fips::enabled()
}
}

Expand Down
Loading

0 comments on commit 12dfd46

Please sign in to comment.