use crypto_core::{CryptoError, MacAlgorithm, MacFailureKind, MacHash};
use subtle::ConstantTimeEq;
use zeroize::{Zeroize, ZeroizeOnDrop, Zeroizing};
pub const HMAC_SHA256_TAG_LENGTH: usize = 32;
pub const HMAC_SHA384_TAG_LENGTH: usize = 48;
pub const HMAC_SHA512_TAG_LENGTH: usize = 64;
pub const HMAC_MAX_KEY_LENGTH: usize = 4096;
#[derive(Zeroize, ZeroizeOnDrop)]
pub struct HmacKey {
bytes: Zeroizing<Vec<u8>>,
}
impl HmacKey {
pub fn from_slice(input: &[u8]) -> Result<Self, CryptoError> {
if input.is_empty() || input.len() > HMAC_MAX_KEY_LENGTH {
return Err(CryptoError::Mac {
hash: MacHash::Sha2_256,
kind: MacFailureKind::InvalidKeyLength,
});
}
Ok(Self {
bytes: Zeroizing::new(input.to_vec()),
})
}
pub fn as_bytes(&self) -> &[u8] {
&self.bytes
}
}
#[derive(Clone, Zeroize, ZeroizeOnDrop)]
pub struct HmacTag {
bytes: [u8; HMAC_SHA512_TAG_LENGTH],
len: usize,
}
impl PartialEq for HmacTag {
fn eq(&self, other: &Self) -> bool {
bool::from(self.bytes.ct_eq(&other.bytes) & self.len.ct_eq(&other.len))
}
}
impl Eq for HmacTag {}
impl HmacTag {
pub fn from_slice(algorithm: MacAlgorithm, input: &[u8]) -> Result<Self, CryptoError> {
let expected = tag_length(algorithm);
if input.len() != expected {
return Err(CryptoError::Mac {
hash: mac_hash(algorithm),
kind: MacFailureKind::InvalidTagLength,
});
}
let mut bytes = [0u8; HMAC_SHA512_TAG_LENGTH];
bytes[..expected].copy_from_slice(input);
Ok(Self {
bytes,
len: expected,
})
}
pub fn as_bytes(&self) -> &[u8] {
&self.bytes[..self.len]
}
pub fn len(&self) -> usize {
self.len
}
pub fn is_empty(&self) -> bool {
self.len == 0
}
pub fn into_vec(self) -> Vec<u8> {
self.as_bytes().to_vec()
}
}
impl core::fmt::Debug for HmacTag {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "HmacTag(len={})", self.len)
}
}
pub(crate) fn tag_length(algorithm: MacAlgorithm) -> usize {
match algorithm {
MacAlgorithm::HmacSha256 => HMAC_SHA256_TAG_LENGTH,
MacAlgorithm::HmacSha384 => HMAC_SHA384_TAG_LENGTH,
MacAlgorithm::HmacSha512 => HMAC_SHA512_TAG_LENGTH,
}
}
pub(crate) fn mac_hash(algorithm: MacAlgorithm) -> MacHash {
match algorithm {
MacAlgorithm::HmacSha256 => MacHash::Sha2_256,
MacAlgorithm::HmacSha384 => MacHash::Sha2_384,
MacAlgorithm::HmacSha512 => MacHash::Sha2_512,
}
}