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 }