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};