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 }