1 module dcrypt.random.drng;
2 
3 /// The DRNG module contains a collection of deterministic random number generators.
4 
5 public import dcrypt.random.prng;
6 import dcrypt.digest;
7 import dcrypt.digests.sha1;
8 import dcrypt.digests.sha2;
9 import dcrypt.digests.sha3;
10 import dcrypt.bitmanip;
11 import dcrypt.util: wipe;
12 import std.algorithm: min;
13 import std.traits: isIntegral;
14 
15 ///
16 /// Test if T is a deterministic random number generator.
17 /// Returns: Returns $(D true) if T can be used as a DRNG.
18 /// 
19 @safe @nogc nothrow
20 template isDRNG(T)
21 {
22 	enum bool isDRNG = isRNG!T &&
23 			T.isDeterministic &&
24 			is(typeof(
25 					{
26 						ubyte[] buf;
27 						T rng = T.init;
28 						rng.setSeed(cast(const ubyte[]) buf); // Can set the generator to well known state.
29 						rng.setSeed(cast(ubyte) 0);					// variadic template
30 						rng.setSeed(cast(ubyte) 0, cast(ubyte) 0);
31 					}));
32 }
33 
34 @safe @nogc nothrow
35 template isDRNGWithInput(T)
36 {
37 	enum bool isDRNGWithInput = isDRNG!T && isRNGWithInput!T;
38 }
39 
40 /// Generate a pseudo random but deterministic sequence of bytes.
41 unittest {
42 	import dcrypt.digests.sha2;
43 	//import std.stdio;
44 
45 	HashDRNG_SHA256 drng;
46 
47 	ubyte[70] buf;
48 
49 	drng.nextBytes(buf);
50 
51 	//writefln("%(%.2x%)", buf);
52 }
53 
54 alias HashDRNG!(SHA1, 440) HashDRNG_SHA1;
55 alias HashDRNG!(SHA256, 440) HashDRNG_SHA256;
56 alias HashDRNG!(SHA384, 888) HashDRNG_SHA384;
57 alias HashDRNG!(SHA512, 888) HashDRNG_SHA512;
58 
59 alias HashDRNG!(SHA3_256, 440) HashDRNG_SHA3_256;
60 alias HashDRNG!(SHA3_384, 888) HashDRNG_SHA3_384;
61 alias HashDRNG!(SHA3_512, 888) HashDRNG_SHA3_512;
62 
63 static assert(isDRNG!HashDRNG_SHA256 && isRNGWithInput!HashDRNG_SHA256 , HashDRNG.name~" is no DRNG.");
64 
65 /// Standard: NIST SP800-90A, HashDRBG
66 /// 
67 /// Params:
68 /// D = The underlying digest.
69 /// seedlen = Length of internal state in bits. See NIST SP800-90A, Section 10.1.
70 @safe
71 struct HashDRNG(D, uint seedlen) if(isStdDigest!D && seedlen % 8 == 0) {
72 
73 	public {
74 		enum isDeterministic = true;
75 		enum name = "HashDRNG-" ~ D.name;
76 	}
77 
78 	private {
79 		ubyte[seedlen/8] V, C;
80 		ulong reseedCounter;
81 	}
82 	nothrow @nogc:
83 
84 	~this() {
85 		wipe(V);
86 		wipe(C);
87 	}
88 
89 	/// Initialize the generator with given seed.
90 	void setSeed(in ubyte[] seed...) {
91 
92 		hashDF!D(V, seed);
93 
94 		hashDF!D(V, cast(ubyte) 0x00, V);
95 
96 		reseedCounter = 1;
97 	}
98 	
99 	/// Add entropy to the generators internal state.
100 	void addSeed(in ubyte[] seed...) {
101 
102 		hashDF!D(V, cast(ubyte) 0x01, V, seed);
103 
104 		hashDF!D(C, cast(ubyte) 0x00, V);
105 		
106 		reseedCounter = 1;
107 	}
108 
109 	/// Generate pseudo random bytes.
110 	/// Params:
111 	/// buf = Fill this buffer with random data.
112 	/// additionalInput = Can provide more entropy. Similar but not equal to callin addSeed() before.
113 	void nextBytes(ubyte[] buf, in ubyte[] additionalInput...) {
114 		D hash;
115 
116 		if(additionalInput.length > 0) {
117 			hash.putAll(cast(ubyte) 0x02, V, additionalInput);
118 			ubyte[D.digestLength] w = hash.finish();
119 			add(V, w);
120 		}
121 
122 		hashGen(buf);
123 
124 		hash.putAll(cast(ubyte) 0x03, V);
125 		immutable ubyte[D.digestLength] H = hash.finish();
126 
127 		add(V, H);
128 		add(V, reseedCounter);
129 
130 		++reseedCounter;
131 	}
132 
133 	private void hashGen(ubyte[] buf) {
134 		ubyte[V.length] data = V;
135 		D hash;
136 		while(buf.length > 0) {
137 			size_t len = min(D.digestLength, buf.length);
138 			hash.put(data);
139 			buf[0..len] = hash.finish()[0..len];
140 			buf = buf[len..$];
141 			increment(data);
142 		}
143 		wipe(data);
144 	}
145 
146 }
147 
148 /// Hash derivation function.
149 /// Standard: NIST SP800-90A, Section 10.4.1
150 /// Note: Number of output bits is implicitly defined by buf.length*8.
151 private void hashDF(D, T...)(ubyte[] buf, in T seed) if(isDigest!D) {
152 	ubyte counter = 1;
153 	ubyte[4] outputLen; /// output length in bits
154 
155 	outputLen = toEndian!uint(cast(uint) buf.length*8);	// BC compatible
156 	//outputLen = toLittleEndian!uint(cast(uint) buf.length*8, outputLen);
157 
158 	D digest;
159 
160 	while(buf.length > 0) {
161 
162 		digest.putAll(counter, outputLen, seed);
163 
164 		size_t len = min(buf.length, D.digestLength);
165 		buf[0..len] = digest.finish()[0..len];
166 		buf = buf[len..$];
167 
168 		++ counter;
169 	}
170 }
171 
172 // test hashDF
173 private unittest {
174 	ubyte[70] buf;
175 	hashDF!SHA256(buf, cast(const ubyte[]) "seed");
176 
177 	assert(buf == x"ae678a8fcbcfaf3bcf57395b6fa3c614516d21182992780fb155bc75ded4369ac44ebfc392d9990553d59f6beffa1fb56d3962be000d1a7d009674240f02855b7a8fd125dd19");
178 }
179 
180 
181 /// Little endian increment.
182 private void increment(ubyte[] v) nothrow @safe @nogc {
183 	ubyte[1] one = 1;
184 	add(v, one);
185 }
186 
187 
188 private alias toBigEndian toEndian; /// easily switch between LE and BE
189 
190 /// Add number to little endian byte string.
191 /// a += b;
192 private void add(T)(ubyte[] a, T b) nothrow @safe @nogc 
193 if(isIntegral!T) {
194 	add(a, toEndian!T(b));
195 }
196 
197 
198 /// a += b;
199 private void add(ubyte[] a, in ubyte[] b) nothrow @safe @nogc
200 in {
201 	assert(a.length >= b.length);
202 } body {
203 	ubyte carry = 0;
204 
205 	for(uint i = 0; i < a.length; ++i) {
206 		uint t = cast(uint) a[$-1-i] + carry;
207 		if(i < b.length) {
208 			t += b[$-1-i];
209 		}
210 		a[$-1-i] = cast(ubyte) t;
211 		carry = cast(ubyte) (t >> 8);
212 	}
213 
214 }
215 
216 // testing add()
217 private unittest {
218 	ubyte[4] a, b;
219 
220 	add(a, 0xFF);
221 	assert(a[0..4] == toEndian(0xFF));
222 	add(b, 0xFF00);
223 	assert(b[0..4] == toEndian(0xFF00));
224 	add(a, b);
225 	assert(a[0..4] == toEndian(0xFFFF));
226 	add(a, 1);
227 	assert(a[0..4] == toEndian(0x10000));
228 }