-
-
Notifications
You must be signed in to change notification settings - Fork 750
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
826f2a0
commit 5ff8594
Showing
3 changed files
with
163 additions
and
167 deletions.
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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::<ffi::OSSL_PARAM>::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); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters