diff options
Diffstat (limited to 'security/nss/lib/freebl/win_rand.c')
-rw-r--r-- | security/nss/lib/freebl/win_rand.c | 648 |
1 files changed, 648 insertions, 0 deletions
diff --git a/security/nss/lib/freebl/win_rand.c b/security/nss/lib/freebl/win_rand.c new file mode 100644 index 000000000..4a3f61b59 --- /dev/null +++ b/security/nss/lib/freebl/win_rand.c @@ -0,0 +1,648 @@ +/* ***** 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 */ |