1 /* 2 * Copyright (c) 2017 Thomas Pornin <pornin@bolet.org> 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining 5 * a copy of this software and associated documentation files (the 6 * "Software"), to deal in the Software without restriction, including 7 * without limitation the rights to use, copy, modify, merge, publish, 8 * distribute, sublicense, and/or sell copies of the Software, and to 9 * permit persons to whom the Software is furnished to do so, subject to 10 * the following conditions: 11 * 12 * The above copyright notice and this permission notice shall be 13 * included in all copies or substantial portions of the Software. 14 * 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 19 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 20 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 21 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 * SOFTWARE. 23 */ 24 25 #define BR_ENABLE_INTRINSICS 1 26 #include "inner.h" 27 28 #if BR_USE_GETENTROPY 29 #include <unistd.h> 30 #endif 31 32 #if BR_USE_URANDOM 33 #include <sys/types.h> 34 #include <unistd.h> 35 #include <fcntl.h> 36 #include <errno.h> 37 #endif 38 39 #if BR_USE_WIN32_RAND 40 #include <windows.h> 41 #include <wincrypt.h> 42 #pragma comment(lib, "advapi32") 43 #endif 44 45 /* 46 * Seeder that uses the RDRAND opcodes (on x86 CPU). 47 */ 48 #if BR_RDRAND 49 BR_TARGETS_X86_UP 50 BR_TARGET("rdrnd") 51 static int 52 seeder_rdrand(const br_prng_class **ctx) 53 { 54 unsigned char tmp[32]; 55 size_t u; 56 57 for (u = 0; u < sizeof tmp; u += sizeof(uint32_t)) { 58 int j; 59 uint32_t x; 60 61 /* 62 * We use the 32-bit intrinsic so that code is compatible 63 * with both 32-bit and 64-bit architectures. 64 * 65 * Intel recommends trying at least 10 times in case of 66 * failure. 67 * 68 * AMD bug: there are reports that some AMD processors 69 * have a bug that makes them fail silently after a 70 * suspend/resume cycle, in which case RDRAND will report 71 * a success but always return 0xFFFFFFFF. 72 * see: https://bugzilla.kernel.org/show_bug.cgi?id=85911 73 * 74 * As a mitigation, if the 32-bit value is 0 or -1, then 75 * it is considered a failure and tried again. This should 76 * reliably detect the buggy case, at least. This also 77 * implies that the selected seed values can never be 78 * 0x00000000 or 0xFFFFFFFF, which is not a problem since 79 * we are generating a seed for a PRNG, and we overdo it 80 * a bit (we generate 32 bytes of randomness, and 256 bits 81 * of entropy are really overkill). 82 */ 83 for (j = 0; j < 10; j ++) { 84 if (_rdrand32_step(&x) && x != 0 && x != (uint32_t)-1) { 85 goto next_word; 86 } 87 } 88 return 0; 89 next_word: 90 br_enc32le(tmp + u, x); 91 } 92 (*ctx)->update(ctx, tmp, sizeof tmp); 93 return 1; 94 } 95 BR_TARGETS_X86_DOWN 96 97 static int 98 rdrand_supported(void) 99 { 100 /* 101 * The RDRND support is bit 30 of ECX, as returned by CPUID. 102 */ 103 return br_cpuid(0, 0, 0x40000000, 0); 104 } 105 #endif 106 107 /* 108 * Seeder that uses /dev/urandom (on Unix-like systems). 109 */ 110 #if BR_USE_URANDOM 111 static int 112 seeder_urandom(const br_prng_class **ctx) 113 { 114 int f; 115 116 f = open("/dev/urandom", O_RDONLY); 117 if (f >= 0) { 118 unsigned char tmp[32]; 119 size_t u; 120 121 for (u = 0; u < sizeof tmp;) { 122 ssize_t len; 123 124 len = read(f, tmp + u, (sizeof tmp) - u); 125 if (len < 0) { 126 if (errno == EINTR) { 127 continue; 128 } 129 break; 130 } 131 u += (size_t)len; 132 } 133 close(f); 134 if (u == sizeof tmp) { 135 (*ctx)->update(ctx, tmp, sizeof tmp); 136 return 1; 137 } 138 } 139 return 0; 140 } 141 #endif 142 143 /* 144 * Seeder that uses getentropy() (backed by getrandom() on some systems, 145 * e.g. Linux). On failure, it will use the /dev/urandom seeder (if 146 * enabled). 147 */ 148 #if BR_USE_GETENTROPY 149 static int 150 seeder_getentropy(const br_prng_class **ctx) 151 { 152 unsigned char tmp[32]; 153 154 if (getentropy(tmp, sizeof tmp) == 0) { 155 (*ctx)->update(ctx, tmp, sizeof tmp); 156 return 1; 157 } 158 #if BR_USE_URANDOM 159 return seeder_urandom(ctx); 160 #else 161 return 0; 162 #endif 163 } 164 #endif 165 166 /* 167 * Seeder that uses CryptGenRandom() (on Windows). 168 */ 169 #if BR_USE_WIN32_RAND 170 static int 171 seeder_win32(const br_prng_class **ctx) 172 { 173 HCRYPTPROV hp; 174 175 if (CryptAcquireContext(&hp, 0, 0, PROV_RSA_FULL, 176 CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) 177 { 178 BYTE buf[32]; 179 BOOL r; 180 181 r = CryptGenRandom(hp, sizeof buf, buf); 182 CryptReleaseContext(hp, 0); 183 if (r) { 184 (*ctx)->update(ctx, buf, sizeof buf); 185 return 1; 186 } 187 } 188 return 0; 189 } 190 #endif 191 192 /* 193 * An aggregate seeder that uses RDRAND, and falls back to an OS-provided 194 * source if RDRAND fails. 195 */ 196 #if BR_RDRAND && (BR_USE_GETENTROPY || BR_USE_URANDOM || BR_USE_WIN32_RAND) 197 static int 198 seeder_rdrand_with_fallback(const br_prng_class **ctx) 199 { 200 if (!seeder_rdrand(ctx)) { 201 #if BR_USE_GETENTROPY 202 return seeder_getentropy(ctx); 203 #elif BR_USE_URANDOM 204 return seeder_urandom(ctx); 205 #elif BR_USE_WIN32_RAND 206 return seeder_win32(ctx); 207 #else 208 #error "macro selection has gone wrong" 209 #endif 210 } 211 return 1; 212 } 213 #endif 214 215 /* see bearssl_rand.h */ 216 br_prng_seeder 217 br_prng_seeder_system(const char **name) 218 { 219 #if BR_RDRAND 220 if (rdrand_supported()) { 221 if (name != NULL) { 222 *name = "rdrand"; 223 } 224 #if BR_USE_GETENTROPY || BR_USE_URANDOM || BR_USE_WIN32_RAND 225 return &seeder_rdrand_with_fallback; 226 #else 227 return &seeder_rdrand; 228 #endif 229 } 230 #endif 231 #if BR_USE_GETENTROPY 232 if (name != NULL) { 233 *name = "getentropy"; 234 } 235 return &seeder_getentropy; 236 #elif BR_USE_URANDOM 237 if (name != NULL) { 238 *name = "urandom"; 239 } 240 return &seeder_urandom; 241 #elif BR_USE_WIN32_RAND 242 if (name != NULL) { 243 *name = "win32"; 244 } 245 return &seeder_win32; 246 #else 247 if (name != NULL) { 248 *name = "none"; 249 } 250 return 0; 251 #endif 252 } 253