1use std::marker::PhantomData;
61
62#[cfg(feature = "serde")]
63use serde::{Deserialize, Serialize};
64use zeroize::{Zeroize, ZeroizeOnDrop};
65
66use crate::classic::crypto_kdf::{
67 crypto_kdf_hkdf_sha256_expand, crypto_kdf_hkdf_sha256_extract, crypto_kdf_hkdf_sha512_expand,
68 crypto_kdf_hkdf_sha512_extract,
69};
70use crate::constants::{
71 CRYPTO_KDF_HKDF_SHA256_BYTES_MAX, CRYPTO_KDF_HKDF_SHA256_BYTES_MIN,
72 CRYPTO_KDF_HKDF_SHA256_KEYBYTES, CRYPTO_KDF_HKDF_SHA512_BYTES_MAX,
73 CRYPTO_KDF_HKDF_SHA512_BYTES_MIN, CRYPTO_KDF_HKDF_SHA512_KEYBYTES,
74};
75use crate::error::Error;
76use crate::types::*;
77
78pub type HkdfSha256Prk = StackByteArray<CRYPTO_KDF_HKDF_SHA256_KEYBYTES>;
80pub type HkdfSha512Prk = StackByteArray<CRYPTO_KDF_HKDF_SHA512_KEYBYTES>;
82pub type HkdfSha256 = Hkdf<HkdfSha256Variant, HkdfSha256Prk, CRYPTO_KDF_HKDF_SHA256_KEYBYTES>;
84pub type HkdfSha512 = Hkdf<HkdfSha512Variant, HkdfSha512Prk, CRYPTO_KDF_HKDF_SHA512_KEYBYTES>;
86
87#[cfg_attr(
88 feature = "serde",
89 derive(Zeroize, Clone, Debug, Serialize, Deserialize)
90)]
91#[cfg_attr(not(feature = "serde"), derive(Zeroize, Clone, Debug))]
92pub struct Hkdf<Variant, Prk, const PRK_LENGTH: usize>
94where
95 Variant: HkdfVariant<PRK_LENGTH>,
96 Prk: ByteArray<PRK_LENGTH> + Zeroize + ZeroizeOnDrop,
97{
98 prk: Prk,
99 _variant: PhantomData<Variant>,
100}
101
102pub type HkdfSha256Expander<Prk> = Hkdf<HkdfSha256Variant, Prk, CRYPTO_KDF_HKDF_SHA256_KEYBYTES>;
104pub type HkdfSha512Expander<Prk> = Hkdf<HkdfSha512Variant, Prk, CRYPTO_KDF_HKDF_SHA512_KEYBYTES>;
106
107#[derive(Clone, Copy, Debug, Default)]
109pub struct HkdfSha256Variant;
110#[derive(Clone, Copy, Debug, Default)]
112pub struct HkdfSha512Variant;
113
114#[cfg(any(all(feature = "protected", any(unix, windows)), all(doc, not(doctest))))]
115#[cfg_attr(all(feature = "nightly", doc), doc(cfg(feature = "protected")))]
116pub mod protected {
117 use super::*;
134 pub use crate::protected::*;
135
136 pub type HkdfSha256Prk = HeapByteArray<CRYPTO_KDF_HKDF_SHA256_KEYBYTES>;
138 pub type HkdfSha512Prk = HeapByteArray<CRYPTO_KDF_HKDF_SHA512_KEYBYTES>;
140
141 pub type LockedHkdfSha256 = HkdfSha256Expander<Locked<HkdfSha256Prk>>;
143 pub type LockedHkdfSha512 = HkdfSha512Expander<Locked<HkdfSha512Prk>>;
145}
146
147pub trait HkdfVariant<const PRK_LENGTH: usize> {
149 type Prk: NewByteArray<PRK_LENGTH> + Zeroize + ZeroizeOnDrop;
151 const OUTPUT_BYTES_MIN: usize;
153 const OUTPUT_BYTES_MAX: usize;
155
156 fn extract(prk: &mut [u8; PRK_LENGTH], salt: Option<&[u8]>, ikm: &[u8]);
158 fn expand(output: &mut [u8], context: &[u8], prk: &[u8; PRK_LENGTH]) -> Result<(), Error>;
165
166 fn validate_output_len(output_len: usize) -> Result<(), Error> {
173 if output_len < Self::OUTPUT_BYTES_MIN || output_len > Self::OUTPUT_BYTES_MAX {
174 Err(length_error!(
175 crate::ErrorContext::Output,
176 output_len,
177 range Self::OUTPUT_BYTES_MIN,
178 Self::OUTPUT_BYTES_MAX
179 ))
180 } else {
181 Ok(())
182 }
183 }
184}
185
186macro_rules! impl_hkdf_variant {
187 (
188 $variant:ty,
189 $prk_len:expr,
190 $prk:ty,
191 $bytes_min:expr,
192 $bytes_max:expr,
193 $extract:path,
194 $expand:path
195 ) => {
196 impl HkdfVariant<$prk_len> for $variant {
197 type Prk = $prk;
198
199 const OUTPUT_BYTES_MAX: usize = $bytes_max;
200 const OUTPUT_BYTES_MIN: usize = $bytes_min;
201
202 fn extract(prk: &mut [u8; $prk_len], salt: Option<&[u8]>, ikm: &[u8]) {
203 $extract(prk, salt, ikm);
204 }
205
206 fn expand(
207 output: &mut [u8],
208 context: &[u8],
209 prk: &[u8; $prk_len],
210 ) -> Result<(), Error> {
211 $expand(output, context, prk)
212 }
213 }
214 };
215}
216
217impl_hkdf_variant!(
218 HkdfSha256Variant,
219 CRYPTO_KDF_HKDF_SHA256_KEYBYTES,
220 HkdfSha256Prk,
221 CRYPTO_KDF_HKDF_SHA256_BYTES_MIN,
222 CRYPTO_KDF_HKDF_SHA256_BYTES_MAX,
223 crypto_kdf_hkdf_sha256_extract,
224 crypto_kdf_hkdf_sha256_expand
225);
226
227impl_hkdf_variant!(
228 HkdfSha512Variant,
229 CRYPTO_KDF_HKDF_SHA512_KEYBYTES,
230 HkdfSha512Prk,
231 CRYPTO_KDF_HKDF_SHA512_BYTES_MIN,
232 CRYPTO_KDF_HKDF_SHA512_BYTES_MAX,
233 crypto_kdf_hkdf_sha512_extract,
234 crypto_kdf_hkdf_sha512_expand
235);
236
237impl<Variant, Prk, const PRK_LENGTH: usize> Hkdf<Variant, Prk, PRK_LENGTH>
238where
239 Variant: HkdfVariant<PRK_LENGTH>,
240 Prk: NewByteArray<PRK_LENGTH> + Zeroize + ZeroizeOnDrop,
241{
242 pub fn generate() -> Self {
244 Self {
245 prk: Prk::generate(),
246 _variant: PhantomData,
247 }
248 }
249
250 #[deprecated(note = "use generate() instead")]
255 pub fn r#gen() -> Self {
256 Self::generate()
257 }
258
259 pub fn extract<Salt: Bytes + ?Sized, Ikm: Bytes + ?Sized>(
261 salt: Option<&Salt>,
262 ikm: &Ikm,
263 ) -> Self {
264 let mut prk = Prk::new_byte_array();
265 Variant::extract(
266 prk.as_mut_array(),
267 salt.map(|s| s.as_slice()),
268 ikm.as_slice(),
269 );
270 Self {
271 prk,
272 _variant: PhantomData,
273 }
274 }
275
276 pub fn extract_and_expand<
283 const OUTPUT_LENGTH: usize,
284 Salt: Bytes + ?Sized,
285 Ikm: Bytes + ?Sized,
286 Context: Bytes + ?Sized,
287 Output: NewByteArray<OUTPUT_LENGTH>,
288 >(
289 salt: Option<&Salt>,
290 ikm: &Ikm,
291 context: &Context,
292 ) -> Result<Output, Error> {
293 Self::extract(salt, ikm).expand(context)
294 }
295
296 pub fn extract_and_expand_to_vec<
303 Salt: Bytes + ?Sized,
304 Ikm: Bytes + ?Sized,
305 Context: Bytes + ?Sized,
306 >(
307 output_len: usize,
308 salt: Option<&Salt>,
309 ikm: &Ikm,
310 context: &Context,
311 ) -> Result<Vec<u8>, Error> {
312 Self::extract(salt, ikm).expand_to_vec(output_len, context)
313 }
314
315 pub fn extract_and_expand_to_bytes<
322 Salt: Bytes + ?Sized,
323 Ikm: Bytes + ?Sized,
324 Context: Bytes + ?Sized,
325 Output: NewBytes + ResizableBytes,
326 >(
327 output_len: usize,
328 salt: Option<&Salt>,
329 ikm: &Ikm,
330 context: &Context,
331 ) -> Result<Output, Error> {
332 Self::extract(salt, ikm).expand_to_bytes(output_len, context)
333 }
334}
335
336impl<Variant, Prk, const PRK_LENGTH: usize> Hkdf<Variant, Prk, PRK_LENGTH>
337where
338 Variant: HkdfVariant<PRK_LENGTH>,
339 Prk: ByteArray<PRK_LENGTH> + Zeroize + ZeroizeOnDrop,
340{
341 pub fn from_prk(prk: Prk) -> Self {
343 Self {
344 prk,
345 _variant: PhantomData,
346 }
347 }
348
349 pub fn into_prk(self) -> Prk {
351 self.prk
352 }
353
354 pub fn expand<const OUTPUT_LENGTH: usize, Context: Bytes + ?Sized, Output>(
361 &self,
362 context: &Context,
363 ) -> Result<Output, Error>
364 where
365 Output: NewByteArray<OUTPUT_LENGTH>,
366 {
367 Variant::validate_output_len(OUTPUT_LENGTH)?;
368 let mut output = Output::new_byte_array();
369 Variant::expand(
370 output.as_mut_slice(),
371 context.as_slice(),
372 self.prk.as_array(),
373 )?;
374 Ok(output)
375 }
376
377 pub fn expand_to_vec<Context: Bytes + ?Sized>(
384 &self,
385 output_len: usize,
386 context: &Context,
387 ) -> Result<Vec<u8>, Error> {
388 self.expand_to_bytes(output_len, context)
389 }
390
391 pub fn expand_to_bytes<Context: Bytes + ?Sized, Output: NewBytes + ResizableBytes>(
398 &self,
399 output_len: usize,
400 context: &Context,
401 ) -> Result<Output, Error> {
402 Variant::validate_output_len(output_len)?;
403 let mut output = Output::new_bytes();
404 output.resize(output_len, 0);
405 Variant::expand(
406 output.as_mut_slice(),
407 context.as_slice(),
408 self.prk.as_array(),
409 )?;
410 Ok(output)
411 }
412}
413
414impl<Variant, const PRK_LENGTH: usize> Hkdf<Variant, Variant::Prk, PRK_LENGTH>
415where
416 Variant: HkdfVariant<PRK_LENGTH>,
417{
418 pub fn generate_with_defaults() -> Self {
420 Self::generate()
421 }
422
423 #[deprecated(note = "use generate_with_defaults() instead")]
428 pub fn gen_with_defaults() -> Self {
429 Self::generate_with_defaults()
430 }
431}
432
433#[cfg(test)]
434mod tests {
435 use super::*;
436
437 #[test]
438 fn test_hkdf_sha256() {
439 let hkdf = HkdfSha256::extract(Some(b"salt"), b"input keying material");
440 let output: HkdfSha256Prk = hkdf.expand(b"context").expect("expand failed");
441 assert_eq!(output.len(), CRYPTO_KDF_HKDF_SHA256_KEYBYTES);
442
443 let output = hkdf.expand_to_vec(42, b"context").expect("expand failed");
444 assert_eq!(output.len(), 42);
445 }
446
447 #[test]
448 fn test_hkdf_sha512() {
449 let output: Vec<u8> =
450 HkdfSha512::extract_and_expand_to_vec(64, Some(b"salt"), b"ikm", b"context")
451 .expect("expand failed");
452 assert_eq!(output.len(), 64);
453 }
454
455 #[test]
456 fn test_hkdf_rejects_invalid_length() {
457 let hkdf = HkdfSha256::extract(None::<&[u8]>, b"ikm");
458 hkdf.expand_to_vec(
459 crate::constants::CRYPTO_KDF_HKDF_SHA256_BYTES_MAX + 1,
460 b"context",
461 )
462 .expect_err("oversized output should fail");
463 }
464
465 #[test]
466 fn test_hkdf_rejects_huge_length_before_allocation() {
467 let hkdf = HkdfSha256::extract(None::<&[u8]>, b"ikm");
468 hkdf.expand_to_vec(usize::MAX, b"context")
469 .expect_err("huge output should fail before allocation");
470 }
471
472 #[test]
473 fn test_hkdf_variant_generic_api() {
474 fn extract_and_expand_with_variant<Variant, const PRK_LENGTH: usize>(
475 salt: Option<&[u8]>,
476 ikm: &[u8],
477 context: &[u8],
478 ) -> Vec<u8>
479 where
480 Variant: HkdfVariant<PRK_LENGTH>,
481 {
482 Hkdf::<Variant, Variant::Prk, PRK_LENGTH>::extract_and_expand_to_vec(
483 42, salt, ikm, context,
484 )
485 .expect("expand failed")
486 }
487
488 let generic_output = extract_and_expand_with_variant::<
489 HkdfSha256Variant,
490 CRYPTO_KDF_HKDF_SHA256_KEYBYTES,
491 >(Some(b"salt"), b"input keying material", b"context");
492 let concrete_output = HkdfSha256::extract_and_expand_to_vec(
493 42,
494 Some(b"salt"),
495 b"input keying material",
496 b"context",
497 )
498 .expect("expand failed");
499
500 assert_eq!(generic_output, concrete_output);
501 }
502}