w.deng | e87b500 | 2025-08-20 10:43:03 +0800 | [diff] [blame] | 1 | /* |
| 2 | * random_seed.c |
| 3 | * |
| 4 | * Copyright (c) 2013 Metaparadigm Pte. Ltd. |
| 5 | * Michael Clark <michael@metaparadigm.com> |
| 6 | * |
| 7 | * This library is free software; you can redistribute it and/or modify |
| 8 | * it under the terms of the MIT license. See COPYING for details. |
| 9 | * |
| 10 | */ |
| 11 | |
| 12 | #include "random_seed.h" |
| 13 | #include "config.h" |
| 14 | #include "strerror_override.h" |
| 15 | #include <stdio.h> |
| 16 | #include <stdlib.h> |
| 17 | #ifdef HAVE_BSD_STDLIB_H |
| 18 | #include <bsd/stdlib.h> |
| 19 | #endif |
| 20 | |
| 21 | #define DEBUG_SEED(s) |
| 22 | |
| 23 | #if defined(__APPLE__) || defined(__unix__) || defined(__linux__) |
| 24 | #define HAVE_DEV_RANDOM 1 |
| 25 | #endif |
| 26 | |
| 27 | #ifdef HAVE_ARC4RANDOM |
| 28 | #undef HAVE_GETRANDOM |
| 29 | #undef HAVE_DEV_RANDOM |
| 30 | #undef HAVE_CRYPTGENRANDOM |
| 31 | #endif |
| 32 | |
| 33 | #if defined ENABLE_RDRAND |
| 34 | |
| 35 | /* cpuid */ |
| 36 | |
| 37 | #if defined __GNUC__ && (defined __i386__ || defined __x86_64__) |
| 38 | #define HAS_X86_CPUID 1 |
| 39 | |
| 40 | static void do_cpuid(int regs[], int h) |
| 41 | { |
| 42 | /* clang-format off */ |
| 43 | __asm__ __volatile__("cpuid" |
| 44 | : "=a"(regs[0]), "=b"(regs[1]), "=c"(regs[2]), "=d"(regs[3]) |
| 45 | : "a"(h)); |
| 46 | /* clang-format on */ |
| 47 | } |
| 48 | |
| 49 | #elif defined _MSC_VER |
| 50 | |
| 51 | #define HAS_X86_CPUID 1 |
| 52 | #define do_cpuid __cpuid |
| 53 | |
| 54 | #endif |
| 55 | |
| 56 | /* has_rdrand */ |
| 57 | |
| 58 | #if HAS_X86_CPUID |
| 59 | |
| 60 | static int get_rdrand_seed(void); |
| 61 | |
| 62 | /* Valid values are -1 (haven't tested), 0 (no), and 1 (yes). */ |
| 63 | static int _has_rdrand = -1; |
| 64 | |
| 65 | static int has_rdrand(void) |
| 66 | { |
| 67 | if (_has_rdrand != -1) |
| 68 | { |
| 69 | return _has_rdrand; |
| 70 | } |
| 71 | |
| 72 | /* CPUID.01H:ECX.RDRAND[bit 30] == 1 */ |
| 73 | int regs[4]; |
| 74 | do_cpuid(regs, 1); |
| 75 | if (!(regs[2] & (1 << 30))) |
| 76 | { |
| 77 | _has_rdrand = 0; |
| 78 | return 0; |
| 79 | } |
| 80 | |
| 81 | /* |
| 82 | * Some CPUs advertise RDRAND in CPUID, but return 0xFFFFFFFF |
| 83 | * unconditionally. To avoid locking up later, test RDRAND here. If over |
| 84 | * 3 trials RDRAND has returned the same value, declare it broken. |
| 85 | * Example CPUs are AMD Ryzen 3000 series |
| 86 | * and much older AMD APUs, such as the E1-1500 |
| 87 | * https://github.com/systemd/systemd/issues/11810 |
| 88 | * https://linuxreviews.org/RDRAND_stops_returning_random_values_on_older_AMD_CPUs_after_suspend |
| 89 | */ |
| 90 | _has_rdrand = 0; |
| 91 | int prev = get_rdrand_seed(); |
| 92 | for (int i = 0; i < 3; i++) |
| 93 | { |
| 94 | int temp = get_rdrand_seed(); |
| 95 | if (temp != prev) |
| 96 | { |
| 97 | _has_rdrand = 1; |
| 98 | break; |
| 99 | } |
| 100 | |
| 101 | prev = temp; |
| 102 | } |
| 103 | |
| 104 | return _has_rdrand; |
| 105 | } |
| 106 | |
| 107 | #endif |
| 108 | |
| 109 | /* get_rdrand_seed - GCC x86 and X64 */ |
| 110 | |
| 111 | #if defined __GNUC__ && (defined __i386__ || defined __x86_64__) |
| 112 | |
| 113 | #define HAVE_RDRAND 1 |
| 114 | |
| 115 | static int get_rdrand_seed(void) |
| 116 | { |
| 117 | DEBUG_SEED("get_rdrand_seed"); |
| 118 | int _eax; |
| 119 | /* rdrand eax */ |
| 120 | /* clang-format off */ |
| 121 | __asm__ __volatile__("1: .byte 0x0F\n" |
| 122 | " .byte 0xC7\n" |
| 123 | " .byte 0xF0\n" |
| 124 | " jnc 1b;\n" |
| 125 | : "=a" (_eax)); |
| 126 | /* clang-format on */ |
| 127 | return _eax; |
| 128 | } |
| 129 | |
| 130 | #endif |
| 131 | |
| 132 | #if defined _MSC_VER |
| 133 | |
| 134 | #if _MSC_VER >= 1700 |
| 135 | #define HAVE_RDRAND 1 |
| 136 | |
| 137 | /* get_rdrand_seed - Visual Studio 2012 and above */ |
| 138 | |
| 139 | static int get_rdrand_seed(void) |
| 140 | { |
| 141 | DEBUG_SEED("get_rdrand_seed"); |
| 142 | int r; |
| 143 | while (_rdrand32_step(&r) == 0) |
| 144 | ; |
| 145 | return r; |
| 146 | } |
| 147 | |
| 148 | #elif defined _M_IX86 |
| 149 | #define HAVE_RDRAND 1 |
| 150 | |
| 151 | /* get_rdrand_seed - Visual Studio 2010 and below - x86 only */ |
| 152 | |
| 153 | /* clang-format off */ |
| 154 | static int get_rdrand_seed(void) |
| 155 | { |
| 156 | DEBUG_SEED("get_rdrand_seed"); |
| 157 | int _eax; |
| 158 | retry: |
| 159 | /* rdrand eax */ |
| 160 | __asm _emit 0x0F __asm _emit 0xC7 __asm _emit 0xF0 |
| 161 | __asm jnc retry |
| 162 | __asm mov _eax, eax |
| 163 | return _eax; |
| 164 | } |
| 165 | /* clang-format on */ |
| 166 | |
| 167 | #endif |
| 168 | #endif |
| 169 | |
| 170 | #endif /* defined ENABLE_RDRAND */ |
| 171 | |
| 172 | #ifdef HAVE_GETRANDOM |
| 173 | |
| 174 | #include <stdlib.h> |
| 175 | #ifdef HAVE_SYS_RANDOM_H |
| 176 | #include <sys/random.h> |
| 177 | #endif |
| 178 | |
| 179 | static int get_getrandom_seed(int *seed) |
| 180 | { |
| 181 | DEBUG_SEED("get_getrandom_seed"); |
| 182 | |
| 183 | ssize_t ret; |
| 184 | |
| 185 | do |
| 186 | { |
| 187 | ret = getrandom(seed, sizeof(*seed), GRND_NONBLOCK); |
| 188 | } while ((ret == -1) && (errno == EINTR)); |
| 189 | |
| 190 | if (ret == -1) |
| 191 | { |
| 192 | if (errno == ENOSYS) /* syscall not available in kernel */ |
| 193 | return -1; |
| 194 | if (errno == EAGAIN) /* entropy not yet initialized */ |
| 195 | return -1; |
| 196 | |
| 197 | fprintf(stderr, "error from getrandom(): %s", strerror(errno)); |
| 198 | return -1; |
| 199 | } |
| 200 | |
| 201 | if (ret != sizeof(*seed)) |
| 202 | return -1; |
| 203 | |
| 204 | return 0; |
| 205 | } |
| 206 | #endif /* defined HAVE_GETRANDOM */ |
| 207 | |
| 208 | /* get_dev_random_seed */ |
| 209 | |
| 210 | #ifdef HAVE_DEV_RANDOM |
| 211 | |
| 212 | #include <fcntl.h> |
| 213 | #include <string.h> |
| 214 | #if HAVE_UNISTD_H |
| 215 | #include <unistd.h> |
| 216 | #endif /* HAVE_UNISTD_H */ |
| 217 | #include <stdlib.h> |
| 218 | #include <sys/stat.h> |
| 219 | |
| 220 | static const char *dev_random_file = "/dev/urandom"; |
| 221 | |
| 222 | static int get_dev_random_seed(int *seed) |
| 223 | { |
| 224 | DEBUG_SEED("get_dev_random_seed"); |
| 225 | |
| 226 | struct stat buf; |
| 227 | if (stat(dev_random_file, &buf)) |
| 228 | return -1; |
| 229 | if ((buf.st_mode & S_IFCHR) == 0) |
| 230 | return -1; |
| 231 | |
| 232 | int fd = open(dev_random_file, O_RDONLY); |
| 233 | if (fd < 0) |
| 234 | { |
| 235 | fprintf(stderr, "error opening %s: %s", dev_random_file, strerror(errno)); |
| 236 | return -1; |
| 237 | } |
| 238 | |
| 239 | ssize_t nread = read(fd, seed, sizeof(*seed)); |
| 240 | |
| 241 | close(fd); |
| 242 | |
| 243 | if (nread != sizeof(*seed)) |
| 244 | { |
| 245 | fprintf(stderr, "error short read %s: %s", dev_random_file, strerror(errno)); |
| 246 | return -1; |
| 247 | } |
| 248 | |
| 249 | return 0; |
| 250 | } |
| 251 | |
| 252 | #endif |
| 253 | |
| 254 | /* get_cryptgenrandom_seed */ |
| 255 | |
| 256 | #ifdef WIN32 |
| 257 | |
| 258 | #define HAVE_CRYPTGENRANDOM 1 |
| 259 | |
| 260 | /* clang-format off */ |
| 261 | #include <windows.h> |
| 262 | |
| 263 | /* Caution: these blank lines must remain so clang-format doesn't reorder |
| 264 | includes to put windows.h after wincrypt.h */ |
| 265 | |
| 266 | #include <wincrypt.h> |
| 267 | /* clang-format on */ |
| 268 | #ifndef __GNUC__ |
| 269 | #pragma comment(lib, "advapi32.lib") |
| 270 | #endif |
| 271 | |
| 272 | static int get_cryptgenrandom_seed(int *seed) |
| 273 | { |
| 274 | HCRYPTPROV hProvider = 0; |
| 275 | DWORD dwFlags = CRYPT_VERIFYCONTEXT; |
| 276 | |
| 277 | DEBUG_SEED("get_cryptgenrandom_seed"); |
| 278 | |
| 279 | /* WinNT 4 and Win98 do no support CRYPT_SILENT */ |
| 280 | if (LOBYTE(LOWORD(GetVersion())) > 4) |
| 281 | dwFlags |= CRYPT_SILENT; |
| 282 | |
| 283 | if (!CryptAcquireContextA(&hProvider, 0, 0, PROV_RSA_FULL, dwFlags)) |
| 284 | { |
| 285 | fprintf(stderr, "error CryptAcquireContextA 0x%08lx", GetLastError()); |
| 286 | return -1; |
| 287 | } |
| 288 | else |
| 289 | { |
| 290 | BOOL ret = CryptGenRandom(hProvider, sizeof(*seed), (BYTE *)seed); |
| 291 | CryptReleaseContext(hProvider, 0); |
| 292 | if (!ret) |
| 293 | { |
| 294 | fprintf(stderr, "error CryptGenRandom 0x%08lx", GetLastError()); |
| 295 | return -1; |
| 296 | } |
| 297 | } |
| 298 | |
| 299 | return 0; |
| 300 | } |
| 301 | |
| 302 | #endif |
| 303 | |
| 304 | /* get_time_seed */ |
| 305 | |
| 306 | #ifndef HAVE_ARC4RANDOM |
| 307 | #include <time.h> |
| 308 | |
| 309 | static int get_time_seed(void) |
| 310 | { |
| 311 | DEBUG_SEED("get_time_seed"); |
| 312 | |
| 313 | /* coverity[store_truncates_time_t] */ |
| 314 | return (unsigned)time(NULL) * 433494437; |
| 315 | } |
| 316 | #endif |
| 317 | |
| 318 | /* json_c_get_random_seed */ |
| 319 | |
| 320 | int json_c_get_random_seed(void) |
| 321 | { |
| 322 | #ifdef OVERRIDE_GET_RANDOM_SEED |
| 323 | OVERRIDE_GET_RANDOM_SEED; |
| 324 | #endif |
| 325 | #if defined HAVE_RDRAND && HAVE_RDRAND |
| 326 | if (has_rdrand()) |
| 327 | return get_rdrand_seed(); |
| 328 | #endif |
| 329 | #ifdef HAVE_ARC4RANDOM |
| 330 | /* arc4random never fails, so use it if it's available */ |
| 331 | return arc4random(); |
| 332 | #else |
| 333 | #ifdef HAVE_GETRANDOM |
| 334 | { |
| 335 | int seed = 0; |
| 336 | if (get_getrandom_seed(&seed) == 0) |
| 337 | return seed; |
| 338 | } |
| 339 | #endif |
| 340 | #if defined HAVE_DEV_RANDOM && HAVE_DEV_RANDOM |
| 341 | { |
| 342 | int seed = 0; |
| 343 | if (get_dev_random_seed(&seed) == 0) |
| 344 | return seed; |
| 345 | } |
| 346 | #endif |
| 347 | #if defined HAVE_CRYPTGENRANDOM && HAVE_CRYPTGENRANDOM |
| 348 | { |
| 349 | int seed = 0; |
| 350 | if (get_cryptgenrandom_seed(&seed) == 0) |
| 351 | return seed; |
| 352 | } |
| 353 | #endif |
| 354 | return get_time_seed(); |
| 355 | #endif /* !HAVE_ARC4RANDOM */ |
| 356 | } |