1 module dcrypt.crypto.random.prng;
2 
3 import std.range;
4 import dcrypt.crypto.random.fortuna.fortuna;
5 import dcrypt.crypto.random.fortuna.generator;
6 
7 import dcrypt.crypto.engines.aes;
8 import dcrypt.crypto.digests.sha2;
9 
10 alias FortunaGenerator!(AES, SHA256) DRNG; /// Deterministic PRNG
11 
12 // TODO implement PRNGs as input ranges
13 
14 ///
15 /// Test if T is a random number generator.
16 /// Returns: Returns $(D true) if T can be used as a PRNG.
17 /// 
18 @safe
19 template isRNG(T)
20 {
21 	enum bool isRNG = 
22 		is(T == struct) && // isInputRange!T &&
23 			is(typeof(
24 					{
25 						ubyte[] buf;
26 						T rng = T.init;
27 						string name = rng.name;
28 						rng.nextBytes(buf);
29 					}));
30 }
31 
32 @safe
33 template isRNGWithInput(T)
34 {
35 	enum bool isRNGWithInput = isRNG!T &&
36 			is(typeof(
37 					{
38 						ubyte[] buf;
39 						T rng = T.init;
40 						rng.addSeed(cast(const ubyte[]) buf);
41 						rng.addSeed(cast(ubyte) 0);
42 						rng.addSeed(cast(ubyte) 0, cast(ubyte) 0);
43 					}));
44 }
45 
46 /// Helper function for prng.
47 /// 
48 /// Params:
49 /// rng = The RNG to put the data into.
50 /// seed = The seed to update the RNG with.
51 /// 
52 /// Example:
53 /// 	ubyte[4] buf;
54 /// 	RNG rng;
55 /// 	rng.addSeed(cast(ubyte) 0x01, buf, buf[0..2]);
56 @safe
57 public void addSeed(R, T...)(ref R rng, in T seed) nothrow @nogc
58 if(isRNGWithInput!D) {
59 	foreach(s; seed) {
60 		digest.addSeed(s);
61 	}
62 }
63 
64 @safe
65 public abstract class RNG {
66 
67 // TODO: default RNG
68 //	/**
69 //	 * Creates a default PRNG. Type depends on your system.
70 //	 */
71 //	public static PRNG getInstance() nothrow
72 //	out (result) {
73 //		assert(result !is null, "failed to initialize PRNG instance");
74 //	}
75 //	body {
76 //		return new Fortuna();
77 //	}
78 	
79 	/// Fill the buffer with random bytes.
80 	/// Params:
81 	/// buf = output buffer to be filled with PRNG data
82 	public abstract void nextBytes(ubyte[] buf) nothrow;
83 	
84 	/// Returns: The name of the RNG algorithm.
85 	@property
86 	public abstract string name() pure nothrow;
87 
88 	///
89 	/// Add a seed value to the PRNG.
90 	/// 
91 	/// Params:
92 	/// seed = The seed value to add to the PRNG.
93 	public abstract void addSeed(in ubyte[] seed) nothrow;
94 	
95 }
96 
97 ///
98 ///	Wrapper class for PRNGs.
99 ///
100 @safe
101 public class WrapperPRNG(R) if(isRNGWithInput!R): RNG {
102 
103 	private R rng;
104 
105 override:
106 	/// fill the buffer with random bytes
107 	/// Params:
108 	/// buf = output buffer to be filled with PRNG data
109 	public void nextBytes(ubyte[] buf) nothrow @nogc {
110 		rng.nextBytes(buf);
111 	}
112 	
113 	/// Returns: the name of the RNG algorithm
114 	@property
115 	public string name() pure nothrow @nogc {
116 		return rng.name;
117 	}
118 	
119 	///
120 	/// Add a seed value to the PRNG.
121 	/// 
122 	/// Params:
123 	/// seed = the seed value to add to the PRNG
124 	public void addSeed(in ubyte[] seed) nothrow @nogc {
125 		rng.addSeed(seed);
126 	}
127 	
128 }