WinCE patches
[openssl.git] / crypto / rand / rand_win.c
index cb8e17634f87f2ef163b4af049a80d4cbbfc9033..37f172f8a4d7d7482c7dbb493cd3ea7d9a76baed 100644 (file)
 #include <openssl/rand.h>
 #include "rand_lcl.h"
 
-#if defined(WINDOWS) || defined(WIN32)
+#if defined(OPENSSL_SYS_WINDOWS) || defined(OPENSSL_SYS_WIN32)
 #include <windows.h>
 #ifndef _WIN32_WINNT
 # define _WIN32_WINNT 0x0400
  * http://developer.intel.com/design/security/rng/redist_license.htm
  */
 #define PROV_INTEL_SEC 22
-#define INTEL_DEF_PROV "Intel Hardware Cryptographic Service Provider"
+#define INTEL_DEF_PROV TEXT("Intel Hardware Cryptographic Service Provider")
 
 static void readtimer(void);
 static void readscreen(void);
 
+/* It appears like CURSORINFO, PCURSORINFO and LPCURSORINFO are only defined
+   when WINVER is 0x0500 and up, which currently only happens on Win2000.
+   Unfortunately, those are typedefs, so they're a little bit difficult to
+   detect properly.  On the other hand, the macro CURSOR_SHOWING is defined
+   within the same conditional, so it can be use to detect the absence of said
+   typedefs. */
+
+#ifndef CURSOR_SHOWING
+/*
+ * Information about the global cursor.
+ */
+typedef struct tagCURSORINFO
+{
+    DWORD   cbSize;
+    DWORD   flags;
+    HCURSOR hCursor;
+    POINT   ptScreenPos;
+} CURSORINFO, *PCURSORINFO, *LPCURSORINFO;
+
+#define CURSOR_SHOWING     0x00000001
+#endif /* CURSOR_SHOWING */
+
 typedef BOOL (WINAPI *CRYPTACQUIRECONTEXT)(HCRYPTPROV *, LPCTSTR, LPCTSTR,
                                    DWORD, DWORD);
 typedef BOOL (WINAPI *CRYPTGENRANDOM)(HCRYPTPROV, DWORD, BYTE *);
@@ -147,6 +169,21 @@ typedef BOOL (WINAPI *PROCESS32)(HANDLE, LPPROCESSENTRY32);
 typedef BOOL (WINAPI *THREAD32)(HANDLE, LPTHREADENTRY32);
 typedef BOOL (WINAPI *MODULE32)(HANDLE, LPMODULEENTRY32);
 
+#include <lmcons.h>
+#ifndef OPENSSL_SYS_WINCE
+#include <lmstats.h>
+#endif
+#if 1 /* The NET API is Unicode only.  It requires the use of the UNICODE
+       * macro.  When UNICODE is defined LPTSTR becomes LPWSTR.  LMSTR was
+       * was added to the Platform SDK to allow the NET API to be used in
+       * non-Unicode applications provided that Unicode strings were still
+       * used for input.  LMSTR is defined as LPWSTR.
+       */
+typedef NET_API_STATUS (NET_API_FUNCTION * NETSTATGET)
+        (LPWSTR, LPWSTR, DWORD, DWORD, LPBYTE*);
+typedef NET_API_STATUS (NET_API_FUNCTION * NETFREE)(LPBYTE);
+#endif /* 1 */
+
 int RAND_poll(void)
 {
        MEMORYSTATUS m;
@@ -155,36 +192,157 @@ int RAND_poll(void)
        DWORD w;
        HWND h;
 
-       HMODULE advapi, kernel, user;
-       CRYPTACQUIRECONTEXT acquire;
-       CRYPTGENRANDOM gen;
-       CRYPTRELEASECONTEXT release;
+       HMODULE advapi, kernel, user, netapi;
+       CRYPTACQUIRECONTEXT acquire = 0;
+       CRYPTGENRANDOM gen = 0;
+       CRYPTRELEASECONTEXT release = 0;
+#if 1 /* There was previously a problem with NETSTATGET.  Currently, this
+       * section is still experimental, but if all goes well, this conditional
+       * will be removed
+       */
+       NETSTATGET netstatget = 0;
+       NETFREE netfree = 0;
+#endif /* 1 */
+
+       /* Determine the OS version we are on so we can turn off things 
+        * that do not work properly.
+        */
+        OSVERSIONINFO osverinfo ;
+        osverinfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO) ;
+        GetVersionEx( &osverinfo ) ;
+
+#ifdef OPENSSL_SYS_WINCE
+       /* poll the CryptoAPI PRNG */
+       /* The CryptoAPI returns sizeof(buf) bytes of randomness */
+       if (CryptAcquireContext(&hProvider, 0, 0, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))
+               {
+               if (CryptGenRandom(hProvider, sizeof(buf), buf))
+                       RAND_add(buf, sizeof(buf), sizeof(buf));
+               CryptReleaseContext(hProvider, 0); 
+               }
+#endif
 
        /* load functions dynamically - not available on all systems */
-       advapi = GetModuleHandle("ADVAPI32.DLL");
-       kernel = GetModuleHandle("KERNEL32.DLL");
-       user = GetModuleHandle("USER32.DLL");
+       advapi = LoadLibrary(TEXT("ADVAPI32.DLL"));
+       kernel = LoadLibrary(TEXT("KERNEL32.DLL"));
+       user = LoadLibrary(TEXT("USER32.DLL"));
+       netapi = LoadLibrary(TEXT("NETAPI32.DLL"));
+
+#ifndef OPENSSL_SYS_WINCE
+#if 1 /* There was previously a problem with NETSTATGET.  Currently, this
+       * section is still experimental, but if all goes well, this conditional
+       * will be removed
+       */
+       if (netapi)
+               {
+               netstatget = (NETSTATGET) GetProcAddress(netapi,TEXT("NetStatisticsGet"));
+               netfree = (NETFREE) GetProcAddress(netapi,TEXT("NetApiBufferFree"));
+               }
+
+       if (netstatget && netfree)
+               {
+               LPBYTE outbuf;
+               /* NetStatisticsGet() is a Unicode only function
+                * STAT_WORKSTATION_0 contains 45 fields and STAT_SERVER_0
+                * contains 17 fields.  We treat each field as a source of
+                * one byte of entropy.
+                 */
+
+               if (netstatget(NULL, L"LanmanWorkstation", 0, 0, &outbuf) == 0)
+                       {
+                       RAND_add(outbuf, sizeof(STAT_WORKSTATION_0), 45);
+                       netfree(outbuf);
+                       }
+               if (netstatget(NULL, L"LanmanServer", 0, 0, &outbuf) == 0)
+                       {
+                       RAND_add(outbuf, sizeof(STAT_SERVER_0), 17);
+                       netfree(outbuf);
+                       }
+               }
+
+       if (netapi)
+               FreeLibrary(netapi);
+#endif /* 1 */
+#endif /* !OPENSSL_SYS_WINCE */
+#ifndef OPENSSL_SYS_WINCE
+        /* It appears like this can cause an exception deep within ADVAPI32.DLL
+         * at random times on Windows 2000.  Reported by Jeffrey Altman.  
+         * Only use it on NT.
+        */
+       /* Wolfgang Marczy <WMarczy@topcall.co.at> reports that
+        * the RegQueryValueEx call below can hang on NT4.0 (SP6).
+        * So we don't use this at all for now. */
+#if 0
+        if ( osverinfo.dwPlatformId == VER_PLATFORM_WIN32_NT &&
+               osverinfo.dwMajorVersion < 5)
+               {
+               /* Read Performance Statistics from NT/2000 registry
+                * The size of the performance data can vary from call
+                * to call so we must guess the size of the buffer to use
+                * and increase its size if we get an ERROR_MORE_DATA
+                * return instead of ERROR_SUCCESS.
+                */
+               LONG   rc=ERROR_MORE_DATA;
+               char * buf=NULL;
+               DWORD bufsz=0;
+               DWORD length;
+
+               while (rc == ERROR_MORE_DATA)
+                       {
+                       buf = realloc(buf,bufsz+8192);
+                       if (!buf)
+                               break;
+                       bufsz += 8192;
+
+                       length = bufsz;
+                       rc = RegQueryValueEx(HKEY_PERFORMANCE_DATA, TEXT("Global"),
+                               NULL, NULL, buf, &length);
+                       }
+               if (rc == ERROR_SUCCESS)
+                       {
+                        /* For entropy count assume only least significant
+                        * byte of each DWORD is random.
+                        */
+                       RAND_add(&length, sizeof(length), 0);
+                       RAND_add(buf, length, length / 4.0);
+
+                       /* Close the Registry Key to allow Windows to cleanup/close
+                        * the open handle
+                        * Note: The 'HKEY_PERFORMANCE_DATA' key is implicitly opened
+                        *       when the RegQueryValueEx above is done.  However, if
+                        *       it is not explicitly closed, it can cause disk
+                        *       partition manipulation problems.
+                        */
+                       RegCloseKey(HKEY_PERFORMANCE_DATA);
+                       }
+               if (buf)
+                       free(buf);
+               }
+#endif
+#endif /* !OPENSSL_SYS_WINCE */
 
        if (advapi)
                {
                acquire = (CRYPTACQUIRECONTEXT) GetProcAddress(advapi,
-                       "CryptAcquireContextA");
+                       TEXT("CryptAcquireContextA"));
                gen = (CRYPTGENRANDOM) GetProcAddress(advapi,
-                       "CryptGenRandom");
+                       TEXT("CryptGenRandom"));
                release = (CRYPTRELEASECONTEXT) GetProcAddress(advapi,
-                       "CryptReleaseContext");
+                       TEXT("CryptReleaseContext"));
                }
 
        if (acquire && gen && release)
                {
                /* poll the CryptoAPI PRNG */
+                /* The CryptoAPI returns sizeof(buf) bytes of randomness */
                if (acquire(&hProvider, 0, 0, PROV_RSA_FULL,
                        CRYPT_VERIFYCONTEXT))
                        {
                        if (gen(hProvider, sizeof(buf), buf) != 0)
                                {
                                RAND_add(buf, sizeof(buf), 0);
-#ifdef DEBUG
+#if 0
                                printf("randomness from PROV_RSA_FULL\n");
 #endif
                                }
@@ -196,8 +354,8 @@ int RAND_poll(void)
                        {
                        if (gen(hProvider, sizeof(buf), buf) != 0)
                                {
-                               RAND_add(buf, sizeof(buf), 0);
-#ifdef DEBUG
+                               RAND_add(buf, sizeof(buf), sizeof(buf));
+#if 0
                                printf("randomness from PROV_INTEL_SEC\n");
 #endif
                                }
@@ -205,6 +363,9 @@ int RAND_poll(void)
                        }
                }
 
+        if (advapi)
+               FreeLibrary(advapi);
+
        /* timer data */
        readtimer();
        
@@ -214,7 +375,7 @@ int RAND_poll(void)
 
        /* process ID */
        w = GetCurrentProcessId();
-       RAND_add(&w, sizeof(w), 0);
+       RAND_add(&w, sizeof(w), 1);
 
        if (user)
                {
@@ -222,39 +383,55 @@ int RAND_poll(void)
                GETFOREGROUNDWINDOW win;
                GETQUEUESTATUS queue;
 
-               win = (GETFOREGROUNDWINDOW) GetProcAddress(user, "GetForegroundWindow");
-               cursor = (GETCURSORINFO) GetProcAddress(user, "GetCursorInfo");
-               queue = (GETQUEUESTATUS) GetProcAddress(user, "GetQueueStatus");
+               win = (GETFOREGROUNDWINDOW) GetProcAddress(user, TEXT("GetForegroundWindow"));
+               cursor = (GETCURSORINFO) GetProcAddress(user, TEXT("GetCursorInfo"));
+               queue = (GETQUEUESTATUS) GetProcAddress(user, TEXT("GetQueueStatus"));
 
                if (win)
-               {
+                       {
                        /* window handle */
                        h = win();
                        RAND_add(&h, sizeof(h), 0);
-               }
-
+                       }
+               if (cursor)
+                       {
+                       /* unfortunately, its not safe to call GetCursorInfo()
+                        * on NT4 even though it exists in SP3 (or SP6) and
+                        * higher.
+                        */
+                       if ( osverinfo.dwPlatformId == VER_PLATFORM_WIN32_NT &&
+                               osverinfo.dwMajorVersion < 5)
+                               cursor = 0;
+                       }
                if (cursor)
                        {
                        /* cursor position */
-                       cursor((PCURSORINFO)buf);
-                       RAND_add(buf, sizeof(buf), 0);
+                        /* assume 2 bytes of entropy */
+                       CURSORINFO ci;
+                       ci.cbSize = sizeof(CURSORINFO);
+                       if (cursor(&ci))
+                               RAND_add(&ci, ci.cbSize, 2);
                        }
 
                if (queue)
                        {
                        /* message queue status */
+                        /* assume 1 byte of entropy */
                        w = queue(QS_ALLEVENTS);
-                       RAND_add(&w, sizeof(w), 0);
+                       RAND_add(&w, sizeof(w), 1);
                        }
+
+               FreeLibrary(user);
                }
 
        /* Toolhelp32 snapshot: enumerate processes, threads, modules and heap
         * http://msdn.microsoft.com/library/psdk/winbase/toolhelp_5pfd.htm
-        * (Win 9x only, not available on NT)
+        * (Win 9x and 2000 only, not available on NT)
         *
         * This seeding method was proposed in Peter Gutmann, Software
         * Generation of Practically Strong Random Numbers,
-        * http://www.cs.auckland.ac.nz/~pgut001/pubs/random2.pdf
+        * http://www.usenix.org/publications/library/proceedings/sec98/gutmann.html
+     * revised version at http://www.cryptoengines.com/~peter/06_random.pdf
         * (The assignment of entropy estimates below is arbitrary, but based
         * on Peter's analysis the full poll appears to be safe. Additional
         * interactive seeding is encouraged.)
@@ -279,17 +456,17 @@ int RAND_poll(void)
                MODULEENTRY32 m;
 
                snap = (CREATETOOLHELP32SNAPSHOT)
-                 GetProcAddress(kernel, "CreateToolhelp32Snapshot");
-               heap_first = (HEAP32FIRST) GetProcAddress(kernel, "Heap32First");
-               heap_next = (HEAP32NEXT) GetProcAddress(kernel, "Heap32Next");
-               heaplist_first = (HEAP32LIST) GetProcAddress(kernel, "Heap32ListFirst");
-               heaplist_next = (HEAP32LIST) GetProcAddress(kernel, "Heap32ListNext");
-               process_first = (PROCESS32) GetProcAddress(kernel, "Process32First");
-               process_next = (PROCESS32) GetProcAddress(kernel, "Process32Next");
-               thread_first = (THREAD32) GetProcAddress(kernel, "Thread32First");
-               thread_next = (THREAD32) GetProcAddress(kernel, "Thread32Next");
-               module_first = (MODULE32) GetProcAddress(kernel, "Module32First");
-               module_next = (MODULE32) GetProcAddress(kernel, "Module32Next");
+                       GetProcAddress(kernel, TEXT("CreateToolhelp32Snapshot"));
+               heap_first = (HEAP32FIRST) GetProcAddress(kernel, TEXT("Heap32First"));
+               heap_next = (HEAP32NEXT) GetProcAddress(kernel, TEXT("Heap32Next"));
+               heaplist_first = (HEAP32LIST) GetProcAddress(kernel, TEXT("Heap32ListFirst"));
+               heaplist_next = (HEAP32LIST) GetProcAddress(kernel, TEXT("Heap32ListNext"));
+               process_first = (PROCESS32) GetProcAddress(kernel, TEXT("Process32First"));
+               process_next = (PROCESS32) GetProcAddress(kernel, TEXT("Process32Next"));
+               thread_first = (THREAD32) GetProcAddress(kernel, TEXT("Thread32First"));
+               thread_next = (THREAD32) GetProcAddress(kernel, TEXT("Thread32Next"));
+               module_first = (MODULE32) GetProcAddress(kernel, TEXT("Module32First"));
+               module_next = (MODULE32) GetProcAddress(kernel, TEXT("Module32Next"));
 
                if (snap && heap_first && heap_next && heaplist_first &&
                        heaplist_next && process_first && process_next &&
@@ -298,48 +475,73 @@ int RAND_poll(void)
                        != NULL)
                        {
                        /* heap list and heap walking */
+                        /* HEAPLIST32 contains 3 fields that will change with
+                         * each entry.  Consider each field a source of 1 byte
+                         * of entropy.
+                         * HEAPENTRY32 contains 5 fields that will change with 
+                         * each entry.  Consider each field a source of 1 byte
+                         * of entropy.
+                         */
                        hlist.dwSize = sizeof(HEAPLIST32);              
                        if (heaplist_first(handle, &hlist))
                                do
                                        {
-                                       RAND_add(&hlist, hlist.dwSize, 0);
+                                       RAND_add(&hlist, hlist.dwSize, 3);
                                        hentry.dwSize = sizeof(HEAPENTRY32);
                                        if (heap_first(&hentry,
                                                hlist.th32ProcessID,
                                                hlist.th32HeapID))
+                                               {
+                                               int entrycnt = 80;
                                                do
                                                        RAND_add(&hentry,
-                                                               hentry.dwSize, 0);
-                                               while (heap_next(&hentry));
+                                                               hentry.dwSize, 5);
+                                               while (heap_next(&hentry)
+                                                       && --entrycnt > 0);
+                                               }
                                        } while (heaplist_next(handle,
                                                &hlist));
-
+                       
                        /* process walking */
+                        /* PROCESSENTRY32 contains 9 fields that will change
+                         * with each entry.  Consider each field a source of
+                         * 1 byte of entropy.
+                         */
                        p.dwSize = sizeof(PROCESSENTRY32);
                        if (process_first(handle, &p))
                                do
-                                       RAND_add(&p, p.dwSize, 0);
+                                       RAND_add(&p, p.dwSize, 9);
                                while (process_next(handle, &p));
-                       
+
                        /* thread walking */
+                        /* THREADENTRY32 contains 6 fields that will change
+                         * with each entry.  Consider each field a source of
+                         * 1 byte of entropy.
+                         */
                        t.dwSize = sizeof(THREADENTRY32);
                        if (thread_first(handle, &t))
                                do
-                                       RAND_add(&t, t.dwSize, 0);
+                                       RAND_add(&t, t.dwSize, 6);
                                while (thread_next(handle, &t));
-                       
+
                        /* module walking */
+                        /* MODULEENTRY32 contains 9 fields that will change
+                         * with each entry.  Consider each field a source of
+                         * 1 byte of entropy.
+                         */
                        m.dwSize = sizeof(MODULEENTRY32);
                        if (module_first(handle, &m))
                                do
-                                       RAND_add(&m, m.dwSize, 1);
+                                       RAND_add(&m, m.dwSize, 9);
                                while (module_next(handle, &m));
-                       
+
                        CloseHandle(handle);
                        }
+
+               FreeLibrary(kernel);
                }
 
-#ifdef DEBUG
+#if 0
        printf("Exiting RAND_poll\n");
 #endif
 
@@ -396,16 +598,18 @@ void RAND_screen(void) /* function available for backward compatibility */
 /* feed timing information to the PRNG */
 static void readtimer(void)
 {
-       DWORD w, cyclecount;
+       DWORD w;
        LARGE_INTEGER l;
        static int have_perfc = 1;
-#ifndef __GNUC__
+#if defined(_MSC_VER) && !defined(OPENSSL_SYS_WINCE)
        static int have_tsc = 1;
+       DWORD cyclecount;
 
        if (have_tsc) {
          __try {
            __asm {
-             rdtsc
+             _emit 0x0f
+             _emit 0x31
              mov cyclecount, eax
              }
            RAND_add(&cyclecount, sizeof(cyclecount), 1);
@@ -450,6 +654,7 @@ static void readtimer(void)
 
 static void readscreen(void)
 {
+#ifndef OPENSSL_SYS_WINCE
   HDC          hScrDC;         /* screen DC */
   HDC          hMemDC;         /* memory DC */
   HBITMAP      hBitmap;        /* handle for our bitmap */
@@ -463,7 +668,7 @@ static void readscreen(void)
   int          n = 16;         /* number of screen lines to grab at a time */
 
   /* Create a screen DC and a memory DC compatible to screen DC */
-  hScrDC = CreateDC("DISPLAY", NULL, NULL, NULL);
+  hScrDC = CreateDC(TEXT("DISPLAY"), NULL, NULL, NULL);
   hMemDC = CreateCompatibleDC(hScrDC);
 
   /* Get screen resolution */
@@ -510,51 +715,7 @@ static void readscreen(void)
   DeleteObject(hBitmap);
   DeleteDC(hMemDC);
   DeleteDC(hScrDC);
-}
-
-#else /* Unix version */
-
-#include <time.h>
-
-int RAND_poll(void)
-{
-       unsigned long l;
-       pid_t curr_pid = getpid();
-#ifdef DEVRANDOM
-       FILE *fh;
-#endif
-
-#ifdef DEVRANDOM
-       /* Use a random entropy pool device. Linux, FreeBSD and OpenBSD
-        * have this. Use /dev/urandom if you can as /dev/random may block
-        * if it runs out of random entries.  */
-
-       if ((fh = fopen(DEVRANDOM, "r")) != NULL)
-               {
-               unsigned char tmpbuf[ENTROPY_NEEDED];
-               int n;
-               
-               setvbuf(fh, NULL, _IONBF, 0);
-               n=fread((unsigned char *)tmpbuf,1,ENTROPY_NEEDED,fh);
-               fclose(fh);
-               RAND_add(tmpbuf,sizeof tmpbuf,n);
-               memset(tmpbuf,0,n);
-               }
-#endif
-
-       /* put in some default random data, we need more than just this */
-       l=curr_pid;
-       RAND_add(&l,sizeof(l),0);
-       l=getuid();
-       RAND_add(&l,sizeof(l),0);
-
-       l=time(NULL);
-       RAND_add(&l,sizeof(l),0);
-
-#ifdef DEVRANDOM
-       return 1;
-#endif
-       return 0;
+#endif /* !OPENSSL_SYS_WINCE */
 }
 
 #endif