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

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 _fips and _base alive for as long as you need the context. In production code, store them in a long-lived struct field alongside the Arc<LibCtx>.

How fetch_in works

Every algorithm type (DigestAlg, CipherAlg, MacAlg, KdfAlg, RandAlg) has:

  • fetch(name, props) — uses the global default context (NULL libctx).
  • 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 stringEffect
NoneNo 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>.