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 }