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 }