mirror of
https://github.com/veracrypt/VeraCrypt.git
synced 2025-11-11 11:08:02 -06:00
Windows: use modern API to gather system entropy for random generation instead of obsolete that were not working
This commit increases randomness quality by using more dynamic/varied sources of entropy. PDH-based disk and network statistics collection in now added to random pool - Introduced `GetDiskStatistics` to gather disk read/write performance data using PDH API. - Introduced `GetNetworkStatistics` to gather network send/receive performance data using PDH API. - Integrated high-resolution timestamps and random intervals to improve entropy in collected data. - Updated `SlowPoll` function to utilize PDH-based disk and network statistics. - Removed obsolete NetAPI32-based network statistics collection.
This commit is contained in:
@@ -20,6 +20,8 @@
|
||||
#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;
|
||||
@@ -82,15 +84,45 @@ DWORD ProcessedMouseEventsCounter = 0;
|
||||
CRITICAL_SECTION critRandProt; /* The critical section */
|
||||
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
|
||||
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)
|
||||
{
|
||||
@@ -191,12 +223,6 @@ void RandStop (BOOL freePool)
|
||||
if (PeriodicFastPollThreadHandle)
|
||||
WaitForSingleObject (PeriodicFastPollThreadHandle, INFINITE);
|
||||
|
||||
if (hNetAPI32 != 0)
|
||||
{
|
||||
FreeLibrary (hNetAPI32);
|
||||
hNetAPI32 = NULL;
|
||||
}
|
||||
|
||||
|
||||
hMouse = NULL;
|
||||
hKeyboard = NULL;
|
||||
@@ -254,7 +280,7 @@ BOOL Randmix ()
|
||||
if (bRandmixEnabled)
|
||||
{
|
||||
unsigned char hashOutputBuffer [MAX_DIGESTSIZE];
|
||||
#ifndef WOLFCRYPT_BACKEND
|
||||
#ifndef WOLFCRYPT_BACKEND
|
||||
WHIRLPOOL_CTX wctx;
|
||||
blake2s_state bctx;
|
||||
STREEBOG_CTX stctx;
|
||||
@@ -273,11 +299,11 @@ BOOL Randmix ()
|
||||
digestSize = SHA256_DIGESTSIZE;
|
||||
break;
|
||||
|
||||
#ifndef WOLFCRYPT_BACKEND
|
||||
#ifndef WOLFCRYPT_BACKEND
|
||||
case BLAKE2S:
|
||||
digestSize = BLAKE2S_DIGESTSIZE;
|
||||
break;
|
||||
|
||||
|
||||
case WHIRLPOOL:
|
||||
digestSize = WHIRLPOOL_DIGESTSIZE;
|
||||
break;
|
||||
@@ -640,142 +666,192 @@ static unsigned __stdcall PeriodicFastPollThreadProc (void *dummy)
|
||||
}
|
||||
}
|
||||
|
||||
/* Type definitions for function pointers to call NetAPI32 functions */
|
||||
|
||||
typedef
|
||||
DWORD (WINAPI * NETSTATISTICSGET) (LPWSTR szServer, LPWSTR szService,
|
||||
DWORD dwLevel, DWORD dwOptions,
|
||||
LPBYTE * lpBuffer);
|
||||
typedef
|
||||
DWORD (WINAPI * NETAPIBUFFERSIZE) (LPVOID lpBuffer, LPDWORD cbBuffer);
|
||||
typedef
|
||||
DWORD (WINAPI * NETAPIBUFFERFREE) (LPVOID lpBuffer);
|
||||
/* -------------------------------------------------------------------------------------
|
||||
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.
|
||||
-------------------------------------------------------------------------------------
|
||||
|
||||
NETSTATISTICSGET pNetStatisticsGet = NULL;
|
||||
NETAPIBUFFERSIZE pNetApiBufferSize = NULL;
|
||||
NETAPIBUFFERFREE pNetApiBufferFree = NULL;
|
||||
*/
|
||||
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)
|
||||
{
|
||||
static int isWorkstation = -1;
|
||||
static int cbPerfData = 0x10000;
|
||||
HANDLE hDevice;
|
||||
LPBYTE lpBuffer;
|
||||
DWORD dwSize, status;
|
||||
LPWSTR lpszLanW, lpszLanS;
|
||||
int nDrive;
|
||||
NTSTATUS bStatus = 0;
|
||||
|
||||
/* Find out whether this is an NT server or workstation if necessary */
|
||||
if (isWorkstation == -1)
|
||||
{
|
||||
HKEY hKey;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
// 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))
|
||||
@@ -803,7 +879,7 @@ BOOL SlowPoll (void)
|
||||
}
|
||||
|
||||
// use RDSEED or RDRAND from CPU as source of entropy if present
|
||||
if ( IsCpuRngEnabled() &&
|
||||
if ( IsCpuRngEnabled() &&
|
||||
( (HasRDSEED() && RDSEED_getBytes (buffer, sizeof (buffer)))
|
||||
|| (HasRDRAND() && RDRAND_getBytes (buffer, sizeof (buffer)))
|
||||
))
|
||||
@@ -812,6 +888,8 @@ BOOL SlowPoll (void)
|
||||
}
|
||||
|
||||
burn(buffer, sizeof (buffer));
|
||||
|
||||
/* Mix the pool */
|
||||
Randmix();
|
||||
|
||||
return TRUE;
|
||||
@@ -934,7 +1012,7 @@ BOOL FastPoll (void)
|
||||
}
|
||||
|
||||
// use RDSEED or RDRAND from CPU as source of entropy if enabled
|
||||
if ( IsCpuRngEnabled() &&
|
||||
if ( IsCpuRngEnabled() &&
|
||||
( (HasRDSEED() && RDSEED_getBytes (buffer, sizeof (buffer)))
|
||||
|| (HasRDRAND() && RDRAND_getBytes (buffer, sizeof (buffer)))
|
||||
))
|
||||
|
||||
Reference in New Issue
Block a user