1#[cfg(ossl320)]
2struct EvpKdf(*mut ffi::EVP_KDF);
3
4#[cfg(ossl320)]
5impl Drop for EvpKdf {
6 fn drop(&mut self) {
7 unsafe {
8 ffi::EVP_KDF_free(self.0);
9 }
10 }
11}
12
13#[cfg(ossl320)]
14struct EvpKdfCtx(*mut ffi::EVP_KDF_CTX);
15
16#[cfg(ossl320)]
17impl Drop for EvpKdfCtx {
18 fn drop(&mut self) {
19 unsafe {
20 ffi::EVP_KDF_CTX_free(self.0);
21 }
22 }
23}
24
25cfg_if::cfg_if! {
26 if #[cfg(all(ossl320, not(osslconf = "OPENSSL_NO_ARGON2")))] {
27 use std::cmp;
28 use std::ffi::c_void;
29 use std::mem::MaybeUninit;
30 use std::ptr;
31 use foreign_types::ForeignTypeRef;
32 use libc::c_char;
33 use crate::{cvt, cvt_p};
34 use crate::lib_ctx::LibCtxRef;
35 use crate::error::ErrorStack;
36
37 #[allow(clippy::too_many_arguments)]
45 pub fn argon2id(
46 ctx: Option<&LibCtxRef>,
47 pass: &[u8],
48 salt: &[u8],
49 ad: Option<&[u8]>,
50 secret: Option<&[u8]>,
51 mut iter: u32,
52 mut lanes: u32,
53 mut memcost: u32,
54 out: &mut [u8],
55 ) -> Result<(), ErrorStack> {
56 unsafe {
57 ffi::init();
58 let libctx = ctx.map_or(ptr::null_mut(), ForeignTypeRef::as_ptr);
59
60 let max_threads = ffi::OSSL_get_max_threads(libctx);
61 let mut threads = 1;
62 if max_threads > 0 {
66 threads = cmp::min(lanes, cmp::min(max_threads, u32::MAX as u64) as u32);
67 }
68 let mut params: [ffi::OSSL_PARAM; 10] =
69 core::array::from_fn(|_| MaybeUninit::<ffi::OSSL_PARAM>::zeroed().assume_init());
70 let mut idx = 0;
71 params[idx] = ffi::OSSL_PARAM_construct_octet_string(
72 b"pass\0".as_ptr() as *const c_char,
73 pass.as_ptr() as *mut c_void,
74 pass.len(),
75 );
76 idx += 1;
77 params[idx] = ffi::OSSL_PARAM_construct_octet_string(
78 b"salt\0".as_ptr() as *const c_char,
79 salt.as_ptr() as *mut c_void,
80 salt.len(),
81 );
82 idx += 1;
83 params[idx] =
84 ffi::OSSL_PARAM_construct_uint(b"threads\0".as_ptr() as *const c_char, &mut threads);
85 idx += 1;
86 params[idx] =
87 ffi::OSSL_PARAM_construct_uint(b"lanes\0".as_ptr() as *const c_char, &mut lanes);
88 idx += 1;
89 params[idx] =
90 ffi::OSSL_PARAM_construct_uint(b"memcost\0".as_ptr() as *const c_char, &mut memcost);
91 idx += 1;
92 params[idx] =
93 ffi::OSSL_PARAM_construct_uint(b"iter\0".as_ptr() as *const c_char, &mut iter);
94 idx += 1;
95 let mut size = out.len() as u32;
96 params[idx] =
97 ffi::OSSL_PARAM_construct_uint(b"size\0".as_ptr() as *const c_char, &mut size);
98 idx += 1;
99 if let Some(ad) = ad {
100 params[idx] = ffi::OSSL_PARAM_construct_octet_string(
101 b"ad\0".as_ptr() as *const c_char,
102 ad.as_ptr() as *mut c_void,
103 ad.len(),
104 );
105 idx += 1;
106 }
107 if let Some(secret) = secret {
108 params[idx] = ffi::OSSL_PARAM_construct_octet_string(
109 b"secret\0".as_ptr() as *const c_char,
110 secret.as_ptr() as *mut c_void,
111 secret.len(),
112 );
113 idx += 1;
114 }
115 params[idx] = ffi::OSSL_PARAM_construct_end();
116
117 let argon2 = EvpKdf(cvt_p(ffi::EVP_KDF_fetch(
118 libctx,
119 b"ARGON2ID\0".as_ptr() as *const c_char,
120 ptr::null(),
121 ))?);
122 let ctx = EvpKdfCtx(cvt_p(ffi::EVP_KDF_CTX_new(argon2.0))?);
123 cvt(ffi::EVP_KDF_derive(
124 ctx.0,
125 out.as_mut_ptr(),
126 out.len(),
127 params.as_ptr(),
128 ))
129 .map(|_| ())
130 }
131 }
132 }
133}
134
135#[cfg(test)]
136mod tests {
137 #[test]
138 #[cfg(all(ossl320, not(osslconf = "OPENSSL_NO_ARGON2")))]
139 fn argon2id() {
140 let pass = hex::decode("0101010101010101010101010101010101010101010101010101010101010101")
142 .unwrap();
143 let salt = hex::decode("02020202020202020202020202020202").unwrap();
144 let secret = hex::decode("0303030303030303").unwrap();
145 let ad = hex::decode("040404040404040404040404").unwrap();
146 let expected = "0d640df58d78766c08c037a34a8b53c9d01ef0452d75b65eb52520e96b01e659";
147
148 let mut actual = [0u8; 32];
149 super::argon2id(
150 None,
151 &pass,
152 &salt,
153 Some(&ad),
154 Some(&secret),
155 3,
156 4,
157 32,
158 &mut actual,
159 )
160 .unwrap();
161 assert_eq!(hex::encode(&actual[..]), expected);
162 }
163
164 #[test]
165 #[cfg(all(ossl320, not(osslconf = "OPENSSL_NO_ARGON2")))]
166 fn argon2id_no_ad_secret() {
167 let pass = b"";
169 let salt = hex::decode("02020202020202020202020202020202").unwrap();
170 let expected = "0a34f1abde67086c82e785eaf17c68382259a264f4e61b91cd2763cb75ac189a";
171
172 let mut actual = [0u8; 32];
173 super::argon2id(None, pass, &salt, None, None, 3, 4, 32, &mut actual).unwrap();
174 assert_eq!(hex::encode(&actual[..]), expected);
175 }
176}