1 module secured.random;
2 
3 import secured.util;
4 
5 version(CRuntime_Bionic)
6     version = SecureARC4Random;//ChaCha20
7 else version(OSX)
8     version = SecureARC4Random;//AES
9 else version(OpenBSD)
10     version = SecureARC4Random;//ChaCha20
11 else version(NetBSD)
12     version = SecureARC4Random;//ChaCha20
13 // Can uncomment following two lines if Solaris versions prior to 11.3 are unsupported:
14 //else version (Solaris)
15 //    version = SecureARC4Random;
16 
17 version(SecureARC4Random)
18 extern(C) @nogc nothrow private @system
19 {
20     void arc4random_buf(scope void* buf, size_t nbytes);
21 }
22 
23 @trusted public ubyte[] random(uint bytes)
24 {
25     if (bytes == 0) {
26         throw new CryptographicException("The number of requested bytes must be greater than zero.");
27     }
28     ubyte[] buffer = new ubyte[bytes];
29 
30     version(SecureARC4Random)
31     {
32         arc4random_buf(buffer.ptr, bytes);
33     }
34     else version(Posix)
35     {
36         import std.exception;
37         import std.format;
38         import std.stdio;
39 
40         try {
41             //Initialize the system random file buffer
42             File urandom = File("/dev/urandom", "rb");
43             urandom.setvbuf(null, _IONBF);
44             scope(exit) urandom.close();
45 
46             //Read into the buffer
47             try {
48                 buffer = urandom.rawRead(buffer);
49             }
50             catch(ErrnoException ex) {
51                 throw new CryptographicException(format("Cannot get the next random bytes. Error ID: %d, Message: %s", ex.errno, ex.msg));
52             }
53             catch(Exception ex) {
54                 throw new CryptographicException(format("Cannot get the next random bytes. Message: %s", ex.msg));
55             }
56         }
57         catch(ErrnoException ex) {
58             throw new CryptographicException(format("Cannot initialize the system RNG. Error ID: %d, Message: %s", ex.errno, ex.msg));
59         }
60         catch(Exception ex) {
61             throw new CryptographicException(format("Cannot initialize the system RNG. Message: %s", ex.msg));
62         }
63     }
64     else version(Windows)
65     {
66         import core.sys.windows.windows;
67 		import core.sys.windows.wincrypt;
68         import std.format;
69 		
70         HCRYPTPROV hCryptProv;
71 
72         //Get the cryptographic context from Windows
73         if (!CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) {
74             throw new CryptographicException("Unable to acquire Cryptographic Context.");
75         }
76         //Release the context when finished
77         scope(exit) CryptReleaseContext(hCryptProv, 0);
78 
79         //Generate the random bytes
80         if (!CryptGenRandom(hCryptProv, cast(DWORD)buffer.length, buffer.ptr)) {
81             throw new CryptographicException(format("Cannot get the next random bytes. Error ID: %d", GetLastError()));
82         }
83     }
84     else
85     {
86         static assert(0, "SecureD does not support this OS.");
87     }
88 
89     return buffer;
90 }
91 
92 unittest
93 {
94     import std.digest;
95     import std.stdio;
96 
97     writeln("Testing Random Number Generator with 32/64/512/2048 bytes:");
98 
99     //Test 32 bytes
100     ubyte[] rnd1 = random(32);
101     writeln("32 Bytes:");
102     writeln(toHexString!(LetterCase.lower)(rnd1));
103     assert(rnd1.length == 32);
104 
105     //Test 128 bytes
106     ubyte[] rnd2 = random(128);
107     writeln("128 Bytes:");
108     writeln(toHexString!(LetterCase.lower)(rnd2));
109     assert(rnd2.length == 128);
110 
111     //Test 512 bytes
112     ubyte[] rnd3 = random(512);
113     writeln("512 Bytes:");
114     writeln(toHexString!(LetterCase.lower)(rnd3));
115     assert(rnd3.length == 512);
116 
117     //Test 2048 bytes
118     ubyte[] rnd4 = random(2048);
119     writeln("2048 Bytes:");
120     writeln(toHexString!(LetterCase.lower)(rnd4));
121     assert(rnd4.length == 2048);
122 }
123 
124 unittest
125 {
126     import std.digest;
127     import std.stdio;
128 
129     writeln("Testing Random Number Generator for Equality:");
130 
131     //Test 32 bytes
132     ubyte[] rnd1 = random(32);
133     ubyte[] rnd2 = random(32);
134     writeln("Testing with 32 Bytes");
135     assert(!constantTimeEquality(rnd1, rnd2));
136 
137     //Test 128 bytes
138     rnd1 = random(128);
139     rnd2 = random(128);
140     writeln("Testing with 128 Bytes");
141     assert(!constantTimeEquality(rnd1, rnd2));
142 
143     //Test 512 bytes
144     rnd1 = random(512);
145     rnd2 = random(512);
146     writeln("Testing with 512 Bytes");
147     assert(!constantTimeEquality(rnd1, rnd2));
148 
149     //Test 2048 bytes
150     rnd1 = random(2048);
151     rnd2 = random(2048);
152     writeln("Testing with 2048 Bytes");
153     assert(!constantTimeEquality(rnd1, rnd2));
154 }