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 }