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