libsodium_rs/crypto_kdf.rs
1//! # Key Derivation Functions
2//!
3//! This module provides key derivation functions (KDFs) that can be used to derive
4//! multiple subkeys from a single master key. This is useful for deriving different
5//! keys for different purposes (e.g., encryption, authentication) from a single master key.
6//!
7//! ## What is a Key Derivation Function?
8//!
9//! A key derivation function (KDF) is a cryptographic algorithm that derives one or more
10//! secret keys from a master key. KDFs are designed to be computationally intensive and
11//! resistant to various cryptographic attacks, ensuring that derived keys maintain high
12//! security properties.
13//!
14//! ## Key Properties of KDFs
15//!
16//! - **Deterministic**: The same inputs always produce the same outputs
17//! - **Collision-resistant**: It's computationally infeasible to find two different inputs that produce the same output
18//! - **Pseudorandom**: Output keys appear random and are statistically independent from each other
19//! - **Domain separation**: Different subkey IDs produce independent keys
20//! - **Context binding**: Keys are bound to specific application contexts
21//!
22//! ## Available KDF Algorithms
23//!
24//! - **Default KDF**: Based on the BLAKE2b hash function (same as `blake2b` submodule)
25//! - **`blake2b`**: Explicitly uses the BLAKE2b hash function, which is optimized for 64-bit platforms and provides
26//! excellent security and performance
27//! - **`hkdf_sha256`**: HMAC-based Key Derivation Function using SHA-256, suitable for environments where
28//! BLAKE2b might not be available or when SHA-256 is specifically required
29//! - **`hkdf_sha512`**: HMAC-based Key Derivation Function using SHA-512, offering increased security
30//! margin at the cost of slightly lower performance
31//!
32//! ## Usage
33//!
34//! ```rust
35//! use libsodium_rs as sodium;
36//! use sodium::crypto_kdf;
37//! use sodium::ensure_init;
38//!
39//! // Initialize libsodium
40//! ensure_init().expect("Failed to initialize libsodium");
41//!
42//! // Generate a master key
43//! let master_key = crypto_kdf::Key::generate().unwrap();
44//!
45//! // Define a context for the key derivation
46//! // Must be exactly CONTEXTBYTES (8) bytes
47//! let context = b"MyAppV01"; // Application-specific context
48//!
49//! // Derive different subkeys for different purposes
50//! let encryption_key = crypto_kdf::derive_from_key(32, 1, context, &master_key).expect("Failed to derive encryption key");
51//! let authentication_key = crypto_kdf::derive_from_key(32, 2, context, &master_key).expect("Failed to derive authentication key");
52//! let database_key = crypto_kdf::derive_from_key(32, 3, context, &master_key).expect("Failed to derive database key");
53//!
54//! // Each subkey is unique and independent
55//! assert_ne!(encryption_key, authentication_key);
56//! assert_ne!(encryption_key, database_key);
57//! assert_ne!(authentication_key, database_key);
58//! ```
59//!
60//! ## Security Considerations
61//!
62//! - **Master Key Protection**: The master key should be kept secret and securely stored.
63//! Compromise of the master key compromises all derived subkeys.
64//!
65//! - **Subkey ID Uniqueness**: Each subkey ID should be unique within the application.
66//! Reusing IDs with the same master key and context will produce identical subkeys.
67//!
68//! - **Context Uniqueness**: The context should be unique to the application to prevent
69//! key reuse across applications. This provides domain separation between different
70//! applications or protocol versions.
71//!
72//! - **Subkey Independence**: Subkeys derived from the same master key with different IDs
73//! are cryptographically independent. Knowledge of one subkey does not reveal information
74//! about other subkeys or the master key.
75//!
76//! - **Algorithm Selection**: The default BLAKE2b-based KDF is suitable for most applications,
77//! but specialized KDFs are available in the submodules for specific requirements or
78//! compliance needs.
79//!
80//! - **Deterministic Derivation**: The KDF is deterministic - the same inputs will always
81//! produce the same subkey. This allows keys to be rederived when needed rather than stored.
82//!
83//! - **Attack Resistance**: The BLAKE2b-based KDF is resistant to length extension attacks
84//! and other common cryptographic vulnerabilities.
85//!
86//! - **Key Length Selection**: The subkey length should be appropriate for the intended use
87//! (e.g., AES-256 keys should be 32 bytes, Ed25519 keys should be 32 bytes).
88//!
89//! - **Forward Secrecy**: KDFs do not provide forward secrecy by themselves. If this property
90//! is required, consider using additional key exchange protocols.
91
92use crate::{Result, SodiumError};
93use libc;
94
95/// Minimum number of bytes in a derived subkey (16)
96///
97/// This is the minimum length of a subkey that can be derived using the KDF.
98pub const BYTES_MIN: usize = libsodium_sys::crypto_kdf_BYTES_MIN as usize;
99
100/// Maximum number of bytes in a derived subkey (64)
101///
102/// This is the maximum length of a subkey that can be derived using the KDF.
103pub const BYTES_MAX: usize = libsodium_sys::crypto_kdf_BYTES_MAX as usize;
104
105/// Number of bytes in a context (8)
106///
107/// The context is a fixed-size bytes that should be unique to the application.
108/// It helps ensure that subkeys derived in different contexts are independent.
109pub const CONTEXTBYTES: usize = libsodium_sys::crypto_kdf_CONTEXTBYTES as usize;
110
111/// Number of bytes in a master key (32)
112///
113/// The master key is used to derive subkeys and should be kept secret.
114pub const KEYBYTES: usize = libsodium_sys::crypto_kdf_KEYBYTES as usize;
115
116/// A master key for the key derivation function
117///
118/// This key is used to derive multiple subkeys for different purposes.
119/// It must be exactly 32 bytes (256 bits) long and should be generated
120/// using a cryptographically secure random number generator.
121///
122/// ## Security Properties
123///
124/// - **High entropy**: Contains 256 bits of entropy when generated properly
125/// - **Secret**: Must be kept confidential to maintain security of all derived subkeys
126/// - **Single source of truth**: Allows multiple application-specific keys to be derived
127/// from a single master key
128///
129/// ## Best Practices
130///
131/// - Generate using the `generate()` method, which uses libsodium's secure RNG
132/// - Store securely, preferably in a hardware security module or secure enclave
133/// - Rotate periodically according to your security policy
134/// - Consider using a key management service for production applications
135///
136/// ## Example
137///
138/// ```rust
139/// use libsodium_rs as sodium;
140/// use sodium::crypto_kdf;
141/// use sodium::ensure_init;
142///
143/// // Initialize libsodium
144/// ensure_init().expect("Failed to initialize libsodium");
145///
146/// // Generate a master key
147/// let master_key = crypto_kdf::Key::generate().unwrap();
148///
149/// // Or create from existing bytes
150/// let key_bytes = [0x42; crypto_kdf::KEYBYTES]; // Example bytes
151/// let master_key = crypto_kdf::Key::from_slice(&key_bytes).unwrap();
152/// ```
153#[derive(Debug, Clone, Eq, PartialEq, zeroize::Zeroize, zeroize::ZeroizeOnDrop)]
154pub struct Key([u8; KEYBYTES]);
155
156impl AsRef<[u8]> for Key {
157 fn as_ref(&self) -> &[u8] {
158 &self.0
159 }
160}
161
162impl TryFrom<&[u8]> for Key {
163 type Error = crate::SodiumError;
164
165 fn try_from(slice: &[u8]) -> std::result::Result<Self, Self::Error> {
166 if slice.len() != KEYBYTES {
167 return Err(SodiumError::InvalidInput(format!(
168 "key must be exactly {KEYBYTES} bytes"
169 )));
170 }
171 let mut key = [0u8; KEYBYTES];
172 key.copy_from_slice(slice);
173 Ok(Key(key))
174 }
175}
176
177impl From<[u8; KEYBYTES]> for Key {
178 fn from(bytes: [u8; KEYBYTES]) -> Self {
179 Key(bytes)
180 }
181}
182
183impl From<Key> for [u8; KEYBYTES] {
184 fn from(key: Key) -> Self {
185 key.0
186 }
187}
188
189impl Key {
190 /// Generates a new random key for key derivation
191 ///
192 /// This function generates a new random master key suitable for deriving subkeys.
193 /// The key is generated using libsodium's secure random number generator.
194 ///
195 /// ## Example
196 ///
197 /// ```rust
198 /// use libsodium_rs as sodium;
199 /// use sodium::crypto_kdf;
200 /// use sodium::ensure_init;
201 ///
202 /// // Initialize libsodium
203 /// ensure_init().expect("Failed to initialize libsodium");
204 ///
205 /// // Generate a master key
206 /// let master_key = crypto_kdf::Key::generate().unwrap();
207 /// ```
208 ///
209 /// ## Returns
210 ///
211 /// * `Result<Self>` - A new randomly generated key or an error
212 pub fn generate() -> Result<Self> {
213 let mut key = [0u8; KEYBYTES];
214 unsafe {
215 libsodium_sys::crypto_kdf_keygen(key.as_mut_ptr());
216 }
217 Ok(Key(key))
218 }
219
220 /// Creates a key from an existing byte slice
221 ///
222 /// This function creates a master key from an existing byte slice.
223 /// The slice must be exactly `KEYBYTES` (32) bytes long.
224 ///
225 /// ## Example
226 ///
227 /// ```rust
228 /// use libsodium_rs as sodium;
229 /// use sodium::crypto_kdf;
230 /// use sodium::ensure_init;
231 ///
232 /// // Initialize libsodium
233 /// ensure_init().expect("Failed to initialize libsodium");
234 ///
235 /// // Create a key from existing bytes
236 /// let key_bytes = [0x42; crypto_kdf::KEYBYTES]; // Example bytes
237 /// let master_key = crypto_kdf::Key::from_slice(&key_bytes).unwrap();
238 /// ```
239 ///
240 /// ## Arguments
241 ///
242 /// * `slice` - Byte slice of exactly `KEYBYTES` length
243 ///
244 /// ## Returns
245 ///
246 /// * `Result<Self>` - A new key created from the slice or an error
247 ///
248 /// ## Errors
249 ///
250 /// Returns an error if the slice is not exactly `KEYBYTES` bytes long
251 pub fn from_slice(slice: &[u8]) -> Result<Self> {
252 if slice.len() != KEYBYTES {
253 return Err(SodiumError::InvalidInput(format!(
254 "key must be exactly {KEYBYTES} bytes"
255 )));
256 }
257 let mut key = [0u8; KEYBYTES];
258 key.copy_from_slice(slice);
259 Ok(Key(key))
260 }
261
262 /// Returns a reference to the key as a byte slice
263 ///
264 /// This function returns a reference to the internal byte representation of the key.
265 /// This is useful when you need to pass the key to other functions.
266 ///
267 /// ## Example
268 ///
269 /// ```rust
270 /// use libsodium_rs as sodium;
271 /// use sodium::crypto_kdf;
272 /// use sodium::ensure_init;
273 ///
274 /// // Initialize libsodium
275 /// ensure_init().expect("Failed to initialize libsodium");
276 ///
277 /// // Generate a master key
278 /// let master_key = crypto_kdf::Key::generate().unwrap();
279 ///
280 /// // Get the bytes of the key
281 /// let key_bytes = master_key.as_bytes();
282 /// assert_eq!(key_bytes.len(), crypto_kdf::KEYBYTES);
283 /// ```
284 ///
285 /// ## Returns
286 ///
287 /// * `&[u8]` - Reference to the key bytes
288 pub fn as_bytes(&self) -> &[u8] {
289 &self.0
290 }
291}
292
293/// Derives a subkey from a master key
294///
295/// This function derives a subkey of the specified length from a master key.
296/// The subkey is derived using the BLAKE2b hash function with the master key,
297/// subkey ID, and context as inputs.
298///
299/// ## Algorithm Details
300///
301/// The derivation process uses BLAKE2b-512 with the following inputs:
302/// - The master key is used as the BLAKE2b key
303/// - The subkey ID is encoded as a 64-bit little-endian integer
304/// - The context is a fixed-size 8-byte string
305/// - The output is truncated to the requested subkey length
306///
307/// This construction ensures that subkeys derived with different IDs or contexts
308/// are independent, even if derived from the same master key.
309///
310/// The derivation process is deterministic - the same inputs will always produce
311/// the same subkey. This allows applications to regenerate the same subkeys
312/// as long as they have access to the master key.
313///
314/// ## Algorithm Details
315///
316/// The subkey derivation works as follows:
317/// 1. Initialize a BLAKE2b hash state with the master key
318/// 2. Update the hash state with the context and subkey ID
319/// 3. Finalize the hash to produce the subkey
320///
321/// ## Security Considerations
322///
323/// - Each subkey ID should be unique within the application
324/// - The context should be unique to the application to prevent key reuse
325/// - Subkeys derived with different IDs are independent
326/// - The subkey length must be between `BYTES_MIN` and `BYTES_MAX`
327///
328/// ## Example
329///
330/// ```rust
331/// use libsodium_rs as sodium;
332/// use sodium::crypto_kdf;
333/// use sodium::ensure_init;
334///
335/// // Initialize libsodium
336/// ensure_init().expect("Failed to initialize libsodium");
337///
338/// // Generate a master key
339/// let master_key = crypto_kdf::Key::generate().unwrap();
340///
341/// // Define a context for the key derivation
342/// let context = b"MyAppV01"; // Must be exactly CONTEXTBYTES (8) bytes
343///
344/// // Derive different subkeys for different purposes
345/// let encryption_key = crypto_kdf::derive_from_key(32, 1, context, &master_key).expect("Failed to derive encryption key");
346/// let authentication_key = crypto_kdf::derive_from_key(32, 2, context, &master_key).expect("Failed to derive authentication key");
347///
348/// // Each subkey is unique and independent
349/// assert_ne!(encryption_key, authentication_key);
350/// ```
351///
352/// # Arguments
353///
354/// * `subkey_len` - Length of the subkey to derive (must be between `BYTES_MIN` and `BYTES_MAX`)
355/// * `subkey_id` - Identifier for the subkey (should be unique for each purpose)
356/// This is a 64-bit integer that distinguishes different subkeys derived from the same master key
357/// * `context` - Application-specific context (must be exactly `CONTEXTBYTES` bytes)
358/// This is an 8-byte identifier that should be unique to your application to prevent key reuse
359/// * `master_key` - Master key used to derive the subkey from
360///
361/// ## Returns
362///
363/// * `Result<Vec<u8>>` - The derived subkey or an error
364///
365pub fn derive_from_key(
366 subkey_len: usize,
367 subkey_id: u64,
368 context: &[u8],
369 master_key: &Key,
370) -> Result<Vec<u8>> {
371 if !(BYTES_MIN..=BYTES_MAX).contains(&subkey_len) {
372 return Err(SodiumError::InvalidInput(format!(
373 "subkey length must be between {BYTES_MIN} and {BYTES_MAX}"
374 )));
375 }
376
377 if context.len() != CONTEXTBYTES {
378 return Err(SodiumError::InvalidInput(format!(
379 "context must be exactly {CONTEXTBYTES} bytes"
380 )));
381 }
382
383 let mut subkey = vec![0u8; subkey_len];
384
385 let result = unsafe {
386 libsodium_sys::crypto_kdf_derive_from_key(
387 subkey.as_mut_ptr(),
388 subkey_len as libc::size_t,
389 subkey_id,
390 context.as_ptr() as *const std::os::raw::c_char,
391 master_key.as_bytes().as_ptr(),
392 )
393 };
394 if result != 0 {
395 return Err(SodiumError::OperationError("key derivation failed".into()));
396 }
397
398 Ok(subkey)
399}
400
401/// Key derivation functions based on the BLAKE2b hash function
402///
403/// This submodule provides key derivation functions that explicitly use the BLAKE2b
404/// hash function. The default KDF in the parent module also uses BLAKE2b, so this
405/// submodule is provided for explicitness and clarity.
406///
407/// ## About BLAKE2b
408///
409/// BLAKE2b is a cryptographic hash function optimized for 64-bit platforms that offers
410/// excellent security and performance. It was designed as a replacement for MD5 and SHA-1,
411/// and has several advantages over SHA-2 and SHA-3:
412///
413/// - Faster than MD5, SHA-1, SHA-2, and SHA-3 on modern 64-bit platforms
414/// - Resistant to length extension attacks
415/// - Supports keyed mode (BLAKE2b can be used as a MAC)
416/// - Supports personalization strings and salts
417/// - Simple design with thorough security analysis
418///
419/// ## When to Use This Module
420///
421/// Use this module when you want to be explicit about which KDF algorithm you're using,
422/// or when you need to document that your application specifically relies on BLAKE2b
423/// properties. Functionally, it's identical to the parent module's KDF.
424///
425/// BLAKE2b is a cryptographically secure hash function that is designed to be
426/// fast and resistant to various cryptographic attacks. It is suitable for
427/// deriving keys for a wide range of applications.
428///
429/// ## Example
430///
431/// ```rust
432/// use libsodium_rs as sodium;
433/// use sodium::crypto_kdf::blake2b;
434/// use sodium::ensure_init;
435///
436/// // Initialize libsodium
437/// ensure_init().expect("Failed to initialize libsodium");
438///
439/// // Generate a master key
440/// let master_key = blake2b::Key::generate().unwrap();
441///
442/// // Define a context for the key derivation
443/// let context = b"MyAppV01"; // Must be exactly CONTEXTBYTES (8) bytes
444///
445/// // Derive a subkey
446/// let subkey = blake2b::derive_from_key(32, 1, context, &master_key).unwrap();
447/// ```
448pub mod blake2b {
449 use super::*;
450
451 /// Minimum number of bytes in a derived subkey (16)
452 pub const BYTES_MIN: usize = libsodium_sys::crypto_kdf_blake2b_BYTES_MIN as usize;
453 /// Maximum number of bytes in a derived subkey (64)
454 pub const BYTES_MAX: usize = libsodium_sys::crypto_kdf_blake2b_BYTES_MAX as usize;
455 /// Number of bytes in a context (8)
456 pub const CONTEXTBYTES: usize = libsodium_sys::crypto_kdf_blake2b_CONTEXTBYTES as usize;
457 /// Number of bytes in a master key (32)
458 pub const KEYBYTES: usize = libsodium_sys::crypto_kdf_blake2b_KEYBYTES as usize;
459
460 /// A master key for the BLAKE2b key derivation function
461 ///
462 /// This key is used to derive multiple subkeys using the BLAKE2b hash function.
463 /// It should be generated using a secure random number generator and kept secret.
464 ///
465 /// ## Example
466 ///
467 /// ```rust
468 /// use libsodium_rs as sodium;
469 /// use sodium::crypto_kdf::blake2b;
470 /// use sodium::ensure_init;
471 ///
472 /// // Initialize libsodium
473 /// ensure_init().expect("Failed to initialize libsodium");
474 ///
475 /// // Generate a master key
476 /// let master_key = blake2b::Key::generate().unwrap();
477 /// ```
478 #[derive(Debug, Clone, Eq, PartialEq, zeroize::Zeroize, zeroize::ZeroizeOnDrop)]
479 pub struct Key([u8; KEYBYTES]);
480
481 impl AsRef<[u8]> for Key {
482 fn as_ref(&self) -> &[u8] {
483 &self.0
484 }
485 }
486
487 impl TryFrom<&[u8]> for Key {
488 type Error = crate::SodiumError;
489
490 fn try_from(slice: &[u8]) -> std::result::Result<Self, Self::Error> {
491 if slice.len() != KEYBYTES {
492 return Err(SodiumError::InvalidInput(format!(
493 "key must be exactly {KEYBYTES} bytes"
494 )));
495 }
496 let mut key = [0u8; KEYBYTES];
497 key.copy_from_slice(slice);
498 Ok(Key(key))
499 }
500 }
501
502 impl From<[u8; KEYBYTES]> for Key {
503 fn from(bytes: [u8; KEYBYTES]) -> Self {
504 Key(bytes)
505 }
506 }
507
508 impl From<Key> for [u8; KEYBYTES] {
509 fn from(key: Key) -> Self {
510 key.0
511 }
512 }
513
514 impl Key {
515 /// Generates a new random key
516 pub fn generate() -> Result<Self> {
517 let mut key = [0u8; KEYBYTES];
518 unsafe {
519 // Use the generic keygen function as the specific one isn't available
520 libsodium_sys::crypto_kdf_keygen(key.as_mut_ptr());
521 }
522 Ok(Key(key))
523 }
524
525 /// Creates a key from a byte slice
526 pub fn from_slice(slice: &[u8]) -> Result<Self> {
527 if slice.len() != KEYBYTES {
528 return Err(SodiumError::InvalidInput(format!(
529 "key must be exactly {KEYBYTES} bytes"
530 )));
531 }
532 let mut key = [0u8; KEYBYTES];
533 key.copy_from_slice(slice);
534 Ok(Key(key))
535 }
536
537 /// Returns a reference to the key as a byte slice
538 pub fn as_bytes(&self) -> &[u8] {
539 &self.0
540 }
541 }
542
543 /// Derives a subkey from a master key using BLAKE2b
544 ///
545 /// This function derives a subkey of the specified length from a master key
546 /// using the BLAKE2b hash function. The subkey is derived using the master key,
547 /// subkey ID, and context as inputs.
548 ///
549 /// ## Algorithm Details
550 ///
551 /// The subkey derivation works as follows:
552 /// 1. Initialize a BLAKE2b hash state with the master key
553 /// 2. Update the hash state with the context and subkey ID
554 /// 3. Finalize the hash to produce the subkey
555 ///
556 /// ## Security Considerations
557 ///
558 /// - Each subkey ID should be unique within the application
559 /// - The context should be unique to the application to prevent key reuse
560 /// - Subkeys derived with different IDs are independent
561 /// - The subkey length must be between `BYTES_MIN` and `BYTES_MAX`
562 ///
563 /// ## Example
564 ///
565 /// ```rust
566 /// use libsodium_rs as sodium;
567 /// use sodium::crypto_kdf::blake2b;
568 /// use sodium::ensure_init;
569 ///
570 /// // Initialize libsodium
571 /// ensure_init().expect("Failed to initialize libsodium");
572 ///
573 /// // Generate a master key
574 /// let master_key = blake2b::Key::generate().unwrap();
575 ///
576 /// // Define a context for the key derivation
577 /// let context = b"MyAppV01"; // Must be exactly CONTEXTBYTES (8) bytes
578 ///
579 /// // Derive different subkeys for different purposes
580 /// let encryption_key = blake2b::derive_from_key(32, 1, context, &master_key).unwrap();
581 /// let authentication_key = blake2b::derive_from_key(32, 2, context, &master_key).unwrap();
582 ///
583 /// // Each subkey is unique and independent
584 /// assert_ne!(encryption_key, authentication_key);
585 /// ```
586 ///
587 /// # Arguments
588 ///
589 /// * `subkey_len` - Length of the subkey to derive (must be between `BYTES_MIN` and `BYTES_MAX`)
590 /// * `subkey_id` - Identifier for the subkey (should be unique for each purpose)
591 /// This is a 64-bit integer that distinguishes different subkeys derived from the same master key
592 /// * `context` - Application-specific context (must be exactly `CONTEXTBYTES` bytes)
593 /// This is an 8-byte identifier that should be unique to your application to prevent key reuse
594 /// * `master_key` - Master key used to derive the subkey from
595 ///
596 /// ## Returns
597 ///
598 /// * `Result<Vec<u8>>` - The derived subkey or an error
599 ///
600 pub fn derive_from_key(
601 subkey_len: usize,
602 subkey_id: u64,
603 context: &[u8],
604 master_key: &Key,
605 ) -> Result<Vec<u8>> {
606 if !(BYTES_MIN..=BYTES_MAX).contains(&subkey_len) {
607 return Err(SodiumError::InvalidInput(format!(
608 "subkey length must be between {BYTES_MIN} and {BYTES_MAX} bytes"
609 )));
610 }
611
612 if context.len() != CONTEXTBYTES {
613 return Err(SodiumError::InvalidInput(format!(
614 "context must be exactly {CONTEXTBYTES} bytes"
615 )));
616 }
617
618 let mut subkey = vec![0u8; subkey_len];
619 let result = unsafe {
620 libsodium_sys::crypto_kdf_blake2b_derive_from_key(
621 subkey.as_mut_ptr(),
622 subkey_len as libc::size_t,
623 subkey_id,
624 context.as_ptr() as *const std::os::raw::c_char,
625 master_key.as_bytes().as_ptr(),
626 )
627 };
628
629 if result != 0 {
630 return Err(SodiumError::OperationError("key derivation failed".into()));
631 }
632
633 Ok(subkey)
634 }
635}
636
637/// HMAC-based Key Derivation Function (HKDF)
638///
639/// This module provides an implementation of HKDF as defined in RFC 5869,
640/// which is a key derivation function based on HMAC. It can be used to derive
641/// multiple keys from a single input key material.
642///
643/// HKDF is a two-step process:
644/// 1. Extract: Derive a pseudorandom key (PRK) from the input key material and salt
645/// 2. Expand: Expand the PRK to the desired output length using the info parameter
646///
647/// This module provides implementations for both SHA-256 and SHA-512 variants of HKDF.
648///
649/// ## Example
650///
651/// ```rust
652/// use libsodium_rs::crypto_kdf::hkdf;
653///
654/// // Generate a random PRK
655/// let prk = hkdf::sha256::keygen();
656///
657/// // Input key material and salt
658/// let ikm = b"input key material";
659/// let salt = Some(b"salt".as_ref());
660///
661/// // Extract a pseudorandom key
662/// let prk = hkdf::sha256::extract(salt, ikm).unwrap();
663///
664/// // Expand the key with a context
665/// let context = Some(b"context info".as_ref());
666/// let out_len = 32;
667/// let output = hkdf::sha256::expand(out_len, context, &prk).unwrap();
668/// ```
669pub mod hkdf {
670 use super::*;
671
672 /// HMAC-based Key Derivation Function using SHA-256
673 ///
674 /// This submodule provides key derivation functions based on HKDF with SHA-256.
675 ///
676 /// HKDF-SHA256 is suitable for deriving keys from input key material with
677 /// cryptographic strength. The extract function produces a pseudorandom key of
678 /// 32 bytes, which can then be expanded to any desired length.
679 pub mod sha256 {
680 use super::*;
681
682 /// Maximum number of bytes in a derived subkey (255 * 32 = 8160)
683 pub const BYTES_MAX: usize = libsodium_sys::crypto_kdf_hkdf_sha256_BYTES_MAX as usize;
684 /// Minimum number of bytes in a derived subkey (0)
685 pub const BYTES_MIN: usize = libsodium_sys::crypto_kdf_hkdf_sha256_BYTES_MIN as usize;
686 /// Number of bytes in the PRK (32)
687 pub const KEYBYTES: usize = libsodium_sys::crypto_kdf_hkdf_sha256_KEYBYTES as usize;
688
689 /// A pseudorandom key (PRK) for HKDF-SHA-256
690 ///
691 /// This structure represents the output of the extract step of HKDF-SHA-256.
692 /// It can be used as input to the expand step to derive multiple keys.
693 #[derive(Debug, Clone, Eq, PartialEq, zeroize::Zeroize, zeroize::ZeroizeOnDrop)]
694 pub struct Prk([u8; KEYBYTES]);
695
696 impl AsRef<[u8]> for Prk {
697 fn as_ref(&self) -> &[u8] {
698 &self.0
699 }
700 }
701
702 impl TryFrom<&[u8]> for Prk {
703 type Error = crate::SodiumError;
704
705 fn try_from(slice: &[u8]) -> std::result::Result<Self, Self::Error> {
706 if slice.len() != KEYBYTES {
707 return Err(SodiumError::InvalidInput(format!(
708 "PRK must be exactly {KEYBYTES} bytes"
709 )));
710 }
711 let mut prk = [0u8; KEYBYTES];
712 prk.copy_from_slice(slice);
713 Ok(Prk(prk))
714 }
715 }
716
717 impl From<[u8; KEYBYTES]> for Prk {
718 fn from(bytes: [u8; KEYBYTES]) -> Self {
719 Prk(bytes)
720 }
721 }
722
723 impl From<Prk> for [u8; KEYBYTES] {
724 fn from(prk: Prk) -> Self {
725 prk.0
726 }
727 }
728
729 impl Prk {
730 /// Returns a reference to the PRK as a byte slice
731 ///
732 /// # Returns
733 ///
734 /// * `&[u8; KEYBYTES]` - Reference to the PRK bytes
735 pub fn as_bytes(&self) -> &[u8; KEYBYTES] {
736 &self.0
737 }
738
739 /// Creates a PRK from a byte slice
740 ///
741 /// # Arguments
742 ///
743 /// * `slice` - Byte slice to create the PRK from
744 ///
745 /// # Returns
746 ///
747 /// * `Result<Self>` - A new PRK created from the slice or an error
748 ///
749 /// # Errors
750 ///
751 /// Returns an error if the slice is not exactly `KEYBYTES` bytes long
752 pub fn from_slice(slice: &[u8]) -> Result<Self> {
753 if slice.len() != KEYBYTES {
754 return Err(SodiumError::InvalidInput(format!(
755 "PRK must be exactly {KEYBYTES} bytes"
756 )));
757 }
758
759 let mut prk = [0u8; KEYBYTES];
760 prk.copy_from_slice(slice);
761 Ok(Self(prk))
762 }
763 }
764 /// Size of the state structure in bytes
765 pub fn statebytes() -> usize {
766 unsafe { libsodium_sys::crypto_kdf_hkdf_sha256_statebytes() as usize }
767 }
768
769 /// Returns the number of bytes in the PRK (32)
770 pub fn keybytes() -> usize {
771 unsafe { libsodium_sys::crypto_kdf_hkdf_sha256_keybytes() as usize }
772 }
773
774 /// Returns the minimum number of bytes in a derived subkey (0)
775 pub fn bytes_min() -> usize {
776 unsafe { libsodium_sys::crypto_kdf_hkdf_sha256_bytes_min() as usize }
777 }
778
779 /// Returns the maximum number of bytes in a derived subkey (255 * 32 = 8160)
780 pub fn bytes_max() -> usize {
781 unsafe { libsodium_sys::crypto_kdf_hkdf_sha256_bytes_max() as usize }
782 }
783
784 /// Extract a pseudorandom key from the input key material and salt
785 ///
786 /// This function performs the extract step of HKDF-SHA-256, which derives a
787 /// pseudorandom key (PRK) from the input key material and salt.
788 ///
789 /// # Arguments
790 ///
791 /// * `salt` - Optional salt value (can be None)
792 /// * `ikm` - Input key material
793 ///
794 /// # Returns
795 ///
796 /// * `Result<Prk>` - The pseudorandom key or an error
797 ///
798 /// # Errors
799 ///
800 /// Returns an error if the extract operation fails
801 pub fn extract(salt: Option<&[u8]>, ikm: &[u8]) -> Result<Prk> {
802 let mut prk = [0u8; KEYBYTES];
803
804 let salt_ptr = match salt {
805 Some(s) => s.as_ptr(),
806 None => std::ptr::null(),
807 };
808 let salt_len = salt.map_or(0, |s| s.len());
809
810 let result = unsafe {
811 libsodium_sys::crypto_kdf_hkdf_sha256_extract(
812 prk.as_mut_ptr(),
813 salt_ptr,
814 salt_len as libc::size_t,
815 ikm.as_ptr(),
816 ikm.len() as libc::size_t,
817 )
818 };
819
820 if result != 0 {
821 return Err(SodiumError::OperationError(
822 "HKDF-SHA-256 extract phase failed".into(),
823 ));
824 }
825
826 Ok(Prk(prk))
827 }
828
829 /// Generate a random pseudorandom key
830 ///
831 /// This function generates a random pseudorandom key for use with HKDF-SHA-256.
832 ///
833 /// # Returns
834 ///
835 /// * `Prk` - A random pseudorandom key
836 pub fn keygen() -> Prk {
837 let mut prk = [0u8; KEYBYTES];
838 unsafe {
839 libsodium_sys::crypto_kdf_hkdf_sha256_keygen(prk.as_mut_ptr());
840 }
841 Prk(prk)
842 }
843
844 /// Expand the pseudorandom key to the desired output length using the context
845 ///
846 /// This function performs the expand step of HKDF-SHA-256, which expands the
847 /// pseudorandom key to the desired output length using the context.
848 ///
849 /// # Arguments
850 ///
851 /// * `out_len` - Length of the output to generate (must be at most `BYTES_MAX`)
852 /// * `ctx` - Optional context (can be None)
853 /// * `prk` - Pseudorandom key from the extract step
854 ///
855 /// # Returns
856 ///
857 /// * `Result<Vec<u8>>` - The expanded output or an error
858 ///
859 /// # Errors
860 ///
861 /// Returns an error if:
862 /// * `out_len` is greater than `BYTES_MAX`
863 /// * The expand operation fails
864 pub fn expand(out_len: usize, ctx: Option<&[u8]>, prk: &Prk) -> Result<Vec<u8>> {
865 if out_len > BYTES_MAX {
866 return Err(SodiumError::InvalidInput(format!(
867 "output length must be at most {BYTES_MAX} bytes"
868 )));
869 }
870
871 let mut out = vec![0u8; out_len];
872
873 let ctx_ptr = match ctx {
874 Some(c) => c.as_ptr() as *const std::os::raw::c_char,
875 None => std::ptr::null(),
876 };
877 let ctx_len = ctx.map_or(0, |c| c.len());
878
879 let result = unsafe {
880 libsodium_sys::crypto_kdf_hkdf_sha256_expand(
881 out.as_mut_ptr(),
882 out_len as libc::size_t,
883 ctx_ptr,
884 ctx_len as libc::size_t,
885 prk.as_bytes().as_ptr(),
886 )
887 };
888
889 if result != 0 {
890 return Err(SodiumError::OperationError(
891 "HKDF-SHA-256 expand phase failed".into(),
892 ));
893 }
894
895 Ok(out)
896 }
897
898 /// State structure for incremental HKDF-SHA-256 extraction
899 pub struct State {
900 state: Box<libsodium_sys::crypto_kdf_hkdf_sha256_state>,
901 }
902
903 impl Default for State {
904 fn default() -> Self {
905 Self::new()
906 }
907 }
908
909 impl State {
910 /// Create a new state for incremental HKDF-SHA-256 extraction
911 pub fn new() -> Self {
912 let state = unsafe {
913 let layout = std::alloc::Layout::from_size_align(statebytes(), 8)
914 .expect("Invalid layout for crypto_kdf_hkdf_sha256_state");
915 let ptr = std::alloc::alloc_zeroed(layout)
916 as *mut libsodium_sys::crypto_kdf_hkdf_sha256_state;
917 Box::from_raw(ptr)
918 };
919 Self { state }
920 }
921
922 /// Initialize the state with the salt
923 ///
924 /// # Arguments
925 ///
926 /// * `salt` - Optional salt value (can be None)
927 ///
928 /// # Returns
929 ///
930 /// * `Result<()>` - Success or an error
931 pub fn extract_init(&mut self, salt: Option<&[u8]>) -> Result<()> {
932 let salt_ptr = match salt {
933 Some(s) => s.as_ptr(),
934 None => std::ptr::null(),
935 };
936 let salt_len = salt.map_or(0, |s| s.len());
937
938 let result = unsafe {
939 libsodium_sys::crypto_kdf_hkdf_sha256_extract_init(
940 self.state.as_mut(),
941 salt_ptr,
942 salt_len as libc::size_t,
943 )
944 };
945
946 if result != 0 {
947 return Err(SodiumError::OperationError(
948 "HKDF-SHA-256 extract init failed".into(),
949 ));
950 }
951
952 Ok(())
953 }
954
955 /// Update the state with input key material
956 ///
957 /// # Arguments
958 ///
959 /// * `ikm` - Input key material
960 ///
961 /// # Returns
962 ///
963 /// * `Result<()>` - Success or an error
964 pub fn extract_update(&mut self, ikm: &[u8]) -> Result<()> {
965 let result = unsafe {
966 libsodium_sys::crypto_kdf_hkdf_sha256_extract_update(
967 self.state.as_mut(),
968 ikm.as_ptr(),
969 ikm.len() as libc::size_t,
970 )
971 };
972
973 if result != 0 {
974 return Err(SodiumError::OperationError(
975 "HKDF-SHA-256 extract update failed".into(),
976 ));
977 }
978
979 Ok(())
980 }
981
982 /// Finalize the extraction and get the pseudorandom key
983 ///
984 /// # Returns
985 ///
986 /// * `Result<Prk>` - The pseudorandom key or an error
987 pub fn extract_final(&mut self) -> Result<Prk> {
988 let mut prk = [0u8; KEYBYTES];
989 let result = unsafe {
990 libsodium_sys::crypto_kdf_hkdf_sha256_extract_final(
991 self.state.as_mut(),
992 prk.as_mut_ptr(),
993 )
994 };
995
996 if result != 0 {
997 return Err(SodiumError::OperationError(
998 "HKDF-SHA-256 extract final failed".into(),
999 ));
1000 }
1001
1002 Ok(Prk(prk))
1003 }
1004 }
1005
1006 impl Drop for State {
1007 fn drop(&mut self) {
1008 unsafe {
1009 let ptr = Box::into_raw(std::mem::replace(
1010 &mut self.state,
1011 Box::new(std::mem::zeroed()),
1012 ));
1013 let layout = std::alloc::Layout::from_size_align(statebytes(), 8)
1014 .expect("Invalid layout for crypto_kdf_hkdf_sha256_state");
1015 std::alloc::dealloc(ptr as *mut u8, layout);
1016 }
1017 }
1018 }
1019
1020 impl zeroize::Zeroize for State {
1021 fn zeroize(&mut self) {
1022 unsafe {
1023 // Zero out the state memory
1024 std::ptr::write_bytes(
1025 self.state.as_mut() as *mut _ as *mut u8,
1026 0,
1027 statebytes(),
1028 );
1029 }
1030 }
1031 }
1032 }
1033
1034 /// HMAC-based Key Derivation Function using SHA-512
1035 ///
1036 /// This submodule provides key derivation functions based on HKDF with SHA-512.
1037 ///
1038 /// HKDF-SHA512 provides a higher security margin than HKDF-SHA256, producing a
1039 /// pseudorandom key of 64 bytes, which can then be expanded to any desired length.
1040 /// This variant is recommended for applications requiring the highest security level.
1041 pub mod sha512 {
1042 use super::*;
1043
1044 /// Maximum number of bytes in a derived subkey (255 * 64 = 16320)
1045 pub const BYTES_MAX: usize = libsodium_sys::crypto_kdf_hkdf_sha512_BYTES_MAX as usize;
1046 /// Minimum number of bytes in a derived subkey (0)
1047 pub const BYTES_MIN: usize = libsodium_sys::crypto_kdf_hkdf_sha512_BYTES_MIN as usize;
1048 /// Number of bytes in the PRK (64)
1049 pub const KEYBYTES: usize = libsodium_sys::crypto_kdf_hkdf_sha512_KEYBYTES as usize;
1050
1051 /// A pseudorandom key (PRK) for HKDF-SHA-512
1052 ///
1053 /// This structure represents the output of the extract step of HKDF-SHA-512.
1054 /// It can be used as input to the expand step to derive multiple keys.
1055 #[derive(Debug, Clone, Eq, PartialEq, zeroize::Zeroize, zeroize::ZeroizeOnDrop)]
1056 pub struct Prk([u8; KEYBYTES]);
1057
1058 impl AsRef<[u8]> for Prk {
1059 fn as_ref(&self) -> &[u8] {
1060 &self.0
1061 }
1062 }
1063
1064 impl TryFrom<&[u8]> for Prk {
1065 type Error = crate::SodiumError;
1066
1067 fn try_from(slice: &[u8]) -> std::result::Result<Self, Self::Error> {
1068 if slice.len() != KEYBYTES {
1069 return Err(SodiumError::InvalidInput(format!(
1070 "PRK must be exactly {KEYBYTES} bytes"
1071 )));
1072 }
1073 let mut prk = [0u8; KEYBYTES];
1074 prk.copy_from_slice(slice);
1075 Ok(Prk(prk))
1076 }
1077 }
1078
1079 impl From<[u8; KEYBYTES]> for Prk {
1080 fn from(bytes: [u8; KEYBYTES]) -> Self {
1081 Prk(bytes)
1082 }
1083 }
1084
1085 impl From<Prk> for [u8; KEYBYTES] {
1086 fn from(prk: Prk) -> Self {
1087 prk.0
1088 }
1089 }
1090
1091 impl Prk {
1092 /// Returns a reference to the PRK as a byte slice
1093 ///
1094 /// # Returns
1095 ///
1096 /// * `&[u8; KEYBYTES]` - Reference to the PRK bytes
1097 pub fn as_bytes(&self) -> &[u8; KEYBYTES] {
1098 &self.0
1099 }
1100
1101 /// Creates a PRK from a byte slice
1102 ///
1103 /// # Arguments
1104 ///
1105 /// * `slice` - Byte slice to create the PRK from
1106 ///
1107 /// # Returns
1108 ///
1109 /// * `Result<Self>` - A new PRK created from the slice or an error
1110 ///
1111 /// # Errors
1112 ///
1113 /// Returns an error if the slice is not exactly `KEYBYTES` bytes long
1114 pub fn from_slice(slice: &[u8]) -> Result<Self> {
1115 if slice.len() != KEYBYTES {
1116 return Err(SodiumError::InvalidInput(format!(
1117 "PRK must be exactly {KEYBYTES} bytes"
1118 )));
1119 }
1120
1121 let mut prk = [0u8; KEYBYTES];
1122 prk.copy_from_slice(slice);
1123 Ok(Self(prk))
1124 }
1125 }
1126
1127 /// Size of the state structure in bytes
1128 pub fn statebytes() -> usize {
1129 unsafe { libsodium_sys::crypto_kdf_hkdf_sha512_statebytes() as usize }
1130 }
1131
1132 /// Returns the number of bytes in the PRK (64)
1133 pub fn keybytes() -> usize {
1134 unsafe { libsodium_sys::crypto_kdf_hkdf_sha512_keybytes() as usize }
1135 }
1136
1137 /// Returns the minimum number of bytes in a derived subkey (0)
1138 pub fn bytes_min() -> usize {
1139 unsafe { libsodium_sys::crypto_kdf_hkdf_sha512_bytes_min() as usize }
1140 }
1141
1142 /// Returns the maximum number of bytes in a derived subkey (255 * 64 = 16320)
1143 pub fn bytes_max() -> usize {
1144 unsafe { libsodium_sys::crypto_kdf_hkdf_sha512_bytes_max() as usize }
1145 }
1146
1147 /// Extract a pseudorandom key from the input key material and salt
1148 ///
1149 /// This function performs the extract step of HKDF-SHA-512, which derives a
1150 /// pseudorandom key (PRK) from the input key material and salt.
1151 ///
1152 /// # Arguments
1153 ///
1154 /// * `salt` - Optional salt value (can be None)
1155 /// * `ikm` - Input key material
1156 ///
1157 /// # Returns
1158 ///
1159 /// * `Result<Prk>` - The pseudorandom key or an error
1160 ///
1161 /// # Errors
1162 ///
1163 /// Returns an error if the extract operation fails
1164 pub fn extract(salt: Option<&[u8]>, ikm: &[u8]) -> Result<Prk> {
1165 let mut prk = [0u8; KEYBYTES];
1166
1167 let salt_ptr = match salt {
1168 Some(s) => s.as_ptr(),
1169 None => std::ptr::null(),
1170 };
1171 let salt_len = salt.map_or(0, |s| s.len());
1172
1173 let result = unsafe {
1174 libsodium_sys::crypto_kdf_hkdf_sha512_extract(
1175 prk.as_mut_ptr(),
1176 salt_ptr,
1177 salt_len as libc::size_t,
1178 ikm.as_ptr(),
1179 ikm.len() as libc::size_t,
1180 )
1181 };
1182
1183 if result != 0 {
1184 return Err(SodiumError::OperationError(
1185 "HKDF-SHA-512 extract phase failed".into(),
1186 ));
1187 }
1188
1189 Ok(Prk(prk))
1190 }
1191
1192 /// Generate a random pseudorandom key
1193 ///
1194 /// This function generates a random pseudorandom key for use with HKDF-SHA-512.
1195 ///
1196 /// # Returns
1197 ///
1198 /// * `Prk` - A random pseudorandom key
1199 pub fn keygen() -> Prk {
1200 let mut prk = [0u8; KEYBYTES];
1201 unsafe {
1202 libsodium_sys::crypto_kdf_hkdf_sha512_keygen(prk.as_mut_ptr());
1203 }
1204 Prk(prk)
1205 }
1206
1207 /// Expand the pseudorandom key to the desired output length using the context.
1208 ///
1209 /// # Arguments
1210 ///
1211 /// * `out_len` - Length of the output to generate (must be at most `BYTES_MAX`)
1212 /// * `ctx` - Optional context (can be None)
1213 /// * `prk` - Pseudorandom key from the extract step
1214 ///
1215 /// # Returns
1216 ///
1217 /// * `Result<Vec<u8>>` - The expanded output or an error
1218 ///
1219 /// # Errors
1220 ///
1221 /// Returns an error if:
1222 /// * `out_len` is greater than `BYTES_MAX`
1223 /// * The expand operation fails
1224 pub fn expand(out_len: usize, ctx: Option<&[u8]>, prk: &Prk) -> Result<Vec<u8>> {
1225 if out_len > BYTES_MAX {
1226 return Err(SodiumError::InvalidInput(format!(
1227 "output length must be at most {BYTES_MAX} bytes"
1228 )));
1229 }
1230
1231 let mut out = vec![0u8; out_len];
1232
1233 let ctx_ptr = match ctx {
1234 Some(c) => c.as_ptr() as *const std::os::raw::c_char,
1235 None => std::ptr::null(),
1236 };
1237 let ctx_len = ctx.map_or(0, |c| c.len());
1238
1239 let result = unsafe {
1240 libsodium_sys::crypto_kdf_hkdf_sha512_expand(
1241 out.as_mut_ptr(),
1242 out_len as libc::size_t,
1243 ctx_ptr,
1244 ctx_len as libc::size_t,
1245 prk.as_bytes().as_ptr(),
1246 )
1247 };
1248
1249 if result != 0 {
1250 return Err(SodiumError::OperationError(
1251 "HKDF-SHA-512 expand phase failed".into(),
1252 ));
1253 }
1254
1255 Ok(out)
1256 }
1257
1258 /// State structure for incremental HKDF-SHA-512 extraction
1259 pub struct State {
1260 state: Box<libsodium_sys::crypto_kdf_hkdf_sha512_state>,
1261 }
1262
1263 impl Default for State {
1264 fn default() -> Self {
1265 Self::new()
1266 }
1267 }
1268
1269 impl State {
1270 /// Create a new state for incremental HKDF-SHA-512 extraction
1271 pub fn new() -> Self {
1272 let state = unsafe {
1273 let layout = std::alloc::Layout::from_size_align(statebytes(), 8)
1274 .expect("Invalid layout for crypto_kdf_hkdf_sha512_state");
1275 let ptr = std::alloc::alloc_zeroed(layout)
1276 as *mut libsodium_sys::crypto_kdf_hkdf_sha512_state;
1277 Box::from_raw(ptr)
1278 };
1279 Self { state }
1280 }
1281
1282 /// Initialize the state with the salt
1283 ///
1284 /// # Arguments
1285 ///
1286 /// * `salt` - Optional salt value (can be None)
1287 ///
1288 /// # Returns
1289 ///
1290 /// * `Result<()>` - Success or an error
1291 pub fn extract_init(&mut self, salt: Option<&[u8]>) -> Result<()> {
1292 let salt_ptr = match salt {
1293 Some(s) => s.as_ptr(),
1294 None => std::ptr::null(),
1295 };
1296 let salt_len = salt.map_or(0, |s| s.len());
1297
1298 let result = unsafe {
1299 libsodium_sys::crypto_kdf_hkdf_sha512_extract_init(
1300 self.state.as_mut(),
1301 salt_ptr,
1302 salt_len as libc::size_t,
1303 )
1304 };
1305
1306 if result != 0 {
1307 return Err(SodiumError::OperationError(
1308 "HKDF-SHA-512 extract init failed".into(),
1309 ));
1310 }
1311
1312 Ok(())
1313 }
1314
1315 /// Update the state with input key material
1316 ///
1317 /// # Arguments
1318 ///
1319 /// * `ikm` - Input key material
1320 ///
1321 /// # Returns
1322 ///
1323 /// * `Result<()>` - Success or an error
1324 pub fn extract_update(&mut self, ikm: &[u8]) -> Result<()> {
1325 let result = unsafe {
1326 libsodium_sys::crypto_kdf_hkdf_sha512_extract_update(
1327 self.state.as_mut(),
1328 ikm.as_ptr(),
1329 ikm.len() as libc::size_t,
1330 )
1331 };
1332
1333 if result != 0 {
1334 return Err(SodiumError::OperationError(
1335 "HKDF-SHA-512 extract update failed".into(),
1336 ));
1337 }
1338
1339 Ok(())
1340 }
1341
1342 /// Finalize the extraction and get the pseudorandom key
1343 ///
1344 /// # Returns
1345 ///
1346 /// * `Result<Prk>` - The pseudorandom key or an error
1347 pub fn extract_final(&mut self) -> Result<Prk> {
1348 let mut prk = [0u8; KEYBYTES];
1349 let result = unsafe {
1350 libsodium_sys::crypto_kdf_hkdf_sha512_extract_final(
1351 self.state.as_mut(),
1352 prk.as_mut_ptr(),
1353 )
1354 };
1355
1356 if result != 0 {
1357 return Err(SodiumError::OperationError(
1358 "HKDF-SHA-512 extract final failed".into(),
1359 ));
1360 }
1361
1362 Ok(Prk(prk))
1363 }
1364 }
1365
1366 impl Drop for State {
1367 fn drop(&mut self) {
1368 unsafe {
1369 let ptr = Box::into_raw(std::mem::replace(
1370 &mut self.state,
1371 Box::new(std::mem::zeroed()),
1372 ));
1373 let layout = std::alloc::Layout::from_size_align(statebytes(), 8)
1374 .expect("Invalid layout for crypto_kdf_hkdf_sha512_state");
1375 std::alloc::dealloc(ptr as *mut u8, layout);
1376 }
1377 }
1378 }
1379
1380 impl zeroize::Zeroize for State {
1381 fn zeroize(&mut self) {
1382 unsafe {
1383 // Zero out the state memory
1384 std::ptr::write_bytes(
1385 self.state.as_mut() as *mut _ as *mut u8,
1386 0,
1387 statebytes(),
1388 );
1389 }
1390 }
1391 }
1392 }
1393}
1394
1395#[cfg(test)]
1396mod tests {
1397 use super::*;
1398
1399 #[test]
1400 fn test_kdf() {
1401 let master_key = Key::generate().unwrap();
1402 let context = b"Examples";
1403 let subkey = derive_from_key(32, 1, context, &master_key).unwrap();
1404 assert_eq!(subkey.len(), 32);
1405
1406 // Derive a different subkey with a different ID
1407 let subkey2 = derive_from_key(32, 2, context, &master_key).unwrap();
1408 assert_ne!(subkey, subkey2);
1409
1410 // Test with invalid subkey length
1411 let invalid_length_result = derive_from_key(BYTES_MAX + 1, 1, context, &master_key);
1412 assert!(invalid_length_result.is_err());
1413
1414 // Test with invalid context length (should be exactly 8 bytes)
1415 let invalid_context = b"Short";
1416 let invalid_context_result = derive_from_key(32, 1, invalid_context, &master_key);
1417 assert!(invalid_context_result.is_err());
1418 }
1419
1420 #[test]
1421 fn test_blake2b() {
1422 let master_key = blake2b::Key::generate().unwrap();
1423 let context = b"Examples";
1424 let subkey = blake2b::derive_from_key(32, 1, context, &master_key).unwrap();
1425 assert_eq!(subkey.len(), 32);
1426
1427 // Derive a different subkey with a different ID
1428 let subkey2 = blake2b::derive_from_key(32, 2, context, &master_key).unwrap();
1429 assert_ne!(subkey, subkey2);
1430 }
1431
1432 #[test]
1433 fn test_hkdf_sha256() {
1434 // Test the extract function
1435 let ikm = b"input key material";
1436 let salt = Some(b"salt".as_ref());
1437 let prk = hkdf::sha256::extract(salt, ikm).unwrap();
1438 assert_eq!(prk.as_bytes().len(), hkdf::sha256::KEYBYTES);
1439
1440 // Test the expand function
1441 let ctx = Some(b"context".as_ref());
1442 let out_len = 32;
1443 let out = hkdf::sha256::expand(out_len, ctx, &prk).unwrap();
1444 assert_eq!(out.len(), out_len);
1445
1446 // Test with different context produces different output
1447 let ctx2 = Some(b"different context".as_ref());
1448 let out2 = hkdf::sha256::expand(out_len, ctx2, &prk).unwrap();
1449 assert_ne!(out, out2);
1450
1451 // Test keygen function
1452 let random_prk = hkdf::sha256::keygen();
1453 assert_eq!(random_prk.as_bytes().len(), hkdf::sha256::KEYBYTES);
1454 }
1455
1456 #[test]
1457 fn test_hkdf_sha512() {
1458 // Test the extract function
1459 let ikm = b"input key material";
1460 let salt = Some(b"salt".as_ref());
1461 let prk = hkdf::sha512::extract(salt, ikm).unwrap();
1462 assert_eq!(prk.as_bytes().len(), hkdf::sha512::KEYBYTES);
1463
1464 // Test the expand function
1465 let ctx = Some(b"context".as_ref());
1466 let out_len = 64;
1467 let out = hkdf::sha512::expand(out_len, ctx, &prk).unwrap();
1468 assert_eq!(out.len(), out_len);
1469
1470 // Test with different context produces different output
1471 let ctx2 = Some(b"different context".as_ref());
1472 let out2 = hkdf::sha512::expand(out_len, ctx2, &prk).unwrap();
1473 assert_ne!(out, out2);
1474
1475 // Test keygen function
1476 let random_prk = hkdf::sha512::keygen();
1477 assert_eq!(random_prk.as_bytes().len(), hkdf::sha512::KEYBYTES);
1478 }
1479
1480 #[test]
1481 fn test_hkdf_sha256_state() {
1482 // Test the incremental extraction API
1483 let ikm1 = b"input key";
1484 let ikm2 = b" material";
1485 let salt = Some(b"salt".as_ref());
1486
1487 // One-shot extraction
1488 let mut combined_ikm = Vec::new();
1489 combined_ikm.extend_from_slice(ikm1);
1490 combined_ikm.extend_from_slice(ikm2);
1491 let prk1 = hkdf::sha256::extract(salt, &combined_ikm).unwrap();
1492
1493 // Incremental extraction
1494 let mut state = hkdf::sha256::State::new();
1495 state.extract_init(salt).unwrap();
1496 state.extract_update(ikm1).unwrap();
1497 state.extract_update(ikm2).unwrap();
1498 let prk2 = state.extract_final().unwrap();
1499
1500 // Both methods should produce the same PRK
1501 assert_eq!(prk1.as_bytes(), prk2.as_bytes());
1502 }
1503
1504 #[test]
1505 fn test_hkdf_sha512_state() {
1506 // Test the incremental extraction API
1507 let ikm1 = b"input key";
1508 let ikm2 = b" material";
1509 let salt = Some(b"salt".as_ref());
1510
1511 // One-shot extraction
1512 let mut combined_ikm = Vec::new();
1513 combined_ikm.extend_from_slice(ikm1);
1514 combined_ikm.extend_from_slice(ikm2);
1515 let prk1 = hkdf::sha512::extract(salt, &combined_ikm).unwrap();
1516
1517 // Incremental extraction
1518 let mut state = hkdf::sha512::State::new();
1519 state.extract_init(salt).unwrap();
1520 state.extract_update(ikm1).unwrap();
1521 state.extract_update(ikm2).unwrap();
1522 let prk2 = state.extract_final().unwrap();
1523
1524 // Both methods should produce the same PRK
1525 assert_eq!(prk1.as_bytes(), prk2.as_bytes());
1526 }
1527
1528 #[test]
1529 fn test_main_key_traits() {
1530 // Test AsRef
1531 let key = Key::generate().unwrap();
1532 let key_ref: &[u8] = key.as_ref();
1533 assert_eq!(key_ref.len(), KEYBYTES);
1534
1535 // Test TryFrom<&[u8]>
1536 let bytes = [0x42; KEYBYTES];
1537 let key_from_slice = Key::try_from(&bytes[..]).unwrap();
1538 assert_eq!(key_from_slice.as_ref(), &bytes);
1539
1540 // Test TryFrom with invalid length
1541 let invalid_bytes = [0x42; KEYBYTES - 1];
1542 assert!(Key::try_from(&invalid_bytes[..]).is_err());
1543
1544 // Test From<[u8; KEYBYTES]>
1545 let key_from_bytes = Key::from(bytes);
1546 assert_eq!(key_from_bytes.as_ref(), &bytes);
1547
1548 // Test From<Key> for [u8; KEYBYTES]
1549 let key = Key::from(bytes);
1550 let bytes_from_key: [u8; KEYBYTES] = key.into();
1551 assert_eq!(bytes_from_key, bytes);
1552 }
1553
1554 #[test]
1555 fn test_blake2b_key_traits() {
1556 // Test AsRef
1557 let key = blake2b::Key::generate().unwrap();
1558 let key_ref: &[u8] = key.as_ref();
1559 assert_eq!(key_ref.len(), blake2b::KEYBYTES);
1560
1561 // Test TryFrom<&[u8]>
1562 let bytes = [0x42; blake2b::KEYBYTES];
1563 let key_from_slice = blake2b::Key::try_from(&bytes[..]).unwrap();
1564 assert_eq!(key_from_slice.as_ref(), &bytes);
1565
1566 // Test TryFrom with invalid length
1567 let invalid_bytes = [0x42; blake2b::KEYBYTES - 1];
1568 assert!(blake2b::Key::try_from(&invalid_bytes[..]).is_err());
1569
1570 // Test From<[u8; KEYBYTES]>
1571 let key_from_bytes = blake2b::Key::from(bytes);
1572 assert_eq!(key_from_bytes.as_ref(), &bytes);
1573
1574 // Test From<Key> for [u8; KEYBYTES]
1575 let key = blake2b::Key::from(bytes);
1576 let bytes_from_key: [u8; blake2b::KEYBYTES] = key.into();
1577 assert_eq!(bytes_from_key, bytes);
1578 }
1579
1580 #[test]
1581 fn test_hkdf_sha256_prk_traits() {
1582 // Test AsRef
1583 let prk = hkdf::sha256::keygen();
1584 let prk_ref: &[u8] = prk.as_ref();
1585 assert_eq!(prk_ref.len(), hkdf::sha256::KEYBYTES);
1586
1587 // Test TryFrom<&[u8]>
1588 let bytes = [0x42; hkdf::sha256::KEYBYTES];
1589 let prk_from_slice = hkdf::sha256::Prk::try_from(&bytes[..]).unwrap();
1590 assert_eq!(prk_from_slice.as_ref(), &bytes);
1591
1592 // Test TryFrom with invalid length
1593 let invalid_bytes = [0x42; hkdf::sha256::KEYBYTES - 1];
1594 assert!(hkdf::sha256::Prk::try_from(&invalid_bytes[..]).is_err());
1595
1596 // Test From<[u8; KEYBYTES]>
1597 let prk_from_bytes = hkdf::sha256::Prk::from(bytes);
1598 assert_eq!(prk_from_bytes.as_ref(), &bytes);
1599
1600 // Test From<Prk> for [u8; KEYBYTES]
1601 let prk = hkdf::sha256::Prk::from(bytes);
1602 let bytes_from_prk: [u8; hkdf::sha256::KEYBYTES] = prk.into();
1603 assert_eq!(bytes_from_prk, bytes);
1604 }
1605
1606 #[test]
1607 fn test_hkdf_sha512_prk_traits() {
1608 // Test AsRef
1609 let prk = hkdf::sha512::keygen();
1610 let prk_ref: &[u8] = prk.as_ref();
1611 assert_eq!(prk_ref.len(), hkdf::sha512::KEYBYTES);
1612
1613 // Test TryFrom<&[u8]>
1614 let bytes = [0x42; hkdf::sha512::KEYBYTES];
1615 let prk_from_slice = hkdf::sha512::Prk::try_from(&bytes[..]).unwrap();
1616 assert_eq!(prk_from_slice.as_ref(), &bytes);
1617
1618 // Test TryFrom with invalid length
1619 let invalid_bytes = [0x42; hkdf::sha512::KEYBYTES - 1];
1620 assert!(hkdf::sha512::Prk::try_from(&invalid_bytes[..]).is_err());
1621
1622 // Test From<[u8; KEYBYTES]>
1623 let prk_from_bytes = hkdf::sha512::Prk::from(bytes);
1624 assert_eq!(prk_from_bytes.as_ref(), &bytes);
1625
1626 // Test From<Prk> for [u8; KEYBYTES]
1627 let prk = hkdf::sha512::Prk::from(bytes);
1628 let bytes_from_prk: [u8; hkdf::sha512::KEYBYTES] = prk.into();
1629 assert_eq!(bytes_from_prk, bytes);
1630 }
1631}