From 26ebfc30d91d55af92d96fc439e0e93a7cad1484 Mon Sep 17 00:00:00 2001 From: greateggsgreg Date: Wed, 28 May 2025 21:02:04 -0400 Subject: [PATCH 1/4] Add support for argon2d and argon2i variants --- openssl/src/kdf.rs | 109 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 104 insertions(+), 5 deletions(-) diff --git a/openssl/src/kdf.rs b/openssl/src/kdf.rs index a5da35250..09755a2e3 100644 --- a/openssl/src/kdf.rs +++ b/openssl/src/kdf.rs @@ -34,15 +34,60 @@ cfg_if::cfg_if! { use crate::lib_ctx::LibCtxRef; use crate::error::ErrorStack; - /// Derives a key using the argon2id algorithm. + #[allow(clippy::too_many_arguments)] + pub fn argon2d( + ctx: Option<&LibCtxRef>, + pass: &[u8], + salt: &[u8], + ad: Option<&[u8]>, + secret: Option<&[u8]>, + mut iter: u32, + mut lanes: u32, + mut memcost: u32, + out: &mut [u8], + ) -> Result<(), ErrorStack> { + return argon2_helper(b"ARGON2D\0", ctx, pass, salt, ad, secret, iter, lanes, memcost, out); + } + + #[allow(clippy::too_many_arguments)] + pub fn argon2i( + ctx: Option<&LibCtxRef>, + pass: &[u8], + salt: &[u8], + ad: Option<&[u8]>, + secret: Option<&[u8]>, + mut iter: u32, + mut lanes: u32, + mut memcost: u32, + out: &mut [u8], + ) -> Result<(), ErrorStack> { + return argon2_helper(b"ARGON2I\0", ctx, pass, salt, ad, secret, iter, lanes, memcost, out); + } + + #[allow(clippy::too_many_arguments)] + pub fn argon2id( + ctx: Option<&LibCtxRef>, + pass: &[u8], + salt: &[u8], + ad: Option<&[u8]>, + secret: Option<&[u8]>, + mut iter: u32, + mut lanes: u32, + mut memcost: u32, + out: &mut [u8], + ) -> Result<(), ErrorStack> { + return argon2_helper(b"ARGON2ID\0", ctx, pass, salt, ad, secret, iter, lanes, memcost, out); + } + + /// Derives a key using the argon2* algorithms. /// /// To use multiple cores to process the lanes in parallel you must /// set a global max thread count using `OSSL_set_max_threads`. On /// builds with no threads all lanes will be processed sequentially. /// /// Requires OpenSSL 3.2.0 or newer. - #[allow(clippy::too_many_arguments)] - pub fn argon2id( + fn argon2_helper( + kdf_identifier: &'static [u8], ctx: Option<&LibCtxRef>, pass: &[u8], salt: &[u8], @@ -61,7 +106,7 @@ cfg_if::cfg_if! { 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. + // 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); } @@ -116,7 +161,7 @@ cfg_if::cfg_if! { let argon2 = EvpKdf(cvt_p(ffi::EVP_KDF_fetch( libctx, - b"ARGON2ID\0".as_ptr() as *const c_char, + kdf_identifier.as_ptr() as *const c_char, ptr::null(), ))?); let ctx = EvpKdfCtx(cvt_p(ffi::EVP_KDF_CTX_new(argon2.0))?); @@ -161,6 +206,60 @@ mod tests { assert_eq!(hex::encode(&actual[..]), expected); } + #[test] + #[cfg(all(ossl320, not(osslconf = "OPENSSL_NO_ARGON2")))] + fn argon2d() { + // RFC 9106 test vector for argon2d + 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 = "512b391b6f1162975371d30919734294f868e3be3984f3c1a13a4db9fabe4acb"; + + let mut actual = [0u8; 32]; + super::argon2d( + None, + &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 argon2i() { + // RFC 9106 test vector for argon2i + 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 = "c814d9d1dc7f37aa13f0d77f2494bda1c8de6b016dd388d29952a4c4672b6ce8"; + + let mut actual = [0u8; 32]; + super::argon2i( + None, + &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() { From 9cb373f42dcb18129914dcce930f0c24435da26e Mon Sep 17 00:00:00 2001 From: greateggsgreg Date: Wed, 28 May 2025 21:21:53 -0400 Subject: [PATCH 2/4] Removing unnecessary mutable markers --- openssl/src/kdf.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/openssl/src/kdf.rs b/openssl/src/kdf.rs index 09755a2e3..f54f6a0be 100644 --- a/openssl/src/kdf.rs +++ b/openssl/src/kdf.rs @@ -41,9 +41,9 @@ 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> { return argon2_helper(b"ARGON2D\0", ctx, pass, salt, ad, secret, iter, lanes, memcost, out); @@ -56,9 +56,9 @@ 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> { return argon2_helper(b"ARGON2I\0", ctx, pass, salt, ad, secret, iter, lanes, memcost, out); @@ -71,9 +71,9 @@ 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> { return argon2_helper(b"ARGON2ID\0", ctx, pass, salt, ad, secret, iter, lanes, memcost, out); From f5bd30e5372e3a0d20021a7d6bba0fef32996ac9 Mon Sep 17 00:00:00 2001 From: greateggsgreg Date: Mon, 11 Aug 2025 18:04:57 -0400 Subject: [PATCH 3/4] Mark KDF identifier as CStr --- openssl/src/kdf.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/openssl/src/kdf.rs b/openssl/src/kdf.rs index f54f6a0be..f2e406269 100644 --- a/openssl/src/kdf.rs +++ b/openssl/src/kdf.rs @@ -26,6 +26,7 @@ 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; @@ -46,7 +47,7 @@ cfg_if::cfg_if! { memcost: u32, out: &mut [u8], ) -> Result<(), ErrorStack> { - return argon2_helper(b"ARGON2D\0", ctx, pass, salt, ad, secret, iter, lanes, memcost, out); + return argon2_helper(c"ARGON2D", ctx, pass, salt, ad, secret, iter, lanes, memcost, out); } #[allow(clippy::too_many_arguments)] @@ -61,7 +62,7 @@ cfg_if::cfg_if! { memcost: u32, out: &mut [u8], ) -> Result<(), ErrorStack> { - return argon2_helper(b"ARGON2I\0", ctx, pass, salt, ad, secret, iter, lanes, memcost, out); + return argon2_helper(c"ARGON2I", ctx, pass, salt, ad, secret, iter, lanes, memcost, out); } #[allow(clippy::too_many_arguments)] @@ -76,7 +77,7 @@ cfg_if::cfg_if! { memcost: u32, out: &mut [u8], ) -> Result<(), ErrorStack> { - return argon2_helper(b"ARGON2ID\0", ctx, pass, salt, ad, secret, iter, lanes, memcost, out); + return argon2_helper(c"ARGON2ID", ctx, pass, salt, ad, secret, iter, lanes, memcost, out); } /// Derives a key using the argon2* algorithms. @@ -87,7 +88,7 @@ cfg_if::cfg_if! { /// /// Requires OpenSSL 3.2.0 or newer. fn argon2_helper( - kdf_identifier: &'static [u8], + kdf_identifier: &CStr, ctx: Option<&LibCtxRef>, pass: &[u8], salt: &[u8], From 44ae8786369fd3b9ff114c914535f85237504887 Mon Sep 17 00:00:00 2001 From: greateggsgreg Date: Mon, 11 Aug 2025 22:57:13 -0400 Subject: [PATCH 4/4] Update building of CStr constants to support Rust pre 1.77 --- openssl/src/kdf.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openssl/src/kdf.rs b/openssl/src/kdf.rs index f2e406269..79e0eff49 100644 --- a/openssl/src/kdf.rs +++ b/openssl/src/kdf.rs @@ -47,7 +47,7 @@ cfg_if::cfg_if! { memcost: u32, out: &mut [u8], ) -> Result<(), ErrorStack> { - return argon2_helper(c"ARGON2D", ctx, pass, salt, ad, secret, iter, lanes, memcost, out); + return argon2_helper(CStr::from_bytes_with_nul(b"ARGON2D\0").unwrap(), ctx, pass, salt, ad, secret, iter, lanes, memcost, out); } #[allow(clippy::too_many_arguments)] @@ -62,7 +62,7 @@ cfg_if::cfg_if! { memcost: u32, out: &mut [u8], ) -> Result<(), ErrorStack> { - return argon2_helper(c"ARGON2I", ctx, pass, salt, ad, secret, iter, lanes, memcost, out); + return argon2_helper(CStr::from_bytes_with_nul(b"ARGON2I\0").unwrap(), ctx, pass, salt, ad, secret, iter, lanes, memcost, out); } #[allow(clippy::too_many_arguments)] @@ -77,7 +77,7 @@ cfg_if::cfg_if! { memcost: u32, out: &mut [u8], ) -> Result<(), ErrorStack> { - return argon2_helper(c"ARGON2ID", ctx, pass, salt, ad, secret, iter, lanes, memcost, out); + return argon2_helper(CStr::from_bytes_with_nul(b"ARGON2ID\0").unwrap(), ctx, pass, salt, ad, secret, iter, lanes, memcost, out); } /// Derives a key using the argon2* algorithms.