cryptlib  3.4.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros
wince.c
Go to the documentation of this file.
1 /****************************************************************************
2 * *
3 * WinCE Randomness-Gathering Code *
4 * Copyright Peter Gutmann 1996-2005 *
5 * *
6 ****************************************************************************/
7 
8 /* This module is part of the cryptlib continuously seeded pseudorandom number
9  generator. For usage conditions, see random.c */
10 
11 /* General includes */
12 
13 #include "crypt.h"
14 #include "random/random.h"
15 
16 /* OS-specific includes */
17 
18 #include <tlhelp32.h>
19 
20 /* The size of the intermediate buffer used to accumulate polled data */
21 
22 #define RANDOM_BUFSIZE 4096
23 
24 /* Handles to various randomness objects */
25 
26 static HANDLE hToolHelp32; /* Handle to Toolhelp.library */
27 static HANDLE hThread; /* Background polling thread handle */
28 static DWORD threadID; /* Background polling thread ID */
29 
30 /****************************************************************************
31 * *
32 * Fast Poll *
33 * *
34 ****************************************************************************/
35 
36 /* Type definitions for function pointers to call CE native functions */
37 
39 typedef DWORD ( *GETSYSTEMPOWERSTATUS )( PSYSTEM_POWER_STATUS_EX2 pSystemPowerStatusEx2,
40  DWORD dwLen, BOOL fUpdate );
41 
42 /* The shared Win32 fast poll routine */
43 
44 void fastPoll( void )
45  {
46  static BOOLEAN addedFixedItems = FALSE;
47  static BOOLEAN hasAdvFeatures = FALSE, hasHardwareRNG = FALSE;
48  static CEGENRANDOM pCeGenRandom = NULL;
49  static GETSYSTEMPOWERSTATUS pGetSystemPowerStatusEx2 = NULL;
50  FILETIME creationTime, exitTime, kernelTime, userTime;
51  LARGE_INTEGER performanceCount;
52  SYSTEM_POWER_STATUS_EX2 powerStatus;
53  MEMORYSTATUS memoryStatus;
54  HANDLE handle;
55  POINT point;
56  RANDOM_STATE randomState;
58  int length;
59 
60  if( krnlIsExiting() )
61  return;
62 
63  /* Initialize the native function pointers if necessary. CeGetRandom()
64  is only available in relatively new versions of WinCE, so we have to
65  link it dynamically */
66  if( pCeGenRandom == NULL )
67  {
68  HANDLE hCoreDLL;
69 
70  if( ( hCoreDLL = GetModuleHandle( TEXT( "Coredll.dll" ) ) ) != NULL )
71  pCeGenRandom = ( CEGENRANDOM ) GetProcAddress( hCoreDLL, TEXT( "CeGenRandom" ) );
72  }
73  if( pGetSystemPowerStatusEx2 == NULL )
74  {
75  HANDLE hGetpower;
76 
77  if( ( hGetpower = GetModuleHandle( TEXT( "Getpower.dll" ) ) ) != NULL )
78  pGetSystemPowerStatusEx2 = ( GETSYSTEMPOWERSTATUS ) \
79  GetProcAddress( hGetpower, TEXT( "GetSystemPowerStatusEx2" ) );
80  }
81 
82  initRandomData( randomState, buffer, RANDOM_BUFSIZE );
83 
84  /* Get various basic pieces of system information: Handle of active
85  window, handle of window with mouse capture, handle of clipboard owner
86  handle of start of clpboard viewer list, pseudohandle of current
87  process, current process ID, pseudohandle of current thread, current
88  thread ID, handle of desktop window, handle of window with keyboard
89  focus, whether system queue has any events, cursor position for last
90  message, 1 ms time for last message, handle of window with clipboard
91  open, handle of process heap, handle of procs window station, types of
92  events in input queue, and milliseconds since Windows was started */
93  addRandomValue( randomState, GetActiveWindow() );
94  addRandomValue( randomState, GetCapture() );
95  addRandomValue( randomState, GetCaretBlinkTime() );
96  addRandomValue( randomState, GetClipboardOwner() );
97  addRandomValue( randomState, GetCurrentProcess() );
98  addRandomValue( randomState, GetCurrentProcessId() );
99  addRandomValue( randomState, GetCurrentThread() );
100  addRandomValue( randomState, GetCurrentThreadId() );
101  addRandomValue( randomState, GetDesktopWindow() );
102  addRandomValue( randomState, GetDC( NULL ) );
103  addRandomValue( randomState, GetDoubleClickTime() );
104  addRandomValue( randomState, GetFocus() );
105  addRandomValue( randomState, GetForegroundWindow() );
106  addRandomValue( randomState, GetMessagePos() );
107  addRandomValue( randomState, GetOpenClipboardWindow() );
108  addRandomValue( randomState, GetProcessHeap() );
109  addRandomValue( randomState, GetQueueStatus( QS_ALLINPUT ) );
110  addRandomValue( randomState, GetTickCount() );
111  if( krnlIsExiting() )
112  return;
113 
114  /* Get multiword system information: Current caret position, current
115  mouse cursor position */
116  GetCaretPos( &point );
117  addRandomData( randomState, &point, sizeof( POINT ) );
118  GetCursorPos( &point );
119  addRandomData( randomState, &point, sizeof( POINT ) );
120 
121  /* Get percent of memory in use, bytes of physical memory, bytes of free
122  physical memory, bytes in paging file, free bytes in paging file, user
123  bytes of address space, and free user bytes */
124  memoryStatus.dwLength = sizeof( MEMORYSTATUS );
125  GlobalMemoryStatus( &memoryStatus );
126  addRandomData( randomState, &memoryStatus, sizeof( MEMORYSTATUS ) );
127 
128  /* Get thread and process creation time, exit time, time in kernel mode,
129  and time in user mode in 100ns intervals */
130  handle = GetCurrentThread();
131  GetThreadTimes( handle, &creationTime, &exitTime, &kernelTime, &userTime );
132  addRandomData( randomState, &creationTime, sizeof( FILETIME ) );
133  addRandomData( randomState, &exitTime, sizeof( FILETIME ) );
134  addRandomData( randomState, &kernelTime, sizeof( FILETIME ) );
135  addRandomData( randomState, &userTime, sizeof( FILETIME ) );
136 
137  /* Get extended battery/power status information. We set the fUpdate
138  flag to force a re-read of fresh data rather than a re-use of cached
139  information */
140  if( pGetSystemPowerStatusEx2 != NULL && \
141  ( length = \
142  pGetSystemPowerStatusEx2( &powerStatus,
143  sizeof( SYSTEM_POWER_STATUS_EX2 ),
144  TRUE ) ) > 0 )
145  addRandomData( randomState, &powerStatus, length );
146 
147  /* Get random data provided by the OS. Since this is expected to be
148  provided by the system vendor, it's quite likely to be the usual
149  process ID + time */
150  if( pCeGenRandom != NULL )
151  {
152  BYTE randomBuffer[ 32 ];
153 
154  if( pCeGenRandom( 32, randomBuffer ) )
155  addRandomData( randomState, randomBuffer, 32 );
156  }
157 
158  /* The following are fixed for the lifetime of the process so we only
159  add them once */
160  if( !addedFixedItems )
161  {
162  SYSTEM_INFO systemInfo;
163 
164  GetSystemInfo( &systemInfo );
165  addRandomData( randomState, &systemInfo, sizeof( SYSTEM_INFO ) );
166  addedFixedItems = TRUE;
167  }
168 
169  /* The performance of QPC varies depending on the architecture it's
170  running on, and is completely platform-dependant. If there's no
171  hardware performance counter available, it uses the 1ms system timer,
172  although usually there's some form of hardware timer available.
173  Since there may be no correlation, or only a weak correlation,
174  between the performance counter and the system clock, we get the
175  time from both sources */
176  if( QueryPerformanceCounter( &performanceCount ) )
177  addRandomData( randomState, &performanceCount,
178  sizeof( LARGE_INTEGER ) );
179  addRandomValue( randomState, GetTickCount() );
180 
181  /* Flush any remaining data through. Quality = int( 33 1/3 % ) */
182  endRandomData( randomState, 34 );
183  }
184 
185 /****************************************************************************
186 * *
187 * Slow Poll *
188 * *
189 ****************************************************************************/
190 
191 /* Type definitions for function pointers to call Toolhelp32 functions */
192 
193 typedef BOOL ( WINAPI *MODULEWALK )( HANDLE hSnapshot, LPMODULEENTRY32 lpme );
194 typedef BOOL ( WINAPI *THREADWALK )( HANDLE hSnapshot, LPTHREADENTRY32 lpte );
195 typedef BOOL ( WINAPI *PROCESSWALK )( HANDLE hSnapshot, LPPROCESSENTRY32 lppe );
196 typedef BOOL ( WINAPI *HEAPLISTWALK )( HANDLE hSnapshot, LPHEAPLIST32 lphl );
197 typedef BOOL ( WINAPI *HEAPFIRST )( HANDLE hSnapshot, LPHEAPENTRY32 lphe,
199 typedef BOOL ( WINAPI *HEAPNEXT )( HANDLE hSnapshot, LPHEAPENTRY32 lphe );
200 typedef HANDLE ( WINAPI *CREATESNAPSHOT )( DWORD dwFlags, DWORD th32ProcessID );
201 typedef BOOL ( WINAPI *CLOSESNAPSHOT )( HANDLE hSnapshot );
202 
203 /* Global function pointers. These are necessary because the functions need to
204  be dynamically linked since only some WinCE builds contain them */
205 
206 static CREATESNAPSHOT pCreateToolhelp32Snapshot = NULL;
207 static CLOSESNAPSHOT pCloseToolhelp32Snapshot = NULL;
208 static MODULEWALK pModule32First = NULL;
209 static MODULEWALK pModule32Next = NULL;
210 static PROCESSWALK pProcess32First = NULL;
211 static PROCESSWALK pProcess32Next = NULL;
212 static THREADWALK pThread32First = NULL;
213 static THREADWALK pThread32Next = NULL;
214 static HEAPLISTWALK pHeap32ListFirst = NULL;
215 static HEAPLISTWALK pHeap32ListNext = NULL;
216 static HEAPFIRST pHeap32First = NULL;
217 static HEAPNEXT pHeap32Next = NULL;
218 
219 /* Since there are a significant number of ToolHelp data blocks, we use a
220  larger-than-usual intermediate buffer to cut down on kernel traffic */
221 
222 #define BIG_RANDOM_BUFSIZE ( RANDOM_BUFSIZE * 4 )
223 
224 static void slowPollWinCE( void )
225  {
226  PROCESSENTRY32 pe32;
227  THREADENTRY32 te32;
228  MODULEENTRY32 me32;
229  HEAPLIST32 hl32;
230  HANDLE hSnapshot;
231  RANDOM_STATE randomState;
233  int iterationCount;
234 
235  /* Initialize the Toolhelp32 function pointers if necessary. The
236  Toolhelp DLL isn't always present (some OEMs omit it) so we have to
237  link it dynamically */
238  if( hToolHelp32 == NULL )
239  {
240  /* Obtain the module handle of the kernel to retrieve the addresses
241  of the ToolHelp32 functions */
242  if( ( hToolHelp32 = LoadLibrary( TEXT( "Toolhelp.dll" ) ) ) == NULL )
243  {
244  /* There's no ToolHelp32 available, now we're in a bit of a
245  bind. Try for at least a fast poll */
246  fastPoll();
247  return;
248  }
249 
250  /* Now get pointers to the functions */
251  pCreateToolhelp32Snapshot = ( CREATESNAPSHOT ) GetProcAddress( hToolHelp32, TEXT( "CreateToolhelp32Snapshot" ) );
252  pCloseToolhelp32Snapshot = ( CLOSESNAPSHOT ) GetProcAddress( hToolHelp32, TEXT( "CloseToolhelp32Snapshot" ) );
253  pModule32First = ( MODULEWALK ) GetProcAddress( hToolHelp32, TEXT( "Module32First" ) );
254  pModule32Next = ( MODULEWALK ) GetProcAddress( hToolHelp32, TEXT( "Module32Next" ) );
255  pProcess32First = ( PROCESSWALK ) GetProcAddress( hToolHelp32, TEXT( "Process32First" ) );
256  pProcess32Next = ( PROCESSWALK ) GetProcAddress( hToolHelp32, TEXT( "Process32Next" ) );
257  pThread32First = ( THREADWALK ) GetProcAddress( hToolHelp32, TEXT( "Thread32First" ) );
258  pThread32Next = ( THREADWALK ) GetProcAddress( hToolHelp32, TEXT( "Thread32Next" ) );
259  pHeap32ListFirst = ( HEAPLISTWALK ) GetProcAddress( hToolHelp32, TEXT( "Heap32ListFirst" ) );
260  pHeap32ListNext = ( HEAPLISTWALK ) GetProcAddress( hToolHelp32, TEXT( "Heap32ListNext" ) );
261  pHeap32First = ( HEAPFIRST ) GetProcAddress( hToolHelp32, TEXT( "Heap32First" ) );
262  pHeap32Next = ( HEAPNEXT ) GetProcAddress( hToolHelp32, TEXT( "Heap32Next" ) );
263 
264  /* Make sure we got valid pointers for every Toolhelp32 function */
265  if( pModule32First == NULL || pModule32Next == NULL || \
266  pProcess32First == NULL || pProcess32Next == NULL || \
267  pThread32First == NULL || pThread32Next == NULL || \
268  pHeap32ListFirst == NULL || pHeap32ListNext == NULL || \
269  pHeap32First == NULL || pHeap32Next == NULL || \
270  pCreateToolhelp32Snapshot == NULL )
271  {
272  /* Mark the main function as unavailable in case for future
273  reference */
274  pCreateToolhelp32Snapshot = NULL;
275  return;
276  }
277  }
278  if( krnlIsExiting() )
279  return;
280 
281  initRandomData( randomState, buffer, BIG_RANDOM_BUFSIZE );
282 
283  /* Take snapshots what's currently in the system. In theory we could
284  do a TH32CS_SNAPALL to get everything at once, but this can lead
285  to out-of-memory errors on some memory-limited systems, so we only
286  snapshot the individual resource that we're interested in.
287 
288  First we walk through the local heap. We have to be careful to not
289  spend excessive amounts of time on this if we're linked into a large
290  application with a great many heaps and/or heap blocks, since the
291  heap-traversal functions are rather slow. Fortunately this is
292  quite rare under WinCE since it implies a large/long-running server
293  app, which we're unlikely to run into.
294 
295  Ideally in order to prevent excessive delays we'd count the number
296  of heaps and ensure that no_heaps * no_heap_blocks doesn't exceed
297  some maximum value, however this requires two passes of (slow) heap
298  traversal rather than one, which doesn't help the situation much.
299  To provide at least some protection, we limit the total number of
300  heaps and heap entries traversed, although this leads to slightly
301  suboptimal performance if we have a small number of deep heaps
302  rather than the current large number of shallow heaps.
303 
304  There is however a second consideration that needs to be taken into
305  account when doing this, which is that the heap-management functions
306  aren't completely thread-safe, so that under (very rare) conditions
307  of heavy allocation/deallocation this can cause problems when calling
308  HeapNext(). By limiting the amount of time that we spend in each
309  heap, we can reduce our exposure somewhat */
310  hSnapshot = pCreateToolhelp32Snapshot( TH32CS_SNAPHEAPLIST, 0 );
311  if( hSnapshot == INVALID_HANDLE_VALUE )
312  {
313  assert( DEBUG_WARN ); /* Make sure that we get some feedback */
314  return;
315  }
316  hl32.dwSize = sizeof( HEAPLIST32 );
317  if( pHeap32ListFirst( hSnapshot, &hl32 ) )
318  {
319  int listCount = 0;
320 
321  do
322  {
323  HEAPENTRY32 he32;
324 
325  /* First add the information from the basic Heaplist32
326  structure */
327  if( krnlIsExiting() )
328  {
329  pCloseToolhelp32Snapshot( hSnapshot );
330  return;
331  }
332  addRandomData( randomState, &hl32, sizeof( HEAPLIST32 ) );
333 
334  /* Now walk through the heap blocks getting information
335  on each of them */
336  he32.dwSize = sizeof( HEAPENTRY32 );
337  if( pHeap32First( hSnapshot, &he32, hl32.th32ProcessID, hl32.th32HeapID ) )
338  {
339  int entryCount = 0;
340 
341  do
342  {
343  if( krnlIsExiting() )
344  {
345  pCloseToolhelp32Snapshot( hSnapshot );
346  return;
347  }
348  addRandomData( randomState, &he32,
349  sizeof( HEAPENTRY32 ) );
350  }
351  while( entryCount++ < 20 && pHeap32Next( hSnapshot, &he32 ) );
352  }
353  }
354  while( listCount++ < 20 && pHeap32ListNext( hSnapshot, &hl32 ) );
355  }
356  pCloseToolhelp32Snapshot( hSnapshot );
357  if( krnlIsExiting() )
358  return;
359 
360  /* Now walk through all processes */
361  hSnapshot = pCreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 );
362  if( hSnapshot == INVALID_HANDLE_VALUE )
363  {
364  endRandomData( randomState, 40 );
365  return;
366  }
367  pe32.dwSize = sizeof( PROCESSENTRY32 );
368  iterationCount = 0;
369  if( pProcess32First( hSnapshot, &pe32 ) )
370  {
371  do
372  {
373  if( krnlIsExiting() )
374  {
375  pCloseToolhelp32Snapshot( hSnapshot );
376  return;
377  }
378  addRandomData( randomState, &pe32, sizeof( PROCESSENTRY32 ) );
379  }
380  while( pProcess32Next( hSnapshot, &pe32 ) && \
381  iterationCount++ < FAILSAFE_ITERATIONS_LARGE );
382  }
383  pCloseToolhelp32Snapshot( hSnapshot );
384  if( krnlIsExiting() )
385  return;
386 
387  /* Then walk through all threads */
388  hSnapshot = pCreateToolhelp32Snapshot( TH32CS_SNAPTHREAD, 0 );
389  if( hSnapshot == INVALID_HANDLE_VALUE )
390  {
391  endRandomData( randomState, 60 );
392  return;
393  }
394  te32.dwSize = sizeof( THREADENTRY32 );
395  iterationCount = 0;
396  if( pThread32First( hSnapshot, &te32 ) )
397  {
398  do
399  {
400  if( krnlIsExiting() )
401  {
402  pCloseToolhelp32Snapshot( hSnapshot );
403  return;
404  }
405  addRandomData( randomState, &te32, sizeof( THREADENTRY32 ) );
406  }
407  while( pThread32Next( hSnapshot, &te32 ) && \
408  iterationCount++ < FAILSAFE_ITERATIONS_LARGE );
409  }
410  pCloseToolhelp32Snapshot( hSnapshot );
411  if( krnlIsExiting() )
412  return;
413 
414  /* Finally, walk through all modules associated with the process */
415  hSnapshot = pCreateToolhelp32Snapshot( TH32CS_SNAPMODULE, 0 );
416  if( hSnapshot == INVALID_HANDLE_VALUE )
417  {
418  endRandomData( randomState, 80 );
419  return;
420  }
421  me32.dwSize = sizeof( MODULEENTRY32 );
422  iterationCount = 0;
423  if( pModule32First( hSnapshot, &me32 ) )
424  {
425  do
426  {
427  if( krnlIsExiting() )
428  {
429  pCloseToolhelp32Snapshot( hSnapshot );
430  return;
431  }
432  addRandomData( randomState, &me32, sizeof( MODULEENTRY32 ) );
433  }
434  while( pModule32Next( hSnapshot, &me32 ) && \
435  iterationCount++ < FAILSAFE_ITERATIONS_LARGE );
436  }
437  pCloseToolhelp32Snapshot( hSnapshot );
438  if( krnlIsExiting() )
439  return;
440 
441  /* Flush any remaining data through */
442  endRandomData( randomState, 100 );
443  }
444 
445 /* Perform a thread-safe slow poll for Windows CE */
446 
448  {
449  UNUSED_ARG( dummy );
450 
451  slowPollWinCE();
452  ExitThread( 0 );
453  return( 0 );
454  }
455 
456 /* Perform a generic slow poll. This starts the OS-specific poll in a
457  separate thread */
458 
459 void slowPoll( void )
460  {
461  if( krnlIsExiting() )
462  return;
463 
464  /* Start a threaded slow poll. If a slow poll is already running, we
465  just return since there isn't much point in running two of them at the
466  same time */
467  if( hThread )
468  return;
469  hThread = CreateThread( NULL, 0, threadSafeSlowPoll, NULL, 0, &threadID );
470  assert( hThread );
471  }
472 
473 /* Wait for the randomness gathering to finish. Anything that requires the
474  gatherer process to have completed gathering entropy should call
475  waitforRandomCompletion(), which will block until the background process
476  completes */
477 
479  {
480  DWORD dwResult;
481  const DWORD timeout = force ? 2000 : 300000L;
482  int status;
483 
484  /* If there's no polling thread running, there's nothing to do. Note
485  that this isn't entirely thread-safe because someone may start
486  another poll after we perform this check, but there's no way to
487  handle this without some form of interlock mechanism with the
488  randomness mutex and the WaitForSingleObject(). In any case all
489  that'll happen is that the caller won't get all of the currently-
490  polling entropy */
491  if( hThread == NULL )
492  return( CRYPT_OK );
493 
494  /* Wait for the polling thread to terminate. If it's a forced shutdown
495  we only wait a short amount of time (2s) before we bail out,
496  otherwise we hang around for as long as it takes (with a sanity-check
497  upper limit of 5 minutes) */
498  dwResult = WaitForSingleObject( hThread, timeout );
499  if( dwResult == WAIT_FAILED )
500  {
501  /* Since this is a cleanup function there's not much that we can do
502  at this point, although we warn in debug mode */
503  assert( DEBUG_WARN );
504  return( CRYPT_OK );
505  }
506  assert( dwResult != WAIT_FAILED ); /* Warn in debug mode */
507 
508  /* Clean up */
509  status = krnlEnterMutex( MUTEX_RANDOM );
510  if( cryptStatusError( status ) )
511  return( status );
512  if( hThread != NULL )
513  {
514  CloseHandle( hThread );
515  hThread = NULL;
516  }
518 
519  return( CRYPT_OK );
520  }
521 
522 /* Initialise and clean up any auxiliary randomness-related objects */
523 
524 void initRandomPolling( void )
525  {
526  /* Reset the various object handles and status info */
527  hToolHelp32 = hThread = NULL;
528  }
529 
530 void endRandomPolling( void )
531  {
532  assert( hThread == NULL );
533  if( hToolHelp32 )
534  {
535  FreeLibrary( hToolHelp32 );
536  hToolHelp32 = NULL;
537  }
538  }