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    /// Return `true` if this key is of the named algorithm (e.g. `c"EC"`, `c"RSA"`).
120    #[must_use]
121    pub fn is_a(&self, name: &std::ffi::CStr) -> bool {
122        unsafe { sys::EVP_PKEY_is_a(self.ptr, name.as_ptr()) == 1 }
123    }
124
125    /// Return `true` if this key's public component equals `other`'s.
126    ///
127    /// Wraps `EVP_PKEY_eq`.  Useful for verifying that a certificate's public
128    /// key matches a private key before using them together.
129    #[must_use]
130    pub fn public_eq<U: HasPublic>(&self, other: &Pkey<U>) -> bool
131    where
132        T: HasPublic,
133    {
134        unsafe { sys::EVP_PKEY_eq(self.ptr, other.ptr) == 1 }
135    }
136
137    /// Fill the values for a pre-prepared mutable `Params` query array.
138    ///
139    /// Wraps `EVP_PKEY_get_params`.  The array must already contain the keys
140    /// of interest with null data pointers; OpenSSL writes the values in place.
141    ///
142    /// # Errors
143    pub fn get_params(&self, params: &mut crate::params::Params<'_>) -> Result<(), ErrorStack> {
144        crate::ossl_call!(sys::EVP_PKEY_get_params(self.ptr, params.as_mut_ptr()))
145    }
146
147    /// DER-encode the public key (`SubjectPublicKeyInfo` format).
148    ///
149    /// Zero-copy: writes directly into a caller-owned `Vec<u8>` — no OpenSSL
150    /// heap allocation occurs.
151    ///
152    /// # Errors
153    ///
154    /// Returns `Err` if serialisation fails.
155    pub fn public_key_to_der(&self) -> Result<Vec<u8>, ErrorStack>
156    where
157        T: HasPublic,
158    {
159        // First call with null to query the DER byte length.
160        let len = unsafe { sys::i2d_PUBKEY(self.ptr, std::ptr::null_mut()) };
161        if len < 0 {
162            return Err(ErrorStack::drain());
163        }
164        // Allocate our own buffer and write into it.
165        // i2d_ advances the out-pointer; our Vec base address is unaffected.
166        let mut buf = vec![0u8; usize::try_from(len).unwrap_or(0)];
167        let mut out_ptr = buf.as_mut_ptr();
168        let written = unsafe { sys::i2d_PUBKEY(self.ptr, std::ptr::addr_of_mut!(out_ptr)) };
169        if written < 0 {
170            return Err(ErrorStack::drain());
171        }
172        buf.truncate(usize::try_from(written).unwrap_or(0));
173        Ok(buf)
174    }
175}
176
177// ── PEM loading — private key ─────────────────────────────────────────────────
178
179impl Pkey<Private> {
180    /// Return the provider-side `keydata` pointer from the `EVP_PKEY` struct.
181    ///
182    /// This is the `void *keydata` field of `evp_pkey_st`.  It holds the
183    /// algorithm-specific key material allocated by the provider's keymgmt
184    /// implementation.  The pointer is valid for the lifetime of `self`.
185    ///
186    /// Only available with the `fips-provider` cargo feature.  Intended for
187    /// use inside a FIPS provider when invoking `EVP_SIGNATURE` vtable functions
188    /// that require `void *provkey` (= `keydata`).
189    ///
190    /// # Safety
191    ///
192    /// The returned pointer points into the internal state of this `Pkey`.
193    /// It must not outlive `self` and must not be freed independently.
194    /// The field offset is computed by the C compiler for the current target
195    /// ABI via a build-time probe; see `native-ossl-sys/build.rs`.
196    #[cfg(feature = "fips-provider")]
197    pub unsafe fn keydata(&self) -> *mut std::ffi::c_void {
198        // SAFETY: self.ptr is a valid non-null EVP_PKEY*.  We read the void* at
199        // the ABI-correct byte offset of the `keydata` field in evp_pkey_st,
200        // determined at build time by a C offsetof probe.
201        self.ptr
202            .cast::<u8>()
203            .add(native_ossl_sys::fips_internal::EVP_PKEY_KEYDATA_OFFSET)
204            .cast::<*mut std::ffi::c_void>()
205            .read()
206    }
207
208    /// Load a private key from PEM bytes.
209    ///
210    /// Pass `passphrase = Some(cb)` for encrypted PEM; `None` for unencrypted.
211    ///
212    /// # Errors
213    pub fn from_pem(pem: &[u8]) -> Result<Self, ErrorStack> {
214        let bio = MemBioBuf::new(pem)?;
215        let ptr = unsafe {
216            sys::PEM_read_bio_PrivateKey(
217                bio.as_ptr(),
218                std::ptr::null_mut(),
219                None,
220                std::ptr::null_mut(),
221            )
222        };
223        if ptr.is_null() {
224            return Err(ErrorStack::drain());
225        }
226        Ok(unsafe { Pkey::from_ptr(ptr) })
227    }
228
229    /// Serialise the private key to PEM (`PKCS#8` `BEGIN PRIVATE KEY`).
230    ///
231    /// # Errors
232    pub fn to_pem(&self) -> Result<Vec<u8>, ErrorStack> {
233        let mut bio = MemBio::new()?;
234        let rc = unsafe {
235            sys::PEM_write_bio_PrivateKey(
236                bio.as_ptr(),
237                self.ptr,
238                std::ptr::null(),
239                std::ptr::null_mut(),
240                0,
241                None,
242                std::ptr::null_mut(),
243            )
244        };
245        if rc != 1 {
246            return Err(ErrorStack::drain());
247        }
248        Ok(bio.into_vec())
249    }
250
251    /// Load a private key from PEM bytes within a specific library context.
252    ///
253    /// Uses `PEM_read_bio_PrivateKey_ex` so the key's internal algorithm fetch
254    /// uses `ctx`'s provider set.  Necessary when the private key is later used
255    /// for EVP operations inside an isolated (e.g. FIPS) context.
256    ///
257    /// # Errors
258    pub fn from_pem_in(ctx: &Arc<crate::lib_ctx::LibCtx>, pem: &[u8]) -> Result<Self, ErrorStack> {
259        let bio = MemBioBuf::new(pem)?;
260        let ptr = unsafe {
261            sys::PEM_read_bio_PrivateKey_ex(
262                bio.as_ptr(),
263                std::ptr::null_mut(),
264                None,
265                std::ptr::null_mut(),
266                ctx.as_ptr(),
267                std::ptr::null(),
268            )
269        };
270        if ptr.is_null() {
271            return Err(ErrorStack::drain());
272        }
273        Ok(unsafe { Pkey::from_ptr(ptr) })
274    }
275
276    /// Load a private key from DER bytes (auto-detecting `PKCS#8` / traditional).
277    ///
278    /// Zero-copy: the `EVP_PKEY` is decoded from the caller's slice without copying.
279    ///
280    /// # Errors
281    pub fn from_der(der: &[u8]) -> Result<Self, ErrorStack> {
282        let bio = MemBioBuf::new(der)?;
283        let ptr = unsafe { sys::d2i_PrivateKey_bio(bio.as_ptr(), std::ptr::null_mut()) };
284        if ptr.is_null() {
285            return Err(ErrorStack::drain());
286        }
287        Ok(unsafe { Pkey::from_ptr(ptr) })
288    }
289
290    /// Load a private key from passphrase-encrypted PEM.
291    ///
292    /// Passes `passphrase` directly to `PEM_read_bio_PrivateKey` via a
293    /// `pem_password_cb`.  Returns `Err` if the key cannot be decrypted or
294    /// the PEM is malformed.  For unencrypted PEM use [`from_pem`](Self::from_pem).
295    ///
296    /// # Errors
297    pub fn from_pem_passphrase(pem: &[u8], passphrase: &[u8]) -> Result<Self, ErrorStack> {
298        extern "C" fn passwd_cb(
299            buf: *mut std::ffi::c_char,
300            size: std::ffi::c_int,
301            _rwflag: std::ffi::c_int,
302            u: *mut std::ffi::c_void,
303        ) -> std::ffi::c_int {
304            // SAFETY: `u` is `&pw` where `pw: &[u8]` lives on the caller's stack.
305            let pw: &[u8] = unsafe { *(u as *const &[u8]) };
306            // size is the callback buffer capacity; it is always > 0 per the C contract.
307            let max_len = usize::try_from(size).unwrap_or(0);
308            let n = pw.len().min(max_len);
309            unsafe { std::ptr::copy_nonoverlapping(pw.as_ptr(), buf.cast::<u8>(), n) };
310            // n <= max_len == size (as usize), so n fits in i32.
311            i32::try_from(n).unwrap()
312        }
313        let bio = MemBioBuf::new(pem)?;
314        let pw: &[u8] = passphrase;
315        let ptr = unsafe {
316            sys::PEM_read_bio_PrivateKey(
317                bio.as_ptr(),
318                std::ptr::null_mut(),
319                Some(passwd_cb),
320                std::ptr::addr_of!(pw).cast::<std::ffi::c_void>().cast_mut(),
321            )
322        };
323        if ptr.is_null() {
324            return Err(ErrorStack::drain());
325        }
326        Ok(unsafe { Pkey::from_ptr(ptr) })
327    }
328
329    /// Serialise the private key as passphrase-encrypted PKCS#8 PEM
330    /// (`BEGIN ENCRYPTED PRIVATE KEY`).
331    ///
332    /// `cipher` controls the wrapping algorithm
333    /// (e.g. `CipherAlg::fetch(c"AES-256-CBC", None)`).
334    /// The passphrase is passed directly to OpenSSL via `kstr`/`klen`.
335    ///
336    /// # Panics
337    ///
338    /// Panics if `passphrase` is longer than `i32::MAX` bytes.
339    ///
340    /// # Errors
341    pub fn to_pem_encrypted(
342        &self,
343        cipher: &crate::cipher::CipherAlg,
344        passphrase: &[u8],
345    ) -> Result<Vec<u8>, ErrorStack> {
346        let mut bio = MemBio::new()?;
347        let rc = unsafe {
348            sys::PEM_write_bio_PKCS8PrivateKey(
349                bio.as_ptr(),
350                self.ptr,
351                cipher.as_ptr(),
352                passphrase.as_ptr().cast(),
353                i32::try_from(passphrase.len()).expect("passphrase too long"),
354                None,
355                std::ptr::null_mut(),
356            )
357        };
358        if rc != 1 {
359            return Err(ErrorStack::drain());
360        }
361        Ok(bio.into_vec())
362    }
363
364    /// Serialise the private key as unencrypted PKCS#8 DER
365    /// (`PrivateKeyInfo` / `OneAsymmetricKey`, RFC 5958).
366    ///
367    /// Equivalent to writing unencrypted PEM and stripping the base64 wrapper,
368    /// but avoids the encode/decode round-trip.  To encrypt the output, use
369    /// [`to_pem_encrypted`](Self::to_pem_encrypted) instead.
370    ///
371    /// # Errors
372    pub fn to_pkcs8_der(&self) -> Result<Vec<u8>, ErrorStack> {
373        let mut bio = MemBio::new()?;
374        let rc = unsafe { sys::i2d_PKCS8PrivateKeyInfo_bio(bio.as_ptr(), self.ptr) };
375        if rc != 1 {
376            return Err(ErrorStack::drain());
377        }
378        Ok(bio.into_vec())
379    }
380}
381
382// ── PEM loading — public key ──────────────────────────────────────────────────
383
384impl Pkey<Public> {
385    /// Load a public key from PEM (`SubjectPublicKeyInfo` or RSA public key).
386    ///
387    /// # Errors
388    pub fn from_pem(pem: &[u8]) -> Result<Self, ErrorStack> {
389        let bio = MemBioBuf::new(pem)?;
390        let ptr = unsafe {
391            sys::PEM_read_bio_PUBKEY(
392                bio.as_ptr(),
393                std::ptr::null_mut(),
394                None,
395                std::ptr::null_mut(),
396            )
397        };
398        if ptr.is_null() {
399            return Err(ErrorStack::drain());
400        }
401        Ok(unsafe { Pkey::from_ptr(ptr) })
402    }
403
404    /// Load a public key from PEM bytes within a specific library context.
405    ///
406    /// Uses `PEM_read_bio_PUBKEY_ex` so the key's internal algorithm fetch
407    /// uses `ctx`'s provider set.  Necessary when the public key is later used
408    /// for EVP operations inside an isolated (e.g. FIPS) context.
409    ///
410    /// # Errors
411    pub fn from_pem_in(ctx: &Arc<crate::lib_ctx::LibCtx>, pem: &[u8]) -> Result<Self, ErrorStack> {
412        let bio = MemBioBuf::new(pem)?;
413        let ptr = unsafe {
414            sys::PEM_read_bio_PUBKEY_ex(
415                bio.as_ptr(),
416                std::ptr::null_mut(),
417                None,
418                std::ptr::null_mut(),
419                ctx.as_ptr(),
420                std::ptr::null(),
421            )
422        };
423        if ptr.is_null() {
424            return Err(ErrorStack::drain());
425        }
426        Ok(unsafe { Pkey::from_ptr(ptr) })
427    }
428
429    /// Load a public key from DER (`SubjectPublicKeyInfo`).
430    ///
431    /// # Errors
432    pub fn from_der(der: &[u8]) -> Result<Self, ErrorStack> {
433        let bio = MemBioBuf::new(der)?;
434        let ptr = unsafe { sys::d2i_PUBKEY_bio(bio.as_ptr(), std::ptr::null_mut()) };
435        if ptr.is_null() {
436            return Err(ErrorStack::drain());
437        }
438        Ok(unsafe { Pkey::from_ptr(ptr) })
439    }
440
441    /// Serialise the public key to PEM.
442    ///
443    /// # Errors
444    pub fn to_pem(&self) -> Result<Vec<u8>, ErrorStack> {
445        let mut bio = MemBio::new()?;
446        let rc = unsafe { sys::PEM_write_bio_PUBKEY(bio.as_ptr(), self.ptr) };
447        if rc != 1 {
448            return Err(ErrorStack::drain());
449        }
450        Ok(bio.into_vec())
451    }
452}
453
454// Upcast: every `Pkey<Private>` can be viewed as `Pkey<Public>`.
455impl From<Pkey<Private>> for Pkey<Public> {
456    fn from(k: Pkey<Private>) -> Self {
457        unsafe { sys::EVP_PKEY_up_ref(k.ptr) };
458        Pkey {
459            ptr: k.ptr,
460            _role: PhantomData,
461        }
462    }
463}
464
465// ── Key import from OSSL_PARAM ────────────────────────────────────────────────
466
467// EVP_PKEY_fromdata / EVP_PKEY_todata selection constants.
468// These must match the macros in <openssl/keymgmt.h>:
469//   EVP_PKEY_PUBLIC_KEY = OSSL_KEYMGMT_SELECT_ALL_PARAMETERS | SELECT_PUBLIC_KEY
470//                       = (0x04 | 0x80) | 0x02 = 0x86
471//   EVP_PKEY_KEYPAIR    = EVP_PKEY_PUBLIC_KEY | SELECT_PRIVATE_KEY
472//                       = 0x86 | 0x01 = 0x87
473// Using the bare SELECT_PUBLIC_KEY (0x02) would omit domain parameters, which
474// breaks EC keys (the curve group is a domain parameter, not a public-key param).
475const PKEY_PUBLIC_KEY: i32 = 0x86;
476const PKEY_KEYPAIR: i32 = 0x87;
477
478/// Shared implementation for `EVP_PKEY_fromdata`.
479fn pkey_fromdata(
480    ctx: Option<&Arc<crate::lib_ctx::LibCtx>>,
481    pkey_type: &std::ffi::CStr,
482    params: &crate::params::Params<'_>,
483    selection: i32,
484) -> Result<*mut sys::EVP_PKEY, ErrorStack> {
485    let libctx = ctx.map_or(std::ptr::null_mut(), |c| c.as_ptr());
486    let pctx =
487        unsafe { sys::EVP_PKEY_CTX_new_from_name(libctx, pkey_type.as_ptr(), std::ptr::null()) };
488    if pctx.is_null() {
489        return Err(ErrorStack::drain());
490    }
491    let rc = unsafe { sys::EVP_PKEY_fromdata_init(pctx) };
492    if rc != 1 {
493        unsafe { sys::EVP_PKEY_CTX_free(pctx) };
494        return Err(ErrorStack::drain());
495    }
496    let mut pkey: *mut sys::EVP_PKEY = std::ptr::null_mut();
497    let rc = unsafe {
498        sys::EVP_PKEY_fromdata(
499            pctx,
500            std::ptr::addr_of_mut!(pkey),
501            selection,
502            // OSSL_PARAM array is read-only during fromdata; cast is safe.
503            params.as_ptr().cast_mut(),
504        )
505    };
506    unsafe { sys::EVP_PKEY_CTX_free(pctx) };
507    if rc != 1 || pkey.is_null() {
508        return Err(ErrorStack::drain());
509    }
510    Ok(pkey)
511}
512
513/// Shared implementation for `EVP_PKEY_todata`.
514fn pkey_todata(
515    ptr: *mut sys::EVP_PKEY,
516    selection: i32,
517) -> Result<crate::params::Params<'static>, ErrorStack> {
518    let mut out: *mut sys::OSSL_PARAM = std::ptr::null_mut();
519    let rc = unsafe { sys::EVP_PKEY_todata(ptr, selection, std::ptr::addr_of_mut!(out)) };
520    if rc != 1 || out.is_null() {
521        return Err(ErrorStack::drain());
522    }
523    // SAFETY: `out` is a freshly allocated OSSL_PARAM array from OpenSSL;
524    // Params takes ownership and will free it via OSSL_PARAM_free on drop.
525    Ok(unsafe { crate::params::Params::from_owned_ptr(out) })
526}
527
528impl Pkey<Private> {
529    /// Import a private key pair from an `OSSL_PARAM` array.
530    ///
531    /// Equivalent to `EVP_PKEY_fromdata` with `EVP_PKEY_KEYPAIR` selection.
532    /// Pass `ctx = None` to use the global default library context.
533    ///
534    /// # Errors
535    pub fn from_params(
536        ctx: Option<&Arc<crate::lib_ctx::LibCtx>>,
537        pkey_type: &std::ffi::CStr,
538        params: &crate::params::Params<'_>,
539    ) -> Result<Self, ErrorStack> {
540        pkey_fromdata(ctx, pkey_type, params, PKEY_KEYPAIR)
541            .map(|ptr| unsafe { Pkey::from_ptr(ptr) })
542    }
543
544    /// Export all key parameters (private + public) as an owned `OSSL_PARAM` array.
545    ///
546    /// Uses `EVP_PKEY_KEYPAIR` selection so both private and public material
547    /// are included in the returned array.
548    ///
549    /// # Errors
550    pub fn export(&self) -> Result<crate::params::Params<'static>, ErrorStack> {
551        pkey_todata(self.ptr, PKEY_KEYPAIR)
552    }
553}
554
555impl Pkey<Public> {
556    /// Import a public key from an `OSSL_PARAM` array.
557    ///
558    /// Equivalent to `EVP_PKEY_fromdata` with `EVP_PKEY_PUBLIC_KEY` selection.
559    /// Pass `ctx = None` to use the global default library context.
560    ///
561    /// # Errors
562    pub fn from_params(
563        ctx: Option<&Arc<crate::lib_ctx::LibCtx>>,
564        pkey_type: &std::ffi::CStr,
565        params: &crate::params::Params<'_>,
566    ) -> Result<Self, ErrorStack> {
567        pkey_fromdata(ctx, pkey_type, params, PKEY_PUBLIC_KEY)
568            .map(|ptr| unsafe { Pkey::from_ptr(ptr) })
569    }
570
571    /// Export the public key parameters as an owned `OSSL_PARAM` array.
572    ///
573    /// Uses `EVP_PKEY_PUBLIC_KEY` selection.
574    ///
575    /// # Errors
576    pub fn export(&self) -> Result<crate::params::Params<'static>, ErrorStack> {
577        pkey_todata(self.ptr, PKEY_PUBLIC_KEY)
578    }
579}
580
581// ── KeygenCtx — key generation ────────────────────────────────────────────────
582
583/// Context for generating asymmetric key pairs (`EVP_PKEY_CTX` in keygen mode).
584pub struct KeygenCtx {
585    ptr: *mut sys::EVP_PKEY_CTX,
586}
587
588impl KeygenCtx {
589    /// Create a keygen context for the named algorithm.
590    ///
591    /// Common names: `c"RSA"`, `c"EC"`, `c"ED25519"`, `c"X25519"`.
592    ///
593    /// # Errors
594    pub fn new(name: &std::ffi::CStr) -> Result<Self, ErrorStack> {
595        let ptr = unsafe {
596            sys::EVP_PKEY_CTX_new_from_name(std::ptr::null_mut(), name.as_ptr(), std::ptr::null())
597        };
598        if ptr.is_null() {
599            return Err(ErrorStack::drain());
600        }
601        let rc = unsafe { sys::EVP_PKEY_keygen_init(ptr) };
602        if rc != 1 {
603            unsafe { sys::EVP_PKEY_CTX_free(ptr) };
604            return Err(ErrorStack::drain());
605        }
606        Ok(KeygenCtx { ptr })
607    }
608
609    /// Configure parameters before calling `generate`.
610    ///
611    /// # Errors
612    pub fn set_params(&mut self, params: &crate::params::Params<'_>) -> Result<(), ErrorStack> {
613        crate::ossl_call!(sys::EVP_PKEY_CTX_set_params(self.ptr, params.as_ptr()))
614    }
615
616    /// Generate a key pair.
617    ///
618    /// # Errors
619    pub fn generate(&mut self) -> Result<Pkey<Private>, ErrorStack> {
620        let mut key: *mut sys::EVP_PKEY = std::ptr::null_mut();
621        crate::ossl_call!(sys::EVP_PKEY_keygen(self.ptr, std::ptr::addr_of_mut!(key)))?;
622        if key.is_null() {
623            return Err(ErrorStack::drain());
624        }
625        Ok(unsafe { Pkey::from_ptr(key) })
626    }
627}
628
629impl Drop for KeygenCtx {
630    fn drop(&mut self) {
631        unsafe { sys::EVP_PKEY_CTX_free(self.ptr) };
632    }
633}
634
635// ── Signer — streaming DigestSign ─────────────────────────────────────────────
636
637/// Parameters for creating a [`Signer`] or [`Verifier`].
638#[derive(Default)]
639pub struct SignInit<'a> {
640    /// Digest algorithm, or `None` for pre-hashed / `EdDSA` one-shot mode.
641    pub digest: Option<&'a crate::digest::DigestAlg>,
642    /// Optional parameters (e.g. RSA PSS salt length).
643    pub params: Option<&'a crate::params::Params<'a>>,
644}
645
646/// Streaming `DigestSign` context.
647///
648/// Call `update` zero or more times, then `finish` to produce the signature.
649pub struct Signer {
650    ctx: crate::digest::DigestCtx,
651    /// The key is kept alive for the duration of the signer.
652    _key: Pkey<Private>,
653}
654
655impl Signer {
656    /// Create a signer.
657    ///
658    /// # Errors
659    pub fn new(key: &Pkey<Private>, init: &SignInit<'_>) -> Result<Self, ErrorStack> {
660        let ctx = alloc_digest_ctx()?;
661        // Resolve digest name for EVP_DigestSignInit_ex (NULL for Ed25519/one-shot).
662        let md_name_ptr = if let Some(d) = init.digest {
663            let p = unsafe { sys::OBJ_nid2sn(d.nid()) };
664            if p.is_null() {
665                return Err(ErrorStack::drain());
666            }
667            p
668        } else {
669            std::ptr::null()
670        };
671        let params_ptr = init
672            .params
673            .map_or(crate::params::null_params(), crate::params::Params::as_ptr);
674        let rc = unsafe {
675            sys::EVP_DigestSignInit_ex(
676                ctx.as_ptr(),
677                std::ptr::null_mut(),
678                md_name_ptr,
679                std::ptr::null_mut(),
680                std::ptr::null(),
681                key.ptr,
682                params_ptr,
683            )
684        };
685        if rc != 1 {
686            return Err(ErrorStack::drain());
687        }
688        Ok(Signer {
689            ctx,
690            _key: key.clone(),
691        })
692    }
693
694    /// Feed data into the hash.
695    ///
696    /// # Errors
697    pub fn update(&mut self, data: &[u8]) -> Result<(), ErrorStack> {
698        crate::ossl_call!(sys::EVP_DigestSignUpdate(
699            self.ctx.as_ptr(),
700            data.as_ptr().cast(),
701            data.len()
702        ))
703    }
704
705    /// Finalise and return the signature.
706    ///
707    /// Not supported by pure one-shot algorithms such as Ed25519 — use
708    /// [`sign_oneshot`](Self::sign_oneshot) for those.
709    ///
710    /// # Errors
711    pub fn finish(&mut self) -> Result<Vec<u8>, ErrorStack> {
712        // First call with null buf to get the required size.
713        let mut siglen: usize = 0;
714        let rc = unsafe {
715            sys::EVP_DigestSignFinal(
716                self.ctx.as_ptr(),
717                std::ptr::null_mut(),
718                std::ptr::addr_of_mut!(siglen),
719            )
720        };
721        if rc != 1 {
722            return Err(ErrorStack::drain());
723        }
724        let mut sig = vec![0u8; siglen];
725        let rc = unsafe {
726            sys::EVP_DigestSignFinal(
727                self.ctx.as_ptr(),
728                sig.as_mut_ptr(),
729                std::ptr::addr_of_mut!(siglen),
730            )
731        };
732        if rc != 1 {
733            return Err(ErrorStack::drain());
734        }
735        sig.truncate(siglen);
736        Ok(sig)
737    }
738
739    /// One-shot sign over `data`.
740    ///
741    /// Required for algorithms that do not support streaming (Ed25519, Ed448).
742    /// For algorithms that do support streaming, prefer `update` + `finish`.
743    ///
744    /// # Errors
745    pub fn sign_oneshot(&mut self, data: &[u8]) -> Result<Vec<u8>, ErrorStack> {
746        // First call: query the required signature length.
747        let mut siglen: usize = 0;
748        let rc = unsafe {
749            sys::EVP_DigestSign(
750                self.ctx.as_ptr(),
751                std::ptr::null_mut(),
752                std::ptr::addr_of_mut!(siglen),
753                data.as_ptr(),
754                data.len(),
755            )
756        };
757        if rc != 1 {
758            return Err(ErrorStack::drain());
759        }
760        let mut sig = vec![0u8; siglen];
761        let rc = unsafe {
762            sys::EVP_DigestSign(
763                self.ctx.as_ptr(),
764                sig.as_mut_ptr(),
765                std::ptr::addr_of_mut!(siglen),
766                data.as_ptr(),
767                data.len(),
768            )
769        };
770        if rc != 1 {
771            return Err(ErrorStack::drain());
772        }
773        sig.truncate(siglen);
774        Ok(sig)
775    }
776
777    /// One-shot sign over `data` into a caller-provided buffer `sig`.
778    ///
779    /// The buffer must be at least as large as the maximum signature size for
780    /// the key (use `EVP_PKEY_get_size` or the algorithm's known fixed length).
781    /// For algorithms with a fixed signature size (e.g. ML-DSA, Ed25519), the
782    /// caller can pre-allocate the exact size and avoid the null-output query.
783    ///
784    /// Returns the number of bytes written.  The caller should truncate `sig`
785    /// to the returned length if the actual size may differ from the buffer size.
786    ///
787    /// # Errors
788    pub fn sign_into(&mut self, data: &[u8], sig: &mut [u8]) -> Result<usize, ErrorStack> {
789        let mut siglen = sig.len();
790        let rc = unsafe {
791            sys::EVP_DigestSign(
792                self.ctx.as_ptr(),
793                sig.as_mut_ptr(),
794                std::ptr::addr_of_mut!(siglen),
795                data.as_ptr(),
796                data.len(),
797            )
798        };
799        if rc != 1 {
800            return Err(ErrorStack::drain());
801        }
802        Ok(siglen)
803    }
804}
805
806// ── Verifier — streaming DigestVerify ─────────────────────────────────────────
807
808/// Streaming `DigestVerify` context.
809pub struct Verifier {
810    ctx: crate::digest::DigestCtx,
811    _key: Pkey<Public>,
812}
813
814impl Verifier {
815    /// Create a verifier.
816    ///
817    /// # Errors
818    pub fn new(key: &Pkey<Public>, init: &SignInit<'_>) -> Result<Self, ErrorStack> {
819        let ctx = alloc_digest_ctx()?;
820        // Resolve digest name for EVP_DigestVerifyInit_ex (NULL for Ed25519/one-shot).
821        let md_name_ptr = if let Some(d) = init.digest {
822            let p = unsafe { sys::OBJ_nid2sn(d.nid()) };
823            if p.is_null() {
824                return Err(ErrorStack::drain());
825            }
826            p
827        } else {
828            std::ptr::null()
829        };
830        let params_ptr = init
831            .params
832            .map_or(crate::params::null_params(), crate::params::Params::as_ptr);
833        let rc = unsafe {
834            sys::EVP_DigestVerifyInit_ex(
835                ctx.as_ptr(),
836                std::ptr::null_mut(),
837                md_name_ptr,
838                std::ptr::null_mut(),
839                std::ptr::null(),
840                key.ptr,
841                params_ptr,
842            )
843        };
844        if rc != 1 {
845            return Err(ErrorStack::drain());
846        }
847        Ok(Verifier {
848            ctx,
849            _key: key.clone(),
850        })
851    }
852
853    /// Feed data into the hash.
854    ///
855    /// # Errors
856    pub fn update(&mut self, data: &[u8]) -> Result<(), ErrorStack> {
857        crate::ossl_call!(sys::EVP_DigestVerifyUpdate(
858            self.ctx.as_ptr(),
859            data.as_ptr().cast(),
860            data.len()
861        ))
862    }
863
864    /// Verify `signature` against all data fed via `update`.
865    ///
866    /// Returns `Ok(true)` if valid, `Ok(false)` if the signature is incorrect,
867    /// or `Err` on a fatal OpenSSL error.
868    ///
869    /// Not supported by pure one-shot algorithms such as Ed25519 — use
870    /// [`verify_oneshot`](Self::verify_oneshot) for those.
871    ///
872    /// # Errors
873    pub fn verify(&mut self, signature: &[u8]) -> Result<bool, ErrorStack> {
874        let rc = unsafe {
875            sys::EVP_DigestVerifyFinal(self.ctx.as_ptr(), signature.as_ptr(), signature.len())
876        };
877        match rc {
878            1 => Ok(true),
879            0 => Ok(false),
880            _ => Err(ErrorStack::drain()),
881        }
882    }
883
884    /// One-shot verify `signature` over `data`.
885    ///
886    /// Required for algorithms that do not support streaming (Ed25519, Ed448).
887    ///
888    /// # Errors
889    pub fn verify_oneshot(&mut self, data: &[u8], signature: &[u8]) -> Result<bool, ErrorStack> {
890        let rc = unsafe {
891            sys::EVP_DigestVerify(
892                self.ctx.as_ptr(),
893                signature.as_ptr(),
894                signature.len(),
895                data.as_ptr(),
896                data.len(),
897            )
898        };
899        match rc {
900            1 => Ok(true),
901            0 => Ok(false),
902            _ => Err(ErrorStack::drain()),
903        }
904    }
905}
906
907// ── Internal helper: allocate an EVP_MD_CTX for DigestSign/Verify ─────────────
908
909/// Allocate a fresh, algorithm-unbound `EVP_MD_CTX` for use with
910/// `EVP_DigestSign*Init_ex` / `EVP_DigestVerify*Init_ex`.
911fn alloc_digest_ctx() -> Result<crate::digest::DigestCtx, ErrorStack> {
912    let ctx_ptr = unsafe { sys::EVP_MD_CTX_new() };
913    if ctx_ptr.is_null() {
914        return Err(ErrorStack::drain());
915    }
916    // SAFETY: DigestCtx takes ownership; ptr is valid and non-null.
917    Ok(unsafe { crate::digest::DigestCtx::from_ptr(ctx_ptr) })
918}
919
920// ── DeriveCtx — ECDH / DH key agreement ──────────────────────────────────────
921
922/// Asymmetric key-agreement context (`EVP_PKEY_CTX` in derive mode).
923pub struct DeriveCtx {
924    ptr: *mut sys::EVP_PKEY_CTX,
925}
926
927impl DeriveCtx {
928    /// Create a derive context from a private key.
929    ///
930    /// # Errors
931    pub fn new(key: &Pkey<Private>) -> Result<Self, ErrorStack> {
932        let ptr = unsafe {
933            sys::EVP_PKEY_CTX_new_from_pkey(std::ptr::null_mut(), key.ptr, std::ptr::null())
934        };
935        if ptr.is_null() {
936            return Err(ErrorStack::drain());
937        }
938        crate::ossl_call!(sys::EVP_PKEY_derive_init(ptr)).map_err(|e| {
939            unsafe { sys::EVP_PKEY_CTX_free(ptr) };
940            e
941        })?;
942        Ok(DeriveCtx { ptr })
943    }
944
945    /// Set the peer's public key.
946    ///
947    /// # Errors
948    pub fn set_peer(&mut self, peer: &Pkey<Public>) -> Result<(), ErrorStack> {
949        crate::ossl_call!(sys::EVP_PKEY_derive_set_peer(self.ptr, peer.ptr))
950    }
951
952    /// Derive the shared secret into `out`.
953    ///
954    /// Returns the number of bytes written.
955    ///
956    /// # Errors
957    pub fn derive(&mut self, out: &mut [u8]) -> Result<usize, ErrorStack> {
958        let mut len = out.len();
959        crate::ossl_call!(sys::EVP_PKEY_derive(
960            self.ptr,
961            out.as_mut_ptr(),
962            std::ptr::addr_of_mut!(len)
963        ))?;
964        Ok(len)
965    }
966
967    /// Query the required output length (call with empty slice).
968    ///
969    /// # Errors
970    pub fn derive_len(&mut self) -> Result<usize, ErrorStack> {
971        let mut len: usize = 0;
972        crate::ossl_call!(sys::EVP_PKEY_derive(
973            self.ptr,
974            std::ptr::null_mut(),
975            std::ptr::addr_of_mut!(len)
976        ))?;
977        Ok(len)
978    }
979}
980
981impl Drop for DeriveCtx {
982    fn drop(&mut self) {
983        unsafe { sys::EVP_PKEY_CTX_free(self.ptr) };
984    }
985}
986
987// ── PkeyEncryptCtx / PkeyDecryptCtx ──────────────────────────────────────────
988
989/// RSA asymmetric encryption context.
990pub struct PkeyEncryptCtx {
991    ptr: *mut sys::EVP_PKEY_CTX,
992}
993
994impl PkeyEncryptCtx {
995    /// Create an encryption context from a public key.
996    ///
997    /// `params` is applied immediately after init if `Some`.  Typical use:
998    /// ```ignore
999    /// let oaep = ParamBuilder::new()?.push_utf8_string(c"pad-mode", c"oaep")?.build()?;
1000    /// let ctx = PkeyEncryptCtx::new(&pub_key, Some(&oaep))?;
1001    /// ```
1002    ///
1003    /// # Errors
1004    pub fn new(
1005        key: &Pkey<Public>,
1006        params: Option<&crate::params::Params<'_>>,
1007    ) -> Result<Self, ErrorStack> {
1008        let ptr = unsafe {
1009            sys::EVP_PKEY_CTX_new_from_pkey(std::ptr::null_mut(), key.ptr, std::ptr::null())
1010        };
1011        if ptr.is_null() {
1012            return Err(ErrorStack::drain());
1013        }
1014        crate::ossl_call!(sys::EVP_PKEY_encrypt_init(ptr)).map_err(|e| {
1015            unsafe { sys::EVP_PKEY_CTX_free(ptr) };
1016            e
1017        })?;
1018        let ctx = PkeyEncryptCtx { ptr };
1019        if let Some(p) = params {
1020            crate::ossl_call!(sys::EVP_PKEY_CTX_set_params(ctx.ptr, p.as_ptr())).map_err(|e| {
1021                unsafe { sys::EVP_PKEY_CTX_free(ptr) };
1022                e
1023            })?;
1024        }
1025        Ok(ctx)
1026    }
1027
1028    /// Configure parameters (e.g. RSA padding mode).
1029    ///
1030    /// # Errors
1031    pub fn set_params(&mut self, params: &crate::params::Params<'_>) -> Result<(), ErrorStack> {
1032        crate::ossl_call!(sys::EVP_PKEY_CTX_set_params(self.ptr, params.as_ptr()))
1033    }
1034
1035    /// Encrypt `plaintext` into `ciphertext`.
1036    ///
1037    /// Returns the number of bytes written.
1038    ///
1039    /// # Errors
1040    pub fn encrypt(
1041        &mut self,
1042        plaintext: &[u8],
1043        ciphertext: &mut [u8],
1044    ) -> Result<usize, ErrorStack> {
1045        let mut outlen = ciphertext.len();
1046        crate::ossl_call!(sys::EVP_PKEY_encrypt(
1047            self.ptr,
1048            ciphertext.as_mut_ptr(),
1049            std::ptr::addr_of_mut!(outlen),
1050            plaintext.as_ptr(),
1051            plaintext.len()
1052        ))?;
1053        Ok(outlen)
1054    }
1055
1056    /// Query the ciphertext length for a given plaintext length.
1057    ///
1058    /// # Errors
1059    pub fn encrypt_len(&mut self, plaintext_len: usize) -> Result<usize, ErrorStack> {
1060        let mut outlen: usize = 0;
1061        crate::ossl_call!(sys::EVP_PKEY_encrypt(
1062            self.ptr,
1063            std::ptr::null_mut(),
1064            std::ptr::addr_of_mut!(outlen),
1065            std::ptr::null(),
1066            plaintext_len
1067        ))?;
1068        Ok(outlen)
1069    }
1070}
1071
1072impl Drop for PkeyEncryptCtx {
1073    fn drop(&mut self) {
1074        unsafe { sys::EVP_PKEY_CTX_free(self.ptr) };
1075    }
1076}
1077
1078/// RSA asymmetric decryption context.
1079pub struct PkeyDecryptCtx {
1080    ptr: *mut sys::EVP_PKEY_CTX,
1081}
1082
1083impl PkeyDecryptCtx {
1084    /// Create a decryption context from a private key.
1085    ///
1086    /// `params` is applied immediately after init if `Some`.
1087    ///
1088    /// # Errors
1089    pub fn new(
1090        key: &Pkey<Private>,
1091        params: Option<&crate::params::Params<'_>>,
1092    ) -> Result<Self, ErrorStack> {
1093        let ptr = unsafe {
1094            sys::EVP_PKEY_CTX_new_from_pkey(std::ptr::null_mut(), key.ptr, std::ptr::null())
1095        };
1096        if ptr.is_null() {
1097            return Err(ErrorStack::drain());
1098        }
1099        crate::ossl_call!(sys::EVP_PKEY_decrypt_init(ptr)).map_err(|e| {
1100            unsafe { sys::EVP_PKEY_CTX_free(ptr) };
1101            e
1102        })?;
1103        let ctx = PkeyDecryptCtx { ptr };
1104        if let Some(p) = params {
1105            crate::ossl_call!(sys::EVP_PKEY_CTX_set_params(ctx.ptr, p.as_ptr())).map_err(|e| {
1106                unsafe { sys::EVP_PKEY_CTX_free(ptr) };
1107                e
1108            })?;
1109        }
1110        Ok(ctx)
1111    }
1112
1113    /// Configure parameters.
1114    ///
1115    /// # Errors
1116    pub fn set_params(&mut self, params: &crate::params::Params<'_>) -> Result<(), ErrorStack> {
1117        crate::ossl_call!(sys::EVP_PKEY_CTX_set_params(self.ptr, params.as_ptr()))
1118    }
1119
1120    /// Decrypt `ciphertext` into `plaintext`.
1121    ///
1122    /// Returns the number of bytes written.
1123    ///
1124    /// # Errors
1125    pub fn decrypt(
1126        &mut self,
1127        ciphertext: &[u8],
1128        plaintext: &mut [u8],
1129    ) -> Result<usize, ErrorStack> {
1130        let mut outlen = plaintext.len();
1131        crate::ossl_call!(sys::EVP_PKEY_decrypt(
1132            self.ptr,
1133            plaintext.as_mut_ptr(),
1134            std::ptr::addr_of_mut!(outlen),
1135            ciphertext.as_ptr(),
1136            ciphertext.len()
1137        ))?;
1138        Ok(outlen)
1139    }
1140}
1141
1142impl Drop for PkeyDecryptCtx {
1143    fn drop(&mut self) {
1144        unsafe { sys::EVP_PKEY_CTX_free(self.ptr) };
1145    }
1146}
1147
1148// ── EncapCtx / DecapCtx — KEM (OpenSSL 3.2+) ────────────────────────────────
1149
1150/// KEM encapsulation output.
1151#[cfg(ossl320)]
1152pub struct EncapResult {
1153    /// Wrapped key (encoded shared secret).
1154    pub wrapped_key: Vec<u8>,
1155    /// Shared secret (plaintext).
1156    pub shared_secret: Vec<u8>,
1157}
1158
1159/// KEM encapsulation context (recipient's public key).
1160#[cfg(ossl320)]
1161pub struct EncapCtx {
1162    ptr: *mut sys::EVP_PKEY_CTX,
1163}
1164
1165#[cfg(ossl320)]
1166impl EncapCtx {
1167    /// Create a KEM encapsulation context from the recipient's public key.
1168    ///
1169    /// # Errors
1170    pub fn new(key: &Pkey<Public>) -> Result<Self, ErrorStack> {
1171        let ptr = unsafe {
1172            sys::EVP_PKEY_CTX_new_from_pkey(std::ptr::null_mut(), key.ptr, std::ptr::null())
1173        };
1174        if ptr.is_null() {
1175            return Err(ErrorStack::drain());
1176        }
1177        crate::ossl_call!(sys::EVP_PKEY_encapsulate_init(ptr, std::ptr::null())).map_err(|e| {
1178            unsafe { sys::EVP_PKEY_CTX_free(ptr) };
1179            e
1180        })?;
1181        Ok(EncapCtx { ptr })
1182    }
1183
1184    /// Perform encapsulation, returning the wrapped key and shared secret.
1185    ///
1186    /// # Errors
1187    pub fn encapsulate(&mut self) -> Result<EncapResult, ErrorStack> {
1188        let mut wkeylen: usize = 0;
1189        let mut sslen: usize = 0;
1190        // Query lengths first.
1191        let rc = unsafe {
1192            sys::EVP_PKEY_encapsulate(
1193                self.ptr,
1194                std::ptr::null_mut(),
1195                std::ptr::addr_of_mut!(wkeylen),
1196                std::ptr::null_mut(),
1197                std::ptr::addr_of_mut!(sslen),
1198            )
1199        };
1200        if rc != 1 {
1201            return Err(ErrorStack::drain());
1202        }
1203        let mut wrapped_key = vec![0u8; wkeylen];
1204        let mut shared_secret = vec![0u8; sslen];
1205        crate::ossl_call!(sys::EVP_PKEY_encapsulate(
1206            self.ptr,
1207            wrapped_key.as_mut_ptr(),
1208            std::ptr::addr_of_mut!(wkeylen),
1209            shared_secret.as_mut_ptr(),
1210            std::ptr::addr_of_mut!(sslen)
1211        ))?;
1212        wrapped_key.truncate(wkeylen);
1213        shared_secret.truncate(sslen);
1214        Ok(EncapResult {
1215            wrapped_key,
1216            shared_secret,
1217        })
1218    }
1219}
1220
1221#[cfg(ossl320)]
1222impl Drop for EncapCtx {
1223    fn drop(&mut self) {
1224        unsafe { sys::EVP_PKEY_CTX_free(self.ptr) };
1225    }
1226}
1227
1228/// KEM decapsulation context (recipient's private key).
1229#[cfg(ossl320)]
1230pub struct DecapCtx {
1231    ptr: *mut sys::EVP_PKEY_CTX,
1232}
1233
1234#[cfg(ossl320)]
1235impl DecapCtx {
1236    /// Create a KEM decapsulation context from the recipient's private key.
1237    ///
1238    /// # Errors
1239    pub fn new(key: &Pkey<Private>) -> Result<Self, ErrorStack> {
1240        let ptr = unsafe {
1241            sys::EVP_PKEY_CTX_new_from_pkey(std::ptr::null_mut(), key.ptr, std::ptr::null())
1242        };
1243        if ptr.is_null() {
1244            return Err(ErrorStack::drain());
1245        }
1246        crate::ossl_call!(sys::EVP_PKEY_decapsulate_init(ptr, std::ptr::null())).map_err(|e| {
1247            unsafe { sys::EVP_PKEY_CTX_free(ptr) };
1248            e
1249        })?;
1250        Ok(DecapCtx { ptr })
1251    }
1252
1253    /// Recover the shared secret from a wrapped key.
1254    ///
1255    /// # Errors
1256    pub fn decapsulate(&mut self, wrapped_key: &[u8]) -> Result<Vec<u8>, ErrorStack> {
1257        let mut sslen: usize = 0;
1258        // Query output length.
1259        let rc = unsafe {
1260            sys::EVP_PKEY_decapsulate(
1261                self.ptr,
1262                std::ptr::null_mut(),
1263                std::ptr::addr_of_mut!(sslen),
1264                wrapped_key.as_ptr(),
1265                wrapped_key.len(),
1266            )
1267        };
1268        if rc != 1 {
1269            return Err(ErrorStack::drain());
1270        }
1271        let mut ss = vec![0u8; sslen];
1272        crate::ossl_call!(sys::EVP_PKEY_decapsulate(
1273            self.ptr,
1274            ss.as_mut_ptr(),
1275            std::ptr::addr_of_mut!(sslen),
1276            wrapped_key.as_ptr(),
1277            wrapped_key.len()
1278        ))?;
1279        ss.truncate(sslen);
1280        Ok(ss)
1281    }
1282}
1283
1284#[cfg(ossl320)]
1285impl Drop for DecapCtx {
1286    fn drop(&mut self) {
1287        unsafe { sys::EVP_PKEY_CTX_free(self.ptr) };
1288    }
1289}
1290
1291// ── RawSigner — pre-hashed / no-digest signing ───────────────────────────────
1292
1293/// Raw (no-digest) signing context wrapping `EVP_PKEY_CTX` after `EVP_PKEY_sign_init`.
1294///
1295/// Use this for algorithms where the caller has already hashed the data, such
1296/// as raw ECDSA (data is the hash) or raw RSA with explicit padding.  For
1297/// algorithms that hash internally, use [`Signer`] or `MessageSigner`.
1298///
1299/// The context is reusable: after a successful [`sign`](Self::sign) call the
1300/// init state is preserved, so the same padding parameters apply to further
1301/// sign calls with the same key.
1302pub struct RawSigner {
1303    ctx: *mut sys::EVP_PKEY_CTX,
1304}
1305
1306// SAFETY: EVP_PKEY_CTX is not thread-safe on a shared pointer, but
1307// RawSigner owns its ctx exclusively and &mut self enforces single-caller.
1308unsafe impl Send for RawSigner {}
1309
1310impl RawSigner {
1311    /// Create and initialise a sign context (`EVP_PKEY_CTX_new_from_pkey` +
1312    /// `EVP_PKEY_sign_init`).
1313    ///
1314    /// Pass `libctx = Some(ctx)` to restrict provider lookup to that library
1315    /// context; `None` uses the key's own library context.
1316    ///
1317    /// # Errors
1318    pub fn new(
1319        key: &Pkey<Private>,
1320        libctx: Option<&Arc<crate::lib_ctx::LibCtx>>,
1321    ) -> Result<Self, ErrorStack> {
1322        let lctx = libctx.map_or(std::ptr::null_mut(), |c| c.as_ptr());
1323        let ptr = unsafe { sys::EVP_PKEY_CTX_new_from_pkey(lctx, key.ptr, std::ptr::null()) };
1324        if ptr.is_null() {
1325            return Err(ErrorStack::drain());
1326        }
1327        crate::ossl_call!(sys::EVP_PKEY_sign_init(ptr)).map_err(|e| {
1328            unsafe { sys::EVP_PKEY_CTX_free(ptr) };
1329            e
1330        })?;
1331        Ok(RawSigner { ctx: ptr })
1332    }
1333
1334    /// Apply parameters after init (e.g. RSA padding mode, salt length).
1335    ///
1336    /// # Errors
1337    pub fn set_params(&mut self, params: &crate::params::Params<'_>) -> Result<(), ErrorStack> {
1338        crate::ossl_call!(sys::EVP_PKEY_CTX_set_params(self.ctx, params.as_ptr()))
1339    }
1340
1341    /// Query the signature output size for the given input length.
1342    ///
1343    /// Calls `EVP_PKEY_sign` with a null output pointer — does not consume
1344    /// the signing state.
1345    ///
1346    /// # Errors
1347    pub fn sign_len(&mut self, tbs_len: usize) -> Result<usize, ErrorStack> {
1348        let mut siglen: usize = 0;
1349        crate::ossl_call!(sys::EVP_PKEY_sign(
1350            self.ctx,
1351            std::ptr::null_mut(),
1352            std::ptr::addr_of_mut!(siglen),
1353            std::ptr::null(),
1354            tbs_len,
1355        ))?;
1356        Ok(siglen)
1357    }
1358
1359    /// Sign pre-hashed data into `sig`.  Returns the number of bytes written.
1360    ///
1361    /// `sig.len()` must be >= `sign_len(tbs.len())`.
1362    ///
1363    /// # Errors
1364    pub fn sign(&mut self, tbs: &[u8], sig: &mut [u8]) -> Result<usize, ErrorStack> {
1365        let mut siglen = sig.len();
1366        crate::ossl_call!(sys::EVP_PKEY_sign(
1367            self.ctx,
1368            sig.as_mut_ptr(),
1369            std::ptr::addr_of_mut!(siglen),
1370            tbs.as_ptr(),
1371            tbs.len(),
1372        ))?;
1373        Ok(siglen)
1374    }
1375
1376    /// Sign pre-hashed data, allocating the output buffer.
1377    ///
1378    /// Convenience wrapper around [`sign_len`](Self::sign_len) + [`sign`](Self::sign).
1379    ///
1380    /// # Errors
1381    pub fn sign_alloc(&mut self, tbs: &[u8]) -> Result<Vec<u8>, ErrorStack> {
1382        let siglen = self.sign_len(tbs.len())?;
1383        let mut sig = vec![0u8; siglen];
1384        let written = self.sign(tbs, &mut sig)?;
1385        sig.truncate(written);
1386        Ok(sig)
1387    }
1388}
1389
1390impl Drop for RawSigner {
1391    fn drop(&mut self) {
1392        unsafe { sys::EVP_PKEY_CTX_free(self.ctx) };
1393    }
1394}
1395
1396// ── RawVerifier — pre-hashed / no-digest verification ────────────────────────
1397
1398/// Raw (no-digest) verification context wrapping `EVP_PKEY_CTX` after
1399/// `EVP_PKEY_verify_init`.
1400///
1401/// Mirror of [`RawSigner`] for the verification side.
1402pub struct RawVerifier {
1403    ctx: *mut sys::EVP_PKEY_CTX,
1404}
1405
1406unsafe impl Send for RawVerifier {}
1407
1408impl RawVerifier {
1409    /// Create and initialise a verify context.
1410    ///
1411    /// # Errors
1412    pub fn new<T: HasPublic>(
1413        key: &Pkey<T>,
1414        libctx: Option<&Arc<crate::lib_ctx::LibCtx>>,
1415    ) -> Result<Self, ErrorStack> {
1416        let lctx = libctx.map_or(std::ptr::null_mut(), |c| c.as_ptr());
1417        let ptr = unsafe { sys::EVP_PKEY_CTX_new_from_pkey(lctx, key.ptr, std::ptr::null()) };
1418        if ptr.is_null() {
1419            return Err(ErrorStack::drain());
1420        }
1421        crate::ossl_call!(sys::EVP_PKEY_verify_init(ptr)).map_err(|e| {
1422            unsafe { sys::EVP_PKEY_CTX_free(ptr) };
1423            e
1424        })?;
1425        Ok(RawVerifier { ctx: ptr })
1426    }
1427
1428    /// Apply parameters after init (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.ctx, params.as_ptr()))
1433    }
1434
1435    /// Verify `sig` against pre-hashed `tbs`.  Returns `Ok(())` if valid.
1436    ///
1437    /// # Errors
1438    ///
1439    /// Returns `Err` if the signature is invalid or on any OpenSSL error.
1440    pub fn verify(&mut self, tbs: &[u8], sig: &[u8]) -> Result<(), ErrorStack> {
1441        crate::ossl_call!(sys::EVP_PKEY_verify(
1442            self.ctx,
1443            sig.as_ptr(),
1444            sig.len(),
1445            tbs.as_ptr(),
1446            tbs.len(),
1447        ))
1448    }
1449}
1450
1451impl Drop for RawVerifier {
1452    fn drop(&mut self) {
1453        unsafe { sys::EVP_PKEY_CTX_free(self.ctx) };
1454    }
1455}
1456
1457// ── SigAlg — EVP_SIGNATURE algorithm descriptor (OpenSSL 3.2+) ──────────────
1458
1459/// Algorithm descriptor for `EVP_SIGNATURE` (OpenSSL 3.2+).
1460///
1461/// Mirrors `DigestAlg` / `CipherAlg` / `MacAlg` in naming and lifecycle.
1462/// Used with [`MessageSigner`] and [`MessageVerifier`] for algorithms such
1463/// as ML-DSA, SLH-DSA, and Ed25519/Ed448 with context strings.
1464#[cfg(ossl320)]
1465pub struct SigAlg {
1466    ptr: *mut sys::EVP_SIGNATURE,
1467}
1468
1469// SAFETY: EVP_SIGNATURE is reference-counted.
1470#[cfg(ossl320)]
1471unsafe impl Send for SigAlg {}
1472#[cfg(ossl320)]
1473unsafe impl Sync for SigAlg {}
1474
1475#[cfg(ossl320)]
1476impl SigAlg {
1477    /// Fetch an `EVP_SIGNATURE` by name from the default library context.
1478    ///
1479    /// Example names: `c"ML-DSA-44"`, `c"ED25519"`, `c"SLH-DSA-SHA2-128s"`.
1480    ///
1481    /// # Errors
1482    pub fn fetch(
1483        name: &std::ffi::CStr,
1484        props: Option<&std::ffi::CStr>,
1485    ) -> Result<Self, ErrorStack> {
1486        let props_ptr = props.map_or(std::ptr::null(), std::ffi::CStr::as_ptr);
1487        let ptr =
1488            unsafe { sys::EVP_SIGNATURE_fetch(std::ptr::null_mut(), name.as_ptr(), props_ptr) };
1489        if ptr.is_null() {
1490            return Err(ErrorStack::drain());
1491        }
1492        Ok(SigAlg { ptr })
1493    }
1494
1495    /// Fetch an `EVP_SIGNATURE` by name within a specific library context.
1496    ///
1497    /// # Errors
1498    pub fn fetch_in(
1499        ctx: &Arc<crate::lib_ctx::LibCtx>,
1500        name: &std::ffi::CStr,
1501        props: Option<&std::ffi::CStr>,
1502    ) -> Result<Self, ErrorStack> {
1503        let props_ptr = props.map_or(std::ptr::null(), std::ffi::CStr::as_ptr);
1504        let ptr = unsafe { sys::EVP_SIGNATURE_fetch(ctx.as_ptr(), name.as_ptr(), props_ptr) };
1505        if ptr.is_null() {
1506            return Err(ErrorStack::drain());
1507        }
1508        Ok(SigAlg { ptr })
1509    }
1510}
1511
1512#[cfg(ossl320)]
1513impl Clone for SigAlg {
1514    fn clone(&self) -> Self {
1515        unsafe { sys::EVP_SIGNATURE_up_ref(self.ptr) };
1516        SigAlg { ptr: self.ptr }
1517    }
1518}
1519
1520#[cfg(ossl320)]
1521impl Drop for SigAlg {
1522    fn drop(&mut self) {
1523        unsafe { sys::EVP_SIGNATURE_free(self.ptr) };
1524    }
1525}
1526
1527// ── MessageSigner — EVP_PKEY_sign_message_* streaming sign (OpenSSL 3.2+) ────
1528
1529/// Stateful signing context using `EVP_PKEY_sign_message_*` (OpenSSL 3.2+).
1530///
1531/// Used for algorithms that do not use a separate internal digest (`ML-DSA`,
1532/// `SLH-DSA`, `Ed25519` with context strings).  Unlike [`Signer`], the
1533/// algorithm is specified as a [`SigAlg`] rather than a digest name.
1534///
1535/// Call [`update`](Self::update) zero or more times (if the algorithm supports
1536/// streaming — check with [`supports_streaming`](Self::supports_streaming)),
1537/// then [`finish`](Self::finish) to produce the signature.  For algorithms
1538/// that only support one-shot operation, use [`sign_oneshot`](Self::sign_oneshot).
1539#[cfg(ossl320)]
1540pub struct MessageSigner {
1541    ctx: *mut sys::EVP_PKEY_CTX,
1542}
1543
1544#[cfg(ossl320)]
1545unsafe impl Send for MessageSigner {}
1546
1547#[cfg(ossl320)]
1548impl MessageSigner {
1549    /// Create and initialise a message-sign context.
1550    ///
1551    /// `alg` is consumed by the init call; pass a clone if you need to reuse it.
1552    /// `params` sets algorithm-specific options (e.g. context string for Ed25519).
1553    ///
1554    /// # Errors
1555    pub fn new(
1556        key: &Pkey<Private>,
1557        alg: &mut SigAlg,
1558        params: Option<&crate::params::Params<'_>>,
1559    ) -> Result<Self, ErrorStack> {
1560        let ptr = unsafe {
1561            sys::EVP_PKEY_CTX_new_from_pkey(std::ptr::null_mut(), key.ptr, std::ptr::null())
1562        };
1563        if ptr.is_null() {
1564            return Err(ErrorStack::drain());
1565        }
1566        let params_ptr = params.map_or(crate::params::null_params(), crate::params::Params::as_ptr);
1567        crate::ossl_call!(sys::EVP_PKEY_sign_message_init(ptr, alg.ptr, params_ptr)).map_err(
1568            |e| {
1569                unsafe { sys::EVP_PKEY_CTX_free(ptr) };
1570                e
1571            },
1572        )?;
1573        Ok(MessageSigner { ctx: ptr })
1574    }
1575
1576    /// Probe whether this algorithm backend supports incremental `update` calls.
1577    ///
1578    /// Calls `EVP_PKEY_sign_message_update` with an empty input, bracketed by
1579    /// `ERR_set_mark` / `ERR_pop_to_mark` so that a failure does not leave
1580    /// entries on the error queue.  Returns `true` if streaming is supported.
1581    ///
1582    /// If this returns `false`, use [`sign_oneshot`](Self::sign_oneshot) instead.
1583    pub fn supports_streaming(&mut self) -> bool {
1584        // Probe: feed 0 bytes and see if the algorithm accepts it.
1585        // ERR mark/pop ensures the error queue is clean regardless of outcome.
1586        unsafe { sys::ERR_set_mark() };
1587        let probe: [u8; 0] = [];
1588        let rc = unsafe { sys::EVP_PKEY_sign_message_update(self.ctx, probe.as_ptr(), 0) };
1589        unsafe { sys::ERR_pop_to_mark() };
1590        rc == 1
1591    }
1592
1593    /// Feed `data` into the signing operation.
1594    ///
1595    /// Returns `Err` if the algorithm does not support streaming — use
1596    /// [`sign_oneshot`](Self::sign_oneshot) in that case.
1597    ///
1598    /// # Errors
1599    pub fn update(&mut self, data: &[u8]) -> Result<(), ErrorStack> {
1600        crate::ossl_call!(sys::EVP_PKEY_sign_message_update(
1601            self.ctx,
1602            data.as_ptr(),
1603            data.len(),
1604        ))
1605    }
1606
1607    /// Query the signature output length.
1608    ///
1609    /// Calls `EVP_PKEY_sign_message_final` with a null buffer — does not
1610    /// consume the signing state.
1611    ///
1612    /// # Errors
1613    pub fn sig_len(&mut self) -> Result<usize, ErrorStack> {
1614        let mut siglen: usize = 0;
1615        crate::ossl_call!(sys::EVP_PKEY_sign_message_final(
1616            self.ctx,
1617            std::ptr::null_mut(),
1618            std::ptr::addr_of_mut!(siglen),
1619        ))?;
1620        Ok(siglen)
1621    }
1622
1623    /// Finalise and produce the signature into `sig`.
1624    ///
1625    /// Consumes `self` because the context is finalised.  Call
1626    /// [`sig_len`](Self::sig_len) first to size the buffer.  Returns the
1627    /// number of bytes written.
1628    ///
1629    /// # Errors
1630    pub fn finish(self, sig: &mut [u8]) -> Result<usize, ErrorStack> {
1631        let mut siglen = sig.len();
1632        let rc = unsafe {
1633            sys::EVP_PKEY_sign_message_final(
1634                self.ctx,
1635                sig.as_mut_ptr(),
1636                std::ptr::addr_of_mut!(siglen),
1637            )
1638        };
1639        // self is dropped here → EVP_PKEY_CTX_free via Drop.
1640        if rc != 1 {
1641            return Err(ErrorStack::drain());
1642        }
1643        Ok(siglen)
1644    }
1645
1646    /// One-shot sign: feed `data` then finalise into `sig`.
1647    ///
1648    /// Consumes `self`.  Use this for algorithms that do not support
1649    /// streaming (`supports_streaming` returns `false`).
1650    ///
1651    /// # Errors
1652    pub fn sign_oneshot(self, data: &[u8], sig: &mut [u8]) -> Result<usize, ErrorStack> {
1653        // Feed all data, then finalise.  Both ops share the same ctx.
1654        let rc_upd =
1655            unsafe { sys::EVP_PKEY_sign_message_update(self.ctx, data.as_ptr(), data.len()) };
1656        if rc_upd != 1 {
1657            // self dropped here → ctx freed.
1658            return Err(ErrorStack::drain());
1659        }
1660        let mut siglen = sig.len();
1661        let rc_fin = unsafe {
1662            sys::EVP_PKEY_sign_message_final(
1663                self.ctx,
1664                sig.as_mut_ptr(),
1665                std::ptr::addr_of_mut!(siglen),
1666            )
1667        };
1668        // self dropped here → ctx freed.
1669        if rc_fin != 1 {
1670            return Err(ErrorStack::drain());
1671        }
1672        Ok(siglen)
1673    }
1674
1675    /// One-shot sign over `data` using `EVP_PKEY_sign`.
1676    ///
1677    /// The context must have been initialised with `EVP_PKEY_sign_message_init`
1678    /// (this type's constructor); `EVP_PKEY_sign` accepts both
1679    /// `EVP_PKEY_OP_SIGN` and `EVP_PKEY_OP_SIGNMSG` operation modes.
1680    ///
1681    /// When `sig` is `None` the call is a **cheap length query**: for ML-DSA
1682    /// and other algorithms with a fixed output size, no cryptographic
1683    /// computation is performed.  When `sig` is `Some(buf)` the signature is
1684    /// written and the number of bytes actually written is returned.
1685    ///
1686    /// The context is *not* consumed so the same `MessageSigner` may be reused
1687    /// across a size-query + actual-sign pair without re-initialisation.
1688    ///
1689    /// Contrast with [`sign_oneshot`](Self::sign_oneshot): `sign_oneshot`
1690    /// consumes `self` and always writes a signature; `sign` borrows `self`,
1691    /// can query the required length cheaply (pass `sig = None`), and can be
1692    /// called multiple times on the same context.
1693    ///
1694    /// # Errors
1695    pub fn sign(&mut self, data: &[u8], sig: Option<&mut [u8]>) -> Result<usize, ErrorStack> {
1696        let (sig_ptr, mut siglen) = match sig {
1697            Some(buf) => (buf.as_mut_ptr(), buf.len()),
1698            None => (std::ptr::null_mut(), 0usize),
1699        };
1700        crate::ossl_call!(sys::EVP_PKEY_sign(
1701            self.ctx,
1702            sig_ptr,
1703            std::ptr::addr_of_mut!(siglen),
1704            data.as_ptr(),
1705            data.len(),
1706        ))?;
1707        Ok(siglen)
1708    }
1709}
1710
1711#[cfg(ossl320)]
1712impl Drop for MessageSigner {
1713    fn drop(&mut self) {
1714        unsafe { sys::EVP_PKEY_CTX_free(self.ctx) };
1715    }
1716}
1717
1718// ── MessageVerifier — EVP_PKEY_verify_message_* streaming verify (OpenSSL 3.2+)
1719
1720/// Stateful verification context using `EVP_PKEY_verify_message_*` (OpenSSL 3.2+).
1721///
1722/// Mirror of [`MessageSigner`] for the verification side.
1723///
1724/// For streaming mode: call [`set_signature`](Self::set_signature), then
1725/// [`update`](Self::update) zero or more times, then [`finish`](Self::finish).
1726/// For one-shot: call [`verify_oneshot`](Self::verify_oneshot).
1727#[cfg(ossl320)]
1728pub struct MessageVerifier {
1729    ctx: *mut sys::EVP_PKEY_CTX,
1730}
1731
1732#[cfg(ossl320)]
1733unsafe impl Send for MessageVerifier {}
1734
1735#[cfg(ossl320)]
1736impl MessageVerifier {
1737    /// Create and initialise a message-verify context.
1738    ///
1739    /// # Errors
1740    pub fn new<T: HasPublic>(
1741        key: &Pkey<T>,
1742        alg: &mut SigAlg,
1743        params: Option<&crate::params::Params<'_>>,
1744    ) -> Result<Self, ErrorStack> {
1745        let ptr = unsafe {
1746            sys::EVP_PKEY_CTX_new_from_pkey(std::ptr::null_mut(), key.ptr, std::ptr::null())
1747        };
1748        if ptr.is_null() {
1749            return Err(ErrorStack::drain());
1750        }
1751        let params_ptr = params.map_or(crate::params::null_params(), crate::params::Params::as_ptr);
1752        crate::ossl_call!(sys::EVP_PKEY_verify_message_init(ptr, alg.ptr, params_ptr)).map_err(
1753            |e| {
1754                unsafe { sys::EVP_PKEY_CTX_free(ptr) };
1755                e
1756            },
1757        )?;
1758        Ok(MessageVerifier { ctx: ptr })
1759    }
1760
1761    /// Apply parameters after init.
1762    ///
1763    /// # Errors
1764    pub fn set_params(&mut self, params: &crate::params::Params<'_>) -> Result<(), ErrorStack> {
1765        crate::ossl_call!(sys::EVP_PKEY_CTX_set_params(self.ctx, params.as_ptr()))
1766    }
1767
1768    /// Supply the signature to verify against (required before streaming `finish`).
1769    ///
1770    /// Calls `EVP_PKEY_CTX_set_signature`.  Not needed for
1771    /// [`verify_oneshot`](Self::verify_oneshot) which sets it internally.
1772    ///
1773    /// # Errors
1774    pub fn set_signature(&mut self, sig: &[u8]) -> Result<(), ErrorStack> {
1775        crate::ossl_call!(sys::EVP_PKEY_CTX_set_signature(
1776            self.ctx,
1777            sig.as_ptr(),
1778            sig.len()
1779        ))
1780    }
1781
1782    /// Probe whether this algorithm supports incremental `update` calls.
1783    ///
1784    /// Uses the same `ERR_set_mark` / `ERR_pop_to_mark` probe as
1785    /// [`MessageSigner::supports_streaming`].
1786    pub fn supports_streaming(&mut self) -> bool {
1787        unsafe { sys::ERR_set_mark() };
1788        let probe: [u8; 0] = [];
1789        let rc = unsafe { sys::EVP_PKEY_verify_message_update(self.ctx, probe.as_ptr(), 0) };
1790        unsafe { sys::ERR_pop_to_mark() };
1791        rc == 1
1792    }
1793
1794    /// Feed `data` into the verification operation.
1795    ///
1796    /// # Errors
1797    pub fn update(&mut self, data: &[u8]) -> Result<(), ErrorStack> {
1798        crate::ossl_call!(sys::EVP_PKEY_verify_message_update(
1799            self.ctx,
1800            data.as_ptr(),
1801            data.len(),
1802        ))
1803    }
1804
1805    /// Finalise and verify.
1806    ///
1807    /// The signature must have been set via [`set_signature`](Self::set_signature).
1808    /// Consumes `self`.  Returns `Ok(())` if the signature is valid.
1809    ///
1810    /// # Errors
1811    pub fn finish(self) -> Result<(), ErrorStack> {
1812        let rc = unsafe { sys::EVP_PKEY_verify_message_final(self.ctx) };
1813        // self dropped here → ctx freed.
1814        if rc != 1 {
1815            return Err(ErrorStack::drain());
1816        }
1817        Ok(())
1818    }
1819
1820    /// One-shot verify `sig` over `data`.
1821    ///
1822    /// Sets the signature, feeds all data, and finalises.  Consumes `self`.
1823    ///
1824    /// # Errors
1825    pub fn verify_oneshot(self, data: &[u8], sig: &[u8]) -> Result<(), ErrorStack> {
1826        let rc_set = unsafe { sys::EVP_PKEY_CTX_set_signature(self.ctx, sig.as_ptr(), sig.len()) };
1827        if rc_set != 1 {
1828            return Err(ErrorStack::drain());
1829        }
1830        let rc_upd =
1831            unsafe { sys::EVP_PKEY_verify_message_update(self.ctx, data.as_ptr(), data.len()) };
1832        if rc_upd != 1 {
1833            return Err(ErrorStack::drain());
1834        }
1835        let rc_fin = unsafe { sys::EVP_PKEY_verify_message_final(self.ctx) };
1836        // self dropped here → ctx freed.
1837        if rc_fin != 1 {
1838            return Err(ErrorStack::drain());
1839        }
1840        Ok(())
1841    }
1842
1843    /// One-shot verify `sig` over `data` using `EVP_PKEY_verify`.
1844    ///
1845    /// The context must have been initialised with `EVP_PKEY_verify_message_init`
1846    /// (this type's constructor); `EVP_PKEY_verify` accepts both
1847    /// `EVP_PKEY_OP_VERIFY` and `EVP_PKEY_OP_VERIFYMSG` operation modes.
1848    ///
1849    /// Returns `Ok(true)` if the signature verifies, `Ok(false)` if it does
1850    /// not.  Fatal protocol or library errors are returned as `Err`.
1851    ///
1852    /// The context is *not* consumed and may be reused for further verifications.
1853    ///
1854    /// Contrast with [`verify_oneshot`](Self::verify_oneshot): `verify_oneshot`
1855    /// consumes `self`; `verify` borrows `self` and may be called repeatedly
1856    /// without re-creating the context.
1857    ///
1858    /// # Errors
1859    pub fn verify(&mut self, data: &[u8], sig: &[u8]) -> Result<bool, ErrorStack> {
1860        let rc = unsafe {
1861            sys::EVP_PKEY_verify(self.ctx, sig.as_ptr(), sig.len(), data.as_ptr(), data.len())
1862        };
1863        match rc {
1864            1 => Ok(true),
1865            0 => Ok(false),
1866            _ => Err(ErrorStack::drain()),
1867        }
1868    }
1869}
1870
1871#[cfg(ossl320)]
1872impl Drop for MessageVerifier {
1873    fn drop(&mut self) {
1874        unsafe { sys::EVP_PKEY_CTX_free(self.ctx) };
1875    }
1876}
1877
1878// ── Tests ─────────────────────────────────────────────────────────────────────
1879
1880#[cfg(test)]
1881mod tests {
1882    use super::*;
1883
1884    /// Generate an Ed25519 key pair; sign and verify "hello world".
1885    ///
1886    /// Ed25519 is a one-shot algorithm — uses `sign_oneshot` / `verify_oneshot`.
1887    #[test]
1888    fn ed25519_sign_verify() {
1889        let mut kgen = KeygenCtx::new(c"ED25519").unwrap();
1890        let priv_key = kgen.generate().unwrap();
1891        let pub_key = Pkey::<Public>::from(priv_key.clone());
1892
1893        let msg = b"hello world";
1894        let init = SignInit::default();
1895
1896        let mut signer = Signer::new(&priv_key, &init).unwrap();
1897        let sig = signer.sign_oneshot(msg).unwrap();
1898        assert!(!sig.is_empty());
1899
1900        let mut verifier = Verifier::new(&pub_key, &init).unwrap();
1901        assert!(verifier.verify_oneshot(msg, &sig).unwrap());
1902    }
1903
1904    /// Tampered message must fail verification.
1905    #[test]
1906    fn ed25519_verify_wrong_msg_fails() {
1907        let mut kgen = KeygenCtx::new(c"ED25519").unwrap();
1908        let priv_key = kgen.generate().unwrap();
1909        let pub_key = Pkey::<Public>::from(priv_key.clone());
1910
1911        let mut signer = Signer::new(&priv_key, &SignInit::default()).unwrap();
1912        let sig = signer.sign_oneshot(b"correct").unwrap();
1913
1914        let mut verifier = Verifier::new(&pub_key, &SignInit::default()).unwrap();
1915        assert!(!verifier.verify_oneshot(b"tampered", &sig).unwrap());
1916    }
1917
1918    /// Generate an X25519 key pair; perform ECDH derive and confirm length.
1919    #[test]
1920    fn x25519_derive() {
1921        let mut kgen_a = KeygenCtx::new(c"X25519").unwrap();
1922        let priv_a = kgen_a.generate().unwrap();
1923
1924        let mut kgen_b = KeygenCtx::new(c"X25519").unwrap();
1925        let priv_b = kgen_b.generate().unwrap();
1926        let pub_b = Pkey::<Public>::from(priv_b);
1927
1928        let mut derive = DeriveCtx::new(&priv_a).unwrap();
1929        derive.set_peer(&pub_b).unwrap();
1930        let len = derive.derive_len().unwrap();
1931        assert_eq!(len, 32); // X25519 shared secret is always 32 bytes
1932
1933        let mut ss = vec![0u8; len];
1934        let n = derive.derive(&mut ss).unwrap();
1935        assert_eq!(n, 32);
1936        assert_ne!(ss, [0u8; 32]);
1937    }
1938
1939    /// Round-trip through PEM: generate Ed25519, export, re-import, verify key equality.
1940    #[test]
1941    fn pem_round_trip() {
1942        let mut kgen = KeygenCtx::new(c"ED25519").unwrap();
1943        let priv_key = kgen.generate().unwrap();
1944
1945        let pem = priv_key.to_pem().unwrap();
1946        assert!(!pem.is_empty());
1947        assert!(pem.starts_with(b"-----BEGIN"));
1948
1949        let priv_key2 = Pkey::<Private>::from_pem(&pem).unwrap();
1950        assert_eq!(priv_key.bits(), priv_key2.bits());
1951        assert!(priv_key.is_a(c"ED25519"));
1952    }
1953
1954    /// Key metadata: Ed25519 should report 256 bits.
1955    #[test]
1956    fn ed25519_metadata() {
1957        let mut kgen = KeygenCtx::new(c"ED25519").unwrap();
1958        let key = kgen.generate().unwrap();
1959        assert_eq!(key.bits(), 256);
1960        assert_eq!(key.security_bits(), 128);
1961        assert!(key.is_a(c"ED25519"));
1962    }
1963
1964    /// ML-DSA-44 sign and verify using `MessageSigner::sign` / `MessageVerifier::verify`.
1965    #[cfg(ossl320)]
1966    #[test]
1967    fn ml_dsa_44_sign_verify() {
1968        let mut kgen = KeygenCtx::new(c"ML-DSA-44").unwrap();
1969        let priv_key = kgen.generate().unwrap();
1970        let pub_key = Pkey::<Public>::from(priv_key.clone());
1971
1972        let msg = b"hello ML-DSA-44";
1973
1974        let mut alg = SigAlg::fetch(c"ML-DSA-44", None).unwrap();
1975        let mut signer = MessageSigner::new(&priv_key, &mut alg, None).unwrap();
1976        // NULL size query must be cheap (FIPS 204 fixed 2420 bytes).
1977        let sig_len = signer.sign(msg, None).unwrap();
1978        assert_eq!(sig_len, 2420);
1979        let mut sig = vec![0u8; sig_len];
1980        let written = signer.sign(msg, Some(&mut sig)).unwrap();
1981        assert_eq!(written, 2420);
1982
1983        let mut alg2 = SigAlg::fetch(c"ML-DSA-44", None).unwrap();
1984        let mut ver = MessageVerifier::new(&pub_key, &mut alg2, None).unwrap();
1985        assert!(ver.verify(msg, &sig).unwrap());
1986
1987        // Tampered signature must fail.
1988        let mut bad = sig.clone();
1989        bad[0] ^= 0xff;
1990        assert!(!ver.verify(msg, &bad).unwrap());
1991    }
1992
1993    /// ML-DSA-44 sign with a context string (FIPS 204 domain separation).
1994    #[cfg(ossl320)]
1995    #[test]
1996    fn ml_dsa_44_sign_with_context() {
1997        let mut kgen = KeygenCtx::new(c"ML-DSA-44").unwrap();
1998        let priv_key = kgen.generate().unwrap();
1999        let pub_key = Pkey::<Public>::from(priv_key.clone());
2000
2001        let msg = b"signed with context";
2002        let ctx_bytes = b"my-app-domain";
2003
2004        let ctx_params = crate::params::ParamBuilder::new()
2005            .unwrap()
2006            .push_octet_slice(c"context-string", ctx_bytes)
2007            .unwrap()
2008            .build()
2009            .unwrap();
2010
2011        let mut alg = SigAlg::fetch(c"ML-DSA-44", None).unwrap();
2012        let mut signer = MessageSigner::new(&priv_key, &mut alg, Some(&ctx_params)).unwrap();
2013        let sig_len = signer.sign(msg, None).unwrap();
2014        let mut sig = vec![0u8; sig_len];
2015        signer.sign(msg, Some(&mut sig)).unwrap();
2016
2017        // Same context: verify must succeed.
2018        let mut alg2 = SigAlg::fetch(c"ML-DSA-44", None).unwrap();
2019        let mut ver = MessageVerifier::new(&pub_key, &mut alg2, Some(&ctx_params)).unwrap();
2020        assert!(ver.verify(msg, &sig).unwrap());
2021
2022        // Different context: verify must fail.
2023        let other_params = crate::params::ParamBuilder::new()
2024            .unwrap()
2025            .push_octet_slice(c"context-string", b"other-domain")
2026            .unwrap()
2027            .build()
2028            .unwrap();
2029        let mut alg3 = SigAlg::fetch(c"ML-DSA-44", None).unwrap();
2030        let mut ver_bad = MessageVerifier::new(&pub_key, &mut alg3, Some(&other_params)).unwrap();
2031        assert!(!ver_bad.verify(msg, &sig).unwrap());
2032    }
2033
2034    /// `RawSigner` / `RawVerifier` round-trip with P-256 ECDSA over a pre-hashed digest.
2035    #[test]
2036    fn ecdsa_p256_raw_sign_verify() {
2037        // Generate P-256 key.
2038        let mut kgen = KeygenCtx::new(c"EC").unwrap();
2039        let params = crate::params::ParamBuilder::new()
2040            .unwrap()
2041            .push_utf8_string(c"group", c"P-256")
2042            .unwrap()
2043            .build()
2044            .unwrap();
2045        kgen.set_params(&params).unwrap();
2046        let priv_key = kgen.generate().unwrap();
2047        let pub_key = Pkey::<Public>::from(priv_key.clone());
2048
2049        // SHA-256 hash of the message (32 bytes — the "pre-hash").
2050        let tbs: [u8; 32] = *b"0123456789abcdef0123456789abcdef";
2051
2052        let mut signer = RawSigner::new(&priv_key, None).unwrap();
2053        let sig = signer.sign_alloc(&tbs).unwrap();
2054        assert!(!sig.is_empty());
2055
2056        let mut verifier = RawVerifier::new(&pub_key, None).unwrap();
2057        verifier.verify(&tbs, &sig).unwrap();
2058    }
2059
2060    /// `RawVerifier` rejects a tampered signature.
2061    #[test]
2062    fn ecdsa_p256_raw_verify_tampered_fails() {
2063        let mut kgen = KeygenCtx::new(c"EC").unwrap();
2064        let params = crate::params::ParamBuilder::new()
2065            .unwrap()
2066            .push_utf8_string(c"group", c"P-256")
2067            .unwrap()
2068            .build()
2069            .unwrap();
2070        kgen.set_params(&params).unwrap();
2071        let priv_key = kgen.generate().unwrap();
2072        let pub_key = Pkey::<Public>::from(priv_key.clone());
2073
2074        let tbs: [u8; 32] = *b"0123456789abcdef0123456789abcdef";
2075        let mut signer = RawSigner::new(&priv_key, None).unwrap();
2076        let mut sig = signer.sign_alloc(&tbs).unwrap();
2077        // Flip a byte in the signature.
2078        if let Some(b) = sig.last_mut() {
2079            *b ^= 0xff;
2080        }
2081
2082        let mut verifier = RawVerifier::new(&pub_key, None).unwrap();
2083        assert!(verifier.verify(&tbs, &sig).is_err());
2084    }
2085
2086    /// `SigAlg`: fetch, clone, drop — verifies `EVP_SIGNATURE` lifecycle.
2087    ///
2088    /// Does not attempt `sign_message_final`: OpenSSL 3.5's built-in ML-DSA
2089    /// provider implements only the one-shot `EVP_DigestSign` path, not
2090    /// `OSSL_FUNC_SIGNATURE_SIGN_MESSAGE_FINAL`.  Actual ML-DSA signing is
2091    /// done via Signer (`DigestSign` with NULL digest).
2092    #[cfg(ossl320)]
2093    #[test]
2094    fn sig_alg_fetch_clone_drop() {
2095        let alg = SigAlg::fetch(c"ML-DSA-44", None).unwrap();
2096        let alg2 = alg.clone();
2097        drop(alg);
2098        drop(alg2); // both up_ref / free paths exercised
2099    }
2100
2101    /// `MessageSigner` construction and `supports_streaming` probe for Ed25519.
2102    ///
2103    /// Ed25519 supports `EVP_PKEY_sign_message_init`.  The streaming probe
2104    /// uses `ERR_set_mark` / `ERR_pop_to_mark` so it cannot leave error state.
2105    #[cfg(ossl320)]
2106    #[test]
2107    fn message_signer_construction_and_streaming_probe() {
2108        let mut kgen = KeygenCtx::new(c"ED25519").unwrap();
2109        let priv_key = kgen.generate().unwrap();
2110
2111        let mut alg = SigAlg::fetch(c"ED25519", None).unwrap();
2112        let mut signer = MessageSigner::new(&priv_key, &mut alg, None).unwrap();
2113
2114        // Probe must not crash or leave the error queue dirty.
2115        let _streaming = signer.supports_streaming();
2116        // Error queue must be empty after the probe.
2117        assert_eq!(crate::error::ErrorStack::drain().errors().count(), 0);
2118    }
2119
2120    /// `MessageVerifier` construction for Ed25519.
2121    #[cfg(ossl320)]
2122    #[test]
2123    fn message_verifier_construction() {
2124        let mut kgen = KeygenCtx::new(c"ED25519").unwrap();
2125        let priv_key = kgen.generate().unwrap();
2126        let pub_key = Pkey::<Public>::from(priv_key.clone());
2127
2128        let mut alg = SigAlg::fetch(c"ED25519", None).unwrap();
2129        let _verifier = MessageVerifier::new(&pub_key, &mut alg, None).unwrap();
2130    }
2131
2132    /// Encrypted PEM round-trip: generate → `to_pem_encrypted` → `from_pem_passphrase`.
2133    #[test]
2134    fn encrypted_pem_round_trip() {
2135        let mut kgen = KeygenCtx::new(c"ED25519").unwrap();
2136        let key = kgen.generate().unwrap();
2137
2138        let cipher = crate::cipher::CipherAlg::fetch(c"AES-256-CBC", None).unwrap();
2139        let pem = key.to_pem_encrypted(&cipher, b"s3cret").unwrap();
2140        assert!(pem.starts_with(b"-----BEGIN ENCRYPTED PRIVATE KEY-----"));
2141
2142        let key2 = Pkey::<Private>::from_pem_passphrase(&pem, b"s3cret").unwrap();
2143        assert!(key.public_eq(&key2));
2144    }
2145
2146    /// Wrong passphrase must return an error, not silently load a garbage key.
2147    #[test]
2148    fn encrypted_pem_wrong_passphrase_fails() {
2149        let mut kgen = KeygenCtx::new(c"ED25519").unwrap();
2150        let key = kgen.generate().unwrap();
2151
2152        let cipher = crate::cipher::CipherAlg::fetch(c"AES-256-CBC", None).unwrap();
2153        let pem = key.to_pem_encrypted(&cipher, b"correct").unwrap();
2154        assert!(Pkey::<Private>::from_pem_passphrase(&pem, b"wrong").is_err());
2155    }
2156
2157    /// PKCS#8 DER round-trip: generate → `to_pkcs8_der` → `from_der`.
2158    #[test]
2159    fn pkcs8_der_round_trip() {
2160        let mut kgen = KeygenCtx::new(c"ED25519").unwrap();
2161        let key = kgen.generate().unwrap();
2162
2163        let der = key.to_pkcs8_der().unwrap();
2164        assert!(!der.is_empty());
2165
2166        let key2 = Pkey::<Private>::from_der(&der).unwrap();
2167        assert!(key.public_eq(&key2));
2168    }
2169
2170    /// `Pkey<Public>::from_pem_in` loads a public key within an explicit `LibCtx`.
2171    #[test]
2172    fn pubkey_from_pem_in_roundtrip() {
2173        // Generate an EC key and export the public half to PEM.
2174        let mut kgen = KeygenCtx::new(c"ED25519").unwrap();
2175        let priv_key = kgen.generate().unwrap();
2176        let pub_pem = Pkey::<Public>::from(priv_key).to_pem().unwrap();
2177
2178        // Re-import via from_pem_in using the default lib ctx.
2179        let lib_ctx = Arc::new(crate::lib_ctx::LibCtx::new().unwrap());
2180        let pub_key = Pkey::<Public>::from_pem_in(&lib_ctx, &pub_pem).unwrap();
2181
2182        // Sanity: key is usable for verification.
2183        assert!(!pub_key.to_pem().unwrap().is_empty());
2184    }
2185}