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
497// ── PEM loading — public key ──────────────────────────────────────────────────
498
499impl Pkey<Public> {
500    /// Load a public key from PEM (`SubjectPublicKeyInfo` or RSA public key).
501    ///
502    /// # Errors
503    pub fn from_pem(pem: &[u8]) -> Result<Self, ErrorStack> {
504        let bio = MemBioBuf::new(pem)?;
505        let ptr = unsafe {
506            sys::PEM_read_bio_PUBKEY(
507                bio.as_ptr(),
508                std::ptr::null_mut(),
509                None,
510                std::ptr::null_mut(),
511            )
512        };
513        if ptr.is_null() {
514            return Err(ErrorStack::drain());
515        }
516        Ok(unsafe { Pkey::from_ptr(ptr) })
517    }
518
519    /// Load a public key from PEM bytes within a specific library context.
520    ///
521    /// Uses `PEM_read_bio_PUBKEY_ex` so the key's internal algorithm fetch
522    /// uses `ctx`'s provider set.  Necessary when the public key is later used
523    /// for EVP operations inside an isolated (e.g. FIPS) context.
524    ///
525    /// # Errors
526    pub fn from_pem_in(ctx: &Arc<crate::lib_ctx::LibCtx>, pem: &[u8]) -> Result<Self, ErrorStack> {
527        let bio = MemBioBuf::new(pem)?;
528        let ptr = unsafe {
529            sys::PEM_read_bio_PUBKEY_ex(
530                bio.as_ptr(),
531                std::ptr::null_mut(),
532                None,
533                std::ptr::null_mut(),
534                ctx.as_ptr(),
535                std::ptr::null(),
536            )
537        };
538        if ptr.is_null() {
539            return Err(ErrorStack::drain());
540        }
541        Ok(unsafe { Pkey::from_ptr(ptr) })
542    }
543
544    /// Load a public key from DER (`SubjectPublicKeyInfo`).
545    ///
546    /// # Errors
547    pub fn from_der(der: &[u8]) -> Result<Self, ErrorStack> {
548        let bio = MemBioBuf::new(der)?;
549        let ptr = unsafe { sys::d2i_PUBKEY_bio(bio.as_ptr(), std::ptr::null_mut()) };
550        if ptr.is_null() {
551            return Err(ErrorStack::drain());
552        }
553        Ok(unsafe { Pkey::from_ptr(ptr) })
554    }
555
556    /// Load a `SubjectPublicKeyInfo` DER public key using an explicit library context.
557    ///
558    /// Wraps `d2i_PUBKEY_ex` — unlike [`from_der`](Self::from_der) (which uses a
559    /// `BIO`), this function passes the raw byte pointer directly so that the
560    /// key's internal algorithm fetch is bound to `ctx`'s provider set.  Use
561    /// this when the loaded key will be used for EVP operations inside an isolated
562    /// (e.g. FIPS) context.
563    ///
564    /// # Errors
565    ///
566    /// Returns `Err` if the DER bytes are malformed or the algorithm is not
567    /// available in `ctx`.
568    pub fn from_der_in(ctx: &Arc<crate::lib_ctx::LibCtx>, der: &[u8]) -> Result<Self, ErrorStack> {
569        let mut ptr = der.as_ptr();
570        // SAFETY:
571        // - Non-null: `der.as_ptr()` is always valid for a shared slice (even if
572        //   zero-length; OpenSSL rejects zero-length DER before dereferencing).
573        // - `ctx.as_ptr()` is non-null by `LibCtx` constructor invariant; `ctx`
574        //   is kept alive by the `&Arc<LibCtx>` borrow for the entire call.
575        // - Lifetime: `ptr` points into `der` which lives for the call duration;
576        //   OpenSSL reads at most `der.len()` bytes before returning.
577        // - Exclusivity / no data races: `der` is a shared immutable borrow; no
578        //   other thread can mutate it.  `ptr` is a local stack copy — OpenSSL's
579        //   write to `*pp` touches only this variable, not the original slice.
580        let pkey = unsafe {
581            sys::d2i_PUBKEY_ex(
582                std::ptr::null_mut(),
583                std::ptr::addr_of_mut!(ptr),
584                i64::try_from(der.len()).unwrap_or(i64::MAX),
585                ctx.as_ptr(),
586                std::ptr::null(),
587            )
588        };
589        if pkey.is_null() {
590            return Err(ErrorStack::drain());
591        }
592        // SAFETY: `pkey` is non-null and freshly allocated by OpenSSL; we take
593        // exclusive ownership of the reference count.
594        Ok(unsafe { Pkey::from_ptr(pkey) })
595    }
596
597    /// Serialise the public key to PEM.
598    ///
599    /// # Errors
600    pub fn to_pem(&self) -> Result<Vec<u8>, ErrorStack> {
601        let mut bio = MemBio::new()?;
602        let rc = unsafe { sys::PEM_write_bio_PUBKEY(bio.as_ptr(), self.ptr) };
603        if rc != 1 {
604            return Err(ErrorStack::drain());
605        }
606        Ok(bio.into_vec())
607    }
608}
609
610// Upcast: every `Pkey<Private>` can be viewed as `Pkey<Public>`.
611impl From<Pkey<Private>> for Pkey<Public> {
612    fn from(k: Pkey<Private>) -> Self {
613        unsafe { sys::EVP_PKEY_up_ref(k.ptr) };
614        Pkey {
615            ptr: k.ptr,
616            _role: PhantomData,
617        }
618    }
619}
620
621// ── Key import from OSSL_PARAM ────────────────────────────────────────────────
622
623// EVP_PKEY_fromdata / EVP_PKEY_todata selection constants.
624// These must match the macros in <openssl/keymgmt.h>:
625//   EVP_PKEY_PUBLIC_KEY = OSSL_KEYMGMT_SELECT_ALL_PARAMETERS | SELECT_PUBLIC_KEY
626//                       = (0x04 | 0x80) | 0x02 = 0x86
627//   EVP_PKEY_KEYPAIR    = EVP_PKEY_PUBLIC_KEY | SELECT_PRIVATE_KEY
628//                       = 0x86 | 0x01 = 0x87
629// Using the bare SELECT_PUBLIC_KEY (0x02) would omit domain parameters, which
630// breaks EC keys (the curve group is a domain parameter, not a public-key param).
631const PKEY_PUBLIC_KEY: i32 = 0x86;
632const PKEY_KEYPAIR: i32 = 0x87;
633
634/// Shared implementation for `EVP_PKEY_fromdata`.
635fn pkey_fromdata(
636    ctx: Option<&Arc<crate::lib_ctx::LibCtx>>,
637    pkey_type: &std::ffi::CStr,
638    params: &crate::params::Params<'_>,
639    selection: i32,
640) -> Result<*mut sys::EVP_PKEY, ErrorStack> {
641    let libctx = ctx.map_or(std::ptr::null_mut(), |c| c.as_ptr());
642    let pctx =
643        unsafe { sys::EVP_PKEY_CTX_new_from_name(libctx, pkey_type.as_ptr(), std::ptr::null()) };
644    if pctx.is_null() {
645        return Err(ErrorStack::drain());
646    }
647    let rc = unsafe { sys::EVP_PKEY_fromdata_init(pctx) };
648    if rc != 1 {
649        unsafe { sys::EVP_PKEY_CTX_free(pctx) };
650        return Err(ErrorStack::drain());
651    }
652    let mut pkey: *mut sys::EVP_PKEY = std::ptr::null_mut();
653    let rc = unsafe {
654        sys::EVP_PKEY_fromdata(
655            pctx,
656            std::ptr::addr_of_mut!(pkey),
657            selection,
658            // OSSL_PARAM array is read-only during fromdata; cast is safe.
659            params.as_ptr().cast_mut(),
660        )
661    };
662    unsafe { sys::EVP_PKEY_CTX_free(pctx) };
663    if rc != 1 || pkey.is_null() {
664        return Err(ErrorStack::drain());
665    }
666    Ok(pkey)
667}
668
669/// Shared implementation for `EVP_PKEY_todata`.
670fn pkey_todata(
671    ptr: *mut sys::EVP_PKEY,
672    selection: i32,
673) -> Result<crate::params::Params<'static>, ErrorStack> {
674    let mut out: *mut sys::OSSL_PARAM = std::ptr::null_mut();
675    let rc = unsafe { sys::EVP_PKEY_todata(ptr, selection, std::ptr::addr_of_mut!(out)) };
676    if rc != 1 || out.is_null() {
677        return Err(ErrorStack::drain());
678    }
679    // SAFETY: `out` is a freshly allocated OSSL_PARAM array from OpenSSL;
680    // Params takes ownership and will free it via OSSL_PARAM_free on drop.
681    Ok(unsafe { crate::params::Params::from_owned_ptr(out) })
682}
683
684impl Pkey<Private> {
685    /// Import a private key pair from an `OSSL_PARAM` array.
686    ///
687    /// Equivalent to `EVP_PKEY_fromdata` with `EVP_PKEY_KEYPAIR` selection.
688    /// Pass `ctx = None` to use the global default library context.
689    ///
690    /// # Errors
691    pub fn from_params(
692        ctx: Option<&Arc<crate::lib_ctx::LibCtx>>,
693        pkey_type: &std::ffi::CStr,
694        params: &crate::params::Params<'_>,
695    ) -> Result<Self, ErrorStack> {
696        pkey_fromdata(ctx, pkey_type, params, PKEY_KEYPAIR)
697            .map(|ptr| unsafe { Pkey::from_ptr(ptr) })
698    }
699
700    /// Export all key parameters (private + public) as an owned `OSSL_PARAM` array.
701    ///
702    /// Uses `EVP_PKEY_KEYPAIR` selection so both private and public material
703    /// are included in the returned array.
704    ///
705    /// # Errors
706    pub fn export(&self) -> Result<crate::params::Params<'static>, ErrorStack> {
707        pkey_todata(self.ptr, PKEY_KEYPAIR)
708    }
709}
710
711impl Pkey<Public> {
712    /// Import a public key from an `OSSL_PARAM` array.
713    ///
714    /// Equivalent to `EVP_PKEY_fromdata` with `EVP_PKEY_PUBLIC_KEY` selection.
715    /// Pass `ctx = None` to use the global default library context.
716    ///
717    /// # Errors
718    pub fn from_params(
719        ctx: Option<&Arc<crate::lib_ctx::LibCtx>>,
720        pkey_type: &std::ffi::CStr,
721        params: &crate::params::Params<'_>,
722    ) -> Result<Self, ErrorStack> {
723        pkey_fromdata(ctx, pkey_type, params, PKEY_PUBLIC_KEY)
724            .map(|ptr| unsafe { Pkey::from_ptr(ptr) })
725    }
726
727    /// Export the public key parameters as an owned `OSSL_PARAM` array.
728    ///
729    /// Uses `EVP_PKEY_PUBLIC_KEY` selection.
730    ///
731    /// # Errors
732    pub fn export(&self) -> Result<crate::params::Params<'static>, ErrorStack> {
733        pkey_todata(self.ptr, PKEY_PUBLIC_KEY)
734    }
735}
736
737// ── Legacy DER encoding ──────────────────────────────────────────────────────
738
739impl<T: HasPrivate> Pkey<T> {
740    /// Encode this private key to legacy raw-key DER (not PKCS#8).
741    ///
742    /// Wraps `i2d_PrivateKey`.  The output is the algorithm-specific raw key
743    /// structure (e.g. `RSAPrivateKey` / RFC 3447 for RSA, `ECPrivateKey` /
744    /// RFC 5915 for EC) — **not** the `PrivateKeyInfo` / PKCS#8 wrapper.
745    ///
746    /// Use this for interoperability with software that requires the legacy
747    /// format.  For new code prefer [`to_pkcs8_der`](crate::pkey::Pkey::to_pkcs8_der)
748    /// (PKCS#8 / RFC 5958), which is algorithm-agnostic and more widely supported
749    /// by modern toolkits.
750    ///
751    /// # Errors
752    ///
753    /// Returns `Err` if serialisation fails (e.g. the algorithm does not support
754    /// legacy DER export, such as some post-quantum algorithms).
755    pub fn to_der_legacy(&self) -> Result<Vec<u8>, ErrorStack> {
756        // First call with null to query the required output length.
757        // SAFETY:
758        // - Non-null: `self.ptr` is non-null by `Pkey` constructor invariant.
759        // - Lifetime: `self.ptr` is valid for the duration of `self`; this call
760        //   does not store the pointer beyond the call.
761        // - Exclusivity / no data races: `&self` ensures no concurrent mutable
762        //   access to `self.ptr`; the null second argument means OpenSSL only
763        //   reads the key, it does not write through `pp`.
764        let len = unsafe { sys::i2d_PrivateKey(self.ptr, std::ptr::null_mut()) };
765        if len < 0 {
766            return Err(ErrorStack::drain());
767        }
768        let mut buf = vec![0u8; usize::try_from(len).unwrap_or(0)];
769        let mut out_ptr = buf.as_mut_ptr();
770        // SAFETY:
771        // - Non-null: `self.ptr` is non-null (constructor invariant).
772        // - `out_ptr` points into `buf` which has capacity `len` bytes; OpenSSL
773        //   writes exactly `len` bytes and advances `out_ptr` past the data.
774        // - Lifetime: `buf` is alive for the duration of this call; OpenSSL does
775        //   not retain the pointer after returning.
776        // - Exclusivity / no data races: `buf` is exclusively owned here; no
777        //   other reference to its backing memory exists during the call.
778        let written = unsafe { sys::i2d_PrivateKey(self.ptr, std::ptr::addr_of_mut!(out_ptr)) };
779        if written < 0 {
780            return Err(ErrorStack::drain());
781        }
782        buf.truncate(usize::try_from(written).unwrap_or(0));
783        Ok(buf)
784    }
785}
786
787// ── KeygenCtx — key generation ────────────────────────────────────────────────
788
789/// Context for generating asymmetric key pairs (`EVP_PKEY_CTX` in keygen mode).
790pub struct KeygenCtx {
791    ptr: *mut sys::EVP_PKEY_CTX,
792}
793
794impl KeygenCtx {
795    /// Create a keygen context for the named algorithm.
796    ///
797    /// Common names: `c"RSA"`, `c"EC"`, `c"ED25519"`, `c"X25519"`.
798    ///
799    /// # Errors
800    pub fn new(name: &std::ffi::CStr) -> Result<Self, ErrorStack> {
801        let ptr = unsafe {
802            sys::EVP_PKEY_CTX_new_from_name(std::ptr::null_mut(), name.as_ptr(), std::ptr::null())
803        };
804        if ptr.is_null() {
805            return Err(ErrorStack::drain());
806        }
807        let rc = unsafe { sys::EVP_PKEY_keygen_init(ptr) };
808        if rc != 1 {
809            unsafe { sys::EVP_PKEY_CTX_free(ptr) };
810            return Err(ErrorStack::drain());
811        }
812        Ok(KeygenCtx { ptr })
813    }
814
815    /// Configure parameters before calling `generate`.
816    ///
817    /// # Errors
818    pub fn set_params(&mut self, params: &crate::params::Params<'_>) -> Result<(), ErrorStack> {
819        // SAFETY:
820        // - self.ptr is non-null (constructor invariant)
821        // - params.as_ptr() is valid for the duration of this call
822        // - &mut self ensures exclusive access; no concurrent reads or writes
823        crate::ossl_call!(sys::EVP_PKEY_CTX_set_params(self.ptr, params.as_ptr()))
824    }
825
826    /// Retrieve parameter values from this keygen context.
827    ///
828    /// Build a [`Params`][crate::params::Params] with placeholder values for the
829    /// keys you want, call this method, then read back with `params.get_*`.
830    ///
831    /// Useful for reading algorithm-negotiated parameters after keygen initialisation
832    /// (e.g. security strength, EC group name, RSA modulus size).
833    ///
834    /// # Errors
835    ///
836    /// Returns `Err` if `EVP_PKEY_CTX_get_params` fails.
837    pub fn get_params(&self, params: &mut crate::params::Params<'_>) -> Result<(), ErrorStack> {
838        // SAFETY:
839        // - self.ptr is non-null (constructor invariant)
840        // - params.as_mut_ptr() is valid for the duration of this call
841        // - &self ensures no concurrent mutable access to self.ptr
842        crate::ossl_call!(sys::EVP_PKEY_CTX_get_params(self.ptr, params.as_mut_ptr()))
843    }
844
845    /// Return the security strength of the key operation in bits.
846    ///
847    /// Available after keygen initialisation; reads `OSSL_PKEY_PARAM_SECURITY_BITS`
848    /// (`"security-bits"`).
849    ///
850    /// # Errors
851    ///
852    /// Returns `Err` if the context does not support this parameter or is not
853    /// sufficiently initialised.
854    pub fn security_bits(&self) -> Result<u32, ErrorStack> {
855        let mut params = crate::params::ParamBuilder::new()?
856            .push_uint(c"security-bits", 0)?
857            .build()?;
858        // SAFETY: same as get_params — self.ptr is non-null, params pointer is valid.
859        crate::ossl_call!(sys::EVP_PKEY_CTX_get_params(self.ptr, params.as_mut_ptr()))?;
860        params
861            .get_uint(c"security-bits")
862            .map_err(|_| crate::error::ErrorStack::drain())
863    }
864
865    /// Generate a key pair.
866    ///
867    /// # Errors
868    pub fn generate(&mut self) -> Result<Pkey<Private>, ErrorStack> {
869        let mut key: *mut sys::EVP_PKEY = std::ptr::null_mut();
870        crate::ossl_call!(sys::EVP_PKEY_keygen(self.ptr, std::ptr::addr_of_mut!(key)))?;
871        if key.is_null() {
872            return Err(ErrorStack::drain());
873        }
874        Ok(unsafe { Pkey::from_ptr(key) })
875    }
876}
877
878impl Drop for KeygenCtx {
879    fn drop(&mut self) {
880        unsafe { sys::EVP_PKEY_CTX_free(self.ptr) };
881    }
882}
883
884// ── Signer — streaming DigestSign ─────────────────────────────────────────────
885
886/// Parameters for creating a [`Signer`] or [`Verifier`].
887#[derive(Default)]
888pub struct SignInit<'a> {
889    /// Digest algorithm, or `None` for pre-hashed / `EdDSA` one-shot mode.
890    pub digest: Option<&'a crate::digest::DigestAlg>,
891    /// Optional parameters (e.g. RSA PSS salt length).
892    pub params: Option<&'a crate::params::Params<'a>>,
893}
894
895/// Streaming `DigestSign` context.
896///
897/// Call `update` zero or more times, then `finish` to produce the signature.
898pub struct Signer {
899    ctx: crate::digest::DigestCtx,
900    /// The key is kept alive for the duration of the signer.
901    _key: Pkey<Private>,
902}
903
904impl Signer {
905    /// Create a signer.
906    ///
907    /// # Errors
908    pub fn new(key: &Pkey<Private>, init: &SignInit<'_>) -> Result<Self, ErrorStack> {
909        let ctx = alloc_digest_ctx()?;
910        // Resolve digest name for EVP_DigestSignInit_ex (NULL for Ed25519/one-shot).
911        let md_name_ptr = if let Some(d) = init.digest {
912            let p = unsafe { sys::OBJ_nid2sn(d.nid()) };
913            if p.is_null() {
914                return Err(ErrorStack::drain());
915            }
916            p
917        } else {
918            std::ptr::null()
919        };
920        let params_ptr = init
921            .params
922            .map_or(crate::params::null_params(), crate::params::Params::as_ptr);
923        let rc = unsafe {
924            sys::EVP_DigestSignInit_ex(
925                ctx.as_ptr(),
926                std::ptr::null_mut(),
927                md_name_ptr,
928                std::ptr::null_mut(),
929                std::ptr::null(),
930                key.ptr,
931                params_ptr,
932            )
933        };
934        if rc != 1 {
935            return Err(ErrorStack::drain());
936        }
937        Ok(Signer {
938            ctx,
939            _key: key.clone(),
940        })
941    }
942
943    /// Feed data into the hash.
944    ///
945    /// # Errors
946    pub fn update(&mut self, data: &[u8]) -> Result<(), ErrorStack> {
947        crate::ossl_call!(sys::EVP_DigestSignUpdate(
948            self.ctx.as_ptr(),
949            data.as_ptr().cast(),
950            data.len()
951        ))
952    }
953
954    /// Finalise and return the signature.
955    ///
956    /// Not supported by pure one-shot algorithms such as Ed25519 — use
957    /// [`sign_oneshot`](Self::sign_oneshot) for those.
958    ///
959    /// # Errors
960    pub fn finish(&mut self) -> Result<Vec<u8>, ErrorStack> {
961        // First call with null buf to get the required size.
962        let mut siglen: usize = 0;
963        let rc = unsafe {
964            sys::EVP_DigestSignFinal(
965                self.ctx.as_ptr(),
966                std::ptr::null_mut(),
967                std::ptr::addr_of_mut!(siglen),
968            )
969        };
970        if rc != 1 {
971            return Err(ErrorStack::drain());
972        }
973        let mut sig = vec![0u8; siglen];
974        let rc = unsafe {
975            sys::EVP_DigestSignFinal(
976                self.ctx.as_ptr(),
977                sig.as_mut_ptr(),
978                std::ptr::addr_of_mut!(siglen),
979            )
980        };
981        if rc != 1 {
982            return Err(ErrorStack::drain());
983        }
984        sig.truncate(siglen);
985        Ok(sig)
986    }
987
988    /// One-shot sign over `data`.
989    ///
990    /// Required for algorithms that do not support streaming (Ed25519, Ed448).
991    /// For algorithms that do support streaming, prefer `update` + `finish`.
992    ///
993    /// # Errors
994    pub fn sign_oneshot(&mut self, data: &[u8]) -> Result<Vec<u8>, ErrorStack> {
995        // First call: query the required signature length.
996        let mut siglen: usize = 0;
997        let rc = unsafe {
998            sys::EVP_DigestSign(
999                self.ctx.as_ptr(),
1000                std::ptr::null_mut(),
1001                std::ptr::addr_of_mut!(siglen),
1002                data.as_ptr(),
1003                data.len(),
1004            )
1005        };
1006        if rc != 1 {
1007            return Err(ErrorStack::drain());
1008        }
1009        let mut sig = vec![0u8; siglen];
1010        let rc = unsafe {
1011            sys::EVP_DigestSign(
1012                self.ctx.as_ptr(),
1013                sig.as_mut_ptr(),
1014                std::ptr::addr_of_mut!(siglen),
1015                data.as_ptr(),
1016                data.len(),
1017            )
1018        };
1019        if rc != 1 {
1020            return Err(ErrorStack::drain());
1021        }
1022        sig.truncate(siglen);
1023        Ok(sig)
1024    }
1025
1026    /// One-shot sign over `data` into a caller-provided buffer `sig`.
1027    ///
1028    /// The buffer must be at least as large as the maximum signature size for
1029    /// the key (use `EVP_PKEY_get_size` or the algorithm's known fixed length).
1030    /// For algorithms with a fixed signature size (e.g. ML-DSA, Ed25519), the
1031    /// caller can pre-allocate the exact size and avoid the null-output query.
1032    ///
1033    /// Returns the number of bytes written.  The caller should truncate `sig`
1034    /// to the returned length if the actual size may differ from the buffer size.
1035    ///
1036    /// # Errors
1037    pub fn sign_into(&mut self, data: &[u8], sig: &mut [u8]) -> Result<usize, ErrorStack> {
1038        let mut siglen = sig.len();
1039        let rc = unsafe {
1040            sys::EVP_DigestSign(
1041                self.ctx.as_ptr(),
1042                sig.as_mut_ptr(),
1043                std::ptr::addr_of_mut!(siglen),
1044                data.as_ptr(),
1045                data.len(),
1046            )
1047        };
1048        if rc != 1 {
1049            return Err(ErrorStack::drain());
1050        }
1051        Ok(siglen)
1052    }
1053}
1054
1055// ── Verifier — streaming DigestVerify ─────────────────────────────────────────
1056
1057/// Streaming `DigestVerify` context.
1058pub struct Verifier {
1059    ctx: crate::digest::DigestCtx,
1060    _key: Pkey<Public>,
1061}
1062
1063impl Verifier {
1064    /// Create a verifier.
1065    ///
1066    /// # Errors
1067    pub fn new(key: &Pkey<Public>, init: &SignInit<'_>) -> Result<Self, ErrorStack> {
1068        let ctx = alloc_digest_ctx()?;
1069        // Resolve digest name for EVP_DigestVerifyInit_ex (NULL for Ed25519/one-shot).
1070        let md_name_ptr = if let Some(d) = init.digest {
1071            let p = unsafe { sys::OBJ_nid2sn(d.nid()) };
1072            if p.is_null() {
1073                return Err(ErrorStack::drain());
1074            }
1075            p
1076        } else {
1077            std::ptr::null()
1078        };
1079        let params_ptr = init
1080            .params
1081            .map_or(crate::params::null_params(), crate::params::Params::as_ptr);
1082        let rc = unsafe {
1083            sys::EVP_DigestVerifyInit_ex(
1084                ctx.as_ptr(),
1085                std::ptr::null_mut(),
1086                md_name_ptr,
1087                std::ptr::null_mut(),
1088                std::ptr::null(),
1089                key.ptr,
1090                params_ptr,
1091            )
1092        };
1093        if rc != 1 {
1094            return Err(ErrorStack::drain());
1095        }
1096        Ok(Verifier {
1097            ctx,
1098            _key: key.clone(),
1099        })
1100    }
1101
1102    /// Feed data into the hash.
1103    ///
1104    /// # Errors
1105    pub fn update(&mut self, data: &[u8]) -> Result<(), ErrorStack> {
1106        crate::ossl_call!(sys::EVP_DigestVerifyUpdate(
1107            self.ctx.as_ptr(),
1108            data.as_ptr().cast(),
1109            data.len()
1110        ))
1111    }
1112
1113    /// Verify `signature` against all data fed via `update`.
1114    ///
1115    /// Returns `Ok(true)` if valid, `Ok(false)` if the signature is incorrect,
1116    /// or `Err` on a fatal OpenSSL error.
1117    ///
1118    /// Not supported by pure one-shot algorithms such as Ed25519 — use
1119    /// [`verify_oneshot`](Self::verify_oneshot) for those.
1120    ///
1121    /// # Errors
1122    pub fn verify(&mut self, signature: &[u8]) -> Result<bool, ErrorStack> {
1123        let rc = unsafe {
1124            sys::EVP_DigestVerifyFinal(self.ctx.as_ptr(), signature.as_ptr(), signature.len())
1125        };
1126        match rc {
1127            1 => Ok(true),
1128            0 => Ok(false),
1129            _ => Err(ErrorStack::drain()),
1130        }
1131    }
1132
1133    /// One-shot verify `signature` over `data`.
1134    ///
1135    /// Required for algorithms that do not support streaming (Ed25519, Ed448).
1136    ///
1137    /// # Errors
1138    pub fn verify_oneshot(&mut self, data: &[u8], signature: &[u8]) -> Result<bool, ErrorStack> {
1139        let rc = unsafe {
1140            sys::EVP_DigestVerify(
1141                self.ctx.as_ptr(),
1142                signature.as_ptr(),
1143                signature.len(),
1144                data.as_ptr(),
1145                data.len(),
1146            )
1147        };
1148        match rc {
1149            1 => Ok(true),
1150            0 => Ok(false),
1151            _ => Err(ErrorStack::drain()),
1152        }
1153    }
1154}
1155
1156// ── Internal helper: allocate an EVP_MD_CTX for DigestSign/Verify ─────────────
1157
1158/// Allocate a fresh, algorithm-unbound `EVP_MD_CTX` for use with
1159/// `EVP_DigestSign*Init_ex` / `EVP_DigestVerify*Init_ex`.
1160fn alloc_digest_ctx() -> Result<crate::digest::DigestCtx, ErrorStack> {
1161    let ctx_ptr = unsafe { sys::EVP_MD_CTX_new() };
1162    if ctx_ptr.is_null() {
1163        return Err(ErrorStack::drain());
1164    }
1165    // SAFETY: DigestCtx takes ownership; ptr is valid and non-null.
1166    Ok(unsafe { crate::digest::DigestCtx::from_ptr(ctx_ptr) })
1167}
1168
1169// ── DeriveCtx — ECDH / DH key agreement ──────────────────────────────────────
1170
1171/// Asymmetric key-agreement context (`EVP_PKEY_CTX` in derive mode).
1172pub struct DeriveCtx {
1173    ptr: *mut sys::EVP_PKEY_CTX,
1174}
1175
1176impl DeriveCtx {
1177    /// Create a derive context from a private key.
1178    ///
1179    /// # Errors
1180    pub fn new(key: &Pkey<Private>) -> Result<Self, ErrorStack> {
1181        let ptr = unsafe {
1182            sys::EVP_PKEY_CTX_new_from_pkey(std::ptr::null_mut(), key.ptr, std::ptr::null())
1183        };
1184        if ptr.is_null() {
1185            return Err(ErrorStack::drain());
1186        }
1187        crate::ossl_call!(sys::EVP_PKEY_derive_init(ptr)).map_err(|e| {
1188            unsafe { sys::EVP_PKEY_CTX_free(ptr) };
1189            e
1190        })?;
1191        Ok(DeriveCtx { ptr })
1192    }
1193
1194    /// Set the peer's public key.
1195    ///
1196    /// # Errors
1197    pub fn set_peer(&mut self, peer: &Pkey<Public>) -> Result<(), ErrorStack> {
1198        crate::ossl_call!(sys::EVP_PKEY_derive_set_peer(self.ptr, peer.ptr))
1199    }
1200
1201    /// Derive the shared secret into `out`.
1202    ///
1203    /// Returns the number of bytes written.
1204    ///
1205    /// # Errors
1206    pub fn derive(&mut self, out: &mut [u8]) -> Result<usize, ErrorStack> {
1207        let mut len = out.len();
1208        crate::ossl_call!(sys::EVP_PKEY_derive(
1209            self.ptr,
1210            out.as_mut_ptr(),
1211            std::ptr::addr_of_mut!(len)
1212        ))?;
1213        Ok(len)
1214    }
1215
1216    /// Query the required output length (call with empty slice).
1217    ///
1218    /// # Errors
1219    pub fn derive_len(&mut self) -> Result<usize, ErrorStack> {
1220        let mut len: usize = 0;
1221        crate::ossl_call!(sys::EVP_PKEY_derive(
1222            self.ptr,
1223            std::ptr::null_mut(),
1224            std::ptr::addr_of_mut!(len)
1225        ))?;
1226        Ok(len)
1227    }
1228}
1229
1230impl Drop for DeriveCtx {
1231    fn drop(&mut self) {
1232        unsafe { sys::EVP_PKEY_CTX_free(self.ptr) };
1233    }
1234}
1235
1236// ── PkeyEncryptCtx / PkeyDecryptCtx ──────────────────────────────────────────
1237
1238/// RSA asymmetric encryption context.
1239pub struct PkeyEncryptCtx {
1240    ptr: *mut sys::EVP_PKEY_CTX,
1241}
1242
1243impl PkeyEncryptCtx {
1244    /// Create an encryption context from a public key.
1245    ///
1246    /// `params` is applied immediately after init if `Some`.  Typical use:
1247    /// ```ignore
1248    /// let oaep = ParamBuilder::new()?.push_utf8_string(c"pad-mode", c"oaep")?.build()?;
1249    /// let ctx = PkeyEncryptCtx::new(&pub_key, Some(&oaep))?;
1250    /// ```
1251    ///
1252    /// # Errors
1253    pub fn new(
1254        key: &Pkey<Public>,
1255        params: Option<&crate::params::Params<'_>>,
1256    ) -> Result<Self, ErrorStack> {
1257        let ptr = unsafe {
1258            sys::EVP_PKEY_CTX_new_from_pkey(std::ptr::null_mut(), key.ptr, std::ptr::null())
1259        };
1260        if ptr.is_null() {
1261            return Err(ErrorStack::drain());
1262        }
1263        crate::ossl_call!(sys::EVP_PKEY_encrypt_init(ptr)).map_err(|e| {
1264            unsafe { sys::EVP_PKEY_CTX_free(ptr) };
1265            e
1266        })?;
1267        let ctx = PkeyEncryptCtx { ptr };
1268        if let Some(p) = params {
1269            crate::ossl_call!(sys::EVP_PKEY_CTX_set_params(ctx.ptr, p.as_ptr())).map_err(|e| {
1270                unsafe { sys::EVP_PKEY_CTX_free(ptr) };
1271                e
1272            })?;
1273        }
1274        Ok(ctx)
1275    }
1276
1277    /// Configure parameters (e.g. RSA padding mode).
1278    ///
1279    /// # Errors
1280    pub fn set_params(&mut self, params: &crate::params::Params<'_>) -> Result<(), ErrorStack> {
1281        crate::ossl_call!(sys::EVP_PKEY_CTX_set_params(self.ptr, params.as_ptr()))
1282    }
1283
1284    /// Encrypt `plaintext` into `ciphertext`.
1285    ///
1286    /// Returns the number of bytes written.
1287    ///
1288    /// # Errors
1289    pub fn encrypt(
1290        &mut self,
1291        plaintext: &[u8],
1292        ciphertext: &mut [u8],
1293    ) -> Result<usize, ErrorStack> {
1294        let mut outlen = ciphertext.len();
1295        crate::ossl_call!(sys::EVP_PKEY_encrypt(
1296            self.ptr,
1297            ciphertext.as_mut_ptr(),
1298            std::ptr::addr_of_mut!(outlen),
1299            plaintext.as_ptr(),
1300            plaintext.len()
1301        ))?;
1302        Ok(outlen)
1303    }
1304
1305    /// Query the ciphertext length for a given plaintext length.
1306    ///
1307    /// # Errors
1308    pub fn encrypt_len(&mut self, plaintext_len: usize) -> Result<usize, ErrorStack> {
1309        let mut outlen: usize = 0;
1310        crate::ossl_call!(sys::EVP_PKEY_encrypt(
1311            self.ptr,
1312            std::ptr::null_mut(),
1313            std::ptr::addr_of_mut!(outlen),
1314            std::ptr::null(),
1315            plaintext_len
1316        ))?;
1317        Ok(outlen)
1318    }
1319}
1320
1321impl Drop for PkeyEncryptCtx {
1322    fn drop(&mut self) {
1323        unsafe { sys::EVP_PKEY_CTX_free(self.ptr) };
1324    }
1325}
1326
1327/// RSA asymmetric decryption context.
1328pub struct PkeyDecryptCtx {
1329    ptr: *mut sys::EVP_PKEY_CTX,
1330}
1331
1332impl PkeyDecryptCtx {
1333    /// Create a decryption context from a private key.
1334    ///
1335    /// `params` is applied immediately after init if `Some`.
1336    ///
1337    /// # Errors
1338    pub fn new(
1339        key: &Pkey<Private>,
1340        params: Option<&crate::params::Params<'_>>,
1341    ) -> Result<Self, ErrorStack> {
1342        let ptr = unsafe {
1343            sys::EVP_PKEY_CTX_new_from_pkey(std::ptr::null_mut(), key.ptr, std::ptr::null())
1344        };
1345        if ptr.is_null() {
1346            return Err(ErrorStack::drain());
1347        }
1348        crate::ossl_call!(sys::EVP_PKEY_decrypt_init(ptr)).map_err(|e| {
1349            unsafe { sys::EVP_PKEY_CTX_free(ptr) };
1350            e
1351        })?;
1352        let ctx = PkeyDecryptCtx { ptr };
1353        if let Some(p) = params {
1354            crate::ossl_call!(sys::EVP_PKEY_CTX_set_params(ctx.ptr, p.as_ptr())).map_err(|e| {
1355                unsafe { sys::EVP_PKEY_CTX_free(ptr) };
1356                e
1357            })?;
1358        }
1359        Ok(ctx)
1360    }
1361
1362    /// Configure parameters.
1363    ///
1364    /// # Errors
1365    pub fn set_params(&mut self, params: &crate::params::Params<'_>) -> Result<(), ErrorStack> {
1366        crate::ossl_call!(sys::EVP_PKEY_CTX_set_params(self.ptr, params.as_ptr()))
1367    }
1368
1369    /// Decrypt `ciphertext` into `plaintext`.
1370    ///
1371    /// Returns the number of bytes written.
1372    ///
1373    /// # Errors
1374    pub fn decrypt(
1375        &mut self,
1376        ciphertext: &[u8],
1377        plaintext: &mut [u8],
1378    ) -> Result<usize, ErrorStack> {
1379        let mut outlen = plaintext.len();
1380        crate::ossl_call!(sys::EVP_PKEY_decrypt(
1381            self.ptr,
1382            plaintext.as_mut_ptr(),
1383            std::ptr::addr_of_mut!(outlen),
1384            ciphertext.as_ptr(),
1385            ciphertext.len()
1386        ))?;
1387        Ok(outlen)
1388    }
1389}
1390
1391impl Drop for PkeyDecryptCtx {
1392    fn drop(&mut self) {
1393        unsafe { sys::EVP_PKEY_CTX_free(self.ptr) };
1394    }
1395}
1396
1397// ── EncapCtx / DecapCtx — KEM (OpenSSL 3.2+) ────────────────────────────────
1398
1399/// KEM encapsulation output.
1400#[cfg(ossl320)]
1401pub struct EncapResult {
1402    /// Wrapped key (encoded shared secret).
1403    pub wrapped_key: Vec<u8>,
1404    /// Shared secret (plaintext).
1405    pub shared_secret: Vec<u8>,
1406}
1407
1408/// KEM encapsulation context (recipient's public key).
1409#[cfg(ossl320)]
1410pub struct EncapCtx {
1411    ptr: *mut sys::EVP_PKEY_CTX,
1412}
1413
1414#[cfg(ossl320)]
1415impl EncapCtx {
1416    /// Create a KEM encapsulation context from the recipient's public key.
1417    ///
1418    /// # Errors
1419    pub fn new(key: &Pkey<Public>) -> Result<Self, ErrorStack> {
1420        let ptr = unsafe {
1421            sys::EVP_PKEY_CTX_new_from_pkey(std::ptr::null_mut(), key.ptr, std::ptr::null())
1422        };
1423        if ptr.is_null() {
1424            return Err(ErrorStack::drain());
1425        }
1426        crate::ossl_call!(sys::EVP_PKEY_encapsulate_init(ptr, std::ptr::null())).map_err(|e| {
1427            unsafe { sys::EVP_PKEY_CTX_free(ptr) };
1428            e
1429        })?;
1430        Ok(EncapCtx { ptr })
1431    }
1432
1433    /// Perform encapsulation, returning the wrapped key and shared secret.
1434    ///
1435    /// # Errors
1436    pub fn encapsulate(&mut self) -> Result<EncapResult, ErrorStack> {
1437        let mut wkeylen: usize = 0;
1438        let mut sslen: usize = 0;
1439        // Query lengths first.
1440        let rc = unsafe {
1441            sys::EVP_PKEY_encapsulate(
1442                self.ptr,
1443                std::ptr::null_mut(),
1444                std::ptr::addr_of_mut!(wkeylen),
1445                std::ptr::null_mut(),
1446                std::ptr::addr_of_mut!(sslen),
1447            )
1448        };
1449        if rc != 1 {
1450            return Err(ErrorStack::drain());
1451        }
1452        let mut wrapped_key = vec![0u8; wkeylen];
1453        let mut shared_secret = vec![0u8; sslen];
1454        crate::ossl_call!(sys::EVP_PKEY_encapsulate(
1455            self.ptr,
1456            wrapped_key.as_mut_ptr(),
1457            std::ptr::addr_of_mut!(wkeylen),
1458            shared_secret.as_mut_ptr(),
1459            std::ptr::addr_of_mut!(sslen)
1460        ))?;
1461        wrapped_key.truncate(wkeylen);
1462        shared_secret.truncate(sslen);
1463        Ok(EncapResult {
1464            wrapped_key,
1465            shared_secret,
1466        })
1467    }
1468}
1469
1470#[cfg(ossl320)]
1471impl Drop for EncapCtx {
1472    fn drop(&mut self) {
1473        unsafe { sys::EVP_PKEY_CTX_free(self.ptr) };
1474    }
1475}
1476
1477/// KEM decapsulation context (recipient's private key).
1478#[cfg(ossl320)]
1479pub struct DecapCtx {
1480    ptr: *mut sys::EVP_PKEY_CTX,
1481}
1482
1483#[cfg(ossl320)]
1484impl DecapCtx {
1485    /// Create a KEM decapsulation context from the recipient's private key.
1486    ///
1487    /// # Errors
1488    pub fn new(key: &Pkey<Private>) -> Result<Self, ErrorStack> {
1489        let ptr = unsafe {
1490            sys::EVP_PKEY_CTX_new_from_pkey(std::ptr::null_mut(), key.ptr, std::ptr::null())
1491        };
1492        if ptr.is_null() {
1493            return Err(ErrorStack::drain());
1494        }
1495        crate::ossl_call!(sys::EVP_PKEY_decapsulate_init(ptr, std::ptr::null())).map_err(|e| {
1496            unsafe { sys::EVP_PKEY_CTX_free(ptr) };
1497            e
1498        })?;
1499        Ok(DecapCtx { ptr })
1500    }
1501
1502    /// Recover the shared secret from a wrapped key.
1503    ///
1504    /// # Errors
1505    pub fn decapsulate(&mut self, wrapped_key: &[u8]) -> Result<Vec<u8>, ErrorStack> {
1506        let mut sslen: usize = 0;
1507        // Query output length.
1508        let rc = unsafe {
1509            sys::EVP_PKEY_decapsulate(
1510                self.ptr,
1511                std::ptr::null_mut(),
1512                std::ptr::addr_of_mut!(sslen),
1513                wrapped_key.as_ptr(),
1514                wrapped_key.len(),
1515            )
1516        };
1517        if rc != 1 {
1518            return Err(ErrorStack::drain());
1519        }
1520        let mut ss = vec![0u8; sslen];
1521        crate::ossl_call!(sys::EVP_PKEY_decapsulate(
1522            self.ptr,
1523            ss.as_mut_ptr(),
1524            std::ptr::addr_of_mut!(sslen),
1525            wrapped_key.as_ptr(),
1526            wrapped_key.len()
1527        ))?;
1528        ss.truncate(sslen);
1529        Ok(ss)
1530    }
1531}
1532
1533#[cfg(ossl320)]
1534impl Drop for DecapCtx {
1535    fn drop(&mut self) {
1536        unsafe { sys::EVP_PKEY_CTX_free(self.ptr) };
1537    }
1538}
1539
1540// ── RawSigner — pre-hashed / no-digest signing ───────────────────────────────
1541
1542/// Raw (no-digest) signing context wrapping `EVP_PKEY_CTX` after `EVP_PKEY_sign_init`.
1543///
1544/// Use this for algorithms where the caller has already hashed the data, such
1545/// as raw ECDSA (data is the hash) or raw RSA with explicit padding.  For
1546/// algorithms that hash internally, use [`Signer`] or `MessageSigner`.
1547///
1548/// The context is reusable: after a successful [`sign`](Self::sign) call the
1549/// init state is preserved, so the same padding parameters apply to further
1550/// sign calls with the same key.
1551pub struct RawSigner {
1552    ctx: *mut sys::EVP_PKEY_CTX,
1553}
1554
1555// SAFETY: EVP_PKEY_CTX is not thread-safe on a shared pointer, but
1556// RawSigner owns its ctx exclusively and &mut self enforces single-caller.
1557unsafe impl Send for RawSigner {}
1558
1559impl RawSigner {
1560    /// Create and initialise a sign context (`EVP_PKEY_CTX_new_from_pkey` +
1561    /// `EVP_PKEY_sign_init`).
1562    ///
1563    /// Pass `libctx = Some(ctx)` to restrict provider lookup to that library
1564    /// context; `None` uses the key's own library context.
1565    ///
1566    /// # Errors
1567    pub fn new(
1568        key: &Pkey<Private>,
1569        libctx: Option<&Arc<crate::lib_ctx::LibCtx>>,
1570    ) -> Result<Self, ErrorStack> {
1571        let lctx = libctx.map_or(std::ptr::null_mut(), |c| c.as_ptr());
1572        let ptr = unsafe { sys::EVP_PKEY_CTX_new_from_pkey(lctx, key.ptr, std::ptr::null()) };
1573        if ptr.is_null() {
1574            return Err(ErrorStack::drain());
1575        }
1576        crate::ossl_call!(sys::EVP_PKEY_sign_init(ptr)).map_err(|e| {
1577            unsafe { sys::EVP_PKEY_CTX_free(ptr) };
1578            e
1579        })?;
1580        Ok(RawSigner { ctx: ptr })
1581    }
1582
1583    /// Apply parameters after init (e.g. RSA padding mode, salt length).
1584    ///
1585    /// # Errors
1586    pub fn set_params(&mut self, params: &crate::params::Params<'_>) -> Result<(), ErrorStack> {
1587        // SAFETY:
1588        // - self.ctx is non-null (constructor invariant)
1589        // - params.as_ptr() is valid for the duration of this call
1590        // - &mut self ensures exclusive access; no concurrent reads or writes
1591        crate::ossl_call!(sys::EVP_PKEY_CTX_set_params(self.ctx, params.as_ptr()))
1592    }
1593
1594    /// Retrieve parameter values from this sign context.
1595    ///
1596    /// Build a [`Params`][crate::params::Params] with placeholder values for the
1597    /// keys you want, call this method, then read back with `params.get_*`.
1598    ///
1599    /// Useful for reading operation-context parameters such as the current RSA
1600    /// padding mode or signature algorithm identifier after initialisation.
1601    ///
1602    /// # Errors
1603    ///
1604    /// Returns `Err` if `EVP_PKEY_CTX_get_params` fails.
1605    pub fn get_params(&self, params: &mut crate::params::Params<'_>) -> Result<(), ErrorStack> {
1606        // SAFETY:
1607        // - self.ctx is non-null (constructor invariant)
1608        // - params.as_mut_ptr() is valid for the duration of this call
1609        // - &self ensures no concurrent mutable access to self.ctx
1610        crate::ossl_call!(sys::EVP_PKEY_CTX_get_params(self.ctx, params.as_mut_ptr()))
1611    }
1612
1613    /// Return the security strength of the key operation in bits.
1614    ///
1615    /// Reads `OSSL_PKEY_PARAM_SECURITY_BITS` (`"security-bits"`) from the sign
1616    /// context.  Available after `EVP_PKEY_sign_init` on contexts backed by a key.
1617    ///
1618    /// # Errors
1619    ///
1620    /// Returns `Err` if the context does not support this parameter.
1621    pub fn security_bits(&self) -> Result<u32, ErrorStack> {
1622        let mut params = crate::params::ParamBuilder::new()?
1623            .push_uint(c"security-bits", 0)?
1624            .build()?;
1625        // SAFETY: same as get_params — self.ctx is non-null, params pointer is valid.
1626        crate::ossl_call!(sys::EVP_PKEY_CTX_get_params(self.ctx, params.as_mut_ptr()))?;
1627        params
1628            .get_uint(c"security-bits")
1629            .map_err(|_| crate::error::ErrorStack::drain())
1630    }
1631
1632    /// Query the signature output size for the given input length.
1633    ///
1634    /// Calls `EVP_PKEY_sign` with a null output pointer — does not consume
1635    /// the signing state.
1636    ///
1637    /// # Errors
1638    pub fn sign_len(&mut self, tbs_len: usize) -> Result<usize, ErrorStack> {
1639        let mut siglen: usize = 0;
1640        crate::ossl_call!(sys::EVP_PKEY_sign(
1641            self.ctx,
1642            std::ptr::null_mut(),
1643            std::ptr::addr_of_mut!(siglen),
1644            std::ptr::null(),
1645            tbs_len,
1646        ))?;
1647        Ok(siglen)
1648    }
1649
1650    /// Sign pre-hashed data into `sig`.  Returns the number of bytes written.
1651    ///
1652    /// `sig.len()` must be >= `sign_len(tbs.len())`.
1653    ///
1654    /// # Errors
1655    pub fn sign(&mut self, tbs: &[u8], sig: &mut [u8]) -> Result<usize, ErrorStack> {
1656        let mut siglen = sig.len();
1657        crate::ossl_call!(sys::EVP_PKEY_sign(
1658            self.ctx,
1659            sig.as_mut_ptr(),
1660            std::ptr::addr_of_mut!(siglen),
1661            tbs.as_ptr(),
1662            tbs.len(),
1663        ))?;
1664        Ok(siglen)
1665    }
1666
1667    /// Sign pre-hashed data, allocating the output buffer.
1668    ///
1669    /// Convenience wrapper around [`sign_len`](Self::sign_len) + [`sign`](Self::sign).
1670    ///
1671    /// # Errors
1672    pub fn sign_alloc(&mut self, tbs: &[u8]) -> Result<Vec<u8>, ErrorStack> {
1673        let siglen = self.sign_len(tbs.len())?;
1674        let mut sig = vec![0u8; siglen];
1675        let written = self.sign(tbs, &mut sig)?;
1676        sig.truncate(written);
1677        Ok(sig)
1678    }
1679}
1680
1681impl Drop for RawSigner {
1682    fn drop(&mut self) {
1683        unsafe { sys::EVP_PKEY_CTX_free(self.ctx) };
1684    }
1685}
1686
1687// ── RawVerifier — pre-hashed / no-digest verification ────────────────────────
1688
1689/// Raw (no-digest) verification context wrapping `EVP_PKEY_CTX` after
1690/// `EVP_PKEY_verify_init`.
1691///
1692/// Mirror of [`RawSigner`] for the verification side.
1693pub struct RawVerifier {
1694    ctx: *mut sys::EVP_PKEY_CTX,
1695}
1696
1697unsafe impl Send for RawVerifier {}
1698
1699impl RawVerifier {
1700    /// Create and initialise a verify context.
1701    ///
1702    /// # Errors
1703    pub fn new<T: HasPublic>(
1704        key: &Pkey<T>,
1705        libctx: Option<&Arc<crate::lib_ctx::LibCtx>>,
1706    ) -> Result<Self, ErrorStack> {
1707        let lctx = libctx.map_or(std::ptr::null_mut(), |c| c.as_ptr());
1708        let ptr = unsafe { sys::EVP_PKEY_CTX_new_from_pkey(lctx, key.ptr, std::ptr::null()) };
1709        if ptr.is_null() {
1710            return Err(ErrorStack::drain());
1711        }
1712        crate::ossl_call!(sys::EVP_PKEY_verify_init(ptr)).map_err(|e| {
1713            unsafe { sys::EVP_PKEY_CTX_free(ptr) };
1714            e
1715        })?;
1716        Ok(RawVerifier { ctx: ptr })
1717    }
1718
1719    /// Apply parameters after init (e.g. RSA padding mode).
1720    ///
1721    /// # Errors
1722    pub fn set_params(&mut self, params: &crate::params::Params<'_>) -> Result<(), ErrorStack> {
1723        crate::ossl_call!(sys::EVP_PKEY_CTX_set_params(self.ctx, params.as_ptr()))
1724    }
1725
1726    /// Verify `sig` against pre-hashed `tbs`.  Returns `Ok(())` if valid.
1727    ///
1728    /// # Errors
1729    ///
1730    /// Returns `Err` if the signature is invalid or on any OpenSSL error.
1731    pub fn verify(&mut self, tbs: &[u8], sig: &[u8]) -> Result<(), ErrorStack> {
1732        crate::ossl_call!(sys::EVP_PKEY_verify(
1733            self.ctx,
1734            sig.as_ptr(),
1735            sig.len(),
1736            tbs.as_ptr(),
1737            tbs.len(),
1738        ))
1739    }
1740}
1741
1742impl Drop for RawVerifier {
1743    fn drop(&mut self) {
1744        unsafe { sys::EVP_PKEY_CTX_free(self.ctx) };
1745    }
1746}
1747
1748// ── SigAlg — EVP_SIGNATURE algorithm descriptor (OpenSSL 3.2+) ──────────────
1749
1750/// Algorithm descriptor for `EVP_SIGNATURE` (OpenSSL 3.2+).
1751///
1752/// Mirrors `DigestAlg` / `CipherAlg` / `MacAlg` in naming and lifecycle.
1753/// Used with [`MessageSigner`] and [`MessageVerifier`] for algorithms such
1754/// as ML-DSA, SLH-DSA, and Ed25519/Ed448 with context strings.
1755#[cfg(ossl320)]
1756pub struct SigAlg {
1757    ptr: *mut sys::EVP_SIGNATURE,
1758}
1759
1760// SAFETY: EVP_SIGNATURE is reference-counted.
1761#[cfg(ossl320)]
1762unsafe impl Send for SigAlg {}
1763#[cfg(ossl320)]
1764unsafe impl Sync for SigAlg {}
1765
1766#[cfg(ossl320)]
1767impl SigAlg {
1768    /// Fetch an `EVP_SIGNATURE` by name from the default library context.
1769    ///
1770    /// Example names: `c"ML-DSA-44"`, `c"ED25519"`, `c"SLH-DSA-SHA2-128s"`.
1771    ///
1772    /// # Errors
1773    pub fn fetch(
1774        name: &std::ffi::CStr,
1775        props: Option<&std::ffi::CStr>,
1776    ) -> Result<Self, ErrorStack> {
1777        let props_ptr = props.map_or(std::ptr::null(), std::ffi::CStr::as_ptr);
1778        let ptr =
1779            unsafe { sys::EVP_SIGNATURE_fetch(std::ptr::null_mut(), name.as_ptr(), props_ptr) };
1780        if ptr.is_null() {
1781            return Err(ErrorStack::drain());
1782        }
1783        Ok(SigAlg { ptr })
1784    }
1785
1786    /// Fetch an `EVP_SIGNATURE` by name within a specific library context.
1787    ///
1788    /// # Errors
1789    pub fn fetch_in(
1790        ctx: &Arc<crate::lib_ctx::LibCtx>,
1791        name: &std::ffi::CStr,
1792        props: Option<&std::ffi::CStr>,
1793    ) -> Result<Self, ErrorStack> {
1794        let props_ptr = props.map_or(std::ptr::null(), std::ffi::CStr::as_ptr);
1795        let ptr = unsafe { sys::EVP_SIGNATURE_fetch(ctx.as_ptr(), name.as_ptr(), props_ptr) };
1796        if ptr.is_null() {
1797            return Err(ErrorStack::drain());
1798        }
1799        Ok(SigAlg { ptr })
1800    }
1801}
1802
1803#[cfg(ossl320)]
1804impl Clone for SigAlg {
1805    fn clone(&self) -> Self {
1806        unsafe { sys::EVP_SIGNATURE_up_ref(self.ptr) };
1807        SigAlg { ptr: self.ptr }
1808    }
1809}
1810
1811#[cfg(ossl320)]
1812impl Drop for SigAlg {
1813    fn drop(&mut self) {
1814        unsafe { sys::EVP_SIGNATURE_free(self.ptr) };
1815    }
1816}
1817
1818// ── MessageSigner — EVP_PKEY_sign_message_* streaming sign (OpenSSL 3.2+) ────
1819
1820/// Stateful signing context using `EVP_PKEY_sign_message_*` (OpenSSL 3.2+).
1821///
1822/// Used for algorithms that do not use a separate internal digest (`ML-DSA`,
1823/// `SLH-DSA`, `Ed25519` with context strings).  Unlike [`Signer`], the
1824/// algorithm is specified as a [`SigAlg`] rather than a digest name.
1825///
1826/// Call [`update`](Self::update) zero or more times (if the algorithm supports
1827/// streaming — check with [`supports_streaming`](Self::supports_streaming)),
1828/// then [`finish`](Self::finish) to produce the signature.  For algorithms
1829/// that only support one-shot operation, use [`sign_oneshot`](Self::sign_oneshot).
1830#[cfg(ossl320)]
1831pub struct MessageSigner {
1832    ctx: *mut sys::EVP_PKEY_CTX,
1833}
1834
1835#[cfg(ossl320)]
1836unsafe impl Send for MessageSigner {}
1837
1838#[cfg(ossl320)]
1839impl MessageSigner {
1840    /// Create and initialise a message-sign context.
1841    ///
1842    /// `alg` is consumed by the init call; pass a clone if you need to reuse it.
1843    /// `params` sets algorithm-specific options (e.g. context string for Ed25519).
1844    ///
1845    /// # Errors
1846    pub fn new(
1847        key: &Pkey<Private>,
1848        alg: &mut SigAlg,
1849        params: Option<&crate::params::Params<'_>>,
1850    ) -> Result<Self, ErrorStack> {
1851        let ptr = unsafe {
1852            sys::EVP_PKEY_CTX_new_from_pkey(std::ptr::null_mut(), key.ptr, std::ptr::null())
1853        };
1854        if ptr.is_null() {
1855            return Err(ErrorStack::drain());
1856        }
1857        let params_ptr = params.map_or(crate::params::null_params(), crate::params::Params::as_ptr);
1858        crate::ossl_call!(sys::EVP_PKEY_sign_message_init(ptr, alg.ptr, params_ptr)).map_err(
1859            |e| {
1860                unsafe { sys::EVP_PKEY_CTX_free(ptr) };
1861                e
1862            },
1863        )?;
1864        Ok(MessageSigner { ctx: ptr })
1865    }
1866
1867    /// Probe whether this algorithm backend supports incremental `update` calls.
1868    ///
1869    /// Calls `EVP_PKEY_sign_message_update` with an empty input, bracketed by
1870    /// `ERR_set_mark` / `ERR_pop_to_mark` so that a failure does not leave
1871    /// entries on the error queue.  Returns `true` if streaming is supported.
1872    ///
1873    /// If this returns `false`, use [`sign_oneshot`](Self::sign_oneshot) instead.
1874    pub fn supports_streaming(&mut self) -> bool {
1875        // Probe: feed 0 bytes and see if the algorithm accepts it.
1876        // ERR mark/pop ensures the error queue is clean regardless of outcome.
1877        unsafe { sys::ERR_set_mark() };
1878        let probe: [u8; 0] = [];
1879        let rc = unsafe { sys::EVP_PKEY_sign_message_update(self.ctx, probe.as_ptr(), 0) };
1880        unsafe { sys::ERR_pop_to_mark() };
1881        rc == 1
1882    }
1883
1884    /// Feed `data` into the signing operation.
1885    ///
1886    /// Returns `Err` if the algorithm does not support streaming — use
1887    /// [`sign_oneshot`](Self::sign_oneshot) in that case.
1888    ///
1889    /// # Errors
1890    pub fn update(&mut self, data: &[u8]) -> Result<(), ErrorStack> {
1891        crate::ossl_call!(sys::EVP_PKEY_sign_message_update(
1892            self.ctx,
1893            data.as_ptr(),
1894            data.len(),
1895        ))
1896    }
1897
1898    /// Query the signature output length.
1899    ///
1900    /// Calls `EVP_PKEY_sign_message_final` with a null buffer — does not
1901    /// consume the signing state.
1902    ///
1903    /// # Errors
1904    pub fn sig_len(&mut self) -> Result<usize, ErrorStack> {
1905        let mut siglen: usize = 0;
1906        crate::ossl_call!(sys::EVP_PKEY_sign_message_final(
1907            self.ctx,
1908            std::ptr::null_mut(),
1909            std::ptr::addr_of_mut!(siglen),
1910        ))?;
1911        Ok(siglen)
1912    }
1913
1914    /// Finalise and produce the signature into `sig`.
1915    ///
1916    /// Consumes `self` because the context is finalised.  Call
1917    /// [`sig_len`](Self::sig_len) first to size the buffer.  Returns the
1918    /// number of bytes written.
1919    ///
1920    /// # Errors
1921    pub fn finish(self, sig: &mut [u8]) -> Result<usize, ErrorStack> {
1922        let mut siglen = sig.len();
1923        let rc = unsafe {
1924            sys::EVP_PKEY_sign_message_final(
1925                self.ctx,
1926                sig.as_mut_ptr(),
1927                std::ptr::addr_of_mut!(siglen),
1928            )
1929        };
1930        // self is dropped here → EVP_PKEY_CTX_free via Drop.
1931        if rc != 1 {
1932            return Err(ErrorStack::drain());
1933        }
1934        Ok(siglen)
1935    }
1936
1937    /// One-shot sign: feed `data` then finalise into `sig`.
1938    ///
1939    /// Consumes `self`.  Use this for algorithms that do not support
1940    /// streaming (`supports_streaming` returns `false`).
1941    ///
1942    /// # Errors
1943    pub fn sign_oneshot(self, data: &[u8], sig: &mut [u8]) -> Result<usize, ErrorStack> {
1944        // Feed all data, then finalise.  Both ops share the same ctx.
1945        let rc_upd =
1946            unsafe { sys::EVP_PKEY_sign_message_update(self.ctx, data.as_ptr(), data.len()) };
1947        if rc_upd != 1 {
1948            // self dropped here → ctx freed.
1949            return Err(ErrorStack::drain());
1950        }
1951        let mut siglen = sig.len();
1952        let rc_fin = unsafe {
1953            sys::EVP_PKEY_sign_message_final(
1954                self.ctx,
1955                sig.as_mut_ptr(),
1956                std::ptr::addr_of_mut!(siglen),
1957            )
1958        };
1959        // self dropped here → ctx freed.
1960        if rc_fin != 1 {
1961            return Err(ErrorStack::drain());
1962        }
1963        Ok(siglen)
1964    }
1965
1966    /// One-shot sign over `data` using `EVP_PKEY_sign`.
1967    ///
1968    /// The context must have been initialised with `EVP_PKEY_sign_message_init`
1969    /// (this type's constructor); `EVP_PKEY_sign` accepts both
1970    /// `EVP_PKEY_OP_SIGN` and `EVP_PKEY_OP_SIGNMSG` operation modes.
1971    ///
1972    /// When `sig` is `None` the call is a **cheap length query**: for ML-DSA
1973    /// and other algorithms with a fixed output size, no cryptographic
1974    /// computation is performed.  When `sig` is `Some(buf)` the signature is
1975    /// written and the number of bytes actually written is returned.
1976    ///
1977    /// The context is *not* consumed so the same `MessageSigner` may be reused
1978    /// across a size-query + actual-sign pair without re-initialisation.
1979    ///
1980    /// Contrast with [`sign_oneshot`](Self::sign_oneshot): `sign_oneshot`
1981    /// consumes `self` and always writes a signature; `sign` borrows `self`,
1982    /// can query the required length cheaply (pass `sig = None`), and can be
1983    /// called multiple times on the same context.
1984    ///
1985    /// # Errors
1986    pub fn sign(&mut self, data: &[u8], sig: Option<&mut [u8]>) -> Result<usize, ErrorStack> {
1987        let (sig_ptr, mut siglen) = match sig {
1988            Some(buf) => (buf.as_mut_ptr(), buf.len()),
1989            None => (std::ptr::null_mut(), 0usize),
1990        };
1991        crate::ossl_call!(sys::EVP_PKEY_sign(
1992            self.ctx,
1993            sig_ptr,
1994            std::ptr::addr_of_mut!(siglen),
1995            data.as_ptr(),
1996            data.len(),
1997        ))?;
1998        Ok(siglen)
1999    }
2000}
2001
2002#[cfg(ossl320)]
2003impl Drop for MessageSigner {
2004    fn drop(&mut self) {
2005        unsafe { sys::EVP_PKEY_CTX_free(self.ctx) };
2006    }
2007}
2008
2009// ── MessageVerifier — EVP_PKEY_verify_message_* streaming verify (OpenSSL 3.2+)
2010
2011/// Stateful verification context using `EVP_PKEY_verify_message_*` (OpenSSL 3.2+).
2012///
2013/// Mirror of [`MessageSigner`] for the verification side.
2014///
2015/// For streaming mode: call [`set_signature`](Self::set_signature), then
2016/// [`update`](Self::update) zero or more times, then [`finish`](Self::finish).
2017/// For one-shot: call [`verify_oneshot`](Self::verify_oneshot).
2018#[cfg(ossl320)]
2019pub struct MessageVerifier {
2020    ctx: *mut sys::EVP_PKEY_CTX,
2021}
2022
2023#[cfg(ossl320)]
2024unsafe impl Send for MessageVerifier {}
2025
2026#[cfg(ossl320)]
2027impl MessageVerifier {
2028    /// Create and initialise a message-verify context.
2029    ///
2030    /// # Errors
2031    pub fn new<T: HasPublic>(
2032        key: &Pkey<T>,
2033        alg: &mut SigAlg,
2034        params: Option<&crate::params::Params<'_>>,
2035    ) -> Result<Self, ErrorStack> {
2036        let ptr = unsafe {
2037            sys::EVP_PKEY_CTX_new_from_pkey(std::ptr::null_mut(), key.ptr, std::ptr::null())
2038        };
2039        if ptr.is_null() {
2040            return Err(ErrorStack::drain());
2041        }
2042        let params_ptr = params.map_or(crate::params::null_params(), crate::params::Params::as_ptr);
2043        crate::ossl_call!(sys::EVP_PKEY_verify_message_init(ptr, alg.ptr, params_ptr)).map_err(
2044            |e| {
2045                unsafe { sys::EVP_PKEY_CTX_free(ptr) };
2046                e
2047            },
2048        )?;
2049        Ok(MessageVerifier { ctx: ptr })
2050    }
2051
2052    /// Apply parameters after init.
2053    ///
2054    /// # Errors
2055    pub fn set_params(&mut self, params: &crate::params::Params<'_>) -> Result<(), ErrorStack> {
2056        crate::ossl_call!(sys::EVP_PKEY_CTX_set_params(self.ctx, params.as_ptr()))
2057    }
2058
2059    /// Supply the signature to verify against (required before streaming `finish`).
2060    ///
2061    /// Calls `EVP_PKEY_CTX_set_signature`.  Not needed for
2062    /// [`verify_oneshot`](Self::verify_oneshot) which sets it internally.
2063    ///
2064    /// # Errors
2065    pub fn set_signature(&mut self, sig: &[u8]) -> Result<(), ErrorStack> {
2066        crate::ossl_call!(sys::EVP_PKEY_CTX_set_signature(
2067            self.ctx,
2068            sig.as_ptr(),
2069            sig.len()
2070        ))
2071    }
2072
2073    /// Probe whether this algorithm supports incremental `update` calls.
2074    ///
2075    /// Uses the same `ERR_set_mark` / `ERR_pop_to_mark` probe as
2076    /// [`MessageSigner::supports_streaming`].
2077    pub fn supports_streaming(&mut self) -> bool {
2078        unsafe { sys::ERR_set_mark() };
2079        let probe: [u8; 0] = [];
2080        let rc = unsafe { sys::EVP_PKEY_verify_message_update(self.ctx, probe.as_ptr(), 0) };
2081        unsafe { sys::ERR_pop_to_mark() };
2082        rc == 1
2083    }
2084
2085    /// Feed `data` into the verification operation.
2086    ///
2087    /// # Errors
2088    pub fn update(&mut self, data: &[u8]) -> Result<(), ErrorStack> {
2089        crate::ossl_call!(sys::EVP_PKEY_verify_message_update(
2090            self.ctx,
2091            data.as_ptr(),
2092            data.len(),
2093        ))
2094    }
2095
2096    /// Finalise and verify.
2097    ///
2098    /// The signature must have been set via [`set_signature`](Self::set_signature).
2099    /// Consumes `self`.  Returns `Ok(())` if the signature is valid.
2100    ///
2101    /// # Errors
2102    pub fn finish(self) -> Result<(), ErrorStack> {
2103        let rc = unsafe { sys::EVP_PKEY_verify_message_final(self.ctx) };
2104        // self dropped here → ctx freed.
2105        if rc != 1 {
2106            return Err(ErrorStack::drain());
2107        }
2108        Ok(())
2109    }
2110
2111    /// One-shot verify `sig` over `data`.
2112    ///
2113    /// Sets the signature, feeds all data, and finalises.  Consumes `self`.
2114    ///
2115    /// # Errors
2116    pub fn verify_oneshot(self, data: &[u8], sig: &[u8]) -> Result<(), ErrorStack> {
2117        let rc_set = unsafe { sys::EVP_PKEY_CTX_set_signature(self.ctx, sig.as_ptr(), sig.len()) };
2118        if rc_set != 1 {
2119            return Err(ErrorStack::drain());
2120        }
2121        let rc_upd =
2122            unsafe { sys::EVP_PKEY_verify_message_update(self.ctx, data.as_ptr(), data.len()) };
2123        if rc_upd != 1 {
2124            return Err(ErrorStack::drain());
2125        }
2126        let rc_fin = unsafe { sys::EVP_PKEY_verify_message_final(self.ctx) };
2127        // self dropped here → ctx freed.
2128        if rc_fin != 1 {
2129            return Err(ErrorStack::drain());
2130        }
2131        Ok(())
2132    }
2133
2134    /// One-shot verify `sig` over `data` using `EVP_PKEY_verify`.
2135    ///
2136    /// The context must have been initialised with `EVP_PKEY_verify_message_init`
2137    /// (this type's constructor); `EVP_PKEY_verify` accepts both
2138    /// `EVP_PKEY_OP_VERIFY` and `EVP_PKEY_OP_VERIFYMSG` operation modes.
2139    ///
2140    /// Returns `Ok(true)` if the signature verifies, `Ok(false)` if it does
2141    /// not.  Fatal protocol or library errors are returned as `Err`.
2142    ///
2143    /// The context is *not* consumed and may be reused for further verifications.
2144    ///
2145    /// Contrast with [`verify_oneshot`](Self::verify_oneshot): `verify_oneshot`
2146    /// consumes `self`; `verify` borrows `self` and may be called repeatedly
2147    /// without re-creating the context.
2148    ///
2149    /// # Errors
2150    pub fn verify(&mut self, data: &[u8], sig: &[u8]) -> Result<bool, ErrorStack> {
2151        let rc = unsafe {
2152            sys::EVP_PKEY_verify(self.ctx, sig.as_ptr(), sig.len(), data.as_ptr(), data.len())
2153        };
2154        match rc {
2155            1 => Ok(true),
2156            0 => Ok(false),
2157            _ => Err(ErrorStack::drain()),
2158        }
2159    }
2160}
2161
2162#[cfg(ossl320)]
2163impl Drop for MessageVerifier {
2164    fn drop(&mut self) {
2165        unsafe { sys::EVP_PKEY_CTX_free(self.ctx) };
2166    }
2167}
2168
2169// ── Tests ─────────────────────────────────────────────────────────────────────
2170
2171#[cfg(test)]
2172mod tests {
2173    use super::*;
2174
2175    /// Generate an Ed25519 key pair; sign and verify "hello world".
2176    ///
2177    /// Ed25519 is a one-shot algorithm — uses `sign_oneshot` / `verify_oneshot`.
2178    #[test]
2179    fn ed25519_sign_verify() {
2180        let mut kgen = KeygenCtx::new(c"ED25519").unwrap();
2181        let priv_key = kgen.generate().unwrap();
2182        let pub_key = Pkey::<Public>::from(priv_key.clone());
2183
2184        let msg = b"hello world";
2185        let init = SignInit::default();
2186
2187        let mut signer = Signer::new(&priv_key, &init).unwrap();
2188        let sig = signer.sign_oneshot(msg).unwrap();
2189        assert!(!sig.is_empty());
2190
2191        let mut verifier = Verifier::new(&pub_key, &init).unwrap();
2192        assert!(verifier.verify_oneshot(msg, &sig).unwrap());
2193    }
2194
2195    /// Tampered message must fail verification.
2196    #[test]
2197    fn ed25519_verify_wrong_msg_fails() {
2198        let mut kgen = KeygenCtx::new(c"ED25519").unwrap();
2199        let priv_key = kgen.generate().unwrap();
2200        let pub_key = Pkey::<Public>::from(priv_key.clone());
2201
2202        let mut signer = Signer::new(&priv_key, &SignInit::default()).unwrap();
2203        let sig = signer.sign_oneshot(b"correct").unwrap();
2204
2205        let mut verifier = Verifier::new(&pub_key, &SignInit::default()).unwrap();
2206        assert!(!verifier.verify_oneshot(b"tampered", &sig).unwrap());
2207    }
2208
2209    /// Generate an X25519 key pair; perform ECDH derive and confirm length.
2210    #[test]
2211    fn x25519_derive() {
2212        let mut kgen_a = KeygenCtx::new(c"X25519").unwrap();
2213        let priv_a = kgen_a.generate().unwrap();
2214
2215        let mut kgen_b = KeygenCtx::new(c"X25519").unwrap();
2216        let priv_b = kgen_b.generate().unwrap();
2217        let pub_b = Pkey::<Public>::from(priv_b);
2218
2219        let mut derive = DeriveCtx::new(&priv_a).unwrap();
2220        derive.set_peer(&pub_b).unwrap();
2221        let len = derive.derive_len().unwrap();
2222        assert_eq!(len, 32); // X25519 shared secret is always 32 bytes
2223
2224        let mut ss = vec![0u8; len];
2225        let n = derive.derive(&mut ss).unwrap();
2226        assert_eq!(n, 32);
2227        assert_ne!(ss, [0u8; 32]);
2228    }
2229
2230    /// Round-trip through PEM: generate Ed25519, export, re-import, verify key equality.
2231    #[test]
2232    fn pem_round_trip() {
2233        let mut kgen = KeygenCtx::new(c"ED25519").unwrap();
2234        let priv_key = kgen.generate().unwrap();
2235
2236        let pem = priv_key.to_pem().unwrap();
2237        assert!(!pem.is_empty());
2238        assert!(pem.starts_with(b"-----BEGIN"));
2239
2240        let priv_key2 = Pkey::<Private>::from_pem(&pem).unwrap();
2241        assert_eq!(priv_key.bits(), priv_key2.bits());
2242        assert!(priv_key.is_a(c"ED25519"));
2243    }
2244
2245    /// Key metadata: Ed25519 should report 256 bits.
2246    #[test]
2247    fn ed25519_metadata() {
2248        let mut kgen = KeygenCtx::new(c"ED25519").unwrap();
2249        let key = kgen.generate().unwrap();
2250        assert_eq!(key.bits(), 256);
2251        assert_eq!(key.security_bits(), 128);
2252        assert!(key.is_a(c"ED25519"));
2253    }
2254
2255    /// ML-DSA-44 sign and verify using `MessageSigner::sign` / `MessageVerifier::verify`.
2256    #[cfg(ossl320)]
2257    #[test]
2258    fn ml_dsa_44_sign_verify() {
2259        let mut kgen = KeygenCtx::new(c"ML-DSA-44").unwrap();
2260        let priv_key = kgen.generate().unwrap();
2261        let pub_key = Pkey::<Public>::from(priv_key.clone());
2262
2263        let msg = b"hello ML-DSA-44";
2264
2265        let mut alg = SigAlg::fetch(c"ML-DSA-44", None).unwrap();
2266        let mut signer = MessageSigner::new(&priv_key, &mut alg, None).unwrap();
2267        // NULL size query must be cheap (FIPS 204 fixed 2420 bytes).
2268        let sig_len = signer.sign(msg, None).unwrap();
2269        assert_eq!(sig_len, 2420);
2270        let mut sig = vec![0u8; sig_len];
2271        let written = signer.sign(msg, Some(&mut sig)).unwrap();
2272        assert_eq!(written, 2420);
2273
2274        let mut alg2 = SigAlg::fetch(c"ML-DSA-44", None).unwrap();
2275        let mut ver = MessageVerifier::new(&pub_key, &mut alg2, None).unwrap();
2276        assert!(ver.verify(msg, &sig).unwrap());
2277
2278        // Tampered signature must fail.
2279        let mut bad = sig.clone();
2280        bad[0] ^= 0xff;
2281        assert!(!ver.verify(msg, &bad).unwrap());
2282    }
2283
2284    /// ML-DSA-44 sign with a context string (FIPS 204 domain separation).
2285    #[cfg(ossl320)]
2286    #[test]
2287    fn ml_dsa_44_sign_with_context() {
2288        let mut kgen = KeygenCtx::new(c"ML-DSA-44").unwrap();
2289        let priv_key = kgen.generate().unwrap();
2290        let pub_key = Pkey::<Public>::from(priv_key.clone());
2291
2292        let msg = b"signed with context";
2293        let ctx_bytes = b"my-app-domain";
2294
2295        let ctx_params = crate::params::ParamBuilder::new()
2296            .unwrap()
2297            .push_octet_slice(c"context-string", ctx_bytes)
2298            .unwrap()
2299            .build()
2300            .unwrap();
2301
2302        let mut alg = SigAlg::fetch(c"ML-DSA-44", None).unwrap();
2303        let mut signer = MessageSigner::new(&priv_key, &mut alg, Some(&ctx_params)).unwrap();
2304        let sig_len = signer.sign(msg, None).unwrap();
2305        let mut sig = vec![0u8; sig_len];
2306        signer.sign(msg, Some(&mut sig)).unwrap();
2307
2308        // Same context: verify must succeed.
2309        let mut alg2 = SigAlg::fetch(c"ML-DSA-44", None).unwrap();
2310        let mut ver = MessageVerifier::new(&pub_key, &mut alg2, Some(&ctx_params)).unwrap();
2311        assert!(ver.verify(msg, &sig).unwrap());
2312
2313        // Different context: verify must fail.
2314        let other_params = crate::params::ParamBuilder::new()
2315            .unwrap()
2316            .push_octet_slice(c"context-string", b"other-domain")
2317            .unwrap()
2318            .build()
2319            .unwrap();
2320        let mut alg3 = SigAlg::fetch(c"ML-DSA-44", None).unwrap();
2321        let mut ver_bad = MessageVerifier::new(&pub_key, &mut alg3, Some(&other_params)).unwrap();
2322        assert!(!ver_bad.verify(msg, &sig).unwrap());
2323    }
2324
2325    /// `RawSigner` / `RawVerifier` round-trip with P-256 ECDSA over a pre-hashed digest.
2326    #[test]
2327    fn ecdsa_p256_raw_sign_verify() {
2328        // Generate P-256 key.
2329        let mut kgen = KeygenCtx::new(c"EC").unwrap();
2330        let params = crate::params::ParamBuilder::new()
2331            .unwrap()
2332            .push_utf8_string(c"group", c"P-256")
2333            .unwrap()
2334            .build()
2335            .unwrap();
2336        kgen.set_params(&params).unwrap();
2337        let priv_key = kgen.generate().unwrap();
2338        let pub_key = Pkey::<Public>::from(priv_key.clone());
2339
2340        // SHA-256 hash of the message (32 bytes — the "pre-hash").
2341        let tbs: [u8; 32] = *b"0123456789abcdef0123456789abcdef";
2342
2343        let mut signer = RawSigner::new(&priv_key, None).unwrap();
2344        let sig = signer.sign_alloc(&tbs).unwrap();
2345        assert!(!sig.is_empty());
2346
2347        let mut verifier = RawVerifier::new(&pub_key, None).unwrap();
2348        verifier.verify(&tbs, &sig).unwrap();
2349    }
2350
2351    /// `RawVerifier` rejects a tampered signature.
2352    #[test]
2353    fn ecdsa_p256_raw_verify_tampered_fails() {
2354        let mut kgen = KeygenCtx::new(c"EC").unwrap();
2355        let params = crate::params::ParamBuilder::new()
2356            .unwrap()
2357            .push_utf8_string(c"group", c"P-256")
2358            .unwrap()
2359            .build()
2360            .unwrap();
2361        kgen.set_params(&params).unwrap();
2362        let priv_key = kgen.generate().unwrap();
2363        let pub_key = Pkey::<Public>::from(priv_key.clone());
2364
2365        let tbs: [u8; 32] = *b"0123456789abcdef0123456789abcdef";
2366        let mut signer = RawSigner::new(&priv_key, None).unwrap();
2367        let mut sig = signer.sign_alloc(&tbs).unwrap();
2368        // Flip a byte in the signature.
2369        if let Some(b) = sig.last_mut() {
2370            *b ^= 0xff;
2371        }
2372
2373        let mut verifier = RawVerifier::new(&pub_key, None).unwrap();
2374        assert!(verifier.verify(&tbs, &sig).is_err());
2375    }
2376
2377    /// `SigAlg`: fetch, clone, drop — verifies `EVP_SIGNATURE` lifecycle.
2378    ///
2379    /// Does not attempt `sign_message_final`: OpenSSL 3.5's built-in ML-DSA
2380    /// provider implements only the one-shot `EVP_DigestSign` path, not
2381    /// `OSSL_FUNC_SIGNATURE_SIGN_MESSAGE_FINAL`.  Actual ML-DSA signing is
2382    /// done via Signer (`DigestSign` with NULL digest).
2383    #[cfg(ossl320)]
2384    #[test]
2385    fn sig_alg_fetch_clone_drop() {
2386        let alg = SigAlg::fetch(c"ML-DSA-44", None).unwrap();
2387        let alg2 = alg.clone();
2388        drop(alg);
2389        drop(alg2); // both up_ref / free paths exercised
2390    }
2391
2392    /// `MessageSigner` construction and `supports_streaming` probe for Ed25519.
2393    ///
2394    /// Ed25519 supports `EVP_PKEY_sign_message_init`.  The streaming probe
2395    /// uses `ERR_set_mark` / `ERR_pop_to_mark` so it cannot leave error state.
2396    #[cfg(ossl320)]
2397    #[test]
2398    fn message_signer_construction_and_streaming_probe() {
2399        let mut kgen = KeygenCtx::new(c"ED25519").unwrap();
2400        let priv_key = kgen.generate().unwrap();
2401
2402        let mut alg = SigAlg::fetch(c"ED25519", None).unwrap();
2403        let mut signer = MessageSigner::new(&priv_key, &mut alg, None).unwrap();
2404
2405        // Probe must not crash or leave the error queue dirty.
2406        let _streaming = signer.supports_streaming();
2407        // Error queue must be empty after the probe.
2408        assert_eq!(crate::error::ErrorStack::drain().errors().count(), 0);
2409    }
2410
2411    /// `MessageVerifier` construction for Ed25519.
2412    #[cfg(ossl320)]
2413    #[test]
2414    fn message_verifier_construction() {
2415        let mut kgen = KeygenCtx::new(c"ED25519").unwrap();
2416        let priv_key = kgen.generate().unwrap();
2417        let pub_key = Pkey::<Public>::from(priv_key.clone());
2418
2419        let mut alg = SigAlg::fetch(c"ED25519", None).unwrap();
2420        let _verifier = MessageVerifier::new(&pub_key, &mut alg, None).unwrap();
2421    }
2422
2423    /// Encrypted PEM round-trip: generate → `to_pem_encrypted` → `from_pem_passphrase`.
2424    #[test]
2425    fn encrypted_pem_round_trip() {
2426        let mut kgen = KeygenCtx::new(c"ED25519").unwrap();
2427        let key = kgen.generate().unwrap();
2428
2429        let cipher = crate::cipher::CipherAlg::fetch(c"AES-256-CBC", None).unwrap();
2430        let pem = key.to_pem_encrypted(&cipher, b"s3cret").unwrap();
2431        assert!(pem.starts_with(b"-----BEGIN ENCRYPTED PRIVATE KEY-----"));
2432
2433        let key2 = Pkey::<Private>::from_pem_passphrase(&pem, b"s3cret").unwrap();
2434        assert!(key.public_eq(&key2));
2435    }
2436
2437    /// Wrong passphrase must return an error, not silently load a garbage key.
2438    #[test]
2439    fn encrypted_pem_wrong_passphrase_fails() {
2440        let mut kgen = KeygenCtx::new(c"ED25519").unwrap();
2441        let key = kgen.generate().unwrap();
2442
2443        let cipher = crate::cipher::CipherAlg::fetch(c"AES-256-CBC", None).unwrap();
2444        let pem = key.to_pem_encrypted(&cipher, b"correct").unwrap();
2445        assert!(Pkey::<Private>::from_pem_passphrase(&pem, b"wrong").is_err());
2446    }
2447
2448    /// PKCS#8 DER round-trip: generate → `to_pkcs8_der` → `from_der`.
2449    #[test]
2450    fn pkcs8_der_round_trip() {
2451        let mut kgen = KeygenCtx::new(c"ED25519").unwrap();
2452        let key = kgen.generate().unwrap();
2453
2454        let der = key.to_pkcs8_der().unwrap();
2455        assert!(!der.is_empty());
2456
2457        let key2 = Pkey::<Private>::from_der(&der).unwrap();
2458        assert!(key.public_eq(&key2));
2459    }
2460
2461    #[test]
2462    fn pkey_max_output_size_ed25519() {
2463        let mut kgen = KeygenCtx::new(c"ED25519").unwrap();
2464        let key = kgen.generate().unwrap();
2465        // Ed25519 produces a fixed 64-byte signature.
2466        assert_eq!(key.max_output_size(), 64);
2467    }
2468
2469    #[test]
2470    fn pkey_max_output_size_rsa() {
2471        let mut kgen = KeygenCtx::new(c"RSA").unwrap();
2472        let key = kgen.generate().unwrap();
2473        // RSA-2048: max output == key size in bytes (256).
2474        let size = key.max_output_size();
2475        assert!(size > 0, "RSA key must report non-zero max output size");
2476        assert_eq!(size, key.bits() as usize / 8);
2477    }
2478
2479    /// `KeygenCtx::set_params` — set RSA bit length via params, generate, verify size.
2480    ///
2481    /// OpenSSL default RSA keygen produces 2048-bit keys; here we explicitly set
2482    /// `bits = 3072` and verify the generated key carries that size.
2483    #[test]
2484    fn pkey_ctx_set_params_rsa() {
2485        let mut kgen = KeygenCtx::new(c"RSA").unwrap();
2486        let params = crate::params::ParamBuilder::new()
2487            .unwrap()
2488            .push_uint(c"bits", 3072)
2489            .unwrap()
2490            .build()
2491            .unwrap();
2492        kgen.set_params(&params).unwrap();
2493        let key = kgen.generate().unwrap();
2494        assert_eq!(key.bits(), 3072, "generated RSA key must be 3072 bits");
2495    }
2496
2497    /// `KeygenCtx::security_bits` — Ed25519 key object reports 128 security bits.
2498    ///
2499    /// `security_bits()` reads `security-bits` from the key context.  For Ed25519,
2500    /// the well-known security strength is 128 bits.  This test uses the
2501    /// `Pkey::security_bits` method (which wraps `EVP_PKEY_get_security_bits`)
2502    /// and the `KeygenCtx::security_bits` helper.  The keygen context method
2503    /// internally calls `EVP_PKEY_CTX_get_params`; the result depends on what the
2504    /// underlying provider exposes on a keygen context.
2505    #[test]
2506    fn pkey_ctx_security_bits() {
2507        // Verify that the key object itself reports the correct security level.
2508        let mut kgen = KeygenCtx::new(c"ED25519").unwrap();
2509        let key = kgen.generate().unwrap();
2510        assert_eq!(
2511            key.security_bits(),
2512            128,
2513            "Ed25519 key must report 128 security bits"
2514        );
2515
2516        // KeygenCtx::security_bits() is a best-effort getter; it may return Err if
2517        // the provider does not expose security-bits on a keygen context.  We just
2518        // verify it does not panic.
2519        let _ = KeygenCtx::new(c"ED25519").unwrap().security_bits();
2520    }
2521
2522    /// `RawSigner::get_params` — read `pad-mode` from an RSA sign context.
2523    ///
2524    /// `EVP_PKEY_CTX_get_params` is most useful on operation (sign/verify)
2525    /// contexts where OpenSSL exposes per-operation configuration parameters.
2526    /// RSA sign contexts expose `pad-mode` as both gettable and settable.
2527    ///
2528    /// The placeholder string must be large enough to receive the returned value;
2529    /// we use a 32-char placeholder to accommodate all padding mode names.
2530    #[test]
2531    fn pkey_ctx_get_params_rsa_sign() {
2532        let mut kgen = KeygenCtx::new(c"RSA").unwrap();
2533        let key = kgen.generate().unwrap();
2534        let signer = RawSigner::new(&key, None).unwrap();
2535
2536        // Query the default RSA padding mode; OpenSSL defaults to "pkcs1".
2537        // Provide a placeholder string of length >= the longest padding mode name.
2538        let placeholder = c"00000000000000000000000000000000"; // 32 chars
2539        let mut params = crate::params::ParamBuilder::new()
2540            .unwrap()
2541            .push_utf8_string(c"pad-mode", placeholder)
2542            .unwrap()
2543            .build()
2544            .unwrap();
2545        signer.get_params(&mut params).unwrap();
2546        // The returned pad-mode must be a non-empty string.
2547        let mode = params.get_utf8_string(c"pad-mode").unwrap();
2548        assert!(
2549            !mode.to_bytes().is_empty(),
2550            "RSA pad-mode must be non-empty"
2551        );
2552    }
2553
2554    /// `Pkey<Public>::from_pem_in` loads a public key within an explicit `LibCtx`.
2555    #[test]
2556    fn pubkey_from_pem_in_roundtrip() {
2557        // Generate an EC key and export the public half to PEM.
2558        let mut kgen = KeygenCtx::new(c"ED25519").unwrap();
2559        let priv_key = kgen.generate().unwrap();
2560        let pub_pem = Pkey::<Public>::from(priv_key).to_pem().unwrap();
2561
2562        // Re-import via from_pem_in using the default lib ctx.
2563        let lib_ctx = Arc::new(crate::lib_ctx::LibCtx::new().unwrap());
2564        let pub_key = Pkey::<Public>::from_pem_in(&lib_ctx, &pub_pem).unwrap();
2565
2566        // Sanity: key is usable for verification.
2567        assert!(!pub_key.to_pem().unwrap().is_empty());
2568    }
2569
2570    /// Ed25519 has no separate digest — `default_digest_name()` must return `Ok(None)`.
2571    #[test]
2572    fn pkey_default_digest_name_ed25519() {
2573        let mut kgen = KeygenCtx::new(c"ED25519").unwrap();
2574        let key = kgen.generate().unwrap();
2575        let result = key.default_digest_name().unwrap();
2576        assert_eq!(
2577            result, None,
2578            "Ed25519 performs its own internal hashing; expected no external digest"
2579        );
2580    }
2581
2582    /// RSA with a default digest should return `Ok(Some(name))` with a non-empty name.
2583    #[test]
2584    fn pkey_default_digest_name_rsa() {
2585        let mut kgen = KeygenCtx::new(c"RSA").unwrap();
2586        let key = kgen.generate().unwrap();
2587        let result = key.default_digest_name().unwrap();
2588        assert!(
2589            result.is_some(),
2590            "RSA key should have a default digest name; got None"
2591        );
2592        let name = result.unwrap();
2593        assert!(
2594            !name.is_empty(),
2595            "RSA default digest name must not be empty"
2596        );
2597        // OpenSSL 3.x reports SHA256 (or SHA-256) as the RSA default digest.
2598        assert!(
2599            name.to_ascii_uppercase().contains("SHA"),
2600            "Expected a SHA-family digest name, got: {name}"
2601        );
2602    }
2603
2604    /// `Pkey::<Private>::from_der_in` — round-trip through `to_der` (PKCS#8) and
2605    /// back via the LibCtx-aware `from_der_in`, using an Ed25519 key.
2606    ///
2607    /// Verifies that the reloaded key's public component matches the original.
2608    #[test]
2609    fn pkey_private_from_der_in_ed25519() {
2610        let ctx = Arc::new(crate::lib_ctx::LibCtx::new().unwrap());
2611
2612        let key = KeygenCtx::new(c"ED25519").unwrap().generate().unwrap();
2613
2614        // Serialise to PKCS#8 DER (the format auto-detected by from_der_in).
2615        let der = key.to_pkcs8_der().unwrap();
2616        assert!(!der.is_empty());
2617
2618        // Reload via the LibCtx-aware path.
2619        let key2 = Pkey::<Private>::from_der_in(&ctx, &der).unwrap();
2620
2621        // Public components must be identical.
2622        assert!(
2623            key.public_eq(&key2),
2624            "reloaded private key must equal original"
2625        );
2626    }
2627
2628    /// `Pkey::<Public>::from_der_in` — round-trip through `public_key_to_der`
2629    /// (`SubjectPublicKeyInfo`) and back via the LibCtx-aware `from_der_in`.
2630    #[test]
2631    fn pkey_public_from_der_in_ed25519() {
2632        let ctx = Arc::new(crate::lib_ctx::LibCtx::new().unwrap());
2633
2634        let priv_key = KeygenCtx::new(c"ED25519").unwrap().generate().unwrap();
2635        let pub_key = Pkey::<Public>::from(priv_key.clone());
2636
2637        // Serialise the public key to SubjectPublicKeyInfo DER.
2638        let der = pub_key.public_key_to_der().unwrap();
2639        assert!(!der.is_empty());
2640
2641        // Reload via the LibCtx-aware path.
2642        let pub_key2 = Pkey::<Public>::from_der_in(&ctx, &der).unwrap();
2643
2644        // Must match the original private key's public component.
2645        assert!(
2646            priv_key.public_eq(&pub_key2),
2647            "reloaded public key must equal original"
2648        );
2649    }
2650
2651    /// `to_der_legacy` — generate an RSA key and verify that the legacy DER
2652    /// output is non-empty (RSA has a well-defined `RSAPrivateKey` legacy format).
2653    #[test]
2654    fn pkey_to_der_legacy_produces_non_empty() {
2655        let key = KeygenCtx::new(c"RSA").unwrap().generate().unwrap();
2656
2657        let der = key.to_der_legacy().unwrap();
2658        assert!(!der.is_empty(), "legacy DER for RSA must be non-empty");
2659    }
2660}