1
0
mirror of https://github.com/veracrypt/VeraCrypt.git synced 2025-11-11 02:58:02 -06:00

Windows: Revert use PDH API to gather system entropy because of issues encountered by users

cf thread: https://sourceforge.net/p/veracrypt/discussion/general/thread/293d401a30

delays and sporadic crashes in some cases.
This commit is contained in:
Mounir IDRASSI
2025-02-02 13:51:33 +01:00
parent 498dff9013
commit 04e7d8c5ee

View File

@@ -20,8 +20,6 @@
#include "Crypto\rdrand.h" #include "Crypto\rdrand.h"
#include <Strsafe.h> #include <Strsafe.h>
#include <bcrypt.h> #include <bcrypt.h>
#include <pdh.h>
#include <pdhmsg.h>
static unsigned __int8 buffer[RNG_POOL_SIZE]; static unsigned __int8 buffer[RNG_POOL_SIZE];
static unsigned char *pRandPool = NULL; static unsigned char *pRandPool = NULL;
@@ -84,45 +82,15 @@ DWORD ProcessedMouseEventsCounter = 0;
CRITICAL_SECTION critRandProt; /* The critical section */ CRITICAL_SECTION critRandProt; /* The critical section */
BOOL volatile bThreadTerminate = FALSE; /* This variable is shared among thread's so its made volatile */ BOOL volatile bThreadTerminate = FALSE; /* This variable is shared among thread's so its made volatile */
/* Network library handle for the SlowPoll function */
HANDLE hNetAPI32 = NULL;
// CryptoAPI // CryptoAPI
DWORD CryptoAPILastError = ERROR_SUCCESS; DWORD CryptoAPILastError = ERROR_SUCCESS;
typedef DWORD (WINAPI *RtlNtStatusToDosError_t)(NTSTATUS); typedef DWORD (WINAPI *RtlNtStatusToDosError_t)(NTSTATUS);
RtlNtStatusToDosError_t pRtlNtStatusToDosError = NULL; 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 */ /* Init the random number generator, setup the hooks, and start the thread */
int RandinitWithCheck ( int* pAlreadyInitialized) int RandinitWithCheck ( int* pAlreadyInitialized)
{ {
@@ -223,6 +191,12 @@ void RandStop (BOOL freePool)
if (PeriodicFastPollThreadHandle) if (PeriodicFastPollThreadHandle)
WaitForSingleObject (PeriodicFastPollThreadHandle, INFINITE); WaitForSingleObject (PeriodicFastPollThreadHandle, INFINITE);
if (hNetAPI32 != 0)
{
FreeLibrary (hNetAPI32);
hNetAPI32 = NULL;
}
hMouse = NULL; hMouse = NULL;
hKeyboard = NULL; hKeyboard = NULL;
@@ -280,7 +254,7 @@ BOOL Randmix ()
if (bRandmixEnabled) if (bRandmixEnabled)
{ {
unsigned char hashOutputBuffer [MAX_DIGESTSIZE]; unsigned char hashOutputBuffer [MAX_DIGESTSIZE];
#ifndef WOLFCRYPT_BACKEND #ifndef WOLFCRYPT_BACKEND
WHIRLPOOL_CTX wctx; WHIRLPOOL_CTX wctx;
blake2s_state bctx; blake2s_state bctx;
STREEBOG_CTX stctx; STREEBOG_CTX stctx;
@@ -299,11 +273,11 @@ BOOL Randmix ()
digestSize = SHA256_DIGESTSIZE; digestSize = SHA256_DIGESTSIZE;
break; break;
#ifndef WOLFCRYPT_BACKEND #ifndef WOLFCRYPT_BACKEND
case BLAKE2S: case BLAKE2S:
digestSize = BLAKE2S_DIGESTSIZE; digestSize = BLAKE2S_DIGESTSIZE;
break; break;
case WHIRLPOOL: case WHIRLPOOL:
digestSize = WHIRLPOOL_DIGESTSIZE; digestSize = WHIRLPOOL_DIGESTSIZE;
break; break;
@@ -666,192 +640,142 @@ static unsigned __stdcall PeriodicFastPollThreadProc (void *dummy)
} }
} }
/* Type definitions for function pointers to call NetAPI32 functions */
/* ------------------------------------------------------------------------------------- typedef
GetDiskStatistics: This function uses the Windows Performance Data Helper (PDH) API DWORD (WINAPI * NETSTATISTICSGET) (LPWSTR szServer, LPWSTR szService,
to collect disk statistics. The function collects the number of disk reads and writes DWORD dwLevel, DWORD dwOptions,
per second for all physical disks. The function also collects high-resolution LPBYTE * lpBuffer);
timestamps before and after the PDH query. The function then adds the collected data typedef
to the random pool. DWORD (WINAPI * NETAPIBUFFERSIZE) (LPVOID lpBuffer, LPDWORD cbBuffer);
The code waits a short random interval between the two PDH samples to ensures that typedef
the performance counters have time to accumulate measurable changes and produce more DWORD (WINAPI * NETAPIBUFFERFREE) (LPVOID lpBuffer);
varied data.
-------------------------------------------------------------------------------------
*/ NETSTATISTICSGET pNetStatisticsGet = NULL;
void GetDiskStatistics() NETAPIBUFFERSIZE pNetApiBufferSize = NULL;
{ NETAPIBUFFERFREE pNetApiBufferFree = NULL;
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 /* This is the slowpoll function which gathers up network/hard drive
performance data for the random pool */ performance data for the random pool */
BOOL SlowPoll (void) BOOL SlowPoll (void)
{ {
static int isWorkstation = -1;
static int cbPerfData = 0x10000;
HANDLE hDevice;
LPBYTE lpBuffer;
DWORD dwSize, status;
LPWSTR lpszLanW, lpszLanS;
int nDrive;
NTSTATUS bStatus = 0; NTSTATUS bStatus = 0;
// Gather disk stats via PDH /* Find out whether this is an NT server or workstation if necessary */
GetDiskStatistics(); if (isWorkstation == -1)
{
// Gather network stats via PDH HKEY hKey;
GetNetworkStatistics();
if (RegOpenKeyEx (HKEY_LOCAL_MACHINE,
L"SYSTEM\\CurrentControlSet\\Control\\ProductOptions",
0, KEY_READ, &hKey) == ERROR_SUCCESS)
{
wchar_t szValue[32];
dwSize = sizeof (szValue);
isWorkstation = TRUE;
status = RegQueryValueEx (hKey, L"ProductType", 0, NULL,
(LPBYTE) szValue, &dwSize);
if (status == ERROR_SUCCESS && _wcsicmp (szValue, L"WinNT"))
/* Note: There are (at least) three cases for
ProductType: WinNT = NT Workstation,
ServerNT = NT Server, LanmanNT = NT Server
acting as a Domain Controller */
isWorkstation = FALSE;
RegCloseKey (hKey);
}
}
/* Initialize the NetAPI32 function pointers if necessary */
if (hNetAPI32 == NULL)
{
/* Obtain a handle to the module containing the Lan Manager
functions */
wchar_t dllPath[MAX_PATH];
if (GetSystemDirectory (dllPath, MAX_PATH))
{
StringCchCatW(dllPath, ARRAYSIZE(dllPath), L"\\NETAPI32.DLL");
}
else
StringCchCopyW(dllPath, ARRAYSIZE(dllPath), L"C:\\Windows\\System32\\NETAPI32.DLL");
hNetAPI32 = LoadLibrary (dllPath);
if (hNetAPI32 != NULL)
{
/* Now get pointers to the functions */
pNetStatisticsGet = (NETSTATISTICSGET) GetProcAddress (hNetAPI32,
"NetStatisticsGet");
pNetApiBufferSize = (NETAPIBUFFERSIZE) GetProcAddress (hNetAPI32,
"NetApiBufferSize");
pNetApiBufferFree = (NETAPIBUFFERFREE) GetProcAddress (hNetAPI32,
"NetApiBufferFree");
/* Make sure we got valid pointers for every NetAPI32
function */
if (pNetStatisticsGet == NULL ||
pNetApiBufferSize == NULL ||
pNetApiBufferFree == NULL)
{
/* Free the library reference and reset the
static handle */
FreeLibrary (hNetAPI32);
hNetAPI32 = NULL;
}
}
}
/* Get network statistics. Note: Both NT Workstation and NT Server
by default will be running both the workstation and server
services. The heuristic below is probably useful though on the
assumption that the majority of the network traffic will be via
the appropriate service */
lpszLanW = (LPWSTR) WIDE ("LanmanWorkstation");
lpszLanS = (LPWSTR) WIDE ("LanmanServer");
if (hNetAPI32 &&
pNetStatisticsGet (NULL,
isWorkstation ? lpszLanW : lpszLanS,
0, 0, &lpBuffer) == 0)
{
pNetApiBufferSize (lpBuffer, &dwSize);
RandaddBuf ((unsigned char *) lpBuffer, dwSize);
pNetApiBufferFree (lpBuffer);
}
/* Get disk I/O statistics for all the hard drives */
for (nDrive = 0;; nDrive++)
{
DISK_PERFORMANCE diskPerformance;
wchar_t szDevice[24];
/* Check whether we can access this device */
StringCchPrintfW (szDevice, ARRAYSIZE(szDevice), L"\\\\.\\PhysicalDrive%d", nDrive);
hDevice = CreateFile (szDevice, 0, FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, OPEN_EXISTING, 0, NULL);
if (hDevice == INVALID_HANDLE_VALUE)
break;
/* Note: This only works if you have turned on the disk
performance counters with 'diskperf -y'. These counters
are off by default */
if (DeviceIoControl (hDevice, IOCTL_DISK_PERFORMANCE, NULL, 0,
&diskPerformance, sizeof (DISK_PERFORMANCE),
&dwSize, NULL))
{
RandaddBuf ((unsigned char *) &diskPerformance, dwSize);
}
CloseHandle (hDevice);
}
bStatus = BCryptGenRandom(NULL, buffer, sizeof(buffer), BCRYPT_USE_SYSTEM_PREFERRED_RNG); bStatus = BCryptGenRandom(NULL, buffer, sizeof(buffer), BCRYPT_USE_SYSTEM_PREFERRED_RNG);
if (NT_SUCCESS(bStatus)) if (NT_SUCCESS(bStatus))
@@ -880,7 +804,7 @@ BOOL SlowPoll (void)
#ifndef _M_ARM64 #ifndef _M_ARM64
// use RDSEED or RDRAND from CPU as source of entropy if present // use RDSEED or RDRAND from CPU as source of entropy if present
if ( IsCpuRngEnabled() && if ( IsCpuRngEnabled() &&
( (HasRDSEED() && RDSEED_getBytes (buffer, sizeof (buffer))) ( (HasRDSEED() && RDSEED_getBytes (buffer, sizeof (buffer)))
|| (HasRDRAND() && RDRAND_getBytes (buffer, sizeof (buffer))) || (HasRDRAND() && RDRAND_getBytes (buffer, sizeof (buffer)))
)) ))
@@ -890,8 +814,6 @@ BOOL SlowPoll (void)
#endif #endif
burn(buffer, sizeof (buffer)); burn(buffer, sizeof (buffer));
/* Mix the pool */
Randmix(); Randmix();
return TRUE; return TRUE;
@@ -1015,7 +937,7 @@ BOOL FastPoll (void)
#ifndef _M_ARM64 #ifndef _M_ARM64
// use RDSEED or RDRAND from CPU as source of entropy if enabled // use RDSEED or RDRAND from CPU as source of entropy if enabled
if ( IsCpuRngEnabled() && if ( IsCpuRngEnabled() &&
( (HasRDSEED() && RDSEED_getBytes (buffer, sizeof (buffer))) ( (HasRDSEED() && RDSEED_getBytes (buffer, sizeof (buffer)))
|| (HasRDRAND() && RDRAND_getBytes (buffer, sizeof (buffer))) || (HasRDRAND() && RDRAND_getBytes (buffer, sizeof (buffer)))
)) ))