// randwin.cpp #include <stdio.h> #include <tchar.h> #define OPENSSL_SYS_WINDOWS 1
// Don't need these to run the relevant parts of RAND_poll //#include "cryptlib.h" //#include <openssl/rand.h> //#include "rand_lcl.h" #if defined(OPENSSL_SYS_WINDOWS) || defined(OPENSSL_SYS_WIN32) #include <windows.h> #ifndef _WIN32_WINNT # define _WIN32_WINNT 0x0400 #endif #include <wincrypt.h> #include <tlhelp32.h> /* 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" /* 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 <lmcons.h> #include <lmstats.h> #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 */ //////////////////////////// My stubs void RAND_add(void* buf, SIZE_T buffer_size, int entropy_size) { //printf("Add%d ", entropy_size); } int RAND_poll(void) { HCRYPTPROV hProvider = 0; int good = 0; printf("Starting RAND_poll.\n"); { /* 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 (advapi) { printf("ADVAPI32.DLL loaded.\n"); /* * 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) { printf("Got ProcAddresses for CryptoAPI functions\n"); /* poll the CryptoAPI PRNG */ /* The CryptoAPI returns sizeof(buf) bytes of randomness */ if (acquire(&hProvider, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) { printf("Acquired RSA_FULL context.\n"); if (gen(hProvider, sizeof(buf), buf) != 0) { RAND_add(buf, sizeof(buf), 0); good = 1; printf("randomness from PROV_RSA_FULL\n"); } release(hProvider, 0); } /* poll the Pentium PRG with CryptoAPI */ if (acquire(&hProvider, 0, INTEL_DEF_PROV, PROV_INTEL_SEC, 0)) { printf("Acquired Pentium PRG context\n"); if (gen(hProvider, sizeof(buf), buf) != 0) { RAND_add(buf, sizeof(buf), sizeof(buf)); good = 1; printf("randomness from PROV_INTEL_SEC\n"); } release(hProvider, 0); } } if (advapi) FreeLibrary(advapi); printf("good: %d\n", good); if (kernel) { printf("KERNEL32.DLL loaded.\n"); 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; DWORD stoptime = 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) { printf("Got ProcAddresses for Heap/Process/Thread/Module functions.\n"); /* 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); DWORD starttime = GetTickCount(); if (good) stoptime = starttime + MAXDELAY; printf("starttime: %d\n", starttime); printf("stoptime: %d\n", stoptime); if (heaplist_first(handle, &hlist)) { printf("Got heaplist_first.\n"); do { RAND_add(&hlist, hlist.dwSize, 3); hentry.dwSize = sizeof(HEAPENTRY32); if (heap_first(&hentry, hlist.th32ProcessID, hlist.th32HeapID)) { printf("heap1st "); int entrycnt = 80; do { RAND_add(&hentry, hentry.dwSize, 5); printf("."); } while (heap_next(&hentry) && --entrycnt > 0); } } while (heaplist_next(handle, &hlist) && GetTickCount() < stoptime); DWORD realstoptime = GetTickCount(); printf("\nreal stop time: %d\n", realstoptime); printf("elapsed time: %d\n", realstoptime - starttime); } } FreeLibrary(kernel); } } printf("Exiting RAND_poll\n"); return(1); } #endif // Heap Allocations, Milliseconds // 100000, 1531 // 200000, 3125 // 300000, 8828 // 400000, 10469 // 500000, 12297 // 600000, 22922 // 700000, 24359 // 800000, 26406 // 900000, 27844 // 1000000, 29453 // 1100000, 49250 // 1200000, 52078 // 1300000, 52953 // 1400000, 54562 // 2000000, 64781 // 2100000, 101172 #define NUM_ALLOCS 2100000 int _tmain(int argc, _TCHAR* argv[]) { char** dummymem = new char*[NUM_ALLOCS]; for (int i = 0; i<NUM_ALLOCS; i++) { dummymem[i] = new char[2]; } RAND_poll(); printf("\nFinished. Hit Enter to terminate.\n"); getchar(); delete [] dummymem; return 0; }
bug report (first time, please forgive any faux-paus) 64-bit Windows 7 Ultimate affects openssl/crypto/rand/rand_win.c from at least 2005 (v1.4.5) to current 0.9.8 and 1.0.0 Even when doing the absolute minimum heap traversal, RAND_poll walks the first 80 entries of the first heap list to generate entropy without checking how much time it's taking. This can easily take more than a minute to execute with a moderate-to-large heap size. Expected behavior: RAND_poll needs to be limited to taking only a few seconds while still gathering enough entropy. Suggested remedies: A) find an alternate method of heap list traversal or B) find an alternate source of entropy The cause is the usage of Heap32Next: my testing shows that performance of Heap32Next is linear w/r/t the number of heap entries in the first heap list. Each invocation of Heap32Next (on Win7, contrasted with XP and Vista) can easily take more than 1 second - graphs of the performance of Heap32Next are here: http://thenewjamesbaker.blogspot.com/2009/11/performance-of-heap32next-on-64-bit.html This report springs from this openssl-users thread: http://groups.google.com/group/mailing.openssl.users/browse_thread/thread/b38d72135da93951/d3cf46fe6311ddbb Attached is rand_win.cpp, a test harness I used in MSVC2005 to instrument a stripped-down version of RAND_poll.
|
bug report (first time, please forgive any faux-paus) 64-bit Windows 7 Ultimate affects openssl/crypto/rand/rand_win.c from at least 2005 (v1.4.5)
to current 0.9.8 and 1.0.0 Even when doing the absolute minimum heap traversal, RAND_poll
walks the first 80 entries of the first heap list to generate entropy without
checking how much time it’s taking. This can easily take more than
a minute to execute with a moderate-to-large heap size. Expected behavior: RAND_poll needs to be limited to
taking only a few seconds while still gathering enough entropy. Suggested
remedies: A) find an alternate method of heap list traversal or B) find an
alternate source of entropy The cause is the usage of Heap32Next: my testing shows that performance
of Heap32Next is linear w/r/t the number of heap entries in the first heap list.
Each invocation of Heap32Next (on Win7, contrasted with XP and Vista) can
easily take more than 1 second - graphs of the performance of Heap32Next are
here: http://thenewjamesbaker.blogspot.com/2009/11/performance-of-heap32next-on-64-bit.html This report springs from this openssl-users thread: http://groups.google.com/group/mailing.openssl.users/browse_thread/thread/b38d72135da93951/d3cf46fe6311ddbb Attached is rand_win.cpp, a test harness I used in MSVC2005
to instrument a stripped-down version of RAND_poll. |
