Skip to main content

native_ossl/
pkey.rs

1//! `Pkey<T>` — asymmetric key container and operations.
2//!
3//! Phase 5 delivers key loading/serialisation (5.1), keygen (5.2),
4//! sign/verify (5.3), derive (5.4), asymmetric encrypt/decrypt (5.5),
5//! and KEM encapsulate/decapsulate (5.6).
6//!
7//! # Type-state markers
8//!
9//! `Pkey<Private>`, `Pkey<Public>`, and `Pkey<Params>` statically prevent
10//! misuse (e.g. signing with a public key).  `HasPrivate: HasPublic` means
11//! every `Pkey<Private>` can also be used wherever `Pkey<Public>` is needed.
12
13use crate::bio::{MemBio, MemBioBuf};
14use crate::error::ErrorStack;
15use native_ossl_sys as sys;
16use std::marker::PhantomData;
17use std::sync::Arc;
18
19// ── Marker types and sealed trait hierarchy ───────────────────────────────────
20
21/// Marker: key holds public key material only.
22pub struct Public;
23/// Marker: key holds public + private key material.
24pub struct Private;
25/// Marker: key holds PKEY parameters only (e.g. EC group with no key).
26pub struct Params;
27
28mod sealed {
29    /// Sealed base: any key role.
30    pub trait HasParams {}
31    impl HasParams for super::Public {}
32    impl HasParams for super::Private {}
33    impl HasParams for super::Params {}
34
35    /// Sealed: key has public material.
36    pub trait HasPublic: HasParams {}
37    impl HasPublic for super::Public {}
38    impl HasPublic for super::Private {}
39
40    /// Sealed: key has private material.
41    pub trait HasPrivate: HasPublic {}
42    impl HasPrivate for super::Private {}
43}
44
45/// All key markers satisfy this bound.
46pub trait HasParams: sealed::HasParams {}
47impl<T: sealed::HasParams> HasParams for T {}
48
49/// Public key material is accessible (both `Public` and `Private` keys).
50pub trait HasPublic: sealed::HasPublic {}
51impl<T: sealed::HasPublic> HasPublic for T {}
52
53/// Private key material is accessible.
54pub trait HasPrivate: sealed::HasPrivate {}
55impl<T: sealed::HasPrivate> HasPrivate for T {}
56
57// ── Pkey<T> — key container ───────────────────────────────────────────────────
58
59/// An asymmetric key (`EVP_PKEY*`) with a compile-time role marker.
60///
61/// Cloneable via `EVP_PKEY_up_ref`; wrapping in `Arc<Pkey<T>>` is safe.
62pub struct Pkey<T> {
63    ptr: *mut sys::EVP_PKEY,
64    _role: PhantomData<T>,
65}
66
67// SAFETY: `EVP_PKEY` is reference-counted.
68unsafe impl<T> Send for Pkey<T> {}
69unsafe impl<T> Sync for Pkey<T> {}
70
71impl<T> Clone for Pkey<T> {
72    fn clone(&self) -> Self {
73        unsafe { sys::EVP_PKEY_up_ref(self.ptr) };
74        Pkey {
75            ptr: self.ptr,
76            _role: PhantomData,
77        }
78    }
79}
80
81impl<T> Drop for Pkey<T> {
82    fn drop(&mut self) {
83        unsafe { sys::EVP_PKEY_free(self.ptr) };
84    }
85}
86
87impl<T: HasParams> Pkey<T> {
88    /// Construct from a raw (owned) `EVP_PKEY*`.
89    ///
90    /// # Safety
91    ///
92    /// `ptr` must be a valid, non-null `EVP_PKEY*` that the caller is giving up ownership of.
93    #[must_use]
94    pub unsafe fn from_ptr(ptr: *mut sys::EVP_PKEY) -> Self {
95        Pkey {
96            ptr,
97            _role: PhantomData,
98        }
99    }
100
101    /// Raw `EVP_PKEY*` pointer valid for the lifetime of `self`.
102    #[must_use]
103    pub fn as_ptr(&self) -> *mut sys::EVP_PKEY {
104        self.ptr
105    }
106
107    /// Size of the key in bits (e.g. 256 for P-256, 2048 for RSA-2048).
108    #[must_use]
109    pub fn bits(&self) -> u32 {
110        u32::try_from(unsafe { sys::EVP_PKEY_get_bits(self.ptr) }).unwrap_or(0)
111    }
112
113    /// Security strength in bits (e.g. 128 for P-256, 112 for RSA-2048).
114    #[must_use]
115    pub fn security_bits(&self) -> u32 {
116        u32::try_from(unsafe { sys::EVP_PKEY_get_security_bits(self.ptr) }).unwrap_or(0)
117    }
118
119    /// Maximum output size in bytes for this key's primary operation.
120    ///
121    /// For signing keys (RSA, ECDSA, Ed25519) this is the maximum signature
122    /// length. For encryption keys (RSA-OAEP) this is the maximum ciphertext
123    /// length. Use this to size output buffers before calling signing or
124    /// encryption operations.
125    ///
126    /// Returns `0` if the key does not support a size query.
127    #[must_use]
128    pub fn max_output_size(&self) -> usize {
129        // SAFETY:
130        // - self.ptr is non-null (constructor invariant)
131        // - no mutable aliasing; &self ensures shared read-only access
132        usize::try_from(unsafe { sys::EVP_PKEY_get_size(self.ptr) }).unwrap_or(0)
133    }
134
135    /// Return `true` if this key is of the named algorithm (e.g. `c"EC"`, `c"RSA"`).
136    #[must_use]
137    pub fn is_a(&self, name: &std::ffi::CStr) -> bool {
138        unsafe { sys::EVP_PKEY_is_a(self.ptr, name.as_ptr()) == 1 }
139    }
140
141    /// Return `true` if this key's public component equals `other`'s.
142    ///
143    /// Wraps `EVP_PKEY_eq`.  Useful for verifying that a certificate's public
144    /// key matches a private key before using them together.
145    #[must_use]
146    pub fn public_eq<U: HasPublic>(&self, other: &Pkey<U>) -> bool
147    where
148        T: HasPublic,
149    {
150        unsafe { sys::EVP_PKEY_eq(self.ptr, other.ptr) == 1 }
151    }
152
153    /// Fill the values for a pre-prepared mutable `Params` query array.
154    ///
155    /// Wraps `EVP_PKEY_get_params`.  The array must already contain the keys
156    /// of interest with null data pointers; OpenSSL writes the values in place.
157    ///
158    /// # Errors
159    pub fn get_params(&self, params: &mut crate::params::Params<'_>) -> Result<(), ErrorStack> {
160        crate::ossl_call!(sys::EVP_PKEY_get_params(self.ptr, params.as_mut_ptr()))
161    }
162
163    /// DER-encode the public key (`SubjectPublicKeyInfo` format).
164    ///
165    /// Zero-copy: writes directly into a caller-owned `Vec<u8>` — no OpenSSL
166    /// heap allocation occurs.
167    ///
168    /// # Errors
169    ///
170    /// Returns `Err` if serialisation fails.
171    pub fn public_key_to_der(&self) -> Result<Vec<u8>, ErrorStack>
172    where
173        T: HasPublic,
174    {
175        // First call with null to query the DER byte length.
176        let len = unsafe { sys::i2d_PUBKEY(self.ptr, std::ptr::null_mut()) };
177        if len < 0 {
178            return Err(ErrorStack::drain());
179        }
180        // Allocate our own buffer and write into it.
181        // i2d_ advances the out-pointer; our Vec base address is unaffected.
182        let mut buf = vec![0u8; usize::try_from(len).unwrap_or(0)];
183        let mut out_ptr = buf.as_mut_ptr();
184        let written = unsafe { sys::i2d_PUBKEY(self.ptr, std::ptr::addr_of_mut!(out_ptr)) };
185        if written < 0 {
186            return Err(ErrorStack::drain());
187        }
188        buf.truncate(usize::try_from(written).unwrap_or(0));
189        Ok(buf)
190    }
191
192    /// Returns the default digest algorithm name for this key, or `None` if the
193    /// key type mandates no external digest (e.g. Ed25519, ML-DSA).
194    ///
195    /// Wraps `EVP_PKEY_get_default_digest_name`.  The function returns 1 when the
196    /// digest is optional and 2 when it is mandatory; both are treated as success.
197    /// When the key's algorithm performs its own internal hashing (Ed25519, ML-DSA,
198    /// SLH-DSA), OpenSSL writes `"none"` or an empty string — both map to `Ok(None)`.
199    ///
200    /// Callers should check this before deciding whether to pass a digest to
201    /// `DigestSign` / `DigestVerify`: if `default_digest_name()` returns `None`,
202    /// pass `digest: None` in `SignInit`; otherwise pass the returned name.
203    ///
204    /// # Errors
205    ///
206    /// Returns `Err` if OpenSSL cannot determine the digest.
207    pub fn default_digest_name(&self) -> Result<Option<String>, ErrorStack>
208    where
209        T: HasPublic,
210    {
211        // SAFETY: self.ptr is a valid non-null EVP_PKEY* (constructor invariant).
212        // EVP_PKEY_get_default_digest_name writes a NUL-terminated C string into
213        // mdname and returns >= 1 on success.
214        let mut buf = [0u8; 64];
215        let rc = unsafe {
216            sys::EVP_PKEY_get_default_digest_name(
217                self.ptr,
218                buf.as_mut_ptr().cast::<std::ffi::c_char>(),
219                buf.len(),
220            )
221        };
222        if rc < 1 {
223            return Err(ErrorStack::drain());
224        }
225        // Find the NUL terminator.
226        let name_bytes = buf
227            .iter()
228            .position(|&b| b == 0)
229            .map_or(&buf[..], |pos| &buf[..pos]);
230        // "none", "UNDEF", or empty string means no separate digest.
231        // OpenSSL 3.5+ returns "UNDEF" for algorithms like Ed25519 that do
232        // their own internal hashing without a caller-supplied digest.
233        if name_bytes.is_empty()
234            || name_bytes.eq_ignore_ascii_case(b"none")
235            || name_bytes.eq_ignore_ascii_case(b"undef")
236        {
237            return Ok(None);
238        }
239        let name = std::str::from_utf8(name_bytes)
240            .map_err(|_| ErrorStack::drain())?
241            .to_owned();
242        Ok(Some(name))
243    }
244}
245
246// ── PEM loading — private key ─────────────────────────────────────────────────
247
248impl Pkey<Private> {
249    /// Return the provider-side `keydata` pointer from the `EVP_PKEY` struct.
250    ///
251    /// This is the `void *keydata` field of `evp_pkey_st`.  It holds the
252    /// algorithm-specific key material allocated by the provider's keymgmt
253    /// implementation.  The pointer is valid for the lifetime of `self`.
254    ///
255    /// Only available with the `fips-provider` cargo feature.  Intended for
256    /// use inside a FIPS provider when invoking `EVP_SIGNATURE` vtable functions
257    /// that require `void *provkey` (= `keydata`).
258    ///
259    /// # Safety
260    ///
261    /// The returned pointer points into the internal state of this `Pkey`.
262    /// It must not outlive `self` and must not be freed independently.
263    /// The field offset is computed by the C compiler for the current target
264    /// ABI via a build-time probe; see `native-ossl-sys/build.rs`.
265    #[cfg(feature = "fips-provider")]
266    pub unsafe fn keydata(&self) -> *mut std::ffi::c_void {
267        // SAFETY: self.ptr is a valid non-null EVP_PKEY*.  We read the void* at
268        // the ABI-correct byte offset of the `keydata` field in evp_pkey_st,
269        // determined at build time by a C offsetof probe.
270        self.ptr
271            .cast::<u8>()
272            .add(native_ossl_sys::fips_internal::EVP_PKEY_KEYDATA_OFFSET)
273            .cast::<*mut std::ffi::c_void>()
274            .read()
275    }
276
277    /// Load a private key from PEM bytes.
278    ///
279    /// Pass `passphrase = Some(cb)` for encrypted PEM; `None` for unencrypted.
280    ///
281    /// # Errors
282    pub fn from_pem(pem: &[u8]) -> Result<Self, ErrorStack> {
283        let bio = MemBioBuf::new(pem)?;
284        let ptr = unsafe {
285            sys::PEM_read_bio_PrivateKey(
286                bio.as_ptr(),
287                std::ptr::null_mut(),
288                None,
289                std::ptr::null_mut(),
290            )
291        };
292        if ptr.is_null() {
293            return Err(ErrorStack::drain());
294        }
295        Ok(unsafe { Pkey::from_ptr(ptr) })
296    }
297
298    /// Serialise the private key to PEM (`PKCS#8` `BEGIN PRIVATE KEY`).
299    ///
300    /// # Errors
301    pub fn to_pem(&self) -> Result<Vec<u8>, ErrorStack> {
302        let mut bio = MemBio::new()?;
303        let rc = unsafe {
304            sys::PEM_write_bio_PrivateKey(
305                bio.as_ptr(),
306                self.ptr,
307                std::ptr::null(),
308                std::ptr::null_mut(),
309                0,
310                None,
311                std::ptr::null_mut(),
312            )
313        };
314        if rc != 1 {
315            return Err(ErrorStack::drain());
316        }
317        Ok(bio.into_vec())
318    }
319
320    /// Load a private key from PEM bytes within a specific library context.
321    ///
322    /// Uses `PEM_read_bio_PrivateKey_ex` so the key's internal algorithm fetch
323    /// uses `ctx`'s provider set.  Necessary when the private key is later used
324    /// for EVP operations inside an isolated (e.g. FIPS) context.
325    ///
326    /// # Errors
327    pub fn from_pem_in(ctx: &Arc<crate::lib_ctx::LibCtx>, pem: &[u8]) -> Result<Self, ErrorStack> {
328        let bio = MemBioBuf::new(pem)?;
329        let ptr = unsafe {
330            sys::PEM_read_bio_PrivateKey_ex(
331                bio.as_ptr(),
332                std::ptr::null_mut(),
333                None,
334                std::ptr::null_mut(),
335                ctx.as_ptr(),
336                std::ptr::null(),
337            )
338        };
339        if ptr.is_null() {
340            return Err(ErrorStack::drain());
341        }
342        Ok(unsafe { Pkey::from_ptr(ptr) })
343    }
344
345    /// Load a private key from DER bytes (auto-detecting `PKCS#8` / traditional).
346    ///
347    /// Zero-copy: the `EVP_PKEY` is decoded from the caller's slice without copying.
348    ///
349    /// # Errors
350    pub fn from_der(der: &[u8]) -> Result<Self, ErrorStack> {
351        let bio = MemBioBuf::new(der)?;
352        let ptr = unsafe { sys::d2i_PrivateKey_bio(bio.as_ptr(), std::ptr::null_mut()) };
353        if ptr.is_null() {
354            return Err(ErrorStack::drain());
355        }
356        Ok(unsafe { Pkey::from_ptr(ptr) })
357    }
358
359    /// Load a private key from DER bytes using an explicit library context.
360    ///
361    /// The key format is detected automatically (`PKCS#8`, traditional, etc.).
362    /// Wraps `d2i_AutoPrivateKey_ex` — unlike [`from_der`](Self::from_der) (which uses a
363    /// `BIO`), this function passes the raw byte pointer directly to OpenSSL so that
364    /// the key's internal algorithm fetch is bound to `ctx`'s provider set.  Use
365    /// this when the loaded key will be used for EVP operations inside an isolated
366    /// (e.g. FIPS) context.
367    ///
368    /// # Errors
369    ///
370    /// Returns `Err` if the DER bytes are malformed or the algorithm is not
371    /// available in `ctx`.
372    pub fn from_der_in(ctx: &Arc<crate::lib_ctx::LibCtx>, der: &[u8]) -> Result<Self, ErrorStack> {
373        // `d2i_AutoPrivateKey_ex` expects a `*mut *const u8` input pointer; it
374        // advances the pointer past the consumed bytes but we do not need the
375        // updated value, so we bind it to a local that is discarded afterwards.
376        let mut ptr = der.as_ptr();
377        // SAFETY:
378        // - Non-null: `der.as_ptr()` is always valid for a shared slice (even if
379        //   zero-length; OpenSSL will reject zero-length DER before dereferencing).
380        // - `ctx.as_ptr()` is non-null by `LibCtx` constructor invariant; `ctx`
381        //   is kept alive by the `&Arc<LibCtx>` borrow for the entire call.
382        // - Lifetime: `ptr` points into `der` which lives for the call duration;
383        //   OpenSSL reads at most `der.len()` bytes before returning.
384        // - Exclusivity / no data races: `der` is a shared immutable borrow; no
385        //   other thread can mutate it.  `ptr` is a local stack copy — OpenSSL's
386        //   write to `*pp` (advancing the pointer) touches only this variable,
387        //   not the original slice.
388        let pkey = unsafe {
389            sys::d2i_AutoPrivateKey_ex(
390                std::ptr::null_mut(),
391                std::ptr::addr_of_mut!(ptr),
392                i64::try_from(der.len()).unwrap_or(i64::MAX),
393                ctx.as_ptr(),
394                std::ptr::null(),
395            )
396        };
397        if pkey.is_null() {
398            return Err(ErrorStack::drain());
399        }
400        // SAFETY: `pkey` is non-null and freshly allocated by OpenSSL; we take
401        // exclusive ownership of the reference count.
402        Ok(unsafe { Pkey::from_ptr(pkey) })
403    }
404
405    /// Load a private key from passphrase-encrypted PEM.
406    ///
407    /// Passes `passphrase` directly to `PEM_read_bio_PrivateKey` via a
408    /// `pem_password_cb`.  Returns `Err` if the key cannot be decrypted or
409    /// the PEM is malformed.  For unencrypted PEM use [`from_pem`](Self::from_pem).
410    ///
411    /// # Errors
412    pub fn from_pem_passphrase(pem: &[u8], passphrase: &[u8]) -> Result<Self, ErrorStack> {
413        extern "C" fn passwd_cb(
414            buf: *mut std::ffi::c_char,
415            size: std::ffi::c_int,
416            _rwflag: std::ffi::c_int,
417            u: *mut std::ffi::c_void,
418        ) -> std::ffi::c_int {
419            // SAFETY: `u` is `&pw` where `pw: &[u8]` lives on the caller's stack.
420            let pw: &[u8] = unsafe { *(u as *const &[u8]) };
421            // size is the callback buffer capacity; it is always > 0 per the C contract.
422            let max_len = usize::try_from(size).unwrap_or(0);
423            let n = pw.len().min(max_len);
424            unsafe { std::ptr::copy_nonoverlapping(pw.as_ptr(), buf.cast::<u8>(), n) };
425            // n <= max_len == size (as usize), so n fits in i32.
426            i32::try_from(n).unwrap()
427        }
428        let bio = MemBioBuf::new(pem)?;
429        let pw: &[u8] = passphrase;
430        let ptr = unsafe {
431            sys::PEM_read_bio_PrivateKey(
432                bio.as_ptr(),
433                std::ptr::null_mut(),
434                Some(passwd_cb),
435                std::ptr::addr_of!(pw).cast::<std::ffi::c_void>().cast_mut(),
436            )
437        };
438        if ptr.is_null() {
439            return Err(ErrorStack::drain());
440        }
441        Ok(unsafe { Pkey::from_ptr(ptr) })
442    }
443
444    /// Serialise the private key as passphrase-encrypted PKCS#8 PEM
445    /// (`BEGIN ENCRYPTED PRIVATE KEY`).
446    ///
447    /// `cipher` controls the wrapping algorithm
448    /// (e.g. `CipherAlg::fetch(c"AES-256-CBC", None)`).
449    /// The passphrase is passed directly to OpenSSL via `kstr`/`klen`.
450    ///
451    /// # Panics
452    ///
453    /// Panics if `passphrase` is longer than `i32::MAX` bytes.
454    ///
455    /// # Errors
456    pub fn to_pem_encrypted(
457        &self,
458        cipher: &crate::cipher::CipherAlg,
459        passphrase: &[u8],
460    ) -> Result<Vec<u8>, ErrorStack> {
461        let mut bio = MemBio::new()?;
462        let rc = unsafe {
463            sys::PEM_write_bio_PKCS8PrivateKey(
464                bio.as_ptr(),
465                self.ptr,
466                cipher.as_ptr(),
467                passphrase.as_ptr().cast(),
468                i32::try_from(passphrase.len()).expect("passphrase too long"),
469                None,
470                std::ptr::null_mut(),
471            )
472        };
473        if rc != 1 {
474            return Err(ErrorStack::drain());
475        }
476        Ok(bio.into_vec())
477    }
478
479    /// Serialise the private key as unencrypted PKCS#8 DER
480    /// (`PrivateKeyInfo` / `OneAsymmetricKey`, RFC 5958).
481    ///
482    /// Equivalent to writing unencrypted PEM and stripping the base64 wrapper,
483    /// but avoids the encode/decode round-trip.  To encrypt the output, use
484    /// [`to_pem_encrypted`](Self::to_pem_encrypted) instead.
485    ///
486    /// # Errors
487    pub fn to_pkcs8_der(&self) -> Result<Vec<u8>, ErrorStack> {
488        let mut bio = MemBio::new()?;
489        let rc = unsafe { sys::i2d_PKCS8PrivateKeyInfo_bio(bio.as_ptr(), self.ptr) };
490        if rc != 1 {
491            return Err(ErrorStack::drain());
492        }
493        Ok(bio.into_vec())
494    }
495
496    /// Load a private key identified by a PKCS#11 URI
497    /// (e.g. `pkcs11:token=mytoken;object=mykey;type=private`).
498    ///
499    /// The `pkcs11` provider must already be loaded into `ctx` before calling
500    /// this function — typically via
501    /// [`LibCtx::load_pkcs11_provider`](crate::lib_ctx::LibCtx::load_pkcs11_provider).
502    ///
503    /// Internally, `OSSL_DECODER` dispatches the URI to the pkcs11 provider
504    /// which returns a live `EVP_PKEY*` backed by the hardware token.
505    ///
506    /// # Errors
507    ///
508    /// Returns `Err` if the URI is syntactically invalid, the pkcs11 provider is
509    /// not loaded, or the token / object cannot be found.
510    pub fn from_pkcs11_uri(
511        ctx: &Arc<crate::lib_ctx::LibCtx>,
512        uri: &str,
513    ) -> Result<Self, ErrorStack> {
514        let uri_cs = std::ffi::CString::new(uri).map_err(|_| ErrorStack::drain())?;
515        let uri_bytes = uri_cs.to_bytes();
516        let mut pkey: *mut sys::EVP_PKEY = std::ptr::null_mut();
517        // Use OSSL_DECODER with keytype=NULL so the pkcs11 provider can resolve
518        // the algorithm from the token object.
519        // SAFETY: pointers are either null sentinels or valid for the call duration.
520        let dctx = unsafe {
521            sys::OSSL_DECODER_CTX_new_for_pkey(
522                std::ptr::addr_of_mut!(pkey),
523                c"PEM".as_ptr(),  // pkcs11 URIs are presented as PEM-style text
524                std::ptr::null(), // input_struct: any
525                std::ptr::null(), // keytype: let provider decide
526                SELECT_DOMAIN_PARAMETERS | 0x01 | 0x02, // KEYPAIR selection
527                ctx.as_ptr(),
528                std::ptr::null(), // propquery: default
529            )
530        };
531        if dctx.is_null() {
532            return Err(ErrorStack::drain());
533        }
534        let mut ptr = uri_bytes.as_ptr();
535        let mut len = uri_bytes.len();
536        let rc = unsafe {
537            sys::OSSL_DECODER_from_data(
538                dctx,
539                std::ptr::addr_of_mut!(ptr),
540                std::ptr::addr_of_mut!(len),
541            )
542        };
543        unsafe { sys::OSSL_DECODER_CTX_free(dctx) };
544        if rc != 1 || pkey.is_null() {
545            return Err(ErrorStack::drain());
546        }
547        Ok(unsafe { Pkey::from_ptr(pkey) })
548    }
549}
550
551// ── PEM loading — public key ──────────────────────────────────────────────────
552
553impl Pkey<Public> {
554    /// Load a public key from PEM (`SubjectPublicKeyInfo` or RSA public key).
555    ///
556    /// # Errors
557    pub fn from_pem(pem: &[u8]) -> Result<Self, ErrorStack> {
558        let bio = MemBioBuf::new(pem)?;
559        let ptr = unsafe {
560            sys::PEM_read_bio_PUBKEY(
561                bio.as_ptr(),
562                std::ptr::null_mut(),
563                None,
564                std::ptr::null_mut(),
565            )
566        };
567        if ptr.is_null() {
568            return Err(ErrorStack::drain());
569        }
570        Ok(unsafe { Pkey::from_ptr(ptr) })
571    }
572
573    /// Load a public key from PEM bytes within a specific library context.
574    ///
575    /// Uses `PEM_read_bio_PUBKEY_ex` so the key's internal algorithm fetch
576    /// uses `ctx`'s provider set.  Necessary when the public key is later used
577    /// for EVP operations inside an isolated (e.g. FIPS) context.
578    ///
579    /// # Errors
580    pub fn from_pem_in(ctx: &Arc<crate::lib_ctx::LibCtx>, pem: &[u8]) -> Result<Self, ErrorStack> {
581        let bio = MemBioBuf::new(pem)?;
582        let ptr = unsafe {
583            sys::PEM_read_bio_PUBKEY_ex(
584                bio.as_ptr(),
585                std::ptr::null_mut(),
586                None,
587                std::ptr::null_mut(),
588                ctx.as_ptr(),
589                std::ptr::null(),
590            )
591        };
592        if ptr.is_null() {
593            return Err(ErrorStack::drain());
594        }
595        Ok(unsafe { Pkey::from_ptr(ptr) })
596    }
597
598    /// Load a public key from DER (`SubjectPublicKeyInfo`).
599    ///
600    /// # Errors
601    pub fn from_der(der: &[u8]) -> Result<Self, ErrorStack> {
602        let bio = MemBioBuf::new(der)?;
603        let ptr = unsafe { sys::d2i_PUBKEY_bio(bio.as_ptr(), std::ptr::null_mut()) };
604        if ptr.is_null() {
605            return Err(ErrorStack::drain());
606        }
607        Ok(unsafe { Pkey::from_ptr(ptr) })
608    }
609
610    /// Load a `SubjectPublicKeyInfo` DER public key using an explicit library context.
611    ///
612    /// Wraps `d2i_PUBKEY_ex` — unlike [`from_der`](Self::from_der) (which uses a
613    /// `BIO`), this function passes the raw byte pointer directly so that the
614    /// key's internal algorithm fetch is bound to `ctx`'s provider set.  Use
615    /// this when the loaded key will be used for EVP operations inside an isolated
616    /// (e.g. FIPS) context.
617    ///
618    /// # Errors
619    ///
620    /// Returns `Err` if the DER bytes are malformed or the algorithm is not
621    /// available in `ctx`.
622    pub fn from_der_in(ctx: &Arc<crate::lib_ctx::LibCtx>, der: &[u8]) -> Result<Self, ErrorStack> {
623        let mut ptr = der.as_ptr();
624        // SAFETY:
625        // - Non-null: `der.as_ptr()` is always valid for a shared slice (even if
626        //   zero-length; OpenSSL rejects zero-length DER before dereferencing).
627        // - `ctx.as_ptr()` is non-null by `LibCtx` constructor invariant; `ctx`
628        //   is kept alive by the `&Arc<LibCtx>` borrow for the entire call.
629        // - Lifetime: `ptr` points into `der` which lives for the call duration;
630        //   OpenSSL reads at most `der.len()` bytes before returning.
631        // - Exclusivity / no data races: `der` is a shared immutable borrow; no
632        //   other thread can mutate it.  `ptr` is a local stack copy — OpenSSL's
633        //   write to `*pp` touches only this variable, not the original slice.
634        let pkey = unsafe {
635            sys::d2i_PUBKEY_ex(
636                std::ptr::null_mut(),
637                std::ptr::addr_of_mut!(ptr),
638                i64::try_from(der.len()).unwrap_or(i64::MAX),
639                ctx.as_ptr(),
640                std::ptr::null(),
641            )
642        };
643        if pkey.is_null() {
644            return Err(ErrorStack::drain());
645        }
646        // SAFETY: `pkey` is non-null and freshly allocated by OpenSSL; we take
647        // exclusive ownership of the reference count.
648        Ok(unsafe { Pkey::from_ptr(pkey) })
649    }
650
651    /// Serialise the public key to PEM.
652    ///
653    /// # Errors
654    pub fn to_pem(&self) -> Result<Vec<u8>, ErrorStack> {
655        let mut bio = MemBio::new()?;
656        let rc = unsafe { sys::PEM_write_bio_PUBKEY(bio.as_ptr(), self.ptr) };
657        if rc != 1 {
658            return Err(ErrorStack::drain());
659        }
660        Ok(bio.into_vec())
661    }
662
663    /// Decode RFC 3279 `DHDomainParameters` DER (OID 1.2.840.10046.2.1).
664    ///
665    /// Used for PKINIT DH group negotiation: the KDC advertises domain
666    /// parameters as a bare DER blob whose outer OID is `dhpublicnumber`.
667    /// `SubjectPublicKeyInfo` DER (from [`from_der`](Self::from_der)) does not
668    /// handle this format; this method uses `OSSL_DECODER` instead.
669    ///
670    /// # Errors
671    ///
672    /// Returns `Err` if `data` is not a valid DER-encoded `DHDomainParameters`,
673    /// or if the DHX algorithm is not available in the default library context.
674    pub fn dhx_params_from_der(data: &[u8]) -> Result<Self, ErrorStack> {
675        decoder_pkey_from_der(data, c"DHX", std::ptr::null_mut())
676    }
677
678    /// Decode RFC 3279 `DHDomainParameters` DER using an explicit library context.
679    ///
680    /// Like [`dhx_params_from_der`](Self::dhx_params_from_der) but binds the
681    /// internal algorithm fetch to `ctx`'s provider set.
682    ///
683    /// # Errors
684    pub fn dhx_params_from_der_in(
685        ctx: &Arc<crate::lib_ctx::LibCtx>,
686        data: &[u8],
687    ) -> Result<Self, ErrorStack> {
688        decoder_pkey_from_der(data, c"DHX", ctx.as_ptr())
689    }
690
691    /// Decode standalone EC domain parameters DER (without a public key).
692    ///
693    /// # Errors
694    ///
695    /// Returns `Err` if `data` is not valid EC domain parameter DER, or if the
696    /// EC algorithm is not available in the default library context.
697    pub fn ec_params_from_der(data: &[u8]) -> Result<Self, ErrorStack> {
698        decoder_pkey_from_der(data, c"EC", std::ptr::null_mut())
699    }
700
701    /// Decode standalone EC domain parameters DER using an explicit library context.
702    ///
703    /// # Errors
704    pub fn ec_params_from_der_in(
705        ctx: &Arc<crate::lib_ctx::LibCtx>,
706        data: &[u8],
707    ) -> Result<Self, ErrorStack> {
708        decoder_pkey_from_der(data, c"EC", ctx.as_ptr())
709    }
710}
711
712// Upcast: every `Pkey<Private>` can be viewed as `Pkey<Public>`.
713impl From<Pkey<Private>> for Pkey<Public> {
714    fn from(k: Pkey<Private>) -> Self {
715        unsafe { sys::EVP_PKEY_up_ref(k.ptr) };
716        Pkey {
717            ptr: k.ptr,
718            _role: PhantomData,
719        }
720    }
721}
722
723// ── Key import from OSSL_PARAM ────────────────────────────────────────────────
724
725// EVP_PKEY_fromdata / EVP_PKEY_todata selection constants.
726// These must match the macros in <openssl/keymgmt.h>:
727//   EVP_PKEY_PUBLIC_KEY = OSSL_KEYMGMT_SELECT_ALL_PARAMETERS | SELECT_PUBLIC_KEY
728//                       = (0x04 | 0x80) | 0x02 = 0x86
729//   EVP_PKEY_KEYPAIR    = EVP_PKEY_PUBLIC_KEY | SELECT_PRIVATE_KEY
730//                       = 0x86 | 0x01 = 0x87
731// Using the bare SELECT_PUBLIC_KEY (0x02) would omit domain parameters, which
732// breaks EC keys (the curve group is a domain parameter, not a public-key param).
733const PKEY_PUBLIC_KEY: i32 = 0x86;
734const PKEY_KEYPAIR: i32 = 0x87;
735
736// OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS = 0x04 (from <openssl/keymgmt.h>)
737const SELECT_DOMAIN_PARAMETERS: i32 = 0x04;
738
739/// Decode DER-encoded key material of the given `keytype` using `OSSL_DECODER`.
740///
741/// `libctx` may be null (to use the global default context).
742fn decoder_pkey_from_der(
743    data: &[u8],
744    keytype: &std::ffi::CStr,
745    libctx: *mut sys::OSSL_LIB_CTX,
746) -> Result<Pkey<Public>, ErrorStack> {
747    let mut pkey: *mut sys::EVP_PKEY = std::ptr::null_mut();
748    // SAFETY: All pointers are either null (valid sentinel) or valid for the
749    // duration of the call. `input_struct` and `propquery` are null to let
750    // OpenSSL pick defaults.
751    let dctx = unsafe {
752        sys::OSSL_DECODER_CTX_new_for_pkey(
753            std::ptr::addr_of_mut!(pkey),
754            c"DER".as_ptr(),
755            std::ptr::null(), // input_struct: any
756            keytype.as_ptr(),
757            SELECT_DOMAIN_PARAMETERS,
758            libctx,
759            std::ptr::null(), // propquery: default
760        )
761    };
762    if dctx.is_null() {
763        return Err(ErrorStack::drain());
764    }
765    let mut ptr = data.as_ptr();
766    let mut len = data.len();
767    // SAFETY: `ptr` points into `data` which lives for the entire call.
768    // `OSSL_DECODER_from_data` advances `ptr` and decrements `len` in place;
769    // neither write escapes this scope.
770    let rc = unsafe {
771        sys::OSSL_DECODER_from_data(
772            dctx,
773            std::ptr::addr_of_mut!(ptr),
774            std::ptr::addr_of_mut!(len),
775        )
776    };
777    unsafe { sys::OSSL_DECODER_CTX_free(dctx) };
778    if rc != 1 || pkey.is_null() {
779        return Err(ErrorStack::drain());
780    }
781    // SAFETY: `pkey` is freshly allocated by OpenSSL; we take sole ownership.
782    Ok(unsafe { Pkey::from_ptr(pkey) })
783}
784
785/// Shared implementation for `EVP_PKEY_fromdata`.
786fn pkey_fromdata(
787    ctx: Option<&Arc<crate::lib_ctx::LibCtx>>,
788    pkey_type: &std::ffi::CStr,
789    params: &crate::params::Params<'_>,
790    selection: i32,
791) -> Result<*mut sys::EVP_PKEY, ErrorStack> {
792    let libctx = ctx.map_or(std::ptr::null_mut(), |c| c.as_ptr());
793    let pctx =
794        unsafe { sys::EVP_PKEY_CTX_new_from_name(libctx, pkey_type.as_ptr(), std::ptr::null()) };
795    if pctx.is_null() {
796        return Err(ErrorStack::drain());
797    }
798    let rc = unsafe { sys::EVP_PKEY_fromdata_init(pctx) };
799    if rc != 1 {
800        unsafe { sys::EVP_PKEY_CTX_free(pctx) };
801        return Err(ErrorStack::drain());
802    }
803    let mut pkey: *mut sys::EVP_PKEY = std::ptr::null_mut();
804    let rc = unsafe {
805        sys::EVP_PKEY_fromdata(
806            pctx,
807            std::ptr::addr_of_mut!(pkey),
808            selection,
809            // OSSL_PARAM array is read-only during fromdata; cast is safe.
810            params.as_ptr().cast_mut(),
811        )
812    };
813    unsafe { sys::EVP_PKEY_CTX_free(pctx) };
814    if rc != 1 || pkey.is_null() {
815        return Err(ErrorStack::drain());
816    }
817    Ok(pkey)
818}
819
820/// Shared implementation for `EVP_PKEY_todata`.
821fn pkey_todata(
822    ptr: *mut sys::EVP_PKEY,
823    selection: i32,
824) -> Result<crate::params::Params<'static>, ErrorStack> {
825    let mut out: *mut sys::OSSL_PARAM = std::ptr::null_mut();
826    let rc = unsafe { sys::EVP_PKEY_todata(ptr, selection, std::ptr::addr_of_mut!(out)) };
827    if rc != 1 || out.is_null() {
828        return Err(ErrorStack::drain());
829    }
830    // SAFETY: `out` is a freshly allocated OSSL_PARAM array from OpenSSL;
831    // Params takes ownership and will free it via OSSL_PARAM_free on drop.
832    Ok(unsafe { crate::params::Params::from_owned_ptr(out) })
833}
834
835impl Pkey<Private> {
836    /// Import a private key pair from an `OSSL_PARAM` array.
837    ///
838    /// Equivalent to `EVP_PKEY_fromdata` with `EVP_PKEY_KEYPAIR` selection.
839    /// Pass `ctx = None` to use the global default library context.
840    ///
841    /// # Errors
842    pub fn from_params(
843        ctx: Option<&Arc<crate::lib_ctx::LibCtx>>,
844        pkey_type: &std::ffi::CStr,
845        params: &crate::params::Params<'_>,
846    ) -> Result<Self, ErrorStack> {
847        pkey_fromdata(ctx, pkey_type, params, PKEY_KEYPAIR)
848            .map(|ptr| unsafe { Pkey::from_ptr(ptr) })
849    }
850
851    /// Export all key parameters (private + public) as an owned `OSSL_PARAM` array.
852    ///
853    /// Uses `EVP_PKEY_KEYPAIR` selection so both private and public material
854    /// are included in the returned array.
855    ///
856    /// # Errors
857    pub fn export(&self) -> Result<crate::params::Params<'static>, ErrorStack> {
858        pkey_todata(self.ptr, PKEY_KEYPAIR)
859    }
860}
861
862impl Pkey<Public> {
863    /// Import a public key from an `OSSL_PARAM` array.
864    ///
865    /// Equivalent to `EVP_PKEY_fromdata` with `EVP_PKEY_PUBLIC_KEY` selection.
866    /// Pass `ctx = None` to use the global default library context.
867    ///
868    /// # Errors
869    pub fn from_params(
870        ctx: Option<&Arc<crate::lib_ctx::LibCtx>>,
871        pkey_type: &std::ffi::CStr,
872        params: &crate::params::Params<'_>,
873    ) -> Result<Self, ErrorStack> {
874        pkey_fromdata(ctx, pkey_type, params, PKEY_PUBLIC_KEY)
875            .map(|ptr| unsafe { Pkey::from_ptr(ptr) })
876    }
877
878    /// Export the public key parameters as an owned `OSSL_PARAM` array.
879    ///
880    /// Uses `EVP_PKEY_PUBLIC_KEY` selection.
881    ///
882    /// # Errors
883    pub fn export(&self) -> Result<crate::params::Params<'static>, ErrorStack> {
884        pkey_todata(self.ptr, PKEY_PUBLIC_KEY)
885    }
886}
887
888// ── Legacy DER encoding ──────────────────────────────────────────────────────
889
890impl<T: HasPrivate> Pkey<T> {
891    /// Encode this private key to legacy raw-key DER (not PKCS#8).
892    ///
893    /// Wraps `i2d_PrivateKey`.  The output is the algorithm-specific raw key
894    /// structure (e.g. `RSAPrivateKey` / RFC 3447 for RSA, `ECPrivateKey` /
895    /// RFC 5915 for EC) — **not** the `PrivateKeyInfo` / PKCS#8 wrapper.
896    ///
897    /// Use this for interoperability with software that requires the legacy
898    /// format.  For new code prefer [`to_pkcs8_der`](crate::pkey::Pkey::to_pkcs8_der)
899    /// (PKCS#8 / RFC 5958), which is algorithm-agnostic and more widely supported
900    /// by modern toolkits.
901    ///
902    /// # Errors
903    ///
904    /// Returns `Err` if serialisation fails (e.g. the algorithm does not support
905    /// legacy DER export, such as some post-quantum algorithms).
906    pub fn to_der_legacy(&self) -> Result<Vec<u8>, ErrorStack> {
907        // First call with null to query the required output length.
908        // SAFETY:
909        // - Non-null: `self.ptr` is non-null by `Pkey` constructor invariant.
910        // - Lifetime: `self.ptr` is valid for the duration of `self`; this call
911        //   does not store the pointer beyond the call.
912        // - Exclusivity / no data races: `&self` ensures no concurrent mutable
913        //   access to `self.ptr`; the null second argument means OpenSSL only
914        //   reads the key, it does not write through `pp`.
915        let len = unsafe { sys::i2d_PrivateKey(self.ptr, std::ptr::null_mut()) };
916        if len < 0 {
917            return Err(ErrorStack::drain());
918        }
919        let mut buf = vec![0u8; usize::try_from(len).unwrap_or(0)];
920        let mut out_ptr = buf.as_mut_ptr();
921        // SAFETY:
922        // - Non-null: `self.ptr` is non-null (constructor invariant).
923        // - `out_ptr` points into `buf` which has capacity `len` bytes; OpenSSL
924        //   writes exactly `len` bytes and advances `out_ptr` past the data.
925        // - Lifetime: `buf` is alive for the duration of this call; OpenSSL does
926        //   not retain the pointer after returning.
927        // - Exclusivity / no data races: `buf` is exclusively owned here; no
928        //   other reference to its backing memory exists during the call.
929        let written = unsafe { sys::i2d_PrivateKey(self.ptr, std::ptr::addr_of_mut!(out_ptr)) };
930        if written < 0 {
931            return Err(ErrorStack::drain());
932        }
933        buf.truncate(usize::try_from(written).unwrap_or(0));
934        Ok(buf)
935    }
936}
937
938// ── KeygenCtx — key generation ────────────────────────────────────────────────
939
940/// Context for generating asymmetric key pairs (`EVP_PKEY_CTX` in keygen mode).
941pub struct KeygenCtx {
942    ptr: *mut sys::EVP_PKEY_CTX,
943}
944
945impl KeygenCtx {
946    /// Create a keygen context for the named algorithm.
947    ///
948    /// Common names: `c"RSA"`, `c"EC"`, `c"ED25519"`, `c"X25519"`.
949    ///
950    /// # Errors
951    pub fn new(name: &std::ffi::CStr) -> Result<Self, ErrorStack> {
952        let ptr = unsafe {
953            sys::EVP_PKEY_CTX_new_from_name(std::ptr::null_mut(), name.as_ptr(), std::ptr::null())
954        };
955        if ptr.is_null() {
956            return Err(ErrorStack::drain());
957        }
958        let rc = unsafe { sys::EVP_PKEY_keygen_init(ptr) };
959        if rc != 1 {
960            unsafe { sys::EVP_PKEY_CTX_free(ptr) };
961            return Err(ErrorStack::drain());
962        }
963        Ok(KeygenCtx { ptr })
964    }
965
966    /// Configure parameters before calling `generate`.
967    ///
968    /// # Errors
969    pub fn set_params(&mut self, params: &crate::params::Params<'_>) -> Result<(), ErrorStack> {
970        // SAFETY:
971        // - self.ptr is non-null (constructor invariant)
972        // - params.as_ptr() is valid for the duration of this call
973        // - &mut self ensures exclusive access; no concurrent reads or writes
974        crate::ossl_call!(sys::EVP_PKEY_CTX_set_params(self.ptr, params.as_ptr()))
975    }
976
977    /// Retrieve parameter values from this keygen context.
978    ///
979    /// Build a [`Params`][crate::params::Params] with placeholder values for the
980    /// keys you want, call this method, then read back with `params.get_*`.
981    ///
982    /// Useful for reading algorithm-negotiated parameters after keygen initialisation
983    /// (e.g. security strength, EC group name, RSA modulus size).
984    ///
985    /// # Errors
986    ///
987    /// Returns `Err` if `EVP_PKEY_CTX_get_params` fails.
988    pub fn get_params(&self, params: &mut crate::params::Params<'_>) -> Result<(), ErrorStack> {
989        // SAFETY:
990        // - self.ptr is non-null (constructor invariant)
991        // - params.as_mut_ptr() is valid for the duration of this call
992        // - &self ensures no concurrent mutable access to self.ptr
993        crate::ossl_call!(sys::EVP_PKEY_CTX_get_params(self.ptr, params.as_mut_ptr()))
994    }
995
996    /// Return the security strength of the key operation in bits.
997    ///
998    /// Available after keygen initialisation; reads `OSSL_PKEY_PARAM_SECURITY_BITS`
999    /// (`"security-bits"`).
1000    ///
1001    /// # Errors
1002    ///
1003    /// Returns `Err` if the context does not support this parameter or is not
1004    /// sufficiently initialised.
1005    pub fn security_bits(&self) -> Result<u32, ErrorStack> {
1006        let mut params = crate::params::ParamBuilder::new()?
1007            .push_uint(c"security-bits", 0)?
1008            .build()?;
1009        // SAFETY: same as get_params — self.ptr is non-null, params pointer is valid.
1010        crate::ossl_call!(sys::EVP_PKEY_CTX_get_params(self.ptr, params.as_mut_ptr()))?;
1011        params
1012            .get_uint(c"security-bits")
1013            .map_err(|_| crate::error::ErrorStack::drain())
1014    }
1015
1016    /// Generate a key pair.
1017    ///
1018    /// # Errors
1019    pub fn generate(&mut self) -> Result<Pkey<Private>, ErrorStack> {
1020        let mut key: *mut sys::EVP_PKEY = std::ptr::null_mut();
1021        crate::ossl_call!(sys::EVP_PKEY_keygen(self.ptr, std::ptr::addr_of_mut!(key)))?;
1022        if key.is_null() {
1023            return Err(ErrorStack::drain());
1024        }
1025        Ok(unsafe { Pkey::from_ptr(key) })
1026    }
1027}
1028
1029impl Drop for KeygenCtx {
1030    fn drop(&mut self) {
1031        unsafe { sys::EVP_PKEY_CTX_free(self.ptr) };
1032    }
1033}
1034
1035// ── Signer — streaming DigestSign ─────────────────────────────────────────────
1036
1037/// Parameters for creating a [`Signer`] or [`Verifier`].
1038#[derive(Default)]
1039pub struct SignInit<'a> {
1040    /// Digest algorithm, or `None` for pre-hashed / `EdDSA` one-shot mode.
1041    pub digest: Option<&'a crate::digest::DigestAlg>,
1042    /// Optional parameters (e.g. RSA PSS salt length).
1043    pub params: Option<&'a crate::params::Params<'a>>,
1044}
1045
1046/// Streaming `DigestSign` context.
1047///
1048/// Call `update` zero or more times, then `finish` to produce the signature.
1049pub struct Signer {
1050    ctx: crate::digest::DigestCtx,
1051    /// The key is kept alive for the duration of the signer.
1052    _key: Pkey<Private>,
1053}
1054
1055impl Signer {
1056    /// Create a signer.
1057    ///
1058    /// # Errors
1059    pub fn new(key: &Pkey<Private>, init: &SignInit<'_>) -> Result<Self, ErrorStack> {
1060        let ctx = alloc_digest_ctx()?;
1061        // Resolve digest name for EVP_DigestSignInit_ex (NULL for Ed25519/one-shot).
1062        let md_name_ptr = if let Some(d) = init.digest {
1063            let p = unsafe { sys::OBJ_nid2sn(d.nid()) };
1064            if p.is_null() {
1065                return Err(ErrorStack::drain());
1066            }
1067            p
1068        } else {
1069            std::ptr::null()
1070        };
1071        let params_ptr = init
1072            .params
1073            .map_or(crate::params::null_params(), crate::params::Params::as_ptr);
1074        let rc = unsafe {
1075            sys::EVP_DigestSignInit_ex(
1076                ctx.as_ptr(),
1077                std::ptr::null_mut(),
1078                md_name_ptr,
1079                std::ptr::null_mut(),
1080                std::ptr::null(),
1081                key.ptr,
1082                params_ptr,
1083            )
1084        };
1085        if rc != 1 {
1086            return Err(ErrorStack::drain());
1087        }
1088        Ok(Signer {
1089            ctx,
1090            _key: key.clone(),
1091        })
1092    }
1093
1094    /// Feed data into the hash.
1095    ///
1096    /// # Errors
1097    pub fn update(&mut self, data: &[u8]) -> Result<(), ErrorStack> {
1098        crate::ossl_call!(sys::EVP_DigestSignUpdate(
1099            self.ctx.as_ptr(),
1100            data.as_ptr().cast(),
1101            data.len()
1102        ))
1103    }
1104
1105    /// Finalise and return the signature.
1106    ///
1107    /// Not supported by pure one-shot algorithms such as Ed25519 — use
1108    /// [`sign_oneshot`](Self::sign_oneshot) for those.
1109    ///
1110    /// # Errors
1111    pub fn finish(&mut self) -> Result<Vec<u8>, ErrorStack> {
1112        // First call with null buf to get the required size.
1113        let mut siglen: usize = 0;
1114        let rc = unsafe {
1115            sys::EVP_DigestSignFinal(
1116                self.ctx.as_ptr(),
1117                std::ptr::null_mut(),
1118                std::ptr::addr_of_mut!(siglen),
1119            )
1120        };
1121        if rc != 1 {
1122            return Err(ErrorStack::drain());
1123        }
1124        let mut sig = vec![0u8; siglen];
1125        let rc = unsafe {
1126            sys::EVP_DigestSignFinal(
1127                self.ctx.as_ptr(),
1128                sig.as_mut_ptr(),
1129                std::ptr::addr_of_mut!(siglen),
1130            )
1131        };
1132        if rc != 1 {
1133            return Err(ErrorStack::drain());
1134        }
1135        sig.truncate(siglen);
1136        Ok(sig)
1137    }
1138
1139    /// One-shot sign over `data`.
1140    ///
1141    /// Required for algorithms that do not support streaming (Ed25519, Ed448).
1142    /// For algorithms that do support streaming, prefer `update` + `finish`.
1143    ///
1144    /// # Errors
1145    pub fn sign_oneshot(&mut self, data: &[u8]) -> Result<Vec<u8>, ErrorStack> {
1146        // First call: query the required signature length.
1147        let mut siglen: usize = 0;
1148        let rc = unsafe {
1149            sys::EVP_DigestSign(
1150                self.ctx.as_ptr(),
1151                std::ptr::null_mut(),
1152                std::ptr::addr_of_mut!(siglen),
1153                data.as_ptr(),
1154                data.len(),
1155            )
1156        };
1157        if rc != 1 {
1158            return Err(ErrorStack::drain());
1159        }
1160        let mut sig = vec![0u8; siglen];
1161        let rc = unsafe {
1162            sys::EVP_DigestSign(
1163                self.ctx.as_ptr(),
1164                sig.as_mut_ptr(),
1165                std::ptr::addr_of_mut!(siglen),
1166                data.as_ptr(),
1167                data.len(),
1168            )
1169        };
1170        if rc != 1 {
1171            return Err(ErrorStack::drain());
1172        }
1173        sig.truncate(siglen);
1174        Ok(sig)
1175    }
1176
1177    /// One-shot sign over `data` into a caller-provided buffer `sig`.
1178    ///
1179    /// The buffer must be at least as large as the maximum signature size for
1180    /// the key (use `EVP_PKEY_get_size` or the algorithm's known fixed length).
1181    /// For algorithms with a fixed signature size (e.g. ML-DSA, Ed25519), the
1182    /// caller can pre-allocate the exact size and avoid the null-output query.
1183    ///
1184    /// Returns the number of bytes written.  The caller should truncate `sig`
1185    /// to the returned length if the actual size may differ from the buffer size.
1186    ///
1187    /// # Errors
1188    pub fn sign_into(&mut self, data: &[u8], sig: &mut [u8]) -> Result<usize, ErrorStack> {
1189        let mut siglen = sig.len();
1190        let rc = unsafe {
1191            sys::EVP_DigestSign(
1192                self.ctx.as_ptr(),
1193                sig.as_mut_ptr(),
1194                std::ptr::addr_of_mut!(siglen),
1195                data.as_ptr(),
1196                data.len(),
1197            )
1198        };
1199        if rc != 1 {
1200            return Err(ErrorStack::drain());
1201        }
1202        Ok(siglen)
1203    }
1204}
1205
1206// ── Verifier — streaming DigestVerify ─────────────────────────────────────────
1207
1208/// Streaming `DigestVerify` context.
1209pub struct Verifier {
1210    ctx: crate::digest::DigestCtx,
1211    _key: Pkey<Public>,
1212}
1213
1214impl Verifier {
1215    /// Create a verifier.
1216    ///
1217    /// # Errors
1218    pub fn new(key: &Pkey<Public>, init: &SignInit<'_>) -> Result<Self, ErrorStack> {
1219        let ctx = alloc_digest_ctx()?;
1220        // Resolve digest name for EVP_DigestVerifyInit_ex (NULL for Ed25519/one-shot).
1221        let md_name_ptr = if let Some(d) = init.digest {
1222            let p = unsafe { sys::OBJ_nid2sn(d.nid()) };
1223            if p.is_null() {
1224                return Err(ErrorStack::drain());
1225            }
1226            p
1227        } else {
1228            std::ptr::null()
1229        };
1230        let params_ptr = init
1231            .params
1232            .map_or(crate::params::null_params(), crate::params::Params::as_ptr);
1233        let rc = unsafe {
1234            sys::EVP_DigestVerifyInit_ex(
1235                ctx.as_ptr(),
1236                std::ptr::null_mut(),
1237                md_name_ptr,
1238                std::ptr::null_mut(),
1239                std::ptr::null(),
1240                key.ptr,
1241                params_ptr,
1242            )
1243        };
1244        if rc != 1 {
1245            return Err(ErrorStack::drain());
1246        }
1247        Ok(Verifier {
1248            ctx,
1249            _key: key.clone(),
1250        })
1251    }
1252
1253    /// Feed data into the hash.
1254    ///
1255    /// # Errors
1256    pub fn update(&mut self, data: &[u8]) -> Result<(), ErrorStack> {
1257        crate::ossl_call!(sys::EVP_DigestVerifyUpdate(
1258            self.ctx.as_ptr(),
1259            data.as_ptr().cast(),
1260            data.len()
1261        ))
1262    }
1263
1264    /// Verify `signature` against all data fed via `update`.
1265    ///
1266    /// Returns `Ok(true)` if valid, `Ok(false)` if the signature is incorrect,
1267    /// or `Err` on a fatal OpenSSL error.
1268    ///
1269    /// Not supported by pure one-shot algorithms such as Ed25519 — use
1270    /// [`verify_oneshot`](Self::verify_oneshot) for those.
1271    ///
1272    /// # Errors
1273    pub fn verify(&mut self, signature: &[u8]) -> Result<bool, ErrorStack> {
1274        let rc = unsafe {
1275            sys::EVP_DigestVerifyFinal(self.ctx.as_ptr(), signature.as_ptr(), signature.len())
1276        };
1277        match rc {
1278            1 => Ok(true),
1279            0 => Ok(false),
1280            _ => Err(ErrorStack::drain()),
1281        }
1282    }
1283
1284    /// One-shot verify `signature` over `data`.
1285    ///
1286    /// Required for algorithms that do not support streaming (Ed25519, Ed448).
1287    ///
1288    /// # Errors
1289    pub fn verify_oneshot(&mut self, data: &[u8], signature: &[u8]) -> Result<bool, ErrorStack> {
1290        let rc = unsafe {
1291            sys::EVP_DigestVerify(
1292                self.ctx.as_ptr(),
1293                signature.as_ptr(),
1294                signature.len(),
1295                data.as_ptr(),
1296                data.len(),
1297            )
1298        };
1299        match rc {
1300            1 => Ok(true),
1301            0 => Ok(false),
1302            _ => Err(ErrorStack::drain()),
1303        }
1304    }
1305}
1306
1307// ── Internal helper: allocate an EVP_MD_CTX for DigestSign/Verify ─────────────
1308
1309/// Allocate a fresh, algorithm-unbound `EVP_MD_CTX` for use with
1310/// `EVP_DigestSign*Init_ex` / `EVP_DigestVerify*Init_ex`.
1311fn alloc_digest_ctx() -> Result<crate::digest::DigestCtx, ErrorStack> {
1312    let ctx_ptr = unsafe { sys::EVP_MD_CTX_new() };
1313    if ctx_ptr.is_null() {
1314        return Err(ErrorStack::drain());
1315    }
1316    // SAFETY: DigestCtx takes ownership; ptr is valid and non-null.
1317    Ok(unsafe { crate::digest::DigestCtx::from_ptr(ctx_ptr) })
1318}
1319
1320// ── DeriveCtx — ECDH / DH key agreement ──────────────────────────────────────
1321
1322/// Asymmetric key-agreement context (`EVP_PKEY_CTX` in derive mode).
1323pub struct DeriveCtx {
1324    ptr: *mut sys::EVP_PKEY_CTX,
1325}
1326
1327impl DeriveCtx {
1328    /// Create a derive context from a private key.
1329    ///
1330    /// # Errors
1331    pub fn new(key: &Pkey<Private>) -> Result<Self, ErrorStack> {
1332        let ptr = unsafe {
1333            sys::EVP_PKEY_CTX_new_from_pkey(std::ptr::null_mut(), key.ptr, std::ptr::null())
1334        };
1335        if ptr.is_null() {
1336            return Err(ErrorStack::drain());
1337        }
1338        crate::ossl_call!(sys::EVP_PKEY_derive_init(ptr)).map_err(|e| {
1339            unsafe { sys::EVP_PKEY_CTX_free(ptr) };
1340            e
1341        })?;
1342        Ok(DeriveCtx { ptr })
1343    }
1344
1345    /// Set the peer's public key.
1346    ///
1347    /// # Errors
1348    pub fn set_peer(&mut self, peer: &Pkey<Public>) -> Result<(), ErrorStack> {
1349        crate::ossl_call!(sys::EVP_PKEY_derive_set_peer(self.ptr, peer.ptr))
1350    }
1351
1352    /// Derive the shared secret into `out`.
1353    ///
1354    /// Returns the number of bytes written.
1355    ///
1356    /// # Errors
1357    pub fn derive(&mut self, out: &mut [u8]) -> Result<usize, ErrorStack> {
1358        let mut len = out.len();
1359        crate::ossl_call!(sys::EVP_PKEY_derive(
1360            self.ptr,
1361            out.as_mut_ptr(),
1362            std::ptr::addr_of_mut!(len)
1363        ))?;
1364        Ok(len)
1365    }
1366
1367    /// Query the required output length (call with empty slice).
1368    ///
1369    /// # Errors
1370    pub fn derive_len(&mut self) -> Result<usize, ErrorStack> {
1371        let mut len: usize = 0;
1372        crate::ossl_call!(sys::EVP_PKEY_derive(
1373            self.ptr,
1374            std::ptr::null_mut(),
1375            std::ptr::addr_of_mut!(len)
1376        ))?;
1377        Ok(len)
1378    }
1379}
1380
1381impl Drop for DeriveCtx {
1382    fn drop(&mut self) {
1383        unsafe { sys::EVP_PKEY_CTX_free(self.ptr) };
1384    }
1385}
1386
1387// ── PkeyEncryptCtx / PkeyDecryptCtx ──────────────────────────────────────────
1388
1389/// RSA asymmetric encryption context.
1390pub struct PkeyEncryptCtx {
1391    ptr: *mut sys::EVP_PKEY_CTX,
1392}
1393
1394impl PkeyEncryptCtx {
1395    /// Create an encryption context from a public key.
1396    ///
1397    /// `params` is applied immediately after init if `Some`.  Typical use:
1398    /// ```ignore
1399    /// let oaep = ParamBuilder::new()?.push_utf8_string(c"pad-mode", c"oaep")?.build()?;
1400    /// let ctx = PkeyEncryptCtx::new(&pub_key, Some(&oaep))?;
1401    /// ```
1402    ///
1403    /// # Errors
1404    pub fn new(
1405        key: &Pkey<Public>,
1406        params: Option<&crate::params::Params<'_>>,
1407    ) -> Result<Self, ErrorStack> {
1408        let ptr = unsafe {
1409            sys::EVP_PKEY_CTX_new_from_pkey(std::ptr::null_mut(), key.ptr, std::ptr::null())
1410        };
1411        if ptr.is_null() {
1412            return Err(ErrorStack::drain());
1413        }
1414        crate::ossl_call!(sys::EVP_PKEY_encrypt_init(ptr)).map_err(|e| {
1415            unsafe { sys::EVP_PKEY_CTX_free(ptr) };
1416            e
1417        })?;
1418        let ctx = PkeyEncryptCtx { ptr };
1419        if let Some(p) = params {
1420            crate::ossl_call!(sys::EVP_PKEY_CTX_set_params(ctx.ptr, p.as_ptr())).map_err(|e| {
1421                unsafe { sys::EVP_PKEY_CTX_free(ptr) };
1422                e
1423            })?;
1424        }
1425        Ok(ctx)
1426    }
1427
1428    /// Configure parameters (e.g. RSA padding mode).
1429    ///
1430    /// # Errors
1431    pub fn set_params(&mut self, params: &crate::params::Params<'_>) -> Result<(), ErrorStack> {
1432        crate::ossl_call!(sys::EVP_PKEY_CTX_set_params(self.ptr, params.as_ptr()))
1433    }
1434
1435    /// Encrypt `plaintext` into `ciphertext`.
1436    ///
1437    /// Returns the number of bytes written.
1438    ///
1439    /// # Errors
1440    pub fn encrypt(
1441        &mut self,
1442        plaintext: &[u8],
1443        ciphertext: &mut [u8],
1444    ) -> Result<usize, ErrorStack> {
1445        let mut outlen = ciphertext.len();
1446        crate::ossl_call!(sys::EVP_PKEY_encrypt(
1447            self.ptr,
1448            ciphertext.as_mut_ptr(),
1449            std::ptr::addr_of_mut!(outlen),
1450            plaintext.as_ptr(),
1451            plaintext.len()
1452        ))?;
1453        Ok(outlen)
1454    }
1455
1456    /// Query the ciphertext length for a given plaintext length.
1457    ///
1458    /// # Errors
1459    pub fn encrypt_len(&mut self, plaintext_len: usize) -> Result<usize, ErrorStack> {
1460        let mut outlen: usize = 0;
1461        crate::ossl_call!(sys::EVP_PKEY_encrypt(
1462            self.ptr,
1463            std::ptr::null_mut(),
1464            std::ptr::addr_of_mut!(outlen),
1465            std::ptr::null(),
1466            plaintext_len
1467        ))?;
1468        Ok(outlen)
1469    }
1470}
1471
1472impl Drop for PkeyEncryptCtx {
1473    fn drop(&mut self) {
1474        unsafe { sys::EVP_PKEY_CTX_free(self.ptr) };
1475    }
1476}
1477
1478/// RSA asymmetric decryption context.
1479pub struct PkeyDecryptCtx {
1480    ptr: *mut sys::EVP_PKEY_CTX,
1481}
1482
1483impl PkeyDecryptCtx {
1484    /// Create a decryption context from a private key.
1485    ///
1486    /// `params` is applied immediately after init if `Some`.
1487    ///
1488    /// # Errors
1489    pub fn new(
1490        key: &Pkey<Private>,
1491        params: Option<&crate::params::Params<'_>>,
1492    ) -> Result<Self, ErrorStack> {
1493        let ptr = unsafe {
1494            sys::EVP_PKEY_CTX_new_from_pkey(std::ptr::null_mut(), key.ptr, std::ptr::null())
1495        };
1496        if ptr.is_null() {
1497            return Err(ErrorStack::drain());
1498        }
1499        crate::ossl_call!(sys::EVP_PKEY_decrypt_init(ptr)).map_err(|e| {
1500            unsafe { sys::EVP_PKEY_CTX_free(ptr) };
1501            e
1502        })?;
1503        let ctx = PkeyDecryptCtx { ptr };
1504        if let Some(p) = params {
1505            crate::ossl_call!(sys::EVP_PKEY_CTX_set_params(ctx.ptr, p.as_ptr())).map_err(|e| {
1506                unsafe { sys::EVP_PKEY_CTX_free(ptr) };
1507                e
1508            })?;
1509        }
1510        Ok(ctx)
1511    }
1512
1513    /// Configure parameters.
1514    ///
1515    /// # Errors
1516    pub fn set_params(&mut self, params: &crate::params::Params<'_>) -> Result<(), ErrorStack> {
1517        crate::ossl_call!(sys::EVP_PKEY_CTX_set_params(self.ptr, params.as_ptr()))
1518    }
1519
1520    /// Decrypt `ciphertext` into `plaintext`.
1521    ///
1522    /// Returns the number of bytes written.
1523    ///
1524    /// # Errors
1525    pub fn decrypt(
1526        &mut self,
1527        ciphertext: &[u8],
1528        plaintext: &mut [u8],
1529    ) -> Result<usize, ErrorStack> {
1530        let mut outlen = plaintext.len();
1531        crate::ossl_call!(sys::EVP_PKEY_decrypt(
1532            self.ptr,
1533            plaintext.as_mut_ptr(),
1534            std::ptr::addr_of_mut!(outlen),
1535            ciphertext.as_ptr(),
1536            ciphertext.len()
1537        ))?;
1538        Ok(outlen)
1539    }
1540}
1541
1542impl Drop for PkeyDecryptCtx {
1543    fn drop(&mut self) {
1544        unsafe { sys::EVP_PKEY_CTX_free(self.ptr) };
1545    }
1546}
1547
1548// ── EncapCtx / DecapCtx — KEM (OpenSSL 3.2+) ────────────────────────────────
1549
1550/// KEM encapsulation output.
1551#[cfg(ossl320)]
1552pub struct EncapResult {
1553    /// Wrapped key (encoded shared secret).
1554    pub wrapped_key: Vec<u8>,
1555    /// Shared secret (plaintext).
1556    pub shared_secret: Vec<u8>,
1557}
1558
1559/// KEM encapsulation context (recipient's public key).
1560#[cfg(ossl320)]
1561pub struct EncapCtx {
1562    ptr: *mut sys::EVP_PKEY_CTX,
1563}
1564
1565#[cfg(ossl320)]
1566impl EncapCtx {
1567    /// Create a KEM encapsulation context from the recipient's public key.
1568    ///
1569    /// # Errors
1570    pub fn new(key: &Pkey<Public>) -> Result<Self, ErrorStack> {
1571        let ptr = unsafe {
1572            sys::EVP_PKEY_CTX_new_from_pkey(std::ptr::null_mut(), key.ptr, std::ptr::null())
1573        };
1574        if ptr.is_null() {
1575            return Err(ErrorStack::drain());
1576        }
1577        crate::ossl_call!(sys::EVP_PKEY_encapsulate_init(ptr, std::ptr::null())).map_err(|e| {
1578            unsafe { sys::EVP_PKEY_CTX_free(ptr) };
1579            e
1580        })?;
1581        Ok(EncapCtx { ptr })
1582    }
1583
1584    /// Perform encapsulation, returning the wrapped key and shared secret.
1585    ///
1586    /// # Errors
1587    pub fn encapsulate(&mut self) -> Result<EncapResult, ErrorStack> {
1588        let mut wkeylen: usize = 0;
1589        let mut sslen: usize = 0;
1590        // Query lengths first.
1591        let rc = unsafe {
1592            sys::EVP_PKEY_encapsulate(
1593                self.ptr,
1594                std::ptr::null_mut(),
1595                std::ptr::addr_of_mut!(wkeylen),
1596                std::ptr::null_mut(),
1597                std::ptr::addr_of_mut!(sslen),
1598            )
1599        };
1600        if rc != 1 {
1601            return Err(ErrorStack::drain());
1602        }
1603        let mut wrapped_key = vec![0u8; wkeylen];
1604        let mut shared_secret = vec![0u8; sslen];
1605        crate::ossl_call!(sys::EVP_PKEY_encapsulate(
1606            self.ptr,
1607            wrapped_key.as_mut_ptr(),
1608            std::ptr::addr_of_mut!(wkeylen),
1609            shared_secret.as_mut_ptr(),
1610            std::ptr::addr_of_mut!(sslen)
1611        ))?;
1612        wrapped_key.truncate(wkeylen);
1613        shared_secret.truncate(sslen);
1614        Ok(EncapResult {
1615            wrapped_key,
1616            shared_secret,
1617        })
1618    }
1619}
1620
1621#[cfg(ossl320)]
1622impl Drop for EncapCtx {
1623    fn drop(&mut self) {
1624        unsafe { sys::EVP_PKEY_CTX_free(self.ptr) };
1625    }
1626}
1627
1628/// KEM decapsulation context (recipient's private key).
1629#[cfg(ossl320)]
1630pub struct DecapCtx {
1631    ptr: *mut sys::EVP_PKEY_CTX,
1632}
1633
1634#[cfg(ossl320)]
1635impl DecapCtx {
1636    /// Create a KEM decapsulation context from the recipient's private key.
1637    ///
1638    /// # Errors
1639    pub fn new(key: &Pkey<Private>) -> Result<Self, ErrorStack> {
1640        let ptr = unsafe {
1641            sys::EVP_PKEY_CTX_new_from_pkey(std::ptr::null_mut(), key.ptr, std::ptr::null())
1642        };
1643        if ptr.is_null() {
1644            return Err(ErrorStack::drain());
1645        }
1646        crate::ossl_call!(sys::EVP_PKEY_decapsulate_init(ptr, std::ptr::null())).map_err(|e| {
1647            unsafe { sys::EVP_PKEY_CTX_free(ptr) };
1648            e
1649        })?;
1650        Ok(DecapCtx { ptr })
1651    }
1652
1653    /// Recover the shared secret from a wrapped key.
1654    ///
1655    /// # Errors
1656    pub fn decapsulate(&mut self, wrapped_key: &[u8]) -> Result<Vec<u8>, ErrorStack> {
1657        let mut sslen: usize = 0;
1658        // Query output length.
1659        let rc = unsafe {
1660            sys::EVP_PKEY_decapsulate(
1661                self.ptr,
1662                std::ptr::null_mut(),
1663                std::ptr::addr_of_mut!(sslen),
1664                wrapped_key.as_ptr(),
1665                wrapped_key.len(),
1666            )
1667        };
1668        if rc != 1 {
1669            return Err(ErrorStack::drain());
1670        }
1671        let mut ss = vec![0u8; sslen];
1672        crate::ossl_call!(sys::EVP_PKEY_decapsulate(
1673            self.ptr,
1674            ss.as_mut_ptr(),
1675            std::ptr::addr_of_mut!(sslen),
1676            wrapped_key.as_ptr(),
1677            wrapped_key.len()
1678        ))?;
1679        ss.truncate(sslen);
1680        Ok(ss)
1681    }
1682}
1683
1684#[cfg(ossl320)]
1685impl Drop for DecapCtx {
1686    fn drop(&mut self) {
1687        unsafe { sys::EVP_PKEY_CTX_free(self.ptr) };
1688    }
1689}
1690
1691// ── RawSigner — pre-hashed / no-digest signing ───────────────────────────────
1692
1693/// Raw (no-digest) signing context wrapping `EVP_PKEY_CTX` after `EVP_PKEY_sign_init`.
1694///
1695/// Use this for algorithms where the caller has already hashed the data, such
1696/// as raw ECDSA (data is the hash) or raw RSA with explicit padding.  For
1697/// algorithms that hash internally, use [`Signer`] or `MessageSigner`.
1698///
1699/// The context is reusable: after a successful [`sign`](Self::sign) call the
1700/// init state is preserved, so the same padding parameters apply to further
1701/// sign calls with the same key.
1702pub struct RawSigner {
1703    ctx: *mut sys::EVP_PKEY_CTX,
1704}
1705
1706// SAFETY: EVP_PKEY_CTX is not thread-safe on a shared pointer, but
1707// RawSigner owns its ctx exclusively and &mut self enforces single-caller.
1708unsafe impl Send for RawSigner {}
1709
1710impl RawSigner {
1711    /// Create and initialise a sign context (`EVP_PKEY_CTX_new_from_pkey` +
1712    /// `EVP_PKEY_sign_init`).
1713    ///
1714    /// Pass `libctx = Some(ctx)` to restrict provider lookup to that library
1715    /// context; `None` uses the key's own library context.
1716    ///
1717    /// # Errors
1718    pub fn new(
1719        key: &Pkey<Private>,
1720        libctx: Option<&Arc<crate::lib_ctx::LibCtx>>,
1721    ) -> Result<Self, ErrorStack> {
1722        let lctx = libctx.map_or(std::ptr::null_mut(), |c| c.as_ptr());
1723        let ptr = unsafe { sys::EVP_PKEY_CTX_new_from_pkey(lctx, key.ptr, std::ptr::null()) };
1724        if ptr.is_null() {
1725            return Err(ErrorStack::drain());
1726        }
1727        crate::ossl_call!(sys::EVP_PKEY_sign_init(ptr)).map_err(|e| {
1728            unsafe { sys::EVP_PKEY_CTX_free(ptr) };
1729            e
1730        })?;
1731        Ok(RawSigner { ctx: ptr })
1732    }
1733
1734    /// Apply parameters after init (e.g. RSA padding mode, salt length).
1735    ///
1736    /// # Errors
1737    pub fn set_params(&mut self, params: &crate::params::Params<'_>) -> Result<(), ErrorStack> {
1738        // SAFETY:
1739        // - self.ctx is non-null (constructor invariant)
1740        // - params.as_ptr() is valid for the duration of this call
1741        // - &mut self ensures exclusive access; no concurrent reads or writes
1742        crate::ossl_call!(sys::EVP_PKEY_CTX_set_params(self.ctx, params.as_ptr()))
1743    }
1744
1745    /// Retrieve parameter values from this sign context.
1746    ///
1747    /// Build a [`Params`][crate::params::Params] with placeholder values for the
1748    /// keys you want, call this method, then read back with `params.get_*`.
1749    ///
1750    /// Useful for reading operation-context parameters such as the current RSA
1751    /// padding mode or signature algorithm identifier after initialisation.
1752    ///
1753    /// # Errors
1754    ///
1755    /// Returns `Err` if `EVP_PKEY_CTX_get_params` fails.
1756    pub fn get_params(&self, params: &mut crate::params::Params<'_>) -> Result<(), ErrorStack> {
1757        // SAFETY:
1758        // - self.ctx is non-null (constructor invariant)
1759        // - params.as_mut_ptr() is valid for the duration of this call
1760        // - &self ensures no concurrent mutable access to self.ctx
1761        crate::ossl_call!(sys::EVP_PKEY_CTX_get_params(self.ctx, params.as_mut_ptr()))
1762    }
1763
1764    /// Return the security strength of the key operation in bits.
1765    ///
1766    /// Reads `OSSL_PKEY_PARAM_SECURITY_BITS` (`"security-bits"`) from the sign
1767    /// context.  Available after `EVP_PKEY_sign_init` on contexts backed by a key.
1768    ///
1769    /// # Errors
1770    ///
1771    /// Returns `Err` if the context does not support this parameter.
1772    pub fn security_bits(&self) -> Result<u32, ErrorStack> {
1773        let mut params = crate::params::ParamBuilder::new()?
1774            .push_uint(c"security-bits", 0)?
1775            .build()?;
1776        // SAFETY: same as get_params — self.ctx is non-null, params pointer is valid.
1777        crate::ossl_call!(sys::EVP_PKEY_CTX_get_params(self.ctx, params.as_mut_ptr()))?;
1778        params
1779            .get_uint(c"security-bits")
1780            .map_err(|_| crate::error::ErrorStack::drain())
1781    }
1782
1783    /// Query the signature output size for the given input length.
1784    ///
1785    /// Calls `EVP_PKEY_sign` with a null output pointer — does not consume
1786    /// the signing state.
1787    ///
1788    /// # Errors
1789    pub fn sign_len(&mut self, tbs_len: usize) -> Result<usize, ErrorStack> {
1790        let mut siglen: usize = 0;
1791        crate::ossl_call!(sys::EVP_PKEY_sign(
1792            self.ctx,
1793            std::ptr::null_mut(),
1794            std::ptr::addr_of_mut!(siglen),
1795            std::ptr::null(),
1796            tbs_len,
1797        ))?;
1798        Ok(siglen)
1799    }
1800
1801    /// Sign pre-hashed data into `sig`.  Returns the number of bytes written.
1802    ///
1803    /// `sig.len()` must be >= `sign_len(tbs.len())`.
1804    ///
1805    /// # Errors
1806    pub fn sign(&mut self, tbs: &[u8], sig: &mut [u8]) -> Result<usize, ErrorStack> {
1807        let mut siglen = sig.len();
1808        crate::ossl_call!(sys::EVP_PKEY_sign(
1809            self.ctx,
1810            sig.as_mut_ptr(),
1811            std::ptr::addr_of_mut!(siglen),
1812            tbs.as_ptr(),
1813            tbs.len(),
1814        ))?;
1815        Ok(siglen)
1816    }
1817
1818    /// Sign pre-hashed data, allocating the output buffer.
1819    ///
1820    /// Convenience wrapper around [`sign_len`](Self::sign_len) + [`sign`](Self::sign).
1821    ///
1822    /// # Errors
1823    pub fn sign_alloc(&mut self, tbs: &[u8]) -> Result<Vec<u8>, ErrorStack> {
1824        let siglen = self.sign_len(tbs.len())?;
1825        let mut sig = vec![0u8; siglen];
1826        let written = self.sign(tbs, &mut sig)?;
1827        sig.truncate(written);
1828        Ok(sig)
1829    }
1830}
1831
1832impl Drop for RawSigner {
1833    fn drop(&mut self) {
1834        unsafe { sys::EVP_PKEY_CTX_free(self.ctx) };
1835    }
1836}
1837
1838// ── RawVerifier — pre-hashed / no-digest verification ────────────────────────
1839
1840/// Raw (no-digest) verification context wrapping `EVP_PKEY_CTX` after
1841/// `EVP_PKEY_verify_init`.
1842///
1843/// Mirror of [`RawSigner`] for the verification side.
1844pub struct RawVerifier {
1845    ctx: *mut sys::EVP_PKEY_CTX,
1846}
1847
1848unsafe impl Send for RawVerifier {}
1849
1850impl RawVerifier {
1851    /// Create and initialise a verify context.
1852    ///
1853    /// # Errors
1854    pub fn new<T: HasPublic>(
1855        key: &Pkey<T>,
1856        libctx: Option<&Arc<crate::lib_ctx::LibCtx>>,
1857    ) -> Result<Self, ErrorStack> {
1858        let lctx = libctx.map_or(std::ptr::null_mut(), |c| c.as_ptr());
1859        let ptr = unsafe { sys::EVP_PKEY_CTX_new_from_pkey(lctx, key.ptr, std::ptr::null()) };
1860        if ptr.is_null() {
1861            return Err(ErrorStack::drain());
1862        }
1863        crate::ossl_call!(sys::EVP_PKEY_verify_init(ptr)).map_err(|e| {
1864            unsafe { sys::EVP_PKEY_CTX_free(ptr) };
1865            e
1866        })?;
1867        Ok(RawVerifier { ctx: ptr })
1868    }
1869
1870    /// Apply parameters after init (e.g. RSA padding mode).
1871    ///
1872    /// # Errors
1873    pub fn set_params(&mut self, params: &crate::params::Params<'_>) -> Result<(), ErrorStack> {
1874        crate::ossl_call!(sys::EVP_PKEY_CTX_set_params(self.ctx, params.as_ptr()))
1875    }
1876
1877    /// Verify `sig` against pre-hashed `tbs`.  Returns `Ok(())` if valid.
1878    ///
1879    /// # Errors
1880    ///
1881    /// Returns `Err` if the signature is invalid or on any OpenSSL error.
1882    pub fn verify(&mut self, tbs: &[u8], sig: &[u8]) -> Result<(), ErrorStack> {
1883        crate::ossl_call!(sys::EVP_PKEY_verify(
1884            self.ctx,
1885            sig.as_ptr(),
1886            sig.len(),
1887            tbs.as_ptr(),
1888            tbs.len(),
1889        ))
1890    }
1891}
1892
1893impl Drop for RawVerifier {
1894    fn drop(&mut self) {
1895        unsafe { sys::EVP_PKEY_CTX_free(self.ctx) };
1896    }
1897}
1898
1899// ── SigAlg — EVP_SIGNATURE algorithm descriptor (OpenSSL 3.2+) ──────────────
1900
1901/// Algorithm descriptor for `EVP_SIGNATURE` (OpenSSL 3.2+).
1902///
1903/// Mirrors `DigestAlg` / `CipherAlg` / `MacAlg` in naming and lifecycle.
1904/// Used with [`MessageSigner`] and [`MessageVerifier`] for algorithms such
1905/// as ML-DSA, SLH-DSA, and Ed25519/Ed448 with context strings.
1906#[cfg(ossl320)]
1907pub struct SigAlg {
1908    ptr: *mut sys::EVP_SIGNATURE,
1909}
1910
1911// SAFETY: EVP_SIGNATURE is reference-counted.
1912#[cfg(ossl320)]
1913unsafe impl Send for SigAlg {}
1914#[cfg(ossl320)]
1915unsafe impl Sync for SigAlg {}
1916
1917#[cfg(ossl320)]
1918impl SigAlg {
1919    /// Fetch an `EVP_SIGNATURE` by name from the default library context.
1920    ///
1921    /// Example names: `c"ML-DSA-44"`, `c"ED25519"`, `c"SLH-DSA-SHA2-128s"`.
1922    ///
1923    /// # Errors
1924    pub fn fetch(
1925        name: &std::ffi::CStr,
1926        props: Option<&std::ffi::CStr>,
1927    ) -> Result<Self, ErrorStack> {
1928        let props_ptr = props.map_or(std::ptr::null(), std::ffi::CStr::as_ptr);
1929        let ptr =
1930            unsafe { sys::EVP_SIGNATURE_fetch(std::ptr::null_mut(), name.as_ptr(), props_ptr) };
1931        if ptr.is_null() {
1932            return Err(ErrorStack::drain());
1933        }
1934        Ok(SigAlg { ptr })
1935    }
1936
1937    /// Fetch an `EVP_SIGNATURE` by name within a specific library context.
1938    ///
1939    /// # Errors
1940    pub fn fetch_in(
1941        ctx: &Arc<crate::lib_ctx::LibCtx>,
1942        name: &std::ffi::CStr,
1943        props: Option<&std::ffi::CStr>,
1944    ) -> Result<Self, ErrorStack> {
1945        let props_ptr = props.map_or(std::ptr::null(), std::ffi::CStr::as_ptr);
1946        let ptr = unsafe { sys::EVP_SIGNATURE_fetch(ctx.as_ptr(), name.as_ptr(), props_ptr) };
1947        if ptr.is_null() {
1948            return Err(ErrorStack::drain());
1949        }
1950        Ok(SigAlg { ptr })
1951    }
1952}
1953
1954#[cfg(ossl320)]
1955impl Clone for SigAlg {
1956    fn clone(&self) -> Self {
1957        unsafe { sys::EVP_SIGNATURE_up_ref(self.ptr) };
1958        SigAlg { ptr: self.ptr }
1959    }
1960}
1961
1962#[cfg(ossl320)]
1963impl Drop for SigAlg {
1964    fn drop(&mut self) {
1965        unsafe { sys::EVP_SIGNATURE_free(self.ptr) };
1966    }
1967}
1968
1969// ── MessageSigner — EVP_PKEY_sign_message_* streaming sign (OpenSSL 3.2+) ────
1970
1971/// Stateful signing context using `EVP_PKEY_sign_message_*` (OpenSSL 3.2+).
1972///
1973/// Used for algorithms that do not use a separate internal digest (`ML-DSA`,
1974/// `SLH-DSA`, `Ed25519` with context strings).  Unlike [`Signer`], the
1975/// algorithm is specified as a [`SigAlg`] rather than a digest name.
1976///
1977/// Call [`update`](Self::update) zero or more times (if the algorithm supports
1978/// streaming — check with [`supports_streaming`](Self::supports_streaming)),
1979/// then [`finish`](Self::finish) to produce the signature.  For algorithms
1980/// that only support one-shot operation, use [`sign_oneshot`](Self::sign_oneshot).
1981#[cfg(ossl320)]
1982pub struct MessageSigner {
1983    ctx: *mut sys::EVP_PKEY_CTX,
1984}
1985
1986#[cfg(ossl320)]
1987unsafe impl Send for MessageSigner {}
1988
1989#[cfg(ossl320)]
1990impl MessageSigner {
1991    /// Create and initialise a message-sign context.
1992    ///
1993    /// `alg` is consumed by the init call; pass a clone if you need to reuse it.
1994    /// `params` sets algorithm-specific options (e.g. context string for Ed25519).
1995    ///
1996    /// # Errors
1997    pub fn new(
1998        key: &Pkey<Private>,
1999        alg: &mut SigAlg,
2000        params: Option<&crate::params::Params<'_>>,
2001    ) -> Result<Self, ErrorStack> {
2002        let ptr = unsafe {
2003            sys::EVP_PKEY_CTX_new_from_pkey(std::ptr::null_mut(), key.ptr, std::ptr::null())
2004        };
2005        if ptr.is_null() {
2006            return Err(ErrorStack::drain());
2007        }
2008        let params_ptr = params.map_or(crate::params::null_params(), crate::params::Params::as_ptr);
2009        crate::ossl_call!(sys::EVP_PKEY_sign_message_init(ptr, alg.ptr, params_ptr)).map_err(
2010            |e| {
2011                unsafe { sys::EVP_PKEY_CTX_free(ptr) };
2012                e
2013            },
2014        )?;
2015        Ok(MessageSigner { ctx: ptr })
2016    }
2017
2018    /// Probe whether this algorithm backend supports incremental `update` calls.
2019    ///
2020    /// Calls `EVP_PKEY_sign_message_update` with an empty input, bracketed by
2021    /// `ERR_set_mark` / `ERR_pop_to_mark` so that a failure does not leave
2022    /// entries on the error queue.  Returns `true` if streaming is supported.
2023    ///
2024    /// If this returns `false`, use [`sign_oneshot`](Self::sign_oneshot) instead.
2025    pub fn supports_streaming(&mut self) -> bool {
2026        // Probe: feed 0 bytes and see if the algorithm accepts it.
2027        // ERR mark/pop ensures the error queue is clean regardless of outcome.
2028        unsafe { sys::ERR_set_mark() };
2029        let probe: [u8; 0] = [];
2030        let rc = unsafe { sys::EVP_PKEY_sign_message_update(self.ctx, probe.as_ptr(), 0) };
2031        unsafe { sys::ERR_pop_to_mark() };
2032        rc == 1
2033    }
2034
2035    /// Feed `data` into the signing operation.
2036    ///
2037    /// Returns `Err` if the algorithm does not support streaming — use
2038    /// [`sign_oneshot`](Self::sign_oneshot) in that case.
2039    ///
2040    /// # Errors
2041    pub fn update(&mut self, data: &[u8]) -> Result<(), ErrorStack> {
2042        crate::ossl_call!(sys::EVP_PKEY_sign_message_update(
2043            self.ctx,
2044            data.as_ptr(),
2045            data.len(),
2046        ))
2047    }
2048
2049    /// Query the signature output length.
2050    ///
2051    /// Calls `EVP_PKEY_sign_message_final` with a null buffer — does not
2052    /// consume the signing state.
2053    ///
2054    /// # Errors
2055    pub fn sig_len(&mut self) -> Result<usize, ErrorStack> {
2056        let mut siglen: usize = 0;
2057        crate::ossl_call!(sys::EVP_PKEY_sign_message_final(
2058            self.ctx,
2059            std::ptr::null_mut(),
2060            std::ptr::addr_of_mut!(siglen),
2061        ))?;
2062        Ok(siglen)
2063    }
2064
2065    /// Finalise and produce the signature into `sig`.
2066    ///
2067    /// Consumes `self` because the context is finalised.  Call
2068    /// [`sig_len`](Self::sig_len) first to size the buffer.  Returns the
2069    /// number of bytes written.
2070    ///
2071    /// # Errors
2072    pub fn finish(self, sig: &mut [u8]) -> Result<usize, ErrorStack> {
2073        let mut siglen = sig.len();
2074        let rc = unsafe {
2075            sys::EVP_PKEY_sign_message_final(
2076                self.ctx,
2077                sig.as_mut_ptr(),
2078                std::ptr::addr_of_mut!(siglen),
2079            )
2080        };
2081        // self is dropped here → EVP_PKEY_CTX_free via Drop.
2082        if rc != 1 {
2083            return Err(ErrorStack::drain());
2084        }
2085        Ok(siglen)
2086    }
2087
2088    /// One-shot sign: feed `data` then finalise into `sig`.
2089    ///
2090    /// Consumes `self`.  Use this for algorithms that do not support
2091    /// streaming (`supports_streaming` returns `false`).
2092    ///
2093    /// # Errors
2094    pub fn sign_oneshot(self, data: &[u8], sig: &mut [u8]) -> Result<usize, ErrorStack> {
2095        // Feed all data, then finalise.  Both ops share the same ctx.
2096        let rc_upd =
2097            unsafe { sys::EVP_PKEY_sign_message_update(self.ctx, data.as_ptr(), data.len()) };
2098        if rc_upd != 1 {
2099            // self dropped here → ctx freed.
2100            return Err(ErrorStack::drain());
2101        }
2102        let mut siglen = sig.len();
2103        let rc_fin = unsafe {
2104            sys::EVP_PKEY_sign_message_final(
2105                self.ctx,
2106                sig.as_mut_ptr(),
2107                std::ptr::addr_of_mut!(siglen),
2108            )
2109        };
2110        // self dropped here → ctx freed.
2111        if rc_fin != 1 {
2112            return Err(ErrorStack::drain());
2113        }
2114        Ok(siglen)
2115    }
2116
2117    /// One-shot sign over `data` using `EVP_PKEY_sign`.
2118    ///
2119    /// The context must have been initialised with `EVP_PKEY_sign_message_init`
2120    /// (this type's constructor); `EVP_PKEY_sign` accepts both
2121    /// `EVP_PKEY_OP_SIGN` and `EVP_PKEY_OP_SIGNMSG` operation modes.
2122    ///
2123    /// When `sig` is `None` the call is a **cheap length query**: for ML-DSA
2124    /// and other algorithms with a fixed output size, no cryptographic
2125    /// computation is performed.  When `sig` is `Some(buf)` the signature is
2126    /// written and the number of bytes actually written is returned.
2127    ///
2128    /// The context is *not* consumed so the same `MessageSigner` may be reused
2129    /// across a size-query + actual-sign pair without re-initialisation.
2130    ///
2131    /// Contrast with [`sign_oneshot`](Self::sign_oneshot): `sign_oneshot`
2132    /// consumes `self` and always writes a signature; `sign` borrows `self`,
2133    /// can query the required length cheaply (pass `sig = None`), and can be
2134    /// called multiple times on the same context.
2135    ///
2136    /// # Errors
2137    pub fn sign(&mut self, data: &[u8], sig: Option<&mut [u8]>) -> Result<usize, ErrorStack> {
2138        let (sig_ptr, mut siglen) = match sig {
2139            Some(buf) => (buf.as_mut_ptr(), buf.len()),
2140            None => (std::ptr::null_mut(), 0usize),
2141        };
2142        crate::ossl_call!(sys::EVP_PKEY_sign(
2143            self.ctx,
2144            sig_ptr,
2145            std::ptr::addr_of_mut!(siglen),
2146            data.as_ptr(),
2147            data.len(),
2148        ))?;
2149        Ok(siglen)
2150    }
2151}
2152
2153#[cfg(ossl320)]
2154impl Drop for MessageSigner {
2155    fn drop(&mut self) {
2156        unsafe { sys::EVP_PKEY_CTX_free(self.ctx) };
2157    }
2158}
2159
2160// ── MessageVerifier — EVP_PKEY_verify_message_* streaming verify (OpenSSL 3.2+)
2161
2162/// Stateful verification context using `EVP_PKEY_verify_message_*` (OpenSSL 3.2+).
2163///
2164/// Mirror of [`MessageSigner`] for the verification side.
2165///
2166/// For streaming mode: call [`set_signature`](Self::set_signature), then
2167/// [`update`](Self::update) zero or more times, then [`finish`](Self::finish).
2168/// For one-shot: call [`verify_oneshot`](Self::verify_oneshot).
2169#[cfg(ossl320)]
2170pub struct MessageVerifier {
2171    ctx: *mut sys::EVP_PKEY_CTX,
2172}
2173
2174#[cfg(ossl320)]
2175unsafe impl Send for MessageVerifier {}
2176
2177#[cfg(ossl320)]
2178impl MessageVerifier {
2179    /// Create and initialise a message-verify context.
2180    ///
2181    /// # Errors
2182    pub fn new<T: HasPublic>(
2183        key: &Pkey<T>,
2184        alg: &mut SigAlg,
2185        params: Option<&crate::params::Params<'_>>,
2186    ) -> Result<Self, ErrorStack> {
2187        let ptr = unsafe {
2188            sys::EVP_PKEY_CTX_new_from_pkey(std::ptr::null_mut(), key.ptr, std::ptr::null())
2189        };
2190        if ptr.is_null() {
2191            return Err(ErrorStack::drain());
2192        }
2193        let params_ptr = params.map_or(crate::params::null_params(), crate::params::Params::as_ptr);
2194        crate::ossl_call!(sys::EVP_PKEY_verify_message_init(ptr, alg.ptr, params_ptr)).map_err(
2195            |e| {
2196                unsafe { sys::EVP_PKEY_CTX_free(ptr) };
2197                e
2198            },
2199        )?;
2200        Ok(MessageVerifier { ctx: ptr })
2201    }
2202
2203    /// Apply parameters after init.
2204    ///
2205    /// # Errors
2206    pub fn set_params(&mut self, params: &crate::params::Params<'_>) -> Result<(), ErrorStack> {
2207        crate::ossl_call!(sys::EVP_PKEY_CTX_set_params(self.ctx, params.as_ptr()))
2208    }
2209
2210    /// Supply the signature to verify against (required before streaming `finish`).
2211    ///
2212    /// Calls `EVP_PKEY_CTX_set_signature`.  Not needed for
2213    /// [`verify_oneshot`](Self::verify_oneshot) which sets it internally.
2214    ///
2215    /// # Errors
2216    pub fn set_signature(&mut self, sig: &[u8]) -> Result<(), ErrorStack> {
2217        crate::ossl_call!(sys::EVP_PKEY_CTX_set_signature(
2218            self.ctx,
2219            sig.as_ptr(),
2220            sig.len()
2221        ))
2222    }
2223
2224    /// Probe whether this algorithm supports incremental `update` calls.
2225    ///
2226    /// Uses the same `ERR_set_mark` / `ERR_pop_to_mark` probe as
2227    /// [`MessageSigner::supports_streaming`].
2228    pub fn supports_streaming(&mut self) -> bool {
2229        unsafe { sys::ERR_set_mark() };
2230        let probe: [u8; 0] = [];
2231        let rc = unsafe { sys::EVP_PKEY_verify_message_update(self.ctx, probe.as_ptr(), 0) };
2232        unsafe { sys::ERR_pop_to_mark() };
2233        rc == 1
2234    }
2235
2236    /// Feed `data` into the verification operation.
2237    ///
2238    /// # Errors
2239    pub fn update(&mut self, data: &[u8]) -> Result<(), ErrorStack> {
2240        crate::ossl_call!(sys::EVP_PKEY_verify_message_update(
2241            self.ctx,
2242            data.as_ptr(),
2243            data.len(),
2244        ))
2245    }
2246
2247    /// Finalise and verify.
2248    ///
2249    /// The signature must have been set via [`set_signature`](Self::set_signature).
2250    /// Consumes `self`.  Returns `Ok(())` if the signature is valid.
2251    ///
2252    /// # Errors
2253    pub fn finish(self) -> Result<(), ErrorStack> {
2254        let rc = unsafe { sys::EVP_PKEY_verify_message_final(self.ctx) };
2255        // self dropped here → ctx freed.
2256        if rc != 1 {
2257            return Err(ErrorStack::drain());
2258        }
2259        Ok(())
2260    }
2261
2262    /// One-shot verify `sig` over `data`.
2263    ///
2264    /// Sets the signature, feeds all data, and finalises.  Consumes `self`.
2265    ///
2266    /// # Errors
2267    pub fn verify_oneshot(self, data: &[u8], sig: &[u8]) -> Result<(), ErrorStack> {
2268        let rc_set = unsafe { sys::EVP_PKEY_CTX_set_signature(self.ctx, sig.as_ptr(), sig.len()) };
2269        if rc_set != 1 {
2270            return Err(ErrorStack::drain());
2271        }
2272        let rc_upd =
2273            unsafe { sys::EVP_PKEY_verify_message_update(self.ctx, data.as_ptr(), data.len()) };
2274        if rc_upd != 1 {
2275            return Err(ErrorStack::drain());
2276        }
2277        let rc_fin = unsafe { sys::EVP_PKEY_verify_message_final(self.ctx) };
2278        // self dropped here → ctx freed.
2279        if rc_fin != 1 {
2280            return Err(ErrorStack::drain());
2281        }
2282        Ok(())
2283    }
2284
2285    /// One-shot verify `sig` over `data` using `EVP_PKEY_verify`.
2286    ///
2287    /// The context must have been initialised with `EVP_PKEY_verify_message_init`
2288    /// (this type's constructor); `EVP_PKEY_verify` accepts both
2289    /// `EVP_PKEY_OP_VERIFY` and `EVP_PKEY_OP_VERIFYMSG` operation modes.
2290    ///
2291    /// Returns `Ok(true)` if the signature verifies, `Ok(false)` if it does
2292    /// not.  Fatal protocol or library errors are returned as `Err`.
2293    ///
2294    /// The context is *not* consumed and may be reused for further verifications.
2295    ///
2296    /// Contrast with [`verify_oneshot`](Self::verify_oneshot): `verify_oneshot`
2297    /// consumes `self`; `verify` borrows `self` and may be called repeatedly
2298    /// without re-creating the context.
2299    ///
2300    /// # Errors
2301    pub fn verify(&mut self, data: &[u8], sig: &[u8]) -> Result<bool, ErrorStack> {
2302        let rc = unsafe {
2303            sys::EVP_PKEY_verify(self.ctx, sig.as_ptr(), sig.len(), data.as_ptr(), data.len())
2304        };
2305        match rc {
2306            1 => Ok(true),
2307            0 => Ok(false),
2308            _ => Err(ErrorStack::drain()),
2309        }
2310    }
2311}
2312
2313#[cfg(ossl320)]
2314impl Drop for MessageVerifier {
2315    fn drop(&mut self) {
2316        unsafe { sys::EVP_PKEY_CTX_free(self.ctx) };
2317    }
2318}
2319
2320// ── Tests ─────────────────────────────────────────────────────────────────────
2321
2322#[cfg(test)]
2323mod tests {
2324    use super::*;
2325
2326    /// Generate an Ed25519 key pair; sign and verify "hello world".
2327    ///
2328    /// Ed25519 is a one-shot algorithm — uses `sign_oneshot` / `verify_oneshot`.
2329    #[test]
2330    fn ed25519_sign_verify() {
2331        let mut kgen = KeygenCtx::new(c"ED25519").unwrap();
2332        let priv_key = kgen.generate().unwrap();
2333        let pub_key = Pkey::<Public>::from(priv_key.clone());
2334
2335        let msg = b"hello world";
2336        let init = SignInit::default();
2337
2338        let mut signer = Signer::new(&priv_key, &init).unwrap();
2339        let sig = signer.sign_oneshot(msg).unwrap();
2340        assert!(!sig.is_empty());
2341
2342        let mut verifier = Verifier::new(&pub_key, &init).unwrap();
2343        assert!(verifier.verify_oneshot(msg, &sig).unwrap());
2344    }
2345
2346    /// Tampered message must fail verification.
2347    #[test]
2348    fn ed25519_verify_wrong_msg_fails() {
2349        let mut kgen = KeygenCtx::new(c"ED25519").unwrap();
2350        let priv_key = kgen.generate().unwrap();
2351        let pub_key = Pkey::<Public>::from(priv_key.clone());
2352
2353        let mut signer = Signer::new(&priv_key, &SignInit::default()).unwrap();
2354        let sig = signer.sign_oneshot(b"correct").unwrap();
2355
2356        let mut verifier = Verifier::new(&pub_key, &SignInit::default()).unwrap();
2357        assert!(!verifier.verify_oneshot(b"tampered", &sig).unwrap());
2358    }
2359
2360    /// Generate an X25519 key pair; perform ECDH derive and confirm length.
2361    #[test]
2362    fn x25519_derive() {
2363        let mut kgen_a = KeygenCtx::new(c"X25519").unwrap();
2364        let priv_a = kgen_a.generate().unwrap();
2365
2366        let mut kgen_b = KeygenCtx::new(c"X25519").unwrap();
2367        let priv_b = kgen_b.generate().unwrap();
2368        let pub_b = Pkey::<Public>::from(priv_b);
2369
2370        let mut derive = DeriveCtx::new(&priv_a).unwrap();
2371        derive.set_peer(&pub_b).unwrap();
2372        let len = derive.derive_len().unwrap();
2373        assert_eq!(len, 32); // X25519 shared secret is always 32 bytes
2374
2375        let mut ss = vec![0u8; len];
2376        let n = derive.derive(&mut ss).unwrap();
2377        assert_eq!(n, 32);
2378        assert_ne!(ss, [0u8; 32]);
2379    }
2380
2381    /// Round-trip through PEM: generate Ed25519, export, re-import, verify key equality.
2382    #[test]
2383    fn pem_round_trip() {
2384        let mut kgen = KeygenCtx::new(c"ED25519").unwrap();
2385        let priv_key = kgen.generate().unwrap();
2386
2387        let pem = priv_key.to_pem().unwrap();
2388        assert!(!pem.is_empty());
2389        assert!(pem.starts_with(b"-----BEGIN"));
2390
2391        let priv_key2 = Pkey::<Private>::from_pem(&pem).unwrap();
2392        assert_eq!(priv_key.bits(), priv_key2.bits());
2393        assert!(priv_key.is_a(c"ED25519"));
2394    }
2395
2396    /// Key metadata: Ed25519 should report 256 bits.
2397    #[test]
2398    fn ed25519_metadata() {
2399        let mut kgen = KeygenCtx::new(c"ED25519").unwrap();
2400        let key = kgen.generate().unwrap();
2401        assert_eq!(key.bits(), 256);
2402        assert_eq!(key.security_bits(), 128);
2403        assert!(key.is_a(c"ED25519"));
2404    }
2405
2406    /// ML-DSA-44 sign and verify using `MessageSigner::sign` / `MessageVerifier::verify`.
2407    #[cfg(ossl320)]
2408    #[test]
2409    fn ml_dsa_44_sign_verify() {
2410        let mut kgen = KeygenCtx::new(c"ML-DSA-44").unwrap();
2411        let priv_key = kgen.generate().unwrap();
2412        let pub_key = Pkey::<Public>::from(priv_key.clone());
2413
2414        let msg = b"hello ML-DSA-44";
2415
2416        let mut alg = SigAlg::fetch(c"ML-DSA-44", None).unwrap();
2417        let mut signer = MessageSigner::new(&priv_key, &mut alg, None).unwrap();
2418        // NULL size query must be cheap (FIPS 204 fixed 2420 bytes).
2419        let sig_len = signer.sign(msg, None).unwrap();
2420        assert_eq!(sig_len, 2420);
2421        let mut sig = vec![0u8; sig_len];
2422        let written = signer.sign(msg, Some(&mut sig)).unwrap();
2423        assert_eq!(written, 2420);
2424
2425        let mut alg2 = SigAlg::fetch(c"ML-DSA-44", None).unwrap();
2426        let mut ver = MessageVerifier::new(&pub_key, &mut alg2, None).unwrap();
2427        assert!(ver.verify(msg, &sig).unwrap());
2428
2429        // Tampered signature must fail.
2430        let mut bad = sig.clone();
2431        bad[0] ^= 0xff;
2432        assert!(!ver.verify(msg, &bad).unwrap());
2433    }
2434
2435    /// ML-DSA-44 sign with a context string (FIPS 204 domain separation).
2436    #[cfg(ossl320)]
2437    #[test]
2438    fn ml_dsa_44_sign_with_context() {
2439        let mut kgen = KeygenCtx::new(c"ML-DSA-44").unwrap();
2440        let priv_key = kgen.generate().unwrap();
2441        let pub_key = Pkey::<Public>::from(priv_key.clone());
2442
2443        let msg = b"signed with context";
2444        let ctx_bytes = b"my-app-domain";
2445
2446        let ctx_params = crate::params::ParamBuilder::new()
2447            .unwrap()
2448            .push_octet_slice(c"context-string", ctx_bytes)
2449            .unwrap()
2450            .build()
2451            .unwrap();
2452
2453        let mut alg = SigAlg::fetch(c"ML-DSA-44", None).unwrap();
2454        let mut signer = MessageSigner::new(&priv_key, &mut alg, Some(&ctx_params)).unwrap();
2455        let sig_len = signer.sign(msg, None).unwrap();
2456        let mut sig = vec![0u8; sig_len];
2457        signer.sign(msg, Some(&mut sig)).unwrap();
2458
2459        // Same context: verify must succeed.
2460        let mut alg2 = SigAlg::fetch(c"ML-DSA-44", None).unwrap();
2461        let mut ver = MessageVerifier::new(&pub_key, &mut alg2, Some(&ctx_params)).unwrap();
2462        assert!(ver.verify(msg, &sig).unwrap());
2463
2464        // Different context: verify must fail.
2465        let other_params = crate::params::ParamBuilder::new()
2466            .unwrap()
2467            .push_octet_slice(c"context-string", b"other-domain")
2468            .unwrap()
2469            .build()
2470            .unwrap();
2471        let mut alg3 = SigAlg::fetch(c"ML-DSA-44", None).unwrap();
2472        let mut ver_bad = MessageVerifier::new(&pub_key, &mut alg3, Some(&other_params)).unwrap();
2473        assert!(!ver_bad.verify(msg, &sig).unwrap());
2474    }
2475
2476    /// `RawSigner` / `RawVerifier` round-trip with P-256 ECDSA over a pre-hashed digest.
2477    #[test]
2478    fn ecdsa_p256_raw_sign_verify() {
2479        // Generate P-256 key.
2480        let mut kgen = KeygenCtx::new(c"EC").unwrap();
2481        let params = crate::params::ParamBuilder::new()
2482            .unwrap()
2483            .push_utf8_string(c"group", c"P-256")
2484            .unwrap()
2485            .build()
2486            .unwrap();
2487        kgen.set_params(&params).unwrap();
2488        let priv_key = kgen.generate().unwrap();
2489        let pub_key = Pkey::<Public>::from(priv_key.clone());
2490
2491        // SHA-256 hash of the message (32 bytes — the "pre-hash").
2492        let tbs: [u8; 32] = *b"0123456789abcdef0123456789abcdef";
2493
2494        let mut signer = RawSigner::new(&priv_key, None).unwrap();
2495        let sig = signer.sign_alloc(&tbs).unwrap();
2496        assert!(!sig.is_empty());
2497
2498        let mut verifier = RawVerifier::new(&pub_key, None).unwrap();
2499        verifier.verify(&tbs, &sig).unwrap();
2500    }
2501
2502    /// `RawVerifier` rejects a tampered signature.
2503    #[test]
2504    fn ecdsa_p256_raw_verify_tampered_fails() {
2505        let mut kgen = KeygenCtx::new(c"EC").unwrap();
2506        let params = crate::params::ParamBuilder::new()
2507            .unwrap()
2508            .push_utf8_string(c"group", c"P-256")
2509            .unwrap()
2510            .build()
2511            .unwrap();
2512        kgen.set_params(&params).unwrap();
2513        let priv_key = kgen.generate().unwrap();
2514        let pub_key = Pkey::<Public>::from(priv_key.clone());
2515
2516        let tbs: [u8; 32] = *b"0123456789abcdef0123456789abcdef";
2517        let mut signer = RawSigner::new(&priv_key, None).unwrap();
2518        let mut sig = signer.sign_alloc(&tbs).unwrap();
2519        // Flip a byte in the signature.
2520        if let Some(b) = sig.last_mut() {
2521            *b ^= 0xff;
2522        }
2523
2524        let mut verifier = RawVerifier::new(&pub_key, None).unwrap();
2525        assert!(verifier.verify(&tbs, &sig).is_err());
2526    }
2527
2528    /// `SigAlg`: fetch, clone, drop — verifies `EVP_SIGNATURE` lifecycle.
2529    ///
2530    /// Does not attempt `sign_message_final`: OpenSSL 3.5's built-in ML-DSA
2531    /// provider implements only the one-shot `EVP_DigestSign` path, not
2532    /// `OSSL_FUNC_SIGNATURE_SIGN_MESSAGE_FINAL`.  Actual ML-DSA signing is
2533    /// done via Signer (`DigestSign` with NULL digest).
2534    #[cfg(ossl320)]
2535    #[test]
2536    fn sig_alg_fetch_clone_drop() {
2537        let alg = SigAlg::fetch(c"ML-DSA-44", None).unwrap();
2538        let alg2 = alg.clone();
2539        drop(alg);
2540        drop(alg2); // both up_ref / free paths exercised
2541    }
2542
2543    /// `MessageSigner` construction and `supports_streaming` probe for Ed25519.
2544    ///
2545    /// Ed25519 supports `EVP_PKEY_sign_message_init`.  The streaming probe
2546    /// uses `ERR_set_mark` / `ERR_pop_to_mark` so it cannot leave error state.
2547    #[cfg(ossl320)]
2548    #[test]
2549    fn message_signer_construction_and_streaming_probe() {
2550        let mut kgen = KeygenCtx::new(c"ED25519").unwrap();
2551        let priv_key = kgen.generate().unwrap();
2552
2553        let mut alg = SigAlg::fetch(c"ED25519", None).unwrap();
2554        let mut signer = MessageSigner::new(&priv_key, &mut alg, None).unwrap();
2555
2556        // Probe must not crash or leave the error queue dirty.
2557        let _streaming = signer.supports_streaming();
2558        // Error queue must be empty after the probe.
2559        assert_eq!(crate::error::ErrorStack::drain().errors().count(), 0);
2560    }
2561
2562    /// `MessageVerifier` construction for Ed25519.
2563    #[cfg(ossl320)]
2564    #[test]
2565    fn message_verifier_construction() {
2566        let mut kgen = KeygenCtx::new(c"ED25519").unwrap();
2567        let priv_key = kgen.generate().unwrap();
2568        let pub_key = Pkey::<Public>::from(priv_key.clone());
2569
2570        let mut alg = SigAlg::fetch(c"ED25519", None).unwrap();
2571        let _verifier = MessageVerifier::new(&pub_key, &mut alg, None).unwrap();
2572    }
2573
2574    /// Encrypted PEM round-trip: generate → `to_pem_encrypted` → `from_pem_passphrase`.
2575    #[test]
2576    fn encrypted_pem_round_trip() {
2577        let mut kgen = KeygenCtx::new(c"ED25519").unwrap();
2578        let key = kgen.generate().unwrap();
2579
2580        let cipher = crate::cipher::CipherAlg::fetch(c"AES-256-CBC", None).unwrap();
2581        let pem = key.to_pem_encrypted(&cipher, b"s3cret").unwrap();
2582        assert!(pem.starts_with(b"-----BEGIN ENCRYPTED PRIVATE KEY-----"));
2583
2584        let key2 = Pkey::<Private>::from_pem_passphrase(&pem, b"s3cret").unwrap();
2585        assert!(key.public_eq(&key2));
2586    }
2587
2588    /// Wrong passphrase must return an error, not silently load a garbage key.
2589    #[test]
2590    fn encrypted_pem_wrong_passphrase_fails() {
2591        let mut kgen = KeygenCtx::new(c"ED25519").unwrap();
2592        let key = kgen.generate().unwrap();
2593
2594        let cipher = crate::cipher::CipherAlg::fetch(c"AES-256-CBC", None).unwrap();
2595        let pem = key.to_pem_encrypted(&cipher, b"correct").unwrap();
2596        assert!(Pkey::<Private>::from_pem_passphrase(&pem, b"wrong").is_err());
2597    }
2598
2599    /// PKCS#8 DER round-trip: generate → `to_pkcs8_der` → `from_der`.
2600    #[test]
2601    fn pkcs8_der_round_trip() {
2602        let mut kgen = KeygenCtx::new(c"ED25519").unwrap();
2603        let key = kgen.generate().unwrap();
2604
2605        let der = key.to_pkcs8_der().unwrap();
2606        assert!(!der.is_empty());
2607
2608        let key2 = Pkey::<Private>::from_der(&der).unwrap();
2609        assert!(key.public_eq(&key2));
2610    }
2611
2612    #[test]
2613    fn pkey_max_output_size_ed25519() {
2614        let mut kgen = KeygenCtx::new(c"ED25519").unwrap();
2615        let key = kgen.generate().unwrap();
2616        // Ed25519 produces a fixed 64-byte signature.
2617        assert_eq!(key.max_output_size(), 64);
2618    }
2619
2620    #[test]
2621    fn pkey_max_output_size_rsa() {
2622        let mut kgen = KeygenCtx::new(c"RSA").unwrap();
2623        let key = kgen.generate().unwrap();
2624        // RSA-2048: max output == key size in bytes (256).
2625        let size = key.max_output_size();
2626        assert!(size > 0, "RSA key must report non-zero max output size");
2627        assert_eq!(size, key.bits() as usize / 8);
2628    }
2629
2630    /// `KeygenCtx::set_params` — set RSA bit length via params, generate, verify size.
2631    ///
2632    /// OpenSSL default RSA keygen produces 2048-bit keys; here we explicitly set
2633    /// `bits = 3072` and verify the generated key carries that size.
2634    #[test]
2635    fn pkey_ctx_set_params_rsa() {
2636        let mut kgen = KeygenCtx::new(c"RSA").unwrap();
2637        let params = crate::params::ParamBuilder::new()
2638            .unwrap()
2639            .push_uint(c"bits", 3072)
2640            .unwrap()
2641            .build()
2642            .unwrap();
2643        kgen.set_params(&params).unwrap();
2644        let key = kgen.generate().unwrap();
2645        assert_eq!(key.bits(), 3072, "generated RSA key must be 3072 bits");
2646    }
2647
2648    /// `KeygenCtx::security_bits` — Ed25519 key object reports 128 security bits.
2649    ///
2650    /// `security_bits()` reads `security-bits` from the key context.  For Ed25519,
2651    /// the well-known security strength is 128 bits.  This test uses the
2652    /// `Pkey::security_bits` method (which wraps `EVP_PKEY_get_security_bits`)
2653    /// and the `KeygenCtx::security_bits` helper.  The keygen context method
2654    /// internally calls `EVP_PKEY_CTX_get_params`; the result depends on what the
2655    /// underlying provider exposes on a keygen context.
2656    #[test]
2657    fn pkey_ctx_security_bits() {
2658        // Verify that the key object itself reports the correct security level.
2659        let mut kgen = KeygenCtx::new(c"ED25519").unwrap();
2660        let key = kgen.generate().unwrap();
2661        assert_eq!(
2662            key.security_bits(),
2663            128,
2664            "Ed25519 key must report 128 security bits"
2665        );
2666
2667        // KeygenCtx::security_bits() is a best-effort getter; it may return Err if
2668        // the provider does not expose security-bits on a keygen context.  We just
2669        // verify it does not panic.
2670        let _ = KeygenCtx::new(c"ED25519").unwrap().security_bits();
2671    }
2672
2673    /// `RawSigner::get_params` — read `pad-mode` from an RSA sign context.
2674    ///
2675    /// `EVP_PKEY_CTX_get_params` is most useful on operation (sign/verify)
2676    /// contexts where OpenSSL exposes per-operation configuration parameters.
2677    /// RSA sign contexts expose `pad-mode` as both gettable and settable.
2678    ///
2679    /// The placeholder string must be large enough to receive the returned value;
2680    /// we use a 32-char placeholder to accommodate all padding mode names.
2681    #[test]
2682    fn pkey_ctx_get_params_rsa_sign() {
2683        let mut kgen = KeygenCtx::new(c"RSA").unwrap();
2684        let key = kgen.generate().unwrap();
2685        let signer = RawSigner::new(&key, None).unwrap();
2686
2687        // Query the default RSA padding mode; OpenSSL defaults to "pkcs1".
2688        // Provide a placeholder string of length >= the longest padding mode name.
2689        let placeholder = c"00000000000000000000000000000000"; // 32 chars
2690        let mut params = crate::params::ParamBuilder::new()
2691            .unwrap()
2692            .push_utf8_string(c"pad-mode", placeholder)
2693            .unwrap()
2694            .build()
2695            .unwrap();
2696        signer.get_params(&mut params).unwrap();
2697        // The returned pad-mode must be a non-empty string.
2698        let mode = params.get_utf8_string(c"pad-mode").unwrap();
2699        assert!(
2700            !mode.to_bytes().is_empty(),
2701            "RSA pad-mode must be non-empty"
2702        );
2703    }
2704
2705    /// `Pkey<Public>::from_pem_in` loads a public key within an explicit `LibCtx`.
2706    #[test]
2707    fn pubkey_from_pem_in_roundtrip() {
2708        // Generate an EC key and export the public half to PEM.
2709        let mut kgen = KeygenCtx::new(c"ED25519").unwrap();
2710        let priv_key = kgen.generate().unwrap();
2711        let pub_pem = Pkey::<Public>::from(priv_key).to_pem().unwrap();
2712
2713        // Re-import via from_pem_in using the default lib ctx.
2714        let lib_ctx = Arc::new(crate::lib_ctx::LibCtx::new().unwrap());
2715        let pub_key = Pkey::<Public>::from_pem_in(&lib_ctx, &pub_pem).unwrap();
2716
2717        // Sanity: key is usable for verification.
2718        assert!(!pub_key.to_pem().unwrap().is_empty());
2719    }
2720
2721    /// Ed25519 has no separate digest — `default_digest_name()` must return `Ok(None)`.
2722    #[test]
2723    fn pkey_default_digest_name_ed25519() {
2724        let mut kgen = KeygenCtx::new(c"ED25519").unwrap();
2725        let key = kgen.generate().unwrap();
2726        let result = key.default_digest_name().unwrap();
2727        assert_eq!(
2728            result, None,
2729            "Ed25519 performs its own internal hashing; expected no external digest"
2730        );
2731    }
2732
2733    /// RSA with a default digest should return `Ok(Some(name))` with a non-empty name.
2734    #[test]
2735    fn pkey_default_digest_name_rsa() {
2736        let mut kgen = KeygenCtx::new(c"RSA").unwrap();
2737        let key = kgen.generate().unwrap();
2738        let result = key.default_digest_name().unwrap();
2739        assert!(
2740            result.is_some(),
2741            "RSA key should have a default digest name; got None"
2742        );
2743        let name = result.unwrap();
2744        assert!(
2745            !name.is_empty(),
2746            "RSA default digest name must not be empty"
2747        );
2748        // OpenSSL 3.x reports SHA256 (or SHA-256) as the RSA default digest.
2749        assert!(
2750            name.to_ascii_uppercase().contains("SHA"),
2751            "Expected a SHA-family digest name, got: {name}"
2752        );
2753    }
2754
2755    /// `Pkey::<Private>::from_der_in` — round-trip through `to_der` (PKCS#8) and
2756    /// back via the LibCtx-aware `from_der_in`, using an Ed25519 key.
2757    ///
2758    /// Verifies that the reloaded key's public component matches the original.
2759    #[test]
2760    fn pkey_private_from_der_in_ed25519() {
2761        let ctx = Arc::new(crate::lib_ctx::LibCtx::new().unwrap());
2762
2763        let key = KeygenCtx::new(c"ED25519").unwrap().generate().unwrap();
2764
2765        // Serialise to PKCS#8 DER (the format auto-detected by from_der_in).
2766        let der = key.to_pkcs8_der().unwrap();
2767        assert!(!der.is_empty());
2768
2769        // Reload via the LibCtx-aware path.
2770        let key2 = Pkey::<Private>::from_der_in(&ctx, &der).unwrap();
2771
2772        // Public components must be identical.
2773        assert!(
2774            key.public_eq(&key2),
2775            "reloaded private key must equal original"
2776        );
2777    }
2778
2779    /// `Pkey::<Public>::from_der_in` — round-trip through `public_key_to_der`
2780    /// (`SubjectPublicKeyInfo`) and back via the LibCtx-aware `from_der_in`.
2781    #[test]
2782    fn pkey_public_from_der_in_ed25519() {
2783        let ctx = Arc::new(crate::lib_ctx::LibCtx::new().unwrap());
2784
2785        let priv_key = KeygenCtx::new(c"ED25519").unwrap().generate().unwrap();
2786        let pub_key = Pkey::<Public>::from(priv_key.clone());
2787
2788        // Serialise the public key to SubjectPublicKeyInfo DER.
2789        let der = pub_key.public_key_to_der().unwrap();
2790        assert!(!der.is_empty());
2791
2792        // Reload via the LibCtx-aware path.
2793        let pub_key2 = Pkey::<Public>::from_der_in(&ctx, &der).unwrap();
2794
2795        // Must match the original private key's public component.
2796        assert!(
2797            priv_key.public_eq(&pub_key2),
2798            "reloaded public key must equal original"
2799        );
2800    }
2801
2802    /// `to_der_legacy` — generate an RSA key and verify that the legacy DER
2803    /// output is non-empty (RSA has a well-defined `RSAPrivateKey` legacy format).
2804    #[test]
2805    fn pkey_to_der_legacy_produces_non_empty() {
2806        let key = KeygenCtx::new(c"RSA").unwrap().generate().unwrap();
2807
2808        let der = key.to_der_legacy().unwrap();
2809        assert!(!der.is_empty(), "legacy DER for RSA must be non-empty");
2810    }
2811}