gecko/security/nss/lib/freebl/win_rand.c
2008-06-06 08:40:11 -04:00

649 lines
17 KiB
C

/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the Netscape security libraries.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1994-2000
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "secrng.h"
#include "secerr.h"
#ifdef XP_WIN
#include <windows.h>
#if defined(_WIN32_WCE)
#include <stdlib.h> /* Win CE puts lots of stuff here. */
#include "prprf.h" /* for PR_snprintf */
#else
#include <time.h>
#include <io.h>
#include <sys/types.h>
#include <sys/stat.h>
#endif
#include <stdio.h>
#ifndef _WIN32
#define VTD_Device_ID 5
#define OP_OVERRIDE _asm _emit 0x66
#include <dos.h>
#endif
#include "prio.h"
#include "prerror.h"
static PRInt32 filesToRead;
static DWORD totalFileBytes;
static DWORD maxFileBytes = 250000; /* 250 thousand */
static DWORD dwNumFiles, dwReadEvery;
static BOOL
CurrentClockTickTime(LPDWORD lpdwHigh, LPDWORD lpdwLow)
{
#ifdef _WIN32
LARGE_INTEGER liCount;
if (!QueryPerformanceCounter(&liCount))
return FALSE;
*lpdwHigh = liCount.u.HighPart;
*lpdwLow = liCount.u.LowPart;
return TRUE;
#else /* is WIN16 */
BOOL bRetVal;
FARPROC lpAPI;
WORD w1, w2, w3, w4;
// Get direct access to the VTD and query the current clock tick time
_asm {
xor di, di
mov es, di
mov ax, 1684h
mov bx, VTD_Device_ID
int 2fh
mov ax, es
or ax, di
jz EnumerateFailed
; VTD API is available. First store the API address (the address actually
; contains an instruction that causes a fault, the fault handler then
; makes the ring transition and calls the API in the VxD)
mov word ptr lpAPI, di
mov word ptr lpAPI+2, es
mov ax, 100h ; API function to VTD_Get_Real_Time
; call dword ptr [lpAPI]
call [lpAPI]
; Result is in EDX:EAX which we will get 16-bits at a time
mov w2, dx
OP_OVERRIDE
shr dx,10h ; really "shr edx, 16"
mov w1, dx
mov w4, ax
OP_OVERRIDE
shr ax,10h ; really "shr eax, 16"
mov w3, ax
mov bRetVal, 1 ; return TRUE
jmp EnumerateExit
EnumerateFailed:
mov bRetVal, 0 ; return FALSE
EnumerateExit:
}
*lpdwHigh = MAKELONG(w2, w1);
*lpdwLow = MAKELONG(w4, w3);
return bRetVal;
#endif /* is WIN16 */
}
size_t RNG_GetNoise(void *buf, size_t maxbuf)
{
DWORD dwHigh, dwLow, dwVal;
int n = 0;
int nBytes;
if (maxbuf <= 0)
return 0;
CurrentClockTickTime(&dwHigh, &dwLow);
// get the maximally changing bits first
nBytes = sizeof(dwLow) > maxbuf ? maxbuf : sizeof(dwLow);
memcpy((char *)buf, &dwLow, nBytes);
n += nBytes;
maxbuf -= nBytes;
if (maxbuf <= 0)
return n;
nBytes = sizeof(dwHigh) > maxbuf ? maxbuf : sizeof(dwHigh);
memcpy(((char *)buf) + n, &dwHigh, nBytes);
n += nBytes;
maxbuf -= nBytes;
if (maxbuf <= 0)
return n;
// get the number of milliseconds that have elapsed since Windows started
dwVal = GetTickCount();
nBytes = sizeof(dwVal) > maxbuf ? maxbuf : sizeof(dwVal);
memcpy(((char *)buf) + n, &dwVal, nBytes);
n += nBytes;
maxbuf -= nBytes;
if (maxbuf <= 0)
return n;
#if defined(_WIN32_WCE)
{
// get the number of milliseconds elapsed since Windows CE was started.
DWORD tickCount = GetTickCount();
nBytes = (sizeof tickCount) > maxbuf ? maxbuf : (sizeof tickCount);
memcpy(((char *)buf) + n, &tickCount, nBytes);
n += nBytes;
}
#else
{
time_t sTime;
// get the time in seconds since midnight Jan 1, 1970
time(&sTime);
nBytes = sizeof(sTime) > maxbuf ? maxbuf : sizeof(sTime);
memcpy(((char *)buf) + n, &sTime, nBytes);
n += nBytes;
}
#endif
return n;
}
#if defined(_WIN32_WCE)
static BOOL
EnumSystemFilesWithNSPR(const char * dirName,
BOOL recursive,
PRInt32 (*func)(const char *))
{
PRDir * pDir;
PRDirEntry * pEntry;
BOOL rv = FALSE;
pDir = PR_OpenDir(dirName);
if (!pDir)
return rv;
while ((pEntry = PR_ReadDir(pDir, PR_SKIP_BOTH|PR_SKIP_HIDDEN)) != NULL) {
PRStatus status;
PRInt32 count;
PRInt32 stop;
PRFileInfo fileInfo;
char szFileName[_MAX_PATH];
count = (PRInt32)PR_snprintf(szFileName, sizeof szFileName, "%s\\%s",
dirName, PR_DirName(pEntry));
if (count < 1)
continue;
status = PR_GetFileInfo(szFileName, &fileInfo);
if (status != PR_SUCCESS)
continue;
if (fileInfo.type == PR_FILE_FILE) {
stop = (*func)(szFileName);
rv = TRUE;
if (stop)
break;
continue;
}
if (recursive && fileInfo.type == PR_FILE_DIRECTORY) {
rv |= EnumSystemFilesWithNSPR(szFileName, recursive, func);
}
}
PR_CloseDir(pDir);
return rv;
}
#endif
static BOOL
EnumSystemFiles(PRInt32 (*func)(const char *))
{
#if defined(_WIN32_WCE)
BOOL rv = FALSE;
rv |= EnumSystemFilesWithNSPR("\\Windows\\Temporary Internet Files", TRUE, func);
rv |= EnumSystemFilesWithNSPR("\\Temp", FALSE, func);
rv |= EnumSystemFilesWithNSPR("\\Windows", FALSE, func);
return rv;
#else
int iStatus;
char szSysDir[_MAX_PATH];
char szFileName[_MAX_PATH];
#ifdef _WIN32
WIN32_FIND_DATA fdData;
HANDLE lFindHandle;
#else
struct _find_t fdData;
#endif
if (!GetSystemDirectory(szSysDir, sizeof(szSysDir)))
return FALSE;
// tack *.* on the end so we actually look for files. this will
// not overflow
strcpy(szFileName, szSysDir);
strcat(szFileName, "\\*.*");
#ifdef _WIN32
lFindHandle = FindFirstFile(szFileName, &fdData);
if (lFindHandle == INVALID_HANDLE_VALUE)
return FALSE;
do {
// pass the full pathname to the callback
sprintf(szFileName, "%s\\%s", szSysDir, fdData.cFileName);
(*func)(szFileName);
iStatus = FindNextFile(lFindHandle, &fdData);
} while (iStatus != 0);
FindClose(lFindHandle);
#else
if (_dos_findfirst(szFileName,
_A_NORMAL | _A_RDONLY | _A_ARCH | _A_SUBDIR, &fdData) != 0)
return FALSE;
do {
// pass the full pathname to the callback
sprintf(szFileName, "%s\\%s", szSysDir, fdData.name);
(*func)(szFileName);
iStatus = _dos_findnext(&fdData);
} while (iStatus == 0);
_dos_findclose(&fdData);
#endif
return TRUE;
#endif
}
static PRInt32
CountFiles(const char *file)
{
dwNumFiles++;
return 0;
}
static PRInt32
ReadFiles(const char *file)
{
if ((dwNumFiles % dwReadEvery) == 0) {
++filesToRead;
}
if (filesToRead) {
DWORD prevFileBytes = totalFileBytes;
RNG_FileForRNG(file);
if (prevFileBytes < totalFileBytes) {
--filesToRead;
}
}
dwNumFiles++;
return (totalFileBytes >= maxFileBytes);
}
static void
ReadSystemFiles()
{
// first count the number of files
dwNumFiles = 0;
if (!EnumSystemFiles(CountFiles))
return;
RNG_RandomUpdate(&dwNumFiles, sizeof(dwNumFiles));
// now read the first 10 readable files, then 10 or 11 files
// spread throughout the system directory
filesToRead = 10;
if (dwNumFiles == 0)
return;
dwReadEvery = dwNumFiles / 10;
if (dwReadEvery == 0)
dwReadEvery = 1; // less than 10 files
dwNumFiles = 0;
EnumSystemFiles(ReadFiles);
}
void RNG_SystemInfoForRNG(void)
{
DWORD dwVal;
char buffer[256];
int nBytes;
#ifdef _WIN32
MEMORYSTATUS sMem;
HANDLE hVal;
#if !defined(_WIN32_WCE)
DWORD dwSerialNum;
DWORD dwComponentLen;
DWORD dwSysFlags;
char volName[128];
DWORD dwSectors, dwBytes, dwFreeClusters, dwNumClusters;
#endif
#else
int iVal;
HTASK hTask;
WORD wDS, wCS;
LPSTR lpszEnv;
#endif
nBytes = RNG_GetNoise(buffer, 20); // get up to 20 bytes
RNG_RandomUpdate(buffer, nBytes);
#ifdef _WIN32
sMem.dwLength = sizeof(sMem);
GlobalMemoryStatus(&sMem); // assorted memory stats
RNG_RandomUpdate(&sMem, sizeof(sMem));
#if !defined(_WIN32_WCE)
dwVal = GetLogicalDrives();
RNG_RandomUpdate(&dwVal, sizeof(dwVal)); // bitfields in bits 0-25
#endif
#else
dwVal = GetFreeSpace(0);
RNG_RandomUpdate(&dwVal, sizeof(dwVal));
_asm mov wDS, ds;
_asm mov wCS, cs;
RNG_RandomUpdate(&wDS, sizeof(wDS));
RNG_RandomUpdate(&wCS, sizeof(wCS));
#endif
#ifdef _WIN32
#if !defined(_WIN32_WCE)
dwVal = sizeof(buffer);
if (GetComputerName(buffer, &dwVal))
RNG_RandomUpdate(buffer, dwVal);
#endif
/* XXX This is code that got yanked because of NSPR20. We should put it
* back someday.
*/
#ifdef notdef
{
POINT ptVal;
GetCursorPos(&ptVal);
RNG_RandomUpdate(&ptVal, sizeof(ptVal));
}
dwVal = GetQueueStatus(QS_ALLINPUT); // high and low significant
RNG_RandomUpdate(&dwVal, sizeof(dwVal));
{
HWND hWnd;
hWnd = GetClipboardOwner(); // 2 or 4 bytes
RNG_RandomUpdate((void *)&hWnd, sizeof(hWnd));
}
{
UUID sUuid;
UuidCreate(&sUuid); // this will fail on machines with no ethernet
RNG_RandomUpdate(&sUuid, sizeof(sUuid)); // boards. shove the bits in regardless
}
#endif
hVal = GetCurrentProcess(); // 4 or 8 byte pseudo handle (a
// constant!) of current process
RNG_RandomUpdate(&hVal, sizeof(hVal));
dwVal = GetCurrentProcessId(); // process ID (4 bytes)
RNG_RandomUpdate(&dwVal, sizeof(dwVal));
dwVal = GetCurrentThreadId(); // thread ID (4 bytes)
RNG_RandomUpdate(&dwVal, sizeof(dwVal));
#if !defined(_WIN32_WCE)
volName[0] = '\0';
buffer[0] = '\0';
GetVolumeInformation(NULL,
volName,
sizeof(volName),
&dwSerialNum,
&dwComponentLen,
&dwSysFlags,
buffer,
sizeof(buffer));
RNG_RandomUpdate(volName, strlen(volName));
RNG_RandomUpdate(&dwSerialNum, sizeof(dwSerialNum));
RNG_RandomUpdate(&dwComponentLen, sizeof(dwComponentLen));
RNG_RandomUpdate(&dwSysFlags, sizeof(dwSysFlags));
RNG_RandomUpdate(buffer, strlen(buffer));
if (GetDiskFreeSpace(NULL, &dwSectors, &dwBytes, &dwFreeClusters, &dwNumClusters)) {
RNG_RandomUpdate(&dwSectors, sizeof(dwSectors));
RNG_RandomUpdate(&dwBytes, sizeof(dwBytes));
RNG_RandomUpdate(&dwFreeClusters, sizeof(dwFreeClusters));
RNG_RandomUpdate(&dwNumClusters, sizeof(dwNumClusters));
}
#endif
#else /* is WIN16 */
hTask = GetCurrentTask();
RNG_RandomUpdate((void *)&hTask, sizeof(hTask));
iVal = GetNumTasks();
RNG_RandomUpdate(&iVal, sizeof(iVal)); // number of running tasks
lpszEnv = GetDOSEnvironment();
while (*lpszEnv != '\0') {
RNG_RandomUpdate(lpszEnv, strlen(lpszEnv));
lpszEnv += strlen(lpszEnv) + 1;
}
#endif /* is WIN16 */
// now let's do some files
ReadSystemFiles();
nBytes = RNG_GetNoise(buffer, 20); // get up to 20 bytes
RNG_RandomUpdate(buffer, nBytes);
}
#if defined(_WIN32_WCE)
void RNG_FileForRNG(const char *filename)
{
PRFileDesc * file;
int nBytes;
PRFileInfo infoBuf;
unsigned char buffer[1024];
/* windows doesn't initialize all the bytes in the stat buf,
* so initialize them all here to avoid UMRs.
*/
memset(&infoBuf, 0, sizeof infoBuf);
if (PR_GetFileInfo(filename, &infoBuf) < 0)
return;
RNG_RandomUpdate((unsigned char*)&infoBuf, sizeof(infoBuf));
file = PR_Open(filename, PR_RDONLY, 0);
if (file != NULL) {
for (;;) {
PRInt32 bytes = PR_Read(file, buffer, sizeof buffer);
if (bytes <= 0)
break;
RNG_RandomUpdate(buffer, bytes);
totalFileBytes += bytes;
if (totalFileBytes > maxFileBytes)
break;
}
PR_Close(file);
}
nBytes = RNG_GetNoise(buffer, 20); // get up to 20 bytes
RNG_RandomUpdate(buffer, nBytes);
}
#else /* not WinCE */
void RNG_FileForRNG(const char *filename)
{
FILE* file;
int nBytes;
struct stat stat_buf;
unsigned char buffer[1024];
/* static DWORD totalFileBytes = 0; */
/* windows doesn't initialize all the bytes in the stat buf,
* so initialize them all here to avoid UMRs.
*/
memset(&stat_buf, 0, sizeof stat_buf);
if (stat((char *)filename, &stat_buf) < 0)
return;
RNG_RandomUpdate((unsigned char*)&stat_buf, sizeof(stat_buf));
file = fopen((char *)filename, "r");
if (file != NULL) {
for (;;) {
size_t bytes = fread(buffer, 1, sizeof(buffer), file);
if (bytes == 0)
break;
RNG_RandomUpdate(buffer, bytes);
totalFileBytes += bytes;
if (totalFileBytes > maxFileBytes)
break;
}
fclose(file);
}
nBytes = RNG_GetNoise(buffer, 20); // get up to 20 bytes
RNG_RandomUpdate(buffer, nBytes);
}
#endif /* not WinCE */
/*
* CryptoAPI requires Windows NT 4.0 or Windows 95 OSR2 and later.
* Until we drop support for Windows 95, we need to emulate some
* definitions and declarations in <wincrypt.h> and look up the
* functions in advapi32.dll at run time.
*/
#ifndef WIN64
typedef unsigned long HCRYPTPROV;
#endif
#define CRYPT_VERIFYCONTEXT 0xF0000000
#define PROV_RSA_FULL 1
typedef BOOL
(WINAPI *CryptAcquireContextAFn)(
HCRYPTPROV *phProv,
LPCSTR pszContainer,
LPCSTR pszProvider,
DWORD dwProvType,
DWORD dwFlags);
typedef BOOL
(WINAPI *CryptReleaseContextFn)(
HCRYPTPROV hProv,
DWORD dwFlags);
typedef BOOL
(WINAPI *CryptGenRandomFn)(
HCRYPTPROV hProv,
DWORD dwLen,
BYTE *pbBuffer);
/*
* Windows XP and Windows Server 2003 and later have RtlGenRandom,
* which must be looked up by the name SystemFunction036.
*/
typedef BOOLEAN
(APIENTRY *RtlGenRandomFn)(
PVOID RandomBuffer,
ULONG RandomBufferLength);
size_t RNG_SystemRNG(void *dest, size_t maxLen)
{
HMODULE hModule;
RtlGenRandomFn pRtlGenRandom;
CryptAcquireContextAFn pCryptAcquireContextA;
CryptReleaseContextFn pCryptReleaseContext;
CryptGenRandomFn pCryptGenRandom;
HCRYPTPROV hCryptProv;
size_t bytes = 0;
hModule = LoadLibrary("advapi32.dll");
if (hModule == NULL) {
PORT_SetError(PR_NOT_IMPLEMENTED_ERROR);
return 0;
}
pRtlGenRandom = (RtlGenRandomFn)
GetProcAddress(hModule, "SystemFunction036");
if (pRtlGenRandom) {
if (pRtlGenRandom(dest, maxLen)) {
bytes = maxLen;
} else {
PORT_SetError(SEC_ERROR_NEED_RANDOM); /* system RNG failed */
}
goto done;
}
pCryptAcquireContextA = (CryptAcquireContextAFn)
GetProcAddress(hModule, "CryptAcquireContextA");
pCryptReleaseContext = (CryptReleaseContextFn)
GetProcAddress(hModule, "CryptReleaseContext");
pCryptGenRandom = (CryptGenRandomFn)
GetProcAddress(hModule, "CryptGenRandom");
if (!pCryptAcquireContextA || !pCryptReleaseContext || !pCryptGenRandom) {
PORT_SetError(PR_NOT_IMPLEMENTED_ERROR);
goto done;
}
if (pCryptAcquireContextA(&hCryptProv, NULL, NULL,
PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) {
if (pCryptGenRandom(hCryptProv, maxLen, dest)) {
bytes = maxLen;
}
pCryptReleaseContext(hCryptProv, 0);
}
if (bytes == 0) {
PORT_SetError(SEC_ERROR_NEED_RANDOM); /* system RNG failed */
}
done:
FreeLibrary(hModule);
return bytes;
}
#endif /* is XP_WIN */