1 module dcrypt.crypto.random.fortuna.fortuna; 2 3 public import dcrypt.crypto.random.prng; 4 public import dcrypt.crypto.blockcipher; 5 public import dcrypt.crypto.digest; 6 7 import dcrypt.crypto.random.fortuna.generator; 8 import dcrypt.crypto.random.fortuna.accumulator; 9 10 import dcrypt.crypto.engines.aes; 11 import dcrypt.crypto.digests.sha2; 12 13 import std.datetime; 14 15 16 /// OOP wrapper 17 public alias WrapperPRNG!(FortunaCore!(AES, SHA256)) FortunaRNG; 18 public alias FortunaCore!(AES, SHA256) Fortuna; 19 20 /// Get some random bytes from Fortuna. 21 unittest { 22 FortunaCore!(AES, SHA256) fortuna; 23 24 ubyte[61] buf1; 25 ubyte[buf1.length] buf2; 26 fortuna.addSeed([0,1,2,3]); 27 foreach(i;0..10) { 28 buf2 = buf1; 29 fortuna.nextBytes(buf1); 30 31 if(i > 0) { 32 assert(buf2 != buf1, "data is not random"); 33 } 34 } 35 } 36 37 38 /// Add real entropy to the global accumulator. 39 /// 40 /// Params: 41 /// sourceID = The ID of the entropy source. Can actually be any number. 42 /// pool = The ID of the pool to add the entropy. 43 /// seed = Random data. 44 @safe 45 public void addEntropy(in ubyte sourceID, in size_t pool, in ubyte[] seed...) nothrow @nogc 46 in { 47 assert(pool < Accumulator.pools, "Pool ID out of range."); 48 } 49 body { 50 assert(globalAcc !is null, "Accumulator not initialized!"); 51 globalAcc.addEntropy(sourceID, pool, seed); 52 53 } 54 55 /// Get 32 bytes of unpredictable* seed from the global accumulator. 56 /// 57 /// Note: 58 /// * The seed can only be unpredictable if the accumulator gets enough entropy from entropy sources. 59 /// 60 /// Params: 61 /// buf = A buffer for exactly 32 bytes. 62 /// 63 /// Throws: 64 /// Error = if buffer has wrong size. 65 @safe 66 private void getSeed(ubyte[] buf) nothrow @nogc 67 in { 68 assert(buf.length == 32, "buf must be 32 bytes long."); 69 } 70 body { 71 assert(globalAcc !is null, "Accumulator not initialized!"); 72 globalAcc.extractEntropy(buf); 73 } 74 75 private shared Accumulator globalAcc; /// The entropy accumulator is used globally. 76 77 /// initialize and seed the global accumulator 78 private shared static this() { 79 globalAcc = new shared Accumulator; 80 81 // seed the accumulator 82 ubyte[32] buf; 83 foreach(i;0..4096/buf.length) { 84 import dcrypt.crypto.random.fortuna.sources.systemtick; 85 86 getTimingEntropy(buf); 87 addEntropy(0, i%Accumulator.pools, buf); 88 89 } 90 } 91 92 93 static assert(isRNG!(FortunaCore!(AES, SHA256)), "Fortuna does not meet requirements for PRNGs."); 94 95 /// FortunaCore is meant to be the mothership of the PRNGs. It should run as a singleton - 96 /// one instance per application that handles the accumulator and entropy sources. 97 /// 98 /// Params: 99 /// Cipher = A block cipher. 100 /// Digest = A hash algorithm. 101 @safe 102 private struct FortunaCore(Cipher, Digest) if(isBlockCipher!Cipher && isDigest!Digest) { 103 nothrow: 104 105 public { 106 107 enum name = "FortunaCore"; 108 109 /// Add entropy to generators state but not to the accumulator. 110 @safe 111 void addSeed(in ubyte[] seed) nothrow @nogc { 112 // pass this call directly to the generator 113 prng.addSeed(seed); 114 } 115 116 /// Fill the buffer with random bytes. 117 void nextBytes(ubyte[] buffer) nothrow @nogc { 118 randomData(buffer); 119 } 120 } 121 122 123 private { 124 enum minReseedInterval = 100; /// minimal time in ms between reseeds 125 126 FortunaGenerator!(Cipher, Digest) prng; 127 128 size_t reseedCount = 0; /// increment each time reseed() is called 129 ulong lastReseed = 0; /// time of the last reseed in ms 130 131 @safe 132 void randomData(ubyte[] buffer) nothrow @nogc 133 { 134 135 if( 136 //a.getLength() >= MINPOOLSIZE && 137 TickDuration.currSystemTick.msecs - lastReseed > minReseedInterval) { 138 139 reseed(); 140 141 } 142 143 if(lastReseed == 0 && reseedCount == 0) { 144 assert(false, "PRNG not seeded yet"); 145 } 146 147 // ready to generate the random data 148 prng.nextBytes(buffer); 149 } 150 151 /// get entropy from accumulator 152 @trusted 153 private void reseed() nothrow @nogc { 154 ubyte[32] buf; 155 156 getSeed(buf); 157 158 assert(std.algorithm.any!"a != 0"(buf[]), "Got only zeros from accumulator instead of noise!"); 159 160 prng.addSeed(buf); 161 162 lastReseed = TickDuration.currSystemTick.msecs; 163 ++reseedCount; 164 165 } 166 167 } 168 }