use hmac;
pub fn extract_and_expand(salt: &hmac::SigningKey, secret: &[u8],
info: &[u8], out: &mut [u8]) {
let prk = extract(salt, secret);
expand(&prk, info, out)
}
pub fn extract(salt: &hmac::SigningKey, secret: &[u8]) -> hmac::SigningKey {
let prk = hmac::sign(salt, secret);
hmac::SigningKey::new(salt.digest_algorithm(), prk.as_ref())
}
pub fn expand(prk: &hmac::SigningKey, info: &[u8], out: &mut [u8]) {
let digest_alg = prk.digest_algorithm();
assert!(out.len() <= 255 * digest_alg.output_len);
assert!(digest_alg.block_len >= digest_alg.output_len);
let mut ctx = hmac::SigningContext::with_key(prk);
let mut n = 1u8;
let mut pos = 0;
loop {
ctx.update(info);
ctx.update(&[n]);
let t = ctx.sign();
let to_copy = if out.len() - pos < digest_alg.output_len {
out.len() - pos
} else {
digest_alg.output_len
};
let t_bytes = t.as_ref();
for i in 0..to_copy {
out[pos + i] = t_bytes[i];
}
if to_copy < digest_alg.output_len {
break;
}
pos += digest_alg.output_len;
ctx = hmac::SigningContext::with_key(prk);
ctx.update(t_bytes);
n += 1;
}
}
#[cfg(test)]
mod tests {
use super::*;
use {error, hmac, test};
#[test]
pub fn hkdf_tests() {
test::from_file("src/hkdf_tests.txt", |section, test_case| {
assert_eq!(section, "");
let digest_alg =
try!(test_case.consume_digest_alg("Hash")
.ok_or(error::Unspecified));
let secret = test_case.consume_bytes("IKM");
let salt = test_case.consume_bytes("salt");
let info = test_case.consume_bytes("info");
let _ = test_case.consume_bytes("PRK");
let expected_out = test_case.consume_bytes("OKM");
let salt = hmac::SigningKey::new(digest_alg, &salt);
let mut out = vec![0u8; expected_out.len()];
extract_and_expand(&salt, &secret, &info, &mut out);
assert_eq!(out, expected_out);
Ok(())
});
}
}