Skip to main content

native_ossl/
fips.rs

1//! FIPS provider helpers.
2//!
3//! [`is_running`] is always available — it uses the public
4//! `OSSL_PROVIDER_available` API and requires no special feature flag.
5//! It is the only function needed by ordinary code that runs in FIPS mode
6//! but does not implement a FIPS provider itself.
7//!
8//! Items under `#[cfg(feature = "fips-provider")]` are only compiled when
9//! the `fips-provider` cargo feature is enabled.  They require non-public
10//! OpenSSL headers (vtable access, `prov_ctx` helpers, `ossl_prov_is_running`,
11//! etc.) and are only meaningful when you are implementing a FIPS provider.
12
13use native_ossl_sys as sys;
14use std::sync::Arc;
15
16/// Returns `true` if the FIPS provider is loaded and available in the given
17/// library context.
18///
19/// Pass `None` to query the default (process-wide) library context.
20///
21/// Uses `OSSL_PROVIDER_available` — no `fips-provider` feature required.
22///
23/// # Example
24///
25/// ```ignore
26/// use native_ossl::fips;
27/// if fips::is_running(None) {
28///     println!("Running in FIPS mode");
29/// }
30/// ```
31#[must_use]
32pub fn is_running(libctx: Option<&Arc<crate::lib_ctx::LibCtx>>) -> bool {
33    let lctx = libctx.map_or(std::ptr::null_mut(), |c| c.as_ptr());
34    unsafe { sys::OSSL_PROVIDER_available(lctx, c"fips".as_ptr()) != 0 }
35}
36
37// ── fips-provider — internal provider APIs ────────────────────────────────────
38
39#[cfg(feature = "fips-provider")]
40pub(crate) mod provider_impl {
41    use crate::error::ErrorStack;
42    use native_ossl_sys as sys;
43    use native_ossl_sys::fips_internal as fips_sys;
44    use std::ffi::{c_char, c_int, c_uchar, c_void, CStr};
45    use std::ptr::null;
46
47    // ── Hand-written EVP_SIGNATURE vtable layout ──────────────────────────────
48    //
49    // bindgen cannot generate the non-opaque `evp_signature_st` struct because
50    // it contains `CRYPTO_REF_COUNT` which uses `_Atomic int` — a C11 feature
51    // that bindgen does not support.
52    //
53    // We define the layout manually, verified against the OpenSSL source tree
54    // using offsetof() in a C test program (see build notes).
55    //
56    // Platform: x86_64 Linux, OpenSSL 3.5.x
57    // sizeof(evp_signature_st)     = 296
58    // offsetof(newctx)             = 40
59    // offsetof(digest_sign_init)   = 144
60    // offsetof(digest_sign_update) = 152
61    // offsetof(digest_sign_final)  = 160
62    // offsetof(digest_sign)        = 168
63    // offsetof(digest_verify_init) = 176
64    // offsetof(digest_verify_update) = 184
65    // offsetof(digest_verify_final) = 192
66    // offsetof(digest_verify)      = 200
67    // offsetof(freectx)            = 208
68
69    type FnNewctx = unsafe extern "C" fn(*mut c_void, *const c_char) -> *mut c_void;
70    type FnDigestSignInit = unsafe extern "C" fn(
71        *mut c_void,
72        *const c_char,
73        *mut c_void,
74        *const sys::OSSL_PARAM,
75    ) -> c_int;
76    type FnDigestSignUpdate = unsafe extern "C" fn(*mut c_void, *const c_uchar, usize) -> c_int;
77    type FnDigestSignFinal =
78        unsafe extern "C" fn(*mut c_void, *mut c_uchar, *mut usize, usize) -> c_int;
79    type FnDigestSign = unsafe extern "C" fn(
80        *mut c_void,
81        *mut c_uchar,
82        *mut usize,
83        usize,
84        *const c_uchar,
85        usize,
86    ) -> c_int;
87    type FnDigestVerifyInit = unsafe extern "C" fn(
88        *mut c_void,
89        *const c_char,
90        *mut c_void,
91        *const sys::OSSL_PARAM,
92    ) -> c_int;
93    type FnDigestVerifyUpdate = unsafe extern "C" fn(*mut c_void, *const c_uchar, usize) -> c_int;
94    type FnDigestVerifyFinal = unsafe extern "C" fn(*mut c_void, *const c_uchar, usize) -> c_int;
95    type FnDigestVerify =
96        unsafe extern "C" fn(*mut c_void, *const c_uchar, usize, *const c_uchar, usize) -> c_int;
97    type FnFreectx = unsafe extern "C" fn(*mut c_void);
98
99    /// Mirror of `evp_signature_st` with only the function pointer fields we
100    /// use, padded to match the actual C layout.
101    ///
102    /// Field offsets (bytes, x86_64 Linux, OpenSSL 3.5.x):
103    /// - `_header`       [0, 40)  — name_id, type_name, description, prov, refcnt + pad
104    /// - `newctx`        [40, 48) — `OSSL_FUNC_signature_newctx_fn*`
105    /// - `_skip_a`       [48, 144) — sign_init/sign, sign_message_*, verify_init/verify,
106    ///                               verify_message_*, verify_recover_*
107    /// - `digest_sign_init`   [144, 152)
108    /// - `digest_sign_update` [152, 160)
109    /// - `digest_sign_final`  [160, 168)
110    /// - `digest_sign`        [168, 176)
111    /// - `digest_verify_init` [176, 184)
112    /// - `digest_verify_update` [184, 192)
113    /// - `digest_verify_final`  [192, 200)
114    /// - `digest_verify`        [200, 208)
115    /// - `freectx`             [208, 216)
116    /// - `_tail`               [216, 296)
117    #[repr(C)]
118    struct EvpSignatureVtable {
119        _header: [u8; 40],
120        newctx: Option<FnNewctx>,
121        _skip_a: [u8; 96], // 48..144
122        digest_sign_init: Option<FnDigestSignInit>,
123        digest_sign_update: Option<FnDigestSignUpdate>,
124        digest_sign_final: Option<FnDigestSignFinal>,
125        digest_sign: Option<FnDigestSign>,
126        digest_verify_init: Option<FnDigestVerifyInit>,
127        digest_verify_update: Option<FnDigestVerifyUpdate>,
128        digest_verify_final: Option<FnDigestVerifyFinal>,
129        digest_verify: Option<FnDigestVerify>,
130        freectx: Option<FnFreectx>,
131        _tail: [u8; 80], // 216..296
132    }
133
134    // Compile-time assertion that the struct has the right total size.
135    const _: () = assert!(
136        std::mem::size_of::<EvpSignatureVtable>() == 296,
137        "EvpSignatureVtable layout does not match evp_signature_st (expected 296 bytes)"
138    );
139
140    // ── Provider state helpers ────────────────────────────────────────────────
141
142    /// Returns `true` if the FIPS module is in a running (non-error) state.
143    ///
144    /// Calls `ossl_prov_is_running()`.  Only meaningful inside a FIPS provider
145    /// implementation.
146    pub fn check_state_ok() -> bool {
147        unsafe { fips_sys::ossl_prov_is_running() != 0 }
148    }
149
150    /// Signal a FIPS self-test failure.
151    ///
152    /// Calls `ossl_set_error_state(OSSL_SELF_TEST_TYPE_PCT)`.  This puts the
153    /// FIPS module into the error state; any subsequent `check_state_ok()` call
154    /// will return `false`.
155    pub fn set_error_state() {
156        unsafe {
157            fips_sys::ossl_set_error_state(
158                fips_sys::OSSL_SELF_TEST_TYPE_PCT.as_ptr() as *const c_char
159            );
160        }
161    }
162
163    // ── PROV_CTX helpers ─────────────────────────────────────────────────────
164
165    /// Allocate a new `PROV_CTX`.
166    ///
167    /// # Safety
168    /// The returned pointer must be freed with [`prov_ctx_free`].
169    pub unsafe fn prov_ctx_new() -> *mut fips_sys::prov_ctx_st {
170        fips_sys::ossl_prov_ctx_new()
171    }
172
173    /// Free a `PROV_CTX` allocated by [`prov_ctx_new`].
174    ///
175    /// # Safety
176    /// `ctx` must be a valid pointer returned by `prov_ctx_new`.
177    pub unsafe fn prov_ctx_free(ctx: *mut fips_sys::prov_ctx_st) {
178        fips_sys::ossl_prov_ctx_free(ctx);
179    }
180
181    /// Store the `OSSL_CORE_HANDLE` into a `PROV_CTX`.
182    ///
183    /// The handle is the opaque `fips_internal::OSSL_CORE_HANDLE` type, which
184    /// the caller receives as the first argument of `OSSL_provider_init_int`.
185    ///
186    /// # Safety
187    /// Both pointers must be valid.
188    pub unsafe fn prov_ctx_set_handle(
189        ctx: *mut fips_sys::prov_ctx_st,
190        handle: *const fips_sys::OSSL_CORE_HANDLE,
191    ) {
192        fips_sys::ossl_prov_ctx_set0_handle(ctx, handle);
193    }
194
195    /// Store a raw `OSSL_LIB_CTX` pointer into a `PROV_CTX`.
196    ///
197    /// The `libctx` pointer is accepted as `*mut c_void` to allow passing a
198    /// pointer obtained from `OSSL_CORE_HANDLE` dispatch functions without
199    /// having to depend on the exact (opaque) `OSSL_LIB_CTX` type.
200    ///
201    /// # Safety
202    /// Both pointers must be valid.
203    pub unsafe fn prov_ctx_set_libctx(ctx: *mut fips_sys::prov_ctx_st, libctx: *mut c_void) {
204        fips_sys::ossl_prov_ctx_set0_libctx(ctx, libctx as *mut fips_sys::OSSL_LIB_CTX);
205    }
206
207    /// Retrieve the `OSSL_LIB_CTX` stored in a `PROV_CTX` as a raw `*mut c_void`.
208    ///
209    /// Cast the result to `*mut native_ossl_sys::OSSL_LIB_CTX` to use it with
210    /// native-ossl's `LibCtx::from_ptr`.
211    ///
212    /// # Safety
213    /// `ctx` must be a valid pointer.
214    pub unsafe fn prov_ctx_get_libctx(ctx: *mut fips_sys::prov_ctx_st) -> *mut c_void {
215        fips_sys::ossl_prov_ctx_get0_libctx(ctx) as *mut c_void
216    }
217
218    // ── ProviderSignatureCtx ──────────────────────────────────────────────────
219
220    /// Vtable-based signature context.
221    ///
222    /// Direct access to `EVP_SIGNATURE` function pointers, needed inside a FIPS
223    /// provider where `EVP_DigestSign*` is unavailable (circular provider
224    /// dependency).
225    ///
226    /// # Lifecycle
227    ///
228    /// 1. Call [`ProviderSignatureCtx::new`] with the library context and the
229    ///    provider's own `provctx` (obtained during `OSSL_provider_init_int`).
230    /// 2. Call [`digest_sign_init`] / [`digest_verify_init`] to bind the key.
231    /// 3. Call `update` and `final` (or `digest_sign` / `digest_verify` for
232    ///    one-shot operations).
233    ///
234    /// [`digest_sign_init`]: ProviderSignatureCtx::digest_sign_init
235    /// [`digest_verify_init`]: ProviderSignatureCtx::digest_verify_init
236    pub struct ProviderSignatureCtx {
237        vtable: *mut sys::EVP_SIGNATURE,
238        ctx: *mut c_void,
239    }
240
241    impl ProviderSignatureCtx {
242        /// Fetch the named signature algorithm and create a provider-side context.
243        ///
244        /// - `libctx` — the `OSSL_LIB_CTX` used to fetch the algorithm.  Cast
245        ///   from a `*mut c_void` to allow passing the value from provider init
246        ///   dispatch functions without depending on the opaque `OSSL_LIB_CTX` type.
247        /// - `provctx` — the provider's own context (the `void *provctx` received
248        ///   during `OSSL_provider_init_int`); passed to the `newctx` vtable fn.
249        /// - `alg_name` — algorithm name, e.g. `c"ECDSA"`, `c"RSA"`, `c"EDDSA"`.
250        pub fn new(
251            libctx: *mut c_void,
252            provctx: *mut c_void,
253            alg_name: &CStr,
254        ) -> Result<Self, ErrorStack> {
255            // SAFETY: libctx is a valid OSSL_LIB_CTX* or null (global context);
256            // alg_name is a valid NUL-terminated C string from &CStr.
257            let vtable_opaque = unsafe {
258                sys::EVP_SIGNATURE_fetch(
259                    libctx as *mut sys::OSSL_LIB_CTX,
260                    alg_name.as_ptr(),
261                    null(),
262                )
263            };
264            if vtable_opaque.is_null() {
265                return Err(ErrorStack::drain());
266            }
267
268            // Cast to our hand-written vtable struct.
269            let vtable = vtable_opaque as *mut EvpSignatureVtable;
270
271            // SAFETY: vtable is a non-null EVP_SIGNATURE* just fetched above, cast
272            // to our hand-verified EvpSignatureVtable layout (size asserted at compile
273            // time).  provctx is the provider context passed in by the caller.
274            let ctx = unsafe {
275                match (*vtable).newctx {
276                    Some(f) => f(provctx, null()),
277                    None => {
278                        sys::EVP_SIGNATURE_free(vtable_opaque);
279                        return Err(ErrorStack::drain());
280                    }
281                }
282            };
283            if ctx.is_null() {
284                unsafe { sys::EVP_SIGNATURE_free(vtable_opaque) };
285                return Err(ErrorStack::drain());
286            }
287
288            Ok(ProviderSignatureCtx {
289                vtable: vtable_opaque,
290                ctx,
291            })
292        }
293
294        fn vtable(&self) -> *mut EvpSignatureVtable {
295            self.vtable as *mut EvpSignatureVtable
296        }
297
298        // ── Signing ──────────────────────────────────────────────────────────
299
300        /// Initialise for digest-sign.
301        ///
302        /// - `md_name` — digest algorithm name; may be null for implicit-digest
303        ///   algorithms.
304        /// - `keydata` — provider-side key data.  For a `Pkey<Private>`, obtain
305        ///   this via `pkey.keydata()` (requires the `fips-provider` feature).
306        /// - `params` — optional additional parameters (pass `null()` for defaults).
307        pub fn digest_sign_init(
308            &mut self,
309            md_name: *const c_char,
310            keydata: *mut c_void,
311            params: *const sys::OSSL_PARAM,
312        ) -> Result<(), ErrorStack> {
313            // SAFETY: self.vtable is non-null (set in new() and never changed).
314            // The vtable layout is verified by the size assertion.  self.ctx is
315            // a valid provider-side context created by newctx and not yet freed.
316            unsafe {
317                match (*self.vtable()).digest_sign_init {
318                    Some(f) => {
319                        if f(self.ctx, md_name, keydata, params) != 1 {
320                            return Err(ErrorStack::drain());
321                        }
322                    }
323                    None => return Err(ErrorStack::drain()),
324                }
325            }
326            Ok(())
327        }
328
329        /// Feed data to the digest-sign operation.
330        pub fn digest_sign_update(&mut self, data: &[u8]) -> Result<(), ErrorStack> {
331            // SAFETY: self.vtable and self.ctx are valid (see digest_sign_init).
332            // data.as_ptr()/len() are valid for the slice lifetime.
333            unsafe {
334                match (*self.vtable()).digest_sign_update {
335                    Some(f) => {
336                        if f(self.ctx, data.as_ptr() as *const c_uchar, data.len()) != 1 {
337                            return Err(ErrorStack::drain());
338                        }
339                    }
340                    None => return Err(ErrorStack::drain()),
341                }
342            }
343            Ok(())
344        }
345
346        /// Finalise digest-sign and write into `signature`.
347        ///
348        /// Returns the number of bytes written.
349        pub fn digest_sign_final(&mut self, signature: &mut [u8]) -> Result<usize, ErrorStack> {
350            let mut siglen: usize = signature.len();
351            // SAFETY: self.vtable and self.ctx are valid (see digest_sign_init).
352            // signature is a valid mutable slice; siglen is initialised to its length.
353            unsafe {
354                match (*self.vtable()).digest_sign_final {
355                    Some(f) => {
356                        if f(
357                            self.ctx,
358                            signature.as_mut_ptr() as *mut c_uchar,
359                            &mut siglen,
360                            signature.len(),
361                        ) != 1
362                        {
363                            return Err(ErrorStack::drain());
364                        }
365                    }
366                    None => return Err(ErrorStack::drain()),
367                }
368            }
369            Ok(siglen)
370        }
371
372        /// One-shot digest-sign.
373        ///
374        /// Pass `None` for `signature` to query the required buffer size.
375        /// Returns the number of bytes written (or required when `signature` is `None`).
376        pub fn digest_sign(
377            &mut self,
378            mut signature: Option<&mut [u8]>,
379            tbs: &[u8],
380        ) -> Result<usize, ErrorStack> {
381            let (sigptr, sigcap) = match &mut signature {
382                Some(s) => (s.as_mut_ptr() as *mut c_uchar, s.len()),
383                None => (std::ptr::null_mut(), 0usize),
384            };
385            let mut siglen = sigcap;
386            // SAFETY: self.vtable and self.ctx are valid (see digest_sign_init).
387            // sigptr is either null (size-query path) or a valid mutable buffer.
388            // tbs.as_ptr()/len() are valid for the slice lifetime.
389            unsafe {
390                match (*self.vtable()).digest_sign {
391                    Some(f) => {
392                        if f(
393                            self.ctx,
394                            sigptr,
395                            &mut siglen,
396                            sigcap,
397                            tbs.as_ptr() as *const c_uchar,
398                            tbs.len(),
399                        ) != 1
400                        {
401                            return Err(ErrorStack::drain());
402                        }
403                    }
404                    None => return Err(ErrorStack::drain()),
405                }
406            }
407            Ok(siglen)
408        }
409
410        // ── Verification ─────────────────────────────────────────────────────
411
412        /// Initialise for digest-verify.
413        pub fn digest_verify_init(
414            &mut self,
415            md_name: *const c_char,
416            keydata: *mut c_void,
417            params: *const sys::OSSL_PARAM,
418        ) -> Result<(), ErrorStack> {
419            // SAFETY: self.vtable and self.ctx are valid (see digest_sign_init).
420            unsafe {
421                match (*self.vtable()).digest_verify_init {
422                    Some(f) => {
423                        if f(self.ctx, md_name, keydata, params) != 1 {
424                            return Err(ErrorStack::drain());
425                        }
426                    }
427                    None => return Err(ErrorStack::drain()),
428                }
429            }
430            Ok(())
431        }
432
433        /// Feed data to the digest-verify operation.
434        pub fn digest_verify_update(&mut self, data: &[u8]) -> Result<(), ErrorStack> {
435            // SAFETY: self.vtable and self.ctx are valid (see digest_sign_init).
436            // data.as_ptr()/len() are valid for the slice lifetime.
437            unsafe {
438                match (*self.vtable()).digest_verify_update {
439                    Some(f) => {
440                        if f(self.ctx, data.as_ptr() as *const c_uchar, data.len()) != 1 {
441                            return Err(ErrorStack::drain());
442                        }
443                    }
444                    None => return Err(ErrorStack::drain()),
445                }
446            }
447            Ok(())
448        }
449
450        /// Finalise digest-verify.  Returns `Ok(())` if the signature is valid.
451        pub fn digest_verify_final(&mut self, signature: &[u8]) -> Result<(), ErrorStack> {
452            // SAFETY: self.vtable and self.ctx are valid (see digest_sign_init).
453            // signature.as_ptr()/len() are valid for the slice lifetime.
454            unsafe {
455                match (*self.vtable()).digest_verify_final {
456                    Some(f) => {
457                        if f(
458                            self.ctx,
459                            signature.as_ptr() as *const c_uchar,
460                            signature.len(),
461                        ) != 1
462                        {
463                            return Err(ErrorStack::drain());
464                        }
465                    }
466                    None => return Err(ErrorStack::drain()),
467                }
468            }
469            Ok(())
470        }
471
472        /// One-shot digest-verify.  Returns `Ok(())` if the signature is valid.
473        pub fn digest_verify(&mut self, signature: &[u8], tbs: &[u8]) -> Result<(), ErrorStack> {
474            // SAFETY: self.vtable and self.ctx are valid (see digest_sign_init).
475            // Both slices' as_ptr()/len() are valid for their respective lifetimes.
476            unsafe {
477                match (*self.vtable()).digest_verify {
478                    Some(f) => {
479                        if f(
480                            self.ctx,
481                            signature.as_ptr() as *const c_uchar,
482                            signature.len(),
483                            tbs.as_ptr() as *const c_uchar,
484                            tbs.len(),
485                        ) != 1
486                        {
487                            return Err(ErrorStack::drain());
488                        }
489                    }
490                    None => return Err(ErrorStack::drain()),
491                }
492            }
493            Ok(())
494        }
495    }
496
497    impl Drop for ProviderSignatureCtx {
498        fn drop(&mut self) {
499            // Call freectx first (provider frees its own context), then release
500            // the EVP_SIGNATURE vtable reference.  EVP_SIGNATURE_free must always
501            // run, so it is placed after the freectx call rather than inside the
502            // same unsafe block to ensure it is not skipped by a hypothetical panic
503            // in a non-conforming provider.
504            if !self.ctx.is_null() {
505                // SAFETY: self.vtable is non-null and self.ctx is the context
506                // allocated by newctx; freectx is the matching destructor.
507                unsafe {
508                    let vt = self.vtable as *mut EvpSignatureVtable;
509                    if let Some(f) = (*vt).freectx {
510                        f(self.ctx);
511                    }
512                }
513            }
514            if !self.vtable.is_null() {
515                // SAFETY: self.vtable was obtained from EVP_SIGNATURE_fetch and
516                // has not been freed yet.
517                unsafe { sys::EVP_SIGNATURE_free(self.vtable) };
518            }
519        }
520    }
521
522    // SAFETY: `EVP_SIGNATURE*` is reference-counted and the vtable is read-only
523    // after the fetch.  `ctx` is a per-operation mutable C context; it is safe to
524    // move across threads (Send) but NOT to access concurrently from multiple
525    // threads without external synchronisation (not Sync).
526    unsafe impl Send for ProviderSignatureCtx {}
527}
528
529#[cfg(feature = "fips-provider")]
530pub use provider_impl::{
531    check_state_ok, prov_ctx_free, prov_ctx_get_libctx, prov_ctx_new, prov_ctx_set_handle,
532    prov_ctx_set_libctx, set_error_state, ProviderSignatureCtx,
533};
534
535// Re-export the prov_ctx_st type alias so callers can name it.
536#[cfg(feature = "fips-provider")]
537pub use native_ossl_sys::fips_internal::{prov_ctx_st as ProvCtx, OSSL_CORE_HANDLE};