Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

FIPS

Overview

OpenSSL’s FIPS support is delivered by the FIPS provider — a separate shared library that implements a validated cryptographic module. Native-ossl exposes two distinct levels of FIPS integration:

  1. Running code in FIPS mode — ordinary application code that uses the FIPS provider for its cryptography. No special feature flag is required.
  2. Implementing a FIPS provider — writing a new FIPS provider in Rust that hooks into OpenSSL’s internal provider API. This requires the fips-provider Cargo feature and non-public OpenSSL headers.

This guide covers level 1. Level 2 is a future capability gated behind the fips-provider feature.

Checking Whether the FIPS Provider Is Active

#![allow(unused)]
fn main() {
use native_ossl::fips;

if fips::is_running(None) {
    println!("FIPS provider is loaded in the default library context");
}
}

fips::is_running wraps OSSL_PROVIDER_available. It returns true if the FIPS provider is currently loaded and available in the specified library context.

Signature

#![allow(unused)]
fn main() {
pub fn is_running(libctx: Option<&Arc<LibCtx>>) -> bool
}

Pass None to query the default (process-wide) library context. Pass Some(ctx) to query a specific isolated LibCtx:

#![allow(unused)]
fn main() {
use std::sync::Arc;
use native_ossl::lib_ctx::LibCtx;
use native_ossl::fips;

let ctx = Arc::new(LibCtx::new()?);
// Load FIPS and base providers into the isolated context.
// ...

if fips::is_running(Some(&ctx)) {
    // The FIPS provider is active in this isolated context.
}
}

is_running is always compiled — it requires no feature flag. It is safe to call even when the FIPS provider is not installed; it simply returns false.

Loading the FIPS Provider

is_running only reports whether the provider is already loaded. To load it, use LibCtx and Provider:

#![allow(unused)]
fn main() {
use std::sync::Arc;
use native_ossl::lib_ctx::{LibCtx, Provider};
use native_ossl::fips;

let ctx = Arc::new(LibCtx::new()?);
let _fips = Provider::load(&ctx, c"fips")?;
let _base = Provider::load(&ctx, c"base")?;

assert!(fips::is_running(Some(&ctx)));
}

Loading the base provider alongside fips is required — base supplies encoding, decoding, and other non-cryptographic primitives that the FIPS provider does not implement.

Using FIPS-Approved Algorithms

Once the FIPS provider is loaded into a LibCtx, fetch algorithms from that context to ensure FIPS-approved implementations are used:

#![allow(unused)]
fn main() {
use native_ossl::digest::DigestAlg;
use native_ossl::pkey::Pkey;

// Fetch SHA-256 from the FIPS context — guaranteed to use the FIPS provider.
let sha256 = DigestAlg::fetch_in(&ctx, c"SHA2-256", None)?;

// Load a key within the FIPS context.
let key = Pkey::<native_ossl::pkey::Private>::from_pem_in(&ctx, pem_bytes)?;
}

Property strings can further restrict algorithm selection:

#![allow(unused)]
fn main() {
// Require fips=yes explicitly on the algorithm fetch.
let sha256 = DigestAlg::fetch_in(&ctx, c"SHA2-256", Some(c"fips=yes"))?;
}

FIPS Mode vs. Implementing a FIPS Provider

These two use cases are often confused:

ScenarioDescriptionFeature required
Running in FIPS modeApplication uses the FIPS provider for cryptographyNone
Implementing a FIPS providerWriting a new provider that exposes a FIPS-validated modulefips-provider

Most applications fall into the first category. They load the FIPS provider, verify it is active with fips::is_running, and then use normal DigestAlg, Pkey, and other types — the FIPS provider enforces algorithm restrictions transparently.

The fips-provider Feature

The fips-provider Cargo feature is declared in both native-ossl and native-ossl-sys. It is intended for code that implements a FIPS provider from Rust — accessing OpenSSL’s internal provider vtable, PROV_CTX helpers, and ossl_prov_is_running.

[dependencies]
native-ossl = { version = "0.1", features = ["fips-provider"] }

This feature is not needed for ordinary application code that runs in FIPS mode. Enable it only if you are writing a FIPS provider implementation.

What fips-provider provides

Enabling this feature performs a second bindgen pass against the non-public OpenSSL headers (pointed to by OPENSSL_SOURCE_DIR) and exposes:

  • ProviderSignatureCtx — vtable-based signature context that calls EVP_SIGNATURE function pointers directly, bypassing EVP_DigestSign* (which would cause a circular provider dependency inside a FIPS module).
  • prov_ctx_new / prov_ctx_free / prov_ctx_set_handle / prov_ctx_set_libctx / prov_ctx_get_libctx — wrappers around ossl_prov_ctx_*.
  • check_state_ok / set_error_stateossl_prov_is_running and ossl_set_error_state for provider self-test reporting.
  • Pkey<Private>::keydata() — reads the void *keydata pointer from an EVP_PKEY, needed to pass provider-side key material to vtable functions.
  • ProvCtx / OSSL_CORE_HANDLE — type aliases re-exported from native_ossl_sys::fips_internal.

These APIs require the OpenSSL source tree because they are declared in non-public headers (crypto/evp.h, prov/provider_ctx.h, internal/provider.h). Set OPENSSL_SOURCE_DIR to the root of the OpenSSL source tree before building:

OPENSSL_SOURCE_DIR=~/src/openssl cargo build --features fips-provider

Platform note: The fips-provider feature is currently verified for x86_64 Linux only. The hand-written EVP_SIGNATURE vtable layout and EVP_PKEY::keydata offset are confirmed against OpenSSL 3.5.x on x86_64; compile-time offsetof assertions for other architectures are planned.

Testing with the FIPS Provider

Install the FIPS provider for your system:

# Fedora / RHEL
sudo dnf install openssl-fips-provider

# Verify it is loadable
openssl list -providers -provider fips

Run FIPS-related tests:

cargo test

No feature flag is required for FIPS tests. Tests that require the FIPS provider call fips::is_running at runtime and skip themselves if the provider is not installed. The fips-provider Cargo feature is unrelated — it is for writing a FIPS provider implementation, not for using one.