1 /// Computes BLAKE2b and BLAKE2s hashes of arbitary data.
2 /// Reference: IETF RFC 7693
3 /// License: $(LINK2 www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
4 /// Authors: $(LINK2 github.com/dd86k, dd86k)
5 module blake2d;
6 
7 public enum BLAKE2D_VERSION_STRING = "0.2.0";
8 
9 private import std.digest;
10 private import core.bitop : ror, bswap;
11 
12 // "For BLAKE2b, the two extra permutations for rounds 10 and 11 are
13 // SIGMA[10..11] = SIGMA[0..1]."
14 private immutable ubyte[16][12] SIGMA = [
15     [ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9,  10, 11, 12, 13, 14, 15, ],
16     [ 14, 10, 4,  8,  9,  15, 13, 6,  1,  12, 0,  2,  11, 7,  5,  3,  ],
17     [ 11, 8,  12, 0,  5,  2,  15, 13, 10, 14, 3,  6,  7,  1,  9,  4,  ],
18     [ 7,  9,  3,  1,  13, 12, 11, 14, 2,  6,  5,  10, 4,  0,  15, 8,  ],
19     [ 9,  0,  5,  7,  2,  4,  10, 15, 14, 1,  11, 12, 6,  8,  3,  13, ],
20     [ 2,  12, 6,  10, 0,  11, 8,  3,  4,  13, 7,  5,  15, 14, 1,  9,  ],
21     [ 12, 5,  1,  15, 14, 13, 4,  10, 0,  7,  6,  3,  9,  2,  8,  11, ],
22     [ 13, 11, 7,  14, 12, 1,  3,  9,  5,  0,  15, 4,  8,  6,  2,  10, ],
23     [ 6,  15, 14, 9,  11, 3,  0,  8,  12, 2,  13, 7,  1,  4,  10, 5,  ],
24     [ 10, 2,  8,  4,  7,  6,  1,  5,  15, 11, 9,  14, 3,  12, 13, 0,  ],
25     [ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9,  10, 11, 12, 13, 14, 15, ],
26     [ 14, 10, 4,  8,  9,  15, 13, 6,  1,  12, 0,  2,  11, 7,  5,  3,  ],
27 ];
28 
29 private
30 immutable ulong[8] B2B_IV = [
31     0x6a09e667f3bcc908, 0xbb67ae8584caa73b,
32     0x3c6ef372fe94f82b, 0xa54ff53a5f1d36f1,
33     0x510e527fade682d1, 0x9b05688c2b3e6c1f,
34     0x1f83d9abfb41bd6b, 0x5be0cd19137e2179
35 ];
36 
37 private
38 immutable uint[8] B2S_IV = [
39     0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
40     0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19
41 ];
42 
43 /// Used with the BLAKE2 structure template to make the BLAKE2s and BLAKE2p
44 /// aliases.
45 enum BLAKE2Variant {
46     b,  /// BLAKE2b (default)
47     s,  /// BLAKE2s
48 }
49 
50 /// BLAKE2 structure template.
51 ///
52 /// It is recommended to use the BLAKE2p512 and BLAKE2s256 aliases.
53 /// However, if you wish to use a custom digest size, this is the structure
54 /// to use.
55 ///
56 /// Example definitions for BLAKE2s-160:
57 /// ---
58 /// alias BLAKE2s160 = BLAKE2!(BLAKE2Variant.s, 160);
59 /// auto blake2s160_Of(T...)(T data) { return digest!(BLAKE2s160, T)(data); }
60 /// public alias BLAKE2s160Digest = WrapperDigest!BLAKE2s160;
61 /// ---
62 /// Params:
63 ///   var = BLAKE2 hash variation.
64 ///   digestSize = Digest size in bits.
65 //   key = HMAC key. This is a temporary hack to allow HMAC usage.
66 struct BLAKE2(BLAKE2Variant var, uint digestSize/*, const(ubyte)[] key = null*/)
67 {
68     @safe: @nogc: nothrow: pure:
69     
70     static assert(digestSize > 0,
71         "Digest size must be non-zero.");
72     static assert(digestSize % 8 == 0,
73         "Digest size must be dividable by 8.");
74     
75     static if (var == BLAKE2Variant.b) { // BLAKE2b
76         // 8 to 512 bits
77         static assert(digestSize >= 8 && digestSize <= 512,
78             "BLAKE2b digest size must be between 8 and 512 bits.");
79         
80         private enum MAXKEYSIZE = 32; // In bytes
81         private enum MAXED = digestSize == 512;
82         private enum BSIZE = 128;   /// Buffer size, "bb" variable
83         private enum ROUNDS = 12;
84         private enum R1 = 32;
85         private enum R2 = 24;
86         private enum R3 = 16;
87         private enum R4 = 63;
88         private alias IV = B2B_IV;
89         private alias inner_t = ulong;
90     } else static if (var == BLAKE2Variant.s) { // BLAKE2s
91         // 8 to 256 bits
92         static assert(digestSize >= 8 && digestSize <= 256,
93             "BLAKE2s digest size must be between 8 and 256 bits.");
94         
95         private enum MAXKEYSIZE = 32; // In bytes
96         private enum MAXED = digestSize == 256;
97         private enum BSIZE = 64;    /// Buffer size, "bb" variable
98         private enum ROUNDS = 10;
99         private enum R1 = 16;
100         private enum R2 = 12;
101         private enum R3 = 8;
102         private enum R4 = 7;
103         private alias IV = B2S_IV;
104         private alias inner_t = uint;
105     } else static assert(0, "Invalid BLAKE2 variant.");
106     
107     enum blockSize = digestSize;    /// Digest size in bits
108     
109     /// Initiate or reset the state of the structure.
110     void start()
111     {
112         this = typeof(this).init;
113     }
114     
115     /// Initiates a key with digest.
116     /// This is meant to be used after the digest initiation.
117     /// The key limit is 64 bytes for BLAKE2b and 32 bytes for
118     /// BLAKE2s. If the limit is reached, it fails silenty by truncating
119     /// key data.
120     /// Params: input = Key.
121     void key(scope const(ubyte)[] input)
122     {
123         enum MASK  = BSIZE - 1;
124         h[0] ^= ((input.length & MASK) << 8);
125         put(input.length > BSIZE ? input[0..BSIZE] : input);
126         c = BSIZE;
127     }
128     
129     /// Feed the algorithm with data.
130     /// Also implements the $(REF isOutputRange, std,range,primitives)
131     /// interface for `ubyte` and `const(ubyte)[]`.
132     /// Params: input = Input data to digest
133     void put(scope const(ubyte)[] input...) @trusted
134     {
135         // Process wordwise if properly aligned.
136         if ((c | cast(size_t) input.ptr) % size_t.alignof == 0)
137         {
138             foreach (const word; (cast(size_t*) input.ptr)[0 .. input.length / size_t.sizeof])
139             {
140                 if (c >= BSIZE)
141                 {
142                     t[0] += c;
143                     if (t[0] < c) ++t[1]; // Overflow
144                     compress();
145                     c = 0;
146                 }
147                 mz.ptr[c / size_t.sizeof] = word;
148                 c += size_t.sizeof;
149             }
150             input = input.ptr[input.length - (input.length % size_t.sizeof) .. input.length];
151         }
152         
153         // Process remainder bytewise.
154         foreach (const i; input)
155         {
156             if (c >= BSIZE)
157             {
158                 t[0] += c;
159                 if (t[0] < c) ++t[1]; // Overflow
160                 compress();
161                 c = 0;
162             }
163             m8[c++] = i;
164         }
165     }
166     
167     /// Returns the finished hash.
168     /// Returns: Raw digest data.
169     ubyte[digestSizeBytes] finish()
170     {
171         // final counter update
172         t[0] += c;
173         if (t[0] < c) ++t[1];
174         
175         // 0-pad message buffer
176         m8[c..$] = 0;
177         last = 1;
178         compress();
179         
180         // Clear out possible sensitive data
181         t[0] = t[1] = c = 0; // clear size information
182         mz[] = 0; // clear input message buffer
183         // Only clear remaining of state if digest isn't fulled used.
184         // e.g., BLAKE2b-512 has a digest size of 64 bytes
185         //       ulong[8] (8*8) is 64 bytes of state
186         //       So is BLAKE2x-256 is used, a digest of 32 bytes
187         //       state (h) will still be 64 bytes, so upper range is cleared
188         static if (MAXED == false)
189             h8[digestSizeBytes..$] = 0; // clear unused state space
190         
191         return h8[0..digestSizeBytes];
192     }
193     
194 private:
195     
196     enum digestSizeBytes = digestSize / 8;
197     //           3 2 1 0
198     // p[0] = 0x0101kknn
199     // kk - Key size. Set to zero since HMAC is done elsewhere.
200     // nn - Digest size in bytes.
201     enum p0 = 0x0101_0000 ^ digestSizeBytes;
202     enum msz = 16 * inner_t.sizeof; /// message size in bytes
203     enum hsz = 8 * inner_t.sizeof;  /// state size in bytes
204     
205     static assert(msz == BSIZE); // e.g. 128 for b2b and 64 for b2s
206     static assert(hsz == BSIZE / 2); // e.g., 64 for b2b and 32 for b2s
207     
208     union // input message buffer
209     {
210         size_t[BSIZE / size_t.sizeof] mz = void;
211         inner_t[16] m;   /// Message
212         ubyte[16 * inner_t.sizeof] m8; /// Message in byte-size
213     }
214     union // state
215     {
216         struct // .init hack since start() can't cover this
217         {
218             inner_t h0 = IV[0] ^ p0;
219             inner_t h1 = IV[1];
220             inner_t h2 = IV[2];
221             inner_t h3 = IV[3];
222             inner_t h4 = IV[4];
223             inner_t h5 = IV[5];
224             inner_t h6 = IV[6];
225             inner_t h7 = IV[7];
226         }
227         inner_t[8] h;   /// State
228         ubyte[8 * inner_t.sizeof] h8;   /// State in byte-size
229     }
230     inner_t[2] t;  /// Total count of input size
231     size_t c;      /// Counter, pointer for buffer
232     uint last;     /// If set, this is the last block to compress.
233     
234     void compress()
235     {
236         //TODO: bswap m on BigEndian platforms?
237         
238         inner_t[16] v = void;
239         v[0]  = h[0];
240         v[1]  = h[1];
241         v[2]  = h[2];
242         v[3]  = h[3];
243         v[4]  = h[4];
244         v[5]  = h[5];
245         v[6]  = h[6];
246         v[7]  = h[7];
247         v[8]  = IV[0];
248         v[9]  = IV[1];
249         v[10] = IV[2];
250         v[11] = IV[3];
251         v[12] = t[0] ^ IV[4];
252         v[13] = t[1] ^ IV[5];
253         v[14] = last ? ~IV[6] : IV[6];
254         v[15] = IV[7];
255         
256         // Assert i=0 v[16]
257         
258         for (size_t round; round < ROUNDS; ++round)
259         {
260             //   a  b   c   d  x                      y
261             G(v, 0, 4,  8, 12, m[SIGMA[round][ 0]], m[SIGMA[round][ 1]]);
262             G(v, 1, 5,  9, 13, m[SIGMA[round][ 2]], m[SIGMA[round][ 3]]);
263             G(v, 2, 6, 10, 14, m[SIGMA[round][ 4]], m[SIGMA[round][ 5]]);
264             G(v, 3, 7, 11, 15, m[SIGMA[round][ 6]], m[SIGMA[round][ 7]]);
265             G(v, 0, 5, 10, 15, m[SIGMA[round][ 8]], m[SIGMA[round][ 9]]);
266             G(v, 1, 6, 11, 12, m[SIGMA[round][10]], m[SIGMA[round][11]]);
267             G(v, 2, 7,  8, 13, m[SIGMA[round][12]], m[SIGMA[round][13]]);
268             G(v, 3, 4,  9, 14, m[SIGMA[round][14]], m[SIGMA[round][15]]);
269             
270             // Assert i=1..i=10/12 v[16]
271         }
272         
273         h[0] ^= v[0] ^ v[8];
274         h[1] ^= v[1] ^ v[9];
275         h[2] ^= v[2] ^ v[10];
276         h[3] ^= v[3] ^ v[11];
277         h[4] ^= v[4] ^ v[12];
278         h[5] ^= v[5] ^ v[13];
279         h[6] ^= v[6] ^ v[14];
280         h[7] ^= v[7] ^ v[15];
281         
282         // Assert h[8]
283     }
284     
285     static void G(ref inner_t[16] v, uint a, uint b, uint c, uint d, inner_t x, inner_t y)
286     {
287         v[a] = v[a] + v[b] + x;
288         v[d] = ror(v[d] ^ v[a], R1);
289         v[c] = v[c] + v[d];
290         v[b] = ror(v[b] ^ v[c], R2);
291         v[a] = v[a] + v[b] + y;
292         v[d] = ror(v[d] ^ v[a], R3);
293         v[c] = v[c] + v[d];
294         v[b] = ror(v[b] ^ v[c], R4);
295     }
296 }
297 
298 /// Alias for BLAKE2b-512
299 public alias BLAKE2b512 = BLAKE2!(BLAKE2Variant.b, 512);
300 /// Alias for BLAKE2s-256
301 public alias BLAKE2s256 = BLAKE2!(BLAKE2Variant.s, 256);
302 
303 /// Convience alias for $(REF digest, std,digest) using the BLAKE2b-512 implementation.
304 auto blake2b_Of(T...)(T data) { return digest!(BLAKE2b512, T)(data); }
305 /// Alias of blake2b_Of. 
306 alias blake2_Of = blake2b_Of;
307 /// Convience alias for $(REF digest, std,digest) using the BLAKE2s-256 implementation.
308 auto blake2s_Of(T...)(T data) { return digest!(BLAKE2s256, T)(data); }
309 
310 /// OOP API BLAKE2 implementation aliases.
311 public alias BLAKE2b512Digest = WrapperDigest!BLAKE2b512;
312 /// Ditto
313 public alias BLAKE2s256Digest = WrapperDigest!BLAKE2s256;
314 
315 /// Of course they correspond to the digest API!
316 @safe unittest
317 {
318     assert(isDigest!BLAKE2b512);
319     assert(isDigest!BLAKE2s256);
320 }
321 
322 /// Of course they have a blockSize!
323 @safe unittest
324 {
325     assert(hasBlockSize!BLAKE2b512);
326     assert(hasBlockSize!BLAKE2s256);
327 }
328 
329 /// Testing "Of" wrappers against digest wrappers.
330 @safe unittest
331 {
332     enum TEXT = "abc";
333     
334     assert(blake2b_Of(TEXT) == digest!BLAKE2b512(TEXT));
335     assert(blake2s_Of(TEXT) == digest!BLAKE2s256(TEXT));
336 }
337 
338 /// Testing template API
339 @system unittest
340 {
341     import std.conv : hexString;
342     
343     ubyte[] s = [ 'a', 'b', 'c' ];
344     
345     BLAKE2s256 b2s;
346     b2s.put(s);
347     assert(b2s.finish() == cast(ubyte[])hexString!(
348         "508c5e8c327c14e2e1a72ba34eeb452f37458b209ed63a294d999b4c86675982"));
349 }
350 
351 /// Test against empty input
352 @safe unittest
353 {
354     assert(toHexString!(LetterCase.lower)(blake2b_Of("")) ==
355         "786a02f742015903c6c6fd852552d272912f4740e15847618a86e217f71f5419"~
356         "d25e1031afee585313896444934eb04b903a685b1448b755d56f701afe9be2ce");
357     assert(toHexString!(LetterCase.lower)(blake2s_Of("")) ==
358         "69217a3079908094e11121d042354a7c1f55b6482ca1a51e1b250dfd1ed0eef9");
359 }
360 
361 /// Test against "abc"
362 @safe unittest
363 {
364     assert(toHexString!(LetterCase.lower)(blake2b_Of("abc")) ==
365         "ba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d1"~
366         "7d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923");
367     assert(toHexString!(LetterCase.lower)(blake2s_Of("abc")) ==
368         "508c5e8c327c14e2e1a72ba34eeb452f37458b209ed63a294d999b4c86675982");
369 }
370 
371 
372 /// Testing template API against one million 'a'
373 @system unittest
374 {
375     import std.conv : hexString;
376     
377     ubyte[] onemilliona = new ubyte[1_000_000];
378     onemilliona[] = 'a';
379     
380     BLAKE2b512 b2b;
381     b2b.put(onemilliona);
382     assert(b2b.finish() == cast(ubyte[]) hexString!(
383         "98fb3efb7206fd19ebf69b6f312cf7b64e3b94dbe1a17107913975a793f177e1"~
384         "d077609d7fba363cbba00d05f7aa4e4fa8715d6428104c0a75643b0ff3fd3eaf"));
385     
386     BLAKE2s256 b2s;
387     b2s.put(onemilliona);
388     assert(b2s.finish() == cast(ubyte[]) hexString!(
389         "bec0c0e6cde5b67acb73b81f79a67a4079ae1c60dac9d2661af18e9f8b50dfa5"));
390 }
391 
392 /// Testing OOP API
393 @system unittest
394 {
395     import std.conv : hexString;
396     
397     ubyte[] s = ['a', 'b', 'c'];
398     
399     Digest b2b = new BLAKE2b512Digest();
400     b2b.put(s);
401     assert(b2b.finish() == cast(ubyte[]) hexString!(
402         "ba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d1"~
403         "7d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923"));
404     
405     Digest b2s = new BLAKE2s256Digest();
406     b2s.put(s);
407     assert(b2s.finish() == cast(ubyte[]) hexString!(
408         "508c5e8c327c14e2e1a72ba34eeb452f37458b209ed63a294d999b4c86675982"));
409 }
410 
411 /// BLAKE2s Template usage
412 @system unittest
413 {
414     import std.conv : hexString;
415     
416     // Let's use the template features:
417     // NOTE: When passing a digest to a function, it must be passed by reference!
418     void doSomething(T)(ref T hash)
419         if (isDigest!T)
420         {
421             hash.put([ 'a', 'b', 'c' ]);
422         }
423     BLAKE2b512 b2b;
424     b2b.start();
425     doSomething(b2b);
426     assert(b2b.finish() == cast(ubyte[]) hexString!(
427         "ba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d1"~
428         "7d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923"));
429     BLAKE2s256 b2s;
430     b2s.start();
431     doSomething(b2s);
432     assert(b2s.finish() == cast(ubyte[]) hexString!(
433         "508c5e8c327c14e2e1a72ba34eeb452f37458b209ed63a294d999b4c86675982"));
434 }
435 
436 /// Keying digests
437 @system unittest
438 {
439     // NOTE: BLAKE2 is a keyed hash and therefore not compatible with
440     //       the hmac/HMAC templates, or OpenSSL does it differently.
441     
442     import std.ascii : LetterCase;
443     import std.string : representation;
444     import std.conv : hexString;
445     
446     auto secret2b = hexString!(
447         "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f"~
448         "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f")
449         .representation;
450     auto secret2s = hexString!(
451         "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f")
452         .representation;
453     auto data = hexString!("000102").representation;
454     
455     BLAKE2b512 b2b;
456     b2b.key(secret2b);
457     b2b.put(data);
458     assert(b2b.finish().toHexString!(LetterCase.lower) ==
459         "33d0825dddf7ada99b0e7e307104ad07ca9cfd9692214f1561356315e784f3e5"~
460         "a17e364ae9dbb14cb2036df932b77f4b292761365fb328de7afdc6d8998f5fc1"
461         , "BLAKE2b+secret failed");
462     
463     BLAKE2s256 b2s;
464     b2s.key(secret2s);
465     b2s.put(data);
466     assert(b2s.finish().toHexString!(LetterCase.lower) ==
467         "1d220dbe2ee134661fdf6d9e74b41704710556f2f6e5a091b227697445dbea6b"
468         , "BLAKE2s+secret failed");
469 }