#![no_std]
#![doc(
html_logo_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg",
html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg"
)]
#![forbid(unsafe_code)]
#![warn(missing_docs, rust_2018_idioms)]
#[cfg(feature = "std")]
extern crate std;
pub use crypto_mac::{self, Mac, NewMac};
pub use digest;
use core::{cmp::min, fmt};
use crypto_mac::{
generic_array::{sequence::GenericSequence, ArrayLength, GenericArray},
InvalidKeyLength, Output,
};
use digest::{BlockInput, FixedOutput, Reset, Update};
const IPAD: u8 = 0x36;
const OPAD: u8 = 0x5C;
pub struct Hmac<D>
where
D: Update + BlockInput + FixedOutput + Reset + Default + Clone,
D::BlockSize: ArrayLength<u8>,
{
digest: D,
i_key_pad: GenericArray<u8, D::BlockSize>,
opad_digest: D,
}
impl<D> Clone for Hmac<D>
where
D: Update + BlockInput + FixedOutput + Reset + Default + Clone,
D::BlockSize: ArrayLength<u8>,
{
fn clone(&self) -> Hmac<D> {
Hmac {
digest: self.digest.clone(),
i_key_pad: self.i_key_pad.clone(),
opad_digest: self.opad_digest.clone(),
}
}
}
impl<D> fmt::Debug for Hmac<D>
where
D: Update + BlockInput + FixedOutput + Reset + Default + Clone + fmt::Debug,
D::BlockSize: ArrayLength<u8>,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Hmac")
.field("digest", &self.digest)
.field("i_key_pad", &self.i_key_pad)
.field("opad_digest", &self.opad_digest)
.finish()
}
}
impl<D> NewMac for Hmac<D>
where
D: Update + BlockInput + FixedOutput + Reset + Default + Clone,
D::BlockSize: ArrayLength<u8>,
D::OutputSize: ArrayLength<u8>,
{
type KeySize = D::BlockSize;
fn new(key: &GenericArray<u8, Self::KeySize>) -> Self {
Self::new_varkey(key.as_slice()).unwrap()
}
#[inline]
fn new_varkey(key: &[u8]) -> Result<Self, InvalidKeyLength> {
let mut hmac = Self {
digest: Default::default(),
i_key_pad: GenericArray::generate(|_| IPAD),
opad_digest: Default::default(),
};
let mut opad = GenericArray::<u8, D::BlockSize>::generate(|_| OPAD);
debug_assert!(hmac.i_key_pad.len() == opad.len());
if key.len() <= hmac.i_key_pad.len() {
for (k_idx, k_itm) in key.iter().enumerate() {
hmac.i_key_pad[k_idx] ^= *k_itm;
opad[k_idx] ^= *k_itm;
}
} else {
let mut digest = D::default();
digest.update(key);
let output = digest.finalize_fixed();
let n = min(output.len(), hmac.i_key_pad.len());
for idx in 0..n {
hmac.i_key_pad[idx] ^= output[idx];
opad[idx] ^= output[idx];
}
}
hmac.digest.update(&hmac.i_key_pad);
hmac.opad_digest.update(&opad);
Ok(hmac)
}
}
impl<D> Mac for Hmac<D>
where
D: Update + BlockInput + FixedOutput + Reset + Default + Clone,
D::BlockSize: ArrayLength<u8>,
D::OutputSize: ArrayLength<u8>,
{
type OutputSize = D::OutputSize;
#[inline]
fn update(&mut self, data: &[u8]) {
self.digest.update(data);
}
#[inline]
fn finalize(self) -> Output<Self> {
let mut opad_digest = self.opad_digest.clone();
let hash = self.digest.finalize_fixed();
opad_digest.update(&hash);
Output::new(opad_digest.finalize_fixed())
}
#[inline]
fn reset(&mut self) {
self.digest.reset();
self.digest.update(&self.i_key_pad);
}
}
#[cfg(feature = "std")]
impl<D> std::io::Write for Hmac<D>
where
D: Update + BlockInput + FixedOutput + Reset + Default + Clone,
D::BlockSize: ArrayLength<u8>,
D::OutputSize: ArrayLength<u8>,
{
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
Mac::update(self, buf);
Ok(buf.len())
}
fn flush(&mut self) -> std::io::Result<()> {
Ok(())
}
}