1#[cfg(ossl300)]
2struct EvpKdf(*mut ffi::EVP_KDF);
3
4#[cfg(ossl300)]
5impl Drop for EvpKdf {
6 fn drop(&mut self) {
7 unsafe {
8 ffi::EVP_KDF_free(self.0);
9 }
10 }
11}
12
13#[cfg(ossl300)]
14struct EvpKdfCtx(*mut ffi::EVP_KDF_CTX);
15
16#[cfg(ossl300)]
17impl Drop for EvpKdfCtx {
18 fn drop(&mut self) {
19 unsafe {
20 ffi::EVP_KDF_CTX_free(self.0);
21 }
22 }
23}
24
25#[cfg(ossl300)]
26#[derive(Copy, Clone)]
27pub enum HkdfMode {
28 ExtractAndExpand,
29 ExtractOnly,
30 ExpandOnly,
31}
32
33cfg_if::cfg_if! {
34 if #[cfg(ossl300)] {
35 use std::ffi::CStr;
36 use std::ptr;
37 use foreign_types::ForeignTypeRef;
38 use libc::c_char;
39 use crate::{cvt, cvt_p};
40 use crate::lib_ctx::LibCtxRef;
41 use crate::error::ErrorStack;
42 use crate::ossl_param::{OsslParamBuilder, OsslParamArray};
43 use crate::md::MdRef;
44
45 const OSSL_KDF_PARAM_DIGEST: &CStr = unsafe { CStr::from_bytes_with_nul_unchecked(b"digest\0") };
46 const OSSL_KDF_PARAM_KEY: &CStr = unsafe { CStr::from_bytes_with_nul_unchecked(b"key\0") };
47 const OSSL_KDF_PARAM_SALT: &CStr = unsafe { CStr::from_bytes_with_nul_unchecked(b"salt\0") };
48 const OSSL_KDF_PARAM_INFO: &CStr = unsafe { CStr::from_bytes_with_nul_unchecked(b"info\0") };
49 const OSSL_KDF_PARAM_MODE: &CStr = unsafe { CStr::from_bytes_with_nul_unchecked(b"mode\0") };
50
51 fn kdf_digest(
57 kdf_identifier: &CStr,
58 ctx: Option<&LibCtxRef>,
59 params: &OsslParamArray,
60 out: &mut [u8],
61 ) -> Result<(), ErrorStack> {
62 let libctx = ctx.map_or(ptr::null_mut(), ForeignTypeRef::as_ptr);
63 unsafe {
64 let kdf = EvpKdf(cvt_p(ffi::EVP_KDF_fetch(
65 libctx,
66 kdf_identifier.as_ptr() as *const c_char,
67 ptr::null(),
68 ))?);
69 let ctx = EvpKdfCtx(cvt_p(ffi::EVP_KDF_CTX_new(kdf.0))?);
70 cvt(ffi::EVP_KDF_derive(
71 ctx.0,
72 out.as_mut_ptr(),
73 out.len(),
74 params.as_ptr(),
75 ))
76 .map(|_| ())
77 }
78 }
79
80 pub fn hkdf(
81 digest: &MdRef,
82 key: &[u8],
83 salt: Option<&[u8]>,
84 info: Option<&[u8]>,
85 mode: HkdfMode,
86 ctx: Option<&LibCtxRef>,
87 out: &mut [u8],
88 ) -> Result<(), ErrorStack> {
89 let mut bld = OsslParamBuilder::new()?;
90 let sn = digest.type_().short_name()?;
91 bld.add_utf8_string(OSSL_KDF_PARAM_DIGEST, sn.as_bytes())?;
92 bld.add_octet_string(OSSL_KDF_PARAM_KEY, key)?;
93 if let Some(salt) = salt {
94 bld.add_octet_string(OSSL_KDF_PARAM_SALT, salt)?;
95 }
96 if let Some(info) = info {
97 bld.add_octet_string(OSSL_KDF_PARAM_INFO, info)?;
98 }
99 let mode_value = match mode {
100 HkdfMode::ExtractAndExpand => ffi::EVP_KDF_HKDF_MODE_EXTRACT_AND_EXPAND,
101 HkdfMode::ExtractOnly => ffi::EVP_KDF_HKDF_MODE_EXTRACT_ONLY,
102 HkdfMode::ExpandOnly => ffi::EVP_KDF_HKDF_MODE_EXPAND_ONLY,
103 };
104 bld.add_int(OSSL_KDF_PARAM_MODE, mode_value)?;
105 let params = bld.to_param()?;
106 kdf_digest(CStr::from_bytes_with_nul(b"HKDF\0").unwrap(), ctx, ¶ms, out)
107 }
108 }
109}
110
111cfg_if::cfg_if! {
112 if #[cfg(all(ossl320, not(osslconf = "OPENSSL_NO_ARGON2")))] {
113 use std::cmp;
114
115 const OSSL_KDF_PARAM_PASSWORD: &CStr = unsafe { CStr::from_bytes_with_nul_unchecked(b"pass\0") };
116 const OSSL_KDF_PARAM_SECRET: &CStr = unsafe { CStr::from_bytes_with_nul_unchecked(b"secret\0") };
117 const OSSL_KDF_PARAM_ITER: &CStr = unsafe { CStr::from_bytes_with_nul_unchecked(b"iter\0") };
118 const OSSL_KDF_PARAM_SIZE: &CStr = unsafe { CStr::from_bytes_with_nul_unchecked(b"size\0") };
119 const OSSL_KDF_PARAM_THREADS: &CStr = unsafe { CStr::from_bytes_with_nul_unchecked(b"threads\0") };
120 const OSSL_KDF_PARAM_ARGON2_AD: &CStr = unsafe { CStr::from_bytes_with_nul_unchecked(b"ad\0") };
121 const OSSL_KDF_PARAM_ARGON2_LANES: &CStr = unsafe { CStr::from_bytes_with_nul_unchecked(b"lanes\0") };
122 const OSSL_KDF_PARAM_ARGON2_MEMCOST: &CStr = unsafe { CStr::from_bytes_with_nul_unchecked(b"memcost\0") };
123
124 #[allow(clippy::too_many_arguments)]
125 pub fn argon2d(
126 ctx: Option<&LibCtxRef>,
127 pass: &[u8],
128 salt: &[u8],
129 ad: Option<&[u8]>,
130 secret: Option<&[u8]>,
131 iter: u32,
132 lanes: u32,
133 memcost: u32,
134 out: &mut [u8],
135 ) -> Result<(), ErrorStack> {
136 argon2_helper(CStr::from_bytes_with_nul(b"ARGON2D\0").unwrap(), ctx, pass, salt, ad, secret, iter, lanes, memcost, out)
137 }
138
139 #[allow(clippy::too_many_arguments)]
140 pub fn argon2i(
141 ctx: Option<&LibCtxRef>,
142 pass: &[u8],
143 salt: &[u8],
144 ad: Option<&[u8]>,
145 secret: Option<&[u8]>,
146 iter: u32,
147 lanes: u32,
148 memcost: u32,
149 out: &mut [u8],
150 ) -> Result<(), ErrorStack> {
151 argon2_helper(CStr::from_bytes_with_nul(b"ARGON2I\0").unwrap(), ctx, pass, salt, ad, secret, iter, lanes, memcost, out)
152 }
153
154 #[allow(clippy::too_many_arguments)]
155 pub fn argon2id(
156 ctx: Option<&LibCtxRef>,
157 pass: &[u8],
158 salt: &[u8],
159 ad: Option<&[u8]>,
160 secret: Option<&[u8]>,
161 iter: u32,
162 lanes: u32,
163 memcost: u32,
164 out: &mut [u8],
165 ) -> Result<(), ErrorStack> {
166 argon2_helper(CStr::from_bytes_with_nul(b"ARGON2ID\0").unwrap(), ctx, pass, salt, ad, secret, iter, lanes, memcost, out)
167 }
168
169 #[allow(clippy::too_many_arguments)]
177 fn argon2_helper(
178 kdf_identifier: &CStr,
179 ctx: Option<&LibCtxRef>,
180 pass: &[u8],
181 salt: &[u8],
182 ad: Option<&[u8]>,
183 secret: Option<&[u8]>,
184 iter: u32,
185 lanes: u32,
186 memcost: u32,
187 out: &mut [u8],
188 ) -> Result<(), ErrorStack> {
189 let libctx = ctx.map_or(ptr::null_mut(), ForeignTypeRef::as_ptr);
190 let max_threads = unsafe {
191 ffi::init();
192 ffi::OSSL_get_max_threads(libctx)
193 };
194 let mut threads = 1;
195 if max_threads > 0 {
199 threads = cmp::min(lanes, cmp::min(max_threads, u32::MAX as u64) as u32);
200 }
201 let mut bld = OsslParamBuilder::new()?;
202 bld.add_octet_string(OSSL_KDF_PARAM_PASSWORD, pass)?;
203 bld.add_octet_string(OSSL_KDF_PARAM_SALT, salt)?;
204 bld.add_uint(OSSL_KDF_PARAM_THREADS, threads)?;
205 bld.add_uint(OSSL_KDF_PARAM_ARGON2_LANES, lanes)?;
206 bld.add_uint(OSSL_KDF_PARAM_ARGON2_MEMCOST, memcost)?;
207 bld.add_uint(OSSL_KDF_PARAM_ITER, iter)?;
208 let size = out.len() as u32;
209 bld.add_uint(OSSL_KDF_PARAM_SIZE, size)?;
210 if let Some(ad) = ad {
211 bld.add_octet_string(OSSL_KDF_PARAM_ARGON2_AD, ad)?;
212 }
213 if let Some(secret) = secret {
214 bld.add_octet_string(OSSL_KDF_PARAM_SECRET, secret)?;
215 }
216 let params = bld.to_param()?;
217 kdf_digest(kdf_identifier, ctx, ¶ms, out)
218 }
219 }
220}
221
222#[cfg(test)]
223mod tests {
224 #[cfg(ossl300)]
225 fn hkdf_test_helper(
226 digest: &crate::md::MdRef,
227 key: &[u8],
228 salt: Option<&[u8]>,
229 info: &[u8],
230 expected_prk_hex: &str,
231 expected_hex: &str,
232 ) {
233 let expected_prk = hex::decode(expected_prk_hex).unwrap();
234 let expected = hex::decode(expected_hex).unwrap();
235 let mut prk = vec![0; expected_prk.len()];
236 let mut actual = vec![0; expected.len()];
237
238 super::hkdf(
240 digest,
241 key,
242 salt,
243 None,
244 crate::kdf::HkdfMode::ExtractOnly,
245 None,
246 &mut prk,
247 )
248 .unwrap();
249 assert_eq!(prk, expected_prk);
250 super::hkdf(
251 digest,
252 &prk,
253 None,
254 Some(info),
255 crate::kdf::HkdfMode::ExpandOnly,
256 None,
257 &mut actual,
258 )
259 .unwrap();
260 assert_eq!(actual, expected);
261
262 super::hkdf(
264 digest,
265 key,
266 salt,
267 Some(info),
268 crate::kdf::HkdfMode::ExtractAndExpand,
269 None,
270 &mut actual,
271 )
272 .unwrap();
273 assert_eq!(actual, expected);
274 }
275
276 #[test]
277 #[cfg(ossl300)]
278 fn hkdf_rfc_case_1() {
279 let digest = crate::md::Md::sha256();
281 let key = hex::decode("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b").unwrap();
282 let salt = hex::decode("000102030405060708090a0b0c").unwrap();
283 let info = hex::decode("f0f1f2f3f4f5f6f7f8f9").unwrap();
284 let expected_prk = concat!(
285 "077709362c2e32df0ddc3f0dc47bba63",
286 "90b6c73bb50f9c3122ec844ad7c2b3e5"
287 );
288 let expected = concat!(
289 "3cb25f25faacd57a90434f64d0362f2a",
290 "2d2d0a90cf1a5a4c5db02d56ecc4c5bf",
291 "34007208d5b887185865"
292 );
293 hkdf_test_helper(digest, &key, Some(&salt), &info, expected_prk, expected);
294 }
295
296 #[test]
297 #[cfg(ossl300)]
298 fn hkdf_rfc_case_2() {
299 let digest = crate::md::Md::sha256();
301 let key = hex::decode(concat!(
302 "000102030405060708090a0b0c0d0e0f",
303 "101112131415161718191a1b1c1d1e1f",
304 "202122232425262728292a2b2c2d2e2f",
305 "303132333435363738393a3b3c3d3e3f",
306 "404142434445464748494a4b4c4d4e4f"
307 ))
308 .unwrap();
309 let salt = hex::decode(concat!(
310 "606162636465666768696a6b6c6d6e6f",
311 "707172737475767778797a7b7c7d7e7f",
312 "808182838485868788898a8b8c8d8e8f",
313 "909192939495969798999a9b9c9d9e9f",
314 "a0a1a2a3a4a5a6a7a8a9aaabacadaeaf",
315 ))
316 .unwrap();
317 let info = hex::decode(concat!(
318 "b0b1b2b3b4b5b6b7b8b9babbbcbdbebf",
319 "c0c1c2c3c4c5c6c7c8c9cacbcccdcecf",
320 "d0d1d2d3d4d5d6d7d8d9dadbdcdddedf",
321 "e0e1e2e3e4e5e6e7e8e9eaebecedeeef",
322 "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff",
323 ))
324 .unwrap();
325 let expected_prk = concat!(
326 "06a6b88c5853361a06104c9ceb35b45c",
327 "ef760014904671014a193f40c15fc244",
328 );
329 let expected = concat!(
330 "b11e398dc80327a1c8e7f78c596a4934",
331 "4f012eda2d4efad8a050cc4c19afa97c",
332 "59045a99cac7827271cb41c65e590e09",
333 "da3275600c2f09b8367793a9aca3db71",
334 "cc30c58179ec3e87c14c01d5c1f3434f",
335 "1d87",
336 );
337 hkdf_test_helper(digest, &key, Some(&salt), &info, expected_prk, expected);
338 }
339
340 #[test]
341 #[cfg(ossl300)]
342 fn hkdf_rfc_case_3() {
343 let digest = crate::md::Md::sha256();
345 let key = hex::decode("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b").unwrap();
346 let salt = [0u8; 0];
347 let info = [0u8; 0];
348 let expected_prk = concat!(
349 "19ef24a32c717b167f33a91d6f648bdf",
350 "96596776afdb6377ac434c1c293ccb04",
351 );
352 let expected = concat!(
353 "8da4e775a563c18f715f802a063c5a31",
354 "b8a11f5c5ee1879ec3454e5f3c738d2d",
355 "9d201395faa4b61a96c8",
356 );
357 hkdf_test_helper(digest, &key, Some(&salt), &info, expected_prk, expected);
358 }
359
360 #[test]
361 #[cfg(ossl300)]
362 fn hkdf_rfc_case_4() {
363 let digest = crate::md::Md::sha1();
365 let key = hex::decode("0b0b0b0b0b0b0b0b0b0b0b").unwrap();
366 let salt = hex::decode("000102030405060708090a0b0c").unwrap();
367 let info = hex::decode("f0f1f2f3f4f5f6f7f8f9").unwrap();
368 let expected_prk = "9b6c18c432a7bf8f0e71c8eb88f4b30baa2ba243";
369 let expected = concat!(
370 "085a01ea1b10f36933068b56efa5ad81",
371 "a4f14b822f5b091568a9cdd4f155fda2",
372 "c22e422478d305f3f896"
373 );
374 hkdf_test_helper(digest, &key, Some(&salt), &info, expected_prk, expected);
375 }
376
377 #[test]
378 #[cfg(ossl300)]
379 fn hkdf_rfc_case_5() {
380 let digest = crate::md::Md::sha1();
382 let key = hex::decode(concat!(
383 "000102030405060708090a0b0c0d0e0f",
384 "101112131415161718191a1b1c1d1e1f",
385 "202122232425262728292a2b2c2d2e2f",
386 "303132333435363738393a3b3c3d3e3f",
387 "404142434445464748494a4b4c4d4e4f"
388 ))
389 .unwrap();
390 let salt = hex::decode(concat!(
391 "606162636465666768696a6b6c6d6e6f",
392 "707172737475767778797a7b7c7d7e7f",
393 "808182838485868788898a8b8c8d8e8f",
394 "909192939495969798999a9b9c9d9e9f",
395 "a0a1a2a3a4a5a6a7a8a9aaabacadaeaf",
396 ))
397 .unwrap();
398 let info = hex::decode(concat!(
399 "b0b1b2b3b4b5b6b7b8b9babbbcbdbebf",
400 "c0c1c2c3c4c5c6c7c8c9cacbcccdcecf",
401 "d0d1d2d3d4d5d6d7d8d9dadbdcdddedf",
402 "e0e1e2e3e4e5e6e7e8e9eaebecedeeef",
403 "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff",
404 ))
405 .unwrap();
406 let expected_prk = "8adae09a2a307059478d309b26c4115a224cfaf6";
407 let expected = concat!(
408 "0bd770a74d1160f7c9f12cd5912a06eb",
409 "ff6adcae899d92191fe4305673ba2ffe",
410 "8fa3f1a4e5ad79f3f334b3b202b2173c",
411 "486ea37ce3d397ed034c7f9dfeb15c5e",
412 "927336d0441f4c4300e2cff0d0900b52",
413 "d3b4",
414 );
415 hkdf_test_helper(digest, &key, Some(&salt), &info, expected_prk, expected);
416 }
417
418 #[test]
419 #[cfg(ossl300)]
420 fn hkdf_rfc_case_6() {
421 let digest = crate::md::Md::sha1();
423 let key = hex::decode("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b").unwrap();
424 let salt = [0u8; 0];
425 let info = [0u8; 0];
426 let expected_prk = "da8c8a73c7fa77288ec6f5e7c297786aa0d32d01";
427 let expected = concat!(
428 "0ac1af7002b3d761d1e55298da9d0506",
429 "b9ae52057220a306e07b6b87e8df21d0",
430 "ea00033de03984d34918",
431 );
432 hkdf_test_helper(digest, &key, Some(&salt), &info, expected_prk, expected);
433 }
434
435 #[test]
436 #[cfg(ossl300)]
437 fn hkdf_rfc_case_7() {
438 let digest = crate::md::Md::sha1();
440 let key = hex::decode("0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c").unwrap();
441 let salt = None;
442 let info = [0u8; 0];
443 let expected_prk = "2adccada18779e7c2077ad2eb19d3f3e731385dd";
444 let expected = concat!(
445 "2c91117204d745f3500d636a62f64f0a",
446 "b3bae548aa53d423b0d1f27ebba6f5e5",
447 "673a081d70cce7acfc48",
448 );
449 hkdf_test_helper(digest, &key, salt, &info, expected_prk, expected);
450 }
451
452 #[test]
453 #[cfg(all(ossl320, not(osslconf = "OPENSSL_NO_ARGON2")))]
454 fn argon2id() {
455 let pass = hex::decode("0101010101010101010101010101010101010101010101010101010101010101")
457 .unwrap();
458 let salt = hex::decode("02020202020202020202020202020202").unwrap();
459 let secret = hex::decode("0303030303030303").unwrap();
460 let ad = hex::decode("040404040404040404040404").unwrap();
461 let expected = "0d640df58d78766c08c037a34a8b53c9d01ef0452d75b65eb52520e96b01e659";
462
463 let mut actual = [0u8; 32];
464 super::argon2id(
465 None,
466 &pass,
467 &salt,
468 Some(&ad),
469 Some(&secret),
470 3,
471 4,
472 32,
473 &mut actual,
474 )
475 .unwrap();
476 assert_eq!(hex::encode(&actual[..]), expected);
477 }
478
479 #[test]
480 #[cfg(all(ossl320, not(osslconf = "OPENSSL_NO_ARGON2")))]
481 fn argon2d() {
482 let pass = hex::decode("0101010101010101010101010101010101010101010101010101010101010101")
484 .unwrap();
485 let salt = hex::decode("02020202020202020202020202020202").unwrap();
486 let secret = hex::decode("0303030303030303").unwrap();
487 let ad = hex::decode("040404040404040404040404").unwrap();
488 let expected = "512b391b6f1162975371d30919734294f868e3be3984f3c1a13a4db9fabe4acb";
489
490 let mut actual = [0u8; 32];
491 super::argon2d(
492 None,
493 &pass,
494 &salt,
495 Some(&ad),
496 Some(&secret),
497 3,
498 4,
499 32,
500 &mut actual,
501 )
502 .unwrap();
503 assert_eq!(hex::encode(&actual[..]), expected);
504 }
505
506 #[test]
507 #[cfg(all(ossl320, not(osslconf = "OPENSSL_NO_ARGON2")))]
508 fn argon2i() {
509 let pass = hex::decode("0101010101010101010101010101010101010101010101010101010101010101")
511 .unwrap();
512 let salt = hex::decode("02020202020202020202020202020202").unwrap();
513 let secret = hex::decode("0303030303030303").unwrap();
514 let ad = hex::decode("040404040404040404040404").unwrap();
515 let expected = "c814d9d1dc7f37aa13f0d77f2494bda1c8de6b016dd388d29952a4c4672b6ce8";
516
517 let mut actual = [0u8; 32];
518 super::argon2i(
519 None,
520 &pass,
521 &salt,
522 Some(&ad),
523 Some(&secret),
524 3,
525 4,
526 32,
527 &mut actual,
528 )
529 .unwrap();
530 assert_eq!(hex::encode(&actual[..]), expected);
531 }
532
533 #[test]
534 #[cfg(all(ossl320, not(osslconf = "OPENSSL_NO_ARGON2")))]
535 fn argon2id_no_ad_secret() {
536 let pass = b"";
538 let salt = hex::decode("02020202020202020202020202020202").unwrap();
539 let expected = "0a34f1abde67086c82e785eaf17c68382259a264f4e61b91cd2763cb75ac189a";
540
541 let mut actual = [0u8; 32];
542 super::argon2id(None, pass, &salt, None, None, 3, 4, 32, &mut actual).unwrap();
543 assert_eq!(hex::encode(&actual[..]), expected);
544 }
545}