use super::super::{
KEY_SIZE, NONCE_SIZE, base_decrypt, base_encrypt, bytes_to_usize, usize_to_bytes,
};
use anyhow::bail;
use blake2::digest::{FixedOutput, Mac};
use chacha20::XNonce;
use chacha20poly1305::aead::Generate;
pub const HMAC_MODE: u8 = 1;
pub fn hmac_encrypt(
fingerprint: [u8; 32],
mut content: Vec<u8>,
hmac_key: [u8; KEY_SIZE],
hmac_value: [u8; KEY_SIZE],
iteration: usize,
) -> anyhow::Result<(Vec<u8>, [u8; KEY_SIZE])> {
let nonce: [u8; NONCE_SIZE] = XNonce::generate().into();
let mut out = nonce.to_vec();
let key = blake2::Blake2sMac256::new_from_slice(&hmac_key)?
.chain_update(hmac_value)
.finalize_fixed();
let (size, bytes) = usize_to_bytes(iteration)?;
out.extend_from_slice(&bytes[..size]);
let encrypted_content = base_encrypt(&fingerprint, &nonce, &key.into(), &mut content)?;
out.extend(encrypted_content);
out.push(HMAC_MODE);
Ok((out, key.into()))
}
pub fn hmac_decrypt(
verifier: Option<&[u8; 32]>,
encrypted_content: &[u8],
hmac_key: [u8; KEY_SIZE],
hmac_value: [u8; KEY_SIZE],
) -> anyhow::Result<(Vec<u8>, [u8; KEY_SIZE])> {
let nonce: [u8; NONCE_SIZE] = match encrypted_content[0..NONCE_SIZE].try_into() {
Ok(n) => n,
Err(_) => bail!("failed to convert to bytes"),
};
let encrypted_content = &encrypted_content[NONCE_SIZE..];
let key = blake2::Blake2sMac256::new_from_slice(&hmac_key)?
.chain_update(hmac_value)
.finalize_fixed();
base_decrypt(verifier, &nonce, &key.into(), encrypted_content)
}
pub fn hmac_extract(encrypted_content: &mut Vec<u8>) -> anyhow::Result<usize> {
let (itr_size, itr) = bytes_to_usize(&encrypted_content[NONCE_SIZE..NONCE_SIZE + 9])?;
encrypted_content.copy_within(NONCE_SIZE + itr_size.., NONCE_SIZE);
encrypted_content.truncate(encrypted_content.len() - itr_size - 1);
Ok(itr)
}
#[cfg(test)]
mod tests {
use super::{hmac_decrypt, hmac_encrypt, hmac_extract};
#[test]
fn test_hmac() -> anyhow::Result<()> {
let (fingerprint, verifier) = crate::generate_fingerprint();
let (hmac_key, hmac_value) = crate::generate_dh_keys();
let content = vec![0u8; 1024];
let (mut encrypted_content, content_key) =
hmac_encrypt(fingerprint, content.clone(), hmac_key, hmac_value, 42)?;
let itr = hmac_extract(&mut encrypted_content)?;
assert_eq!(itr, 42);
let (decrypted_content, decrypted_content_key) =
hmac_decrypt(Some(&verifier), &encrypted_content, hmac_key, hmac_value)?;
assert_eq!(content, decrypted_content);
assert_eq!(content_key, decrypted_content_key);
Ok(())
}
}