p2panda_encryption/crypto/
hkdf.rs1use hkdf::Hkdf;
8use sha2::Sha256;
9use thiserror::Error;
10
11pub fn hkdf<const N: usize>(
12 salt: &[u8],
13 ikm: &[u8],
14 info: Option<&[u8]>,
15) -> Result<[u8; N], HkdfError> {
16 let salt = if salt.is_empty() { None } else { Some(salt) };
17 let hk = Hkdf::<Sha256>::new(salt, ikm);
18 let mut okm = [0u8; N];
19 hk.expand(info.unwrap_or_default(), &mut okm)
20 .map_err(|_| HkdfError::InvalidArguments)?;
21 Ok(okm)
22}
23
24#[derive(Debug, Error)]
25pub enum HkdfError {
26 #[error("arguments too large for hkdf")]
27 InvalidArguments,
28}
29
30#[cfg(test)]
31mod tests {
32 use super::hkdf;
33
34 #[test]
35 fn key_material_len() {
36 let result_1: [u8; 18] = hkdf(b"salt", b"ikm", None).unwrap();
37 assert_eq!(result_1.len(), 18);
38 }
39
40 #[test]
41 fn info_needs_to_match() {
42 let result_1: [u8; 18] = hkdf(b"salt", b"ikm", Some(b"info")).unwrap();
43 let result_2: [u8; 18] = hkdf(b"salt", b"ikm", Some(b"info")).unwrap();
44 let result_3: [u8; 18] = hkdf(b"salt", b"ikm", Some(b"different info")).unwrap();
45 assert_eq!(result_1, result_2);
46 assert_ne!(result_2, result_3);
47 }
48}