diff options
author | Mounir IDRASSI <mounir.idrassi@idrix.fr> | 2025-02-02 13:51:33 +0100 |
---|---|---|
committer | Mounir IDRASSI <mounir.idrassi@idrix.fr> | 2025-02-02 13:51:33 +0100 |
commit | 04e7d8c5ee563dc0b72a030c57d1a2fa28133e87 (patch) | |
tree | 1e44f4e8f44dea2db13cff3620b8088e46ee07fa | |
parent | 498dff9013d18e5978ab77c14ea2b2d0229603a4 (diff) | |
download | VeraCrypt-04e7d8c5ee563dc0b72a030c57d1a2fa28133e87.tar.gz VeraCrypt-04e7d8c5ee563dc0b72a030c57d1a2fa28133e87.zip |
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.
-rw-r--r-- | src/Common/Random.c | 346 |
1 files changed, 134 insertions, 212 deletions
diff --git a/src/Common/Random.c b/src/Common/Random.c index 10995f74..4c6382b4 100644 --- a/src/Common/Random.c +++ b/src/Common/Random.c @@ -20,8 +20,6 @@ #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; @@ -84,45 +82,15 @@ 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) { @@ -223,6 +191,12 @@ void RandStop (BOOL freePool) if (PeriodicFastPollThreadHandle) WaitForSingleObject (PeriodicFastPollThreadHandle, INFINITE); + if (hNetAPI32 != 0) + { + FreeLibrary (hNetAPI32); + hNetAPI32 = NULL; + } + hMouse = NULL; hKeyboard = NULL; @@ -280,7 +254,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; @@ -299,11 +273,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; @@ -666,192 +640,142 @@ static unsigned __stdcall PeriodicFastPollThreadProc (void *dummy) } } +/* Type definitions for function pointers to call NetAPI32 functions */ -/* ------------------------------------------------------------------------------------- - 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. - ------------------------------------------------------------------------------------- +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); -*/ -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); -} +NETSTATISTICSGET pNetStatisticsGet = NULL; +NETAPIBUFFERSIZE pNetApiBufferSize = NULL; +NETAPIBUFFERFREE pNetApiBufferFree = NULL; -/* ------------------------------------------------------------------------------------- - 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() +/* This is the slowpoll function which gathers up network/hard drive + performance data for the random pool */ +BOOL SlowPoll (void) { - 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; + static int isWorkstation = -1; + static int cbPerfData = 0x10000; + HANDLE hDevice; + LPBYTE lpBuffer; + DWORD dwSize, status; + LPWSTR lpszLanW, lpszLanS; + int nDrive; + NTSTATUS bStatus = 0; - // First sample - status = pfnPdhCollectQueryData(query); - if (status != ERROR_SUCCESS) - goto error; + /* Find out whether this is an NT server or workstation if necessary */ + if (isWorkstation == -1) + { + HKEY hKey; - // Wait short, dynamic interval - Sleep(10 + (GetCurrentProcessId() % 40)); + if (RegOpenKeyEx (HKEY_LOCAL_MACHINE, + L"SYSTEM\\CurrentControlSet\\Control\\ProductOptions", + 0, KEY_READ, &hKey) == ERROR_SUCCESS) + { + wchar_t szValue[32]; + dwSize = sizeof (szValue); - // Second sample - status = pfnPdhCollectQueryData(query); - if (status != ERROR_SUCCESS) - goto error; + isWorkstation = TRUE; + status = RegQueryValueEx (hKey, L"ProductType", 0, NULL, + (LPBYTE) szValue, &dwSize); - // Format counters - status = pfnPdhGetFormattedCounterValue(counterBytesSent, PDH_FMT_LARGE, &dwType, &counterValue); - if (status == ERROR_SUCCESS) - llBytesSent = counterValue.largeValue; + 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; - status = pfnPdhGetFormattedCounterValue(counterBytesReceived, PDH_FMT_LARGE, &dwType, &counterValue); - if (status == ERROR_SUCCESS) - llBytesReceived = counterValue.largeValue; + 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"); - if (!QueryPerformanceCounter(&perfCounterAfter)) - goto error; - tstampAfter = GetTickCount64(); + 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; + } + } + } - // Close PDH query - pfnPdhCloseQuery(query); - query = 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); + } - // 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)); + /* 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; -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; + /* 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)) @@ -880,7 +804,7 @@ BOOL SlowPoll (void) #ifndef _M_ARM64 // 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))) )) @@ -890,8 +814,6 @@ BOOL SlowPoll (void) #endif burn(buffer, sizeof (buffer)); - - /* Mix the pool */ Randmix(); return TRUE; @@ -1015,7 +937,7 @@ BOOL FastPoll (void) #ifndef _M_ARM64 // 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))) )) |