Skip to main content

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}