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