diff --git a/openssl-sys/build/run_bindgen.rs b/openssl-sys/build/run_bindgen.rs index 27bd482b3..a90fb9293 100644 --- a/openssl-sys/build/run_bindgen.rs +++ b/openssl-sys/build/run_bindgen.rs @@ -53,6 +53,7 @@ const INCLUDES: &str = " #endif #if OPENSSL_VERSION_NUMBER >= 0x30000000 +#include #include #endif diff --git a/openssl-sys/src/core_dispatch.rs b/openssl-sys/src/core_dispatch.rs new file mode 100644 index 000000000..446dfc96e --- /dev/null +++ b/openssl-sys/src/core_dispatch.rs @@ -0,0 +1,11 @@ +use super::*; +use libc::*; + +/* OpenSSL 3.* only */ + +pub const OSSL_KEYMGMT_SELECT_PRIVATE_KEY: c_int = 0x01; +pub const OSSL_KEYMGMT_SELECT_PUBLIC_KEY: c_int = 0x02; +pub const OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS: c_int = 0x04; +pub const OSSL_KEYMGMT_SELECT_OTHER_PARAMETERS: c_int = 0x80; +pub const OSSL_KEYMGMT_SELECT_ALL_PARAMETERS: c_int = + OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS | OSSL_KEYMGMT_SELECT_OTHER_PARAMETERS; diff --git a/openssl-sys/src/evp.rs b/openssl-sys/src/evp.rs index 4d26f0f60..6ace42b86 100644 --- a/openssl-sys/src/evp.rs +++ b/openssl-sys/src/evp.rs @@ -38,6 +38,15 @@ pub const EVP_CTRL_GCM_SET_IVLEN: c_int = 0x9; pub const EVP_CTRL_GCM_GET_TAG: c_int = 0x10; pub const EVP_CTRL_GCM_SET_TAG: c_int = 0x11; +#[cfg(ossl300)] +pub const EVP_PKEY_KEY_PARAMETERS: c_int = OSSL_KEYMGMT_SELECT_ALL_PARAMETERS; +#[cfg(ossl300)] +pub const EVP_PKEY_PRIVATE_KEY: c_int = EVP_PKEY_KEY_PARAMETERS | OSSL_KEYMGMT_SELECT_PRIVATE_KEY; +#[cfg(ossl300)] +pub const EVP_PKEY_PUBLIC_KEY: c_int = EVP_PKEY_KEY_PARAMETERS | OSSL_KEYMGMT_SELECT_PUBLIC_KEY; +#[cfg(ossl300)] +pub const EVP_PKEY_KEYPAIR: c_int = EVP_PKEY_PUBLIC_KEY | OSSL_KEYMGMT_SELECT_PRIVATE_KEY; + pub unsafe fn EVP_get_digestbynid(type_: c_int) -> *const EVP_MD { EVP_get_digestbyname(OBJ_nid2sn(type_)) } diff --git a/openssl-sys/src/handwritten/evp.rs b/openssl-sys/src/handwritten/evp.rs index a1be1da68..1c543e1bb 100644 --- a/openssl-sys/src/handwritten/evp.rs +++ b/openssl-sys/src/handwritten/evp.rs @@ -489,6 +489,34 @@ extern "C" { #[cfg(any(ossl110, libressl270))] pub fn EVP_PKEY_up_ref(pkey: *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, + param: *mut OSSL_PARAM, + ) -> c_int; + + #[cfg(ossl300)] + pub fn EVP_PKEY_todata( + ppkey: *const EVP_PKEY, + selection: c_int, + param: *mut *mut OSSL_PARAM, + ) -> c_int; + + #[cfg(ossl300)] + pub fn EVP_PKEY_set_bn_param( + k: *mut EVP_PKEY, + key_name: *const c_char, + bn: *const BIGNUM, + ) -> c_int; + + #[cfg(ossl300)] + pub fn EVP_PKEY_generate(ctx: *mut EVP_PKEY_CTX, k: *mut *mut EVP_PKEY) -> c_int; + pub fn d2i_AutoPrivateKey( a: *mut *mut EVP_PKEY, pp: *mut *const c_uchar, @@ -535,6 +563,12 @@ extern "C" { pub fn EVP_PKEY_CTX_new(k: *mut EVP_PKEY, e: *mut ENGINE) -> *mut EVP_PKEY_CTX; pub fn EVP_PKEY_CTX_new_id(id: c_int, e: *mut ENGINE) -> *mut EVP_PKEY_CTX; + #[cfg(ossl300)] + pub fn EVP_PKEY_CTX_new_from_name( + libctx: *mut OSSL_LIB_CTX, + name: *const c_char, + propquery: *const c_char, + ) -> *mut EVP_PKEY_CTX; pub fn EVP_PKEY_CTX_free(ctx: *mut EVP_PKEY_CTX); pub fn EVP_PKEY_CTX_ctrl( diff --git a/openssl-sys/src/handwritten/mod.rs b/openssl-sys/src/handwritten/mod.rs index 47b3360fd..33e405221 100644 --- a/openssl-sys/src/handwritten/mod.rs +++ b/openssl-sys/src/handwritten/mod.rs @@ -15,6 +15,9 @@ pub use self::hmac::*; pub use self::kdf::*; pub use self::object::*; pub use self::ocsp::*; +#[cfg(ossl300)] +pub use self::param_build::*; +#[cfg(ossl300)] pub use self::params::*; pub use self::pem::*; pub use self::pkcs12::*; @@ -54,6 +57,9 @@ mod hmac; mod kdf; mod object; mod ocsp; +#[cfg(ossl300)] +mod param_build; +#[cfg(ossl300)] mod params; mod pem; mod pkcs12; diff --git a/openssl-sys/src/handwritten/param_build.rs b/openssl-sys/src/handwritten/param_build.rs new file mode 100644 index 000000000..7efbdb99c --- /dev/null +++ b/openssl-sys/src/handwritten/param_build.rs @@ -0,0 +1,32 @@ +use super::super::*; +use libc::*; + +/* OpenSSL 3.* only */ + +extern "C" { + 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_push_BN( + bld: *mut OSSL_PARAM_BLD, + key: *const c_char, + bn: *const BIGNUM, + ) -> c_int; + pub fn OSSL_PARAM_BLD_push_utf8_string( + bld: *mut OSSL_PARAM_BLD, + key: *const c_char, + buf: *const c_char, + bsize: usize, + ) -> c_int; + pub fn OSSL_PARAM_BLD_push_octet_string( + bld: *mut OSSL_PARAM_BLD, + key: *const c_char, + buf: *const c_void, + bsize: usize, + ) -> c_int; + pub fn OSSL_PARAM_BLD_push_uint( + bld: *mut OSSL_PARAM_BLD, + key: *const c_char, + buf: c_uint, + ) -> c_int; + pub fn OSSL_PARAM_BLD_to_param(bld: *mut OSSL_PARAM_BLD) -> *mut OSSL_PARAM; +} diff --git a/openssl-sys/src/handwritten/params.rs b/openssl-sys/src/handwritten/params.rs index 542cef337..913cc0e23 100644 --- a/openssl-sys/src/handwritten/params.rs +++ b/openssl-sys/src/handwritten/params.rs @@ -2,15 +2,32 @@ use super::super::*; use libc::*; extern "C" { - #[cfg(ossl300)] + pub fn OSSL_PARAM_free(p: *mut 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_locate(p: *mut OSSL_PARAM, key: *const c_char) -> *mut OSSL_PARAM; + pub fn OSSL_PARAM_get_BN(p: *const OSSL_PARAM, val: *mut *mut BIGNUM) -> c_int; + pub fn OSSL_PARAM_get_utf8_string( + p: *const OSSL_PARAM, + val: *mut *mut c_char, + max_len: usize, + ) -> c_int; + pub fn OSSL_PARAM_get_utf8_string_ptr(p: *const OSSL_PARAM, val: *mut *const c_char) -> c_int; + pub fn OSSL_PARAM_get_octet_string( + p: *const OSSL_PARAM, + val: *mut *mut c_void, + max_len: usize, + used_len: *mut usize, + ) -> c_int; + pub fn OSSL_PARAM_get_octet_string_ptr( + p: *const OSSL_PARAM, + val: *mut *const c_void, + used_len: *mut usize, + ) -> c_int; } diff --git a/openssl-sys/src/handwritten/types.rs b/openssl-sys/src/handwritten/types.rs index d465a4414..6fda6fa6e 100644 --- a/openssl-sys/src/handwritten/types.rs +++ b/openssl-sys/src/handwritten/types.rs @@ -1140,6 +1140,9 @@ pub struct OSSL_PARAM { return_size: size_t, } +#[cfg(ossl300)] +pub enum OSSL_PARAM_BLD {} + #[cfg(ossl300)] pub enum EVP_KDF {} #[cfg(ossl300)] diff --git a/openssl-sys/src/lib.rs b/openssl-sys/src/lib.rs index 0e23386fd..17c594b17 100644 --- a/openssl-sys/src/lib.rs +++ b/openssl-sys/src/lib.rs @@ -45,6 +45,8 @@ mod openssl { pub use self::bio::*; pub use self::bn::*; pub use self::cms::*; + #[cfg(ossl300)] + pub use self::core_dispatch::*; pub use self::crypto::*; pub use self::dtls1::*; pub use self::ec::*; @@ -75,6 +77,8 @@ mod openssl { mod bio; mod bn; mod cms; + #[cfg(ossl300)] + mod core_dispatch; mod crypto; mod dtls1; mod ec; diff --git a/openssl/src/lib.rs b/openssl/src/lib.rs index c58e5bf59..796d64ad3 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)] +pub mod ossl_param; pub mod pkcs12; pub mod pkcs5; #[cfg(not(boringssl))] diff --git a/openssl/src/ossl_param.rs b/openssl/src/ossl_param.rs new file mode 100644 index 000000000..607c68cee --- /dev/null +++ b/openssl/src/ossl_param.rs @@ -0,0 +1,341 @@ +//! OSSL_PARAM management for OpenSSL 3.* +//! +//! The OSSL_PARAM structure represents generic attribute that can represent various +//! properties in OpenSSL, including keys and operations. +//! +//! For convinience, the OSSL_PARAM_BLD builder can be used to dynamically construct +//! these structure. +//! +//! Note, that this module is available only in OpenSSL 3.* +//! +//! # Example: Generate RSA Key +//! +//! let mut ctx = PkeyCtx::new_from_name(None, "RSA", None).unwrap(); +//! ctx.keygen_init().unwrap(); +//! let mut bld = OsslParamBuilder::new().unwrap(); +//! bld.add_uint("bits\0", 3096).unwrap(); +//! let params = bld.to_params().unwrap(); +//! ctx.set_params(params).unwrap(); +//! let key = ctx.generate().unwrap(); +//! +use crate::bn::BigNumRef; +use crate::error::ErrorStack; +use crate::util; +use crate::{cvt, cvt_p}; +use foreign_types::{ForeignType, ForeignTypeRef}; +use libc::{c_char, c_uint, c_void}; +use openssl_macros::corresponds; +use std::ffi::CStr; +use std::ptr; + +foreign_type_and_impl_send_sync! { + type CType = ffi::OSSL_PARAM; + fn drop = ffi::OSSL_PARAM_free; + + /// `OsslParam` constructed using `OsslParamBuilder`. + pub struct OsslParam; + /// Reference to `OsslParam`. + pub struct OsslParamRef; +} + +impl OsslParam {} + +impl OsslParamRef { + /// Locates the `OsslParam` in the `OsslParam` array + #[corresponds(OSSL_PARAM_locate)] + pub fn locate(&self, key: &str) -> Result<&OsslParamRef, ErrorStack> { + unsafe { + let param = cvt_p(ffi::OSSL_PARAM_locate( + self.as_ptr(), + key.as_ptr() as *const c_char, + ))?; + Ok(OsslParamRef::from_ptr(param)) + } + } + + /// Get `BigNumRef` from the current `OsslParam` + #[corresponds(OSSL_PARAM_get_BN)] + pub fn get_bn(&self) -> Result<&BigNumRef, ErrorStack> { + unsafe { + let mut bn: *mut ffi::BIGNUM = ptr::null_mut(); + cvt(ffi::OSSL_PARAM_get_BN(self.as_ptr(), &mut bn))?; + Ok(BigNumRef::from_ptr(bn)) + } + } + + /// Get `&str` from the current `OsslParam` + #[corresponds(OSSL_PARAM_get_utf8_string)] + pub fn get_utf8_string(&self) -> Result<&str, ErrorStack> { + unsafe { + let mut val: *const c_char = ptr::null_mut(); + cvt(ffi::OSSL_PARAM_get_utf8_string_ptr(self.as_ptr(), &mut val))?; + Ok(CStr::from_ptr(val).to_str().unwrap()) + } + } + + /// Get octet string (as `&[u8]) from the current `OsslParam` + #[corresponds(OSSL_PARAM_get_octet_string)] + pub fn get_octet_string(&self) -> Result<&[u8], ErrorStack> { + unsafe { + let mut val: *const c_void = ptr::null_mut(); + let mut val_len: usize = 0; + cvt(ffi::OSSL_PARAM_get_octet_string_ptr( + self.as_ptr(), + &mut val, + &mut val_len, + ))?; + Ok(util::from_raw_parts(val as *const u8, val_len)) + } + } +} + +foreign_type_and_impl_send_sync! { + type CType = ffi::OSSL_PARAM_BLD; + fn drop = ffi::OSSL_PARAM_BLD_free; + + /// Builder used to construct `OsslParam`. + pub struct OsslParamBuilder; + /// Reference to `OsslParamBuilder`. + pub struct OsslParamBuilderRef; +} + +impl OsslParamBuilder { + /// Returns a builder for a OsslParam arrays. + /// + /// The array is initially empty. + #[corresponds(OSSL_PARAM_BLD_new)] + pub fn new() -> Result { + unsafe { + ffi::init(); + + cvt_p(ffi::OSSL_PARAM_BLD_new()).map(OsslParamBuilder) + } + } + + /// Constructs the `OsslParam`. + #[corresponds(OSSL_PARAM_BLD_to_param)] + pub fn to_params(self) -> Result { + unsafe { + let params = cvt_p(ffi::OSSL_PARAM_BLD_to_param(self.0))?; + Ok(OsslParam::from_ptr(params)) + } + } +} + +impl OsslParamBuilderRef { + /// Adds a `BigNum` to `OsslParamBuilder`. + /// + /// Note, that both key and bn need to exist until the `to_params` is called! + #[corresponds(OSSL_PARAM_BLD_push_BN)] + pub fn add_bn(&mut self, key: &str, bn: &BigNumRef) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::OSSL_PARAM_BLD_push_BN( + self.as_ptr(), + key.as_ptr() as *const c_char, + bn.as_ptr(), + )) + .map(|_| ()) + } + } + + /// Adds a utf8 string to `OsslParamBuilder`. + /// + /// Note, that both `key` and `buf` need to exist until the `to_params` is called! + #[corresponds(OSSL_PARAM_BLD_push_utf8_string)] + pub fn add_utf8_string(&mut self, key: &str, buf: &str) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::OSSL_PARAM_BLD_push_utf8_string( + self.as_ptr(), + key.as_ptr() as *const c_char, + buf.as_ptr() as *const c_char, + buf.len(), + )) + .map(|_| ()) + } + } + + /// Adds a octet string to `OsslParamBuilder`. + /// + /// Note, that both `key` and `buf` need to exist until the `to_params` is called! + #[corresponds(OSSL_PARAM_BLD_push_octet_string)] + pub fn add_octet_string(&mut self, key: &str, buf: &[u8]) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::OSSL_PARAM_BLD_push_octet_string( + self.as_ptr(), + key.as_ptr() as *const c_char, + buf.as_ptr() as *const c_void, + buf.len(), + )) + .map(|_| ()) + } + } + + /// Adds a unsigned int to `OsslParamBuilder`. + /// + /// Note, that both `key` and `buf` need to exist until the `to_params` is called! + #[corresponds(OSSL_PARAM_BLD_push_uint)] + pub fn add_uint(&mut self, key: &str, val: u32) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::OSSL_PARAM_BLD_push_uint( + self.as_ptr(), + key.as_ptr() as *const c_char, + val as c_uint, + )) + .map(|_| ()) + } + } +} + +#[cfg(test)] +mod tests { + + use crate::ec::EcKey; + use crate::error::Error; + use crate::nid::Nid; + use crate::pkey::{PKey, Private}; + use crate::pkey_ctx::PkeyCtx; + use crate::rsa::Rsa; + + use super::*; + + #[test] + fn test_build_pkey_rsa() { + /* First, generate the key with old API */ + let rsa = Rsa::generate(2048).unwrap(); + let pkey1 = PKey::from_rsa(rsa.clone()).unwrap(); + + /* Now, construct the OSSL_PARAM manually from the old key */ + let mut bld = OsslParamBuilder::new().unwrap(); + // TODO do we want a better API with the parameter names? + bld.add_bn("n\0", rsa.n()).unwrap(); + bld.add_bn("e\0", rsa.e()).unwrap(); + bld.add_bn("d\0", rsa.d()).unwrap(); + bld.add_bn("rsa-factor1\0", rsa.p().unwrap()).unwrap(); + bld.add_bn("rsa-factor2\0", rsa.q().unwrap()).unwrap(); + bld.add_bn("rsa-exponent1\0", rsa.dmp1().unwrap()).unwrap(); + bld.add_bn("rsa-exponent2\0", rsa.dmq1().unwrap()).unwrap(); + bld.add_bn("rsa-coefficient1\0", rsa.iqmp().unwrap()) + .unwrap(); + let params = bld.to_params().unwrap(); + + let mut ctx = PkeyCtx::new_from_name(None, "RSA", None).unwrap(); + ctx.fromdata_init().unwrap(); + let pkey2 = PKey::::fromdata(ctx, params).unwrap(); + + /* Verify it works the same way as the old one */ + assert!(pkey1.public_eq(&pkey2)); + assert!(Error::get().is_none()); + + // FIXME use of ffi in test is not intended -- we will need some constants + let params = pkey2.todata(ffi::EVP_PKEY_KEYPAIR).unwrap(); + assert_eq!(params.locate("n\0").unwrap().get_bn().unwrap(), rsa.n()); + assert_eq!(params.locate("e\0").unwrap().get_bn().unwrap(), rsa.e()); + assert_eq!(params.locate("d\0").unwrap().get_bn().unwrap(), rsa.d()); + assert_eq!( + params.locate("rsa-factor1\0").unwrap().get_bn().unwrap(), + rsa.p().unwrap() + ); + assert_eq!( + params.locate("rsa-factor2\0").unwrap().get_bn().unwrap(), + rsa.q().unwrap() + ); + assert_eq!( + params.locate("rsa-exponent1\0").unwrap().get_bn().unwrap(), + rsa.dmp1().unwrap() + ); + assert_eq!( + params.locate("rsa-exponent2\0").unwrap().get_bn().unwrap(), + rsa.dmq1().unwrap() + ); + assert_eq!( + params + .locate("rsa-coefficient1\0") + .unwrap() + .get_bn() + .unwrap(), + rsa.iqmp().unwrap() + ); + } + + #[test] + fn test_build_pkey_ecdsa() { + use crate::bn::BigNumContext; + use crate::ec::PointConversionForm; + + /* First, generate the key with old API */ + let group = crate::ec::EcGroup::from_curve_name(Nid::SECP256K1).unwrap(); + let ec_key = EcKey::generate(&group).unwrap(); + let pkey1 = PKey::from_ec_key(ec_key.clone()).unwrap(); + + /* is there a better way? */ + let mut ctx = BigNumContext::new().unwrap(); + let pubkey = ec_key + .public_key() + .to_bytes(&group, PointConversionForm::UNCOMPRESSED, &mut ctx) + .unwrap(); + + /* Now, construct the OSSL_PARAM manually from the old key */ + let mut bld = OsslParamBuilder::new().unwrap(); + // TODO do we want a better API with the parameter names? + bld.add_utf8_string("group\0", "secp256k1").unwrap(); + bld.add_octet_string("pub\0", pubkey.as_slice()).unwrap(); + bld.add_bn("priv\0", ec_key.private_key()).unwrap(); + let params = bld.to_params().unwrap(); + + /* Build new key */ + let mut ctx = PkeyCtx::new_from_name(None, "EC", None).unwrap(); + ctx.fromdata_init().unwrap(); + let pkey2 = PKey::::fromdata(ctx, params).unwrap(); + + /* Verify it works the same way as the old one */ + assert!(pkey1.public_eq(&pkey2)); + assert!(Error::get().is_none()); + + // FIXME use of ffi in test is not intended -- we will need some constants + let params = pkey2.todata(ffi::EVP_PKEY_KEYPAIR).unwrap(); + assert_eq!( + params.locate("priv\0").unwrap().get_bn().unwrap(), + ec_key.private_key() + ); + assert_eq!( + params.locate("group\0").unwrap().get_utf8_string().unwrap(), + "secp256k1" + ); + assert_eq!( + params.locate("pub\0").unwrap().get_octet_string().unwrap(), + pubkey.as_slice() + ); + } + + #[test] + fn test_generate_rsa() { + use crate::pkey::Id; + + let mut ctx = PkeyCtx::new_from_name(None, "RSA", None).unwrap(); + ctx.keygen_init().unwrap(); + + let mut bld = OsslParamBuilder::new().unwrap(); + bld.add_uint("bits\0", 3096).unwrap(); + let params = bld.to_params().unwrap(); + ctx.set_params(params).unwrap(); + let key = ctx.generate().unwrap(); + + assert_eq!(key.id(), Id::RSA); + assert!(key.dsa().is_err()); + + let rsa = key.rsa().unwrap(); + // FIXME use of ffi in test is not intended -- we will need some constants + let params = key.todata(ffi::EVP_PKEY_KEYPAIR).unwrap(); + assert_eq!(rsa.e(), params.locate("e\0").unwrap().get_bn().unwrap()); + assert_eq!(rsa.n(), params.locate("n\0").unwrap().get_bn().unwrap()); + assert_eq!(rsa.d(), params.locate("d\0").unwrap().get_bn().unwrap()); + assert_eq!( + rsa.p().unwrap(), + params.locate("rsa-factor1\0").unwrap().get_bn().unwrap() + ); + assert_eq!( + rsa.q().unwrap(), + params.locate("rsa-factor2\0").unwrap().get_bn().unwrap() + ); + } +} diff --git a/openssl/src/pkey.rs b/openssl/src/pkey.rs index f2cedd271..59439d332 100644 --- a/openssl/src/pkey.rs +++ b/openssl/src/pkey.rs @@ -41,12 +41,16 @@ //! ``` #![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; use crate::dsa::Dsa; use crate::ec::EcKey; use crate::error::ErrorStack; +#[cfg(ossl300)] +use crate::ossl_param::{OsslParam, OsslParamRef}; #[cfg(any(ossl110, boringssl, libressl370))] use crate::pkey_ctx::PkeyCtx; use crate::rsa::Rsa; @@ -55,6 +59,8 @@ 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}; @@ -207,6 +213,36 @@ impl PKeyRef { pub fn size(&self) -> usize { unsafe { ffi::EVP_PKEY_size(self.as_ptr()) as usize } } + + /// Sets a `BigNum` parameter to the given `PKey` + #[corresponds(EVP_PKEY_set_bn_param)] + #[cfg(ossl300)] + pub fn set_bn_param(&mut self, key: &str, bn: &BigNumRef) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::EVP_PKEY_set_bn_param( + self.as_ptr(), + key.as_ptr() as *const c_char, + bn.as_ptr(), + )) + .map(|_| ())? + } + Ok(()) + } + + /// Creates a new `OsslParam` from `PKey`. + #[corresponds(EVP_PKEY_todata)] + #[cfg(ossl300)] + pub fn todata(&self, selection: c_int) -> Result<&OsslParamRef, ErrorStack> { + unsafe { + let mut params: *mut ffi::OSSL_PARAM = ptr::null_mut(); + cvt(ffi::EVP_PKEY_todata( + self.as_ptr(), + selection as c_int, + &mut params, + ))?; + Ok(OsslParamRef::from_ptr(params)) + } + } } impl PKeyRef @@ -759,6 +795,23 @@ impl PKey { .map(|p| PKey::from_ptr(p)) } } + + /// Creates a new `PKey` from `OsslParam`. + #[corresponds(EVP_PKEY_fromdata)] + #[cfg(ossl300)] + pub fn fromdata(ctx: PkeyCtx<()>, params: OsslParam) -> Result, ErrorStack> { + unsafe { + let evp = cvt_p(ffi::EVP_PKEY_new())?; + let pkey = PKey::from_ptr(evp); + cvt(ffi::EVP_PKEY_fromdata( + ctx.as_ptr(), + &mut pkey.as_ptr(), + ffi::EVP_PKEY_PRIVATE_KEY, + params.as_ptr(), + ))?; + Ok(pkey) + } + } } impl PKey { @@ -810,6 +863,23 @@ impl PKey { .map(|p| PKey::from_ptr(p)) } } + + /// Creates a new `PKey` from `OsslParam`. + #[corresponds(EVP_PKEY_fromdata)] + #[cfg(ossl300)] + pub fn fromdata(ctx: PkeyCtx<()>, params: OsslParam) -> Result, ErrorStack> { + unsafe { + let evp = cvt_p(ffi::EVP_PKEY_new())?; + let pkey = PKey::from_ptr(evp); + cvt(ffi::EVP_PKEY_fromdata( + ctx.as_ptr(), + &mut pkey.as_ptr(), + ffi::EVP_PKEY_PUBLIC_KEY, + params.as_ptr(), + ))?; + Ok(pkey) + } + } } cfg_if! { diff --git a/openssl/src/pkey_ctx.rs b/openssl/src/pkey_ctx.rs index f30f06973..f8437d623 100644 --- a/openssl/src/pkey_ctx.rs +++ b/openssl/src/pkey_ctx.rs @@ -67,7 +67,11 @@ let cmac_key = ctx.keygen().unwrap(); #[cfg(not(boringssl))] use crate::cipher::CipherRef; use crate::error::ErrorStack; +#[cfg(ossl300)] +use crate::lib_ctx::LibCtxRef; use crate::md::MdRef; +#[cfg(ossl300)] +use crate::ossl_param::OsslParam; use crate::pkey::{HasPrivate, HasPublic, Id, PKey, PKeyRef, Private}; use crate::rsa::Padding; use crate::sign::RsaPssSaltlen; @@ -81,6 +85,8 @@ use openssl_macros::corresponds; use std::convert::TryFrom; #[cfg(ossl320)] use std::ffi::CStr; +#[cfg(ossl300)] +use std::ffi::CString; use std::ptr; /// HKDF modes of operation. @@ -148,7 +154,7 @@ impl PkeyCtx { impl PkeyCtx<()> { /// Creates a new pkey context for the specified algorithm ID. - #[corresponds(EVP_PKEY_new_id)] + #[corresponds(EVP_PKEY_CTX_new_id)] #[inline] pub fn new_id(id: Id) -> Result { unsafe { @@ -156,6 +162,26 @@ impl PkeyCtx<()> { Ok(PkeyCtx::from_ptr(ptr)) } } + + /// Creates a new pkey context from the algorithm name. + #[corresponds(EVP_PKEY_CTX_new_from_name)] + #[cfg(ossl300)] + pub fn new_from_name( + libctx: Option<&LibCtxRef>, + name: &str, + propquery: Option<&str>, + ) -> Result { + unsafe { + let propquery = propquery.map(|s| CString::new(s).unwrap()); + let name = CString::new(name).unwrap(); + let ptr = cvt_p(ffi::EVP_PKEY_CTX_new_from_name( + libctx.map_or(ptr::null_mut(), ForeignTypeRef::as_ptr), + name.as_ptr(), + propquery.map_or(ptr::null_mut(), |s| s.as_ptr()), + ))?; + Ok(PkeyCtx::from_ptr(ptr)) + } + } } impl PkeyCtxRef @@ -756,6 +782,32 @@ impl PkeyCtxRef { Ok(()) } + /// Sets params for the pkey context. + /// + /// Requires OpenSSL 3.0.0 or newer. + #[corresponds(EVP_PKEY_CTX_set_params)] + #[cfg(ossl300)] + pub fn set_params(&mut self, params: OsslParam) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::EVP_PKEY_CTX_set_params(self.as_ptr(), params.as_ptr()))?; + } + Ok(()) + } + + /// Generates a new public/private keypair. + /// + /// New OpenSSL 3.0 function, that should do the same thing as keygen() + #[corresponds(EVP_PKEY_generate)] + #[cfg(ossl300)] + #[inline] + pub fn generate(&mut self) -> Result, ErrorStack> { + unsafe { + let mut key = ptr::null_mut(); + cvt(ffi::EVP_PKEY_generate(self.as_ptr(), &mut key))?; + Ok(PKey::from_ptr(key)) + } + } + /// Gets 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). @@ -780,6 +832,14 @@ impl PkeyCtxRef { } Ok(NonceType(nonce_type)) } + + /// Initializes a conversion from `OsllParam` to `PKey` on given `PkeyCtx`. + #[corresponds(EVP_PKEY_fromdata_init)] + #[cfg(ossl300)] + pub fn fromdata_init(&mut self) -> Result<(), ErrorStack> { + unsafe { cvt(ffi::EVP_PKEY_fromdata_init(self.as_ptr()))? }; + Ok(()) + } } #[cfg(test)] @@ -1107,4 +1167,14 @@ mxJ7imIrEg9nIQ== assert_eq!(output, expected_output); assert!(ErrorStack::get().errors().is_empty()); } + + #[test] + #[cfg(ossl300)] + fn test_pkeyctx_from_name() { + let lib_ctx = crate::lib_ctx::LibCtx::new().unwrap(); + let _: PkeyCtx<()> = PkeyCtx::new_from_name(Some(lib_ctx.as_ref()), "RSA", None).unwrap(); + + /* no libctx is ok */ + let _: PkeyCtx<()> = PkeyCtx::new_from_name(None, "RSA", None).unwrap(); + } } diff --git a/systest/build.rs b/systest/build.rs index fc970f410..43b5c2334 100644 --- a/systest/build.rs +++ b/systest/build.rs @@ -83,7 +83,8 @@ fn main() { } if version >= 0x30000000 { - cfg.header("openssl/provider.h"); + cfg.header("openssl/provider.h") + .header("openssl/param_build.h"); } if version >= 0x30200000 { cfg.header("openssl/thread.h");