1 module dcrypt.crypto.random.fortuna.entropysource;
2 
3 
4 import core.thread;
5 
6 import dcrypt.crypto.random.fortuna.accumulator;
7 
8 ///
9 /// This class provides a simple method to pass an entropy event
10 /// to the accumulator of the PRNG algorithm. Namely: sendEntropyEvent()
11 /// 
12 /// 
13 @safe
14 public abstract class EntropySource
15 {
16 	private static ubyte idCounter = 0;
17 	private ubyte sourceID;
18 	private size_t pool = 0; // ID of the pool where the entropy gets sent to.
19 	private bool calledSendEntropyEvent; /// Used to control wether the source calls sendEntropyEvent or not.
20 	private Thread worker;
21 
22 	
23 	final this() nothrow {
24 		this.sourceID = idCounter++; // give each source another ID (as long as there are less than 256 sources)
25 	}
26 
27 	@trusted
28 	public final void start() nothrow {
29 		try {
30 			worker = new Thread(&run);
31 			worker.isDaemon = true;
32 			worker.start();
33 		} catch(Exception e) {
34 			assert(false, e.toString());
35 		}
36 	}
37 
38 	private void run() {
39 		bool running = true;
40 
41 		while(running) {
42 
43 			calledSendEntropyEvent = false;
44 			collectEntropy();
45 
46 			assert(calledSendEntropyEvent, name~" did not call sendEntropyEvent().");
47 
48 			uint delay = scheduleNext();	// Ask the source when it wants to be invoked.
49 
50 			if(delay > 0) {
51 				trustedSleep!"msecs"(delay);
52 			} else {
53 				// delay == 0 means the source wants to be closed
54 				running = false;
55 			}
56 		}
57 
58 	}
59 
60 	@trusted
61 	private static void trustedSleep(string s)(uint i) nothrow {
62 		try {
63 			Thread.sleep(dur!s(i));
64 		} catch (ThreadError te) {
65 			// swallow
66 		}
67 	}
68 
69 	/// Collect entropy.
70 	/// Note: Implementation must call `sendEntropyEvent(in ubyte[] buf)` to send data to the accumulator.
71 	public abstract void collectEntropy();
72 
73 	@safe @nogc nothrow
74 	public abstract uint scheduleNext();
75 
76 	@property @nogc nothrow
77 	public abstract string name();
78 
79 	/// use this method to send entropy to the accumulator
80 	@safe
81 	protected final void sendEntropyEvent(in ubyte[] buf) nothrow {
82 		import dcrypt.crypto.random.fortuna.fortuna: addEntropy;
83 
84 		calledSendEntropyEvent = true;
85 
86 		scope(exit) {
87 			pool = (pool + 1) % Accumulator.pools;
88 		}
89 
90 		addEntropy(sourceID, pool, buf);
91 
92 //		debug {
93 //			try {
94 //				import std.stdio;
95 //				writeln(sourceID, " ", pool, " ",  name, ":\t", dcrypt.encoders.hex.toHexStr(buf[0..$/4]));
96 //			} catch(Exception e) {}
97 //		}
98 	}
99 
100 	invariant {
101 		assert(pool < Accumulator.pools);
102 	}
103 
104 }
105