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