1use crate::blake2b;
75use crate::classic::crypto_auth_hmac_impl::{
76 HmacHash, HmacState, hmac_final, hmac_init, hmac_keygen, hmac_update,
77};
78use crate::constants::{
79 CRYPTO_GENERICHASH_BLAKE2B_PERSONALBYTES, CRYPTO_GENERICHASH_BLAKE2B_SALTBYTES,
80 CRYPTO_KDF_BLAKE2B_BYTES_MAX, CRYPTO_KDF_BLAKE2B_BYTES_MIN, CRYPTO_KDF_CONTEXTBYTES,
81 CRYPTO_KDF_HKDF_SHA256_BYTES_MAX, CRYPTO_KDF_HKDF_SHA256_BYTES_MIN,
82 CRYPTO_KDF_HKDF_SHA256_KEYBYTES, CRYPTO_KDF_HKDF_SHA512_BYTES_MAX,
83 CRYPTO_KDF_HKDF_SHA512_BYTES_MIN, CRYPTO_KDF_HKDF_SHA512_KEYBYTES, CRYPTO_KDF_KEYBYTES,
84};
85use crate::error::Error;
86use crate::rng::copy_randombytes;
87use crate::sha256::Sha256;
88use crate::sha512::Sha512;
89use crate::utils::zeroize_bytes;
90
91pub type Key = [u8; CRYPTO_KDF_KEYBYTES];
93pub type Context = [u8; CRYPTO_KDF_CONTEXTBYTES];
95pub type HkdfSha256Key = [u8; CRYPTO_KDF_HKDF_SHA256_KEYBYTES];
97pub type HkdfSha512Key = [u8; CRYPTO_KDF_HKDF_SHA512_KEYBYTES];
99
100pub struct HkdfSha256State(HmacState<Sha256, 64, CRYPTO_KDF_HKDF_SHA256_KEYBYTES>);
102
103pub struct HkdfSha512State(HmacState<Sha512, 128, CRYPTO_KDF_HKDF_SHA512_KEYBYTES>);
105
106#[must_use]
109pub fn crypto_kdf_keygen() -> Key {
110 let mut key = Key::default();
111 copy_randombytes(&mut key);
112 key
113}
114
115#[must_use]
117pub fn crypto_kdf_hkdf_sha256_keygen() -> HkdfSha256Key {
118 hmac_keygen()
119}
120
121#[must_use]
123pub fn crypto_kdf_hkdf_sha512_keygen() -> HkdfSha512Key {
124 hmac_keygen()
125}
126
127pub fn crypto_kdf_derive_from_key(
135 subkey: &mut [u8],
136 subkey_id: u64,
137 context: &Context,
138 main_key: &Key,
139) -> Result<(), Error> {
140 validate_subkey_length(subkey.len())?;
141
142 let mut ctx_padded = [0u8; CRYPTO_GENERICHASH_BLAKE2B_PERSONALBYTES];
143 let mut salt = [0u8; CRYPTO_GENERICHASH_BLAKE2B_SALTBYTES];
144
145 ctx_padded[..CRYPTO_KDF_CONTEXTBYTES].copy_from_slice(context);
146 salt[..8].copy_from_slice(&subkey_id.to_le_bytes());
147
148 blake2b::hash_key_only(subkey, main_key, &salt, &ctx_padded)
149}
150
151pub(crate) fn validate_subkey_length(length: usize) -> Result<(), Error> {
152 if !(CRYPTO_KDF_BLAKE2B_BYTES_MIN..=CRYPTO_KDF_BLAKE2B_BYTES_MAX).contains(&length) {
153 Err(length_error!(
154 crate::ErrorContext::Subkey,
155 length,
156 range CRYPTO_KDF_BLAKE2B_BYTES_MIN,
157 CRYPTO_KDF_BLAKE2B_BYTES_MAX
158 ))
159 } else {
160 Ok(())
161 }
162}
163
164fn validate_hkdf_output_len(
165 output_len: usize,
166 min_len: usize,
167 max_len: usize,
168) -> Result<(), Error> {
169 if output_len < min_len || output_len > max_len {
170 Err(length_error!(crate::ErrorContext::Output, output_len, range min_len, max_len))
171 } else {
172 Ok(())
173 }
174}
175
176pub fn crypto_kdf_hkdf_sha256_extract(prk: &mut HkdfSha256Key, salt: Option<&[u8]>, ikm: &[u8]) {
178 let mut state = crypto_kdf_hkdf_sha256_extract_init(salt);
179 crypto_kdf_hkdf_sha256_extract_update(&mut state, ikm);
180 crypto_kdf_hkdf_sha256_extract_final(state, prk);
181}
182
183#[must_use]
185pub fn crypto_kdf_hkdf_sha256_extract_init(salt: Option<&[u8]>) -> HkdfSha256State {
186 HkdfSha256State(hmac_init::<Sha256, 64, CRYPTO_KDF_HKDF_SHA256_KEYBYTES>(
187 salt.unwrap_or(&[]),
188 ))
189}
190
191pub fn crypto_kdf_hkdf_sha256_extract_update(state: &mut HkdfSha256State, ikm: &[u8]) {
193 hmac_update(&mut state.0, ikm);
194}
195
196pub fn crypto_kdf_hkdf_sha256_extract_final(state: HkdfSha256State, prk: &mut HkdfSha256Key) {
198 hmac_final(state.0, prk);
199}
200
201pub fn crypto_kdf_hkdf_sha256_expand(
208 output: &mut [u8],
209 context: &[u8],
210 prk: &HkdfSha256Key,
211) -> Result<(), Error> {
212 hkdf_expand::<Sha256, 64, CRYPTO_KDF_HKDF_SHA256_KEYBYTES>(
213 output,
214 context,
215 prk,
216 CRYPTO_KDF_HKDF_SHA256_BYTES_MIN,
217 CRYPTO_KDF_HKDF_SHA256_BYTES_MAX,
218 )
219}
220
221pub fn crypto_kdf_hkdf_sha512_extract(prk: &mut HkdfSha512Key, salt: Option<&[u8]>, ikm: &[u8]) {
223 let mut state = crypto_kdf_hkdf_sha512_extract_init(salt);
224 crypto_kdf_hkdf_sha512_extract_update(&mut state, ikm);
225 crypto_kdf_hkdf_sha512_extract_final(state, prk);
226}
227
228#[must_use]
230pub fn crypto_kdf_hkdf_sha512_extract_init(salt: Option<&[u8]>) -> HkdfSha512State {
231 HkdfSha512State(hmac_init::<Sha512, 128, CRYPTO_KDF_HKDF_SHA512_KEYBYTES>(
232 salt.unwrap_or(&[]),
233 ))
234}
235
236pub fn crypto_kdf_hkdf_sha512_extract_update(state: &mut HkdfSha512State, ikm: &[u8]) {
238 hmac_update(&mut state.0, ikm);
239}
240
241pub fn crypto_kdf_hkdf_sha512_extract_final(state: HkdfSha512State, prk: &mut HkdfSha512Key) {
243 hmac_final(state.0, prk);
244}
245
246pub fn crypto_kdf_hkdf_sha512_expand(
253 output: &mut [u8],
254 context: &[u8],
255 prk: &HkdfSha512Key,
256) -> Result<(), Error> {
257 hkdf_expand::<Sha512, 128, CRYPTO_KDF_HKDF_SHA512_KEYBYTES>(
258 output,
259 context,
260 prk,
261 CRYPTO_KDF_HKDF_SHA512_BYTES_MIN,
262 CRYPTO_KDF_HKDF_SHA512_BYTES_MAX,
263 )
264}
265
266fn hkdf_expand<H, const BLOCK_BYTES: usize, const OUT_BYTES: usize>(
267 output: &mut [u8],
268 context: &[u8],
269 prk: &[u8; OUT_BYTES],
270 min_len: usize,
271 max_len: usize,
272) -> Result<(), Error>
273where
274 H: HmacHash<OUT_BYTES>,
275{
276 validate_hkdf_output_len(output.len(), min_len, max_len)?;
277
278 let keyed = hmac_init::<H, BLOCK_BYTES, OUT_BYTES>(prk);
281 let mut previous = [0u8; OUT_BYTES];
282 let mut offset = 0usize;
283 for counter in 1..=255u8 {
284 if offset == output.len() {
285 break;
286 }
287
288 let mut state = keyed.clone();
289 if counter > 1 {
290 hmac_update(&mut state, &previous);
291 }
292 hmac_update(&mut state, context);
293 hmac_update(&mut state, &[counter]);
294 hmac_final(state, &mut previous);
295
296 let chunk_len = (output.len() - offset).min(previous.len());
297 output[offset..offset + chunk_len].copy_from_slice(&previous[..chunk_len]);
298 offset += chunk_len;
299 }
300
301 zeroize_bytes(&mut previous);
302
303 Ok(())
304}
305
306#[cfg(test)]
307mod tests {
308 use super::*;
309 use crate::test_prelude::*;
310
311 #[test]
312 fn test_crypto_kdf_rejects_invalid_subkey_lengths() {
313 let context = Context::default();
314 let key = Key::default();
315
316 for length in [
317 CRYPTO_KDF_BLAKE2B_BYTES_MIN - 1,
318 CRYPTO_KDF_BLAKE2B_BYTES_MAX + 1,
319 ] {
320 let mut subkey = vec![0u8; length];
321 assert!(matches!(
322 crypto_kdf_derive_from_key(&mut subkey, 0, &context, &key),
323 Err(Error::InvalidLength {
324 context: crate::ErrorContext::Subkey,
325 actual,
326 constraint: crate::LengthConstraint::Between {
327 min: CRYPTO_KDF_BLAKE2B_BYTES_MIN,
328 max: CRYPTO_KDF_BLAKE2B_BYTES_MAX,
329 },
330 }) if actual == length
331 ));
332 }
333 }
334
335 #[cfg(dryoc_native_tests)]
336 #[test]
337 fn test_crypto_kdf_boundary_parameters_match_libsodium() {
338 crate::native_test_util::init();
339 let key: Key = core::array::from_fn(|i| (i as u8).wrapping_mul(37).wrapping_add(11));
340 for context in [
341 [0u8; CRYPTO_KDF_CONTEXTBYTES],
342 [0xff; CRYPTO_KDF_CONTEXTBYTES],
343 ] {
344 for subkey_id in [0, 1, u64::MAX] {
345 for length in [16usize, 64] {
346 let mut ours = vec![0u8; length];
347 let mut sodium = vec![0u8; length];
348 crypto_kdf_derive_from_key(&mut ours, subkey_id, &context, &key)
349 .expect("dryoc derivation failed");
350 let rc = unsafe {
351 libsodium_sys::crypto_kdf_derive_from_key(
352 sodium.as_mut_ptr(),
353 sodium.len(),
354 subkey_id,
355 context.as_ptr().cast(),
356 key.as_ptr(),
357 )
358 };
359 assert_eq!(rc, 0);
360 assert_eq!(
361 ours, sodium,
362 "context {context:x?}, id {subkey_id}, length {length}"
363 );
364 }
365 }
366 }
367 }
368
369 fn bytes_in_range(start: u8, end_inclusive: u8) -> Vec<u8> {
370 (start..=end_inclusive).collect()
371 }
372
373 fn assert_hkdf_sha256(
374 ikm: &[u8],
375 salt: Option<&[u8]>,
376 info: &[u8],
377 output_len: usize,
378 expected_prk_hex: &str,
379 expected_okm_hex: &str,
380 ) {
381 let expected_prk = hex::decode(expected_prk_hex).expect("hex failed");
382 let expected_okm = hex::decode(expected_okm_hex).expect("hex failed");
383 let mut prk = HkdfSha256Key::default();
384 crypto_kdf_hkdf_sha256_extract(&mut prk, salt, ikm);
385 assert_eq!(prk.as_slice(), expected_prk.as_slice());
386
387 let mut okm = vec![0u8; output_len];
388 crypto_kdf_hkdf_sha256_expand(&mut okm, info, &prk).expect("expand failed");
389 assert_eq!(okm, expected_okm);
390 }
391
392 fn assert_hkdf_sha512(
393 ikm: &[u8],
394 salt: Option<&[u8]>,
395 info: &[u8],
396 output_len: usize,
397 expected_prk_hex: &str,
398 expected_okm_hex: &str,
399 ) {
400 let expected_prk = hex::decode(expected_prk_hex).expect("hex failed");
401 let expected_okm = hex::decode(expected_okm_hex).expect("hex failed");
402 let mut prk = [0u8; CRYPTO_KDF_HKDF_SHA512_KEYBYTES];
403 crypto_kdf_hkdf_sha512_extract(&mut prk, salt, ikm);
404 assert_eq!(prk.as_slice(), expected_prk.as_slice());
405
406 let mut okm = vec![0u8; output_len];
407 crypto_kdf_hkdf_sha512_expand(&mut okm, info, &prk).expect("expand failed");
408 assert_eq!(okm, expected_okm);
409 }
410
411 #[test]
412 fn test_hkdf_sha256_rfc5869_case_1() {
413 let ikm = [0x0bu8; 22];
414 let salt = hex::decode("000102030405060708090a0b0c").expect("hex failed");
415 let info = hex::decode("f0f1f2f3f4f5f6f7f8f9").expect("hex failed");
416 assert_hkdf_sha256(
417 &ikm,
418 Some(&salt),
419 &info,
420 42,
421 "077709362c2e32df0ddc3f0dc47bba6390b6c73bb50f9c3122ec844ad7c2b3e5",
422 concat!(
423 "3cb25f25faacd57a90434f64d0362f2a",
424 "2d2d0a90cf1a5a4c5db02d56ecc4c5bf",
425 "34007208d5b887185865",
426 ),
427 );
428 }
429
430 #[test]
431 fn test_hkdf_sha256_rfc5869_case_2() {
432 let ikm = bytes_in_range(0x00, 0x4f);
433 let salt = bytes_in_range(0x60, 0xaf);
434 let info = bytes_in_range(0xb0, 0xff);
435 assert_hkdf_sha256(
436 &ikm,
437 Some(&salt),
438 &info,
439 82,
440 "06a6b88c5853361a06104c9ceb35b45cef760014904671014a193f40c15fc244",
441 concat!(
442 "b11e398dc80327a1c8e7f78c596a4934",
443 "4f012eda2d4efad8a050cc4c19afa97c",
444 "59045a99cac7827271cb41c65e590e09",
445 "da3275600c2f09b8367793a9aca3db71",
446 "cc30c58179ec3e87c14c01d5c1f3434f",
447 "1d87",
448 ),
449 );
450 }
451
452 #[test]
453 fn test_hkdf_sha256_rfc5869_case_3_no_salt_or_info() {
454 let ikm = [0x0bu8; 22];
455 assert_hkdf_sha256(
456 &ikm,
457 None,
458 &[],
459 42,
460 "19ef24a32c717b167f33a91d6f648bdf96596776afdb6377ac434c1c293ccb04",
461 concat!(
462 "8da4e775a563c18f715f802a063c5a31",
463 "b8a11f5c5ee1879ec3454e5f3c738d2d",
464 "9d201395faa4b61a96c8",
465 ),
466 );
467 }
468
469 #[test]
473 fn test_hkdf_sha512_a1_inputs_openssl_vector() {
474 let ikm = [0x0bu8; 22];
475 let salt = hex::decode("000102030405060708090a0b0c").expect("hex failed");
476 let info = hex::decode("f0f1f2f3f4f5f6f7f8f9").expect("hex failed");
477 assert_hkdf_sha512(
478 &ikm,
479 Some(&salt),
480 &info,
481 42,
482 "665799823737ded04a88e47e54a5890bb2c3d247c7a4254a8e61350723590a26c36238127d8661b88cf80ef802d57e2f7cebcf1e00e083848be19929c61b4237",
483 "832390086cda71fb47625bb5ceb168e4c8e26a1a16ed34d9fc7fe92c1481579338da362cb8d9f925d7cb",
484 );
485 }
486
487 #[test]
488 fn test_hkdf_sha512_long_inputs_openssl_vector() {
489 let ikm = bytes_in_range(0x00, 0x4f);
490 let salt = bytes_in_range(0x60, 0xaf);
491 let info = bytes_in_range(0xb0, 0xff);
492 assert_hkdf_sha512(
493 &ikm,
494 Some(&salt),
495 &info,
496 82,
497 concat!(
498 "35672542907d4e142c00e84499e74e1d",
499 "e08be86535f924e022804ad775dde27e",
500 "c86cd1e5b7d178c74489bdbeb30712be",
501 "b82d4f97416c5a94ea81ebdf3e629e4a",
502 ),
503 concat!(
504 "ce6c97192805b346e6161e821ed16567",
505 "3b84f400a2b514b2fe23d84cd189ddf1",
506 "b695b48cbd1c8388441137b3ce28f16a",
507 "a64ba33ba466b24df6cfcb021ecff235",
508 "f6a2056ce3af1de44d572097a8505d",
509 "9e7a93",
510 ),
511 );
512 }
513
514 #[test]
515 fn test_hkdf_sha512_no_salt_or_info_openssl_vector() {
516 let ikm = [0x0bu8; 22];
517 assert_hkdf_sha512(
518 &ikm,
519 None,
520 &[],
521 42,
522 concat!(
523 "fd200c4987ac491313bd4a2a13287121",
524 "247239e11c9ef82802044b66ef357e5b",
525 "194498d0682611382348572a7b1611de",
526 "54764094286320578a863f36562b0df6",
527 ),
528 concat!(
529 "f5fa02b18298a72a8c23898a8703472c",
530 "6eb179dc204c03425c970e3b164bf90f",
531 "ff22d04836d0e2343bac",
532 ),
533 );
534 }
535
536 #[test]
537 fn test_hkdf_output_length_limits() {
538 let prk256 = HkdfSha256Key::default();
539 let mut okm256 = vec![0u8; CRYPTO_KDF_HKDF_SHA256_BYTES_MAX + 1];
540 crypto_kdf_hkdf_sha256_expand(&mut okm256, b"context", &prk256)
541 .expect_err("oversized output should fail");
542
543 let prk512 = [0u8; CRYPTO_KDF_HKDF_SHA512_KEYBYTES];
544 let mut okm512 = vec![0u8; CRYPTO_KDF_HKDF_SHA512_BYTES_MAX + 1];
545 crypto_kdf_hkdf_sha512_expand(&mut okm512, b"context", &prk512)
546 .expect_err("oversized output should fail");
547 }
548
549 #[test]
550 fn test_hkdf_empty_and_max_output_lengths() {
551 let prk256 = HkdfSha256Key::default();
552 let mut empty256 = [];
553 crypto_kdf_hkdf_sha256_expand(&mut empty256, b"context", &prk256)
554 .expect("empty SHA-256 output should be allowed");
555 let mut max256 = vec![0u8; CRYPTO_KDF_HKDF_SHA256_BYTES_MAX];
556 crypto_kdf_hkdf_sha256_expand(&mut max256, b"context", &prk256)
557 .expect("max SHA-256 output should be allowed");
558
559 let prk512 = [0u8; CRYPTO_KDF_HKDF_SHA512_KEYBYTES];
560 let mut empty512 = [];
561 crypto_kdf_hkdf_sha512_expand(&mut empty512, b"context", &prk512)
562 .expect("empty SHA-512 output should be allowed");
563 let mut max512 = vec![0u8; CRYPTO_KDF_HKDF_SHA512_BYTES_MAX];
564 crypto_kdf_hkdf_sha512_expand(&mut max512, b"context", &prk512)
565 .expect("max SHA-512 output should be allowed");
566 }
567
568 #[test]
569 fn test_hkdf_digest_boundary_output_lengths() {
570 let prk256 = HkdfSha256Key::default();
571 for len in [31, 32, 33] {
572 let mut okm = vec![0u8; len];
573 crypto_kdf_hkdf_sha256_expand(&mut okm, b"context", &prk256)
574 .expect("SHA-256 boundary output should be allowed");
575 }
576
577 let prk512 = [0u8; CRYPTO_KDF_HKDF_SHA512_KEYBYTES];
578 for len in [63, 64, 65] {
579 let mut okm = vec![0u8; len];
580 crypto_kdf_hkdf_sha512_expand(&mut okm, b"context", &prk512)
581 .expect("SHA-512 boundary output should be allowed");
582 }
583 }
584
585 #[test]
586 fn test_hkdf_none_and_empty_salt_match() {
587 let ikm = [0xabu8; 20];
588
589 let mut none_salt256 = HkdfSha256Key::default();
590 let mut empty_salt256 = HkdfSha256Key::default();
591 crypto_kdf_hkdf_sha256_extract(&mut none_salt256, None, &ikm);
592 crypto_kdf_hkdf_sha256_extract(&mut empty_salt256, Some(&[]), &ikm);
593 assert_eq!(none_salt256, empty_salt256);
594
595 let mut none_salt512 = [0u8; CRYPTO_KDF_HKDF_SHA512_KEYBYTES];
596 let mut empty_salt512 = [0u8; CRYPTO_KDF_HKDF_SHA512_KEYBYTES];
597 crypto_kdf_hkdf_sha512_extract(&mut none_salt512, None, &ikm);
598 crypto_kdf_hkdf_sha512_extract(&mut empty_salt512, Some(&[]), &ikm);
599 assert_eq!(none_salt512, empty_salt512);
600 }
601
602 #[test]
603 fn test_hkdf_incremental_extract_matches_one_shot() {
604 let salt = b"salt";
605 let ikm_parts = [
606 b"input ".as_slice(),
607 b"keying ".as_slice(),
608 b"material".as_slice(),
609 ];
610 let ikm = ikm_parts.concat();
611
612 let mut one_shot256 = HkdfSha256Key::default();
613 crypto_kdf_hkdf_sha256_extract(&mut one_shot256, Some(salt), &ikm);
614
615 let mut state256 = crypto_kdf_hkdf_sha256_extract_init(Some(salt));
616 for part in ikm_parts {
617 crypto_kdf_hkdf_sha256_extract_update(&mut state256, part);
618 }
619 let mut incremental256 = HkdfSha256Key::default();
620 crypto_kdf_hkdf_sha256_extract_final(state256, &mut incremental256);
621 assert_eq!(one_shot256, incremental256);
622
623 let mut one_shot512 = [0u8; CRYPTO_KDF_HKDF_SHA512_KEYBYTES];
624 crypto_kdf_hkdf_sha512_extract(&mut one_shot512, Some(salt), &ikm);
625
626 let mut state512 = crypto_kdf_hkdf_sha512_extract_init(Some(salt));
627 for part in ikm_parts {
628 crypto_kdf_hkdf_sha512_extract_update(&mut state512, part);
629 }
630 let mut incremental512 = [0u8; CRYPTO_KDF_HKDF_SHA512_KEYBYTES];
631 crypto_kdf_hkdf_sha512_extract_final(state512, &mut incremental512);
632 assert_eq!(one_shot512, incremental512);
633 }
634
635 #[cfg(dryoc_native_tests)]
636 #[test]
637 fn test_derive_key() {
638 use crate::native_test_util::kdf_blake2b_derive_from_key;
639
640 let main_key = crypto_kdf_keygen();
641 let context = b"hello123";
642
643 for i in 0..20 {
644 let mut key = Key::default();
645 crypto_kdf_derive_from_key(&mut key, i, context, &main_key).expect("kdf failed");
646
647 let so_key: [u8; 32] = kdf_blake2b_derive_from_key(i, context, &main_key);
648
649 assert_eq!(so_key, key);
650 }
651 }
652}