home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
OS/2 Shareware BBS: 10 Tools
/
10-Tools.zip
/
crypl200.zip
/
LIB_RAND.C
< prev
next >
Wrap
Text File
|
1996-10-13
|
57KB
|
1,541 lines
/****************************************************************************
* *
* cryptlib Randomness Routines *
* Copyright Peter Gutmann, Matt Thomlinson, Blake Coverett, Paul Kendall, *
* Chris Wedgwood, and Brian Warner 1995-1996 *
* *
****************************************************************************/
/* This code probably needs more work - I'm not sure how thread-safe or
reentrant the whole thing is, especially under Unix, and the way the
weighting of the gathered randomness is handled under Unix probably needs
work as well - it's a bit pessimistic on some systems */
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "crypt.h"
#ifdef INC_ALL
#include "sha.h"
#else
#include "hash/sha.h"
#endif /* Compiler-specific includes */
/* The size of the random byte pool and the allocated size, which allows
for the overflow created by the fact that the SHA blocksize isn't any
useful multiple of a power of 2 */
#define RANDOMPOOL_SIZE 256
#define RANDOMPOOL_ALLOCSIZE ( ( RANDOMPOOL_SIZE + SHA_DIGESTSIZE - 1 ) / SHA_DIGESTSIZE ) * SHA_DIGESTSIZE
/* The buffer containing the random byte pool. This isn't allocated until
needed because the allocation strategy may be changed through
cryptIoctl().
Since the slow poll executes in the background, it can cause
synchronisation problems when a cryptAddRandom() or fast poll are
executed when a slow poll is in progress. Under Win32 a critical section
is used to serialise access when adding data to the random pool. The
synchronisation calls protect threadSafeAddRandomBuffer() and the
appropriate fastPoll() function. For this reason fastPoll() must call
addRandmomBuffer (the low-level variant).
Any functions added to lib_rand.c which call addRandomLong and
addRandomWord should enter the critical section as appropriate (as
fastPoll() does) */
#ifdef __WIN32__
#define THREADVAR volatile
#else
#define THREADVAR
#endif /* __WIN32__ */
static BYTE *randomPool; /* Random byte pool */
static THREADVAR int randomWritePos;/* Current write position in the pool */
static THREADVAR int randomReadPos; /* Current read position in the pool */
static THREADVAR int randomStatus; /* Wether there's any randomness in the pool */
#ifdef __WIN32__
static CRITICAL_SECTION randProt;
volatile HANDLE threadID = NULL; /* Thread ID, set by the thread */
HANDLE threadEvent = NULL; /* Thread event handle, used to signal
thread termination */
#endif /* __WIN32__ */
/****************************************************************************
* *
* Random Pool Management Routines *
* *
****************************************************************************/
/* The SHA transformation, taken from the MDC/SHS code. */
void SHATransform( LONG *digest, LONG *data );
/* Stir up the data in the random buffer. Given a circular buffer of length
n bytes, we use SHA to hash the 20 bytes at n with the 64 bytes at
n...n + 64 as the input data block. Then move on to the next 20 bytes
until the entire buffer has been mixed. This carries 512 bits of
randomness along with it, and wraps around the entire buffer. We don't
bother with SHA data endianess-adjustment since we're not really
interested in the final output values, as long as they're well-mixed */
static void mixRandomPool( void )
{
int i;
/* Stir up the entire pool */
#ifdef _BIG_WORDS
for( i = 0; i < RANDOMPOOL_SIZE; i += SHA_DIGESTSIZE )
{
LONG digestLong[ SHA_DIGESTSIZE / 4 ];
LONG inputBuffer[ SHA_DATASIZE / 4 ];
BYTE *digestPtr;
int digestCount, j;
/* Copy SHA_DATASIZE bytes from the circular buffer into the hash
data buffer, hash the data, and copy the result back into the
random pool */
for( j = 0; j < SHA_DATASIZE; j += 4 )
inputBuffer[ j / 4 ] = \
( ( LONG ) randomPool[ ( i + j ) % RANDOMPOOL_SIZE ] << 24 ) | \
( ( LONG ) randomPool[ ( i + j + 1 ) % RANDOMPOOL_SIZE ] << 16 ) | \
( ( LONG ) randomPool[ ( i + j + 2 ) % RANDOMPOOL_SIZE ] << 8 ) | \
( ( LONG ) randomPool[ ( i + j + 3 ) % RANDOMPOOL_SIZE ] );
digestPtr = randomPool + i;
for( j = 0; j < SHA_DIGESTSIZE / 4; j++ )
{
digestLong[ j ] = mgetBLong( digestPtr );
}
SHATransform( digestLong, inputBuffer );
digestPtr = randomPool + i;
for( j = 0; j < SHA_DIGESTSIZE / 4; j++ )
{
mputBLong( digestPtr, digestLong[ j ] );
}
zeroise( inputBuffer, SHA_DATASIZE );
}
#else
for( i = 0; i < RANDOMPOOL_SIZE; i += SHA_DIGESTSIZE )
{
BYTE inputBuffer[ SHA_DATASIZE ];
int j;
/* Copy SHA_DATASIZE bytes from the circular buffer into the hash
data buffer, hash the data, and copy the result back into the
random pool */
for( j = 0; j < SHA_DATASIZE; j++ )
inputBuffer[ j ] = randomPool[ ( i + j ) % RANDOMPOOL_SIZE ];
SHATransform( ( LONG * ) ( randomPool + i ), ( LONG * ) inputBuffer );
zeroise( inputBuffer, SHA_DATASIZE );
}
#endif /* _BIG_WORDS */
/* We're back to reading and writing from the start of the pool */
randomReadPos = randomWritePos = 0;
}
/* Add a random offset to the location where the next amount of data is to
be inserted */
#define randomizeAddPos() randomWritePos += randomPool[ 0 ] & 0x0F
/* Add a byte to the random buffer. This is implemented as a macro to avoid
leaving random data passed to a function on the stack. These are low-
level routines which are not thread-safe */
#define addRandomByte( data ) \
{ \
if( randomWritePos > RANDOMPOOL_SIZE - 1 ) \
mixRandomPool(); \
randomPool[ randomWritePos++ ] += data; \
}
#define addRandomWord( word ) \
addRandomByte( ( BYTE ) ( ( WORD ) word >> 8 ) ); \
addRandomByte( ( BYTE ) ( WORD ) word )
#define addRandomLong( word ) \
addRandomByte( ( BYTE ) ( ( LONG ) word >> 24 ) ); \
addRandomByte( ( BYTE ) ( ( LONG ) word >> 16 ) ); \
addRandomByte( ( BYTE ) ( ( LONG ) word >> 8 ) ); \
addRandomByte( ( BYTE ) ( LONG ) word )
/* Add a block of data to the random buffer */
static void addRandomBuffer( BYTE *buffer, int count )
{
while( count-- )
addRandomByte( *buffer++ );
}
#ifdef __WIN32__
/* Higher-level thread-safe version of addRandomBuffer */
static void threadSafeAddRandomBuffer( BYTE *buffer, int count )
{
EnterCriticalSection( &randProt );
addRandomBuffer( buffer, count );
LeaveCriticalSection( &randProt );
}
#else
#define threadSafeAddRandomBuffer addRandomBuffer
#endif /* __WIN32__ */
/****************************************************************************
* *
* OS-Specific Randomness-Gathering Functions *
* *
****************************************************************************/
#if defined( __MSDOS__ ) /* DOS */
#include <fcntl.h>
#include <io.h>
static void fastPoll( void )
{
/* There's not much we can do under DOS, we rely entirely on the
/dev/random read for information */
addRandomLong( time( NULL ) );
}
static void slowPoll( void )
{
BYTE buffer[ 128 ];
int fd, count;
/* Read 128 bytes from /dev/random and add it to the buffer. Since DOS
doesn't swap we don't need to be as careful about copying data to
temporary buffers as we usually are. We also have to use unbuffered
I/O, since the high-level functions will read BUFSIZ bytes at once
from the input, comletely draining the driver of any randomness */
if( ( fd = open( "/dev/random", O_RDONLY ) ) == -1 )
return;
count = read( fd, buffer, 128 );
randomizeAddPos();
addRandomBuffer( buffer, count );
zeroise( buffer, 128 );
close( fd );
/* Remember that we've got some randomness we can use */
randomStatus = CRYPT_OK;
}
#elif defined( __WIN16__ ) /* Win16 */
#include <stress.h>
#include <toolhelp.h>
static void fastPoll( void )
{
static int noFastPolls = 0;
SYSHEAPINFO sysHeapInfo;
MEMMANINFO memManInfo;
TIMERINFO timerInfo;
POINT point;
/* Get various basic pieces of system information */
addRandomWord( GetCapture() ); /* Handle of window with mouse capture */
addRandomWord( GetFocus() ); /* Handle of window with input focus */
addRandomLong( GetFreeSpace( 0 ) ); /* Amount of space in global heap */
addRandomWord( GetInputState() ); /* Whether system queue has any events */
addRandomLong( GetMessagePos() ); /* Cursor pos.for last message */
addRandomLong( GetMessageTime() ); /* 55 ms time for last message */
addRandomWord( GetNumTasks() ); /* Number of active tasks */
addRandomLong( GetTickCount() );/* 55 ms time since Windows started */
GetCursorPos( &point ); /* Current mouse cursor position */
addRandomBuffer( ( BYTE * ) &point, sizeof( POINT ) );
GetCaretPos( &point ); /* Current caret position */
addRandomBuffer( ( BYTE * ) &point, sizeof( POINT ) );
/* Get the largest free memory block, number of lockable pages, number of
unlocked pages, number of free and used pages, and number of swapped
pages */
memManInfo.dwSize = sizeof( MEMMANINFO );
MemManInfo( &memManInfo );
addRandomBuffer( ( BYTE * ) &memManInfo, sizeof( MEMMANINFO ) );
/* Get the execution times of the current task and VM to approximately
1ms resolution */
timerInfo.dwSize = sizeof( TIMERINFO );
TimerCount( &timerInfo );
addRandomBuffer( ( BYTE * ) &timerInfo, sizeof( TIMERINFO ) );
/* Get the percentage free and segment of the user and GDI heap */
sysHeapInfo.dwSize = sizeof( SYSHEAPINFO );
SystemHeapInfo( &sysHeapInfo );
addRandomBuffer( ( BYTE * ) &sysHeapInfo, sizeof( SYSHEAPINFO ) );
/* Since the Win16 fast poll gathers a reasonable amount of information,
we treat five of them as being equivalent to one slow poll */
if( ++noFastPolls >= 5 )
/* Remember that we've got some randomness we can use */
randomStatus = CRYPT_OK;
}
/* The slow poll can get *very* slow because of the overhead involved in
obtaining the necessary information. On a moderately loaded system there
are often 500+ objects on the global heap and 50+ modules, so we limit
the number checked to a reasonable level to make sure we don't spend
forever polling. We give the global heap walk the most leeway since this
provides the best source of randomness */
static void slowPoll( void )
{
MODULEENTRY moduleEntry;
GLOBALENTRY globalEntry;
TASKENTRY taskEntry;
int count;
randomizeAddPos();
/* Walk the global heap getting information on each entry in it. This
retrieves the objects linear address, size, handle, lock count, owner,
object type, and segment type */
count = 0;
globalEntry.dwSize = sizeof( GLOBALENTRY );
if( GlobalFirst( &globalEntry, GLOBAL_ALL ) )
do
{
addRandomBuffer( ( BYTE * ) &globalEntry, sizeof( GLOBALENTRY ) );
count++;
}
while( count < 70 && GlobalNext( &globalEntry, GLOBAL_ALL ) );
/* Walk the module list getting information on each entry in it. This
retrieves the module name, handle, reference count, executable path,
and next module */
count = 0;
moduleEntry.dwSize = sizeof( MODULEENTRY );
if( ModuleFirst( &moduleEntry ) )
do
{
addRandomBuffer( ( BYTE * ) &moduleEntry, sizeof( MODULEENTRY ) );
count++;
}
while( count < 20 && ModuleNext( &moduleEntry ) );
/* Walk the task list getting information on each entry in it. This
retrieves the task handle, parent task handle, instance handle, stack
segment and offset, stack size, number of pending events, task queue,
and the name of module executing the task. We also call TaskGetCSIP()
for the code segment and offset of each task if it's safe to do so */
count = 0;
taskEntry.dwSize = sizeof( TASKENTRY );
if( TaskFirst( &taskEntry ) )
do
{
addRandomBuffer( ( BYTE * ) &taskEntry, sizeof( TASKENTRY ) );
if( taskEntry.hTask != GetCurrentTask() )
addRandomLong( TaskGetCSIP( taskEntry.hTask ) );
count++;
}
while( count < 100 && TaskNext( &taskEntry ) );
/* Remember that we've got some randomness we can use */
randomStatus = CRYPT_OK;
}
#elif defined( __WIN32__ ) /* Win32 */
#include <tlhelp32.h>
#include <winperf.h>
#include <winioctl.h>
#include <process.h>
#pragma comment( lib, "advapi32" )
#define THREADSTACKSIZE 8192
static HANDLE hNetAPI32 = NULL;
static void fastPoll( void )
{
static BOOLEAN addedFixedItems = FALSE;
static int noFastPolls = 0;
FILETIME creationTime, exitTime, kernelTime, userTime;
DWORD minimumWorkingSetSize, maximumWorkingSetSize;
LARGE_INTEGER performanceCount;
MEMORYSTATUS memoryStatus;
HANDLE handle;
POINT point;
EnterCriticalSection( &randProt );
/* Get various basic pieces of system information */
addRandomLong( GetActiveWindow() ); /* Handle of active window */
addRandomLong( GetCapture() ); /* Handle of window with mouse capture */
addRandomLong( GetClipboardOwner() );/* Handle of clipboard owner */
addRandomLong( GetClipboardViewer() );/* Handle of start of clpbd.viewer list */
addRandomLong( GetCurrentProcess() );/* Pseudohandle of current process */
addRandomLong( GetCurrentProcessId() );/* Current process ID */
addRandomLong( GetCurrentThread() );/* Pseudohandle of current thread */
addRandomLong( GetCurrentThreadId() );/* Current thread ID */
addRandomLong( GetCurrentTime() ); /* Milliseconds since Windows started */
addRandomLong( GetDesktopWindow() );/* Handle of desktop window */
addRandomLong( GetFocus() ); /* Handle of window with kb.focus */
addRandomWord( GetInputState() ); /* Whether sys.queue has any events */
addRandomLong( GetMessagePos() ); /* Cursor pos.for last message */
addRandomLong( GetMessageTime() ); /* 1 ms time for last message */
addRandomLong( GetOpenClipboardWindow() ); /* Handle of window with clpbd.open */
addRandomLong( GetProcessHeap() ); /* Handle of process heap */
addRandomLong( GetProcessWindowStation() ); /* Handle of procs window station */
addRandomLong( GetQueueStatus( QS_ALLEVENTS ) );/* Types of events in input queue */
/* Get multiword system information */
GetCaretPos( &point ); /* Current caret position */
addRandomBuffer( ( BYTE * ) &point, sizeof( POINT ) );
GetCursorPos( &point ); /* Current mouse cursor position */
addRandomBuffer( ( BYTE * ) &point, sizeof( POINT ) );
/* Get percent of memory in use, bytes of physical memory, bytes of free
physical memory, bytes in paging file, free bytes in paging file, user
bytes of address space, and free user bytes */
memoryStatus.dwLength = sizeof( MEMORYSTATUS );
GlobalMemoryStatus( &memoryStatus );
addRandomBuffer( ( BYTE * ) &memoryStatus, sizeof( MEMORYSTATUS ) );
/* Get thread and process creation time, exit time, time in kernel mode,
and time in user mode in 100ns intervals */
handle = GetCurrentThread();
GetThreadTimes( handle, &creationTime, &exitTime, &kernelTime, &userTime );
addRandomBuffer( ( BYTE * ) &creationTime, sizeof( FILETIME ) );
addRandomBuffer( ( BYTE * ) &exitTime, sizeof( FILETIME ) );
addRandomBuffer( ( BYTE * ) &kernelTime, sizeof( FILETIME ) );
addRandomBuffer( ( BYTE * ) &userTime, sizeof( FILETIME ) );
handle = GetCurrentProcess();
GetProcessTimes( handle, &creationTime, &exitTime, &kernelTime, &userTime );
addRandomBuffer( ( BYTE * ) &creationTime, sizeof( FILETIME ) );
addRandomBuffer( ( BYTE * ) &exitTime, sizeof( FILETIME ) );
addRandomBuffer( ( BYTE * ) &kernelTime, sizeof( FILETIME ) );
addRandomBuffer( ( BYTE * ) &userTime, sizeof( FILETIME ) );
/* Get the minimum and maximum working set size for the current process */
GetProcessWorkingSetSize( handle, &minimumWorkingSetSize,
&maximumWorkingSetSize );
addRandomLong( minimumWorkingSetSize );
addRandomLong( maximumWorkingSetSize );
/* The following are fixed for the lifetime of the process so we only
add them once */
if( !addedFixedItems )
{
STARTUPINFO startupInfo;
/* Get name of desktop, console window title, new window position and
size, window flags, and handles for stdin, stdout, and stderr */
startupInfo.cb = sizeof( STARTUPINFO );
GetStartupInfo( &startupInfo );
addRandomBuffer( ( BYTE * ) &startupInfo, sizeof( STARTUPINFO ) );
addedFixedItems = TRUE;
}
/* The docs say QPC can fail if appropriate hardware is not available.
It works on 486 & Pentium boxes, but hasn't been tested for 386 or
RISC boxes */
if( QueryPerformanceCounter( &performanceCount ) )
addRandomBuffer( ( BYTE * ) &performanceCount, sizeof( LARGE_INTEGER ) );
else
{
/* Millisecond accuracy at best... */
DWORD dwTicks = GetTickCount();
addRandomBuffer( ( BYTE * ) &dwTicks, sizeof( dwTicks ) );
}
/* Since the Win32 fast poll gathers quite a bit of information, we treat
three of them as being equivalent to one slow poll */
if( ++noFastPolls >= 3 )
/* Remember that we've got some randomness we can use */
randomStatus = CRYPT_OK;
LeaveCriticalSection( &randProt );
}
/* Type definitions for function pointers to call Toolhelp32 functions */
typedef BOOL ( WINAPI *MODULEWALK )( HANDLE hSnapshot, LPMODULEENTRY32 lpme );
typedef BOOL ( WINAPI *THREADWALK )( HANDLE hSnapshot, LPTHREADENTRY32 lpte );
typedef BOOL ( WINAPI *PROCESSWALK )( HANDLE hSnapshot, LPPROCESSENTRY32 lppe );
typedef BOOL ( WINAPI *HEAPLISTWALK )( HANDLE hSnapshot, LPHEAPLIST32 lphl );
typedef BOOL ( WINAPI *HEAPFIRST )( LPHEAPENTRY32 lphe, DWORD th32ProcessID, DWORD th32HeapID );
typedef BOOL ( WINAPI *HEAPNEXT )( LPHEAPENTRY32 lphe );
typedef HANDLE ( WINAPI *CREATESNAPSHOT )( DWORD dwFlags, DWORD th32ProcessID );
/* Global function pointers. These are necessary because the functions need
to be dynamically linked since only the Win95 kernel currently contains
them. Explicitly linking to them will make the program unloadable under
NT */
static CREATESNAPSHOT pCreateToolhelp32Snapshot = NULL;
static MODULEWALK pModule32First = NULL;
static MODULEWALK pModule32Next = NULL;
static PROCESSWALK pProcess32First = NULL;
static PROCESSWALK pProcess32Next = NULL;
static THREADWALK pThread32First = NULL;
static THREADWALK pThread32Next = NULL;
static HEAPLISTWALK pHeap32ListFirst = NULL;
static HEAPLISTWALK pHeap32ListNext = NULL;
static HEAPFIRST pHeap32First = NULL;
static HEAPNEXT pHeap32Next = NULL;
static void slowPollWin95( void )
{
PROCESSENTRY32 pe32;
THREADENTRY32 te32;
MODULEENTRY32 me32;
HEAPLIST32 hl32;
HANDLE hSnapshot;
/* Initialize the Toolhelp32 function pointers if necessary */
if( pCreateToolhelp32Snapshot == NULL )
{
HANDLE hKernel = NULL;
/* Obtain the module handle of the kernel to retrieve the addresses
of the Toolhelp32 functions */
if( ( hKernel = GetModuleHandle( "KERNEL32.DLL" ) ) == NULL )
return;
/* Now get pointers to the functions */
pCreateToolhelp32Snapshot = ( CREATESNAPSHOT ) GetProcAddress( hKernel,
"CreateToolhelp32Snapshot" );
pModule32First = ( MODULEWALK ) GetProcAddress( hKernel,
"Module32First" );
pModule32Next = ( MODULEWALK ) GetProcAddress( hKernel,
"Module32Next" );
pProcess32First = ( PROCESSWALK ) GetProcAddress( hKernel,
"Process32First" );
pProcess32Next = ( PROCESSWALK ) GetProcAddress( hKernel,
"Process32Next" );
pThread32First = ( THREADWALK ) GetProcAddress( hKernel,
"Thread32First" );
pThread32Next = ( THREADWALK ) GetProcAddress( hKernel,
"Thread32Next" );
pHeap32ListFirst = ( HEAPLISTWALK ) GetProcAddress( hKernel,
"Heap32ListFirst" );
pHeap32ListNext = ( HEAPLISTWALK ) GetProcAddress( hKernel,
"Heap32ListNext" );
pHeap32First = ( HEAPFIRST ) GetProcAddress( hKernel,
"Heap32First" );
pHeap32Next = ( HEAPNEXT ) GetProcAddress( hKernel,
"Heap32Next" );
/* Make sure we got valid pointers for every Toolhelp32 function */
if( pModule32First == NULL || pModule32Next == NULL || \
pProcess32First == NULL || pProcess32Next == NULL || \
pThread32First == NULL || pThread32Next == NULL || \
pHeap32ListFirst == NULL || pHeap32ListNext == NULL || \
pHeap32First == NULL || pHeap32Next == NULL || \
pCreateToolhelp32Snapshot == NULL )
{
/* Mark the main function as unavailable in case for future
reference */
pCreateToolhelp32Snapshot = NULL;
return;
}
}
/* Take a snapshot of everything we can get to which is currently
in the system */
hSnapshot = pCreateToolhelp32Snapshot( TH32CS_SNAPALL, 0 );
if( !hSnapshot )
return;
/* Walk through the local heap */
hl32.dwSize = sizeof( HEAPLIST32 );
if( pHeap32ListFirst( hSnapshot, &hl32 ) )
do
{
HEAPENTRY32 he32;
/* First add the information from the basic Heaplist32
structure */
threadSafeAddRandomBuffer( ( BYTE * ) &hl32, sizeof( HEAPLIST32 ) );
/* Now walk through the heap blocks getting information
on each of them */
he32.dwSize = sizeof( HEAPENTRY32 );
if( pHeap32First( &he32, hl32.th32ProcessID, hl32.th32HeapID ) )
do
threadSafeAddRandomBuffer( ( BYTE * ) &he32, sizeof( HEAPENTRY32 ) );
while( pHeap32Next( &he32 ) );
}
while( pHeap32ListNext( hSnapshot, &hl32 ) );
/* Walk through all processes */
pe32.dwSize = sizeof( PROCESSENTRY32 );
if( pProcess32First( hSnapshot, &pe32 ) )
do
threadSafeAddRandomBuffer( ( BYTE * ) &pe32, sizeof( PROCESSENTRY32 ) );
while( pProcess32Next( hSnapshot, &pe32 ) );
/* Walk through all threads */
te32.dwSize = sizeof( THREADENTRY32 );
if( pThread32First( hSnapshot, &te32 ) )
do
threadSafeAddRandomBuffer( ( BYTE * ) &te32, sizeof( THREADENTRY32 ) );
while( pThread32Next( hSnapshot, &te32 ) );
/* Walk through all modules associated with the process */
me32.dwSize = sizeof( MODULEENTRY32 );
if( pModule32First( hSnapshot, &me32 ) )
do
threadSafeAddRandomBuffer( ( BYTE * ) &me32, sizeof( MODULEENTRY32 ) );
while( pModule32Next( hSnapshot, &me32 ) );
/* Clean up the snapshot */
CloseHandle( hSnapshot );
/* Remember that we've got some randomness we can use */
randomStatus = CRYPT_OK;
}
/* Perform a thread-safe slow poll. The following function *must* be started
as a thread */
static void threadSafeSlowPollWin95( void *dummy )
{
ResetEvent( threadEvent );
slowPollWin95();
SetEvent( threadEvent );
threadID = NULL;
_endthread();
}
/* Type definitions for function pointers to call NetAPI32 functions */
typedef DWORD ( WINAPI *NETSTATISTICSGET )( LPWSTR szServer, LPWSTR szService,
DWORD dwLevel, DWORD dwOptions,
LPBYTE *lpBuffer );
typedef DWORD ( WINAPI *NETAPIBUFFERSIZE )( LPVOID lpBuffer, LPDWORD cbBuffer );
typedef DWORD ( WINAPI *NETAPIBUFFERFREE )( LPVOID lpBuffer );
/* Global function pointers. These are necessary because the functions need
to be dynamically linked since only the WinNT kernel currently contains
them. Explicitly linking to them will make the program unloadable under
Win95 */
static NETSTATISTICSGET pNetStatisticsGet = NULL;
static NETAPIBUFFERSIZE pNetApiBufferSize = NULL;
static NETAPIBUFFERFREE pNetApiBufferFree = NULL;
static void slowPollWinNT( void )
{
static int isWorkstation = CRYPT_ERROR;
PPERF_DATA_BLOCK pPerfData;
static int cbPerfData = 0x10000;
HANDLE hDevice;
LPBYTE lpBuffer;
DWORD dwSize, status;
int nDrive;
/* Find out whether this is an NT server or workstation if necessary */
if( isWorkstation == CRYPT_ERROR )
{
HKEY hKey;
if( RegOpenKeyEx( HKEY_LOCAL_MACHINE,
"SYSTEM\\CurrentControlSet\\Control\\ProductOptions",
0, KEY_READ, &hKey ) == ERROR_SUCCESS )
{
BYTE szValue[ 32 ];
dwSize = sizeof( szValue );
isWorkstation = TRUE;
status = RegQueryValueEx( hKey, "ProductType", 0, NULL,
szValue, &dwSize );
if( status == ERROR_SUCCESS && stricmp( szValue, "WinNT" ) )
/* Note: There are (at least) three cases for ProductType:
WinNT = NT Workstation, ServerNT = NT Server, LanmanNT =
NT Server acting as a Domain Controller */
isWorkstation = FALSE;
RegCloseKey( hKey );
}
}
/* Initialize the NetAPI32 function pointers if necessary */
if( hNetAPI32 == NULL )
{
/* Obtain a handle to the module containing the Lan Manager functions */
if( hNetAPI32 = LoadLibrary( "NETAPI32.DLL" ) )
{
/* Now get pointers to the functions */
pNetStatisticsGet = ( NETSTATISTICSGET ) GetProcAddress( hNetAPI32,
"NetStatisticsGet" );
pNetApiBufferSize = ( NETAPIBUFFERSIZE ) GetProcAddress( hNetAPI32,
"NetApiBufferSize" );
pNetApiBufferFree = ( NETAPIBUFFERFREE ) GetProcAddress( hNetAPI32,
"NetApiBufferFree" );
/* Make sure we got valid pointers for every NetAPI32 function */
if( pNetStatisticsGet == NULL ||
pNetApiBufferSize == NULL ||
pNetApiBufferFree == NULL )
{
/* Free the library reference and reset the static handle */
FreeLibrary( hNetAPI32 );
hNetAPI32 = NULL;
}
}
}
/* Get network statistics. Note: Both NT Workstation and NT Server by
default will be running both the workstation and server services. The
heuristic below is probably useful though on the assumption that the
majority of the network traffic will be via the appropriate service */
if( hNetAPI32 &&
pNetStatisticsGet( NULL,
isWorkstation ? L"LanmanWorkstation" : L"LanmanServer",
0, 0, &lpBuffer ) == 0 )
{
pNetApiBufferSize( lpBuffer, &dwSize );
threadSafeAddRandomBuffer( ( BYTE * ) lpBuffer, dwSize );
pNetApiBufferFree( lpBuffer );
}
/* Get disk I/O statistics for all the hard drives */
for( nDrive = 0;; nDrive++ )
{
DISK_PERFORMANCE diskPerformance;
char szDevice[ 24 ];
/* Check whether we can access this device */
sprintf( szDevice, "\\\\.\\PhysicalDrive%d", nDrive );
hDevice = CreateFile( szDevice, 0, FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, OPEN_EXISTING, 0, NULL );
if( hDevice == INVALID_HANDLE_VALUE )
break;
/* Note: This only works if you have turned on the disk performance
counters with 'diskperf -y'. These counters are off by default */
if( DeviceIoControl( hDevice, IOCTL_DISK_PERFORMANCE, NULL, 0,
&diskPerformance, sizeof( DISK_PERFORMANCE ),
&dwSize, NULL ) )
{
threadSafeAddRandomBuffer( ( BYTE * ) &diskPerformance, dwSize );
}
CloseHandle( hDevice );
}
/* Get the performance counters */
pPerfData = ( PPERF_DATA_BLOCK ) malloc( cbPerfData );
while( pPerfData )
{
dwSize = cbPerfData;
status = RegQueryValueEx( HKEY_PERFORMANCE_DATA, "Global", NULL,
NULL, ( LPBYTE ) pPerfData, &dwSize );
if( status == ERROR_SUCCESS )
{
if( !memcmp( pPerfData->Signature, L"PERF", 8 ) )
threadSafeAddRandomBuffer( ( BYTE * ) pPerfData, dwSize );
free( pPerfData );
pPerfData = NULL;
}
else
if( status == ERROR_MORE_DATA )
{
cbPerfData += 4096;
pPerfData == ( PPERF_DATA_BLOCK ) realloc( pPerfData, cbPerfData );
}
}
/* Remember that we've got some randomness we can use */
randomStatus = CRYPT_OK;
}
/* Perform a thread-safe slow poll. The following function *must* be started
as a thread */
static void threadSafeSlowPollWinNT( void *dummy )
{
ResetEvent( threadEvent );
slowPollWinNT();
SetEvent(threadEvent);
threadID = NULL;
_endthread();
}
static void slowPoll( void )
{
static DWORD dwPlatform = ( DWORD ) CRYPT_ERROR;
if( dwPlatform == CRYPT_ERROR )
{
OSVERSIONINFO osvi = { sizeof( osvi ) };
if( GetVersionEx( &osvi ) )
dwPlatform = osvi.dwPlatformId;
}
/* Start a threaded slow poll. If a slow poll is already running, we
just return since there isn't much point in running two of them at the
same time */
switch( dwPlatform )
{
case VER_PLATFORM_WIN32_NT:
if( !threadID )
threadID = ( HANDLE ) _beginthread( threadSafeSlowPollWinNT,
THREADSTACKSIZE, NULL );
break;
case VER_PLATFORM_WIN32_WINDOWS:
if( !threadID )
threadID = ( HANDLE ) _beginthread( threadSafeSlowPollWin95,
THREADSTACKSIZE, NULL );
break;
case VER_PLATFORM_WIN32s:
break;
}
}
/* Wait for the randomness gathering to finish. Anything that requires the
gatherer process to have completed gathering entropy should call
randomFlush, which will block until the background process completes. At
the moment this call is in the getRandomByte routine */
void randomFlush( void )
{
if( threadID != NULL )
WaitForSingleObject( threadEvent, INFINITE );
}
#elif defined( __UNIX__ ) /* Unix */
#ifdef __osf__
/* Somewhere in the morass of system-specific cruft which OSF/1 pulls in
via the following includes are various endianness defines, so we
undefine the cryptlib ones, which aren't really needed for this module
anyway */
#undef BIG_ENDIAN
#undef LITTLE_ENDIAN
#endif /* __osf__ */
#include <unistd.h>
#include <fcntl.h>
#include <pwd.h>
#include <sys/ipc.h>
#include <sys/time.h> /* SCO and SunOS need this before resource.h */
#include <sys/resource.h>
#ifdef _AIX
#include <sys/select.h>
#endif /* _AIX */
#include <sys/shm.h>
#include <sys/signal.h>
#include <sys/stat.h>
#include <sys/types.h> /* Verschiedene komische Typen */
#include <sys/wait.h>
/* #include <kitchensink.h> */
/* The structure containing information on random-data sources. Each
record contains the source and a relative estimate of its usefulness
(weighting) which is used to scale the number of kB of output from the
source. Usually the weighting is in the range 1-3 (or 0 for especially
useless sources). If the source is constantly changing (certain types of
network statistics have this characteristic) but the amount of output is
small, the constant value 100 is added to the weighting to indicate that
the output should be treated as if a minimum of 1K of output had been
obtained. If the source produces a lot of output then the scale factor is
fractional. In order to provide enough randomness to satisfy the
requirements for a slow poll, we need to accumulate at least 25 points of
usefulness (a typical system should get about 30-35 points).
Some potential options are missed out because of special considerations.
pstat -i and pstat -f can produce amazing amounts of output (the record
is 600K on an Oracle server) which floods the buffer and doesn't yield
anything useful (apart from perhaps increasing the entropy of the vmstat
output a bit), so we don't bother with this. pstat in general produces
quite a bit of output, but it doesn't change much over time, so it gets
very low weightings. netstat -s produces constantly-changing output but
also produces quite a bit of it, so it only gets a weighting of 2 rather
than 3. The same holds for netstat -in, which gets 1 rather than 2.
Some binaries are stored in different locations on different systems so
alternative paths are given for them. The code sorts out which one to
run by itself, once it finds an exectable somewhere it moves on to the
next source. The sources are arranged roughly in their order of
usefulness, occasionally sources which provide a tiny amount of
relatively useless data are placed ahead of ones which provide a large
amount of possibly useful data because another 100 bytes can't hurt, and
it means the buffer won't be swamped by one or two high-output sources.
All the high-output sources are clustered towards the end of the list
for this reason. Some binaries checked for in a certain order, for
example under Slowaris /usr/ucb/ps understands aux as an arg, but the
others don't. Some systems have conditional defines enabling alternatives
to commands which don't understand the usual options but will provide
enough output (in the form of error messages) to look like they're the
real thing, causing alternative options to be skipped (we can't check the
return either because some commands return peculiar, non-zero status even
when they're working correctly).
In order to maximise use of the buffer, the code performs a form of run-
length compression on its input where a repeated sequence of bytes is
replaced by the occurrence count mod 256. Some commands output an awful
lot of whitespace, this measure greatly increases the amount of data we
can fit in the buffer.
Some broken preprocessors may give a division by zero warning on the
following macro */
#define SC( weight ) ( weight ? 1024 / weight : 0 ) /* Scale factor */
static struct RI {
const char *path; /* Path to check for existence of source */
const char *arg; /* Args for source */
const int usefulness; /* Usefulness of source */
FILE *pipe; /* Pipe to source as FILE * */
int pipeFD; /* Pipe to source as FD */
pid_t pid; /* pid of child for waitpid() */
int length; /* Quantity of output produced */
const BOOLEAN hasAlternative; /* Whether source has alt.location */
} dataSources[] = {
{ "/bin/vmstat", "-s", SC( -3 ), NULL, 0, 0, 0, TRUE },
{ "/usr/bin/vmstat", "-s", SC( -3 ), NULL, 0, 0, 0, FALSE },
{ "/bin/vmstat", "-c", SC( -3 ), NULL, 0, 0, 0, TRUE },
{ "/usr/bin/vmstat", "-c", SC( -3 ), NULL, 0, 0, 0, FALSE },
{ "/usr/bin/pfstat", NULL, SC( -2 ), NULL, 0, 0, 0, FALSE },
{ "/bin/vmstat", "-i", SC( -2 ), NULL, 0, 0, 0, TRUE },
{ "/usr/bin/vmstat", "-i", SC( -2 ), NULL, 0, 0, 0, FALSE },
{ "/usr/ucb/netstat", "-s", SC( 2 ), NULL, 0, 0, 0, TRUE },
{ "/usr/bin/netstat", "-s", SC( 2 ), NULL, 0, 0, 0, TRUE },
{ "/usr/sbin/netstat", "-s", SC( 2 ), NULL, 0, 0, 0, FALSE },
{ "/usr/bin/nfsstat", NULL, SC( 2 ), NULL, 0, 0, 0, FALSE },
{ "/usr/ucb/netstat", "-m", SC( -1 ), NULL, 0, 0, 0, TRUE },
{ "/usr/bin/netstat", "-m", SC( -1 ), NULL, 0, 0, 0, TRUE },
{ "/usr/sbin/netstat", "-m", SC( -1 ), NULL, 0, 0, 0, FALSE },
{ "/usr/ucb/netstat", "-in", SC( -1 ), NULL, 0, 0, 0, TRUE },
{ "/usr/bin/netstat", "-in", SC( -1 ), NULL, 0, 0, 0, TRUE },
{ "/usr/sbin/netstat", "-in", SC( -1 ), NULL, 0, 0, 0, FALSE },
{ "/usr/bin/mpstat", NULL, SC( 1 ), NULL, 0, 0, 0, FALSE },
{ "/usr/bin/w", NULL, SC( 1 ), NULL, 0, 0, 0, FALSE },
{ "/usr/bin/df", NULL, SC( 1 ), NULL, 0, 0, 0, TRUE },
{ "/bin/df", NULL, SC( 1 ), NULL, 0, 0, 0, FALSE },
{ "/usr/bin/iostat", NULL, SC( 0 ), NULL, 0, 0, 0, FALSE },
{ "/usr/bin/uptime", NULL, SC( 0 ), NULL, 0, 0, 0, FALSE },
{ "/bin/vmstat", "-f", SC( 0 ), NULL, 0, 0, 0, TRUE },
{ "/usr/bin/vmstat", "-f", SC( 0 ), NULL, 0, 0, 0, FALSE },
{ "/bin/vmstat", NULL, SC( 0 ), NULL, 0, 0, 0, TRUE },
{ "/usr/bin/vmstat", NULL, SC( 0 ), NULL, 0, 0, 0, FALSE },
{ "/usr/ucb/netstat", "-n", SC( 0.5 ), NULL, 0, 0, 0, TRUE },
{ "/usr/bin/netstat", "-n", SC( 0.5 ), NULL, 0, 0, 0, TRUE },
{ "/usr/sbin/netstat", "-n", SC( 0.5) , NULL, 0, 0, 0, FALSE },
#ifdef __sgi
{ "/bin/ps", "-el", SC( 0.3 ), NULL, 0, 0, 0, TRUE },
#endif /* __sgi */
{ "/usr/ucb/ps", "aux", SC( 0.3 ), NULL, 0, 0, 0, TRUE },
{ "/usr/bin/ps", "aux", SC( 0.3 ), NULL, 0, 0, 0, TRUE },
{ "/bin/ps", "aux", SC( 0.3 ), NULL, 0, 0, 0, FALSE },
{ "/usr/bin/ipcs", "-a", SC( 0.5 ), NULL, 0, 0, 0, TRUE },
{ "/bin/ipcs", "-a", SC( 0.5 ), NULL, 0, 0, 0, FALSE },
/* Unreliable source, depends on system usage */
{ "/etc/pstat", "-p", SC( 0.5 ), NULL, 0, 0, 0, TRUE },
{ "/bin/pstat", "-p", SC( 0.5 ), NULL, 0, 0, 0, FALSE },
{ "/etc/pstat", "-S", SC( 0.2 ), NULL, 0, 0, 0, TRUE },
{ "/bin/pstat", "-S", SC( 0.2 ), NULL, 0, 0, 0, FALSE },
{ "/etc/pstat", "-v", SC( 0.2 ), NULL, 0, 0, 0, TRUE },
{ "/bin/pstat", "-v", SC( 0.2 ), NULL, 0, 0, 0, FALSE },
{ "/etc/pstat", "-x", SC( 0.2 ), NULL, 0, 0, 0, TRUE },
{ "/bin/pstat", "-x", SC( 0.2 ), NULL, 0, 0, 0, FALSE },
{ "/etc/pstat", "-t", SC( 0.1 ), NULL, 0, 0, 0, TRUE },
{ "/bin/pstat", "-t", SC( 0.1 ), NULL, 0, 0, 0, FALSE },
/* pstat is your friend (usually) */
{ "/usr/sbin/advfsstat", "-b usr_domain", SC( 0 ), NULL, 0, 0, 0, FALSE },
{ "/usr/sbin/advfsstat", "-l 2 usr_domain", SC( 0.5 ), NULL, 0, 0, 0, FALSE },
{ "/usr/sbin/advfsstat", "-p usr_domain", SC( 0 ), NULL, 0, 0, 0, FALSE },
/* This is a complex and screwball program. Some
systems have things like rX_dmn, x = integer,
for RAID systems, but the statistics are
pretty dodgy */
{ NULL, NULL, 0, NULL, 0, 0, 0, FALSE } };
/* Variables to manage the child process which fills the buffer */
static pid_t gathererProcess = 0;/* The child process which fills the buffer */
static BYTE *gathererBuffer; /* Shared buffer for gathering random noise */
static int gathererMemID; /* ID for shared memory */
static int gathererBufSize; /* Size of the shared memory buffer */
static uid_t gathererID = -1; /* Gatherers user ID */
/* The struct at the start of the shared memory buffer used to communicate
information from the child to the parent */
typedef struct {
int usefulness; /* Usefulness of data in buffer */
int noBytes; /* No.of bytes in buffer */
} GATHERER_INFO;
/* Under SunOS popen() doesn't record the pid of the child process. When
pclose() is called, instead of calling waitpid() for the correct child, it
calls wait() repeatedly until the right child is reaped. The problem is
that this reaps any other children that happen to have died at that
moment, and when their pclose() comes along, the process hangs forever.
The fix is to use a wrapper for popen()/pclose() which saves the pid in
the dataSources structure (code adapted from GNU-libc's popen() call) */
#include <sys/errno.h>
extern int errno;
static FILE *my_popen( struct RI *entry )
{
int pipedes[ 2 ];
FILE *stream;
/* Create the pipe */
if( pipe( pipedes ) < 0 )
return( NULL );
/* Fork off the child ("vfork() is like an OS orgasm. All OS's want to
do it, but most just end up faking it" - Chris Wedgwood). If your OS
supports it, you should try to use vfork() here because it's somewhat
more efficient */
#if defined( sun ) || defined( __ultrix__ ) || defined( __osf__ )
entry->pid = vfork();
#else
entry->pid = fork();
#endif /* Unixen which have vfork() */
if( entry->pid == ( pid_t ) -1 )
{
/* The fork failed */
close( pipedes[ 0 ] );
close( pipedes[ 1 ] );
return( NULL );
}
else
if( entry->pid == ( pid_t ) 0 )
{
/* We are the child. Make the read side of the pipe be stdout */
if( dup2( pipedes[ STDOUT_FILENO ], STDOUT_FILENO ) < 0 )
exit( 127 );
/* Close the pipe descriptors */
close( pipedes[ STDIN_FILENO ] );
close( pipedes[ STDOUT_FILENO ] );
/* Try and exec the program */
execl( entry->path, entry->path, entry->arg, NULL );
/* Die if the exec failed */
exit( 127 );
}
/* We are the parent. Close the irrelevant side of the pipe and open the
relevant side as a new stream. Mark our side of the pipe to close on
exec, so new children won't see it */
close( pipedes[ STDOUT_FILENO ] );
fcntl( pipedes[ STDIN_FILENO ], F_SETFD, FD_CLOEXEC );
stream = fdopen( pipedes[ STDIN_FILENO ], "r" );
if( stream == NULL )
{
int savedErrno = errno;
/* The stream couldn't be opened or the child structure couldn't be
allocated. Kill the child and close the other side of the pipe */
kill( entry->pid, SIGKILL );
if( stream == NULL )
close( pipedes[ STDOUT_FILENO ] );
else
fclose( stream );
waitpid( entry->pid, NULL, 0 );
entry->pid = 0;
errno = savedErrno;
return( NULL );
}
return( stream );
}
static int my_pclose( struct RI *entry )
{
int status = 0;
if( fclose( entry->pipe ) )
return( -1 );
/* We ignore the return value from the process because some programs
return funny values which would result in the input being discarded
even if they executed successfully. This isn't a problem because
the 1K size threshold will filter out any programs which exit with
a usage message without producing useful output */
if( waitpid( entry->pid, NULL, 0 ) != entry->pid )
status = -1;
entry->pipe = NULL;
entry->pid = 0;
return( status );
}
/* Unix fast poll - not terribly useful */
static void fastPoll( void )
{
#ifndef _M_XENIX /* SCO has a gettimeofday() prototype, but no sys.call */
struct timeval tv;
#ifndef __hpux /* PHUX has the fn.prototypes, but not the system call */
struct rusage rusage;
#endif /* __hpux */
gettimeofday( &tv, NULL );
addRandomLong( tv.tv_sec );
addRandomLong( tv.tv_usec );
#ifndef __hpux
getrusage( RUSAGE_SELF, &rusage );
addRandomBuffer( ( BYTE * ) &rusage, sizeof( struct rusage ) );
#endif /* __hpux */
#endif /* _M_XENIX */
}
/* Unix slow poll with special support for Linux. Really for Linux >=1.3.43
(>=2.0.12 recommended), "mknod /dev/urandom c 1 9" if you don't have this
device, and also "mknod /dev/random c 1 8" (this assumes you're root -
"Use Linux, be your own luser").
If a few of the randomness sources create a large amount of output then
the slowPoll() stops once the buffer has been filled (but before all the
randomness sources have been sucked dry) so that the 'usefulness' factor
remains below the threshold. For this reason the gatherer buffer has to
be fairly sizeable on moderately loaded systems. This is something of a
bug since the usefulness should be influenced by the amount of output as
well as the source type */
#define DEVRANDOM_BITS 1024
#define SHARED_BUFSIZE 49152 /* Usually about 25K are filled */
static void slowPoll( void )
{
int fd;
if( ( fd = open( "/dev/urandom", O_RDONLY ) ) >= 0 )
{
BYTE buffer[ DEVRANDOM_BITS / 8 ];
/* Read data from /dev/urandom, which won't block (although the
quality of the noise is lesser). This is Linux-specific, but we
may as well leave it in for other systems in case it's present
there */
read( fd, buffer, DEVRANDOM_BITS / 8 );
randomizeAddPos();
addRandomBuffer( buffer, DEVRANDOM_BITS / 8 );
zeroise( buffer, DEVRANDOM_BITS / 8 );
close( fd );
gathererProcess = 0; /* We never forked off a child */
/* Remember that we've got some randomness we can use */
randomStatus = CRYPT_OK;
}
else
{
GATHERER_INFO *gathererInfo;
BOOLEAN moreSources;
struct timeval tv;
struct passwd *passwd;
fd_set fds;
#if defined( __hpux )
size_t maxFD = 0;
int pageSize = 4096; /* PHUX doesn't have getpagesize() */
#elif defined( _M_XENIX )
int maxFD = 0, pageSize = 4096; /* Nor does SCO, but it gets fd right */
#else
int maxFD = 0, pageSize = getpagesize();
#endif /* OS-specific brokenness */
int bufPos, i, usefulness = 0;
/* Set up the shared memory */
gathererBufSize = ( SHARED_BUFSIZE / pageSize ) * ( pageSize + 1 );
if( ( gathererMemID = shmget( IPC_PRIVATE, gathererBufSize,
IPC_CREAT | 0600 ) ) == -1 )
return; /* Something broke */
if( ( gathererBuffer = ( BYTE * ) shmat( gathererMemID, NULL, 0 ) ) == ( BYTE * ) -1 )
return; /* Something broke */
/* Fork off the gatherer, the parent process returns to the caller */
if( ( gathererProcess = fork() ) || ( gathererProcess == -1 ) )
return; /* Error/parent process returns */
fclose( stderr ); /* Arrghh!! It's Stuart code!! */
/* Make sure we don't call popen() as root */
if( gathererID == -1 && \
( passwd = getpwnam( "nobody" ) ) != NULL )
gathererID = passwd->pw_uid;
setuid( gathererID );
/* Fire up each randomness source */
FD_ZERO( &fds );
for( i = 0; dataSources[ i ].path != NULL; i++ )
{
/* Since popen() is a fairly heavy function, we check to see
whether the executable exists before we try to run it */
if( access( dataSources[ i ].path, X_OK ) )
{
#ifdef DEBUG_RANDOM
printf( "%s not present%s\n", dataSources[ i ].path,
dataSources[ i ].hasAlternative ? ", has alternatives" : "" );
#endif /* DEBUG_RANDOM */
dataSources[ i ].pipe = NULL;
}
else
dataSources[ i ].pipe = my_popen( &dataSources[ i ] );
if( dataSources[ i ].pipe != NULL )
{
dataSources[ i ].pipeFD = fileno( dataSources[ i ].pipe );
if( dataSources[ i ].pipeFD > maxFD )
maxFD = dataSources[ i ].pipeFD;
fcntl( dataSources[ i ].pipeFD, F_SETFL, O_NONBLOCK );
FD_SET( dataSources[ i ].pipeFD, &fds );
dataSources[ i ].length = 0;
/* If there are alternatives for this command, don't try and
execute them */
while( dataSources[ i ].hasAlternative )
{
#ifdef DEBUG_RANDOM
printf( "Skipping %s\n", dataSources[ i + 1 ].path );
#endif /* DEBUG_RANDOM */
i++;
}
}
}
gathererInfo = ( GATHERER_INFO * ) gathererBuffer;
bufPos = sizeof( GATHERER_INFO ); /* Start of buf.has status info */
/* Suck all the data we can get from each of the sources */
moreSources = TRUE;
while( moreSources && bufPos <= gathererBufSize )
{
/* Wait for data to become available from any of the sources,
with a timeout of 10 seconds. This adds even more randomness
since data becomes available in a nondeterministic fashion.
Kudos to HP's QA department for managing to ship a select()
which breaks its own prototype */
tv.tv_sec = 10;
tv.tv_usec = 0;
#ifdef __hpux
if( select( maxFD + 1, ( int * ) &fds, NULL, NULL, &tv ) == -1 )
#else
if( select( maxFD + 1, &fds, NULL, NULL, &tv ) == -1 )
#endif /* __hpux */
break;
/* One of the sources has data available, read it into the
buffer */
for( i = 0; dataSources[ i ].path != NULL; i++ )
if( dataSources[ i ].pipe != NULL && \
FD_ISSET( dataSources[ i ].pipeFD, &fds ) )
{
size_t noBytes;
if( ( noBytes = fread( gathererBuffer + bufPos, 1,
gathererBufSize - bufPos,
dataSources[ i ].pipe ) ) == 0 )
{
if( my_pclose( &dataSources[ i ] ) == 0 )
{
int total = 0;
/* Try and estimate how much entropy we're
getting from a data source */
if( dataSources[ i ].usefulness )
if( dataSources[ i ].usefulness < 0 )
total = ( dataSources[ i ].length + 999 ) / \
-dataSources[ i ].usefulness;
else
total = dataSources[ i ].length / \
dataSources[ i ].usefulness;
#ifdef DEBUG_RANDOM
printf( "%s %s contributed %d bytes "
"(compressed), usefulness = %d\n",
dataSources[ i ].path,
( dataSources[ i ].arg != NULL ) ? \
dataSources[ i ].arg : "",
dataSources[ i ].length, total );
#endif /* DEBUG_RANDOM */
usefulness += total;
}
dataSources[ i ].pipe = NULL;
}
else
{
int currPos = bufPos;
int endPos = bufPos + noBytes;
/* Run-length compress the input byte sequence */
while( currPos < endPos )
{
int ch = gathererBuffer[ currPos ];
/* If it's a single byte, just copy it over */
if( ch != gathererBuffer[ currPos + 1 ] )
{
gathererBuffer[ bufPos++ ] = ch;
currPos++;
}
else
{
int count = 0;
/* It's a run of repeated bytes, replace
them with the byte count mod 256 */
while( ( ch == gathererBuffer[ currPos ] ) && \
currPos < endPos )
{
count++;
currPos++;
}
gathererBuffer[ bufPos++ ] = count;
noBytes -= count - 1;
}
}
/* Remember the number of (compressed) bytes of input
we obtained */
dataSources[ i ].length += noBytes;
}
}
/* Check if there is more input available on any of the sources */
moreSources = FALSE;
FD_ZERO( &fds );
for( i = 0; dataSources[ i ].path != NULL; i++ )
if( dataSources[ i ].pipe != NULL )
{
FD_SET( dataSources[ i ].pipeFD, &fds );
moreSources = TRUE;
}
}
gathererInfo->usefulness = usefulness;
gathererInfo->noBytes = bufPos;
#ifdef DEBUG_RANDOM
printf( "Got %d bytes, usefulness = %d\n", bufPos, usefulness );
#endif /* DEBUG_RANDOM */
/* Child MUST exit here */
exit( 0 );
}
}
/* Wait for the randomness gathering to finish. Anything that requires the
gatherer process to have completed gathering entropy should call
randomFlush(), which will block until the background process completes.
At the moment this call is in the getRandomByte() routine */
static void randomFlush()
{
if( gathererProcess )
{
GATHERER_INFO *gathererInfo = ( GATHERER_INFO * ) gathererBuffer;
int status;
/* Wait for the gathering process to finish, add the randomness it's
gathered, and detach the shared memory */
waitpid( gathererProcess, &status, 0 ); /* Should prob.check status */
addRandomBuffer( gathererBuffer, gathererInfo->noBytes );
if( gathererInfo->usefulness >= 25 )
randomStatus = CRYPT_OK;
zeroise( gathererBuffer, gathererBufSize );
shmdt( gathererBuffer );
shmctl( gathererMemID, IPC_RMID, NULL );
gathererProcess = 0;
}
}
#else
#error You need to create the OS-specific randomness-gathering functions for lib_rand.c
#endif /* Various OS-specific defines */
/****************************************************************************
* *
* Random Pool External Interface *
* *
****************************************************************************/
/* Add random data to the random pool. We don't try to estimate the amount
of entroy which we're adding due to the difficulty in doing this - if this
sort of thing is required it's up to the user to look after it */
CRET cryptAddRandom( void CPTR randomData, int randomDataLength )
{
BYTE *randomDataPtr = ( BYTE * ) randomData;
/* Perform basic error checking */
if( randomData == NULL )
{
if( randomDataLength != CRYPT_RANDOM_FASTPOLL && \
randomDataLength != CRYPT_RANDOM_SLOWPOLL )
return( CRYPT_BADPARM1 );
}
else
if( randomDataLength <= 0 )
return( CRYPT_BADPARM2 );
/* If the random data pool doesn't exist yet, create it now */
if( randomPool == NULL )
{
int status;
#ifdef __WIN32__
InitializeCriticalSection( &randProt );
if( ( threadEvent = CreateEvent( NULL, FALSE, FALSE, NULL ) ) == NULL )
return( CRYPT_NOMEM ); /* Anon.event, auto reset, not signalled */
#endif /* __WIN32__ */
if( ( status = secureMalloc( ( void ** ) &randomPool,
RANDOMPOOL_ALLOCSIZE ) ) != CRYPT_OK )
return( status );
randomWritePos = randomReadPos = 0;
randomStatus = CRYPT_NORANDOM;
}
/* If we're adding data to the pool, add it now and exit */
if( randomData != NULL )
{
threadSafeAddRandomBuffer( randomDataPtr, randomDataLength );
/* We assume that the externally-added randomness is strong enough to
satisfy the requirements for good random data. Presumably anyone
who bothers to use this call will ensure that they're using
appropriate data such as the output of a hardware source and not
just a call to time() */
randomStatus = CRYPT_OK;
return( CRYPT_OK );
}
/* Perform either a fast or slow poll for random system data */
if( randomDataLength == CRYPT_RANDOM_FASTPOLL )
fastPoll();
else
slowPoll();
return( CRYPT_OK );
}
/* Generate a session key in an encryption context from data in the random
pool */
CRET cryptGenerateContextEx( CRYPT_CONTEXT cryptContext, const int userKeyLength )
{
CRYPT_INFO *cryptInfoPtr = CONTEXT_TO_INFO( cryptContext );
CAPABILITY_INFO *capabilityInfoPtr;
int keyLength;
/* Perform basic error checking */
if( isBadCookie( cryptContext ) )
return( CRYPT_BADPARM1 );
if( cryptInfoPtr->checkValue != CRYPT_MAGIC )
return( CRYPT_NOTINITED );
capabilityInfoPtr = cryptInfoPtr->capabilityInfo;
/* If it's a hash function or PKC, the session key generate operation is
meaningless */
if( cryptInfoPtr->capabilityInfo->cryptMode == CRYPT_MODE_NONE || \
cryptInfoPtr->isPKCcontext )
return( CRYPT_NOTAVAIL );
/* Determine the number of random bytes we'll need. In order to avoid
problems with space inside shorter RSA-encrypted blocks, we limit the
total keysize to 448 bits, which is adequate for all purposes - the
limiting factor is three-key triple DESX, which requires ( 3 * 64 ) +
( 4 * 64 ) bits of key, and absolutely must have that many bits or it
just reduces to triple-DES with a bit of DESX. Unfortunately we can't
tell what the user is going to do with the generated key so we have to
take the approach of using the shortest possible length. This means
that the default keys generated for Blowfish, MDC/SHS, and triple DESX
can't be exported when very short RSA keys are used */
if( ( keyLength = userKeyLength ) == CRYPT_USE_DEFAULT )
if( capabilityInfoPtr->getKeysizeFunction != NULL )
keyLength = capabilityInfoPtr->getKeysizeFunction( cryptInfoPtr );
else
keyLength = capabilityInfoPtr->maxKeySize;
if( keyLength > bitsToBytes( 448 ) )
keyLength = bitsToBytes( 448 );
if( keyLength > RANDOMPOOL_SIZE )
return( CRYPT_BADPARM3 );
/* Perform a failsafe check - this should only ever be called once per
app, because after the first blocking poll the programmer of the
calling app will make sure there's a slow poll done earlier on */
if( randomPool == NULL )
cryptAddRandom( NULL, CRYPT_RANDOM_SLOWPOLL );
#if defined( __UNIX__ ) || defined( __WIN32__ )
randomFlush(); /* Make sure any gatherer thread/process has completed */
#endif /* __UNIX__ || __WIN32__ */
/* If we still can't get any random information, let the user know */
if( cryptStatusError( randomStatus ) )
return( randomStatus );
/* Load a session key into the encryption context. Before we use the
information from the random pool, we perform a final quick poll of
the system to get any last bit of entropy. We then pass the
(probably pagelocked) random pool directly to cryptLoadContext() to
avoid copying it to a pageable temporary buffer.
Note that we don't want to set the clearBuffer flag in the encryption
context here because (a) the entropy pool is probably pagelocked
anyway and (b) we don't want to lose the contents of the pool */
fastPoll();
#ifdef __WIN32__
EnterCriticalSection( &randProt );
#endif /* __WIN32__ */
mixRandomPool();
#ifdef __WIN32__
LeaveCriticalSection( &randProt );
#endif /* __WIN32__ */
return( cryptLoadContext( cryptContext, randomPool, keyLength ) );
}
/* Retrieve data from the random pool */
int getRandomByte( void )
{
#if defined( __UNIX__ ) || defined( __WIN32__ )
randomFlush(); /* Make sure any gatherer thread/process has completed */
#endif /* __UNIX__ || __WIN32__ */
/* Perform a failsafe check - this should only ever be called once per
app, because after the first blocking poll the programmer of the
calling app will make sure there's a slow poll done earlier on */
if( randomPool == NULL )
cryptAddRandom( NULL, CRYPT_RANDOM_SLOWPOLL );
/* If we still can't get any random information, let the user know */
if( cryptStatusError( randomStatus ) )
return( randomStatus );
/* If there's newly-written (and currently unmixed) data in the pool or
we've reached the end of the pool, mix it up, then return the next
byte in the pool */
if( randomWritePos || randomReadPos > RANDOMPOOL_SIZE )
mixRandomPool();
return( randomPool[ randomReadPos++ ] );
}
/* Shut down the random data pool */
void endRandom( void )
{
#if defined( __WIN32__ )
if( threadID != NULL )
CloseHandle( threadID );
if( threadEvent != NULL )
CloseHandle( threadEvent );
DeleteCriticalSection( &randProt );
if( hNetAPI32 )
{
FreeLibrary( hNetAPI32 );
hNetAPI32 = NULL;
}
#endif /* __WIN32__ */
if( randomPool != NULL )
{
secureFree( ( void ** ) &randomPool );
randomWritePos = randomReadPos = 0;
}
}