1 module dcrypt.crypto.digests.sha3; 2 3 /// Implementation of Keccak, SHA3 and extendable output functions SHAKE128/256. 4 /// 5 /// Standard: FIPS 202, SHA 3 6 7 import dcrypt.crypto.digest; 8 import dcrypt.bitmanip; 9 10 import std.conv: text; 11 import std.algorithm: min; 12 13 alias Keccak!(224*2) Keccak224; 14 alias Keccak!(256*2) Keccak256; 15 alias Keccak!(288*2) Keccak288; 16 alias Keccak!(384*2) Keccak384; 17 alias Keccak!(512*2) Keccak512; 18 19 alias SHA3!224 SHA3_224; 20 alias SHA3!256 SHA3_256; 21 alias SHA3!384 SHA3_384; 22 alias SHA3!512 SHA3_512; 23 24 alias WrapperDigest!SHA3_224 SHA3_224Digest; 25 alias WrapperDigest!SHA3_256 SHA3_256Digest; 26 alias WrapperDigest!SHA3_384 SHA3_384Digest; 27 alias WrapperDigest!SHA3_512 SHA3_512Digest; 28 29 static assert(isDigest!Keccak224); 30 static assert(isDigest!Keccak256); 31 static assert(isDigest!Keccak384); 32 static assert(isDigest!Keccak512); 33 34 static assert(isDigest!SHA3_224); 35 static assert(isDigest!SHA3_256); 36 static assert(isDigest!SHA3_384); 37 static assert(isDigest!SHA3_512); 38 39 /// Test Keccak 40 unittest { 41 import dcrypt.encoders.hex; 42 43 string msg1600 = x"8C3798E51BC68482D7337D3ABB75DC9FFE860714A9AD73551E120059860DDE24AB87327222B64CF774415A70F724CDF270DE3FE47DDA07B61C9EF2A3551F45A5584860248FABDE676E1CD75F6355AA3EAEABE3B51DC813D9FB2EAA4F0F1D9F834D7CAD9C7C695AE84B329385BC0BEF895B9F1EDF44A03D4B410CC23A79A6B62E4F346A5E8DD851C2857995DDBF5B2D717AEB847310E1F6A46AC3D26A7F9B44985AF656D2B7C9406E8A9E8F47DCB4EF6B83CAACF9AEFB6118BFCFF7E44BEF6937EBDDC89186839B77"; 44 45 immutable string[] plaintexts = [x"",x"",x"",x"", 46 // https://cloud.github.com/downloads/johanns/sha3/KeccakTestVectors.zip 47 x"CC", 48 msg1600, 49 x"CC", 50 msg1600, 51 x"CC", 52 msg1600, 53 x"CC", 54 msg1600 55 ]; 56 57 immutable string[] hexHashes = [ 58 x"f71837502ba8e10837bdd8d365adb85591895602fc552b48b7390abd", 59 x"c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", 60 x"2c23146a63a29acf99e73b88f8c24eaa7dc60aa771780ccc006afbfa8fe2479b2dd2b21362337441ac12b515911957ff", 61 x"0eab42de4c3ceb9235fc91acffe746b29c29a8c366b7c60e4e67c466f36a4304c00fa9caf9d87976ba469bcbe06713b435f091ef2769fb160cdab33d3670680e", 62 // https://cloud.github.com/downloads/johanns/sha3/KeccakTestVectors.zip 63 x"A9CAB59EB40A10B246290F2D6086E32E3689FAF1D26B470C899F2802", 64 x"1029CA117957D80F3C859E8394DD34969331CA3BCEDC436B1EAB0849", 65 x"EEAD6DBFC7340A56CAEDC044696A168870549A6A7F6F56961E84A54BD9970B8A", 66 x"E83EA21F5BC0976953AF86069A10EB6024A1AC59D609688E4A9759BB8B6C9441", 67 x"1B84E62A46E5A201861754AF5DC95C4A1A69CAF4A796AE405680161E29572641F5FA1E8641D7958336EE7B11C58F73E9", 68 x"B5A7160112E0825A7C03643BEB98B1FC2549B81F01C3C4271DFF99BE57D472A7FAD133808D7D2D414D6011E9A2E8DFEC", 69 x"8630C13CBD066EA74BBE7FE468FEC1DEE10EDC1254FB4C1B7C5FD69B646E44160B8CE01D05A0908CA790DFB080F4B513BC3B6225ECE7A810371441A5AC666EB9", 70 x"2A11CB6921EA662A39DDEE7982E3CF5B317195661D5505AD04D11EE23E178ED65F3E06A7F096F4EAF1FF6A09239CF5A0A39DC9F4C92AF63FDF7211E1CF467653", 71 72 ]; 73 74 75 for(size_t i = 0; i < plaintexts.length; ++i) { 76 const (ubyte)[] plain = cast(const ubyte[]) plaintexts[i]; 77 const ubyte[] expectedHash = cast(const ubyte[]) hexHashes[i]; 78 ubyte[] actualHash; 79 80 switch(expectedHash.length*8) { 81 case 224: 82 Keccak224 k; 83 k.put(plain); 84 actualHash = k.finish!224(); 85 break; 86 case 256: 87 Keccak256 k; 88 k.put(plain); 89 actualHash = k.finish!256(); 90 break; 91 case 384: 92 Keccak384 k; 93 k.put(plain); 94 actualHash = k.finish!384(); 95 break; 96 case 512: 97 Keccak512 k; 98 k.put(plain); 99 actualHash = k.finish!512(); 100 break; 101 default: assert(0); 102 } 103 104 assert(expectedHash == actualHash, "produced wrong hash: " ~ toHexStr(actualHash) 105 ~ " instead of " ~ toHexStr(expectedHash)); 106 } 107 } 108 109 unittest { 110 SHA3!224 sha3; 111 112 // empty message 113 assert(sha3.finish() == x" 114 6B 4E 03 42 36 67 DB B7 3B 6E 15 45 4F 0E B1 AB 115 D4 59 7F 9A 1B 07 8E 3F 5B 5A 6B C7", 116 sha3.name~" failed."); 117 118 ubyte[200] msg = 0b10100011; 119 sha3.put(msg); 120 121 assert(sha3.finish() == x" 122 93 76 81 6A BA 50 3F 72 F9 6C E7 EB 65 AC 09 5D 123 EE E3 BE 4B F9 BB C2 A1 CB 7E 11 E0", 124 sha3.name~" failed."); 125 } 126 127 unittest { 128 SHA3!256 sha3; 129 130 // empty message 131 assert(sha3.finish() == x"A7 FF C6 F8 BF 1E D7 66 51 C1 47 56 A0 61 D6 62 132 F5 80 FF 4D E4 3B 49 FA 82 D8 0A 4B 80 F8 43 4A", 133 sha3.name~" failed."); 134 } 135 136 unittest { 137 SHA3!384 sha3; 138 139 // empty message 140 assert(sha3.finish() == x" 141 0C 63 A7 5B 84 5E 4F 7D 01 10 7D 85 2E 4C 24 85 142 C5 1A 50 AA AA 94 FC 61 99 5E 71 BB EE 98 3A 2A 143 C3 71 38 31 26 4A DB 47 FB 6B D1 E0 58 D5 F0 04", 144 sha3.name~" failed."); 145 } 146 147 unittest { 148 SHA3!512 sha3; 149 150 // empty message 151 assert(sha3.finish() == x"A6 9F 73 CC A2 3A 9A C5 C8 B5 67 DC 18 5A 75 6E 152 97 C9 82 16 4F E2 58 59 E0 D1 DC C1 47 5C 80 A6 153 15 B2 12 3A F1 F5 F9 4C 11 E3 E9 40 2C 3A C5 58 154 F5 00 19 9D 95 B6 D3 E3 01 75 85 86 28 1D CD 26", 155 sha3.name~" failed."); 156 } 157 158 /// Implementation of SHA3. 159 /// 160 /// Standard: FIPS 202, SHA 3 161 @safe 162 public struct SHA3(uint bitLength) 163 if(bitLength == 224 || bitLength == 256 || bitLength == 384 || bitLength == 512) 164 { 165 private Keccak!(bitLength*2) keccak; 166 167 enum name = text("SHA3-", bitLength); 168 enum digestLength = keccak.digestLength; 169 170 enum blockSize = [224: 144, 256: 136, 384: 104, 512: 72][bitLength]; /// Block size for HMAC as defined in FIPS 202, section 7, table 3. 171 172 void start() nothrow @nogc { 173 keccak.start(); 174 } 175 176 public void put(in ubyte[] b...) nothrow @nogc { 177 keccak.put(b); 178 } 179 180 /// Calculate the final hash value. 181 /// Params: 182 /// output = buffer for hash value. 183 /// Returns: length of hash value in bytes. 184 ubyte[] finish(ubyte[] output) nothrow @nogc 185 in { 186 assert(output.length == digestLength, "output.length != digestLength."); 187 } body { 188 189 keccak.absorbBits(0b10, 2); 190 191 return keccak.finish(output); 192 } 193 194 /// Calculate the final hash value. 195 /// Returns: the hash value 196 ubyte[digestLength] finish() nothrow @nogc { 197 ubyte[digestLength] hash; 198 finish(hash); 199 return hash; 200 } 201 } 202 203 204 /// Implementation of SHA-3 based on following KeccakNISTInterface.c from http://keccak.noekeon.org/ 205 @safe 206 public struct Keccak(uint capacity) 207 if(capacity % 8 == 0) 208 { 209 210 public { 211 //static assert(bitLength == 224 || bitLength == 256 || bitLength == 288 || bitLength == 384 || bitLength == 512); 212 enum name = text("Keccak[", capacity, ", ", rate, "]"); 213 enum digestLength = bitLength / 8; 214 215 public enum blockSize = 0; 216 217 @nogc 218 void put(in ubyte[] input...) nothrow 219 { 220 absorb(input); 221 } 222 223 /// Fills the output buffer with the hash value. 224 /// Note: Hash will be as long as the output buffer. 225 /// Params: 226 /// output = buffer for hash value. 227 /// Returns: Slice of `output` containing the hash. 228 ubyte[] finish(ubyte[] output) nothrow @nogc { 229 squeeze(output); 230 start(); 231 return output; 232 } 233 234 /// Calculate the final hash value. 235 /// Params: 236 /// outputLen = Hash size in bits. 237 /// Returns: the hash value 238 ubyte[outputLen/8] finish(uint outputLen = bitLength)() nothrow @nogc 239 if (outputLen % 8 == 0) 240 { 241 ubyte[outputLen/8] buf; 242 finish(buf); 243 return buf; 244 } 245 246 void start() nothrow @nogc 247 { 248 initSponge(); 249 } 250 } 251 252 private { 253 254 enum rate = 1600 - capacity; 255 enum bitLength = capacity / 2; 256 enum byteStateLength = 1600 / 8; 257 enum rounds = 24; 258 259 uint bitsInQueue; 260 bool squeezing; 261 uint bitsAvailableForSqueezing; 262 ubyte[byteStateLength] state; 263 ubyte[rate / 8] dataQueue; 264 } 265 266 private: 267 nothrow @nogc: 268 269 void clearDataQueueSection(in size_t off, in size_t len) { 270 dataQueue[off..off+len] = 0; 271 } 272 273 /// Handles data with arbitrary bit size. 274 void doUpdate(in ubyte[] data, in size_t databitlen) 275 in { 276 assert(data.length == (databitlen+7) / 8); 277 } body { 278 if ((databitlen % 8) == 0) 279 { 280 assert(databitlen == data.length * 8); 281 absorb(data); 282 } 283 else 284 { 285 if(databitlen > 8) { 286 // Absorb all bytes except the last. 287 absorb(data[0..$-2]); 288 } 289 290 immutable size_t bitLen = databitlen % 8; 291 292 ubyte lastByte = cast(ubyte)(data[(databitlen / 8)] >>> (8 - bitLen)); 293 //absorb(lastByte, databitlen % 8); 294 absorbBits(lastByte, bitLen); 295 } 296 } 297 298 /// Resets Keccak. 299 void initSponge() 300 { 301 state[] = 0; 302 dataQueue[] = 0; 303 bitsInQueue = 0; 304 squeezing = false; 305 bitsAvailableForSqueezing = 0; 306 } 307 308 void absorbQueue() 309 { 310 KeccakAbsorb!rounds(state, dataQueue[0..rate / 8]); 311 bitsInQueue = 0; 312 } 313 314 package void absorbBits(in ubyte partialByte, in ulong bitLen) 315 in { 316 assert(bitLen < 8, "bitLen must be < 8."); 317 assert ((bitsInQueue % 8) == 0, "attempt to absorb with odd length queue."); 318 assert(!squeezing, "attempt to absorb while squeezing."); 319 } 320 body { 321 if (bitsInQueue == rate) 322 { 323 absorbQueue(); 324 } 325 uint mask = (1 << bitLen) - 1; 326 dataQueue[bitsInQueue / 8] = cast(ubyte)(partialByte & mask); 327 bitsInQueue += bitLen; 328 } 329 330 /// Absorb even bytes. 331 void absorb(in ubyte[] data) 332 in { 333 assert ((bitsInQueue % 8) == 0, "attempt to absorb with odd length queue."); 334 assert(!squeezing, "attempt to absorb while squeezing."); 335 } 336 body { 337 338 const (ubyte)[] iBuf = data; 339 340 while (iBuf.length > 0) 341 { 342 assert(bitsInQueue % 8 == 0); 343 344 if ((bitsInQueue == 0) && (iBuf.length >= rate/8)) 345 { 346 while(iBuf.length > rate/8) 347 { 348 KeccakAbsorb!rounds(state, iBuf[0..rate / 8]); 349 iBuf = iBuf[rate / 8..$]; 350 } 351 } else { 352 immutable size_t partialBlock = min(iBuf.length, rate/8 - bitsInQueue/8); 353 354 dataQueue[bitsInQueue / 8 .. bitsInQueue / 8 + partialBlock] 355 = iBuf[0 .. partialBlock]; 356 iBuf = iBuf[partialBlock..$]; 357 358 bitsInQueue += partialBlock*8; 359 360 if (bitsInQueue == rate) 361 { 362 absorbQueue(); 363 } 364 } 365 } 366 } 367 368 void padAndSwitchToSqueezingPhase() 369 { 370 if (bitsInQueue + 1 == rate) 371 { 372 dataQueue[bitsInQueue / 8] |= 1 << (bitsInQueue % 8); 373 absorbQueue(); 374 clearDataQueueSection(0, rate / 8); 375 } 376 else 377 { 378 clearDataQueueSection((bitsInQueue + 7) / 8, rate / 8 - (bitsInQueue + 7) / 8); 379 dataQueue[bitsInQueue / 8] |= 1 << (bitsInQueue % 8); 380 } 381 dataQueue[(rate - 1) / 8] |= 1 << ((rate - 1) % 8); 382 absorbQueue(); 383 384 KeccakExtract(state, dataQueue, rate / 64); 385 386 bitsAvailableForSqueezing = rate; 387 388 squeezing = true; 389 } 390 391 392 package void squeeze(ubyte[] output) 393 { 394 immutable size_t outputLength = output.length*8; 395 uint partialBlock; 396 397 if (!squeezing) 398 { 399 padAndSwitchToSqueezingPhase(); 400 } 401 402 while (output.length > 0) 403 { 404 if (bitsAvailableForSqueezing == 0) 405 { 406 keccakPermutation!rounds(state); 407 408 KeccakExtract(state, dataQueue, rate / 64); 409 bitsAvailableForSqueezing = rate; 410 411 } 412 partialBlock = min(bitsAvailableForSqueezing/8, output.length); 413 414 output[0..partialBlock] = dataQueue[(rate/8 - bitsAvailableForSqueezing/8)..(rate/8 - bitsAvailableForSqueezing/8) + partialBlock]; 415 output = output[partialBlock..$]; 416 417 bitsAvailableForSqueezing -= partialBlock*8; 418 } 419 } 420 421 static void keccakPermutation(uint rounds)(ref ubyte[byteStateLength] state) pure 422 { 423 ulong[25] longState; 424 425 fromLittleEndian(state[], longState[]); 426 keccakPermutation!rounds(longState); 427 toLittleEndian(longState[], state[]); 428 } 429 430 static void keccakPermutation(uint rounds)(ref ulong[25] state) pure 431 { 432 foreach (uint i; 0..rounds) 433 { 434 theta(state); 435 rho(state); 436 pi(state); 437 chi(state); 438 iota(state, i); 439 } 440 } 441 442 static void theta(ref ulong[25] A) pure 443 { 444 ulong[5] C; 445 foreach (uint x; 0..5) 446 { 447 foreach (uint y; 0..5) 448 { 449 C[x] ^= A[x + 5 * y]; 450 } 451 } 452 foreach (uint x; 0..5) 453 { 454 ulong dX = rol(C[(x + 1) % 5], 1) ^ C[(x + 4) % 5]; 455 foreach (uint y; 0..5) 456 { 457 A[x + 5 * y] ^= dX; 458 } 459 } 460 } 461 462 /// Rotate each element of A by the index in the KeccakRhoOffsets table. 463 static void rho(ref ulong[25] A) pure 464 { 465 foreach (uint index; 0..25) 466 { 467 immutable uint rhoOffset = KeccakRhoOffsets[index]; 468 A[index] = rol(A[index], rhoOffset); 469 } 470 } 471 472 473 static void pi(ref ulong[25] A) pure 474 { 475 ulong[25] tempA = A; 476 477 foreach (uint x; 0..5) 478 { 479 foreach (uint y; 0..5) 480 { 481 A[y + 5 * ((2 * x + 3 * y) % 5)] = tempA[x + 5 * y]; 482 } 483 } 484 } 485 486 487 static void chi(ref ulong[25] A) pure 488 { 489 ulong[5] chiC; 490 foreach (uint y; 0..5) 491 { 492 foreach (uint x; 0..5) 493 { 494 chiC[x] = A[x + 5 * y] ^ ((~A[(((x + 1) % 5) + 5 * y)]) & A[(((x + 2) % 5) + 5 * y)]); 495 } 496 497 A[5*y..5*y+5] = chiC[]; 498 } 499 } 500 501 502 static void iota(ref ulong[25] A, in uint indexRound) pure 503 { 504 A[0] ^= KeccakRoundConstants[indexRound]; 505 } 506 507 static void KeccakAbsorb(uint rounds)(ref ubyte[byteStateLength] byteState, in ubyte[] data) pure 508 in { 509 assert(data.length <= byteState.length); 510 } body { 511 byteState[0..data.length] ^= data[]; 512 keccakPermutation!rounds(byteState); 513 } 514 515 static void KeccakExtract(in ubyte[] byteState, ubyte[] data, in uint laneCount) pure 516 { 517 data[0..laneCount*8] = byteState[0..laneCount*8]; 518 } 519 } 520 521 522 // Keccak constants 523 private @safe { 524 enum ulong[24] KeccakRoundConstants = keccakInitializeRoundConstants(); 525 enum uint[25] KeccakRhoOffsets = keccakInitializeRhoOffsets(); 526 527 static ulong[24] keccakInitializeRoundConstants() pure nothrow @nogc 528 { 529 ulong[24] keccakRoundConstants; 530 ubyte[1] LFSRstate; 531 532 LFSRstate[0] = 0x01; 533 uint i, j, bitPosition; 534 535 for (i = 0; i < 24; i++) 536 { 537 keccakRoundConstants[i] = 0; 538 for (j = 0; j < 7; j++) 539 { 540 bitPosition = (1 << j) - 1; 541 if (LFSR86540(LFSRstate)) 542 { 543 keccakRoundConstants[i] ^= 1L << bitPosition; 544 } 545 } 546 } 547 548 return keccakRoundConstants; 549 } 550 551 static bool LFSR86540(ubyte[] LFSR) pure nothrow @nogc 552 { 553 bool result = (((LFSR[0]) & 0x01) != 0); 554 if (((LFSR[0]) & 0x80) != 0) 555 { 556 LFSR[0] = cast(ubyte)(((LFSR[0]) << 1) ^ 0x71); 557 } 558 else 559 { 560 LFSR[0] <<= 1; 561 } 562 563 return result; 564 } 565 566 static uint[25] keccakInitializeRhoOffsets() pure nothrow @nogc 567 { 568 uint[25] keccakRhoOffsets; 569 uint x, y, t, newX, newY; 570 571 keccakRhoOffsets[(((0) % 5) + 5 * ((0) % 5))] = 0; 572 x = 1; 573 y = 0; 574 for (t = 0; t < 24; t++) 575 { 576 keccakRhoOffsets[(((x) % 5) + 5 * ((y) % 5))] = ((t + 1) * (t + 2) / 2) % 64; 577 newX = (0 * x + 1 * y) % 5; 578 newY = (2 * x + 3 * y) % 5; 579 x = newX; 580 y = newY; 581 } 582 583 return keccakRhoOffsets; 584 } 585 } 586 587 alias SHAKE!128 SHAKE128; 588 alias SHAKE!256 SHAKE256; 589 alias SHAKE!(128, true) RawSHAKE128; 590 alias SHAKE!(256, true) RawSHAKE256; 591 592 /// Implementation of the SHAKE extendable output function (XOF). 593 /// Standard: FIPS 202, SHA 3, Section 6.3 594 public struct SHAKE(uint bitsize, bool raw = false) if(bitsize == 128 || bitsize == 256) { 595 596 public enum name = text(raw ? "RawSHAKE" : "SHAKE", bitsize); 597 598 private { 599 Keccak!(bitsize*2) keccak; 600 bool squeezing = false; 601 } 602 603 public void start() { 604 keccak.start(); 605 squeezing = false; 606 } 607 608 public void put(in ubyte[] b...) { 609 assert(!squeezing, name~": Illegal state. Can't absorb data while after extracting some. Use start() to reset."); 610 611 keccak.put(b); 612 } 613 614 public void nextBytes(ubyte[] buf) { 615 616 if(!squeezing) { 617 // switch to squeezing 618 static if(raw) { 619 keccak.absorbBits(0b11, 2); 620 } else { 621 keccak.absorbBits(0b1111, 4); 622 } 623 squeezing = true; 624 } 625 626 keccak.squeeze(buf); 627 } 628 629 } 630 631 /// Test SHAKE128 632 unittest { 633 import std.stdio; 634 635 SHAKE128 shake; 636 ubyte[32] buf; 637 638 shake.nextBytes(buf); 639 640 assert(buf == x"7f9c2ba4e88f827d616045507605853ed73b8093f6efbc88eb1a6eacfa66ef26", shake.name~" failed."); 641 642 shake.start(); 643 shake.put(cast(const ubyte[]) "The quick brown fox jumps over the lazy dog"); 644 shake.nextBytes(buf); 645 assert(buf == x"f4202e3c5852f9182a0430fd8144f0a74b95e7417ecae17db0f8cfeed0e3e66e", shake.name~" failed."); 646 } 647 648 /// Test SHAKE256 649 unittest { 650 import std.stdio; 651 652 SHAKE256 shake; 653 ubyte[64] buf; 654 655 shake.nextBytes(buf); 656 657 assert(buf == x"46b9dd2b0ba88d13233b3feb743eeb243fcd52ea62b81b82b50c27646ed5762fd75dc4ddd8c0f200cb05019d67b592f6fc821c49479ab48640292eacb3b7c4be", shake.name~" failed."); 658 }