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 }