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