Updated the Windows entropy gatherer.
authorWerner Koch <wk@gnupg.org>
Thu, 25 Oct 2007 17:36:29 +0000 (17:36 +0000)
committerWerner Koch <wk@gnupg.org>
Thu, 25 Oct 2007 17:36:29 +0000 (17:36 +0000)
Typo fixes.

NEWS
cipher/ChangeLog
cipher/rndw32.c
src/Makefile.am
src/dumpsexp.c

diff --git a/NEWS b/NEWS
index 575ad41..38bd1dc 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -10,6 +10,8 @@ Noteworthy changes in version 1.3.1
  * Fixed a bug in the detection of symbol prefixes which inhibited the
    build of optimzied assembler code on certain systems.
 
+ * Updated the entropy gatherer for W32.
+
  * Interface changes relative to the 1.3.0 release:
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
index 868a8ad..7c9a579 100644 (file)
@@ -1,3 +1,14 @@
+2007-10-25  Werner Koch  <wk@g10code.com>
+
+       * rndw32.c: Updated from current cryptlib snapshot and modified
+       for our use.  Removed support from pre NT systems.
+       (slow_gatherer_windows95): Remove.
+       (_gcry_rndw32_gather_random): Require an NT platform.
+       (init_system_rng, read_system_rng, read_mbm_data): New.
+       (slow_gatherer_windowsNT): Rename to ...
+       (slow_gatherer): .. this.  Read system RNG and MBM.
+       (registry_poll): New with code factored out from slow_gatherer.
+
 2007-08-23  Werner Koch  <wk@g10code.com>
 
        * random.c (pool_filled_counter): New.
index e5c864e..8bdd72c 100644 (file)
@@ -1,6 +1,6 @@
-/* rndw32.c  - W32 entropy gatherer
+/* rndw32.c  -  W32 entropy gatherer
  * Copyright (C) 1999, 2000, 2002, 2003, 2007 Free Software Foundation, Inc.
- * Copyright Peter Gutmann, Matt Thomlinson and Blake Coverett 1996-1999
+ * Copyright Peter Gutmann, Matt Thomlinson and Blake Coverett 1996-2006
  *
  * This file is part of Libgcrypt.
  *
  Subject: Re: LGPL for the windows entropy gatherer
  To: wk@gnupg.org
  Date: Wed, 22 Aug 2007 03:05:42 +1200
+
  Hi,
+
  >As of now libgcrypt is GPL under Windows due to that module and some people
  >would really like to see it under LGPL too.  Can you do such a license change
  >to LGPL version 2?  Note that LGPL give the user the option to relicense it
  >under GPL, so the change would be pretty easy and backwar compatible.
+
  Sure.  I assumed that since GPG was GPLd, you'd prefer the GPL for the entropy
  code as well, but Ian asked for LGPL as an option so as of the next release
  I'll have LGPL in there.  You can consider it to be retroactive, so your
  current version will be LGPLd as well.
+
  Peter.
  *==========
  */
@@ -74,6 +74,9 @@
 #include <assert.h>
 #include <errno.h>
 #include <string.h>
+#ifdef __GNUC__  
+#include <stdint.h>
+#endif
 
 #include <windows.h>
 
 #include "rand-internal.h"
 
 
-static int debug_me;
-
-/*
- * Definitions which are missing from the current GNU Windows32Api
- */
-
-#ifndef TH32CS_SNAPHEAPLIST
-#define TH32CS_SNAPHEAPLIST 1
-#define TH32CS_SNAPPROCESS  2
-#define TH32CS_SNAPTHREAD   4
-#define TH32CS_SNAPMODULE   8
-#define TH32CS_SNAPALL     (1|2|4|8)
-#define TH32CS_INHERIT     0x80000000
-#endif /*TH32CS_SNAPHEAPLIST*/
-
+/* Definitions which are missing from the current GNU Windows32Api.  */
 #ifndef IOCTL_DISK_PERFORMANCE
-#define IOCTL_DISK_PERFORMANCE 0x00070020
-#endif
-#ifndef VER_PLATFORM_WIN32_WINDOWS
-#define VER_PLATFORM_WIN32_WINDOWS 1
+#define IOCTL_DISK_PERFORMANCE  0x00070020
 #endif
 
 /* This used to be (6*8+5*4+8*2), but Peter Gutmann figured a larger
    value in a newer release. So we use a far larger value. */
 #define SIZEOF_DISK_PERFORMANCE_STRUCT 256
 
-
-typedef struct {
-    DWORD dwSize;
-    DWORD th32ProcessID;
-    DWORD th32HeapID;
-    DWORD dwFlags;
-} HEAPLIST32;
-
-typedef struct {
-    DWORD dwSize;
-    HANDLE hHandle;
-    DWORD dwAddress;
-    DWORD dwBlockSize;
-    DWORD dwFlags;
-    DWORD dwLockCount;
-    DWORD dwResvd;
-    DWORD th32ProcessID;
-    DWORD th32HeapID;
-} HEAPENTRY32;
-
-typedef struct {
-    DWORD dwSize;
-    DWORD cntUsage;
-    DWORD th32ProcessID;
-    DWORD th32DefaultHeapID;
-    DWORD th32ModuleID;
-    DWORD cntThreads;
-    DWORD th32ParentProcessID;
-    LONG  pcPriClassBase;
-    DWORD dwFlags;
-    char  szExeFile[260];
-} PROCESSENTRY32;
-
-typedef struct {
-    DWORD dwSize;
-    DWORD cntUsage;
-    DWORD th32ThreadID;
-    DWORD th32OwnerProcessID;
-    LONG  tpBasePri;
-    LONG  tpDeltaPri;
-    DWORD dwFlags;
-} THREADENTRY32;
-
-typedef struct {
-    DWORD dwSize;
-    DWORD th32ModuleID;
-    DWORD th32ProcessID;
-    DWORD GlblcntUsage;
-    DWORD ProccntUsage;
-    BYTE  *modBaseAddr;
-    DWORD modBaseSize;
-    HMODULE hModule;
-    char  szModule[256];
-    char  szExePath[260];
-} MODULEENTRY32;
-
-
-
-/* Type definitions for function pointers to call Toolhelp32 functions
- * used with the windows95 gatherer */
-typedef BOOL (WINAPI * MODULEWALK) (HANDLE hSnapshot, MODULEENTRY32 *lpme);
-typedef BOOL (WINAPI * THREADWALK) (HANDLE hSnapshot, THREADENTRY32 *lpte);
-typedef BOOL (WINAPI * PROCESSWALK) (HANDLE hSnapshot, PROCESSENTRY32 *lppe);
-typedef BOOL (WINAPI * HEAPLISTWALK) (HANDLE hSnapshot, HEAPLIST32 *lphl);
-typedef BOOL (WINAPI * HEAPFIRST) (HEAPENTRY32 *lphe, DWORD th32ProcessID,
-                                  DWORD th32HeapID);
-typedef BOOL (WINAPI * HEAPNEXT) (HEAPENTRY32 *lphe);
-typedef HANDLE (WINAPI * CREATESNAPSHOT) (DWORD dwFlags, DWORD th32ProcessID);
-
-/* Type definitions for function pointers to call NetAPI32 functions */
-typedef DWORD (WINAPI * NETSTATISTICSGET) (LPWSTR szServer, LPWSTR szService,
-                                          DWORD dwLevel, DWORD dwOptions,
-                                          LPBYTE * lpBuffer);
-typedef DWORD (WINAPI * NETAPIBUFFERSIZE) (LPVOID lpBuffer, LPDWORD cbBuffer);
-typedef DWORD (WINAPI * NETAPIBUFFERFREE) (LPVOID lpBuffer);
+/* We don't include wincrypt.h so define it here.  */
+#define HCRYPTPROV  HANDLE
 
 
 /* When we query the performance counters, we allocate an initial buffer and
@@ -191,332 +104,636 @@ typedef DWORD (WINAPI * NETAPIBUFFERFREE) (LPVOID lpBuffer);
  * ERROR_MORE_DATA.  The following values define the initial buffer size and
  * step size by which the buffer is increased
  */
-#define PERFORMANCE_BUFFER_SIZE        65536   /* Start at 64K */
-#define PERFORMANCE_BUFFER_STEP        16384   /* Step by 16K */
+#define PERFORMANCE_BUFFER_SIZE         65536   /* Start at 64K */
+#define PERFORMANCE_BUFFER_STEP         16384   /* Step by 16K */
+
+
+/* The number of bytes to read from the system RNG on each slow poll.  */
+#define SYSTEMRNG_BYTES 64
+
+/* Intel Chipset CSP type and name */
+#define PROV_INTEL_SEC  22
+#define INTEL_DEF_PROV  "Intel Hardware Cryptographic Service Provider"
+
+
+
+
+/* Type definitions for function pointers to call NetAPI32 functions.  */
+typedef DWORD (WINAPI *NETSTATISTICSGET)(LPWSTR szServer, LPWSTR szService,
+                                         DWORD dwLevel, DWORD dwOptions,
+                                         LPBYTE *lpBuffer);
+typedef DWORD (WINAPI *NETAPIBUFFERSIZE)(LPVOID lpBuffer, LPDWORD cbBuffer);
+typedef DWORD (WINAPI *NETAPIBUFFERFREE)(LPVOID lpBuffer);
+
+/* Type definitions for function pointers to call native NT functions.  */
+typedef DWORD (WINAPI *NTQUERYSYSTEMINFORMATION)(DWORD systemInformationClass,
+                                                 PVOID systemInformation,
+                                                 ULONG systemInformationLength,
+                                                 PULONG returnLength);
+typedef DWORD (WINAPI *NTQUERYINFORMATIONPROCESS)
+     (HANDLE processHandle, DWORD processInformationClass,
+      PVOID processInformation, ULONG processInformationLength,
+      PULONG returnLength);
+typedef DWORD (WINAPI *NTPOWERINFORMATION)
+     (DWORD powerInformationClass, PVOID inputBuffer,
+      ULONG inputBufferLength, PVOID outputBuffer, ULONG outputBufferLength );
+
+/* Type definitions for function pointers to call CryptoAPI functions. */
+typedef BOOL (WINAPI *CRYPTACQUIRECONTEXT)(HCRYPTPROV *phProv,
+                                           LPCTSTR pszContainer,
+                                           LPCTSTR pszProvider, 
+                                           DWORD dwProvType,
+                                           DWORD dwFlags);
+typedef BOOL (WINAPI *CRYPTGENRANDOM)(HCRYPTPROV hProv, DWORD dwLen,
+                                      BYTE *pbBuffer);
+typedef BOOL (WINAPI *CRYPTRELEASECONTEXT)(HCRYPTPROV hProv, DWORD dwFlags);
+
+/* Somewhat alternative functionality available as a direct call, for 
+   Windows XP and newer.  This is the CryptoAPI RNG, which isn't anywhere
+   near as good as the HW RNG, but we use it if it's present on the basis
+   that at least it can't make things any worse.  This direct access version 
+   is only available under Windows XP, we don't go out of our way to access
+   the more general CryptoAPI one since the main purpose of using it is to 
+   take advantage of any possible future hardware RNGs that may be added, 
+   for example via TCPA devices.  */
+typedef BOOL (WINAPI *RTLGENRANDOM)(PVOID RandomBuffer, 
+                                    ULONG RandomBufferLength);
+
+
+
+/* MBM data structures, originally by Alexander van Kaam, converted to C by
+   Anders@Majland.org, finally updated by Chris Zahrt <techn0@iastate.edu> */
+#define BusType         char
+#define SMBType         char
+#define SensorType      char
+
+typedef struct 
+{
+  SensorType iType;               /* Type of sensor.  */
+  int Count;                      /* Number of sensor for that type.  */
+} SharedIndex;
 
+typedef struct 
+{
+  SensorType ssType;              /* Type of sensor */
+  unsigned char ssName[12];       /* Name of sensor */
+  char sspadding1[3];             /* Padding of 3 bytes */
+  double ssCurrent;               /* Current value */
+  double ssLow;                   /* Lowest readout */
+  double ssHigh;                  /* Highest readout */
+  long ssCount;                   /* Total number of readout */
+  char sspadding2[4];             /* Padding of 4 bytes */
+  long double ssTotal;            /* Total amout of all readouts */
+  char sspadding3[6];             /* Padding of 6 bytes */
+  double ssAlarm1;                /* Temp & fan: high alarm; voltage: % off */
+  double ssAlarm2;                /* Temp: low alarm */
+} SharedSensor;
+
+typedef struct
+{
+  short siSMB_Base;               /* SMBus base address */
+  BusType siSMB_Type;             /* SMBus/Isa bus used to access chip */
+  SMBType siSMB_Code;             /* SMBus sub type, Intel, AMD or ALi */
+  char siSMB_Addr;                /* Address of sensor chip on SMBus */
+  unsigned char siSMB_Name[41];   /* Nice name for SMBus */
+  short siISA_Base;               /* ISA base address of sensor chip on ISA */
+  int siChipType;                 /* Chip nr, connects with Chipinfo.ini */
+  char siVoltageSubType;          /* Subvoltage option selected */
+} SharedInfo;
+
+typedef struct
+{
+  double sdVersion;               /* Version number (example: 51090) */
+  SharedIndex sdIndex[10];        /* Sensor index */
+  SharedSensor sdSensor[100];     /* Sensor info */
+  SharedInfo sdInfo;              /* Misc.info */
+  unsigned char sdStart[41];      /* Start time */
 
-static void
-slow_gatherer_windows95( void (*add)(const void*, size_t, int), int requester )
+  /* We don't use the next two fields both because they're not random
+     and because it provides a nice safety margin in case of data size
+     mis- estimates (we always under-estimate the buffer size).  */
+#if 0
+  unsigned char sdCurrent[41];    /* Current time */
+  unsigned char sdPath[256];      /* MBM path */
+#endif /*0*/
+} SharedData;
+
+
+
+/* One time intialized handles and function pointers.  We use dynamic
+   loading of the DLLs to do without them in case libgcrypt does not
+   need any random.  */
+static HANDLE hNetAPI32;
+static NETSTATISTICSGET pNetStatisticsGet;
+static NETAPIBUFFERSIZE pNetApiBufferSize;
+static NETAPIBUFFERFREE pNetApiBufferFree;
+
+static HANDLE hNTAPI;
+static NTQUERYSYSTEMINFORMATION  pNtQuerySystemInformation;
+static NTQUERYINFORMATIONPROCESS pNtQueryInformationProcess;
+static NTPOWERINFORMATION        pNtPowerInformation;
+
+static HANDLE hAdvAPI32;
+static CRYPTACQUIRECONTEXT pCryptAcquireContext;
+static CRYPTGENRANDOM      pCryptGenRandom;
+static CRYPTRELEASECONTEXT pCryptReleaseContext;
+static RTLGENRANDOM        pRtlGenRandom;
+
+
+/* Other module global variables.  */
+static int system_rng_available; /* Whether a system RNG is available.  */
+static HCRYPTPROV hRNGProv;      /* Handle to Intel RNG CSP. */
+
+static int debug_me = 1;  /* Debug flag.  */
+
+
+
+\f
+/* Try and connect to the system RNG if there's one present. */
+static void 
+init_system_rng (void)
 {
-    static CREATESNAPSHOT pCreateToolhelp32Snapshot = NULL;
-    static MODULEWALK pModule32First = NULL;
-    static MODULEWALK pModule32Next = NULL;
-    static PROCESSWALK pProcess32First = NULL;
-    static PROCESSWALK pProcess32Next = NULL;
-    static THREADWALK pThread32First = NULL;
-    static THREADWALK pThread32Next = NULL;
-    static HEAPLISTWALK pHeap32ListFirst = NULL;
-    static HEAPLISTWALK pHeap32ListNext = NULL;
-    static HEAPFIRST pHeap32First = NULL;
-    static HEAPNEXT pHeap32Next = NULL;
-    HANDLE hSnapshot;
-
-
-    /* initialize the Toolhelp32 function pointers */
-    if ( !pCreateToolhelp32Snapshot ) {
-       HANDLE hKernel;
-
-       if ( debug_me )
-           log_debug ("rndw32#slow_gatherer_95: init toolkit\n" );
-
-       /* Obtain the module handle of the kernel to retrieve the addresses
-        * of the Toolhelp32 functions */
-       if ( ( !(hKernel = GetModuleHandle ("KERNEL32.DLL"))) ) {
-           log_fatal ( "rndw32: can't get module handle\n" );
-       }
-
-       /* Now get pointers to the functions */
-       pCreateToolhelp32Snapshot = (CREATESNAPSHOT) GetProcAddress (hKernel,
-                                                 "CreateToolhelp32Snapshot");
-       pModule32First = (MODULEWALK) GetProcAddress (hKernel, "Module32First");
-       pModule32Next = (MODULEWALK) GetProcAddress (hKernel, "Module32Next");
-       pProcess32First = (PROCESSWALK) GetProcAddress (hKernel,
-                                                       "Process32First");
-       pProcess32Next = (PROCESSWALK) GetProcAddress (hKernel,
-                                                      "Process32Next");
-       pThread32First = (THREADWALK) GetProcAddress (hKernel, "Thread32First");
-       pThread32Next = (THREADWALK) GetProcAddress (hKernel, "Thread32Next");
-       pHeap32ListFirst = (HEAPLISTWALK) GetProcAddress (hKernel,
-                                                         "Heap32ListFirst");
-       pHeap32ListNext = (HEAPLISTWALK) GetProcAddress (hKernel,
-                                                        "Heap32ListNext");
-       pHeap32First = (HEAPFIRST) GetProcAddress (hKernel, "Heap32First");
-       pHeap32Next = (HEAPNEXT) GetProcAddress (hKernel, "Heap32Next");
-
-       if (    !pCreateToolhelp32Snapshot
-            || !pModule32First || !pModule32Next
-            || !pProcess32First || !pProcess32Next
-            || !pThread32First  || !pThread32Next
-            || !pHeap32ListFirst || !pHeap32ListNext
-            || !pHeap32First     || !pHeap32Next  ) {
-           log_fatal ( "rndw32: failed to get a toolhelp function\n" );
-       }
+  system_rng_available = 0;
+  hRNGProv = NULL;
+
+  hAdvAPI32 = GetModuleHandle ("AdvAPI32.dll");
+  if (!hAdvAPI32)
+    return;
+
+  pCryptAcquireContext = (CRYPTACQUIRECONTEXT)
+    GetProcAddress (hAdvAPI32, "CryptAcquireContextA");
+  pCryptGenRandom = (CRYPTGENRANDOM)
+    GetProcAddress (hAdvAPI32, "CryptGenRandom");
+  pCryptReleaseContext = (CRYPTRELEASECONTEXT)
+    GetProcAddress (hAdvAPI32, "CryptReleaseContext");
+  
+  /* Get a pointer to the native randomness function if it's available.  
+     This isn't exported by name, so we have to get it by ordinal.  */
+  pRtlGenRandom = (RTLGENRANDOM)
+    GetProcAddress (hAdvAPI32, "SystemFunction036");
+
+  /* Try and connect to the PIII RNG CSP.  The AMD 768 southbridge (from 
+     the 760 MP chipset) also has a hardware RNG, but there doesn't appear 
+     to be any driver support for this as there is for the Intel RNG so we 
+     can't do much with it.  OTOH the Intel RNG is also effectively dead 
+     as well, mostly due to virtually nonexistant support/marketing by 
+     Intel, it's included here mostly for form's sake.  */
+  if ( (!pCryptAcquireContext || !pCryptGenRandom || !pCryptReleaseContext
+        || !pCryptAcquireContext (&hRNGProv, NULL, INTEL_DEF_PROV,
+                                  PROV_INTEL_SEC, 0) )
+       && !pRtlGenRandom)
+    {
+      hAdvAPI32 = NULL;
     }
+  else
+    system_rng_available = 1;
+}
 
-    /* Take a snapshot of everything we can get to which is currently
-     * in the system */
-    if ( !(hSnapshot = pCreateToolhelp32Snapshot (TH32CS_SNAPALL, 0)) ) {
-       log_fatal ( "rndw32: failed to take a toolhelp snapshot\n" );
-    }
 
-    /* Walk through the local heap */
-    {  HEAPLIST32 hl32;
-       hl32.dwSize = sizeof (HEAPLIST32);
-       if (pHeap32ListFirst (hSnapshot, &hl32)) {
-           if ( debug_me )
-               log_debug ("rndw32#slow_gatherer_95: walk heap\n" );
-           do {
-               HEAPENTRY32 he32;
-
-               /* First add the information from the basic Heaplist32 struct */
-               (*add) ( &hl32, sizeof (hl32), requester );
-
-               /* Now walk through the heap blocks getting information
-                * on each of them */
-               he32.dwSize = sizeof (HEAPENTRY32);
-               if (pHeap32First (&he32, hl32.th32ProcessID, hl32.th32HeapID)){
-                   do {
-                       (*add) ( &he32, sizeof (he32), requester );
-                   } while (pHeap32Next (&he32));
-               }
-           } while (pHeap32ListNext (hSnapshot, &hl32));
-       }
-    }
+/* Read data from the system RNG if availavle.  */
+static void 
+read_system_rng (void (*add)(const void*, size_t, enum random_origins),
+                 enum random_origins requester)
+{
+  BYTE buffer[ SYSTEMRNG_BYTES + 8 ];
+  int quality = 0;
 
+  if (!system_rng_available)
+    return;
 
-    /* Walk through all processes */
-    {  PROCESSENTRY32 pe32;
-       pe32.dwSize = sizeof (PROCESSENTRY32);
-       if (pProcess32First (hSnapshot, &pe32)) {
-           if ( debug_me )
-               log_debug ("rndw32#slow_gatherer_95: walk processes\n" );
-           do {
-               (*add) ( &pe32, sizeof (pe32), requester );
-           } while (pProcess32Next (hSnapshot, &pe32));
-       }
+  /* Read SYSTEMRNG_BYTES bytes from the system RNG.  We don't rely on
+     this for all our randomness requirements (particularly the
+     software RNG) in case it's broken in some way.  */
+  if (hRNGProv)
+    {
+      if (pCryptGenRandom (hRNGProv, SYSTEMRNG_BYTES, buffer))
+        quality = 80;
     }
-
-    /* Walk through all threads */
-    {  THREADENTRY32 te32;
-       te32.dwSize = sizeof (THREADENTRY32);
-       if (pThread32First (hSnapshot, &te32)) {
-           if ( debug_me )
-               log_debug ("rndw32#slow_gatherer_95: walk threads\n" );
-           do {
-               (*add) ( &te32, sizeof (te32), requester );
-           } while (pThread32Next (hSnapshot, &te32));
-       }
+  else if (pRtlGenRandom)
+    {
+      if ( pRtlGenRandom (buffer, SYSTEMRNG_BYTES))
+        quality = 50;
     }
-
-    /* Walk through all modules associated with the process */
-    {  MODULEENTRY32 me32;
-       me32.dwSize = sizeof (MODULEENTRY32);
-       if (pModule32First (hSnapshot, &me32)) {
-           if ( debug_me )
-               log_debug ("rndw32#slow_gatherer_95: walk modules\n" );
-           do {
-               (*add) ( &me32, sizeof (me32), requester );
-           } while (pModule32Next (hSnapshot, &me32));
-       }
+  if (quality > 0)
+    {
+      if (debug_me)
+        log_debug ("rndw32#read_system_rng: got %d bytes of quality %d\n",
+                   SYSTEMRNG_BYTES, quality);
+      (*add) (buffer, SYSTEMRNG_BYTES, requester);
+      wipememory (buffer, SYSTEMRNG_BYTES);
     }
+}
 
-    CloseHandle (hSnapshot);
+
+/* Read data from MBM.  This communicates via shared memory, so all we
+   need to do is map a file and read the data out.  */
+static void
+read_mbm_data (void (*add)(const void*, size_t, enum random_origins), 
+               enum random_origins requester)
+{
+  HANDLE hMBMData;
+  SharedData *mbmDataPtr;
+
+  hMBMData = OpenFileMapping (FILE_MAP_READ, FALSE, "$M$B$M$5$S$D$" );
+  if (hMBMData)
+    {
+      mbmDataPtr = (SharedData*)MapViewOfFile (hMBMData, FILE_MAP_READ,0,0,0);
+      if (mbmDataPtr)
+        {
+          if (debug_me)
+            log_debug ("rndw32#read_mbm_data: got %d bytes\n",
+                       (int)sizeof (SharedData));
+          (*add) (mbmDataPtr, sizeof (SharedData), requester);
+          UnmapViewOfFile (mbmDataPtr);
+        }
+      CloseHandle (hMBMData);
+    }
 }
 
 
+/* Fallback method using the registry to poll the statistics.  */
+static void
+registry_poll (void (*add)(const void*, size_t, enum random_origins), 
+               enum random_origins requester)
+{
+  static int cbPerfData = PERFORMANCE_BUFFER_SIZE;
+  int iterations;
+  DWORD dwSize, status;
+  PERF_DATA_BLOCK *pPerfData;
+
+  /* Get information from the system performance counters.  This can take a
+     few seconds to do.  In some environments the call to RegQueryValueEx()
+     can produce an access violation at some random time in the future, in
+     some cases adding a short delay after the following code block makes
+     the problem go away.  This problem is extremely difficult to
+     reproduce, I haven't been able to get it to occur despite running it
+     on a number of machines.  MS knowledge base article Q178887 covers
+     this type of problem, it's typically caused by an external driver or
+     other program that adds its own values under the
+     HKEY_PERFORMANCE_DATA key.  The NT kernel, via Advapi32.dll, calls the
+     required external module to map in the data inside an SEH try/except
+     block, so problems in the module's collect function don't pop up until
+     after it has finished, so the fault appears to occur in Advapi32.dll.
+     There may be problems in the NT kernel as well though, a low-level
+     memory checker indicated that ExpandEnvironmentStrings() in
+     Kernel32.dll, called an interminable number of calls down inside
+     RegQueryValueEx(), was overwriting memory (it wrote twice the
+     allocated size of a buffer to a buffer allocated by the NT kernel).
+     OTOH this could be coming from the external module calling back into
+     the kernel, which eventually causes the problem described above.
+
+     Possibly as an extension of the problem that the krnlWaitSemaphore()
+     call above works around, running two instances of cryptlib (e.g. two
+     applications that use it) under NT4 can result in one of them hanging
+     in the RegQueryValueEx() call.  This happens only under NT4 and is
+     hard to reproduce in any consistent manner.
+
+     One workaround that helps a bit is to read the registry as a remote
+     (rather than local) registry, it's possible that the use of a network
+     RPC call isolates the calling app from the problem in that whatever
+     service handles the RPC is taking the hit and not affecting the
+     calling app.  Since this would require another round of extensive
+     testing to verify and the NT native API call is working fine, we'll
+     stick with the native API call for now.
+
+     Some versions of NT4 had a problem where the amount of data returned
+     was mis-reported and would never settle down, because of this the code
+     below includes a safety-catch that bails out after 10 attempts have
+     been made, this results in no data being returned but at does ensure
+     that the thread will terminate.
+
+     In addition to these problems the code in RegQueryValueEx() that
+     estimates the amount of memory required to return the performance
+     counter information isn't very accurate (it's much worse than the
+     "slightly-inaccurate" level that the MS docs warn about, it's usually
+     wildly off) since it always returns a worst-case estimate which is
+     usually nowhere near the actual amount required.  For example it may
+     report that 128K of memory is required, but only return 64K of data.
+
+     Even worse than the registry-based performance counters is the
+     performance data helper (PDH) shim that tries to make the counters
+     look like the old Win16 API (which is also used by Win95).  Under NT
+     this can consume tens of MB of memory and huge amounts of CPU time
+     while it gathers its data, and even running once can still consume
+     about 1/2MB of memory */
+  pPerfData = gcry_xmalloc (cbPerfData);
+  for (iterations=0; iterations < 10; iterations++)
+    {
+      dwSize = cbPerfData;
+      if ( debug_me )
+        log_debug ("rndw32#slow_gatherer_nt: get perf data\n" );
+
+      status = RegQueryValueEx (HKEY_PERFORMANCE_DATA, "Global", NULL,
+                                NULL, (LPBYTE) pPerfData, &dwSize);
+      if (status == ERROR_SUCCESS)
+        {
+          if (!memcmp (pPerfData->Signature, L"PERF", 8))
+            (*add) ( pPerfData, dwSize, requester );
+          else
+            log_debug ("rndw32: no PERF signature\n");
+          break;
+        }
+      else if (status == ERROR_MORE_DATA)
+        {
+          cbPerfData += PERFORMANCE_BUFFER_STEP;
+          pPerfData = gcry_xrealloc (pPerfData, cbPerfData);
+        }
+      else
+        {
+          log_debug ("rndw32: get performance data problem: ec=%ld\n",
+                     status);
+          break;
+        }
+    }
+  gcry_free (pPerfData);
+
+  /* Although this isn't documented in the Win32 API docs, it's necessary
+     to explicitly close the HKEY_PERFORMANCE_DATA key after use (it's
+     implicitly opened on the first call to RegQueryValueEx()).  If this
+     isn't done then any system components which provide performance data
+     can't be removed or changed while the handle remains active.  */
+  RegCloseKey (HKEY_PERFORMANCE_DATA);
+}
+
 
 static void
-slow_gatherer_windowsNT( void (*add)(const void*, size_t, int), int requester )
+slow_gatherer ( void (*add)(const void*, size_t, enum random_origins), 
+                enum random_origins requester )
 {
-    static int is_initialized = 0;
-    static NETSTATISTICSGET pNetStatisticsGet = NULL;
-    static NETAPIBUFFERSIZE pNetApiBufferSize = NULL;
-    static NETAPIBUFFERFREE pNetApiBufferFree = NULL;
-    static int is_workstation = 1;
-
-    static int cbPerfData = PERFORMANCE_BUFFER_SIZE;
-    PERF_DATA_BLOCK *pPerfData;
-    HANDLE hDevice, hNetAPI32 = NULL;
-    DWORD dwSize, status;
-    int nDrive;
-
-    if ( !is_initialized ) {
-       HKEY hKey;
-
-       if ( debug_me )
-           log_debug ("rndw32#slow_gatherer_nt: init toolkit\n" );
-       /* Find out whether this is an NT server or workstation if necessary */
-       if (RegOpenKeyEx (HKEY_LOCAL_MACHINE,
-                         "SYSTEM\\CurrentControlSet\\Control\\ProductOptions",
-                         0, KEY_READ, &hKey) == ERROR_SUCCESS) {
-           BYTE szValue[32];
-           dwSize = sizeof (szValue);
-
-           if ( debug_me )
-               log_debug ("rndw32#slow_gatherer_nt: check product options\n" );
-           status = RegQueryValueEx (hKey, "ProductType", 0, NULL,
-                                     szValue, &dwSize);
-           if (status == ERROR_SUCCESS && stricmp (szValue, "WinNT")) {
-               /* Note: There are (at least) three cases for ProductType:
-                * WinNT = NT Workstation, ServerNT = NT Server, LanmanNT =
-                * NT Server acting as a Domain Controller */
-               is_workstation = 0;
-               if ( debug_me )
-                   log_debug ("rndw32: this is a NT server\n");
-           }
-           RegCloseKey (hKey);
-       }
-
-       /* Initialize the NetAPI32 function pointers if necessary */
-       if ( (hNetAPI32 = LoadLibrary ("NETAPI32.DLL")) ) {
-           if ( debug_me )
-               log_debug ("rndw32#slow_gatherer_nt: netapi32 loaded\n" );
-           pNetStatisticsGet = (NETSTATISTICSGET) GetProcAddress (hNetAPI32,
-                                                      "NetStatisticsGet");
-           pNetApiBufferSize = (NETAPIBUFFERSIZE) GetProcAddress (hNetAPI32,
-                                                      "NetApiBufferSize");
-           pNetApiBufferFree = (NETAPIBUFFERFREE) GetProcAddress (hNetAPI32,
-                                                      "NetApiBufferFree");
-
-           if ( !pNetStatisticsGet
-                || !pNetApiBufferSize || !pNetApiBufferFree ) {
-               FreeLibrary (hNetAPI32);
-               hNetAPI32 = NULL;
-               log_debug ("rndw32: No NETAPI found\n" );
-           }
-       }
-
-       is_initialized = 1;
+  static int is_initialized = 0;
+  static int is_workstation = 1;
+  HANDLE hDevice;
+  DWORD dwType, dwSize, dwResult;
+  ULONG ulSize;
+  int drive_no, status;
+  int no_results = 0;
+  void *buffer;
+
+  if ( !is_initialized )
+    {
+      HKEY hKey;
+
+      if ( debug_me )
+        log_debug ("rndw32#slow_gatherer: init toolkit\n" );
+      /* Find out whether this is an NT server or workstation if necessary */
+      if (RegOpenKeyEx (HKEY_LOCAL_MACHINE,
+                        "SYSTEM\\CurrentControlSet\\Control\\ProductOptions",
+                        0, KEY_READ, &hKey) == ERROR_SUCCESS)
+        {
+          BYTE szValue[32 + 8];
+          dwSize = 32;
+
+          if ( debug_me )
+            log_debug ("rndw32#slow_gatherer: check product options\n" );
+
+          status = RegQueryValueEx (hKey, "ProductType", 0, NULL,
+                                    szValue, &dwSize);
+          if (status == ERROR_SUCCESS && stricmp (szValue, "WinNT"))
+            {
+              /* Note: There are (at least) three cases for ProductType:
+                 WinNT = NT Workstation, ServerNT = NT Server, LanmanNT =
+                 NT Server acting as a Domain Controller.  */
+              is_workstation = 0;
+              if ( debug_me )
+                log_debug ("rndw32: this is a NT server\n");
+            }
+          RegCloseKey (hKey);
+        }
+
+      /* The following are fixed for the lifetime of the process so we
+         only add them once */
+      /* readPnPData ();  - we have not implemented that.  */
+
+      /* Initialize the NetAPI32 function pointers if necessary */
+      hNetAPI32 = LoadLibrary ("NETAPI32.DLL");
+      if (hNetAPI32)
+        {
+          if (debug_me)
+            log_debug ("rndw32#slow_gatherer: netapi32 loaded\n" );
+          pNetStatisticsGet = (NETSTATISTICSGET)
+            GetProcAddress (hNetAPI32, "NetStatisticsGet");
+          pNetApiBufferSize = (NETAPIBUFFERSIZE)
+            GetProcAddress (hNetAPI32, "NetApiBufferSize");
+          pNetApiBufferFree = (NETAPIBUFFERFREE)
+            GetProcAddress (hNetAPI32, "NetApiBufferFree");
+
+          if (!pNetStatisticsGet || !pNetApiBufferSize || !pNetApiBufferFree)
+            {
+              FreeLibrary (hNetAPI32);
+              hNetAPI32 = NULL;
+              log_debug ("rndw32: No NETAPI found\n" );
+            }
+        }
+
+      /* Initialize the NT kernel native API function pointers if necessary */
+      hNTAPI = GetModuleHandle ("NTDll.dll");
+      if (hNTAPI)
+        {
+          /* Get a pointer to the NT native information query functions */
+          pNtQuerySystemInformation = (NTQUERYSYSTEMINFORMATION)
+            GetProcAddress (hNTAPI, "NtQuerySystemInformation");
+          pNtQueryInformationProcess = (NTQUERYINFORMATIONPROCESS)
+            GetProcAddress (hNTAPI, "NtQueryInformationProcess");
+          pNtPowerInformation = (NTPOWERINFORMATION)
+            GetProcAddress(hNTAPI, "NtPowerInformation");
+
+          if (!pNtQuerySystemInformation || !pNtQueryInformationProcess)
+            hNTAPI = NULL;
+        }
+
+
+      is_initialized = 1;
     }
+  
+  read_system_rng ( add, requester );
+  read_mbm_data ( add, requester );
+  
+  /* Get network statistics.    Note: Both NT Workstation and NT Server by
+     default will be running both the workstation and server services.  The
+     heuristic below is probably useful though on the assumption that the
+     majority of the network traffic will be via the appropriate service.
+     In any case the network statistics return almost no randomness.  */
+  {
+    LPBYTE lpBuffer;
+    
+    if (hNetAPI32
+        && !pNetStatisticsGet (NULL,
+                               is_workstation ? L"LanmanWorkstation" :
+                               L"LanmanServer", 0, 0, &lpBuffer))
+      {
+        if ( debug_me )
+          log_debug ("rndw32#slow_gatherer: get netstats\n" );
+        pNetApiBufferSize (lpBuffer, &dwSize);
+        (*add) ( lpBuffer, dwSize, requester );
+        pNetApiBufferFree (lpBuffer);
+      }
+  }
 
-    /* Get network statistics. Note: Both NT Workstation and NT Server by
-     * default will be running both the workstation and server services.  The
-     * heuristic below is probably useful though on the assumption that the
-     * majority of the network traffic will be via the appropriate service.
-     * In any case the network statistics return almost no randomness */
-    {  LPBYTE lpBuffer;
-       if (hNetAPI32 && !pNetStatisticsGet (NULL,
-                          is_workstation ? L"LanmanWorkstation" :
-                          L"LanmanServer", 0, 0, &lpBuffer) ) {
-           if ( debug_me )
-               log_debug ("rndw32#slow_gatherer_nt: get netstats\n" );
-           pNetApiBufferSize (lpBuffer, &dwSize);
-           (*add) ( lpBuffer, dwSize,requester );
-           pNetApiBufferFree (lpBuffer);
-       }
+  /* Get disk I/O statistics for all the hard drives.  100 is an
+     arbitrary failsafe limit.  */
+  for (drive_no = 0; drive_no < 100 ; drive_no++)
+    {
+      char diskPerformance[SIZEOF_DISK_PERFORMANCE_STRUCT + 8];
+      char szDevice[50];
+      
+      /* Check whether we can access this device.  */
+      snprintf (szDevice, sizeof szDevice, "\\\\.\\PhysicalDrive%d",
+                drive_no);
+      hDevice = CreateFile (szDevice, 0, FILE_SHARE_READ | FILE_SHARE_WRITE,
+                            NULL, OPEN_EXISTING, 0, NULL);
+      if (hDevice == INVALID_HANDLE_VALUE)
+        break; /* No more drives.  */
+        
+      /* Note: This only works if you have turned on the disk performance
+         counters with 'diskperf -y'.  These counters are off by default. */
+      dwSize = sizeof diskPerformance;
+      if (DeviceIoControl (hDevice, IOCTL_DISK_PERFORMANCE, NULL, 0,
+                           diskPerformance, SIZEOF_DISK_PERFORMANCE_STRUCT,
+                           &dwSize, NULL))
+        {
+          if ( debug_me )
+            log_debug ("rndw32#slow_gatherer: iostat drive %d\n",
+                       drive_no);
+          (*add) (diskPerformance, dwSize, requester);
+        }
+      else
+        {
+          log_info ("NOTE: you should run 'diskperf -y' "
+                    "to enable the disk statistics\n");
+        }
+      CloseHandle (hDevice);
     }
 
-    /* Get disk I/O statistics for all the hard drives */
-    for (nDrive = 0;; nDrive++) {
-        char diskPerformance[SIZEOF_DISK_PERFORMANCE_STRUCT];
-       char szDevice[50];
-
-       /* Check whether we can access this device */
-       sprintf (szDevice, "\\\\.\\PhysicalDrive%d", nDrive);
-       hDevice = CreateFile (szDevice, 0, FILE_SHARE_READ | FILE_SHARE_WRITE,
-                             NULL, OPEN_EXISTING, 0, NULL);
-       if (hDevice == INVALID_HANDLE_VALUE)
-           break;
-
-       /* Note: This only works if you have turned on the disk performance
-        * counters with 'diskperf -y'.  These counters are off by default */
-       if (DeviceIoControl (hDevice, IOCTL_DISK_PERFORMANCE, NULL, 0,
-                            diskPerformance, SIZEOF_DISK_PERFORMANCE_STRUCT,
-                            &dwSize, NULL))
-       {
-           if ( debug_me )
-               log_debug ("rndw32#slow_gatherer_nt: iostats drive %d\n",
-                                                                 nDrive );
-           (*add) (diskPerformance, dwSize, requester );
-       }
-       else {
-           log_info ("NOTE: you should run 'diskperf -y' "
-                     "to enable the disk statistics\n");
-       }
-       CloseHandle (hDevice);
+  /* In theory we should be using the Win32 performance query API to obtain
+     unpredictable data from the system, however this is so unreliable (see
+     the multiple sets of comments in registryPoll()) that it's too risky
+     to rely on it except as a fallback in emergencies.  Instead, we rely
+     mostly on the NT native API function NtQuerySystemInformation(), which
+     has the dual advantages that it doesn't have as many (known) problems
+     as the Win32 equivalent and that it doesn't access the data indirectly
+     via pseudo-registry keys, which means that it's much faster.  Note
+     that the Win32 equivalent actually works almost all of the time, the
+     problem is that on one or two systems it can fail in strange ways that
+     are never the same and can't be reproduced on any other system, which
+     is why we use the native API here.  Microsoft officially documented
+     this function in early 2003, so it'll be fairly safe to use.  */
+  if ( !hNTAPI )
+    {
+      registry_poll (add, requester);
+      return;
     }
 
-#if 0 /* we don't need this in GnuPG  */
-    /* Wait for any async keyset driver binding to complete.  You may be
-     * wondering what this call is doing here... the reason it's necessary is
-     * because RegQueryValueEx() will hang indefinitely if the async driver
-     * bind is in progress.  The problem occurs in the dynamic loading and
-     * linking of driver DLL's, which work as follows:
-     *
-     * hDriver = LoadLibrary( DRIVERNAME );
-     * pFunction1 = ( TYPE_FUNC1 ) GetProcAddress( hDriver, NAME_FUNC1 );
-     * pFunction2 = ( TYPE_FUNC1 ) GetProcAddress( hDriver, NAME_FUNC2 );
-     *
-     * If RegQueryValueEx() is called while the GetProcAddress()'s are in
-     * progress, it will hang indefinitely.  This is probably due to some
-     * synchronisation problem in the NT kernel where the GetProcAddress()
-     * calls affect something like a module reference count or function
-     * reference count while RegQueryValueEx() is trying to take a snapshot
-     * of the statistics, which include the reference counts.  Because of
-     * this, we have to wait until any async driver bind has completed
-     * before we can call RegQueryValueEx() */
-    waitSemaphore (SEMAPHORE_DRIVERBIND);
-#endif
 
-    /* Get information from the system performance counters.  This can take
-     * a few seconds to do.  In some environments the call to
-     * RegQueryValueEx() can produce an access violation at some random time
-     * in the future, adding a short delay after the following code block
-     * makes the problem go away.  This problem is extremely difficult to
-     * reproduce, I haven't been able to get it to occur despite running it
-     * on a number of machines.  The best explanation for the problem is that
-     * on the machine where it did occur, it was caused by an external driver
-     * or other program which adds its own values under the
-     * HKEY_PERFORMANCE_DATA key.  The NT kernel calls the required external
-     * modules to map in the data, if there's a synchronisation problem the
-     * external module would write its data at an inappropriate moment,
-     * causing the access violation.  A low-level memory checker indicated
-     * that ExpandEnvironmentStrings() in KERNEL32.DLL, called an
-     * interminable number of calls down inside RegQueryValueEx(), was
-     * overwriting memory (it wrote twice the allocated size of a buffer to a
-     * buffer allocated by the NT kernel).  This may be what's causing the
-     * problem, but since it's in the kernel there isn't much which can be
-     * done.
-     *
-     * In addition to these problems the code in RegQueryValueEx() which
-     * estimates the amount of memory required to return the performance
-     * counter information isn't very accurate, since it always returns a
-     * worst-case estimate which is usually nowhere near the actual amount
-     * required.  For example it may report that 128K of memory is required,
-     * but only return 64K of data */
-    {  pPerfData =  gcry_xmalloc (cbPerfData);
-       for (;;) {
-           dwSize = cbPerfData;
-           if ( debug_me )
-               log_debug ("rndw32#slow_gatherer_nt: get perf data\n" );
-           status = RegQueryValueEx (HKEY_PERFORMANCE_DATA, "Global", NULL,
-                                     NULL, (LPBYTE) pPerfData, &dwSize);
-           if (status == ERROR_SUCCESS) {
-               if (!memcmp (pPerfData->Signature, L"PERF", 8)) {
-                   (*add) ( pPerfData, dwSize, requester );
-               }
-               else
-                   log_debug ( "rndw32: no PERF signature\n");
-               break;
-           }
-           else if (status == ERROR_MORE_DATA) {
-               cbPerfData += PERFORMANCE_BUFFER_STEP;
-               pPerfData = gcry_realloc (pPerfData, cbPerfData);
-           }
-           else {
-               log_debug ("rndw32: get performance data problem: ec=%ld\n", 
-                           status);
-               break;
-           }
-       }
-       gcry_free (pPerfData);
+  /* Scan the first 64 possible information types (we don't bother with
+     increasing the buffer size as we do with the Win32 version of the
+     performance data read, we may miss a few classes but it's no big deal).
+     This scan typically yields around 20 pieces of data, there's nothing
+     in the range 65...128 so chances are there won't be anything above
+     there either.  */
+  buffer = gcry_xmalloc (PERFORMANCE_BUFFER_SIZE);
+  for (dwType = 0; dwType < 64; dwType++)
+    {
+      switch (dwType)
+        {
+          /* Some information types are write-only (the IDs are shared with
+             a set-information call), we skip these.  */
+        case 26: case 27: case 38: case 46: case 47: case 48: case 52:
+          continue;
+
+          /* ID 53 = SystemSessionProcessInformation reads input from the
+             output buffer, which has to contain a session ID and pointer
+             to the actual buffer in which to store the session information.
+             Because this isn't a standard query, we skip this.  */
+        case  53:
+          continue;
+        }
+
+      /* Query the info for this ID.  Some results (for example for
+         ID = 6, SystemCallCounts) are only available in checked builds
+         of the kernel.  A smaller subcless of results require that
+         certain system config flags be set, for example
+         SystemObjectInformation requires that the
+         FLG_MAINTAIN_OBJECT_TYPELIST be set in NtGlobalFlags.  To avoid
+         having to special-case all of these, we try reading each one and
+         only use those for which we get a success status.  */
+      dwResult = pNtQuerySystemInformation (dwType, buffer,
+                                            PERFORMANCE_BUFFER_SIZE - 2048,
+                                            &ulSize);
+      if (dwResult != ERROR_SUCCESS)
+        continue;
+
+      /* Some calls (e.g. ID = 23, SystemProcessorStatistics, and ID = 24,
+         SystemDpcInformation) incorrectly return a length of zero, so we
+         manually adjust the length to the correct value.  */
+      if ( !ulSize )
+        {
+          if (dwType == 23)
+            ulSize = 6 * sizeof (ULONG);
+          else if (dwType == 24)
+            ulSize = 5 * sizeof (ULONG);
+        }
+
+      /* If we got some data back, add it to the entropy pool.  */
+      if (ulSize > 0 && ulSize <= PERFORMANCE_BUFFER_SIZE - 2048)
+        {
+          if (debug_me)
+            log_debug ("rndw32#slow_gatherer: %lu bytes from sysinfo %ld\n",
+                       ulSize, dwType);
+          (*add) (buffer, ulSize, requester);
+          no_results++;
+        }
+    }
+
+  /* Now we would do the same for the process information.  This
+     call would rather ugly in that it requires an exact length
+     match for the data returned, failing with a
+     STATUS_INFO_LENGTH_MISMATCH error code (0xC0000004) if the
+     length isn't an exact match.  It requires a compiler to handle
+     complex nested structs, alignment issues, and so on, and
+     without the headers in which the entries are declared it's
+     almost impossible to do.  Thus we don't.  */
+
+
+  /* Finally, do the same for the system power status information.  There
+     are only a limited number of useful information types available so we
+     restrict ourselves to the useful types.  In addition since this
+     function doesn't return length information, we have to hardcode in
+     length data.  */
+  if (pNtPowerInformation)
+    {
+      static const struct { int type; int size; } powerInfo[] = {
+        { 0, 128 },     /* SystemPowerPolicyAc */
+        { 1, 128 },     /* SystemPowerPolicyDc */
+        { 4, 64 },      /* SystemPowerCapabilities */
+        { 5, 48 },      /* SystemBatteryState */
+        { 11, 48 },     /* ProcessorInformation */
+        { 12, 24 },     /* SystemPowerInformation */
+        { -1, -1 }
+      };
+      int i;
+
+      /* The 100 is a failsafe limit.  */
+      for (i = 0; powerInfo[i].type != -1 && i < 100; i++ )
+        {
+          /* Query the info for this ID */
+          dwResult = pNtPowerInformation (powerInfo[i].type, NULL, 0, buffer,
+                                          PERFORMANCE_BUFFER_SIZE - 2048);
+          if (dwResult != ERROR_SUCCESS)
+            continue;
+          if (debug_me)
+            log_debug ("rndw32#slow_gatherer: %u bytes from powerinfo %d\n",
+                       powerInfo[i].size, i);
+          (*add) (buffer, powerInfo[i].size, requester);
+          no_results++;
+        }
+      assert (i < 100);
     }
-    /* Although this isn't documented in the Win32 API docs, it's necessary
-       to explicitly close the HKEY_PERFORMANCE_DATA key after use (it's
-       implicitly opened on the first call to RegQueryValueEx()).  If this
-       isn't done then any system components which provide performance data
-       can't be removed or changed while the handle remains active */
-    RegCloseKey (HKEY_PERFORMANCE_DATA);
+  gcry_free (buffer);
+
+  /* We couldn't get enough results from the kernel, fall back to the
+     somewhat troublesome registry poll.  */
+  if (no_results < 15)
+    registry_poll (add, requester);
 }
 
 
@@ -527,58 +744,47 @@ _gcry_rndw32_gather_random (void (*add)(const void*, size_t,
                             size_t length, int level )
 {
   static int is_initialized;
-  static int is_windowsNT, has_toolhelp;
-
 
-  if ( !level )
+  if (!level)
     return 0;
+
   /* We don't differentiate between level 1 and 2 here because there
      is no internal entropy pool as a scary resource.  It may all work
      slower, but because our entropy source will never block but
      deliver some not easy to measure entropy, we assume level 2.  */
 
-  if ( !is_initialized )
+  if (!is_initialized)
     {
       OSVERSIONINFO osvi = { sizeof( osvi ) };
-      DWORD platform;
-      
-      GetVersionEx( &osvi );
-      platform = osvi.dwPlatformId;
-      is_windowsNT = platform == VER_PLATFORM_WIN32_NT;
-      has_toolhelp = (platform == VER_PLATFORM_WIN32_WINDOWS
-                      || (is_windowsNT && osvi.dwMajorVersion >= 5));
 
-      if ( platform == VER_PLATFORM_WIN32s ) 
-        log_fatal("can't run on a W32s platform\n" );
+      GetVersionEx( &osvi );
+      if ( osvi.dwPlatformId != VER_PLATFORM_WIN32_NT)
+        log_fatal ("can only run on a Windows NT platform\n" );
+      init_system_rng ();
       is_initialized = 1;
-      if ( debug_me )
-        log_debug ("rndw32#gather_random: platform=%d\n", (int)platform );
     }
-  
-  
-  if ( debug_me )
+
+  if (debug_me)
     log_debug ("rndw32#gather_random: ori=%d len=%u lvl=%d\n",
                origin, (unsigned int)length, level );
-  
-  if ( has_toolhelp )
-    slow_gatherer_windows95 ( add, origin );
-  if ( is_windowsNT )
-    slow_gatherer_windowsNT ( add, origin );
-  
+
+  slow_gatherer (add, origin);
+
   return 0;
 }
 
 
+
 void
-_gcry_rndw32_gather_random_fast (void (*add)(const void*, size_t, 
+_gcry_rndw32_gather_random_fast (void (*add)(const void*, size_t,
                                              enum random_origins),
                                  enum random_origins origin)
 {
   static int addedFixedItems = 0;
-  
+
   if ( debug_me )
     log_debug ("rndw32#gather_random_fast: ori=%d\n", origin );
-  
+
   /* Get various basic pieces of system information: Handle of active
      window, handle of window with mouse capture, handle of clipboard
      owner handle of start of clpboard viewer list, pseudohandle of
@@ -589,14 +795,14 @@ _gcry_rndw32_gather_random_fast (void (*add)(const void*, size_t,
      handle of window with clipboard open, handle of process heap,
      handle of procs window station, types of events in input queue,
      and milliseconds since Windows was started.  */
-  
+
   {
     byte buffer[20*sizeof(ulong)], *bufptr;
 
     bufptr = buffer;
-#define ADD(f)  do { ulong along = (ulong)(f);                 \
-                    memcpy (bufptr, &along, sizeof (along) );  \
-                    bufptr += sizeof (along);                  \
+#define ADD(f)  do { ulong along = (ulong)(f);                  \
+                     memcpy (bufptr, &along, sizeof (along) );  \
+                     bufptr += sizeof (along);                  \
                    } while (0)
 
     ADD ( GetActiveWindow ());
@@ -617,7 +823,7 @@ _gcry_rndw32_gather_random_fast (void (*add)(const void*, size_t,
     ADD ( GetProcessWindowStation ());
     ADD ( GetQueueStatus (QS_ALLEVENTS));
     ADD ( GetTickCount ());
-    
+
     assert ( bufptr-buffer < sizeof (buffer) );
     (*add) ( buffer, bufptr-buffer, origin );
 #undef ADD
@@ -625,9 +831,9 @@ _gcry_rndw32_gather_random_fast (void (*add)(const void*, size_t,
 
   /* Get multiword system information: Current caret position, current
      mouse cursor position.  */
-  {    
+  {
     POINT point;
-    
+
     GetCaretPos (&point);
     (*add) ( &point, sizeof (point), origin );
     GetCursorPos (&point);
@@ -637,7 +843,7 @@ _gcry_rndw32_gather_random_fast (void (*add)(const void*, size_t,
   /* Get percent of memory in use, bytes of physical memory, bytes of
      free physical memory, bytes in paging file, free bytes in paging
      file, user bytes of address space, and free user bytes.  */
-  {    
+  {
     MEMORYSTATUS memoryStatus;
 
     memoryStatus.dwLength = sizeof (MEMORYSTATUS);
@@ -659,7 +865,7 @@ _gcry_rndw32_gather_random_fast (void (*add)(const void*, size_t,
     (*add) ( &exitTime, sizeof (exitTime), origin );
     (*add) ( &kernelTime, sizeof (kernelTime), origin );
     (*add) ( &userTime, sizeof (userTime), origin );
-    
+
     handle = GetCurrentProcess ();
     GetProcessTimes (handle, &creationTime, &exitTime,
                      &kernelTime, &userTime);
@@ -667,7 +873,7 @@ _gcry_rndw32_gather_random_fast (void (*add)(const void*, size_t,
     (*add) ( &exitTime, sizeof (exitTime), origin );
     (*add) ( &kernelTime, sizeof (kernelTime), origin );
     (*add) ( &userTime, sizeof (userTime), origin );
-    
+
     /* Get the minimum and maximum working set size for the current
        process.  */
     GetProcessWorkingSetSize (handle, &minimumWorkingSetSize,
@@ -677,7 +883,7 @@ _gcry_rndw32_gather_random_fast (void (*add)(const void*, size_t,
     (*add) ( &maximumWorkingSetSize,
              sizeof (maximumWorkingSetSize), origin );
   }
-  
+
 
   /* The following are fixed for the lifetime of the process so we only
    * add them once */
@@ -686,8 +892,8 @@ _gcry_rndw32_gather_random_fast (void (*add)(const void*, size_t,
       STARTUPINFO startupInfo;
 
       /* Get name of desktop, console window title, new window
-        position and size, window flags, and handles for stdin,
-        stdout, and stderr.  */
+         position and size, window flags, and handles for stdin,
+         stdout, and stderr.  */
       startupInfo.cb = sizeof (STARTUPINFO);
       GetStartupInfo (&startupInfo);
       (*add) ( &startupInfo, sizeof (STARTUPINFO), origin );
@@ -695,28 +901,61 @@ _gcry_rndw32_gather_random_fast (void (*add)(const void*, size_t,
     }
 
   /* The performance of QPC varies depending on the architecture it's
-     running on and on the OS.  Under NT it reads the CPU's 64-bit
-     timestamp counter (at least on a Pentium and newer '486's, it
-     hasn't been tested on anything without a TSC), under Win95 it
-     reads the 1.193180 MHz PIC timer.  There are vague mumblings in
-     the docs that it may fail if the appropriate hardware isn't
-     available (possibly '386's or MIPS machines running NT), but
-     who's going to run NT on a '386?.  */
-  {    
-    LARGE_INTEGER performanceCount;
-    
-    if (QueryPerformanceCounter (&performanceCount))
-      {
-        if ( debug_me )
+     running on and on the OS, the MS documentation is vague about the
+     details because it varies so much.  Under Win9x/ME it reads the
+     1.193180 MHz PIC timer.  Under NT/Win2K/XP it may or may not read the
+     64-bit TSC depending on the HAL and assorted other circumstances,
+     generally on machines with a uniprocessor HAL
+     KeQueryPerformanceCounter() uses a 3.579545MHz timer and on machines
+     with a multiprocessor or APIC HAL it uses the TSC (the exact time
+     source is controlled by the HalpUse8254 flag in the kernel).  That
+     choice of time sources is somewhat peculiar because on a
+     multiprocessor machine it's theoretically possible to get completely
+     different TSC readings depending on which CPU you're currently
+     running on, while for uniprocessor machines it's not a problem.
+     However, the kernel appears to synchronise the TSCs across CPUs at
+     boot time (it resets the TSC as part of its system init), so this
+     shouldn't really be a problem.  Under WinCE it's completely platform-
+     dependant, if there's no hardware performance counter available, it
+     uses the 1ms system timer.
+     
+     Another feature of the TSC (although it doesn't really affect us here)
+     is that mobile CPUs will turn off the TSC when they idle, Pentiums
+     will change the rate of the counter when they clock-throttle (to
+     match the current CPU speed), and hyperthreading Pentiums will turn
+     it off when both threads are idle (this more or less makes sense,
+     since the CPU will be in the halted state and not executing any
+     instructions to count).
+     
+     To make things unambiguous, we detect a CPU new enough to call RDTSC
+     directly by checking for CPUID capabilities, and fall back to QPC if
+     this isn't present.  */
+#ifdef __GNUC__  
+/*   FIXME: We need to implement the CPU feature tests first.  */
+/*   if (cpu_has_feature_rdtsc) */
+/*     { */
+/*       uint32_t lo, hi; */
+      /* We cannot use "=A", since this would use %rax on x86_64. */
+/*       __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi)); */
+      /* Ignore high 32 bits, hwich are >1s res.  */
+/*       (*add) (&lo, 4, origin ); */
+/*     } */
+/*   else */
+#endif /*!__GNUC__*/
+    {
+      LARGE_INTEGER performanceCount;
+      
+      if (QueryPerformanceCounter (&performanceCount))
+        {
+          if ( debug_me )
           log_debug ("rndw32#gather_random_fast: perf data\n");
-        (*add) (&performanceCount, sizeof (performanceCount), origin);
-      }
-    else
-      { 
-        /* Millisecond accuracy at best... */
-        DWORD aword = GetTickCount ();
-        (*add) (&aword, sizeof (aword), origin );
-      }
-  }
+          (*add) (&performanceCount, sizeof (performanceCount), origin);
+        }
+      else
+        {
+          /* Millisecond accuracy at best... */
+          DWORD aword = GetTickCount ();
+          (*add) (&aword, sizeof (aword), origin );
+        }
+    }
 }
-
index ba6963e..8b203f1 100644 (file)
@@ -100,7 +100,7 @@ dumpsexp_LDADD =
 if USE_RANDOM_DAEMON
 gcryptrnd_SOURCES = gcryptrnd.c
 gcryptrnd_CFLAGS = $(GPG_ERROR_CFLAGS) $(PTH_CFLAGS)
-gcryptrnd_LDADD = ../src/libgcrypt.la $(PTH_LIBS)
+gcryptrnd_LDADD = libgcrypt.la $(PTH_LIBS)
 
 getrandom_SOURCES = getrandom.c
 endif USE_RANDOM_DAEMON
index 2494c18..b91dbb7 100644 (file)
@@ -50,7 +50,7 @@ print_version (int with_help)
            "Usage: " PGM " [OPTIONS] [file]\n"
            "Debug tool for S-expressions\n"
            "\n"
-           "  --decimal     Print offsetc using decimal notation\n"
+           "  --decimal     Print offsets using decimal notation\n"
            "  --assume-hex  Assume input is a hex dump\n"
            "  --verbose     Show what we are doing\n"
            "  --version     Print version of the program and exit\n"