diff --git a/openssl-sys/build/run_bindgen.rs b/openssl-sys/build/run_bindgen.rs index cba6b5556..e016a092b 100644 --- a/openssl-sys/build/run_bindgen.rs +++ b/openssl-sys/build/run_bindgen.rs @@ -59,6 +59,8 @@ const INCLUDES: &str = " #if OPENSSL_VERSION_NUMBER >= 0x30000000 #include +#include +#include #endif #if OPENSSL_VERSION_NUMBER >= 0x30200000 diff --git a/openssl-sys/src/evp.rs b/openssl-sys/src/evp.rs index 6374b2e60..bd1802eaa 100644 --- a/openssl-sys/src/evp.rs +++ b/openssl-sys/src/evp.rs @@ -341,3 +341,27 @@ pub unsafe fn EVP_PKEY_assign_DH(pkey: *mut EVP_PKEY, dh: *mut DH) -> c_int { pub unsafe fn EVP_PKEY_assign_EC_KEY(pkey: *mut EVP_PKEY, ec_key: *mut EC_KEY) -> c_int { EVP_PKEY_assign(pkey, EVP_PKEY_EC, ec_key as *mut c_void) } + +cfg_if! { + if #[cfg(ossl300)] { + // consts required for EVP_PKEY_fromdata selection value + + // From + const OSSL_KEYMGMT_SELECT_PRIVATE_KEY: c_int = 0x01; + const OSSL_KEYMGMT_SELECT_PUBLIC_KEY: c_int = 0x02; + const OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS: c_int = 0x04; + const OSSL_KEYMGMT_SELECT_OTHER_PARAMETERS: c_int = 0x80; + const OSSL_KEYMGMT_SELECT_ALL_PARAMETERS: c_int = + OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS | OSSL_KEYMGMT_SELECT_OTHER_PARAMETERS; + const OSSL_KEYMGMT_SELECT_KEYPAIR: c_int = + OSSL_KEYMGMT_SELECT_PRIVATE_KEY | OSSL_KEYMGMT_SELECT_PUBLIC_KEY; + const OSSL_KEYMGMT_SELECT_ALL: c_int = + OSSL_KEYMGMT_SELECT_KEYPAIR | OSSL_KEYMGMT_SELECT_ALL_PARAMETERS; + + // From + pub const EVP_PKEY_KEY_PARAMETERS: c_int = OSSL_KEYMGMT_SELECT_ALL_PARAMETERS; + pub const EVP_PKEY_PRIVATE_KEY: c_int = EVP_PKEY_KEY_PARAMETERS | OSSL_KEYMGMT_SELECT_PRIVATE_KEY; + pub const EVP_PKEY_PUBLIC_KEY: c_int = EVP_PKEY_KEY_PARAMETERS | OSSL_KEYMGMT_SELECT_PUBLIC_KEY; + pub const EVP_PKEY_KEYPAIR: c_int = EVP_PKEY_PUBLIC_KEY | OSSL_KEYMGMT_SELECT_PRIVATE_KEY; + } +} diff --git a/openssl-sys/src/handwritten/evp.rs b/openssl-sys/src/handwritten/evp.rs index 22d614550..6ebefd304 100644 --- a/openssl-sys/src/handwritten/evp.rs +++ b/openssl-sys/src/handwritten/evp.rs @@ -588,6 +588,16 @@ extern "C" { pub fn EVP_PKEY_keygen(ctx: *mut EVP_PKEY_CTX, key: *mut *mut EVP_PKEY) -> c_int; pub fn EVP_PKEY_paramgen(ctx: *mut EVP_PKEY_CTX, key: *mut *mut EVP_PKEY) -> c_int; + #[cfg(ossl300)] + pub fn EVP_PKEY_fromdata_init(ctx: *mut EVP_PKEY_CTX) -> c_int; + #[cfg(ossl300)] + pub fn EVP_PKEY_fromdata( + ctx: *mut EVP_PKEY_CTX, + ppkey: *mut *mut EVP_PKEY, + selection: c_int, + params: *mut OSSL_PARAM, + ) -> c_int; + pub fn EVP_PKEY_sign_init(ctx: *mut EVP_PKEY_CTX) -> c_int; pub fn EVP_PKEY_sign( ctx: *mut EVP_PKEY_CTX, diff --git a/openssl-sys/src/handwritten/params.rs b/openssl-sys/src/handwritten/params.rs index 542cef337..8c5c45b77 100644 --- a/openssl-sys/src/handwritten/params.rs +++ b/openssl-sys/src/handwritten/params.rs @@ -1,16 +1,73 @@ use super::super::*; use libc::*; +#[cfg(ossl300)] extern "C" { - #[cfg(ossl300)] + pub fn OSSL_PARAM_dup(params: *const OSSL_PARAM) -> *mut OSSL_PARAM; + pub fn OSSL_PARAM_free(params: *mut OSSL_PARAM); + pub fn OSSL_PARAM_merge( + params: *const OSSL_PARAM, + params1: *const OSSL_PARAM, + ) -> *mut OSSL_PARAM; + pub fn OSSL_PARAM_locate(params: *mut OSSL_PARAM, key: *const c_char) -> *mut OSSL_PARAM; + pub fn OSSL_PARAM_locate_const( + params: *const OSSL_PARAM, + key: *const c_char, + ) -> *const OSSL_PARAM; pub fn OSSL_PARAM_construct_uint(key: *const c_char, buf: *mut c_uint) -> OSSL_PARAM; - #[cfg(ossl300)] pub fn OSSL_PARAM_construct_end() -> OSSL_PARAM; - #[cfg(ossl300)] pub fn OSSL_PARAM_construct_octet_string( key: *const c_char, buf: *mut c_void, bsize: size_t, ) -> OSSL_PARAM; + pub fn OSSL_PARAM_BLD_new() -> *mut OSSL_PARAM_BLD; + pub fn OSSL_PARAM_BLD_free(bld: *mut OSSL_PARAM_BLD); + pub fn OSSL_PARAM_BLD_to_param(bld: *mut OSSL_PARAM_BLD) -> *mut OSSL_PARAM; + pub fn OSSL_PARAM_BLD_push_uint( + bld: *mut OSSL_PARAM_BLD, + key: *const c_char, + val: c_uint, + ) -> c_int; + pub fn OSSL_PARAM_BLD_push_size_t( + bld: *mut OSSL_PARAM_BLD, + key: *const c_char, + val: size_t, + ) -> c_int; + pub fn OSSL_PARAM_BLD_push_BN( + bld: *mut OSSL_PARAM_BLD, + key: *const c_char, + bn: *const BIGNUM, + ) -> c_int; + pub fn OSSL_PARAM_BLD_push_BN_pad( + bld: *mut OSSL_PARAM_BLD, + key: *const c_char, + bn: *const BIGNUM, + sz: size_t, + ) -> c_int; + pub fn OSSL_PARAM_BLD_push_utf8_string( + bld: *mut OSSL_PARAM_BLD, + key: *const c_char, + buf: *const c_char, + bsize: size_t, + ) -> c_int; + pub fn OSSL_PARAM_BLD_push_utf8_ptr( + bld: *mut OSSL_PARAM_BLD, + key: *const c_char, + buf: *mut c_char, + bsize: size_t, + ) -> c_int; + pub fn OSSL_PARAM_BLD_push_octet_string( + bld: *mut OSSL_PARAM_BLD, + key: *const c_char, + buf: *const c_void, + bsize: size_t, + ) -> c_int; + pub fn OSSL_PARAM_BLD_push_octet_ptr( + bld: *mut OSSL_PARAM_BLD, + key: *const c_char, + buf: *mut c_void, + bsize: size_t, + ) -> c_int; } diff --git a/openssl-sys/src/handwritten/types.rs b/openssl-sys/src/handwritten/types.rs index a4b90d789..576811c02 100644 --- a/openssl-sys/src/handwritten/types.rs +++ b/openssl-sys/src/handwritten/types.rs @@ -1134,12 +1134,15 @@ pub enum OSSL_LIB_CTX {} #[repr(C)] pub struct OSSL_PARAM { key: *const c_char, - data_type: c_uchar, + data_type: c_uint, data: *mut c_void, data_size: size_t, return_size: size_t, } +#[cfg(ossl300)] +pub enum OSSL_PARAM_BLD {} + #[cfg(ossl300)] pub enum EVP_KDF {} #[cfg(ossl300)] diff --git a/openssl/src/kdf.rs b/openssl/src/kdf.rs index 79e0eff49..c42863311 100644 --- a/openssl/src/kdf.rs +++ b/openssl/src/kdf.rs @@ -25,15 +25,14 @@ impl Drop for EvpKdfCtx { cfg_if::cfg_if! { if #[cfg(all(ossl320, not(osslconf = "OPENSSL_NO_ARGON2")))] { use std::cmp; - use std::ffi::c_void; use std::ffi::CStr; - use std::mem::MaybeUninit; use std::ptr; use foreign_types::ForeignTypeRef; - use libc::c_char; use crate::{cvt, cvt_p}; use crate::lib_ctx::LibCtxRef; use crate::error::ErrorStack; + use crate::params::ParamBuilder; + use crate::util::c_str; #[allow(clippy::too_many_arguments)] pub fn argon2d( @@ -94,86 +93,50 @@ cfg_if::cfg_if! { salt: &[u8], ad: Option<&[u8]>, secret: Option<&[u8]>, - mut iter: u32, - mut lanes: u32, - mut memcost: u32, + iter: u32, + lanes: u32, + memcost: u32, out: &mut [u8], ) -> Result<(), ErrorStack> { - unsafe { - ffi::init(); - let libctx = ctx.map_or(ptr::null_mut(), ForeignTypeRef::as_ptr); - - let max_threads = ffi::OSSL_get_max_threads(libctx); - let mut threads = 1; - // If max_threads is 0, then this isn't a threaded build. - // If max_threads is > u32::MAX we need to clamp since - // argon2's threads parameter is a u32. - if max_threads > 0 { - threads = cmp::min(lanes, cmp::min(max_threads, u32::MAX as u64) as u32); - } - let mut params: [ffi::OSSL_PARAM; 10] = - core::array::from_fn(|_| MaybeUninit::::zeroed().assume_init()); - let mut idx = 0; - params[idx] = ffi::OSSL_PARAM_construct_octet_string( - b"pass\0".as_ptr() as *const c_char, - pass.as_ptr() as *mut c_void, - pass.len(), - ); - idx += 1; - params[idx] = ffi::OSSL_PARAM_construct_octet_string( - b"salt\0".as_ptr() as *const c_char, - salt.as_ptr() as *mut c_void, - salt.len(), - ); - idx += 1; - params[idx] = - ffi::OSSL_PARAM_construct_uint(b"threads\0".as_ptr() as *const c_char, &mut threads); - idx += 1; - params[idx] = - ffi::OSSL_PARAM_construct_uint(b"lanes\0".as_ptr() as *const c_char, &mut lanes); - idx += 1; - params[idx] = - ffi::OSSL_PARAM_construct_uint(b"memcost\0".as_ptr() as *const c_char, &mut memcost); - idx += 1; - params[idx] = - ffi::OSSL_PARAM_construct_uint(b"iter\0".as_ptr() as *const c_char, &mut iter); - idx += 1; - let mut size = out.len() as u32; - params[idx] = - ffi::OSSL_PARAM_construct_uint(b"size\0".as_ptr() as *const c_char, &mut size); - idx += 1; - if let Some(ad) = ad { - params[idx] = ffi::OSSL_PARAM_construct_octet_string( - b"ad\0".as_ptr() as *const c_char, - ad.as_ptr() as *mut c_void, - ad.len(), - ); - idx += 1; - } - if let Some(secret) = secret { - params[idx] = ffi::OSSL_PARAM_construct_octet_string( - b"secret\0".as_ptr() as *const c_char, - secret.as_ptr() as *mut c_void, - secret.len(), - ); - idx += 1; - } - params[idx] = ffi::OSSL_PARAM_construct_end(); + ffi::init(); + let libctx = ctx.map_or(ptr::null_mut(), ForeignTypeRef::as_ptr); + + let max_threads = unsafe { ffi::OSSL_get_max_threads(libctx) }; + let mut threads = 1; + // If max_threads is 0, then this isn't a threaded build. + // If max_threads is > u32::MAX we need to clamp since + // argon2's threads parameter is a u32. + if max_threads > 0 { + threads = cmp::min(lanes, cmp::min(max_threads, u32::MAX as u64) as u32); + } + let mut param_builder = ParamBuilder::new() + .push_byte_string(c_str(b"pass\0"), pass)? + .push_byte_string(c_str(b"salt\0"), salt)? + .push_uint(c_str(b"threads\0"), threads)? + .push_uint(c_str(b"lanes\0"), lanes)? + .push_uint(c_str(b"memcost\0"), memcost)? + .push_uint(c_str(b"iter\0"), iter)? + .push_size_t(c_str(b"size\0"), out.len())?; + if let Some(ad) = ad { + param_builder = param_builder.push_byte_string(c_str(b"ad\0"), ad)?; + } + if let Some(secret) = secret { + param_builder = param_builder.push_byte_string(c_str(b"secret\0"), secret)?; + } - let argon2 = EvpKdf(cvt_p(ffi::EVP_KDF_fetch( - libctx, - kdf_identifier.as_ptr() as *const c_char, - ptr::null(), - ))?); - let ctx = EvpKdfCtx(cvt_p(ffi::EVP_KDF_CTX_new(argon2.0))?); - cvt(ffi::EVP_KDF_derive( + let argon2 = EvpKdf(cvt_p(unsafe { + ffi::EVP_KDF_fetch(libctx, kdf_identifier.as_ptr(), ptr::null()) + })?); + let ctx = EvpKdfCtx(cvt_p(unsafe { ffi::EVP_KDF_CTX_new(argon2.0) })?); + cvt(unsafe { + ffi::EVP_KDF_derive( ctx.0, out.as_mut_ptr(), out.len(), - params.as_ptr(), - )) - .map(|_| ()) - } + param_builder.build()?.as_ptr(), + ) + }) + .map(|_| ()) } } } diff --git a/openssl/src/lib.rs b/openssl/src/lib.rs index 1afe5de38..21407e5e2 100644 --- a/openssl/src/lib.rs +++ b/openssl/src/lib.rs @@ -177,6 +177,8 @@ pub mod memcmp; pub mod nid; #[cfg(not(osslconf = "OPENSSL_NO_OCSP"))] pub mod ocsp; +#[cfg(ossl300)] +mod params; pub mod pkcs12; pub mod pkcs5; #[cfg(not(any(boringssl, awslc)))] diff --git a/openssl/src/params.rs b/openssl/src/params.rs new file mode 100644 index 000000000..be9290524 --- /dev/null +++ b/openssl/src/params.rs @@ -0,0 +1,332 @@ +use crate::bn::BigNumRef; +use crate::error::ErrorStack; +use crate::{cvt, cvt_p}; +use foreign_types::{ForeignType, ForeignTypeRef, Opaque}; +use libc::{c_char, c_void}; +use openssl_macros::corresponds; +use std::ffi::CStr; +use std::marker::PhantomData; + +pub struct Params<'a>(*mut ffi::OSSL_PARAM, PhantomData<&'a ()>); + +impl<'a> ForeignType for Params<'a> { + type CType = ffi::OSSL_PARAM; + type Ref = ParamsRef<'a>; + + #[inline] + unsafe fn from_ptr(ptr: *mut ffi::OSSL_PARAM) -> Params<'a> { + Self(ptr, PhantomData) + } + + #[inline] + fn as_ptr(&self) -> *mut ffi::OSSL_PARAM { + self.0 + } +} + +impl Drop for Params<'_> { + fn drop(&mut self) { + unsafe { ffi::OSSL_PARAM_free(self.0) }; + } +} + +impl Clone for Params<'_> { + #[inline] + fn clone(&self) -> Self { + Self(unsafe { ffi::OSSL_PARAM_dup(self.0) }, PhantomData) + } +} + +impl<'a> ToOwned for ParamsRef<'a> { + type Owned = Params<'a>; + + #[inline] + fn to_owned(&self) -> Params<'a> { + unsafe { + let handle: *mut ffi::OSSL_PARAM = ffi::OSSL_PARAM_dup(self.as_ptr()); + ForeignType::from_ptr(handle) + } + } +} + +impl<'a> std::ops::Deref for Params<'a> { + type Target = ParamsRef<'a>; + + #[inline] + fn deref(&self) -> &ParamsRef<'a> { + unsafe { ParamsRef::from_ptr(self.as_ptr()) } + } +} + +impl<'a> std::ops::DerefMut for Params<'a> { + #[inline] + fn deref_mut(&mut self) -> &mut ParamsRef<'a> { + unsafe { ParamsRef::from_ptr_mut(self.as_ptr()) } + } +} + +impl<'a> std::borrow::Borrow> for Params<'a> { + #[inline] + fn borrow(&self) -> &ParamsRef<'a> { + self + } +} + +impl<'a> AsRef> for Params<'a> { + #[inline] + fn as_ref(&self) -> &ParamsRef<'a> { + self + } +} + +pub struct ParamsRef<'a>(Opaque, PhantomData<&'a ()>); + +impl ForeignTypeRef for ParamsRef<'_> { + type CType = ffi::OSSL_PARAM; +} + +unsafe impl Send for Params<'_> {} +unsafe impl Send for ParamsRef<'_> {} +unsafe impl Sync for Params<'_> {} +unsafe impl Sync for ParamsRef<'_> {} + +impl<'a> ParamsRef<'a> { + /// Merges two `ParamsRef` objects into a new `Params` object. + #[corresponds(OSSL_PARAM_merge)] + #[allow(dead_code)] + pub fn merge(&self, other: &ParamsRef<'_>) -> Result, ErrorStack> { + // 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 { Params::from_ptr(p) }) + } + + /// Locate a parameter by the given key. + #[corresponds(OSSL_PARAM_locate_const)] + #[allow(dead_code)] + fn locate(&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) + } + } +} + +pub struct ParamBuilder<'a>(*mut ffi::OSSL_PARAM_BLD, PhantomData<&'a ()>); + +impl Drop for ParamBuilder<'_> { + #[inline] + fn drop(&mut self) { + unsafe { ffi::OSSL_PARAM_BLD_free(self.0) } + } +} + +unsafe impl Send for ParamBuilder<'_> {} +unsafe impl Sync for ParamBuilder<'_> {} + +impl<'a, 'b> ParamBuilder<'a> { + /// Creates a new `ParamBuilder`. + #[corresponds[OSSL_PARAM_BLD_new]] + #[allow(dead_code)] + pub fn new() -> Self { + unsafe { ParamBuilder(ffi::OSSL_PARAM_BLD_new(), PhantomData) } + } + + /// Push a BigNum parameter into the builder. + #[corresponds[OSSL_PARAM_BLD_push_BN]] + #[allow(dead_code)] + pub fn push_bignum(self, key: &'b CStr, bn: &'a BigNumRef) -> Result { + cvt(unsafe { ffi::OSSL_PARAM_BLD_push_BN(self.0, key.as_ptr(), bn.as_ptr()) })?; + Ok(self) + } + + /// Push a UTF-8 String parameter into the builder. + #[corresponds[OSSL_PARAM_BLD_push_utf8_string]] + #[allow(dead_code)] + pub fn push_utf_string(self, key: &'b CStr, string: &'a str) -> Result { + let value = string.as_bytes(); + cvt(unsafe { + ffi::OSSL_PARAM_BLD_push_utf8_string( + self.0, + key.as_ptr(), + value.as_ptr().cast::(), + value.len(), + ) + })?; + Ok(self) + } + + /// Push a byte string parameter into the builder. + #[corresponds[OSSL_PARAM_BLD_push_utf8_string]] + #[allow(dead_code)] + pub fn push_byte_string(self, key: &'b CStr, value: &'a [u8]) -> Result { + cvt(unsafe { + ffi::OSSL_PARAM_BLD_push_octet_string( + self.0, + key.as_ptr(), + value.as_ptr().cast::(), + value.len(), + ) + })?; + Ok(self) + } + + /// Push a uint parameter into the builder. + #[corresponds[OSSL_PARAM_BLD_push_uint]] + #[allow(dead_code)] + pub fn push_uint(self, key: &'b CStr, val: u32) -> Result { + cvt(unsafe { ffi::OSSL_PARAM_BLD_push_uint(self.0, key.as_ptr(), val) })?; + Ok(self) + } + + /// Push a size_t parameter into the builder. + #[corresponds[OSSL_PARAM_BLD_push_size_t]] + #[allow(dead_code)] + pub fn push_size_t(self, key: &'b CStr, val: usize) -> Result { + cvt(unsafe { ffi::OSSL_PARAM_BLD_push_size_t(self.0, key.as_ptr(), val) })?; + Ok(self) + } + + /// Build a `Params` array from the builder consuming the builder. + #[corresponds(OSSL_PARAM_BLD_to_param)] + #[allow(dead_code)] + pub fn build(self) -> Result, ErrorStack> { + let ptr = cvt_p(unsafe { ffi::OSSL_PARAM_BLD_to_param(self.0) })?; + Ok(unsafe { Params::from_ptr(ptr) }) + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::bn::BigNum; + use crate::util::c_str; + + fn assert_param(params: &ParamsRef<'_>, key: &CStr, is_null: bool) { + match params.locate(key) { + Some(_) => assert!(!is_null, "Unexpectedly found param: {key:?}"), + None => assert!(is_null, "Failed to find param: {key:?}"), + } + } + + #[test] + fn test_param_builder_uint() { + let params = ParamBuilder::new() + .push_uint(c_str(b"nonce-type\0"), 42) + .unwrap() + .build() + .unwrap(); + + assert_param(¶ms, c_str(b"nonce-type\0"), false); + assert_param(¶ms, c_str(b"group\0"), true); + } + + #[test] + fn test_param_builder_size_t() { + let params = ParamBuilder::new() + .push_size_t(c_str(b"size\0"), 42) + .unwrap() + .build() + .unwrap(); + + assert_param(¶ms, c_str(b"size\0"), false); + assert_param(¶ms, c_str(b"out\0"), true); + } + + #[test] + fn test_param_builder_bignum() { + let a = BigNum::from_u32(0x01).unwrap(); + let b = BigNum::from_u32(0x02).unwrap(); + let c = BigNum::from_u32(0x03).unwrap(); + + let params = ParamBuilder::new() + .push_bignum(c_str(b"a\0"), &a) + .unwrap() + .push_bignum(c_str(b"b\0"), &b) + .unwrap() + .push_bignum(c_str(b"c\0"), &c) + .unwrap() + .build() + .unwrap(); + + for param in [b"a\0", b"b\0", b"c\0"] { + assert_param(¶ms, c_str(param), false); + } + + assert_param(¶ms, c_str(b"group\0"), true); + } + + #[test] + fn test_param_builder_string() { + let params = ParamBuilder::new() + .push_utf_string(c_str(b"group\0"), "primve256v1") + .unwrap() + .build() + .unwrap(); + + assert_param(¶ms, c_str(b"group\0"), false); + assert_param(¶ms, c_str(b"n\0"), true); + } + + #[test] + fn test_param_builder_byte_string() { + let params = ParamBuilder::new() + .push_byte_string(c_str(b"pass\0"), b"primve256v1") + .unwrap() + .build() + .unwrap(); + + assert_param(¶ms, c_str(b"pass\0"), false); + assert_param(¶ms, c_str(b"group\0"), true); + } + + #[test] + fn test_merge() { + let n = BigNum::from_u32(0xbc747fc5).unwrap(); + let e = BigNum::from_u32(0x10001).unwrap(); + let d = BigNum::from_u32(0x7b133399).unwrap(); + + let mut merged_params: Params<'_>; + let params1 = ParamBuilder::new() + .push_bignum(c_str(b"n\0"), &n) + .unwrap() + .build() + .unwrap(); + { + let params2 = ParamBuilder::new() + .push_bignum(c_str(b"e\0"), &e) + .unwrap() + .build() + .unwrap(); + merged_params = params1.merge(¶ms2).unwrap(); + } + + // Merge 1 & 2, d (added in 3) should not be present + assert_param(&merged_params, c_str(b"n\0"), false); + assert_param(&merged_params, c_str(b"e\0"), false); + assert_param(&merged_params, c_str(b"d\0"), true); + + { + let params3 = ParamBuilder::new() + .push_bignum(c_str(b"d\0"), &d) + .unwrap() + .build() + .unwrap(); + merged_params = merged_params.merge(¶ms3).unwrap(); + } + + // Merge 3 into 1+2, we should now have all params + assert_param(&merged_params, c_str(b"n\0"), false); + assert_param(&merged_params, c_str(b"e\0"), false); + assert_param(&merged_params, c_str(b"e\0"), false); + } +} diff --git a/openssl/src/pkey_ctx.rs b/openssl/src/pkey_ctx.rs index 1b58108ae..4da1805b1 100644 --- a/openssl/src/pkey_ctx.rs +++ b/openssl/src/pkey_ctx.rs @@ -70,9 +70,17 @@ use crate::cipher::CipherRef; use crate::error::ErrorStack; use crate::md::MdRef; use crate::nid::Nid; +#[cfg(ossl320)] +use crate::params::ParamBuilder; +#[cfg(ossl300)] +use crate::params::ParamsRef; +#[cfg(ossl300)] +use crate::pkey::Public; use crate::pkey::{HasPrivate, HasPublic, Id, PKey, PKeyRef, Params, Private}; use crate::rsa::Padding; use crate::sign::RsaPssSaltlen; +#[cfg(ossl320)] +use crate::util::c_str; use crate::{cvt, cvt_p}; use cfg_if::cfg_if; use foreign_types::{ForeignType, ForeignTypeRef}; @@ -82,8 +90,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. @@ -127,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 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; @@ -434,6 +481,36 @@ impl PkeyCtxRef { Ok(()) } + /// Prepares the context for creating a key from user data. + #[corresponds(EVP_PKEY_fromdata_init)] + #[inline] + #[cfg(ossl300)] + #[allow(dead_code)] + 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)] + #[allow(dead_code)] + pub(crate) fn fromdata( + &mut self, + params: &ParamsRef<'_>, + ) -> Result, ErrorStack> { + let mut key_ptr = ptr::null_mut(); + cvt(unsafe { + ffi::EVP_PKEY_fromdata( + self.as_ptr(), + &mut key_ptr, + K::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 @@ -867,6 +944,14 @@ impl PkeyCtxRef { } } + /// Sets parameters on the given context + #[corresponds(EVP_PKEY_CTX_set_params)] + #[cfg(ossl300)] + #[allow(dead_code)] + fn set_params(&mut self, params: &ParamsRef<'_>) -> 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 +961,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 params = ParamBuilder::new() + .push_uint(c_str(b"nonce-type\0"), nonce_type.0)? + .build()?; + self.set_params(¶ms) } /// Gets the nonce type for a private key context. @@ -898,7 +976,7 @@ 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 nonce_field_name = c_str(b"nonce-type\0"); let mut nonce_type: c_uint = 0; unsafe { let param_nonce = @@ -915,6 +993,18 @@ impl PkeyCtxRef { } } +/// Creates a new `PKey` from the given ID and parameters. +#[cfg(ossl300)] +#[allow(dead_code)] +pub(crate) fn pkey_from_params( + id: Id, + params: &ParamsRef<'_>, +) -> Result, ErrorStack> { + let mut ctx = PkeyCtx::new_id(id)?; + ctx.fromdata_init()?; + ctx.fromdata(params) +} + #[cfg(test)] mod test { use super::*; @@ -925,11 +1015,17 @@ mod test { use crate::hash::{hash, MessageDigest}; use crate::md::Md; use crate::nid::Nid; + #[cfg(ossl300)] + use crate::params::ParamBuilder; use crate::pkey::PKey; use crate::rsa::Rsa; use crate::sign::Verifier; + #[cfg(ossl300)] + use crate::util::c_str; #[cfg(not(boringssl))] use cfg_if::cfg_if; + #[cfg(ossl300)] + use std::cmp::Ordering; #[test] fn rsa() { @@ -1301,4 +1397,30 @@ mxJ7imIrEg9nIQ== assert_eq!(output, expected_output); assert!(ErrorStack::get().errors().is_empty()); } + + #[test] + #[cfg(ossl300)] + fn test_fromdata() { + let n = BigNum::from_u32(0xbc747fc5).unwrap(); + let e = BigNum::from_u32(0x10001).unwrap(); + let d = BigNum::from_u32(0x7b133399).unwrap(); + + let params = ParamBuilder::new() + .push_bignum(c_str(b"n\0"), &n) + .unwrap() + .push_bignum(c_str(b"e\0"), &e) + .unwrap() + .push_bignum(c_str(b"d\0"), &d) + .unwrap() + .build() + .unwrap(); + + let pkey: PKey = pkey_from_params(Id::RSA, ¶ms).unwrap(); + + let rsa = pkey.rsa().unwrap(); + + assert_eq!(rsa.n().ucmp(&n), Ordering::Equal); + assert_eq!(rsa.e().ucmp(&e), Ordering::Equal); + assert_eq!(rsa.d().ucmp(&d), Ordering::Equal); + } } diff --git a/openssl/src/util.rs b/openssl/src/util.rs index c903a3209..5cefdefd2 100644 --- a/openssl/src/util.rs +++ b/openssl/src/util.rs @@ -3,6 +3,7 @@ use crate::util; use foreign_types::{ForeignType, ForeignTypeRef}; use libc::{c_char, c_int, c_void}; use std::any::Any; +use std::ffi::CStr; use std::panic::{self, AssertUnwindSafe}; use std::slice; @@ -116,3 +117,11 @@ pub unsafe fn from_raw_parts_mut<'a, T>(data: *mut T, len: usize) -> &'a mut [T] slice::from_raw_parts_mut(data, len) } } + +/// Converts a byte slice to a C string. +/// +/// String should be null-terminated. +#[allow(dead_code)] +pub fn c_str(s: &[u8]) -> &CStr { + CStr::from_bytes_with_nul(s).unwrap() +} diff --git a/systest/build.rs b/systest/build.rs index 22fc6b836..eddcf6076 100644 --- a/systest/build.rs +++ b/systest/build.rs @@ -83,7 +83,9 @@ fn main() { } if version >= 0x30000000 { - cfg.header("openssl/provider.h"); + cfg.header("openssl/provider.h") + .header("openssl/params.h") + .header("openssl/param_build.h"); } if version >= 0x30200000 { cfg.header("openssl/thread.h");