Skip to main content

native_ossl/
pkey.rs

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