dogecoin/src/random.cpp
Escanor Liones cac75ea877 [ Security ] Increased entropy from OpenSSL and OS to a full SHA512 block (64 bytes) and added additional entropy from RNSEED on CPUs that support it.
[ lowlevel ] Header file for wizardry and the dark arts.

 [ lowlevel ] Define this header only if running on x86. Run RDSEED only when available.

 [ lowlevel ] Define RDSEED in byte code. Use RDSEED assembly byte code in apple Operating Systems. Improve Windows detection with better macros.

 [ lowlevel ] Define bit_RDSEED. Fix typo.

 [ lowlevel ] Fix typo.

[ nit ] Fixed typos in the comments.

 [ update assertion ]

[ configure ] Add -mrdseed flag to building options by default and allow it to be disabled.

[ configure ] RDSEED is not available on ARM. Do not use rdseed compiler flag on ARM.

[ configure ] More commonly used ARM architecture machine tags.

[ configure ] Use -p flag instead of -m flag for arch tag detection.

[ configure ] Simplify ARM architecture detection.

[ configure ] Try detecting compiler instead of ARM architecture.

Update src/random.cpp

Get the right number of 64-bit words written to buffer.

Co-authored-by: RichDevX <richdevx@gmail.com>

[ configure.ac ] Determine rdseed based on architecture not compiler.

[ macro guard ] Create guard for RDSEED specifically. Clean scope of LOWLEVER_H guard by creating one for rdseed.

[ rdseed ] Clean up implementation dealing with apple OS.

Update src/random.cpp

Use more better type.

Co-authored-by: RichDevX <richdevx@gmail.com>

[ rdseed ] Restrict compiler flag to x86.

fix host detection

Co-authored-by: Patrick Lodder <patricklodder@noreply.github.com>
2021-09-06 16:35:01 -04:00

266 lines
7.1 KiB
C++

// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2016 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "random.h"
#include "crypto/sha512.h"
#include "support/cleanse.h"
#ifdef WIN32
#include "compat.h" // for Windows API
#include <wincrypt.h>
#endif
#include "util.h" // for LogPrint()
#include "utilstrencodings.h" // for GetTime()
#include <stdlib.h>
#include <limits>
#ifndef WIN32
#include <sys/time.h>
#endif
#include <openssl/err.h>
#include <openssl/rand.h>
#include <lowlevel.h>
static void RandFailure()
{
LogPrintf("Failed to read randomness, aborting\n");
abort();
}
static inline int64_t GetPerformanceCounter()
{
int64_t nCounter = 0;
#ifdef WIN32
QueryPerformanceCounter((LARGE_INTEGER*)&nCounter);
#else
timeval t;
gettimeofday(&t, NULL);
nCounter = (int64_t)(t.tv_sec * 1000000 + t.tv_usec);
#endif
return nCounter;
}
void RandAddSeed()
{
// Seed with CPU performance counter
int64_t nCounter = GetPerformanceCounter();
RAND_add(&nCounter, sizeof(nCounter), 1.5);
memory_cleanse((void*)&nCounter, sizeof(nCounter));
}
static void RandAddSeedPerfmon()
{
RandAddSeed();
#ifdef WIN32
// Don't need this on Linux, OpenSSL automatically uses /dev/urandom
// Seed with the entire set of perfmon data
// This can take up to 2 seconds, so only do it every 10 minutes
static int64_t nLastPerfmon;
if (GetTime() < nLastPerfmon + 10 * 60)
return;
nLastPerfmon = GetTime();
std::vector<unsigned char> vData(250000, 0);
long ret = 0;
unsigned long nSize = 0;
const size_t nMaxSize = 10000000; // Bail out at more than 10MB of performance data
while (true) {
nSize = vData.size();
ret = RegQueryValueExA(HKEY_PERFORMANCE_DATA, "Global", NULL, NULL, vData.data(), &nSize);
if (ret != ERROR_MORE_DATA || vData.size() >= nMaxSize)
break;
vData.resize(std::max((vData.size() * 3) / 2, nMaxSize)); // Grow size of buffer exponentially
}
RegCloseKey(HKEY_PERFORMANCE_DATA);
if (ret == ERROR_SUCCESS) {
RAND_add(vData.data(), nSize, nSize / 100.0);
memory_cleanse(vData.data(), nSize);
LogPrint("rand", "%s: %lu bytes\n", __func__, nSize);
} else {
static bool warned = false; // Warn only once
if (!warned) {
LogPrintf("%s: Warning: RegQueryValueExA(HKEY_PERFORMANCE_DATA) failed with code %i\n", __func__, ret);
warned = true;
}
}
#endif
}
/** Get 32 bytes of system entropy. */
static void GetOSRand(unsigned char *ent32)
{
#ifdef WIN32
HCRYPTPROV hProvider;
int ret = CryptAcquireContextW(&hProvider, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT);
if (!ret) {
RandFailure();
}
ret = CryptGenRandom(hProvider, 32, ent32);
if (!ret) {
RandFailure();
}
CryptReleaseContext(hProvider, 0);
#else
int f = open("/dev/urandom", O_RDONLY);
if (f == -1) {
RandFailure();
}
int have = 0;
do {
ssize_t n = read(f, ent32 + have, 32 - have);
if (n <= 0 || n + have > 32) {
RandFailure();
}
have += n;
} while (have < 32);
close(f);
#endif
}
void GetRandBytes(unsigned char* buf, int num)
{
if (RAND_bytes(buf, num) != 1) {
RandFailure();
}
}
/* /fn int getRDSEED( uint64_t *buff, int num)
* /brief Use Intel's SP 800-90B & C compliant Hardware implemented
* instruction to add entropy to the pool use to create keys.
*
* For details, see: https://software.intel.com/content/www/us/en/develop/blogs/the-difference-between-rdrand-and-rdseed.html
*
* /param buff Buffer to fill with random bits.
* /param num Number of 64-bit words to write to buffer.
* /return Returns 1 if the hardware returned a valid stream of random numbers, 0 otherwise.
*
*/
int getRDSEED( uint64_t *buff, uint32_t num){
//Initialize state to 0, or failed.
uint32_t cumulativeStatus = 0;
#ifdef LOWELEVEL_USE_RDSEEED
//Check to see if the processor running this code has
//this Instruction. Most Intel/AMD CPUs after 2015
//should have this instruction.
uint32_t level, eax, ebx, ecx, edx;
//Level where the RDSEED feature is described
level = 1;
//Extract feature vector
__get_cpuid(level, &eax, &ebx, &ecx, &edx);
//Check if RDSEED exists on this processor
if( (ecx & bit_RDSEED) == bit_RDSEED ) {
//Re-initialize value to 1, or success
cumulativeStatus = 1;
for( uint32_t kk = 0; kk < num; kk++){
//Get RBG data
uint32_t rng_high, rng_low;
int32_t status1 = _rdseed32_step( &rng_low );
int32_t status2 = _rdseed32_step( &rng_high );
//Place values into a 64-bit register
uint64_t temp = rng_high;
temp <<= 32;
temp += rng_low;
//Store into buffer
buff[kk] = temp;
//Update status value.
//cumulativeStatus will be 1 iff all iterations are 1.
cumulativeStatus &= status1 & status2;
}
}
#endif /*LOWLEVEL_USE_RDSEED*/
return cumulativeStatus;
}
void GetStrongRandBytes(unsigned char* out, int num)
{
assert(num <= 64);
CSHA512 hasher;
unsigned char buf[64];
// First source: OpenSSL's RNG
RandAddSeedPerfmon();
GetRandBytes(buf, 64);
hasher.Write(buf, 64);
// Second source: OS RNG
GetOSRand(buf);
hasher.Write(buf, 64);
//Third Source: Intel's NIST SP 800-90B & C compliant RBG
//if available, use it.
#ifdef LOWLEVEL_H
block_512bits buf2;
if ( getRDSEED(buf2.qword, 8) == 1 )
hasher.Write(buf2.ubyte, 64);
#endif
// Produce output
hasher.Finalize(buf);
memcpy(out, buf, num);
memory_cleanse(buf, 64);
}
uint64_t GetRand(uint64_t nMax)
{
if (nMax == 0)
return 0;
// The range of the random source must be a multiple of the modulus
// to give every possible output value an equal possibility
uint64_t nRange = (std::numeric_limits<uint64_t>::max() / nMax) * nMax;
uint64_t nRand = 0;
do {
GetRandBytes((unsigned char*)&nRand, sizeof(nRand));
} while (nRand >= nRange);
return (nRand % nMax);
}
int GetRandInt(int nMax)
{
return GetRand(nMax);
}
uint256 GetRandHash()
{
uint256 hash;
GetRandBytes((unsigned char*)&hash, sizeof(hash));
return hash;
}
FastRandomContext::FastRandomContext(bool fDeterministic)
{
// The seed values have some unlikely fixed points which we avoid.
if (fDeterministic) {
Rz = Rw = 11;
} else {
uint32_t tmp;
do {
GetRandBytes((unsigned char*)&tmp, 4);
} while (tmp == 0 || tmp == 0x9068ffffU);
Rz = tmp;
do {
GetRandBytes((unsigned char*)&tmp, 4);
} while (tmp == 0 || tmp == 0x464fffffU);
Rw = tmp;
}
}