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