retail-mac 0.1.0

Implementation of Retail Message Authentication Code (Retail MAC)
Documentation
use cipher::{
    BlockCipherDecrypt, BlockCipherEncBackend, BlockCipherEncClosure, BlockCipherEncrypt,
    InvalidLength, KeySizeUser,
};
use core::{fmt, ops::Mul};
use digest::{
    Key, KeyInit, MacMarker, Output, OutputSizeUser, Reset,
    array::{Array, ArraySize},
    block_api::{
        AlgorithmName, Block, BlockSizeUser, Buffer, BufferKindUser, Eager, FixedOutputCore,
        SmallBlockSizeUser, UpdateCore,
    },
    block_buffer::BlockSizes,
    typenum::{Prod, U2},
};

#[cfg(feature = "zeroize")]
use cipher::zeroize::{Zeroize, ZeroizeOnDrop};

/// Generic core Retail MAC instance, which operates over blocks.
#[derive(Clone)]
pub struct RetailMacCore<C>
where
    C: BlockCipherEncrypt + SmallBlockSizeUser + BlockCipherDecrypt,
{
    cipher: C,
    cipher_prime: C,
    state: Block<C>,
}

impl<C> BlockSizeUser for RetailMacCore<C>
where
    C: BlockCipherEncrypt + SmallBlockSizeUser + BlockCipherDecrypt,
{
    type BlockSize = C::BlockSize;
}

impl<C> OutputSizeUser for RetailMacCore<C>
where
    C: BlockCipherEncrypt + SmallBlockSizeUser + BlockCipherDecrypt,
{
    type OutputSize = C::BlockSize;
}

impl<C> KeySizeUser for RetailMacCore<C>
where
    C: BlockCipherEncrypt + SmallBlockSizeUser + BlockCipherDecrypt,
    <C as SmallBlockSizeUser>::_BlockSize: Mul<U2>,
    Prod<<C as SmallBlockSizeUser>::_BlockSize, U2>: ArraySize,
{
    type KeySize = Prod<<C as SmallBlockSizeUser>::_BlockSize, U2>;
}

impl<C> MacMarker for RetailMacCore<C> where
    C: BlockCipherEncrypt + SmallBlockSizeUser + BlockCipherDecrypt
{
}

impl<C> BufferKindUser for RetailMacCore<C>
where
    C: BlockCipherEncrypt + SmallBlockSizeUser + BlockCipherDecrypt,
{
    type BufferKind = Eager;
}

impl<C> KeyInit for RetailMacCore<C>
where
    C: BlockCipherEncrypt + SmallBlockSizeUser + BlockCipherDecrypt + KeyInit,
    <C as SmallBlockSizeUser>::_BlockSize: Mul<U2>,
    Prod<<C as SmallBlockSizeUser>::_BlockSize, U2>: ArraySize,
{
    #[inline(always)]
    fn new(key: &Key<Self>) -> Self {
        Self::new_from_slice(key.as_slice()).expect("HMAC accepts keys of any length")
    }

    #[inline(always)]
    fn new_from_slice(key: &[u8]) -> Result<Self, InvalidLength> {
        let cipher = C::new_from_slice(&key[..key.len() / 2])?;
        let cipher_prime = C::new_from_slice(&key[key.len() / 2..])?;
        Ok(Self {
            cipher,
            cipher_prime,
            state: Block::<Self>::default(),
        })
    }
}

impl<C> UpdateCore for RetailMacCore<C>
where
    C: BlockCipherEncrypt + SmallBlockSizeUser + BlockCipherDecrypt,
{
    #[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 RetailMacCore<C>
where
    C: BlockCipherEncrypt + SmallBlockSizeUser + BlockCipherDecrypt,
{
    #[inline(always)]
    fn reset(&mut self) {
        self.state = Default::default();
    }
}

impl<C> FixedOutputCore for RetailMacCore<C>
where
    C: BlockCipherEncrypt + SmallBlockSizeUser + BlockCipherDecrypt,
{
    #[inline]
    fn finalize_fixed_core(&mut self, buffer: &mut Buffer<Self>, out: &mut Output<Self>) {
        let Self {
            state,
            cipher,
            cipher_prime,
        } = self;
        let pos = buffer.get_pos();
        if pos != 0 {
            xor(state, &buffer.pad_with_zeros());
            cipher.encrypt_block(state);
        }
        cipher_prime.decrypt_block(state);
        cipher.encrypt_block(state);
        out.copy_from_slice(state);
    }
}

impl<C> AlgorithmName for RetailMacCore<C>
where
    C: BlockCipherEncrypt + SmallBlockSizeUser + BlockCipherDecrypt + AlgorithmName,
{
    fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.write_str("RetailMac<")?;
        <C as AlgorithmName>::write_alg_name(f)?;
        f.write_str(">")
    }
}

impl<C> fmt::Debug for RetailMacCore<C>
where
    C: BlockCipherEncrypt + SmallBlockSizeUser + BlockCipherDecrypt + AlgorithmName,
{
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.write_str("RetailMacCore<")?;
        <C as AlgorithmName>::write_alg_name(f)?;
        f.write_str("> { ... }")
    }
}

#[cfg(feature = "zeroize")]
impl<C> Drop for RetailMacCore<C>
where
    C: BlockCipherEncrypt + SmallBlockSizeUser + BlockCipherDecrypt,
{
    fn drop(&mut self) {
        self.state.zeroize();
    }
}

#[cfg(feature = "zeroize")]
impl<C> ZeroizeOnDrop for RetailMacCore<C> where
    C: BlockCipherEncrypt + SmallBlockSizeUser + BlockCipherDecrypt + 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];
    }
}