1564 lines
37 KiB
C
1564 lines
37 KiB
C
/*++
|
|
|
|
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 <nt.h>
|
|
#include <ntrtl.h>
|
|
#include <nturtl.h>
|
|
#include <windows.h>
|
|
|
|
#else
|
|
|
|
#include <ntosp.h>
|
|
#include <windef.h>
|
|
|
|
#ifdef USE_HW_RNG
|
|
#ifdef _M_IX86
|
|
#include <io.h>
|
|
#include "deftypes.h" //ISD typedefs and constants
|
|
#include "ioctldef.h" //ISD ioctl definitions
|
|
#endif // _M_IX86
|
|
#endif // USE_HW_RNG
|
|
|
|
#endif // KMODE_RNG
|
|
|
|
#include <zwapi.h>
|
|
|
|
#include <winioctl.h>
|
|
#include <lmcons.h>
|
|
|
|
#include <rc4.h>
|
|
#include <sha.h>
|
|
#include <md4.h>
|
|
|
|
#include <ntddksec.h> // IOCTL_
|
|
#include <randlib.h>
|
|
|
|
#include "vlhash.h"
|
|
#include "circhash.h"
|
|
#include "cpu.h"
|
|
#include "seed.h"
|
|
|
|
|
|
#ifdef KMODE_RNG
|
|
//#include <ntos.h>
|
|
#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
|
|
|