Skip to content

Commit d19d45a

Browse files
committed
Merge #9821: util: Specific GetOSRandom for Linux/FreeBSD/OpenBSD
7e6dcd9 random: Add fallback if getrandom syscall not available (Wladimir J. van der Laan) 7cad849 sanity: Move OS random to sanity check function (Wladimir J. van der Laan) aa09ccb squashme: comment that NUM_OS_RANDOM_BYTES should not be changed lightly (Wladimir J. van der Laan) 224e6eb util: Specific GetOSRandom for Linux/FreeBSD/OpenBSD (Wladimir J. van der Laan) Tree-SHA512: 9fd408b1316c69de86674f342339b2f89192fd317c8c036b5df4320f828fa263c7966146bfc1904c51137ee4a26e4cb0f560b2cd05e18cde4d808b9b92ad15c4
2 parents cbdb473 + 7e6dcd9 commit d19d45a

File tree

6 files changed

+174
-11
lines changed

6 files changed

+174
-11
lines changed

configure.ac

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -574,6 +574,33 @@ AC_LINK_IFELSE([AC_LANG_SOURCE([
574574
]
575575
)
576576

577+
# Check for different ways of gathering OS randomness
578+
AC_MSG_CHECKING(for Linux getrandom syscall)
579+
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <unistd.h>
580+
#include <sys/syscall.h>
581+
#include <linux/random.h>]],
582+
[[ syscall(SYS_getrandom, nullptr, 32, 0); ]])],
583+
[ AC_MSG_RESULT(yes); AC_DEFINE(HAVE_SYS_GETRANDOM, 1,[Define this symbol if the Linux getrandom system call is available]) ],
584+
[ AC_MSG_RESULT(no)]
585+
)
586+
587+
AC_MSG_CHECKING(for getentropy)
588+
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <unistd.h>]],
589+
[[ getentropy(nullptr, 32) ]])],
590+
[ AC_MSG_RESULT(yes); AC_DEFINE(HAVE_GETENTROPY, 1,[Define this symbol if the BSD getentropy system call is available]) ],
591+
[ AC_MSG_RESULT(no)]
592+
)
593+
594+
AC_MSG_CHECKING(for sysctl KERN_ARND)
595+
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <sys/types.h>
596+
#include <sys/sysctl.h>]],
597+
[[ static const int name[2] = {CTL_KERN, KERN_ARND};
598+
sysctl(name, 2, nullptr, nullptr, nullptr, 0); ]])],
599+
[ AC_MSG_RESULT(yes); AC_DEFINE(HAVE_SYSCTL_ARND, 1,[Define this symbol if the BSD sysctl(KERN_ARND) is available]) ],
600+
[ AC_MSG_RESULT(no)]
601+
)
602+
603+
# Check for reduced exports
577604
if test x$use_reduce_exports = xyes; then
578605
AX_CHECK_COMPILE_FLAG([-fvisibility=hidden],[RE_CXXFLAGS="-fvisibility=hidden"],
579606
[AC_MSG_ERROR([Cannot set default symbol visibility. Use --disable-reduce-exports.])])

src/Makefile.test.include

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ BITCOIN_TESTS =\
110110
test/policyestimator_tests.cpp \
111111
test/pow_tests.cpp \
112112
test/prevector_tests.cpp \
113+
test/random_tests.cpp \
113114
test/raii_event_tests.cpp \
114115
test/reverselock_tests.cpp \
115116
test/rpc_tests.cpp \

src/init.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -683,9 +683,15 @@ bool InitSanityCheck(void)
683683
InitError("Elliptic curve cryptography sanity check failure. Aborting.");
684684
return false;
685685
}
686+
686687
if (!glibc_sanity_test() || !glibcxx_sanity_test())
687688
return false;
688689

690+
if (!Random_SanityCheck()) {
691+
InitError("OS cryptographic RNG sanity check failure. Aborting.");
692+
return false;
693+
}
694+
689695
return true;
690696
}
691697

src/random.cpp

Lines changed: 104 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,17 @@
2121
#include <sys/time.h>
2222
#endif
2323

24+
#ifdef HAVE_SYS_GETRANDOM
25+
#include <sys/syscall.h>
26+
#include <linux/random.h>
27+
#endif
28+
#ifdef HAVE_GETENTROPY
29+
#include <unistd.h>
30+
#endif
31+
#ifdef HAVE_SYSCTL_ARND
32+
#include <sys/sysctl.h>
33+
#endif
34+
2435
#include <openssl/err.h>
2536
#include <openssl/rand.h>
2637

@@ -91,34 +102,86 @@ static void RandAddSeedPerfmon()
91102
#endif
92103
}
93104

105+
#ifndef WIN32
106+
/** Fallback: get 32 bytes of system entropy from /dev/urandom. The most
107+
* compatible way to get cryptographic randomness on UNIX-ish platforms.
108+
*/
109+
void GetDevURandom(unsigned char *ent32)
110+
{
111+
int f = open("/dev/urandom", O_RDONLY);
112+
if (f == -1) {
113+
RandFailure();
114+
}
115+
int have = 0;
116+
do {
117+
ssize_t n = read(f, ent32 + have, NUM_OS_RANDOM_BYTES - have);
118+
if (n <= 0 || n + have > NUM_OS_RANDOM_BYTES) {
119+
RandFailure();
120+
}
121+
have += n;
122+
} while (have < NUM_OS_RANDOM_BYTES);
123+
close(f);
124+
}
125+
#endif
126+
94127
/** Get 32 bytes of system entropy. */
95-
static void GetOSRand(unsigned char *ent32)
128+
void GetOSRand(unsigned char *ent32)
96129
{
97-
#ifdef WIN32
130+
#if defined(WIN32)
98131
HCRYPTPROV hProvider;
99132
int ret = CryptAcquireContextW(&hProvider, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT);
100133
if (!ret) {
101134
RandFailure();
102135
}
103-
ret = CryptGenRandom(hProvider, 32, ent32);
136+
ret = CryptGenRandom(hProvider, NUM_OS_RANDOM_BYTES, ent32);
104137
if (!ret) {
105138
RandFailure();
106139
}
107140
CryptReleaseContext(hProvider, 0);
108-
#else
109-
int f = open("/dev/urandom", O_RDONLY);
110-
if (f == -1) {
141+
#elif defined(HAVE_SYS_GETRANDOM)
142+
/* Linux. From the getrandom(2) man page:
143+
* "If the urandom source has been initialized, reads of up to 256 bytes
144+
* will always return as many bytes as requested and will not be
145+
* interrupted by signals."
146+
*/
147+
int rv = syscall(SYS_getrandom, ent32, NUM_OS_RANDOM_BYTES, 0);
148+
if (rv != NUM_OS_RANDOM_BYTES) {
149+
if (rv < 0 && errno == ENOSYS) {
150+
/* Fallback for kernel <3.17: the return value will be -1 and errno
151+
* ENOSYS if the syscall is not available, in that case fall back
152+
* to /dev/urandom.
153+
*/
154+
GetDevURandom(ent32);
155+
} else {
156+
RandFailure();
157+
}
158+
}
159+
#elif defined(HAVE_GETENTROPY)
160+
/* On OpenBSD this can return up to 256 bytes of entropy, will return an
161+
* error if more are requested.
162+
* The call cannot return less than the requested number of bytes.
163+
*/
164+
if (getentropy(ent32, NUM_OS_RANDOM_BYTES) != 0) {
111165
RandFailure();
112166
}
167+
#elif defined(HAVE_SYSCTL_ARND)
168+
/* FreeBSD and similar. It is possible for the call to return less
169+
* bytes than requested, so need to read in a loop.
170+
*/
171+
static const int name[2] = {CTL_KERN, KERN_ARND};
113172
int have = 0;
114173
do {
115-
ssize_t n = read(f, ent32 + have, 32 - have);
116-
if (n <= 0 || n + have > 32) {
174+
size_t len = NUM_OS_RANDOM_BYTES - have;
175+
if (sysctl(name, ARRAYLEN(name), ent32 + have, &len, NULL, 0) != 0) {
117176
RandFailure();
118177
}
119-
have += n;
120-
} while (have < 32);
121-
close(f);
178+
have += len;
179+
} while (have < NUM_OS_RANDOM_BYTES);
180+
#else
181+
/* Fall back to /dev/urandom if there is no specific method implemented to
182+
* get system entropy for this OS.
183+
*/
184+
GetDevURandom(ent32);
122185
#endif
123186
}
124187

@@ -195,3 +258,33 @@ FastRandomContext::FastRandomContext(bool fDeterministic)
195258
}
196259
}
197260

261+
bool Random_SanityCheck()
262+
{
263+
/* This does not measure the quality of randomness, but it does test that
264+
* OSRandom() overwrites all 32 bytes of the output given a maximum
265+
* number of tries.
266+
*/
267+
static const ssize_t MAX_TRIES = 1024;
268+
uint8_t data[NUM_OS_RANDOM_BYTES];
269+
bool overwritten[NUM_OS_RANDOM_BYTES] = {}; /* Tracks which bytes have been overwritten at least once */
270+
int num_overwritten;
271+
int tries = 0;
272+
/* Loop until all bytes have been overwritten at least once, or max number tries reached */
273+
do {
274+
memset(data, 0, NUM_OS_RANDOM_BYTES);
275+
GetOSRand(data);
276+
for (int x=0; x < NUM_OS_RANDOM_BYTES; ++x) {
277+
overwritten[x] |= (data[x] != 0);
278+
}
279+
280+
num_overwritten = 0;
281+
for (int x=0; x < NUM_OS_RANDOM_BYTES; ++x) {
282+
if (overwritten[x]) {
283+
num_overwritten += 1;
284+
}
285+
}
286+
287+
tries += 1;
288+
} while (num_overwritten < NUM_OS_RANDOM_BYTES && tries < MAX_TRIES);
289+
return (num_overwritten == NUM_OS_RANDOM_BYTES); /* If this failed, bailed out after too many tries */
290+
}

src/random.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,4 +46,21 @@ class FastRandomContext {
4646
uint32_t Rw;
4747
};
4848

49+
/* Number of random bytes returned by GetOSRand.
50+
* When changing this constant make sure to change all call sites, and make
51+
* sure that the underlying OS APIs for all platforms support the number.
52+
* (many cap out at 256 bytes).
53+
*/
54+
static const ssize_t NUM_OS_RANDOM_BYTES = 32;
55+
56+
/** Get 32 bytes of system entropy. Do not use this in application code: use
57+
* GetStrongRandBytes instead.
58+
*/
59+
void GetOSRand(unsigned char *ent32);
60+
61+
/** Check that OS randomness is available and returning the requested number
62+
* of bytes.
63+
*/
64+
bool Random_SanityCheck();
65+
4966
#endif // BITCOIN_RANDOM_H

src/test/random_tests.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Copyright (c) 2017 The Bitcoin Core developers
2+
// Distributed under the MIT software license, see the accompanying
3+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4+
5+
#include "random.h"
6+
7+
#include "test/test_bitcoin.h"
8+
9+
#include <boost/test/unit_test.hpp>
10+
11+
BOOST_FIXTURE_TEST_SUITE(random_tests, BasicTestingSetup)
12+
13+
BOOST_AUTO_TEST_CASE(osrandom_tests)
14+
{
15+
BOOST_CHECK(Random_SanityCheck());
16+
}
17+
18+
BOOST_AUTO_TEST_SUITE_END()
19+

0 commit comments

Comments
 (0)