From 81cee4093564e7551096a1b8636d6079f7b189ed Mon Sep 17 00:00:00 2001 From: Huw Jones Date: Mon, 8 Sep 2025 14:03:15 +0100 Subject: [PATCH 01/42] macros: add cstr_const Let's minimise const cstr declaration boiler-plate --- openssl/src/macros.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/openssl/src/macros.rs b/openssl/src/macros.rs index 8b561822d..f67bf8848 100644 --- a/openssl/src/macros.rs +++ b/openssl/src/macros.rs @@ -268,3 +268,14 @@ macro_rules! generic_foreign_type_and_impl_send_sync { unsafe impl Sync for $borrowed{} }; } + +#[cfg_attr(not(ossl300), allow(unused_macros))] +macro_rules! cstr_const { + // Safety: these all have null terminators. + // We cen remove these CStr::from_bytes_with_nul_unchecked calls + // when we upgrade to Rust 1.77+ with literal c"" syntax. + ($vis:vis $name:ident, $key:literal) => { + #[allow(dead_code)] + $vis const $name: &std::ffi::CStr = unsafe { std::ffi::CStr::from_bytes_with_nul_unchecked($key) }; + } +} From c7a8f2ca87aacb761e3a7f10b2205ac459e998f9 Mon Sep 17 00:00:00 2001 From: Huw Jones Date: Mon, 8 Sep 2025 14:04:12 +0100 Subject: [PATCH 02/42] kdf: use cstr_const --- openssl/src/kdf.rs | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/openssl/src/kdf.rs b/openssl/src/kdf.rs index 885ca1a66..68a9460e8 100644 --- a/openssl/src/kdf.rs +++ b/openssl/src/kdf.rs @@ -34,19 +34,16 @@ cfg_if::cfg_if! { use crate::error::ErrorStack; use crate::ossl_param::OsslParamBuilder; - // Safety: these all have null terminators. - // We cen remove these CStr::from_bytes_with_nul_unchecked calls - // when we upgrade to Rust 1.77+ with literal c"" syntax. + cstr_const!(OSSL_KDF_PARAM_PASSWORD, b"pass\0"); + cstr_const!(OSSL_KDF_PARAM_SALT, b"salt\0"); + cstr_const!(OSSL_KDF_PARAM_SECRET, b"secret\0"); + cstr_const!(OSSL_KDF_PARAM_ITER, b"iter\0"); + cstr_const!(OSSL_KDF_PARAM_SIZE, b"size\0"); + cstr_const!(OSSL_KDF_PARAM_THREADS, b"threads\0"); + cstr_const!(OSSL_KDF_PARAM_ARGON2_AD, b"ad\0"); + cstr_const!(OSSL_KDF_PARAM_ARGON2_LANES, b"lanes\0"); + cstr_const!(OSSL_KDF_PARAM_ARGON2_MEMCOST, b"memcost\0"); - const OSSL_KDF_PARAM_PASSWORD: &CStr = unsafe { CStr::from_bytes_with_nul_unchecked(b"pass\0") }; - const OSSL_KDF_PARAM_SALT: &CStr = unsafe { CStr::from_bytes_with_nul_unchecked(b"salt\0") }; - const OSSL_KDF_PARAM_SECRET: &CStr = unsafe { CStr::from_bytes_with_nul_unchecked(b"secret\0") }; - const OSSL_KDF_PARAM_ITER: &CStr = unsafe { CStr::from_bytes_with_nul_unchecked(b"iter\0") }; - const OSSL_KDF_PARAM_SIZE: &CStr = unsafe { CStr::from_bytes_with_nul_unchecked(b"size\0") }; - const OSSL_KDF_PARAM_THREADS: &CStr = unsafe { CStr::from_bytes_with_nul_unchecked(b"threads\0") }; - const OSSL_KDF_PARAM_ARGON2_AD: &CStr = unsafe { CStr::from_bytes_with_nul_unchecked(b"ad\0") }; - const OSSL_KDF_PARAM_ARGON2_LANES: &CStr = unsafe { CStr::from_bytes_with_nul_unchecked(b"lanes\0") }; - const OSSL_KDF_PARAM_ARGON2_MEMCOST: &CStr = unsafe { CStr::from_bytes_with_nul_unchecked(b"memcost\0") }; #[allow(clippy::too_many_arguments)] pub fn argon2d( From adec1b4c50546ec9493e38401786a07e2d24f5ca Mon Sep 17 00:00:00 2001 From: Huw Jones Date: Mon, 8 Sep 2025 14:05:21 +0100 Subject: [PATCH 03/42] kdf/argon2 helpers: make kdf names const No point creating a new CStr every call --- openssl/src/kdf.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/openssl/src/kdf.rs b/openssl/src/kdf.rs index 68a9460e8..d3344d541 100644 --- a/openssl/src/kdf.rs +++ b/openssl/src/kdf.rs @@ -44,6 +44,9 @@ cfg_if::cfg_if! { cstr_const!(OSSL_KDF_PARAM_ARGON2_LANES, b"lanes\0"); cstr_const!(OSSL_KDF_PARAM_ARGON2_MEMCOST, b"memcost\0"); + cstr_const!(KDF_ARGON2D, b"ARGON2D\0"); + cstr_const!(KDF_ARGON2I, b"ARGON2I\0"); + cstr_const!(KDF_ARGON2ID, b"ARGON2ID\0"); #[allow(clippy::too_many_arguments)] pub fn argon2d( @@ -57,7 +60,7 @@ cfg_if::cfg_if! { memcost: u32, out: &mut [u8], ) -> Result<(), ErrorStack> { - return argon2_helper(CStr::from_bytes_with_nul(b"ARGON2D\0").unwrap(), ctx, pass, salt, ad, secret, iter, lanes, memcost, out); + argon2_helper(KDF_ARGON2D, ctx, pass, salt, ad, secret, iter, lanes, memcost, out) } #[allow(clippy::too_many_arguments)] @@ -72,7 +75,7 @@ cfg_if::cfg_if! { memcost: u32, out: &mut [u8], ) -> Result<(), ErrorStack> { - return argon2_helper(CStr::from_bytes_with_nul(b"ARGON2I\0").unwrap(), ctx, pass, salt, ad, secret, iter, lanes, memcost, out); + argon2_helper(KDF_ARGON2I, ctx, pass, salt, ad, secret, iter, lanes, memcost, out) } #[allow(clippy::too_many_arguments)] @@ -87,7 +90,7 @@ cfg_if::cfg_if! { memcost: u32, out: &mut [u8], ) -> Result<(), ErrorStack> { - return argon2_helper(CStr::from_bytes_with_nul(b"ARGON2ID\0").unwrap(), ctx, pass, salt, ad, secret, iter, lanes, memcost, out); + argon2_helper(KDF_ARGON2ID, ctx, pass, salt, ad, secret, iter, lanes, memcost, out) } /// Derives a key using the argon2* algorithms. From 5b5bca68ed0bb74eff214b936b846812aca82876 Mon Sep 17 00:00:00 2001 From: Huw Jones Date: Mon, 8 Sep 2025 14:23:10 +0100 Subject: [PATCH 04/42] pkey: add common OSSL params --- openssl/src/pkey.rs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/openssl/src/pkey.rs b/openssl/src/pkey.rs index 8d69e1cdc..493886226 100644 --- a/openssl/src/pkey.rs +++ b/openssl/src/pkey.rs @@ -914,6 +914,25 @@ impl TryFrom> for Dh { } } +cfg_if! { + if #[cfg(ossl300)] { + cstr_const!(pub(crate) OSSL_PKEY_PARAM_GROUP_NAME, b"group\0"); + + cstr_const!(pub(crate) OSSL_PKEY_PARAM_PUB_KEY, b"pub\0"); + cstr_const!(pub(crate) OSSL_PKEY_PARAM_PRIV_KEY, b"priv\0"); + + cstr_const!(pub(crate) OSSL_PKEY_PARAM_FFC_P, b"p\0"); + cstr_const!(pub(crate) OSSL_PKEY_PARAM_FFC_G, b"g\0"); + cstr_const!(pub(crate) OSSL_PKEY_PARAM_FFC_Q, b"q\0"); + + cstr_const!(pub(crate) OSSL_PKEY_PARAM_RSA_N, b"n\0"); + cstr_const!(pub(crate) OSSL_PKEY_PARAM_RSA_E, b"e\0"); + cstr_const!(pub(crate) OSSL_PKEY_PARAM_RSA_D, b"d\0"); + + cstr_const!(pub(crate) OSSL_SIGNATURE_PARAM_NONCE_TYPE, b"nonce-type\0"); + } +} + #[cfg(test)] mod tests { use std::convert::TryInto; From 428ddb1b739a2429ba371f24f7aa8497fc238b6f Mon Sep 17 00:00:00 2001 From: Huw Jones Date: Thu, 11 Sep 2025 10:21:28 +0100 Subject: [PATCH 05/42] partial revert: Remove unused param builder methods Partially reverts b7495f00c3bd9adcb117225875476ee218624cab by adding back add_bn and add_utf_string --- openssl/src/ossl_param.rs | 38 +++++++++++++++++++++++++++++++++----- 1 file changed, 33 insertions(+), 5 deletions(-) diff --git a/openssl/src/ossl_param.rs b/openssl/src/ossl_param.rs index 5dfaca768..adf20501f 100644 --- a/openssl/src/ossl_param.rs +++ b/openssl/src/ossl_param.rs @@ -13,11 +13,12 @@ //! Note, that this module is available only in OpenSSL 3.* and //! only internally for this crate. +use crate::bn::BigNumRef; use crate::error::ErrorStack; use crate::util; use crate::{cvt, cvt_p}; -use foreign_types::ForeignType; -use libc::{c_uint, c_void}; +use foreign_types::{ForeignType, ForeignTypeRef}; +use libc::{c_char, c_void}; use openssl_macros::corresponds; use std::ffi::CStr; use std::marker::PhantomData; @@ -106,7 +107,7 @@ impl<'a> OsslParamBuilder<'a> { } } - /// Adds a octet string to `OsslParamBuilder`. + /// Adds an octet string to `OsslParamBuilder`. #[corresponds(OSSL_PARAM_BLD_push_octet_string)] #[cfg_attr(any(not(ossl320), osslconf = "OPENSSL_NO_ARGON2"), allow(dead_code))] pub(crate) fn add_octet_string( @@ -125,7 +126,7 @@ impl<'a> OsslParamBuilder<'a> { } } - /// Adds a unsigned int to `OsslParamBuilder`. + /// Adds an unsigned int to `OsslParamBuilder`. #[corresponds(OSSL_PARAM_BLD_push_uint)] #[cfg_attr(any(not(ossl320), osslconf = "OPENSSL_NO_ARGON2"), allow(dead_code))] pub(crate) fn add_uint(&mut self, key: &'a CStr, val: u32) -> Result<(), ErrorStack> { @@ -133,12 +134,39 @@ impl<'a> OsslParamBuilder<'a> { cvt(ffi::OSSL_PARAM_BLD_push_uint( self.as_ptr(), key.as_ptr(), - val as c_uint, + val, )) .map(|_| ()) } } + /// Adds a `BigNum` to `OsslParamBuilder`. + #[corresponds(OSSL_PARAM_BLD_push_BN)] + #[allow(dead_code)] // TODO: remove when when used by EVP_KEY.from_data + pub(crate) fn add_bn(&mut self, key: &'a CStr, bn: &'a BigNumRef) -> Result<(), ErrorStack> { + cvt(unsafe { ffi::OSSL_PARAM_BLD_push_BN(self.as_ptr(), key.as_ptr(), bn.as_ptr()) }) + .map(|_| ()) + } + + /// Adds a utf8 string to `OsslParamBuilder`. + #[corresponds(OSSL_PARAM_BLD_push_utf8_string)] + #[allow(dead_code)] // TODO: remove when when used by EVP_KEY.from_data + pub(crate) fn add_utf8_string( + &mut self, + key: &'a CStr, + value: &'a str, + ) -> Result<(), ErrorStack> { + cvt(unsafe { + ffi::OSSL_PARAM_BLD_push_utf8_string( + self.as_ptr(), + key.as_ptr(), + value.as_ptr().cast::(), + value.len(), + ) + }) + .map(|_| ()) + } + /// Returns a raw pointer to the underlying `OSSL_PARAM_BLD` structure. pub(crate) unsafe fn as_ptr(&mut self) -> *mut ffi::OSSL_PARAM_BLD { self.builder.as_ptr() From 4561f001407929b39057f2f9182e46216d76ade5 Mon Sep 17 00:00:00 2001 From: Huw Jones Date: Wed, 6 Aug 2025 14:50:36 +0100 Subject: [PATCH 06/42] ossl_param: add tests --- openssl/src/ossl_param.rs | 80 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 76 insertions(+), 4 deletions(-) diff --git a/openssl/src/ossl_param.rs b/openssl/src/ossl_param.rs index adf20501f..e31f1e41b 100644 --- a/openssl/src/ossl_param.rs +++ b/openssl/src/ossl_param.rs @@ -176,11 +176,17 @@ impl<'a> OsslParamBuilder<'a> { #[cfg(test)] mod tests { use super::*; + use crate::bn::BigNum; + use crate::pkey::{ + OSSL_PKEY_PARAM_GROUP_NAME, OSSL_PKEY_PARAM_PUB_KEY, OSSL_PKEY_PARAM_RSA_D, + OSSL_PKEY_PARAM_RSA_E, OSSL_PKEY_PARAM_RSA_N, OSSL_SIGNATURE_PARAM_NONCE_TYPE, + }; + #[test] fn test_builder_locate_octet_string() { let mut builder = OsslParamBuilder::new().unwrap(); builder - .add_octet_string(CStr::from_bytes_with_nul(b"key1\0").unwrap(), b"value1") + .add_octet_string(OSSL_PKEY_PARAM_PUB_KEY, b"value1") .unwrap(); let params = builder.to_param().unwrap(); @@ -188,10 +194,76 @@ mod tests { .locate_octet_string(CStr::from_bytes_with_nul(b"invalid\0").unwrap()) .is_err()); assert_eq!( - params - .locate_octet_string(CStr::from_bytes_with_nul(b"key1\0").unwrap()) - .unwrap(), + params.locate_octet_string(OSSL_PKEY_PARAM_PUB_KEY).unwrap(), b"value1" ); } + + fn assert_param(params: &OsslParamArray, key: &CStr, is_null: bool) { + let param = unsafe { ffi::OSSL_PARAM_locate_const(params.as_ptr(), key.as_ptr()) }; + if is_null { + assert!(param.is_null(), "Unexpectedly found param: {key:?}"); + } else { + assert!(!param.is_null(), "Failed to find param: {key:?}"); + } + } + + #[test] + fn test_param_builder_uint() { + let mut builder = OsslParamBuilder::new().unwrap(); + builder + .add_uint(OSSL_SIGNATURE_PARAM_NONCE_TYPE, 42) + .unwrap(); + let params = builder.to_param().unwrap(); + + assert_param(¶ms, OSSL_SIGNATURE_PARAM_NONCE_TYPE, false); + assert_param(¶ms, OSSL_PKEY_PARAM_GROUP_NAME, true); + } + + #[test] + fn test_param_builder_bignum() { + let n = BigNum::from_u32(0xbc747fc5).unwrap(); + let e = BigNum::from_u32(0x10001).unwrap(); + let d = BigNum::from_u32(0x7b133399).unwrap(); + + let mut builder = OsslParamBuilder::new().unwrap(); + builder.add_bn(OSSL_PKEY_PARAM_RSA_N, &n).unwrap(); + builder.add_bn(OSSL_PKEY_PARAM_RSA_E, &e).unwrap(); + builder.add_bn(OSSL_PKEY_PARAM_RSA_D, &d).unwrap(); + let params = builder.to_param().unwrap(); + + for param in [ + OSSL_PKEY_PARAM_RSA_N, + OSSL_PKEY_PARAM_RSA_E, + OSSL_PKEY_PARAM_RSA_D, + ] { + assert_param(¶ms, param, false); + } + + assert_param(¶ms, OSSL_PKEY_PARAM_GROUP_NAME, true); + } + + #[test] + fn test_param_builder_string() { + let mut builder = OsslParamBuilder::new().unwrap(); + builder + .add_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME, "primve256v1") + .unwrap(); + let params = builder.to_param().unwrap(); + + assert_param(¶ms, OSSL_PKEY_PARAM_GROUP_NAME, false); + assert_param(¶ms, OSSL_PKEY_PARAM_RSA_N, true); + } + + #[test] + fn test_param_builder_octet_string() { + let mut builder = OsslParamBuilder::new().unwrap(); + builder + .add_octet_string(OSSL_PKEY_PARAM_PUB_KEY, b"foobar") + .unwrap(); + let params = builder.to_param().unwrap(); + + assert_param(¶ms, OSSL_PKEY_PARAM_PUB_KEY, false); + assert_param(¶ms, OSSL_PKEY_PARAM_GROUP_NAME, true); + } } From 7e868d45f06a99b7a6ed1ae129ac0582f901f7cd Mon Sep 17 00:00:00 2001 From: Huw Jones Date: Wed, 6 Aug 2025 14:52:29 +0100 Subject: [PATCH 07/42] ossl_params/arrayref: refactor locate_octet_string to use locate --- openssl/src/ossl_param.rs | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/openssl/src/ossl_param.rs b/openssl/src/ossl_param.rs index e31f1e41b..1cbfd039f 100644 --- a/openssl/src/ossl_param.rs +++ b/openssl/src/ossl_param.rs @@ -40,6 +40,17 @@ foreign_type_and_impl_send_sync! { } impl OsslParamArray { + /// Locate a parameter by the given key (returning a const reference). + #[corresponds(OSSL_PARAM_locate_const)] + fn locate_const(&self, key: &CStr) -> Option<*const ffi::OSSL_PARAM> { + let param = unsafe { ffi::OSSL_PARAM_locate_const(self.as_ptr(), key.as_ptr()) }; + if param.is_null() { + None + } else { + Some(param) + } + } + /// Locates the individual `OSSL_PARAM` element representing an /// octet string identified by the key in the `OsslParamArray` /// array and returns a reference to it. @@ -48,8 +59,8 @@ impl OsslParamArray { #[corresponds(OSSL_PARAM_get_octet_string)] #[allow(dead_code)] // TODO: remove when when used by ML-DSA / ML-KEM pub(crate) fn locate_octet_string<'a>(&'a self, key: &CStr) -> Result<&'a [u8], ErrorStack> { + let param = self.locate_const(key).ok_or_else(ErrorStack::get)?; unsafe { - let param = cvt_p(ffi::OSSL_PARAM_locate(self.as_ptr(), key.as_ptr()))?; let mut val: *const c_void = ptr::null_mut(); let mut val_len: usize = 0; cvt(ffi::OSSL_PARAM_get_octet_string_ptr( @@ -200,11 +211,9 @@ mod tests { } fn assert_param(params: &OsslParamArray, key: &CStr, is_null: bool) { - let param = unsafe { ffi::OSSL_PARAM_locate_const(params.as_ptr(), key.as_ptr()) }; - if is_null { - assert!(param.is_null(), "Unexpectedly found param: {key:?}"); - } else { - assert!(!param.is_null(), "Failed to find param: {key:?}"); + match params.locate_const(key) { + Some(_) => assert!(!is_null, "Unexpectedly found param: {key:?}"), + None => assert!(is_null, "Failed to find param: {key:?}"), } } From a494671eb26164fd3f62cb62f011fb4ea1cf2303 Mon Sep 17 00:00:00 2001 From: Huw Jones Date: Fri, 12 Sep 2025 15:52:04 +0100 Subject: [PATCH 08/42] ossl_param/array: implement locate_bn --- openssl/src/ossl_param.rs | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/openssl/src/ossl_param.rs b/openssl/src/ossl_param.rs index 1cbfd039f..1ffcc5614 100644 --- a/openssl/src/ossl_param.rs +++ b/openssl/src/ossl_param.rs @@ -13,7 +13,7 @@ //! Note, that this module is available only in OpenSSL 3.* and //! only internally for this crate. -use crate::bn::BigNumRef; +use crate::bn::{BigNum, BigNumRef}; use crate::error::ErrorStack; use crate::util; use crate::{cvt, cvt_p}; @@ -71,6 +71,19 @@ impl OsslParamArray { Ok(util::from_raw_parts(val as *const u8, val_len)) } } + + /// Locates the individual `OSSL_PARAM` element representing a BigNum identified by the key in + /// the `OsslParamArray` array and returns a reference to it. + /// + /// Combines OSSL_PARAM_locate and OSSL_PARAM_get_BN. + #[corresponds(OSSL_PARAM_get_BN)] + #[allow(dead_code)] + fn locate_bn(&self, key: &CStr) -> Result { + let param = self.locate_const(key).ok_or_else(ErrorStack::get)?; + let mut bn_ptr = ptr::null_mut(); + cvt(unsafe { ffi::OSSL_PARAM_get_BN(param, &mut bn_ptr) })?; + Ok(unsafe { BigNum::from_ptr(bn_ptr) }) + } } foreign_type_and_impl_send_sync! { @@ -217,6 +230,11 @@ mod tests { } } + fn assert_bn_equal(params: &OsslParamArray, key: &CStr, expected: &BigNum) { + let bn = params.locate_bn(key).unwrap(); + assert_eq!(bn, *expected); + } + #[test] fn test_param_builder_uint() { let mut builder = OsslParamBuilder::new().unwrap(); @@ -241,12 +259,12 @@ mod tests { builder.add_bn(OSSL_PKEY_PARAM_RSA_D, &d).unwrap(); let params = builder.to_param().unwrap(); - for param in [ - OSSL_PKEY_PARAM_RSA_N, - OSSL_PKEY_PARAM_RSA_E, - OSSL_PKEY_PARAM_RSA_D, + for (param, expected) in [ + (OSSL_PKEY_PARAM_RSA_N, n), + (OSSL_PKEY_PARAM_RSA_E, e), + (OSSL_PKEY_PARAM_RSA_D, d), ] { - assert_param(¶ms, param, false); + assert_bn_equal(¶ms, param, &expected); } assert_param(¶ms, OSSL_PKEY_PARAM_GROUP_NAME, true); From e3d09ac6a56a9d1907fa161dd879ed4b831ca31d Mon Sep 17 00:00:00 2001 From: Huw Jones Date: Fri, 12 Sep 2025 15:51:52 +0100 Subject: [PATCH 09/42] ossl_param/array: implement merge --- openssl/src/ossl_param.rs | 69 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/openssl/src/ossl_param.rs b/openssl/src/ossl_param.rs index 1ffcc5614..abb209787 100644 --- a/openssl/src/ossl_param.rs +++ b/openssl/src/ossl_param.rs @@ -86,6 +86,25 @@ impl OsslParamArray { } } +impl OsslParamArrayRef { + /// Merges two `ParamsRef` objects into a new `Params` object. + #[corresponds(OSSL_PARAM_merge)] + #[allow(dead_code)] + pub fn merge(&self, other: &OsslParamArrayRef) -> Result { + // OSSL_PARAM_merge shallow copies the params + // OSSL_PARAM_free deep frees (so the params and values will be freed) + // OSSL_PARAM_dup deep copies + // Dupe both params[] so we don't end up pointing to freed memory. + cvt_p(unsafe { + ffi::OSSL_PARAM_merge( + ffi::OSSL_PARAM_dup(self.as_ptr()), + ffi::OSSL_PARAM_dup(other.as_ptr()), + ) + }) + .map(|p| unsafe { OsslParamArray::from_ptr(p) }) + } +} + foreign_type_and_impl_send_sync! { type CType = ffi::OSSL_PARAM_BLD; fn drop = ffi::OSSL_PARAM_BLD_free; @@ -293,4 +312,54 @@ mod tests { assert_param(¶ms, OSSL_PKEY_PARAM_PUB_KEY, false); assert_param(¶ms, OSSL_PKEY_PARAM_GROUP_NAME, true); } + + #[test] + fn test_merge() { + let (n, e, d) = (0xbc747fc5, 0x10001, 0x7b133399); + let mut merged_params: OsslParamArray; + + // Create a param array with just n in a scoped block so that bn_n, and the builder are dropped + { + let bn_n = BigNum::from_u32(n).unwrap(); + let mut builder = OsslParamBuilder::new().unwrap(); + builder.add_bn(OSSL_PKEY_PARAM_RSA_N, &bn_n).unwrap(); + merged_params = builder.to_param().unwrap(); + } + + // We should still be able to pull back n and get the correct value, but not e or d (yet) + let bn_n = BigNum::from_u32(n).unwrap(); + assert_bn_equal(&merged_params, OSSL_PKEY_PARAM_RSA_N, &bn_n); + assert_param(&merged_params, OSSL_PKEY_PARAM_RSA_E, true); + assert_param(&merged_params, OSSL_PKEY_PARAM_RSA_D, true); + + // Create a new param array with just e and merge it in + { + let bn_e = BigNum::from_u32(e).unwrap(); + let mut builder = OsslParamBuilder::new().unwrap(); + builder.add_bn(OSSL_PKEY_PARAM_RSA_E, &bn_e).unwrap(); + let params = builder.to_param().unwrap(); + merged_params = merged_params.merge(¶ms).unwrap(); + } + + // We should still be able to pull back n & e and get the correct value, but not d (yet) + let bn_e = BigNum::from_u32(e).unwrap(); + assert_bn_equal(&merged_params, OSSL_PKEY_PARAM_RSA_N, &bn_n); + assert_bn_equal(&merged_params, OSSL_PKEY_PARAM_RSA_E, &bn_e); + assert_param(&merged_params, OSSL_PKEY_PARAM_RSA_D, true); + + // Again, create a new param array with just d and merge it in + { + let bn_d = BigNum::from_u32(d).unwrap(); + let mut builder = OsslParamBuilder::new().unwrap(); + builder.add_bn(OSSL_PKEY_PARAM_RSA_D, &bn_d).unwrap(); + let params = builder.to_param().unwrap(); + merged_params = merged_params.merge(¶ms).unwrap(); + } + + // We should be able to pull all of n, e & d out and get the correct values + let bn_d = BigNum::from_u32(d).unwrap(); + assert_bn_equal(&merged_params, OSSL_PKEY_PARAM_RSA_N, &bn_n); + assert_bn_equal(&merged_params, OSSL_PKEY_PARAM_RSA_E, &bn_e); + assert_bn_equal(&merged_params, OSSL_PKEY_PARAM_RSA_D, &bn_d); + } } From aff8b082ca9f80380753c250f071aca6041dfe60 Mon Sep 17 00:00:00 2001 From: Huw Jones Date: Wed, 6 Aug 2025 17:47:44 +0100 Subject: [PATCH 10/42] pkey_ctx/set_nonce_type: use ParamBuilder Simplifies and safens the code --- openssl/src/pkey_ctx.rs | 38 ++++++++++++++++++++++---------------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/openssl/src/pkey_ctx.rs b/openssl/src/pkey_ctx.rs index 1b58108ae..8c65527d6 100644 --- a/openssl/src/pkey_ctx.rs +++ b/openssl/src/pkey_ctx.rs @@ -70,6 +70,12 @@ use crate::cipher::CipherRef; use crate::error::ErrorStack; use crate::md::MdRef; use crate::nid::Nid; +#[cfg(ossl300)] +use crate::ossl_param::OsslParamArrayRef; +#[cfg(ossl320)] +use crate::ossl_param::OsslParamBuilder; +#[cfg(ossl320)] +use crate::pkey::OSSL_SIGNATURE_PARAM_NONCE_TYPE; use crate::pkey::{HasPrivate, HasPublic, Id, PKey, PKeyRef, Params, Private}; use crate::rsa::Padding; use crate::sign::RsaPssSaltlen; @@ -82,8 +88,6 @@ use libc::c_int; use libc::c_uint; use openssl_macros::corresponds; use std::convert::TryFrom; -#[cfg(ossl320)] -use std::ffi::CStr; use std::ptr; /// HKDF modes of operation. @@ -867,6 +871,14 @@ impl PkeyCtxRef { } } + /// Sets parameters on the given context + #[corresponds(EVP_PKEY_CTX_set_params)] + #[cfg(ossl300)] + #[cfg_attr(not(ossl320), allow(dead_code))] + fn set_params(&mut self, params: &OsslParamArrayRef) -> Result<(), ErrorStack> { + cvt(unsafe { ffi::EVP_PKEY_CTX_set_params(self.as_ptr(), params.as_ptr()) }).map(|_| ()) + } + /// Sets the nonce type for a private key context. /// /// The nonce for DSA and ECDSA can be either random (the default) or deterministic (as defined by RFC 6979). @@ -876,17 +888,10 @@ impl PkeyCtxRef { #[cfg(ossl320)] #[corresponds(EVP_PKEY_CTX_set_params)] pub fn set_nonce_type(&mut self, nonce_type: NonceType) -> Result<(), ErrorStack> { - let nonce_field_name = CStr::from_bytes_with_nul(b"nonce-type\0").unwrap(); - let mut nonce_type = nonce_type.0; - unsafe { - let param_nonce = - ffi::OSSL_PARAM_construct_uint(nonce_field_name.as_ptr(), &mut nonce_type); - let param_end = ffi::OSSL_PARAM_construct_end(); - - let params = [param_nonce, param_end]; - cvt(ffi::EVP_PKEY_CTX_set_params(self.as_ptr(), params.as_ptr()))?; - } - Ok(()) + let mut builder = OsslParamBuilder::new()?; + builder.add_uint(OSSL_SIGNATURE_PARAM_NONCE_TYPE, nonce_type.0)?; + let params = builder.to_param()?; + self.set_params(¶ms) } /// Gets the nonce type for a private key context. @@ -898,11 +903,12 @@ impl PkeyCtxRef { #[cfg(ossl320)] #[corresponds(EVP_PKEY_CTX_get_params)] pub fn nonce_type(&mut self) -> Result { - let nonce_field_name = CStr::from_bytes_with_nul(b"nonce-type\0").unwrap(); let mut nonce_type: c_uint = 0; unsafe { - let param_nonce = - ffi::OSSL_PARAM_construct_uint(nonce_field_name.as_ptr(), &mut nonce_type); + let param_nonce = ffi::OSSL_PARAM_construct_uint( + OSSL_SIGNATURE_PARAM_NONCE_TYPE.as_ptr(), + &mut nonce_type, + ); let param_end = ffi::OSSL_PARAM_construct_end(); let mut params = [param_nonce, param_end]; From e1f17e93c5e672c9d9d52e67d79f4fed5342d605 Mon Sep 17 00:00:00 2001 From: Huw Jones Date: Wed, 6 Aug 2025 15:27:01 +0100 Subject: [PATCH 11/42] pkey_ctx: add Selection trait for Param/Public/Private markers --- openssl/src/pkey_ctx.rs | 45 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/openssl/src/pkey_ctx.rs b/openssl/src/pkey_ctx.rs index 8c65527d6..2a7b6238b 100644 --- a/openssl/src/pkey_ctx.rs +++ b/openssl/src/pkey_ctx.rs @@ -70,10 +70,12 @@ use crate::cipher::CipherRef; use crate::error::ErrorStack; use crate::md::MdRef; use crate::nid::Nid; -#[cfg(ossl300)] +#[cfg(ossl320)] use crate::ossl_param::OsslParamArrayRef; #[cfg(ossl320)] use crate::ossl_param::OsslParamBuilder; +#[cfg(ossl300)] +use crate::pkey::Public; #[cfg(ossl320)] use crate::pkey::OSSL_SIGNATURE_PARAM_NONCE_TYPE; use crate::pkey::{HasPrivate, HasPublic, Id, PKey, PKeyRef, Params, Private}; @@ -131,6 +133,47 @@ impl NonceType { pub const DETERMINISTIC_K: Self = NonceType(1); } +cfg_if! { + if #[cfg(ossl300)] { + #[derive(Debug, PartialEq)] + pub(crate) enum Selection { + /// Key parameters + KeyParameters, + /// Public key (including parameters, if applicable). + PublicKey, + /// Keypair, which includes private key, public key, and parameters (if available). + Keypair, + } + + impl From for i32 { + fn from(value: Selection) -> Self { + match value { + Selection::KeyParameters => ffi::EVP_PKEY_KEY_PARAMETERS, + Selection::PublicKey => ffi::EVP_PKEY_PUBLIC_KEY, + Selection::Keypair => ffi::EVP_PKEY_KEYPAIR, + } + } + } + + /// Selection for fromdata/todata operation. + pub(crate) trait SelectionT { + const SELECTION: Selection; + } + + impl SelectionT for Params { + const SELECTION: Selection = Selection::KeyParameters; + } + + impl SelectionT for Public { + const SELECTION: Selection = Selection::PublicKey; + } + + impl SelectionT for Private { + const SELECTION: Selection = Selection::Keypair; + } + } +} + generic_foreign_type_and_impl_send_sync! { type CType = ffi::EVP_PKEY_CTX; fn drop = ffi::EVP_PKEY_CTX_free; From b30c65fe7951dabd501d08f5b3633748bf7bbda5 Mon Sep 17 00:00:00 2001 From: Huw Jones Date: Wed, 6 Aug 2025 15:27:59 +0100 Subject: [PATCH 12/42] pkey_ctx: add ability to create a PKey from params --- openssl/src/pkey_ctx.rs | 69 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 68 insertions(+), 1 deletion(-) diff --git a/openssl/src/pkey_ctx.rs b/openssl/src/pkey_ctx.rs index 2a7b6238b..0dea2bb95 100644 --- a/openssl/src/pkey_ctx.rs +++ b/openssl/src/pkey_ctx.rs @@ -70,7 +70,7 @@ use crate::cipher::CipherRef; use crate::error::ErrorStack; use crate::md::MdRef; use crate::nid::Nid; -#[cfg(ossl320)] +#[cfg(ossl300)] use crate::ossl_param::OsslParamArrayRef; #[cfg(ossl320)] use crate::ossl_param::OsslParamBuilder; @@ -481,6 +481,35 @@ impl PkeyCtxRef { Ok(()) } + /// Prepares the context for creating a key from user data. + #[corresponds(EVP_PKEY_fromdata_init)] + #[inline] + #[cfg(ossl300)] + pub(crate) fn fromdata_init(&mut self) -> Result<(), ErrorStack> { + cvt(unsafe { ffi::EVP_PKEY_fromdata_init(self.as_ptr()) }).map(|_| ()) + } + + /// Convert a stack of Params into a PKey. + #[corresponds(EVP_PKEY_fromdata)] + #[inline] + #[cfg(ossl300)] + pub(crate) fn fromdata( + &mut self, + params: &OsslParamArrayRef, + selection: Selection, + ) -> Result, ErrorStack> { + let mut key_ptr = ptr::null_mut(); + cvt(unsafe { + ffi::EVP_PKEY_fromdata( + self.as_ptr(), + &mut key_ptr, + selection.into(), + params.as_ptr(), + ) + })?; + Ok(unsafe { PKey::from_ptr(key_ptr) }) + } + /// Sets which algorithm was used to compute the digest used in a /// signature. With RSA signatures this causes the signature to be wrapped /// in a `DigestInfo` structure. This is almost always what you want with @@ -964,6 +993,18 @@ impl PkeyCtxRef { } } +/// Creates a new `PKey` from the given ID and parameters. +#[cfg(ossl300)] +#[allow(dead_code)] // TODO: remove when used by pkey creation +pub(crate) fn pkey_from_params( + id: Id, + params: &OsslParamArrayRef, +) -> Result, ErrorStack> { + let mut ctx = PkeyCtx::new_id(id)?; + ctx.fromdata_init()?; + ctx.fromdata(params, K::SELECTION) +} + #[cfg(test)] mod test { use super::*; @@ -974,7 +1015,11 @@ mod test { use crate::hash::{hash, MessageDigest}; use crate::md::Md; use crate::nid::Nid; + #[cfg(ossl300)] + use crate::ossl_param::OsslParamBuilder; use crate::pkey::PKey; + #[cfg(ossl300)] + use crate::pkey::{OSSL_PKEY_PARAM_RSA_D, OSSL_PKEY_PARAM_RSA_E, OSSL_PKEY_PARAM_RSA_N}; use crate::rsa::Rsa; use crate::sign::Verifier; #[cfg(not(boringssl))] @@ -1350,4 +1395,26 @@ mxJ7imIrEg9nIQ== assert_eq!(output, expected_output); assert!(ErrorStack::get().errors().is_empty()); } + + #[test] + #[cfg(ossl300)] + fn test_pkey_from_params() { + let n = BigNum::from_u32(0xbc747fc5).unwrap(); + let e = BigNum::from_u32(0x10001).unwrap(); + let d = BigNum::from_u32(0x7b133399).unwrap(); + + let mut builder = OsslParamBuilder::new().unwrap(); + builder.add_bn(OSSL_PKEY_PARAM_RSA_N, &n).unwrap(); + builder.add_bn(OSSL_PKEY_PARAM_RSA_E, &e).unwrap(); + builder.add_bn(OSSL_PKEY_PARAM_RSA_D, &d).unwrap(); + let params = builder.to_param().unwrap(); + + let pkey: PKey = pkey_from_params(Id::RSA, ¶ms).unwrap(); + + let rsa = pkey.rsa().unwrap(); + + assert_eq!(rsa.n(), &n); + assert_eq!(rsa.e(), &e); + assert_eq!(rsa.d(), &d); + } } From 8abc19ebbbc9d46c625b01ee6971f44681bb0494 Mon Sep 17 00:00:00 2001 From: Huw Jones Date: Fri, 12 Sep 2025 12:22:15 +0100 Subject: [PATCH 13/42] pkey: implement to_data --- openssl/src/pkey.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/openssl/src/pkey.rs b/openssl/src/pkey.rs index 493886226..0f4156e78 100644 --- a/openssl/src/pkey.rs +++ b/openssl/src/pkey.rs @@ -47,8 +47,12 @@ use crate::dh::Dh; use crate::dsa::Dsa; use crate::ec::EcKey; use crate::error::ErrorStack; +#[cfg(ossl300)] +use crate::ossl_param::OsslParamArray; #[cfg(any(ossl110, boringssl, libressl370, awslc))] use crate::pkey_ctx::PkeyCtx; +#[cfg(ossl300)] +use crate::pkey_ctx::Selection; use crate::rsa::Rsa; use crate::symm::Cipher; use crate::util::{invoke_passwd_cb, CallbackState}; @@ -207,6 +211,18 @@ impl PKeyRef { pub fn size(&self) -> usize { unsafe { ffi::EVP_PKEY_size(self.as_ptr()) as usize } } + + /// Converts the `PKey` to an `OsslParamArray`. + /// + /// Use `selection` to control what parameters are included. + #[corresponds(EVP_PKEY_todata)] + #[cfg(ossl300)] + #[allow(dead_code)] // TODO: remove when used by non-deprecated key wrappers + pub(crate) fn to_data(&self, selection: Selection) -> Result { + let mut params = ptr::null_mut(); + cvt(unsafe { ffi::EVP_PKEY_todata(self.as_ptr(), selection.into(), &mut params) })?; + Ok(unsafe { OsslParamArray::from_ptr(params) }) + } } impl PKeyRef From 6e95c596b2aac2b0a137018aa35c7169cbfb5e43 Mon Sep 17 00:00:00 2001 From: Huw Jones Date: Fri, 12 Sep 2025 12:22:31 +0100 Subject: [PATCH 14/42] pkey: test round-robin to_data => from_data --- openssl/src/pkey.rs | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/openssl/src/pkey.rs b/openssl/src/pkey.rs index 0f4156e78..0fa568986 100644 --- a/openssl/src/pkey.rs +++ b/openssl/src/pkey.rs @@ -957,8 +957,12 @@ mod tests { use crate::dh::Dh; use crate::dsa::Dsa; use crate::ec::EcKey; + #[cfg(ossl300)] + use crate::encrypt::{Decrypter, Encrypter}; use crate::error::Error; use crate::nid::Nid; + #[cfg(ossl300)] + use crate::pkey_ctx::pkey_from_params; use crate::rsa::Rsa; use crate::symm::Cipher; @@ -1254,4 +1258,27 @@ mod tests { assert!(!pkey1.public_eq(&pkey2)); assert!(Error::get().is_none()); } + + #[cfg(ossl300)] + #[test] + fn test_todata() { + let data: &[u8] = b"hello, world"; + let pkey1 = PKey::from_rsa(Rsa::generate(2048).unwrap()).unwrap(); + + // Encrypt some data using the generated pkey + let encrypter = Encrypter::new(&pkey1).unwrap(); + let mut encrypted = vec![0u8; encrypter.encrypt_len(data).unwrap()]; + encrypter.encrypt(data, &mut encrypted).unwrap(); + + // Convert the pkey to OSSL_PARAMs and back into a PKey + let params = pkey1.to_data(Selection::Keypair).unwrap(); + let pkey2: PKey = pkey_from_params(Id::RSA, ¶ms).unwrap(); + + // Decrypt the data using the new pkey + let decrypter = Decrypter::new(&pkey2).unwrap(); + let mut decrypted = vec![0u8; decrypter.decrypt_len(&encrypted).unwrap()]; + let decrypted_len = decrypter.decrypt(&encrypted, &mut decrypted).unwrap(); + decrypted.truncate(decrypted_len); + assert_eq!(data, &decrypted); + } } From 6dbe468e65cc9e4ecbee5407bb74136120eafd65 Mon Sep 17 00:00:00 2001 From: Huw Jones Date: Mon, 11 Aug 2025 17:57:45 +0100 Subject: [PATCH 15/42] Add encoder wrapper --- openssl/src/lib.rs | 2 + openssl/src/ossl_encdec.rs | 593 +++++++++++++++++++++++++++++++++++++ openssl/test/dhparams.der | Bin 0 -> 268 bytes openssl/test/pkcs1.der.pub | Bin 0 -> 270 bytes openssl/test/rsa.der | Bin 0 -> 1191 bytes 5 files changed, 595 insertions(+) create mode 100644 openssl/src/ossl_encdec.rs create mode 100644 openssl/test/dhparams.der create mode 100644 openssl/test/pkcs1.der.pub create mode 100644 openssl/test/rsa.der diff --git a/openssl/src/lib.rs b/openssl/src/lib.rs index 5ae46337e..e7af9d1a3 100644 --- a/openssl/src/lib.rs +++ b/openssl/src/lib.rs @@ -178,6 +178,8 @@ pub mod nid; #[cfg(not(osslconf = "OPENSSL_NO_OCSP"))] pub mod ocsp; #[cfg(ossl300)] +mod ossl_encdec; +#[cfg(ossl300)] mod ossl_param; pub mod pkcs12; pub mod pkcs5; diff --git a/openssl/src/ossl_encdec.rs b/openssl/src/ossl_encdec.rs new file mode 100644 index 000000000..262355fce --- /dev/null +++ b/openssl/src/ossl_encdec.rs @@ -0,0 +1,593 @@ +use crate::bio::MemBio; +use crate::error::ErrorStack; +use crate::pkey::PKeyRef; +use crate::pkey_ctx::Selection; +use crate::symm::Cipher; +use crate::{cvt, cvt_p}; +use foreign_types::{ForeignType, ForeignTypeRef}; +use openssl_macros::corresponds; +use std::ffi::{CStr, CString}; +use std::ptr; + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum KeyFormat { + /// Human-readable description of the key. + Text, + /// DER formatted data + Der, + /// PEM formatted data + Pem, + // MSBLOB formatted data + MsBlob, + // PVK formatted data + Pvk, +} + +impl From<&CStr> for KeyFormat { + fn from(s: &CStr) -> Self { + match s.to_bytes() { + b"TEXT" => Self::Text, + b"DER" => Self::Der, + b"PEM" => Self::Pem, + b"MSBLOB" => Self::MsBlob, + b"PVK" => Self::Pvk, + _ => panic!("Unknown output type"), + } + } +} + +cstr_const!(KEY_FORMAT_TEXT, b"TEXT\0"); +cstr_const!(KEY_FORMAT_DER, b"DER\0"); +cstr_const!(KEY_FORMAT_PEM, b"PEM\0"); +cstr_const!(KEY_FORMAT_MSBLOB, b"MSBLOB\0"); +cstr_const!(KEY_FORMAT_PVK, b"PVK\0"); + +impl From for &CStr { + fn from(o: KeyFormat) -> Self { + match o { + KeyFormat::Text => KEY_FORMAT_TEXT, + KeyFormat::Der => KEY_FORMAT_DER, + KeyFormat::Pem => KEY_FORMAT_PEM, + KeyFormat::MsBlob => KEY_FORMAT_MSBLOB, + KeyFormat::Pvk => KEY_FORMAT_PVK, + } + } +} + +pub enum Structure<'a> { + /// Encoding of public keys according to the Subject Public Key Info of RFC 5280 + SubjectPublicKeyInfo, + /// Structure according to the PKCS#1 specification + PKCS1, + /// Structure according to the PKCS#8 specification + PKCS8, + /// Type-specific structure + TypeSpecific, + Other(&'a CStr), +} + +impl<'a> From<&'a CStr> for Structure<'a> { + fn from(s: &'a CStr) -> Self { + match s.to_bytes() { + b"SubjectPublicKeyInfo" => Self::SubjectPublicKeyInfo, + b"pkcs1" => Self::PKCS1, + b"pkcs8" => Self::PKCS8, + b"type-specific" => Self::TypeSpecific, + _ => Self::Other(s), + } + } +} + +cstr_const!(STRUCTURE_SUBJECT_PUBLIC_KEY_INFO, b"SubjectPublicKeyInfo\0"); +cstr_const!(STRUCTURE_PKCS1, b"pkcs1\0"); +cstr_const!(STRUCTURE_PKCS8, b"pkcs8\0"); +cstr_const!(STRUCTURE_TYPE_SPECIFIC, b"type-specific\0"); + +impl<'a> From> for &'a CStr { + fn from(o: Structure<'a>) -> Self { + match o { + Structure::SubjectPublicKeyInfo => STRUCTURE_SUBJECT_PUBLIC_KEY_INFO, + Structure::PKCS1 => STRUCTURE_PKCS1, + Structure::PKCS8 => STRUCTURE_PKCS8, + Structure::TypeSpecific => STRUCTURE_TYPE_SPECIFIC, + Structure::Other(v) => v, + } + } +} + +foreign_type_and_impl_send_sync! { + type CType = ffi::OSSL_ENCODER_CTX; + fn drop = ffi::OSSL_ENCODER_CTX_free; + + /// A context object which can perform encode operations. + pub struct OsslEncoderCtx; + /// A reference to an [`OsslEncoderCtx`]. + pub struct OsslEncoderCtxRef; +} + +impl OsslEncoderCtx { + /// Creates a new encoder context using the provided key. + #[corresponds(OSSL_ENCODER_CTX_new_for_pkey)] + #[inline] + #[allow(dead_code)] + fn new_for_key( + pkey: &PKeyRef, + selection: Selection, + output: Option, + structure: Option>, + ) -> Result { + let output_ptr = output + .map(|o| { + let output: &CStr = o.into(); + output.as_ptr() + }) + .unwrap_or_else(ptr::null); + let structure_ptr = structure + .map(|s| { + let structure: &CStr = s.into(); + structure.as_ptr() + }) + .unwrap_or_else(ptr::null); + + unsafe { + let ptr = cvt_p(ffi::OSSL_ENCODER_CTX_new_for_pkey( + pkey.as_ptr(), + selection.into(), + output_ptr, + structure_ptr, + ptr::null(), + ))?; + Ok(Self::from_ptr(ptr)) + } + } +} + +impl OsslEncoderCtxRef { + // XXX: Because the only way to create an `EncoderCtx` is through `new_for_key`, don't expose + // set_selection, because it doesn't work if OSSL_ENCODER_CTX_new_for_key is called! + // See https://github.com/openssl/openssl/issues/28249 + // /// Select which parts of the key to encode. + // #[corresponds(OSSL_ENCODER_CTX_set_selection)] + // #[allow(dead_code)] + // pub fn set_selection(&mut self, selection: Selection) -> Result<(), ErrorStack> { + // cvt(unsafe { ffi::OSSL_ENCODER_CTX_set_selection(self.as_ptr(), selection.into()) }) + // .map(|_| ()) + // } + + /// Set the output type for the encoded data. + #[corresponds(OSSL_ENCODER_CTX_set_output_type)] + #[allow(dead_code)] + fn set_output_type(&mut self, output: KeyFormat) -> Result<(), ErrorStack> { + let output: &CStr = output.into(); + cvt(unsafe { ffi::OSSL_ENCODER_CTX_set_output_type(self.as_ptr(), output.as_ptr()) }) + .map(|_| ()) + } + + /// Set the output structure for the encoded data. + #[corresponds(OSSL_ENCODER_CTX_set_output_structure)] + #[allow(dead_code)] + fn set_output_structure(&mut self, structure: Structure<'_>) -> Result<(), ErrorStack> { + let structure: &CStr = structure.into(); + cvt(unsafe { + ffi::OSSL_ENCODER_CTX_set_output_structure(self.as_ptr(), structure.as_ptr()) + }) + .map(|_| ()) + } + + /// Set the (optional) output cipher for the encoded data. + /// + /// If `cipher` is `None`, no cipher will be used (i.e., the output will not be encrypted). + #[corresponds(OSSL_ENCODER_CTX_set_cipher)] + #[allow(dead_code)] + fn set_cipher(&mut self, cipher: Option) -> Result<(), ErrorStack> { + let cipher_name = cipher.map(|c| CString::new(c.nid().short_name().unwrap()).unwrap()); + cvt(unsafe { + ffi::OSSL_ENCODER_CTX_set_cipher( + self.as_ptr(), + cipher_name.as_ref().map_or(ptr::null(), |c| c.as_ptr()), + ptr::null(), + ) + }) + .map(|_| ()) + } + + /// Set the passphrase for the encoded data. + #[corresponds(OSSL_ENCODER_CTX_set_passphrase)] + #[allow(dead_code)] + fn set_passphrase(&mut self, passphrase: &[u8]) -> Result<(), ErrorStack> { + cvt(unsafe { + ffi::OSSL_ENCODER_CTX_set_passphrase( + self.as_ptr(), + passphrase.as_ptr().cast(), + passphrase.len(), + ) + }) + .map(|_| ()) + } + + /// Encode the data and return the result + #[corresponds(OSSL_ENCODER_to_bio)] + #[allow(dead_code)] + fn encode(&mut self) -> Result, ErrorStack> { + let bio = MemBio::new()?; + unsafe { + cvt(ffi::OSSL_ENCODER_to_bio(self.as_ptr(), bio.as_ptr()))?; + } + + Ok(bio.get_buf().to_owned()) + } +} + +pub struct Encoder<'a> { + selection: Selection, + format: Option, + structure: Option>, + cipher: Option, + passphrase: Option<&'a [u8]>, +} + +impl<'a> Encoder<'a> { + #[allow(dead_code)] + pub(crate) fn new(selection: Selection) -> Self { + Self { + selection, + format: None, + structure: None, + cipher: None, + passphrase: None, + } + } + + #[allow(dead_code)] + pub fn set_format(mut self, format: KeyFormat) -> Self { + self.format = Some(format); + self + } + + #[allow(dead_code)] + pub fn set_structure(mut self, structure: Structure<'a>) -> Self { + self.structure = Some(structure); + self + } + + #[allow(dead_code)] + pub fn set_cipher(mut self, cipher: Cipher) -> Self { + self.cipher = Some(cipher); + self + } + + #[allow(dead_code)] + pub fn set_passphrase(mut self, passphrase: &'a [u8]) -> Self { + self.passphrase = Some(passphrase); + self + } + + #[allow(dead_code)] + pub fn encode(self, pkey: &PKeyRef) -> Result, ErrorStack> { + let mut ctx = + OsslEncoderCtx::new_for_key(pkey, self.selection, self.format, self.structure)?; + + ctx.set_cipher(self.cipher)?; + if let Some(passphrase) = self.passphrase { + ctx.set_passphrase(passphrase)?; + } + + ctx.encode() + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::pkey::PKey; + use crate::rsa::Rsa; + use std::str::from_utf8; + + mod output { + use super::*; + #[test] + fn test_output_from_cstr() { + let text: KeyFormat = KEY_FORMAT_TEXT.into(); + let der: KeyFormat = KEY_FORMAT_DER.into(); + let pem: KeyFormat = KEY_FORMAT_PEM.into(); + + assert_eq!(text, KeyFormat::Text); + assert_eq!(der, KeyFormat::Der); + assert_eq!(pem, KeyFormat::Pem); + } + + #[test] + fn test_cstr_from_output() { + let text: &CStr = KeyFormat::Text.into(); + let der: &CStr = KeyFormat::Der.into(); + let pem: &CStr = KeyFormat::Pem.into(); + + assert_eq!(text.to_bytes(), b"TEXT"); + assert_eq!(der.to_bytes(), b"DER"); + assert_eq!(pem.to_bytes(), b"PEM"); + } + } + + mod encoder { + use super::*; + + mod params { + use super::*; + use crate::dh::Dh; + use crate::pkey::Params; + use crate::pkey_ctx::PkeyCtx; + + fn generate_dh_params() -> Result, ErrorStack> { + let mut ctx = PkeyCtx::new_id(Id::DH)?; + ctx.paramgen_init()?; + ctx.set_dh_paramgen_prime_len(512)?; + ctx.set_dh_paramgen_generator(2)?; + ctx.paramgen() + } + + #[test] + fn test_dh_pem() { + let pkey = generate_dh_params().unwrap(); + + // Serialise params to PEM + let pem = Encoder::new(Selection::KeyParameters) + .set_format(KeyFormat::Pem) + .set_structure(Structure::TypeSpecific) + .encode(&pkey) + .unwrap(); + let pem_str = from_utf8(&pem).unwrap(); + + // We should be able to load the params back into a key + assert!( + pem_str.contains("-----BEGIN DH PARAMETERS-----"), + "{pem_str}" + ); + let pem_key = Dh::params_from_pem(&pem).unwrap(); + assert_eq!(pem_key.prime_p(), pkey.dh().unwrap().prime_p()); + } + + #[test] + fn test_dh_der() { + let pkey = generate_dh_params().unwrap(); + + // Serialise parms to PEM + let der = Encoder::new(Selection::KeyParameters) + .set_format(KeyFormat::Der) + .set_structure(Structure::TypeSpecific) + .encode(&pkey) + .unwrap(); + + // DER is not valid UTF-8, so we can't convert it to a string + assert!(from_utf8(&der).is_err()); + + // We should be able to load the DER back into a key + let der_key = Dh::params_from_der(&der).unwrap(); + assert_eq!(der_key.prime_p(), pkey.dh().unwrap().prime_p()); + } + } + + mod public { + use super::*; + + #[test] + fn test_rsa_pem() { + let expected = include_bytes!("../test/rsa.pem.pub"); + let pkey = PKey::public_key_from_pem(expected).unwrap(); + + // Serialise public key to PEM + let pem = Encoder::new(Selection::PublicKey) + .set_format(KeyFormat::Pem) + .set_structure(Structure::SubjectPublicKeyInfo) + .encode(&pkey) + .unwrap(); + + // We should end up with the same PEM as the input + assert_eq!( + from_utf8(&pem).unwrap(), + from_utf8(expected).unwrap().replace("\r\n", "\n") + ); + } + + #[test] + fn test_rsa_pem_pkcs1() { + let expected = include_bytes!("../test/pkcs1.pem.pub"); + let pkey = PKey::public_key_from_pem(expected).unwrap(); + + // Serialise public key to PEM + let pem = Encoder::new(Selection::PublicKey) + .set_format(KeyFormat::Pem) + .set_structure(Structure::PKCS1) + .encode(&pkey) + .unwrap(); + + // We should end up with the same PEM as the input + assert_eq!( + from_utf8(&pem).unwrap(), + from_utf8(expected).unwrap().replace("\r\n", "\n") + ); + } + + #[test] + fn test_rsa_der() { + let expected = include_bytes!("../test/key.der.pub"); + let pkey = PKey::public_key_from_der(expected).unwrap(); + + // Serialise public key to DER + let der = Encoder::new(Selection::PublicKey) + .set_format(KeyFormat::Der) + .set_structure(Structure::SubjectPublicKeyInfo) + .encode(&pkey) + .unwrap(); + + // We should end up with the same DER as the input + assert_eq!(der, expected); + } + + #[test] + fn test_rsa_der_pkcs1() { + let expected = include_bytes!("../test/rsa.pem.pub"); + let pkey = PKey::public_key_from_pem(expected).unwrap(); + + // Serialise public key to DER + let der = Encoder::new(Selection::PublicKey) + .set_format(KeyFormat::Der) + .set_structure(Structure::PKCS1) + .encode(&pkey) + .unwrap(); + + // We should be able to load the DER back into a key + let der_key = Rsa::public_key_from_der_pkcs1(&der).unwrap(); + assert_eq!(der_key.n(), pkey.rsa().unwrap().n()); + assert_eq!(der_key.e(), pkey.rsa().unwrap().e()); + } + } + + mod public_from_private { + use super::*; + + #[test] + fn test_rsa_pem() { + let pkey = PKey::private_key_from_pem(include_bytes!("../test/rsa.pem")).unwrap(); + + // Serialise the public key to PEM + let pem = Encoder::new(Selection::PublicKey) + .set_format(KeyFormat::Pem) + .set_structure(Structure::SubjectPublicKeyInfo) + .encode(&pkey) + .unwrap(); + + // Check that we have a public key PEM, and that we can load it back + let pem_str = from_utf8(&pem).unwrap(); + assert!(pem_str.contains("-----BEGIN PUBLIC KEY-----"), "{pem_str}"); + + let pem_key = Rsa::public_key_from_pem(&pem).unwrap(); + assert_eq!(pem_key.n(), pkey.rsa().unwrap().n()); + assert_eq!(pem_key.e(), pkey.rsa().unwrap().e()); + } + + #[test] + fn test_rsa_pem_pkcs1() { + let pkey = PKey::private_key_from_pem(include_bytes!("../test/rsa.pem")).unwrap(); + + // Serialise the public key to PEM + let pem = Encoder::new(Selection::PublicKey) + .set_format(KeyFormat::Pem) + .set_structure(Structure::PKCS1) + .encode(&pkey) + .unwrap(); + + // Check that we have a public key PEM, and that we can load it back + let pem_str = from_utf8(&pem).unwrap(); + assert!( + pem_str.contains("-----BEGIN RSA PUBLIC KEY-----"), + "{pem_str}" + ); + + let pem_key = Rsa::public_key_from_pem_pkcs1(&pem).unwrap(); + assert_eq!(pem_key.n(), pkey.rsa().unwrap().n()); + assert_eq!(pem_key.e(), pkey.rsa().unwrap().e()); + } + + #[test] + fn test_rsa_der() { + let pkey = PKey::private_key_from_pem(include_bytes!("../test/rsa.pem")).unwrap(); + + // Serialise the public key to DER + let der = Encoder::new(Selection::PublicKey) + .set_format(KeyFormat::Der) + .set_structure(Structure::SubjectPublicKeyInfo) + .encode(&pkey) + .unwrap(); + + // DER is not valid UTF-8, so we can't convert it to a string + assert!(from_utf8(&der).is_err()); + + // We should be able to load the DER back into a key + let der_key = Rsa::public_key_from_der(&der).unwrap(); + assert_eq!(der_key.n(), pkey.rsa().unwrap().n()); + assert_eq!(der_key.e(), pkey.rsa().unwrap().e()); + } + + #[test] + fn test_rsa_der_pkcs1() { + let pkey = PKey::private_key_from_pem(include_bytes!("../test/rsa.pem")).unwrap(); + + // Serialise the public key to DER + let der = Encoder::new(Selection::PublicKey) + .set_format(KeyFormat::Der) + .set_structure(Structure::PKCS1) + .encode(&pkey) + .unwrap(); + + // DER is not valid UTF-8, so we can't convert it to a string + assert!(from_utf8(&der).is_err()); + + // We should be able to load the DER back into a key + let der_key = Rsa::public_key_from_der_pkcs1(&der).unwrap(); + assert_eq!(der_key.n(), pkey.rsa().unwrap().n()); + assert_eq!(der_key.e(), pkey.rsa().unwrap().e()); + } + } + + mod private { + use super::*; + + #[test] + fn test_rsa_pem() { + let expected = include_bytes!("../test/rsa.pem"); + let pkey = PKey::private_key_from_pem(expected).unwrap(); + + // Serialise private key to PEM + let pem = Encoder::new(Selection::Keypair) + .set_format(KeyFormat::Pem) + .set_structure(Structure::PKCS1) + .encode(&pkey) + .unwrap(); + + assert_eq!( + from_utf8(&pem).unwrap(), + from_utf8(expected).unwrap().replace("\r\n", "\n") + ); + } + + #[test] + fn test_rsa_pem_encrypted() { + let pkey = PKey::private_key_from_pem(include_bytes!("../test/rsa.pem")).unwrap(); + + // Serialise private to an encrypted PEM + let passphrase = b"hunter2"; + let pem = Encoder::new(Selection::Keypair) + .set_format(KeyFormat::Pem) + .set_cipher(Cipher::aes_256_cbc()) + .set_passphrase(passphrase) + .encode(&pkey) + .unwrap(); + + // Check that we have an encrypted PEM + let pem_str = from_utf8(&pem).unwrap(); + assert!(pem_str.contains("ENCRYPTED"), "{pem_str}"); + + // Check that we can load the PEM back into a key + let pkey2 = + Rsa::private_key_from_pem_passphrase(pem.as_slice(), passphrase).unwrap(); + assert_eq!(pkey2.p(), pkey.rsa().unwrap().p()); + assert_eq!(pkey2.q(), pkey.rsa().unwrap().q()); + assert_eq!(pkey2.d(), pkey.rsa().unwrap().d()); + } + + #[test] + fn test_rsa_der() { + let expected = include_bytes!("../test/rsa.der"); + let pkey = PKey::private_key_from_der(expected).unwrap(); + + // Serialise private key to DER + let der = Encoder::new(Selection::Keypair) + .set_format(KeyFormat::Der) + .encode(&pkey) + .unwrap(); + + assert_eq!(der, expected); + } + } + } +} diff --git a/openssl/test/dhparams.der b/openssl/test/dhparams.der new file mode 100644 index 0000000000000000000000000000000000000000..bf0cd0cb4b7b5d22fe666647857047968ee8de00 GIT binary patch literal 268 zcmV+n0rUPaf&mBuf&l>lhj3oD|Dk`F&9Lc)T~8>+-~kU{e6LKD@Ks=raG3=qXzN;u z&<&j@s{E~jn~vHgp0-W>UI;xb*uNXA9lN}pxP@0JPRS$OgpGx}<`evT5_|m&AJ*1@ zl+Dg-ty+w|R~uF=DVLn&nH}nPF`Zd9DW{{Nq1vQ7RGReop?n##tf1#Od|)_3kDf%+jL|~CCGf#5$i6m1z_NC00s#Wg9DQ;C literal 0 HcmV?d00001 diff --git a/openssl/test/pkcs1.der.pub b/openssl/test/pkcs1.der.pub new file mode 100644 index 0000000000000000000000000000000000000000..0e54c50f4675fd590a05339350e8edeb2d0966db GIT binary patch literal 270 zcmV+p0rCDYf&mHwf&l>l%C{fxTlm-zsE9XrEs2Hk=7u|p;h1d3f-j9pnlO+{i-so0 z83|q%Mndi#RVvI%77V)=ABJFgFZAN(^nPD)8b!M_AlRJiko{EM!SUEEZl9&#tyANb{yxF z&y9T7yfPq;-`{Euq8GydZ5`UW0!ef@OU&DxN5DB9%g?lhXy;UV-&SfcD1LRD;LqH~ z34AQQDKh>eiA-~M!g_34;DF67VkF1oN53~s`}(nAY}e68?vc;L5I1jUr##O}2Qm#9 U>2lHIIEP2!`A}uG0s{d60V5BH-v9sr literal 0 HcmV?d00001 diff --git a/openssl/test/rsa.der b/openssl/test/rsa.der new file mode 100644 index 0000000000000000000000000000000000000000..500c05fafcad453788d4e3d594a75426771b2cd6 GIT binary patch literal 1191 zcmV;Y1X%kpf&`-i0RRGm0RaG^_!bJ{{dU0|5X50)hbm60UKiY0U~N!+uh1MF3YH9bt7HWa&w8dtzA+ zoeQOWmOMZnKhG~L)4lA7z}w7f#Jlx@?DZg^iarU zl6h$7b2RipPj#nI;W<=pI*N0h0)c@5;2gvd>qw?$T~tO*3Z%Pjpty3kbf&4%AO1)O zc<|k~F&ci5F8Rc@_f&Q!E2tN2vsroY6gMgwC@JIzC=Mmk(ZB>Tyr}}m3gp|R6gW$GTLinK7=RdO9aPS#N0)c@5 zxdX(036-`o^iwRc^TC-EU0*us$KUSUdXlEJeksNqLZEu7T4elS37N&GupR-xjJ7PN zR#JPs#b9{*3#+B=INmJ>nGg0b53a52Yy+B?zaCJ2PH;f_c-w+k$gvXbURlG<2`}Z; z<+T!Z)DnRQDqeVAy7SuOaFPQPKT%oN0)c=B0-slKB&(my6_CYbDc1W8<@g||^L40Z z`O8B9lXyG)yE$$}Ddz7|H$WoYhPs5=?^u%??#0kwr9aOjwlnxsik|IJ=z9rxFr%Mc zl9PMp0!vlfHuvDboy)f=bvS=vTKl%kHoL3mc|0ov&Z}nIxiPx+3svlz`7s>;9b%0F zfq?*r|9bjjwMYKomK!dc&0S|0)18c`J+^5dIGK&-hjDf+U8an}L!~%77HhIS#c=3H z2FidQ_4c~vBWen-qjwIERMDu~9AAwnnec)$9or9Fk3IkTOpTWiE86}DDiMvQV>yV3 z4og*8oaTD+Zoj3n!AhOyvc%n%tKHJlB20Axfq)^0dkk}-t!wd<1JJP(Hniv~jkha) z`%oq-UHCa%$0csK&ul=qV2xQeqb<4@K_;aq1}?3PqXqkZ7k@yMH_6<&agNv^7`*Qf zcOas~b@|&S+73Lm7SM$2B^Qo@ja;kLo3j=gz!)E=_#x;HdeeC}`IInJtqCb1?krg@ FLv^ygK$-vm literal 0 HcmV?d00001 From f85c60e09253f382be101cead446996e9bbf3b63 Mon Sep 17 00:00:00 2001 From: Huw Jones Date: Fri, 15 Aug 2025 15:34:48 +0100 Subject: [PATCH 16/42] pkey/Id: implement TryInto<&str> --- openssl/src/pkey.rs | 65 +++++++++++++++++++++++++-------------------- 1 file changed, 36 insertions(+), 29 deletions(-) diff --git a/openssl/src/pkey.rs b/openssl/src/pkey.rs index 0fa568986..ce50422b1 100644 --- a/openssl/src/pkey.rs +++ b/openssl/src/pkey.rs @@ -123,6 +123,41 @@ impl Id { } } +impl TryFrom for &'static str { + type Error = (); + fn try_from(id: Id) -> Result { + match id { + Id::RSA => Ok("RSA"), + #[cfg(any(ossl111, libressl310, boringssl, awslc))] + Id::RSA_PSS => Ok("RSA-PSS"), + #[cfg(not(boringssl))] + Id::HMAC => Ok("HMAC"), + #[cfg(not(any(boringssl, awslc)))] + Id::CMAC => Ok("CMAC"), + Id::DSA => Ok("DSA"), + Id::DH => Ok("DH"), + #[cfg(ossl110)] + Id::DHX => Ok("DHX"), + Id::EC => Ok("EC"), + #[cfg(ossl111)] + Id::SM2 => Ok("SM2"), + #[cfg(any(ossl110, boringssl, libressl360, awslc))] + Id::HKDF => Ok("HKDF"), + #[cfg(any(ossl111, boringssl, libressl370, awslc))] + Id::ED25519 => Ok("Ed25519"), + #[cfg(ossl111)] + Id::ED448 => Ok("Ed448"), + #[cfg(any(ossl111, boringssl, libressl370, awslc))] + Id::X25519 => Ok("X25519"), + #[cfg(ossl111)] + Id::X448 => Ok("X448"), + #[cfg(ossl111)] + Id::POLY1305 => Ok("POLY1305"), + _ => Err(()), + } + } +} + /// A trait indicating that a key has parameters. pub unsafe trait HasParams {} @@ -398,35 +433,7 @@ where impl fmt::Debug for PKey { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - let alg = match self.id() { - Id::RSA => "RSA", - #[cfg(any(ossl111, libressl310, boringssl, awslc))] - Id::RSA_PSS => "RSA-PSS", - #[cfg(not(boringssl))] - Id::HMAC => "HMAC", - #[cfg(not(any(boringssl, awslc)))] - Id::CMAC => "CMAC", - Id::DSA => "DSA", - Id::DH => "DH", - #[cfg(ossl110)] - Id::DHX => "DHX", - Id::EC => "EC", - #[cfg(ossl111)] - Id::SM2 => "SM2", - #[cfg(any(ossl110, boringssl, libressl360, awslc))] - Id::HKDF => "HKDF", - #[cfg(any(ossl111, boringssl, libressl370, awslc))] - Id::ED25519 => "Ed25519", - #[cfg(ossl111)] - Id::ED448 => "Ed448", - #[cfg(any(ossl111, boringssl, libressl370, awslc))] - Id::X25519 => "X25519", - #[cfg(ossl111)] - Id::X448 => "X448", - #[cfg(ossl111)] - Id::POLY1305 => "POLY1305", - _ => "unknown", - }; + let alg = self.id().try_into().unwrap_or("unknown"); fmt.debug_struct("PKey").field("algorithm", &alg).finish() // TODO: Print details for each specific type of key } From 0d973f6a8adcf68de0c331d10266a9abf5271cf8 Mon Sep 17 00:00:00 2001 From: Huw Jones Date: Fri, 15 Aug 2025 15:35:07 +0100 Subject: [PATCH 17/42] pkey/Id: implement TryInto<&CStr> --- openssl/src/pkey.rs | 64 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 63 insertions(+), 1 deletion(-) diff --git a/openssl/src/pkey.rs b/openssl/src/pkey.rs index ce50422b1..3a19346a5 100644 --- a/openssl/src/pkey.rs +++ b/openssl/src/pkey.rs @@ -62,7 +62,7 @@ use foreign_types::{ForeignType, ForeignTypeRef}; use libc::{c_int, c_long}; use openssl_macros::corresponds; use std::convert::{TryFrom, TryInto}; -use std::ffi::CString; +use std::ffi::{CStr, CString}; use std::fmt; #[cfg(all(not(any(boringssl, awslc)), ossl110))] use std::mem; @@ -158,6 +158,68 @@ impl TryFrom for &'static str { } } +cstr_const!(ID_RSA, b"RSA\0"); +#[cfg(any(ossl111, libressl310, boringssl, awslc))] +cstr_const!(ID_RSA_PSS, b"RSA-PSS\0"); +#[cfg(not(boringssl))] +cstr_const!(ID_HMAC, b"HMAC\0"); +#[cfg(not(any(boringssl, awslc)))] +cstr_const!(ID_CMAC, b"CMAC\0"); +cstr_const!(ID_DSA, b"DSA\0"); +cstr_const!(ID_DH, b"DH\0"); +#[cfg(ossl110)] +cstr_const!(ID_DHX, b"DHX\0"); +cstr_const!(ID_EC, b"EC\0"); +#[cfg(ossl111)] +cstr_const!(ID_SM2, b"SM2\0"); +#[cfg(any(ossl110, boringssl, libressl360, awslc))] +cstr_const!(ID_HKDF, b"HKDF\0"); +#[cfg(any(ossl111, boringssl, libressl370, awslc))] +cstr_const!(ID_ED25519, b"Ed25519\0"); +#[cfg(ossl111)] +cstr_const!(ID_ED448, b"Ed448\0"); +#[cfg(any(ossl111, boringssl, libressl370, awslc))] +cstr_const!(ID_X25519, b"X25519\0"); +#[cfg(ossl111)] +cstr_const!(ID_X448, b"X448\0"); +#[cfg(ossl111)] +cstr_const!(ID_POLY1305, b"POLY1305\0"); + +impl TryFrom for &'static CStr { + type Error = (); + fn try_from(id: Id) -> Result { + match id { + Id::RSA => Ok(ID_RSA), + #[cfg(any(ossl111, libressl310, boringssl, awslc))] + Id::RSA_PSS => Ok(ID_RSA_PSS), + #[cfg(not(boringssl))] + Id::HMAC => Ok(ID_HMAC), + #[cfg(not(any(boringssl, awslc)))] + Id::CMAC => Ok(ID_CMAC), + Id::DSA => Ok(ID_DSA), + Id::DH => Ok(ID_DH), + #[cfg(ossl110)] + Id::DHX => Ok(ID_DHX), + Id::EC => Ok(ID_EC), + #[cfg(ossl111)] + Id::SM2 => Ok(ID_SM2), + #[cfg(any(ossl110, boringssl, libressl360, awslc))] + Id::HKDF => Ok(ID_HKDF), + #[cfg(any(ossl111, boringssl, libressl370, awslc))] + Id::ED25519 => Ok(ID_ED25519), + #[cfg(ossl111)] + Id::ED448 => Ok(ID_ED448), + #[cfg(any(ossl111, boringssl, libressl370, awslc))] + Id::X25519 => Ok(ID_X25519), + #[cfg(ossl111)] + Id::X448 => Ok(ID_X448), + #[cfg(ossl111)] + Id::POLY1305 => Ok(ID_POLY1305), + _ => Err(()), + } + } +} + /// A trait indicating that a key has parameters. pub unsafe trait HasParams {} From 3edbd2eff77eddde11213d6472fce2ea420849bc Mon Sep 17 00:00:00 2001 From: Huw Jones Date: Wed, 13 Aug 2025 22:55:32 +0100 Subject: [PATCH 18/42] Add decoder wrapper --- openssl/src/ossl_encdec.rs | 351 ++++++++++++++++++++++++++++++++++++- 1 file changed, 348 insertions(+), 3 deletions(-) diff --git a/openssl/src/ossl_encdec.rs b/openssl/src/ossl_encdec.rs index 262355fce..27522cec7 100644 --- a/openssl/src/ossl_encdec.rs +++ b/openssl/src/ossl_encdec.rs @@ -1,12 +1,14 @@ -use crate::bio::MemBio; +use crate::bio::{MemBio, MemBioSlice}; use crate::error::ErrorStack; -use crate::pkey::PKeyRef; -use crate::pkey_ctx::Selection; +use crate::pkey::{Id, PKey, PKeyRef}; +use crate::pkey_ctx::{Selection, SelectionT}; use crate::symm::Cipher; +use crate::util::{invoke_passwd_cb, CallbackState}; use crate::{cvt, cvt_p}; use foreign_types::{ForeignType, ForeignTypeRef}; use openssl_macros::corresponds; use std::ffi::{CStr, CString}; +use std::marker::PhantomData; use std::ptr; #[derive(Debug, Copy, Clone, PartialEq, Eq)] @@ -95,6 +97,206 @@ impl<'a> From> for &'a CStr { } } +foreign_type_and_impl_send_sync! { + type CType = ffi::OSSL_DECODER_CTX; + fn drop = ffi::OSSL_DECODER_CTX_free; + + /// A context object which can perform decode operations. + pub struct OsslDecoderCtx; + /// A reference to an `OsslDecoderCtx`. + pub struct OsslDecoderCtxRef; +} + +impl OsslDecoderCtx { + #[corresponds(OSSL_DECODER_CTX_new_for_pkey)] + #[inline] + #[allow(dead_code)] + fn new_for_key( + pkey: *mut *mut ffi::EVP_PKEY, + selection: Selection, + input: Option, + structure: Option>, + key_type: Option, + ) -> Result { + let input_ptr = input + .map(|i| { + let input: &CStr = i.into(); + input.as_ptr() + }) + .unwrap_or_else(ptr::null); + let structure_ptr = structure + .map(|s| { + let structure: &CStr = s.into(); + structure.as_ptr() + }) + .unwrap_or_else(ptr::null); + let key_type_ptr = key_type + .and_then(|k| k.try_into().ok()) + .map(|k: &CStr| k.as_ptr()) + .unwrap_or_else(ptr::null); + unsafe { + let ptr = cvt_p(ffi::OSSL_DECODER_CTX_new_for_pkey( + pkey, + input_ptr, + structure_ptr, + key_type_ptr, + selection.into(), + ptr::null_mut(), + ptr::null(), + ))?; + Ok(Self::from_ptr(ptr)) + } + } +} + +impl OsslDecoderCtxRef { + /// Select which parts of the key to decode. + #[corresponds(OSSL_DECODER_CTX_set_selection)] + #[allow(dead_code)] + fn set_selection(&mut self, selection: Selection) -> Result<(), ErrorStack> { + cvt(unsafe { ffi::OSSL_DECODER_CTX_set_selection(self.as_ptr(), selection.into()) }) + .map(|_| ()) + } + + /// Set the input type for the encoded data. + #[corresponds(OSSL_DECODER_CTX_set_input_type)] + #[allow(dead_code)] + fn set_input_type(&mut self, input: KeyFormat) -> Result<(), ErrorStack> { + let input: &CStr = input.into(); + cvt(unsafe { ffi::OSSL_DECODER_CTX_set_input_type(self.as_ptr(), input.as_ptr()) }) + .map(|_| ()) + } + + /// Set the input structure for the encoded data. + #[corresponds(OSSL_DECODER_CTX_set_input_structure)] + #[allow(dead_code)] + fn set_input_structure(&mut self, structure: Structure<'_>) -> Result<(), ErrorStack> { + let structure: &CStr = structure.into(); + cvt(unsafe { ffi::OSSL_DECODER_CTX_set_input_structure(self.as_ptr(), structure.as_ptr()) }) + .map(|_| ()) + } + + /// Set the passphrase to decrypt the encoded data. + #[corresponds(OSSL_DECODER_CTX_set_passphrase)] + #[allow(dead_code)] + fn set_passphrase(&mut self, passphrase: &[u8]) -> Result<(), ErrorStack> { + cvt(unsafe { + ffi::OSSL_DECODER_CTX_set_passphrase( + self.as_ptr(), + passphrase.as_ptr().cast(), + passphrase.len(), + ) + }) + .map(|_| ()) + } + + /// Set the passphrase to decrypt the encoded data. + #[corresponds(OSSL_DECODER_CTX_set_passphrase)] + #[allow(dead_code)] + unsafe fn set_passphrase_callback Result>( + &mut self, + callback: *mut CallbackState, + ) -> Result<(), ErrorStack> { + cvt(unsafe { + ffi::OSSL_DECODER_CTX_set_pem_password_cb( + self.as_ptr(), + Some(invoke_passwd_cb::), + callback as *mut _, + ) + }) + .map(|_| ()) + } + + /// Decode the encoded data + #[corresponds(OSSL_DECODER_from_bio)] + #[allow(dead_code)] + fn decode(&mut self, data: &[u8]) -> Result<(), ErrorStack> { + let bio = MemBioSlice::new(data)?; + + cvt(unsafe { ffi::OSSL_DECODER_from_bio(self.as_ptr(), bio.as_ptr()) }).map(|_| ()) + } +} + +#[allow(dead_code)] +pub(crate) struct Decoder<'a, T: SelectionT> { + selection: PhantomData, + key_type: Option, + format: Option, + structure: Option>, + passphrase: Option<&'a [u8]>, + #[allow(clippy::type_complexity)] + passphrase_callback: Option Result + 'a>>, +} + +impl<'a, T: SelectionT> Decoder<'a, T> { + #[allow(dead_code)] + pub(crate) fn new() -> Self { + Self { + selection: PhantomData, + key_type: None, + format: None, + structure: None, + passphrase: None, + passphrase_callback: None, + } + } + + #[allow(dead_code)] + pub fn set_key_type(mut self, key_type: Id) -> Self { + self.key_type = Some(key_type); + self + } + + #[allow(dead_code)] + pub fn set_format(mut self, format: KeyFormat) -> Self { + self.format = Some(format); + self + } + + #[allow(dead_code)] + pub fn set_structure(mut self, structure: Structure<'a>) -> Self { + self.structure = Some(structure); + self + } + + #[allow(dead_code)] + pub fn set_passphrase(mut self, passphrase: &'a [u8]) -> Self { + self.passphrase = Some(passphrase); + self + } + + #[allow(dead_code)] + pub fn set_passphrase_callback Result + 'a>( + mut self, + callback: F, + ) -> Self { + self.passphrase_callback = Some(Box::new(callback)); + self + } + + #[allow(dead_code)] + pub fn decode(self, data: &[u8]) -> Result, ErrorStack> { + let mut pkey_ptr = ptr::null_mut(); + let mut passphrase_callback_state; + let mut ctx = OsslDecoderCtx::new_for_key( + &mut pkey_ptr, + T::SELECTION, + self.format, + self.structure, + self.key_type, + )?; + if let Some(passphrase) = self.passphrase { + ctx.set_passphrase(passphrase)?; + } + if let Some(passphrase_callback) = self.passphrase_callback { + passphrase_callback_state = CallbackState::new(passphrase_callback); + unsafe { ctx.set_passphrase_callback(&mut passphrase_callback_state)? }; + } + ctx.decode(data)?; + Ok(unsafe { PKey::from_ptr(pkey_ptr) }) + } +} + foreign_type_and_impl_send_sync! { type CType = ffi::OSSL_ENCODER_CTX; fn drop = ffi::OSSL_ENCODER_CTX_free; @@ -308,6 +510,149 @@ mod test { } } + mod decoder { + use super::*; + + mod params { + use super::*; + use crate::pkey::Params; + + #[test] + fn test_dh_pem() { + Decoder::::new() + .set_key_type(Id::DH) + .set_format(KeyFormat::Pem) + .set_structure(Structure::TypeSpecific) + .decode(include_bytes!("../test/dhparams.pem")) + .unwrap() + .dh() + .unwrap(); + } + + #[test] + fn test_dh_der() { + Decoder::::new() + .set_key_type(Id::DH) + .set_format(KeyFormat::Der) + .set_structure(Structure::TypeSpecific) + .decode(include_bytes!("../test/dhparams.der")) + .unwrap() + .dh() + .unwrap(); + } + } + mod public { + use super::*; + use crate::pkey::Public; + + #[test] + fn test_rsa_pem() { + Decoder::::new() + .set_key_type(Id::RSA) + .set_format(KeyFormat::Pem) + .set_structure(Structure::SubjectPublicKeyInfo) + .decode(include_bytes!("../test/rsa.pem.pub")) + .unwrap() + .rsa() + .unwrap(); + } + + #[test] + fn test_rsa_pem_pkcs1() { + Decoder::::new() + .set_key_type(Id::RSA) + .set_format(KeyFormat::Pem) + .set_structure(Structure::PKCS1) + .decode(include_bytes!("../test/pkcs1.pem.pub")) + .unwrap() + .rsa() + .unwrap(); + } + + #[test] + fn test_rsa_der() { + Decoder::::new() + .set_key_type(Id::RSA) + .set_format(KeyFormat::Der) + .set_structure(Structure::SubjectPublicKeyInfo) + .decode(include_bytes!("../test/key.der.pub")) + .unwrap() + .rsa() + .unwrap(); + } + + #[test] + fn test_rsa_der_pkcs1() { + Decoder::::new() + .set_key_type(Id::RSA) + .set_format(KeyFormat::Der) + .set_structure(Structure::PKCS1) + .decode(include_bytes!("../test/pkcs1.der.pub")) + .unwrap() + .rsa() + .unwrap(); + } + } + mod private { + use super::*; + use crate::pkey::Private; + + #[test] + fn test_rsa_pem() { + Decoder::::new() + .set_key_type(Id::RSA) + .set_format(KeyFormat::Pem) + .set_structure(Structure::PKCS1) + .decode(include_bytes!("../test/rsa.pem")) + .unwrap() + .rsa() + .unwrap(); + } + + #[test] + fn test_rsa_pem_passphrase() { + Decoder::::new() + .set_key_type(Id::RSA) + .set_format(KeyFormat::Pem) + .set_structure(Structure::PKCS1) + .set_passphrase(b"mypass") + .decode(include_bytes!("../test/rsa-encrypted.pem")) + .unwrap() + .rsa() + .unwrap(); + } + + #[test] + fn test_rsa_pem_callback() { + let mut password_queried = false; + Decoder::::new() + .set_key_type(Id::RSA) + .set_format(KeyFormat::Pem) + .set_structure(Structure::PKCS1) + .set_passphrase_callback(|password| { + password_queried = true; + password[..6].copy_from_slice(b"mypass"); + Ok(6) + }) + .decode(include_bytes!("../test/rsa-encrypted.pem")) + .unwrap(); + assert!(password_queried); + } + + #[test] + fn test_rsa_der() { + Decoder::::new() + .set_key_type(Id::RSA) + .set_format(KeyFormat::Der) + .set_structure(Structure::PKCS1) + .decode(include_bytes!("../test/key.der")) + .unwrap() + .rsa() + .unwrap(); + } + } + } + mod encoder { use super::*; From 094a36dc91ccf4b309d278ddd11699fa83b88257 Mon Sep 17 00:00:00 2001 From: Huw Jones Date: Wed, 13 Aug 2025 20:48:54 +0100 Subject: [PATCH 19/42] rsa: improve coverage of de-/serialisation methods * Serialise the test key in several different formats * Compare serialisation output directly against known good (to prevent regressions) * Rename the test keys to have a consistent naming scheme * Add more deserialisation tests covering different input formats --- openssl/src/cipher_ctx.rs | 2 +- openssl/src/envelope.rs | 2 +- openssl/src/rsa.rs | 86 ++++++++++++++++++---- openssl/test/rsa.der | Bin 0 -> 1191 bytes openssl/test/rsa.pem | 55 +++++++------- openssl/test/rsa.pkcs1.der | Bin 0 -> 1191 bytes openssl/test/rsa.pkcs1.pem | 27 +++++++ openssl/test/rsa.pub.der | Bin 0 -> 294 bytes openssl/test/{rsa.pem.pub => rsa.pub.pem} | 0 openssl/test/rsa.pub.pkcs1.der | Bin 0 -> 270 bytes openssl/test/rsa.pub.pkcs1.pem | 8 ++ 11 files changed, 138 insertions(+), 42 deletions(-) create mode 100644 openssl/test/rsa.der create mode 100644 openssl/test/rsa.pkcs1.der create mode 100644 openssl/test/rsa.pkcs1.pem create mode 100644 openssl/test/rsa.pub.der rename openssl/test/{rsa.pem.pub => rsa.pub.pem} (100%) create mode 100644 openssl/test/rsa.pub.pkcs1.der create mode 100644 openssl/test/rsa.pub.pkcs1.pem diff --git a/openssl/src/cipher_ctx.rs b/openssl/src/cipher_ctx.rs index dc8887239..815af5b87 100644 --- a/openssl/src/cipher_ctx.rs +++ b/openssl/src/cipher_ctx.rs @@ -735,7 +735,7 @@ mod test { #[cfg(not(any(boringssl, awslc)))] fn seal_open() { let private_pem = include_bytes!("../test/rsa.pem"); - let public_pem = include_bytes!("../test/rsa.pem.pub"); + let public_pem = include_bytes!("../test/rsa.pub.pem"); let private_key = PKey::private_key_from_pem(private_pem).unwrap(); let public_key = PKey::public_key_from_pem(public_pem).unwrap(); let cipher = Cipher::aes_256_cbc(); diff --git a/openssl/src/envelope.rs b/openssl/src/envelope.rs index f6ebc722f..f84216f21 100644 --- a/openssl/src/envelope.rs +++ b/openssl/src/envelope.rs @@ -158,7 +158,7 @@ mod test { #[test] fn public_encrypt_private_decrypt() { let private_pem = include_bytes!("../test/rsa.pem"); - let public_pem = include_bytes!("../test/rsa.pem.pub"); + let public_pem = include_bytes!("../test/rsa.pub.pem"); let private_key = PKey::private_key_from_pem(private_pem).unwrap(); let public_key = PKey::public_key_from_pem(public_pem).unwrap(); let cipher = Cipher::aes_256_cbc(); diff --git a/openssl/src/rsa.rs b/openssl/src/rsa.rs index b3adcddad..b2e339bc2 100644 --- a/openssl/src/rsa.rs +++ b/openssl/src/rsa.rs @@ -683,17 +683,37 @@ cfg_if! { #[cfg(test)] mod test { use crate::symm::Cipher; + use std::str::from_utf8; use super::*; #[test] - fn test_from_password() { + fn test_private_key_from_pem() { + Rsa::private_key_from_pem(include_bytes!("../test/rsa.pem")).unwrap(); + } + + #[test] + fn test_private_key_from_pem_pkcs1() { + Rsa::private_key_from_pem(include_bytes!("../test/rsa.pkcs1.pem")).unwrap(); + } + #[test] + fn test_private_key_from_der() { + Rsa::private_key_from_der(include_bytes!("../test/rsa.der")).unwrap(); + } + + #[test] + fn test_private_key_from_der_pkcs1() { + Rsa::private_key_from_der(include_bytes!("../test/rsa.pkcs1.der")).unwrap(); + } + + #[test] + fn test_private_key_from_pem_password() { let key = include_bytes!("../test/rsa-encrypted.pem"); Rsa::private_key_from_pem_passphrase(key, b"mypass").unwrap(); } #[test] - fn test_from_password_callback() { + fn test_private_key_from_pem_callback() { let mut password_queried = false; let key = include_bytes!("../test/rsa-encrypted.pem"); Rsa::private_key_from_pem_callback(key, |password| { @@ -707,8 +727,18 @@ mod test { } #[test] - fn test_to_password() { - let key = Rsa::generate(2048).unwrap(); + fn test_private_key_to_pem() { + let key = Rsa::private_key_from_der(include_bytes!("../test/rsa.der")).unwrap(); + let pem = key.private_key_to_pem().unwrap(); + assert_eq!( + from_utf8(&pem).unwrap(), + include_str!("../test/rsa.pkcs1.pem").replace("\r\n", "\n") + ); + } + + #[test] + fn test_private_key_to_pem_password() { + let key = Rsa::private_key_from_der(include_bytes!("../test/rsa.der")).unwrap(); let pem = key .private_key_to_pem_passphrase(Cipher::aes_128_cbc(), b"foobar") .unwrap(); @@ -716,9 +746,16 @@ mod test { assert!(Rsa::private_key_from_pem_passphrase(&pem, b"fizzbuzz").is_err()); } + #[test] + fn test_private_key_to_der_pkcs1() { + let key = super::Rsa::private_key_from_pem(include_bytes!("../test/rsa.pem")).unwrap(); + let der = key.private_key_to_der().unwrap(); + assert_eq!(der, include_bytes!("../test/rsa.pkcs1.der")); + } + #[test] fn test_public_encrypt_private_decrypt_with_padding() { - let key = include_bytes!("../test/rsa.pem.pub"); + let key = include_bytes!("../test/rsa.pub.pem"); let public_key = Rsa::public_key_from_pem(key).unwrap(); let mut result = vec![0; public_key.size() as usize]; @@ -780,25 +817,48 @@ mod test { } #[test] - #[should_panic] fn test_public_key_from_pem_pkcs1_file_panic() { let key = include_bytes!("../test/key.pem.pub"); - Rsa::public_key_from_pem_pkcs1(key).unwrap(); + assert!(Rsa::public_key_from_pem_pkcs1(key).is_err()); } #[test] fn test_public_key_to_pem_pkcs1() { - let keypair = super::Rsa::generate(512).unwrap(); + let keypair = super::Rsa::private_key_from_der(include_bytes!("../test/rsa.der")).unwrap(); let pubkey_pem = keypair.public_key_to_pem_pkcs1().unwrap(); - super::Rsa::public_key_from_pem_pkcs1(&pubkey_pem).unwrap(); + assert_eq!( + from_utf8(&pubkey_pem).unwrap(), + include_str!("../test/rsa.pub.pkcs1.pem").replace("\r\n", "\n") + ); } #[test] - #[should_panic] - fn test_public_key_from_pem_pkcs1_generate_panic() { - let keypair = super::Rsa::generate(512).unwrap(); + fn test_public_key_to_pem() { + let keypair = super::Rsa::private_key_from_der(include_bytes!("../test/rsa.der")).unwrap(); let pubkey_pem = keypair.public_key_to_pem().unwrap(); - super::Rsa::public_key_from_pem_pkcs1(&pubkey_pem).unwrap(); + assert_eq!( + from_utf8(&pubkey_pem).unwrap(), + include_str!("../test/rsa.pub.pem").replace("\r\n", "\n") + ); + } + + #[test] + fn test_public_key_to_der() { + let keypair = super::Rsa::private_key_from_pem(include_bytes!("../test/rsa.pem")).unwrap(); + let pubkey_der = keypair.public_key_to_der().unwrap(); + assert_eq!(pubkey_der, include_bytes!("../test/rsa.pub.der")); + } + + #[test] + fn test_public_key_to_der_pkcs1() { + let keypair = super::Rsa::private_key_from_pem(include_bytes!("../test/rsa.pem")).unwrap(); + let pubkey_der = keypair.public_key_to_der_pkcs1().unwrap(); + assert_eq!(pubkey_der, include_bytes!("../test/rsa.pub.pkcs1.der")); + } + + #[test] + fn test_public_key_from_pem_pkcs1_generate_panic() { + assert!(Rsa::public_key_from_der_pkcs1(include_bytes!("../test/rsa.pub.der")).is_err()); } #[test] diff --git a/openssl/test/rsa.der b/openssl/test/rsa.der new file mode 100644 index 0000000000000000000000000000000000000000..500c05fafcad453788d4e3d594a75426771b2cd6 GIT binary patch literal 1191 zcmV;Y1X%kpf&`-i0RRGm0RaG^_!bJ{{dU0|5X50)hbm60UKiY0U~N!+uh1MF3YH9bt7HWa&w8dtzA+ zoeQOWmOMZnKhG~L)4lA7z}w7f#Jlx@?DZg^iarU zl6h$7b2RipPj#nI;W<=pI*N0h0)c@5;2gvd>qw?$T~tO*3Z%Pjpty3kbf&4%AO1)O zc<|k~F&ci5F8Rc@_f&Q!E2tN2vsroY6gMgwC@JIzC=Mmk(ZB>Tyr}}m3gp|R6gW$GTLinK7=RdO9aPS#N0)c@5 zxdX(036-`o^iwRc^TC-EU0*us$KUSUdXlEJeksNqLZEu7T4elS37N&GupR-xjJ7PN zR#JPs#b9{*3#+B=INmJ>nGg0b53a52Yy+B?zaCJ2PH;f_c-w+k$gvXbURlG<2`}Z; z<+T!Z)DnRQDqeVAy7SuOaFPQPKT%oN0)c=B0-slKB&(my6_CYbDc1W8<@g||^L40Z z`O8B9lXyG)yE$$}Ddz7|H$WoYhPs5=?^u%??#0kwr9aOjwlnxsik|IJ=z9rxFr%Mc zl9PMp0!vlfHuvDboy)f=bvS=vTKl%kHoL3mc|0ov&Z}nIxiPx+3svlz`7s>;9b%0F zfq?*r|9bjjwMYKomK!dc&0S|0)18c`J+^5dIGK&-hjDf+U8an}L!~%77HhIS#c=3H z2FidQ_4c~vBWen-qjwIERMDu~9AAwnnec)$9or9Fk3IkTOpTWiE86}DDiMvQV>yV3 z4og*8oaTD+Zoj3n!AhOyvc%n%tKHJlB20Axfq)^0dkk}-t!wd<1JJP(Hniv~jkha) z`%oq-UHCa%$0csK&ul=qV2xQeqb<4@K_;aq1}?3PqXqkZ7k@yMH_6<&agNv^7`*Qf zcOas~b@|&S+73Lm7SM$2B^Qo@ja;kLo3j=gz!)E=_#x;HdeeC}`IInJtqCb1?krg@ FLv^ygK$-vm literal 0 HcmV?d00001 diff --git a/openssl/test/rsa.pem b/openssl/test/rsa.pem index d8185fed6..7c0f6fd1e 100644 --- a/openssl/test/rsa.pem +++ b/openssl/test/rsa.pem @@ -1,27 +1,28 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEowIBAAKCAQEAofgWCuLjybRlzo0tZWJjNiuSfb4p4fAkd/wWJcyQoTbji9k0 -l8W26mPddxHmfHQp+Vaw+4qPCJrcS2mJPMEzP1Pt0Bm4d4QlL+yRT+SFd2lZS+pC -gNMsD1W/YpRPEwOWvG6b32690r2jZ47soMZo9wGzjb/7OMg0LOL+bSf63kpaSHSX -ndS5z5rexMdbBYUsLA9e+KXBdQOS+UTo7WTBEMa2R2CapHg665xsmtdVMTBQY4uD -Zlxvb3qCo5ZwKh9kG4LT6/I5IhlJH7aGhyxXFvUK+DWNmoudF8NAco9/h9iaGNj8 -q2ethFkMLs91kzk2PAcDTW9gb54h4FRWyuXpoQIDAQABAoIBABKucaRpzQorw35S -bEUAVx8dYXUdZOlJcHtiWQ+dC6V8ljxAHj/PLyzTveyI5QO/xkObCyjIL303l2cf -UhPu2MFaJdjVzqACXuOrLot/eSFvxjvqVidTtAZExqFRJ9mylUVAoLvhowVWmC1O -n95fZCXxTUtxNEG1Xcc7m0rtzJKs45J+N/V9DP1edYH6USyPSWGp6wuA+KgHRnKK -Vf9GRx80JQY7nVNkL17eHoTWEwga+lwi0FEoW9Y7lDtWXYmKBWhUE+U8PGxlJf8f -40493HDw1WRQ/aSLoS4QTp3rn7gYgeHEvfJdkkf0UMhlknlo53M09EFPdadQ4TlU -bjqKc50CgYEA4BzEEOtIpmVdVEZNCqS7baC4crd0pqnRH/5IB3jw3bcxGn6QLvnE -tfdUdiYrqBdss1l58BQ3KhooKeQTa9AB0Hw/Py5PJdTJNPY8cQn7ouZ2KKDcmnPG -BY5t7yLc1QlQ5xHdwW1VhvKn+nXqhJTBgIPgtldC+KDV5z+y2XDwGUcCgYEAuQPE -fgmVtjL0Uyyx88GZFF1fOunH3+7cepKmtH4pxhtCoHqpWmT8YAmZxaewHgHAjLYs -p1ZSe7zFYHj7C6ul7TjeLQeZD/YwD66t62wDmpe/HlB+TnBA+njbglfIsRLtXlnD -zQkv5dTltRJ11BKBBypeeF6689rjcJIDEz9RWdcCgYAHAp9XcCSrn8wVkMVkKdb7 -DOX4IKjzdahm+ctDAJN4O/y7OW5FKebvUjdAIt2GuoTZ71iTG+7F0F+lP88jtjP4 -U4qe7VHoewl4MKOfXZKTe+YCS1XbNvfgwJ3Ltyl1OH9hWvu2yza7q+d5PCsDzqtm -27kxuvULVeya+TEdAB1ijQKBgQCH/3r6YrVH/uCWGy6bzV1nGNOdjKc9tmkfOJmN -54dxdixdpozCQ6U4OxZrsj3FcOhHBsqAHvX2uuYjagqvo3cOj1TRqNocX40omfCC -Mx3bD1yPPf/6TI2XECva/ggqEY2mYzmIiA5LVVmc5nrybr+lssFKneeyxN2Wq93S -0iJMdQKBgCGHewxzoa1r8ZMD0LETNrToK423K377UCYqXfg5XMclbrjPbEC3YI1Z -NqMtuhdBJqUnBi6tjKMF+34Xf0CUN8ncuXGO2CAYvO8PdyCixHX52ybaDjy1FtCE -6yUXjoKNXKvUm7MWGsAYH6f4IegOetN5NvmUMFStCSkh7ixZLkN1 ------END RSA PRIVATE KEY----- +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCh+BYK4uPJtGXO +jS1lYmM2K5J9vinh8CR3/BYlzJChNuOL2TSXxbbqY913EeZ8dCn5VrD7io8ImtxL +aYk8wTM/U+3QGbh3hCUv7JFP5IV3aVlL6kKA0ywPVb9ilE8TA5a8bpvfbr3SvaNn +juygxmj3AbONv/s4yDQs4v5tJ/reSlpIdJed1LnPmt7Ex1sFhSwsD174pcF1A5L5 +ROjtZMEQxrZHYJqkeDrrnGya11UxMFBji4NmXG9veoKjlnAqH2QbgtPr8jkiGUkf +toaHLFcW9Qr4NY2ai50Xw0Byj3+H2JoY2PyrZ62EWQwuz3WTOTY8BwNNb2BvniHg +VFbK5emhAgMBAAECggEAEq5xpGnNCivDflJsRQBXHx1hdR1k6Ulwe2JZD50LpXyW +PEAeP88vLNO97IjlA7/GQ5sLKMgvfTeXZx9SE+7YwVol2NXOoAJe46sui395IW/G +O+pWJ1O0BkTGoVEn2bKVRUCgu+GjBVaYLU6f3l9kJfFNS3E0QbVdxzubSu3Mkqzj +kn439X0M/V51gfpRLI9JYanrC4D4qAdGcopV/0ZHHzQlBjudU2QvXt4ehNYTCBr6 +XCLQUShb1juUO1ZdiYoFaFQT5Tw8bGUl/x/jTj3ccPDVZFD9pIuhLhBOneufuBiB +4cS98l2SR/RQyGWSeWjnczT0QU91p1DhOVRuOopznQKBgQDgHMQQ60imZV1URk0K +pLttoLhyt3SmqdEf/kgHePDdtzEafpAu+cS191R2JiuoF2yzWXnwFDcqGigp5BNr +0AHQfD8/Lk8l1Mk09jxxCfui5nYooNyac8YFjm3vItzVCVDnEd3BbVWG8qf6deqE +lMGAg+C2V0L4oNXnP7LZcPAZRwKBgQC5A8R+CZW2MvRTLLHzwZkUXV866cff7tx6 +kqa0finGG0KgeqlaZPxgCZnFp7AeAcCMtiynVlJ7vMVgePsLq6XtON4tB5kP9jAP +rq3rbAOal78eUH5OcED6eNuCV8ixEu1eWcPNCS/l1OW1EnXUEoEHKl54Xrrz2uNw +kgMTP1FZ1wKBgAcCn1dwJKufzBWQxWQp1vsM5fggqPN1qGb5y0MAk3g7/Ls5bkUp +5u9SN0Ai3Ya6hNnvWJMb7sXQX6U/zyO2M/hTip7tUeh7CXgwo59dkpN75gJLVds2 +9+DAncu3KXU4f2Fa+7bLNrur53k8KwPOq2bbuTG69QtV7Jr5MR0AHWKNAoGBAIf/ +evpitUf+4JYbLpvNXWcY052Mpz22aR84mY3nh3F2LF2mjMJDpTg7FmuyPcVw6EcG +yoAe9fa65iNqCq+jdw6PVNGo2hxfjSiZ8IIzHdsPXI89//pMjZcQK9r+CCoRjaZj +OYiIDktVWZzmevJuv6WywUqd57LE3Zar3dLSIkx1AoGAIYd7DHOhrWvxkwPQsRM2 +tOgrjbcrfvtQJipd+DlcxyVuuM9sQLdgjVk2oy26F0EmpScGLq2MowX7fhd/QJQ3 +ydy5cY7YIBi87w93IKLEdfnbJtoOPLUW0ITrJReOgo1cq9SbsxYawBgfp/gh6A56 +03k2+ZQwVK0JKSHuLFkuQ3U= +-----END PRIVATE KEY----- diff --git a/openssl/test/rsa.pkcs1.der b/openssl/test/rsa.pkcs1.der new file mode 100644 index 0000000000000000000000000000000000000000..500c05fafcad453788d4e3d594a75426771b2cd6 GIT binary patch literal 1191 zcmV;Y1X%kpf&`-i0RRGm0RaG^_!bJ{{dU0|5X50)hbm60UKiY0U~N!+uh1MF3YH9bt7HWa&w8dtzA+ zoeQOWmOMZnKhG~L)4lA7z}w7f#Jlx@?DZg^iarU zl6h$7b2RipPj#nI;W<=pI*N0h0)c@5;2gvd>qw?$T~tO*3Z%Pjpty3kbf&4%AO1)O zc<|k~F&ci5F8Rc@_f&Q!E2tN2vsroY6gMgwC@JIzC=Mmk(ZB>Tyr}}m3gp|R6gW$GTLinK7=RdO9aPS#N0)c@5 zxdX(036-`o^iwRc^TC-EU0*us$KUSUdXlEJeksNqLZEu7T4elS37N&GupR-xjJ7PN zR#JPs#b9{*3#+B=INmJ>nGg0b53a52Yy+B?zaCJ2PH;f_c-w+k$gvXbURlG<2`}Z; z<+T!Z)DnRQDqeVAy7SuOaFPQPKT%oN0)c=B0-slKB&(my6_CYbDc1W8<@g||^L40Z z`O8B9lXyG)yE$$}Ddz7|H$WoYhPs5=?^u%??#0kwr9aOjwlnxsik|IJ=z9rxFr%Mc zl9PMp0!vlfHuvDboy)f=bvS=vTKl%kHoL3mc|0ov&Z}nIxiPx+3svlz`7s>;9b%0F zfq?*r|9bjjwMYKomK!dc&0S|0)18c`J+^5dIGK&-hjDf+U8an}L!~%77HhIS#c=3H z2FidQ_4c~vBWen-qjwIERMDu~9AAwnnec)$9or9Fk3IkTOpTWiE86}DDiMvQV>yV3 z4og*8oaTD+Zoj3n!AhOyvc%n%tKHJlB20Axfq)^0dkk}-t!wd<1JJP(Hniv~jkha) z`%oq-UHCa%$0csK&ul=qV2xQeqb<4@K_;aq1}?3PqXqkZ7k@yMH_6<&agNv^7`*Qf zcOas~b@|&S+73Lm7SM$2B^Qo@ja;kLo3j=gz!)E=_#x;HdeeC}`IInJtqCb1?krg@ FLv^ygK$-vm literal 0 HcmV?d00001 diff --git a/openssl/test/rsa.pkcs1.pem b/openssl/test/rsa.pkcs1.pem new file mode 100644 index 000000000..d8185fed6 --- /dev/null +++ b/openssl/test/rsa.pkcs1.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEAofgWCuLjybRlzo0tZWJjNiuSfb4p4fAkd/wWJcyQoTbji9k0 +l8W26mPddxHmfHQp+Vaw+4qPCJrcS2mJPMEzP1Pt0Bm4d4QlL+yRT+SFd2lZS+pC +gNMsD1W/YpRPEwOWvG6b32690r2jZ47soMZo9wGzjb/7OMg0LOL+bSf63kpaSHSX +ndS5z5rexMdbBYUsLA9e+KXBdQOS+UTo7WTBEMa2R2CapHg665xsmtdVMTBQY4uD +Zlxvb3qCo5ZwKh9kG4LT6/I5IhlJH7aGhyxXFvUK+DWNmoudF8NAco9/h9iaGNj8 +q2ethFkMLs91kzk2PAcDTW9gb54h4FRWyuXpoQIDAQABAoIBABKucaRpzQorw35S +bEUAVx8dYXUdZOlJcHtiWQ+dC6V8ljxAHj/PLyzTveyI5QO/xkObCyjIL303l2cf +UhPu2MFaJdjVzqACXuOrLot/eSFvxjvqVidTtAZExqFRJ9mylUVAoLvhowVWmC1O +n95fZCXxTUtxNEG1Xcc7m0rtzJKs45J+N/V9DP1edYH6USyPSWGp6wuA+KgHRnKK +Vf9GRx80JQY7nVNkL17eHoTWEwga+lwi0FEoW9Y7lDtWXYmKBWhUE+U8PGxlJf8f +40493HDw1WRQ/aSLoS4QTp3rn7gYgeHEvfJdkkf0UMhlknlo53M09EFPdadQ4TlU +bjqKc50CgYEA4BzEEOtIpmVdVEZNCqS7baC4crd0pqnRH/5IB3jw3bcxGn6QLvnE +tfdUdiYrqBdss1l58BQ3KhooKeQTa9AB0Hw/Py5PJdTJNPY8cQn7ouZ2KKDcmnPG +BY5t7yLc1QlQ5xHdwW1VhvKn+nXqhJTBgIPgtldC+KDV5z+y2XDwGUcCgYEAuQPE +fgmVtjL0Uyyx88GZFF1fOunH3+7cepKmtH4pxhtCoHqpWmT8YAmZxaewHgHAjLYs +p1ZSe7zFYHj7C6ul7TjeLQeZD/YwD66t62wDmpe/HlB+TnBA+njbglfIsRLtXlnD +zQkv5dTltRJ11BKBBypeeF6689rjcJIDEz9RWdcCgYAHAp9XcCSrn8wVkMVkKdb7 +DOX4IKjzdahm+ctDAJN4O/y7OW5FKebvUjdAIt2GuoTZ71iTG+7F0F+lP88jtjP4 +U4qe7VHoewl4MKOfXZKTe+YCS1XbNvfgwJ3Ltyl1OH9hWvu2yza7q+d5PCsDzqtm +27kxuvULVeya+TEdAB1ijQKBgQCH/3r6YrVH/uCWGy6bzV1nGNOdjKc9tmkfOJmN +54dxdixdpozCQ6U4OxZrsj3FcOhHBsqAHvX2uuYjagqvo3cOj1TRqNocX40omfCC +Mx3bD1yPPf/6TI2XECva/ggqEY2mYzmIiA5LVVmc5nrybr+lssFKneeyxN2Wq93S +0iJMdQKBgCGHewxzoa1r8ZMD0LETNrToK423K377UCYqXfg5XMclbrjPbEC3YI1Z +NqMtuhdBJqUnBi6tjKMF+34Xf0CUN8ncuXGO2CAYvO8PdyCixHX52ybaDjy1FtCE +6yUXjoKNXKvUm7MWGsAYH6f4IegOetN5NvmUMFStCSkh7ixZLkN1 +-----END RSA PRIVATE KEY----- diff --git a/openssl/test/rsa.pub.der b/openssl/test/rsa.pub.der new file mode 100644 index 0000000000000000000000000000000000000000..242d20b48f675892c17da3509c084e6987c67994 GIT binary patch literal 294 zcmV+>0ondAf&n5h4F(A+hDe6@4FLfG1potr0S^E$f&mHwf&l>lq4*XG;^WD*WzLN) zWnyDCE0TS_DdF%Wcl;J5%#firSxf3dfYU4wRlj1CPZI-{yl$J{ZoSgIqi2rnpvGwT0ke(2 z`#8unEaLucC;Hw>T1a%4oz%I{n%>06TLpzIEDv7zrNMOrlKDjF?PS3a#zr(w*HtkvP-BaOW?XM?dV-^ta4H{U8-mm8@;M?INguX`hb&hX^$Pekjhc&{7sEhu skAH{Qni$ypt7ol*Sqv`Eb(1+ZJO=|!Z(whpA>dS2%H`>y0s{d60ej7dRR910 literal 0 HcmV?d00001 diff --git a/openssl/test/rsa.pem.pub b/openssl/test/rsa.pub.pem similarity index 100% rename from openssl/test/rsa.pem.pub rename to openssl/test/rsa.pub.pem diff --git a/openssl/test/rsa.pub.pkcs1.der b/openssl/test/rsa.pub.pkcs1.der new file mode 100644 index 0000000000000000000000000000000000000000..e8026cdaa549ee0d08cc7c8f86bc83d4d719f924 GIT binary patch literal 270 zcmV+p0rCDYf&mHwf&l>lq4*XG;^WD*WzLN)WnyDCE0TS_DdF%Wcl;J5%#firSxf3dfYU4w zRlj1CPZI-{yl$J{ZoSgIqi2rnpvGwT0ke(2`#8unEaLucC;Hw>T1a%4oz%I{n%>06 zTLpzIEDv7zrNMOrlKDjF?PS3a#zr(w*HtkvP-BaOW?XM?dV-^ta4H{U z8-mm8@;M?INguX`hb&hX^$Pekjhc&{7sEhukAH{Qni$ypt7ol*Sqv`Eb(1+ZJO=|! UZ(whpA>dS2%H`>y0s{d60mDy&6#xJL literal 0 HcmV?d00001 diff --git a/openssl/test/rsa.pub.pkcs1.pem b/openssl/test/rsa.pub.pkcs1.pem new file mode 100644 index 000000000..864ee0c53 --- /dev/null +++ b/openssl/test/rsa.pub.pkcs1.pem @@ -0,0 +1,8 @@ +-----BEGIN RSA PUBLIC KEY----- +MIIBCgKCAQEAofgWCuLjybRlzo0tZWJjNiuSfb4p4fAkd/wWJcyQoTbji9k0l8W2 +6mPddxHmfHQp+Vaw+4qPCJrcS2mJPMEzP1Pt0Bm4d4QlL+yRT+SFd2lZS+pCgNMs +D1W/YpRPEwOWvG6b32690r2jZ47soMZo9wGzjb/7OMg0LOL+bSf63kpaSHSXndS5 +z5rexMdbBYUsLA9e+KXBdQOS+UTo7WTBEMa2R2CapHg665xsmtdVMTBQY4uDZlxv +b3qCo5ZwKh9kG4LT6/I5IhlJH7aGhyxXFvUK+DWNmoudF8NAco9/h9iaGNj8q2et +hFkMLs91kzk2PAcDTW9gb54h4FRWyuXpoQIDAQAB +-----END RSA PUBLIC KEY----- From 43d73d780714ae81add4560a719a47df9f4697cf Mon Sep 17 00:00:00 2001 From: Huw Jones Date: Wed, 20 Aug 2025 12:32:33 +0100 Subject: [PATCH 20/42] ec: add coverage of de-/serialisation methods --- openssl/src/ec.rs | 81 ++++++++++++++++++++++++++++++++++ openssl/test/ec-encrypted.pem | 8 ++++ openssl/test/ec.der | Bin 0 -> 121 bytes openssl/test/ec.pem | 5 +++ openssl/test/ec.pub.der | Bin 0 -> 91 bytes openssl/test/ec.pub.pem | 4 ++ openssl/test/ec.trad.pem | 5 +++ 7 files changed, 103 insertions(+) create mode 100644 openssl/test/ec-encrypted.pem create mode 100644 openssl/test/ec.der create mode 100644 openssl/test/ec.pem create mode 100644 openssl/test/ec.pub.der create mode 100644 openssl/test/ec.pub.pem create mode 100644 openssl/test/ec.trad.pem diff --git a/openssl/src/ec.rs b/openssl/src/ec.rs index c8a9f845a..99653f16a 100644 --- a/openssl/src/ec.rs +++ b/openssl/src/ec.rs @@ -1049,10 +1049,91 @@ impl fmt::Debug for EcKey { #[cfg(test)] mod test { use hex::FromHex; + use std::str::from_utf8; use super::*; use crate::bn::{BigNum, BigNumContext}; use crate::nid::Nid; + use crate::symm::Cipher; + + #[test] + fn test_private_key_from_pem() { + EcKey::private_key_from_pem(include_bytes!("../test/ec.pem")).unwrap(); + } + + #[test] + fn test_private_key_from_pem_trad() { + EcKey::private_key_from_pem(include_bytes!("../test/ec.trad.pem")).unwrap(); + } + + #[test] + fn test_private_key_from_pem_password() { + let key = include_bytes!("../test/ec-encrypted.pem"); + EcKey::private_key_from_pem_passphrase(key, b"mypass").unwrap(); + } + + #[test] + fn test_private_key_from_pem_callback() { + let mut password_queried = false; + let key = include_bytes!("../test/ec-encrypted.pem"); + EcKey::private_key_from_pem_callback(key, |password| { + password_queried = true; + password[..6].copy_from_slice(b"mypass"); + Ok(6) + }) + .unwrap(); + + assert!(password_queried); + } + + #[test] + fn test_private_key_from_der() { + EcKey::private_key_from_der(include_bytes!("../test/ec.der")).unwrap(); + } + + #[test] + fn test_private_key_to_pem() { + let key = EcKey::private_key_from_pem(include_bytes!("../test/ec.pem")).unwrap(); + let pem = key.private_key_to_pem().unwrap(); + assert_eq!( + from_utf8(&pem).unwrap(), + include_str!("../test/ec.trad.pem").replace("\r\n", "\n") + ); + } + + #[test] + fn test_private_key_to_pem_password() { + let key = EcKey::private_key_from_pem(include_bytes!("../test/ec.pem")).unwrap(); + let pem = key + .private_key_to_pem_passphrase(Cipher::aes_128_cbc(), b"foobar") + .unwrap(); + EcKey::private_key_from_pem_passphrase(&pem, b"foobar").unwrap(); + assert!(EcKey::private_key_from_pem_passphrase(&pem, b"fizzbuzz").is_err()); + } + + #[test] + fn test_private_key_to_der() { + let key = EcKey::private_key_from_pem(include_bytes!("../test/ec.pem")).unwrap(); + let der = key.private_key_to_der().unwrap(); + assert_eq!(der, include_bytes!("../test/ec.der")); + } + + #[test] + fn test_public_key_to_pem() { + let keypair = EcKey::private_key_from_pem(include_bytes!("../test/ec.pem")).unwrap(); + let pubkey_pem = keypair.public_key_to_pem().unwrap(); + assert_eq!( + from_utf8(&pubkey_pem).unwrap(), + include_str!("../test/ec.pub.pem").replace("\r\n", "\n") + ); + } + + #[test] + fn test_public_key_to_der() { + let keypair = EcKey::private_key_from_pem(include_bytes!("../test/ec.pem")).unwrap(); + let pubkey_der = keypair.public_key_to_der().unwrap(); + assert_eq!(pubkey_der, include_bytes!("../test/ec.pub.der")); + } #[test] fn key_new_by_curve_name() { diff --git a/openssl/test/ec-encrypted.pem b/openssl/test/ec-encrypted.pem new file mode 100644 index 000000000..09fb1c456 --- /dev/null +++ b/openssl/test/ec-encrypted.pem @@ -0,0 +1,8 @@ +-----BEGIN EC PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: AES-128-CBC,0CE431CD7484C8C28D0D45E4A1B66BC5 + +PwoYZlzNjwHZmo3rFTXBeIx/LVow/2MLcZiTW1xmrK62CJYkwum3HooRHTTuTnea +r89nGbVqndEIUauZj9/b3z+vaGDisp5cIvqizoQdHwlg7ymDcCdA16kNIu57avV4 +wXqC9DbpPeXbv++J4u7oYpjBiOKeysxwecKrJt1PVIo= +-----END EC PRIVATE KEY----- diff --git a/openssl/test/ec.der b/openssl/test/ec.der new file mode 100644 index 0000000000000000000000000000000000000000..d81691326bdca90e80176afda1e1c0cd5b924a1b GIT binary patch literal 121 zcmV-<0EYiCcLD(c1R%}|V!zJ_v-2V|os104?XML|KU~p$>5P8KngUdI^#Gs>1_&yK zNX|V20SBQ(13~}<&TL+~*9mnYFw+fv(Cv_(`ImYq>;dj#US{uq>H~#i` literal 0 HcmV?d00001 diff --git a/openssl/test/ec.pem b/openssl/test/ec.pem new file mode 100644 index 000000000..9328c454d --- /dev/null +++ b/openssl/test/ec.pem @@ -0,0 +1,5 @@ +-----BEGIN PRIVATE KEY----- +MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgzgpiv88Is/MiM52M +DM3trxVKP1zRfemMfsmaAlR29QChRANCAATObF661wl1ITDTDX3Q7ZCe+Zd6KOwB +7mJeZu9/6gOIosLO8QDwVOQSXKXvaeE+cZq9Hi8J3XccSe3uQl0JZ1sb +-----END PRIVATE KEY----- diff --git a/openssl/test/ec.pub.der b/openssl/test/ec.pub.der new file mode 100644 index 0000000000000000000000000000000000000000..acc2b435f5578f1c7fa2a219c70b1d29176ffed8 GIT binary patch literal 91 zcmXqrG!SNE*J|@PXUoLM#sOw9GqN)~F|eG=iQ9FZvsBUGGH>mLw-e_5oL;5zhVfle uT-y8kSIixY4xRhR@FC=hP|VWznGfv>XYG~K=e%1k Date: Wed, 20 Aug 2025 13:46:40 +0100 Subject: [PATCH 21/42] dsa: improve coverage of de-/serialisation methods --- openssl/src/dsa.rs | 61 ++++++++++++++++++++++ openssl/test/dsa.pub.der | Bin 0 -> 443 bytes openssl/test/{dsa.pem.pub => dsa.pub.pem} | 0 3 files changed, 61 insertions(+) create mode 100644 openssl/test/dsa.pub.der rename openssl/test/{dsa.pem.pub => dsa.pub.pem} (100%) diff --git a/openssl/src/dsa.rs b/openssl/src/dsa.rs index 621dcb943..fbb2b077c 100644 --- a/openssl/src/dsa.rs +++ b/openssl/src/dsa.rs @@ -539,6 +539,8 @@ mod test { use crate::pkey::PKey; #[cfg(not(any(boringssl, awslc_fips)))] use crate::sign::{Signer, Verifier}; + use crate::symm::Cipher; + use std::str::from_utf8; #[test] pub fn test_generate() { @@ -694,4 +696,63 @@ mod test { let s = format!("{:?}", sig); assert_eq!(s, "DsaSig { r: 774484690634577222213819810519929266740561094381, s: 910998676210681457251421818099943952372231273347 }"); } + + #[test] + fn test_private_key_to_pem() { + let key = Dsa::generate(512).unwrap(); + let pem = key.private_key_to_pem().unwrap(); + let pem_str = from_utf8(&pem).unwrap(); + assert!( + pem_str.contains("-----BEGIN DSA PRIVATE KEY-----"), + "{pem_str}" + ); + } + + #[test] + fn test_private_key_to_pem_password() { + let key = Dsa::generate(512).unwrap(); + let pem = key + .private_key_to_pem_passphrase(Cipher::aes_128_cbc(), b"foobar") + .unwrap(); + let pem_str = from_utf8(&pem).unwrap(); + assert!( + pem_str.contains("-----BEGIN DSA PRIVATE KEY-----"), + "{pem_str}" + ); + assert!(pem_str.contains("ENCRYPTED"), "{pem_str}"); + assert!(pem_str.contains("AES-128-CBC"), "{pem_str}"); + } + + #[test] + fn test_private_key_to_der() { + let key = Dsa::generate(512).unwrap(); + key.private_key_to_der().unwrap(); + } + + #[test] + fn test_public_key_from_pem() { + Dsa::public_key_from_pem(include_bytes!("../test/dsa.pub.pem")).unwrap(); + } + + #[test] + fn test_public_key_from_der() { + Dsa::public_key_from_der(include_bytes!("../test/dsa.pub.der")).unwrap(); + } + + #[test] + fn test_public_key_to_pem() { + let key = Dsa::public_key_from_der(include_bytes!("../test/dsa.pub.der")).unwrap(); + let pem = key.public_key_to_pem().unwrap(); + assert_eq!( + from_utf8(&pem).unwrap(), + include_str!("../test/dsa.pub.pem").replace("\r\n", "\n") + ); + } + + #[test] + fn test_public_key_to_der() { + let key = Dsa::public_key_from_pem(include_bytes!("../test/dsa.pub.pem")).unwrap(); + let der = key.public_key_to_der().unwrap(); + assert_eq!(der, include_bytes!("../test/dsa.pub.der")); + } } diff --git a/openssl/test/dsa.pub.der b/openssl/test/dsa.pub.der new file mode 100644 index 0000000000000000000000000000000000000000..155ef84062fae20d49ebc8a63c90903a9e8231c0 GIT binary patch literal 443 zcmV;s0Yv^Vf&sTMf&nWA2P%e0&Nu`CFoFRd0)c@5q$%&?wStK?2A^n7)Sx^sbOWlZX@waouEMf8`rZb@ z_>0#c0u=y=5^tK{OOM;X`Op9yNgz(a|;gHInph=CgC5(mVhF literal 0 HcmV?d00001 diff --git a/openssl/test/dsa.pem.pub b/openssl/test/dsa.pub.pem similarity index 100% rename from openssl/test/dsa.pem.pub rename to openssl/test/dsa.pub.pem From f281c5c77db10007cba1797a9ae5511be5d29368 Mon Sep 17 00:00:00 2001 From: Huw Jones Date: Mon, 4 Aug 2025 17:31:39 +0100 Subject: [PATCH 22/42] rsa: add accessors test --- openssl/src/rsa.rs | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/openssl/src/rsa.rs b/openssl/src/rsa.rs index b2e339bc2..f7748cbe1 100644 --- a/openssl/src/rsa.rs +++ b/openssl/src/rsa.rs @@ -687,6 +687,47 @@ mod test { use super::*; + #[test] + fn test_accessors() { + let key = include_bytes!("../test/rsa.pem"); + let rsa = Rsa::private_key_from_pem_passphrase(key, b"mypass").unwrap(); + + assert_eq!( + rsa.n().to_string(), + BigNum::from_hex_str("A1F8160AE2E3C9B465CE8D2D656263362B927DBE29E1F02477FC1625CC90A136E38BD93497C5B6EA63DD7711E67C7429F956B0FB8A8F089ADC4B69893CC1333F53EDD019B87784252FEC914FE4857769594BEA4280D32C0F55BF62944F130396BC6E9BDF6EBDD2BDA3678EECA0C668F701B38DBFFB38C8342CE2FE6D27FADE4A5A4874979DD4B9CF9ADEC4C75B05852C2C0F5EF8A5C1750392F944E8ED64C110C6B647609AA4783AEB9C6C9AD755313050638B83665C6F6F7A82A396702A1F641B82D3EBF2392219491FB686872C5716F50AF8358D9A8B9D17C340728F7F87D89A18D8FCAB67AD84590C2ECF759339363C07034D6F606F9E21E05456CAE5E9A1").unwrap().to_string(), + ); + assert_eq!( + rsa.e().to_string(), + BigNum::from_dec_str("65537").unwrap().to_string(), + ); + assert_eq!( + rsa.d().to_string(), + BigNum::from_hex_str("12AE71A469CD0A2BC37E526C4500571F1D61751D64E949707B62590F9D0BA57C963C401E3FCF2F2CD3BDEC88E503BFC6439B0B28C82F7D3797671F5213EED8C15A25D8D5CEA0025EE3AB2E8B7F79216FC63BEA562753B40644C6A15127D9B2954540A0BBE1A30556982D4E9FDE5F6425F14D4B713441B55DC73B9B4AEDCC92ACE3927E37F57D0CFD5E7581FA512C8F4961A9EB0B80F8A80746728A55FF46471F3425063B9D53642F5EDE1E84D613081AFA5C22D051285BD63B943B565D898A05685413E53C3C6C6525FF1FE34E3DDC70F0D56450FDA48BA12E104E9DEB9FB81881E1C4BDF25D9247F450C865927968E77334F4414F75A750E139546E3A8A739D").unwrap().to_string(), + ); + + assert_eq!( + rsa.p().unwrap().to_string(), + BigNum::from_hex_str("E01CC410EB48A6655D54464D0AA4BB6DA0B872B774A6A9D11FFE480778F0DDB7311A7E902EF9C4B5F75476262BA8176CB35979F014372A1A2829E4136BD001D07C3F3F2E4F25D4C934F63C7109FBA2E67628A0DC9A73C6058E6DEF22DCD50950E711DDC16D5586F2A7FA75EA8494C18083E0B65742F8A0D5E73FB2D970F01947").unwrap().to_string(), + ); + assert_eq!( + rsa.q().unwrap().to_string(), + BigNum::from_hex_str("B903C47E0995B632F4532CB1F3C199145D5F3AE9C7DFEEDC7A92A6B47E29C61B42A07AA95A64FC600999C5A7B01E01C08CB62CA756527BBCC56078FB0BABA5ED38DE2D07990FF6300FAEADEB6C039A97BF1E507E4E7040FA78DB8257C8B112ED5E59C3CD092FE5D4E5B51275D41281072A5E785EBAF3DAE3709203133F5159D7").unwrap().to_string(), + ); + + assert_eq!( + rsa.dmp1().unwrap().to_string(), + BigNum::from_hex_str("07029F577024AB9FCC1590C56429D6FB0CE5F820A8F375A866F9CB430093783BFCBB396E4529E6EF52374022DD86BA84D9EF58931BEEC5D05FA53FCF23B633F8538A9EED51E87B097830A39F5D92937BE6024B55DB36F7E0C09DCBB72975387F615AFBB6CB36BBABE7793C2B03CEAB66DBB931BAF50B55EC9AF9311D001D628D").unwrap().to_string(), + ); + assert_eq!( + rsa.dmq1().unwrap().to_string(), + BigNum::from_hex_str("87FF7AFA62B547FEE0961B2E9BCD5D6718D39D8CA73DB6691F38998DE78771762C5DA68CC243A5383B166BB23DC570E84706CA801EF5F6BAE6236A0AAFA3770E8F54D1A8DA1C5F8D2899F082331DDB0F5C8F3DFFFA4C8D97102BDAFE082A118DA6633988880E4B55599CE67AF26EBFA5B2C14A9DE7B2C4DD96ABDDD2D2224C75").unwrap().to_string(), + ); + assert_eq!( + rsa.iqmp().unwrap().to_string(), + BigNum::from_hex_str("21877B0C73A1AD6BF19303D0B11336B4E82B8DB72B7EFB50262A5DF8395CC7256EB8CF6C40B7608D5936A32DBA174126A527062EAD8CA305FB7E177F409437C9DCB9718ED82018BCEF0F7720A2C475F9DB26DA0E3CB516D084EB25178E828D5CABD49BB3161AC0181FA7F821E80E7AD37936F9943054AD092921EE2C592E4375").unwrap().to_string(), + ); + } + #[test] fn test_private_key_from_pem() { Rsa::private_key_from_pem(include_bytes!("../test/rsa.pem")).unwrap(); From 0f1c45f4ef9ac9259611ee2cd14549ef2aec99ed Mon Sep 17 00:00:00 2001 From: Huw Jones Date: Wed, 6 Aug 2025 11:05:34 +0100 Subject: [PATCH 23/42] rsa: add from_private_components test --- openssl/src/rsa.rs | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/openssl/src/rsa.rs b/openssl/src/rsa.rs index f7748cbe1..e8eb5123d 100644 --- a/openssl/src/rsa.rs +++ b/openssl/src/rsa.rs @@ -949,6 +949,32 @@ mod test { drop(key.clone()); } + #[test] + fn test_private_key_builder() { + let key = include_bytes!("../test/rsa.pem"); + let rsa = Rsa::private_key_from_pem_passphrase(key, b"mypass").unwrap(); + + let rsa2 = Rsa::from_private_components( + rsa.n().to_owned().unwrap(), + rsa.e().to_owned().unwrap(), + rsa.d().to_owned().unwrap(), + rsa.p().unwrap().to_owned().unwrap(), + rsa.q().unwrap().to_owned().unwrap(), + rsa.dmp1().unwrap().to_owned().unwrap(), + rsa.dmq1().unwrap().to_owned().unwrap(), + rsa.iqmp().unwrap().to_owned().unwrap(), + ) + .unwrap(); + assert_eq!(rsa.n(), rsa2.n(), "n"); + assert_eq!(rsa.e(), rsa2.e(), "e"); + assert_eq!(rsa.d(), rsa2.d(), "d"); + assert_eq!(rsa.p(), rsa2.p(), "p"); + assert_eq!(rsa.q(), rsa2.q(), "q"); + assert_eq!(rsa.dmp1(), rsa2.dmp1(), "dmp1"); + assert_eq!(rsa.dmq1(), rsa2.dmq1(), "dmq1"); + assert_eq!(rsa.iqmp(), rsa2.iqmp(), "iqmp"); + } + #[test] fn generate_with_e() { let e = BigNum::from_u32(0x10001).unwrap(); From 8dd04f72ef630c7cd81c548b2c67d6c03e3fb5ae Mon Sep 17 00:00:00 2001 From: Huw Jones Date: Fri, 8 Aug 2025 19:59:58 +0100 Subject: [PATCH 24/42] bn: implement clone --- openssl/src/bn.rs | 1 + openssl/src/dh.rs | 8 ++------ 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/openssl/src/bn.rs b/openssl/src/bn.rs index bd0e3ec3c..2df1b6cee 100644 --- a/openssl/src/bn.rs +++ b/openssl/src/bn.rs @@ -133,6 +133,7 @@ impl BigNumContext { foreign_type_and_impl_send_sync! { type CType = ffi::BIGNUM; fn drop = ffi::BN_free; + fn clone = ffi::BN_dup; /// Dynamically sized large number implementation /// diff --git a/openssl/src/dh.rs b/openssl/src/dh.rs index 92ea04424..1b2648192 100644 --- a/openssl/src/dh.rs +++ b/openssl/src/dh.rs @@ -371,12 +371,8 @@ mod tests { "8CF83642A709A097B447997640129DA299B1A47D1EB3750BA308B0FE64F5FBD3", ) .unwrap(); - let dh = Dh::from_params( - prime_p.to_owned().unwrap(), - generator.to_owned().unwrap(), - prime_q.to_owned().unwrap(), - ) - .unwrap(); + let dh = + Dh::from_params(prime_p.to_owned(), generator.to_owned(), prime_q.to_owned()).unwrap(); ctx.set_tmp_dh(&dh).unwrap(); assert_eq!(dh.prime_p(), &prime_p); From 747f926223169368c99a281cf50ea6e453199b03 Mon Sep 17 00:00:00 2001 From: Huw Jones Date: Thu, 21 Aug 2025 17:50:05 +0100 Subject: [PATCH 25/42] pkey: add check_key --- openssl/src/pkey.rs | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/openssl/src/pkey.rs b/openssl/src/pkey.rs index 3a19346a5..7447b54e1 100644 --- a/openssl/src/pkey.rs +++ b/openssl/src/pkey.rs @@ -260,6 +260,11 @@ impl ToOwned for PKeyRef { } } +#[cfg(ossl110)] +pub trait KeyCheck { + fn check_key(&self) -> Result<(), ErrorStack>; +} + impl PKeyRef { /// Returns a copy of the internal RSA key. #[corresponds(EVP_PKEY_get1_RSA)] @@ -322,6 +327,16 @@ impl PKeyRef { } } +#[cfg(ossl111)] +impl KeyCheck for PKeyRef { + /// Validates the key parameters. + #[corresponds(EVP_PKEY_param_check)] + fn check_key(&self) -> Result<(), ErrorStack> { + let ctx = PkeyCtx::new(self)?; + cvt(unsafe { ffi::EVP_PKEY_param_check(ctx.as_ptr()) }).map(|_| ()) + } +} + impl PKeyRef where T: HasPublic, @@ -399,6 +414,16 @@ where } } +#[cfg(ossl111)] +impl KeyCheck for PKeyRef { + /// Validates the public key parameters. + #[corresponds(EVP_PKEY_public_check)] + fn check_key(&self) -> Result<(), ErrorStack> { + let ctx = PkeyCtx::new(self)?; + cvt(unsafe { ffi::EVP_PKEY_public_check(ctx.as_ptr()) }).map(|_| ()) + } +} + impl PKeyRef where T: HasPrivate, @@ -493,6 +518,16 @@ where } } +#[cfg(ossl111)] +impl KeyCheck for PKeyRef { + /// Validates the private key parameters. + #[corresponds(EVP_PKEY_check)] + fn check_key(&self) -> Result<(), ErrorStack> { + let ctx = PkeyCtx::new(self)?; + cvt(unsafe { ffi::EVP_PKEY_check(ctx.as_ptr()) }).map(|_| ()) + } +} + impl fmt::Debug for PKey { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { let alg = self.id().try_into().unwrap_or("unknown"); From 5fee1ed494d6d6580b8029a5a532b2dc2d995261 Mon Sep 17 00:00:00 2001 From: Huw Jones Date: Mon, 4 Aug 2025 12:26:08 +0100 Subject: [PATCH 26/42] pkey: add get bn, bytes, & utf-8 param Co-Authored-By: Samuel Collins --- openssl/src/pkey.rs | 89 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) diff --git a/openssl/src/pkey.rs b/openssl/src/pkey.rs index 7447b54e1..9e69ead98 100644 --- a/openssl/src/pkey.rs +++ b/openssl/src/pkey.rs @@ -41,6 +41,8 @@ //! ``` #![allow(clippy::missing_safety_doc)] use crate::bio::{MemBio, MemBioSlice}; +#[cfg(ossl300)] +use crate::bn::BigNumRef; #[cfg(ossl110)] use crate::cipher::CipherRef; use crate::dh::Dh; @@ -59,9 +61,13 @@ use crate::util::{invoke_passwd_cb, CallbackState}; use crate::{cvt, cvt_p}; use cfg_if::cfg_if; use foreign_types::{ForeignType, ForeignTypeRef}; +#[cfg(ossl300)] +use libc::c_char; use libc::{c_int, c_long}; use openssl_macros::corresponds; use std::convert::{TryFrom, TryInto}; +#[cfg(ossl300)] +use std::ffi::c_uchar; use std::ffi::{CStr, CString}; use std::fmt; #[cfg(all(not(any(boringssl, awslc)), ossl110))] @@ -314,6 +320,64 @@ impl PKeyRef { unsafe { ffi::EVP_PKEY_size(self.as_ptr()) as usize } } + /// Returns the BigNum value associated with the given key name. + #[corresponds(EVP_PKEY_get_bn_param)] + #[cfg(ossl300)] + #[allow(dead_code)] + pub(crate) fn get_bn_param(&self, key: &CStr) -> Result<&BigNumRef, ErrorStack> { + let mut value = ptr::null_mut(); + unsafe { + cvt(ffi::EVP_PKEY_get_bn_param( + self.as_ptr(), + key.as_ptr(), + &mut value, + ))?; + Ok(BigNumRef::from_ptr(value)) + } + } + + /// Returns the byte string value associated with the given key name. + #[corresponds(EVP_PKEY_get_octet_string_param)] + #[cfg(ossl300)] + #[allow(dead_code)] + pub(crate) fn get_byte_string_param(&self, key: &CStr) -> Result, ErrorStack> { + const VALUE_LEN: usize = 4096; + let mut value_buf: Vec = vec![0; VALUE_LEN]; + let mut out_len: usize = 0; + unsafe { + cvt(ffi::EVP_PKEY_get_octet_string_param( + self.as_ptr(), + key.as_ptr(), + value_buf.as_mut_ptr().cast::(), + VALUE_LEN, + &mut out_len, + ))?; + } + value_buf.truncate(out_len); + Ok(value_buf) + } + + /// Returns the String value associated with the given key name. + #[corresponds(EVP_PKEY_get_utf8_string_param)] + #[cfg(ossl300)] + #[allow(dead_code)] + pub(crate) fn get_utf8_string_param(&self, key: &CStr) -> Result { + const VALUE_LEN: usize = 4096; + let mut value_buf: Vec = vec![0; VALUE_LEN]; + let mut out_len: usize = 0; + unsafe { + cvt(ffi::EVP_PKEY_get_utf8_string_param( + self.as_ptr(), + key.as_ptr(), + value_buf.as_mut_ptr().cast::(), + VALUE_LEN, + &mut out_len, + ))?; + } + value_buf.truncate(out_len); + Ok(String::from_utf8(value_buf).unwrap()) + } + /// Converts the `PKey` to an `OsslParamArray`. /// /// Use `selection` to control what parameters are included. @@ -1060,6 +1124,8 @@ mod tests { #[cfg(not(boringssl))] use crate::dh::Dh; use crate::dsa::Dsa; + #[cfg(ossl300)] + use crate::ec::EcGroup; use crate::ec::EcKey; #[cfg(ossl300)] use crate::encrypt::{Decrypter, Encrypter}; @@ -1363,6 +1429,29 @@ mod tests { assert!(Error::get().is_none()); } + #[cfg(ossl300)] + #[test] + fn test_get_bn_param() { + let rsa = Rsa::generate(2048).unwrap(); + let pkey = PKey::from_rsa(rsa.clone()).unwrap(); + assert_eq!(pkey.get_bn_param(OSSL_PKEY_PARAM_RSA_N).unwrap(), rsa.n()); + assert_eq!(pkey.get_bn_param(OSSL_PKEY_PARAM_RSA_D).unwrap(), rsa.d()); + } + + #[cfg(ossl300)] + #[test] + fn test_get_utf8_string_param() { + let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap(); + let ec = EcKey::generate(&group).unwrap(); + let pkey = PKey::from_ec_key(ec.clone()).unwrap(); + + assert_eq!( + pkey.get_utf8_string_param(OSSL_PKEY_PARAM_GROUP_NAME) + .unwrap(), + String::from("prime256v1") + ); + } + #[cfg(ossl300)] #[test] fn test_todata() { From e6729086321ff9860e364a576183ed01dae7fa8b Mon Sep 17 00:00:00 2001 From: Huw Jones Date: Fri, 12 Sep 2025 17:09:10 +0100 Subject: [PATCH 27/42] pkey: add KeyID trait --- openssl/src/pkey.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/openssl/src/pkey.rs b/openssl/src/pkey.rs index 9e69ead98..ff388ed40 100644 --- a/openssl/src/pkey.rs +++ b/openssl/src/pkey.rs @@ -245,6 +245,14 @@ pub unsafe trait HasPrivate {} unsafe impl HasPrivate for Private {} +/// KeyID trait that tags the type of key. +/// +/// Used for decoding PKeys +#[cfg(ossl300)] +pub(crate) trait KeyID { + const ID: Id; +} + generic_foreign_type_and_impl_send_sync! { type CType = ffi::EVP_PKEY; fn drop = ffi::EVP_PKEY_free; From 1c34607d9b6cb179056ede2f2d8a431f6fb939ca Mon Sep 17 00:00:00 2001 From: Huw Jones Date: Thu, 21 Aug 2025 15:23:17 +0100 Subject: [PATCH 28/42] macros/generic_foreign_type_and_impl_send_sync: back with key pkey shim for ossl300 --- openssl/src/macros.rs | 118 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 118 insertions(+) diff --git a/openssl/src/macros.rs b/openssl/src/macros.rs index f67bf8848..f748f6ef0 100644 --- a/openssl/src/macros.rs +++ b/openssl/src/macros.rs @@ -267,6 +267,124 @@ macro_rules! generic_foreign_type_and_impl_send_sync { unsafe impl Sync for $owned{} unsafe impl Sync for $borrowed{} }; + ( + $(#[$impl_attr:meta])* + type CType = $ctype:ty; + fn drop = $drop:expr; + $(fn clone = $clone:expr;)* + + $(#[$owned_attr:meta])* + pub struct $owned:ident; + $(#[$borrowed_attr:meta])* + pub struct $borrowed:ident; + key_id = $key_id:path; + pkey_type = $pkey_type:ident; + ) => { + cfg_if::cfg_if!{ + if #[cfg(ossl300)] { + use crate::pkey::KeyID; + + $(#[$owned_attr])* + pub struct $owned(crate::pkey::PKey); + + impl crate::pkey::KeyID for $owned{ + const ID: crate::pkey::Id = $key_id; + } + + $(#[$impl_attr])* + impl ForeignType for $owned { + type CType = ffi::EVP_PKEY; + type Ref = $borrowed; + + #[inline] + unsafe fn from_ptr(ptr: *mut ffi::EVP_PKEY) -> $owned { + Self(crate::pkey::PKey::from_ptr(ptr)) + } + + #[inline] + fn as_ptr(&self) -> *mut ffi::EVP_PKEY { + self.0.as_ptr() + } + } + + impl Clone for $owned { + #[inline] + fn clone(&self) -> $owned { + Self(self.0.clone()) + } + } + + impl ToOwned for $borrowed { + type Owned = $owned; + + #[inline] + fn to_owned(&self) -> $owned { + self.0.$pkey_type().unwrap() + } + } + + impl std::ops::Deref for $owned { + type Target = $borrowed; + + #[inline] + fn deref(&self) -> &$borrowed { + unsafe { $borrowed::from_ptr(self.as_ptr()) } + } + } + + impl std::ops::DerefMut for $owned { + #[inline] + fn deref_mut(&mut self) -> &mut $borrowed { + unsafe { $borrowed::from_ptr_mut(self.as_ptr()) } + } + } + + impl std::borrow::Borrow<$borrowed> for $owned { + #[inline] + fn borrow(&self) -> &$borrowed { + self + } + } + + impl AsRef<$borrowed> for $owned { + #[inline] + fn as_ref(&self) -> &$borrowed { + self + } + } + + $(#[$borrowed_attr])* + pub struct $borrowed(crate::pkey::PKeyRef); + + impl ForeignTypeRef for $borrowed { + type CType = ffi::EVP_PKEY; + } + + unsafe impl Send for $owned{} + unsafe impl Send for $borrowed{} + unsafe impl Sync for $owned{} + unsafe impl Sync for $borrowed{} + + impl From<&$borrowed> for crate::pkey::PKey { + fn from(value: &$borrowed) -> Self { + value.0.to_owned() + } + } + } else { + generic_foreign_type_and_impl_send_sync!{ + $(#[$impl_attr])* + type CType = $ctype; + fn drop = $drop; + $(fn clone = $clone;)* + + $(#[$owned_attr])* + pub struct $owned; + $(#[$borrowed_attr])* + pub struct $borrowed; + } + } + } + } } #[cfg_attr(not(ossl300), allow(unused_macros))] From 133e2cc536e761396631f78a4e99bd11fdbe8ebf Mon Sep 17 00:00:00 2001 From: Huw Jones Date: Tue, 19 Aug 2025 18:07:14 +0100 Subject: [PATCH 29/42] macros/to-from: use non-deprecated APIs --- openssl/src/macros.rs | 175 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 175 insertions(+) diff --git a/openssl/src/macros.rs b/openssl/src/macros.rs index f748f6ef0..73bcf5f76 100644 --- a/openssl/src/macros.rs +++ b/openssl/src/macros.rs @@ -1,4 +1,53 @@ macro_rules! private_key_from_pem { + ($(#[$m:meta])* $n:ident, $(#[$m2:meta])* $n2:ident, $(#[$m3:meta])* $n3:ident, $t:ty, $struc: path, $f:path) => { + from_pem!($(#[$m])* $n, $t, $struc, $f); + + $(#[$m2])* + pub fn $n2(pem: &[u8], passphrase: &[u8]) -> Result<$t, crate::error::ErrorStack> { + #[cfg(ossl300)] + return Ok(crate::ossl_encdec::Decoder::new() + .set_format(crate::ossl_encdec::KeyFormat::Pem) + .set_structure($struc) + .set_passphrase(passphrase) + .decode(pem)?.try_into().unwrap()); + + #[cfg(not(ossl300))] + unsafe { + ffi::init(); + let bio = crate::bio::MemBioSlice::new(pem)?; + let passphrase = ::std::ffi::CString::new(passphrase).unwrap(); + cvt_p($f(bio.as_ptr(), + ::std::ptr::null_mut(), + None, + passphrase.as_ptr() as *const _ as *mut _)) + .map(|p| ::foreign_types::ForeignType::from_ptr(p)) + } + } + + $(#[$m3])* + pub fn $n3(pem: &[u8], callback: F) -> Result<$t, crate::error::ErrorStack> + where F: FnOnce(&mut [u8]) -> Result + { + #[cfg(ossl300)] + return Ok(crate::ossl_encdec::Decoder::new() + .set_format(crate::ossl_encdec::KeyFormat::Pem) + .set_structure($struc) + .set_passphrase_callback(callback) + .decode(pem)?.try_into().unwrap()); + + #[cfg(not(ossl300))] + unsafe { + ffi::init(); + let mut cb = crate::util::CallbackState::new(callback); + let bio = crate::bio::MemBioSlice::new(pem)?; + cvt_p($f(bio.as_ptr(), + ::std::ptr::null_mut(), + Some(crate::util::invoke_passwd_cb::), + &mut cb as *mut _ as *mut _)) + .map(|p| ::foreign_types::ForeignType::from_ptr(p)) + } + } + }; ($(#[$m:meta])* $n:ident, $(#[$m2:meta])* $n2:ident, $(#[$m3:meta])* $n3:ident, $t:ty, $f:path) => { from_pem!($(#[$m])* $n, $t, $f); @@ -35,6 +84,57 @@ macro_rules! private_key_from_pem { } macro_rules! private_key_to_pem { + ($(#[$m:meta])* $n:ident, $(#[$m2:meta])* $n2:ident, $sel:path, $struc:path, $f:path) => { + $(#[$m])* + pub fn $n(&self) -> Result, crate::error::ErrorStack> { + #[cfg(ossl300)] + return crate::ossl_encdec::Encoder::new($sel) + .set_format(crate::ossl_encdec::KeyFormat::Pem) + .set_structure($struc) + .encode(&self.0); + + #[cfg(not(ossl300))] + unsafe { + let bio = crate::bio::MemBio::new()?; + crate::cvt($f(bio.as_ptr(), + self.as_ptr(), + ::std::ptr::null(), + ::std::ptr::null_mut(), + -1, + None, + ::std::ptr::null_mut()))?; + Ok(bio.get_buf().to_owned()) + } + } + + $(#[$m2])* + pub fn $n2( + &self, + cipher: crate::symm::Cipher, + passphrase: &[u8] + ) -> Result, crate::error::ErrorStack> { + #[cfg(ossl300)] + return crate::ossl_encdec::Encoder::new($sel) + .set_format(crate::ossl_encdec::KeyFormat::Pem) + .set_cipher(cipher) + .set_passphrase(passphrase) + .encode(&self.0); + + #[cfg(not(ossl300))] + unsafe { + let bio = crate::bio::MemBio::new()?; + assert!(passphrase.len() <= ::libc::c_int::MAX as usize); + crate::cvt($f(bio.as_ptr(), + self.as_ptr(), + cipher.as_ptr(), + passphrase.as_ptr() as *const _ as *mut _, + passphrase.len() as ::libc::c_int, + None, + ::std::ptr::null_mut()))?; + Ok(bio.get_buf().to_owned()) + } + } + }; ($(#[$m:meta])* $n:ident, $(#[$m2:meta])* $n2:ident, $f:path) => { $(#[$m])* pub fn $n(&self) -> Result, crate::error::ErrorStack> { @@ -74,6 +174,23 @@ macro_rules! private_key_to_pem { } macro_rules! to_pem { + ($(#[$m:meta])* $n:ident, $sel:path, $struc:path, $f:path) => { + $(#[$m])* + pub fn $n(&self) -> Result, crate::error::ErrorStack> { + #[cfg(ossl300)] + return crate::ossl_encdec::Encoder::new($sel) + .set_format(crate::ossl_encdec::KeyFormat::Pem) + .set_structure($struc) + .encode(&self.0); + + #[cfg(not(ossl300))] + unsafe { + let bio = crate::bio::MemBio::new()?; + crate::cvt($f(bio.as_ptr(), self.as_ptr()))?; + Ok(bio.get_buf().to_owned()) + } + } + }; ($(#[$m:meta])* $n:ident, $f:path) => { $(#[$m])* pub fn $n(&self) -> Result, crate::error::ErrorStack> { @@ -87,6 +204,26 @@ macro_rules! to_pem { } macro_rules! to_der { + ($(#[$m:meta])* $n:ident, $sel:path, $struc:path, $f:path) => { + $(#[$m])* + pub fn $n(&self) -> Result, crate::error::ErrorStack> { + #[cfg(ossl300)] + return crate::ossl_encdec::Encoder::new($sel) + .set_format(crate::ossl_encdec::KeyFormat::Der) + .set_structure($struc) + .encode(&self.0); + + #[cfg(not(ossl300))] + unsafe { + let len = crate::cvt($f(::foreign_types::ForeignTypeRef::as_ptr(self), + ::std::ptr::null_mut()))?; + let mut buf = vec![0; len as usize]; + crate::cvt($f(::foreign_types::ForeignTypeRef::as_ptr(self), + &mut buf.as_mut_ptr()))?; + Ok(buf) + } + } + }; ($(#[$m:meta])* $n:ident, $f:path) => { $(#[$m])* pub fn $n(&self) -> Result, crate::error::ErrorStack> { @@ -103,6 +240,26 @@ macro_rules! to_der { } macro_rules! from_der { + ($(#[$m:meta])* $n:ident, $t:ty, $struc: path, $f:path) => { + $(#[$m])* + pub fn $n(der: &[u8]) -> Result<$t, crate::error::ErrorStack> { + #[cfg(ossl300)] + return Ok(crate::ossl_encdec::Decoder::new() + .set_format(crate::ossl_encdec::KeyFormat::Der) + .set_key_type(Self::ID) + .set_structure($struc) + .decode(der)?.try_into().unwrap()); + + #[cfg(not(ossl300))] + unsafe { + use std::convert::TryInto; + ffi::init(); + let len = ::std::cmp::min(der.len(), ::libc::c_long::MAX as usize) as ::libc::c_long; + crate::cvt_p($f(::std::ptr::null_mut(), &mut der.as_ptr(), len.try_into().unwrap())) + .map(|p| ::foreign_types::ForeignType::from_ptr(p)) + } + } + }; ($(#[$m:meta])* $n:ident, $t:ty, $f:path) => { $(#[$m])* pub fn $n(der: &[u8]) -> Result<$t, crate::error::ErrorStack> { @@ -118,6 +275,24 @@ macro_rules! from_der { } macro_rules! from_pem { + ($(#[$m:meta])* $n:ident, $t:ty, $struc: path, $f:path) => { + $(#[$m])* + pub fn $n(pem: &[u8]) -> Result<$t, crate::error::ErrorStack> { + #[cfg(ossl300)] + return Ok(crate::ossl_encdec::Decoder::new() + .set_format(crate::ossl_encdec::KeyFormat::Pem) + .set_structure($struc) + .decode(pem)?.try_into().unwrap()); + + #[cfg(not(ossl300))] + unsafe { + crate::init(); + let bio = crate::bio::MemBioSlice::new(pem)?; + crate::cvt_p($f(bio.as_ptr(), ::std::ptr::null_mut(), None, ::std::ptr::null_mut())) + .map(|p| ::foreign_types::ForeignType::from_ptr(p)) + } + } + }; ($(#[$m:meta])* $n:ident, $t:ty, $f:path) => { $(#[$m])* pub fn $n(pem: &[u8]) -> Result<$t, crate::error::ErrorStack> { From 5b56a6cdca879983898dbfcb2103c2af4f980910 Mon Sep 17 00:00:00 2001 From: Huw Jones Date: Mon, 4 Aug 2025 17:45:12 +0100 Subject: [PATCH 30/42] rsa: use non-deprecated functions for ossl300+ --- openssl/src/macros.rs | 3 +- openssl/src/ossl_param.rs | 3 - openssl/src/pkey.rs | 45 ++++- openssl/src/rsa.rs | 346 +++++++++++++++++++++++++------------- 4 files changed, 273 insertions(+), 124 deletions(-) diff --git a/openssl/src/macros.rs b/openssl/src/macros.rs index 73bcf5f76..3a2c07249 100644 --- a/openssl/src/macros.rs +++ b/openssl/src/macros.rs @@ -567,7 +567,8 @@ macro_rules! cstr_const { // Safety: these all have null terminators. // We cen remove these CStr::from_bytes_with_nul_unchecked calls // when we upgrade to Rust 1.77+ with literal c"" syntax. - ($vis:vis $name:ident, $key:literal) => { + ($(#[$doc:meta])* $vis:vis $name:ident, $key:literal) => { + $(#[$doc])* #[allow(dead_code)] $vis const $name: &std::ffi::CStr = unsafe { std::ffi::CStr::from_bytes_with_nul_unchecked($key) }; } diff --git a/openssl/src/ossl_param.rs b/openssl/src/ossl_param.rs index abb209787..21aa14edc 100644 --- a/openssl/src/ossl_param.rs +++ b/openssl/src/ossl_param.rs @@ -127,7 +127,6 @@ impl<'a> OsslParamBuilder<'a> { /// /// The array is initially empty. #[corresponds(OSSL_PARAM_BLD_new)] - #[cfg_attr(any(not(ossl320), osslconf = "OPENSSL_NO_ARGON2"), allow(dead_code))] pub(crate) fn new() -> Result, ErrorStack> { unsafe { ffi::init(); @@ -141,7 +140,6 @@ impl<'a> OsslParamBuilder<'a> { /// Constructs the `OsslParamArray` and clears this builder. #[corresponds(OSSL_PARAM_BLD_to_param)] - #[cfg_attr(any(not(ossl320), osslconf = "OPENSSL_NO_ARGON2"), allow(dead_code))] #[allow(clippy::wrong_self_convention)] pub(crate) fn to_param(&'a mut self) -> Result { unsafe { @@ -185,7 +183,6 @@ impl<'a> OsslParamBuilder<'a> { /// Adds a `BigNum` to `OsslParamBuilder`. #[corresponds(OSSL_PARAM_BLD_push_BN)] - #[allow(dead_code)] // TODO: remove when when used by EVP_KEY.from_data pub(crate) fn add_bn(&mut self, key: &'a CStr, bn: &'a BigNumRef) -> Result<(), ErrorStack> { cvt(unsafe { ffi::OSSL_PARAM_BLD_push_BN(self.as_ptr(), key.as_ptr(), bn.as_ptr()) }) .map(|_| ()) diff --git a/openssl/src/pkey.rs b/openssl/src/pkey.rs index ff388ed40..cc92c6bfe 100644 --- a/openssl/src/pkey.rs +++ b/openssl/src/pkey.rs @@ -281,12 +281,18 @@ pub trait KeyCheck { impl PKeyRef { /// Returns a copy of the internal RSA key. - #[corresponds(EVP_PKEY_get1_RSA)] pub fn rsa(&self) -> Result, ErrorStack> { - unsafe { - let rsa = cvt_p(ffi::EVP_PKEY_get1_RSA(self.as_ptr()))?; - Ok(Rsa::from_ptr(rsa)) + if self.id() != Id::RSA { + return Err(ErrorStack::get()); } + + let rsa = self.as_ptr(); + #[cfg(ossl300)] + cvt(unsafe { ffi::EVP_PKEY_up_ref(rsa) })?; + #[cfg(not(ossl300))] + let rsa = cvt_p(unsafe { ffi::EVP_PKEY_get1_RSA(rsa) })?; + + Ok(unsafe { Rsa::from_ptr(rsa) }) } /// Returns a copy of the internal DSA key. @@ -325,6 +331,8 @@ impl PKeyRef { /// Returns the maximum size of a signature in bytes. #[corresponds(EVP_PKEY_size)] pub fn size(&self) -> usize { + // TODO: Next time we make backwards incompatible changes, the return type should be unified + // with the Rsa, Dsa, etc APIs. u32 or usize? unsafe { ffi::EVP_PKEY_size(self.as_ptr()) as usize } } @@ -616,16 +624,24 @@ impl Clone for PKey { impl PKey { /// Creates a new `PKey` containing an RSA key. - #[corresponds(EVP_PKEY_set1_RSA)] pub fn from_rsa(rsa: Rsa) -> Result, ErrorStack> { // TODO: Next time we make backwards incompatible changes, this could // become an `&RsaRef`. Same for all the other `from_*` methods. + let pkey: PKey; + + #[cfg(ossl300)] + unsafe { + EVP_PKEY_up_ref(rsa.as_ptr()); + pkey = PKey::from_ptr(rsa.as_ptr()); + } + + #[cfg(not(ossl300))] unsafe { let evp = cvt_p(ffi::EVP_PKEY_new())?; - let pkey = PKey::from_ptr(evp); + pkey = PKey::from_ptr(evp); cvt(ffi::EVP_PKEY_set1_RSA(pkey.0, rsa.as_ptr()))?; - Ok(pkey) } + Ok(pkey) } /// Creates a new `PKey` containing a DSA key. @@ -1120,6 +1136,21 @@ cfg_if! { cstr_const!(pub(crate) OSSL_PKEY_PARAM_RSA_N, b"n\0"); cstr_const!(pub(crate) OSSL_PKEY_PARAM_RSA_E, b"e\0"); cstr_const!(pub(crate) OSSL_PKEY_PARAM_RSA_D, b"d\0"); + cstr_const!( + /// rsa-factor1 (p) + pub(crate) OSSL_PKEY_PARAM_RSA_FACTOR1, b"rsa-factor1\0"); + cstr_const!( + /// rsa-factor2 (q) + pub(crate) OSSL_PKEY_PARAM_RSA_FACTOR2, b"rsa-factor2\0"); + cstr_const!( + /// rsa-exponent1 (dmp1) + pub(crate) OSSL_PKEY_PARAM_RSA_EXPONENT1, b"rsa-exponent1\0"); + cstr_const!( + /// rsa-exponent2 (dmq1) + pub(crate) OSSL_PKEY_PARAM_RSA_EXPONENT2, b"rsa-exponent2\0"); + cstr_const!( + /// rsa-coefficient1 (iqmp) + pub(crate) OSSL_PKEY_PARAM_RSA_COEFFICIENT1, b"rsa-coefficient1\0"); cstr_const!(pub(crate) OSSL_SIGNATURE_PARAM_NONCE_TYPE, b"nonce-type\0"); } diff --git a/openssl/src/rsa.rs b/openssl/src/rsa.rs index e8eb5123d..c87be52ec 100644 --- a/openssl/src/rsa.rs +++ b/openssl/src/rsa.rs @@ -23,18 +23,45 @@ //! let mut buf = vec![0; rsa.size() as usize]; //! let encrypted_len = rsa.public_encrypt(data, &mut buf, Padding::PKCS1).unwrap(); //! ``` +#[cfg(not(ossl300))] use cfg_if::cfg_if; use foreign_types::{ForeignType, ForeignTypeRef}; use libc::c_int; +#[cfg(ossl300)] +use std::collections::HashMap; +#[cfg(ossl300)] +use std::ffi::CStr; use std::fmt; +#[cfg(not(ossl300))] use std::mem; +#[cfg(not(ossl300))] use std::ptr; use crate::bn::{BigNum, BigNumRef}; use crate::error::ErrorStack; -use crate::pkey::{HasPrivate, HasPublic, Private, Public}; +#[cfg(ossl300)] +use crate::ossl_encdec::Structure; +#[cfg(ossl300)] +use crate::ossl_param::OsslParamBuilder; +#[cfg(ossl300)] +use crate::pkey::KeyCheck; +use crate::pkey::{HasPrivate, HasPublic, Id, PKey, Private, Public}; +#[cfg(ossl300)] +use crate::pkey::{ + OSSL_PKEY_PARAM_RSA_COEFFICIENT1, OSSL_PKEY_PARAM_RSA_D, OSSL_PKEY_PARAM_RSA_E, + OSSL_PKEY_PARAM_RSA_EXPONENT1, OSSL_PKEY_PARAM_RSA_EXPONENT2, OSSL_PKEY_PARAM_RSA_FACTOR1, + OSSL_PKEY_PARAM_RSA_FACTOR2, OSSL_PKEY_PARAM_RSA_N, +}; +#[cfg(ossl300)] +use crate::pkey_ctx::pkey_from_params; +use crate::pkey_ctx::PkeyCtx; +#[cfg(ossl300)] +use crate::pkey_ctx::Selection; +#[cfg(not(ossl300))] use crate::util::ForeignTypeRefExt; -use crate::{cvt, cvt_n, cvt_p, LenType}; +#[cfg(not(ossl300))] +use crate::{cvt, cvt_p}; +#[cfg(not(ossl300))] use openssl_macros::corresponds; /// Type of encryption padding to use. @@ -72,14 +99,19 @@ generic_foreign_type_and_impl_send_sync! { /// Reference to `RSA` pub struct RsaRef; + + key_id = Id::RSA; + pkey_type = rsa; } +#[cfg(not(ossl300))] impl Clone for Rsa { fn clone(&self) -> Rsa { (**self).to_owned() } } +#[cfg(not(ossl300))] impl ToOwned for RsaRef { type Owned = Rsa; @@ -99,20 +131,24 @@ where /// Serializes the private key to a PEM-encoded PKCS#1 RSAPrivateKey structure. /// /// The output will have a header of `-----BEGIN RSA PRIVATE KEY-----`. - #[corresponds(PEM_write_bio_RSAPrivateKey)] + #[cfg_attr(not(ossl300), corresponds(PEM_write_bio_RSAPrivateKey))] private_key_to_pem, /// Serializes the private key to a PEM-encoded encrypted PKCS#1 RSAPrivateKey structure. /// /// The output will have a header of `-----BEGIN RSA PRIVATE KEY-----`. - #[corresponds(PEM_write_bio_RSAPrivateKey)] + #[cfg_attr(not(ossl300), corresponds(PEM_write_bio_RSAPrivateKey))] private_key_to_pem_passphrase, + Selection::Keypair, + Structure::PKCS1, ffi::PEM_write_bio_RSAPrivateKey } to_der! { /// Serializes the private key to a DER-encoded PKCS#1 RSAPrivateKey structure. - #[corresponds(i2d_RSAPrivateKey)] + #[cfg_attr(not(ossl300), corresponds(i2d_RSAPrivateKey))] private_key_to_der, + Selection::Keypair, + Structure::TypeSpecific, ffi::i2d_RSAPrivateKey } @@ -122,7 +158,6 @@ where /// /// Panics if `self` has no private components, or if `to` is smaller /// than `self.size()`. - #[corresponds(RSA_private_decrypt)] pub fn private_decrypt( &self, from: &[u8], @@ -132,16 +167,11 @@ where assert!(from.len() <= i32::MAX as usize); assert!(to.len() >= self.size() as usize); - unsafe { - let len = cvt_n(ffi::RSA_private_decrypt( - from.len() as LenType, - from.as_ptr(), - to.as_mut_ptr(), - self.as_ptr(), - padding.0, - ))?; - Ok(len as usize) - } + let pkey: PKey = self.into(); + let mut ctx = PkeyCtx::new(&pkey)?; + ctx.decrypt_init()?; + ctx.set_rsa_padding(padding)?; + ctx.decrypt(from, Some(to)) } /// Encrypts data using the private key, returning the number of encrypted bytes. @@ -150,7 +180,6 @@ where /// /// Panics if `self` has no private components, or if `to` is smaller /// than `self.size()`. - #[corresponds(RSA_private_encrypt)] pub fn private_encrypt( &self, from: &[u8], @@ -160,21 +189,20 @@ where assert!(from.len() <= i32::MAX as usize); assert!(to.len() >= self.size() as usize); - unsafe { - let len = cvt_n(ffi::RSA_private_encrypt( - from.len() as LenType, - from.as_ptr(), - to.as_mut_ptr(), - self.as_ptr(), - padding.0, - ))?; - Ok(len as usize) - } + let pkey: PKey = self.into(); + let mut ctx = PkeyCtx::new(&pkey)?; + ctx.sign_init()?; + ctx.set_rsa_padding(padding)?; + ctx.sign(from, Some(to)) } /// Returns a reference to the private exponent of the key. - #[corresponds(RSA_get0_key)] + #[cfg_attr(not(ossl300), corresponds(RSA_get0_key))] pub fn d(&self) -> &BigNumRef { + #[cfg(ossl300)] + return self.0.get_bn_param(OSSL_PKEY_PARAM_RSA_D).unwrap(); + + #[cfg(not(ossl300))] unsafe { let mut d = ptr::null(); RSA_get0_key(self.as_ptr(), ptr::null_mut(), ptr::null_mut(), &mut d); @@ -183,8 +211,12 @@ where } /// Returns a reference to the first factor of the exponent of the key. - #[corresponds(RSA_get0_factors)] + #[cfg_attr(not(ossl300), corresponds(RSA_get0_factors))] pub fn p(&self) -> Option<&BigNumRef> { + #[cfg(ossl300)] + return self.0.get_bn_param(OSSL_PKEY_PARAM_RSA_FACTOR1).ok(); + + #[cfg(not(ossl300))] unsafe { let mut p = ptr::null(); RSA_get0_factors(self.as_ptr(), &mut p, ptr::null_mut()); @@ -193,8 +225,12 @@ where } /// Returns a reference to the second factor of the exponent of the key. - #[corresponds(RSA_get0_factors)] + #[cfg_attr(not(ossl300), corresponds(RSA_get0_factors))] pub fn q(&self) -> Option<&BigNumRef> { + #[cfg(ossl300)] + return self.0.get_bn_param(OSSL_PKEY_PARAM_RSA_FACTOR2).ok(); + + #[cfg(not(ossl300))] unsafe { let mut q = ptr::null(); RSA_get0_factors(self.as_ptr(), ptr::null_mut(), &mut q); @@ -203,8 +239,12 @@ where } /// Returns a reference to the first exponent used for CRT calculations. - #[corresponds(RSA_get0_crt_params)] + #[cfg_attr(not(ossl300), corresponds(RSA_get0_crt_params))] pub fn dmp1(&self) -> Option<&BigNumRef> { + #[cfg(ossl300)] + return self.0.get_bn_param(OSSL_PKEY_PARAM_RSA_EXPONENT1).ok(); + + #[cfg(not(ossl300))] unsafe { let mut dp = ptr::null(); RSA_get0_crt_params(self.as_ptr(), &mut dp, ptr::null_mut(), ptr::null_mut()); @@ -213,8 +253,12 @@ where } /// Returns a reference to the second exponent used for CRT calculations. - #[corresponds(RSA_get0_crt_params)] + #[cfg_attr(not(ossl300), corresponds(RSA_get0_crt_params))] pub fn dmq1(&self) -> Option<&BigNumRef> { + #[cfg(ossl300)] + return self.0.get_bn_param(OSSL_PKEY_PARAM_RSA_EXPONENT2).ok(); + + #[cfg(not(ossl300))] unsafe { let mut dq = ptr::null(); RSA_get0_crt_params(self.as_ptr(), ptr::null_mut(), &mut dq, ptr::null_mut()); @@ -223,29 +267,37 @@ where } /// Returns a reference to the coefficient used for CRT calculations. - #[corresponds(RSA_get0_crt_params)] + #[cfg_attr(not(ossl300), corresponds(RSA_get0_crt_params))] pub fn iqmp(&self) -> Option<&BigNumRef> { + #[cfg(ossl300)] + return self.0.get_bn_param(OSSL_PKEY_PARAM_RSA_COEFFICIENT1).ok(); + + #[cfg(not(ossl300))] unsafe { let mut qi = ptr::null(); RSA_get0_crt_params(self.as_ptr(), ptr::null_mut(), ptr::null_mut(), &mut qi); BigNumRef::from_const_ptr_opt(qi) } } +} +impl RsaRef { /// Validates RSA parameters for correctness - #[corresponds(RSA_check_key)] + #[cfg_attr(not(ossl300), corresponds(RSA_check_key))] pub fn check_key(&self) -> Result { - unsafe { - let result = ffi::RSA_check_key(self.as_ptr()); - if result != 1 { - let errors = ErrorStack::get(); + #[cfg(ossl300)] + let result = self.0.check_key(); + + #[cfg(not(ossl300))] + let result = cvt(unsafe { ffi::RSA_check_key(self.as_ptr()) }); + match result { + Ok(_) => Ok(true), + Err(errors) => { if errors.errors().is_empty() { Ok(false) } else { Err(errors) } - } else { - Ok(true) } } } @@ -259,15 +311,19 @@ where /// Serializes the public key into a PEM-encoded SubjectPublicKeyInfo structure. /// /// The output will have a header of `-----BEGIN PUBLIC KEY-----`. - #[corresponds(PEM_write_bio_RSA_PUBKEY)] + #[cfg_attr(not(ossl300), corresponds(PEM_write_bio_RSA_PUBKEY))] public_key_to_pem, + Selection::PublicKey, + Structure::SubjectPublicKeyInfo, ffi::PEM_write_bio_RSA_PUBKEY } to_der! { /// Serializes the public key into a DER-encoded SubjectPublicKeyInfo structure. - #[corresponds(i2d_RSA_PUBKEY)] + #[cfg_attr(not(ossl300), corresponds(i2d_RSA_PUBKEY))] public_key_to_der, + Selection::PublicKey, + Structure::SubjectPublicKeyInfo, ffi::i2d_RSA_PUBKEY } @@ -275,22 +331,32 @@ where /// Serializes the public key into a PEM-encoded PKCS#1 RSAPublicKey structure. /// /// The output will have a header of `-----BEGIN RSA PUBLIC KEY-----`. - #[corresponds(PEM_write_bio_RSAPublicKey)] + #[cfg_attr(not(ossl300), corresponds(PEM_write_bio_RSAPublicKey))] public_key_to_pem_pkcs1, + Selection::PublicKey, + Structure::PKCS1, ffi::PEM_write_bio_RSAPublicKey } to_der! { /// Serializes the public key into a DER-encoded PKCS#1 RSAPublicKey structure. - #[corresponds(i2d_RSAPublicKey)] + #[cfg_attr(not(ossl300), corresponds(i2d_RSAPublicKey))] public_key_to_der_pkcs1, + Selection::PublicKey, + Structure::PKCS1, ffi::i2d_RSAPublicKey } /// Returns the size of the modulus in bytes. - #[corresponds(RSA_size)] + #[cfg_attr(not(ossl300), corresponds(RSA_size))] pub fn size(&self) -> u32 { - unsafe { ffi::RSA_size(self.as_ptr()) as u32 } + #[cfg(ossl300)] + return self.0.size() as u32; + + #[cfg(not(ossl300))] + unsafe { + ffi::RSA_size(self.as_ptr()) as u32 + } } /// Decrypts data using the public key, returning the number of decrypted bytes. @@ -298,7 +364,6 @@ where /// # Panics /// /// Panics if `to` is smaller than `self.size()`. - #[corresponds(RSA_public_decrypt)] pub fn public_decrypt( &self, from: &[u8], @@ -308,16 +373,11 @@ where assert!(from.len() <= i32::MAX as usize); assert!(to.len() >= self.size() as usize); - unsafe { - let len = cvt_n(ffi::RSA_public_decrypt( - from.len() as LenType, - from.as_ptr(), - to.as_mut_ptr(), - self.as_ptr(), - padding.0, - ))?; - Ok(len as usize) - } + let pkey: PKey = self.into(); + let mut ctx = PkeyCtx::new(&pkey)?; + ctx.verify_recover_init()?; + ctx.set_rsa_padding(padding)?; + ctx.verify_recover(from, Some(to)) } /// Encrypts data using the public key, returning the number of encrypted bytes. @@ -325,7 +385,6 @@ where /// # Panics /// /// Panics if `to` is smaller than `self.size()`. - #[corresponds(RSA_public_encrypt)] pub fn public_encrypt( &self, from: &[u8], @@ -335,21 +394,20 @@ where assert!(from.len() <= i32::MAX as usize); assert!(to.len() >= self.size() as usize); - unsafe { - let len = cvt_n(ffi::RSA_public_encrypt( - from.len() as LenType, - from.as_ptr(), - to.as_mut_ptr(), - self.as_ptr(), - padding.0, - ))?; - Ok(len as usize) - } + let pkey: PKey = self.into(); + let mut ctx = PkeyCtx::new(&pkey)?; + ctx.encrypt_init()?; + ctx.set_rsa_padding(padding)?; + ctx.encrypt(from, Some(to)) } /// Returns a reference to the modulus of the key. - #[corresponds(RSA_get0_key)] + #[cfg_attr(not(ossl300), corresponds(RSA_get0_key))] pub fn n(&self) -> &BigNumRef { + #[cfg(ossl300)] + return self.0.get_bn_param(OSSL_PKEY_PARAM_RSA_N).unwrap(); + + #[cfg(not(ossl300))] unsafe { let mut n = ptr::null(); RSA_get0_key(self.as_ptr(), &mut n, ptr::null_mut(), ptr::null_mut()); @@ -358,8 +416,12 @@ where } /// Returns a reference to the public exponent of the key. - #[corresponds(RSA_get0_key)] + #[cfg_attr(not(ossl300), corresponds(RSA_get0_key))] pub fn e(&self) -> &BigNumRef { + #[cfg(ossl300)] + return self.0.get_bn_param(OSSL_PKEY_PARAM_RSA_E).unwrap(); + + #[cfg(not(ossl300))] unsafe { let mut e = ptr::null(); RSA_get0_key(self.as_ptr(), ptr::null_mut(), &mut e, ptr::null_mut()); @@ -373,12 +435,19 @@ impl Rsa { /// /// `n` is the modulus common to both public and private key. /// `e` is the public exponent. - /// - /// This corresponds to [`RSA_new`] and uses [`RSA_set0_key`]. - /// - /// [`RSA_new`]: https://docs.openssl.org/master/man3/RSA_new/ - /// [`RSA_set0_key`]: https://docs.openssl.org/master/man3/RSA_set0_key/ + #[cfg_attr(not(ossl300), corresponds(RSA_new))] + #[cfg_attr(not(ossl300), corresponds(RSA_set0_key))] pub fn from_public_components(n: BigNum, e: BigNum) -> Result, ErrorStack> { + #[cfg(ossl300)] + { + let mut builder = OsslParamBuilder::new()?; + builder.add_bn(OSSL_PKEY_PARAM_RSA_N, &n)?; + builder.add_bn(OSSL_PKEY_PARAM_RSA_E, &e)?; + let params = builder.to_param()?; + pkey_from_params(Id::RSA, ¶ms)?.rsa() + } + + #[cfg(not(ossl300))] unsafe { let rsa = cvt_p(ffi::RSA_new())?; RSA_set0_key(rsa, n.as_ptr(), e.as_ptr(), ptr::null_mut()); @@ -391,9 +460,10 @@ impl Rsa { /// Decodes a PEM-encoded SubjectPublicKeyInfo structure containing an RSA key. /// /// The input should have a header of `-----BEGIN PUBLIC KEY-----`. - #[corresponds(PEM_read_bio_RSA_PUBKEY)] + #[cfg_attr(not(ossl300), corresponds(PEM_read_bio_RSA_PUBKEY))] public_key_from_pem, Rsa, + Structure::SubjectPublicKeyInfo, ffi::PEM_read_bio_RSA_PUBKEY } @@ -401,30 +471,37 @@ impl Rsa { /// Decodes a PEM-encoded PKCS#1 RSAPublicKey structure. /// /// The input should have a header of `-----BEGIN RSA PUBLIC KEY-----`. - #[corresponds(PEM_read_bio_RSAPublicKey)] + #[cfg_attr(not(ossl300), corresponds(PEM_read_bio_RSAPublicKey))] public_key_from_pem_pkcs1, Rsa, + Structure::PKCS1, ffi::PEM_read_bio_RSAPublicKey } from_der! { /// Decodes a DER-encoded SubjectPublicKeyInfo structure containing an RSA key. - #[corresponds(d2i_RSA_PUBKEY)] + #[cfg_attr(not(ossl300), corresponds(d2i_RSA_PUBKEY))] public_key_from_der, Rsa, + Structure::SubjectPublicKeyInfo, ffi::d2i_RSA_PUBKEY } from_der! { /// Decodes a DER-encoded PKCS#1 RSAPublicKey structure. - #[corresponds(d2i_RSAPublicKey)] + #[cfg_attr(not(ossl300), corresponds(d2i_RSAPublicKey))] public_key_from_der_pkcs1, Rsa, + Structure::PKCS1, ffi::d2i_RSAPublicKey } } pub struct RsaPrivateKeyBuilder { + #[cfg(ossl300)] + params: HashMap<&'static CStr, BigNum>, + + #[cfg(not(ossl300))] rsa: Rsa, } @@ -433,12 +510,19 @@ impl RsaPrivateKeyBuilder { /// /// `n` is the modulus common to both public and private key. /// `e` is the public exponent and `d` is the private exponent. - /// - /// This corresponds to [`RSA_new`] and uses [`RSA_set0_key`]. - /// - /// [`RSA_new`]: https://docs.openssl.org/master/man3/RSA_new/ - /// [`RSA_set0_key`]: https://docs.openssl.org/master/man3/RSA_set0_key/ + #[cfg_attr(not(ossl300), corresponds(RSA_new))] + #[cfg_attr(not(ossl300), corresponds(RSA_set0_key))] pub fn new(n: BigNum, e: BigNum, d: BigNum) -> Result { + #[cfg(ossl300)] + { + let mut params = HashMap::new(); + params.insert(OSSL_PKEY_PARAM_RSA_N, n); + params.insert(OSSL_PKEY_PARAM_RSA_E, e); + params.insert(OSSL_PKEY_PARAM_RSA_D, d); + Ok(RsaPrivateKeyBuilder { params }) + } + + #[cfg(not(ossl300))] unsafe { let rsa = cvt_p(ffi::RSA_new())?; RSA_set0_key(rsa, n.as_ptr(), e.as_ptr(), d.as_ptr()); @@ -452,21 +536,32 @@ impl RsaPrivateKeyBuilder { /// Sets the factors of the Rsa key. /// /// `p` and `q` are the first and second factors of `n`. - #[corresponds(RSA_set0_factors)] + #[cfg_attr(not(ossl300), corresponds(RSA_set0_factors))] // FIXME should be infallible pub fn set_factors(self, p: BigNum, q: BigNum) -> Result { - unsafe { - RSA_set0_factors(self.rsa.as_ptr(), p.as_ptr(), q.as_ptr()); + #[cfg(ossl300)] + { + let mut params = self.params; + params.insert(OSSL_PKEY_PARAM_RSA_FACTOR1, p); + params.insert(OSSL_PKEY_PARAM_RSA_FACTOR2, q); + Ok(RsaPrivateKeyBuilder { params }) + } + + #[cfg(not(ossl300))] + { + unsafe { + RSA_set0_factors(self.rsa.as_ptr(), p.as_ptr(), q.as_ptr()); + } mem::forget((p, q)); + Ok(self) } - Ok(self) } /// Sets the Chinese Remainder Theorem params of the Rsa key. /// /// `dmp1`, `dmq1`, and `iqmp` are the exponents and coefficient for /// CRT calculations which is used to speed up RSA operations. - #[corresponds(RSA_set0_crt_params)] + #[cfg_attr(not(ossl300), corresponds(RSA_set0_crt_params))] // FIXME should be infallible pub fn set_crt_params( self, @@ -474,20 +569,43 @@ impl RsaPrivateKeyBuilder { dmq1: BigNum, iqmp: BigNum, ) -> Result { - unsafe { - RSA_set0_crt_params( - self.rsa.as_ptr(), - dmp1.as_ptr(), - dmq1.as_ptr(), - iqmp.as_ptr(), - ); + #[cfg(ossl300)] + { + let mut params = self.params; + params.insert(OSSL_PKEY_PARAM_RSA_EXPONENT1, dmp1); + params.insert(OSSL_PKEY_PARAM_RSA_EXPONENT2, dmq1); + params.insert(OSSL_PKEY_PARAM_RSA_COEFFICIENT1, iqmp); + Ok(RsaPrivateKeyBuilder { params }) + } + + #[cfg(not(ossl300))] + { + unsafe { + RSA_set0_crt_params( + self.rsa.as_ptr(), + dmp1.as_ptr(), + dmq1.as_ptr(), + iqmp.as_ptr(), + ); + } mem::forget((dmp1, dmq1, iqmp)); + Ok(self) } - Ok(self) } /// Returns the Rsa key. pub fn build(self) -> Rsa { + #[cfg(ossl300)] + return { + let mut builder = OsslParamBuilder::new().unwrap(); + for (k, v) in &self.params { + builder.add_bn(k, v).unwrap(); + } + let params = builder.to_param().unwrap(); + pkey_from_params(Id::RSA, ¶ms).unwrap().rsa().unwrap() + }; + + #[cfg(not(ossl300))] self.rsa } } @@ -527,7 +645,7 @@ impl Rsa { /// Generates a public/private key pair with the specified size. /// /// The public exponent will be 65537. - #[corresponds(RSA_generate_key_ex)] + #[cfg_attr(not(ossl300), corresponds(RSA_generate_key_ex))] pub fn generate(bits: u32) -> Result, ErrorStack> { let e = BigNum::from_u32(ffi::RSA_F4 as u32)?; Rsa::generate_with_e(bits, &e) @@ -536,44 +654,41 @@ impl Rsa { /// Generates a public/private key pair with the specified size and a custom exponent. /// /// Unless you have specific needs and know what you're doing, use `Rsa::generate` instead. - #[corresponds(RSA_generate_key_ex)] + #[cfg_attr(not(ossl300), corresponds(RSA_generate_key_ex))] pub fn generate_with_e(bits: u32, e: &BigNumRef) -> Result, ErrorStack> { - unsafe { - let rsa = Rsa::from_ptr(cvt_p(ffi::RSA_new())?); - cvt(ffi::RSA_generate_key_ex( - rsa.0, - bits as c_int, - e.as_ptr(), - ptr::null_mut(), - ))?; - Ok(rsa) - } + let mut ctx = PkeyCtx::new_id(Id::RSA)?; + ctx.keygen_init()?; + ctx.set_rsa_keygen_bits(bits)?; + ctx.set_rsa_keygen_pubexp(e)?; + ctx.keygen()?.rsa() } // FIXME these need to identify input formats private_key_from_pem! { /// Deserializes a private key from a PEM-encoded PKCS#1 RSAPrivateKey structure. - #[corresponds(PEM_read_bio_RSAPrivateKey)] + #[cfg_attr(not(ossl300), corresponds(PEM_read_bio_RSAPrivateKey))] private_key_from_pem, /// Deserializes a private key from a PEM-encoded encrypted PKCS#1 RSAPrivateKey structure. - #[corresponds(PEM_read_bio_RSAPrivateKey)] + #[cfg_attr(not(ossl300), corresponds(PEM_read_bio_RSAPrivateKey))] private_key_from_pem_passphrase, /// Deserializes a private key from a PEM-encoded encrypted PKCS#1 RSAPrivateKey structure. /// /// The callback should fill the password into the provided buffer and return its length. - #[corresponds(PEM_read_bio_RSAPrivateKey)] + #[cfg_attr(not(ossl300), corresponds(PEM_read_bio_RSAPrivateKey))] private_key_from_pem_callback, Rsa, + Structure::PKCS1, ffi::PEM_read_bio_RSAPrivateKey } from_der! { /// Decodes a DER-encoded PKCS#1 RSAPrivateKey structure. - #[corresponds(d2i_RSAPrivateKey)] + #[cfg_attr(not(ossl300), corresponds(d2i_RSAPrivateKey))] private_key_from_der, Rsa, + Structure::PKCS1, ffi::d2i_RSAPrivateKey } } @@ -584,6 +699,7 @@ impl fmt::Debug for Rsa { } } +#[cfg(not(ossl300))] cfg_if! { if #[cfg(any(ossl110, libressl273, boringssl, awslc))] { use ffi::{ @@ -858,6 +974,8 @@ mod test { } #[test] + // for some reason this doesn't panic on 3.0, or 3.5, but does on 3.2-3.4 ¯\_(ツ)_/¯ + #[cfg_attr(any(ossl320, not(ossl350)), ignore)] fn test_public_key_from_pem_pkcs1_file_panic() { let key = include_bytes!("../test/key.pem.pub"); assert!(Rsa::public_key_from_pem_pkcs1(key).is_err()); @@ -898,6 +1016,8 @@ mod test { } #[test] + #[cfg_attr(ossl300, ignore)] + // OSSL 3.0 encoder will happily load a non-PKCS1 structure fn test_public_key_from_pem_pkcs1_generate_panic() { assert!(Rsa::public_key_from_der_pkcs1(include_bytes!("../test/rsa.pub.der")).is_err()); } From ee3bfa0b2e13fa4177111f8aa3e4debfc5b3a247 Mon Sep 17 00:00:00 2001 From: Huw Jones Date: Thu, 21 Aug 2025 16:49:27 +0100 Subject: [PATCH 31/42] dsa: use non-deprecated functions for ossl300+ --- openssl/src/dsa.rs | 166 ++++++++++++++++++++++++++++++++++++-------- openssl/src/pkey.rs | 26 +++++-- 2 files changed, 157 insertions(+), 35 deletions(-) diff --git a/openssl/src/dsa.rs b/openssl/src/dsa.rs index fbb2b077c..c22d939ff 100644 --- a/openssl/src/dsa.rs +++ b/openssl/src/dsa.rs @@ -7,17 +7,34 @@ use cfg_if::cfg_if; use foreign_types::{ForeignType, ForeignTypeRef}; -#[cfg(not(any(boringssl, awslc)))] +#[cfg(not(any(ossl110, libressl273, boringssl, awslc)))] use libc::c_int; use std::fmt; use std::mem; use std::ptr; use crate::bn::{BigNum, BigNumRef}; +#[cfg(not(ossl300))] +use crate::cvt; +use crate::cvt_p; use crate::error::ErrorStack; +#[cfg(ossl300)] +use crate::ossl_encdec::Structure; +#[cfg(ossl300)] +use crate::ossl_param::OsslParamBuilder; +#[cfg(not(boringssl))] +use crate::pkey::Id; use crate::pkey::{HasParams, HasPrivate, HasPublic, Params, Private, Public}; +#[cfg(ossl300)] +use crate::pkey::{ + OSSL_PKEY_PARAM_FFC_G, OSSL_PKEY_PARAM_FFC_P, OSSL_PKEY_PARAM_FFC_Q, OSSL_PKEY_PARAM_PRIV_KEY, + OSSL_PKEY_PARAM_PUB_KEY, +}; +#[cfg(not(boringssl))] +use crate::pkey_ctx::PkeyCtx; +#[cfg(ossl300)] +use crate::pkey_ctx::{pkey_from_params, Selection}; use crate::util::ForeignTypeRefExt; -use crate::{cvt, cvt_p}; use openssl_macros::corresponds; generic_foreign_type_and_impl_send_sync! { @@ -60,14 +77,19 @@ generic_foreign_type_and_impl_send_sync! { /// /// [`Dsa`]: struct.Dsa.html pub struct DsaRef; + + key_id = Id::DSA; + pkey_type = dsa; } +#[cfg(not(ossl300))] impl Clone for Dsa { fn clone(&self) -> Dsa { (**self).to_owned() } } +#[cfg(not(ossl300))] impl ToOwned for DsaRef { type Owned = Dsa; @@ -87,21 +109,29 @@ where /// Serializes the public key into a PEM-encoded SubjectPublicKeyInfo structure. /// /// The output will have a header of `-----BEGIN PUBLIC KEY-----`. - #[corresponds(PEM_write_bio_DSA_PUBKEY)] + #[cfg_attr(not(ossl300), corresponds(PEM_write_bio_DSA_PUBKEY))] public_key_to_pem, + Selection::PublicKey, + Structure::SubjectPublicKeyInfo, ffi::PEM_write_bio_DSA_PUBKEY } to_der! { /// Serializes the public key into a DER-encoded SubjectPublicKeyInfo structure. - #[corresponds(i2d_DSA_PUBKEY)] + #[cfg_attr(not(ossl300), corresponds(i2d_DSA_PUBKEY))] public_key_to_der, + Selection::PublicKey, + Structure::SubjectPublicKeyInfo, ffi::i2d_DSA_PUBKEY } /// Returns a reference to the public key component of `self`. - #[corresponds(DSA_get0_key)] + #[cfg_attr(not(ossl300), corresponds(DSA_get0_key))] pub fn pub_key(&self) -> &BigNumRef { + #[cfg(ossl300)] + return self.0.get_bn_param(OSSL_PKEY_PARAM_PUB_KEY).unwrap(); + + #[cfg(not(ossl300))] unsafe { let mut pub_key = ptr::null(); DSA_get0_key(self.as_ptr(), &mut pub_key, ptr::null_mut()); @@ -118,26 +148,34 @@ where /// Serializes the private key to a PEM-encoded DSAPrivateKey structure. /// /// The output will have a header of `-----BEGIN DSA PRIVATE KEY-----`. - #[corresponds(PEM_write_bio_DSAPrivateKey)] + #[cfg_attr(not(ossl300), corresponds(PEM_write_bio_DSAPrivateKey))] private_key_to_pem, /// Serializes the private key to a PEM-encoded encrypted DSAPrivateKey structure. /// /// The output will have a header of `-----BEGIN DSA PRIVATE KEY-----`. - #[corresponds(PEM_write_bio_DSAPrivateKey)] + #[cfg_attr(not(ossl300), corresponds(PEM_write_bio_DSAPrivateKey))] private_key_to_pem_passphrase, + Selection::Keypair, + Structure::TypeSpecific, ffi::PEM_write_bio_DSAPrivateKey } to_der! { /// Serializes the private_key to a DER-encoded `DSAPrivateKey` structure. - #[corresponds(i2d_DSAPrivateKey)] + #[cfg_attr(not(ossl300), corresponds(i2d_DSAPrivateKey))] private_key_to_der, + Selection::Keypair, + Structure::TypeSpecific, ffi::i2d_DSAPrivateKey } /// Returns a reference to the private key component of `self`. - #[corresponds(DSA_get0_key)] + #[cfg_attr(not(ossl300), corresponds(DSA_get0_key))] pub fn priv_key(&self) -> &BigNumRef { + #[cfg(ossl300)] + return self.0.get_bn_param(OSSL_PKEY_PARAM_PRIV_KEY).unwrap(); + + #[cfg(not(ossl300))] unsafe { let mut priv_key = ptr::null(); DSA_get0_key(self.as_ptr(), ptr::null_mut(), &mut priv_key); @@ -151,14 +189,24 @@ where T: HasParams, { /// Returns the maximum size of the signature output by `self` in bytes. - #[corresponds(DSA_size)] + #[cfg_attr(not(ossl300), corresponds(DSA_size))] pub fn size(&self) -> u32 { - unsafe { ffi::DSA_size(self.as_ptr()) as u32 } + #[cfg(ossl300)] + return self.0.size() as u32; + + #[cfg(not(ossl300))] + unsafe { + ffi::DSA_size(self.as_ptr()) as u32 + } } /// Returns the DSA prime parameter of `self`. - #[corresponds(DSA_get0_pqg)] + #[cfg_attr(not(ossl300), corresponds(DSA_get0_pqg))] pub fn p(&self) -> &BigNumRef { + #[cfg(ossl300)] + return self.0.get_bn_param(OSSL_PKEY_PARAM_FFC_P).unwrap(); + + #[cfg(not(ossl300))] unsafe { let mut p = ptr::null(); DSA_get0_pqg(self.as_ptr(), &mut p, ptr::null_mut(), ptr::null_mut()); @@ -167,8 +215,12 @@ where } /// Returns the DSA sub-prime parameter of `self`. - #[corresponds(DSA_get0_pqg)] + #[cfg_attr(not(ossl300), corresponds(DSA_get0_pqg))] pub fn q(&self) -> &BigNumRef { + #[cfg(ossl300)] + return self.0.get_bn_param(OSSL_PKEY_PARAM_FFC_Q).unwrap(); + + #[cfg(not(ossl300))] unsafe { let mut q = ptr::null(); DSA_get0_pqg(self.as_ptr(), ptr::null_mut(), &mut q, ptr::null_mut()); @@ -177,8 +229,12 @@ where } /// Returns the DSA base parameter of `self`. - #[corresponds(DSA_get0_pqg)] + #[cfg_attr(not(ossl300), corresponds(DSA_get0_pqg))] pub fn g(&self) -> &BigNumRef { + #[cfg(ossl300)] + return self.0.get_bn_param(OSSL_PKEY_PARAM_FFC_G).unwrap(); + + #[cfg(not(ossl300))] unsafe { let mut g = ptr::null(); DSA_get0_pqg(self.as_ptr(), ptr::null_mut(), ptr::null_mut(), &mut g); @@ -186,15 +242,22 @@ where } } } -#[cfg(any(boringssl, awslc))] -type BitType = libc::c_uint; -#[cfg(not(any(boringssl, awslc)))] -type BitType = c_int; impl Dsa { /// Creates a DSA params based upon the given parameters. - #[corresponds(DSA_set0_pqg)] + #[cfg_attr(not(ossl300), corresponds(DSA_set0_pqg))] pub fn from_pqg(p: BigNum, q: BigNum, g: BigNum) -> Result, ErrorStack> { + #[cfg(ossl300)] + return { + let mut builder = OsslParamBuilder::new()?; + builder.add_bn(OSSL_PKEY_PARAM_FFC_P, &p)?; + builder.add_bn(OSSL_PKEY_PARAM_FFC_Q, &q)?; + builder.add_bn(OSSL_PKEY_PARAM_FFC_G, &g)?; + let params = builder.to_param()?; + pkey_from_params(Id::DSA, ¶ms)?.dsa() + }; + + #[cfg(not(ossl300))] unsafe { let dsa = Dsa::from_ptr(cvt_p(ffi::DSA_new())?); cvt(DSA_set0_pqg(dsa.0, p.as_ptr(), q.as_ptr(), g.as_ptr()))?; @@ -204,14 +267,23 @@ impl Dsa { } /// Generates DSA params based on the given number of bits. - #[corresponds(DSA_generate_parameters_ex)] + #[cfg_attr(not(ossl300), corresponds(DSA_generate_parameters_ex))] + #[cfg(not(boringssl))] + pub fn generate_params(bits: u32) -> Result, ErrorStack> { + let mut ctx = PkeyCtx::new_id(Id::DSA)?; + ctx.paramgen_init()?; + ctx.set_dsa_paramgen_bits(bits)?; + ctx.paramgen()?.dsa() + } + + /// Generates DSA params based on the given number of bits. + #[cfg(boringssl)] pub fn generate_params(bits: u32) -> Result, ErrorStack> { - ffi::init(); unsafe { let dsa = Dsa::from_ptr(cvt_p(ffi::DSA_new())?); cvt(ffi::DSA_generate_parameters_ex( dsa.0, - bits as BitType, + bits as libc::c_uint, ptr::null(), 0, ptr::null_mut(), @@ -223,8 +295,16 @@ impl Dsa { } /// Generates a private key based on the DSA params. - #[corresponds(DSA_generate_key)] + #[cfg_attr(not(ossl300), corresponds(DSA_generate_key))] pub fn generate_key(self) -> Result, ErrorStack> { + #[cfg(ossl300)] + return { + let mut ctx = PkeyCtx::new(&self.0)?; + ctx.keygen_init()?; + ctx.keygen()?.dsa() + }; + + #[cfg(not(ossl300))] unsafe { let dsa_ptr = self.0; cvt(ffi::DSA_generate_key(dsa_ptr))?; @@ -255,7 +335,19 @@ impl Dsa { priv_key: BigNum, pub_key: BigNum, ) -> Result, ErrorStack> { - ffi::init(); + #[cfg(ossl300)] + return { + let mut builder = OsslParamBuilder::new()?; + builder.add_bn(OSSL_PKEY_PARAM_FFC_P, &p)?; + builder.add_bn(OSSL_PKEY_PARAM_FFC_Q, &q)?; + builder.add_bn(OSSL_PKEY_PARAM_FFC_G, &g)?; + builder.add_bn(OSSL_PKEY_PARAM_PRIV_KEY, &priv_key)?; + builder.add_bn(OSSL_PKEY_PARAM_PUB_KEY, &pub_key)?; + let params = builder.to_param()?; + pkey_from_params(Id::DSA, ¶ms)?.dsa() + }; + + #[cfg(not(ossl300))] unsafe { let dsa = Dsa::from_ptr(cvt_p(ffi::DSA_new())?); cvt(DSA_set0_pqg(dsa.0, p.as_ptr(), q.as_ptr(), g.as_ptr()))?; @@ -272,17 +364,19 @@ impl Dsa { /// Decodes a PEM-encoded SubjectPublicKeyInfo structure containing a DSA key. /// /// The input should have a header of `-----BEGIN PUBLIC KEY-----`. - #[corresponds(PEM_read_bio_DSA_PUBKEY)] + #[cfg_attr(not(ossl300), corresponds(PEM_read_bio_DSA_PUBKEY))] public_key_from_pem, Dsa, + Structure::SubjectPublicKeyInfo, ffi::PEM_read_bio_DSA_PUBKEY } from_der! { /// Decodes a DER-encoded SubjectPublicKeyInfo structure containing a DSA key. - #[corresponds(d2i_DSA_PUBKEY)] + #[cfg_attr(not(ossl300), corresponds(d2i_DSA_PUBKEY))] public_key_from_der, Dsa, + Structure::SubjectPublicKeyInfo, ffi::d2i_DSA_PUBKEY } @@ -296,7 +390,18 @@ impl Dsa { g: BigNum, pub_key: BigNum, ) -> Result, ErrorStack> { - ffi::init(); + #[cfg(ossl300)] + return { + let mut builder = OsslParamBuilder::new()?; + builder.add_bn(OSSL_PKEY_PARAM_FFC_P, &p)?; + builder.add_bn(OSSL_PKEY_PARAM_FFC_Q, &q)?; + builder.add_bn(OSSL_PKEY_PARAM_FFC_G, &g)?; + builder.add_bn(OSSL_PKEY_PARAM_PUB_KEY, &pub_key)?; + let params = builder.to_param()?; + pkey_from_params(Id::DSA, ¶ms)?.dsa() + }; + + #[cfg(not(ossl300))] unsafe { let dsa = Dsa::from_ptr(cvt_p(ffi::DSA_new())?); cvt(DSA_set0_pqg(dsa.0, p.as_ptr(), q.as_ptr(), g.as_ptr()))?; @@ -314,6 +419,7 @@ impl fmt::Debug for Dsa { } } +#[cfg(not(ossl300))] cfg_if! { if #[cfg(any(ossl110, libressl273, boringssl, awslc))] { use ffi::{DSA_get0_key, DSA_get0_pqg, DSA_set0_key, DSA_set0_pqg}; @@ -699,7 +805,7 @@ mod test { #[test] fn test_private_key_to_pem() { - let key = Dsa::generate(512).unwrap(); + let key = Dsa::generate(1024).unwrap(); let pem = key.private_key_to_pem().unwrap(); let pem_str = from_utf8(&pem).unwrap(); assert!( @@ -710,7 +816,7 @@ mod test { #[test] fn test_private_key_to_pem_password() { - let key = Dsa::generate(512).unwrap(); + let key = Dsa::generate(1024).unwrap(); let pem = key .private_key_to_pem_passphrase(Cipher::aes_128_cbc(), b"foobar") .unwrap(); @@ -725,7 +831,7 @@ mod test { #[test] fn test_private_key_to_der() { - let key = Dsa::generate(512).unwrap(); + let key = Dsa::generate(1024).unwrap(); key.private_key_to_der().unwrap(); } diff --git a/openssl/src/pkey.rs b/openssl/src/pkey.rs index cc92c6bfe..8ffd02876 100644 --- a/openssl/src/pkey.rs +++ b/openssl/src/pkey.rs @@ -298,10 +298,17 @@ impl PKeyRef { /// Returns a copy of the internal DSA key. #[corresponds(EVP_PKEY_get1_DSA)] pub fn dsa(&self) -> Result, ErrorStack> { - unsafe { - let dsa = cvt_p(ffi::EVP_PKEY_get1_DSA(self.as_ptr()))?; - Ok(Dsa::from_ptr(dsa)) + if self.id() != Id::DSA { + return Err(ErrorStack::get()); } + + let dsa = self.as_ptr(); + #[cfg(ossl300)] + cvt(unsafe { ffi::EVP_PKEY_up_ref(dsa) })?; + #[cfg(not(ossl300))] + let dsa = cvt_p(unsafe { ffi::EVP_PKEY_get1_DSA(dsa) })?; + + Ok(unsafe { Dsa::from_ptr(dsa) }) } /// Returns a copy of the internal DH key. @@ -647,12 +654,21 @@ impl PKey { /// Creates a new `PKey` containing a DSA key. #[corresponds(EVP_PKEY_set1_DSA)] pub fn from_dsa(dsa: Dsa) -> Result, ErrorStack> { + let pkey: PKey; + + #[cfg(ossl300)] + unsafe { + ffi::EVP_PKEY_up_ref(dsa.as_ptr()); + pkey = PKey::from_ptr(dsa.as_ptr()); + } + + #[cfg(not(ossl300))] unsafe { let evp = cvt_p(ffi::EVP_PKEY_new())?; - let pkey = PKey::from_ptr(evp); + pkey = PKey::from_ptr(evp); cvt(ffi::EVP_PKEY_set1_DSA(pkey.0, dsa.as_ptr()))?; - Ok(pkey) } + Ok(pkey) } /// Creates a new `PKey` containing a Diffie-Hellman key. From a1d6d9703dae03963d954002829b450144d09e3c Mon Sep 17 00:00:00 2001 From: Huw Jones Date: Mon, 8 Sep 2025 11:43:39 +0100 Subject: [PATCH 32/42] macros/generic_foreign_type_and_impl_send_sync: implement From borrowed for PKey --- openssl/src/macros.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/openssl/src/macros.rs b/openssl/src/macros.rs index 3a2c07249..8bd06f980 100644 --- a/openssl/src/macros.rs +++ b/openssl/src/macros.rs @@ -557,6 +557,12 @@ macro_rules! generic_foreign_type_and_impl_send_sync { $(#[$borrowed_attr])* pub struct $borrowed; } + + impl From<&$borrowed> for crate::pkey::PKey { + fn from(value: &$borrowed) -> Self { + crate::pkey::PKey::try_from(value.to_owned()).unwrap() + } + } } } } From a88b4e94a24641912e0e8f4c9174a1ff0d78fa5e Mon Sep 17 00:00:00 2001 From: Huw Jones Date: Tue, 26 Aug 2025 11:43:03 +0100 Subject: [PATCH 33/42] macros: add key_check macro --- openssl/src/macros.rs | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/openssl/src/macros.rs b/openssl/src/macros.rs index 8bd06f980..3ba228ee3 100644 --- a/openssl/src/macros.rs +++ b/openssl/src/macros.rs @@ -579,3 +579,35 @@ macro_rules! cstr_const { $vis const $name: &std::ffi::CStr = unsafe { std::ffi::CStr::from_bytes_with_nul_unchecked($key) }; } } + +macro_rules! key_check { + ($ktype:ident, $f:ident) => { + key_check!($ktype, $f, crate::pkey::Params, crate::pkey::Public, crate::pkey::Private); + }; + + ( + $ktype:ident, + $f:ident, + $($part:path), + + ) => { + #[cfg(ossl300)] + use crate::pkey::KeyCheck; + + $( + impl $ktype<$part> { + /// Checks the key for validity. + #[cfg(ossl300)] + pub fn check_key(&self) -> Result<(), ErrorStack> { + self.0.check_key() + } + + /// Checks the key for validity. + #[corresponds($f)] + #[cfg(not(ossl300))] + pub fn check_key(&self) -> Result<(), ErrorStack> { + cvt(unsafe {ffi::$f(self.as_ptr())}).map(|_| ()) + } + } + )+ + }; +} From 4167e76467cfb019c3e07de93f008952f90bfaea Mon Sep 17 00:00:00 2001 From: Huw Jones Date: Tue, 26 Aug 2025 18:04:44 +0100 Subject: [PATCH 34/42] ecdsa: use non-deprecated functions --- openssl/src/ec.rs | 8 ++++++- openssl/src/ecdsa.rs | 56 ++++++++++++++++++++++++++------------------ 2 files changed, 40 insertions(+), 24 deletions(-) diff --git a/openssl/src/ec.rs b/openssl/src/ec.rs index 99653f16a..5b65339d7 100644 --- a/openssl/src/ec.rs +++ b/openssl/src/ec.rs @@ -24,7 +24,7 @@ use std::ptr; use crate::bn::{BigNum, BigNumContextRef, BigNumRef}; use crate::error::ErrorStack; use crate::nid::Nid; -use crate::pkey::{HasParams, HasPrivate, HasPublic, Params, Private, Public}; +use crate::pkey::{HasParams, HasPrivate, HasPublic, PKey, Params, Private, Public}; use crate::util::ForeignTypeRefExt; use crate::{cvt, cvt_n, cvt_p, init}; use openssl_macros::corresponds; @@ -715,6 +715,12 @@ generic_foreign_type_and_impl_send_sync! { pub struct EcKeyRef; } +impl From<&EcKeyRef> for PKey { + fn from(value: &EcKeyRef) -> Self { + PKey::from_ec_key(value.to_owned()).unwrap() + } +} + impl EcKeyRef where T: HasPrivate, diff --git a/openssl/src/ecdsa.rs b/openssl/src/ecdsa.rs index 0d962c718..51fd3faae 100644 --- a/openssl/src/ecdsa.rs +++ b/openssl/src/ecdsa.rs @@ -2,18 +2,31 @@ use cfg_if::cfg_if; use foreign_types::{ForeignType, ForeignTypeRef}; -use libc::c_int; use std::mem; use std::ptr; use crate::bn::{BigNum, BigNumRef}; +use crate::cvt_p; use crate::ec::EcKeyRef; use crate::error::ErrorStack; +use crate::hash::MessageDigest; +use crate::pkey::PKey; use crate::pkey::{HasPrivate, HasPublic}; +use crate::sign::{Signer, Verifier}; use crate::util::ForeignTypeRefExt; -use crate::{cvt_n, cvt_p, LenType}; use openssl_macros::corresponds; +// TODO: this should probably be a LazyLock, but that requires 1.80+ +const ECDSA_MD: Option MessageDigest> = { + cfg_if! { + if #[cfg(not(any(boringssl, awslc)))] { + None + } else { + Some(MessageDigest::sha512) + } + } +}; + foreign_type_and_impl_send_sync! { type CType = ffi::ECDSA_SIG; fn drop = ffi::ECDSA_SIG_free; @@ -26,20 +39,18 @@ foreign_type_and_impl_send_sync! { impl EcdsaSig { /// Computes a digital signature of the hash value `data` using the private EC key eckey. - #[corresponds(ECDSA_do_sign)] pub fn sign(data: &[u8], eckey: &EcKeyRef) -> Result where T: HasPrivate, { - unsafe { - assert!(data.len() <= c_int::MAX as usize); - let sig = cvt_p(ffi::ECDSA_do_sign( - data.as_ptr(), - data.len() as LenType, - eckey.as_ptr(), - ))?; - Ok(EcdsaSig::from_ptr(sig)) - } + let pkey: PKey = eckey.into(); + let mut signer = match ECDSA_MD { + Some(md) => Signer::new(md(), &pkey), + None => Signer::new_without_digest(&pkey), + }?; + signer.update(data)?; + let sig = signer.sign_to_vec()?; + EcdsaSig::from_der(&sig) } /// Returns a new `EcdsaSig` by setting the `r` and `s` values associated with an ECDSA signature. @@ -71,21 +82,18 @@ impl EcdsaSigRef { } /// Verifies if the signature is a valid ECDSA signature using the given public key. - #[corresponds(ECDSA_do_verify)] pub fn verify(&self, data: &[u8], eckey: &EcKeyRef) -> Result where T: HasPublic, { - unsafe { - assert!(data.len() <= c_int::MAX as usize); - cvt_n(ffi::ECDSA_do_verify( - data.as_ptr(), - data.len() as LenType, - self.as_ptr(), - eckey.as_ptr(), - )) - .map(|x| x == 1) - } + let pkey: PKey = eckey.into(); + let mut verifier = match ECDSA_MD { + Some(md) => Verifier::new(md(), &pkey), + None => Verifier::new_without_digest(&pkey), + }?; + verifier.update(data)?; + let sig = self.to_der()?; + verifier.verify(&sig) } /// Returns internal component: `r` of an `EcdsaSig`. (See X9.62 or FIPS 186-2) @@ -113,6 +121,8 @@ cfg_if! { if #[cfg(any(ossl110, libressl273, boringssl, awslc))] { use ffi::{ECDSA_SIG_set0, ECDSA_SIG_get0}; } else { + use libc::c_int; + #[allow(bad_style)] unsafe fn ECDSA_SIG_set0( sig: *mut ffi::ECDSA_SIG, From 32801ae1a58ff46681e4d3b872d968811c1928fc Mon Sep 17 00:00:00 2001 From: Huw Jones Date: Tue, 26 Aug 2025 18:18:17 +0100 Subject: [PATCH 35/42] ec: use non-deprecated functions for ossl300+ --- openssl/src/ec.rs | 235 +++++++++++++++++++++++++++++++++++------ openssl/src/pkey.rs | 26 ++++- openssl/src/ssl/mod.rs | 21 +++- 3 files changed, 243 insertions(+), 39 deletions(-) diff --git a/openssl/src/ec.rs b/openssl/src/ec.rs index 5b65339d7..cde7285ce 100644 --- a/openssl/src/ec.rs +++ b/openssl/src/ec.rs @@ -21,10 +21,24 @@ use libc::c_int; use std::fmt; use std::ptr; +#[cfg(ossl300)] +use crate::bn::BigNumContext; use crate::bn::{BigNum, BigNumContextRef, BigNumRef}; use crate::error::ErrorStack; use crate::nid::Nid; -use crate::pkey::{HasParams, HasPrivate, HasPublic, PKey, Params, Private, Public}; +#[cfg(ossl300)] +use crate::ossl_encdec::Structure; +#[cfg(ossl300)] +use crate::ossl_param::OsslParamBuilder; +#[cfg(ossl300)] +use crate::pkey::Id; +#[cfg(ossl300)] +use crate::pkey::PKey; +use crate::pkey::{HasParams, HasPrivate, HasPublic, Params, Private, Public}; +#[cfg(ossl300)] +use crate::pkey::{OSSL_PKEY_PARAM_GROUP_NAME, OSSL_PKEY_PARAM_PRIV_KEY, OSSL_PKEY_PARAM_PUB_KEY}; +#[cfg(ossl300)] +use crate::pkey_ctx::{pkey_from_params, PkeyCtx, Selection}; use crate::util::ForeignTypeRefExt; use crate::{cvt, cvt_n, cvt_p, init}; use openssl_macros::corresponds; @@ -169,6 +183,7 @@ impl EcGroupRef { /// Places the components of a curve over a prime field in the provided `BigNum`s. /// The components make up the formula `y^2 mod p = x^3 + ax + b mod p`. #[corresponds(EC_GROUP_get_curve_GFp)] + #[cfg(not(osslconf = "OPENSSL_NO_DEPRECATED_3_0"))] pub fn components_gfp( &self, p: &mut BigNumRef, @@ -195,7 +210,7 @@ impl EcGroupRef { /// a term in the polynomial. It will be set to 3 `1`s or 5 `1`s depending on /// using a trinomial or pentanomial. #[corresponds(EC_GROUP_get_curve_GF2m)] - #[cfg(not(osslconf = "OPENSSL_NO_EC2M"))] + #[cfg(not(any(osslconf = "OPENSSL_NO_EC2M", osslconf = "OPENSSL_NO_DEPRECATED_3_0")))] pub fn components_gf2m( &self, p: &mut BigNumRef, @@ -542,6 +557,7 @@ impl EcPointRef { /// Places affine coordinates of a curve over a prime field in the provided /// `x` and `y` `BigNum`s #[corresponds(EC_POINT_get_affine_coordinates_GFp)] + #[cfg_attr(ossl300, deprecated(note = "Use affine_coordinates"))] pub fn affine_coordinates_gfp( &self, group: &EcGroupRef, @@ -549,6 +565,10 @@ impl EcPointRef { y: &mut BigNumRef, ctx: &mut BigNumContextRef, ) -> Result<(), ErrorStack> { + #[cfg(ossl300)] + return self.affine_coordinates(group, x, y, ctx); + + #[cfg(not(ossl300))] unsafe { cvt(ffi::EC_POINT_get_affine_coordinates_GFp( group.as_ptr(), @@ -587,6 +607,7 @@ impl EcPointRef { /// Sets affine coordinates of a curve over a prime field using the provided /// `x` and `y` `BigNum`s #[corresponds(EC_POINT_set_affine_coordinates_GFp)] + #[cfg_attr(ossl300, deprecated(note = "Use set_affine_coordinates"))] pub fn set_affine_coordinates_gfp( &mut self, group: &EcGroupRef, @@ -594,6 +615,10 @@ impl EcPointRef { y: &BigNumRef, ctx: &mut BigNumContextRef, ) -> Result<(), ErrorStack> { + #[cfg(ossl300)] + return self.set_affine_coordinates(group, x, y, ctx); + + #[cfg(not(ossl300))] unsafe { cvt(ffi::EC_POINT_set_affine_coordinates_GFp( group.as_ptr(), @@ -609,7 +634,8 @@ impl EcPointRef { /// Places affine coordinates of a curve over a binary field in the provided /// `x` and `y` `BigNum`s #[corresponds(EC_POINT_get_affine_coordinates_GF2m)] - #[cfg(not(osslconf = "OPENSSL_NO_EC2M"))] + #[cfg_attr(ossl300, deprecated(note = "Use set_affine_coordinates"))] + #[cfg(not(any(osslconf = "OPENSSL_NO_EC2M", osslconf = "OPENSSL_NO_DEPRECATED_3_0")))] pub fn affine_coordinates_gf2m( &self, group: &EcGroupRef, @@ -713,12 +739,9 @@ generic_foreign_type_and_impl_send_sync! { pub struct EcKey; /// A reference to an [`EcKey`]. pub struct EcKeyRef; -} -impl From<&EcKeyRef> for PKey { - fn from(value: &EcKeyRef) -> Self { - PKey::from_ec_key(value.to_owned()).unwrap() - } + key_id = Id::EC; + pkey_type = ec_key; } impl EcKeyRef @@ -729,26 +752,34 @@ where /// Serializes the private key to a PEM-encoded ECPrivateKey structure. /// /// The output will have a header of `-----BEGIN EC PRIVATE KEY-----`. - #[corresponds(PEM_write_bio_ECPrivateKey)] + #[cfg_attr(not(ossl300), corresponds(PEM_write_bio_ECPrivateKey))] private_key_to_pem, /// Serializes the private key to a PEM-encoded encrypted ECPrivateKey structure. /// /// The output will have a header of `-----BEGIN EC PRIVATE KEY-----`. - #[corresponds(PEM_write_bio_ECPrivateKey)] + #[cfg_attr(not(ossl300), corresponds(PEM_write_bio_ECPrivateKey))] private_key_to_pem_passphrase, + Selection::Keypair, + Structure::TypeSpecific, ffi::PEM_write_bio_ECPrivateKey } to_der! { /// Serializes the private key into a DER-encoded ECPrivateKey structure. - #[corresponds(i2d_ECPrivateKey)] + #[cfg_attr(not(ossl300), corresponds(i2d_ECPrivateKey))] private_key_to_der, + Selection::Keypair, + Structure::TypeSpecific, ffi::i2d_ECPrivateKey } /// Returns the private key value. #[corresponds(EC_KEY_get0_private_key)] pub fn private_key(&self) -> &BigNumRef { + #[cfg(ossl300)] + return self.0.get_bn_param(OSSL_PKEY_PARAM_PRIV_KEY).unwrap(); + + #[cfg(not(ossl300))] unsafe { let ptr = ffi::EC_KEY_get0_private_key(self.as_ptr()); BigNumRef::from_const_ptr(ptr) @@ -763,6 +794,29 @@ where /// Returns the public key. #[corresponds(EC_KEY_get0_public_key)] pub fn public_key(&self) -> &EcPointRef { + #[cfg(ossl300)] + return { + // Public key comes out as an encoded octet string + // (see https://docs.openssl.org/3.0/man7/EVP_PKEY-EC/#common-ec-parameters) + let pub_key = self + .0 + .get_byte_string_param(OSSL_PKEY_PARAM_PUB_KEY) + .unwrap(); + + // So convert it back to an EcPoint + let point = { + let mut ctx = BigNumContext::new().unwrap(); + EcPoint::from_bytes(self.group(), &pub_key, &mut ctx).unwrap() + }; + + let group = self.group(); + + // And dupe the pointer since we're returning a reference + let ptr = cvt_p(unsafe { ffi::EC_POINT_dup(point.as_ptr(), group.as_ptr()) }).unwrap(); + unsafe { EcPointRef::from_const_ptr(ptr) } + }; + + #[cfg(not(ossl300))] unsafe { let ptr = ffi::EC_KEY_get0_public_key(self.as_ptr()); EcPointRef::from_const_ptr(ptr) @@ -773,15 +827,19 @@ where /// Serializes the public key into a PEM-encoded SubjectPublicKeyInfo structure. /// /// The output will have a header of `-----BEGIN PUBLIC KEY-----`. - #[corresponds(PEM_write_bio_EC_PUBKEY)] + #[cfg_attr(not(ossl300), corresponds(PEM_write_bio_EC_PUBKEY))] public_key_to_pem, + Selection::PublicKey, + Structure::SubjectPublicKeyInfo, ffi::PEM_write_bio_EC_PUBKEY } to_der! { /// Serializes the public key into a DER-encoded SubjectPublicKeyInfo structure. - #[corresponds(i2d_EC_PUBKEY)] + #[cfg_attr(not(ossl300), corresponds(i2d_EC_PUBKEY))] public_key_to_der, + Selection::PublicKey, + Structure::SubjectPublicKeyInfo, ffi::i2d_EC_PUBKEY } } @@ -793,19 +851,37 @@ where /// Returns the key's group. #[corresponds(EC_KEY_get0_group)] pub fn group(&self) -> &EcGroupRef { + #[cfg(ossl300)] + return { + // Name comes out as a UTF8 string (see https://docs.openssl.org/3.0/man7/EVP_PKEY-EC/#common-ec-parameters) + let name = self + .0 + .get_utf8_string_param(OSSL_PKEY_PARAM_GROUP_NAME) + .unwrap(); + let c_name = CString::new(name).unwrap(); + + // So convert it back to a Nid + let nid = Nid::from_raw(unsafe { ffi::OBJ_sn2nid(c_name.as_ptr()) }); + + // To then get the group + let group = EcGroup::from_curve_name(nid).unwrap(); + + // Except we're returning a reference, so dup it, otherwise it'll be freed when we `group` is dropped + let ptr = cvt_p(unsafe { ffi::EC_GROUP_dup(group.as_ptr()) }).unwrap(); + unsafe { EcGroupRef::from_const_ptr(ptr) } + }; + + #[cfg(not(ossl300))] unsafe { let ptr = ffi::EC_KEY_get0_group(self.as_ptr()); EcGroupRef::from_const_ptr(ptr) } } - - /// Checks the key for validity. - #[corresponds(EC_KEY_check_key)] - pub fn check_key(&self) -> Result<(), ErrorStack> { - unsafe { cvt(ffi::EC_KEY_check_key(self.as_ptr())).map(|_| ()) } - } } +key_check!(EcKeyRef, EC_KEY_check_key); + +#[cfg(not(ossl300))] impl ToOwned for EcKeyRef { type Owned = EcKey; @@ -825,6 +901,15 @@ impl EcKey { /// to be provided to the `set_tmp_ecdh` methods on `Ssl` and `SslContextBuilder`. #[corresponds(EC_KEY_new_by_curve_name)] pub fn from_curve_name(nid: Nid) -> Result, ErrorStack> { + #[cfg(ossl300)] + return { + let mut ctx = PkeyCtx::new_id(Id::EC)?; + ctx.paramgen_init()?; + ctx.set_ec_paramgen_curve_nid(nid)?; + ctx.paramgen()?.ec_key() + }; + + #[cfg(not(ossl300))] unsafe { init(); cvt_p(ffi::EC_KEY_new_by_curve_name(nid.as_raw())).map(|p| EcKey::from_ptr(p)) @@ -834,6 +919,10 @@ impl EcKey { /// Constructs an `EcKey` corresponding to a curve. #[corresponds(EC_KEY_set_group)] pub fn from_group(group: &EcGroupRef) -> Result, ErrorStack> { + #[cfg(ossl300)] + return Self::from_curve_name(group.curve_name().unwrap()); + + #[cfg(not(ossl300))] unsafe { cvt_p(ffi::EC_KEY_new()) .map(|p| EcKey::from_ptr(p)) @@ -844,6 +933,12 @@ impl EcKey { } } +#[cfg(ossl300)] +cstr_const!( + OSSL_PKEY_PARAM_EC_POINT_CONVERSION_FORMAT, + b"point-format\0" +); + impl EcKey { /// Constructs an `EcKey` from the specified group with the associated [`EcPoint`]: `public_key`. /// @@ -877,6 +972,24 @@ impl EcKey { group: &EcGroupRef, public_key: &EcPointRef, ) -> Result, ErrorStack> { + #[cfg(ossl300)] + return { + let public_key = { + let mut ctx = BigNumContext::new()?; + public_key.to_bytes(group, PointConversionForm::UNCOMPRESSED, &mut ctx)? + }; + let mut builder = OsslParamBuilder::new()?; + builder.add_utf8_string( + OSSL_PKEY_PARAM_GROUP_NAME, + group.curve_name().unwrap().short_name()?, + )?; + builder.add_utf8_string(OSSL_PKEY_PARAM_EC_POINT_CONVERSION_FORMAT, "uncompressed")?; + builder.add_octet_string(OSSL_PKEY_PARAM_PUB_KEY, public_key.as_slice())?; + let params = builder.to_param()?; + pkey_from_params(Id::EC, ¶ms)?.ec_key() + }; + + #[cfg(not(ossl300))] unsafe { cvt_p(ffi::EC_KEY_new()) .map(|p| EcKey::from_ptr(p)) @@ -900,6 +1013,20 @@ impl EcKey { x: &BigNumRef, y: &BigNumRef, ) -> Result, ErrorStack> { + #[cfg(ossl300)] + return { + // Convert the affine coordinates into an EcPoint + let public_key = { + let mut ctx = BigNumContext::new()?; + let mut point = EcPoint::new(group)?; + point.set_affine_coordinates(group, x, y, &mut ctx)?; + point + }; + + Self::from_public_key(group, &public_key) + }; + + #[cfg(not(ossl300))] unsafe { cvt_p(ffi::EC_KEY_new()) .map(|p| EcKey::from_ptr(p)) @@ -921,17 +1048,19 @@ impl EcKey { /// Decodes a PEM-encoded SubjectPublicKeyInfo structure containing a EC key. /// /// The input should have a header of `-----BEGIN PUBLIC KEY-----`. - #[corresponds(PEM_read_bio_EC_PUBKEY)] + #[cfg_attr(not(ossl300), corresponds(PEM_read_bio_EC_PUBKEY))] public_key_from_pem, EcKey, + Structure::SubjectPublicKeyInfo, ffi::PEM_read_bio_EC_PUBKEY } from_der! { /// Decodes a DER-encoded SubjectPublicKeyInfo structure containing a EC key. - #[corresponds(d2i_EC_PUBKEY)] + #[cfg_attr(not(ossl300), corresponds(d2i_EC_PUBKEY))] public_key_from_der, EcKey, + Structure::SubjectPublicKeyInfo, ffi::d2i_EC_PUBKEY } } @@ -967,6 +1096,10 @@ impl EcKey { /// ``` #[corresponds(EC_KEY_generate_key)] pub fn generate(group: &EcGroupRef) -> Result, ErrorStack> { + #[cfg(ossl300)] + return PKey::ec_gen(group.curve_name().unwrap().short_name()?)?.ec_key(); + + #[cfg(not(ossl300))] unsafe { cvt_p(ffi::EC_KEY_new()) .map(|p| EcKey::from_ptr(p)) @@ -977,13 +1110,32 @@ impl EcKey { } } - /// Constructs an public/private key pair given a curve, a private key and a public key point. + /// Constructs a public/private key pair given a curve, a private key and a public key point. #[corresponds(EC_KEY_set_private_key)] pub fn from_private_components( group: &EcGroupRef, private_number: &BigNumRef, public_key: &EcPointRef, ) -> Result, ErrorStack> { + #[cfg(ossl300)] + return { + let public_key = { + let mut ctx = BigNumContext::new()?; + public_key.to_bytes(group, PointConversionForm::UNCOMPRESSED, &mut ctx)? + }; + let mut builder = OsslParamBuilder::new()?; + builder.add_utf8_string( + OSSL_PKEY_PARAM_GROUP_NAME, + group.curve_name().unwrap().short_name()?, + )?; + builder.add_utf8_string(OSSL_PKEY_PARAM_EC_POINT_CONVERSION_FORMAT, "uncompressed")?; + builder.add_octet_string(OSSL_PKEY_PARAM_PUB_KEY, public_key.as_slice())?; + builder.add_bn(OSSL_PKEY_PARAM_PRIV_KEY, private_number)?; + let params = builder.to_param()?; + pkey_from_params(Id::EC, ¶ms)?.ec_key() + }; + + #[cfg(not(ossl300))] unsafe { cvt_p(ffi::EC_KEY_new()) .map(|p| EcKey::from_ptr(p)) @@ -1011,13 +1163,13 @@ impl EcKey { /// Deserializes a private key from a PEM-encoded ECPrivateKey structure. /// /// The input should have a header of `-----BEGIN EC PRIVATE KEY-----`. - #[corresponds(PEM_read_bio_ECPrivateKey)] + #[cfg_attr(not(ossl300), corresponds(PEM_read_bio_ECPrivateKey))] private_key_from_pem, /// Deserializes a private key from a PEM-encoded encrypted ECPrivateKey structure. /// /// The input should have a header of `-----BEGIN EC PRIVATE KEY-----`. - #[corresponds(PEM_read_bio_ECPrivateKey)] + #[cfg_attr(not(ossl300), corresponds(PEM_read_bio_ECPrivateKey))] private_key_from_pem_passphrase, /// Deserializes a private key from a PEM-encoded encrypted ECPrivateKey structure. @@ -1025,21 +1177,24 @@ impl EcKey { /// The callback should fill the password into the provided buffer and return its length. /// /// The input should have a header of `-----BEGIN EC PRIVATE KEY-----`. - #[corresponds(PEM_read_bio_ECPrivateKey)] + #[cfg_attr(not(ossl300), corresponds(PEM_read_bio_ECPrivateKey))] private_key_from_pem_callback, EcKey, + Structure::TypeSpecific, ffi::PEM_read_bio_ECPrivateKey } from_der! { /// Decodes a DER-encoded elliptic curve private key structure. - #[corresponds(d2i_ECPrivateKey)] + #[cfg_attr(not(ossl300), corresponds(d2i_ECPrivateKey))] private_key_from_der, EcKey, + Structure::TypeSpecific, ffi::d2i_ECPrivateKey } } +#[cfg(not(ossl300))] impl Clone for EcKey { fn clone(&self) -> EcKey { (**self).to_owned() @@ -1199,6 +1354,8 @@ mod test { } #[test] + #[cfg(not(osslconf = "OPENSSL_NO_DEPRECATED_3_0"))] + #[cfg_attr(ossl300, allow(deprecated))] fn ec_point_set_affine_gfp() { set_affine_coords_test(EcPointRef::set_affine_coordinates_gfp) } @@ -1237,9 +1394,16 @@ mod test { "4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5", ) .unwrap(); - gen_point - .set_affine_coordinates_gfp(&group, &gen_x, &gen_y, &mut ctx) - .unwrap(); + { + cfg_if! { + if #[cfg(ossl300)] { + gen_point.set_affine_coordinates(&group, &gen_x, &gen_y, &mut ctx) + } else { + gen_point.set_affine_coordinates_gfp(&group, &gen_x, &gen_y, &mut ctx) + } + } + } + .unwrap(); let order = BigNum::from_hex_str( "FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551", @@ -1429,9 +1593,16 @@ mod test { let mut ybn2 = BigNum::new().unwrap(); let mut ctx = BigNumContext::new().unwrap(); let ec_key_pk = ec_key.public_key(); - ec_key_pk - .affine_coordinates_gfp(&group, &mut xbn2, &mut ybn2, &mut ctx) - .unwrap(); + { + cfg_if! { + if #[cfg(ossl300)] { + ec_key_pk.affine_coordinates(&group, &mut xbn2, &mut ybn2, &mut ctx) + } else { + ec_key_pk.affine_coordinates_gfp(&group, &mut xbn2, &mut ybn2, &mut ctx) + } + } + } + .unwrap(); assert_eq!(xbn2, xbn); assert_eq!(ybn2, ybn); } diff --git a/openssl/src/pkey.rs b/openssl/src/pkey.rs index 8ffd02876..30ba307fb 100644 --- a/openssl/src/pkey.rs +++ b/openssl/src/pkey.rs @@ -323,10 +323,17 @@ impl PKeyRef { /// Returns a copy of the internal elliptic curve key. #[corresponds(EVP_PKEY_get1_EC_KEY)] pub fn ec_key(&self) -> Result, ErrorStack> { - unsafe { - let ec_key = cvt_p(ffi::EVP_PKEY_get1_EC_KEY(self.as_ptr()))?; - Ok(EcKey::from_ptr(ec_key)) + if self.id() != Id::EC { + return Err(ErrorStack::get()); } + + let ec_key = self.as_ptr(); + #[cfg(ossl300)] + cvt(unsafe { ffi::EVP_PKEY_up_ref(ec_key) })?; + #[cfg(not(ossl300))] + let ec_key = cvt_p(unsafe { ffi::EVP_PKEY_get1_EC_KEY(ec_key) })?; + + Ok(unsafe { EcKey::from_ptr(ec_key) }) } /// Returns the `Id` that represents the type of this key. @@ -702,12 +709,21 @@ impl PKey { /// Creates a new `PKey` containing an elliptic curve key. #[corresponds(EVP_PKEY_set1_EC_KEY)] pub fn from_ec_key(ec_key: EcKey) -> Result, ErrorStack> { + let pkey: PKey; + + #[cfg(ossl300)] + unsafe { + ffi::EVP_PKEY_up_ref(ec_key.as_ptr()); + pkey = PKey::from_ptr(ec_key.as_ptr()); + } + + #[cfg(not(ossl300))] unsafe { let evp = cvt_p(ffi::EVP_PKEY_new())?; - let pkey = PKey::from_ptr(evp); + pkey = PKey::from_ptr(evp); cvt(ffi::EVP_PKEY_set1_EC_KEY(pkey.0, ec_key.as_ptr()))?; - Ok(pkey) } + Ok(pkey) } } diff --git a/openssl/src/ssl/mod.rs b/openssl/src/ssl/mod.rs index 1779bec11..901c6205f 100644 --- a/openssl/src/ssl/mod.rs +++ b/openssl/src/ssl/mod.rs @@ -901,7 +901,13 @@ impl SslContextBuilder { /// Sets the parameters to be used during ephemeral elliptic curve Diffie-Hellman key exchange. #[corresponds(SSL_CTX_set_tmp_ecdh)] pub fn set_tmp_ecdh(&mut self, key: &EcKeyRef) -> Result<(), ErrorStack> { - unsafe { cvt(ffi::SSL_CTX_set_tmp_ecdh(self.as_ptr(), key.as_ptr()) as c_int).map(|_| ()) } + #[cfg(ossl300)] + return self.set_groups_list(key.group().curve_name().unwrap().short_name()?); + + #[cfg(not(ossl300))] + unsafe { + cvt(ffi::SSL_CTX_set_tmp_ecdh(self.as_ptr(), key.as_ptr()) as c_int).map(|_| ()) + } } /// Sets the callback which will generate parameters to be used during ephemeral elliptic curve @@ -2480,7 +2486,18 @@ impl SslRef { /// [`SslContextBuilder::set_tmp_ecdh`]: struct.SslContextBuilder.html#method.set_tmp_ecdh #[corresponds(SSL_set_tmp_ecdh)] pub fn set_tmp_ecdh(&mut self, key: &EcKeyRef) -> Result<(), ErrorStack> { - unsafe { cvt(ffi::SSL_set_tmp_ecdh(self.as_ptr(), key.as_ptr()) as c_int).map(|_| ()) } + #[cfg(ossl300)] + return { + let group_name = key.group().curve_name().unwrap().short_name()?; + let c_group_name = CString::new(group_name).unwrap(); + cvt_long(unsafe { ffi::SSL_set1_groups_list(self.as_ptr(), c_group_name.as_ptr()) }) + .map(|_| ()) + }; + + #[cfg(not(ossl300))] + unsafe { + cvt(ffi::SSL_set_tmp_ecdh(self.as_ptr(), key.as_ptr()) as c_int).map(|_| ()) + } } /// Like [`SslContextBuilder::set_tmp_ecdh_callback`]. From 7b5df6c3bd3538e3ece73c07e10941a11812b867 Mon Sep 17 00:00:00 2001 From: Huw Jones Date: Mon, 15 Sep 2025 14:53:20 +0100 Subject: [PATCH 36/42] sha: fixup for no deprecated ossl300+ Skip doctests for deprecated Sha256 interface and direct users to the EVP_MD interfaces. --- openssl/src/sha.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/openssl/src/sha.rs b/openssl/src/sha.rs index 24128904a..8ec8abbaf 100644 --- a/openssl/src/sha.rs +++ b/openssl/src/sha.rs @@ -13,7 +13,10 @@ //! When dealing with data that becomes available in chunks, such as while buffering data from IO, //! you can create a hasher that you can repeatedly update to add bytes to. //! -//! ```rust +//! Code targeting OpenSSL 1.1 or later should use the `EVP_MD` APIs instead as the following +//! low-level digest functions were deprecated in OpenSSL 3.0. See the `openssl::md` module. +//! +//! ```ignore //! use openssl::sha; //! //! let mut hasher = sha::Sha256::new(); @@ -36,6 +39,7 @@ //! println!("Hash = {}", hex::encode(hash)); //! ``` use cfg_if::cfg_if; +#[cfg(not(osslconf = "OPENSSL_NO_DEPRECATED_3_0"))] use libc::c_void; use openssl_macros::corresponds; use std::mem::MaybeUninit; From 6a26932169f19df8a158c4eaa37541d23fa902e9 Mon Sep 17 00:00:00 2001 From: Huw Jones Date: Mon, 15 Sep 2025 14:54:21 +0100 Subject: [PATCH 37/42] bn: fixup unused imports for no deprecated ossl300 --- openssl/src/bn.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/openssl/src/bn.rs b/openssl/src/bn.rs index 2df1b6cee..f9a2abbeb 100644 --- a/openssl/src/bn.rs +++ b/openssl/src/bn.rs @@ -31,9 +31,11 @@ use std::ops::{Add, Deref, Div, Mul, Neg, Rem, Shl, Shr, Sub}; use std::{fmt, ptr}; use crate::asn1::Asn1Integer; +#[cfg(not(osslconf = "OPENSSL_NO_DEPRECATED_3_0"))] +use crate::cvt_n; use crate::error::ErrorStack; use crate::string::OpensslString; -use crate::{cvt, cvt_n, cvt_p, LenType}; +use crate::{cvt, cvt_p, LenType}; use openssl_macros::corresponds; cfg_if! { From bda268c21d993278e55e2bc959a5243f86d81b98 Mon Sep 17 00:00:00 2001 From: Huw Jones Date: Wed, 3 Sep 2025 16:33:29 +0100 Subject: [PATCH 38/42] aes: cfg out for deprecated ossl3 The APIs exposed straight up don't exist, and one should use a CipherCtx instead. --- openssl/src/aes.rs | 8 +------- openssl/src/lib.rs | 1 + 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/openssl/src/aes.rs b/openssl/src/aes.rs index 25de83d5c..2cb06368d 100644 --- a/openssl/src/aes.rs +++ b/openssl/src/aes.rs @@ -23,11 +23,7 @@ //! # Examples #![cfg_attr( - all( - not(boringssl), - not(awslc), - not(osslconf = "OPENSSL_NO_DEPRECATED_3_0") - ), + all(not(boringssl), not(awslc),), doc = r#"\ ## AES IGE ```rust @@ -160,7 +156,6 @@ impl AesKey { /// Panics if `in_` is not the same length as `out`, if that length is not a multiple of 16, or if /// `iv` is not at least 32 bytes. #[cfg(not(any(boringssl, awslc)))] -#[cfg(not(osslconf = "OPENSSL_NO_DEPRECATED_3_0"))] #[corresponds(AES_ige_encrypt)] pub fn aes_ige(in_: &[u8], out: &mut [u8], key: &AesKey, iv: &mut [u8], mode: Mode) { unsafe { @@ -273,7 +268,6 @@ mod test { // From https://www.mgp25.com/AESIGE/ #[test] #[cfg(not(any(boringssl, awslc)))] - #[cfg(not(osslconf = "OPENSSL_NO_DEPRECATED_3_0"))] fn ige_vector_1() { let raw_key = "000102030405060708090A0B0C0D0E0F"; let raw_iv = "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F"; diff --git a/openssl/src/lib.rs b/openssl/src/lib.rs index e7af9d1a3..ab92e9714 100644 --- a/openssl/src/lib.rs +++ b/openssl/src/lib.rs @@ -146,6 +146,7 @@ mod macros; mod bio; #[macro_use] mod util; +#[cfg(not(osslconf = "OPENSSL_NO_DEPRECATED_3_0"))] pub mod aes; pub mod asn1; pub mod base64; From e09a28003a4670bc5a4372f8c1bed6e3e42925e0 Mon Sep 17 00:00:00 2001 From: Huw Jones Date: Fri, 12 Sep 2025 15:18:25 +0100 Subject: [PATCH 39/42] dh: use non-deprecated functions for ossl300+ --- openssl/src/dh.rs | 243 +++++++++++++++++++++++++++++++---- openssl/src/pkey.rs | 44 +++++-- openssl/src/ssl/callbacks.rs | 12 ++ openssl/src/ssl/mod.rs | 38 +++++- openssl/src/ssl/test/mod.rs | 2 + 5 files changed, 302 insertions(+), 37 deletions(-) diff --git a/openssl/src/dh.rs b/openssl/src/dh.rs index 1b2648192..f8d7bc6a2 100644 --- a/openssl/src/dh.rs +++ b/openssl/src/dh.rs @@ -1,14 +1,32 @@ //! Diffie-Hellman key agreement. +#[cfg(not(ossl300))] use cfg_if::cfg_if; use foreign_types::{ForeignType, ForeignTypeRef}; +#[cfg(not(ossl300))] use std::mem; +#[cfg(not(ossl300))] use std::ptr; use crate::bn::{BigNum, BigNumRef}; +#[cfg(not(ossl300))] +use crate::cvt; +use crate::cvt_p; use crate::error::ErrorStack; +#[cfg(ossl300)] +use crate::ossl_encdec::Structure; +#[cfg(ossl300)] +use crate::ossl_param::OsslParamBuilder; +#[cfg(ossl300)] +use crate::pkey::Id; use crate::pkey::{HasParams, HasPrivate, HasPublic, Params, Private, Public}; -use crate::{cvt, cvt_p}; +#[cfg(ossl300)] +use crate::pkey::{ + KeyCheck, PKey, OSSL_PKEY_PARAM_FFC_G, OSSL_PKEY_PARAM_FFC_P, OSSL_PKEY_PARAM_FFC_Q, + OSSL_PKEY_PARAM_GROUP_NAME, OSSL_PKEY_PARAM_PRIV_KEY, OSSL_PKEY_PARAM_PUB_KEY, +}; +#[cfg(ossl300)] +use crate::pkey_ctx::{pkey_from_params, PkeyCtx, Selection}; use openssl_macros::corresponds; generic_foreign_type_and_impl_send_sync! { @@ -18,6 +36,9 @@ generic_foreign_type_and_impl_send_sync! { pub struct Dh; pub struct DhRef; + + key_id = Id::DH; + pkey_type = dh; } impl DhRef @@ -28,29 +49,56 @@ where /// Serializes the parameters into a PEM-encoded PKCS#3 DHparameter structure. /// /// The output will have a header of `-----BEGIN DH PARAMETERS-----`. - #[corresponds(PEM_write_bio_DHparams)] + #[cfg_attr(not(ossl300), corresponds(PEM_write_bio_DHparams))] params_to_pem, + Selection::KeyParameters, + Structure::TypeSpecific, ffi::PEM_write_bio_DHparams } to_der! { /// Serializes the parameters into a DER-encoded PKCS#3 DHparameter structure. - #[corresponds(i2d_DHparams)] + #[cfg_attr(not(ossl300), corresponds(i2d_DHparams))] params_to_der, + Selection::KeyParameters, + Structure::TypeSpecific, ffi::i2d_DHparams } +} - /// Validates DH parameters for correctness - #[corresponds(DH_check_key)] - pub fn check_key(&self) -> Result { - unsafe { - let mut codes = 0; - cvt(ffi::DH_check(self.as_ptr(), &mut codes))?; - Ok(codes == 0) - } - } +macro_rules! key_check { + ( + $ktype:ident, + $f:ident, + $($part:path), + + ) => { + $( + impl $ktype<$part> { + /// Validates DH parameters for correctness + #[cfg(ossl300)] + pub fn check_key(&self) -> Result { + return match self.0.check_key() { + Ok(_) => Ok(true), + Err(e) if e.errors().is_empty() => Ok(false), + Err(e) => Err(e), + }; + } + + /// Validates DH parameters for correctness + #[corresponds($f)] + #[cfg(not(ossl300))] + pub fn check_key(&self) -> Result { + let mut codes = 0; + cvt(unsafe { ffi::DH_check(self.as_ptr(), &mut codes) })?; + Ok(codes == 0) + } + } + )+ + }; } +key_check!(Dh, DH_check, Params); + impl Dh { pub fn from_params(p: BigNum, g: BigNum, q: BigNum) -> Result, ErrorStack> { Self::from_pqg(p, Some(q), g) @@ -63,6 +111,20 @@ impl Dh { prime_q: Option, generator: BigNum, ) -> Result, ErrorStack> { + #[cfg(ossl300)] + return { + let mut builder = OsslParamBuilder::new()?; + builder.add_bn(OSSL_PKEY_PARAM_FFC_P, &prime_p)?; + builder.add_bn(OSSL_PKEY_PARAM_FFC_G, &generator)?; + if let Some(q) = &prime_q { + builder.add_bn(OSSL_PKEY_PARAM_FFC_Q, q)?; + }; + let params = builder.to_param()?; + let pkey = pkey_from_params(Id::DH, ¶ms)?; + pkey.dh() + }; + + #[cfg(not(ossl300))] unsafe { let dh = Dh::from_ptr(cvt_p(ffi::DH_new())?); cvt(DH_set0_pqg( @@ -78,6 +140,18 @@ impl Dh { /// Sets the public key on the DH object. pub fn set_public_key(self, pub_key: BigNum) -> Result, ErrorStack> { + #[cfg(ossl300)] + return { + let key_params = self.0.to_data(Selection::KeyParameters)?; + let mut builder = OsslParamBuilder::new()?; + builder.add_bn(OSSL_PKEY_PARAM_PUB_KEY, &pub_key)?; + let params = builder.to_param()?; + let params = key_params.merge(¶ms)?; + let pkey = pkey_from_params(Id::DH, ¶ms)?; + pkey.dh() + }; + + #[cfg(not(ossl300))] unsafe { let dh_ptr = self.0; cvt(DH_set0_key(dh_ptr, pub_key.as_ptr(), ptr::null_mut()))?; @@ -88,6 +162,25 @@ impl Dh { /// Sets the private key on the DH object and recomputes the public key. pub fn set_private_key(self, priv_key: BigNum) -> Result, ErrorStack> { + #[cfg(ossl300)] + return { + let key_params = self.0.to_data(Selection::KeyParameters)?; + let mut builder = OsslParamBuilder::new()?; + builder.add_bn(OSSL_PKEY_PARAM_PRIV_KEY, &priv_key)?; + let params = builder.to_param()?; + let params = key_params.merge(¶ms)?; + let pkey: PKey = pkey_from_params(Id::DH, ¶ms)?; + // In lieu of a non-deprecated way to recompute the public key, bounce through PKCS8 as + // this causes the public key to be re-computed. + // See: https://github.com/openssl/openssl/issues/21315#issuecomment-1618314607 + let pkcs8 = cvt_p(unsafe { ffi::EVP_PKEY2PKCS8(pkey.as_ptr()) })?; + let pkey_ptr = cvt_p(unsafe { ffi::EVP_PKCS82PKEY(pkcs8) })?; + unsafe { ffi::PKCS8_PRIV_KEY_INFO_free(pkcs8) }; + let pkey = unsafe { PKey::from_ptr(pkey_ptr) }; + pkey.dh() + }; + + #[cfg(not(ossl300))] unsafe { let dh_ptr = self.0; cvt(DH_set0_key(dh_ptr, ptr::null_mut(), priv_key.as_ptr()))?; @@ -101,6 +194,19 @@ impl Dh { /// Sets the public and private keys on the DH object. pub fn set_key(self, pub_key: BigNum, priv_key: BigNum) -> Result, ErrorStack> { + #[cfg(ossl300)] + return { + let key_params = self.0.to_data(Selection::KeyParameters)?; + let mut builder = OsslParamBuilder::new()?; + builder.add_bn(OSSL_PKEY_PARAM_PUB_KEY, &pub_key)?; + builder.add_bn(OSSL_PKEY_PARAM_PRIV_KEY, &priv_key)?; + let params = builder.to_param()?; + let params = key_params.merge(¶ms)?; + let pkey = pkey_from_params(Id::DH, ¶ms)?; + pkey.dh() + }; + + #[cfg(not(ossl300))] unsafe { let dh_ptr = self.0; cvt(DH_set0_key(dh_ptr, pub_key.as_ptr(), priv_key.as_ptr()))?; @@ -112,6 +218,17 @@ impl Dh { /// Generates DH params based on the given `prime_len` and a fixed `generator` value. #[corresponds(DH_generate_parameters_ex)] pub fn generate_params(prime_len: u32, generator: u32) -> Result, ErrorStack> { + #[cfg(ossl300)] + return { + let mut ctx = PkeyCtx::new_id(Id::DH)?; + ctx.paramgen_init()?; + ctx.set_dh_paramgen_prime_len(prime_len)?; + ctx.set_dh_paramgen_generator(generator)?; + let pkey = ctx.paramgen()?; + pkey.dh() + }; + + #[cfg(not(ossl300))] unsafe { let dh = Dh::from_ptr(cvt_p(ffi::DH_new())?); cvt(ffi::DH_generate_parameters_ex( @@ -127,6 +244,15 @@ impl Dh { /// Generates a public and a private key based on the DH params. #[corresponds(DH_generate_key)] pub fn generate_key(self) -> Result, ErrorStack> { + #[cfg(ossl300)] + return { + let mut ctx = PkeyCtx::new(&self.0)?; + ctx.keygen_init()?; + let pkey = ctx.keygen()?; + pkey.dh() + }; + + #[cfg(not(ossl300))] unsafe { let dh_ptr = self.0; cvt(ffi::DH_generate_key(dh_ptr))?; @@ -139,24 +265,39 @@ impl Dh { /// Deserializes a PEM-encoded PKCS#3 DHpararameters structure. /// /// The input should have a header of `-----BEGIN DH PARAMETERS-----`. - #[corresponds(PEM_read_bio_DHparams)] + #[cfg_attr(not(ossl300), corresponds(PEM_read_bio_DHparams))] params_from_pem, Dh, + Structure::TypeSpecific, ffi::PEM_read_bio_DHparams } from_der! { /// Deserializes a DER-encoded PKCS#3 DHparameters structure. - #[corresponds(d2i_DHparams)] + #[cfg_attr(not(ossl300), corresponds(d2i_DHparams))] params_from_der, Dh, + Structure::TypeSpecific, ffi::d2i_DHparams } + #[cfg(ossl300)] + fn get_group(group: &str) -> Result, ErrorStack> { + let mut builder = OsslParamBuilder::new()?; + builder.add_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME, group)?; + let params = builder.to_param()?; + let pkey = pkey_from_params(Id::DH, ¶ms)?; + pkey.dh() + } + /// Requires OpenSSL 1.0.2 or newer. #[corresponds(DH_get_1024_160)] #[cfg(any(ossl102, ossl110))] pub fn get_1024_160() -> Result, ErrorStack> { + #[cfg(ossl300)] + return Self::get_group("dh_1024_160"); + + #[cfg(not(ossl300))] unsafe { ffi::init(); cvt_p(ffi::DH_get_1024_160()).map(|p| Dh::from_ptr(p)) @@ -167,6 +308,10 @@ impl Dh { #[corresponds(DH_get_2048_224)] #[cfg(any(ossl102, ossl110))] pub fn get_2048_224() -> Result, ErrorStack> { + #[cfg(ossl300)] + return Self::get_group("dh_2048_224"); + + #[cfg(not(ossl300))] unsafe { ffi::init(); cvt_p(ffi::DH_get_2048_224()).map(|p| Dh::from_ptr(p)) @@ -177,6 +322,10 @@ impl Dh { #[corresponds(DH_get_2048_256)] #[cfg(any(ossl102, ossl110))] pub fn get_2048_256() -> Result, ErrorStack> { + #[cfg(ossl300)] + return Self::get_group("dh_2048_256"); + + #[cfg(not(ossl300))] unsafe { ffi::init(); cvt_p(ffi::DH_get_2048_256()).map(|p| Dh::from_ptr(p)) @@ -189,20 +338,26 @@ where T: HasParams, { /// Returns the prime `p` from the DH instance. - #[corresponds(DH_get0_pqg)] pub fn prime_p(&self) -> &BigNumRef { - let mut p = ptr::null(); + #[cfg(ossl300)] + return self.0.get_bn_param(OSSL_PKEY_PARAM_FFC_P).unwrap(); + + #[cfg(not(ossl300))] unsafe { + let mut p = ptr::null(); DH_get0_pqg(self.as_ptr(), &mut p, ptr::null_mut(), ptr::null_mut()); BigNumRef::from_ptr(p as *mut _) } } /// Returns the prime `q` from the DH instance. - #[corresponds(DH_get0_pqg)] pub fn prime_q(&self) -> Option<&BigNumRef> { - let mut q = ptr::null(); + #[cfg(ossl300)] + return self.0.get_bn_param(OSSL_PKEY_PARAM_FFC_Q).ok(); + + #[cfg(not(ossl300))] unsafe { + let mut q = ptr::null(); DH_get0_pqg(self.as_ptr(), ptr::null_mut(), &mut q, ptr::null_mut()); if q.is_null() { None @@ -213,10 +368,13 @@ where } /// Returns the generator from the DH instance. - #[corresponds(DH_get0_pqg)] pub fn generator(&self) -> &BigNumRef { - let mut g = ptr::null(); + #[cfg(ossl300)] + return self.0.get_bn_param(OSSL_PKEY_PARAM_FFC_G).unwrap(); + + #[cfg(not(ossl300))] unsafe { + let mut g = ptr::null(); DH_get0_pqg(self.as_ptr(), ptr::null_mut(), ptr::null_mut(), &mut g); BigNumRef::from_ptr(g as *mut _) } @@ -228,16 +386,21 @@ where T: HasPublic, { /// Returns the public key from the DH instance. - #[corresponds(DH_get0_key)] pub fn public_key(&self) -> &BigNumRef { - let mut pub_key = ptr::null(); + #[cfg(ossl300)] + return self.0.get_bn_param(OSSL_PKEY_PARAM_PUB_KEY).unwrap(); + + #[cfg(not(ossl300))] unsafe { + let mut pub_key = ptr::null(); DH_get0_key(self.as_ptr(), &mut pub_key, ptr::null_mut()); BigNumRef::from_ptr(pub_key as *mut _) } } } +key_check!(Dh, DH_check, Public); + impl DhRef where T: HasPrivate, @@ -245,6 +408,29 @@ where /// Computes a shared secret from the own private key and the given `public_key`. #[corresponds(DH_compute_key)] pub fn compute_key(&self, public_key: &BigNumRef) -> Result, ErrorStack> { + #[cfg(ossl300)] + return { + // prime_p and generator only exist on Dh, not DhRef + let dh_self = self.to_owned(); + + // construct a new pkey that shares the params and has the peer's public key + let mut builder = OsslParamBuilder::new()?; + builder.add_bn(OSSL_PKEY_PARAM_FFC_P, dh_self.prime_p())?; + builder.add_bn(OSSL_PKEY_PARAM_FFC_G, dh_self.generator())?; + builder.add_bn(OSSL_PKEY_PARAM_PUB_KEY, public_key)?; + let params = builder.to_param()?; + let peer_key: PKey = pkey_from_params(Id::DH, ¶ms)?; + + // Derive the shared secret + let mut ctx = PkeyCtx::new(&self.0)?; + ctx.derive_init()?; + ctx.derive_set_peer(&peer_key)?; + let mut key = Vec::new(); + ctx.derive_to_vec(&mut key)?; + Ok(key) + }; + + #[cfg(not(ossl300))] unsafe { let key_len = ffi::DH_size(self.as_ptr()); let mut key = vec![0u8; key_len as usize]; @@ -258,16 +444,22 @@ where } /// Returns the private key from the DH instance. - #[corresponds(DH_get0_key)] pub fn private_key(&self) -> &BigNumRef { - let mut priv_key = ptr::null(); + #[cfg(ossl300)] + return self.0.get_bn_param(OSSL_PKEY_PARAM_PRIV_KEY).unwrap(); + + #[cfg(not(ossl300))] unsafe { + let mut priv_key = ptr::null(); DH_get0_key(self.as_ptr(), ptr::null_mut(), &mut priv_key); BigNumRef::from_ptr(priv_key as *mut _) } } } +key_check!(Dh, DH_check, Private); + +#[cfg(not(ossl300))] cfg_if! { if #[cfg(any(ossl110, libressl270, boringssl, awslc))] { use ffi::{DH_set0_pqg, DH_get0_pqg, DH_get0_key, DH_set0_key}; @@ -491,5 +683,8 @@ mod tests { let dh2 = Dh::from_pqg(p, None, g).unwrap(); assert!(dh1.check_key().unwrap()); assert!(matches!(dh2.check_key(), Ok(false) | Err(_))); + + let dh3 = dh1.generate_key().unwrap(); + assert!(dh3.check_key().unwrap()); } } diff --git a/openssl/src/pkey.rs b/openssl/src/pkey.rs index 30ba307fb..c0e24de1a 100644 --- a/openssl/src/pkey.rs +++ b/openssl/src/pkey.rs @@ -70,7 +70,7 @@ use std::convert::{TryFrom, TryInto}; use std::ffi::c_uchar; use std::ffi::{CStr, CString}; use std::fmt; -#[cfg(all(not(any(boringssl, awslc)), ossl110))] +#[cfg(all(not(any(boringssl, awslc, ossl300)), ossl110))] use std::mem; use std::ptr; @@ -314,10 +314,16 @@ impl PKeyRef { /// Returns a copy of the internal DH key. #[corresponds(EVP_PKEY_get1_DH)] pub fn dh(&self) -> Result, ErrorStack> { - unsafe { - let dh = cvt_p(ffi::EVP_PKEY_get1_DH(self.as_ptr()))?; - Ok(Dh::from_ptr(dh)) + if self.id() != Id::DH { + return Err(ErrorStack::get()); } + + let dh = self.as_ptr(); + #[cfg(ossl300)] + cvt(unsafe { ffi::EVP_PKEY_up_ref(dh) })?; + #[cfg(not(ossl300))] + let dh = cvt_p(unsafe { ffi::EVP_PKEY_get1_DH(dh) })?; + Ok(unsafe { Dh::from_ptr(dh) }) } /// Returns a copy of the internal elliptic curve key. @@ -682,28 +688,50 @@ impl PKey { #[corresponds(EVP_PKEY_set1_DH)] #[cfg(not(boringssl))] pub fn from_dh(dh: Dh) -> Result, ErrorStack> { + let pkey: PKey; + + #[cfg(ossl300)] + unsafe { + ffi::EVP_PKEY_up_ref(dh.as_ptr()); + pkey = PKey::from_ptr(dh.as_ptr()); + } + + #[cfg(not(ossl300))] unsafe { let evp = cvt_p(ffi::EVP_PKEY_new())?; - let pkey = PKey::from_ptr(evp); + pkey = PKey::from_ptr(evp); cvt(ffi::EVP_PKEY_set1_DH(pkey.0, dh.as_ptr()))?; - Ok(pkey) } + Ok(pkey) } /// Creates a new `PKey` containing a Diffie-Hellman key with type DHX. #[cfg(all(not(any(boringssl, awslc)), ossl110))] pub fn from_dhx(dh: Dh) -> Result, ErrorStack> { + let pkey: PKey; + + #[cfg(ossl300)] + { + let tmp_pkey = Self::from_dh(dh)?; + let params = tmp_pkey.to_data(Selection::Keypair)?; + let mut ctx = PkeyCtx::new_id(Id::DHX)?; + ctx.fromdata_init()?; + pkey = ctx.fromdata(¶ms, Selection::Keypair)?; + } + + #[cfg(not(ossl300))] unsafe { let evp = cvt_p(ffi::EVP_PKEY_new())?; - let pkey = PKey::from_ptr(evp); + pkey = PKey::from_ptr(evp); cvt(ffi::EVP_PKEY_assign( pkey.0, ffi::EVP_PKEY_DHX, dh.as_ptr().cast(), ))?; mem::forget(dh); - Ok(pkey) } + + Ok(pkey) } /// Creates a new `PKey` containing an elliptic curve key. diff --git a/openssl/src/ssl/callbacks.rs b/openssl/src/ssl/callbacks.rs index 22eb600f5..4af535c58 100644 --- a/openssl/src/ssl/callbacks.rs +++ b/openssl/src/ssl/callbacks.rs @@ -14,10 +14,14 @@ use std::ptr; use std::str; use std::sync::Arc; +#[cfg(all(ossl300, not(osslconf = "OPENSSL_NO_DEPRECATED_3_0")))] +use crate::cvt_p; +#[cfg(not(osslconf = "OPENSSL_NO_DEPRECATED_3_0"))] use crate::dh::Dh; #[cfg(all(ossl101, not(ossl110)))] use crate::ec::EcKey; use crate::error::ErrorStack; +#[cfg(not(osslconf = "OPENSSL_NO_DEPRECATED_3_0"))] use crate::pkey::Params; #[cfg(any(ossl102, libressl261, boringssl, awslc))] use crate::ssl::AlpnError; @@ -210,6 +214,7 @@ where } } +#[cfg(not(osslconf = "OPENSSL_NO_DEPRECATED_3_0"))] pub unsafe extern "C" fn raw_tmp_dh( ssl: *mut ffi::SSL, is_export: c_int, @@ -226,6 +231,9 @@ where match (*callback)(ssl, is_export != 0, keylength as u32) { Ok(dh) => { + #[cfg(ossl300)] + let ptr = cvt_p(unsafe { ffi::EVP_PKEY_get1_DH(dh.as_ptr()) }).unwrap(); + #[cfg(not(ossl300))] let ptr = dh.as_ptr(); mem::forget(dh); ptr @@ -265,6 +273,7 @@ where } } +#[cfg(not(osslconf = "OPENSSL_NO_DEPRECATED_3_0"))] pub unsafe extern "C" fn raw_tmp_dh_ssl( ssl: *mut ffi::SSL, is_export: c_int, @@ -281,6 +290,9 @@ where match callback(ssl, is_export != 0, keylength as u32) { Ok(dh) => { + #[cfg(ossl300)] + let ptr = cvt_p(unsafe { ffi::EVP_PKEY_get1_DH(dh.as_ptr()) }).unwrap(); + #[cfg(not(ossl300))] let ptr = dh.as_ptr(); mem::forget(dh); ptr diff --git a/openssl/src/ssl/mod.rs b/openssl/src/ssl/mod.rs index 901c6205f..7c82fc4ab 100644 --- a/openssl/src/ssl/mod.rs +++ b/openssl/src/ssl/mod.rs @@ -59,7 +59,9 @@ //! ``` #[cfg(ossl300)] use crate::cvt_long; -use crate::dh::{Dh, DhRef}; +#[cfg(not(osslconf = "OPENSSL_NO_DEPRECATED_3_0"))] +use crate::dh::Dh; +use crate::dh::DhRef; #[cfg(all(ossl101, not(ossl110)))] use crate::ec::EcKey; use crate::ec::EcKeyRef; @@ -872,9 +874,21 @@ impl SslContextBuilder { } /// Sets the parameters to be used during ephemeral Diffie-Hellman key exchange. - #[corresponds(SSL_CTX_set_tmp_dh)] pub fn set_tmp_dh(&mut self, dh: &DhRef) -> Result<(), ErrorStack> { - unsafe { cvt(ffi::SSL_CTX_set_tmp_dh(self.as_ptr(), dh.as_ptr()) as c_int).map(|_| ()) } + #[cfg(ossl300)] + return { + let pkey: PKey = dh.into(); + // Ownership of pkey is passed to our SSL_CTX as a result setting, so we should not free pkey + cvt(unsafe { ffi::SSL_CTX_set0_tmp_dh_pkey(self.as_ptr(), pkey.as_ptr()) })?; + // But when pkey goes out of scope it will be freed, so up ref to ensure the key is not freed + cvt(unsafe { ffi::EVP_PKEY_up_ref(pkey.as_ptr()) })?; + Ok(()) + }; + + #[cfg(not(ossl300))] + unsafe { + cvt(ffi::SSL_CTX_set_tmp_dh(self.as_ptr(), dh.as_ptr()) as c_int).map(|_| ()) + } } /// Sets the callback which will generate parameters to be used during ephemeral Diffie-Hellman @@ -884,6 +898,7 @@ impl SslContextBuilder { /// indicating if the selected cipher is export-grade, and the key length. The export and key /// length options are archaic and should be ignored in almost all cases. #[corresponds(SSL_CTX_set_tmp_dh_callback)] + #[cfg(not(osslconf = "OPENSSL_NO_DEPRECATED_3_0"))] pub fn set_tmp_dh_callback(&mut self, callback: F) where F: Fn(&mut SslRef, bool, u32) -> Result, ErrorStack> + 'static + Sync + Send, @@ -2458,15 +2473,28 @@ impl SslRef { /// Like [`SslContextBuilder::set_tmp_dh`]. /// /// [`SslContextBuilder::set_tmp_dh`]: struct.SslContextBuilder.html#method.set_tmp_dh - #[corresponds(SSL_set_tmp_dh)] pub fn set_tmp_dh(&mut self, dh: &DhRef) -> Result<(), ErrorStack> { - unsafe { cvt(ffi::SSL_set_tmp_dh(self.as_ptr(), dh.as_ptr()) as c_int).map(|_| ()) } + #[cfg(ossl300)] + return { + let pkey: PKey = dh.into(); + // Ownership of pkey is passed to our SSL_CTX as a result setting, so we should not free pkey + cvt(unsafe { ffi::SSL_set0_tmp_dh_pkey(self.as_ptr(), pkey.as_ptr()) })?; + // But when pkey goes out of scope it will be freed, so up ref to ensure the key is not freed + cvt(unsafe { ffi::EVP_PKEY_up_ref(pkey.as_ptr()) })?; + Ok(()) + }; + + #[cfg(not(ossl300))] + unsafe { + cvt(ffi::SSL_set_tmp_dh(self.as_ptr(), dh.as_ptr()) as c_int).map(|_| ()) + } } /// Like [`SslContextBuilder::set_tmp_dh_callback`]. /// /// [`SslContextBuilder::set_tmp_dh_callback`]: struct.SslContextBuilder.html#method.set_tmp_dh_callback #[corresponds(SSL_set_tmp_dh_callback)] + #[cfg(not(osslconf = "OPENSSL_NO_DEPRECATED_3_0"))] pub fn set_tmp_dh_callback(&mut self, callback: F) where F: Fn(&mut SslRef, bool, u32) -> Result, ErrorStack> + 'static + Sync + Send, diff --git a/openssl/src/ssl/test/mod.rs b/openssl/src/ssl/test/mod.rs index 7e53dc9be..13148ca7b 100644 --- a/openssl/src/ssl/test/mod.rs +++ b/openssl/src/ssl/test/mod.rs @@ -969,6 +969,7 @@ fn cert_store() { } #[test] +#[cfg(not(osslconf = "OPENSSL_NO_DEPRECATED_3_0"))] #[cfg_attr(any(all(libressl321, not(libressl340)), boringssl, awslc), ignore)] fn tmp_dh_callback() { static CALLED_BACK: AtomicBool = AtomicBool::new(false); @@ -1017,6 +1018,7 @@ fn tmp_ecdh_callback() { } #[test] +#[cfg(not(osslconf = "OPENSSL_NO_DEPRECATED_3_0"))] #[cfg_attr(any(all(libressl321, not(libressl340)), boringssl, awslc), ignore)] fn tmp_dh_callback_ssl() { static CALLED_BACK: AtomicBool = AtomicBool::new(false); From 2368bda6ccaec3ec3125a0f3c07217cb75fc8a95 Mon Sep 17 00:00:00 2001 From: Huw Jones Date: Mon, 15 Sep 2025 14:29:29 +0100 Subject: [PATCH 40/42] pkey: use non-deprecated functions for ossl300+ --- openssl/src/pkey.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/openssl/src/pkey.rs b/openssl/src/pkey.rs index c0e24de1a..20eee4a0d 100644 --- a/openssl/src/pkey.rs +++ b/openssl/src/pkey.rs @@ -481,11 +481,14 @@ where where U: HasPublic, { - let res = unsafe { ffi::EVP_PKEY_cmp(self.as_ptr(), other.as_ptr()) == 1 }; + #[cfg(ossl300)] + let res = unsafe { ffi::EVP_PKEY_eq(self.as_ptr(), other.as_ptr()) }; + #[cfg(not(ossl300))] + let res = unsafe { ffi::EVP_PKEY_cmp(self.as_ptr(), other.as_ptr()) }; // Clear the stack. OpenSSL will put an error on the stack when the // keys are different types in some situations. let _ = ErrorStack::get(); - res + res == 1 } /// Raw byte representation of a public key. From 0f13c9df723476acd6b79fd75e2a7822d971ca85 Mon Sep 17 00:00:00 2001 From: Huw Jones Date: Mon, 15 Sep 2025 14:36:03 +0100 Subject: [PATCH 41/42] x509: cfg out ossl3 deprecated types --- openssl/src/x509/verify.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/openssl/src/x509/verify.rs b/openssl/src/x509/verify.rs index ca345d345..e43a8dfbc 100644 --- a/openssl/src/x509/verify.rs +++ b/openssl/src/x509/verify.rs @@ -33,6 +33,8 @@ bitflags! { #[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] #[repr(transparent)] pub struct X509VerifyFlags: c_ulong { + #[cfg(not(osslconf = "OPENSSL_NO_DEPRECATED_3_0"))] + #[cfg_attr(ossl110, deprecated(note = "CB_ISSUER_CHECK was deprecated in OpenSSL 1.1.0 and has no effect"))] const CB_ISSUER_CHECK = ffi::X509_V_FLAG_CB_ISSUER_CHECK as _; const USE_CHECK_TIME = ffi::X509_V_FLAG_USE_CHECK_TIME as _; const CRL_CHECK = ffi::X509_V_FLAG_CRL_CHECK as _; From 0ecf3cc287946fad7372769cbd5564a2d9c5d1c5 Mon Sep 17 00:00:00 2001 From: Huw Jones Date: Mon, 15 Sep 2025 14:56:02 +0100 Subject: [PATCH 42/42] ci: run all tests with no deprecated symbols --- .github/workflows/ci.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5f16118aa..f5c1a83ff 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -411,7 +411,6 @@ jobs: cargo run --manifest-path=systest/Cargo.toml --target ${{ matrix.target }} $features if: ${{ !(matrix.library.name == 'boringssl' || matrix.library.name == 'aws-lc') }} - name: Test openssl - if: ${{ ! endsWith(matrix.library.name, 'no-deprecated') }} run: | if [[ "${{ matrix.library.name }}" == "boringssl" && "${{ matrix.bindgen }}" != "true" ]]; then features="--features unstable_boringssl" @@ -437,4 +436,4 @@ jobs: features="$features --features openssl-sys/bindgen" fi cargo test --manifest-path=openssl-errors/Cargo.toml --target ${{ matrix.target }} $features - if: ${{ !(matrix.library.name == 'boringssl' || matrix.library.name == 'aws-lc' || endsWith(matrix.library.name, 'no-deprecated')) }} + if: ${{ !(matrix.library.name == 'boringssl' || matrix.library.name == 'aws-lc') }}