blake2/
macros.rs

1macro_rules! blake2_impl {
2    (
3        $name:ident, $alg_name:expr, $word:ident, $vec:ident, $bytes:ident,
4        $block_size:ident, $R1:expr, $R2:expr, $R3:expr, $R4:expr, $IV:expr,
5        $vardoc:expr, $doc:expr,
6    ) => {
7        #[derive(Clone)]
8        #[doc=$vardoc]
9        pub struct $name {
10            h: [$vec; 2],
11            t: u64,
12            #[cfg(feature = "reset")]
13            h0: [$vec; 2],
14        }
15
16        impl $name {
17            #[inline(always)]
18            fn iv0() -> $vec {
19                $vec::new($IV[0], $IV[1], $IV[2], $IV[3])
20            }
21            #[inline(always)]
22            fn iv1() -> $vec {
23                $vec::new($IV[4], $IV[5], $IV[6], $IV[7])
24            }
25
26            /// Creates a new context with the full set of sequential-mode parameters.
27            pub fn new_with_params(
28                salt: &[u8],
29                persona: &[u8],
30                key_size: usize,
31                output_size: usize,
32            ) -> Self {
33                assert!(key_size <= $bytes::to_usize());
34                assert!(output_size <= $bytes::to_usize());
35
36                // The number of bytes needed to express two words.
37                let length = $bytes::to_usize() / 4;
38                assert!(salt.len() <= length);
39                assert!(persona.len() <= length);
40
41                // Build a parameter block
42                let mut p = [0 as $word; 8];
43                p[0] = 0x0101_0000 ^ ((key_size as $word) << 8) ^ (output_size as $word);
44
45                // salt is two words long
46                if salt.len() < length {
47                    let mut padded_salt =
48                        GenericArray::<u8, <$bytes as Div<U4>>::Output>::default();
49                    for i in 0..salt.len() {
50                        padded_salt[i] = salt[i];
51                    }
52                    p[4] = $word::from_le_bytes(padded_salt[0..length / 2].try_into().unwrap());
53                    p[5] = $word::from_le_bytes(
54                        padded_salt[length / 2..padded_salt.len()]
55                            .try_into()
56                            .unwrap(),
57                    );
58                } else {
59                    p[4] = $word::from_le_bytes(salt[0..salt.len() / 2].try_into().unwrap());
60                    p[5] =
61                        $word::from_le_bytes(salt[salt.len() / 2..salt.len()].try_into().unwrap());
62                }
63
64                // persona is also two words long
65                if persona.len() < length {
66                    let mut padded_persona =
67                        GenericArray::<u8, <$bytes as Div<U4>>::Output>::default();
68                    for i in 0..persona.len() {
69                        padded_persona[i] = persona[i];
70                    }
71                    p[6] = $word::from_le_bytes(padded_persona[0..length / 2].try_into().unwrap());
72                    p[7] = $word::from_le_bytes(
73                        padded_persona[length / 2..padded_persona.len()]
74                            .try_into()
75                            .unwrap(),
76                    );
77                } else {
78                    p[6] = $word::from_le_bytes(persona[0..length / 2].try_into().unwrap());
79                    p[7] = $word::from_le_bytes(
80                        persona[length / 2..persona.len()].try_into().unwrap(),
81                    );
82                }
83
84                let h = [
85                    Self::iv0() ^ $vec::new(p[0], p[1], p[2], p[3]),
86                    Self::iv1() ^ $vec::new(p[4], p[5], p[6], p[7]),
87                ];
88                $name {
89                    #[cfg(feature = "reset")]
90                    h0: h.clone(),
91                    h,
92                    t: 0,
93                }
94            }
95
96            fn finalize_with_flag(
97                &mut self,
98                final_block: &GenericArray<u8, $block_size>,
99                flag: $word,
100                out: &mut Output<Self>,
101            ) {
102                self.compress(final_block, !0, flag);
103                let buf = [self.h[0].to_le(), self.h[1].to_le()];
104                out.copy_from_slice(buf.as_bytes())
105            }
106
107            fn compress(&mut self, block: &Block<Self>, f0: $word, f1: $word) {
108                use $crate::consts::SIGMA;
109
110                #[cfg_attr(not(feature = "size_opt"), inline(always))]
111                fn quarter_round(v: &mut [$vec; 4], rd: u32, rb: u32, m: $vec) {
112                    v[0] = v[0].wrapping_add(v[1]).wrapping_add(m.from_le());
113                    v[3] = (v[3] ^ v[0]).rotate_right_const(rd);
114                    v[2] = v[2].wrapping_add(v[3]);
115                    v[1] = (v[1] ^ v[2]).rotate_right_const(rb);
116                }
117
118                #[cfg_attr(not(feature = "size_opt"), inline(always))]
119                fn shuffle(v: &mut [$vec; 4]) {
120                    v[1] = v[1].shuffle_left_1();
121                    v[2] = v[2].shuffle_left_2();
122                    v[3] = v[3].shuffle_left_3();
123                }
124
125                #[cfg_attr(not(feature = "size_opt"), inline(always))]
126                fn unshuffle(v: &mut [$vec; 4]) {
127                    v[1] = v[1].shuffle_right_1();
128                    v[2] = v[2].shuffle_right_2();
129                    v[3] = v[3].shuffle_right_3();
130                }
131
132                #[cfg_attr(not(feature = "size_opt"), inline(always))]
133                fn round(v: &mut [$vec; 4], m: &[$word; 16], s: &[usize; 16]) {
134                    quarter_round(v, $R1, $R2, $vec::gather(m, s[0], s[2], s[4], s[6]));
135                    quarter_round(v, $R3, $R4, $vec::gather(m, s[1], s[3], s[5], s[7]));
136
137                    shuffle(v);
138                    quarter_round(v, $R1, $R2, $vec::gather(m, s[8], s[10], s[12], s[14]));
139                    quarter_round(v, $R3, $R4, $vec::gather(m, s[9], s[11], s[13], s[15]));
140                    unshuffle(v);
141                }
142
143                let mut m: [$word; 16] = Default::default();
144                let n = core::mem::size_of::<$word>();
145                for (v, chunk) in m.iter_mut().zip(block.chunks_exact(n)) {
146                    *v = $word::from_ne_bytes(chunk.try_into().unwrap());
147                }
148                let h = &mut self.h;
149
150                let t0 = self.t as $word;
151                let t1 = match $bytes::to_u8() {
152                    64 => 0,
153                    32 => (self.t >> 32) as $word,
154                    _ => unreachable!(),
155                };
156
157                let mut v = [
158                    h[0],
159                    h[1],
160                    Self::iv0(),
161                    Self::iv1() ^ $vec::new(t0, t1, f0, f1),
162                ];
163
164                round(&mut v, &m, &SIGMA[0]);
165                round(&mut v, &m, &SIGMA[1]);
166                round(&mut v, &m, &SIGMA[2]);
167                round(&mut v, &m, &SIGMA[3]);
168                round(&mut v, &m, &SIGMA[4]);
169                round(&mut v, &m, &SIGMA[5]);
170                round(&mut v, &m, &SIGMA[6]);
171                round(&mut v, &m, &SIGMA[7]);
172                round(&mut v, &m, &SIGMA[8]);
173                round(&mut v, &m, &SIGMA[9]);
174                if $bytes::to_u8() == 64 {
175                    round(&mut v, &m, &SIGMA[0]);
176                    round(&mut v, &m, &SIGMA[1]);
177                }
178
179                h[0] = h[0] ^ (v[0] ^ v[2]);
180                h[1] = h[1] ^ (v[1] ^ v[3]);
181            }
182        }
183
184        impl HashMarker for $name {}
185
186        impl BlockSizeUser for $name {
187            type BlockSize = $block_size;
188        }
189
190        impl BufferKindUser for $name {
191            type BufferKind = Lazy;
192        }
193
194        impl UpdateCore for $name {
195            #[inline]
196            fn update_blocks(&mut self, blocks: &[Block<Self>]) {
197                for block in blocks {
198                    self.t += block.len() as u64;
199                    self.compress(block, 0, 0);
200                }
201            }
202        }
203
204        impl OutputSizeUser for $name {
205            type OutputSize = $bytes;
206        }
207
208        impl VariableOutputCore for $name {
209            const TRUNC_SIDE: TruncSide = TruncSide::Left;
210
211            #[inline]
212            fn new(output_size: usize) -> Result<Self, InvalidOutputSize> {
213                if output_size > Self::OutputSize::USIZE {
214                    return Err(InvalidOutputSize);
215                }
216                Ok(Self::new_with_params(&[], &[], 0, output_size))
217            }
218
219            #[inline]
220            fn finalize_variable_core(
221                &mut self,
222                buffer: &mut Buffer<Self>,
223                out: &mut Output<Self>,
224            ) {
225                self.t += buffer.get_pos() as u64;
226                let block = buffer.pad_with_zeros();
227                self.finalize_with_flag(block, 0, out);
228            }
229        }
230
231        #[cfg(feature = "reset")]
232        impl Reset for $name {
233            fn reset(&mut self) {
234                self.h = self.h0;
235                self.t = 0;
236            }
237        }
238
239        impl AlgorithmName for $name {
240            #[inline]
241            fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result {
242                f.write_str($alg_name)
243            }
244        }
245
246        impl fmt::Debug for $name {
247            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
248                f.write_str(concat!(stringify!($name), " { ... }"))
249            }
250        }
251    };
252}
253
254macro_rules! blake2_mac_impl {
255    (
256        $name:ident, $hash:ty, $max_size:ty, $doc:expr
257    ) => {
258        #[derive(Clone)]
259        #[doc=$doc]
260        pub struct $name<OutSize>
261        where
262            OutSize: ArrayLength<u8> + IsLessOrEqual<$max_size>,
263            LeEq<OutSize, $max_size>: NonZero,
264        {
265            core: $hash,
266            buffer: LazyBuffer<<$hash as BlockSizeUser>::BlockSize>,
267            #[cfg(feature = "reset")]
268            key_block: Key<Self>,
269            _out: PhantomData<OutSize>,
270        }
271
272        impl<OutSize> $name<OutSize>
273        where
274            OutSize: ArrayLength<u8> + IsLessOrEqual<$max_size>,
275            LeEq<OutSize, $max_size>: NonZero,
276        {
277            /// Create new instance using provided key, salt, and persona.
278            ///
279            /// Key length should not be bigger than block size, salt and persona
280            /// length should not be bigger than quarter of block size. If any
281            /// of those conditions is false the method will return an error.
282            #[inline]
283            pub fn new_with_salt_and_personal(
284                key: &[u8],
285                salt: &[u8],
286                persona: &[u8],
287            ) -> Result<Self, InvalidLength> {
288                let kl = key.len();
289                let bs = <$hash as BlockSizeUser>::BlockSize::USIZE;
290                let qbs = bs / 4;
291                if kl > bs || salt.len() > qbs || persona.len() > qbs {
292                    return Err(InvalidLength);
293                }
294                let mut padded_key = Block::<$hash>::default();
295                padded_key[..kl].copy_from_slice(key);
296                Ok(Self {
297                    core: <$hash>::new_with_params(salt, persona, key.len(), OutSize::USIZE),
298                    buffer: LazyBuffer::new(&padded_key),
299                    #[cfg(feature = "reset")]
300                    key_block: {
301                        let mut t = Key::<Self>::default();
302                        t[..kl].copy_from_slice(key);
303                        t
304                    },
305                    _out: PhantomData,
306                })
307            }
308        }
309
310        impl<OutSize> KeySizeUser for $name<OutSize>
311        where
312            OutSize: ArrayLength<u8> + IsLessOrEqual<$max_size>,
313            LeEq<OutSize, $max_size>: NonZero,
314        {
315            type KeySize = $max_size;
316        }
317
318        impl<OutSize> KeyInit for $name<OutSize>
319        where
320            OutSize: ArrayLength<u8> + IsLessOrEqual<$max_size>,
321            LeEq<OutSize, $max_size>: NonZero,
322        {
323            #[inline]
324            fn new(key: &Key<Self>) -> Self {
325                Self::new_from_slice(key).expect("Key has correct length")
326            }
327
328            #[inline]
329            fn new_from_slice(key: &[u8]) -> Result<Self, InvalidLength> {
330                let kl = key.len();
331                if kl > <Self as KeySizeUser>::KeySize::USIZE {
332                    return Err(InvalidLength);
333                }
334                let mut padded_key = Block::<$hash>::default();
335                padded_key[..kl].copy_from_slice(key);
336                Ok(Self {
337                    core: <$hash>::new_with_params(&[], &[], key.len(), OutSize::USIZE),
338                    buffer: LazyBuffer::new(&padded_key),
339                    #[cfg(feature = "reset")]
340                    key_block: {
341                        let mut t = Key::<Self>::default();
342                        t[..kl].copy_from_slice(key);
343                        t
344                    },
345                    _out: PhantomData,
346                })
347            }
348        }
349
350        impl<OutSize> Update for $name<OutSize>
351        where
352            OutSize: ArrayLength<u8> + IsLessOrEqual<$max_size>,
353            LeEq<OutSize, $max_size>: NonZero,
354        {
355            #[inline]
356            fn update(&mut self, input: &[u8]) {
357                let Self { core, buffer, .. } = self;
358                buffer.digest_blocks(input, |blocks| core.update_blocks(blocks));
359            }
360        }
361
362        impl<OutSize> OutputSizeUser for $name<OutSize>
363        where
364            OutSize: ArrayLength<u8> + IsLessOrEqual<$max_size> + 'static,
365            LeEq<OutSize, $max_size>: NonZero,
366        {
367            type OutputSize = OutSize;
368        }
369
370        impl<OutSize> FixedOutput for $name<OutSize>
371        where
372            OutSize: ArrayLength<u8> + IsLessOrEqual<$max_size> + 'static,
373            LeEq<OutSize, $max_size>: NonZero,
374        {
375            #[inline]
376            fn finalize_into(mut self, out: &mut Output<Self>) {
377                let Self { core, buffer, .. } = &mut self;
378                let mut full_res = Default::default();
379                core.finalize_variable_core(buffer, &mut full_res);
380                out.copy_from_slice(&full_res[..OutSize::USIZE]);
381            }
382        }
383
384        #[cfg(feature = "reset")]
385        impl<OutSize> Reset for $name<OutSize>
386        where
387            OutSize: ArrayLength<u8> + IsLessOrEqual<$max_size>,
388            LeEq<OutSize, $max_size>: NonZero,
389        {
390            fn reset(&mut self) {
391                self.core.reset();
392                let kl = self.key_block.len();
393                let mut padded_key = Block::<$hash>::default();
394                padded_key[..kl].copy_from_slice(&self.key_block);
395                self.buffer = LazyBuffer::new(&padded_key);
396            }
397        }
398
399        #[cfg(feature = "reset")]
400        impl<OutSize> FixedOutputReset for $name<OutSize>
401        where
402            OutSize: ArrayLength<u8> + IsLessOrEqual<$max_size>,
403            LeEq<OutSize, $max_size>: NonZero,
404        {
405            #[inline]
406            fn finalize_into_reset(&mut self, out: &mut Output<Self>) {
407                let Self { core, buffer, .. } = self;
408                let mut full_res = Default::default();
409                core.finalize_variable_core(buffer, &mut full_res);
410                out.copy_from_slice(&full_res[..OutSize::USIZE]);
411                self.reset();
412            }
413        }
414
415        impl<OutSize> MacMarker for $name<OutSize>
416        where
417            OutSize: ArrayLength<u8> + IsLessOrEqual<$max_size>,
418            LeEq<OutSize, $max_size>: NonZero,
419        {
420        }
421
422        impl<OutSize> fmt::Debug for $name<OutSize>
423        where
424            OutSize: ArrayLength<u8> + IsLessOrEqual<$max_size>,
425            LeEq<OutSize, $max_size>: NonZero,
426        {
427            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
428                write!(f, "{}{} {{ ... }}", stringify!($name), OutSize::USIZE)
429            }
430        }
431    };
432}