1 /++
2 DAuth - Authentication Utility for D
3 Hash_DRBG Cryptographic Random Number Generator
4 
5 Main module: $(LINK2 index.html,dauth)$(BR)
6 +/
7 
8 module dauth.hashdrbg;
9 
10 import std.conv;
11 import std.exception;
12 import std.random;
13 import std.range;
14 import std.stdio;
15 import std.traits;
16 import std.typecons;
17 import std.typetuple;
18 
19 import dauth.core;
20 import dauth.sha;
21 
22 version(Windows)
23 {
24 	import std.c.windows.windows;
25 	import core.runtime;
26 }
27 
28 ///
29 enum isRandomStream(T) =
30 	is(typeof({
31 		static assert(T.isUniformRandomStream);
32 		T t;
33 		ubyte[] buf;
34 		t.read(buf);
35 	}));
36 
37 /// Check whether T is either isUniformRNG or isRandomStream.
38 enum isSomeStream(T) = isUniformRNG!T || isRandomStream!T;
39 
40 static assert(isRandomStream!(SystemEntropyStream));
41 static assert(isRandomStream!(HashDRBGStream!()));
42 static assert(!isRandomStream!(SystemEntropy!uint));
43 static assert(!isRandomStream!(HashDRBG!uint));
44 static assert(!isRandomStream!uint);
45 static assert(!isRandomStream!File);
46 
47 /++
48 Reads any desired amount of random entropy from a system-specific cryptographic
49 random number generator. On Windows, this loads ADVAPI32.DLL and uses
50 RtlGenRandom. On Posix, this uses '/dev/random'.
51 
52 Optionally, you can use open() and close() to control the lifetime of
53 SystemEntropyStream's system handles (ie, loading/uloading ADVAPI32.DLL and
54 opening/closing '/dev/random'). But this is not normally necessary since
55 SystemEntropyStream automatically opens them upon reading and closes upon
56 module destruction.
57 
58 The speed and cryptographic security of this is dependent on your operating
59 system. This may be perfectly suitable for many cryptographic-grade random
60 number generation needs, but it's primary inteded for seeding/reseeding
61 cryptographic psuedo-random number generators, such as Hash_DRBG or HMAC_DRBG,
62 which are likely to be faster and no less secure than using an entropy source
63 directly.
64 +/
65 struct SystemEntropyStream
66 {
67 	enum isUniformRandomStream = true; /// Mark this as a Rng Stream
68 
69 	version(Windows)
70 	{
71 		private static HMODULE _advapi32;
72 		private static extern(Windows) BOOL function(void*, uint) _RtlGenRandom;
73 	}
74 	else version(Posix)
75 		private static File devRandom;
76 	else
77 		static assert(false);
78 	 
79 	/// Fills the buffer with entropy from the system-specific entropy generator.
80 	/// Automatically opens SystemEntropyStream if it's closed.
81 	static void read(ubyte[] buf)
82 	{
83 		open();
84 		
85 		version(Windows)
86 		{
87 			enforce(buf.length < uint.max, "Cannot read more than uint.max bytes from RtlGenRandom");
88 			_RtlGenRandom(buf.ptr, cast(uint)buf.length);
89 		}
90 		else version(Posix)
91 			devRandom.rawRead(buf);
92 		else
93 			static assert(false);
94 	}
95 
96 	/// Establishes a handle/connection to the system-specific entropy generator.
97 	/// Does nothing if already open.
98 	static void open()
99 	{
100 		if(isOpen)
101 			return;
102 		
103 		version(Windows)
104 		{
105 			// Reference: http://blogs.msdn.com/b/michael_howard/archive/2005/01/14/353379.aspx
106 			_advapi32 = Runtime.loadLibrary("ADVAPI32.DLL");
107 			_RtlGenRandom = cast(typeof(_RtlGenRandom))_advapi32.GetProcAddress("SystemFunction036");
108 			enforce(_RtlGenRandom);
109 		}
110 		else version(Posix)
111 		{
112 			devRandom = File("/dev/random");
113 			devRandom.setvbuf(null, _IONBF); // Disable buffering for security
114 		}
115 		else
116 			static assert(false);
117 	}
118 	
119 	///	Manually release the handle/connection to the system-specific entropy generator.
120 	static void close()
121 	{
122 		version(Windows)
123 		{
124 			if(_advapi32)
125 			{
126 				Runtime.unloadLibrary(_advapi32);
127 				_advapi32 = null;
128 				_RtlGenRandom = null;
129 			}
130 		}
131 		else version(Posix)
132 		{
133 			if(devRandom.isOpen)
134 				devRandom.close();
135 		}
136 		else
137 			static assert(false);
138 	}
139 
140 	/// Check whether SystemEntropyStream is currently connected to with the
141 	/// system-specific entropy generator.
142 	static @property bool isOpen()
143 	{
144 		version(Windows)
145 			return _advapi32 && _RtlGenRandom;
146 		else version(Posix)
147 			return devRandom.isOpen;
148 		else
149 			static assert(false);
150 	}
151 	
152 	/// Automatically close upon module destruction.
153 	static ~this()
154 	{
155 		close();
156 	}
157 }
158 
159 /// A convenience alias to create a UniformRNG from SystemEntropyStream.
160 /// See the WrappedStreamRNG documentation for important information.
161 alias SystemEntropy(Elem) = WrappedStreamRNG!(SystemEntropyStream, Elem);
162 
163 static assert(isUniformRNG!(SystemEntropy!(ubyte[1]), ubyte[1]));
164 static assert(isUniformRNG!(SystemEntropy!(ubyte[5]), ubyte[5]));
165 static assert(isUniformRNG!(SystemEntropy!ubyte,      ubyte   ));
166 static assert(isUniformRNG!(SystemEntropy!ushort,     ushort  ));
167 static assert(isUniformRNG!(SystemEntropy!uint,       uint    ));
168 static assert(isUniformRNG!(SystemEntropy!ulong,      ulong   ));
169 
170 /++
171 Cryptographic random number generator Hash_DRBG, as defined in
172 NIST's $(LINK2 http://csrc.nist.gov/publications/nistpubs/800-90A/SP800-90A.pdf, SP800-90A).
173 
174 TSHA: Any SHA-1 or SHA-2 digest type. Default is SHA512.
175 
176 custom: Hash_DRBG's personalization string. You can optionally set this to any
177 specific value of your own choosing for improved security.
178 +/
179 struct HashDRBGStream(TSHA = SHA512, string custom = "D Crypto RNG", EntropyStream = SystemEntropyStream)
180 	if(isInstanceOf!(SHA, TSHA))
181 {
182 	enum isUniformRandomStream = true; /// Mark this as a Rng Stream
183 
184 	// In bits. This is the same as the SHA's digestSize
185 	private enum outputSizeBits = TemplateArgsOf!(TSHA)[1];
186 
187 	static if(outputSizeBits < 384)
188 		private enum seedSizeBytes = 440/8; // In bytes
189 	else
190 		private enum seedSizeBytes = 888/8; // In bytes
191 	
192 	// This can be just about any arbitrary size, although there is a
193 	// minimum. 1024 bits is above the minimum for SHA-1 and all SHA-2.
194 	// See NIST's [SP800-90A] and [SP800-57] for details.
195 	private enum entropySizeBytes = 1024/8;
196 	
197 	// This must be at least entropySizeBytes/2
198 	private enum nonceSizeBytes = entropySizeBytes/2;
199 	
200 	// value[1..$] is Hash_DRBG's secret working state value V
201 	// value[0] is a scratchpad to avoid unnecessary copying/concating of V
202 	private static ubyte[seedSizeBytes+1] value;
203 
204 	private static ubyte[seedSizeBytes] constant; // Hash_DRBG's secret working state value C
205 	private static uint numGenerated; // Number of values generated with the current seed
206 
207 	// Maximum number of values generated before automatically reseeding with fresh entropy.
208 	// The algorithm's spec permits this to be anything less than or equal to 2^48,
209 	// but we should take care not to overflow our actual countner.
210 	private enum int maxGenerated = 0x0FFF_FFFF;
211 	
212 	/++
213 	Set to Yes.PredictionResistance for additional protection against
214 	prediction attacks by forcing a reseed with fresh entropy for each call
215 	to read(). Reset back to No.PredictionResistance afterwords for faster,
216 	but still cryptographically-secure, operation when you're done with
217 	extra-elevated security needs.
218 
219 	Default is No.PredictionResistance.
220 	
221 	This setting is for changing read()'s default bahavior. Individual calls to
222 	read() can manually override this per call.
223 	+/
224 	Flag!"PredictionResistance" predictionResistance = No.PredictionResistance;
225 
226 	/++
227 	Further improve	security by setting Hash_DRBG's optional "additional input"
228 	for each call to read(). This can be set to a new value before each read()
229 	call for maximum effect.
230 
231 	This setting is for changing read()'s default bahavior. Individual calls to
232 	read() can manually override this per call.
233 	+/
234 	ubyte[] extraInput = null;
235 
236 	private static bool inited = false;
237 	private void init()
238 	{
239 		if(inited)
240 			return;
241 		
242 		// seedMaterial = entropy ~ nonce ~ custom;
243 		ubyte[entropySizeBytes + nonceSizeBytes + custom.length] seedMaterial = void;
244 		EntropyStream.read( seedMaterial[0 .. $-custom.length] );
245 		seedMaterial[$-custom.length .. $] = cast(ubyte[])custom;
246 		
247 		// Generate seed for V
248 		hashDerivation(seedMaterial, null, value[1..$]);
249 		
250 		// Generate constant
251 		value[0] = 0x00;
252 		hashDerivation(value, null, constant);
253 		
254 		numGenerated = 0;
255 		inited = true;
256 	}
257 	
258 	private void reseed(ubyte[] extraInput=null)
259 	{
260 		// seedMaterial = 0x01 ~ V ~ entropy;
261 		ubyte[value.sizeof + entropySizeBytes] seedMaterial = void;
262 		seedMaterial[0] = 0x01;
263 		seedMaterial[1 .. $-entropySizeBytes] = value[1..$];
264 		EntropyStream.read( seedMaterial[$-entropySizeBytes .. $] );
265 		
266 		// Generate seed for V
267 		hashDerivation(seedMaterial, extraInput, value[1..$]);
268 		
269 		// Generate constant
270 		value[0] = 0x00;
271 		hashDerivation(value, null, constant);
272 
273 		numGenerated = 0;
274 	}
275 	
276 	/++
277 	Fills the buffer with random values using the Hash_DRBG algorithm.
278 	
279 	overridePredictionResistance:
280 	Override this.predictionResistance setting for this call only.
281 	
282 	overrideExtraInput:
283 	Override this.extraInput setting for this call only.
284 	+/
285 	void read(ubyte[] buf)
286 	{
287 		read(buf, predictionResistance, extraInput);
288 	}
289 
290 	///ditto
291 	void read(ubyte[] buf, ubyte[] overrideExtraInput)
292 	{
293 		read(buf, predictionResistance, overrideExtraInput);
294 	}
295 
296 	///ditto
297 	void read(ubyte[] buf, Flag!"PredictionResistance" overridePredictionResistance)
298 	{
299 		read(buf, overridePredictionResistance, extraInput);
300 	}
301 
302 	///ditto
303 	void read(ubyte[] buf,
304 		Flag!"PredictionResistance" overridePredictionResistance,
305 		ubyte[] overrideExtraInput)
306 	{
307 		if(numGenerated >= maxGenerated || overridePredictionResistance == Yes.PredictionResistance)
308 			reseed(overrideExtraInput);
309 		
310 		if(overrideExtraInput)
311 		{
312 			value[0] = 0x02;
313 
314 			TSHA sha;
315 			sha.put(value);
316 			sha.put(overrideExtraInput);
317 			ubyte[seedSizeBytes] tempHash;
318 			tempHash[0..outputSizeBits/8] = sha.finish();
319 			addHash!seedSizeBytes(value[1..$], tempHash, value[1..$]);
320 		}
321 		
322 		ubyte[seedSizeBytes] workingData = value[1..$];
323 		if(buf.length > 0)
324 		while(true)
325 		{
326 			// Fill the front of buf with up to seedSizeBytes of random data
327 			ubyte[outputSizeBits/8] currHash = digest!TSHA(workingData);
328 			auto length = buf.length < currHash.length? buf.length : currHash.length;
329 			buf[0..length] = currHash[0..length];
330 			buf = buf[length..$];
331 			
332 			// Buffer filled?
333 			if(buf.length == 0)
334 				break;
335 			
336 			incrementHash(workingData);
337 		}
338 		
339 		// Update V
340 		value[0] = 0x03;
341 		ubyte[seedSizeBytes] hashSum = void;
342 		hashSum[0 .. outputSizeBits/8] = digest!TSHA(value);
343 		hashSum[outputSizeBits/8 .. $] = 0;
344 		addHash!seedSizeBytes(hashSum, value[1..$], hashSum);
345 		addHash!seedSizeBytes(hashSum, constant, hashSum);
346 		addHash!seedSizeBytes(hashSum, numGenerated+1, value[1..$]);
347 		
348 		numGenerated++;
349 	}
350 	
351 	private static void hashDerivation(ubyte[] input, ubyte[] extraInput, ubyte[] buf)
352 	{
353 		ubyte counter = 1;
354 		ulong originalBufLength = buf.length;
355 		while(buf.length)
356 		{
357 			// Generate hashed data
358 			TSHA sha;
359 			sha.put(counter);
360 			sha.put(*(cast(ubyte[8]*) &originalBufLength));
361 			sha.put(input);
362 			if(extraInput)
363 				sha.put(extraInput);
364 			auto currHash = sha.finish();
365 			
366 			// Fill the front of buf with the hashed data
367 			auto length = buf.length < currHash.length? buf.length : currHash.length;
368 			buf[0..length] = currHash[0..length];
369 			buf = buf[length..$];
370 			
371 			counter++;
372 		}
373 	}
374 	
375 	private static void incrementHash(int numBytes)(ref ubyte[numBytes] arr)
376 	{
377 		// Endianness (small, big or even weird mixes) doesn't matter since hashes
378 		// don't have a particularly meaningful least/most significant bit. As
379 		// long as we're consistent across the RNG instance's lifetime, we're good.
380 
381 		foreach(ref b; arr)
382 		{
383 			b++;
384 			if(b != 0)
385 				break;
386 		}
387 	}
388 
389 	private static void addHash(int numBytes)(ubyte[numBytes] arr1,
390 		ubyte[numBytes] arr2, ubyte[] result)
391 	{
392 		// As with incrementHash, endianness doesn't matter here.
393 		
394 		enforce(arr1.length == arr2.length);
395 		enforce(arr1.length == result.length);
396 		uint carry = 0;
397 		foreach(i; 0..arr1.length)
398 		{
399 			auto sum = arr1[i] + arr2[i] + carry;
400 			result[i] = sum & 0xFF;
401 			carry = sum >> 8;
402 		}
403 	}
404 
405 	private static void addHash(int numBytes)(ubyte[numBytes] arr, uint value,
406 		ubyte[] result)
407 	{
408 		// As with incrementHash, endianness doesn't matter here.
409 		
410 		enforce(arr.length == result.length);
411 		uint carry = value;
412 		foreach(i; 0..arr.length)
413 		{
414 			uint sum = arr[i] + carry;
415 			result[i] = sum & 0xFF;
416 			carry = sum >> 8;
417 		}
418 	}
419 }
420 
421 /// A convenience template to create a UniformRNG from HashDRBGStream.
422 /// See the WrappedStreamRNG documentation for important information.
423 template HashDRBG(Elem, TSHA = SHA512, string custom = "D Crypto RNG", EntropyStream = SystemEntropyStream)
424 	if(isInstanceOf!(SHA, TSHA))
425 {
426 	alias HashDRBG = WrappedStreamRNG!(HashDRBGStream!(TSHA, custom, EntropyStream), Elem);
427 }
428 
429 static assert(isUniformRNG!(HashDRBG!(ubyte[1]), ubyte[1]));
430 static assert(isUniformRNG!(HashDRBG!(ubyte[5]), ubyte[5]));
431 static assert(isUniformRNG!(HashDRBG!ubyte,      ubyte   ));
432 static assert(isUniformRNG!(HashDRBG!ushort,     ushort  ));
433 static assert(isUniformRNG!(HashDRBG!uint,       uint    ));
434 static assert(isUniformRNG!(HashDRBG!ulong,      ulong   ));
435 static assert(isUniformRNG!(HashDRBG!(uint), uint));
436 static assert(isUniformRNG!(HashDRBG!(uint, SHA256), uint));
437 static assert(isUniformRNG!(HashDRBG!(uint, SHA256, "custom"), uint));
438 static assert(isUniformRNG!(HashDRBG!(uint, SHA256, "custom", SystemEntropyStream), uint));
439 
440 version(DAuth_Unittest)
441 unittest
442 {
443 	unitlog("Testing HashDRBGStream.incrementHash");
444 	
445 	HashDRBGStream!SHA1 rand;
446 	ubyte[5] val      = [0xFF, 0xFF, 0b0000_1011, 0x00, 0x00];
447 	ubyte[5] expected = [0x00, 0x00, 0b0000_1100, 0x00, 0x00];
448 	
449 	assert(val != expected);
450 	rand.incrementHash(val);
451 	assert(val == expected);
452 }
453 
454 version(DAuth_Unittest)
455 unittest
456 {
457 	unitlog("Testing HashDRBGStream.addHash(arr,arr,arr)");
458 	
459 	HashDRBGStream!SHA1 rand;
460 	ubyte[5] val1     = [0xCC, 0x05, 0xFE, 0x01, 0x00];
461 	ubyte[5] val2     = [0x33, 0x02, 0x9E, 0x00, 0x00];
462 	ubyte[5] expected = [0xFF, 0x07, 0x9C, 0x02, 0x00];
463 	ubyte[5] result;
464 	
465 	assert(result != expected);
466 	rand.addHash(val1, val2, result);
467 	assert(result == expected);
468 }
469 
470 version(DAuth_Unittest)
471 unittest
472 {
473 	unitlog("Testing HashDRBGStream.addHash(arr,int,arr)");
474 	
475 	HashDRBGStream!SHA1 rand;
476 	ubyte[5] val1     = [0xCC, 0x05, 0xFE, 0x01, 0x00];
477 	uint val2         = 0x009E_0233;
478 	ubyte[5] expected = [0xFF, 0x07, 0x9C, 0x02, 0x00];
479 	ubyte[5] result;
480 	
481 	assert(result != expected);
482 	rand.addHash(val1, val2, result);
483 	assert(result == expected);
484 }
485 
486 /++
487 Takes a RandomStream (ex: SystemEntropyStream or HashDRBGStream) and
488 wraps it into a UniformRNG InputRange.
489 
490 Note that, to conform to the expected InputRange interface, this must keep a
491 copy of the last generated value in memory. For security purposes, it may
492 occasionally be appropriate to make an extra popFront() call before and/or
493 after retreiving entropy values. This may decrease the chance of using
494 a compromized entropy value in the event of a memory-sniffing attacker.
495 +/
496 struct WrappedStreamRNG(RandomStream, StaticUByteArr)
497 	if(isRandomStream!RandomStream && isStaticArray!StaticUByteArr && is(ElementType!StaticUByteArr==ubyte))
498 {
499 	enum isUniformRandom = true; /// Mark this as a Rng
500 	
501 	private StaticUByteArr _front;
502 	private bool inited = false;
503 	
504 	/// Access to underlying RandomStream so RNG-specific functionality can be accessed.
505 	RandomStream stream;
506 	
507 	/// Implements an InputRange
508 	@property StaticUByteArr front()
509 	{
510 		if(!inited)
511 		{
512 			popFront();
513 			inited = true;
514 		}
515 		
516 		return _front;
517 	}
518 	
519 	///ditto
520 	void popFront()
521 	{
522 		stream.read(_front);
523 	}
524 	
525 	/// Infinite range. Never empty.
526 	enum empty = false;
527 	
528 	/// Smallest generated value.
529 	enum min = StaticUByteArr.init;
530 	
531 	/// Largest generated value.
532 	static @property StaticUByteArr max()
533 	{
534 		StaticUByteArr val = void;
535 		val[] = 0xFF;
536 		return val;
537 	}
538 }
539 
540 ///ditto
541 struct WrappedStreamRNG(RandomStream, UIntType)
542 	if(isRandomStream!RandomStream && isUnsigned!UIntType)
543 {
544 	private WrappedStreamRNG!(RandomStream, ubyte[UIntType.sizeof]) bytesImpl;
545 	
546 	enum isUniformRandom = true; /// Mark this as a Rng
547 	
548 	private UIntType _front;
549 	private bool inited = false;
550 	
551 	/// Implements an InputRange
552 	@property UIntType front()
553 	{
554 		auto val = bytesImpl.front;
555 		return *(cast(UIntType*) &val);
556 	}
557 	
558 	///ditto
559 	void popFront()
560 	{
561 		bytesImpl.popFront();
562 	}
563 	
564 	enum empty = false; /// Infinite range. Never empty.
565 	enum min = UIntType.min; /// Smallest generated value.
566 	enum max = UIntType.max; /// Largest generated value.
567 }
568 
569 version(DAuth_Unittest)
570 unittest
571 {
572 	alias RandStreamTypes = TypeTuple!(
573 		SystemEntropyStream,
574 		HashDRBGStream!SHA1,
575 		HashDRBGStream!SHA224,
576 		HashDRBGStream!SHA256,
577 		HashDRBGStream!SHA384,
578 		HashDRBGStream!SHA512,
579 		HashDRBGStream!SHA512_224,
580 		HashDRBGStream!SHA512_256,
581 		HashDRBGStream!(SHA512, "other custom str"),
582 	);
583 	
584 	unitlog("Testing SystemEntropyStream/HashDRBGStream");
585 	foreach(RandStream; RandStreamTypes)
586 	{
587 		//unitlog("Testing RandStream: "~RandStream.stringof);
588 		
589 		RandStream rand;
590 		ubyte[] values1;
591 		ubyte[] values2;
592 		values1.length = 10;
593 		values2.length = 10;
594 		
595 		rand.read(values1);
596 		assert(values1 != typeof(values1).init);
597 		assert(values1[0..4] != values1[4..8]);
598 		rand.read(values2);
599 		assert(values1 != values2);
600 		
601 		auto randCopy = rand;
602 		rand.read(values1);
603 		randCopy.read(values2);
604 		assert(values1 != values2);
605 		
606 		static if(!is(RandStream == SystemEntropyStream))
607 		{
608 			values2[] = ubyte.init;
609 
610 			values1[] = ubyte.init;
611 			rand.read(values1, Yes.PredictionResistance);
612 			assert(values1 != values2);
613 			
614 			values1[] = ubyte.init;
615 			rand.read(values1, cast(ubyte[])"additional input");
616 			assert(values1 != values2);
617 			
618 			values1[] = ubyte.init;
619 			rand.read(values1, Yes.PredictionResistance, cast(ubyte[])"additional input");
620 			assert(values1 != values2);
621 		}
622 	}
623 }
624 
625 version(DAuth_Unittest)
626 unittest
627 {
628 	foreach(Rand; TypeTuple!(SystemEntropy, HashDRBG))
629 	{
630 		unitlog("Testing Rand's min/max: "~Rand.stringof);
631 
632 		assert(Rand!(ubyte[1]).min == [0x00]);
633 		assert(Rand!(ubyte[1]).max == [0xFF]);
634 		assert(Rand!(ubyte[5]).min == [0x00,0x00,0x00,0x00,0x00]);
635 		assert(Rand!(ubyte[5]).max == [0xFF,0xFF,0xFF,0xFF,0xFF]);
636 		assert(Rand!(ubyte   ).min == ubyte .min);
637 		assert(Rand!(ubyte   ).max == ubyte .max);
638 		assert(Rand!(ushort  ).min == ushort.min);
639 		assert(Rand!(ushort  ).max == ushort.max);
640 		assert(Rand!(uint    ).min == uint  .min);
641 		assert(Rand!(uint    ).max == uint  .max);
642 		assert(Rand!(ulong   ).min == ulong .min);
643 		assert(Rand!(ulong   ).max == ulong .max);
644 	}
645 }
646 
647 version(DAuth_Unittest)
648 unittest
649 {
650 	// Don't test ubyte or ushort versions here because legitimate repeated
651 	// values are too likely and would trigger a failure and unfounded worry.
652 	
653 	alias RandTypes = TypeTuple!(
654 		SystemEntropy!ulong,
655 		SystemEntropy!uint,
656 		SystemEntropy!(ubyte[5]),
657 		SystemEntropy!(ubyte[1024]),
658 		HashDRBG!(ulong, SHA1),
659 		HashDRBG!(ulong, SHA224),
660 		HashDRBG!(ulong, SHA256),
661 		HashDRBG!(ulong, SHA384),
662 		HashDRBG!(ulong, SHA512),
663 		HashDRBG!(ulong, SHA512_224),
664 		HashDRBG!(ulong, SHA512_256),
665 		HashDRBG!(ulong, SHA512, "other custom str"),
666 		HashDRBG!(uint,    SHA512),
667 		HashDRBG!(ubyte[5], SHA512),
668 		HashDRBG!(ubyte[1024], SHA512),
669 	);
670 	
671 	unitlog("Testing SystemEntropy/HashDRBG");
672 	foreach(Rand; RandTypes)
673 	{
674 		//unitlog("Testing Rand: "~Rand.stringof);
675 
676 		Rand rand;
677 		assert(!rand.empty);
678 		
679 		assert(rand.front == rand.front);
680 		auto val = rand.front;
681 		assert(val != ElementType!(Rand).init);
682 
683 		rand.popFront();
684 		assert(val != rand.front);
685 
686 		auto randCopy = rand;
687 		assert(rand.front == randCopy.front);
688 		rand.popFront();
689 		randCopy.popFront();
690 		assert(rand.front != randCopy.front);
691 	}
692 }