From 5ff8594e9c98f2ffbaf677c663dce8d4ff72fa88 Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Sun, 1 Sep 2024 14:49:20 -0700 Subject: [PATCH] rename to kdf, remove thread arg --- openssl/src/argon2.rs | 165 ------------------------------------------ openssl/src/kdf.rs | 162 +++++++++++++++++++++++++++++++++++++++++ openssl/src/lib.rs | 3 +- 3 files changed, 163 insertions(+), 167 deletions(-) delete mode 100644 openssl/src/argon2.rs create mode 100644 openssl/src/kdf.rs diff --git a/openssl/src/argon2.rs b/openssl/src/argon2.rs deleted file mode 100644 index 836b45dce..000000000 --- a/openssl/src/argon2.rs +++ /dev/null @@ -1,165 +0,0 @@ -use std::ffi::{c_char, c_void}; -use std::mem::MaybeUninit; -use std::ptr; - -use crate::error::ErrorStack; -use crate::{cvt, cvt_p}; - -struct EvpKdf(*mut ffi::EVP_KDF); - -impl Drop for EvpKdf { - fn drop(&mut self) { - unsafe { - ffi::EVP_KDF_free(self.0); - } - } -} - -struct EvpKdfCtx(*mut ffi::EVP_KDF_CTX); - -impl Drop for EvpKdfCtx { - fn drop(&mut self) { - unsafe { - ffi::EVP_KDF_CTX_free(self.0); - } - } -} - -/// Derives a key using the argon2id algorithm. -/// -/// Requires OpenSSL 3.2.0 or newer. -#[allow(clippy::too_many_arguments)] -pub fn argon2id( - pass: &[u8], - salt: &[u8], - ad: Option<&[u8]>, - secret: Option<&[u8]>, - mut iter: u32, - mut threads: u32, - mut lanes: u32, - mut memcost: u32, - out: &mut [u8], -) -> Result<(), ErrorStack> { - // We only support single-threaded operation for now since rust-openssl doesn't - // bind OSSL_set_max_threads - assert!(threads == 1); - unsafe { - ffi::init(); - 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(); - - let argon2_p = cvt_p(ffi::EVP_KDF_fetch( - ptr::null_mut(), - b"ARGON2ID\0".as_ptr() as *const c_char, - ptr::null(), - ))?; - let argon2 = EvpKdf(argon2_p); - let ctx_p = cvt_p(ffi::EVP_KDF_CTX_new(argon2.0))?; - let ctx = EvpKdfCtx(ctx_p); - cvt(ffi::EVP_KDF_derive( - ctx.0, - out.as_mut_ptr(), - out.len(), - params.as_ptr(), - )) - .map(|_| ()) - } -} - -#[cfg(test)] -mod tests { - #[test] - fn argon2id() { - // RFC 9106 test vector for argon2id - let pass = hex::decode("0101010101010101010101010101010101010101010101010101010101010101") - .unwrap(); - let salt = hex::decode("02020202020202020202020202020202").unwrap(); - let secret = hex::decode("0303030303030303").unwrap(); - let ad = hex::decode("040404040404040404040404").unwrap(); - let expected = "0d640df58d78766c08c037a34a8b53c9d01ef0452d75b65eb52520e96b01e659"; - - let mut actual = [0 as u8; 32]; - super::argon2id( - &pass, - &salt, - Some(&ad), - Some(&secret), - 3, - 1, - 4, - 32, - &mut actual, - ) - .unwrap(); - assert_eq!(hex::encode(&actual[..]), expected); - } - - #[test] - fn argon2id_no_ad_secret() { - // Test vector from OpenSSL - let pass = ""; - let salt = hex::decode("02020202020202020202020202020202").unwrap(); - let expected = "0a34f1abde67086c82e785eaf17c68382259a264f4e61b91cd2763cb75ac189a"; - - let mut actual = [0 as u8; 32]; - super::argon2id( - &pass.as_bytes(), - &salt, - None, - None, - 3, - 1, - 4, - 32, - &mut actual, - ) - .unwrap(); - assert_eq!(hex::encode(&actual[..]), expected); - } -} diff --git a/openssl/src/kdf.rs b/openssl/src/kdf.rs new file mode 100644 index 000000000..4bd89cc6d --- /dev/null +++ b/openssl/src/kdf.rs @@ -0,0 +1,162 @@ +#[cfg(ossl300)] +struct EvpKdf(*mut ffi::EVP_KDF); + +#[cfg(ossl300)] +impl Drop for EvpKdf { + fn drop(&mut self) { + unsafe { + ffi::EVP_KDF_free(self.0); + } + } +} + +#[cfg(ossl300)] +struct EvpKdfCtx(*mut ffi::EVP_KDF_CTX); + +#[cfg(ossl300)] +impl Drop for EvpKdfCtx { + fn drop(&mut self) { + unsafe { + ffi::EVP_KDF_CTX_free(self.0); + } + } +} + +cfg_if::cfg_if! { + if #[cfg(all(ossl320, not(osslconf = "OPENSSL_NO_ARGON2")))] { + use std::ffi::{c_char, c_void}; + use std::mem::MaybeUninit; + use std::ptr; + use crate::{cvt, cvt_p}; + use crate::error::ErrorStack; + + /// Derives a key using the argon2id algorithm. + /// + /// This function currently does not support multi-threaded operation, so + /// lanes greater than 1 will be processed sequentially. + /// + /// Requires OpenSSL 3.2.0 or newer. + #[allow(clippy::too_many_arguments)] + pub fn argon2id( + pass: &[u8], + salt: &[u8], + ad: Option<&[u8]>, + secret: Option<&[u8]>, + mut iter: u32, + mut lanes: u32, + mut memcost: u32, + out: &mut [u8], + ) -> Result<(), ErrorStack> { + unsafe { + ffi::init(); + let mut threads = 1; + 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(); + + let argon2_p = cvt_p(ffi::EVP_KDF_fetch( + ptr::null_mut(), + b"ARGON2ID\0".as_ptr() as *const c_char, + ptr::null(), + ))?; + let argon2 = EvpKdf(argon2_p); + let ctx_p = cvt_p(ffi::EVP_KDF_CTX_new(argon2.0))?; + let ctx = EvpKdfCtx(ctx_p); + cvt(ffi::EVP_KDF_derive( + ctx.0, + out.as_mut_ptr(), + out.len(), + params.as_ptr(), + )) + .map(|_| ()) + } + } + } +} + +#[cfg(test)] +mod tests { + #[test] + #[cfg(all(ossl320, not(osslconf = "OPENSSL_NO_ARGON2")))] + fn argon2id() { + // RFC 9106 test vector for argon2id + let pass = hex::decode("0101010101010101010101010101010101010101010101010101010101010101") + .unwrap(); + let salt = hex::decode("02020202020202020202020202020202").unwrap(); + let secret = hex::decode("0303030303030303").unwrap(); + let ad = hex::decode("040404040404040404040404").unwrap(); + let expected = "0d640df58d78766c08c037a34a8b53c9d01ef0452d75b65eb52520e96b01e659"; + + let mut actual = [0 as u8; 32]; + super::argon2id( + &pass, + &salt, + Some(&ad), + Some(&secret), + 3, + 4, + 32, + &mut actual, + ) + .unwrap(); + assert_eq!(hex::encode(&actual[..]), expected); + } + + #[test] + #[cfg(all(ossl320, not(osslconf = "OPENSSL_NO_ARGON2")))] + fn argon2id_no_ad_secret() { + // Test vector from OpenSSL + let pass = ""; + let salt = hex::decode("02020202020202020202020202020202").unwrap(); + let expected = "0a34f1abde67086c82e785eaf17c68382259a264f4e61b91cd2763cb75ac189a"; + + let mut actual = [0 as u8; 32]; + super::argon2id(&pass.as_bytes(), &salt, None, None, 3, 4, 32, &mut actual).unwrap(); + assert_eq!(hex::encode(&actual[..]), expected); + } +} diff --git a/openssl/src/lib.rs b/openssl/src/lib.rs index d758d5085..5942734f5 100644 --- a/openssl/src/lib.rs +++ b/openssl/src/lib.rs @@ -148,8 +148,6 @@ mod bio; mod util; pub mod aes; #[cfg(ossl320)] -#[cfg(all(ossl320, not(osslconf = "OPENSSL_NO_ARGON2")))] -pub mod argon2; pub mod asn1; pub mod base64; pub mod bn; @@ -171,6 +169,7 @@ pub mod ex_data; #[cfg(not(any(libressl, ossl300)))] pub mod fips; pub mod hash; +pub mod kdf; #[cfg(ossl300)] pub mod lib_ctx; pub mod md;