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 17 #define DEBUG_SEED(s) 18 19 #if defined ENABLE_RDRAND 20 21 /* cpuid */ 22 23 #if defined __GNUC__ && (defined __i386__ || defined __x86_64__) 24 #define HAS_X86_CPUID 1 25 26 static void do_cpuid(int regs[], int h) 27 { 28 /* clang-format off */ 29 __asm__ __volatile__("cpuid" 30 : "=a"(regs[0]), "=b"(regs[1]), "=c"(regs[2]), "=d"(regs[3]) 31 : "a"(h)); 32 /* clang-format on */ 33 } 34 35 #elif defined _MSC_VER 36 37 #define HAS_X86_CPUID 1 38 #define do_cpuid __cpuid 39 40 #endif 41 42 /* has_rdrand */ 43 44 #if HAS_X86_CPUID 45 46 static int get_rdrand_seed(void); 47 48 /* Valid values are -1 (haven't tested), 0 (no), and 1 (yes). */ 49 static int _has_rdrand = -1; 50 51 static int has_rdrand(void) 52 { 53 if (_has_rdrand != -1) 54 { 55 return _has_rdrand; 56 } 57 58 /* CPUID.01H:ECX.RDRAND[bit 30] == 1 */ 59 int regs[4]; 60 do_cpuid(regs, 1); 61 if (!(regs[2] & (1 << 30))) 62 { 63 _has_rdrand = 0; 64 return 0; 65 } 66 67 /* 68 * Some CPUs advertise RDRAND in CPUID, but return 0xFFFFFFFF 69 * unconditionally. To avoid locking up later, test RDRAND here. If over 70 * 3 trials RDRAND has returned the same value, declare it broken. 71 * Example CPUs are AMD Ryzen 3000 series 72 * and much older AMD APUs, such as the E1-1500 73 * https://github.com/systemd/systemd/issues/11810 74 * https://linuxreviews.org/RDRAND_stops_returning_random_values_on_older_AMD_CPUs_after_suspend 75 */ 76 _has_rdrand = 0; 77 int prev = get_rdrand_seed(); 78 for (int i = 0; i < 3; i++) 79 { 80 int temp = get_rdrand_seed(); 81 if (temp != prev) 82 { 83 _has_rdrand = 1; 84 break; 85 } 86 87 prev = temp; 88 } 89 90 return _has_rdrand; 91 } 92 93 #endif 94 95 /* get_rdrand_seed - GCC x86 and X64 */ 96 97 #if defined __GNUC__ && (defined __i386__ || defined __x86_64__) 98 99 #define HAVE_RDRAND 1 100 101 static int get_rdrand_seed(void) 102 { 103 DEBUG_SEED("get_rdrand_seed"); 104 int _eax; 105 /* rdrand eax */ 106 /* clang-format off */ 107 __asm__ __volatile__("1: .byte 0x0F\n" 108 " .byte 0xC7\n" 109 " .byte 0xF0\n" 110 " jnc 1b;\n" 111 : "=a" (_eax)); 112 /* clang-format on */ 113 return _eax; 114 } 115 116 #endif 117 118 #if defined _MSC_VER 119 120 #if _MSC_VER >= 1700 121 #define HAVE_RDRAND 1 122 123 /* get_rdrand_seed - Visual Studio 2012 and above */ 124 125 static int get_rdrand_seed(void) 126 { 127 DEBUG_SEED("get_rdrand_seed"); 128 int r; 129 while (_rdrand32_step(&r) == 0) 130 ; 131 return r; 132 } 133 134 #elif defined _M_IX86 135 #define HAVE_RDRAND 1 136 137 /* get_rdrand_seed - Visual Studio 2010 and below - x86 only */ 138 139 /* clang-format off */ 140 static int get_rdrand_seed(void) 141 { 142 DEBUG_SEED("get_rdrand_seed"); 143 int _eax; 144 retry: 145 /* rdrand eax */ 146 __asm _emit 0x0F __asm _emit 0xC7 __asm _emit 0xF0 147 __asm jnc retry 148 __asm mov _eax, eax 149 return _eax; 150 } 151 /* clang-format on */ 152 153 #endif 154 #endif 155 156 #endif /* defined ENABLE_RDRAND */ 157 158 /* has_dev_urandom */ 159 160 #if defined(__APPLE__) || defined(__unix__) || defined(__linux__) 161 162 #include <fcntl.h> 163 #include <string.h> 164 #if HAVE_UNISTD_H 165 #include <unistd.h> 166 #endif /* HAVE_UNISTD_H */ 167 #include <stdlib.h> 168 #include <sys/stat.h> 169 170 #define HAVE_DEV_RANDOM 1 171 172 static const char *dev_random_file = "/dev/urandom"; 173 174 static int has_dev_urandom(void) 175 { 176 struct stat buf; 177 if (stat(dev_random_file, &buf)) 178 { 179 return 0; 180 } 181 return ((buf.st_mode & S_IFCHR) != 0); 182 } 183 184 /* get_dev_random_seed */ 185 186 static int get_dev_random_seed(void) 187 { 188 DEBUG_SEED("get_dev_random_seed"); 189 190 int fd = open(dev_random_file, O_RDONLY); 191 if (fd < 0) 192 { 193 fprintf(stderr, "error opening %s: %s", dev_random_file, strerror(errno)); 194 exit(1); 195 } 196 197 int r; 198 ssize_t nread = read(fd, &r, sizeof(r)); 199 if (nread != sizeof(r)) 200 { 201 fprintf(stderr, "error short read %s: %s", dev_random_file, strerror(errno)); 202 exit(1); 203 } 204 205 close(fd); 206 return r; 207 } 208 209 #endif 210 211 /* get_cryptgenrandom_seed */ 212 213 #ifdef WIN32 214 215 #define HAVE_CRYPTGENRANDOM 1 216 217 /* clang-format off */ 218 #include <windows.h> 219 220 /* Caution: these blank lines must remain so clang-format doesn't reorder 221 includes to put windows.h after wincrypt.h */ 222 223 #include <wincrypt.h> 224 /* clang-format on */ 225 #ifndef __GNUC__ 226 #pragma comment(lib, "advapi32.lib") 227 #endif 228 229 static int get_time_seed(void); 230 231 static int get_cryptgenrandom_seed(void) 232 { 233 HCRYPTPROV hProvider = 0; 234 DWORD dwFlags = CRYPT_VERIFYCONTEXT; 235 int r; 236 237 DEBUG_SEED("get_cryptgenrandom_seed"); 238 239 /* WinNT 4 and Win98 do no support CRYPT_SILENT */ 240 if (LOBYTE(LOWORD(GetVersion())) > 4) 241 dwFlags |= CRYPT_SILENT; 242 243 if (!CryptAcquireContextA(&hProvider, 0, 0, PROV_RSA_FULL, dwFlags)) 244 { 245 fprintf(stderr, "error CryptAcquireContextA 0x%08lx", GetLastError()); 246 r = get_time_seed(); 247 } 248 else 249 { 250 BOOL ret = CryptGenRandom(hProvider, sizeof(r), (BYTE*)&r); 251 CryptReleaseContext(hProvider, 0); 252 if (!ret) 253 { 254 fprintf(stderr, "error CryptGenRandom 0x%08lx", GetLastError()); 255 r = get_time_seed(); 256 } 257 } 258 259 return r; 260 } 261 262 #endif 263 264 /* get_time_seed */ 265 266 #include <time.h> 267 268 static int get_time_seed(void) 269 { 270 DEBUG_SEED("get_time_seed"); 271 272 return (int)time(NULL) * 433494437; 273 } 274 275 /* json_c_get_random_seed */ 276 277 int json_c_get_random_seed(void) 278 { 279 #ifdef OVERRIDE_GET_RANDOM_SEED 280 OVERRIDE_GET_RANDOM_SEED; 281 #endif 282 #if defined HAVE_RDRAND && HAVE_RDRAND 283 if (has_rdrand()) 284 return get_rdrand_seed(); 285 #endif 286 #if defined HAVE_DEV_RANDOM && HAVE_DEV_RANDOM 287 if (has_dev_urandom()) 288 return get_dev_random_seed(); 289 #endif 290 #if defined HAVE_CRYPTGENRANDOM && HAVE_CRYPTGENRANDOM 291 return get_cryptgenrandom_seed(); 292 #endif 293 return get_time_seed(); 294 } 295
This page was automatically generated by LXR 0.3.1. • OpenWrt