/*++ Copyright (c) 1993, 1998 Microsoft Corporation Module Name: randlib.c Abstract: This module implements the core cryptographic random number generator for use by system components. The #define KMODE_RNG affects whether the file is built in a way suitable for kernel mode usage. if KMODE_RNG is not defined, the file is built in a way suitable for user mode usage. Author: Scott Field (sfield) 27-Nov-96 Jeff Spelman (jeffspel) 14-Oct-96 --*/ #ifndef KMODE_RNG #include #include #include #include #else #include #include #ifdef USE_HW_RNG #ifdef _M_IX86 #include #include "deftypes.h" //ISD typedefs and constants #include "ioctldef.h" //ISD ioctl definitions #endif // _M_IX86 #endif // USE_HW_RNG #endif // KMODE_RNG #include #include #include #include #include #include #include // IOCTL_ #include #include "vlhash.h" #include "circhash.h" #include "cpu.h" #include "seed.h" #ifdef KMODE_RNG //#include #ifdef USE_HW_RNG #ifdef _M_IX86 static DWORD g_dwHWDriver = 0; static PFILE_OBJECT g_pFileObject = NULL; static PDEVICE_OBJECT g_pDeviceObject = NULL; #endif // _M_IX86 #endif // USE_HW_RNG #endif // KMODE_RNG #include "umkm.h" // // note: RAND_CTXT_LEN dictates the maximum input quantity for re-seed entropy // is. We make this fairly large, so that we can take all the entropy generated // during the GatherRandomBits(). Since the lifetime of the RandContext structure // is very short, and it lives on the stack, this larger than necessary size // is ok. The last few items processed during GatherRandomBits() are of // variable size, up to a maximum of of UNLEN for the username. // #define RAND_CTXT_LEN (256) #define RC4_REKEY_PARAM_NT (16384) // rekey less often on NT #ifndef KMODE_RNG #define RC4_REKEY_PARAM_DEFAULT (512) // rekey every 512 bytes by default #else #define RC4_REKEY_PARAM_DEFAULT RC4_REKEY_PARAM_NT #endif static unsigned int g_dwRC4RekeyParam = RC4_REKEY_PARAM_DEFAULT; static CircularHash g_CircularHashCtx; #ifndef WINNT_RNG static BYTE g_VeryLargeHash[A_SHA_DIGEST_LEN*4]; #endif static void * g_RC4SafeCtx; #ifndef KMODE_RNG typedef NTSYSAPI NTSTATUS (NTAPI *NTQUERYSYSTEMINFORMATION) ( IN SYSTEM_INFORMATION_CLASS SystemInformationClass, OUT PVOID SystemInformation, IN ULONG SystemInformationLength, OUT PULONG ReturnLength OPTIONAL ); typedef NTSYSAPI NTSTATUS (NTAPI *NTOPENFILE) ( OUT PHANDLE FileHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes, OUT PIO_STATUS_BLOCK IoStatusBlock, IN ULONG ShareAccess, IN ULONG OpenOptions ); typedef NTSYSAPI VOID (NTAPI *RTLINITUNICODESTRING) ( PUNICODE_STRING DestinationString, PCWSTR SourceString ); typedef BOOL (WINAPI *GETCURSORPOS)( LPPOINT lpPoint ); typedef LONG (WINAPI *GETMESSAGETIME)( VOID ); GETCURSORPOS ___GetCursorPosRNG = NULL; GETMESSAGETIME ___GetMessageTimeRNG = NULL; #define _GetCursorPos ___GetCursorPosRNG #define _GetMessageTime ___GetMessageTimeRNG #ifndef WINNT_RNG NTQUERYSYSTEMINFORMATION ___NtQuerySystemInformationRNG = NULL; NTOPENFILE ___NtOpenFileRNG = NULL; RTLINITUNICODESTRING ___RtlInitUnicodeStringRNG = NULL; #define _NtQuerySystemInformation ___NtQuerySystemInformationRNG #define _NtOpenFile ___NtOpenFileRNG #define _RtlInitUnicodeString ___RtlInitUnicodeStringRNG #else #define _NtQuerySystemInformation NtQuerySystemInformation #define _NtOpenFile NtOpenFile #define _RtlInitUnicodeString RtlInitUnicodeString #endif #ifndef WINNT_RNG HANDLE g_hKsecDD = NULL; #else extern HANDLE g_hKsecDD; #endif #else #define _NtQuerySystemInformation ZwQuerySystemInformation #endif // !KMODE_RNG /// TODO: cache hKeySeed later. ///extern HKEY g_hKeySeed; // // private function prototypes. // BOOL GenRandom ( IN PVOID hUID, OUT BYTE *pbBuffer, IN size_t dwLength ); BOOL RandomFillBuffer( OUT BYTE *pbBuffer, IN DWORD *pdwLength ); BOOL GatherRandomKey( IN BYTE *pbUserSeed, IN DWORD cbUserSeed, IN OUT BYTE *pbRandomKey, IN OUT DWORD *pcbRandomKey ); BOOL GatherRandomKeyFastUserMode( IN BYTE *pbUserSeed, IN DWORD cbUserSeed, IN OUT BYTE *pbRandomKey, IN OUT DWORD *pcbRandomKey ); BOOL IsRNGWinNT( VOID ); #ifdef _M_IX86 unsigned int QueryForHWRandomBits( IN DWORD *pdwRandom, IN OUT DWORD cdwRandom ); #endif //_M_IX86 #ifdef KMODE_RNG #ifdef ALLOC_PRAGMA #pragma alloc_text(PAGE, NewGenRandom) #pragma alloc_text(PAGE, NewGenRandomEx) #pragma alloc_text(PAGE, GenRandom) #pragma alloc_text(PAGE, RandomFillBuffer) #pragma alloc_text(PAGE, InitializeRNG) #pragma alloc_text(PAGE, ShutdownRNG) #ifdef _M_IX86 #pragma alloc_text(PAGE, QueryForHWRandomBits) #endif //_M_IX86 #pragma alloc_text(PAGE, GatherRandomKey) #endif // ALLOC_PRAGMA #endif // KMODE_RNG /************************************************************************/ /* NewGenRandom generates a specified number of random bytes and places */ /* them into the specified buffer. */ /************************************************************************/ /* */ /* Pseudocode logic flow: */ /* */ /* if (bits streamed >= threshold) */ /* { */ /* Gather_Bits() */ /* SHAMix_Bits(User, Gathered, Static -> Static) */ /* RC4Key(Static -> newRC4Key) */ /* SaveToRegistry(Static) */ /* } */ /* else */ /* { */ /* Mix_Bits(User, Static -> Static) */ /* } */ /* */ /* RC4(newRC4Key -> outbuf) */ /* bits streamed += sizeof(outbuf) */ /* */ /************************************************************************/ unsigned int RSA32API NewGenRandomEx( IN RNG_CONTEXT *pRNGContext, IN OUT unsigned char *pbRandBuffer, IN unsigned long cbRandBuffer ) { unsigned char **ppbRandSeed; unsigned long *pcbRandSeed; unsigned int fRet; #ifdef KMODE_RNG PAGED_CODE(); #endif // KMODE_RNG fRet = TRUE; if( pRNGContext->cbSize != sizeof( RNG_CONTEXT ) ) return FALSE; if( pRNGContext->pbRandSeed && pRNGContext->cbRandSeed ) { ppbRandSeed = &pRNGContext->pbRandSeed; pcbRandSeed = &pRNGContext->cbRandSeed; } else { ppbRandSeed = NULL; pcbRandSeed = NULL; } if(!InitializeRNG( NULL )) { return FALSE; } InitRand( ppbRandSeed, pcbRandSeed ); if( pRNGContext->Flags & RNG_FLAG_REKEY_ONLY ) { // // caller wants REKEY only. // fRet = GatherRandomKey( NULL, 0, pbRandBuffer, &cbRandBuffer ); } else { // // standard RNG request. // fRet = GenRandom(0, pbRandBuffer, cbRandBuffer); } if( ppbRandSeed && pcbRandSeed ) { DeInitRand( *ppbRandSeed, *pcbRandSeed); } return fRet; } unsigned int RSA32API NewGenRandom ( IN OUT unsigned char **ppbRandSeed, IN unsigned long *pcbRandSeed, IN OUT unsigned char *pbBuffer, IN unsigned long dwLength ) { RNG_CONTEXT RNGContext; #ifdef KMODE_RNG PAGED_CODE(); #endif // KMODE_RNG RtlZeroMemory( &RNGContext, sizeof(RNGContext) ); RNGContext.cbSize = sizeof(RNGContext); if( ppbRandSeed && pcbRandSeed ) { BOOL fRet; RNGContext.pbRandSeed = *ppbRandSeed; RNGContext.cbRandSeed = *pcbRandSeed; fRet = NewGenRandomEx( &RNGContext, pbBuffer, dwLength ); *pcbRandSeed = RNGContext.cbRandSeed; return fRet; } return NewGenRandomEx( &RNGContext, pbBuffer, dwLength ); } unsigned int RSA32API InitRand( IN OUT unsigned char **ppbRandSeed, IN unsigned long *pcbRandSeed ) { static BOOL fInitialized = FALSE; #ifdef KMODE_RNG PAGED_CODE(); #endif // KMODE_RNG if( !fInitialized ) { InitCircularHash( &g_CircularHashCtx, 7, CH_ALG_MD4, 0 // CH_MODE_FEEDBACK ); #ifndef WINNT_RNG // // get prior seed. // ReadSeed( g_VeryLargeHash, sizeof( g_VeryLargeHash ) ); #endif fInitialized = TRUE; } if( ppbRandSeed != NULL && pcbRandSeed != NULL && *pcbRandSeed != 0 ) UpdateCircularHash( &g_CircularHashCtx, *ppbRandSeed, *pcbRandSeed ); return TRUE; } unsigned int RSA32API DeInitRand( IN OUT unsigned char *pbRandSeed, IN unsigned long cbRandSeed ) { PBYTE pbCircularHash; DWORD cbCircularHash; #ifdef KMODE_RNG PAGED_CODE(); #endif // KMODE_RNG if( pbRandSeed == NULL || cbRandSeed == 0 ) return TRUE; if(GetCircularHashValue( &g_CircularHashCtx, &pbCircularHash, &cbCircularHash )) { unsigned long cbToCopy; if( cbRandSeed > cbCircularHash ) { cbToCopy = cbCircularHash; } else { cbToCopy = cbRandSeed; } memcpy(pbRandSeed, pbCircularHash, cbToCopy); } return TRUE; } unsigned int RSA32API InitializeRNG( VOID *pvReserved ) { void *pvCtx; void *pvOldCtx; #ifdef KMODE_RNG PAGED_CODE(); #endif // KMODE_RNG if( g_RC4SafeCtx ) { return TRUE; } if(!rc4_safe_startup( &pvCtx )) { return FALSE; } pvOldCtx = INTERLOCKEDCOMPAREEXCHANGEPOINTER( &g_RC4SafeCtx, pvCtx, NULL ); if( pvOldCtx ) { // // race condition occured during init. // rc4_safe_shutdown( pvCtx ); } return TRUE; } void RSA32API ShutdownRNG( VOID *pvReserved ) { void *pvCtx; #ifdef KMODE_RNG PAGED_CODE(); #endif // KMODE_RNG pvCtx = InterlockedExchangePointer( &g_RC4SafeCtx, NULL ); if( pvCtx ) { rc4_safe_shutdown( pvCtx ); } #ifndef KMODE_RNG #ifndef WINNT_RNG { HANDLE hFile; hFile = InterlockedExchangePointer( &g_hKsecDD, NULL ); if( hFile ) { CloseHandle( hFile ); } } #endif #endif #if 0 // TODO later: finish logic for caching registry key. hKey = InterlockedExchangePointer( &g_hKeySeed, NULL ); if( hKey ) { REGCLOSEKEY( hKey ); } #endif } BOOL GenRandom ( IN PVOID hUID, OUT BYTE *pbBuffer, IN size_t dwLength ) { DWORD dwBytesThisPass; DWORD dwFilledBytes; #ifdef KMODE_RNG PAGED_CODE(); #endif // KMODE_RNG dwFilledBytes = 0; // break request into chunks that we rekey between while(dwFilledBytes < dwLength) { dwBytesThisPass = dwLength - dwFilledBytes; if(!RandomFillBuffer( pbBuffer + dwFilledBytes, &dwBytesThisPass )) { return FALSE; } dwFilledBytes += dwBytesThisPass; } return TRUE; } BOOL RandomFillBuffer( OUT BYTE *pbBuffer, IN DWORD *pdwLength ) { unsigned int RC4BytesUsed; unsigned int KeyId; #ifdef KMODE_RNG PAGED_CODE(); #endif // KMODE_RNG // // update circular hash with user supplied bits. // if(!UpdateCircularHash( &g_CircularHashCtx, pbBuffer, *pdwLength )) return FALSE; // // select key. // rc4_safe_select( g_RC4SafeCtx, &KeyId, &RC4BytesUsed ); // // check if re-key required. // if ( RC4BytesUsed >= g_dwRC4RekeyParam ) { PBYTE pbCircularHash; DWORD cbCircularHash; BYTE pbRandomKey[ 256 ]; DWORD cbRandomKey = sizeof(pbRandomKey); RC4BytesUsed = g_dwRC4RekeyParam; if(!GetCircularHashValue( &g_CircularHashCtx, &pbCircularHash, &cbCircularHash )) { return FALSE; } if(!GatherRandomKey( pbCircularHash, cbCircularHash, pbRandomKey, &cbRandomKey )) return FALSE; // // Create RC4 key // rc4_safe_key( g_RC4SafeCtx, KeyId, cbRandomKey, pbRandomKey ); RtlZeroMemory( pbRandomKey, sizeof(pbRandomKey) ); } // // only use RC4_REKEY_PARAM bytes from each RC4 key // { DWORD dwMaxPossibleBytes = g_dwRC4RekeyParam - RC4BytesUsed; if (*pdwLength > dwMaxPossibleBytes) *pdwLength = dwMaxPossibleBytes; } rc4_safe( g_RC4SafeCtx, KeyId, *pdwLength, pbBuffer ); return TRUE; } #ifdef KMODE_RNG #ifdef USE_HW_RNG #ifdef _M_IX86 #define NUM_HW_DWORDS_TO_GATHER 4 #define INTEL_DRIVER_NAME L"\\Device\\ISECDRV" unsigned int QueryForHWRandomBits( IN DWORD *pdwRandom, IN OUT DWORD cdwRandom ) { UNICODE_STRING ObjectName; IO_STATUS_BLOCK StatusBlock; KEVENT Event; PIRP pIrp = NULL; ISD_Capability ISD_Cap; //in/out for GetCapability ISD_RandomNumber ISD_Random; //in/out for GetRandomNumber PDEVICE_OBJECT pDeviceObject = NULL; DWORD i = 0; unsigned int Status = ERROR_SUCCESS; PAGED_CODE(); if (1 == g_dwHWDriver) { Status = STATUS_ACCESS_DENIED; goto Ret; } RtlZeroMemory( &ObjectName, sizeof(ObjectName) ); RtlZeroMemory( &StatusBlock, sizeof(StatusBlock) ); RtlZeroMemory(&ISD_Cap, sizeof(ISD_Cap)); if (NULL == g_pDeviceObject) { ObjectName.Length = sizeof(INTEL_DRIVER_NAME) - sizeof(WCHAR); ObjectName.MaximumLength = sizeof(INTEL_DRIVER_NAME); ObjectName.Buffer = INTEL_DRIVER_NAME; Status = IoGetDeviceObjectPointer(&ObjectName, FILE_ALL_ACCESS, &g_pFileObject, &pDeviceObject); if ( !NT_SUCCESS(Status) ) { g_dwHWDriver = 1; goto Ret; } if (NULL == g_pDeviceObject) { InterlockedExchangePointer(&g_pDeviceObject, pDeviceObject); } } // // If this fails then it is because there is no such device // which signals completion. // KeInitializeEvent(&Event, NotificationEvent, FALSE); ISD_Cap.uiIndex = ISD_RNG_ENABLED; //Set input member pIrp = IoBuildDeviceIoControlRequest( IOCTL_ISD_GetCapability, g_pDeviceObject, &ISD_Cap, sizeof(ISD_Cap), &ISD_Cap, sizeof(ISD_Cap), FALSE, &Event, &StatusBlock); if (pIrp == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto Ret; } Status = IoCallDriver(g_pDeviceObject, pIrp); if (Status == STATUS_PENDING) { KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); Status = StatusBlock.Status; } if (ISD_Cap.iStatus != ISD_EOK) { Status = STATUS_NOT_IMPLEMENTED; goto Ret; } // now get the random bits for (i = 0; i < cdwRandom; i++) { RtlZeroMemory(&ISD_Random, sizeof(ISD_Random)); KeInitializeEvent(&Event, NotificationEvent, FALSE); pIrp = IoBuildDeviceIoControlRequest( IOCTL_ISD_GetRandomNumber, g_pDeviceObject, &ISD_Random, sizeof(ISD_Random), &ISD_Random, sizeof(ISD_Random), FALSE, &Event, &StatusBlock); if (pIrp == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto Ret; } Status = IoCallDriver(g_pDeviceObject, pIrp); if (Status == STATUS_PENDING) { KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); Status = StatusBlock.Status; } if (ISD_Random.iStatus != ISD_EOK) { Status = STATUS_NOT_IMPLEMENTED; goto Ret; } pdwRandom[i] = pdwRandom[i] ^ ISD_Random.uiRandomNum; } Ret: return Status; } #endif // _M_IX86 #endif // USE_HW_RNG #endif // KMODE_RNG BOOL GatherRandomKey( IN BYTE *pbUserSeed, IN DWORD cbUserSeed, IN OUT BYTE *pbRandomKey, IN OUT DWORD *pcbRandomKey ) { LPBYTE pbWorkingBuffer = NULL; DWORD cbWorkingBuffer; DWORD cbBufferRemaining; BYTE *pbCurrentBuffer; DWORD *pdwTmp; BOOL fRet; #ifdef KMODE_RNG PAGED_CODE(); #endif // KMODE_RNG // // in NT Usermode, try to re-seed by calling the Kernelmode RNG. // #ifndef KMODE_RNG #ifdef WINNT_RNG return GatherRandomKeyFastUserMode( pbUserSeed, cbUserSeed, pbRandomKey, pcbRandomKey ); #else if(GatherRandomKeyFastUserMode( pbUserSeed, cbUserSeed, pbRandomKey, pcbRandomKey )) { return TRUE; } #endif #endif #ifndef WINNT_RNG // // verify current working buffer has space for candidate data. // #define VERIFY_BUFFER( size ) { \ if( cbBufferRemaining < size ) \ goto finished; \ } // // update working buffer and increment to next QWORD aligned boundary. // #define UPDATE_BUFFER( size ) { \ DWORD dwSizeRounded; \ dwSizeRounded = (size + sizeof(ULONG64)) & ~(sizeof(ULONG64)-1); \ if(dwSizeRounded > cbBufferRemaining) \ goto finished; \ pbCurrentBuffer += dwSizeRounded; \ cbBufferRemaining -= dwSizeRounded; \ } cbWorkingBuffer = 3584; pbWorkingBuffer = (PBYTE)ALLOC( cbWorkingBuffer ); if( pbWorkingBuffer == NULL ) { return FALSE; } cbBufferRemaining = cbWorkingBuffer; pbCurrentBuffer = pbWorkingBuffer; // // pickup user supplied bits. // VERIFY_BUFFER( cbUserSeed ); RtlCopyMemory( pbCurrentBuffer, pbUserSeed, cbUserSeed ); UPDATE_BUFFER( cbUserSeed ); // // ** indicates US DoD's specific recommendations for password generation // // // process id // #ifndef KMODE_RNG pdwTmp = (PDWORD)pbCurrentBuffer; *pdwTmp = GetCurrentProcessId(); UPDATE_BUFFER( sizeof(DWORD) ); #else { PHANDLE hTmp = (PHANDLE)pbCurrentBuffer; VERIFY_BUFFER( sizeof(HANDLE) ); *hTmp = PsGetCurrentProcessId(); UPDATE_BUFFER( sizeof(HANDLE) ); } #endif // // thread id // #ifndef KMODE_RNG pdwTmp = (PDWORD)pbCurrentBuffer; *pdwTmp = GetCurrentThreadId(); UPDATE_BUFFER( sizeof(DWORD) ); #else { PHANDLE hTmp = (PHANDLE)pbCurrentBuffer; VERIFY_BUFFER( sizeof(HANDLE) ); *hTmp = PsGetCurrentThreadId(); UPDATE_BUFFER( sizeof(HANDLE) ); } #endif // // ** ticks since boot (system clock) // #ifndef KMODE_RNG pdwTmp = (PDWORD)pbCurrentBuffer; *pdwTmp = GetTickCount(); UPDATE_BUFFER( sizeof(DWORD) ); #else { PLARGE_INTEGER Tick = (PLARGE_INTEGER)pbCurrentBuffer; VERIFY_BUFFER( sizeof(LARGE_INTEGER) ); KeQueryTickCount( Tick ); UPDATE_BUFFER( sizeof(LARGE_INTEGER) ); } #endif // !KMODE_RNG // // ** system time, in ms, sec, min (date & time) // #ifndef KMODE_RNG { PSYSTEMTIME psysTime = (PSYSTEMTIME)pbCurrentBuffer; VERIFY_BUFFER( sizeof( *psysTime ) ); GetLocalTime(psysTime); UPDATE_BUFFER( sizeof( *psysTime ) ); } #else { PSYSTEM_TIMEOFDAY_INFORMATION pTimeOfDay; ULONG cbSystemInfo; pTimeOfDay = (PSYSTEM_TIMEOFDAY_INFORMATION)pbCurrentBuffer; VERIFY_BUFFER( sizeof(*pTimeOfDay) ); _NtQuerySystemInformation( SystemTimeOfDayInformation, pTimeOfDay, sizeof(*pTimeOfDay), &cbSystemInfo ); UPDATE_BUFFER( cbSystemInfo ); } #endif // !KMODE_RNG // // ** hi-res performance counter (system counters) // { LARGE_INTEGER *pliPerfCount = (PLARGE_INTEGER)pbCurrentBuffer; VERIFY_BUFFER( sizeof(*pliPerfCount) ); #ifndef KMODE_RNG QueryPerformanceCounter(pliPerfCount); #else /// ZwQueryPerformanceCounter(pliPerfCount, NULL); // Defined in zwapi.h, but not exported by ntoskrnl.exe ??? #endif // !KMODE_RNG UPDATE_BUFFER( sizeof(*pliPerfCount) ); } #ifndef KMODE_RNG // // memory status // { MEMORYSTATUS *pmstMemStat = (MEMORYSTATUS *)pbCurrentBuffer; VERIFY_BUFFER( sizeof(*pmstMemStat) ); pmstMemStat->dwLength = sizeof(MEMORYSTATUS); GlobalMemoryStatus( pmstMemStat ); UPDATE_BUFFER( sizeof(*pmstMemStat) ); } #endif // !KMODE_RNG // // free disk clusters // #ifndef KMODE_RNG { PDWORD pdwDiskInfo = (PDWORD)pbCurrentBuffer; VERIFY_BUFFER( (sizeof(DWORD) * 4) ); GetDiskFreeSpace( NULL, &pdwDiskInfo[0], // sectors per cluster &pdwDiskInfo[1], // bytes per sector &pdwDiskInfo[2], // number of free clusters &pdwDiskInfo[3] // total number of clusters ); UPDATE_BUFFER( (sizeof(DWORD) * 4) ); } #endif // !KMODE_RNG #ifndef KMODE_RNG { // // hash the entire user environment block. // we do this instead of GetUserName & GetComputerName, // as the environment block contains these values, plus additional // values. // static BOOL fHashedEnv; static BYTE HashEnv[ MD4_LEN ]; if( !fHashedEnv ) { LPVOID lpEnvBlock; BOOL fAnsi = FALSE; // // try the Unicode version first, as, on WinNT, this returns us // a pointer to the existing Unicode environment block, rather // than an allocated copy. Fallback to ANSI if this fails (eg: Win9x) // lpEnvBlock = GetEnvironmentStringsW(); if( lpEnvBlock == NULL ) { lpEnvBlock = GetEnvironmentStringsA(); fAnsi = TRUE; } if( lpEnvBlock != NULL ) { ULONG cbEntry; PBYTE pbEntry; MD4_CTX MD4Ctx; MD4Init( &MD4Ctx ); pbEntry = (PBYTE)lpEnvBlock; cbEntry = 0; do { if( !fAnsi ) { pbEntry += (cbEntry + sizeof(WCHAR)); cbEntry = lstrlenW( (LPWSTR)pbEntry ) * sizeof(WCHAR); } else { pbEntry += (cbEntry + sizeof(CHAR)); cbEntry = lstrlenA( (LPSTR)pbEntry ) * sizeof(CHAR); } MD4Update( &MD4Ctx, (unsigned char *)pbEntry, (unsigned int)cbEntry ); } while( cbEntry ); MD4Final( &MD4Ctx ); CopyMemory( HashEnv, MD4Ctx.digest, sizeof(HashEnv) ); if( !fAnsi ) { FreeEnvironmentStringsW( lpEnvBlock ); } else { FreeEnvironmentStringsA( lpEnvBlock ); } } // // only try this once. if it failed once, it will likely never // succeed. // fHashedEnv = TRUE; } VERIFY_BUFFER( (sizeof(HashEnv)) ); CopyMemory( pbCurrentBuffer, HashEnv, sizeof(HashEnv) ); UPDATE_BUFFER( (sizeof(HashEnv)) ); } #endif // !KMODE_RNG // // this code path has been moved to the end so that our CombineRand() // operation on NT mixes in with everything slammed into the // rand context buffer. // #ifndef KMODE_RNG if(!IsRNGWinNT()) { // // only user info if we are not running on NT. // this prevents deadlocks on WinNT when the RNG is called from CSRSS // POINT *ppoint; LONG *plTime; // // cursor position // ppoint = (POINT*)pbCurrentBuffer; VERIFY_BUFFER( sizeof(*ppoint) ); _GetCursorPos(ppoint); UPDATE_BUFFER( sizeof(*ppoint) ); // // last messages' timestamp // plTime = (LONG*)pbCurrentBuffer; VERIFY_BUFFER( sizeof(*plTime) ); *plTime = _GetMessageTime(); UPDATE_BUFFER( sizeof(*plTime) ); } else #endif // !KMODE_RNG { unsigned char *pbCounterState = (unsigned char*)pbCurrentBuffer; unsigned long cbCounterState = 64; VERIFY_BUFFER(cbCounterState); if(GatherCPUSpecificCounters( pbCounterState, &cbCounterState )) { UPDATE_BUFFER( cbCounterState ); } // // call NtQuerySystemInformation on NT if available. // if( (void*)_NtQuerySystemInformation ) { PSYSTEM_PROCESSOR_PERFORMANCE_INFORMATION pSystemProcessorPerformanceInfo; PSYSTEM_PERFORMANCE_INFORMATION pSystemPerformanceInfo; PSYSTEM_EXCEPTION_INFORMATION pSystemExceptionInfo; PSYSTEM_LOOKASIDE_INFORMATION pSystemLookasideInfo; PSYSTEM_INTERRUPT_INFORMATION pSystemInterruptInfo; PSYSTEM_PROCESS_INFORMATION pSystemProcessInfo; ULONG cbSystemInfo; NTSTATUS Status; // // fixed length system info calls. // pSystemProcessorPerformanceInfo = (PSYSTEM_PROCESSOR_PERFORMANCE_INFORMATION)pbCurrentBuffer; VERIFY_BUFFER( sizeof(*pSystemProcessorPerformanceInfo) ); Status = _NtQuerySystemInformation( SystemProcessorPerformanceInformation, pSystemProcessorPerformanceInfo, sizeof(*pSystemProcessorPerformanceInfo), &cbSystemInfo ); if ( NT_SUCCESS(Status) ) { UPDATE_BUFFER( cbSystemInfo ); } pSystemPerformanceInfo = (PSYSTEM_PERFORMANCE_INFORMATION)pbCurrentBuffer; VERIFY_BUFFER( sizeof(*pSystemPerformanceInfo) ); Status = _NtQuerySystemInformation( SystemPerformanceInformation, pSystemPerformanceInfo, sizeof(*pSystemPerformanceInfo), &cbSystemInfo ); if ( NT_SUCCESS(Status) ) { UPDATE_BUFFER( cbSystemInfo ); } pSystemExceptionInfo = (PSYSTEM_EXCEPTION_INFORMATION)pbCurrentBuffer; VERIFY_BUFFER( sizeof(*pSystemExceptionInfo) ); Status = _NtQuerySystemInformation( SystemExceptionInformation, pSystemExceptionInfo, sizeof(*pSystemExceptionInfo), &cbSystemInfo ); if ( NT_SUCCESS(Status) ) { UPDATE_BUFFER( cbSystemInfo ); } pSystemLookasideInfo = (PSYSTEM_LOOKASIDE_INFORMATION)pbCurrentBuffer; VERIFY_BUFFER( sizeof(*pSystemLookasideInfo) ); Status = _NtQuerySystemInformation( SystemLookasideInformation, pSystemLookasideInfo, sizeof(*pSystemLookasideInfo), &cbSystemInfo ); if ( NT_SUCCESS(Status) ) { UPDATE_BUFFER( cbSystemInfo ); } // // variable length system info calls. // pSystemInterruptInfo = (PSYSTEM_INTERRUPT_INFORMATION)pbCurrentBuffer; cbSystemInfo = cbBufferRemaining; Status = _NtQuerySystemInformation( SystemInterruptInformation, pSystemInterruptInfo, cbSystemInfo, &cbSystemInfo ); if ( NT_SUCCESS(Status) ) { UPDATE_BUFFER( cbSystemInfo ); } pSystemProcessInfo = (PSYSTEM_PROCESS_INFORMATION)pbCurrentBuffer; cbSystemInfo = cbBufferRemaining; Status = _NtQuerySystemInformation( SystemProcessInformation, pSystemProcessInfo, cbSystemInfo, &cbSystemInfo ); if ( NT_SUCCESS(Status) ) { UPDATE_BUFFER( cbSystemInfo ); } } // _NtQuerySystemInformation } #ifdef KMODE_RNG #ifdef USE_HW_RNG #ifdef _M_IX86 // attempt to get bits from the INTEL HW RNG { DWORD rgdwHWRandom[NUM_HW_DWORDS_TO_GATHER]; NTSTATUS Status; VERIFY_BUFFER( sizeof(rgdwHWRandom) ); Status = QueryForHWRandomBits( rgdwHWRandom, NUM_HW_DWORDS_TO_GATHER ); if ( NT_SUCCESS(Status) ) { UPDATE_BUFFER( sizeof(rgdwHWRandom) ); } } #endif // _M_IX86 #endif // USE_HW_RNG #endif // KMODE_RNG finished: { RC4_KEYSTRUCT rc4Key; BYTE NewSeed[ sizeof(g_VeryLargeHash) ]; BYTE LocalHash[ sizeof( g_VeryLargeHash ) ]; DWORD cbBufferSize; RtlCopyMemory( LocalHash, g_VeryLargeHash, sizeof(g_VeryLargeHash) ); rc4_key( &rc4Key, sizeof(LocalHash), LocalHash ); cbBufferSize = cbWorkingBuffer - cbBufferRemaining; if( cbBufferSize > cbWorkingBuffer ) cbBufferSize = cbWorkingBuffer; fRet = VeryLargeHashUpdate( pbWorkingBuffer, // buffer to hash cbBufferSize, LocalHash ); RtlCopyMemory( NewSeed, LocalHash, sizeof(LocalHash) ); RtlCopyMemory( g_VeryLargeHash, LocalHash, sizeof(LocalHash) ); rc4( &rc4Key, sizeof( NewSeed ), NewSeed ); // // write seed out. // WriteSeed( NewSeed, sizeof(NewSeed) ); RtlZeroMemory( NewSeed, sizeof(NewSeed) ); rc4_key( &rc4Key, sizeof(LocalHash), LocalHash ); RtlZeroMemory( LocalHash, sizeof(LocalHash) ); rc4( &rc4Key, *pcbRandomKey, pbRandomKey ); RtlZeroMemory( &rc4Key, sizeof(rc4Key) ); if( pbWorkingBuffer ) { FREE( pbWorkingBuffer ); } } return fRet; #endif // WINNT_RNG } #ifndef KMODE_RNG BOOL GatherRandomKeyFastUserMode( IN BYTE *pbUserSeed, IN DWORD cbUserSeed, IN OUT BYTE *pbRandomKey, IN OUT DWORD *pcbRandomKey ) /*++ This routine attempts to gather RNG re-seed material for usermode callers from the Kernel mode version of the RNG. This is accomplished by making a device IOCTL into the ksecdd.sys device driver. --*/ { HANDLE hFile; NTSTATUS Status; if(!IsRNGWinNT()) return FALSE; hFile = g_hKsecDD; if( hFile == NULL ) { UNICODE_STRING DriverName; OBJECT_ATTRIBUTES ObjA; IO_STATUS_BLOCK IOSB; HANDLE hPreviousValue; // // call via the ksecdd.sys device driver to get the random bits. // if( _NtOpenFile == NULL || _RtlInitUnicodeString == NULL ) { return FALSE; } // // have to use the Nt flavor of the file open call because it's a base // device not aliased to \DosDevices // _RtlInitUnicodeString( &DriverName, DD_KSEC_DEVICE_NAME_U ); InitializeObjectAttributes( &ObjA, &DriverName, OBJ_CASE_INSENSITIVE, 0, 0 ); Status = _NtOpenFile( &hFile, SYNCHRONIZE | FILE_READ_DATA, &ObjA, &IOSB, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_SYNCHRONOUS_IO_ALERT ); if( !NT_SUCCESS(Status) ) return FALSE; hPreviousValue = INTERLOCKEDCOMPAREEXCHANGEPOINTER( &g_hKsecDD, hFile, NULL ); if( hPreviousValue != NULL ) { // // race condition, set current value to previously initialized version. // CloseHandle( hFile ); hFile = hPreviousValue; } } return DeviceIoControl( hFile, IOCTL_KSEC_RNG_REKEY, // indicate a RNG rekey pbUserSeed, // input buffer (existing material) cbUserSeed, // input buffer size pbRandomKey, // output buffer *pcbRandomKey, // output buffer size pcbRandomKey, // bytes written to output buffer NULL ); } BOOL IsRNGWinNT( VOID ) /*++ This function determines if we are running on Windows NT and furthermore, if it is appropriate to make use of certain user operations where the code is running. If the function returns TRUE, the caller cannot make calls to user based function and should use an alternative approach such as NtQuerySystemInformation. If the function returns FALSE, the caller can safely call user based functions to gather random material. --*/ { static BOOL fIKnow = FALSE; // we assume WinNT in case of error. static BOOL fIsWinNT = TRUE; OSVERSIONINFO osVer; if(fIKnow) return(fIsWinNT); RtlZeroMemory(&osVer, sizeof(osVer)); osVer.dwOSVersionInfoSize = sizeof(osVer); if( GetVersionEx(&osVer) ) { fIsWinNT = (osVer.dwPlatformId == VER_PLATFORM_WIN32_NT); if( fIsWinNT ) { #ifndef WINNT_RNG // // if we're on NT, collect entry point address. // HMODULE hNTDll = GetModuleHandleW( L"ntdll.dll" ); if( hNTDll ) { _NtQuerySystemInformation = (NTQUERYSYSTEMINFORMATION)GetProcAddress( hNTDll, "NtQuerySystemInformation" ); // // On WinNT, adjust the rekey param to be a much larger value // because we have more entropy to key from. // if( _NtQuerySystemInformation ) g_dwRC4RekeyParam = RC4_REKEY_PARAM_NT; _NtOpenFile = (NTOPENFILE)GetProcAddress( hNTDll, "NtOpenFile" ); _RtlInitUnicodeString = (RTLINITUNICODESTRING)GetProcAddress( hNTDll, "RtlInitUnicodeString" ); } #else g_dwRC4RekeyParam = RC4_REKEY_PARAM_NT; #endif } else { // // collect entry point addresses for Win95 // HMODULE hUser32 = LoadLibraryA("user32.dll"); if( hUser32 ) { _GetCursorPos = (GETCURSORPOS)GetProcAddress( hUser32, "GetCursorPos" ); _GetMessageTime = (GETMESSAGETIME)GetProcAddress( hUser32, "GetMessageTime" ); } } } // even on an error, this is as good as it gets fIKnow = TRUE; return fIsWinNT; } #endif // !KMODE_RNG