Skip to main content

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