blake_hash/
lib.rs

1// copyright 2017 Kaz Wesley
2
3//! BLAKE, the SHA-3 hash finalist based on the ChaCha cipher
4
5#![cfg_attr(not(feature = "std"), no_std)]
6#[cfg(feature = "std")]
7use std as core;
8
9extern crate block_buffer;
10pub extern crate digest;
11pub extern crate simd;
12
13mod consts;
14
15use block_buffer::BlockBuffer;
16use core::convert::TryInto;
17use core::mem;
18use digest::generic_array::typenum::{PartialDiv, Unsigned, U2};
19use digest::generic_array::GenericArray;
20pub use digest::Digest;
21use simd::*;
22
23use simd::{
24    vec128_storage, vec256_storage, Machine, RotateEachWord32, RotateEachWord64, StoreBytes, Words4,
25};
26
27#[inline(always)]
28fn round32<M: Machine>(
29    (mut a, mut b, mut c, mut d): (M::u32x4, M::u32x4, M::u32x4, M::u32x4),
30    m0: M::u32x4,
31    m1: M::u32x4,
32) -> (M::u32x4, M::u32x4, M::u32x4, M::u32x4) {
33    a += m0;
34    a += b;
35    d ^= a;
36    d = d.rotate_each_word_right16();
37    c += d;
38    b ^= c;
39    b = b.rotate_each_word_right12();
40    a += m1;
41    a += b;
42    d ^= a;
43    d = d.rotate_each_word_right8();
44    c += d;
45    b ^= c;
46    b = b.rotate_each_word_right7();
47    (a, b, c, d)
48}
49
50#[inline(always)]
51fn round64<M: Machine>(
52    (mut a, mut b, mut c, mut d): (M::u64x4, M::u64x4, M::u64x4, M::u64x4),
53    m0: M::u64x4,
54    m1: M::u64x4,
55) -> (M::u64x4, M::u64x4, M::u64x4, M::u64x4) {
56    a += m0;
57    a += b;
58    d ^= a;
59    d = d.rotate_each_word_right32();
60    c += d;
61    b ^= c;
62    b = b.rotate_each_word_right25();
63    a += m1;
64    a += b;
65    d ^= a;
66    d = d.rotate_each_word_right16();
67    c += d;
68    b ^= c;
69    b = b.rotate_each_word_right11();
70    (a, b, c, d)
71}
72
73#[inline(always)]
74fn diagonalize<X4: Words4>((a, b, c, d): (X4, X4, X4, X4)) -> (X4, X4, X4, X4) {
75    // Since b has the critical data dependency, avoid rotating b to hide latency.
76    (a.shuffle1230(), b, c.shuffle3012(), d.shuffle2301())
77}
78
79#[inline(always)]
80fn undiagonalize<X4: Words4>((a, b, c, d): (X4, X4, X4, X4)) -> (X4, X4, X4, X4) {
81    (a.shuffle3012(), b, c.shuffle1230(), d.shuffle2301())
82}
83
84macro_rules! define_compressor {
85    ($compressor:ident, $storage:ident, $word:ident, $Bufsz:ty, $uval:expr, $rounds:expr, $round:ident, $X4:ident) => {
86        #[derive(Clone, Copy, Default)]
87        pub struct $compressor {
88            h: [$storage; 2],
89        }
90
91        #[allow(non_snake_case)]
92        pub mod $X4 {
93            use super::*;
94            #[inline(always)]
95            pub fn put_block<M: Machine>(mach: M, state: &mut $compressor, block: &GenericArray<u8, $Bufsz>, t: ($word, $word)) {
96                const U: [$word; 16] = $uval;
97
98                let mut m = [0; 16];
99                for (mx, b) in m
100                    .iter_mut()
101                    .zip(block.chunks_exact(mem::size_of::<$word>()))
102                {
103                    *mx = $word::from_be_bytes(b.try_into().unwrap());
104                }
105
106                let u = (mach.vec([U[0], U[1], U[2], U[3]]), mach.vec([U[4], U[5], U[6], U[7]]));
107                let mut xs: (M::$X4, M::$X4, _, _) = (mach.unpack(state.h[0]), mach.unpack(state.h[1]), u.0, u.1);
108                xs.3 ^= mach.vec([t.0, t.0, t.1, t.1]);
109                for sigma in &SIGMA[..$rounds] {
110                    macro_rules! m0 { ($e:expr) => (m[sigma[$e] as usize] ^ U[sigma[$e + 1] as usize]) }
111                    macro_rules! m1 { ($e:expr) => (m[sigma[$e + 1] as usize] ^ U[sigma[$e] as usize]) }
112                    // column step
113                    let m0 = mach.vec([m0!(0), m0!(2), m0!(4), m0!(6)]);
114                    let m1 = mach.vec([m1!(0), m1!(2), m1!(4), m1!(6)]);
115                    xs = $round::<M>(xs, m0, m1);
116                    // diagonal step
117                    let m0 = mach.vec([m0!(14), m0!(8), m0!(10), m0!(12)]);
118                    let m1 = mach.vec([m1!(14), m1!(8), m1!(10), m1!(12)]);
119                    xs = undiagonalize($round::<M>(diagonalize(xs), m0, m1));
120                }
121                let h: (M::$X4, M::$X4) = (mach.unpack(state.h[0]), mach.unpack(state.h[1]));
122                state.h[0] = (h.0 ^ xs.0 ^ xs.2).into();
123                state.h[1] = (h.1 ^ xs.1 ^ xs.3).into();
124            }
125        }
126
127        impl $compressor {
128            #[inline(always)]
129            fn put_block(&mut self, block: &GenericArray<u8, $Bufsz>, t: ($word, $word)) {
130                dispatch!(mach, M, {
131                    fn put_block(state: &mut $compressor, block: &GenericArray<u8, $Bufsz>, t: ($word, $word)) {
132                            $X4::put_block(mach, state, block, t)
133                    }
134                });
135                put_block(self, block, t);
136            }
137
138            fn finalize(self) -> GenericArray<u8, <$Bufsz as PartialDiv<U2>>::Output> {
139                dispatch_light256!(mach, M, {
140                    fn finalize(h: &[$storage; 2], out: &mut GenericArray<u8, <$Bufsz as PartialDiv<U2>>::Output>) {
141                        let len = out.len();
142                        let o = out.split_at_mut(len / 2);
143                        let h0: M::$X4 = mach.unpack(h[0]);
144                        let h1: M::$X4 = mach.unpack(h[1]);
145                        h0.write_be(o.0);
146                        h1.write_be(o.1);
147                    }
148                });
149                let mut out = GenericArray::default();
150                finalize(&self.h, &mut out);
151                out
152            }
153        }
154    };
155}
156
157macro_rules! define_hasher {
158    ($name:ident, $word:ident, $buf:expr, $Bufsz:ty, $bits:expr, $Bytes:ident,
159     $compressor:ident, $iv:expr) => {
160        #[derive(Clone)]
161        pub struct $name {
162            compressor: $compressor,
163            buffer: BlockBuffer<$Bufsz>,
164            t: ($word, $word),
165        }
166
167        impl $name {
168            fn increase_count(t: &mut ($word, $word), count: $word) {
169                let (new_t0, carry) = t.0.overflowing_add(count * 8);
170                t.0 = new_t0;
171                if carry {
172                    t.1 += 1;
173                }
174            }
175        }
176
177        impl core::fmt::Debug for $name {
178            fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
179                f.debug_struct("(Blake)").finish()
180            }
181        }
182
183        impl Default for $name {
184            fn default() -> Self {
185                Self {
186                    compressor: $compressor {
187                        h: [$iv[0].into(), $iv[1].into()],
188                    },
189                    buffer: BlockBuffer::default(),
190                    t: (0, 0),
191                }
192            }
193        }
194
195        impl digest::BlockInput for $name {
196            type BlockSize = $Bytes;
197        }
198
199        impl digest::Update for $name {
200            fn update(&mut self, data: impl AsRef<[u8]>) {
201                let compressor = &mut self.compressor;
202                let t = &mut self.t;
203                self.buffer.input_block(data.as_ref(), |block| {
204                    Self::increase_count(t, (mem::size_of::<$word>() * 16) as $word);
205                    compressor.put_block(block, *t);
206                });
207            }
208        }
209
210        impl digest::FixedOutputDirty for $name {
211            type OutputSize = $Bytes;
212
213            fn finalize_into_dirty(&mut self, out: &mut GenericArray<u8, Self::OutputSize>) {
214                let mut compressor = self.compressor;
215                let buffer = &mut self.buffer;
216                let mut t = self.t;
217
218                Self::increase_count(&mut t, buffer.position() as $word);
219
220                let mut msglen = [0u8; $buf / 8];
221                msglen[..$buf / 16].copy_from_slice(&t.1.to_be_bytes());
222                msglen[$buf / 16..].copy_from_slice(&t.0.to_be_bytes());
223
224                let footerlen = 1 + 2 * mem::size_of::<$word>();
225
226                // low bit indicates full-length variant
227                let isfull = ($bits == 8 * mem::size_of::<[$word; 8]>()) as u8;
228                // high bit indicates fit with no padding
229                let exactfit = if buffer.position() + footerlen != $buf {
230                    0x00
231                } else {
232                    0x80
233                };
234                let magic = isfull | exactfit;
235
236                // if header won't fit in last data block, pad to the end and start a new one
237                let extra_block = buffer.position() + footerlen > $buf;
238                if extra_block {
239                    let pad = $buf - buffer.position();
240                    buffer.input_block(&PADDING[..pad], |block| compressor.put_block(block, t));
241                    debug_assert_eq!(buffer.position(), 0);
242                }
243
244                // pad last block up to footer start point
245                if buffer.position() == 0 {
246                    // don't xor t when the block is only padding
247                    t = (0, 0);
248                }
249                // skip begin-padding byte if continuing padding
250                let x = extra_block as usize;
251                let (start, end) = (x, x + ($buf - footerlen - buffer.position()));
252                buffer.input_block(&PADDING[start..end], |_| unreachable!());
253                buffer.input_block(&[magic], |_| unreachable!());
254                buffer.input_block(&msglen, |block| compressor.put_block(block, t));
255                debug_assert_eq!(buffer.position(), 0);
256
257                out.copy_from_slice(&compressor.finalize()[..$Bytes::to_usize()]);
258            }
259        }
260
261        impl digest::Reset for $name {
262            fn reset(&mut self) {
263                *self = Self::default()
264            }
265        }
266    };
267}
268
269use consts::{
270    BLAKE224_IV, BLAKE256_IV, BLAKE256_U, BLAKE384_IV, BLAKE512_IV, BLAKE512_U, PADDING, SIGMA,
271};
272use digest::generic_array::typenum::{U128, U28, U32, U48, U64};
273
274#[rustfmt::skip]
275define_compressor!(Compressor256, vec128_storage, u32, U64, BLAKE256_U, 14, round32, u32x4);
276
277#[rustfmt::skip]
278define_hasher!(Blake224, u32, 64, U64, 224, U28, Compressor256, BLAKE224_IV);
279
280#[rustfmt::skip]
281define_hasher!(Blake256, u32, 64, U64, 256, U32, Compressor256, BLAKE256_IV);
282
283#[rustfmt::skip]
284define_compressor!(Compressor512, vec256_storage, u64, U128, BLAKE512_U, 16, round64, u64x4);
285
286#[rustfmt::skip]
287define_hasher!(Blake384, u64, 128, U128, 384, U48, Compressor512, BLAKE384_IV);
288
289#[rustfmt::skip]
290define_hasher!(Blake512, u64, 128, U128, 512, U64, Compressor512, BLAKE512_IV);