apple_cryptokit/key_derivation/mod.rs
1use crate::error::{CryptoKitError, Result};
2
3pub mod hkdf_sha256;
4pub mod hkdf_sha384;
5pub mod hkdf_sha512;
6
7/// Hash algorithm enumeration
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub enum HashAlgorithm {
10 /// SHA256 hash algorithm
11 SHA256,
12 /// SHA384 hash algorithm
13 SHA384,
14 /// SHA512 hash algorithm
15 SHA512,
16}
17
18impl HashAlgorithm {
19 /// Get the output length of the hash algorithm (in bytes)
20 pub fn output_length(&self) -> usize {
21 match self {
22 HashAlgorithm::SHA256 => 32,
23 HashAlgorithm::SHA384 => 48,
24 HashAlgorithm::SHA512 => 64,
25 }
26 }
27
28 /// Get the maximum output length of HKDF (255 * hash_length)
29 pub fn max_hkdf_output_length(&self) -> usize {
30 255 * self.output_length()
31 }
32}
33
34/// Generic trait for key derivation functions
35pub trait KeyDerivationFunction {
36 /// Derive key from input key material
37 fn derive(
38 input_key_material: &[u8],
39 salt: &[u8],
40 info: &[u8],
41 output_length: usize,
42 ) -> Result<Vec<u8>> {
43 let mut output = vec![0u8; output_length];
44 Self::derive_to(input_key_material, salt, info, &mut output)?;
45 Ok(output)
46 }
47
48 /// Derive key from input key material to the provided buffer (zero-allocation)
49 ///
50 /// # Parameters
51 /// - `output`: Buffer whose length determines the length of the derived key
52 ///
53 /// # Returns
54 /// Number of bytes written (same as output.len())
55 fn derive_to(
56 input_key_material: &[u8],
57 salt: &[u8],
58 info: &[u8],
59 output: &mut [u8],
60 ) -> Result<usize>;
61}
62
63/// Generic HKDF implementation
64pub struct HKDF;
65
66impl HKDF {
67 /// Perform HKDF key derivation using the specified hash algorithm
68 ///
69 /// # Parameters
70 /// * `algorithm` - The hash algorithm to use
71 /// * `input_key_material` - Input Key Material (IKM)
72 /// * `salt` - Optional salt value, recommended to use a random value
73 /// * `info` - Optional context and application-specific information
74 /// * `output_length` - Desired output key length
75 ///
76 /// # Returns
77 /// Derived key data
78 ///
79 /// # Errors
80 /// * `CryptoKitError::InvalidInput` - If the input key material is empty
81 /// * `CryptoKitError::InvalidLength` - If the output length is invalid
82 /// * `CryptoKitError::DerivationFailed` - If key derivation fails
83 ///
84 /// # Example
85 /// ```rust,no_run
86 /// use apple_cryptokit::key_derivation::{HKDF, HashAlgorithm};
87 ///
88 /// let ikm = b"input key material";
89 /// let salt = b"optional salt";
90 /// let info = b"application context";
91 ///
92 /// let derived_key = HKDF::derive_key(
93 /// HashAlgorithm::SHA256,
94 /// ikm,
95 /// salt,
96 /// info,
97 /// 32
98 /// ).unwrap();
99 /// ```
100 pub fn derive_key(
101 algorithm: HashAlgorithm,
102 input_key_material: &[u8],
103 salt: &[u8],
104 info: &[u8],
105 output_length: usize,
106 ) -> Result<Vec<u8>> {
107 // Validate input parameters
108 if input_key_material.is_empty() {
109 return Err(CryptoKitError::InvalidInput(
110 "Input key material cannot be empty".to_string(),
111 ));
112 }
113
114 if output_length == 0 || output_length > algorithm.max_hkdf_output_length() {
115 return Err(CryptoKitError::InvalidLength);
116 }
117
118 match algorithm {
119 HashAlgorithm::SHA256 => {
120 hkdf_sha256::HKDF_SHA256::derive(input_key_material, salt, info, output_length)
121 }
122 HashAlgorithm::SHA384 => {
123 hkdf_sha384::HKDF_SHA384::derive(input_key_material, salt, info, output_length)
124 }
125 HashAlgorithm::SHA512 => {
126 hkdf_sha512::HKDF_SHA512::derive(input_key_material, salt, info, output_length)
127 }
128 }
129 }
130
131 /// Perform HKDF key derivation to the provided buffer using the specified hash algorithm (zero-allocation)
132 ///
133 /// # Parameters
134 /// * `algorithm` - The hash algorithm to use
135 /// * `input_key_material` - Input Key Material (IKM)
136 /// * `salt` - Optional salt value, recommended to use a random value
137 /// * `info` - Optional context and application-specific information
138 /// * `output` - Output buffer whose length determines the length of the derived key
139 ///
140 /// # Returns
141 /// Number of bytes written to output
142 pub fn derive_key_to(
143 algorithm: HashAlgorithm,
144 input_key_material: &[u8],
145 salt: &[u8],
146 info: &[u8],
147 output: &mut [u8],
148 ) -> Result<usize> {
149 // Validate input parameters
150 if input_key_material.is_empty() {
151 return Err(CryptoKitError::InvalidInput(
152 "Input key material cannot be empty".to_string(),
153 ));
154 }
155
156 let output_length = output.len();
157 if output_length == 0 || output_length > algorithm.max_hkdf_output_length() {
158 return Err(CryptoKitError::InvalidLength);
159 }
160
161 match algorithm {
162 HashAlgorithm::SHA256 => {
163 hkdf_sha256::HKDF_SHA256::derive_to(input_key_material, salt, info, output)
164 }
165 HashAlgorithm::SHA384 => {
166 hkdf_sha384::HKDF_SHA384::derive_to(input_key_material, salt, info, output)
167 }
168 HashAlgorithm::SHA512 => {
169 hkdf_sha512::HKDF_SHA512::derive_to(input_key_material, salt, info, output)
170 }
171 }
172 }
173
174 /// Convenience method: HKDF key derivation using SHA256
175 pub fn derive_key_sha256(
176 input_key_material: &[u8],
177 salt: &[u8],
178 info: &[u8],
179 output_length: usize,
180 ) -> Result<Vec<u8>> {
181 Self::derive_key(
182 HashAlgorithm::SHA256,
183 input_key_material,
184 salt,
185 info,
186 output_length,
187 )
188 }
189
190 /// Convenience method: HKDF key derivation using SHA384
191 pub fn derive_key_sha384(
192 input_key_material: &[u8],
193 salt: &[u8],
194 info: &[u8],
195 output_length: usize,
196 ) -> Result<Vec<u8>> {
197 Self::derive_key(
198 HashAlgorithm::SHA384,
199 input_key_material,
200 salt,
201 info,
202 output_length,
203 )
204 }
205
206 /// Convenience method: HKDF key derivation using SHA512
207 pub fn derive_key_sha512(
208 input_key_material: &[u8],
209 salt: &[u8],
210 info: &[u8],
211 output_length: usize,
212 ) -> Result<Vec<u8>> {
213 Self::derive_key(
214 HashAlgorithm::SHA512,
215 input_key_material,
216 salt,
217 info,
218 output_length,
219 )
220 }
221
222 /// Derive symmetric key from shared secret
223 ///
224 /// This is a convenience method for deriving symmetric encryption keys from shared secrets
225 /// obtained from operations such as elliptic curve key agreement
226 ///
227 /// # Parameters
228 /// * `shared_secret` - Shared secret data
229 /// * `algorithm` - Hash algorithm
230 /// * `salt` - Optional salt value
231 /// * `info` - Application context information
232 /// * `output_length` - Output key length
233 pub fn derive_symmetric_key(
234 shared_secret: &[u8],
235 algorithm: HashAlgorithm,
236 salt: Option<&[u8]>,
237 info: &[u8],
238 output_length: usize,
239 ) -> Result<Vec<u8>> {
240 let salt = salt.unwrap_or(&[]);
241 Self::derive_key(algorithm, shared_secret, salt, info, output_length)
242 }
243}
244
245/// HKDF-SHA256 key derivation implementation (re-exported)
246pub use hkdf_sha256::HKDF_SHA256;
247
248/// HKDF-SHA384 key derivation implementation (re-export)
249pub use hkdf_sha384::HKDF_SHA384;
250
251/// HKDF-SHA512 key derivation implementation (re-export)
252pub use hkdf_sha512::HKDF_SHA512;
253
254// ============================================================================
255// Convenience functions
256// ============================================================================
257
258/// Convenience function: HKDF-SHA256 key derivation
259///
260/// # Parameters
261/// * `input_key_material` - Input key material
262/// * `salt` - Salt value (optional, recommended)
263/// * `info` - Application-specific information (optional)
264/// * `output_length` - Output key length
265///
266/// # Example
267/// ```rust,no_run
268/// use apple_cryptokit::key_derivation::hkdf_sha256_derive;
269///
270/// let ikm = b"input key material";
271/// let salt = b"optional salt";
272/// let info = b"application context";
273///
274/// let derived_key = hkdf_sha256_derive(ikm, salt, info, 32).unwrap();
275/// ```
276pub fn hkdf_sha256_derive(
277 input_key_material: &[u8],
278 salt: &[u8],
279 info: &[u8],
280 output_length: usize,
281) -> Result<Vec<u8>> {
282 hkdf_sha256::hkdf_sha256_derive(input_key_material, salt, info, output_length)
283}
284
285/// Convenience function: HKDF-SHA384 key derivation
286///
287/// # Parameters
288/// * `input_key_material` - Input key material
289/// * `salt` - Salt value (optional, recommended)
290/// * `info` - Application-specific information (optional)
291/// * `output_length` - Output key length
292pub fn hkdf_sha384_derive(
293 input_key_material: &[u8],
294 salt: &[u8],
295 info: &[u8],
296 output_length: usize,
297) -> Result<Vec<u8>> {
298 hkdf_sha384::hkdf_sha384_derive(input_key_material, salt, info, output_length)
299}
300
301/// Convenience function: HKDF-SHA512 key derivation
302///
303/// # Parameters
304/// * `input_key_material` - Input key material
305/// * `salt` - Salt value (optional, recommended)
306/// * `info` - Application-specific information (optional)
307/// * `output_length` - Output key length
308pub fn hkdf_sha512_derive(
309 input_key_material: &[u8],
310 salt: &[u8],
311 info: &[u8],
312 output_length: usize,
313) -> Result<Vec<u8>> {
314 hkdf_sha512::hkdf_sha512_derive(input_key_material, salt, info, output_length)
315}
316
317// ============================================================================
318// Predefined common key lengths
319// ============================================================================
320
321/// Common symmetric key length definitions
322pub mod key_sizes {
323 /// AES-128 key length (16 bytes)
324 pub const AES_128: usize = 16;
325 /// AES-192 key length (24 bytes)
326 pub const AES_192: usize = 24;
327 /// AES-256 key length (32 bytes)
328 pub const AES_256: usize = 32;
329 /// ChaCha20 key length (32 bytes)
330 pub const CHACHA20: usize = 32;
331 /// HMAC-SHA256 recommended key length (32 bytes)
332 pub const HMAC_SHA256: usize = 32;
333 /// HMAC-SHA384 recommended key length (48 bytes)
334 pub const HMAC_SHA384: usize = 48;
335 /// HMAC-SHA512 recommended key length (64 bytes)
336 pub const HMAC_SHA512: usize = 64;
337}
338
339// ============================================================================
340// Test module
341// ============================================================================
342
343#[cfg(test)]
344mod tests {
345 use super::*;
346
347 #[test]
348 fn test_hash_algorithm_properties() {
349 assert_eq!(HashAlgorithm::SHA256.output_length(), 32);
350 assert_eq!(HashAlgorithm::SHA384.output_length(), 48);
351 assert_eq!(HashAlgorithm::SHA512.output_length(), 64);
352
353 assert_eq!(HashAlgorithm::SHA256.max_hkdf_output_length(), 255 * 32);
354 assert_eq!(HashAlgorithm::SHA384.max_hkdf_output_length(), 255 * 48);
355 assert_eq!(HashAlgorithm::SHA512.max_hkdf_output_length(), 255 * 64);
356 }
357
358 #[test]
359 fn test_invalid_input_validation() {
360 // Test empty input key material
361 let result = HKDF::derive_key(HashAlgorithm::SHA256, &[], b"salt", b"info", 32);
362 assert!(matches!(result, Err(CryptoKitError::InvalidInput(_))));
363
364 // Test zero output length
365 let result = HKDF::derive_key(HashAlgorithm::SHA256, b"ikm", b"salt", b"info", 0);
366 assert!(matches!(result, Err(CryptoKitError::InvalidLength)));
367
368 // Test excessive output length
369 let result = HKDF::derive_key(
370 HashAlgorithm::SHA256,
371 b"ikm",
372 b"salt",
373 b"info",
374 255 * 32 + 1,
375 );
376 assert!(matches!(result, Err(CryptoKitError::InvalidLength)));
377 }
378}