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