X-Git-Url: https://git.openssl.org/gitweb/?p=openssl.git;a=blobdiff_plain;f=crypto%2Frand%2Frand_win.c;h=07ab4eb8dfea5c219aaf88bc05656a7b7b9e3341;hp=222ea4a608befa93cdeede4be0dfa0a6209608a6;hb=06b433acade4732a7f647faace14dfd6950ac49c;hpb=130832150c1313824868b154cccda3ace88fa950 diff --git a/crypto/rand/rand_win.c b/crypto/rand/rand_win.c index 222ea4a608..07ab4eb8df 100644 --- a/crypto/rand/rand_win.c +++ b/crypto/rand/rand_win.c @@ -109,51 +109,533 @@ * */ +#define OPENSSL_FIPSAPI -#if defined(WINDOWS) || defined(WIN32) #include "cryptlib.h" -#include #include -/* XXX There are probably other includes missing here ... */ - - -#if !defined(USE_MD5_RAND) && !defined(USE_SHA1_RAND) && !defined(USE_MDC2_RAND) && !defined(USE_MD2_RAND) -#if !defined(NO_SHA) && !defined(NO_SHA1) -#define USE_SHA1_RAND -#elif !defined(NO_MD5) -#define USE_MD5_RAND -#elif !defined(NO_MDC2) && !defined(NO_DES) -#define USE_MDC2_RAND -#elif !defined(NO_MD2) -#define USE_MD2_RAND -#else -#error No message digest algorithm available +#include "rand_lcl.h" + +#if defined(OPENSSL_SYS_WINDOWS) || defined(OPENSSL_SYS_WIN32) +#include +#ifndef _WIN32_WINNT +# define _WIN32_WINNT 0x0400 #endif +#include +#include + +/* Limit the time spent walking through the heap, processes, threads and modules to + a maximum of 1000 miliseconds each, unless CryptoGenRandom failed */ +#define MAXDELAY 1000 + +/* Intel hardware RNG CSP -- available from + * http://developer.intel.com/design/security/rng/redist_license.htm + */ +#define PROV_INTEL_SEC 22 +#define INTEL_DEF_PROV L"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 */ + +#if !defined(OPENSSL_SYS_WINCE) +typedef BOOL (WINAPI *CRYPTACQUIRECONTEXTW)(HCRYPTPROV *, LPCWSTR, LPCWSTR, + DWORD, DWORD); +typedef BOOL (WINAPI *CRYPTGENRANDOM)(HCRYPTPROV, DWORD, BYTE *); +typedef BOOL (WINAPI *CRYPTRELEASECONTEXT)(HCRYPTPROV, DWORD); + +typedef HWND (WINAPI *GETFOREGROUNDWINDOW)(VOID); +typedef BOOL (WINAPI *GETCURSORINFO)(PCURSORINFO); +typedef DWORD (WINAPI *GETQUEUESTATUS)(UINT); + +typedef HANDLE (WINAPI *CREATETOOLHELP32SNAPSHOT)(DWORD, DWORD); +typedef BOOL (WINAPI *CLOSETOOLHELP32SNAPSHOT)(HANDLE); +typedef BOOL (WINAPI *HEAP32FIRST)(LPHEAPENTRY32, DWORD, size_t); +typedef BOOL (WINAPI *HEAP32NEXT)(LPHEAPENTRY32); +typedef BOOL (WINAPI *HEAP32LIST)(HANDLE, LPHEAPLIST32); +typedef BOOL (WINAPI *PROCESS32)(HANDLE, LPPROCESSENTRY32); +typedef BOOL (WINAPI *THREAD32)(HANDLE, LPTHREADENTRY32); +typedef BOOL (WINAPI *MODULE32)(HANDLE, LPMODULEENTRY32); + +#include +#include +#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 */ +#endif /* !OPENSSL_SYS_WINCE */ + +int RAND_poll(void) +{ + MEMORYSTATUS m; + HCRYPTPROV hProvider = 0; + DWORD w; + int good = 0; + + /* 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 ) ; + +#if defined(OPENSSL_SYS_WINCE) +# if defined(_WIN32_WCE) && _WIN32_WCE>=300 +/* Even though MSDN says _WIN32_WCE>=210, it doesn't seem to be available + * in commonly available implementations prior 300... */ + { + BYTE buf[64]; + /* poll the CryptoAPI PRNG */ + /* The CryptoAPI returns sizeof(buf) bytes of randomness */ + if (CryptAcquireContextW(&hProvider, NULL, NULL, PROV_RSA_FULL, + CRYPT_VERIFYCONTEXT)) + { + if (CryptGenRandom(hProvider, sizeof(buf), buf)) + RAND_add(buf, sizeof(buf), sizeof(buf)); + CryptReleaseContext(hProvider, 0); + } + } +# endif +#else /* OPENSSL_SYS_WINCE */ + /* + * None of below libraries are present on Windows CE, which is + * why we #ifndef the whole section. This also excuses us from + * handling the GetProcAddress issue. The trouble is that in + * real Win32 API GetProcAddress is available in ANSI flavor + * only. In WinCE on the other hand GetProcAddress is a macro + * most commonly defined as GetProcAddressW, which accepts + * Unicode argument. If we were to call GetProcAddress under + * WinCE, I'd recommend to either redefine GetProcAddress as + * GetProcAddressA (there seem to be one in common CE spec) or + * implement own shim routine, which would accept ANSI argument + * and expand it to Unicode. + */ + { + /* load functions dynamically - not available on all systems */ + HMODULE advapi = LoadLibrary(TEXT("ADVAPI32.DLL")); + HMODULE kernel = LoadLibrary(TEXT("KERNEL32.DLL")); + HMODULE user = NULL; + HMODULE netapi = LoadLibrary(TEXT("NETAPI32.DLL")); + CRYPTACQUIRECONTEXTW acquire = NULL; + CRYPTGENRANDOM gen = NULL; + CRYPTRELEASECONTEXT release = NULL; + NETSTATGET netstatget = NULL; + NETFREE netfree = NULL; + BYTE buf[64]; + + if (netapi) + { + netstatget = (NETSTATGET) GetProcAddress(netapi,"NetStatisticsGet"); + netfree = (NETFREE) GetProcAddress(netapi,"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); + + /* 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 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 -#if defined(USE_MD5_RAND) -#include -#define MD_DIGEST_LENGTH MD5_DIGEST_LENGTH -#define MD(a,b,c) MD5(a,b,c) -#elif defined(USE_SHA1_RAND) -#include -#define MD_DIGEST_LENGTH SHA_DIGEST_LENGTH -#define MD(a,b,c) SHA1(a,b,c) -#elif defined(USE_MDC2_RAND) -#include -#define MD_DIGEST_LENGTH MDC2_DIGEST_LENGTH -#define MD(a,b,c) MDC2(a,b,c) -#elif defined(USE_MD2_RAND) -#include -#define MD_DIGEST_LENGTH MD2_DIGEST_LENGTH -#define MD(a,b,c) MD2(a,b,c) + if (advapi) + { + /* + * If it's available, then it's available in both ANSI + * and UNICODE flavors even in Win9x, documentation says. + * We favor Unicode... + */ + acquire = (CRYPTACQUIRECONTEXTW) GetProcAddress(advapi, + "CryptAcquireContextW"); + gen = (CRYPTGENRANDOM) GetProcAddress(advapi, + "CryptGenRandom"); + release = (CRYPTRELEASECONTEXT) GetProcAddress(advapi, + "CryptReleaseContext"); + } + + if (acquire && gen && release) + { + /* poll the CryptoAPI PRNG */ + /* The CryptoAPI returns sizeof(buf) bytes of randomness */ + if (acquire(&hProvider, NULL, NULL, PROV_RSA_FULL, + CRYPT_VERIFYCONTEXT)) + { + if (gen(hProvider, sizeof(buf), buf) != 0) + { + RAND_add(buf, sizeof(buf), 0); + good = 1; +#if 0 + printf("randomness from PROV_RSA_FULL\n"); +#endif + } + release(hProvider, 0); + } + + /* poll the Pentium PRG with CryptoAPI */ + if (acquire(&hProvider, 0, INTEL_DEF_PROV, PROV_INTEL_SEC, 0)) + { + if (gen(hProvider, sizeof(buf), buf) != 0) + { + RAND_add(buf, sizeof(buf), sizeof(buf)); + good = 1; +#if 0 + printf("randomness from PROV_INTEL_SEC\n"); #endif + } + release(hProvider, 0); + } + } + + if (advapi) + FreeLibrary(advapi); + + if ((osverinfo.dwPlatformId != VER_PLATFORM_WIN32_NT || + !OPENSSL_isservice()) && + (user = LoadLibrary(TEXT("USER32.DLL")))) + { + GETCURSORINFO cursor; + GETFOREGROUNDWINDOW win; + GETQUEUESTATUS queue; + + win = (GETFOREGROUNDWINDOW) GetProcAddress(user, "GetForegroundWindow"); + cursor = (GETCURSORINFO) GetProcAddress(user, "GetCursorInfo"); + queue = (GETQUEUESTATUS) GetProcAddress(user, "GetQueueStatus"); + + if (win) + { + /* window handle */ + HWND 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 */ + /* 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), 1); + } + + FreeLibrary(user); + } + /* Toolhelp32 snapshot: enumerate processes, threads, modules and heap + * http://msdn.microsoft.com/library/psdk/winbase/toolhelp_5pfd.htm + * (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.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.) + */ + + if (kernel) + { + CREATETOOLHELP32SNAPSHOT snap; + CLOSETOOLHELP32SNAPSHOT close_snap; + HANDLE handle; + + HEAP32FIRST heap_first; + HEAP32NEXT heap_next; + HEAP32LIST heaplist_first, heaplist_next; + PROCESS32 process_first, process_next; + THREAD32 thread_first, thread_next; + MODULE32 module_first, module_next; + + HEAPLIST32 hlist; + HEAPENTRY32 hentry; + PROCESSENTRY32 p; + THREADENTRY32 t; + MODULEENTRY32 m; + DWORD starttime = 0; + + snap = (CREATETOOLHELP32SNAPSHOT) + GetProcAddress(kernel, "CreateToolhelp32Snapshot"); + close_snap = (CLOSETOOLHELP32SNAPSHOT) + GetProcAddress(kernel, "CloseToolhelp32Snapshot"); + 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"); + + if (snap && heap_first && heap_next && heaplist_first && + heaplist_next && process_first && process_next && + thread_first && thread_next && module_first && + module_next && (handle = snap(TH32CS_SNAPALL,0)) + != INVALID_HANDLE_VALUE) + { + /* 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. + */ + ZeroMemory(&hlist, sizeof(HEAPLIST32)); + hlist.dwSize = sizeof(HEAPLIST32); + if (good) starttime = GetTickCount(); +#ifdef _MSC_VER + if (heaplist_first(handle, &hlist)) + { + /* + following discussion on dev ML, exception on WinCE (or other Win + platform) is theoretically of unknown origin; prevent infinite + loop here when this theoretical case occurs; otherwise cope with + the expected (MSDN documented) exception-throwing behaviour of + Heap32Next() on WinCE. + + based on patch in original message by Tanguy Fautré (2009/03/02) + Subject: RAND_poll() and CreateToolhelp32Snapshot() stability + */ + int ex_cnt_limit = 42; + do + { + RAND_add(&hlist, hlist.dwSize, 3); + __try + { + ZeroMemory(&hentry, sizeof(HEAPENTRY32)); + hentry.dwSize = sizeof(HEAPENTRY32); + if (heap_first(&hentry, + hlist.th32ProcessID, + hlist.th32HeapID)) + { + int entrycnt = 80; + do + RAND_add(&hentry, + hentry.dwSize, 5); + while (heap_next(&hentry) + && (!good || (GetTickCount()-starttime) 0); + } + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + /* ignore access violations when walking the heap list */ + ex_cnt_limit--; + } + } while (heaplist_next(handle, &hlist) + && (!good || (GetTickCount()-starttime) 0); + } + +#else + if (heaplist_first(handle, &hlist)) + { + do + { + 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, 5); + while (heap_next(&hentry) + && --entrycnt > 0); + } + } while (heaplist_next(handle, &hlist) + && (!good || (GetTickCount()-starttime); + * ; * the original copyright message is: * * (C) Copyright Microsoft Corp. 1993. All rights reserved. @@ -210,19 +736,10 @@ int RAND_event(UINT iMsg, WPARAM wParam, LPARAM lParam) * Microsoft has no warranty obligations or liability for any * Sample Application Files which are modified. */ -/* - * I have modified the loading of bytes via RAND_seed() mechanism since - * the original would have been very very CPU intensive since RAND_seed() - * does an MD5 per 16 bytes of input. The cost to digest 16 bytes is the same - * as that to digest 56 bytes. So under the old system, a screen of - * 1024*768*256 would have been CPU cost of approximately 49,000 56 byte MD5 - * digests or digesting 2.7 mbytes. What I have put in place would - * be 48 16k MD5 digests, or effectively 48*16+48 MD5 bytes or 816 kbytes - * or about 3.5 times as much. - * - eric - */ -void RAND_screen(void) + +static void readscreen(void) { +#if !defined(OPENSSL_SYS_WINCE) && !defined(OPENSSL_SYS_WIN32_CYGWIN) HDC hScrDC; /* screen DC */ HDC hMemDC; /* memory DC */ HBITMAP hBitmap; /* handle for our bitmap */ @@ -235,8 +752,11 @@ void RAND_screen(void) int y; /* y-coordinate of screen lines to grab */ int n = 16; /* number of screen lines to grab at a time */ + if (GetVersion() < 0x80000000 && OPENSSL_isservice()>0) + return; + /* 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 */ @@ -266,11 +786,11 @@ void RAND_screen(void) /* Copy bitmap bits from memory DC to bmbits */ GetBitmapBits(hBitmap, size, bmbits); - /* Get the MD5 of the bitmap */ + /* Get the hash of the bitmap */ MD(bmbits,size,md); - /* Seed the random generator with the MD5 digest */ - RAND_seed(md, MD_DIGEST_LENGTH); + /* Seed the random generator with the hash value */ + RAND_add(md, MD_DIGEST_LENGTH, 0); } OPENSSL_free(bmbits); @@ -283,12 +803,7 @@ void RAND_screen(void) DeleteObject(hBitmap); DeleteDC(hMemDC); DeleteDC(hScrDC); +#endif /* !OPENSSL_SYS_WINCE */ } -#else - -# if PEDANTIC -static void *dummy=&dummy; -# endif - #endif