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