1
/* rndw32.c - W32 entropy gatherer
2
* Copyright (C) 1999, 2000, 2001, 2002 Free Software Foundation, Inc.
3
* Copyright Peter Gutmann, Matt Thomlinson and Blake Coverett 1996-1999
5
* This file is part of GnuPG.
7
* GnuPG is free software; you can redistribute it and/or modify
8
* it under the terms of the GNU General Public License as published by
9
* the Free Software Foundation; either version 2 of the License, or
10
* (at your option) any later version.
12
* GnuPG is distributed in the hope that it will be useful,
13
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
* GNU General Public License for more details.
17
* You should have received a copy of the GNU General Public License
18
* along with this program; if not, write to the Free Software
19
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
21
*************************************************************************
22
* The code here is based on code from Cryptlib 3.0 beta by Peter Gutmann.
23
* Source file misc/rndwin32.c "Win32 Randomness-Gathering Code" with this
26
* This module is part of the cryptlib continuously seeded pseudorandom
27
* number generator. For usage conditions, see lib_rand.c
29
* [Here is the notice from lib_rand.c, which is now called dev_sys.c]
31
* This module and the misc/rnd*.c modules represent the cryptlib
32
* continuously seeded pseudorandom number generator (CSPRNG) as described in
33
* my 1998 Usenix Security Symposium paper "The generation of random numbers
34
* for cryptographic purposes".
36
* The CSPRNG code is copyright Peter Gutmann (and various others) 1996,
37
* 1997, 1998, 1999, all rights reserved. Redistribution of the CSPRNG
38
* modules and use in source and binary forms, with or without modification,
39
* are permitted provided that the following conditions are met:
41
* 1. Redistributions of source code must retain the above copyright notice
42
* and this permission notice in its entirety.
44
* 2. Redistributions in binary form must reproduce the copyright notice in
45
* the documentation and/or other materials provided with the distribution.
47
* 3. A copy of any bugfixes or enhancements made must be provided to the
48
* author, <pgut001@cs.auckland.ac.nz> to allow them to be added to the
49
* baseline version of the code.
51
* ALTERNATIVELY, the code may be distributed under the terms of the GNU
52
* General Public License, version 2 or any later version published by the
53
* Free Software Foundation, in which case the provisions of the GNU GPL are
54
* required INSTEAD OF the above restrictions.
56
* Although not required under the terms of the GPL, it would still be nice if
57
* you could make any changes available to the author to allow a consistent
58
* code base to be maintained
59
*************************************************************************
74
# include <winioctl.h>
80
#include "algorithms.h"
88
* Definitions which are missing from the current GNU Windows32Api
91
#ifndef TH32CS_SNAPHEAPLIST
92
#define TH32CS_SNAPHEAPLIST 1
93
#define TH32CS_SNAPPROCESS 2
94
#define TH32CS_SNAPTHREAD 4
95
#define TH32CS_SNAPMODULE 8
96
#define TH32CS_SNAPALL (1|2|4|8)
97
#define TH32CS_INHERIT 0x80000000
98
#endif /*TH32CS_SNAPHEAPLIST*/
100
#ifndef IOCTL_DISK_PERFORMANCE
101
#define IOCTL_DISK_PERFORMANCE 0x00070020
103
#ifndef VER_PLATFORM_WIN32_WINDOWS
104
#define VER_PLATFORM_WIN32_WINDOWS 1
107
/* This used to be (6*8+5*4+8*2), but Peter Gutmann figured a larger
108
value in a newer release. So we use a far larger value. */
109
#define SIZEOF_DISK_PERFORMANCE_STRUCT 256
135
DWORD th32DefaultHeapID;
138
DWORD th32ParentProcessID;
148
DWORD th32OwnerProcessID;
169
/* Type definitions for function pointers to call Toolhelp32 functions
170
* used with the windows95 gatherer */
171
typedef BOOL (WINAPI * MODULEWALK) (HANDLE hSnapshot, MODULEENTRY32 *lpme);
172
typedef BOOL (WINAPI * THREADWALK) (HANDLE hSnapshot, THREADENTRY32 *lpte);
173
typedef BOOL (WINAPI * PROCESSWALK) (HANDLE hSnapshot, PROCESSENTRY32 *lppe);
174
typedef BOOL (WINAPI * HEAPLISTWALK) (HANDLE hSnapshot, HEAPLIST32 *lphl);
175
typedef BOOL (WINAPI * HEAPFIRST) (HEAPENTRY32 *lphe, DWORD th32ProcessID,
177
typedef BOOL (WINAPI * HEAPNEXT) (HEAPENTRY32 *lphe);
178
typedef HANDLE (WINAPI * CREATESNAPSHOT) (DWORD dwFlags, DWORD th32ProcessID);
180
/* Type definitions for function pointers to call NetAPI32 functions */
181
typedef DWORD (WINAPI * NETSTATISTICSGET) (LPWSTR szServer, LPWSTR szService,
182
DWORD dwLevel, DWORD dwOptions,
184
typedef DWORD (WINAPI * NETAPIBUFFERSIZE) (LPVOID lpBuffer, LPDWORD cbBuffer);
185
typedef DWORD (WINAPI * NETAPIBUFFERFREE) (LPVOID lpBuffer);
188
/* When we query the performance counters, we allocate an initial buffer and
189
* then reallocate it as required until RegQueryValueEx() stops returning
190
* ERROR_MORE_DATA. The following values define the initial buffer size and
191
* step size by which the buffer is increased
193
#define PERFORMANCE_BUFFER_SIZE 65536 /* Start at 64K */
194
#define PERFORMANCE_BUFFER_STEP 16384 /* Step by 16K */
198
slow_gatherer_windows95( void (*add)(const void*, size_t, int), int requester )
200
static CREATESNAPSHOT pCreateToolhelp32Snapshot = NULL;
201
static MODULEWALK pModule32First = NULL;
202
static MODULEWALK pModule32Next = NULL;
203
static PROCESSWALK pProcess32First = NULL;
204
static PROCESSWALK pProcess32Next = NULL;
205
static THREADWALK pThread32First = NULL;
206
static THREADWALK pThread32Next = NULL;
207
static HEAPLISTWALK pHeap32ListFirst = NULL;
208
static HEAPLISTWALK pHeap32ListNext = NULL;
209
static HEAPFIRST pHeap32First = NULL;
210
static HEAPNEXT pHeap32Next = NULL;
214
/* initialize the Toolhelp32 function pointers */
215
if ( !pCreateToolhelp32Snapshot ) {
219
log_debug ("rndw32#slow_gatherer_95: init toolkit\n" );
221
/* Obtain the module handle of the kernel to retrieve the addresses
222
* of the Toolhelp32 functions */
223
if ( ( !(hKernel = GetModuleHandle ("KERNEL32.DLL"))) ) {
224
g10_log_fatal ( "rndw32: can't get module handle\n" );
227
/* Now get pointers to the functions */
228
pCreateToolhelp32Snapshot = (CREATESNAPSHOT) GetProcAddress (hKernel,
229
"CreateToolhelp32Snapshot");
230
pModule32First = (MODULEWALK) GetProcAddress (hKernel, "Module32First");
231
pModule32Next = (MODULEWALK) GetProcAddress (hKernel, "Module32Next");
232
pProcess32First = (PROCESSWALK) GetProcAddress (hKernel,
234
pProcess32Next = (PROCESSWALK) GetProcAddress (hKernel,
236
pThread32First = (THREADWALK) GetProcAddress (hKernel, "Thread32First");
237
pThread32Next = (THREADWALK) GetProcAddress (hKernel, "Thread32Next");
238
pHeap32ListFirst = (HEAPLISTWALK) GetProcAddress (hKernel,
240
pHeap32ListNext = (HEAPLISTWALK) GetProcAddress (hKernel,
242
pHeap32First = (HEAPFIRST) GetProcAddress (hKernel, "Heap32First");
243
pHeap32Next = (HEAPNEXT) GetProcAddress (hKernel, "Heap32Next");
245
if ( !pCreateToolhelp32Snapshot
246
|| !pModule32First || !pModule32Next
247
|| !pProcess32First || !pProcess32Next
248
|| !pThread32First || !pThread32Next
249
|| !pHeap32ListFirst || !pHeap32ListNext
250
|| !pHeap32First || !pHeap32Next ) {
251
g10_log_fatal ( "rndw32: failed to get a toolhep function\n" );
255
/* Take a snapshot of everything we can get to which is currently
257
if ( !(hSnapshot = pCreateToolhelp32Snapshot (TH32CS_SNAPALL, 0)) ) {
258
g10_log_fatal ( "rndw32: failed to take a toolhelp snapshot\n" );
261
/* Walk through the local heap */
263
hl32.dwSize = sizeof (HEAPLIST32);
264
if (pHeap32ListFirst (hSnapshot, &hl32)) {
266
log_debug ("rndw32#slow_gatherer_95: walk heap\n" );
270
/* First add the information from the basic Heaplist32 struct */
271
(*add) ( &hl32, sizeof (hl32), requester );
273
/* Now walk through the heap blocks getting information
275
he32.dwSize = sizeof (HEAPENTRY32);
276
if (pHeap32First (&he32, hl32.th32ProcessID, hl32.th32HeapID)){
278
(*add) ( &he32, sizeof (he32), requester );
279
} while (pHeap32Next (&he32));
281
} while (pHeap32ListNext (hSnapshot, &hl32));
286
/* Walk through all processes */
287
{ PROCESSENTRY32 pe32;
288
pe32.dwSize = sizeof (PROCESSENTRY32);
289
if (pProcess32First (hSnapshot, &pe32)) {
291
log_debug ("rndw32#slow_gatherer_95: walk processes\n" );
293
(*add) ( &pe32, sizeof (pe32), requester );
294
} while (pProcess32Next (hSnapshot, &pe32));
298
/* Walk through all threads */
299
{ THREADENTRY32 te32;
300
te32.dwSize = sizeof (THREADENTRY32);
301
if (pThread32First (hSnapshot, &te32)) {
303
log_debug ("rndw32#slow_gatherer_95: walk threads\n" );
305
(*add) ( &te32, sizeof (te32), requester );
306
} while (pThread32Next (hSnapshot, &te32));
310
/* Walk through all modules associated with the process */
311
{ MODULEENTRY32 me32;
312
me32.dwSize = sizeof (MODULEENTRY32);
313
if (pModule32First (hSnapshot, &me32)) {
315
log_debug ("rndw32#slow_gatherer_95: walk modules\n" );
317
(*add) ( &me32, sizeof (me32), requester );
318
} while (pModule32Next (hSnapshot, &me32));
322
CloseHandle (hSnapshot);
328
slow_gatherer_windowsNT( void (*add)(const void*, size_t, int), int requester )
330
static int is_initialized = 0;
331
static NETSTATISTICSGET pNetStatisticsGet = NULL;
332
static NETAPIBUFFERSIZE pNetApiBufferSize = NULL;
333
static NETAPIBUFFERFREE pNetApiBufferFree = NULL;
334
static int is_workstation = 1;
336
static int cbPerfData = PERFORMANCE_BUFFER_SIZE;
337
PERF_DATA_BLOCK *pPerfData;
338
HANDLE hDevice, hNetAPI32 = NULL;
339
DWORD dwSize, status;
342
if ( !is_initialized ) {
346
log_debug ("rndw32#slow_gatherer_nt: init toolkit\n" );
347
/* Find out whether this is an NT server or workstation if necessary */
348
if (RegOpenKeyEx (HKEY_LOCAL_MACHINE,
349
"SYSTEM\\CurrentControlSet\\Control\\ProductOptions",
350
0, KEY_READ, &hKey) == ERROR_SUCCESS) {
352
dwSize = sizeof (szValue);
355
log_debug ("rndw32#slow_gatherer_nt: check product options\n" );
356
status = RegQueryValueEx (hKey, "ProductType", 0, NULL,
358
if (status == ERROR_SUCCESS
359
&& ascii_strcasecmp (szValue, "WinNT")) {
360
/* Note: There are (at least) three cases for ProductType:
361
* WinNT = NT Workstation, ServerNT = NT Server, LanmanNT =
362
* NT Server acting as a Domain Controller */
365
log_debug ("rndw32: this is a NT server\n");
370
/* Initialize the NetAPI32 function pointers if necessary */
371
if ( (hNetAPI32 = LoadLibrary ("NETAPI32.DLL")) ) {
373
log_debug ("rndw32#slow_gatherer_nt: netapi32 loaded\n" );
374
pNetStatisticsGet = (NETSTATISTICSGET) GetProcAddress (hNetAPI32,
376
pNetApiBufferSize = (NETAPIBUFFERSIZE) GetProcAddress (hNetAPI32,
378
pNetApiBufferFree = (NETAPIBUFFERFREE) GetProcAddress (hNetAPI32,
381
if ( !pNetStatisticsGet
382
|| !pNetApiBufferSize || !pNetApiBufferFree ) {
383
FreeLibrary (hNetAPI32);
385
g10_log_debug ("rndw32: No NETAPI found\n" );
392
/* Get network statistics. Note: Both NT Workstation and NT Server by
393
* default will be running both the workstation and server services. The
394
* heuristic below is probably useful though on the assumption that the
395
* majority of the network traffic will be via the appropriate service.
396
* In any case the network statistics return almost no randomness */
398
if (hNetAPI32 && !pNetStatisticsGet (NULL,
399
is_workstation ? L"LanmanWorkstation" :
400
L"LanmanServer", 0, 0, &lpBuffer) ) {
402
log_debug ("rndw32#slow_gatherer_nt: get netstats\n" );
403
pNetApiBufferSize (lpBuffer, &dwSize);
404
(*add) ( lpBuffer, dwSize,requester );
405
pNetApiBufferFree (lpBuffer);
409
/* Get disk I/O statistics for all the hard drives */
410
for (nDrive = 0;; nDrive++) {
411
char diskPerformance[SIZEOF_DISK_PERFORMANCE_STRUCT];
414
/* Check whether we can access this device */
415
sprintf (szDevice, "\\\\.\\PhysicalDrive%d", nDrive);
416
hDevice = CreateFile (szDevice, 0, FILE_SHARE_READ | FILE_SHARE_WRITE,
417
NULL, OPEN_EXISTING, 0, NULL);
418
if (hDevice == INVALID_HANDLE_VALUE)
421
/* Note: This only works if you have turned on the disk performance
422
* counters with 'diskperf -y'. These counters are off by default */
423
if (DeviceIoControl (hDevice, IOCTL_DISK_PERFORMANCE, NULL, 0,
424
diskPerformance, SIZEOF_DISK_PERFORMANCE_STRUCT,
428
log_debug ("rndw32#slow_gatherer_nt: iostats drive %d\n",
430
(*add) (diskPerformance, dwSize, requester );
433
log_info ("NOTE: you should run 'diskperf -y' "
434
"to enable the disk statistics\n");
436
CloseHandle (hDevice);
439
#if 0 /* we don't need this in GnuPG */
440
/* Wait for any async keyset driver binding to complete. You may be
441
* wondering what this call is doing here... the reason it's necessary is
442
* because RegQueryValueEx() will hang indefinitely if the async driver
443
* bind is in progress. The problem occurs in the dynamic loading and
444
* linking of driver DLL's, which work as follows:
446
* hDriver = LoadLibrary( DRIVERNAME );
447
* pFunction1 = ( TYPE_FUNC1 ) GetProcAddress( hDriver, NAME_FUNC1 );
448
* pFunction2 = ( TYPE_FUNC1 ) GetProcAddress( hDriver, NAME_FUNC2 );
450
* If RegQueryValueEx() is called while the GetProcAddress()'s are in
451
* progress, it will hang indefinitely. This is probably due to some
452
* synchronisation problem in the NT kernel where the GetProcAddress()
453
* calls affect something like a module reference count or function
454
* reference count while RegQueryValueEx() is trying to take a snapshot
455
* of the statistics, which include the reference counts. Because of
456
* this, we have to wait until any async driver bind has completed
457
* before we can call RegQueryValueEx() */
458
waitSemaphore (SEMAPHORE_DRIVERBIND);
461
/* Get information from the system performance counters. This can take
462
* a few seconds to do. In some environments the call to
463
* RegQueryValueEx() can produce an access violation at some random time
464
* in the future, adding a short delay after the following code block
465
* makes the problem go away. This problem is extremely difficult to
466
* reproduce, I haven't been able to get it to occur despite running it
467
* on a number of machines. The best explanation for the problem is that
468
* on the machine where it did occur, it was caused by an external driver
469
* or other program which adds its own values under the
470
* HKEY_PERFORMANCE_DATA key. The NT kernel calls the required external
471
* modules to map in the data, if there's a synchronisation problem the
472
* external module would write its data at an inappropriate moment,
473
* causing the access violation. A low-level memory checker indicated
474
* that ExpandEnvironmentStrings() in KERNEL32.DLL, called an
475
* interminable number of calls down inside RegQueryValueEx(), was
476
* overwriting memory (it wrote twice the allocated size of a buffer to a
477
* buffer allocated by the NT kernel). This may be what's causing the
478
* problem, but since it's in the kernel there isn't much which can be
481
* In addition to these problems the code in RegQueryValueEx() which
482
* estimates the amount of memory required to return the performance
483
* counter information isn't very accurate, since it always returns a
484
* worst-case estimate which is usually nowhere near the actual amount
485
* required. For example it may report that 128K of memory is required,
486
* but only return 64K of data */
487
{ pPerfData = m_alloc (cbPerfData);
491
log_debug ("rndw32#slow_gatherer_nt: get perf data\n" );
492
status = RegQueryValueEx (HKEY_PERFORMANCE_DATA, "Global", NULL,
493
NULL, (LPBYTE) pPerfData, &dwSize);
494
if (status == ERROR_SUCCESS) {
495
if (!memcmp (pPerfData->Signature, L"PERF", 8)) {
496
(*add) ( pPerfData, dwSize, requester );
499
g10_log_debug ( "rndw32: no PERF signature\n");
502
else if (status == ERROR_MORE_DATA) {
503
cbPerfData += PERFORMANCE_BUFFER_STEP;
504
pPerfData = m_realloc (pPerfData, cbPerfData);
507
g10_log_debug ( "rndw32: get performance data problem\n");
513
/* Although this isn't documented in the Win32 API docs, it's necessary
514
to explicitly close the HKEY_PERFORMANCE_DATA key after use (it's
515
implicitly opened on the first call to RegQueryValueEx()). If this
516
isn't done then any system components which provide performance data
517
can't be removed or changed while the handle remains active */
518
RegCloseKey (HKEY_PERFORMANCE_DATA);
523
rndw32_gather_random (void (*add)(const void*, size_t, int), int requester,
524
size_t length, int level )
526
static int is_initialized;
527
static int is_windowsNT, has_toolhelp;
532
/* We don't differentiate between level 1 and 2 here because
533
* there is no nternal entropy pool as a scary resource. It may
534
* all work slower, but because our entropy source will never
535
* block but deliver some not easy to measure entropy, we assume level 2
539
if ( !is_initialized ) {
540
OSVERSIONINFO osvi = { sizeof( osvi ) };
543
GetVersionEx( &osvi );
544
platform = osvi.dwPlatformId;
545
is_windowsNT = platform == VER_PLATFORM_WIN32_NT;
546
has_toolhelp = (platform == VER_PLATFORM_WIN32_WINDOWS
547
|| (is_windowsNT && osvi.dwMajorVersion >= 5));
549
if ( platform == VER_PLATFORM_WIN32s ) {
550
g10_log_fatal("can't run on a W32s platform\n" );
554
log_debug ("rndw32#gather_random: platform=%d\n", (int)platform );
559
log_debug ("rndw32#gather_random: req=%d len=%u lvl=%d\n",
560
requester, (unsigned int)length, level );
562
if ( has_toolhelp ) {
563
slow_gatherer_windows95 ( add, requester );
565
if ( is_windowsNT ) {
566
slow_gatherer_windowsNT ( add, requester );
575
rndw32_gather_random_fast( void (*add)(const void*, size_t, int), int requester )
577
static int addedFixedItems = 0;
580
log_debug ("rndw32#gather_random_fast: req=%d\n", requester );
582
/* Get various basic pieces of system information: Handle of active
583
* window, handle of window with mouse capture, handle of clipboard owner
584
* handle of start of clpboard viewer list, pseudohandle of current
585
* process, current process ID, pseudohandle of current thread, current
586
* thread ID, handle of desktop window, handle of window with keyboard
587
* focus, whether system queue has any events, cursor position for last
588
* message, 1 ms time for last message, handle of window with clipboard
589
* open, handle of process heap, handle of procs window station, types of
590
* events in input queue, and milliseconds since Windows was started */
591
{ byte buffer[20*sizeof(ulong)], *bufptr;
593
#define ADD(f) do { ulong along = (ulong)(f); \
594
memcpy (bufptr, &along, sizeof (along) ); \
595
bufptr += sizeof (along); } while (0)
596
ADD ( GetActiveWindow ());
597
ADD ( GetCapture ());
598
ADD ( GetClipboardOwner ());
599
ADD ( GetClipboardViewer ());
600
ADD ( GetCurrentProcess ());
601
ADD ( GetCurrentProcessId ());
602
ADD ( GetCurrentThread ());
603
ADD ( GetCurrentThreadId ());
604
ADD ( GetDesktopWindow ());
606
ADD ( GetInputState ());
607
ADD ( GetMessagePos ());
608
ADD ( GetMessageTime ());
609
ADD ( GetOpenClipboardWindow ());
610
ADD ( GetProcessHeap ());
611
ADD ( GetProcessWindowStation ());
612
ADD ( GetQueueStatus (QS_ALLEVENTS));
613
ADD ( GetTickCount ());
615
assert ( bufptr-buffer < sizeof (buffer) );
616
(*add) ( buffer, bufptr-buffer, requester );
620
/* Get multiword system information: Current caret position, current
621
* mouse cursor position */
623
GetCaretPos (&point);
624
(*add) ( &point, sizeof (point), requester );
625
GetCursorPos (&point);
626
(*add) ( &point, sizeof (point), requester );
629
/* Get percent of memory in use, bytes of physical memory, bytes of free
630
* physical memory, bytes in paging file, free bytes in paging file, user
631
* bytes of address space, and free user bytes */
632
{ MEMORYSTATUS memoryStatus;
633
memoryStatus.dwLength = sizeof (MEMORYSTATUS);
634
GlobalMemoryStatus (&memoryStatus);
635
(*add) ( &memoryStatus, sizeof (memoryStatus), requester );
638
/* Get thread and process creation time, exit time, time in kernel mode,
639
and time in user mode in 100ns intervals */
641
FILETIME creationTime, exitTime, kernelTime, userTime;
642
DWORD minimumWorkingSetSize, maximumWorkingSetSize;
644
handle = GetCurrentThread ();
645
GetThreadTimes (handle, &creationTime, &exitTime,
646
&kernelTime, &userTime);
647
(*add) ( &creationTime, sizeof (creationTime), requester );
648
(*add) ( &exitTime, sizeof (exitTime), requester );
649
(*add) ( &kernelTime, sizeof (kernelTime), requester );
650
(*add) ( &userTime, sizeof (userTime), requester );
652
handle = GetCurrentProcess ();
653
GetProcessTimes (handle, &creationTime, &exitTime,
654
&kernelTime, &userTime);
655
(*add) ( &creationTime, sizeof (creationTime), requester );
656
(*add) ( &exitTime, sizeof (exitTime), requester );
657
(*add) ( &kernelTime, sizeof (kernelTime), requester );
658
(*add) ( &userTime, sizeof (userTime), requester );
660
/* Get the minimum and maximum working set size for the current process */
661
GetProcessWorkingSetSize (handle, &minimumWorkingSetSize,
662
&maximumWorkingSetSize);
663
(*add) ( &minimumWorkingSetSize,
664
sizeof (&minimumWorkingSetSize), requester );
665
(*add) ( &maximumWorkingSetSize,
666
sizeof (&maximumWorkingSetSize), requester );
670
/* The following are fixed for the lifetime of the process so we only
672
if (!addedFixedItems) {
673
STARTUPINFO startupInfo;
675
/* Get name of desktop, console window title, new window position and
676
* size, window flags, and handles for stdin, stdout, and stderr */
677
startupInfo.cb = sizeof (STARTUPINFO);
678
GetStartupInfo (&startupInfo);
679
(*add) ( &startupInfo, sizeof (STARTUPINFO), requester );
683
/* The performance of QPC varies depending on the architecture it's
684
* running on and on the OS. Under NT it reads the CPU's 64-bit timestamp
685
* counter (at least on a Pentium and newer '486's, it hasn't been tested
686
* on anything without a TSC), under Win95 it reads the 1.193180 MHz PIC
687
* timer. There are vague mumblings in the docs that it may fail if the
688
* appropriate hardware isn't available (possibly '386's or MIPS machines
689
* running NT), but who's going to run NT on a '386? */
690
{ LARGE_INTEGER performanceCount;
691
if (QueryPerformanceCounter (&performanceCount)) {
693
log_debug ("rndw32#gather_random_fast: perf data\n");
694
(*add) (&performanceCount, sizeof (&performanceCount), requester);
696
else { /* Millisecond accuracy at best... */
697
DWORD aword = GetTickCount ();
698
(*add) (&aword, sizeof (aword), requester );
706
#endif /*USE_RNDW32*/