mirror of
https://github.com/veracrypt/VeraCrypt.git
synced 2025-11-11 02:58:02 -06:00
1043 lines
30 KiB
C
1043 lines
30 KiB
C
/*
|
|
Legal Notice: Some portions of the source code contained in this file were
|
|
derived from the source code of TrueCrypt 7.1a, which is
|
|
Copyright (c) 2003-2012 TrueCrypt Developers Association and which is
|
|
governed by the TrueCrypt License 3.0, also from the source code of
|
|
Encryption for the Masses 2.02a, which is Copyright (c) 1998-2000 Paul Le Roux
|
|
and which is governed by the 'License Agreement for Encryption for the Masses'
|
|
Modifications and additions to the original source code (contained in this file)
|
|
and all other portions of this file are Copyright (c) 2013-2025 IDRIX
|
|
and are governed by the Apache License 2.0 the full text of which is
|
|
contained in the file License.txt included in VeraCrypt binary and source
|
|
code distribution packages. */
|
|
|
|
#include "Tcdefs.h"
|
|
#include "Crc.h"
|
|
#include "Random.h"
|
|
#include "Dlgcode.h"
|
|
#include "Crypto\cpu.h"
|
|
#include "Crypto\jitterentropy.h"
|
|
#include "Crypto\rdrand.h"
|
|
#include <Strsafe.h>
|
|
#include <bcrypt.h>
|
|
#include <pdh.h>
|
|
#include <pdhmsg.h>
|
|
|
|
static unsigned __int8 buffer[RNG_POOL_SIZE];
|
|
static unsigned char *pRandPool = NULL;
|
|
static BOOL bRandDidInit = FALSE;
|
|
static int nRandIndex = 0, randPoolReadIndex = 0;
|
|
static int HashFunction = DEFAULT_HASH_ALGORITHM;
|
|
static BOOL bDidSlowPoll = FALSE;
|
|
BOOL volatile bFastPollEnabled = TRUE; /* Used to reduce CPU load when performing benchmarks */
|
|
BOOL volatile bRandmixEnabled = TRUE; /* Used to reduce CPU load when performing benchmarks */
|
|
static BOOL RandomPoolEnrichedByUser = FALSE;
|
|
static HANDLE PeriodicFastPollThreadHandle = NULL;
|
|
|
|
/* Macro to add a single byte to the pool */
|
|
#define RandaddByte(x) {\
|
|
if (nRandIndex == RNG_POOL_SIZE) nRandIndex = 0;\
|
|
pRandPool[nRandIndex] = (unsigned char) ((unsigned char)x + pRandPool[nRandIndex]); \
|
|
if (nRandIndex % RANDMIX_BYTE_INTERVAL == 0) Randmix();\
|
|
nRandIndex++; \
|
|
}
|
|
|
|
/* Macro to add four bytes to the pool */
|
|
#define RandaddInt32(x) RandAddInt((unsigned __int32)x);
|
|
|
|
#define RandaddIntPtr(x) RandAddInt64((unsigned __int64)x);
|
|
|
|
void RandAddInt (unsigned __int32 x)
|
|
{
|
|
RandaddByte(x);
|
|
RandaddByte((x >> 8));
|
|
RandaddByte((x >> 16));
|
|
RandaddByte((x >> 24));
|
|
}
|
|
|
|
void RandAddInt64 (unsigned __int64 x)
|
|
{
|
|
RandaddByte(x);
|
|
RandaddByte((x >> 8));
|
|
RandaddByte((x >> 16));
|
|
RandaddByte((x >> 24));
|
|
|
|
RandaddByte((x >> 32));
|
|
RandaddByte((x >> 40));
|
|
RandaddByte((x >> 48));
|
|
RandaddByte((x >> 56));
|
|
}
|
|
|
|
#include <tlhelp32.h>
|
|
#include "Dlgcode.h"
|
|
|
|
#ifndef SRC_POS
|
|
#define SRC_POS (__FUNCTION__ ":" TC_TO_STRING(__LINE__))
|
|
#endif
|
|
|
|
HHOOK hMouse = NULL; /* Mouse hook for the random number generator */
|
|
HHOOK hKeyboard = NULL; /* Keyboard hook for the random number generator */
|
|
DWORD ProcessedMouseEventsCounter = 0;
|
|
|
|
/* Variables for thread control, the thread is used to gather up info about
|
|
the system in in the background */
|
|
CRITICAL_SECTION critRandProt; /* The critical section */
|
|
BOOL volatile bThreadTerminate = FALSE; /* This variable is shared among thread's so its made volatile */
|
|
|
|
// CryptoAPI
|
|
DWORD CryptoAPILastError = ERROR_SUCCESS;
|
|
|
|
typedef DWORD (WINAPI *RtlNtStatusToDosError_t)(NTSTATUS);
|
|
RtlNtStatusToDosError_t pRtlNtStatusToDosError = NULL;
|
|
|
|
static HMODULE hPdhLib = NULL;
|
|
|
|
typedef PDH_STATUS (WINAPI *PfnPdhOpenQueryW)(LPCWSTR, DWORD_PTR, PDH_HQUERY *);
|
|
typedef PDH_STATUS (WINAPI *PfnPdhAddCounterW)(PDH_HQUERY, LPCWSTR, DWORD_PTR, PDH_HCOUNTER *);
|
|
typedef PDH_STATUS (WINAPI *PfnPdhCollectQueryData)(PDH_HQUERY);
|
|
typedef PDH_STATUS (WINAPI *PfnPdhGetFormattedCounterValue)(PDH_HCOUNTER, DWORD, LPDWORD, PPDH_FMT_COUNTERVALUE);
|
|
typedef PDH_STATUS (WINAPI *PfnPdhCloseQuery)(PDH_HQUERY);
|
|
|
|
static PfnPdhOpenQueryW pfnPdhOpenQuery = NULL;
|
|
static PfnPdhAddCounterW pfnPdhAddCounter = NULL;
|
|
static PfnPdhCollectQueryData pfnPdhCollectQueryData = NULL;
|
|
static PfnPdhGetFormattedCounterValue pfnPdhGetFormattedCounterValue = NULL;
|
|
static PfnPdhCloseQuery pfnPdhCloseQuery = NULL;
|
|
|
|
static BOOL LoadPdhDll()
|
|
{
|
|
if (!hPdhLib)
|
|
{
|
|
hPdhLib = LoadLibraryExW(L"pdh.dll", NULL, LOAD_LIBRARY_SEARCH_SYSTEM32);
|
|
if (!hPdhLib)
|
|
return FALSE;
|
|
|
|
pfnPdhOpenQuery = (PfnPdhOpenQueryW) GetProcAddress(hPdhLib, "PdhOpenQueryW");
|
|
pfnPdhAddCounter = (PfnPdhAddCounterW) GetProcAddress(hPdhLib, "PdhAddCounterW");
|
|
pfnPdhCollectQueryData = (PfnPdhCollectQueryData) GetProcAddress(hPdhLib, "PdhCollectQueryData");
|
|
pfnPdhGetFormattedCounterValue = (PfnPdhGetFormattedCounterValue) GetProcAddress(hPdhLib, "PdhGetFormattedCounterValue");
|
|
pfnPdhCloseQuery = (PfnPdhCloseQuery) GetProcAddress(hPdhLib, "PdhCloseQuery");
|
|
}
|
|
|
|
return (pfnPdhOpenQuery && pfnPdhAddCounter && pfnPdhCollectQueryData &&
|
|
pfnPdhGetFormattedCounterValue && pfnPdhCloseQuery);
|
|
}
|
|
|
|
/* Init the random number generator, setup the hooks, and start the thread */
|
|
int RandinitWithCheck ( int* pAlreadyInitialized)
|
|
{
|
|
BOOL bIgnoreHookError = FALSE;
|
|
DWORD dwLastError = ERROR_SUCCESS;
|
|
HMODULE ntdll;
|
|
if (GetMaxPkcs5OutSize() > RNG_POOL_SIZE)
|
|
TC_THROW_FATAL_EXCEPTION;
|
|
|
|
if(bRandDidInit)
|
|
{
|
|
if (pAlreadyInitialized)
|
|
*pAlreadyInitialized = 1;
|
|
return 0;
|
|
}
|
|
|
|
if (pAlreadyInitialized)
|
|
*pAlreadyInitialized = 0;
|
|
|
|
InitializeCriticalSection (&critRandProt);
|
|
|
|
bRandDidInit = TRUE;
|
|
CryptoAPILastError = ERROR_SUCCESS;
|
|
ProcessedMouseEventsCounter = 0;
|
|
|
|
if (pRandPool == NULL)
|
|
{
|
|
pRandPool = (unsigned char *) _aligned_malloc (RANDOMPOOL_ALLOCSIZE, 16);
|
|
if (pRandPool == NULL)
|
|
goto error;
|
|
|
|
bDidSlowPoll = FALSE;
|
|
RandomPoolEnrichedByUser = FALSE;
|
|
|
|
memset (pRandPool, 0, RANDOMPOOL_ALLOCSIZE);
|
|
VirtualLock (pRandPool, RANDOMPOOL_ALLOCSIZE);
|
|
}
|
|
|
|
bIgnoreHookError = IsThreadInSecureDesktop(GetCurrentThreadId());
|
|
|
|
hKeyboard = SetWindowsHookEx (WH_KEYBOARD, (HOOKPROC)&KeyboardProc, NULL, GetCurrentThreadId ());
|
|
if (hKeyboard == 0 && !bIgnoreHookError) handleWin32Error (0, SRC_POS);
|
|
|
|
hMouse = SetWindowsHookEx (WH_MOUSE, (HOOKPROC)&MouseProc, NULL, GetCurrentThreadId ());
|
|
if (hMouse == 0 && !bIgnoreHookError)
|
|
{
|
|
handleWin32Error (0, SRC_POS);
|
|
goto error;
|
|
}
|
|
|
|
ntdll = GetModuleHandleW(L"ntdll.dll");
|
|
if (!ntdll) {
|
|
// If ntdll.dll is not found, return a fallback error code
|
|
CryptoAPILastError = ERROR_MOD_NOT_FOUND;
|
|
goto error;
|
|
}
|
|
else
|
|
pRtlNtStatusToDosError = (RtlNtStatusToDosError_t)GetProcAddress(ntdll, "RtlNtStatusToDosError");
|
|
|
|
if (!(PeriodicFastPollThreadHandle = (HANDLE) _beginthreadex (NULL, 0, PeriodicFastPollThreadProc, NULL, 0, NULL)))
|
|
goto error;
|
|
|
|
return 0;
|
|
|
|
error:
|
|
dwLastError = GetLastError();
|
|
RandStop (TRUE);
|
|
SetLastError (dwLastError);
|
|
return 1;
|
|
}
|
|
|
|
int Randinit ()
|
|
{
|
|
return RandinitWithCheck (NULL);
|
|
}
|
|
|
|
/* Close everything down, including the thread which is closed down by
|
|
setting a flag which eventually causes the thread function to exit */
|
|
void RandStop (BOOL freePool)
|
|
{
|
|
if (!bRandDidInit && freePool && pRandPool)
|
|
goto freePool;
|
|
|
|
if (bRandDidInit == FALSE)
|
|
return;
|
|
|
|
EnterCriticalSection (&critRandProt);
|
|
|
|
if (hMouse != 0)
|
|
UnhookWindowsHookEx (hMouse);
|
|
if (hKeyboard != 0)
|
|
UnhookWindowsHookEx (hKeyboard);
|
|
|
|
bThreadTerminate = TRUE;
|
|
|
|
LeaveCriticalSection (&critRandProt);
|
|
|
|
if (PeriodicFastPollThreadHandle)
|
|
WaitForSingleObject (PeriodicFastPollThreadHandle, INFINITE);
|
|
|
|
|
|
hMouse = NULL;
|
|
hKeyboard = NULL;
|
|
bThreadTerminate = FALSE;
|
|
DeleteCriticalSection (&critRandProt);
|
|
|
|
bRandDidInit = FALSE;
|
|
|
|
freePool:
|
|
if (freePool)
|
|
{
|
|
bDidSlowPoll = FALSE;
|
|
RandomPoolEnrichedByUser = FALSE;
|
|
|
|
if (pRandPool != NULL)
|
|
{
|
|
burn (pRandPool, RANDOMPOOL_ALLOCSIZE);
|
|
_aligned_free (pRandPool);
|
|
pRandPool = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
BOOL IsRandomNumberGeneratorStarted ()
|
|
{
|
|
return bRandDidInit;
|
|
}
|
|
|
|
void RandSetHashFunction (int hash_algo_id)
|
|
{
|
|
if (HashIsDeprecated (hash_algo_id))
|
|
hash_algo_id = DEFAULT_HASH_ALGORITHM;
|
|
|
|
HashFunction = hash_algo_id;
|
|
}
|
|
|
|
int RandGetHashFunction (void)
|
|
{
|
|
return HashFunction;
|
|
}
|
|
|
|
void SetRandomPoolEnrichedByUserStatus (BOOL enriched)
|
|
{
|
|
RandomPoolEnrichedByUser = enriched;
|
|
}
|
|
|
|
BOOL IsRandomPoolEnrichedByUser ()
|
|
{
|
|
return RandomPoolEnrichedByUser;
|
|
}
|
|
|
|
/* The random pool mixing function */
|
|
BOOL Randmix ()
|
|
{
|
|
if (bRandmixEnabled)
|
|
{
|
|
unsigned char hashOutputBuffer [MAX_DIGESTSIZE];
|
|
#ifndef WOLFCRYPT_BACKEND
|
|
WHIRLPOOL_CTX wctx;
|
|
blake2s_state bctx;
|
|
STREEBOG_CTX stctx;
|
|
#endif
|
|
sha512_ctx sctx;
|
|
sha256_ctx s256ctx;
|
|
int poolIndex, digestIndex, digestSize;
|
|
|
|
switch (HashFunction)
|
|
{
|
|
case SHA512:
|
|
digestSize = SHA512_DIGESTSIZE;
|
|
break;
|
|
|
|
case SHA256:
|
|
digestSize = SHA256_DIGESTSIZE;
|
|
break;
|
|
|
|
#ifndef WOLFCRYPT_BACKEND
|
|
case BLAKE2S:
|
|
digestSize = BLAKE2S_DIGESTSIZE;
|
|
break;
|
|
|
|
case WHIRLPOOL:
|
|
digestSize = WHIRLPOOL_DIGESTSIZE;
|
|
break;
|
|
|
|
case STREEBOG:
|
|
digestSize = STREEBOG_DIGESTSIZE;
|
|
break;
|
|
#endif
|
|
default:
|
|
TC_THROW_FATAL_EXCEPTION;
|
|
}
|
|
|
|
if (RNG_POOL_SIZE % digestSize)
|
|
TC_THROW_FATAL_EXCEPTION;
|
|
|
|
for (poolIndex = 0; poolIndex < RNG_POOL_SIZE; poolIndex += digestSize)
|
|
{
|
|
/* Compute the message digest of the entire pool using the selected hash function. */
|
|
switch (HashFunction)
|
|
{
|
|
case SHA512:
|
|
sha512_begin (&sctx);
|
|
sha512_hash (pRandPool, RNG_POOL_SIZE, &sctx);
|
|
sha512_end (hashOutputBuffer, &sctx);
|
|
break;
|
|
|
|
case SHA256:
|
|
sha256_begin (&s256ctx);
|
|
sha256_hash (pRandPool, RNG_POOL_SIZE, &s256ctx);
|
|
sha256_end (hashOutputBuffer, &s256ctx);
|
|
break;
|
|
|
|
#ifndef WOLFCRYPT_BACKEND
|
|
case BLAKE2S:
|
|
blake2s_init(&bctx);
|
|
blake2s_update(&bctx, pRandPool, RNG_POOL_SIZE);
|
|
blake2s_final(&bctx, hashOutputBuffer);
|
|
break;
|
|
|
|
case WHIRLPOOL:
|
|
WHIRLPOOL_init (&wctx);
|
|
WHIRLPOOL_add (pRandPool, RNG_POOL_SIZE, &wctx);
|
|
WHIRLPOOL_finalize (&wctx, hashOutputBuffer);
|
|
break;
|
|
|
|
case STREEBOG:
|
|
STREEBOG_init (&stctx);
|
|
STREEBOG_add (&stctx, pRandPool, RNG_POOL_SIZE);
|
|
STREEBOG_finalize (&stctx, hashOutputBuffer);
|
|
break;
|
|
#endif
|
|
default:
|
|
// Unknown/wrong ID
|
|
TC_THROW_FATAL_EXCEPTION;
|
|
}
|
|
|
|
/* XOR the resultant message digest to the pool at the poolIndex position. */
|
|
for (digestIndex = 0; digestIndex < digestSize; digestIndex++)
|
|
{
|
|
pRandPool [poolIndex + digestIndex] ^= hashOutputBuffer [digestIndex];
|
|
}
|
|
}
|
|
|
|
/* Prevent leaks */
|
|
burn (hashOutputBuffer, MAX_DIGESTSIZE);
|
|
switch (HashFunction)
|
|
{
|
|
case SHA512:
|
|
burn (&sctx, sizeof(sctx));
|
|
break;
|
|
|
|
case SHA256:
|
|
burn (&s256ctx, sizeof(s256ctx));
|
|
break;
|
|
|
|
#ifndef WOLFCRYPT_BACKEND
|
|
case BLAKE2S:
|
|
burn (&bctx, sizeof(bctx));
|
|
break;
|
|
|
|
case WHIRLPOOL:
|
|
burn (&wctx, sizeof(wctx));
|
|
break;
|
|
|
|
case STREEBOG:
|
|
burn (&stctx, sizeof(sctx));
|
|
break;
|
|
#endif
|
|
default:
|
|
// Unknown/wrong ID
|
|
TC_THROW_FATAL_EXCEPTION;
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
/* Add a buffer to the pool */
|
|
void RandaddBuf (void *buf, int len)
|
|
{
|
|
int i;
|
|
for (i = 0; i < len; i++)
|
|
{
|
|
RandaddByte (((unsigned char *) buf)[i]);
|
|
}
|
|
}
|
|
|
|
BOOL RandpeekBytes (void* hwndDlg, unsigned char *buf, int len, DWORD* mouseCounter)
|
|
{
|
|
if (!bRandDidInit)
|
|
return FALSE;
|
|
|
|
if (len > RNG_POOL_SIZE)
|
|
{
|
|
Error ("ERR_NOT_ENOUGH_RANDOM_DATA", (HWND) hwndDlg);
|
|
len = RNG_POOL_SIZE;
|
|
}
|
|
|
|
EnterCriticalSection (&critRandProt);
|
|
*mouseCounter = ProcessedMouseEventsCounter;
|
|
memcpy (buf, pRandPool, len);
|
|
LeaveCriticalSection (&critRandProt);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/* Get len random bytes from the pool (max. RNG_POOL_SIZE bytes per a single call) */
|
|
BOOL RandgetBytes (void* hwndDlg, unsigned char *buf, int len, BOOL forceSlowPoll)
|
|
{
|
|
return RandgetBytesFull (hwndDlg, buf, len, forceSlowPoll, FALSE);
|
|
}
|
|
|
|
/* Get len random bytes from the pool.
|
|
* If allowAnyLength is FALSE, then len must be less or equal to RNG_POOL_SIZE
|
|
* If allowAnyLength is TRUE, then len can have any positive value
|
|
*/
|
|
BOOL RandgetBytesFull ( void* hwndDlg, unsigned char *buf , int len, BOOL forceSlowPoll , BOOL allowAnyLength)
|
|
{
|
|
int i, looplen;
|
|
BOOL ret = TRUE;
|
|
|
|
if (!bRandDidInit || HashFunction == 0)
|
|
TC_THROW_FATAL_EXCEPTION;
|
|
|
|
EnterCriticalSection (&critRandProt);
|
|
|
|
if (bDidSlowPoll == FALSE || forceSlowPoll)
|
|
{
|
|
if (!SlowPoll ())
|
|
{
|
|
handleError ((HWND) hwndDlg, ERR_CAPI_INIT_FAILED, SRC_POS);
|
|
ret = FALSE;
|
|
}
|
|
else
|
|
bDidSlowPoll = TRUE;
|
|
}
|
|
|
|
if (!FastPoll ())
|
|
{
|
|
handleError ((HWND) hwndDlg, ERR_CAPI_INIT_FAILED, SRC_POS);
|
|
ret = FALSE;
|
|
}
|
|
|
|
/* There's never more than RNG_POOL_SIZE worth of randomess */
|
|
if ( (!allowAnyLength) && (len > RNG_POOL_SIZE))
|
|
{
|
|
Error ("ERR_NOT_ENOUGH_RANDOM_DATA", (HWND) hwndDlg);
|
|
len = RNG_POOL_SIZE;
|
|
LeaveCriticalSection (&critRandProt);
|
|
return FALSE;
|
|
}
|
|
|
|
while (len > 0)
|
|
{
|
|
if (len > RNG_POOL_SIZE)
|
|
{
|
|
looplen = RNG_POOL_SIZE;
|
|
len -= RNG_POOL_SIZE;
|
|
}
|
|
else
|
|
{
|
|
looplen = len;
|
|
len = 0;
|
|
}
|
|
|
|
// this loop number of bytes is copied from pool to output buffer,
|
|
// pool is rehashed, and output buffer is XORed with new data from pool
|
|
for (i = 0; i < looplen; i++)
|
|
{
|
|
buf[i] = pRandPool[randPoolReadIndex++];
|
|
if (randPoolReadIndex == RNG_POOL_SIZE) randPoolReadIndex = 0;
|
|
}
|
|
|
|
/* Invert the pool */
|
|
for (i = 0; i < RNG_POOL_SIZE / 4; i++)
|
|
{
|
|
((unsigned __int32 *) pRandPool)[i] = ~((unsigned __int32 *) pRandPool)[i];
|
|
}
|
|
|
|
// Mix the pool
|
|
if (!FastPoll ())
|
|
ret = FALSE;
|
|
|
|
// XOR the current pool content into the output buffer to prevent pool state leaks
|
|
for (i = 0; i < looplen; i++)
|
|
{
|
|
buf[i] ^= pRandPool[randPoolReadIndex++];
|
|
if (randPoolReadIndex == RNG_POOL_SIZE) randPoolReadIndex = 0;
|
|
}
|
|
|
|
// increment the pointer for the next loop
|
|
buf += looplen;
|
|
}
|
|
|
|
LeaveCriticalSection (&critRandProt);
|
|
|
|
if (!ret)
|
|
TC_THROW_FATAL_EXCEPTION;
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* Capture the mouse, and as long as the event is not the same as the last
|
|
two events, add the crc of the event, and the crc of the time difference
|
|
between this event and the last + the current time to the pool.
|
|
The role of CRC-32 is merely to perform diffusion. Note that the output
|
|
of CRC-32 is subsequently processed using a cryptographically secure hash
|
|
algorithm. */
|
|
LRESULT CALLBACK MouseProc (int nCode, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
static DWORD dwLastTimer;
|
|
static unsigned __int32 lastCrc, lastCrc2;
|
|
static POINT lastPoint;
|
|
MOUSEHOOKSTRUCT *lpMouse = (MOUSEHOOKSTRUCT *) lParam;
|
|
|
|
if (nCode < 0)
|
|
return CallNextHookEx (hMouse, nCode, wParam, lParam);
|
|
else
|
|
{
|
|
DWORD dwTimer = GetTickCount ();
|
|
DWORD j = dwLastTimer - dwTimer;
|
|
unsigned __int32 crc = 0L;
|
|
int i;
|
|
POINT pt = lpMouse->pt;
|
|
|
|
dwLastTimer = dwTimer;
|
|
|
|
for (i = 0; i < sizeof (MOUSEHOOKSTRUCT); i++)
|
|
{
|
|
crc = UPDC32 (((unsigned char *) lpMouse)[i], crc);
|
|
}
|
|
|
|
if (crc != lastCrc && crc != lastCrc2)
|
|
{
|
|
unsigned __int32 timeCrc = 0L;
|
|
|
|
for (i = 0; i < 4; i++)
|
|
{
|
|
timeCrc = UPDC32 (((unsigned char *) &j)[i], timeCrc);
|
|
}
|
|
|
|
for (i = 0; i < 4; i++)
|
|
{
|
|
timeCrc = UPDC32 (((unsigned char *) &dwTimer)[i], timeCrc);
|
|
}
|
|
|
|
EnterCriticalSection (&critRandProt);
|
|
/* only count real mouse messages in entropy estimation */
|
|
if ( (nCode == HC_ACTION) && (wParam == WM_MOUSEMOVE)
|
|
&& ((pt.x != lastPoint.x) || (pt.y != lastPoint.y)))
|
|
{
|
|
ProcessedMouseEventsCounter++;
|
|
lastPoint = pt;
|
|
}
|
|
RandaddInt32 ((unsigned __int32) (crc + timeCrc));
|
|
LeaveCriticalSection (&critRandProt);
|
|
}
|
|
lastCrc2 = lastCrc;
|
|
lastCrc = crc;
|
|
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* Capture the keyboard, as long as the event is not the same as the last two
|
|
events, add the crc of the event to the pool along with the crc of the time
|
|
difference between this event and the last. The role of CRC-32 is merely to
|
|
perform diffusion. Note that the output of CRC-32 is subsequently processed
|
|
using a cryptographically secure hash algorithm. */
|
|
LRESULT CALLBACK KeyboardProc (int nCode, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
static int lLastKey, lLastKey2;
|
|
static DWORD dwLastTimer;
|
|
int nKey = (lParam & 0x00ff0000) >> 16;
|
|
int nCapture = 0;
|
|
|
|
if (nCode < 0)
|
|
return CallNextHookEx (hMouse, nCode, wParam, lParam);
|
|
|
|
if ((lParam & 0x0000ffff) == 1 && !(lParam & 0x20000000) &&
|
|
(lParam & 0x80000000))
|
|
{
|
|
if (nKey != lLastKey)
|
|
nCapture = 1; /* Capture this key */
|
|
else if (nKey != lLastKey2)
|
|
nCapture = 1; /* Allow for one repeat */
|
|
}
|
|
if (nCapture)
|
|
{
|
|
DWORD dwTimer = GetTickCount ();
|
|
DWORD j = dwLastTimer - dwTimer;
|
|
unsigned __int32 timeCrc = 0L;
|
|
int i;
|
|
|
|
dwLastTimer = dwTimer;
|
|
lLastKey2 = lLastKey;
|
|
lLastKey = nKey;
|
|
|
|
for (i = 0; i < 4; i++)
|
|
{
|
|
timeCrc = UPDC32 (((unsigned char *) &j)[i], timeCrc);
|
|
}
|
|
|
|
for (i = 0; i < 4; i++)
|
|
{
|
|
timeCrc = UPDC32 (((unsigned char *) &dwTimer)[i], timeCrc);
|
|
}
|
|
|
|
EnterCriticalSection (&critRandProt);
|
|
RandaddInt32 ((unsigned __int32) (GetCrc32((unsigned char*) &lParam, sizeof(lParam)) + timeCrc));
|
|
LeaveCriticalSection (&critRandProt);
|
|
}
|
|
|
|
return CallNextHookEx (hMouse, nCode, wParam, lParam);
|
|
}
|
|
|
|
/* This is the thread function which will poll the system for randomness */
|
|
static unsigned __stdcall PeriodicFastPollThreadProc (void *dummy)
|
|
{
|
|
UNREFERENCED_PARAMETER (dummy); /* Remove unused parameter warning */
|
|
|
|
for (;;)
|
|
{
|
|
EnterCriticalSection (&critRandProt);
|
|
|
|
if (bThreadTerminate)
|
|
{
|
|
bThreadTerminate = FALSE;
|
|
LeaveCriticalSection (&critRandProt);
|
|
_endthreadex (0);
|
|
}
|
|
else if (bFastPollEnabled)
|
|
{
|
|
FastPoll ();
|
|
}
|
|
|
|
LeaveCriticalSection (&critRandProt);
|
|
|
|
Sleep (FASTPOLL_INTERVAL);
|
|
}
|
|
}
|
|
|
|
|
|
/* -------------------------------------------------------------------------------------
|
|
GetDiskStatistics: This function uses the Windows Performance Data Helper (PDH) API
|
|
to collect disk statistics. The function collects the number of disk reads and writes
|
|
per second for all physical disks. The function also collects high-resolution
|
|
timestamps before and after the PDH query. The function then adds the collected data
|
|
to the random pool.
|
|
The code waits a short random interval between the two PDH samples to ensures that
|
|
the performance counters have time to accumulate measurable changes and produce more
|
|
varied data.
|
|
-------------------------------------------------------------------------------------
|
|
|
|
*/
|
|
void GetDiskStatistics()
|
|
{
|
|
if (!LoadPdhDll())
|
|
return;
|
|
PDH_STATUS status;
|
|
PDH_HQUERY query = NULL;
|
|
PDH_HCOUNTER counterReads = NULL, counterWrites = NULL;
|
|
PDH_FMT_COUNTERVALUE counterValue;
|
|
DWORD dwType;
|
|
LONGLONG llReads = 0, llWrites = 0;
|
|
DWORDLONG tstampBefore = 0, tstampAfter = 0;
|
|
LARGE_INTEGER perfCounterBefore, perfCounterAfter;
|
|
|
|
// High-resolution timestamps
|
|
if (!QueryPerformanceCounter(&perfCounterBefore))
|
|
return;
|
|
tstampBefore = GetTickCount64();
|
|
|
|
// Open PDH query
|
|
status = pfnPdhOpenQuery(NULL, 0, &query);
|
|
if (status != ERROR_SUCCESS)
|
|
goto error;
|
|
|
|
// Add counters for disk reads and writes (all physical disks).
|
|
status = pfnPdhAddCounter(query, L"\\PhysicalDisk(*)\\Disk Reads/sec", 0, &counterReads);
|
|
if (status != ERROR_SUCCESS)
|
|
goto error;
|
|
|
|
status = pfnPdhAddCounter(query, L"\\PhysicalDisk(*)\\Disk Writes/sec", 0, &counterWrites);
|
|
if (status != ERROR_SUCCESS)
|
|
goto error;
|
|
|
|
// First sample
|
|
status = pfnPdhCollectQueryData(query);
|
|
if (status != ERROR_SUCCESS)
|
|
goto error;
|
|
|
|
// Wait a short random interval
|
|
Sleep(10 + (GetCurrentProcessId() % 40));
|
|
|
|
// Second sample
|
|
status = pfnPdhCollectQueryData(query);
|
|
if (status != ERROR_SUCCESS)
|
|
goto error;
|
|
|
|
// Format counters in PDH_FMT_LARGE
|
|
status = pfnPdhGetFormattedCounterValue(counterReads, PDH_FMT_LARGE, &dwType, &counterValue);
|
|
if (status == ERROR_SUCCESS)
|
|
llReads = counterValue.largeValue;
|
|
|
|
status = pfnPdhGetFormattedCounterValue(counterWrites, PDH_FMT_LARGE, &dwType, &counterValue);
|
|
if (status == ERROR_SUCCESS)
|
|
llWrites = counterValue.largeValue;
|
|
|
|
// Another high-resolution timestamp
|
|
if (!QueryPerformanceCounter(&perfCounterAfter))
|
|
goto error;
|
|
tstampAfter = GetTickCount64();
|
|
|
|
// Close PDH query
|
|
pfnPdhCloseQuery(query);
|
|
query = NULL;
|
|
|
|
// Collect results into the random pool
|
|
RandaddBuf(&llReads, sizeof(llReads));
|
|
RandaddBuf(&llWrites, sizeof(llWrites));
|
|
RandaddBuf(&tstampBefore, sizeof(tstampBefore));
|
|
RandaddBuf(&tstampAfter, sizeof(tstampAfter));
|
|
RandaddBuf(&perfCounterBefore.QuadPart, sizeof(perfCounterBefore.QuadPart));
|
|
RandaddBuf(&perfCounterAfter.QuadPart, sizeof(perfCounterAfter.QuadPart));
|
|
|
|
error:
|
|
if (query)
|
|
pfnPdhCloseQuery(query);
|
|
}
|
|
|
|
|
|
/* -------------------------------------------------------------------------------------
|
|
GetNetworkStatistics: This function uses the Windows Performance Data Helper (PDH) API
|
|
to collect network statistics. The function collects the number of bytes sent and
|
|
received per second for all network interfaces. The function also collects
|
|
high-resolution timestamps before and after the PDH query. The function then adds the
|
|
collected data to the random pool.
|
|
The code waits a short random interval between the two PDH samples to ensures that
|
|
the performance counters have time to accumulate measurable changes and produce more
|
|
varied data.
|
|
*/
|
|
void GetNetworkStatistics()
|
|
{
|
|
if (!LoadPdhDll())
|
|
return;
|
|
PDH_STATUS status;
|
|
PDH_HQUERY query = NULL;
|
|
PDH_HCOUNTER counterBytesSent = NULL, counterBytesReceived = NULL;
|
|
PDH_FMT_COUNTERVALUE counterValue;
|
|
DWORD dwType;
|
|
LONGLONG llBytesSent = 0, llBytesReceived = 0;
|
|
DWORDLONG tstampBefore = 0, tstampAfter = 0;
|
|
LARGE_INTEGER perfCounterBefore, perfCounterAfter;
|
|
|
|
// High-resolution timestamps
|
|
if (!QueryPerformanceCounter(&perfCounterBefore))
|
|
return;
|
|
tstampBefore = GetTickCount64();
|
|
|
|
// Open PDH query
|
|
status = pfnPdhOpenQuery(NULL, 0, &query);
|
|
if (status != ERROR_SUCCESS)
|
|
goto error;
|
|
|
|
// Add counters for network bytes sent and received
|
|
status = pfnPdhAddCounter(query, L"\\Network Interface(*)\\Bytes Sent/sec", 0, &counterBytesSent);
|
|
if (status != ERROR_SUCCESS)
|
|
goto error;
|
|
|
|
status = pfnPdhAddCounter(query, L"\\Network Interface(*)\\Bytes Received/sec", 0, &counterBytesReceived);
|
|
if (status != ERROR_SUCCESS)
|
|
goto error;
|
|
|
|
// First sample
|
|
status = pfnPdhCollectQueryData(query);
|
|
if (status != ERROR_SUCCESS)
|
|
goto error;
|
|
|
|
// Wait short, dynamic interval
|
|
Sleep(10 + (GetCurrentProcessId() % 40));
|
|
|
|
// Second sample
|
|
status = pfnPdhCollectQueryData(query);
|
|
if (status != ERROR_SUCCESS)
|
|
goto error;
|
|
|
|
// Format counters
|
|
status = pfnPdhGetFormattedCounterValue(counterBytesSent, PDH_FMT_LARGE, &dwType, &counterValue);
|
|
if (status == ERROR_SUCCESS)
|
|
llBytesSent = counterValue.largeValue;
|
|
|
|
status = pfnPdhGetFormattedCounterValue(counterBytesReceived, PDH_FMT_LARGE, &dwType, &counterValue);
|
|
if (status == ERROR_SUCCESS)
|
|
llBytesReceived = counterValue.largeValue;
|
|
|
|
if (!QueryPerformanceCounter(&perfCounterAfter))
|
|
goto error;
|
|
tstampAfter = GetTickCount64();
|
|
|
|
// Close PDH query
|
|
pfnPdhCloseQuery(query);
|
|
query = NULL;
|
|
|
|
// Collect results into our random pool
|
|
RandaddBuf(&llBytesSent, sizeof(llBytesSent));
|
|
RandaddBuf(&llBytesReceived, sizeof(llBytesReceived));
|
|
RandaddBuf(&tstampBefore, sizeof(tstampBefore));
|
|
RandaddBuf(&tstampAfter, sizeof(tstampAfter));
|
|
RandaddBuf(&perfCounterBefore.QuadPart, sizeof(perfCounterBefore.QuadPart));
|
|
RandaddBuf(&perfCounterAfter.QuadPart, sizeof(perfCounterAfter.QuadPart));
|
|
|
|
error:
|
|
if (query)
|
|
pfnPdhCloseQuery(query);
|
|
}
|
|
|
|
/* This is the slowpoll function which gathers up network/hard drive
|
|
performance data for the random pool */
|
|
BOOL SlowPoll (void)
|
|
{
|
|
NTSTATUS bStatus = 0;
|
|
|
|
// Gather disk stats via PDH
|
|
GetDiskStatistics();
|
|
|
|
// Gather network stats via PDH
|
|
GetNetworkStatistics();
|
|
|
|
bStatus = BCryptGenRandom(NULL, buffer, sizeof(buffer), BCRYPT_USE_SYSTEM_PREFERRED_RNG);
|
|
if (NT_SUCCESS(bStatus))
|
|
{
|
|
RandaddBuf (buffer, sizeof (buffer));
|
|
}
|
|
else
|
|
{
|
|
/* return error in case BCryptGenRandom fails */
|
|
CryptoAPILastError = pRtlNtStatusToDosError (bStatus);
|
|
return FALSE;
|
|
}
|
|
|
|
/* use JitterEntropy library to get good quality random bytes based on CPU timing jitter */
|
|
if (0 == jent_entropy_init ())
|
|
{
|
|
struct rand_data *ec = jent_entropy_collector_alloc (1, 0);
|
|
if (ec)
|
|
{
|
|
ssize_t rndLen = jent_read_entropy (ec, (char*) buffer, sizeof (buffer));
|
|
if (rndLen > 0)
|
|
RandaddBuf (buffer, (int) rndLen);
|
|
jent_entropy_collector_free (ec);
|
|
}
|
|
}
|
|
|
|
#ifndef _M_ARM64
|
|
// use RDSEED or RDRAND from CPU as source of entropy if present
|
|
if ( IsCpuRngEnabled() &&
|
|
( (HasRDSEED() && RDSEED_getBytes (buffer, sizeof (buffer)))
|
|
|| (HasRDRAND() && RDRAND_getBytes (buffer, sizeof (buffer)))
|
|
))
|
|
{
|
|
RandaddBuf (buffer, sizeof (buffer));
|
|
}
|
|
#endif
|
|
|
|
burn(buffer, sizeof (buffer));
|
|
|
|
/* Mix the pool */
|
|
Randmix();
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/* This is the fastpoll function which gathers up info by calling various api's */
|
|
BOOL FastPoll (void)
|
|
{
|
|
int nOriginalRandIndex = nRandIndex;
|
|
static BOOL addedFixedItems = FALSE;
|
|
FILETIME creationTime, exitTime, kernelTime, userTime;
|
|
SIZE_T minimumWorkingSetSize, maximumWorkingSetSize;
|
|
LARGE_INTEGER performanceCount;
|
|
MEMORYSTATUSEX memoryStatus;
|
|
HANDLE handle;
|
|
POINT point;
|
|
NTSTATUS bStatus = 0;
|
|
|
|
/* Get various basic pieces of system information */
|
|
RandaddIntPtr (GetActiveWindow ()); /* Handle of active window */
|
|
RandaddIntPtr (GetCapture ()); /* Handle of window with mouse
|
|
capture */
|
|
RandaddIntPtr (GetClipboardOwner ()); /* Handle of clipboard owner */
|
|
RandaddIntPtr (GetClipboardViewer ()); /* Handle of start of
|
|
clpbd.viewer list */
|
|
RandaddIntPtr (GetCurrentProcess ()); /* Pseudohandle of current
|
|
process */
|
|
RandaddInt32 (GetCurrentProcessId ()); /* Current process ID */
|
|
RandaddIntPtr (GetCurrentThread ()); /* Pseudohandle of current
|
|
thread */
|
|
RandaddInt32 (GetCurrentThreadId ()); /* Current thread ID */
|
|
RandaddInt32 (GetCurrentTime ()); /* Milliseconds since Windows
|
|
started */
|
|
RandaddIntPtr (GetDesktopWindow ()); /* Handle of desktop window */
|
|
RandaddIntPtr (GetFocus ()); /* Handle of window with kb.focus */
|
|
RandaddInt32 (GetInputState ()); /* Whether sys.queue has any events */
|
|
RandaddInt32 (GetMessagePos ()); /* Cursor pos.for last message */
|
|
RandaddInt32 (GetMessageTime ()); /* 1 ms time for last message */
|
|
RandaddIntPtr (GetOpenClipboardWindow ()); /* Handle of window with
|
|
clpbd.open */
|
|
RandaddIntPtr (GetProcessHeap ()); /* Handle of process heap */
|
|
RandaddIntPtr (GetProcessWindowStation ()); /* Handle of procs
|
|
window station */
|
|
RandaddInt32 (GetQueueStatus (QS_ALLEVENTS)); /* Types of events in
|
|
input queue */
|
|
|
|
/* Get multiword system information */
|
|
GetCaretPos (&point); /* Current caret position */
|
|
RandaddBuf ((unsigned char *) &point, sizeof (POINT));
|
|
GetCursorPos (&point); /* Current mouse cursor position */
|
|
RandaddBuf ((unsigned char *) &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 (MEMORYSTATUSEX);
|
|
GlobalMemoryStatusEx (&memoryStatus);
|
|
RandaddBuf ((unsigned char *) &memoryStatus, sizeof (MEMORYSTATUSEX));
|
|
|
|
/* 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);
|
|
RandaddBuf ((unsigned char *) &creationTime, sizeof (FILETIME));
|
|
RandaddBuf ((unsigned char *) &exitTime, sizeof (FILETIME));
|
|
RandaddBuf ((unsigned char *) &kernelTime, sizeof (FILETIME));
|
|
RandaddBuf ((unsigned char *) &userTime, sizeof (FILETIME));
|
|
handle = GetCurrentProcess ();
|
|
GetProcessTimes (handle, &creationTime, &exitTime, &kernelTime, &userTime);
|
|
RandaddBuf ((unsigned char *) &creationTime, sizeof (FILETIME));
|
|
RandaddBuf ((unsigned char *) &exitTime, sizeof (FILETIME));
|
|
RandaddBuf ((unsigned char *) &kernelTime, sizeof (FILETIME));
|
|
RandaddBuf ((unsigned char *) &userTime, sizeof (FILETIME));
|
|
|
|
/* Get the minimum and maximum working set size for the current
|
|
process */
|
|
GetProcessWorkingSetSize (handle, &minimumWorkingSetSize,
|
|
&maximumWorkingSetSize);
|
|
RandaddIntPtr (minimumWorkingSetSize);
|
|
RandaddIntPtr (maximumWorkingSetSize);
|
|
|
|
/* The following are fixed for the lifetime of the process so we only
|
|
add them once */
|
|
if (addedFixedItems == 0)
|
|
{
|
|
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);
|
|
RandaddBuf ((unsigned char *) &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))
|
|
RandaddBuf ((unsigned char *) &performanceCount, sizeof (LARGE_INTEGER));
|
|
else
|
|
{
|
|
/* Millisecond accuracy at best... */
|
|
DWORD dwTicks = GetTickCount ();
|
|
RandaddBuf ((unsigned char *) &dwTicks, sizeof (dwTicks));
|
|
}
|
|
|
|
|
|
bStatus = BCryptGenRandom(NULL, buffer, sizeof(buffer), BCRYPT_USE_SYSTEM_PREFERRED_RNG);
|
|
if (NT_SUCCESS(bStatus))
|
|
{
|
|
RandaddBuf (buffer, sizeof (buffer));
|
|
}
|
|
else
|
|
{
|
|
/* return error in case BCryptGenRandom fails */
|
|
CryptoAPILastError = pRtlNtStatusToDosError (bStatus);
|
|
return FALSE;
|
|
}
|
|
|
|
#ifndef _M_ARM64
|
|
// use RDSEED or RDRAND from CPU as source of entropy if enabled
|
|
if ( IsCpuRngEnabled() &&
|
|
( (HasRDSEED() && RDSEED_getBytes (buffer, sizeof (buffer)))
|
|
|| (HasRDRAND() && RDRAND_getBytes (buffer, sizeof (buffer)))
|
|
))
|
|
{
|
|
RandaddBuf (buffer, sizeof (buffer));
|
|
}
|
|
#endif
|
|
|
|
burn (buffer, sizeof(buffer));
|
|
|
|
/* Apply the pool mixing function */
|
|
Randmix();
|
|
|
|
/* Restore the original pool cursor position. If this wasn't done, mouse coordinates
|
|
could be written to a limited area of the pool, especially when moving the mouse
|
|
uninterruptedly. The severity of the problem would depend on the length of data
|
|
written by FastPoll (if it was equal to the size of the pool, mouse coordinates
|
|
would be written only to a particular 4-byte area, whenever moving the mouse
|
|
uninterruptedly). */
|
|
nRandIndex = nOriginalRandIndex;
|
|
|
|
return TRUE;
|
|
}
|
|
|