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.util.encoders.hex.toHexStr(buf[0..$/4])); 96 // } catch(Exception e) {} 97 // } 98 } 99 100 invariant { 101 assert(pool < Accumulator.pools); 102 } 103 104 } 105