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}; /// Simplified HMAC instance able to operate over hash functions /// which do not expose block-level API and hash functions which /// process blocks lazily (e.g. BLAKE2). #[derive(Clone)] pub struct SimpleHmac { digest: D, opad_key: Block, #[cfg(feature = "reset")] ipad_key: Block, } impl KeySizeUser for SimpleHmac { type KeySize = D::BlockSize; } impl MacMarker for SimpleHmac {} impl KeyInit for SimpleHmac { fn new(key: &Key) -> Self { Self::new_from_slice(key.as_slice()).unwrap() } #[inline] fn new_from_slice(key: &[u8]) -> Result { let der_key = get_der_key::(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 Update for SimpleHmac { #[inline(always)] fn update(&mut self, data: &[u8]) { self.digest.update(data); } } impl OutputSizeUser for SimpleHmac { type OutputSize = D::OutputSize; } impl FixedOutput for SimpleHmac { fn finalize_into(self, out: &mut Output) { let mut h = D::new(); h.update(&self.opad_key); h.update(&self.digest.finalize()); h.finalize_into(out); } } impl fmt::Debug for SimpleHmac { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("SimpleHmac") .field("digest", &self.digest) // TODO: replace with `finish_non_exhaustive` on MSRV // bump to 1.53 .field("..", &"..") .finish() } } #[cfg(feature = "reset")] #[cfg_attr(docsrs, doc(cfg(feature = "reset")))] impl Reset for SimpleHmac { 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 FixedOutputReset for SimpleHmac { fn finalize_into_reset(&mut self, out: &mut Output) { 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); } }