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}