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