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 std.format;
68         HCRYPTPROV hCryptProv;
69 
70         //Get the cryptographic context from Windows
71         if (!CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) {
72             throw new CryptographicException("Unable to acquire Cryptographic Context.");
73         }
74         //Release the context when finished
75         scope(exit) CryptReleaseContext(hCryptoProv, 0);
76 
77         //Generate the random bytes
78         if (!CryptGenRandom(hCryptProv, cast(DWORD)buffer.length, buffer.ptr)) {
79             throw new CryptographicException(format("Cannot get the next random bytes. Error ID: %d", GetLastError()));
80         }
81     }
82     else
83     {
84         static assert(0, "SecureD does not support this OS.");
85     }
86 
87     return buffer;
88 }
89 
90 unittest
91 {
92     import std.digest;
93     import std.stdio;
94 
95     writeln("Testing Random Number Generator with 32/64/512/2048 bytes:");
96 
97     //Test 32 bytes
98     ubyte[] rnd1 = random(32);
99     writeln("32 Bytes:");
100     writeln(toHexString!(LetterCase.lower)(rnd1));
101     assert(rnd1.length == 32);
102 
103     //Test 128 bytes
104     ubyte[] rnd2 = random(128);
105     writeln("128 Bytes:");
106     writeln(toHexString!(LetterCase.lower)(rnd2));
107     assert(rnd2.length == 128);
108 
109     //Test 512 bytes
110     ubyte[] rnd3 = random(512);
111     writeln("512 Bytes:");
112     writeln(toHexString!(LetterCase.lower)(rnd3));
113     assert(rnd3.length == 512);
114 
115     //Test 2048 bytes
116     ubyte[] rnd4 = random(2048);
117     writeln("2048 Bytes:");
118     writeln(toHexString!(LetterCase.lower)(rnd4));
119     assert(rnd4.length == 2048);
120 }
121 
122 unittest
123 {
124     import std.digest;
125     import std.stdio;
126 
127     writeln("Testing Random Number Generator for Equality:");
128 
129     //Test 32 bytes
130     ubyte[] rnd1 = random(32);
131     ubyte[] rnd2 = random(32);
132     writeln("Testing with 32 Bytes");
133     assert(!constantTimeEquality(rnd1, rnd2));
134 
135     //Test 128 bytes
136     rnd1 = random(128);
137     rnd2 = random(128);
138     writeln("Testing with 128 Bytes");
139     assert(!constantTimeEquality(rnd1, rnd2));
140 
141     //Test 512 bytes
142     rnd1 = random(512);
143     rnd2 = random(512);
144     writeln("Testing with 512 Bytes");
145     assert(!constantTimeEquality(rnd1, rnd2));
146 
147     //Test 2048 bytes
148     rnd1 = random(2048);
149     rnd2 = random(2048);
150     writeln("Testing with 2048 Bytes");
151     assert(!constantTimeEquality(rnd1, rnd2));
152 }