1 module dcrypt.random.prng;
2 
3 import std.range;
4 import dcrypt.random.fortuna.fortuna;
5 import dcrypt.random.fortuna.generator;
6 
7 import dcrypt.blockcipher.aes;
8 import dcrypt.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 PRNG {
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 	/// 
81 	/// Params:
82 	/// buf = Output buffer to be filled with PRNG data.
83 	public abstract void nextBytes(ubyte[] buf) nothrow;
84 	
85 	/// Returns: The name of the RNG algorithm.
86 	@property
87 	public abstract string name() pure nothrow;
88 
89 	///
90 	/// Add a seed value to the PRNG.
91 	/// 
92 	/// Params:
93 	/// seed = The seed value to add to the PRNG.
94 	public abstract void addSeed(in ubyte[] seed) nothrow;
95 	
96 }
97 
98 ///
99 ///	Wrapper class for PRNGs.
100 ///
101 @safe
102 public class WrapperPRNG(R) if(isRNGWithInput!R): PRNG {
103 
104 	private R rng;
105 
106 override:
107 	/// Fill the buffer with random bytes.
108 	/// 
109 	/// Params:
110 	/// buf = Output buffer to be filled with PRNG data.
111 	public void nextBytes(ubyte[] buf) nothrow {
112 		rng.nextBytes(buf);
113 	}
114 	
115 	/// Returns: the name of the RNG algorithm
116 	@property
117 	public string name() pure nothrow @nogc {
118 		return rng.name;
119 	}
120 	
121 	///
122 	/// Add a seed value to the PRNG.
123 	/// 
124 	/// Params:
125 	/// seed = the seed value to add to the PRNG
126 	public void addSeed(in ubyte[] seed) nothrow @nogc {
127 		rng.addSeed(seed);
128 	}
129 }