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 }