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