1 module crypto.random; 2 3 import std.traits : isIntegral; 4 import std.random; 5 6 version (CRuntime_Bionic) version = SecureARC4Random; // ChaCha20 7 version (OSX) version = SecureARC4Random; // AES 8 version (OpenBSD) version = SecureARC4Random; // ChaCha20 9 version (NetBSD) version = SecureARC4Random; // ChaCha20 10 //version (FreeBSD) version = SecureARC4Random; // ARC4 before FreeBSD 12; ChaCha20 in FreeBSD 12. 11 12 version (SecureARC4Random) 13 { 14 private extern(C) uint arc4random() @nogc nothrow @safe; 15 private extern(C) uint arc4random_uniform(uint upperBound) @nogc nothrow @safe; 16 17 /++ Cryptographically secure source of random numbers. Not available on all platforms. +/ 18 struct ARC4RandomGenerator 19 { 20 /++ 21 Params: 22 min = min inclusive 23 max = max inclusive 24 Returns: `x` such that `min <= x <= max` 25 +/ 26 T next(T = uint)(T min = T.min, T max = T.max) if (isIntegral!T) 27 { 28 if (min == T.min && max == T.max) 29 return cast(T) arc4random(); 30 else 31 return cast(T) (min + arc4random_uniform(1U + max - min)); 32 } 33 } 34 35 __gshared ARC4RandomGenerator rnd; // Can be global because it has no mutable state. 36 } 37 else version (Windows) 38 { 39 /++ Cryptographically secure source of random numbers. Not available on all platforms. +/ 40 struct CryptGenRandomGenerator 41 { 42 import core.sys.windows.wincrypt; 43 44 __gshared private HCRYPTPROV hProvider; 45 46 private uint[32] buffer; 47 private uint pos = buffer.length; 48 49 static this() 50 { 51 if (!hProvider) 52 if (!CryptAcquireContextW(&hProvider, null, null, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) 53 if (!CryptAcquireContextA(&hProvider, null, null, PROV_RSA_FULL, CRYPT_NEWKEYSET | CRYPT_SILENT)) 54 throw new Error("CryptAcquireContext failed"); 55 } 56 57 /++ 58 Params: 59 min = min inclusive 60 max = max inclusive 61 Returns: `x` such that `min <= x <= max` 62 +/ 63 T next(T = uint)(T min = T.min, T max = T.max) if (isIntegral!T) 64 { 65 return uniform!("[]", T, T)(min, max, this); 66 } 67 68 // Stuff to make this work with std.random: 69 enum uint min = uint.min; 70 enum uint max = uint.max; 71 enum bool isUniformRandom = true; 72 enum bool empty = false; 73 74 uint front() @nogc nothrow @trusted 75 { 76 if (pos >= buffer.length) 77 { 78 while (!CryptGenRandom(hProvider, buffer.sizeof, cast(ubyte*) &buffer)) 79 { 80 // Repeat until successful. 81 } 82 pos = 0; 83 } 84 return buffer[pos]; 85 } 86 87 void popFront() @nogc nothrow @safe 88 { 89 pos++; 90 } 91 } 92 93 static CryptGenRandomGenerator rnd; // Thread-local because it has a mutable buffer. 94 } 95 else version (Posix) 96 { 97 version (linux) 98 { 99 // getentropy available on Linux 3.17 and later. 100 // http://www.man7.org/linux/man-pages/man3/getentropy.3.html 101 import core.sys.linux.dlfcn : dlsym, RTLD_DEFAULT; 102 private enum maybeHasGetEntropy = true; 103 } 104 else version (FreeBSD) 105 { 106 // getentropy available on FreeBSD 12 and later. 107 // https://www.freebsd.org/cgi/man.cgi?query=getentropy&sektion=3&manpath=FreeBSD+12.0-stable 108 import core.sys.freebsd.dlfcn : dlsym, RTLD_DEFAULT; 109 private enum maybeHasGetEntropy = true; 110 } 111 else version (Solaris) 112 { 113 // getentropy available on Solaris 11.3 and later. 114 // https://docs.oracle.com/cd/E86824_01/html/E54765/getentropy-2.html#REFMAN2getentropy-2 115 import core.sys.solaris.dlfcn : dlsym, RTLD_DEFAULT; 116 private enum maybeHasGetEntropy = true; 117 } 118 else 119 { 120 private enum maybeHasGetEntropy = false; 121 } 122 123 /++ Cryptographically secure source of random numbers. Not available on all platforms. +/ 124 struct PosixRandomGenerator 125 { 126 private uint[32] buffer; // Fill using `getentropy` if possible. Otherwise fill using /dev/urandom. 127 private uint pos = buffer.length; 128 129 static if (maybeHasGetEntropy) 130 { 131 private alias GetEntropyFn = extern(C) int function(scope void*, size_t) @nogc nothrow @system; 132 private __gshared GetEntropyFn getentropy; // If unavailable, null. 133 shared static this() 134 { 135 getentropy = cast(GetEntropyFn) dlsym(RTLD_DEFAULT, "getentropy"); 136 } 137 static assert(buffer.sizeof <= 256, "buffer must be no more than 256 bytes for use with getentropy"); 138 } 139 140 /++ 141 Params: 142 min = min inclusive 143 max = max inclusive 144 Returns: `x` such that `min <= x <= max` 145 +/ 146 T next(T = uint)(T min = T.min, T max = T.max) if (isIntegral!T) 147 { 148 return uniform!("[]", T, T)(min, max, this); 149 } 150 151 // Stuff to make this work with std.random: 152 enum uint min = uint.min; 153 enum uint max = uint.max; 154 enum bool isUniformRandom = true; 155 enum bool empty = false; 156 157 uint front() @trusted 158 { 159 if (pos >= buffer.length) 160 { 161 static if (maybeHasGetEntropy) 162 { 163 if (getentropy is null || 0 != getentropy(buffer.ptr, buffer.sizeof)) 164 fillBufferFromDevUrandom(); 165 } 166 else 167 { 168 fillBufferFromDevUrandom(); 169 } 170 pos = 0; 171 } 172 return buffer[pos]; 173 } 174 175 void popFront() @nogc nothrow @safe 176 { 177 pos++; 178 } 179 180 private void fillBufferFromDevUrandom() 181 { 182 import core.stdc.errno : errno, EAGAIN, EINTR, EWOULDBLOCK; 183 import core.sys.posix.fcntl : open, O_RDONLY; 184 import core.sys.posix.unistd : close, read; 185 186 static if (maybeHasGetEntropy) 187 pragma(inline, false); 188 189 // Open file descriptor. 190 int fd; 191 while (-1 == (fd = open("/dev/urandom", O_RDONLY))) 192 { 193 import std.exception : ErrnoException; 194 if (errno != EAGAIN && errno != EINTR) 195 throw new ErrnoException("Error opening /dev/urandom"); 196 } 197 scope(exit) close(fd); 198 199 // Read from /dev/urandom. 200 ubyte* ptr = cast(ubyte*) buffer.ptr; 201 size_t remaining = buffer.sizeof; 202 do { 203 const nread = read(fd, ptr, remaining); 204 if (nread >= 0) 205 { 206 remaining -= nread; 207 } 208 else 209 { 210 import std.exception : ErrnoException; 211 if (errno != EAGAIN && errno != EINTR && errno != EWOULDBLOCK) 212 throw new ErrnoException("Error reading from /dev/urandom"); 213 } 214 } while (remaining > 0); 215 } 216 } 217 218 static PosixRandomGenerator rnd; // Thread-local because it has a mutable buffer. 219 } 220 else 221 { 222 /++ Fast but cryptographically insecure source of random numbers. +/ 223 struct InsecureRandomGenerator 224 { 225 private static Mt19937 generator; 226 227 static this() 228 { 229 generator.seed(unpredictableSeed); 230 } 231 232 /++ 233 Params: 234 min = min inclusive 235 max = max inclusive 236 Returns: `x` such that `min <= x <= max` 237 +/ 238 T next(T = uint)(T min = T.min, T max = T.max) if (isIntegral!T) 239 { 240 return uniform!("[]", T, T, typeof(generator))(min, max, generator); 241 } 242 } 243 244 __gshared InsecureRandomGenerator rnd; // Can be global because it has no mutable state. 245 }