Library Contexts
What is a library context?
OpenSSL 3.x introduces OSSL_LIB_CTX — an isolated environment that controls which
cryptographic providers are loaded and used for algorithm fetch. The global default
context is created automatically on the first API call and loads the default provider.
You would need an explicit LibCtx when:
- Running FIPS-validated operations in isolation from non-FIPS ones.
- Building a library that consumes OpenSSL and is supposed to be loaded into other applications
- Loading a non-standard configuration file for one component only.
- Testing — spinning up a context with a known configuration and tearing it down.
LibCtx — library context wrapper
#![allow(unused)]
fn main() {
pub struct LibCtx { /* OSSL_LIB_CTX* */ }
impl LibCtx {
/// Create a new, empty library context.
pub fn new() -> Result<Self, ErrorStack>;
/// Load a named provider into this context.
/// The returned `Provider` handle keeps the provider alive; drop it to unload.
pub fn load_provider(&self, name: &CStr) -> Result<Provider, ErrorStack>;
}
}
LibCtx is Send + Sync. Wrapping it in Arc<LibCtx> lets you share one context
across many threads and algorithm descriptors.
Provider — provider handle
#![allow(unused)]
fn main() {
pub struct Provider { /* OSSL_PROVIDER* */ }
impl Drop for Provider { /* OSSL_PROVIDER_unload */ }
}
Drop the Provider to unload; keep it alive for the duration you need the provider.
FIPS isolation example
#![allow(unused)]
fn main() {
use std::sync::Arc;
use native_ossl::lib_ctx::LibCtx;
use native_ossl::cipher::CipherAlg;
use native_ossl::pkey::Pkey;
// Create an isolated context.
let fips_ctx = Arc::new(LibCtx::new()?);
// FIPS provider: validated algorithm implementations.
// Base provider: encoders/decoders (PEM, DER). Always load it alongside FIPS.
let _fips = fips_ctx.load_provider(c"fips")?;
let _base = fips_ctx.load_provider(c"base")?;
// Fetch algorithms from the FIPS context — only FIPS-approved implementations.
let alg = CipherAlg::fetch_in(&fips_ctx, c"AES-256-GCM", None)?;
// Load a private key bound to the FIPS context.
let key = Pkey::<native_ossl::pkey::Private>::from_pem_in(&fips_ctx, &pem_bytes)?;
}
Keep
_fipsand_basealive for as long as you need the context. In production code, store them in a long-lived struct field alongside theArc<LibCtx>.
How fetch_in works
Every algorithm type (DigestAlg, CipherAlg, MacAlg, KdfAlg, RandAlg) has:
fetch(name, props)— uses the global default context (NULLlibctx).fetch_in(ctx, name, props)— uses the supplied explicit context.
The Arc<LibCtx> is cloned and stored inside the algorithm descriptor, so the context
stays alive as long as the descriptor or any context derived from it is in use.
Property queries
The optional props argument is an OpenSSL property query string. Common values:
| Query string | Effect |
|---|---|
None | No restriction — any matching provider |
c"fips=yes" | FIPS-validated implementations only |
c"fips=no" | Non-FIPS implementations only |
c"provider=default" | Explicit provider name |
#![allow(unused)]
fn main() {
// Fetch only FIPS-approved SHA-256 from the global default context.
let alg = DigestAlg::fetch(c"SHA2-256", Some(c"fips=yes"))?;
}
Thread safety
LibCtx is Send + Sync. All algorithm fetch operations are internally thread-safe;
you may call fetch_in from multiple threads simultaneously on the same Arc<LibCtx>.