apple_cryptokit/key_derivation/
hkdf_sha256.rs

1use super::KeyDerivationFunction;
2use crate::error::{CryptoKitError, Result};
3
4// HKDF-SHA256 Swift FFI declarations
5extern "C" {
6    #[link_name = "hkdf_sha256_derive"]
7    fn swift_hkdf_sha256_derive(
8        input_key: *const u8,
9        input_key_len: i32,
10        salt: *const u8,
11        salt_len: i32,
12        info: *const u8,
13        info_len: i32,
14        output_length: i32,
15        output: *mut u8,
16    ) -> i32;
17}
18
19/// HKDF-SHA256 key derivation implementation
20#[allow(non_camel_case_types)]
21pub struct HKDF_SHA256;
22
23impl HKDF_SHA256 {
24    /// Derive key from input key material using SHA256 hash algorithm
25    ///
26    /// # Parameters
27    /// * `input_key_material` - Input key material
28    /// * `salt` - Salt value (optional, recommended)
29    /// * `info` - Application-specific information (optional)
30    /// * `output_length` - Output key length
31    ///
32    /// # Returns
33    /// Derived key data
34    ///
35    /// # Errors
36    /// Returns `CryptoKitError::DerivationFailed` if key derivation fails
37    ///
38    /// # Example
39    /// ```rust,no_run
40    /// use apple_cryptokit::key_derivation::hkdf_sha256::HKDF_SHA256;
41    /// use apple_cryptokit::key_derivation::KeyDerivationFunction;
42    ///
43    /// let ikm = b"input key material";
44    /// let salt = b"optional salt";
45    /// let info = b"application context";
46    ///
47    /// let derived_key = HKDF_SHA256::derive(ikm, salt, info, 32).unwrap();
48    /// ```
49    pub fn derive_key(
50        input_key_material: &[u8],
51        salt: &[u8],
52        info: &[u8],
53        output_length: usize,
54    ) -> Result<Vec<u8>> {
55        Self::derive(input_key_material, salt, info, output_length)
56    }
57}
58
59impl KeyDerivationFunction for HKDF_SHA256 {
60    fn derive_to(
61        input_key_material: &[u8],
62        salt: &[u8],
63        info: &[u8],
64        output: &mut [u8],
65    ) -> Result<usize> {
66        if input_key_material.is_empty() {
67            return Err(CryptoKitError::InvalidInput(
68                "Input key material cannot be empty".to_string(),
69            ));
70        }
71
72        let output_length = output.len();
73        if output_length == 0 || output_length > 255 * 32 {
74            // SHA256 outputs 32 bytes, maximum 255 output blocks
75            return Err(CryptoKitError::InvalidLength);
76        }
77
78        unsafe {
79            let result = swift_hkdf_sha256_derive(
80                input_key_material.as_ptr(),
81                input_key_material.len() as i32,
82                salt.as_ptr(),
83                salt.len() as i32,
84                info.as_ptr(),
85                info.len() as i32,
86                output_length as i32,
87                output.as_mut_ptr(),
88            );
89
90            if result == 0 {
91                Ok(output_length)
92            } else {
93                Err(CryptoKitError::DerivationFailed)
94            }
95        }
96    }
97}
98
99/// Convenience function: HKDF-SHA256 key derivation
100///
101/// # Parameters
102/// * `input_key_material` - Input key material
103/// * `salt` - Salt value (optional, recommended)
104/// * `info` - Application-specific information (optional)
105/// * `output_length` - Output key length
106///
107/// # Example
108/// ```rust,no_run
109/// use apple_cryptokit::key_derivation::hkdf_sha256_derive;
110///
111/// let ikm = b"input key material";
112/// let salt = b"optional salt";
113/// let info = b"application context";
114///
115/// let derived_key = hkdf_sha256_derive(ikm, salt, info, 32).unwrap();
116/// ```
117pub fn hkdf_sha256_derive(
118    input_key_material: &[u8],
119    salt: &[u8],
120    info: &[u8],
121    output_length: usize,
122) -> Result<Vec<u8>> {
123    HKDF_SHA256::derive(input_key_material, salt, info, output_length)
124}