Skip to main content

cryptoxide/hashing/
blake2s.rs

1//! Blake2S hash function
2//!
3//! Blake2 [Specification][1].
4//!
5//! # Example
6//!
7//! Hashing using Blake2s-256:
8//!
9//! ```
10//! use cryptoxide::hashing::blake2s::Blake2s;
11//!
12//! let mut context = Blake2s::<256>::new();
13//! context.update_mut(b"hello world");
14//! let digest = context.finalize();
15//! ```
16//!
17//! MAC using Blake2s-224 with 16-bytes key :
18//!
19//! ```
20//! use cryptoxide::hashing::blake2s::Blake2s;
21//!
22//! let key : [u8; 16] = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15];
23//! let mut context = Blake2s::<224>::new_keyed(&key);
24//! context.update_mut(b"hello world");
25//! let mac = context.finalize();
26//! ```
27//!
28//!
29//! [1]: <https://eprint.iacr.org/2013/322.pdf>
30
31use super::blake2::{EngineS as Engine, LastBlock};
32use crate::cryptoutil::{write_u32v_le, zero};
33
34/// Blake2s Algorithm parametrized by the number of bits to output
35#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
36pub struct Blake2s<const BITS: usize>;
37
38impl<const BITS: usize> Blake2s<BITS> {
39    /// Output of the hashing algorithm in bits
40    pub const OUTPUT_BITS: usize = BITS;
41    /// The block size in bytes of the algorithm, which is the number of bytes the algorithm typically buffer
42    /// before calling its compression function
43    pub const BLOCK_BYTES: usize = Engine::BLOCK_BYTES;
44
45    /// Create a new context for this algorithm
46    pub fn new() -> Context<BITS> {
47        Context::new()
48    }
49    /// Create a new context with a key for this algorithm
50    pub fn new_keyed(key: &[u8]) -> Context<BITS> {
51        Context::new_keyed(key)
52    }
53}
54
55/// Blake2s Context
56#[derive(Clone)]
57pub struct Context<const BITS: usize> {
58    eng: Engine,
59    buf: [u8; Engine::BLOCK_BYTES],
60    buflen: usize,
61}
62
63/// Blake2s Context with dynamic output size determined by initial parameter
64#[derive(Clone)]
65pub struct ContextDyn {
66    eng: Engine,
67    buf: [u8; Engine::BLOCK_BYTES],
68    buflen: usize,
69    outlen: usize,
70}
71
72impl<const BITS: usize> Context<BITS> {
73    /// Create a new Blake2s context with a specific output size in bytes
74    ///
75    /// the size in bytes need to be between 0 (non included) and 32 bytes (included),
76    /// which means BITS need to be between 1 and 256.
77    pub fn new() -> Self {
78        assert!(BITS > 0 && ((BITS + 7) / 8) <= Engine::MAX_OUTLEN);
79        Self::new_keyed(&[])
80    }
81
82    /// Similar to `new` but also takes a variable size key
83    /// to tweak the context initialization
84    pub fn new_keyed(key: &[u8]) -> Self {
85        assert!(BITS > 0 && ((BITS + 7) / 8) <= Engine::MAX_OUTLEN);
86        assert!(key.len() <= Engine::MAX_KEYLEN);
87
88        let mut buf = [0u8; Engine::BLOCK_BYTES];
89
90        let eng = Engine::new((BITS + 7) / 8, key.len());
91        let buflen = if !key.is_empty() {
92            buf[0..key.len()].copy_from_slice(key);
93            Engine::BLOCK_BYTES
94        } else {
95            0
96        };
97
98        Self { eng, buf, buflen }
99    }
100
101    /// Update the hashing state by adding the input bytes slice into the state
102    pub fn update(mut self, input: &[u8]) -> Self {
103        self.update_mut(input);
104        self
105    }
106
107    /// Update in-place the hashing state by adding the input bytes slice into the state
108    ///
109    /// For the immutable version see [`update`]
110    pub fn update_mut(&mut self, mut input: &[u8]) {
111        if input.is_empty() {
112            return;
113        }
114        let fill = Engine::BLOCK_BYTES - self.buflen;
115
116        if input.len() > fill {
117            self.buf[self.buflen..self.buflen + fill].copy_from_slice(&input[0..fill]);
118            self.buflen = 0;
119            self.eng.increment_counter(Engine::BLOCK_BYTES_NATIVE);
120            self.eng
121                .compress(&self.buf[0..Engine::BLOCK_BYTES], LastBlock::No);
122
123            input = &input[fill..];
124
125            while input.len() > Engine::BLOCK_BYTES {
126                self.eng.increment_counter(Engine::BLOCK_BYTES_NATIVE);
127                self.eng
128                    .compress(&input[0..Engine::BLOCK_BYTES], LastBlock::No);
129                input = &input[Engine::BLOCK_BYTES..];
130            }
131        }
132        self.buf[self.buflen..self.buflen + input.len()].copy_from_slice(input);
133        self.buflen += input.len();
134    }
135
136    fn internal_final(&mut self) {
137        self.eng.increment_counter(self.buflen as u32);
138        zero(&mut self.buf[self.buflen..]);
139        self.eng
140            .compress(&self.buf[0..Engine::BLOCK_BYTES], LastBlock::Yes);
141
142        write_u32v_le(&mut self.buf[0..32], &self.eng.h);
143    }
144
145    /// Finalize the context and output the array of bytes into the mut output slice
146    ///
147    /// The context is consumed by this function, to prevent buggy reuse.
148    /// The output slice size is assert checked to have the correct expected size.
149    ///
150    /// If the context need to be kept before finalizing, the user can clone the Context
151    pub fn finalize_at(mut self, out: &mut [u8]) {
152        assert!(out.len() == ((BITS + 7) / 8));
153        self.internal_final();
154        out.copy_from_slice(&self.buf[0..out.len()]);
155    }
156
157    /// Same as `finalize` but do not consume the context, but instead
158    /// reset it in a ready to use state.
159    pub fn finalize_reset_at(&mut self, out: &mut [u8]) {
160        assert!(out.len() == ((BITS + 7) / 8));
161        self.internal_final();
162        out.copy_from_slice(&self.buf[0..out.len()]);
163        self.reset();
164    }
165
166    /// Same as `finalize_reset` but also specify the key to reset with
167    pub fn finalize_reset_with_key_at(&mut self, key: &[u8], out: &mut [u8]) {
168        assert!(out.len() == ((BITS + 7) / 8));
169        self.internal_final();
170        out.copy_from_slice(&self.buf[0..out.len()]);
171        self.reset_with_key(key);
172    }
173
174    /// Reset the context to the state after calling `new`
175    pub fn reset(&mut self) {
176        self.eng.reset((BITS + 7) / 8, 0);
177        self.buflen = 0;
178        zero(&mut self.buf[..]);
179    }
180
181    /// Reset the context to the state after calling `new_keyed` with the given key
182    pub fn reset_with_key(&mut self, key: &[u8]) {
183        assert!(key.len() <= Engine::MAX_KEYLEN);
184
185        self.eng.reset((BITS + 7) / 8, key.len());
186        zero(&mut self.buf[..]);
187
188        if !key.is_empty() {
189            self.buf[0..key.len()].copy_from_slice(key);
190            self.buflen = Engine::BLOCK_BYTES;
191        } else {
192            self.buf = [0; Engine::BLOCK_BYTES];
193            self.buflen = 0;
194        }
195    }
196}
197
198impl ContextDyn {
199    /// Create a new Blake2s context with a specific output size in bytes
200    ///
201    /// the size in bytes need to be between 0 (non included) and 32 bytes (included),
202    /// which means BITS need to be between 1 and 256.
203    pub fn new(output_bytes: usize) -> Self {
204        assert!(output_bytes > 0 && output_bytes <= Engine::MAX_OUTLEN);
205        Self::new_keyed(output_bytes, &[])
206    }
207
208    /// Similar to `new` but also takes a variable size key
209    /// to tweak the context initialization
210    pub fn new_keyed(output_bytes: usize, key: &[u8]) -> Self {
211        assert!(output_bytes > 0 && output_bytes <= Engine::MAX_OUTLEN);
212        assert!(key.len() <= Engine::MAX_KEYLEN);
213
214        let mut buf = [0u8; Engine::BLOCK_BYTES];
215
216        let eng = Engine::new(output_bytes, key.len());
217        let buflen = if !key.is_empty() {
218            buf[0..key.len()].copy_from_slice(key);
219            Engine::BLOCK_BYTES
220        } else {
221            0
222        };
223
224        Self {
225            eng,
226            buf,
227            buflen,
228            outlen: output_bytes,
229        }
230    }
231
232    /// Update the hashing state by adding the input bytes slice into the state
233    pub fn update(mut self, input: &[u8]) -> Self {
234        self.update_mut(input);
235        self
236    }
237
238    /// Update in-place the hashing state by adding the input bytes slice into the state
239    ///
240    /// For the immutable version see [`update`]
241    pub fn update_mut(&mut self, mut input: &[u8]) {
242        if input.is_empty() {
243            return;
244        }
245        let fill = Engine::BLOCK_BYTES - self.buflen;
246
247        if input.len() > fill {
248            self.buf[self.buflen..self.buflen + fill].copy_from_slice(&input[0..fill]);
249            self.buflen = 0;
250            self.eng.increment_counter(Engine::BLOCK_BYTES_NATIVE);
251            self.eng
252                .compress(&self.buf[0..Engine::BLOCK_BYTES], LastBlock::No);
253
254            input = &input[fill..];
255
256            while input.len() > Engine::BLOCK_BYTES {
257                self.eng.increment_counter(Engine::BLOCK_BYTES_NATIVE);
258                self.eng
259                    .compress(&input[0..Engine::BLOCK_BYTES], LastBlock::No);
260                input = &input[Engine::BLOCK_BYTES..];
261            }
262        }
263        self.buf[self.buflen..self.buflen + input.len()].copy_from_slice(input);
264        self.buflen += input.len();
265    }
266
267    fn internal_final(&mut self) {
268        self.eng.increment_counter(self.buflen as u32);
269        zero(&mut self.buf[self.buflen..]);
270        self.eng
271            .compress(&self.buf[0..Engine::BLOCK_BYTES], LastBlock::Yes);
272
273        write_u32v_le(&mut self.buf[0..32], &self.eng.h);
274    }
275
276    /// Finalize the context and output the array of bytes into the mut output slice
277    ///
278    /// The context is consumed by this function, to prevent buggy reuse.
279    /// The output slice size is assert checked to have the correct expected size.
280    ///
281    /// If the context need to be kept before finalizing, the user can clone the Context
282    pub fn finalize_at(mut self, out: &mut [u8]) {
283        assert!(out.len() == self.outlen);
284        self.internal_final();
285        out.copy_from_slice(&self.buf[0..out.len()]);
286    }
287
288    /// Same as `finalize` but do not consume the context, but instead
289    /// reset it in a ready to use state.
290    pub fn finalize_reset_at(&mut self, out: &mut [u8]) {
291        assert!(out.len() == self.outlen);
292        self.internal_final();
293        out.copy_from_slice(&self.buf[0..out.len()]);
294        self.reset();
295    }
296
297    /// Same as `finalize_reset` but also specify the key to reset with
298    pub fn finalize_reset_with_key_at(&mut self, key: &[u8], out: &mut [u8]) {
299        assert!(out.len() == self.outlen);
300        self.internal_final();
301        out.copy_from_slice(&self.buf[0..out.len()]);
302        self.reset_with_key(key);
303    }
304
305    /// Reset the context to the state after calling `new`
306    pub fn reset(&mut self) {
307        self.eng.reset(self.outlen, 0);
308        self.buflen = 0;
309        zero(&mut self.buf[..]);
310    }
311
312    /// Reset the context to the state after calling `new_keyed` with the given key
313    pub fn reset_with_key(&mut self, key: &[u8]) {
314        assert!(key.len() <= Engine::MAX_KEYLEN);
315
316        self.eng.reset(self.outlen, key.len());
317        zero(&mut self.buf[..]);
318
319        if !key.is_empty() {
320            self.buf[0..key.len()].copy_from_slice(key);
321            self.buflen = Engine::BLOCK_BYTES;
322        } else {
323            self.buf = [0; Engine::BLOCK_BYTES];
324            self.buflen = 0;
325        }
326    }
327
328    /// The output size in bits
329    pub fn output_bits(&self) -> usize {
330        self.outlen * 8
331    }
332}
333
334// Due to limitation of const generic, we can't define finalize in the generic context, so instead
335// define support for specific known size, until the limitation is lifted
336macro_rules! context_finalize {
337    ($size:literal) => {
338        impl Context<$size> {
339            /// Finalize the context and return an array of bytes
340            ///
341            /// The context is consumed by this function, to prevent buggy reuse.
342            ///
343            /// If the context need to be kept before finalizing, the user can clone the Context
344            pub fn finalize(self) -> [u8; $size / 8] {
345                let mut out = [0; $size / 8];
346                self.finalize_at(&mut out);
347                out
348            }
349
350            /// Same as `finalize` but do not consume the context, but instead
351            /// reset it in a ready to use state.
352            pub fn finalize_reset(&mut self) -> [u8; $size / 8] {
353                let mut out = [0; $size / 8];
354                self.finalize_reset_at(&mut out);
355                out
356            }
357
358            /// Same as `finalize_reset` but also specify the key to reset with
359            pub fn finalize_reset_with_key(&mut self, key: &[u8]) -> [u8; $size / 8] {
360                let mut out = [0; $size / 8];
361                self.finalize_reset_with_key_at(key, &mut out);
362                out
363            }
364        }
365    };
366}
367context_finalize!(224);
368context_finalize!(256);
369
370#[cfg(test)]
371mod digest_tests {
372    use super::super::tests::{test_hashing, Test};
373    use super::{Blake2s, Context};
374
375    #[test]
376    fn test_vector() {
377        let tests = [Test {
378            input: b"abc",
379            output: [
380                80, 140, 94, 140, 50, 124, 20, 226, 225, 167, 43, 163, 78, 235, 69, 47, 55, 69,
381                139, 32, 158, 214, 58, 41, 77, 153, 155, 76, 134, 103, 89, 130,
382            ],
383        }];
384
385        test_hashing(
386            &tests,
387            Blake2s::<256>,
388            |_| Context::<256>::new(),
389            |ctx, input| ctx.update(input),
390            |ctx, input| ctx.update_mut(input),
391            |ctx| ctx.finalize(),
392            |ctx| ctx.finalize_reset(),
393            |ctx| ctx.reset(),
394        )
395    }
396}
397
398#[cfg(test)]
399mod mac_tests {
400    use super::super::tests::{test_hashing_keyed, TestKey};
401    use super::{Blake2s, Context};
402
403    #[test]
404    fn test_mac() {
405        let tests = [
406            TestKey {
407                input: &[1, 2, 4, 8],
408                key: &[
409                    0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
410                    22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
411                ],
412                output: [
413                    0x0e, 0x88, 0xf6, 0x8a, 0xaa, 0x5c, 0x4e, 0xd8, 0xf7, 0xed, 0x28, 0xf8, 0x04,
414                    0x45, 0x01, 0x9c, 0x7e, 0xf9, 0x76, 0x2b, 0x4f, 0xf1, 0xad, 0x7e, 0x05, 0x5b,
415                    0xa8, 0xc8, 0x82, 0x9e, 0xe2, 0x49,
416                ],
417            },
418            TestKey {
419                input: &[0x00, 0x01, 0x02, 0x03, 0x04, 0x05],
420                key: &[
421                    0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
422                    22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
423                ],
424                output: [
425                    0xfd, 0xd8, 0x99, 0x3d, 0xcd, 0x43, 0xf6, 0x96, 0xd4, 0x4f, 0x3c, 0xea, 0x0f,
426                    0xf3, 0x53, 0x45, 0x23, 0x4e, 0xc8, 0xee, 0x08, 0x3e, 0xb3, 0xca, 0xda, 0x01,
427                    0x7c, 0x7f, 0x78, 0xc1, 0x71, 0x43,
428                ],
429            },
430        ];
431
432        test_hashing_keyed(
433            &tests,
434            Blake2s::<256>,
435            |_, k| Context::<256>::new_keyed(k),
436            |ctx, input| ctx.update(input),
437            |ctx, input| ctx.update_mut(input),
438            |ctx| ctx.finalize(),
439            |ctx, key| ctx.finalize_reset_with_key(key),
440            |ctx, key| ctx.reset_with_key(key),
441        )
442    }
443}
444
445#[cfg(all(test, feature = "with-bench"))]
446mod bench {
447    use test::Bencher;
448
449    use super::Blake2s;
450
451    #[bench]
452    pub fn blake2s_10(bh: &mut Bencher) {
453        let mut sh = Blake2s::<256>::new();
454        let bytes = [1u8; 10];
455        bh.iter(|| {
456            sh.update_mut(&bytes);
457        });
458        bh.bytes = bytes.len() as u64;
459    }
460
461    #[bench]
462    pub fn blake2s_1k(bh: &mut Bencher) {
463        let mut sh = Blake2s::<256>::new();
464        let bytes = [1u8; 1024];
465        bh.iter(|| {
466            sh.update_mut(&bytes);
467        });
468        bh.bytes = bytes.len() as u64;
469    }
470
471    #[bench]
472    pub fn blake2s_64k(bh: &mut Bencher) {
473        let mut sh = Blake2s::<256>::new();
474        let bytes = [1u8; 65536];
475        bh.iter(|| {
476            sh.update_mut(&bytes);
477        });
478        bh.bytes = bytes.len() as u64;
479    }
480}