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::CStr;
29 use std::ptr;
30 use foreign_types::ForeignTypeRef;
31 use libc::c_char;
32 use crate::{cvt, cvt_p};
33 use crate::lib_ctx::LibCtxRef;
34 use crate::error::ErrorStack;
35 use crate::ossl_param::OsslParamBuilder;
36
37 const OSSL_KDF_PARAM_PASSWORD: &CStr = unsafe { CStr::from_bytes_with_nul_unchecked(b"pass\0") };
42 const OSSL_KDF_PARAM_SALT: &CStr = unsafe { CStr::from_bytes_with_nul_unchecked(b"salt\0") };
43 const OSSL_KDF_PARAM_SECRET: &CStr = unsafe { CStr::from_bytes_with_nul_unchecked(b"secret\0") };
44 const OSSL_KDF_PARAM_ITER: &CStr = unsafe { CStr::from_bytes_with_nul_unchecked(b"iter\0") };
45 const OSSL_KDF_PARAM_SIZE: &CStr = unsafe { CStr::from_bytes_with_nul_unchecked(b"size\0") };
46 const OSSL_KDF_PARAM_THREADS: &CStr = unsafe { CStr::from_bytes_with_nul_unchecked(b"threads\0") };
47 const OSSL_KDF_PARAM_ARGON2_AD: &CStr = unsafe { CStr::from_bytes_with_nul_unchecked(b"ad\0") };
48 const OSSL_KDF_PARAM_ARGON2_LANES: &CStr = unsafe { CStr::from_bytes_with_nul_unchecked(b"lanes\0") };
49 const OSSL_KDF_PARAM_ARGON2_MEMCOST: &CStr = unsafe { CStr::from_bytes_with_nul_unchecked(b"memcost\0") };
50
51 #[allow(clippy::too_many_arguments)]
52 pub fn argon2d(
53 ctx: Option<&LibCtxRef>,
54 pass: &[u8],
55 salt: &[u8],
56 ad: Option<&[u8]>,
57 secret: Option<&[u8]>,
58 iter: u32,
59 lanes: u32,
60 memcost: u32,
61 out: &mut [u8],
62 ) -> Result<(), ErrorStack> {
63 argon2_helper(CStr::from_bytes_with_nul(b"ARGON2D\0").unwrap(), ctx, pass, salt, ad, secret, iter, lanes, memcost, out)
64 }
65
66 #[allow(clippy::too_many_arguments)]
67 pub fn argon2i(
68 ctx: Option<&LibCtxRef>,
69 pass: &[u8],
70 salt: &[u8],
71 ad: Option<&[u8]>,
72 secret: Option<&[u8]>,
73 iter: u32,
74 lanes: u32,
75 memcost: u32,
76 out: &mut [u8],
77 ) -> Result<(), ErrorStack> {
78 argon2_helper(CStr::from_bytes_with_nul(b"ARGON2I\0").unwrap(), ctx, pass, salt, ad, secret, iter, lanes, memcost, out)
79 }
80
81 #[allow(clippy::too_many_arguments)]
82 pub fn argon2id(
83 ctx: Option<&LibCtxRef>,
84 pass: &[u8],
85 salt: &[u8],
86 ad: Option<&[u8]>,
87 secret: Option<&[u8]>,
88 iter: u32,
89 lanes: u32,
90 memcost: u32,
91 out: &mut [u8],
92 ) -> Result<(), ErrorStack> {
93 argon2_helper(CStr::from_bytes_with_nul(b"ARGON2ID\0").unwrap(), ctx, pass, salt, ad, secret, iter, lanes, memcost, out)
94 }
95
96 #[allow(clippy::too_many_arguments)]
104 fn argon2_helper(
105 kdf_identifier: &CStr,
106 ctx: Option<&LibCtxRef>,
107 pass: &[u8],
108 salt: &[u8],
109 ad: Option<&[u8]>,
110 secret: Option<&[u8]>,
111 iter: u32,
112 lanes: u32,
113 memcost: u32,
114 out: &mut [u8],
115 ) -> Result<(), ErrorStack> {
116 let libctx = ctx.map_or(ptr::null_mut(), ForeignTypeRef::as_ptr);
117 let max_threads = unsafe {
118 ffi::init();
119 ffi::OSSL_get_max_threads(libctx)
120 };
121 let mut threads = 1;
122 if max_threads > 0 {
126 threads = cmp::min(lanes, cmp::min(max_threads, u32::MAX as u64) as u32);
127 }
128 let mut bld = OsslParamBuilder::new()?;
129 bld.add_octet_string(OSSL_KDF_PARAM_PASSWORD, pass)?;
130 bld.add_octet_string(OSSL_KDF_PARAM_SALT, salt)?;
131 bld.add_uint(OSSL_KDF_PARAM_THREADS, threads)?;
132 bld.add_uint(OSSL_KDF_PARAM_ARGON2_LANES, lanes)?;
133 bld.add_uint(OSSL_KDF_PARAM_ARGON2_MEMCOST, memcost)?;
134 bld.add_uint(OSSL_KDF_PARAM_ITER, iter)?;
135 let size = out.len() as u32;
136 bld.add_uint(OSSL_KDF_PARAM_SIZE, size)?;
137 if let Some(ad) = ad {
138 bld.add_octet_string(OSSL_KDF_PARAM_ARGON2_AD, ad)?;
139 }
140 if let Some(secret) = secret {
141 bld.add_octet_string(OSSL_KDF_PARAM_SECRET, secret)?;
142 }
143 let params = bld.to_param()?;
144 unsafe {
145 let argon2 = EvpKdf(cvt_p(ffi::EVP_KDF_fetch(
146 libctx,
147 kdf_identifier.as_ptr() as *const c_char,
148 ptr::null(),
149 ))?);
150 let ctx = EvpKdfCtx(cvt_p(ffi::EVP_KDF_CTX_new(argon2.0))?);
151 cvt(ffi::EVP_KDF_derive(
152 ctx.0,
153 out.as_mut_ptr(),
154 out.len(),
155 params.as_ptr(),
156 ))
157 .map(|_| ())
158 }
159 }
160 }
161}
162
163#[cfg(test)]
164mod tests {
165 #[test]
166 #[cfg(all(ossl320, not(osslconf = "OPENSSL_NO_ARGON2")))]
167 fn argon2id() {
168 let pass = hex::decode("0101010101010101010101010101010101010101010101010101010101010101")
170 .unwrap();
171 let salt = hex::decode("02020202020202020202020202020202").unwrap();
172 let secret = hex::decode("0303030303030303").unwrap();
173 let ad = hex::decode("040404040404040404040404").unwrap();
174 let expected = "0d640df58d78766c08c037a34a8b53c9d01ef0452d75b65eb52520e96b01e659";
175
176 let mut actual = [0u8; 32];
177 super::argon2id(
178 None,
179 &pass,
180 &salt,
181 Some(&ad),
182 Some(&secret),
183 3,
184 4,
185 32,
186 &mut actual,
187 )
188 .unwrap();
189 assert_eq!(hex::encode(&actual[..]), expected);
190 }
191
192 #[test]
193 #[cfg(all(ossl320, not(osslconf = "OPENSSL_NO_ARGON2")))]
194 fn argon2d() {
195 let pass = hex::decode("0101010101010101010101010101010101010101010101010101010101010101")
197 .unwrap();
198 let salt = hex::decode("02020202020202020202020202020202").unwrap();
199 let secret = hex::decode("0303030303030303").unwrap();
200 let ad = hex::decode("040404040404040404040404").unwrap();
201 let expected = "512b391b6f1162975371d30919734294f868e3be3984f3c1a13a4db9fabe4acb";
202
203 let mut actual = [0u8; 32];
204 super::argon2d(
205 None,
206 &pass,
207 &salt,
208 Some(&ad),
209 Some(&secret),
210 3,
211 4,
212 32,
213 &mut actual,
214 )
215 .unwrap();
216 assert_eq!(hex::encode(&actual[..]), expected);
217 }
218
219 #[test]
220 #[cfg(all(ossl320, not(osslconf = "OPENSSL_NO_ARGON2")))]
221 fn argon2i() {
222 let pass = hex::decode("0101010101010101010101010101010101010101010101010101010101010101")
224 .unwrap();
225 let salt = hex::decode("02020202020202020202020202020202").unwrap();
226 let secret = hex::decode("0303030303030303").unwrap();
227 let ad = hex::decode("040404040404040404040404").unwrap();
228 let expected = "c814d9d1dc7f37aa13f0d77f2494bda1c8de6b016dd388d29952a4c4672b6ce8";
229
230 let mut actual = [0u8; 32];
231 super::argon2i(
232 None,
233 &pass,
234 &salt,
235 Some(&ad),
236 Some(&secret),
237 3,
238 4,
239 32,
240 &mut actual,
241 )
242 .unwrap();
243 assert_eq!(hex::encode(&actual[..]), expected);
244 }
245
246 #[test]
247 #[cfg(all(ossl320, not(osslconf = "OPENSSL_NO_ARGON2")))]
248 fn argon2id_no_ad_secret() {
249 let pass = b"";
251 let salt = hex::decode("02020202020202020202020202020202").unwrap();
252 let expected = "0a34f1abde67086c82e785eaf17c68382259a264f4e61b91cd2763cb75ac189a";
253
254 let mut actual = [0u8; 32];
255 super::argon2id(None, pass, &salt, None, None, 3, 4, 32, &mut actual).unwrap();
256 assert_eq!(hex::encode(&actual[..]), expected);
257 }
258}