use std::fmt::Debug;
pub trait Digest: Clone + Sized {
const DIGEST_NAME: &'static str;
const OUTPUT_LEN: usize;
const BLOCK_LEN: usize;
type Output: Copy + Eq + Sized + Send + AsRef<[u8]> + for<'a> TryFrom<&'a [u8]> + Debug;
fn new() -> Self;
fn with_output_slice(slice: &[u8]) -> Option<Self> {
if slice.len() != Self::OUTPUT_LEN {
return None;
}
let mut buf = [0u8; 32];
buf.copy_from_slice(slice);
todo!()
}
fn digest(data: impl AsRef<[u8]>) -> Self::Output { Self::digest_concat([data]) }
fn digest_concat(data: impl IntoIterator<Item = impl AsRef<[u8]>>) -> Self::Output {
let mut engine = Self::new();
for item in data {
engine.input(item);
}
engine.finalize()
}
fn input(&mut self, data: impl AsRef<[u8]>);
fn finalize(self) -> Self::Output;
}
pub trait Digest32: Digest<Output = [u8; 32]> {}
pub trait Digest64: Digest<Output = [u8; 64]> {}
pub trait KeyedDigest: Digest {
type Key;
fn with_key(key: Self::Key) -> Self;
}
pub trait HmacDigest<D: Digest>: Digest {
fn with_key(key: impl AsRef<[u8]>) -> Self;
}
#[cfg(feature = "sha2")]
mod sha2 {
use ::sha2::digest::{FixedOutput, Update};
use super::*;
impl Digest for Sha256 {
const DIGEST_NAME: &'static str = "SHA256";
const OUTPUT_LEN: usize = 32;
const BLOCK_LEN: usize = 64;
type Output = [u8; 32];
fn new() -> Self { Sha256::default() }
fn input(&mut self, data: impl AsRef<[u8]>) { self.update(data.as_ref()); }
fn finalize(self) -> Self::Output {
let mut buf = [0u8; 32];
let out = &*self.finalize_fixed();
buf.copy_from_slice(out);
buf
}
}
}
#[cfg(feature = "sha2")]
pub use ::sha2::Sha256;
#[derive(Clone, Eq, PartialEq, Hash, Debug)]
pub struct Hmac<D: Digest> {
iengine: D,
oengine: D,
}
impl<D: Digest> Hmac<D> {
pub fn keyed(key: impl AsRef<[u8]>) -> Self {
let mut ipad = [0x36u8; 128];
let mut opad = [0x5cu8; 128];
let mut iengine = D::new();
let mut oengine = D::new();
let key = key.as_ref();
if key.len() > D::BLOCK_LEN {
let hash = D::digest(key);
for (b_i, b_h) in ipad.iter_mut().zip(hash.as_ref()) {
*b_i ^= *b_h;
}
for (b_o, b_h) in opad.iter_mut().zip(hash.as_ref()) {
*b_o ^= *b_h;
}
} else {
for (b_i, b_h) in ipad.iter_mut().zip(key) {
*b_i ^= *b_h;
}
for (b_o, b_h) in opad.iter_mut().zip(key) {
*b_o ^= *b_h;
}
};
iengine.input(&ipad[..D::BLOCK_LEN]);
oengine.input(&opad[..D::BLOCK_LEN]);
Self { iengine, oengine }
}
}
impl<D: Digest> Digest for Hmac<D> {
const DIGEST_NAME: &'static str = "HMAC";
const OUTPUT_LEN: usize = D::OUTPUT_LEN;
const BLOCK_LEN: usize = D::BLOCK_LEN;
type Output = D::Output;
fn new() -> Self { Self::keyed([]) }
fn input(&mut self, data: impl AsRef<[u8]>) { self.iengine.input(data); }
fn finalize(mut self) -> Self::Output {
let ihash = self.iengine.finalize();
self.oengine.input(ihash.as_ref());
self.oengine.finalize()
}
}
impl<D: Digest> HmacDigest<D> for Hmac<D> {
fn with_key(key: impl AsRef<[u8]>) -> Self { Self::keyed(key) }
}