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