You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
300 lines
7.9 KiB
300 lines
7.9 KiB
// osrng.cpp - originally written and placed in the public domain by Wei Dai |
|
|
|
// Thanks to Leonard Janke for the suggestion for AutoSeededRandomPool. |
|
|
|
#include "pch.h" |
|
#include "config.h" |
|
|
|
#ifndef CRYPTOPP_IMPORTS |
|
|
|
// Win32 has CryptoAPI and <wincrypt.h>. Windows 10 and Windows Store 10 have CNG and <bcrypt.h>. |
|
// There's a hole for Windows Phone 8 and Windows Store 8. There is no userland RNG available. |
|
// Also see http://www.drdobbs.com/windows/using-c-and-com-with-winrt/240168150 and |
|
// http://stackoverflow.com/questions/36974545/random-numbers-for-windows-phone-8-and-windows-store-8 and |
|
// https://social.msdn.microsoft.com/Forums/vstudio/en-US/25b83e13-c85f-4aa1-a057-88a279ea3fd6/what-crypto-random-generator-c-code-could-use-on-wp81 |
|
#if defined(CRYPTOPP_WIN32_AVAILABLE) && !defined(OS_RNG_AVAILABLE) |
|
# pragma message("WARNING: Compiling for Windows but an OS RNG is not available. This is likely a Windows Phone 8 or Windows Store 8 app.") |
|
#endif |
|
|
|
#if !defined(NO_OS_DEPENDENCE) && defined(OS_RNG_AVAILABLE) |
|
|
|
#include "osrng.h" |
|
#include "rng.h" |
|
|
|
#ifdef CRYPTOPP_WIN32_AVAILABLE |
|
#define WIN32_LEAN_AND_MEAN |
|
#include <windows.h> |
|
#ifndef ERROR_INCORRECT_SIZE |
|
# define ERROR_INCORRECT_SIZE 0x000005B6 |
|
#endif |
|
#if defined(USE_MS_CRYPTOAPI) |
|
#include <wincrypt.h> |
|
#ifndef CRYPT_NEWKEYSET |
|
# define CRYPT_NEWKEYSET 0x00000008 |
|
#endif |
|
#ifndef CRYPT_MACHINE_KEYSET |
|
# define CRYPT_MACHINE_KEYSET 0x00000020 |
|
#endif |
|
#elif defined(USE_MS_CNGAPI) |
|
#include <bcrypt.h> |
|
#ifndef BCRYPT_SUCCESS |
|
# define BCRYPT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0) |
|
#endif |
|
#ifndef STATUS_INVALID_PARAMETER |
|
# define STATUS_INVALID_PARAMETER 0xC000000D |
|
#endif |
|
#ifndef STATUS_INVALID_HANDLE |
|
# define STATUS_INVALID_HANDLE 0xC0000008 |
|
#endif |
|
#endif |
|
#endif // Win32 |
|
|
|
#ifdef CRYPTOPP_UNIX_AVAILABLE |
|
#include <errno.h> |
|
#include <fcntl.h> |
|
#include <unistd.h> |
|
#endif |
|
|
|
NAMESPACE_BEGIN(CryptoPP) |
|
|
|
#if defined(NONBLOCKING_RNG_AVAILABLE) || defined(BLOCKING_RNG_AVAILABLE) |
|
OS_RNG_Err::OS_RNG_Err(const std::string &operation) |
|
: Exception(OTHER_ERROR, "OS_Rng: " + operation + " operation failed with error " + |
|
#ifdef CRYPTOPP_WIN32_AVAILABLE |
|
"0x" + IntToString(GetLastError(), 16) |
|
#else |
|
IntToString(errno) |
|
#endif |
|
) |
|
{ |
|
} |
|
#endif |
|
|
|
#ifdef NONBLOCKING_RNG_AVAILABLE |
|
|
|
#ifdef CRYPTOPP_WIN32_AVAILABLE |
|
|
|
#if defined(USE_MS_CNGAPI) |
|
inline DWORD NtStatusToErrorCode(NTSTATUS status) |
|
{ |
|
if (status == STATUS_INVALID_PARAMETER) |
|
return ERROR_INVALID_PARAMETER; |
|
else if (status == STATUS_INVALID_HANDLE) |
|
return ERROR_INVALID_HANDLE; |
|
else |
|
return (DWORD)status; |
|
} |
|
#endif |
|
|
|
#if defined(UNICODE) || defined(_UNICODE) |
|
# define CRYPTOPP_CONTAINER L"Crypto++ RNG" |
|
#else |
|
# define CRYPTOPP_CONTAINER "Crypto++ RNG" |
|
#endif |
|
|
|
MicrosoftCryptoProvider::MicrosoftCryptoProvider() : m_hProvider(0) |
|
{ |
|
#if defined(USE_MS_CRYPTOAPI) |
|
// See http://support.microsoft.com/en-us/kb/238187 for CRYPT_NEWKEYSET fallback strategy |
|
if (!CryptAcquireContext(&m_hProvider, 0, 0, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) |
|
{ |
|
const DWORD firstErr = GetLastError(); |
|
if (!CryptAcquireContext(&m_hProvider, CRYPTOPP_CONTAINER, 0, PROV_RSA_FULL, CRYPT_NEWKEYSET /*user*/) && |
|
!CryptAcquireContext(&m_hProvider, CRYPTOPP_CONTAINER, 0, PROV_RSA_FULL, CRYPT_MACHINE_KEYSET|CRYPT_NEWKEYSET)) |
|
{ |
|
// Set original error with original code |
|
SetLastError(firstErr); |
|
throw OS_RNG_Err("CryptAcquireContext"); |
|
} |
|
} |
|
#elif defined(USE_MS_CNGAPI) |
|
NTSTATUS ret = BCryptOpenAlgorithmProvider(&m_hProvider, BCRYPT_RNG_ALGORITHM, MS_PRIMITIVE_PROVIDER, 0); |
|
if (!(BCRYPT_SUCCESS(ret))) |
|
{ |
|
// Hack... OS_RNG_Err calls GetLastError() |
|
SetLastError(NtStatusToErrorCode(ret)); |
|
throw OS_RNG_Err("BCryptOpenAlgorithmProvider"); |
|
} |
|
#endif |
|
} |
|
|
|
MicrosoftCryptoProvider::~MicrosoftCryptoProvider() |
|
{ |
|
#if defined(USE_MS_CRYPTOAPI) |
|
if (m_hProvider) |
|
CryptReleaseContext(m_hProvider, 0); |
|
#elif defined(USE_MS_CNGAPI) |
|
if (m_hProvider) |
|
BCryptCloseAlgorithmProvider(m_hProvider, 0); |
|
#endif |
|
} |
|
|
|
#endif // CRYPTOPP_WIN32_AVAILABLE |
|
|
|
NonblockingRng::NonblockingRng() |
|
{ |
|
#ifndef CRYPTOPP_WIN32_AVAILABLE |
|
m_fd = open("/dev/urandom",O_RDONLY); |
|
if (m_fd == -1) |
|
throw OS_RNG_Err("open /dev/urandom"); |
|
|
|
// Do some OSes return -NNN instead of -1? |
|
CRYPTOPP_ASSERT(m_fd >= 0); |
|
#endif |
|
} |
|
|
|
NonblockingRng::~NonblockingRng() |
|
{ |
|
#ifndef CRYPTOPP_WIN32_AVAILABLE |
|
close(m_fd); |
|
#endif |
|
} |
|
|
|
void NonblockingRng::GenerateBlock(byte *output, size_t size) |
|
{ |
|
#ifdef CRYPTOPP_WIN32_AVAILABLE |
|
// Acquiring a provider is expensive. Do it once and retain the reference. |
|
# if defined(CRYPTOPP_CXX11_STATIC_INIT) |
|
static const MicrosoftCryptoProvider hProvider = MicrosoftCryptoProvider(); |
|
# else |
|
const MicrosoftCryptoProvider &hProvider = Singleton<MicrosoftCryptoProvider>().Ref(); |
|
# endif |
|
# if defined(USE_MS_CRYPTOAPI) |
|
DWORD dwSize; |
|
CRYPTOPP_ASSERT(SafeConvert(size, dwSize)); |
|
if (!SafeConvert(size, dwSize)) |
|
{ |
|
SetLastError(ERROR_INCORRECT_SIZE); |
|
throw OS_RNG_Err("GenerateBlock size"); |
|
} |
|
BOOL ret = CryptGenRandom(hProvider.GetProviderHandle(), dwSize, output); |
|
CRYPTOPP_ASSERT(ret != FALSE); |
|
if (ret == FALSE) |
|
throw OS_RNG_Err("CryptGenRandom"); |
|
# elif defined(USE_MS_CNGAPI) |
|
ULONG ulSize; |
|
CRYPTOPP_ASSERT(SafeConvert(size, ulSize)); |
|
if (!SafeConvert(size, ulSize)) |
|
{ |
|
SetLastError(ERROR_INCORRECT_SIZE); |
|
throw OS_RNG_Err("GenerateBlock size"); |
|
} |
|
NTSTATUS ret = BCryptGenRandom(hProvider.GetProviderHandle(), output, ulSize, 0); |
|
CRYPTOPP_ASSERT(BCRYPT_SUCCESS(ret)); |
|
if (!(BCRYPT_SUCCESS(ret))) |
|
{ |
|
// Hack... OS_RNG_Err calls GetLastError() |
|
SetLastError(NtStatusToErrorCode(ret)); |
|
throw OS_RNG_Err("BCryptGenRandom"); |
|
} |
|
# endif |
|
#else |
|
while (size) |
|
{ |
|
ssize_t len = read(m_fd, output, size); |
|
if (len < 0) |
|
{ |
|
// /dev/urandom reads CAN give EAGAIN errors! (maybe EINTR as well) |
|
if (errno != EINTR && errno != EAGAIN) |
|
throw OS_RNG_Err("read /dev/urandom"); |
|
|
|
continue; |
|
} |
|
|
|
output += len; |
|
size -= len; |
|
} |
|
#endif // CRYPTOPP_WIN32_AVAILABLE |
|
} |
|
|
|
#endif // NONBLOCKING_RNG_AVAILABLE |
|
|
|
// ************************************************************* |
|
|
|
#ifdef BLOCKING_RNG_AVAILABLE |
|
|
|
#ifndef CRYPTOPP_BLOCKING_RNG_FILENAME |
|
#ifdef __OpenBSD__ |
|
#define CRYPTOPP_BLOCKING_RNG_FILENAME "/dev/srandom" |
|
#else |
|
#define CRYPTOPP_BLOCKING_RNG_FILENAME "/dev/random" |
|
#endif |
|
#endif |
|
|
|
BlockingRng::BlockingRng() |
|
{ |
|
m_fd = open(CRYPTOPP_BLOCKING_RNG_FILENAME,O_RDONLY); |
|
if (m_fd == -1) |
|
throw OS_RNG_Err("open " CRYPTOPP_BLOCKING_RNG_FILENAME); |
|
|
|
// Do some OSes return -NNN instead of -1? |
|
CRYPTOPP_ASSERT(m_fd >= 0); |
|
} |
|
|
|
BlockingRng::~BlockingRng() |
|
{ |
|
close(m_fd); |
|
} |
|
|
|
void BlockingRng::GenerateBlock(byte *output, size_t size) |
|
{ |
|
while (size) |
|
{ |
|
// on some systems /dev/random will block until all bytes |
|
// are available, on others it returns immediately |
|
ssize_t len = read(m_fd, output, size); |
|
if (len < 0) |
|
{ |
|
// /dev/random reads CAN give EAGAIN errors! (maybe EINTR as well) |
|
if (errno != EINTR && errno != EAGAIN) |
|
throw OS_RNG_Err("read " CRYPTOPP_BLOCKING_RNG_FILENAME); |
|
|
|
continue; |
|
} |
|
|
|
size -= len; |
|
output += len; |
|
if (size) |
|
sleep(1); |
|
} |
|
} |
|
|
|
#endif // BLOCKING_RNG_AVAILABLE |
|
|
|
// ************************************************************* |
|
|
|
void OS_GenerateRandomBlock(bool blocking, byte *output, size_t size) |
|
{ |
|
#ifdef NONBLOCKING_RNG_AVAILABLE |
|
if (blocking) |
|
#endif |
|
{ |
|
#ifdef BLOCKING_RNG_AVAILABLE |
|
BlockingRng rng; |
|
rng.GenerateBlock(output, size); |
|
#endif |
|
} |
|
|
|
#ifdef BLOCKING_RNG_AVAILABLE |
|
if (!blocking) |
|
#endif |
|
{ |
|
#ifdef NONBLOCKING_RNG_AVAILABLE |
|
NonblockingRng rng; |
|
rng.GenerateBlock(output, size); |
|
#endif |
|
} |
|
} |
|
|
|
void AutoSeededRandomPool::Reseed(bool blocking, unsigned int seedSize) |
|
{ |
|
SecByteBlock seed(seedSize); |
|
OS_GenerateRandomBlock(blocking, seed, seedSize); |
|
IncorporateEntropy(seed, seedSize); |
|
} |
|
|
|
NAMESPACE_END |
|
|
|
#endif // OS_RNG_AVAILABLE |
|
|
|
#endif // CRYPTOPP_IMPORTS
|
|
|