1 /* 2 * Generate uint.sizeof bytes of as random data as possible to seed 3 * the hash function. 4 */ 5 /** 6 * License: MIT 7 */ 8 module jansson_d.hashtable_seed; 9 10 11 package: 12 13 private static import core.stdc.config; 14 private static import core.stdc.stdio; 15 private static import core.stdc.time; 16 private static import core.sys.posix.fcntl; 17 private static import core.sys.posix.sched; 18 private static import core.sys.posix.sys.time; 19 private static import core.sys.posix.unistd; 20 private static import core.sys.posix.unistd; 21 private static import core.sys.windows.basetsd; 22 private static import core.sys.windows.winbase; 23 private static import core.sys.windows.wincrypt; 24 private static import core.sys.windows.windef; 25 private static import core.sys.windows.winnt; 26 27 pure nothrow @trusted @nogc @live 28 private uint buf_to_uint32(scope const char* data) 29 30 in 31 { 32 assert(data != null); 33 } 34 35 do 36 { 37 uint result = 0; 38 39 for (size_t i = 0; i < uint.sizeof; i++) { 40 result = (result << 8) | cast(ubyte)(data[i]); 41 } 42 43 return result; 44 } 45 46 /* /dev/urandom */ 47 version (Posix) 48 nothrow @nogc @live 49 private int seed_from_urandom(scope uint* seed) 50 51 in 52 { 53 assert(seed != null); 54 } 55 56 do 57 { 58 /* 59 * Use unbuffered I/O if we have open(), close() and read(). Otherwise 60 * fall back to fopen() 61 */ 62 63 char[uint.sizeof] data = void; 64 int ok = void; 65 66 static if ((__traits(compiles, core.sys.posix.fcntl.open)) && (__traits(compiles, core.sys.posix.unistd.read)) && (__traits(compiles, core.sys.posix.unistd.close))) { 67 int urandom = core.sys.posix.fcntl.open("/dev/urandom", core.sys.posix.fcntl.O_RDONLY); 68 69 if (urandom == -1) { 70 return 1; 71 } 72 73 ok = core.sys.posix.unistd.read(urandom, &(data[0]), uint.sizeof) == uint.sizeof; 74 core.sys.posix.unistd.close(urandom); 75 } else { 76 core.stdc.stdio.FILE* urandom = core.stdc.stdio.fopen("/dev/urandom", "rb"); 77 78 if (urandom == null) { 79 return 1; 80 } 81 82 ok = core.stdc.stdio.fread(&(data[0]), 1, uint.sizeof, urandom) == uint.sizeof; 83 core.stdc.stdio.fclose(urandom); 84 } 85 86 if (!ok) { 87 return 1; 88 } 89 90 *seed = .buf_to_uint32(&(data[0])); 91 92 return 0; 93 } 94 95 /* Windows Crypto API */ 96 version (Windows) { 97 alias CRYPTACQUIRECONTEXTA = extern (Windows) nothrow @nogc @live core.sys.windows.windef.BOOL function(core.sys.windows.wincrypt.HCRYPTPROV* phProv, core.sys.windows.winnt.LPCSTR pszContainer, core.sys.windows.winnt.LPCSTR pszProvider, core.sys.windows.windef.DWORD dwProvType, core.sys.windows.windef.DWORD dwFlags); 98 alias CRYPTGENRANDOM = extern (Windows) nothrow @nogc @live core.sys.windows.windef.BOOL function(core.sys.windows.wincrypt.HCRYPTPROV hProv, core.sys.windows.windef.DWORD dwLen, core.sys.windows.windef.BYTE* pbBuffer); 99 alias CRYPTRELEASECONTEXT = extern (Windows) nothrow @nogc @live core.sys.windows.windef.BOOL function(core.sys.windows.wincrypt.HCRYPTPROV hProv, core.sys.windows.windef.DWORD dwFlags); 100 101 nothrow @nogc @live 102 private bool seed_from_windows_cryptoapi(scope uint* seed) 103 104 in 105 { 106 assert(seed != null); 107 } 108 109 do 110 { 111 core.sys.windows.windef.HINSTANCE hAdvAPI32 = core.sys.windows.winbase.GetModuleHandleA("advapi32.dll"); 112 113 if (hAdvAPI32 == null) { 114 return false; 115 } 116 117 .CRYPTACQUIRECONTEXTA pCryptAcquireContext = cast(.CRYPTACQUIRECONTEXTA)(core.sys.windows.winbase.GetProcAddress(hAdvAPI32, "CryptAcquireContextA")); 118 119 if (pCryptAcquireContext == null) { 120 return false; 121 } 122 123 .CRYPTGENRANDOM pCryptGenRandom = cast(.CRYPTGENRANDOM)(core.sys.windows.winbase.GetProcAddress(hAdvAPI32, "CryptGenRandom")); 124 125 if (pCryptGenRandom == null) { 126 return false; 127 } 128 129 .CRYPTRELEASECONTEXT pCryptReleaseContext = cast(.CRYPTRELEASECONTEXT)(core.sys.windows.winbase.GetProcAddress(hAdvAPI32, "CryptReleaseContext")); 130 131 if (pCryptReleaseContext == null) { 132 return false; 133 } 134 135 core.sys.windows.wincrypt.HCRYPTPROV hCryptProv = 0; 136 137 if (!pCryptAcquireContext(&hCryptProv, null, null, core.sys.windows.wincrypt.PROV_RSA_FULL, core.sys.windows.wincrypt.CRYPT_VERIFYCONTEXT)) { 138 return false; 139 } 140 141 core.sys.windows.windef.BYTE[uint.sizeof] data = void; 142 int ok = pCryptGenRandom(hCryptProv, uint.sizeof, &(data[0])); 143 pCryptReleaseContext(hCryptProv, 0); 144 145 if (!ok) { 146 return false; 147 } 148 149 *seed = .buf_to_uint32(cast(char*)(&(data[0]))); 150 151 return true; 152 } 153 } 154 155 /* gettimeofday() and getpid() */ 156 nothrow @nogc @live 157 private int seed_from_timestamp_and_pid(scope uint* seed) 158 159 in 160 { 161 assert(seed != null); 162 } 163 164 do 165 { 166 static if (__traits(compiles, core.sys.posix.sys.time.gettimeofday)) { 167 /* XOR of seconds and microseconds */ 168 core.sys.posix.sys.time.timeval tv = void; 169 core.sys.posix.sys.time.gettimeofday(&tv, null); 170 *seed = cast(uint)(tv.tv_sec) ^ cast(uint)(tv.tv_usec); 171 } else { 172 /* Seconds only */ 173 *seed = cast(uint)(core.stdc.time.time(null)); 174 } 175 176 /* XOR with PID for more randomness */ 177 version (Windows) { 178 *seed ^= cast(uint)(core.sys.windows.winbase.GetCurrentProcessId()); 179 } else static if (__traits(compiles, core.sys.posix.unistd.getpid)) { 180 *seed ^= cast(uint)(core.sys.posix.unistd.getpid()); 181 } 182 183 return 0; 184 } 185 186 nothrow @nogc @live 187 private uint generate_seed() 188 189 do 190 { 191 uint seed = 0; 192 bool done = false; 193 194 version (Windows) { 195 if (.seed_from_windows_cryptoapi(&seed)) { 196 done = true; 197 } 198 } else { 199 if (.seed_from_urandom(&seed) == 0) { 200 done = true; 201 } 202 } 203 204 if (!done) { 205 /* 206 * Fall back to timestamp and PID if no better randomness is 207 * available 208 */ 209 .seed_from_timestamp_and_pid(&seed); 210 } 211 212 /* Make sure the seed is never zero */ 213 if (seed == 0) { 214 seed = 1; 215 } 216 217 return seed; 218 } 219 220 /* volatile */ 221 package __gshared uint hashtable_seed = 0; 222 223 //Posix 224 static if ((__traits(compiles, __atomic_test_and_set)) && (__traits(compiles, __ATOMIC_RELAXED)) && (__traits(compiles, __atomic_store_n)) && (__traits(compiles, __ATOMIC_RELEASE)) && (__traits(compiles, __atomic_load_n)) && (__traits(compiles, __ATOMIC_ACQUIRE))) { 225 //If you modify the code in this Conditional Statement, please also modify the code in init_unittest(). 226 227 version (Windows) { 228 static assert (false); 229 } 230 231 /* volatile */ 232 //private 233 __gshared char seed_initialized = 0; 234 235 /// 236 extern (C) 237 nothrow @nogc @live 238 public void json_object_seed(size_t seed) 239 240 do 241 { 242 uint new_seed = cast(uint)(seed); 243 244 if (.hashtable_seed == 0) { 245 if (__atomic_test_and_set(&.seed_initialized, __ATOMIC_RELAXED) == 0) { 246 /* Do the seeding ourselves */ 247 if (new_seed == 0) { 248 new_seed = .generate_seed(); 249 } 250 251 __atomic_store_n(&.hashtable_seed, new_seed, __ATOMIC_RELEASE); 252 } else { 253 /* Wait for another thread to do the seeding */ 254 do { 255 static if (__traits(compiles, core.sys.posix.sched.sched_yield)) { 256 core.sys.posix.sched.sched_yield(); 257 } 258 } while (__atomic_load_n(&.hashtable_seed, __ATOMIC_ACQUIRE) == 0); 259 } 260 } 261 } 262 } else static if (__traits(compiles, __sync_bool_compare_and_swap)) { 263 version (Windows) { 264 static assert (false); 265 } 266 267 /// 268 extern (C) 269 nothrow @nogc @live 270 public void json_object_seed(size_t seed) 271 272 do 273 { 274 uint new_seed = cast(uint)(seed); 275 276 if (.hashtable_seed == 0) { 277 if (new_seed == 0) { 278 /* 279 * Explicit synchronization fences are not supported by the 280 * __sync builtins, so every thread getting here has to 281 * generate the seed value. 282 */ 283 new_seed = .generate_seed(); 284 } 285 286 do { 287 if (__sync_bool_compare_and_swap(&.hashtable_seed, 0, new_seed)) { 288 /* We were the first to seed */ 289 break; 290 } else { 291 /* Wait for another thread to do the seeding */ 292 static if (__traits(compiles, core.sys.posix.sched.sched_yield)) { 293 core.sys.posix.sched.sched_yield(); 294 } 295 } 296 } while (.hashtable_seed == 0); 297 } 298 } 299 } else version (Win32) { 300 //private 301 __gshared core.sys.windows.windef.LONG seed_initialized = 0; 302 303 /// 304 extern (C) 305 nothrow @nogc @live 306 public void json_object_seed(size_t seed) 307 308 do 309 { 310 uint new_seed = cast(uint)(seed); 311 312 if (.hashtable_seed == 0) { 313 if (core.sys.windows.winbase.InterlockedIncrement(&.seed_initialized) == 1) { 314 /* Do the seeding ourselves */ 315 if (new_seed == 0) { 316 new_seed = .generate_seed(); 317 } 318 319 .hashtable_seed = new_seed; 320 } else { 321 /* Wait for another thread to do the seeding */ 322 do { 323 core.sys.windows.winbase.SwitchToThread(); 324 } while (.hashtable_seed == 0); 325 } 326 } 327 } 328 } else { 329 /* Fall back to a thread-unsafe version */ 330 /// 331 extern (C) 332 nothrow @nogc @live 333 public void json_object_seed(size_t seed) 334 335 do 336 { 337 uint new_seed = cast(uint)(seed); 338 339 if (.hashtable_seed == 0) { 340 if (new_seed == 0) { 341 new_seed = .generate_seed(); 342 } 343 344 .hashtable_seed = new_seed; 345 } 346 } 347 }