use hmac::{KeyInit, Mac};
use sha2::Sha256;
use soft_fido2::Error;
use zeroize::Zeroizing;
pub fn kdfe(
z: &[u8],
label: &[u8],
party_u: &[u8],
party_v: &[u8],
bits: usize,
) -> Zeroizing<Vec<u8>> {
let klen = bits / 8;
let mut result = Zeroizing::new(Vec::with_capacity(klen));
use sha2::Digest;
let mut counter = 1u32;
while result.len() < klen {
let mut hasher = Sha256::new();
hasher.update(counter.to_be_bytes());
hasher.update(z);
hasher.update(label);
hasher.update(party_u);
hasher.update(party_v);
result.extend_from_slice(&hasher.finalize());
counter += 1;
}
result.truncate(klen);
result
}
pub fn kdfa(
key: &[u8],
label: &[u8],
context_u: &[u8],
context_v: &[u8],
bits: usize,
) -> Zeroizing<Vec<u8>> {
let klen = bits / 8;
let mut result = Zeroizing::new(Vec::with_capacity(klen));
type HmacSha256 = hmac::Hmac<Sha256>;
let mut counter = 1u32;
while result.len() < klen {
let mut mac = HmacSha256::new_from_slice(key).expect("HMAC accepts any key size");
mac.update(&counter.to_be_bytes());
mac.update(label);
mac.update(&[0x00]);
mac.update(context_u);
mac.update(context_v);
mac.update(&(bits as u32).to_be_bytes());
result.extend_from_slice(&mac.finalize().into_bytes());
counter += 1;
}
result.truncate(klen);
result
}
pub fn aes_128_cfb_encrypt(key: &[u8], plaintext: &[u8]) -> Result<Zeroizing<Vec<u8>>, Error> {
aes_128_cfb_encrypt_with_iv(key, &[0u8; 16], plaintext)
}
fn aes_128_cfb_encrypt_with_iv(
key: &[u8],
iv: &[u8],
plaintext: &[u8],
) -> Result<Zeroizing<Vec<u8>>, Error> {
use aes::Aes128;
use aes::cipher::{BlockCipherEncrypt, KeyInit};
if key.len() != 16 {
return Err(Error::Other);
}
let cipher = Aes128::new_from_slice(key).map_err(|_| Error::Other)?;
let mut result = Zeroizing::new(vec![0u8; plaintext.len()]);
let mut block_iv = [0u8; 16];
block_iv.copy_from_slice(iv);
let block_size = 16;
for (i, chunk) in plaintext.chunks(block_size).enumerate() {
let mut block = block_iv;
cipher.encrypt_block((&mut block).into());
let start = i * block_size;
for (j, &byte) in chunk.iter().enumerate() {
result[start + j] = byte ^ block[j];
}
if chunk.len() == block_size {
block_iv.copy_from_slice(&result[start..start + block_size]);
}
}
Ok(result)
}
pub fn hmac_sha256(key: &[u8], data: &[u8]) -> Zeroizing<Vec<u8>> {
type HmacSha256 = hmac::Hmac<Sha256>;
let mut mac = HmacSha256::new_from_slice(key).expect("HMAC accepts any key size");
mac.update(data);
Zeroizing::new(mac.finalize().into_bytes().to_vec())
}
#[cfg(test)]
mod tests {
use super::*;
use hex::FromHex;
use sha2::Digest;
#[test]
fn kdfe_output_length_matches_requested_bits() {
let z = [0xAA; 32];
for bits in [128, 256, 384, 512] {
let out = kdfe(&z, b"label", b"u", b"v", bits);
assert_eq!(out.len(), bits / 8);
}
}
#[test]
fn kdfe_256_matches_single_sha256_block() {
let z = b"shared_secret_zz";
let label = b"IDU\0";
let party_u = b"partyUinfo";
let party_v = b"partyVinfo";
let mut hasher = Sha256::new();
hasher.update(1u32.to_be_bytes());
hasher.update(z);
hasher.update(label);
hasher.update(party_u);
hasher.update(party_v);
let expected = hasher.finalize();
let out = kdfe(z, label, party_u, party_v, 256);
assert_eq!(&out[..], &expected[..]);
}
#[test]
fn kdfe_deterministic_and_sensitive_to_inputs() {
let z = [0x42; 32];
let a = kdfe(&z, b"label", b"u", b"v", 256);
let b = kdfe(&z, b"label", b"u", b"v", 256);
assert_eq!(a, b);
let c = kdfe(&z, b"other_label", b"u", b"v", 256);
assert_ne!(a, c);
}
#[test]
fn kdfa_output_length_matches_requested_bits() {
let key = [0xBB; 32];
for bits in [128, 256, 384, 512] {
let out = kdfa(&key, b"label", b"cu", b"cv", bits);
assert_eq!(out.len(), bits / 8);
}
}
#[test]
fn kdfa_256_matches_single_hmac_block() {
let key = b"derivation_key";
let label = b"identity\0";
let context_u = b"contextU";
let context_v = b"contextV";
let bits: usize = 256;
type HmacSha256 = hmac::Hmac<Sha256>;
let mut mac = HmacSha256::new_from_slice(key).expect("HMAC accepts any key size");
mac.update(&1u32.to_be_bytes());
mac.update(label);
mac.update(&[0x00]);
mac.update(context_u);
mac.update(context_v);
mac.update(&(bits as u32).to_be_bytes());
let expected = mac.finalize().into_bytes();
let out = kdfa(key, label, context_u, context_v, bits);
assert_eq!(&out[..], &expected[..]);
}
#[test]
fn kdfa_deterministic_and_sensitive_to_inputs() {
let key = [0x11; 32];
let a = kdfa(&key, b"label", b"cu", b"cv", 256);
let b = kdfa(&key, b"label", b"cu", b"cv", 256);
assert_eq!(a, b);
let c = kdfa(&key, b"label", b"cu", b"cv", 128);
assert_ne!(&a[..32], &c[..16]);
}
#[test]
fn hmac_sha256_rfc4231_test_case_2() {
let key = b"Jefe";
let data = b"what do ya want for nothing?";
let expected =
Vec::from_hex("5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843")
.unwrap();
let out = hmac_sha256(key, data);
assert_eq!(&out[..], &expected[..]);
}
#[test]
fn hmac_sha256_output_length_is_32() {
let out = hmac_sha256(b"key", b"data");
assert_eq!(out.len(), 32);
}
#[test]
fn hmac_sha256_deterministic_and_sensitive_to_inputs() {
let a = hmac_sha256(b"key", b"data");
let b = hmac_sha256(b"key", b"data");
assert_eq!(a, b);
let c = hmac_sha256(b"key", b"other");
assert_ne!(a, c);
let d = hmac_sha256(b"other_key", b"data");
assert_ne!(a, d);
}
#[test]
fn aes_128_cfb_nist_sp800_38a_test_vector() {
let key = Vec::from_hex("2b7e151628aed2a6abf7158809cf4f3c").unwrap();
let iv = Vec::from_hex("000102030405060708090a0b0c0d0e0f").unwrap();
let plaintext = Vec::from_hex("6bc1bee22e409f96e93d7e117393172a").unwrap();
let expected = Vec::from_hex("3b3fd92eb72dad20333449f8e83cfb4a").unwrap();
let result = aes_128_cfb_encrypt_with_iv(&key, &iv, &plaintext).unwrap();
assert_eq!(&result[..], &expected[..]);
}
#[test]
fn aes_128_cfb_rejects_invalid_key_length() {
let short_key = [0u8; 15];
let long_key = [0u8; 17];
let plaintext = [0u8; 16];
assert!(aes_128_cfb_encrypt(&short_key, &plaintext).is_err());
assert!(aes_128_cfb_encrypt(&long_key, &plaintext).is_err());
}
#[test]
fn aes_128_cfb_deterministic_output() {
let key = [0x42u8; 16];
let plaintext = b"test plaintext!!";
let a = aes_128_cfb_encrypt(&key, plaintext).unwrap();
let b = aes_128_cfb_encrypt(&key, plaintext).unwrap();
assert_eq!(a, b);
}
#[test]
fn aes_128_cfb_output_length_matches_input() {
let key = [0x42u8; 16];
for len in [0, 1, 15, 16, 17, 31, 32, 33, 100] {
let plaintext = vec![0xAA; len];
let ciphertext = aes_128_cfb_encrypt(&key, &plaintext).unwrap();
assert_eq!(ciphertext.len(), len);
}
}
}