use crate::Result;
pub trait Kdf {
fn derive(ikm: &[u8], salt: &[u8], info: &[u8], okm: &mut [u8]) -> Result<()>;
fn derive_32(ikm: &[u8], salt: &[u8], info: &[u8]) -> Result<[u8; 32]> {
let mut out = [0u8; 32];
Self::derive(ikm, salt, info, &mut out)?;
Ok(out)
}
}
#[cfg(feature = "std-primitives")]
#[cfg_attr(docsrs, doc(cfg(feature = "std-primitives")))]
pub struct HkdfSha256;
#[cfg(feature = "std-primitives")]
impl Kdf for HkdfSha256 {
fn derive(ikm: &[u8], salt: &[u8], info: &[u8], okm: &mut [u8]) -> Result<()> {
let salt_opt = if salt.is_empty() { None } else { Some(salt) };
let hk = hkdf::Hkdf::<sha2::Sha256>::new(salt_opt, ikm);
hk.expand(info, okm)
.map_err(|_| crate::Error::Primitive("HKDF-SHA-256 expand"))
}
}
pub fn derive_wrapping_key<K: Kdf>(
user_key: &[u8],
prf_salt: &[u8],
credential_id: &[u8],
version: u16,
) -> Result<[u8; 32]> {
use crate::primitives::domain::DS_WRAP;
let mut info = Vec::with_capacity(DS_WRAP.len() + credential_id.len() + 2);
info.extend_from_slice(DS_WRAP);
info.extend_from_slice(credential_id);
info.extend_from_slice(&version.to_be_bytes());
K::derive_32(user_key, prf_salt, &info)
}
#[cfg(all(test, feature = "std-primitives"))]
mod tests {
use super::*;
#[test]
fn derive_wrapping_key_matches_ts_authorizer_conformance_vector() {
let user_key = [0x22u8; 32];
let prf_salt = [0x33u8; 32];
let cid = [10u8, 20, 30, 40];
let wc = derive_wrapping_key::<HkdfSha256>(&user_key, &prf_salt, &cid, 1).unwrap();
let hex: String = wc.iter().map(|b| format!("{:02x}", b)).collect();
assert_eq!(
hex,
"957e05e935d84cebfa408361f358cb408956f845ddea025f38b83dccd491cd90"
);
}
#[test]
fn aead_matches_ts_authorizer_conformance_vector() {
use crate::primitives::{aead::Aead, ChaCha20Poly1305};
let key = [0x11u8; 32];
let nonce = [0x22u8; 24];
let plaintext = b"the lazy dog jumps over...";
let mut ad = Vec::new();
ad.extend_from_slice(crate::primitives::domain::DS_WRAP);
ad.extend_from_slice(&[0xAAu8; 8]);
ad.extend_from_slice(&1u16.to_be_bytes());
let ct = ChaCha20Poly1305::encrypt(&key, &nonce, plaintext, &ad).unwrap();
let hex: String = ct.iter().map(|b| format!("{:02x}", b)).collect();
assert_eq!(
hex,
"f70f822c30d89eedc5297bac9d13d48f42e4e3bb63fb88ca4e6581fb03f4812766f6b8776d301bef7135"
);
}
}