w32: New envvar GCRYPT_RNDW32_DBG.
[libgcrypt.git] / random / rndw32.c
index 4511cee..8c507ac 100644 (file)
 #include <stdlib.h>
 #include <errno.h>
 #include <string.h>
-#ifdef __GNUC__  
+#ifdef __GNUC__
 #include <stdint.h>
 #endif
 
+#include <winsock2.h>
 #include <windows.h>
 
 
@@ -141,22 +142,22 @@ typedef DWORD (WINAPI *NTPOWERINFORMATION)
 /* Type definitions for function pointers to call CryptoAPI functions. */
 typedef BOOL (WINAPI *CRYPTACQUIRECONTEXT)(HCRYPTPROV *phProv,
                                            LPCTSTR pszContainer,
-                                           LPCTSTR pszProvider, 
+                                           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 
+/* 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 
+   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, 
+   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, 
+typedef BOOL (WINAPI *RTLGENRANDOM)(PVOID RandomBuffer,
                                     ULONG RandomBufferLength);
 
 
@@ -167,13 +168,13 @@ typedef BOOL (WINAPI *RTLGENRANDOM)(PVOID RandomBuffer,
 #define SMBType         char
 #define SensorType      char
 
-typedef struct 
+typedef struct
 {
   SensorType iType;               /* Type of sensor.  */
   int Count;                      /* Number of sensor for that type.  */
 } SharedIndex;
 
-typedef struct 
+typedef struct
 {
   SensorType ssType;              /* Type of sensor */
   unsigned char ssName[12];       /* Name of sensor */
@@ -244,15 +245,16 @@ static RTLGENRANDOM        pRtlGenRandom;
 static int system_rng_available; /* Whether a system RNG is available.  */
 static HCRYPTPROV hRNGProv;      /* Handle to Intel RNG CSP. */
 
-static int debug_me;  /* Debug flag.  */
+/* The debug flag.  Debugging is enabled if the value of the envvar
+ * GCRY_RNDW32_DBG is a postive number.*/
+static int debug_me;
 
 static int system_is_w2000;     /* True if running on W2000.  */
 
 
-
 \f
 /* Try and connect to the system RNG if there's one present. */
-static void 
+static void
 init_system_rng (void)
 {
   system_rng_available = 0;
@@ -268,17 +270,17 @@ init_system_rng (void)
     GetProcAddress (hAdvAPI32, "CryptGenRandom");
   pCryptReleaseContext = (CRYPTRELEASECONTEXT)
     GetProcAddress (hAdvAPI32, "CryptReleaseContext");
-  
-  /* Get a pointer to the native randomness function if it's available.  
+
+  /* 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 
+  /* 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 nonexistent support/marketing by
      Intel, it's included here mostly for form's sake.  */
   if ( (!pCryptAcquireContext || !pCryptGenRandom || !pCryptReleaseContext
         || !pCryptAcquireContext (&hRNGProv, NULL, INTEL_DEF_PROV,
@@ -293,7 +295,7 @@ init_system_rng (void)
 
 
 /* Read data from the system RNG if availavle.  */
-static void 
+static void
 read_system_rng (void (*add)(const void*, size_t, enum random_origins),
                  enum random_origins requester)
 {
@@ -329,9 +331,8 @@ read_system_rng (void (*add)(const void*, size_t, enum random_origins),
 
 /* Read data from MBM.  This communicates via shared memory, so all we
    need to do is map a file and read the data out.  */
-#ifndef HAVE_W32CE_SYSTEM
 static void
-read_mbm_data (void (*add)(const void*, size_t, enum random_origins), 
+read_mbm_data (void (*add)(const void*, size_t, enum random_origins),
                enum random_origins requester)
 {
   HANDLE hMBMData;
@@ -352,12 +353,11 @@ read_mbm_data (void (*add)(const void*, size_t, enum random_origins),
       CloseHandle (hMBMData);
     }
 }
-#endif /*!HAVE_W32CE_SYSTEM*/
 
 
 /* Fallback method using the registry to poll the statistics.  */
 static void
-registry_poll (void (*add)(const void*, size_t, enum random_origins), 
+registry_poll (void (*add)(const void*, size_t, enum random_origins),
                enum random_origins requester)
 {
   static int cbPerfData = PERFORMANCE_BUFFER_SIZE;
@@ -420,45 +420,58 @@ registry_poll (void (*add)(const void*, size_t, enum random_origins),
      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++)
+  if (getenv ("GCRYPT_RNDW32_NOPERF"))
     {
-      dwSize = cbPerfData;
-      if ( debug_me )
-        log_debug ("rndw32#slow_gatherer_nt: get perf data\n" );
+      static int shown;
 
-      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)
+      if (!shown)
         {
-          cbPerfData += PERFORMANCE_BUFFER_STEP;
-          pPerfData = gcry_xrealloc (pPerfData, cbPerfData);
+          shown = 1;
+          log_info ("note: get performance data has been disabled\n");
         }
-      else
+    }
+  else
+    {
+      pPerfData = xmalloc (cbPerfData);
+      for (iterations=0; iterations < 10; iterations++)
         {
-          static int been_here;
+          dwSize = cbPerfData;
+          if ( debug_me )
+            log_debug ("rndw32#slow_gatherer_nt: get perf data\n" );
 
-          /* Silence the error message.  In particular under Wine (as
-             of 2008) we would get swamped with such diagnotiscs.  One
-             such diagnotiscs should be enough.  */
-          if (been_here != status)
+          status = RegQueryValueEx (HKEY_PERFORMANCE_DATA, "Global", NULL,
+                                    NULL, (LPBYTE) pPerfData, &dwSize);
+          if (status == ERROR_SUCCESS)
             {
-              been_here = status;
-              log_debug ("rndw32: get performance data problem: ec=%ld\n",
-                         status);
+              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 = xrealloc (pPerfData, cbPerfData);
+            }
+          else
+            {
+              static int been_here;
+
+              /* Silence the error message.  In particular under Wine (as
+                 of 2008) we would get swamped with such diagnotiscs.  One
+                 such diagnotiscs should be enough.  */
+              if (been_here != status)
+                {
+                  been_here = status;
+                  log_debug ("rndw32: get performance data problem: ec=%ld\n",
+                             status);
+                }
+              break;
             }
-          break;
         }
+      xfree (pPerfData);
     }
-  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
@@ -470,7 +483,7 @@ registry_poll (void (*add)(const void*, size_t, enum random_origins),
 
 
 static void
-slow_gatherer ( void (*add)(const void*, size_t, enum random_origins), 
+slow_gatherer ( void (*add)(const void*, size_t, enum random_origins),
                 enum random_origins requester )
 {
   static int is_initialized = 0;
@@ -501,7 +514,7 @@ slow_gatherer ( void (*add)(const void*, size_t, enum random_origins),
 
           status = RegQueryValueEx (hKey, "ProductType", 0, NULL,
                                     szValue, &dwSize);
-          if (status == ERROR_SUCCESS && stricmp (szValue, "WinNT"))
+          if (status == ERROR_SUCCESS && stricmp ((char*)szValue, "WinNT"))
             {
               /* Note: There are (at least) three cases for ProductType:
                  WinNT = NT Workstation, ServerNT = NT Server, LanmanNT =
@@ -557,11 +570,9 @@ slow_gatherer ( void (*add)(const void*, size_t, enum random_origins),
 
       is_initialized = 1;
     }
-  
+
   read_system_rng ( add, requester );
-#ifndef HAVE_W32CE_SYSTEM
   read_mbm_data ( add, requester );
-#endif  
 
   /* Get network statistics.    Note: Both NT Workstation and NT Server by
      default will be running both the workstation and server services.  The
@@ -570,7 +581,7 @@ slow_gatherer ( void (*add)(const void*, size_t, enum random_origins),
      In any case the network statistics return almost no randomness.  */
   {
     LPBYTE lpBuffer;
-    
+
     if (hNetAPI32
         && !pNetStatisticsGet (NULL,
                                is_workstation ? L"LanmanWorkstation" :
@@ -590,7 +601,7 @@ slow_gatherer ( void (*add)(const void*, size_t, enum random_origins),
     {
       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);
@@ -598,7 +609,7 @@ slow_gatherer ( void (*add)(const void*, size_t, enum random_origins),
                             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;
@@ -645,7 +656,7 @@ slow_gatherer ( void (*add)(const void*, size_t, enum random_origins),
      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);
+  buffer = xmalloc (PERFORMANCE_BUFFER_SIZE);
   for (dwType = 0; dwType < 64; dwType++)
     {
       switch (dwType)
@@ -749,7 +760,7 @@ slow_gatherer ( void (*add)(const void*, size_t, enum random_origins),
         }
       gcry_assert (i < 100);
     }
-  gcry_free (buffer);
+  xfree (buffer);
 
   /* We couldn't get enough results from the kernel, fall back to the
      somewhat troublesome registry poll.  */
@@ -777,16 +788,16 @@ _gcry_rndw32_gather_random (void (*add)(const void*, size_t,
   if (!is_initialized)
     {
       OSVERSIONINFO osvi = { sizeof( osvi ) };
+      const char *s;
+
+      if ((s = getenv ("GCRYPT_RNDW32_DBG")) && atoi (s) > 0)
+        debug_me = 1;
 
       GetVersionEx( &osvi );
-#ifdef HAVE_W32CE_SYSTEM
-      if (osvi.dwPlatformId != VER_PLATFORM_WIN32_CE)
-        log_fatal ("can only run on a Windows CE platform\n" );
-#else
       if (osvi.dwPlatformId != VER_PLATFORM_WIN32_NT)
         log_fatal ("can only run on a Windows NT platform\n" );
-#endif
       system_is_w2000 = (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0);
+
       init_system_rng ();
       is_initialized = 1;
     }
@@ -821,44 +832,47 @@ _gcry_rndw32_gather_random_fast (void (*add)(const void*, size_t,
      cursor position for last message, 1 ms time for last message,
      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.  */
+     and milliseconds since Windows was started. On 64-bit platform
+     some of these return values are pointers and thus 64-bit wide.
+     We discard the upper 32-bit of those values.  */
 
   {
     byte buffer[20*sizeof(ulong)], *bufptr;
 
     bufptr = buffer;
-#define ADD(f)  do { ulong along = (ulong)(f);                  \
-                     memcpy (bufptr, &along, sizeof (along) );  \
-                     bufptr += sizeof (along);                  \
-                   } while (0)
-#ifdef HAVE_W32CE_SYSTEM
-# define ADD_NO_CE(f)
-#else
-# define ADD_NO_CE(f)  ADD(f)
-#endif
-
-    ADD ( GetActiveWindow ());
-    ADD ( GetCapture ());
-    ADD ( GetClipboardOwner ());
-    ADD_NO_CE ( GetClipboardViewer ());
-    ADD ( GetCurrentProcess ());
-    ADD ( GetCurrentProcessId ());
-    ADD ( GetCurrentThread ());
-    ADD ( GetCurrentThreadId ());
-    ADD ( GetDesktopWindow ());
-    ADD ( GetFocus ());
-    ADD_NO_CE ( GetInputState ());
-    ADD ( GetMessagePos ());
-    ADD_NO_CE ( GetMessageTime ());
-    ADD ( GetOpenClipboardWindow ());
-    ADD ( GetProcessHeap ());
-    ADD_NO_CE ( GetProcessWindowStation ());
-    ADD ( GetQueueStatus (QS_ALLEVENTS));
-    ADD ( GetTickCount ());
+#define ADDINT(f)  do { ulong along = (ulong)(f);                  \
+                        memcpy (bufptr, &along, sizeof (along) );  \
+                        bufptr += sizeof (along);                  \
+                      } while (0)
+#define ADDPTR(f)  do { void *aptr = (f);                          \
+                        ADDINT((SIZE_T)aptr);                      \
+                      } while (0)
+
+    ADDPTR ( GetActiveWindow ());
+    ADDPTR ( GetCapture ());
+    ADDPTR ( GetClipboardOwner ());
+    ADDPTR ( GetClipboardViewer ());
+    ADDPTR ( GetCurrentProcess ());
+    ADDINT ( GetCurrentProcessId ());
+    ADDPTR ( GetCurrentThread ());
+    ADDINT ( GetCurrentThreadId ());
+    ADDPTR ( GetDesktopWindow ());
+    ADDPTR ( GetFocus ());
+    ADDINT ( GetInputState ());
+    ADDINT ( GetMessagePos ());
+    ADDINT ( GetMessageTime ());
+    ADDPTR ( GetOpenClipboardWindow ());
+    ADDPTR ( GetProcessHeap ());
+    ADDPTR ( GetProcessWindowStation ());
+    /* Following function in some cases stops returning events, and cannot
+       be used as an entropy source.  */
+    /*ADDINT ( GetQueueStatus (QS_ALLEVENTS));*/
+    ADDINT ( GetTickCount ());
 
     gcry_assert ( bufptr-buffer < sizeof (buffer) );
     (*add) ( buffer, bufptr-buffer, origin );
-#undef ADD
+#undef ADDINT
+#undef ADDPTR
   }
 
   /* Get multiword system information: Current caret position, current
@@ -888,7 +902,7 @@ _gcry_rndw32_gather_random_fast (void (*add)(const void*, size_t,
   {
     HANDLE handle;
     FILETIME creationTime, exitTime, kernelTime, userTime;
-    DWORD minimumWorkingSetSize, maximumWorkingSetSize;
+    SIZE_T minimumWorkingSetSize, maximumWorkingSetSize;
 
     handle = GetCurrentThread ();
     GetThreadTimes (handle, &creationTime, &exitTime,
@@ -898,15 +912,9 @@ _gcry_rndw32_gather_random_fast (void (*add)(const void*, size_t,
     (*add) ( &kernelTime, sizeof (kernelTime), origin );
     (*add) ( &userTime, sizeof (userTime), origin );
 
-#ifdef HAVE_W32CE_SYSTEM
-    handle = GetCurrentThread ();
-    GetThreadTimes (handle, &creationTime, &exitTime,
-                     &kernelTime, &userTime);
-#else
     handle = GetCurrentProcess ();
     GetProcessTimes (handle, &creationTime, &exitTime,
                      &kernelTime, &userTime);
-#endif
     (*add) ( &creationTime, sizeof (creationTime), origin );
     (*add) ( &exitTime, sizeof (exitTime), origin );
     (*add) ( &kernelTime, sizeof (kernelTime), origin );
@@ -914,20 +922,16 @@ _gcry_rndw32_gather_random_fast (void (*add)(const void*, size_t,
 
     /* Get the minimum and maximum working set size for the current
        process.  */
-#ifndef HAVE_W32CE_SYSTEM
     GetProcessWorkingSetSize (handle, &minimumWorkingSetSize,
                               &maximumWorkingSetSize);
-    (*add) ( &minimumWorkingSetSize,
-             sizeof (minimumWorkingSetSize), origin );
-    (*add) ( &maximumWorkingSetSize,
-             sizeof (maximumWorkingSetSize), origin );
-#endif /*!HAVE_W32CE_SYSTEM*/
+    /* On 64-bit system, discard the high 32-bits. */
+    (*add) ( &minimumWorkingSetSize, sizeof (int), origin );
+    (*add) ( &maximumWorkingSetSize, sizeof (int), origin );
   }
 
 
   /* The following are fixed for the lifetime of the process so we only
    * add them once */
-#ifndef HAVE_W32CE_SYSTEM
   if (!addedFixedItems)
     {
       STARTUPINFO startupInfo;
@@ -940,7 +944,6 @@ _gcry_rndw32_gather_random_fast (void (*add)(const void*, size_t,
       (*add) ( &startupInfo, sizeof (STARTUPINFO), origin );
       addedFixedItems = 1;
     }
-#endif /*!HAVE_W32CE_SYSTEM*/
 
   /* The performance of QPC varies depending on the architecture it's
      running on and on the OS, the MS documentation is vague about the
@@ -958,9 +961,9 @@ _gcry_rndw32_gather_random_fast (void (*add)(const void*, size_t,
      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
+     dependent, 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
@@ -968,11 +971,24 @@ _gcry_rndw32_gather_random_fast (void (*add)(const void*, size_t,
      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__  
+     this isn't present.
+
+     On AMD64, TSC is always available and intrinsic is provided for accessing
+     it.  */
+#ifdef __WIN64__
+    {
+      unsigned __int64 aint64;
+
+      /* Note: cryptlib does not discard upper 32 bits of TSC on WIN64, but does
+       * on WIN32.  Is this correct?  */
+      aint64 = __rdtsc();
+      (*add) (&aint64, sizeof(aint64), origin);
+    }
+#else
+#ifdef __GNUC__
 /*   FIXME: We would need to implement the CPU feature tests first.  */
 /*   if (cpu_has_feature_rdtsc) */
 /*     { */
@@ -986,7 +1002,7 @@ _gcry_rndw32_gather_random_fast (void (*add)(const void*, size_t,
 #endif /*!__GNUC__*/
     {
       LARGE_INTEGER performanceCount;
-      
+
       if (QueryPerformanceCounter (&performanceCount))
         {
           if ( debug_me )
@@ -1000,6 +1016,7 @@ _gcry_rndw32_gather_random_fast (void (*add)(const void*, size_t,
           (*add) (&aword, sizeof (aword), origin );
         }
     }
+#endif /*__WIN64__*/
 
 
 }