mirror of
https://github.com/shadps4-emu/ext-cryptopp.git
synced 2024-11-23 09:59:42 +00:00
330 lines
8.7 KiB
C++
330 lines
8.7 KiB
C++
// 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"
|
|
|
|
// FreeBSD links /dev/urandom -> /dev/random. It showed up when we added
|
|
// O_NOFOLLOW to harden the non-blocking generator. Use Arc4Random instead
|
|
// for a non-blocking generator. Arc4Random is cryptograhic quality prng
|
|
// based on ChaCha20. The ChaCha20 generator is seeded from /dev/random,
|
|
// so we can't completely avoid the blocking.
|
|
// https://www.freebsd.org/cgi/man.cgi?query=arc4random_buf.
|
|
#ifdef __FreeBSD__
|
|
# define USE_FREEBSD_ARC4RANDOM 1
|
|
# include <stdlib.h>
|
|
#endif
|
|
|
|
#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
|
|
# ifndef USE_FREEBSD_ARC4RANDOM
|
|
# ifdef O_NOFOLLOW
|
|
const int flags = O_RDONLY|O_NOFOLLOW;
|
|
# else
|
|
const int flags = O_RDONLY;
|
|
# endif
|
|
|
|
m_fd = open("/dev/urandom", flags);
|
|
if (m_fd == -1)
|
|
throw OS_RNG_Err("open /dev/urandom");
|
|
|
|
# endif
|
|
#endif
|
|
}
|
|
|
|
NonblockingRng::~NonblockingRng()
|
|
{
|
|
#ifndef CRYPTOPP_WIN32_AVAILABLE
|
|
# ifndef USE_FREEBSD_ARC4RANDOM
|
|
close(m_fd);
|
|
# endif
|
|
#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
|
|
|
|
# if defined(USE_FREEBSD_ARC4RANDOM)
|
|
// Cryptographic quality prng based on ChaCha20,
|
|
// https://www.freebsd.org/cgi/man.cgi?query=arc4random_buf
|
|
arc4random_buf(output, size);
|
|
# 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 // USE_FREEBSD_ARC4RANDOM
|
|
|
|
#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()
|
|
{
|
|
#ifdef O_NOFOLLOW
|
|
const int flags = O_RDONLY|O_NOFOLLOW;
|
|
#else
|
|
const int flags = O_RDONLY;
|
|
#endif
|
|
|
|
m_fd = open(CRYPTOPP_BLOCKING_RNG_FILENAME, flags);
|
|
if (m_fd == -1)
|
|
throw OS_RNG_Err("open " CRYPTOPP_BLOCKING_RNG_FILENAME);
|
|
}
|
|
|
|
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
|