1 /++
2 DAuth - Salted Hashed Password Library for D
3 Hash_DRBG Cryptographic Random Number Generator
4 +/
5 
6 module dauth.hashdrbg;
7 
8 import std.conv;
9 import std.exception;
10 import std.random;
11 import std.range;
12 import std.stdio;
13 import std.traits;
14 import std.typecons;
15 import std.typetuple;
16 
17 import dauth.core;
18 
19 // Only use dauth.sha if SHA-2 isn't in Phobos (ie, DMD 2.065 and below)
20 import phobos_sha = std.digest.sha;
21 static if(!is(phobos_sha.SHA512))
22 	import dauth.sha;
23 else
24 	import std.digest.sha;
25 
26 // TemplateArgsOf only exists in Phobos of DMD 2.066 and up
27 private struct dummy(T) {}
28 static if(!is(std.traits.TemplateArgsOf!(dummy!int)))
29 	private alias TemplateArgsOf = DAuth_TemplateArgsOf;
30 
31 version(Windows)
32 {
33 	import std.c.windows.windows;
34 	import core.runtime;
35 }
36 
37 /++
38 Check if T is a stream-like random number generator.
39 
40 Because std.stream is pending a full replacement, be aware that
41 stream-like random number generators currently use a temporary
42 design that may change once a new std.stream is available.
43 +/
44 enum isRandomStream(T) =
45 	is(typeof({
46 		static assert(T.isUniformRandomStream);
47 		T t;
48 		ubyte[] buf;
49 		t.read(buf);
50 		t.read(buf, No.PredictionResistance);
51 	}));
52 
53 static assert(isRandomStream!(SystemEntropyStream!()));
54 static assert(isRandomStream!(HashDRBGStream!()));
55 static assert(!isRandomStream!(SystemEntropy!uint));
56 static assert(!isRandomStream!(HashDRBG!uint));
57 static assert(!isRandomStream!uint);
58 static assert(!isRandomStream!File);
59 
60 /++
61 The underlying stream-like interface for SystemEntropy.
62 
63 On Windows, pathToRandom and pathToStrongRandom must be null because Windows
64 uses a system call, not a file path, to retreive system entropy.
65 
66 On Posix, pathToRandom must NOT be null. If pathToStrongRandom is null,
67 then pathToStrongRandom is assumed to be pathToRandom.
68 
69 Because std.stream is pending a full replacement, be aware that
70 stream-like random number generators currently use a temporary
71 design that may change once a new std.stream is available.
72 
73 Declaration:
74 -----------------------
75 struct SystemEntropyStream(string pathToRandom = defaultPathToRandom,
76 	string pathToStrongRandom = defaultPathToStrongRandom) {...}
77 -----------------------
78 +/
79 struct SystemEntropyStream(string pathToRandom = defaultPathToRandom,
80 	string pathToStrongRandom = defaultPathToStrongRandom)
81 {
82 	enum isUniformRandomStream = true; /// Mark this as a Rng Stream
83 
84 	version(Windows)
85 	{
86 		import std.c.windows.windows;
87 		import core.runtime;
88 		
89 		static assert(pathToRandom is null, "On Windows, SystemEntropyStream's pathToRandom must be null");
90 		static assert(pathToStrongRandom is null, "On Windows, SystemEntropyStream's pathToStrongRandom must be null");
91 		
92 		private static HMODULE _advapi32;
93 		private static extern(Windows) BOOL function(void*, uint) _RtlGenRandom;
94 	}
95 	else version(Posix)
96 	{
97 		import std.stdio : File, _IONBF;
98 
99 		static assert(pathToRandom !is null, "On Posix, SystemEntropyStream's pathToRandom must NOT be null");
100 
101 		private static File devRandom;
102 		private static File devStrongRandom;
103 	}
104 	else
105 		static assert(0);
106 	 
107 	/++
108 	Fills the buffer with entropy from the system-specific entropy generator.
109 	Automatically opens SystemEntropyStream if it's closed.
110 	
111 	If predictionResistance is Yes.PredictionResistance, then this will read
112 	from a secondary source (if available), such as /dev/random instead of
113 	/dev/urandom, which may block for a noticable amount of time to ensure
114 	a minimum amount of estimated entropy is available. If no secondary
115 	source is available, then predictionResistance is ignored.
116 
117 	Optional_Params:
118 	predictionResistance - Default value is 'No.PredictionResistance'
119 	+/
120 	static void read(ubyte[] buf, Flag!"PredictionResistance" predictionResistance = No.PredictionResistance)
121 	{
122 		open();
123 		
124 		version(Windows)
125 		{
126 			enforce(buf.length < uint.max, "Cannot read more than uint.max bytes from RtlGenRandom");
127 			_RtlGenRandom(buf.ptr, cast(uint)buf.length);
128 		}
129 		else version(Posix)
130 		{
131 			if(predictionResistance == Yes.PredictionResistance && pathToStrongRandom)
132 				devStrongRandom.rawRead(buf);
133 			else
134 				devRandom.rawRead(buf);
135 		}
136 		else
137 			static assert(0);
138 	}
139 
140 	/++
141 	Establishes a handle/connection to the system-specific entropy generator.
142 	Does nothing if already open.
143 	+/
144 	static void open()
145 	{
146 		if(isOpen)
147 			return;
148 		
149 		version(Windows)
150 		{
151 			// Reference: http://blogs.msdn.com/b/michael_howard/archive/2005/01/14/353379.aspx
152 			_advapi32 = Runtime.loadLibrary("ADVAPI32.DLL");
153 			_RtlGenRandom = cast(typeof(_RtlGenRandom))_advapi32.GetProcAddress("SystemFunction036");
154 			enforce(_RtlGenRandom);
155 		}
156 		else version(Posix)
157 		{
158 			static void openFile(ref File f, string path)
159 			{
160 				if(!f.isOpen)
161 				{
162 					f = File(path);
163 					
164 					// Disable buffering for security, and to avoid consuming
165 					// more system entropy than necessary.
166 					f.setvbuf(null, _IONBF);
167 				}
168 			}
169 			
170 			openFile(devRandom, pathToRandom);
171 			if(pathToStrongRandom)
172 				openFile(devStrongRandom, pathToStrongRandom);
173 		}
174 		else
175 			static assert(0);
176 	}
177 	
178 	
179 	///	Manually release the handle/connection to the system-specific entropy generator.
180 	static void close()
181 	{
182 		version(Windows)
183 		{
184 			if(_advapi32)
185 			{
186 				Runtime.unloadLibrary(_advapi32);
187 				_advapi32 = null;
188 				_RtlGenRandom = null;
189 			}
190 		}
191 		else version(Posix)
192 		{
193 			if(devRandom.isOpen)
194 				devRandom.close();
195 			if(devStrongRandom.isOpen)
196 				devStrongRandom.close();
197 		}
198 		else
199 			static assert(0);
200 	}
201 
202 	/++
203 	Check whether SystemEntropyStream is currently connected to with the
204 	system-specific entropy generator.
205 	+/
206 	static @property bool isOpen()
207 	{
208 		version(Windows)
209 			return _advapi32 && _RtlGenRandom;
210 		else version(Posix)
211 			return devRandom.isOpen && (!pathToStrongRandom || devStrongRandom.isOpen);
212 		else
213 			static assert(0);
214 	}
215 	
216 	/// Automatically close upon module destruction.
217 	static ~this()
218 	{
219 		close();
220 	}
221 }
222 
223 /++
224 Reads random entropy from a system-specific cryptographic random number
225 generator. On Windows, this loads ADVAPI32.DLL and uses RtlGenRandom.
226 On Posix, this reads from a file (by default, "/dev/urandom" normally and
227 "/dev/random" when Yes.PredictionResistance is requested). The speed
228 and cryptographic security of this is dependent on your operating system.
229 
230 In most cases, this should not be used directly. It quickly consumes
231 available system entropy, which can decrease the cryptographic RNG
232 effectiveness across the whole computer and, on Linux, can cause reads from
233 "/dev/random" to stall for noticably long periods of time. Instead,
234 this is best used for seeding cryptographic psuedo-random number generators,
235 such as HashDRBG.
236 
237 Optionally, you can use open() and close() to control the lifetime of
238 SystemEntropyStream's system handles (ie, loading/uloading ADVAPI32.DLL and
239 opening/closing pathToRandom). But this is not normally necessary since
240 SystemEntropyStream automatically opens them upon reading and closes upon
241 module destruction.
242 
243 On Windows, pathToRandom and pathToStrongRandom must be null because Windows
244 uses a system call, not a file path, to retreive system entropy.
245 
246 On Posix, pathToRandom must NOT be null. If pathToStrongRandom is null,
247 then pathToStrongRandom is assumed to be pathToRandom.
248 
249 This is a convenience alias for WrappedStreamRNG!(SystemEntropyStream, Elem).
250 
251 Note that to conform to the expected InputRange interface, this must keep a
252 copy of the last generated value in memory. For security purposes, it may
253 occasionally be appropriate to make an extra popFront() call before and/or
254 after retreiving entropy values. This may decrease the chance of using
255 a compromized entropy value in the event of a memory-sniffing attacker.
256 
257 Optional_Params:
258 pathToRandom - Default value is 'defaultPathToRandom'
259 
260 pathToStrongRandom - Default value is 'defaultPathToStrongRandom'
261 +/
262 alias SystemEntropy(Elem, string pathToRandom = defaultPathToRandom,
263 	string pathToStrongRandom = defaultPathToStrongRandom) =
264 	WrappedStreamRNG!(SystemEntropyStream!(pathToRandom, pathToStrongRandom), Elem);
265 
266 version (StdDdoc)
267 {
268 	/++
269 	The path to the default OS-provided cryptographic entropy generator.
270 	This should not be a blocking generator.
271 	
272 	On Posix, this is "/dev/urandom". On Windows is empty string, because
273 	Windows uses a system call, not a file path, to retreive system entropy.
274 	+/
275 	enum string defaultPathToRandom = null;
276 
277 	/++
278 	The path to an OS-provided cryptographic entropy generator to be used
279 	when Yes.PredictionResistance is requested. This should be at least as
280 	strong as defaultPathToRandom. But unlike defaultPathToRandom, this may
281 	be a generator that blocks when system entropy is low.
282 	
283 	On Posix, this is "/dev/random". On Windows is empty string, because
284 	Windows uses a system call, not a file path, to retreive system entropy.
285 	+/
286 	enum string defaultPathToStrongRandom = null;
287 }
288 else version (Windows)
289 {
290 	enum string defaultPathToRandom = null;
291 	enum string defaultPathToStrongRandom = null;
292 }
293 else version (Posix)
294 {
295 	enum defaultPathToRandom = "/dev/urandom";
296 	enum defaultPathToStrongRandom = "/dev/random";
297 }
298 else
299 	static assert(0);
300 
301 static assert(isUniformRNG!(SystemEntropy!(ubyte[1]), ubyte[1]));
302 static assert(isUniformRNG!(SystemEntropy!(ubyte[5]), ubyte[5]));
303 static assert(isUniformRNG!(SystemEntropy!ubyte,      ubyte   ));
304 static assert(isUniformRNG!(SystemEntropy!ushort,     ushort  ));
305 static assert(isUniformRNG!(SystemEntropy!uint,       uint    ));
306 static assert(isUniformRNG!(SystemEntropy!ulong,      ulong   ));
307 
308 /++
309 The underlying stream-like interface for SystemEntropy.
310 
311 TSHA: Any SHA-1 or SHA-2 digest type. Default is SHA512.
312 
313 custom: The Hash_DRBG algorithm's personalization string. You
314 can optionally set this to any specific value of your own choosing for
315 improved security.
316 
317 EntropyStream: The source of entropy from which to draw.
318 The default is SystemEntropyStream!(), but can be overridden. If you provide
319 your own, then it's your responsibility to ensure your entropy source is
320 non-deterministic.
321 
322 Because std.stream is pending a full replacement, be aware that
323 stream-like random number generators currently use a temporary
324 design that may change once a new std.stream is available.
325 
326 Declaration:
327 -----------------------
328 struct HashDRBGStream(TSHA = SHA512, string custom = "D Crypto RNG", EntropyStream = SystemEntropyStream!())
329 	if(isInstanceOf!(SHA, TSHA))
330 	{...}
331 -----------------------
332 +/
333 struct HashDRBGStream(TSHA = SHA512, string custom = "D Crypto RNG", EntropyStream = SystemEntropyStream!())
334 	if(isInstanceOf!(SHA, TSHA))
335 {
336 	enum isUniformRandomStream = true; /// Mark this as a Rng Stream
337 
338 	// In bits. This is the same as the SHA's digestSize
339 	private enum outputSizeBits = TemplateArgsOf!(TSHA)[1];
340 
341 	static if (outputSizeBits < 384)
342 		private enum seedSizeBytes = 440/8; // In bytes
343 	else
344 		private enum seedSizeBytes = 888/8; // In bytes
345 	
346 	// Securty strength 256 bits. Less could provide insufficitent security,
347 	// but more would consume more of the system's entropy for no benefit.
348 	// See NIST's [SP800-90A] and [SP800-57] for details on this value.
349 	private enum entropySizeBytes = 256/8;
350 	
351 	// This must be at least entropySizeBytes/2
352 	private enum nonceSizeBytes = entropySizeBytes/2;
353 	
354 	// value[1..$] is Hash_DRBG's secret working state value V
355 	// value[0] is a scratchpad to avoid unnecessary copying/concating of V
356 	private static ubyte[seedSizeBytes+1] value;
357 
358 	private static ubyte[seedSizeBytes] constant; // Hash_DRBG's secret working state value C
359 	private static uint numGenerated; // Number of values generated with the current seed
360 
361 	// Maximum number of values generated before automatically reseeding with fresh entropy.
362 	// The algorithm's spec permits this to be anything less than or equal to 2^48,
363 	// but we should take care not to overflow our actual countner.
364 	private enum int maxGenerated = 0x0FFF_FFFF;
365 	
366 	/++
367 	If your security needs are high enough that you'd rather risk blocking
368 	for an arbitrarily-long period of time while sufficient system entropy
369 	builds, than risk generating values from potentially insufficient entropy
370 	(ex, if you'd rather reseed from Linux's /dev/random than /dev/urandom),
371 	then set this to Yes.PredictionResistance. The next time a value is
372 	generated, the internal state will first be replenished with additional
373 	entropy, potentially from a blocking source.
374 	
375 	After the next value is generated, this will automatically reset back
376 	to No.PredictionResistance to avoid needlessly consuming the system's
377 	available entropy. Note that forcefully setting this to Yes.PredictionResistance
378 	before each and every value generated is NOT cryptographically necessary,
379 	can quickly starve the system of entropy, and should not be done.
380 	
381 	Default is No.PredictionResistance.
382 	
383 	This setting is for changing read()'s default bahavior. Individual calls
384 	to read() can manually override this per call.
385 	+/
386 	Flag!"PredictionResistance" predictionResistance = No.PredictionResistance;
387 
388 	/++
389 	Further improve security by setting Hash_DRBG's optional "additional input"
390 	for each call to read(). This can be set to a new value before each read()
391 	call for maximum effect.
392 	
393 	This setting is for changing read()'s default bahavior. Individual calls
394 	to read() can manually override this per call.
395 	+/
396 	ubyte[] extraInput = null;
397 
398 	private static bool inited = false;
399 	private void init()
400 	{
401 		if(inited)
402 			return;
403 		
404 		// seedMaterial = entropy ~ nonce ~ custom;
405 		ubyte[entropySizeBytes + nonceSizeBytes + custom.length] seedMaterial = void;
406 		EntropyStream.read( seedMaterial[0 .. $-custom.length], predictionResistance );
407 		seedMaterial[$-custom.length .. $] = cast(ubyte[])custom;
408 		
409 		// Generate seed for V
410 		hashDerivation(seedMaterial, null, value[1..$]);
411 		
412 		// Generate constant
413 		value[0] = 0x00;
414 		hashDerivation(value, null, constant);
415 		
416 		numGenerated = 0;
417 		inited = true;
418 	}
419 	
420 	private void reseed(ubyte[] extraInput=null)
421 	{
422 		// seedMaterial = 0x01 ~ V ~ entropy;
423 		ubyte[value.sizeof + entropySizeBytes] seedMaterial = void;
424 		seedMaterial[0] = 0x01;
425 		seedMaterial[1 .. $-entropySizeBytes] = value[1..$];
426 		EntropyStream.read( seedMaterial[$-entropySizeBytes .. $], predictionResistance );
427 		
428 		// Generate seed for V
429 		hashDerivation(seedMaterial, extraInput, value[1..$]);
430 		
431 		// Generate constant
432 		value[0] = 0x00;
433 		hashDerivation(value, null, constant);
434 
435 		numGenerated = 0;
436 	}
437 	
438 	/++
439 	Fills the buffer with random values using the Hash_DRBG algorithm.
440 	
441 	overridePredictionResistance:
442 	Override this.predictionResistance setting for this call only.
443 	
444 	overrideExtraInput:
445 	Override this.extraInput setting for this call only.
446 	+/
447 	void read(ubyte[] buf)
448 	{
449 		read(buf, predictionResistance, extraInput);
450 	}
451 
452 	///ditto
453 	void read(ubyte[] buf, ubyte[] overrideExtraInput)
454 	{
455 		read(buf, predictionResistance, overrideExtraInput);
456 	}
457 
458 	///ditto
459 	void read(ubyte[] buf, Flag!"PredictionResistance" overridePredictionResistance)
460 	{
461 		read(buf, overridePredictionResistance, extraInput);
462 	}
463 
464 	///ditto
465 	void read(ubyte[] buf,
466 		Flag!"PredictionResistance" overridePredictionResistance,
467 		ubyte[] overrideExtraInput)
468 	{
469 		init();
470 
471 		if(numGenerated >= maxGenerated || overridePredictionResistance == Yes.PredictionResistance)
472 			reseed(overrideExtraInput);
473 		
474 		predictionResistance = No.PredictionResistance;
475 		
476 		if(overrideExtraInput)
477 		{
478 			value[0] = 0x02;
479 
480 			TSHA sha;
481 			sha.put(value);
482 			sha.put(overrideExtraInput);
483 			ubyte[seedSizeBytes] tempHash;
484 			tempHash[0..outputSizeBits/8] = sha.finish();
485 			addHash!seedSizeBytes(value[1..$], tempHash, value[1..$]);
486 		}
487 		
488 		ubyte[seedSizeBytes] workingData = value[1..$];
489 		if(buf.length > 0)
490 		while(true)
491 		{
492 			// Fill the front of buf with up to seedSizeBytes of random data
493 			ubyte[outputSizeBits/8] currHash = digest!TSHA(workingData);
494 			auto length = buf.length < currHash.length? buf.length : currHash.length;
495 			buf[0..length] = currHash[0..length];
496 			buf = buf[length..$];
497 			
498 			// Buffer filled?
499 			if(buf.length == 0)
500 				break;
501 			
502 			incrementHash(workingData);
503 		}
504 		
505 		// Update V
506 		value[0] = 0x03;
507 		ubyte[seedSizeBytes] hashSum = void;
508 		hashSum[0 .. outputSizeBits/8] = digest!TSHA(value);
509 		hashSum[outputSizeBits/8 .. $] = 0;
510 		addHash!seedSizeBytes(hashSum, value[1..$], hashSum);
511 		addHash!seedSizeBytes(hashSum, constant, hashSum);
512 		addHash!seedSizeBytes(hashSum, numGenerated+1, value[1..$]);
513 		
514 		numGenerated++;
515 	}
516 	
517 	private static void hashDerivation(ubyte[] input, ubyte[] extraInput, ubyte[] buf)
518 	{
519 		ubyte counter = 1;
520 		ulong originalBufLength = buf.length;
521 		while(buf.length)
522 		{
523 			// Generate hashed data
524 			TSHA sha;
525 			sha.put(counter);
526 			sha.put(*(cast(ubyte[8]*) &originalBufLength));
527 			sha.put(input);
528 			if(extraInput)
529 				sha.put(extraInput);
530 			auto currHash = sha.finish();
531 			
532 			// Fill the front of buf with the hashed data
533 			auto length = buf.length < currHash.length? buf.length : currHash.length;
534 			buf[0..length] = currHash[0..length];
535 			buf = buf[length..$];
536 			
537 			counter++;
538 		}
539 	}
540 	
541 	private static void incrementHash(int numBytes)(ref ubyte[numBytes] arr)
542 	{
543 		// Endianness (small, big or even weird mixes) doesn't matter since hashes
544 		// don't have a particularly meaningful least/most significant bit. As
545 		// long as we're consistent across the RNG instance's lifetime, we're good.
546 
547 		foreach(ref b; arr)
548 		{
549 			b++;
550 			if(b != 0)
551 				break;
552 		}
553 	}
554 
555 	private static void addHash(int numBytes)(ubyte[numBytes] arr1,
556 		ubyte[numBytes] arr2, ubyte[] result)
557 	{
558 		// As with incrementHash, endianness doesn't matter here.
559 		
560 		enforce(arr1.length == arr2.length);
561 		enforce(arr1.length == result.length);
562 		uint carry = 0;
563 		foreach(i; 0..arr1.length)
564 		{
565 			auto sum = arr1[i] + arr2[i] + carry;
566 			result[i] = sum & 0xFF;
567 			carry = sum >> 8;
568 		}
569 	}
570 
571 	private static void addHash(int numBytes)(ubyte[numBytes] arr, uint value,
572 		ubyte[] result)
573 	{
574 		// As with incrementHash, endianness doesn't matter here.
575 		
576 		enforce(arr.length == result.length);
577 		uint carry = value;
578 		foreach(i; 0..arr.length)
579 		{
580 			uint sum = arr[i] + carry;
581 			result[i] = sum & 0xFF;
582 			carry = sum >> 8;
583 		}
584 	}
585 }
586 
587 /++
588 Cryptographic random number generator Hash_DRBG, as defined in
589 NIST's $(LINK2 http://csrc.nist.gov/publications/nistpubs/800-90A/SP800-90A.pdf, SP800-90A).
590 
591 The Hash_DRBG algorithm ("Hash - Deterministic Random Bit Generator") uses
592 SHA-2 (or optionally SHA-1) to stretch the useful life of a non-deterministic
593 $(LINK2 https://en.wikipedia.org/wiki/Entropy_%28information_theory%29, entropy)
594 source for security and cryptographic purposes, such as
595 $(LINK2 http://en.wikipedia.org/wiki/One-time_password, single-use tokens),
596 $(LINK2 http://en.wikipedia.org/wiki/Cryptographic_nonce, nonces)
597 or $(LINK2 http://en.wikipedia.org/wiki/Salt_%28cryptography%29, password salts).
598 
599 While technically deterministic, Hash_DRBG is not intended for deterministic,
600 repeatable uses of psuedo-random number generation (such as generating randomized
601 interactive worlds with minimal-storage requirements - for which something like
602 Mt19937 would be better suited). For the sake of security, the algorithm is
603 intentionally defined to not support direct seeding and to automatically
604 accumulate (but never discard) entropy from not only a pre-determined source
605 of unpredictable entropy, but also from actual usage patterns. In that
606 spirit, this implementation is non-seedable, non-ForwardRange (only InputRange),
607 and all instances share a static state (albeit per thread, per EntropyStream type).
608 
609 Mainly through the underlying HashDRBGStream (accessed via the $(D stream) member),
610 this supports the optional features of the Hash_DRBG algorithm. Specifically,
611 prediction resistance via forced reseeding, providing additional input for
612 each value generated, and custom personalization strings.
613 
614 TSHA: Any SHA-1 or SHA-2 digest type. Default is SHA512.
615 
616 custom: The Hash_DRBG algorithm's personalization string. You
617 can optionally set this to any specific value of your own choosing for
618 extra security.
619 
620 EntropyStream: The source of entropy from which to draw.
621 The default is SystemEntropyStream!(), but can be overridden. If you provide
622 your own, then it's your responsibility to ensure your entropy source is
623 non-deterministic.
624 
625 This is a convenience alias for WrappedStreamRNG!(HashDRBGStream, Elem).
626 
627 Note that to conform to the expected InputRange interface, this must keep a
628 copy of the last generated value in memory. For security purposes, it may
629 occasionally be appropriate to make an extra popFront() call before and/or
630 after retrieving entropy values. This may decrease the chance of using
631 a compromised entropy value in the event of a memory-sniffing attacker.
632 
633 Declaration:
634 -----------------------
635 struct HashDRBGStream(TSHA = SHA512, string custom = "D Crypto RNG", EntropyStream = SystemEntropyStream!())
636 	if(isInstanceOf!(SHA, TSHA))
637 	{...}
638 -----------------------
639 +/
640 template HashDRBG(Elem, TSHA = SHA512, string custom = "D Crypto RNG", EntropyStream = SystemEntropyStream!())
641 	if(isInstanceOf!(SHA, TSHA))
642 {
643 	alias HashDRBG = WrappedStreamRNG!(HashDRBGStream!(TSHA, custom, EntropyStream), Elem);
644 }
645 
646 static assert(isUniformRNG!(HashDRBG!(ubyte[1]), ubyte[1]));
647 static assert(isUniformRNG!(HashDRBG!(ubyte[5]), ubyte[5]));
648 static assert(isUniformRNG!(HashDRBG!ubyte,      ubyte   ));
649 static assert(isUniformRNG!(HashDRBG!ushort,     ushort  ));
650 static assert(isUniformRNG!(HashDRBG!uint,       uint    ));
651 static assert(isUniformRNG!(HashDRBG!ulong,      ulong   ));
652 static assert(isUniformRNG!(HashDRBG!(uint), uint));
653 static assert(isUniformRNG!(HashDRBG!(uint, SHA256), uint));
654 static assert(isUniformRNG!(HashDRBG!(uint, SHA256, "custom"), uint));
655 static assert(isUniformRNG!(HashDRBG!(uint, SHA256, "custom", SystemEntropyStream!()), uint));
656 
657 version(DAuth_Unittest)
658 unittest
659 {
660 	unitlog("Testing HashDRBGStream.incrementHash");
661 	
662 	HashDRBGStream!SHA1 rand;
663 	ubyte[5] val      = [0xFF, 0xFF, 0b0000_1011, 0x00, 0x00];
664 	ubyte[5] expected = [0x00, 0x00, 0b0000_1100, 0x00, 0x00];
665 	
666 	assert(val != expected);
667 	rand.incrementHash(val);
668 	assert(val == expected);
669 }
670 
671 version(DAuth_Unittest)
672 unittest
673 {
674 	unitlog("Testing HashDRBGStream.addHash(arr,arr,arr)");
675 	
676 	HashDRBGStream!SHA1 rand;
677 	ubyte[5] val1     = [0xCC, 0x05, 0xFE, 0x01, 0x00];
678 	ubyte[5] val2     = [0x33, 0x02, 0x9E, 0x00, 0x00];
679 	ubyte[5] expected = [0xFF, 0x07, 0x9C, 0x02, 0x00];
680 	ubyte[5] result;
681 	
682 	assert(result != expected);
683 	rand.addHash(val1, val2, result);
684 	assert(result == expected);
685 }
686 
687 version(DAuth_Unittest)
688 unittest
689 {
690 	unitlog("Testing HashDRBGStream.addHash(arr,int,arr)");
691 	
692 	HashDRBGStream!SHA1 rand;
693 	ubyte[5] val1     = [0xCC, 0x05, 0xFE, 0x01, 0x00];
694 	uint val2         = 0x009E_0233;
695 	ubyte[5] expected = [0xFF, 0x07, 0x9C, 0x02, 0x00];
696 	ubyte[5] result;
697 	
698 	assert(result != expected);
699 	rand.addHash(val1, val2, result);
700 	assert(result == expected);
701 }
702 
703 /++
704 Takes a RandomStream (ex: SystemEntropyStream or HashDRBGStream) and
705 wraps it into a UniformRNG InputRange.
706 
707 Note that to conform to the expected InputRange interface, this must keep a
708 copy of the last generated value in memory. If using this for security-related
709 purposes, it may occasionally be appropriate to make an extra popFront()
710 call before and/or after retreiving entropy values. This may decrease the
711 chance of using a compromized entropy value in the event of a
712 memory-sniffing attacker.
713 
714 Declaration:
715 -----------------------
716 struct WrappedStreamRNG(RandomStream, StaticUByteArr)
717 	if(isRandomStream!RandomStream && isStaticArray!StaticUByteArr && is(ElementType!StaticUByteArr==ubyte))
718 	{...}
719 -----------------------
720 +/
721 struct WrappedStreamRNG(RandomStream, StaticUByteArr)
722 	if(isRandomStream!RandomStream && isStaticArray!StaticUByteArr && is(ElementType!StaticUByteArr==ubyte))
723 {
724 	enum isUniformRandom = true; /// Mark this as a Rng
725 	
726 	private StaticUByteArr _front;
727 	private bool inited = false;
728 	
729 	/// Access to underlying RandomStream so RNG-specific functionality can be accessed.
730 	RandomStream stream;
731 	
732 	/// Implements an InputRange
733 	@property StaticUByteArr front()
734 	{
735 		if(!inited)
736 		{
737 			popFront();
738 			inited = true;
739 		}
740 		
741 		return _front;
742 	}
743 	
744 	///ditto
745 	void popFront()
746 	{
747 		stream.read(_front);
748 	}
749 	
750 	/// Infinite range. Never empty.
751 	enum empty = false;
752 	
753 	/// Smallest generated value.
754 	enum min = StaticUByteArr.init;
755 	
756 	/// Largest generated value.
757 	static @property StaticUByteArr max()
758 	{
759 		StaticUByteArr val = void;
760 		val[] = 0xFF;
761 		return val;
762 	}
763 }
764 
765 ///ditto
766 struct WrappedStreamRNG(RandomStream, UIntType)
767 	if(isRandomStream!RandomStream && isUnsigned!UIntType)
768 {
769 	private WrappedStreamRNG!(RandomStream, ubyte[UIntType.sizeof]) bytesImpl;
770 	
771 	enum isUniformRandom = true; /// Mark this as a Rng
772 	
773 	private UIntType _front;
774 	private bool inited = false;
775 	
776 	/// Implements an InputRange
777 	@property UIntType front()
778 	{
779 		auto val = bytesImpl.front;
780 		return *(cast(UIntType*) &val);
781 	}
782 	
783 	///ditto
784 	void popFront()
785 	{
786 		bytesImpl.popFront();
787 	}
788 	
789 	enum empty = false; /// Infinite range. Never empty.
790 	enum min = UIntType.min; /// Smallest generated value.
791 	enum max = UIntType.max; /// Largest generated value.
792 }
793 
794 version(DAuth_Unittest)
795 unittest
796 {
797 	alias RandStreamTypes = TypeTuple!(
798 		SystemEntropyStream!(),
799 		HashDRBGStream!SHA1,
800 		HashDRBGStream!SHA224,
801 		HashDRBGStream!SHA256,
802 		HashDRBGStream!SHA384,
803 		HashDRBGStream!SHA512,
804 		HashDRBGStream!SHA512_224,
805 		HashDRBGStream!SHA512_256,
806 		HashDRBGStream!(SHA512, "other custom str"),
807 	);
808 	
809 	unitlog("Testing SystemEntropyStream/HashDRBGStream");
810 	foreach(RandStream; RandStreamTypes)
811 	{
812 		//unitlog("Testing RandStream: "~RandStream.stringof);
813 		
814 		RandStream rand;
815 		ubyte[] values1;
816 		ubyte[] values2;
817 		values1.length = 10;
818 		values2.length = 10;
819 		
820 		rand.read(values1);
821 		assert(values1 != typeof(values1).init);
822 		assert(values1[0..4] != values1[4..8]);
823 		rand.read(values2);
824 		assert(values1 != values2);
825 		
826 		auto randCopy = rand;
827 		rand.read(values1);
828 		randCopy.read(values2);
829 		assert(values1 != values2);
830 		
831 		static if(!is(RandStream == SystemEntropyStream!()))
832 		{
833 			values2[] = ubyte.init;
834 
835 			values1[] = ubyte.init;
836 			rand.read(values1, Yes.PredictionResistance);
837 			assert(values1 != values2);
838 			
839 			values1[] = ubyte.init;
840 			rand.read(values1, cast(ubyte[])"additional input");
841 			assert(values1 != values2);
842 			
843 			values1[] = ubyte.init;
844 			rand.read(values1, Yes.PredictionResistance, cast(ubyte[])"additional input");
845 			assert(values1 != values2);
846 		}
847 	}
848 }
849 
850 version(DAuth_Unittest)
851 unittest
852 {
853 	foreach(Rand; TypeTuple!(SystemEntropy, HashDRBG))
854 	{
855 		unitlog("Testing Rand's min/max: "~Rand.stringof);
856 
857 		assert(Rand!(ubyte[1]).min == [0x00]);
858 		assert(Rand!(ubyte[1]).max == [0xFF]);
859 		assert(Rand!(ubyte[5]).min == [0x00,0x00,0x00,0x00,0x00]);
860 		assert(Rand!(ubyte[5]).max == [0xFF,0xFF,0xFF,0xFF,0xFF]);
861 		assert(Rand!(ubyte   ).min == ubyte .min);
862 		assert(Rand!(ubyte   ).max == ubyte .max);
863 		assert(Rand!(ushort  ).min == ushort.min);
864 		assert(Rand!(ushort  ).max == ushort.max);
865 		assert(Rand!(uint    ).min == uint  .min);
866 		assert(Rand!(uint    ).max == uint  .max);
867 		assert(Rand!(ulong   ).min == ulong .min);
868 		assert(Rand!(ulong   ).max == ulong .max);
869 	}
870 }
871 
872 version(DAuth_Unittest)
873 unittest
874 {
875 	// Don't test ubyte or ushort versions here because legitimate repeated
876 	// values are too likely and would trigger a failure and unfounded worry.
877 	
878 	alias RandTypes = TypeTuple!(
879 		SystemEntropy!ulong,
880 		SystemEntropy!uint,
881 		SystemEntropy!(ubyte[5]),
882 		SystemEntropy!(ubyte[1024]),
883 		HashDRBG!(ulong, SHA1),
884 		HashDRBG!(ulong, SHA224),
885 		HashDRBG!(ulong, SHA256),
886 		HashDRBG!(ulong, SHA384),
887 		HashDRBG!(ulong, SHA512),
888 		HashDRBG!(ulong, SHA512_224),
889 		HashDRBG!(ulong, SHA512_256),
890 		HashDRBG!(ulong, SHA512, "other custom str"),
891 		HashDRBG!(uint,    SHA512),
892 		HashDRBG!(ubyte[5], SHA512),
893 		HashDRBG!(ubyte[1024], SHA512),
894 	);
895 	
896 	unitlog("Testing SystemEntropy/HashDRBG");
897 	foreach(Rand; RandTypes)
898 	{
899 		//unitlog("Testing Rand: "~Rand.stringof);
900 
901 		Rand rand;
902 		assert(!rand.empty);
903 		
904 		assert(rand.front == rand.front);
905 		auto val = rand.front;
906 		assert(val != ElementType!(Rand).init);
907 
908 		rand.popFront();
909 		assert(val != rand.front);
910 
911 		auto randCopy = rand;
912 		assert(rand.front == randCopy.front);
913 		rand.popFront();
914 		randCopy.popFront();
915 		assert(rand.front != randCopy.front);
916 	}
917 }