use crate::error::{CryptoKitError, Result};
pub trait HMAC {
const OUTPUT_SIZE: usize;
fn authenticate(key: &[u8], data: &[u8]) -> Result<Vec<u8>> {
let mut output = vec![0u8; Self::OUTPUT_SIZE];
Self::authenticate_to(key, data, &mut output)?;
Ok(output)
}
fn authenticate_to(key: &[u8], data: &[u8], output: &mut [u8]) -> Result<()>;
fn output_size() -> usize {
Self::OUTPUT_SIZE
}
}
pub fn verify_hmac<T: AsRef<[u8]>>(expected: T, computed: T) -> bool {
let expected_bytes = expected.as_ref();
let computed_bytes = computed.as_ref();
if expected_bytes.len() != computed_bytes.len() {
return false;
}
constant_time_eq(expected_bytes, computed_bytes)
}
pub fn constant_time_eq(a: &[u8], b: &[u8]) -> bool {
if a.len() != b.len() {
return false;
}
let mut result = 0u8;
for (x, y) in a.iter().zip(b.iter()) {
result |= x ^ y;
}
result == 0
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum HmacAlgorithm {
Sha1,
Sha256,
Sha384,
Sha512,
}
impl HmacAlgorithm {
pub fn output_size(&self) -> usize {
match self {
HmacAlgorithm::Sha1 => 20,
HmacAlgorithm::Sha256 => 32,
HmacAlgorithm::Sha384 => 48,
HmacAlgorithm::Sha512 => 64,
}
}
pub fn compute(&self, key: &[u8], data: &[u8]) -> Result<Vec<u8>> {
let mut output = vec![0u8; self.output_size()];
self.compute_to(key, data, &mut output)?;
Ok(output)
}
pub fn compute_to(&self, key: &[u8], data: &[u8], output: &mut [u8]) -> Result<()> {
assert!(
output.len() >= self.output_size(),
"Output buffer too small: {} < {}",
output.len(),
self.output_size()
);
match self {
HmacAlgorithm::Sha1 => {
use crate::authentication::sha1::hmac_sha1_to;
hmac_sha1_to(key, data, output)
}
HmacAlgorithm::Sha256 => {
use crate::authentication::sha256::hmac_sha256_to;
hmac_sha256_to(key, data, output)
}
HmacAlgorithm::Sha384 => {
use crate::authentication::sha384::hmac_sha384_to;
hmac_sha384_to(key, data, output)
}
HmacAlgorithm::Sha512 => {
use crate::authentication::sha512::hmac_sha512_to;
hmac_sha512_to(key, data, output)
}
}
}
pub fn verify(&self, key: &[u8], data: &[u8], expected_hmac: &[u8]) -> Result<bool> {
let computed = self.compute(key, data)?;
Ok(constant_time_eq(&computed, expected_hmac))
}
}
pub struct HmacBuilder {
algorithm: HmacAlgorithm,
key: Vec<u8>,
}
impl HmacBuilder {
pub fn new(algorithm: HmacAlgorithm) -> Self {
Self {
algorithm,
key: Vec::new(),
}
}
pub fn key(mut self, key: &[u8]) -> Self {
self.key = key.to_vec();
self
}
pub fn compute(&self, data: &[u8]) -> Result<Vec<u8>> {
if self.key.is_empty() {
return Err(CryptoKitError::InvalidKey);
}
self.algorithm.compute(&self.key, data)
}
pub fn compute_to(&self, data: &[u8], output: &mut [u8]) -> Result<()> {
if self.key.is_empty() {
return Err(CryptoKitError::InvalidKey);
}
self.algorithm.compute_to(&self.key, data, output)
}
pub fn verify(&self, data: &[u8], expected_hmac: &[u8]) -> Result<bool> {
let computed = self.compute(data)?;
Ok(constant_time_eq(&computed, expected_hmac))
}
pub fn output_size(&self) -> usize {
self.algorithm.output_size()
}
}