diff options
author | SoniEx2 <endermoneymod@gmail.com> | 2021-04-09 07:19:03 -0300 |
---|---|---|
committer | SoniEx2 <endermoneymod@gmail.com> | 2021-04-09 07:19:03 -0300 |
commit | 0e752a6e215aee21dc73da097c3225495d54a5b6 (patch) | |
tree | b81be02cbf2f06aebf322ac4a5d014b44176bba5 /libotr/libgcrypt-1.8.7/random/rndw32.c | |
parent | 7754076c715285173311a1b6811ce377950e18a6 (diff) |
Add libotr/etc sources
Diffstat (limited to 'libotr/libgcrypt-1.8.7/random/rndw32.c')
-rw-r--r-- | libotr/libgcrypt-1.8.7/random/rndw32.c | 1030 |
1 files changed, 1030 insertions, 0 deletions
diff --git a/libotr/libgcrypt-1.8.7/random/rndw32.c b/libotr/libgcrypt-1.8.7/random/rndw32.c new file mode 100644 index 0000000..08a8867 --- /dev/null +++ b/libotr/libgcrypt-1.8.7/random/rndw32.c @@ -0,0 +1,1030 @@ +/* rndw32.c - W32 entropy gatherer + * Copyright (C) 1999, 2000, 2002, 2003, 2007, + * 2010 Free Software Foundation, Inc. + * Copyright Peter Gutmann, Matt Thomlinson and Blake Coverett 1996-2006 + * + * This file is part of Libgcrypt. + * + ************************************************************************* + * The code here is based on code from Cryptlib 3.0 beta by Peter Gutmann. + * Source file misc/rndwin32.c "Win32 Randomness-Gathering Code" with this + * copyright notice: + * + * This module is part of the cryptlib continuously seeded pseudorandom + * number generator. For usage conditions, see lib_rand.c + * + * [Here is the notice from lib_rand.c, which is now called dev_sys.c] + * + * This module and the misc/rnd*.c modules represent the cryptlib + * continuously seeded pseudorandom number generator (CSPRNG) as described in + * my 1998 Usenix Security Symposium paper "The generation of random numbers + * for cryptographic purposes". + * + * The CSPRNG code is copyright Peter Gutmann (and various others) 1996, + * 1997, 1998, 1999, all rights reserved. Redistribution of the CSPRNG + * modules and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice + * and this permission notice in its entirety. + * + * 2. Redistributions in binary form must reproduce the copyright notice in + * the documentation and/or other materials provided with the distribution. + * + * 3. A copy of any bugfixes or enhancements made must be provided to the + * author, <pgut001@cs.auckland.ac.nz> to allow them to be added to the + * baseline version of the code. + * + * ALTERNATIVELY, the code may be distributed under the terms of the + * GNU Lesser General Public License, version 2.1 or any later version + * published by the Free Software Foundation, in which case the + * provisions of the GNU LGPL are required INSTEAD OF the above + * restrictions. + * + * Although not required under the terms of the LGPL, it would still + * be nice if you could make any changes available to the author to + * allow a consistent code base to be maintained. + ************************************************************************* + * The above alternative was changed from GPL to LGPL on 2007-08-22 with + * permission from Peter Gutmann: + *========== + From: pgut001 <pgut001@cs.auckland.ac.nz> + Subject: Re: LGPL for the windows entropy gatherer + To: wk@gnupg.org + Date: Wed, 22 Aug 2007 03:05:42 +1200 + + Hi, + + >As of now libgcrypt is GPL under Windows due to that module and some people + >would really like to see it under LGPL too. Can you do such a license change + >to LGPL version 2? Note that LGPL give the user the option to relicense it + >under GPL, so the change would be pretty easy and backwar compatible. + + Sure. I assumed that since GPG was GPLd, you'd prefer the GPL for the entropy + code as well, but Ian asked for LGPL as an option so as of the next release + I'll have LGPL in there. You can consider it to be retroactive, so your + current version will be LGPLd as well. + + Peter. + *========== + */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <string.h> +#ifdef __GNUC__ +#include <stdint.h> +#endif + +#include <winsock2.h> +#include <windows.h> + + +#include "types.h" +#include "g10lib.h" +#include "rand-internal.h" + + +/* Definitions which are missing from the current GNU Windows32Api. */ +#ifndef IOCTL_DISK_PERFORMANCE +#define IOCTL_DISK_PERFORMANCE 0x00070020 +#endif + +/* This used to be (6*8+5*4+8*2), but Peter Gutmann figured a larger + value in a newer release. So we use a far larger value. */ +#define SIZEOF_DISK_PERFORMANCE_STRUCT 256 + +/* We don't include wincrypt.h so define it here. */ +#define HCRYPTPROV HANDLE + + +/* When we query the performance counters, we allocate an initial buffer and + * then reallocate it as required until RegQueryValueEx() stops returning + * ERROR_MORE_DATA. The following values define the initial buffer size and + * step size by which the buffer is increased + */ +#define PERFORMANCE_BUFFER_SIZE 65536 /* Start at 64K */ +#define PERFORMANCE_BUFFER_STEP 16384 /* Step by 16K */ + + +/* The number of bytes to read from the system RNG on each slow poll. */ +#define SYSTEMRNG_BYTES 64 + +/* Intel Chipset CSP type and name */ +#define PROV_INTEL_SEC 22 +#define INTEL_DEF_PROV "Intel Hardware Cryptographic Service Provider" + + + + +/* 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); + +/* Type definitions for function pointers to call native NT functions. */ +typedef DWORD (WINAPI *NTQUERYSYSTEMINFORMATION)(DWORD systemInformationClass, + PVOID systemInformation, + ULONG systemInformationLength, + PULONG returnLength); +typedef DWORD (WINAPI *NTQUERYINFORMATIONPROCESS) + (HANDLE processHandle, DWORD processInformationClass, + PVOID processInformation, ULONG processInformationLength, + PULONG returnLength); +typedef DWORD (WINAPI *NTPOWERINFORMATION) + (DWORD powerInformationClass, PVOID inputBuffer, + ULONG inputBufferLength, PVOID outputBuffer, ULONG outputBufferLength ); + +/* Type definitions for function pointers to call CryptoAPI functions. */ +typedef BOOL (WINAPI *CRYPTACQUIRECONTEXT)(HCRYPTPROV *phProv, + LPCTSTR pszContainer, + LPCTSTR pszProvider, + DWORD dwProvType, + DWORD dwFlags); +typedef BOOL (WINAPI *CRYPTGENRANDOM)(HCRYPTPROV hProv, DWORD dwLen, + BYTE *pbBuffer); +typedef BOOL (WINAPI *CRYPTRELEASECONTEXT)(HCRYPTPROV hProv, DWORD dwFlags); + +/* Somewhat alternative functionality available as a direct call, for + Windows XP and newer. This is the CryptoAPI RNG, which isn't anywhere + near as good as the HW RNG, but we use it if it's present on the basis + that at least it can't make things any worse. This direct access version + is only available under Windows XP, we don't go out of our way to access + the more general CryptoAPI one since the main purpose of using it is to + take advantage of any possible future hardware RNGs that may be added, + for example via TCPA devices. */ +typedef BOOL (WINAPI *RTLGENRANDOM)(PVOID RandomBuffer, + ULONG RandomBufferLength); + + + +/* MBM data structures, originally by Alexander van Kaam, converted to C by + Anders@Majland.org, finally updated by Chris Zahrt <techn0@iastate.edu> */ +#define BusType char +#define SMBType char +#define SensorType char + +typedef struct +{ + SensorType iType; /* Type of sensor. */ + int Count; /* Number of sensor for that type. */ +} SharedIndex; + +typedef struct +{ + SensorType ssType; /* Type of sensor */ + unsigned char ssName[12]; /* Name of sensor */ + char sspadding1[3]; /* Padding of 3 bytes */ + double ssCurrent; /* Current value */ + double ssLow; /* Lowest readout */ + double ssHigh; /* Highest readout */ + long ssCount; /* Total number of readout */ + char sspadding2[4]; /* Padding of 4 bytes */ + long double ssTotal; /* Total amount of all readouts */ + char sspadding3[6]; /* Padding of 6 bytes */ + double ssAlarm1; /* Temp & fan: high alarm; voltage: % off */ + double ssAlarm2; /* Temp: low alarm */ +} SharedSensor; + +typedef struct +{ + short siSMB_Base; /* SMBus base address */ + BusType siSMB_Type; /* SMBus/Isa bus used to access chip */ + SMBType siSMB_Code; /* SMBus sub type, Intel, AMD or ALi */ + char siSMB_Addr; /* Address of sensor chip on SMBus */ + unsigned char siSMB_Name[41]; /* Nice name for SMBus */ + short siISA_Base; /* ISA base address of sensor chip on ISA */ + int siChipType; /* Chip nr, connects with Chipinfo.ini */ + char siVoltageSubType; /* Subvoltage option selected */ +} SharedInfo; + +typedef struct +{ + double sdVersion; /* Version number (example: 51090) */ + SharedIndex sdIndex[10]; /* Sensor index */ + SharedSensor sdSensor[100]; /* Sensor info */ + SharedInfo sdInfo; /* Misc.info */ + unsigned char sdStart[41]; /* Start time */ + + /* We don't use the next two fields both because they're not random + and because it provides a nice safety margin in case of data size + mis- estimates (we always under-estimate the buffer size). */ +#if 0 + unsigned char sdCurrent[41]; /* Current time */ + unsigned char sdPath[256]; /* MBM path */ +#endif /*0*/ +} SharedData; + + + +/* One time initialized handles and function pointers. We use dynamic + loading of the DLLs to do without them in case libgcrypt does not + need any random. */ +static HANDLE hNetAPI32; +static NETSTATISTICSGET pNetStatisticsGet; +static NETAPIBUFFERSIZE pNetApiBufferSize; +static NETAPIBUFFERFREE pNetApiBufferFree; + +static HANDLE hNTAPI; +static NTQUERYSYSTEMINFORMATION pNtQuerySystemInformation; +static NTQUERYINFORMATIONPROCESS pNtQueryInformationProcess; +static NTPOWERINFORMATION pNtPowerInformation; + +static HANDLE hAdvAPI32; +static CRYPTACQUIRECONTEXT pCryptAcquireContext; +static CRYPTGENRANDOM pCryptGenRandom; +static CRYPTRELEASECONTEXT pCryptReleaseContext; +static RTLGENRANDOM pRtlGenRandom; + + +/* Other module global variables. */ +static int system_rng_available; /* Whether a system RNG is available. */ +static HCRYPTPROV hRNGProv; /* Handle to Intel RNG CSP. */ + +/* The debug flag. Debugging is enabled if the value of the envvar + * GCRY_RNDW32_DBG is a positive number.*/ +static int debug_me; + +static int system_is_w2000; /* True if running on W2000. */ + + + +/* Try and connect to the system RNG if there's one present. */ +static void +init_system_rng (void) +{ + system_rng_available = 0; + hRNGProv = NULL; + + hAdvAPI32 = GetModuleHandle ("AdvAPI32.dll"); + if (!hAdvAPI32) + return; + + pCryptAcquireContext = (CRYPTACQUIRECONTEXT) + GetProcAddress (hAdvAPI32, "CryptAcquireContextA"); + pCryptGenRandom = (CRYPTGENRANDOM) + GetProcAddress (hAdvAPI32, "CryptGenRandom"); + pCryptReleaseContext = (CRYPTRELEASECONTEXT) + GetProcAddress (hAdvAPI32, "CryptReleaseContext"); + + /* Get a pointer to the native randomness function if it's available. + This isn't exported by name, so we have to get it by ordinal. */ + pRtlGenRandom = (RTLGENRANDOM) + GetProcAddress (hAdvAPI32, "SystemFunction036"); + + /* Try and connect to the PIII RNG CSP. The AMD 768 southbridge (from + the 760 MP chipset) also has a hardware RNG, but there doesn't appear + to be any driver support for this as there is for the Intel RNG so we + can't do much with it. OTOH the Intel RNG is also effectively dead + as well, mostly due to virtually nonexistent support/marketing by + Intel, it's included here mostly for form's sake. */ + if ( (!pCryptAcquireContext || !pCryptGenRandom || !pCryptReleaseContext + || !pCryptAcquireContext (&hRNGProv, NULL, INTEL_DEF_PROV, + PROV_INTEL_SEC, 0) ) + && !pRtlGenRandom) + { + hAdvAPI32 = NULL; + } + else + system_rng_available = 1; +} + + +/* Read data from the system RNG if availavle. */ +static void +read_system_rng (void (*add)(const void*, size_t, enum random_origins), + enum random_origins requester) +{ + BYTE buffer[ SYSTEMRNG_BYTES + 8 ]; + int quality = 0; + + if (!system_rng_available) + return; + + /* Read SYSTEMRNG_BYTES bytes from the system RNG. We don't rely on + this for all our randomness requirements (particularly the + software RNG) in case it's broken in some way. */ + if (hRNGProv) + { + if (pCryptGenRandom (hRNGProv, SYSTEMRNG_BYTES, buffer)) + quality = 80; + } + else if (pRtlGenRandom) + { + if ( pRtlGenRandom (buffer, SYSTEMRNG_BYTES)) + quality = 50; + } + if (quality > 0) + { + if (debug_me) + log_debug ("rndw32#read_system_rng: got %d bytes of quality %d\n", + SYSTEMRNG_BYTES, quality); + (*add) (buffer, SYSTEMRNG_BYTES, requester); + wipememory (buffer, SYSTEMRNG_BYTES); + } +} + + +/* Read data from MBM. This communicates via shared memory, so all we + need to do is map a file and read the data out. */ +static void +read_mbm_data (void (*add)(const void*, size_t, enum random_origins), + enum random_origins requester) +{ + HANDLE hMBMData; + SharedData *mbmDataPtr; + + hMBMData = OpenFileMapping (FILE_MAP_READ, FALSE, "$M$B$M$5$S$D$" ); + if (hMBMData) + { + mbmDataPtr = (SharedData*)MapViewOfFile (hMBMData, FILE_MAP_READ,0,0,0); + if (mbmDataPtr) + { + if (debug_me) + log_debug ("rndw32#read_mbm_data: got %d bytes\n", + (int)sizeof (SharedData)); + (*add) (mbmDataPtr, sizeof (SharedData), requester); + UnmapViewOfFile (mbmDataPtr); + } + CloseHandle (hMBMData); + } +} + + +/* Fallback method using the registry to poll the statistics. */ +static void +registry_poll (void (*add)(const void*, size_t, enum random_origins), + enum random_origins requester) +{ + static int cbPerfData = PERFORMANCE_BUFFER_SIZE; + int iterations; + DWORD dwSize, status; + PERF_DATA_BLOCK *pPerfData; + + /* Get information from the system performance counters. This can take a + few seconds to do. In some environments the call to RegQueryValueEx() + can produce an access violation at some random time in the future, in + some cases adding a short delay after the following code block makes + the problem go away. This problem is extremely difficult to + reproduce, I haven't been able to get it to occur despite running it + on a number of machines. MS knowledge base article Q178887 covers + this type of problem, it's typically caused by an external driver or + other program that adds its own values under the + HKEY_PERFORMANCE_DATA key. The NT kernel, via Advapi32.dll, calls the + required external module to map in the data inside an SEH try/except + block, so problems in the module's collect function don't pop up until + after it has finished, so the fault appears to occur in Advapi32.dll. + There may be problems in the NT kernel as well though, a low-level + memory checker indicated that ExpandEnvironmentStrings() in + Kernel32.dll, called an interminable number of calls down inside + RegQueryValueEx(), was overwriting memory (it wrote twice the + allocated size of a buffer to a buffer allocated by the NT kernel). + OTOH this could be coming from the external module calling back into + the kernel, which eventually causes the problem described above. + + Possibly as an extension of the problem that the krnlWaitSemaphore() + call above works around, running two instances of cryptlib (e.g. two + applications that use it) under NT4 can result in one of them hanging + in the RegQueryValueEx() call. This happens only under NT4 and is + hard to reproduce in any consistent manner. + + One workaround that helps a bit is to read the registry as a remote + (rather than local) registry, it's possible that the use of a network + RPC call isolates the calling app from the problem in that whatever + service handles the RPC is taking the hit and not affecting the + calling app. Since this would require another round of extensive + testing to verify and the NT native API call is working fine, we'll + stick with the native API call for now. + + Some versions of NT4 had a problem where the amount of data returned + was mis-reported and would never settle down, because of this the code + below includes a safety-catch that bails out after 10 attempts have + been made, this results in no data being returned but at does ensure + that the thread will terminate. + + In addition to these problems the code in RegQueryValueEx() that + estimates the amount of memory required to return the performance + counter information isn't very accurate (it's much worse than the + "slightly-inaccurate" level that the MS docs warn about, it's usually + wildly off) since it always returns a worst-case estimate which is + usually nowhere near the actual amount required. For example it may + report that 128K of memory is required, but only return 64K of data. + + Even worse than the registry-based performance counters is the + performance data helper (PDH) shim that tries to make the counters + look like the old Win16 API (which is also used by Win95). Under NT + this can consume tens of MB of memory and huge amounts of CPU time + while it gathers its data, and even running once can still consume + about 1/2MB of memory */ + if (getenv ("GCRYPT_RNDW32_NOPERF")) + { + static int shown; + + if (!shown) + { + shown = 1; + log_info ("note: get performance data has been disabled\n"); + } + } + else + { + pPerfData = xmalloc (cbPerfData); + for (iterations=0; iterations < 10; iterations++) + { + dwSize = cbPerfData; + if ( debug_me ) + log_debug ("rndw32#slow_gatherer_nt: get perf data\n" ); + + status = RegQueryValueEx (HKEY_PERFORMANCE_DATA, "Global", NULL, + NULL, (LPBYTE) pPerfData, &dwSize); + if (status == ERROR_SUCCESS) + { + if (!memcmp (pPerfData->Signature, L"PERF", 8)) + (*add) ( pPerfData, dwSize, requester ); + else + log_debug ("rndw32: no PERF signature\n"); + break; + } + else if (status == ERROR_MORE_DATA) + { + cbPerfData += PERFORMANCE_BUFFER_STEP; + pPerfData = xrealloc (pPerfData, cbPerfData); + } + else + { + static int been_here; + + /* Silence the error message. In particular under Wine (as + of 2008) we would get swamped with such diagnotiscs. One + such diagnotiscs should be enough. */ + if (been_here != status) + { + been_here = status; + log_debug ("rndw32: get performance data problem: ec=%ld\n", + status); + } + break; + } + } + xfree (pPerfData); + } + + /* Although this isn't documented in the Win32 API docs, it's necessary + to explicitly close the HKEY_PERFORMANCE_DATA key after use (it's + implicitly opened on the first call to RegQueryValueEx()). If this + isn't done then any system components which provide performance data + can't be removed or changed while the handle remains active. */ + RegCloseKey (HKEY_PERFORMANCE_DATA); +} + + +static void +slow_gatherer ( void (*add)(const void*, size_t, enum random_origins), + enum random_origins requester ) +{ + static int is_initialized = 0; + static int is_workstation = 1; + HANDLE hDevice; + DWORD dwType, dwSize, dwResult; + ULONG ulSize; + int drive_no, status; + int no_results = 0; + void *buffer; + + if ( !is_initialized ) + { + HKEY hKey; + + if ( debug_me ) + log_debug ("rndw32#slow_gatherer: init toolkit\n" ); + /* Find out whether this is an NT server or workstation if necessary */ + if (RegOpenKeyEx (HKEY_LOCAL_MACHINE, + "SYSTEM\\CurrentControlSet\\Control\\ProductOptions", + 0, KEY_READ, &hKey) == ERROR_SUCCESS) + { + BYTE szValue[32 + 8]; + dwSize = 32; + + if ( debug_me ) + log_debug ("rndw32#slow_gatherer: check product options\n" ); + + status = RegQueryValueEx (hKey, "ProductType", 0, NULL, + szValue, &dwSize); + if (status == ERROR_SUCCESS && stricmp ((char*)szValue, "WinNT")) + { + /* Note: There are (at least) three cases for ProductType: + WinNT = NT Workstation, ServerNT = NT Server, LanmanNT = + NT Server acting as a Domain Controller. */ + is_workstation = 0; + if ( debug_me ) + log_debug ("rndw32: this is a NT server\n"); + } + RegCloseKey (hKey); + } + + /* The following are fixed for the lifetime of the process so we + only add them once */ + /* readPnPData (); - we have not implemented that. */ + + /* Initialize the NetAPI32 function pointers if necessary */ + hNetAPI32 = LoadLibrary ("NETAPI32.DLL"); + if (hNetAPI32) + { + if (debug_me) + log_debug ("rndw32#slow_gatherer: netapi32 loaded\n" ); + pNetStatisticsGet = (NETSTATISTICSGET) + GetProcAddress (hNetAPI32, "NetStatisticsGet"); + pNetApiBufferSize = (NETAPIBUFFERSIZE) + GetProcAddress (hNetAPI32, "NetApiBufferSize"); + pNetApiBufferFree = (NETAPIBUFFERFREE) + GetProcAddress (hNetAPI32, "NetApiBufferFree"); + + if (!pNetStatisticsGet || !pNetApiBufferSize || !pNetApiBufferFree) + { + FreeLibrary (hNetAPI32); + hNetAPI32 = NULL; + log_debug ("rndw32: No NETAPI found\n" ); + } + } + + /* Initialize the NT kernel native API function pointers if necessary */ + hNTAPI = GetModuleHandle ("NTDll.dll"); + if (hNTAPI) + { + /* Get a pointer to the NT native information query functions */ + pNtQuerySystemInformation = (NTQUERYSYSTEMINFORMATION) + GetProcAddress (hNTAPI, "NtQuerySystemInformation"); + pNtQueryInformationProcess = (NTQUERYINFORMATIONPROCESS) + GetProcAddress (hNTAPI, "NtQueryInformationProcess"); + pNtPowerInformation = (NTPOWERINFORMATION) + GetProcAddress(hNTAPI, "NtPowerInformation"); + + if (!pNtQuerySystemInformation || !pNtQueryInformationProcess) + hNTAPI = NULL; + } + + + is_initialized = 1; + } + + read_system_rng ( add, requester ); + read_mbm_data ( add, requester ); + + /* 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. + In any case the network statistics return almost no randomness. */ + { + LPBYTE lpBuffer; + + if (hNetAPI32 + && !pNetStatisticsGet (NULL, + (LPWSTR)(is_workstation ? L"LanmanWorkstation" : + L"LanmanServer"), 0, 0, &lpBuffer)) + { + if ( debug_me ) + log_debug ("rndw32#slow_gatherer: get netstats\n" ); + pNetApiBufferSize (lpBuffer, &dwSize); + (*add) ( lpBuffer, dwSize, requester ); + pNetApiBufferFree (lpBuffer); + } + } + + /* Get disk I/O statistics for all the hard drives. 100 is an + arbitrary failsafe limit. */ + for (drive_no = 0; drive_no < 100 ; drive_no++) + { + char diskPerformance[SIZEOF_DISK_PERFORMANCE_STRUCT + 8]; + char szDevice[50]; + + /* Check whether we can access this device. */ + snprintf (szDevice, sizeof szDevice, "\\\\.\\PhysicalDrive%d", + drive_no); + hDevice = CreateFile (szDevice, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, OPEN_EXISTING, 0, NULL); + if (hDevice == INVALID_HANDLE_VALUE) + break; /* No more drives. */ + + /* Note: This only works if you have turned on the disk performance + counters with 'diskperf -y'. These counters are off by default. */ + dwSize = sizeof diskPerformance; + if (DeviceIoControl (hDevice, IOCTL_DISK_PERFORMANCE, NULL, 0, + diskPerformance, SIZEOF_DISK_PERFORMANCE_STRUCT, + &dwSize, NULL)) + { + if ( debug_me ) + log_debug ("rndw32#slow_gatherer: iostat drive %d\n", + drive_no); + (*add) (diskPerformance, dwSize, requester); + } + else + { + log_info ("NOTE: you should run 'diskperf -y' " + "to enable the disk statistics\n"); + } + CloseHandle (hDevice); + } + + /* In theory we should be using the Win32 performance query API to obtain + unpredictable data from the system, however this is so unreliable (see + the multiple sets of comments in registryPoll()) that it's too risky + to rely on it except as a fallback in emergencies. Instead, we rely + mostly on the NT native API function NtQuerySystemInformation(), which + has the dual advantages that it doesn't have as many (known) problems + as the Win32 equivalent and that it doesn't access the data indirectly + via pseudo-registry keys, which means that it's much faster. Note + that the Win32 equivalent actually works almost all of the time, the + problem is that on one or two systems it can fail in strange ways that + are never the same and can't be reproduced on any other system, which + is why we use the native API here. Microsoft officially documented + this function in early 2003, so it'll be fairly safe to use. */ + if ( !hNTAPI ) + { + registry_poll (add, requester); + return; + } + + + /* Scan the first 64 possible information types (we don't bother with + increasing the buffer size as we do with the Win32 version of the + performance data read, we may miss a few classes but it's no big deal). + This scan typically yields around 20 pieces of data, there's nothing + in the range 65...128 so chances are there won't be anything above + there either. */ + buffer = xmalloc (PERFORMANCE_BUFFER_SIZE); + for (dwType = 0; dwType < 64; dwType++) + { + switch (dwType) + { + /* ID 17 = SystemObjectInformation hangs on some win2k systems. */ + case 17: + if (system_is_w2000) + continue; + break; + + /* Some information types are write-only (the IDs are shared with + a set-information call), we skip these. */ + case 26: case 27: case 38: case 46: case 47: case 48: case 52: + continue; + + /* ID 53 = SystemSessionProcessInformation reads input from the + output buffer, which has to contain a session ID and pointer + to the actual buffer in which to store the session information. + Because this isn't a standard query, we skip this. */ + case 53: + continue; + } + + /* Query the info for this ID. Some results (for example for + ID = 6, SystemCallCounts) are only available in checked builds + of the kernel. A smaller subcless of results require that + certain system config flags be set, for example + SystemObjectInformation requires that the + FLG_MAINTAIN_OBJECT_TYPELIST be set in NtGlobalFlags. To avoid + having to special-case all of these, we try reading each one and + only use those for which we get a success status. */ + dwResult = pNtQuerySystemInformation (dwType, buffer, + PERFORMANCE_BUFFER_SIZE - 2048, + &ulSize); + if (dwResult != ERROR_SUCCESS) + continue; + + /* Some calls (e.g. ID = 23, SystemProcessorStatistics, and ID = 24, + SystemDpcInformation) incorrectly return a length of zero, so we + manually adjust the length to the correct value. */ + if ( !ulSize ) + { + if (dwType == 23) + ulSize = 6 * sizeof (ULONG); + else if (dwType == 24) + ulSize = 5 * sizeof (ULONG); + } + + /* If we got some data back, add it to the entropy pool. */ + if (ulSize > 0 && ulSize <= PERFORMANCE_BUFFER_SIZE - 2048) + { + if (debug_me) + log_debug ("rndw32#slow_gatherer: %lu bytes from sysinfo %ld\n", + ulSize, dwType); + (*add) (buffer, ulSize, requester); + no_results++; + } + } + + /* Now we would do the same for the process information. This + call would rather ugly in that it requires an exact length + match for the data returned, failing with a + STATUS_INFO_LENGTH_MISMATCH error code (0xC0000004) if the + length isn't an exact match. It requires a compiler to handle + complex nested structs, alignment issues, and so on, and + without the headers in which the entries are declared it's + almost impossible to do. Thus we don't. */ + + + /* Finally, do the same for the system power status information. There + are only a limited number of useful information types available so we + restrict ourselves to the useful types. In addition since this + function doesn't return length information, we have to hardcode in + length data. */ + if (pNtPowerInformation) + { + static const struct { int type; int size; } powerInfo[] = { + { 0, 128 }, /* SystemPowerPolicyAc */ + { 1, 128 }, /* SystemPowerPolicyDc */ + { 4, 64 }, /* SystemPowerCapabilities */ + { 5, 48 }, /* SystemBatteryState */ + { 11, 48 }, /* ProcessorInformation */ + { 12, 24 }, /* SystemPowerInformation */ + { -1, -1 } + }; + int i; + + /* The 100 is a failsafe limit. */ + for (i = 0; powerInfo[i].type != -1 && i < 100; i++ ) + { + /* Query the info for this ID */ + dwResult = pNtPowerInformation (powerInfo[i].type, NULL, 0, buffer, + PERFORMANCE_BUFFER_SIZE - 2048); + if (dwResult != ERROR_SUCCESS) + continue; + if (debug_me) + log_debug ("rndw32#slow_gatherer: %u bytes from powerinfo %d\n", + powerInfo[i].size, i); + (*add) (buffer, powerInfo[i].size, requester); + no_results++; + } + gcry_assert (i < 100); + } + xfree (buffer); + + /* We couldn't get enough results from the kernel, fall back to the + somewhat troublesome registry poll. */ + if (no_results < 15) + registry_poll (add, requester); +} + + +int +_gcry_rndw32_gather_random (void (*add)(const void*, size_t, + enum random_origins), + enum random_origins origin, + size_t length, int level ) +{ + static int is_initialized; + size_t n; + + if (!level) + return 0; + + /* We don't differentiate between level 1 and 2 here because there + is no internal entropy pool as a scary resource. It may all work + slower, but because our entropy source will never block but + deliver some not easy to measure entropy, we assume level 2. */ + + if (!is_initialized) + { + OSVERSIONINFO osvi = { sizeof( osvi ) }; + const char *s; + + if ((s = getenv ("GCRYPT_RNDW32_DBG")) && atoi (s) > 0) + debug_me = 1; + + GetVersionEx( &osvi ); + if (osvi.dwPlatformId != VER_PLATFORM_WIN32_NT) + log_fatal ("can only run on a Windows NT platform\n" ); + system_is_w2000 = (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0); + + init_system_rng (); + is_initialized = 1; + } + + if (debug_me) + log_debug ("rndw32#gather_random: ori=%d len=%u lvl=%d\n", + origin, (unsigned int)length, level ); + + slow_gatherer (add, origin); + + /* Round requested LENGTH up to full 32 bytes. */ + n = _gcry_rndjent_poll (add, origin, ((length + 31) / 32) * 32); + + if (debug_me) + log_debug ("rndw32#gather_random: jent contributed extra %u bytes\n", + (unsigned int)n); + + return 0; +} + + + +void +_gcry_rndw32_gather_random_fast (void (*add)(const void*, size_t, + enum random_origins), + enum random_origins origin) +{ + static int addedFixedItems = 0; + + if ( debug_me ) + log_debug ("rndw32#gather_random_fast: ori=%d\n", origin ); + + /* Get various basic pieces of system information: Handle of active + window, handle of window with mouse capture, handle of clipboard + owner handle of start of clpboard viewer list, pseudohandle of + current process, current process ID, pseudohandle of current + thread, current thread ID, handle of desktop window, handle of + window with keyboard focus, whether system queue has any events, + cursor position for last message, 1 ms time for last message, + handle of window with clipboard open, handle of process heap, + handle of procs window station, types of events in input queue, + and milliseconds since Windows was started. On 64-bit platform + some of these return values are pointers and thus 64-bit wide. + We discard the upper 32-bit of those values. */ + + { + byte buffer[20*sizeof(ulong)], *bufptr; + + bufptr = buffer; +#define ADDINT(f) do { ulong along = (ulong)(f); \ + memcpy (bufptr, &along, sizeof (along) ); \ + bufptr += sizeof (along); \ + } while (0) +#define ADDPTR(f) do { void *aptr = (f); \ + ADDINT((SIZE_T)aptr); \ + } while (0) + + ADDPTR ( GetActiveWindow ()); + ADDPTR ( GetCapture ()); + ADDPTR ( GetClipboardOwner ()); + ADDPTR ( GetClipboardViewer ()); + ADDPTR ( GetCurrentProcess ()); + ADDINT ( GetCurrentProcessId ()); + ADDPTR ( GetCurrentThread ()); + ADDINT ( GetCurrentThreadId ()); + ADDPTR ( GetDesktopWindow ()); + ADDPTR ( GetFocus ()); + ADDINT ( GetInputState ()); + ADDINT ( GetMessagePos ()); + ADDINT ( GetMessageTime ()); + ADDPTR ( GetOpenClipboardWindow ()); + ADDPTR ( GetProcessHeap ()); + ADDPTR ( GetProcessWindowStation ()); + /* Following function in some cases stops returning events, and cannot + be used as an entropy source. */ + /*ADDINT ( GetQueueStatus (QS_ALLEVENTS));*/ + ADDINT ( GetTickCount ()); + + gcry_assert ( bufptr-buffer < sizeof (buffer) ); + (*add) ( buffer, bufptr-buffer, origin ); +#undef ADDINT +#undef ADDPTR + } + + /* Get multiword system information: Current caret position, current + mouse cursor position. */ + { + POINT point; + + GetCaretPos (&point); + (*add) ( &point, sizeof (point), origin ); + GetCursorPos (&point); + (*add) ( &point, sizeof (point), origin ); + } + + /* 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 memoryStatus; + + memoryStatus.dwLength = sizeof (MEMORYSTATUS); + GlobalMemoryStatus (&memoryStatus); + (*add) ( &memoryStatus, sizeof (memoryStatus), origin ); + } + + /* Get thread and process creation time, exit time, time in kernel + mode, and time in user mode in 100ns intervals. */ + { + HANDLE handle; + FILETIME creationTime, exitTime, kernelTime, userTime; + SIZE_T minimumWorkingSetSize, maximumWorkingSetSize; + + handle = GetCurrentThread (); + GetThreadTimes (handle, &creationTime, &exitTime, + &kernelTime, &userTime); + (*add) ( &creationTime, sizeof (creationTime), origin ); + (*add) ( &exitTime, sizeof (exitTime), origin ); + (*add) ( &kernelTime, sizeof (kernelTime), origin ); + (*add) ( &userTime, sizeof (userTime), origin ); + + handle = GetCurrentProcess (); + GetProcessTimes (handle, &creationTime, &exitTime, + &kernelTime, &userTime); + (*add) ( &creationTime, sizeof (creationTime), origin ); + (*add) ( &exitTime, sizeof (exitTime), origin ); + (*add) ( &kernelTime, sizeof (kernelTime), origin ); + (*add) ( &userTime, sizeof (userTime), origin ); + + /* Get the minimum and maximum working set size for the current + process. */ + GetProcessWorkingSetSize (handle, &minimumWorkingSetSize, + &maximumWorkingSetSize); + /* On 64-bit system, discard the high 32-bits. */ + (*add) ( &minimumWorkingSetSize, sizeof (int), origin ); + (*add) ( &maximumWorkingSetSize, sizeof (int), origin ); + } + + + /* The following are fixed for the lifetime of the process so we only + * add them once */ + if (!addedFixedItems) + { + 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); + (*add) ( &startupInfo, sizeof (STARTUPINFO), origin ); + addedFixedItems = 1; + } + + /* The performance of QPC varies depending on the architecture it's + running on and on the OS, the MS documentation is vague about the + details because it varies so much. Under Win9x/ME it reads the + 1.193180 MHz PIC timer. Under NT/Win2K/XP it may or may not read the + 64-bit TSC depending on the HAL and assorted other circumstances, + generally on machines with a uniprocessor HAL + KeQueryPerformanceCounter() uses a 3.579545MHz timer and on machines + with a multiprocessor or APIC HAL it uses the TSC (the exact time + source is controlled by the HalpUse8254 flag in the kernel). That + choice of time sources is somewhat peculiar because on a + multiprocessor machine it's theoretically possible to get completely + different TSC readings depending on which CPU you're currently + running on, while for uniprocessor machines it's not a problem. + However, the kernel appears to synchronise the TSCs across CPUs at + boot time (it resets the TSC as part of its system init), so this + shouldn't really be a problem. Under WinCE it's completely platform- + dependent, if there's no hardware performance counter available, it + uses the 1ms system timer. + + Another feature of the TSC (although it doesn't really affect us here) + is that mobile CPUs will turn off the TSC when they idle, Pentiums + will change the rate of the counter when they clock-throttle (to + match the current CPU speed), and hyperthreading Pentiums will turn + it off when both threads are idle (this more or less makes sense, + since the CPU will be in the halted state and not executing any + instructions to count). + + To make things unambiguous, we detect a CPU new enough to call RDTSC + directly by checking for CPUID capabilities, and fall back to QPC if + this isn't present. + + On AMD64, TSC is always available and intrinsic is provided for accessing + it. */ +#ifdef __x86_64__ + { + unsigned __int64 aint64; + + /* Note: cryptlib does not discard upper 32 bits of TSC on WIN64, but does + * on WIN32. Is this correct? */ + aint64 = __rdtsc(); + (*add) (&aint64, sizeof(aint64), origin); + } +#else +#ifdef __GNUC__ +/* FIXME: We would need to implement the CPU feature tests first. */ +/* if (cpu_has_feature_rdtsc) */ +/* { */ +/* uint32_t lo, hi; */ + /* We cannot use "=A", since this would use %rax on x86_64. */ +/* __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi)); */ + /* Ignore high 32 bits, hwich are >1s res. */ +/* (*add) (&lo, 4, origin ); */ +/* } */ +/* else */ +#endif /*!__GNUC__*/ + { + LARGE_INTEGER performanceCount; + + if (QueryPerformanceCounter (&performanceCount)) + { + if ( debug_me ) + log_debug ("rndw32#gather_random_fast: perf data\n"); + (*add) (&performanceCount, sizeof (performanceCount), origin); + } + else + { + /* Millisecond accuracy at best... */ + DWORD aword = GetTickCount (); + (*add) (&aword, sizeof (aword), origin ); + } + } +#endif /*__x86_64__*/ + + +} |