use super::{get_der_key, IPAD, OPAD};
use core::fmt;
use digest::{
crypto_common::{Block, BlockSizeUser, InvalidLength, Key, KeySizeUser},
Digest, FixedOutput, KeyInit, MacMarker, Output, OutputSizeUser, Update,
};
#[cfg(feature = "reset")]
use digest::{FixedOutputReset, Reset};
#[derive(Clone)]
pub struct SimpleHmac<D: Digest + BlockSizeUser> {
digest: D,
opad_key: Block<D>,
#[cfg(feature = "reset")]
ipad_key: Block<D>,
}
impl<D: Digest + BlockSizeUser> KeySizeUser for SimpleHmac<D> {
type KeySize = D::BlockSize;
}
impl<D: Digest + BlockSizeUser> MacMarker for SimpleHmac<D> {}
impl<D: Digest + BlockSizeUser> KeyInit for SimpleHmac<D> {
fn new(key: &Key<Self>) -> Self {
Self::new_from_slice(key.as_slice()).unwrap()
}
#[inline]
fn new_from_slice(key: &[u8]) -> Result<Self, InvalidLength> {
let der_key = get_der_key::<D>(key);
let mut ipad_key = der_key.clone();
for b in ipad_key.iter_mut() {
*b ^= IPAD;
}
let mut digest = D::new();
digest.update(&ipad_key);
let mut opad_key = der_key;
for b in opad_key.iter_mut() {
*b ^= OPAD;
}
Ok(Self {
digest,
opad_key,
#[cfg(feature = "reset")]
ipad_key,
})
}
}
impl<D: Digest + BlockSizeUser> Update for SimpleHmac<D> {
#[inline(always)]
fn update(&mut self, data: &[u8]) {
self.digest.update(data);
}
}
impl<D: Digest + BlockSizeUser> OutputSizeUser for SimpleHmac<D> {
type OutputSize = D::OutputSize;
}
impl<D: Digest + BlockSizeUser> FixedOutput for SimpleHmac<D> {
fn finalize_into(self, out: &mut Output<Self>) {
let mut h = D::new();
h.update(&self.opad_key);
h.update(&self.digest.finalize());
h.finalize_into(out);
}
}
impl<D: Digest + BlockSizeUser + fmt::Debug> fmt::Debug for SimpleHmac<D> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("SimpleHmac")
.field("digest", &self.digest)
.field("..", &"..")
.finish()
}
}
#[cfg(feature = "reset")]
#[cfg_attr(docsrs, doc(cfg(feature = "reset")))]
impl<D: Digest + BlockSizeUser + Reset> Reset for SimpleHmac<D> {
fn reset(&mut self) {
Reset::reset(&mut self.digest);
self.digest.update(&self.ipad_key);
}
}
#[cfg(feature = "reset")]
#[cfg_attr(docsrs, doc(cfg(feature = "reset")))]
impl<D: Digest + BlockSizeUser + FixedOutputReset> FixedOutputReset for SimpleHmac<D> {
fn finalize_into_reset(&mut self, out: &mut Output<Self>) {
let mut h = D::new();
Update::update(&mut h, &self.opad_key);
Update::update(&mut h, &self.digest.finalize_reset());
Update::update(&mut self.digest, &self.ipad_key);
Digest::finalize_into(h, out);
}
}