1 module dcrypt.crypto.random.fortuna.sources.reboot;
2 
3 /// The reboot entropy source loads entropy from a specified file
4 /// and updates the seed in this file with new random data.
5 /// The goal of this entropy source is to provide good entropy at start of
6 /// the program. This is however not the case when the program is run the first time.
7 
8 import dcrypt.crypto.random.fortuna.entropysource;
9 import dcrypt.crypto.random.fortuna.fortuna;
10 
11 import std.stdio;
12 import std.file;
13 
14 private enum blockSize = 32*32;
15 private enum blocksPerFile = 16;
16 
17 version (linux) {
18 	unittest {
19 		RebootEntropySource rbs = new RebootEntropySource("/tmp/reboot.seed");
20 		rbs.start();
21 	}
22 }
23 
24 @safe
25 public class RebootEntropySource: EntropySource
26 {
27 
28 	private string seedFile;
29 	private Fortuna rng;
30 	private uint blockCounter = 0;
31 	private uint delay = 1;
32 
33 	/// Params:
34 	/// seedFile = The file to load the seed from and to store new seed.
35 	this(string seedFile) nothrow
36 	{
37 		this.seedFile = seedFile;
38 	}
39 
40 	/// Read entropy from file.
41 	@trusted
42 	override public void collectEntropy() nothrow {
43 
44 		delay = 0; // one shot
45 
46 		if(exists(seedFile)) {
47 			try {
48 				// read whole file and send it to the accumulator
49 				File inputFile = File(seedFile, "rb");
50 
51 				scope(exit) {
52 					inputFile.close();
53 				}
54 
55 				foreach(ubyte[] c; inputFile.chunks(32)) {
56 					sendEntropyEvent(c);
57 				}
58 
59 
60 			} catch (Exception e) {
61 				
62 				// TODO
63 				assert(false, "error opening entropy file");
64 			}
65 		}
66 	}
67 
68 	@nogc @property nothrow
69 	override public string name() {
70 		return "RebootSource";
71 	}
72 
73 	@safe @nogc nothrow
74 	override uint scheduleNext() {
75 		return delay;
76 	}
77 
78 	/// Get random data from Fortuna and store it to a file for use at next program start.
79 	@trusted
80 	private void storeEntropy() nothrow {
81 		try {
82 			ubyte[32] buf;
83 
84 			File f = File(seedFile, "wb");
85 
86 			//f.seek(blockSize*blockCounter, SEEK_SET); // FIXME seek does not work as intended
87 
88 			foreach(i; 0..32) {
89 				rng.nextBytes(buf);
90 				f.rawWrite(buf);
91 			}
92 
93 			blockCounter++;
94 			blockCounter %= blocksPerFile;
95 
96 		}catch (Exception e) {
97 			version(assert) {
98 				assert(false, "'reboot' entropy source: could not store entropy to file!");
99 			}
100 			// TODO use a logger
101 		}
102 	}
103 
104 	~this() {
105 		storeEntropy();
106 	}
107 }
108