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..6fe190b3f 100644 --- a/openssl-sys/src/handwritten/evp.rs +++ b/openssl-sys/src/handwritten/evp.rs @@ -489,6 +489,27 @@ 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_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 +556,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 0a262db89..c2adacae6 100644 --- a/openssl-sys/src/lib.rs +++ b/openssl-sys/src/lib.rs @@ -42,6 +42,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::*; @@ -72,6 +74,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/kdf.rs b/openssl/src/kdf.rs index a5da35250..13ad2899e 100644 --- a/openssl/src/kdf.rs +++ b/openssl/src/kdf.rs @@ -25,14 +25,23 @@ 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::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::ossl_param::OsslParamBuilder; + + const OSSL_KDF_PARAM_PASSWORD: &[u8; 5] = b"pass\0"; + const OSSL_KDF_PARAM_SALT: &[u8; 5] = b"salt\0"; + const OSSL_KDF_PARAM_SECRET: &[u8; 7] = b"secret\0"; + const OSSL_KDF_PARAM_ITER: &[u8; 5] = b"iter\0"; + const OSSL_KDF_PARAM_SIZE: &[u8; 5] = b"size\0"; + const OSSL_KDF_PARAM_THREADS: &[u8; 8] = b"threads\0"; + const OSSL_KDF_PARAM_ARGON2_AD: &[u8; 3] = b"ad\0"; + const OSSL_KDF_PARAM_ARGON2_LANES: &[u8; 6] = b"lanes\0"; + const OSSL_KDF_PARAM_ARGON2_MEMCOST: &[u8; 8] = b"memcost\0"; /// Derives a key using the argon2id algorithm. /// @@ -48,72 +57,40 @@ 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 { + let libctx = ctx.map_or(ptr::null_mut(), ForeignTypeRef::as_ptr); + let max_threads = 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 - // argon2id'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::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 + // argon2id'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 bld = OsslParamBuilder::new()?; + bld.add_octet_string(OSSL_KDF_PARAM_PASSWORD, pass)?; + bld.add_octet_string(OSSL_KDF_PARAM_SALT, salt)?; + bld.add_uint(OSSL_KDF_PARAM_THREADS, threads)?; + bld.add_uint(OSSL_KDF_PARAM_ARGON2_LANES, lanes)?; + bld.add_uint(OSSL_KDF_PARAM_ARGON2_MEMCOST, memcost)?; + bld.add_uint(OSSL_KDF_PARAM_ITER, iter)?; + let size = out.len() as u32; + bld.add_uint(OSSL_KDF_PARAM_SIZE, size)?; + if let Some(ad) = ad { + bld.add_octet_string(OSSL_KDF_PARAM_ARGON2_AD, ad)?; + } + if let Some(secret) = secret { + bld.add_octet_string(OSSL_KDF_PARAM_SECRET, secret)?; + } + let params = bld.to_param()?; + unsafe { let argon2 = EvpKdf(cvt_p(ffi::EVP_KDF_fetch( libctx, b"ARGON2ID\0".as_ptr() as *const c_char, diff --git a/openssl/src/lib.rs b/openssl/src/lib.rs index c58e5bf59..bb0a265ab 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 ossl_param; pub mod pkcs12; pub mod pkcs5; #[cfg(not(boringssl))] @@ -184,6 +186,10 @@ pub mod pkcs7; pub mod pkey; pub mod pkey_ctx; #[cfg(ossl300)] +pub mod pkey_ecdsa; +#[cfg(ossl300)] +pub mod pkey_rsa; +#[cfg(ossl300)] pub mod provider; pub mod rand; pub mod rsa; diff --git a/openssl/src/ossl_param.rs b/openssl/src/ossl_param.rs new file mode 100644 index 000000000..0434e462a --- /dev/null +++ b/openssl/src/ossl_param.rs @@ -0,0 +1,178 @@ +//! 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.* and +//! only internally for this crate! +//! +use crate::bn::{BigNum, 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: &[u8]) -> 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 `BigNum` from the current `OsslParam` + #[corresponds(OSSL_PARAM_get_BN)] + pub fn get_bn(&self) -> Result { + unsafe { + let mut bn: *mut ffi::BIGNUM = ptr::null_mut(); + cvt(ffi::OSSL_PARAM_get_BN(self.as_ptr(), &mut bn))?; + Ok(BigNum::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_param(&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_param` is called! + #[corresponds(OSSL_PARAM_BLD_push_BN)] + pub fn add_bn(&self, key: &[u8], 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_param` is called! + #[corresponds(OSSL_PARAM_BLD_push_utf8_string)] + pub fn add_utf8_string(&self, key: &[u8], 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_param` is called! + #[corresponds(OSSL_PARAM_BLD_push_octet_string)] + pub fn add_octet_string(&self, key: &[u8], 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_param` is called! + #[corresponds(OSSL_PARAM_BLD_push_uint)] + pub fn add_uint(&self, key: &[u8], 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(|_| ()) + } + } +} diff --git a/openssl/src/pkey_ctx.rs b/openssl/src/pkey_ctx.rs index 47188111c..ee310db3c 100644 --- a/openssl/src/pkey_ctx.rs +++ b/openssl/src/pkey_ctx.rs @@ -67,6 +67,8 @@ 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; use crate::pkey::{HasPrivate, HasPublic, Id, PKey, PKeyRef, Private}; use crate::rsa::Padding; @@ -81,6 +83,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. @@ -156,6 +160,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 +780,20 @@ impl PkeyCtxRef { 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 +818,14 @@ impl PkeyCtxRef { } Ok(NonceType(nonce_type)) } + + /// Initializes a conversion from `OsslParam` 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 +1153,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/openssl/src/pkey_ecdsa.rs b/openssl/src/pkey_ecdsa.rs new file mode 100644 index 000000000..58a36f209 --- /dev/null +++ b/openssl/src/pkey_ecdsa.rs @@ -0,0 +1,209 @@ +//! Elliptic Curve using OpenSSL 3.* API +//! +//! Cryptography relies on the difficulty of solving mathematical problems, such as the factor +//! of large integers composed of two large prime numbers and the discrete logarithm of a +//! random elliptic curve. This module provides low-level features of the latter. +//! Elliptic Curve protocols can provide the same security with smaller keys. + +use foreign_types::ForeignType; +use libc::c_int; +use std::ptr; + +use crate::bn::{BigNum, BigNumRef}; +use crate::error::ErrorStack; +use crate::ossl_param::{OsslParam, OsslParamBuilder}; +use crate::pkey::{PKey, Private, Public}; +use crate::pkey_ctx::PkeyCtx; +use crate::{cvt, cvt_p}; +use openssl_macros::corresponds; + +const OSSL_PKEY_PARAM_GROUP_NAME: &[u8; 6] = b"group\0"; +const OSSL_PKEY_PARAM_PUB_KEY: &[u8; 4] = b"pub\0"; +const OSSL_PKEY_PARAM_PRIV_KEY: &[u8; 5] = b"priv\0"; + +pub struct PKeyEcdsaBuilder { + bld: OsslParamBuilder, + _m: ::std::marker::PhantomData, +} + +impl PKeyEcdsaBuilder { + /// Creates a new `PKeyEcdsaBuilder` to build ECDSA private or public keys. + /// + /// `n` is the modulus common to both public and private key. + /// `e` is the public exponent and `d` is the private exponent. + /// + pub fn new( + group: &str, + point: &[u8], + private: Option<&BigNumRef>, + ) -> Result, ErrorStack> { + let bld = OsslParamBuilder::new()?; + bld.add_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME, group)?; + bld.add_octet_string(OSSL_PKEY_PARAM_PUB_KEY, point)?; + if let Some(private) = private { + bld.add_bn(OSSL_PKEY_PARAM_PRIV_KEY, private)? + }; + Ok(PKeyEcdsaBuilder:: { + bld, + _m: ::std::marker::PhantomData, + }) + } + + /// Build PKey. Internal. + #[corresponds(EVP_PKEY_fromdata)] + fn build_internal(self, selection: c_int) -> Result, ErrorStack> { + let mut ctx = PkeyCtx::new_from_name(None, "EC", None)?; + ctx.fromdata_init()?; + let params = self.bld.to_param()?; + 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(), + selection, + params.as_ptr(), + ))?; + Ok(pkey) + } + } +} + +impl PKeyEcdsaBuilder { + /// Returns the Private ECDSA PKey from the provided parameters. + #[corresponds(EVP_PKEY_fromdata)] + pub fn build(self) -> Result, ErrorStack> { + /* The ECDSA requires here a keypair as the private key does not work without public point! */ + self.build_internal(ffi::EVP_PKEY_KEYPAIR) + } +} + +impl PKeyEcdsaBuilder { + /// Returns the Public ECDSA PKey from the provided parameters. + #[corresponds(EVP_PKEY_fromdata)] + pub fn build(self) -> Result, ErrorStack> { + self.build_internal(ffi::EVP_PKEY_PUBLIC_KEY) + } +} + +pub struct PKeyEcdsaParams { + params: OsslParam, + _m: ::std::marker::PhantomData, +} + +impl PKeyEcdsaParams { + /// Creates a new `PKeyEcdsaParams` from existing ECDSA PKey. Internal. + #[corresponds(EVP_PKEY_todata)] + fn _new_from_pkey(pkey: &PKey, selection: c_int) -> Result, ErrorStack> { + unsafe { + let mut params: *mut ffi::OSSL_PARAM = ptr::null_mut(); + cvt(ffi::EVP_PKEY_todata(pkey.as_ptr(), selection, &mut params))?; + Ok(PKeyEcdsaParams:: { + params: OsslParam::from_ptr(params), + _m: ::std::marker::PhantomData, + }) + } + } + + /// Returns a reference to the public key. + pub fn public_key(&self) -> Result<&[u8], ErrorStack> { + self.params + .locate(OSSL_PKEY_PARAM_PUB_KEY)? + .get_octet_string() + } + + /// Returns a reference to a group name + pub fn group(&self) -> Result<&str, ErrorStack> { + self.params + .locate(OSSL_PKEY_PARAM_GROUP_NAME)? + .get_utf8_string() + } +} + +impl PKeyEcdsaParams { + /// Creates a new `PKeyEcdsaParams` from existing Public ECDSA PKey. + #[corresponds(EVP_PKEY_todata)] + pub fn from_pkey(pkey: &PKey) -> Result, ErrorStack> { + Self::_new_from_pkey(pkey, ffi::EVP_PKEY_PUBLIC_KEY) + } +} + +impl PKeyEcdsaParams { + /// Creates a new `PKeyEcdsaParams` from existing Private ECDSA PKey. + #[corresponds(EVP_PKEY_todata)] + pub fn from_pkey(pkey: &PKey) -> Result, ErrorStack> { + Self::_new_from_pkey(pkey, ffi::EVP_PKEY_KEYPAIR) + } + + /// Returns the private key. + pub fn private_key(&self) -> Result { + self.params.locate(OSSL_PKEY_PARAM_PRIV_KEY)?.get_bn() + } +} + +#[cfg(test)] +mod tests { + + use crate::bn::BigNumContext; + use crate::ec::{EcKey, PointConversionForm}; + use crate::error::Error; + use crate::nid::Nid; + + use super::*; + + #[test] + fn test_build_pkey_ecdsa_private() { + /* First, generate the key with old API */ + let nid: Nid = Nid::SECP256K1; + let curve_name = nid.short_name().unwrap(); + let group = crate::ec::EcGroup::from_curve_name(nid).unwrap(); + let ec_key = EcKey::generate(&group).unwrap(); + let pkey1 = PKey::from_ec_key(ec_key.clone()).unwrap(); + + /* Now, build the new PKey from the old key with new API */ + let mut ctx = BigNumContext::new().unwrap(); + let pubkey = ec_key + .public_key() + .to_bytes(&group, PointConversionForm::UNCOMPRESSED, &mut ctx) + .unwrap(); + let bld = PKeyEcdsaBuilder::::new(curve_name, &pubkey, Some(ec_key.private_key())) + .unwrap(); + let pkey2 = bld.build().unwrap(); + + /* Verify it works the same way as the old one */ + assert!(pkey1.public_eq(&pkey2)); + assert!(Error::get().is_none()); + + let params = PKeyEcdsaParams::::from_pkey(&pkey2).unwrap(); + assert_eq!(params.group().unwrap(), curve_name); + assert_eq!(¶ms.private_key().unwrap(), ec_key.private_key()); + assert_eq!(params.public_key().unwrap(), pubkey); + } + + #[test] + fn test_build_pkey_ecdsa_public() { + /* First, generate the key with old API */ + let nid: Nid = Nid::SECP256K1; + let curve_name = nid.short_name().unwrap(); + let group = crate::ec::EcGroup::from_curve_name(nid).unwrap(); + let ec_key = EcKey::generate(&group).unwrap(); + let pkey1 = PKey::from_ec_key(ec_key.clone()).unwrap(); + + /* Now, build the new PKey from the old key with new API */ + let mut ctx = BigNumContext::new().unwrap(); + let pubkey = ec_key + .public_key() + .to_bytes(&group, PointConversionForm::UNCOMPRESSED, &mut ctx) + .unwrap(); + let bld = PKeyEcdsaBuilder::::new(curve_name, &pubkey, None).unwrap(); + let pkey2 = bld.build().unwrap(); + + /* Verify it works the same way as the old one */ + assert!(pkey1.public_eq(&pkey2)); + assert!(Error::get().is_none()); + + let params = PKeyEcdsaParams::::from_pkey(&pkey2).unwrap(); + assert_eq!(params.group().unwrap(), curve_name); + assert_eq!(params.public_key().unwrap(), pubkey); + } +} diff --git a/openssl/src/pkey_rsa.rs b/openssl/src/pkey_rsa.rs new file mode 100644 index 000000000..3b9f20b61 --- /dev/null +++ b/openssl/src/pkey_rsa.rs @@ -0,0 +1,331 @@ +//! Rivest–Shamir–Adleman cryptosystem used through the OpenSSL 3.* API +//! +//! RSA is one of the earliest asymmetric public key encryption schemes. +//! Like many other cryptosystems, RSA relies on the presumed difficulty of a hard +//! mathematical problem, namely factorization of the product of two large prime +//! numbers. At the moment there does not exist an algorithm that can factor such +//! large numbers in reasonable time. RSA is used in a wide variety of +//! applications including digital signatures and key exchanges such as +//! establishing a TLS/SSL connection. +//! +//! The RSA acronym is derived from the first letters of the surnames of the +//! algorithm's founding trio. +//! +//! # Example +//! +//! Generate a 3096-bit RSA key pair and use the public key to encrypt some data. +//! +//! ```rust +//! use openssl::pkey::{PKey, Private}; +//! use openssl::pkey_ctx::PkeyCtx; +//! use openssl::pkey_rsa::PKeyRsaBuilder; +//! +//! let bld = PKeyRsaBuilder::::new_generate(3096, None).unwrap(); +//! let key = bld.generate().unwrap(); +//! +//! let mut ctx = PkeyCtx::new(&key).unwrap(); +//! ctx.encrypt_init().unwrap(); +//! +//! let data = b"Some Crypto Text"; +//! let mut ciphertext = vec![]; +//! ctx.encrypt_to_vec(data, &mut ciphertext).unwrap(); +//! ``` + +use foreign_types::ForeignType; +use libc::c_int; +use std::ptr; + +use crate::bn::{BigNum, BigNumRef}; +use crate::error::ErrorStack; +use crate::ossl_param::{OsslParam, OsslParamBuilder}; +use crate::pkey::{PKey, Private, Public}; +use crate::pkey_ctx::PkeyCtx; +use crate::{cvt, cvt_p}; +use openssl_macros::corresponds; + +const OSSL_PKEY_PARAM_RSA_BITS: &[u8; 5] = b"bits\0"; + +const OSSL_PKEY_PARAM_RSA_N: &[u8; 2] = b"n\0"; +const OSSL_PKEY_PARAM_RSA_E: &[u8; 2] = b"e\0"; +const OSSL_PKEY_PARAM_RSA_D: &[u8; 2] = b"d\0"; +const OSSL_PKEY_PARAM_RSA_FACTOR1: &[u8; 12] = b"rsa-factor1\0"; +const OSSL_PKEY_PARAM_RSA_FACTOR2: &[u8; 12] = b"rsa-factor2\0"; +const OSSL_PKEY_PARAM_RSA_EXPONENT1: &[u8; 14] = b"rsa-exponent1\0"; +const OSSL_PKEY_PARAM_RSA_EXPONENT2: &[u8; 14] = b"rsa-exponent2\0"; +const OSSL_PKEY_PARAM_RSA_COEFFICIENT1: &[u8; 17] = b"rsa-coefficient1\0"; + +pub struct PKeyRsaBuilder { + bld: OsslParamBuilder, + _m: ::std::marker::PhantomData, +} + +impl PKeyRsaBuilder { + /// Creates a new `PKeyRsaBuilder` to build RSA private or public keys. + /// + /// `n` is the modulus common to both public and private key. + /// `e` is the public exponent and `d` is the private exponent. + /// + pub fn new( + n: &BigNumRef, + e: &BigNumRef, + d: Option<&BigNumRef>, + ) -> Result, ErrorStack> { + let bld = OsslParamBuilder::new()?; + bld.add_bn(OSSL_PKEY_PARAM_RSA_N, n)?; + bld.add_bn(OSSL_PKEY_PARAM_RSA_E, e)?; + if let Some(d) = d { + bld.add_bn(OSSL_PKEY_PARAM_RSA_D, d)? + }; + Ok(PKeyRsaBuilder:: { + bld, + _m: ::std::marker::PhantomData, + }) + } + + /// Sets the factors of the private Rsa key for the builder. + /// + /// `p` and `q` are the first and second factors of `n`. + pub fn set_factors( + self, + p: &BigNumRef, + q: &BigNumRef, + ) -> Result, ErrorStack> { + self.bld.add_bn(OSSL_PKEY_PARAM_RSA_FACTOR1, p)?; + self.bld.add_bn(OSSL_PKEY_PARAM_RSA_FACTOR2, q)?; + Ok(self) + } + + /// Sets the Chinese Remainder Theorem params of the private Rsa key. + /// + /// `dmp1`, `dmq1`, and `iqmp` are the exponents and coefficient for + /// CRT calculations which is used to speed up RSA operations. + pub fn set_crt_params( + self, + dmp1: &BigNumRef, + dmq1: &BigNumRef, + iqmp: &BigNumRef, + ) -> Result, ErrorStack> { + self.bld.add_bn(OSSL_PKEY_PARAM_RSA_EXPONENT1, dmp1)?; + self.bld.add_bn(OSSL_PKEY_PARAM_RSA_EXPONENT2, dmq1)?; + self.bld.add_bn(OSSL_PKEY_PARAM_RSA_COEFFICIENT1, iqmp)?; + Ok(self) + } + + /// Build PKey. Internal. + #[corresponds(EVP_PKEY_fromdata)] + fn build_internal(self, selection: c_int) -> Result, ErrorStack> { + let mut ctx = PkeyCtx::new_from_name(None, "RSA", None)?; + ctx.fromdata_init()?; + let params = self.bld.to_param()?; + 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(), + selection, + params.as_ptr(), + ))?; + Ok(pkey) + } + } +} + +impl PKeyRsaBuilder { + /// Returns the Private RSA PKey from the provided parameters. + #[corresponds(EVP_PKEY_fromdata)] + pub fn build(self) -> Result, ErrorStack> { + self.build_internal(ffi::EVP_PKEY_PRIVATE_KEY) + } + + /// Creates a new `PKeyRsaBuilder` to generate a new RSA key pair + /// + pub fn new_generate(bits: u32, e: Option) -> Result, ErrorStack> { + let bld = OsslParamBuilder::new()?; + bld.add_uint(OSSL_PKEY_PARAM_RSA_BITS, bits)?; + if let Some(e) = e { + bld.add_uint(OSSL_PKEY_PARAM_RSA_E, e)? + }; + Ok(PKeyRsaBuilder:: { + bld, + _m: ::std::marker::PhantomData, + }) + } + + /// Generate RSA PKey. + pub fn generate(self) -> Result, ErrorStack> { + let mut ctx = PkeyCtx::new_from_name(None, "RSA", None)?; + ctx.keygen_init()?; + let params = self.bld.to_param()?; + unsafe { + cvt(ffi::EVP_PKEY_CTX_set_params(ctx.as_ptr(), params.as_ptr()))?; + } + ctx.generate() + } +} + +impl PKeyRsaBuilder { + /// Builds the Public RSA PKey from the provideded parameters. + #[corresponds(EVP_PKEY_fromdata)] + pub fn build(self) -> Result, ErrorStack> { + self.build_internal(ffi::EVP_PKEY_PUBLIC_KEY) + } +} + +pub struct PKeyRsaParams { + params: OsslParam, + _m: ::std::marker::PhantomData, +} + +impl PKeyRsaParams { + /// Creates a new `PKeyRsaParams` from existing RSA PKey. Internal. + #[corresponds(EVP_PKEY_todata)] + fn _new_from_pkey(pkey: &PKey, selection: c_int) -> Result, ErrorStack> { + unsafe { + let mut params: *mut ffi::OSSL_PARAM = ptr::null_mut(); + cvt(ffi::EVP_PKEY_todata(pkey.as_ptr(), selection, &mut params))?; + Ok(PKeyRsaParams:: { + params: OsslParam::from_ptr(params), + _m: ::std::marker::PhantomData, + }) + } + } + + /// Returns the modulus of the key. + pub fn n(&self) -> Result { + self.params.locate(OSSL_PKEY_PARAM_RSA_N)?.get_bn() + } + + /// Returns the public exponent of the key. + pub fn e(&self) -> Result { + self.params.locate(OSSL_PKEY_PARAM_RSA_E)?.get_bn() + } +} + +impl PKeyRsaParams { + /// Creates a new `PKeyRsaParams` from existing Private RSA PKey. + #[corresponds(EVP_PKEY_todata)] + pub fn from_pkey(pkey: &PKey) -> Result, ErrorStack> { + Self::_new_from_pkey(pkey, ffi::EVP_PKEY_PRIVATE_KEY) + } + + /// Returns the private exponent of the key. + pub fn d(&self) -> Result { + self.params.locate(OSSL_PKEY_PARAM_RSA_D)?.get_bn() + } + + /// Returns the first factor of the exponent of the key. + pub fn p(&self) -> Result { + self.params.locate(OSSL_PKEY_PARAM_RSA_FACTOR1)?.get_bn() + } + + /// Returns the second factor of the exponent of the key. + pub fn q(&self) -> Result { + self.params.locate(OSSL_PKEY_PARAM_RSA_FACTOR2)?.get_bn() + } + + /// Returns the first exponent used for CRT calculations. + pub fn dmp1(&self) -> Result { + self.params.locate(OSSL_PKEY_PARAM_RSA_EXPONENT1)?.get_bn() + } + + /// Returns the second exponent used for CRT calculations. + pub fn dmq1(&self) -> Result { + self.params.locate(OSSL_PKEY_PARAM_RSA_EXPONENT2)?.get_bn() + } + + /// Returns the coefficient used for CRT calculations. + pub fn iqmp(&self) -> Result { + self.params + .locate(OSSL_PKEY_PARAM_RSA_COEFFICIENT1)? + .get_bn() + } +} + +impl PKeyRsaParams { + /// Creates a new `PKeyRsaParams` from existing Public RSA PKey. + #[corresponds(EVP_PKEY_todata)] + pub fn from_pkey(pkey: &PKey) -> Result, ErrorStack> { + Self::_new_from_pkey(pkey, ffi::EVP_PKEY_PUBLIC_KEY) + } +} + +#[cfg(test)] +mod tests { + + use crate::error::Error; + use crate::rsa::Rsa; + + use super::*; + + #[test] + fn test_build_pkey_rsa_private() { + /* First, generate the key with old API */ + let rsa = Rsa::generate(2048).unwrap(); + let pkey1 = PKey::from_rsa(rsa.clone()).unwrap(); + + /* Now, build the new PKey from the old key with new API */ + let bld = PKeyRsaBuilder::::new(rsa.n(), rsa.e(), Some(rsa.d())) + .unwrap() + .set_factors(rsa.p().unwrap(), rsa.q().unwrap()) + .unwrap() + .set_crt_params( + rsa.dmp1().unwrap(), + rsa.dmq1().unwrap(), + rsa.iqmp().unwrap(), + ) + .unwrap(); + let pkey2 = bld.build().unwrap(); + + /* Verify it works the same way as the old one */ + assert!(pkey1.public_eq(&pkey2)); + assert!(Error::get().is_none()); + + let params = PKeyRsaParams::::from_pkey(&pkey2).unwrap(); + assert_eq!(¶ms.n().unwrap(), rsa.n()); + assert_eq!(¶ms.e().unwrap(), rsa.e()); + assert_eq!(¶ms.d().unwrap(), rsa.d()); + assert_eq!(¶ms.p().unwrap(), rsa.p().unwrap()); + assert_eq!(¶ms.q().unwrap(), rsa.q().unwrap()); + assert_eq!(¶ms.dmp1().unwrap(), rsa.dmp1().unwrap()); + assert_eq!(¶ms.dmq1().unwrap(), rsa.dmq1().unwrap()); + assert_eq!(¶ms.iqmp().unwrap(), rsa.iqmp().unwrap()); + } + + #[test] + fn test_build_pkey_rsa_public() { + /* First, generate the key with old API */ + let rsa = Rsa::generate(2048).unwrap(); + let pkey1 = PKey::from_rsa(rsa.clone()).unwrap(); + + /* Now, build the new public PKey from the old key with new API */ + let bld = PKeyRsaBuilder::::new(rsa.n(), rsa.e(), None).unwrap(); + let pkey2 = bld.build().unwrap(); + + /* Verify it works the same way as the old one */ + assert!(pkey1.public_eq(&pkey2)); + assert!(Error::get().is_none()); + + let params = PKeyRsaParams::::from_pkey(&pkey2).unwrap(); + assert_eq!(¶ms.n().unwrap(), rsa.n()); + assert_eq!(¶ms.e().unwrap(), rsa.e()); + } + + #[test] + fn test_generate_rsa() { + let bld = PKeyRsaBuilder::::new_generate(3096, None).unwrap(); + let key = bld.generate().unwrap(); + + let mut ctx = PkeyCtx::new(&key).unwrap(); + ctx.encrypt_init().unwrap(); + let pt = "Some Crypto Text".as_bytes(); + let mut ct = vec![]; + ctx.encrypt_to_vec(pt, &mut ct).unwrap(); + + ctx.decrypt_init().unwrap(); + let mut out = vec![]; + ctx.decrypt_to_vec(&ct, &mut out).unwrap(); + + assert_eq!(pt, out); + } +} 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");