Skip to main content

native_ossl/
lib_ctx.rs

1//! `LibCtx` — `OSSL_LIB_CTX` wrapper and `Provider` — `OSSL_PROVIDER` wrapper.
2//!
3//! Most callers use the global default library context and never need this module.
4//! Create an explicit context when:
5//! - FIPS mode is required (load `"fips"` + `"base"` providers).
6//! - Per-process isolation of algorithm state is needed.
7//!
8//! ## `Arc<LibCtx>` ownership
9//!
10//! OpenSSL does not expose `OSSL_LIB_CTX_up_ref`.  Rust manages the lifetime
11//! via `Arc<LibCtx>`.  Algorithm descriptors that use an explicit context store
12//! `Option<Arc<LibCtx>>` to extend its lifetime.
13
14use crate::error::ErrorStack;
15use native_ossl_sys as sys;
16
17// ── LibCtx ────────────────────────────────────────────────────────────────────
18
19/// An OpenSSL library context (`OSSL_LIB_CTX*`).
20///
21/// Wrap in `Arc<LibCtx>` before passing to algorithm descriptors; they will
22/// clone the `Arc` to keep the context alive.
23#[derive(Debug)]
24pub struct LibCtx {
25    ptr: *mut sys::OSSL_LIB_CTX,
26    /// When `false` this `LibCtx` was created from an externally-owned pointer
27    /// and must **not** call `OSSL_LIB_CTX_free` on drop.
28    owned: bool,
29}
30
31impl LibCtx {
32    /// Create a new, empty library context.
33    ///
34    /// No providers are loaded by default.  Call `load_provider` at least once
35    /// before using algorithms.
36    ///
37    /// # Errors
38    ///
39    /// Returns `Err` if OpenSSL cannot allocate the context.
40    pub fn new() -> Result<Self, ErrorStack> {
41        let ptr = unsafe { sys::OSSL_LIB_CTX_new() };
42        if ptr.is_null() {
43            return Err(ErrorStack::drain());
44        }
45        Ok(LibCtx { ptr, owned: true })
46    }
47
48    /// Load a provider into this library context.
49    ///
50    /// Common provider names:
51    /// - `c"default"` — standard algorithms.
52    /// - `c"fips"` — FIPS 140-3 validated algorithms (requires OpenSSL FIPS module).
53    /// - `c"base"` — must be loaded alongside `"fips"` for PEM/DER encoders.
54    ///
55    /// The returned `Provider` keeps the provider loaded until dropped.
56    ///
57    /// # Errors
58    ///
59    /// Returns `Err` if the provider cannot be loaded.
60    pub fn load_provider(&self, name: &std::ffi::CStr) -> Result<Provider, ErrorStack> {
61        let ptr = unsafe { sys::OSSL_PROVIDER_load(self.ptr, name.as_ptr()) };
62        if ptr.is_null() {
63            return Err(ErrorStack::drain());
64        }
65        Ok(Provider { ptr })
66    }
67
68    /// Return the raw `OSSL_LIB_CTX*` pointer.  Valid while `self` is alive.
69    #[must_use]
70    pub fn as_ptr(&self) -> *mut sys::OSSL_LIB_CTX {
71        self.ptr
72    }
73
74    /// Wrap a raw `OSSL_LIB_CTX*` that is owned and managed externally.
75    ///
76    /// The resulting `LibCtx` will NOT call `OSSL_LIB_CTX_free` when dropped.
77    /// Use this only when the raw pointer's lifetime is guaranteed to exceed the
78    /// `LibCtx` (e.g. a context received from a FIPS provider callback).
79    ///
80    /// # Safety
81    ///
82    /// The caller must ensure that `ptr` is a valid, non-null `OSSL_LIB_CTX*`
83    /// that remains valid for as long as the returned `LibCtx` is alive.
84    pub unsafe fn from_raw_unowned(ptr: *mut sys::OSSL_LIB_CTX) -> Self {
85        LibCtx { ptr, owned: false }
86    }
87}
88
89impl Drop for LibCtx {
90    fn drop(&mut self) {
91        if self.owned {
92            unsafe { sys::OSSL_LIB_CTX_free(self.ptr) };
93        }
94    }
95}
96
97// SAFETY: `OSSL_LIB_CTX` is designed to be shared across threads after setup.
98unsafe impl Send for LibCtx {}
99unsafe impl Sync for LibCtx {}
100
101// ── Provider ──────────────────────────────────────────────────────────────────
102
103/// A loaded OpenSSL provider (`OSSL_PROVIDER*`).
104///
105/// The provider remains loaded until this value is dropped.  Keep it alive for
106/// the lifetime of any algorithm descriptors that use the associated `LibCtx`.
107pub struct Provider {
108    ptr: *mut sys::OSSL_PROVIDER,
109}
110
111impl Drop for Provider {
112    fn drop(&mut self) {
113        unsafe { sys::OSSL_PROVIDER_unload(self.ptr) };
114    }
115}
116
117// SAFETY: `OSSL_PROVIDER*` is managed by the library context thread-safely.
118unsafe impl Send for Provider {}
119unsafe impl Sync for Provider {}
120
121// ── Tests ─────────────────────────────────────────────────────────────────────
122
123#[cfg(test)]
124mod tests {
125    use super::*;
126    use std::sync::Arc;
127
128    #[test]
129    fn create_and_drop() {
130        let ctx = LibCtx::new().unwrap();
131        drop(ctx);
132    }
133
134    #[test]
135    fn load_default_provider() {
136        let ctx = LibCtx::new().unwrap();
137        let _prov = ctx.load_provider(c"default").unwrap();
138        // Provider unloaded when `_prov` drops.
139    }
140
141    #[test]
142    fn arc_shared_context() {
143        let ctx = Arc::new(LibCtx::new().unwrap());
144        let _prov = ctx.load_provider(c"default").unwrap();
145        let ctx2 = Arc::clone(&ctx);
146        drop(ctx);
147        // ctx2 keeps the context alive; prov is still valid.
148        drop(ctx2);
149    }
150}