@ -21,6 +21,17 @@
@@ -21,6 +21,17 @@
# include <sys/time.h>
# endif
# ifdef HAVE_SYS_GETRANDOM
# include <sys/syscall.h>
# include <linux/random.h>
# endif
# ifdef HAVE_GETENTROPY
# include <unistd.h>
# endif
# ifdef HAVE_SYSCTL_ARND
# include <sys/sysctl.h>
# endif
# include <openssl/err.h>
# include <openssl/rand.h>
@ -91,34 +102,86 @@ static void RandAddSeedPerfmon()
@@ -91,34 +102,86 @@ static void RandAddSeedPerfmon()
# endif
}
# ifndef WIN32
/** Fallback: get 32 bytes of system entropy from /dev/urandom. The most
* compatible way to get cryptographic randomness on UNIX - ish platforms .
*/
void GetDevURandom ( unsigned char * ent32 )
{
int f = open ( " /dev/urandom " , O_RDONLY ) ;
if ( f = = - 1 ) {
RandFailure ( ) ;
}
int have = 0 ;
do {
ssize_t n = read ( f , ent32 + have , NUM_OS_RANDOM_BYTES - have ) ;
if ( n < = 0 | | n + have > NUM_OS_RANDOM_BYTES ) {
RandFailure ( ) ;
}
have + = n ;
} while ( have < NUM_OS_RANDOM_BYTES ) ;
close ( f ) ;
}
# endif
/** Get 32 bytes of system entropy. */
static void GetOSRand ( unsigned char * ent32 )
void GetOSRand ( unsigned char * ent32 )
{
# ifdef WIN32
# if defined(WIN32)
HCRYPTPROV hProvider ;
int ret = CryptAcquireContextW ( & hProvider , NULL , NULL , PROV_RSA_FULL , CRYPT_VERIFYCONTEXT ) ;
if ( ! ret ) {
RandFailure ( ) ;
}
ret = CryptGenRandom ( hProvider , 32 , ent32 ) ;
ret = CryptGenRandom ( hProvider , NUM_OS_RANDOM_BYTES , ent32 ) ;
if ( ! ret ) {
RandFailure ( ) ;
}
CryptReleaseContext ( hProvider , 0 ) ;
# else
int f = open ( " /dev/urandom " , O_RDONLY ) ;
if ( f = = - 1 ) {
# elif defined(HAVE_SYS_GETRANDOM)
/* Linux. From the getrandom(2) man page:
* " If the urandom source has been initialized, reads of up to 256 bytes
* will always return as many bytes as requested and will not be
* interrupted by signals . "
*/
int rv = syscall ( SYS_getrandom , ent32 , NUM_OS_RANDOM_BYTES , 0 ) ;
if ( rv ! = NUM_OS_RANDOM_BYTES ) {
if ( rv < 0 & & errno = = ENOSYS ) {
/* Fallback for kernel <3.17: the return value will be -1 and errno
* ENOSYS if the syscall is not available , in that case fall back
* to / dev / urandom .
*/
GetDevURandom ( ent32 ) ;
} else {
RandFailure ( ) ;
}
}
# elif defined(HAVE_GETENTROPY)
/* On OpenBSD this can return up to 256 bytes of entropy, will return an
* error if more are requested .
* The call cannot return less than the requested number of bytes .
*/
if ( getentropy ( ent32 , NUM_OS_RANDOM_BYTES ) ! = 0 ) {
RandFailure ( ) ;
}
# elif defined(HAVE_SYSCTL_ARND)
/* FreeBSD and similar. It is possible for the call to return less
* bytes than requested , so need to read in a loop .
*/
static const int name [ 2 ] = { CTL_KERN , KERN_ARND } ;
int have = 0 ;
do {
ssize_t n = read ( f , ent32 + have , 32 - have ) ;
if ( n < = 0 | | n + have > 32 ) {
size_t len = NUM_OS_RANDOM_BYTES - have ;
if ( sysctl ( name , ARRAYLEN ( name ) , ent32 + have , & len , NULL , 0 ) ! = 0 ) {
RandFailure ( ) ;
}
have + = n ;
} while ( have < 32 ) ;
close ( f ) ;
have + = len ;
} while ( have < NUM_OS_RANDOM_BYTES ) ;
# else
/* Fall back to /dev/urandom if there is no specific method implemented to
* get system entropy for this OS .
*/
GetDevURandom ( ent32 ) ;
# endif
}
@ -195,3 +258,33 @@ FastRandomContext::FastRandomContext(bool fDeterministic)
@@ -195,3 +258,33 @@ FastRandomContext::FastRandomContext(bool fDeterministic)
}
}
bool Random_SanityCheck ( )
{
/* This does not measure the quality of randomness, but it does test that
* OSRandom ( ) overwrites all 32 bytes of the output given a maximum
* number of tries .
*/
static const ssize_t MAX_TRIES = 1024 ;
uint8_t data [ NUM_OS_RANDOM_BYTES ] ;
bool overwritten [ NUM_OS_RANDOM_BYTES ] = { } ; /* Tracks which bytes have been overwritten at least once */
int num_overwritten ;
int tries = 0 ;
/* Loop until all bytes have been overwritten at least once, or max number tries reached */
do {
memset ( data , 0 , NUM_OS_RANDOM_BYTES ) ;
GetOSRand ( data ) ;
for ( int x = 0 ; x < NUM_OS_RANDOM_BYTES ; + + x ) {
overwritten [ x ] | = ( data [ x ] ! = 0 ) ;
}
num_overwritten = 0 ;
for ( int x = 0 ; x < NUM_OS_RANDOM_BYTES ; + + x ) {
if ( overwritten [ x ] ) {
num_overwritten + = 1 ;
}
}
tries + = 1 ;
} while ( num_overwritten < NUM_OS_RANDOM_BYTES & & tries < MAX_TRIES ) ;
return ( num_overwritten = = NUM_OS_RANDOM_BYTES ) ; /* If this failed, bailed out after too many tries */
}