use cipher::{BlockCipherEncBackend, BlockCipherEncClosure, BlockCipherEncrypt};
use core::fmt;
use digest::{
MacMarker, Output, OutputSizeUser, Reset,
array::{Array, ArraySize},
block_api::{
AlgorithmName, Block, BlockSizeUser, Buffer, BufferKindUser, FixedOutputCore, Lazy,
SmallBlockSizeUser, UpdateCore,
},
block_buffer::BlockSizes,
common::{InnerInit, InnerUser},
};
#[cfg(feature = "zeroize")]
use digest::zeroize::{Zeroize, ZeroizeOnDrop};
#[derive(Clone)]
pub struct BeltMacCore<C>
where
C: BlockCipherEncrypt + SmallBlockSizeUser,
{
cipher: C,
state: Block<C>,
r: Block<C>,
}
impl<C> BlockSizeUser for BeltMacCore<C>
where
C: BlockCipherEncrypt + SmallBlockSizeUser,
{
type BlockSize = C::BlockSize;
}
impl<C> OutputSizeUser for BeltMacCore<C>
where
C: BlockCipherEncrypt + SmallBlockSizeUser,
{
type OutputSize = C::BlockSize;
}
impl<C> InnerUser for BeltMacCore<C>
where
C: BlockCipherEncrypt + SmallBlockSizeUser,
{
type Inner = C;
}
impl<C> MacMarker for BeltMacCore<C> where C: BlockCipherEncrypt + SmallBlockSizeUser {}
impl<C> InnerInit for BeltMacCore<C>
where
C: BlockCipherEncrypt + SmallBlockSizeUser,
{
#[inline]
fn inner_init(cipher: C) -> Self {
let state = Default::default();
let mut r = Default::default();
cipher.encrypt_block(&mut r);
Self { cipher, state, r }
}
}
impl<C> BufferKindUser for BeltMacCore<C>
where
C: BlockCipherEncrypt + SmallBlockSizeUser,
{
type BufferKind = Lazy;
}
impl<C> UpdateCore for BeltMacCore<C>
where
C: BlockCipherEncrypt + SmallBlockSizeUser,
{
#[inline]
fn update_blocks(&mut self, blocks: &[Block<Self>]) {
struct Closure<'a, N: BlockSizes> {
state: &'a mut Block<Self>,
blocks: &'a [Block<Self>],
}
impl<N: BlockSizes> BlockSizeUser for Closure<'_, N> {
type BlockSize = N;
}
impl<N: BlockSizes> BlockCipherEncClosure for Closure<'_, N> {
#[inline(always)]
fn call<B: BlockCipherEncBackend<BlockSize = Self::BlockSize>>(self, backend: &B) {
for block in self.blocks {
xor(self.state, block);
backend.encrypt_block((self.state).into());
}
}
}
let Self { cipher, state, .. } = self;
cipher.encrypt_with_backend(Closure { state, blocks });
}
}
impl<C> Reset for BeltMacCore<C>
where
C: BlockCipherEncrypt + SmallBlockSizeUser,
{
#[inline(always)]
fn reset(&mut self) {
self.state = Default::default();
}
}
impl<C> FixedOutputCore for BeltMacCore<C>
where
C: BlockCipherEncrypt + SmallBlockSizeUser,
{
#[inline]
fn finalize_fixed_core(&mut self, buffer: &mut Buffer<Self>, out: &mut Output<Self>) {
let pos = buffer.get_pos();
let mut buf = buffer.pad_with_zeros();
let cipher = &mut self.cipher;
let r = &self.r;
let bs = r.len();
let mut new_r = Block::<C>::default();
if pos == bs {
let (h1, h2) = new_r.split_at_mut(bs - 4);
h1.copy_from_slice(&r[4..]);
for i in 0..4 {
h2[i] = r[i] ^ r[4 + i];
}
} else {
buf[pos] = 0x80;
let (h1, h2) = new_r.split_at_mut(4);
for i in 0..4 {
h1[i] = r[i] ^ r[bs - 4 + i];
}
h2.copy_from_slice(&r[..bs - 4]);
}
let mut state = self.state.clone();
xor(&mut state, &buf);
xor(&mut state, &new_r);
cipher.encrypt_block_b2b(&state, out);
}
}
impl<C> AlgorithmName for BeltMacCore<C>
where
C: BlockCipherEncrypt + SmallBlockSizeUser,
{
fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("BeltMac")
}
}
impl<C> fmt::Debug for BeltMacCore<C>
where
C: BlockCipherEncrypt + SmallBlockSizeUser,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("BeltMacCore { ... }")
}
}
#[cfg(feature = "zeroize")]
impl<C> Drop for BeltMacCore<C>
where
C: BlockCipherEncrypt + SmallBlockSizeUser,
{
fn drop(&mut self) {
self.state.zeroize();
}
}
#[cfg(feature = "zeroize")]
impl<C> ZeroizeOnDrop for BeltMacCore<C> where
C: BlockCipherEncrypt + SmallBlockSizeUser + ZeroizeOnDrop
{
}
#[inline(always)]
fn xor<N: ArraySize>(buf: &mut Array<u8, N>, data: &Array<u8, N>) {
for i in 0..N::USIZE {
buf[i] ^= data[i];
}
}