1#![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 (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 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 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 let isfull = ($bits == 8 * mem::size_of::<[$word; 8]>()) as u8;
228 let exactfit = if buffer.position() + footerlen != $buf {
230 0x00
231 } else {
232 0x80
233 };
234 let magic = isfull | exactfit;
235
236 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 if buffer.position() == 0 {
246 t = (0, 0);
248 }
249 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);