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