diff --git a/openssl-sys/build/run_bindgen.rs b/openssl-sys/build/run_bindgen.rs index cba6b5556..efd891c77 100644 --- a/openssl-sys/build/run_bindgen.rs +++ b/openssl-sys/build/run_bindgen.rs @@ -59,6 +59,10 @@ const INCLUDES: &str = " #if OPENSSL_VERSION_NUMBER >= 0x30000000 #include +#include +#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/decoder.rs b/openssl-sys/src/handwritten/decoder.rs new file mode 100644 index 000000000..eabb5f7da --- /dev/null +++ b/openssl-sys/src/handwritten/decoder.rs @@ -0,0 +1,53 @@ +use super::super::*; +use libc::*; + +#[cfg(ossl300)] +extern "C" { + pub fn OSSL_DECODER_CTX_new() -> *mut OSSL_DECODER_CTX; + pub fn OSSL_DECODER_CTX_free(ctx: *mut OSSL_DECODER_CTX); + + pub fn OSSL_DECODER_CTX_new_for_pkey( + pkey: *mut *mut EVP_PKEY, + input_type: *const c_char, + input_struct: *const c_char, + keytype: *const c_char, + selection: c_int, + libctx: *mut OSSL_LIB_CTX, + propquery: *const c_char, + ) -> *mut OSSL_DECODER_CTX; + + pub fn OSSL_DECODER_CTX_set_selection(ctx: *mut OSSL_DECODER_CTX, selection: c_int) -> c_int; + pub fn OSSL_DECODER_CTX_set_input_type( + ctx: *mut OSSL_DECODER_CTX, + input_type: *const c_char, + ) -> c_int; + pub fn OSSL_DECODER_CTX_set_input_structure( + ctx: *mut OSSL_DECODER_CTX, + input_structure: *const c_char, + ) -> c_int; + + pub fn OSSL_DECODER_CTX_set_passphrase( + ctx: *mut OSSL_DECODER_CTX, + kstr: *const c_uchar, + klen: size_t, + ) -> c_int; + pub fn OSSL_DECODER_CTX_set_pem_password_cb( + ctx: *mut OSSL_DECODER_CTX, + cb: pem_password_cb, + cbarg: *mut c_void, + ) -> c_int; + pub fn OSSL_DECODER_CTX_set_passphrase_cb( + ctx: *mut OSSL_DECODER_CTX, + cb: OSSL_PASSPHRASE_CALLBACK, + cbarg: *mut c_void, + ) -> c_int; + + pub fn OSSL_DECODER_from_bio(ctx: *mut OSSL_DECODER_CTX, b_in: *mut BIO) -> c_int; + #[cfg(not(osslconf = "OPENSSL_NO_STDIO"))] + pub fn OSSL_DECODER_from_fp(ctx: *mut OSSL_DECODER_CTX, fp: *mut FILE) -> c_int; + pub fn OSSL_DECODER_from_data( + ctx: *mut OSSL_DECODER_CTX, + pdata: *mut *const c_uchar, + pdata_len: *mut size_t, + ) -> c_int; +} diff --git a/openssl-sys/src/handwritten/encoder.rs b/openssl-sys/src/handwritten/encoder.rs new file mode 100644 index 000000000..3613fab1c --- /dev/null +++ b/openssl-sys/src/handwritten/encoder.rs @@ -0,0 +1,56 @@ +use super::super::*; +use libc::*; + +#[cfg(ossl300)] +extern "C" { + pub fn OSSL_ENCODER_CTX_new() -> *mut OSSL_ENCODER_CTX; + pub fn OSSL_ENCODER_CTX_free(ctx: *mut OSSL_ENCODER_CTX); + + pub fn OSSL_ENCODER_CTX_new_for_pkey( + pkey: *const EVP_PKEY, + selection: c_int, + output_type: *const c_char, + output_structure: *const c_char, + propquery: *const c_char, + ) -> *mut OSSL_ENCODER_CTX; + + pub fn OSSL_ENCODER_CTX_set_selection(ctx: *mut OSSL_ENCODER_CTX, selection: c_int) -> c_int; + pub fn OSSL_ENCODER_CTX_set_output_type( + ctx: *mut OSSL_ENCODER_CTX, + output_type: *const c_char, + ) -> c_int; + pub fn OSSL_ENCODER_CTX_set_output_structure( + ctx: *mut OSSL_ENCODER_CTX, + output_structure: *const c_char, + ) -> c_int; + + pub fn OSSL_ENCODER_CTX_set_cipher( + ctx: *mut OSSL_ENCODER_CTX, + cipher_name: *const c_char, + propquery: *const c_char, + ) -> c_int; + pub fn OSSL_ENCODER_CTX_set_passphrase( + ctx: *mut OSSL_ENCODER_CTX, + kstr: *const c_uchar, + klen: size_t, + ) -> c_int; + pub fn OSSL_ENCODER_CTX_set_pem_password_cb( + ctx: *mut OSSL_ENCODER_CTX, + cb: pem_password_cb, + cbarg: *mut c_void, + ) -> c_int; + pub fn OSSL_ENCODER_CTX_set_passphrase_cb( + ctx: *mut OSSL_ENCODER_CTX, + cb: OSSL_PASSPHRASE_CALLBACK, + cbarg: *mut c_void, + ) -> c_int; + + pub fn OSSL_ENCODER_to_data( + ctx: *mut OSSL_ENCODER_CTX, + pdata: *mut *mut c_uchar, + pdata_len: *mut size_t, + ) -> c_int; + pub fn OSSL_ENCODER_to_bio(ctx: *mut OSSL_ENCODER_CTX, out: *mut BIO) -> c_int; + #[cfg(not(osslconf = "OPENSSL_NO_STDIO"))] + pub fn OSSL_ENCODER_to_fp(ctx: *mut OSSL_ENCODER_CTX, fp: *mut FILE) -> c_int; +} 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/mod.rs b/openssl-sys/src/handwritten/mod.rs index 47b3360fd..b0c71b579 100644 --- a/openssl-sys/src/handwritten/mod.rs +++ b/openssl-sys/src/handwritten/mod.rs @@ -6,9 +6,11 @@ pub use self::cmac::*; pub use self::cms::*; pub use self::conf::*; pub use self::crypto::*; +pub use self::decoder::*; pub use self::dh::*; pub use self::dsa::*; pub use self::ec::*; +pub use self::encoder::*; pub use self::err::*; pub use self::evp::*; pub use self::hmac::*; @@ -45,9 +47,11 @@ mod cmac; mod cms; mod conf; mod crypto; +mod decoder; mod dh; mod dsa; mod ec; +mod encoder; mod err; mod evp; mod hmac; 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..c4692b1bd 100644 --- a/openssl-sys/src/handwritten/types.rs +++ b/openssl-sys/src/handwritten/types.rs @@ -1134,13 +1134,32 @@ 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)] pub enum EVP_KDF_CTX {} + +#[cfg(ossl300)] +pub enum OSSL_ENCODER_CTX {} +#[cfg(ossl300)] +pub enum OSSL_DECODER_CTX {} + +#[cfg(ossl300)] +pub type OSSL_PASSPHRASE_CALLBACK = Option< + unsafe extern "C" fn( + pass: *mut c_char, + pass_size: size_t, + pass_len: *mut size_t, + params: *const OSSL_PARAM, + arg: *mut c_void, + ) -> c_int, +>; diff --git a/openssl/src/encdec.rs b/openssl/src/encdec.rs new file mode 100644 index 000000000..ba8ef3ec5 --- /dev/null +++ b/openssl/src/encdec.rs @@ -0,0 +1,926 @@ +use crate::bio::{MemBio, MemBioSlice}; +use crate::error::ErrorStack; +use crate::pkey::{Id, PKey, PKeyRef}; +use crate::pkey_ctx::{Selection, SelectionT}; +use crate::symm::Cipher; +use crate::util::{c_str, 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)] +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"), + } + } +} + +impl From for &CStr { + fn from(o: KeyFormat) -> Self { + match o { + KeyFormat::Text => c_str(b"TEXT\0"), + KeyFormat::Der => c_str(b"DER\0"), + KeyFormat::Pem => c_str(b"PEM\0"), + KeyFormat::MsBlob => c_str(b"MSBLOB\0"), + KeyFormat::Pvk => c_str(b"PVK\0"), + } + } +} + +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), + } + } +} + +impl<'a> From> for &'a CStr { + fn from(o: Structure<'a>) -> Self { + match o { + Structure::SubjectPublicKeyInfo => c_str(b"SubjectPublicKeyInfo\0"), + Structure::PKCS1 => c_str(b"pkcs1\0"), + Structure::PKCS8 => c_str(b"pkcs8\0"), + Structure::TypeSpecific => c_str(b"type-specific\0"), + Structure::Other(v) => v, + } + } +} + +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 DecoderCtx; + /// A reference to a `DecoderCtx`. + pub struct DecoderCtxRef; +} + +impl DecoderCtx { + #[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(DecoderCtx::from_ptr(ptr)) + } + } +} + +impl DecoderCtxRef { + /// 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 = DecoderCtx::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; + + /// A context object which can perform encode operations. + pub struct EncoderCtx; + /// A reference to a [`DecoderCtx`]. + pub struct EncoderCtxRef; +} + +impl EncoderCtx { + /// 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(EncoderCtx::from_ptr(ptr)) + } + } +} + +impl EncoderCtxRef { + // 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 = EncoderCtx::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 = c_str(b"TEXT\0").into(); + let der: KeyFormat = c_str(b"DER\0").into(); + let pem: KeyFormat = c_str(b"PEM\0").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 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::*; + + 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/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..a60cf8594 100644 --- a/openssl/src/lib.rs +++ b/openssl/src/lib.rs @@ -160,6 +160,8 @@ pub mod dh; pub mod dsa; pub mod ec; pub mod ecdsa; +#[cfg(ossl300)] +mod encdec; pub mod encrypt; #[cfg(not(any(boringssl, awslc)))] pub mod envelope; @@ -177,6 +179,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.rs b/openssl/src/pkey.rs index 8d69e1cdc..1fe6f3871 100644 --- a/openssl/src/pkey.rs +++ b/openssl/src/pkey.rs @@ -51,14 +51,14 @@ use crate::error::ErrorStack; use crate::pkey_ctx::PkeyCtx; use crate::rsa::Rsa; use crate::symm::Cipher; -use crate::util::{invoke_passwd_cb, CallbackState}; +use crate::util::{c_str, invoke_passwd_cb, CallbackState}; use crate::{cvt, cvt_p}; use cfg_if::cfg_if; 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; @@ -119,6 +119,76 @@ 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(()), + } + } +} + +impl TryFrom for &'static CStr { + type Error = (); + fn try_from(id: Id) -> Result { + match id { + Id::RSA => Ok(c_str(b"RSA\0")), + #[cfg(any(ossl111, libressl310, boringssl, awslc))] + Id::RSA_PSS => Ok(c_str(b"RSA-PSS\0")), + #[cfg(not(boringssl))] + Id::HMAC => Ok(c_str(b"HMAC\0")), + #[cfg(not(any(boringssl, awslc)))] + Id::CMAC => Ok(c_str(b"CMAC\0")), + Id::DSA => Ok(c_str(b"DSA\0")), + Id::DH => Ok(c_str(b"DH\0")), + #[cfg(ossl110)] + Id::DHX => Ok(c_str(b"DHX\0")), + Id::EC => Ok(c_str(b"EC\0")), + #[cfg(ossl111)] + Id::SM2 => Ok(c_str(b"SM2\0")), + #[cfg(any(ossl110, boringssl, libressl360, awslc))] + Id::HKDF => Ok(c_str(b"HKDF\0")), + #[cfg(any(ossl111, boringssl, libressl370, awslc))] + Id::ED25519 => Ok(c_str(b"Ed25519\0")), + #[cfg(ossl111)] + Id::ED448 => Ok(c_str(b"Ed448\0")), + #[cfg(any(ossl111, boringssl, libressl370, awslc))] + Id::X25519 => Ok(c_str(b"X25519\0")), + #[cfg(ossl111)] + Id::X448 => Ok(c_str(b"X448\0")), + #[cfg(ossl111)] + Id::POLY1305 => Ok(c_str(b"POLY1305\0")), + _ => Err(()), + } + } +} + /// A trait indicating that a key has parameters. pub unsafe trait HasParams {} @@ -382,35 +452,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 } 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/openssl/test/dhparams.der b/openssl/test/dhparams.der new file mode 100644 index 000000000..bf0cd0cb4 Binary files /dev/null and b/openssl/test/dhparams.der differ diff --git a/openssl/test/pkcs1.der.pub b/openssl/test/pkcs1.der.pub new file mode 100644 index 000000000..0e54c50f4 Binary files /dev/null and b/openssl/test/pkcs1.der.pub differ diff --git a/openssl/test/rsa.der b/openssl/test/rsa.der new file mode 100644 index 000000000..500c05faf Binary files /dev/null and b/openssl/test/rsa.der differ diff --git a/systest/build.rs b/systest/build.rs index 22fc6b836..c22be4b9b 100644 --- a/systest/build.rs +++ b/systest/build.rs @@ -83,7 +83,11 @@ fn main() { } if version >= 0x30000000 { - cfg.header("openssl/provider.h"); + cfg.header("openssl/provider.h") + .header("openssl/params.h") + .header("openssl/param_build.h") + .header("openssl/decoder.h") + .header("openssl/encoder.h"); } if version >= 0x30200000 { cfg.header("openssl/thread.h"); @@ -118,6 +122,7 @@ fn main() { s == "PasswordCallback" || s == "pem_password_cb" || s == "bio_info_cb" + || s == "OSSL_PASSPHRASE_CALLBACK" || s.starts_with("CRYPTO_EX_") }); cfg.skip_struct(|s| {