1 module dcrypt.crypto.digests.sha2; 2 3 /// Implementation of SHA256, SHA384 and SHA512 hash algorithms. 4 5 public import dcrypt.crypto.digest; 6 7 import dcrypt.util.bitmanip; 8 import dcrypt.util.pack; 9 import std.conv: text; 10 11 alias SHA!256 SHA256; 12 alias SHA!384 SHA384; 13 alias SHA!512 SHA512; 14 15 // OOP Wrapper 16 alias WrapperDigest!SHA256 SHA256Digest; 17 alias WrapperDigest!SHA384 SHA384Digest; 18 alias WrapperDigest!SHA512 SHA512Digest; 19 20 static assert(isDigest!SHA256, "SHA256 does not fullfill requirements of isDigest."); 21 static assert(isDigest!SHA384, "SHA384 does not fullfill requirements of isDigest."); 22 static assert(isDigest!SHA512, "SHA512 does not fullfill requirements of isDigest."); 23 24 25 @safe 26 public struct SHA(uint bitLength) 27 if(bitLength == 256 || bitLength == 384 || bitLength == 512) { 28 29 public enum name = text("SHA", bitLength); 30 public enum digestLength = bitLength / 8; 31 32 33 /// Reset the digest to its initial state. It is not necessary to call start after finish or doFinal. 34 public void start() nothrow @nogc { 35 36 H1 = initH1; 37 H2 = initH2; 38 H3 = initH3; 39 H4 = initH4; 40 H5 = initH5; 41 H6 = initH6; 42 H7 = initH7; 43 H8 = initH8; 44 45 byteCount1 = 0; 46 47 static if(bitLength > 256) { 48 byteCount2 = 0; 49 } 50 51 xBufOff = 0; 52 xBuf[] = 0; 53 54 xOff = 0; 55 X[] = 0; 56 }; 57 58 alias put update; /// ensure compatibility to older code 59 60 void put(const (ubyte)[] input...) nothrow @nogc 61 { 62 // fill the current word 63 while(xBufOff != 0 && input.length > 0) { 64 putSingleByte(input[0]); 65 input = input[1..$]; 66 } 67 68 // process whole words 69 while(input.length > xBuf.length) { 70 processWord(input); 71 byteCount1 += xBuf.length; 72 input = input[xBuf.length..$]; 73 } 74 75 // process remainder 76 foreach(ubyte b; input) { 77 putSingleByte(b); 78 } 79 } 80 81 /// Calculate the final hash value. 82 /// Params: 83 /// output = buffer for hash value. 84 /// Returns: length of hash value in bytes. 85 uint doFinal(ubyte[] output) nothrow @nogc 86 { 87 88 _finish(); 89 90 // pack the integers into a byte array 91 // toBigEndian!ulong([H1,H2,H3,H4,H5,H6,H7,H8], output); 92 93 enum wordBytes = Word.sizeof; 94 95 toBigEndian!Word(H1, output[0*wordBytes..1*wordBytes]); 96 toBigEndian!Word(H2, output[1*wordBytes..2*wordBytes]); 97 toBigEndian!Word(H3, output[2*wordBytes..3*wordBytes]); 98 toBigEndian!Word(H4, output[3*wordBytes..4*wordBytes]); 99 toBigEndian!Word(H5, output[4*wordBytes..5*wordBytes]); 100 toBigEndian!Word(H6, output[5*wordBytes..6*wordBytes]); 101 102 static if(bitLength == 256 || bitLength == 512) { 103 toBigEndian!Word(H7, output[6*wordBytes..7*wordBytes]); 104 toBigEndian!Word(H8, output[7*wordBytes..8*wordBytes]); 105 } 106 107 start(); 108 109 return digestLength; 110 } 111 112 /// Calculate the final hash value. 113 /// Returns: the hash value 114 ubyte[digestLength] finish() nothrow @nogc { 115 ubyte[digestLength] buf; 116 doFinal(buf); 117 return buf; 118 } 119 120 121 private: 122 123 static if(bitLength == 256) { 124 alias uint Word; 125 } else { 126 alias ulong Word; 127 } 128 129 void putSingleByte(ubyte input) nothrow @nogc 130 { 131 xBuf[xBufOff++] = input; 132 133 if (xBufOff == xBuf.length) 134 { 135 processWord(xBuf); 136 xBufOff = 0; 137 } 138 139 byteCount1++; 140 } 141 142 /// process one word of input (4 bytes for sha256, 8 bytes for longer hashes) 143 void processWord(in ubyte[] input) nothrow @nogc 144 { 145 X[xOff] = fromBigEndian!Word(input); 146 147 if (++xOff == 16) 148 { 149 processBlock(); 150 } 151 } 152 153 void processLength(Word lowW, Word hiW) nothrow @nogc 154 { 155 if (xOff > 14) 156 { 157 processBlock(); 158 } 159 160 X[14] = hiW; 161 X[15] = lowW; 162 } 163 164 void processBlock() nothrow @nogc 165 { 166 static if(bitLength > 256){ 167 adjustByteCounts(); 168 } 169 170 // 171 // expand 16 word block into 80 word blocks. 172 // 173 foreach (size_t t; 16..X.length) 174 { 175 X[t] = Sigma1(X[t - 2]) + X[t - 7] + Sigma0(X[t - 15]) + X[t - 16]; 176 } 177 178 // 179 // set up working variables. 180 // 181 Word a = H1; 182 Word b = H2; 183 Word c = H3; 184 Word d = H4; 185 Word e = H5; 186 Word f = H6; 187 Word g = H7; 188 Word h = H8; 189 190 size_t t = 0; 191 192 static if(bitLength == 256) { 193 enum rounds = 8; 194 } else { 195 enum rounds = 10; 196 } 197 198 foreach(size_t i; 0..rounds) 199 { 200 // t = 8 * i 201 h += Sum1(e) + Ch(e, f, g) + K[t] + X[t]; 202 d += h; 203 h += Sum0(a) + Maj(a, b, c); 204 ++t; 205 206 // t = 8 * i + 1 207 g += Sum1(d) + Ch(d, e, f) + K[t] + X[t]; 208 c += g; 209 g += Sum0(h) + Maj(h, a, b); 210 ++t; 211 212 // t = 8 * i + 2 213 f += Sum1(c) + Ch(c, d, e) + K[t] + X[t]; 214 b += f; 215 f += Sum0(g) + Maj(g, h, a); 216 ++t; 217 218 // t = 8 * i + 3 219 e += Sum1(b) + Ch(b, c, d) + K[t] + X[t]; 220 a += e; 221 e += Sum0(f) + Maj(f, g, h); 222 ++t; 223 224 // t = 8 * i + 4 225 d += Sum1(a) + Ch(a, b, c) + K[t] + X[t]; 226 h += d; 227 d += Sum0(e) + Maj(e, f, g); 228 ++t; 229 230 // t = 8 * i + 5 231 c += Sum1(h) + Ch(h, a, b) + K[t] + X[t]; 232 g += c; 233 c += Sum0(d) + Maj(d, e, f); 234 ++t; 235 236 // t = 8 * i + 6 237 b += Sum1(g) + Ch(g, h, a) + K[t] + X[t]; 238 f += b; 239 b += Sum0(c) + Maj(c, d, e); 240 ++t; 241 242 // t = 8 * i + 7 243 a += Sum1(f) + Ch(f, g, h) + K[t] + X[t]; 244 e += a; 245 a += Sum0(b) + Maj(b, c, d); 246 ++t; 247 } 248 249 H1 += a; 250 H2 += b; 251 H3 += c; 252 H4 += d; 253 H5 += e; 254 H6 += f; 255 H7 += g; 256 H8 += h; 257 258 // 259 // reset the offset and clean out the word buffer. 260 // 261 xOff = 0; 262 X[] = 0; 263 } 264 265 private void _finish() nothrow @nogc 266 { 267 static if(bitLength == 256) { 268 ulong bitlen = byteCount1 << 3; 269 270 // Word = uint 271 Word lowBitLength = cast(uint)(bitlen); 272 Word hiBitLength = cast(uint)(bitlen >>> 32); 273 274 } else { 275 adjustByteCounts(); 276 277 278 // Word = ulong 279 Word lowBitLength = byteCount1 << 3; 280 Word hiBitLength = byteCount2; 281 } 282 283 // 284 // add the pad bytes. 285 // 286 put(128); 287 288 while (xBufOff != 0) 289 { 290 put(0); 291 } 292 293 //processLength(bitLength); 294 295 processLength(lowBitLength, hiBitLength); 296 297 processBlock(); 298 } 299 300 pure nothrow @nogc { 301 302 // SHA functions 303 304 static if(bitLength == 256) { 305 /* SHA-256 functions */ 306 uint Ch(uint x, uint y, uint z) 307 { 308 return (x & y) ^ ((~x) & z); 309 } 310 311 uint Maj(uint x, uint y, uint z) 312 { 313 return (x & y) ^ (x & z) ^ (y & z); 314 } 315 316 uint Sum0(uint x) 317 { 318 return ror(x,2) ^ ror(x,13) ^ ror(x,22); 319 } 320 321 uint Sum1(uint x) 322 { 323 return ror(x,6) ^ ror(x,11) ^ ror(x,25); 324 } 325 326 uint Theta0(uint x) 327 { 328 return ror(x,7) ^ ror(x,18) ^ (x >>> 3); 329 } 330 331 uint Theta1(uint x) 332 { 333 return ror(x,17) ^ ror(x,19) ^ (x >>> 10); 334 } 335 336 alias Theta0 Sigma0; 337 alias Theta1 Sigma1; 338 } else { 339 340 /* SHA-384 and SHA-512 functions (as for SHA-256 but for longs) */ 341 342 ulong Ch(ulong x, ulong y, ulong z) 343 { 344 return ((x & y) ^ ((~x) & z)); 345 } 346 347 ulong Maj(ulong x, ulong y, ulong z) 348 { 349 return ((x & y) ^ (x & z) ^ (y & z)); 350 } 351 352 ulong Sum0(ulong x) 353 { 354 return ((x << 36)|(x >>> 28)) ^ ((x << 30)|(x >>> 34)) ^ ((x << 25)|(x >>> 39)); 355 } 356 357 ulong Sum1(ulong x) 358 { 359 return ((x << 50)|(x >>> 14)) ^ ((x << 46)|(x >>> 18)) ^ ((x << 23)|(x >>> 41)); 360 } 361 362 ulong Sigma0(ulong x) 363 { 364 return ((x << 63)|(x >>> 1)) ^ ((x << 56)|(x >>> 8)) ^ (x >>> 7); 365 } 366 367 ulong Sigma1(ulong x) 368 { 369 return ((x << 45)|(x >>> 19)) ^ ((x << 3)|(x >>> 61)) ^ (x >>> 6); 370 } 371 } 372 373 } 374 375 static if(bitLength > 256) { 376 /// adjust the byte counts so that byteCount2 represents the 377 /// upper long (less 3 bits) word of the byte count. 378 379 void adjustByteCounts() nothrow @nogc 380 { 381 if (byteCount1 > 0x1fffffffffffffffL) 382 { 383 byteCount2 += (byteCount1 >>> 61); 384 byteCount1 &= 0x1fffffffffffffffL; 385 } 386 } 387 } 388 389 // constants 390 391 static if(bitLength == 256) { 392 /* SHA-256 Constants 393 * (represent the first 32 bits of the fractional parts of the 394 * cube roots of the first sixty-four prime numbers) 395 */ 396 enum uint[64] K = [ 397 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 398 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 399 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 400 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, 401 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 402 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 403 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, 404 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 405 ]; 406 407 public enum byteLength = 64; 408 public enum blockSize = 64; 409 410 uint[64] X; 411 } else { 412 413 /* SHA-384 and SHA-512 Constants 414 * (represent the first 64 bits of the fractional parts of the 415 * cube roots of the first sixty-four prime numbers) 416 */ 417 enum ulong[80] K = [ 418 0x428a2f98d728ae22L, 0x7137449123ef65cdL, 0xb5c0fbcfec4d3b2fL, 0xe9b5dba58189dbbcL, 419 0x3956c25bf348b538L, 0x59f111f1b605d019L, 0x923f82a4af194f9bL, 0xab1c5ed5da6d8118L, 420 0xd807aa98a3030242L, 0x12835b0145706fbeL, 0x243185be4ee4b28cL, 0x550c7dc3d5ffb4e2L, 421 0x72be5d74f27b896fL, 0x80deb1fe3b1696b1L, 0x9bdc06a725c71235L, 0xc19bf174cf692694L, 422 0xe49b69c19ef14ad2L, 0xefbe4786384f25e3L, 0x0fc19dc68b8cd5b5L, 0x240ca1cc77ac9c65L, 423 0x2de92c6f592b0275L, 0x4a7484aa6ea6e483L, 0x5cb0a9dcbd41fbd4L, 0x76f988da831153b5L, 424 0x983e5152ee66dfabL, 0xa831c66d2db43210L, 0xb00327c898fb213fL, 0xbf597fc7beef0ee4L, 425 0xc6e00bf33da88fc2L, 0xd5a79147930aa725L, 0x06ca6351e003826fL, 0x142929670a0e6e70L, 426 0x27b70a8546d22ffcL, 0x2e1b21385c26c926L, 0x4d2c6dfc5ac42aedL, 0x53380d139d95b3dfL, 427 0x650a73548baf63deL, 0x766a0abb3c77b2a8L, 0x81c2c92e47edaee6L, 0x92722c851482353bL, 428 0xa2bfe8a14cf10364L, 0xa81a664bbc423001L, 0xc24b8b70d0f89791L, 0xc76c51a30654be30L, 429 0xd192e819d6ef5218L, 0xd69906245565a910L, 0xf40e35855771202aL, 0x106aa07032bbd1b8L, 430 0x19a4c116b8d2d0c8L, 0x1e376c085141ab53L, 0x2748774cdf8eeb99L, 0x34b0bcb5e19b48a8L, 431 0x391c0cb3c5c95a63L, 0x4ed8aa4ae3418acbL, 0x5b9cca4f7763e373L, 0x682e6ff3d6b2b8a3L, 432 0x748f82ee5defb2fcL, 0x78a5636f43172f60L, 0x84c87814a1f0ab72L, 0x8cc702081a6439ecL, 433 0x90befffa23631e28L, 0xa4506cebde82bde9L, 0xbef9a3f7b2c67915L, 0xc67178f2e372532bL, 434 0xca273eceea26619cL, 0xd186b8c721c0c207L, 0xeada7dd6cde0eb1eL, 0xf57d4f7fee6ed178L, 435 0x06f067aa72176fbaL, 0x0a637dc5a2c898a6L, 0x113f9804bef90daeL, 0x1b710b35131c471bL, 436 0x28db77f523047d84L, 0x32caab7b40c72493L, 0x3c9ebe0a15c9bebcL, 0x431d67c49c100d4cL, 437 0x4cc5d4becb3e42b6L, 0x597f299cfc657e2aL, 0x5fcb6fab3ad6faecL, 0x6c44198c4a475817L 438 ]; 439 440 public enum byteLength = 128; 441 public enum blockSize = 128; 442 443 ulong byteCount2; 444 445 ulong[80] X; 446 } 447 448 Word 449 H1 = initH1, 450 H2 = initH2, 451 H3 = initH3, 452 H4 = initH4, 453 H5 = initH5, 454 H6 = initH6, 455 H7 = initH7, 456 H8 = initH8; 457 458 // define initial values for H1 - H8 459 static if(bitLength == 256) { 460 enum Word 461 initH1 = 0x6a09e667, 462 initH2 = 0xbb67ae85, 463 initH3 = 0x3c6ef372, 464 initH4 = 0xa54ff53a, 465 initH5 = 0x510e527f, 466 initH6 = 0x9b05688c, 467 initH7 = 0x1f83d9ab, 468 initH8 = 0x5be0cd19; 469 } else static if(bitLength == 384) { 470 enum Word 471 initH1 = 0xcbbb9d5dc1059ed8, 472 initH2 = 0x629a292a367cd507, 473 initH3 = 0x9159015a3070dd17, 474 initH4 = 0x152fecd8f70e5939, 475 initH5 = 0x67332667ffc00b31, 476 initH6 = 0x8eb44a8768581511, 477 initH7 = 0xdb0c2e0d64f98fa7, 478 initH8 = 0x47b5481dbefa4fa4; 479 } else static if(bitLength == 512) { 480 enum Word 481 initH1 = 0x6a09e667f3bcc908, 482 initH2 = 0xbb67ae8584caa73b, 483 initH3 = 0x3c6ef372fe94f82b, 484 initH4 = 0xa54ff53a5f1d36f1, 485 initH5 = 0x510e527fade682d1, 486 initH6 = 0x9b05688c2b3e6c1f, 487 initH7 = 0x1f83d9abfb41bd6b, 488 initH8 = 0x5be0cd19137e2179; 489 } else { 490 static assert(false, "invalid bitlength"); 491 } 492 493 ulong byteCount1; 494 495 ubyte[Word.sizeof] xBuf; 496 size_t xBufOff; 497 size_t xOff; 498 } 499 500 501 /// testing SHA256 algorithm 502 unittest { 503 504 immutable string[] plaintexts = [ 505 x"", 506 x"", // twice the same to test start() 507 x"616263", 508 "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu" 509 510 ]; 511 512 immutable string[] hexHashes = [ 513 x"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", 514 x"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", 515 x"ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad", 516 x"cf5b16a778af8380036ce59e7b0492370b249b11e8f07a51afac45037afee9d1" 517 ]; 518 519 testDigest(new SHA256Digest, plaintexts, hexHashes); 520 } 521 522 /// testing SHA384 algorithm 523 unittest { 524 525 immutable string[] plaintexts = [ 526 x"", 527 x"", // twice the same to test start() 528 x"616263", 529 "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu", 530 531 ]; 532 533 immutable string[] hexHashes = [ 534 x"38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b", 535 x"38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b", 536 x"cb00753f45a35e8bb5a03d699ac65007272c32ab0eded1631a8b605a43ff5bed8086072ba1e7cc2358baeca134c825a7", 537 x"09330c33f71147e83d192fc782cd1b4753111b173b3b05d22fa08086e3b0f712fcc7c71a557e2db966c3e9fa91746039" 538 ]; 539 540 testDigest(new SHA384Digest, plaintexts, hexHashes); 541 } 542 543 544 /// testing SHA512 algorithm 545 unittest { 546 547 immutable string[] plaintexts = [ 548 x"", 549 x"", // twice the same to test start() 550 x"616263", 551 "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu" 552 ]; 553 554 immutable string[] hexHashes = [ 555 x"cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e", 556 x"cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e", 557 x"ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f", 558 x"8e959b75dae313da8cf4f72814fc143f8f7779c6eb9f7fa17299aeadb6889018501d289e4900f7e4331b99dec4b5433ac7d329eeb6dd26545e96e55b874be909" 559 ]; 560 561 562 testDigest(new SHA512Digest, plaintexts, hexHashes); 563 }