Skip to main content

retail_mac/
block_api.rs

1use cipher::{
2    BlockCipherDecrypt, BlockCipherEncBackend, BlockCipherEncClosure, BlockCipherEncrypt,
3    InvalidLength, KeySizeUser,
4};
5use core::{fmt, ops::Mul};
6use digest::{
7    Key, KeyInit, MacMarker, Output, OutputSizeUser, Reset,
8    array::{Array, ArraySize},
9    block_api::{
10        AlgorithmName, Block, BlockSizeUser, Buffer, BufferKindUser, Eager, FixedOutputCore,
11        SmallBlockSizeUser, UpdateCore,
12    },
13    block_buffer::BlockSizes,
14    typenum::{Prod, U2},
15};
16
17#[cfg(feature = "zeroize")]
18use cipher::zeroize::{Zeroize, ZeroizeOnDrop};
19
20/// Generic core Retail MAC instance, which operates over blocks.
21#[derive(Clone)]
22pub struct RetailMacCore<C>
23where
24    C: BlockCipherEncrypt + SmallBlockSizeUser + BlockCipherDecrypt,
25{
26    cipher: C,
27    cipher_prime: C,
28    state: Block<C>,
29}
30
31impl<C> BlockSizeUser for RetailMacCore<C>
32where
33    C: BlockCipherEncrypt + SmallBlockSizeUser + BlockCipherDecrypt,
34{
35    type BlockSize = C::BlockSize;
36}
37
38impl<C> OutputSizeUser for RetailMacCore<C>
39where
40    C: BlockCipherEncrypt + SmallBlockSizeUser + BlockCipherDecrypt,
41{
42    type OutputSize = C::BlockSize;
43}
44
45impl<C> KeySizeUser for RetailMacCore<C>
46where
47    C: BlockCipherEncrypt + SmallBlockSizeUser + BlockCipherDecrypt,
48    <C as SmallBlockSizeUser>::_BlockSize: Mul<U2>,
49    Prod<<C as SmallBlockSizeUser>::_BlockSize, U2>: ArraySize,
50{
51    type KeySize = Prod<<C as SmallBlockSizeUser>::_BlockSize, U2>;
52}
53
54impl<C> MacMarker for RetailMacCore<C> where
55    C: BlockCipherEncrypt + SmallBlockSizeUser + BlockCipherDecrypt
56{
57}
58
59impl<C> BufferKindUser for RetailMacCore<C>
60where
61    C: BlockCipherEncrypt + SmallBlockSizeUser + BlockCipherDecrypt,
62{
63    type BufferKind = Eager;
64}
65
66impl<C> KeyInit for RetailMacCore<C>
67where
68    C: BlockCipherEncrypt + SmallBlockSizeUser + BlockCipherDecrypt + KeyInit,
69    <C as SmallBlockSizeUser>::_BlockSize: Mul<U2>,
70    Prod<<C as SmallBlockSizeUser>::_BlockSize, U2>: ArraySize,
71{
72    #[inline(always)]
73    fn new(key: &Key<Self>) -> Self {
74        Self::new_from_slice(key.as_slice()).expect("HMAC accepts keys of any length")
75    }
76
77    #[inline(always)]
78    fn new_from_slice(key: &[u8]) -> Result<Self, InvalidLength> {
79        let cipher = C::new_from_slice(&key[..key.len() / 2])?;
80        let cipher_prime = C::new_from_slice(&key[key.len() / 2..])?;
81        Ok(Self {
82            cipher,
83            cipher_prime,
84            state: Block::<Self>::default(),
85        })
86    }
87}
88
89impl<C> UpdateCore for RetailMacCore<C>
90where
91    C: BlockCipherEncrypt + SmallBlockSizeUser + BlockCipherDecrypt,
92{
93    #[inline]
94    fn update_blocks(&mut self, blocks: &[Block<Self>]) {
95        struct Closure<'a, N: BlockSizes> {
96            state: &'a mut Block<Self>,
97            blocks: &'a [Block<Self>],
98        }
99
100        impl<N: BlockSizes> BlockSizeUser for Closure<'_, N> {
101            type BlockSize = N;
102        }
103
104        impl<N: BlockSizes> BlockCipherEncClosure for Closure<'_, N> {
105            #[inline(always)]
106            fn call<B: BlockCipherEncBackend<BlockSize = Self::BlockSize>>(self, backend: &B) {
107                for block in self.blocks {
108                    xor(self.state, block);
109                    backend.encrypt_block((self.state).into());
110                }
111            }
112        }
113
114        let Self { cipher, state, .. } = self;
115        cipher.encrypt_with_backend(Closure { state, blocks });
116    }
117}
118
119impl<C> Reset for RetailMacCore<C>
120where
121    C: BlockCipherEncrypt + SmallBlockSizeUser + BlockCipherDecrypt,
122{
123    #[inline(always)]
124    fn reset(&mut self) {
125        self.state = Default::default();
126    }
127}
128
129impl<C> FixedOutputCore for RetailMacCore<C>
130where
131    C: BlockCipherEncrypt + SmallBlockSizeUser + BlockCipherDecrypt,
132{
133    #[inline]
134    fn finalize_fixed_core(&mut self, buffer: &mut Buffer<Self>, out: &mut Output<Self>) {
135        let Self {
136            state,
137            cipher,
138            cipher_prime,
139        } = self;
140        let pos = buffer.get_pos();
141        if pos != 0 {
142            xor(state, &buffer.pad_with_zeros());
143            cipher.encrypt_block(state);
144        }
145        cipher_prime.decrypt_block(state);
146        cipher.encrypt_block(state);
147        out.copy_from_slice(state);
148    }
149}
150
151impl<C> AlgorithmName for RetailMacCore<C>
152where
153    C: BlockCipherEncrypt + SmallBlockSizeUser + BlockCipherDecrypt + AlgorithmName,
154{
155    fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result {
156        f.write_str("RetailMac<")?;
157        <C as AlgorithmName>::write_alg_name(f)?;
158        f.write_str(">")
159    }
160}
161
162impl<C> fmt::Debug for RetailMacCore<C>
163where
164    C: BlockCipherEncrypt + SmallBlockSizeUser + BlockCipherDecrypt + AlgorithmName,
165{
166    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
167        f.write_str("RetailMacCore<")?;
168        <C as AlgorithmName>::write_alg_name(f)?;
169        f.write_str("> { ... }")
170    }
171}
172
173#[cfg(feature = "zeroize")]
174impl<C> Drop for RetailMacCore<C>
175where
176    C: BlockCipherEncrypt + SmallBlockSizeUser + BlockCipherDecrypt,
177{
178    fn drop(&mut self) {
179        self.state.zeroize();
180    }
181}
182
183#[cfg(feature = "zeroize")]
184impl<C> ZeroizeOnDrop for RetailMacCore<C> where
185    C: BlockCipherEncrypt + SmallBlockSizeUser + BlockCipherDecrypt + ZeroizeOnDrop
186{
187}
188
189#[inline(always)]
190fn xor<N: ArraySize>(buf: &mut Array<u8, N>, data: &Array<u8, N>) {
191    for i in 0..N::USIZE {
192        buf[i] ^= data[i];
193    }
194}