1 /// Computes SHA-3 hashes of arbitrary data using a native implementation.
2 /// Standards: NIST FIPS PUB 202
3 /// License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
4 /// Authors: $(LINK2 https://github.com/dd86k, dd86k)
5 module sha3d;
6 
7 /// Version string of sha3-d that can be used for printing purposes.
8 public enum SHA3D_VERSION_STRING = "1.2.4";
9 
10 private import std.digest;
11 private import core.bitop : rol, bswap;
12 
13 version (SHA3D_Trace)
14     private import std.stdio;
15 
16 /// Template API SHA-3/SHAKE implementation using the Keccak[1600,24] function.
17 ///
18 /// It supports SHA-3 and SHAKE XOFs. Though, it is recommended to use the
19 /// SHA3_224, SHA3_256, SHA3_384, SHA3_512, SHAKE128, and SHAKE256 template
20 /// aliases.
21 ///
22 /// Examples:
23 /// ---
24 /// // Defines SHAKE-128/256 with Template API, OOP API, and helper function.
25 /// alias SHAKE128_256 = KECCAK!(128, 256);
26 /// alias SHAKE128_256Digest = WrapperDigest!SHAKE128_256;
27 /// auto shake128_256Of(T...)(T data) { return digest!(SHAKE128_256, T)(data); }
28 /// ---
29 ///
30 /// Params:
31 ///   digestSize = Digest size in bits.
32 ///   shake = SHAKE XOF digest size in bits. Defaults to 0 for SHA-3.
33 ///
34 /// Throws: No exceptions are thrown.
35 public struct KECCAK(uint digestSize, uint shake = 0)
36 {
37     version (SHA3D_Trace) {}
38     else
39     {
40         @safe: @nogc: nothrow: pure:
41     }
42     
43     //TODO: Consider moving the definitions as template parameters
44     
45     /// Sponge size. Default is 1600 bits (200 Bytes).
46     /// Must be a power of 25 (25, 50, 100, 200, 400, 800, or 1600).
47     private enum SPONGE = 1600;
48     /// Number of rounds. 24 rounds for SHA-3 and SHAKE.
49     private enum ROUNDS = 24;
50     /// RC constants.
51     private immutable static ulong[ROUNDS] K_RC = [
52         0x0000000000000001, 0x0000000000008082, 0x800000000000808a, 0x8000000080008000,
53         0x000000000000808b, 0x0000000080000001, 0x8000000080008081, 0x8000000000008009,
54         0x000000000000008a, 0x0000000000000088, 0x0000000080008009, 0x000000008000000a,
55         0x000000008000808b, 0x800000000000008b, 0x8000000000008089, 0x8000000000008003,
56         0x8000000000008002, 0x8000000000000080, 0x000000000000800a, 0x800000008000000a,
57         0x8000000080008081, 0x8000000000008080, 0x0000000080000001, 0x8000000080008008
58     ];
59     /// Rho indexes.
60     private immutable static int[ROUNDS] K_RHO = [
61         1,  3,  6, 10, 15, 21, 28, 36, 45, 55,  2, 14,
62         27, 41, 56,  8, 25, 43, 62, 18, 39, 61, 20, 44
63     ];
64     /// PI indexes.
65     private immutable static size_t[ROUNDS] K_PI = [
66         10,  7, 11, 17, 18, 3,  5, 16,  8, 21, 24, 4,
67         15, 23, 19, 13, 12, 2, 20, 14, 22,  9,  6, 1
68     ];
69     
70     static if (shake)
71     {
72         static assert(digestSize == 128 || digestSize == 256,
73             "SHAKE digest size must be 128 or 256 bits");
74         static assert(shake > 0,
75             "SHAKE digest size must be higher than zero.");
76         static assert(shake % 8 == 0,
77             "SHAKE digest size must be a factor of 25.");
78         private enum digestSizeBytes = shake / 8; /// Digest size in bytes
79     }
80     else // SHA-3
81     {
82         static assert(digestSize == 224 || digestSize == 256 ||
83             digestSize == 384 || digestSize == 512,
84             "SHA-3 digest size must be 224, 256, 384, or 512 bits");
85         private enum digestSizeBytes = digestSize / 8; /// Digest size in bytes
86     }
87     
88     /// Digest size in bits.
89     enum blockSize = (SPONGE - digestSize * 2); // Required for HMAC.
90     
91     //  ...0: Reserved
92     //    01: SHA-3
93     // ...11: RawSHAKE
94     //  1111: SHAKE
95     private enum delim = shake ? 0x1f : 0x06; /// Delimiter suffix when finishing
96     private enum rate = blockSize / 8; /// Sponge rate in bytes
97     private enum stateSize = 200; /// Constant for any derivatives
98     private enum state64Size = stateSize / ulong.sizeof;
99     private enum statezSize = stateSize / size_t.sizeof;
100     
101     union
102     {
103         private size_t[statezSize] statez;  // state (size_t)
104         private ulong[state64Size] state64; // state (ulong)
105         private ubyte[stateSize] state;  // state (ubyte)
106     }
107     static assert(state64.sizeof == state.sizeof);
108     static assert(statez.sizeof == state.sizeof);
109     
110     private ulong[5] bc; // Transformation data
111     private ulong t;     // Transformation temporary
112     private size_t pt;   // Left-over sponge pointer
113     
114     /// Initiate or reset the state of the instance.
115     void start()
116     {
117         this = typeof(this).init;
118     }
119     
120     /// Feed the algorithm with data.
121     /// Also implements the $(REF isOutputRange, std,range,primitives)
122     /// interface for `ubyte` and `const(ubyte)[]`.
123     /// Params: input = Input data to digest
124     void put(scope const(ubyte)[] input...) @trusted
125     {
126         size_t i = pt;
127         
128         // Process wordwise if properly aligned.
129         if ((i | cast(size_t)input.ptr) % size_t.alignof == 0)
130         {
131             static assert(rate % size_t.sizeof == 0);
132             foreach (const word; (cast(size_t*)input.ptr)[0..input.length / size_t.sizeof])
133             {
134                 statez[i / size_t.sizeof] ^= word;
135                 i += size_t.sizeof;
136                 if (i >= rate)
137                 {
138                     transform;
139                     i = 0;
140                 }
141             }
142             input = input[input.length - (input.length % size_t.sizeof)..input.length];
143         }
144         
145         // Process remainder bytewise.
146         foreach (const b; input)
147         {
148             state.ptr[i++] ^= b;
149             if (i >= rate)
150             {
151                 transform;
152                 i = 0;
153             }
154         }
155         
156         pt = i;
157     }
158     
159     /// Returns the finished hash.
160     /// This also clears part of the state, leaving just the final digest.
161     /// Returns: Raw digest data.
162     ubyte[digestSizeBytes] finish()
163     {
164         state[pt] ^= delim;
165         state[rate - 1] ^= 0x80;
166         
167         static if (shake)
168         {
169             ubyte[digestSizeBytes] output = void;
170             
171             size_t i;
172             
173             // Transform at {rate} until output is filled.
174             do
175             {
176                 transform;
177                 size_t end = i + rate;
178                 if (end > digestSizeBytes)
179                 {
180                     output[i..$] = state[0..digestSizeBytes - i];
181                     break;
182                 }
183                 output[i..end] = state[0..rate];
184                 i += rate;
185             } while (true);
186             
187             // Clear state of potential sensitive data.
188             state64[] = 0;
189             bc[] = t = 0;
190             
191             return output;
192         }
193         else // SHA-3
194         {
195             transform;
196             
197             // Clear potentially sensitive data.
198             // State sanitized only if digestSize is less than state
199             // of 1600 bits, so 200 Bytes.
200             static if (digestSizeBytes < stateSize)
201                 state[digestSizeBytes..$] = 0;
202             bc[] = t = 0;
203             
204             version (SHA3D_Trace)
205             {
206                 ubyte[digestSizeBytes] r = state[0..digestSizeBytes];
207                 writeln("HASH=", toHexString!(LetterCase.lower)(r));
208                 return r;
209             }
210             else
211             {
212                 return state[0..digestSizeBytes];
213             }
214         }
215     }
216     
217 private:
218     
219     void transform()
220     {
221         version (BigEndian) swap;
222 
223         for (size_t round; round < ROUNDS; ++round)
224         {
225             // Theta
226             THETA1(0); THETA1(1); THETA1(2); THETA1(3); THETA1(4);
227             THETA2(0); THETA2(1); THETA2(2); THETA2(3); THETA2(4);
228             t = state64[1];
229             // Rho
230             RHO(0); RHO(1); RHO(2); RHO(3); RHO(4);
231             RHO(5); RHO(6); RHO(7); RHO(8); RHO(9);
232             RHO(10); RHO(11); RHO(12); RHO(13); RHO(14);
233             RHO(15); RHO(16); RHO(17); RHO(18); RHO(19);
234             RHO(20); RHO(21); RHO(22); RHO(23);
235             // Chi
236             CHI(0); CHI(5); CHI(10); CHI(15); CHI(20);
237             // Iota
238             state64[0] ^= K_RC[round];
239             
240             version (SHA3D_Trace)
241             {
242                 writefln("ROUND=%d", round);
243                 int i;
244                 foreach (ulong s; state64)
245                     if (i & 1)
246                         writefln(" v[%2d]=%16x", i++, s);
247                     else
248                         writef("\tv[%2d]=%16x", i++, s);
249                 writeln;
250             }
251         }
252 
253         version (BigEndian) swap;
254     }
255     
256     pragma(inline, true)
257     void THETA1(size_t i)
258     {
259         bc[i] = state64[i] ^ state64[i + 5] ^ state64[i + 10] ^
260             state64[i + 15] ^ state64[i + 20];
261     }
262     
263     pragma(inline, true)
264     void THETA2(size_t i)
265     {
266         t = bc[(i + 4) % 5] ^ rol(bc[(i + 1) % 5], 1);
267         state64[     i] ^= t;
268         state64[ 5 + i] ^= t;
269         state64[10 + i] ^= t;
270         state64[15 + i] ^= t;
271         state64[20 + i] ^= t;
272     }
273     
274     pragma(inline, true)
275     void RHO(size_t i)
276     {
277         size_t j = K_PI[i];
278         bc[0] = state64[j];
279         state64[j] = rol(t, K_RHO[i]);
280         t = bc[0];
281     }
282     
283     pragma(inline, true)
284     void CHI(size_t j)
285     {
286         bc[0] = state64[j];
287         bc[1] = state64[j + 1];
288         bc[2] = state64[j + 2];
289         bc[3] = state64[j + 3];
290         bc[4] = state64[j + 4];
291 
292         state64[j]     ^= (~bc[1]) & bc[2];
293         state64[j + 1] ^= (~bc[2]) & bc[3];
294         state64[j + 2] ^= (~bc[3]) & bc[4];
295         state64[j + 3] ^= (~bc[4]) & bc[0];
296         state64[j + 4] ^= (~bc[0]) & bc[1];
297     }
298     
299     version (BigEndian)
300     void swap()
301     {
302         state64[ 0] = bswap(state64[ 0]);
303         state64[ 1] = bswap(state64[ 1]);
304         state64[ 2] = bswap(state64[ 2]);
305         state64[ 3] = bswap(state64[ 3]);
306         state64[ 4] = bswap(state64[ 4]);
307         state64[ 5] = bswap(state64[ 5]);
308         state64[ 6] = bswap(state64[ 6]);
309         state64[ 7] = bswap(state64[ 7]);
310         state64[ 8] = bswap(state64[ 8]);
311         state64[ 9] = bswap(state64[ 9]);
312         state64[10] = bswap(state64[10]);
313         state64[11] = bswap(state64[11]);
314         state64[12] = bswap(state64[12]);
315         state64[13] = bswap(state64[13]);
316         state64[14] = bswap(state64[14]);
317         state64[15] = bswap(state64[15]);
318         state64[16] = bswap(state64[16]);
319         state64[17] = bswap(state64[17]);
320         state64[18] = bswap(state64[18]);
321         state64[19] = bswap(state64[19]);
322         state64[20] = bswap(state64[20]);
323         state64[21] = bswap(state64[21]);
324         state64[22] = bswap(state64[22]);
325         state64[23] = bswap(state64[23]);
326     }
327 }
328 
329 /// Template alias for SHA-3-224.
330 public alias SHA3_224 = KECCAK!(224);
331 /// Template alias for SHA-3-256.
332 public alias SHA3_256 = KECCAK!(256);
333 /// Template alias for SHA-3-384.
334 public alias SHA3_384 = KECCAK!(384);
335 /// Template alias for SHA-3-512.
336 public alias SHA3_512 = KECCAK!(512);
337 /// Template alias for SHAKE-128.
338 public alias SHAKE128 = KECCAK!(128, 128);
339 /// Template alias for SHAKE-256.
340 public alias SHAKE256 = KECCAK!(256, 256);
341 
342 /// Convience alias using the SHA-3 implementation.
343 auto sha3_224Of(T...)(T data) { return digest!(SHA3_224, T)(data); }
344 /// Ditto
345 auto sha3_256Of(T...)(T data) { return digest!(SHA3_256, T)(data); }
346 /// Ditto
347 auto sha3_384Of(T...)(T data) { return digest!(SHA3_384, T)(data); }
348 /// Ditto
349 auto sha3_512Of(T...)(T data) { return digest!(SHA3_512, T)(data); }
350 /// Ditto
351 auto shake128Of(T...)(T data) { return digest!(SHAKE128, T)(data); }
352 /// Ditto
353 auto shake256Of(T...)(T data) { return digest!(SHAKE256, T)(data); }
354 
355 // Unittests based on https://www.di-mgt.com.au/sha_testvectors.html
356 
357 /// Test against empty datasets
358 @safe unittest
359 {
360     import std.ascii : LetterCase;
361     
362     assert(toHexString!(LetterCase.lower)(sha3_224Of("")) ==
363         "6b4e03423667dbb73b6e15454f0eb1abd4597f9a1b078e3f5b5a6bc7");
364 
365     assert(toHexString!(LetterCase.lower)(sha3_256Of("")) ==
366         "a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a");
367 
368     assert(toHexString!(LetterCase.lower)(sha3_384Of("")) ==
369         "0c63a75b845e4f7d01107d852e4c2485c51a50aaaa94fc61995e71bbee983a2a"~
370         "c3713831264adb47fb6bd1e058d5f004");
371 
372     assert(toHexString!(LetterCase.lower)(sha3_512Of("")) ==
373         "a69f73cca23a9ac5c8b567dc185a756e97c982164fe25859e0d1dcc1475c80a6"~
374         "15b2123af1f5f94c11e3e9402c3ac558f500199d95b6d3e301758586281dcd26");
375 
376     assert(toHexString!(LetterCase.lower)(shake128Of("")) ==
377         "7f9c2ba4e88f827d616045507605853e");
378 
379     assert(toHexString!(LetterCase.lower)(shake256Of("")) ==
380         "46b9dd2b0ba88d13233b3feb743eeb243fcd52ea62b81b82b50c27646ed5762f");
381 }
382 
383 /// Using convenience wrappers.
384 @safe unittest
385 {
386     import std.ascii : LetterCase;
387     
388     assert(toHexString!(LetterCase.lower)(sha3_224Of("abc")) ==
389         "e642824c3f8cf24ad09234ee7d3c766fc9a3a5168d0c94ad73b46fdf");
390 
391     assert(toHexString!(LetterCase.lower)(sha3_256Of("abc")) ==
392         "3a985da74fe225b2045c172d6bd390bd855f086e3e9d525b46bfe24511431532");
393 
394     assert(toHexString!(LetterCase.lower)(sha3_384Of("abc")) ==
395         "ec01498288516fc926459f58e2c6ad8df9b473cb0fc08c2596da7cf0e49be4b2"~
396         "98d88cea927ac7f539f1edf228376d25");
397 
398     assert(toHexString!(LetterCase.lower)(sha3_512Of("abc")) ==
399         "b751850b1a57168a5693cd924b6b096e08f621827444f70d884f5d0240d2712e"~
400         "10e116e9192af3c91a7ec57647e3934057340b4cf408d5a56592f8274eec53f0");
401 
402     assert(toHexString!(LetterCase.lower)(shake128Of("abc")) ==
403         "5881092dd818bf5cf8a3ddb793fbcba7");
404 
405     assert(toHexString!(LetterCase.lower)(shake256Of("abc")) ==
406         "483366601360a8771c6863080cc4114d8db44530f8f1e1ee4f94ea37e78b5739");
407 }
408 
409 /// Template API functions.
410 @safe unittest
411 {
412     SHA3_224 hash;
413     hash.start();
414     ubyte[1024] data;
415     hash.put(data);
416     ubyte[28] result = hash.finish();
417     assert(toHexString!(LetterCase.lower)(result) ==
418         "8c6c078646496be04b6f06d0ae323e62bbd0d08201f6a1bbb475ba3e");
419 }
420 
421 /// Template API features.
422 @safe unittest
423 {
424     import std.ascii : LetterCase;
425     
426     // NOTE: Because the digest is a structure, it must be passed by reference.
427     void doSomething(T)(ref T hash)
428         if (isDigest!T)
429         {
430             hash.put(cast(ubyte) 0);
431         }
432     SHA3_224 sha;
433     sha.start();
434     doSomething(sha);
435     assert(toHexString!(LetterCase.lower)(sha.finish()) ==
436         "bdd5167212d2dc69665f5a8875ab87f23d5ce7849132f56371a19096");
437 }
438 
439 /// This module conforms to the Digest API.
440 @safe unittest
441 {
442     assert(isDigest!SHA3_224);
443     assert(isDigest!SHA3_256);
444     assert(isDigest!SHA3_384);
445     assert(isDigest!SHA3_512);
446     assert(isDigest!SHAKE128);
447     assert(isDigest!SHAKE256);
448 }
449 
450 /// The KECCAK structure has a blockSize field.
451 @safe unittest
452 {
453     assert(hasBlockSize!SHA3_224);
454     assert(hasBlockSize!SHA3_256);
455     assert(hasBlockSize!SHA3_384);
456     assert(hasBlockSize!SHA3_512);
457     assert(hasBlockSize!SHAKE128);
458     assert(hasBlockSize!SHAKE256);
459 }
460 
461 /// Using the digest template API.
462 @safe unittest
463 {
464     ubyte[28] hash224 = sha3_224Of("abc");
465     assert(hash224 == digest!SHA3_224("abc"));
466 
467     ubyte[32] hash256 = sha3_256Of("abc");
468     assert(hash256 == digest!SHA3_256("abc"));
469 
470     ubyte[48] hash384 = sha3_384Of("abc");
471     assert(hash384 == digest!SHA3_384("abc"));
472 
473     ubyte[64] hash512 = sha3_512Of("abc");
474     assert(hash512 == digest!SHA3_512("abc"));
475 
476     ubyte[16] shakeHash128 = shake128Of("abc");
477     assert(shakeHash128 == digest!SHAKE128("abc"));
478 
479     ubyte[32] shakeHash256 = shake256Of("abc");
480     assert(shakeHash256 == digest!SHAKE256("abc"));
481 }
482 
483 /// Using the template API.
484 @system unittest
485 {
486     import std.conv : hexString;
487     
488     SHA3_224 dgst_sha3_224;
489     dgst_sha3_224.put(cast(ubyte[]) "abcdef");
490     dgst_sha3_224.start();
491     dgst_sha3_224.put(cast(ubyte[]) "");
492     assert(dgst_sha3_224.finish() == cast(ubyte[]) hexString!
493         "6b4e03423667dbb73b6e15454f0eb1abd4597f9a1b078e3f5b5a6bc7");
494     
495     SHA3_256 dgst_sha3_256;
496     dgst_sha3_256.put(cast(ubyte[]) "abcdef");
497     dgst_sha3_256.start();
498     dgst_sha3_256.put(cast(ubyte[]) "");
499     assert(dgst_sha3_256.finish() == cast(ubyte[]) hexString!
500         "a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a");
501     
502     SHA3_384 dgst_sha3_384;
503     dgst_sha3_384.put(cast(ubyte[]) "abcdef");
504     dgst_sha3_384.start();
505     dgst_sha3_384.put(cast(ubyte[]) "");
506     assert(dgst_sha3_384.finish() == cast(ubyte[]) hexString!(
507         "0c63a75b845e4f7d01107d852e4c2485c51a50aaaa94fc61995e71bbee983a2a"
508         ~"c3713831264adb47fb6bd1e058d5f004"));
509     
510     SHA3_512 dgst_sha3_512;
511     dgst_sha3_512.put(cast(ubyte[]) "abcdef");
512     dgst_sha3_512.start();
513     dgst_sha3_512.put(cast(ubyte[]) "");
514     assert(dgst_sha3_512.finish() == cast(ubyte[]) hexString!(
515         "a69f73cca23a9ac5c8b567dc185a756e97c982164fe25859e0d1dcc1475c80a6"~
516         "15b2123af1f5f94c11e3e9402c3ac558f500199d95b6d3e301758586281dcd26"));
517     
518     SHAKE128 dgst_shake128;
519     dgst_shake128.put(cast(ubyte[]) "abcdef");
520     dgst_shake128.start();
521     dgst_shake128.put(cast(ubyte[]) "");
522     assert(dgst_shake128.finish() == cast(ubyte[]) hexString!
523         "7f9c2ba4e88f827d616045507605853e");
524     
525     SHAKE256 dgst_shake256;
526     dgst_shake256.put(cast(ubyte[]) "abcdef");
527     dgst_shake256.start();
528     dgst_shake256.put(cast(ubyte[]) "");
529     assert(dgst_shake256.finish() == cast(ubyte[]) hexString!
530         "46b9dd2b0ba88d13233b3feb743eeb243fcd52ea62b81b82b50c27646ed5762f");
531 }
532 
533 /// Convenience wrappers.
534 @system unittest
535 {
536     import std.conv : hexString;
537     
538     immutable string a = "a";
539     
540     auto digest224      = sha3_224Of(a);
541     assert(digest224 == cast(ubyte[]) hexString!
542         "9e86ff69557ca95f405f081269685b38e3a819b309ee942f482b6a8b");
543     auto digest256      = sha3_256Of(a);
544     assert(digest256 == cast(ubyte[]) hexString!
545         "80084bf2fba02475726feb2cab2d8215eab14bc6bdd8bfb2c8151257032ecd8b");
546     auto digest384      = sha3_384Of(a);
547     assert(digest384 == cast(ubyte[]) hexString!(
548         "1815f774f320491b48569efec794d249eeb59aae46d22bf77dafe25c5edc28d7"~
549         "ea44f93ee1234aa88f61c91912a4ccd9"));
550     auto digest512      = sha3_512Of(a);
551     assert(digest512 == cast(ubyte[]) hexString!(
552         "697f2d856172cb8309d6b8b97dac4de344b549d4dee61edfb4962d8698b7fa80"~
553         "3f4f93ff24393586e28b5b957ac3d1d369420ce53332712f997bd336d09ab02a"));
554     auto digestshake128 = shake128Of(a);
555     assert(digestshake128 == cast(ubyte[]) hexString!
556         "85c8de88d28866bf0868090b3961162b");
557     auto digestshake256 = shake256Of(a);
558     assert(digestshake256 == cast(ubyte[]) hexString!(
559         "867e2cb04f5a04dcbd592501a5e8fe9ceaafca50255626ca736c138042530ba4"));
560         
561     immutable string abc = "abc";
562     
563     digest224      = sha3_224Of(abc);
564     assert(digest224 == cast(ubyte[]) hexString!
565         "e642824c3f8cf24ad09234ee7d3c766fc9a3a5168d0c94ad73b46fdf");
566     digest256      = sha3_256Of(abc);
567     assert(digest256 == cast(ubyte[]) hexString!
568         "3a985da74fe225b2045c172d6bd390bd855f086e3e9d525b46bfe24511431532");
569     digest384      = sha3_384Of(abc);
570     assert(digest384 == cast(ubyte[]) hexString!(
571         "ec01498288516fc926459f58e2c6ad8df9b473cb0fc08c2596da7cf0e49be4b2"~
572         "98d88cea927ac7f539f1edf228376d25"));
573     digest512      = sha3_512Of(abc);
574     assert(digest512 == cast(ubyte[]) hexString!(
575         "b751850b1a57168a5693cd924b6b096e08f621827444f70d884f5d0240d2712e"~
576         "10e116e9192af3c91a7ec57647e3934057340b4cf408d5a56592f8274eec53f0"));
577     digestshake128 = shake128Of(abc);
578     assert(digestshake128 == cast(ubyte[]) hexString!
579         "5881092dd818bf5cf8a3ddb793fbcba7");
580     digestshake256 = shake256Of(abc);
581     assert(digestshake256 == cast(ubyte[]) hexString!(
582         "483366601360a8771c6863080cc4114d8db44530f8f1e1ee4f94ea37e78b5739"));
583     
584     immutable string longString =
585         "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq";
586     
587     digest224      = sha3_224Of(longString);
588     assert(digest224 == cast(ubyte[]) hexString!
589         "8a24108b154ada21c9fd5574494479ba5c7e7ab76ef264ead0fcce33");
590     digest256      = sha3_256Of(longString);
591     assert(digest256 == cast(ubyte[]) hexString!
592         "41c0dba2a9d6240849100376a8235e2c82e1b9998a999e21db32dd97496d3376");
593     digest384      = sha3_384Of(longString);
594     assert(digest384 == cast(ubyte[]) hexString!(
595         "991c665755eb3a4b6bbdfb75c78a492e8c56a22c5c4d7e429bfdbc32b9d4ad5a"~
596         "a04a1f076e62fea19eef51acd0657c22"));
597     digest512      = sha3_512Of(longString);
598     assert(digest512 == cast(ubyte[]) hexString!(
599         "04a371e84ecfb5b8b77cb48610fca8182dd457ce6f326a0fd3d7ec2f1e91636d"~
600         "ee691fbe0c985302ba1b0d8dc78c086346b533b49c030d99a27daf1139d6e75e"));
601     digestshake128 = shake128Of(longString);
602     assert(digestshake128 == cast(ubyte[]) hexString!
603         "1a96182b50fb8c7e74e0a707788f55e9");
604     digestshake256 = shake256Of(longString);
605     assert(digestshake256 == cast(ubyte[]) hexString!(
606         "4d8c2dd2435a0128eefbb8c36f6f87133a7911e18d979ee1ae6be5d4fd2e3329"));
607     
608     ubyte[] onemilliona = new ubyte[1_000_000];
609     onemilliona[] = 'a';
610     
611     digest224      = sha3_224Of(onemilliona);
612     assert(digest224 == cast(ubyte[]) hexString!
613         "d69335b93325192e516a912e6d19a15cb51c6ed5c15243e7a7fd653c");
614     digest256      = sha3_256Of(onemilliona);
615     assert(digest256 == cast(ubyte[]) hexString!
616         "5c8875ae474a3634ba4fd55ec85bffd661f32aca75c6d699d0cdcb6c115891c1");
617     digest384      = sha3_384Of(onemilliona);
618     assert(digest384 == cast(ubyte[]) hexString!(
619         "eee9e24d78c1855337983451df97c8ad9eedf256c6334f8e948d252d5e0e7684"~
620         "7aa0774ddb90a842190d2c558b4b8340"));
621     digest512      = sha3_512Of(onemilliona);
622     assert(digest512 == cast(ubyte[]) hexString!(
623         "3c3a876da14034ab60627c077bb98f7e120a2a5370212dffb3385a18d4f38859"~
624         "ed311d0a9d5141ce9cc5c66ee689b266a8aa18ace8282a0e0db596c90b0a7b87"));
625     digestshake128 = shake128Of(onemilliona);
626     assert(digestshake128 == cast(ubyte[]) hexString!
627         "9d222c79c4ff9d092cf6ca86143aa411");
628     digestshake256 = shake256Of(onemilliona);
629     assert(digestshake256 == cast(ubyte[]) hexString!(
630         "3578a7a4ca9137569cdf76ed617d31bb994fca9c1bbf8b184013de8234dfd13a"));
631 }
632 
633 // Because WrapperDigest requires functions to be nothrow
634 version (SHA3D_Trace) {}
635 else
636 {
637     /// OOP API SHA-3/SHAKE implementation alias.
638     public alias SHA3_224Digest = WrapperDigest!SHA3_224;
639     /// Ditto
640     public alias SHA3_256Digest = WrapperDigest!SHA3_256;
641     /// Ditto
642     public alias SHA3_384Digest = WrapperDigest!SHA3_384;
643     /// Ditto
644     public alias SHA3_512Digest = WrapperDigest!SHA3_512;
645     /// Ditto
646     public alias SHAKE128Digest = WrapperDigest!SHAKE128;
647     /// Ditto
648     public alias SHAKE256Digest = WrapperDigest!SHAKE256;
649     
650     /// Using the OOP API.
651     @system unittest
652     {
653         import std.conv : hexString;
654         
655         SHA3_224Digest sha3_224 = new SHA3_224Digest();
656         assert(sha3_224.finish() == cast(ubyte[]) hexString!
657             "6b4e03423667dbb73b6e15454f0eb1abd4597f9a1b078e3f5b5a6bc7");
658         
659         SHA3_256Digest sha3_256 = new SHA3_256Digest();
660         assert(sha3_256.finish() == cast(ubyte[]) hexString!
661             "a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a");
662         
663         SHA3_384Digest sha3_384 = new SHA3_384Digest();
664         assert(sha3_384.finish() == cast(ubyte[]) hexString!(
665             "0c63a75b845e4f7d01107d852e4c2485c51a50aaaa94fc61995e71bbee983a2a"~
666             "c3713831264adb47fb6bd1e058d5f004"));
667         
668         SHA3_512Digest sha3_512 = new SHA3_512Digest();
669         assert(sha3_512.finish() == cast(ubyte[]) hexString!(
670             "a69f73cca23a9ac5c8b567dc185a756e97c982164fe25859e0d1dcc1475c80a6"~
671             "15b2123af1f5f94c11e3e9402c3ac558f500199d95b6d3e301758586281dcd26"));
672         
673         SHAKE128Digest shake128 = new SHAKE128Digest();
674         assert(shake128.finish() == cast(ubyte[]) hexString!(
675             "7f9c2ba4e88f827d616045507605853e"));
676         
677         SHAKE256Digest shake256 = new SHAKE256Digest();
678         assert(shake256.finish() == cast(ubyte[]) hexString!(
679             "46b9dd2b0ba88d13233b3feb743eeb243fcd52ea62b81b82b50c27646ed5762f"));
680     }
681 }
682 
683 /// Testing with HMAC
684 @system unittest
685 {
686     // NOTE: OpenSSL (3.0.1) seems to be incapable of producing a hash
687     //       using SHAKE and HMAC. However, this should work since
688     //       unittests for SHAKE-related hashes are passing.
689     
690     import std.ascii : LetterCase;
691     import std.string : representation;
692     import std.digest.hmac : hmac;
693     
694     immutable string input =
695         "The quick brown fox jumps over the lazy dog";
696     auto secret = "secret".representation;
697     
698     assert(input
699         .representation
700         .hmac!SHA3_256(secret)
701         .toHexString!(LetterCase.lower) ==
702 	    "93379fab68fae6d0fde0c816ea8a49fbd3c80f136c6af08bc61df5268d01b4d8");
703     assert(input
704         .representation
705         .hmac!SHA3_512(secret)
706         .toHexString!(LetterCase.lower) ==
707 	    "394e52da72b28bab49174a0d22cd48eac415de750027e6485ceb945b9948d8ae"~
708         "e656e61e217ac1352a41c66454e2a9ae830fddbdf4f8aa6215c586b88e158ee8");
709 }
710 
711 // because of WrapperDigest
712 version (SHA3D_Trace) {}
713 else
714 {
715     /// Testing out various SHAKE XOFs.
716     @system unittest
717     {
718         import std.conv : hexString;
719         
720         // Define SHAKE-128/256
721         alias SHAKE128_256 = KECCAK!(128, 256);
722         alias SHAKE128_256Digest = WrapperDigest!SHAKE128_256;
723         auto shake128_256Of(T...)(T data) { return digest!(SHAKE128_256, T)(data); }
724         
725         // SHAKE128("", 256) =
726         auto shake128_256empty = hexString!(
727             "7f9c2ba4e88f827d616045507605853ed73b8093f6efbc88eb1a6eacfa66ef26");
728         
729         // Using convenience alias
730         assert(shake128_256Of("") == shake128_256empty);
731         
732         // Using OOP API
733         Digest shake128_256 = new SHAKE128_256Digest();
734         assert(shake128_256.finish() == shake128_256empty);
735         
736         // Define SHAKE-256/512
737         alias SHAKE256_512 = KECCAK!(256, 512);
738         alias SHAKE256_512Digest = WrapperDigest!SHAKE256_512;
739         auto shake256_512Of(T...)(T data) { return digest!(SHAKE256_512, T)(data); }
740         
741         // SHAKE256("", 512) =
742         auto shake256_512empty = hexString!(
743             "46b9dd2b0ba88d13233b3feb743eeb243fcd52ea62b81b82b50c27646ed5762f"~
744             "d75dc4ddd8c0f200cb05019d67b592f6fc821c49479ab48640292eacb3b7c4be");
745         
746         // Convenience alias
747         assert(shake256_512Of("") == shake256_512empty);
748         
749         // OOP API
750         Digest shake256_512 = new SHAKE256_512Digest();
751         assert(shake256_512.finish() == shake256_512empty);
752     }
753 }
754 
755 /// Stretching out XOFs functions to extremes.
756 @system unittest
757 {
758     import std.conv : hexString;
759     
760     // Define SHAKE-256/2048
761     alias SHAKE256_2048 = KECCAK!(256, 2048);
762     auto shake256_2048Of(T...)(T data) { return digest!(SHAKE256_2048, T)(data); }
763     
764     // SHAKE-256/2048('abc')
765     assert(shake256_2048Of("abc") == hexString!(
766         "483366601360a8771c6863080cc4114d8db44530f8f1e1ee4f94ea37e78b5739"~
767         "d5a15bef186a5386c75744c0527e1faa9f8726e462a12a4feb06bd8801e751e4"~
768         "1385141204f329979fd3047a13c5657724ada64d2470157b3cdc288620944d78"~
769         "dbcddbd912993f0913f164fb2ce95131a2d09a3e6d51cbfc622720d7a75c6334"~
770         "e8a2d7ec71a7cc29cf0ea610eeff1a588290a53000faa79932becec0bd3cd0b3"~
771         "3a7e5d397fed1ada9442b99903f4dcfd8559ed3950faf40fe6f3b5d710ed3b67"~
772         "7513771af6bfe11934817e8762d9896ba579d88d84ba7aa3cdc7055f6796f195"~
773         "bd9ae788f2f5bb96100d6bbaff7fbc6eea24d4449a2477d172a5507dcc931412"));
774 }