|
21 | 21 | #include <sys/time.h>
|
22 | 22 | #endif
|
23 | 23 |
|
| 24 | +#ifdef HAVE_SYS_GETRANDOM |
| 25 | +#include <sys/syscall.h> |
| 26 | +#include <linux/random.h> |
| 27 | +#endif |
| 28 | +#ifdef HAVE_GETENTROPY |
| 29 | +#include <unistd.h> |
| 30 | +#endif |
| 31 | +#ifdef HAVE_SYSCTL_ARND |
| 32 | +#include <sys/sysctl.h> |
| 33 | +#endif |
| 34 | + |
24 | 35 | #include <openssl/err.h>
|
25 | 36 | #include <openssl/rand.h>
|
26 | 37 |
|
@@ -91,34 +102,86 @@ static void RandAddSeedPerfmon()
|
91 | 102 | #endif
|
92 | 103 | }
|
93 | 104 |
|
| 105 | +#ifndef WIN32 |
| 106 | +/** Fallback: get 32 bytes of system entropy from /dev/urandom. The most |
| 107 | + * compatible way to get cryptographic randomness on UNIX-ish platforms. |
| 108 | + */ |
| 109 | +void GetDevURandom(unsigned char *ent32) |
| 110 | +{ |
| 111 | + int f = open("/dev/urandom", O_RDONLY); |
| 112 | + if (f == -1) { |
| 113 | + RandFailure(); |
| 114 | + } |
| 115 | + int have = 0; |
| 116 | + do { |
| 117 | + ssize_t n = read(f, ent32 + have, NUM_OS_RANDOM_BYTES - have); |
| 118 | + if (n <= 0 || n + have > NUM_OS_RANDOM_BYTES) { |
| 119 | + RandFailure(); |
| 120 | + } |
| 121 | + have += n; |
| 122 | + } while (have < NUM_OS_RANDOM_BYTES); |
| 123 | + close(f); |
| 124 | +} |
| 125 | +#endif |
| 126 | + |
94 | 127 | /** Get 32 bytes of system entropy. */
|
95 |
| -static void GetOSRand(unsigned char *ent32) |
| 128 | +void GetOSRand(unsigned char *ent32) |
96 | 129 | {
|
97 |
| -#ifdef WIN32 |
| 130 | +#if defined(WIN32) |
98 | 131 | HCRYPTPROV hProvider;
|
99 | 132 | int ret = CryptAcquireContextW(&hProvider, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT);
|
100 | 133 | if (!ret) {
|
101 | 134 | RandFailure();
|
102 | 135 | }
|
103 |
| - ret = CryptGenRandom(hProvider, 32, ent32); |
| 136 | + ret = CryptGenRandom(hProvider, NUM_OS_RANDOM_BYTES, ent32); |
104 | 137 | if (!ret) {
|
105 | 138 | RandFailure();
|
106 | 139 | }
|
107 | 140 | CryptReleaseContext(hProvider, 0);
|
108 |
| -#else |
109 |
| - int f = open("/dev/urandom", O_RDONLY); |
110 |
| - if (f == -1) { |
| 141 | +#elif defined(HAVE_SYS_GETRANDOM) |
| 142 | + /* Linux. From the getrandom(2) man page: |
| 143 | + * "If the urandom source has been initialized, reads of up to 256 bytes |
| 144 | + * will always return as many bytes as requested and will not be |
| 145 | + * interrupted by signals." |
| 146 | + */ |
| 147 | + int rv = syscall(SYS_getrandom, ent32, NUM_OS_RANDOM_BYTES, 0); |
| 148 | + if (rv != NUM_OS_RANDOM_BYTES) { |
| 149 | + if (rv < 0 && errno == ENOSYS) { |
| 150 | + /* Fallback for kernel <3.17: the return value will be -1 and errno |
| 151 | + * ENOSYS if the syscall is not available, in that case fall back |
| 152 | + * to /dev/urandom. |
| 153 | + */ |
| 154 | + GetDevURandom(ent32); |
| 155 | + } else { |
| 156 | + RandFailure(); |
| 157 | + } |
| 158 | + } |
| 159 | +#elif defined(HAVE_GETENTROPY) |
| 160 | + /* On OpenBSD this can return up to 256 bytes of entropy, will return an |
| 161 | + * error if more are requested. |
| 162 | + * The call cannot return less than the requested number of bytes. |
| 163 | + */ |
| 164 | + if (getentropy(ent32, NUM_OS_RANDOM_BYTES) != 0) { |
111 | 165 | RandFailure();
|
112 | 166 | }
|
| 167 | +#elif defined(HAVE_SYSCTL_ARND) |
| 168 | + /* FreeBSD and similar. It is possible for the call to return less |
| 169 | + * bytes than requested, so need to read in a loop. |
| 170 | + */ |
| 171 | + static const int name[2] = {CTL_KERN, KERN_ARND}; |
113 | 172 | int have = 0;
|
114 | 173 | do {
|
115 |
| - ssize_t n = read(f, ent32 + have, 32 - have); |
116 |
| - if (n <= 0 || n + have > 32) { |
| 174 | + size_t len = NUM_OS_RANDOM_BYTES - have; |
| 175 | + if (sysctl(name, ARRAYLEN(name), ent32 + have, &len, NULL, 0) != 0) { |
117 | 176 | RandFailure();
|
118 | 177 | }
|
119 |
| - have += n; |
120 |
| - } while (have < 32); |
121 |
| - close(f); |
| 178 | + have += len; |
| 179 | + } while (have < NUM_OS_RANDOM_BYTES); |
| 180 | +#else |
| 181 | + /* Fall back to /dev/urandom if there is no specific method implemented to |
| 182 | + * get system entropy for this OS. |
| 183 | + */ |
| 184 | + GetDevURandom(ent32); |
122 | 185 | #endif
|
123 | 186 | }
|
124 | 187 |
|
@@ -195,3 +258,33 @@ FastRandomContext::FastRandomContext(bool fDeterministic)
|
195 | 258 | }
|
196 | 259 | }
|
197 | 260 |
|
| 261 | +bool Random_SanityCheck() |
| 262 | +{ |
| 263 | + /* This does not measure the quality of randomness, but it does test that |
| 264 | + * OSRandom() overwrites all 32 bytes of the output given a maximum |
| 265 | + * number of tries. |
| 266 | + */ |
| 267 | + static const ssize_t MAX_TRIES = 1024; |
| 268 | + uint8_t data[NUM_OS_RANDOM_BYTES]; |
| 269 | + bool overwritten[NUM_OS_RANDOM_BYTES] = {}; /* Tracks which bytes have been overwritten at least once */ |
| 270 | + int num_overwritten; |
| 271 | + int tries = 0; |
| 272 | + /* Loop until all bytes have been overwritten at least once, or max number tries reached */ |
| 273 | + do { |
| 274 | + memset(data, 0, NUM_OS_RANDOM_BYTES); |
| 275 | + GetOSRand(data); |
| 276 | + for (int x=0; x < NUM_OS_RANDOM_BYTES; ++x) { |
| 277 | + overwritten[x] |= (data[x] != 0); |
| 278 | + } |
| 279 | + |
| 280 | + num_overwritten = 0; |
| 281 | + for (int x=0; x < NUM_OS_RANDOM_BYTES; ++x) { |
| 282 | + if (overwritten[x]) { |
| 283 | + num_overwritten += 1; |
| 284 | + } |
| 285 | + } |
| 286 | + |
| 287 | + tries += 1; |
| 288 | + } while (num_overwritten < NUM_OS_RANDOM_BYTES && tries < MAX_TRIES); |
| 289 | + return (num_overwritten == NUM_OS_RANDOM_BYTES); /* If this failed, bailed out after too many tries */ |
| 290 | +} |
0 commit comments