Merge #9792: FastRandomContext improvements and switch to ChaCha20
4fd2d2f
Add a FastRandomContext::randrange and use it (Pieter Wuille)1632922
Switch FastRandomContext to ChaCha20 (Pieter Wuille)e04326f
Add ChaCha20 (Pieter Wuille)663fbae
FastRandom benchmark (Pieter Wuille)c21cbe6
Introduce FastRandomContext::randbool() (Pieter Wuille) Tree-SHA512: 7fff61e3f6d6dc6ac846ca643d877b377db609646dd401a0e8f50b052c6b9bcd2f5fc34de6bbf28f04afd1724f6279ee163ead5f37d724fb782a00239f35db1d
This commit is contained in:
commit
342b9bc390
20 changed files with 483 additions and 45 deletions
|
@ -549,6 +549,8 @@ AC_CHECK_DECLS([bswap_16, bswap_32, bswap_64],,,
|
||||||
#include <byteswap.h>
|
#include <byteswap.h>
|
||||||
#endif])
|
#endif])
|
||||||
|
|
||||||
|
AC_CHECK_DECLS([__builtin_clz, __builtin_clzl, __builtin_clzll])
|
||||||
|
|
||||||
dnl Check for MSG_NOSIGNAL
|
dnl Check for MSG_NOSIGNAL
|
||||||
AC_MSG_CHECKING(for MSG_NOSIGNAL)
|
AC_MSG_CHECKING(for MSG_NOSIGNAL)
|
||||||
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <sys/socket.h>]],
|
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <sys/socket.h>]],
|
||||||
|
|
|
@ -246,6 +246,8 @@ crypto_libbitcoin_crypto_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
|
||||||
crypto_libbitcoin_crypto_a_SOURCES = \
|
crypto_libbitcoin_crypto_a_SOURCES = \
|
||||||
crypto/aes.cpp \
|
crypto/aes.cpp \
|
||||||
crypto/aes.h \
|
crypto/aes.h \
|
||||||
|
crypto/chacha20.h \
|
||||||
|
crypto/chacha20.cpp \
|
||||||
crypto/common.h \
|
crypto/common.h \
|
||||||
crypto/hmac_sha256.cpp \
|
crypto/hmac_sha256.cpp \
|
||||||
crypto/hmac_sha256.h \
|
crypto/hmac_sha256.h \
|
||||||
|
|
|
@ -57,8 +57,8 @@ BITCOIN_TESTS =\
|
||||||
test/policyestimator_tests.cpp \
|
test/policyestimator_tests.cpp \
|
||||||
test/pow_tests.cpp \
|
test/pow_tests.cpp \
|
||||||
test/prevector_tests.cpp \
|
test/prevector_tests.cpp \
|
||||||
test/random_tests.cpp \
|
|
||||||
test/raii_event_tests.cpp \
|
test/raii_event_tests.cpp \
|
||||||
|
test/random_tests.cpp \
|
||||||
test/reverselock_tests.cpp \
|
test/reverselock_tests.cpp \
|
||||||
test/rpc_tests.cpp \
|
test/rpc_tests.cpp \
|
||||||
test/sanity_tests.cpp \
|
test/sanity_tests.cpp \
|
||||||
|
|
|
@ -351,8 +351,8 @@ CAddrInfo CAddrMan::Select_(bool newOnly)
|
||||||
int nKBucket = RandomInt(ADDRMAN_TRIED_BUCKET_COUNT);
|
int nKBucket = RandomInt(ADDRMAN_TRIED_BUCKET_COUNT);
|
||||||
int nKBucketPos = RandomInt(ADDRMAN_BUCKET_SIZE);
|
int nKBucketPos = RandomInt(ADDRMAN_BUCKET_SIZE);
|
||||||
while (vvTried[nKBucket][nKBucketPos] == -1) {
|
while (vvTried[nKBucket][nKBucketPos] == -1) {
|
||||||
nKBucket = (nKBucket + insecure_rand.rand32()) % ADDRMAN_TRIED_BUCKET_COUNT;
|
nKBucket = (nKBucket + insecure_rand.randbits(ADDRMAN_TRIED_BUCKET_COUNT_LOG2)) % ADDRMAN_TRIED_BUCKET_COUNT;
|
||||||
nKBucketPos = (nKBucketPos + insecure_rand.rand32()) % ADDRMAN_BUCKET_SIZE;
|
nKBucketPos = (nKBucketPos + insecure_rand.randbits(ADDRMAN_BUCKET_SIZE_LOG2)) % ADDRMAN_BUCKET_SIZE;
|
||||||
}
|
}
|
||||||
int nId = vvTried[nKBucket][nKBucketPos];
|
int nId = vvTried[nKBucket][nKBucketPos];
|
||||||
assert(mapInfo.count(nId) == 1);
|
assert(mapInfo.count(nId) == 1);
|
||||||
|
@ -368,8 +368,8 @@ CAddrInfo CAddrMan::Select_(bool newOnly)
|
||||||
int nUBucket = RandomInt(ADDRMAN_NEW_BUCKET_COUNT);
|
int nUBucket = RandomInt(ADDRMAN_NEW_BUCKET_COUNT);
|
||||||
int nUBucketPos = RandomInt(ADDRMAN_BUCKET_SIZE);
|
int nUBucketPos = RandomInt(ADDRMAN_BUCKET_SIZE);
|
||||||
while (vvNew[nUBucket][nUBucketPos] == -1) {
|
while (vvNew[nUBucket][nUBucketPos] == -1) {
|
||||||
nUBucket = (nUBucket + insecure_rand.rand32()) % ADDRMAN_NEW_BUCKET_COUNT;
|
nUBucket = (nUBucket + insecure_rand.randbits(ADDRMAN_NEW_BUCKET_COUNT_LOG2)) % ADDRMAN_NEW_BUCKET_COUNT;
|
||||||
nUBucketPos = (nUBucketPos + insecure_rand.rand32()) % ADDRMAN_BUCKET_SIZE;
|
nUBucketPos = (nUBucketPos + insecure_rand.randbits(ADDRMAN_BUCKET_SIZE_LOG2)) % ADDRMAN_BUCKET_SIZE;
|
||||||
}
|
}
|
||||||
int nId = vvNew[nUBucket][nUBucketPos];
|
int nId = vvNew[nUBucket][nUBucketPos];
|
||||||
assert(mapInfo.count(nId) == 1);
|
assert(mapInfo.count(nId) == 1);
|
||||||
|
|
|
@ -136,13 +136,13 @@ public:
|
||||||
*/
|
*/
|
||||||
|
|
||||||
//! total number of buckets for tried addresses
|
//! total number of buckets for tried addresses
|
||||||
#define ADDRMAN_TRIED_BUCKET_COUNT 256
|
#define ADDRMAN_TRIED_BUCKET_COUNT_LOG2 8
|
||||||
|
|
||||||
//! total number of buckets for new addresses
|
//! total number of buckets for new addresses
|
||||||
#define ADDRMAN_NEW_BUCKET_COUNT 1024
|
#define ADDRMAN_NEW_BUCKET_COUNT_LOG2 10
|
||||||
|
|
||||||
//! maximum allowed number of entries in buckets for new and tried addresses
|
//! maximum allowed number of entries in buckets for new and tried addresses
|
||||||
#define ADDRMAN_BUCKET_SIZE 64
|
#define ADDRMAN_BUCKET_SIZE_LOG2 6
|
||||||
|
|
||||||
//! over how many buckets entries with tried addresses from a single group (/16 for IPv4) are spread
|
//! over how many buckets entries with tried addresses from a single group (/16 for IPv4) are spread
|
||||||
#define ADDRMAN_TRIED_BUCKETS_PER_GROUP 8
|
#define ADDRMAN_TRIED_BUCKETS_PER_GROUP 8
|
||||||
|
@ -171,6 +171,11 @@ public:
|
||||||
//! the maximum number of nodes to return in a getaddr call
|
//! the maximum number of nodes to return in a getaddr call
|
||||||
#define ADDRMAN_GETADDR_MAX 2500
|
#define ADDRMAN_GETADDR_MAX 2500
|
||||||
|
|
||||||
|
//! Convenience
|
||||||
|
#define ADDRMAN_TRIED_BUCKET_COUNT (1 << ADDRMAN_TRIED_BUCKET_COUNT_LOG2)
|
||||||
|
#define ADDRMAN_NEW_BUCKET_COUNT (1 << ADDRMAN_NEW_BUCKET_COUNT_LOG2)
|
||||||
|
#define ADDRMAN_BUCKET_SIZE (1 << ADDRMAN_BUCKET_SIZE_LOG2)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stochastical (IP) address manager
|
* Stochastical (IP) address manager
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -68,7 +68,7 @@ static void CCheckQueueSpeedPrevectorJob(benchmark::State& state)
|
||||||
PrevectorJob(){
|
PrevectorJob(){
|
||||||
}
|
}
|
||||||
PrevectorJob(FastRandomContext& insecure_rand){
|
PrevectorJob(FastRandomContext& insecure_rand){
|
||||||
p.resize(insecure_rand.rand32() % (PREVECTOR_SIZE*2));
|
p.resize(insecure_rand.randrange(PREVECTOR_SIZE*2));
|
||||||
}
|
}
|
||||||
bool operator()()
|
bool operator()()
|
||||||
{
|
{
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
#include "bench.h"
|
#include "bench.h"
|
||||||
#include "bloom.h"
|
#include "bloom.h"
|
||||||
#include "hash.h"
|
#include "hash.h"
|
||||||
|
#include "random.h"
|
||||||
#include "uint256.h"
|
#include "uint256.h"
|
||||||
#include "utiltime.h"
|
#include "utiltime.h"
|
||||||
#include "crypto/ripemd160.h"
|
#include "crypto/ripemd160.h"
|
||||||
|
@ -69,6 +70,28 @@ static void SipHash_32b(benchmark::State& state)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void FastRandom_32bit(benchmark::State& state)
|
||||||
|
{
|
||||||
|
FastRandomContext rng(true);
|
||||||
|
uint32_t x;
|
||||||
|
while (state.KeepRunning()) {
|
||||||
|
for (int i = 0; i < 1000000; i++) {
|
||||||
|
x += rng.rand32();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void FastRandom_1bit(benchmark::State& state)
|
||||||
|
{
|
||||||
|
FastRandomContext rng(true);
|
||||||
|
uint32_t x;
|
||||||
|
while (state.KeepRunning()) {
|
||||||
|
for (int i = 0; i < 1000000; i++) {
|
||||||
|
x += rng.randbool();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
BENCHMARK(RIPEMD160);
|
BENCHMARK(RIPEMD160);
|
||||||
BENCHMARK(SHA1);
|
BENCHMARK(SHA1);
|
||||||
BENCHMARK(SHA256);
|
BENCHMARK(SHA256);
|
||||||
|
@ -76,3 +99,5 @@ BENCHMARK(SHA512);
|
||||||
|
|
||||||
BENCHMARK(SHA256_32b);
|
BENCHMARK(SHA256_32b);
|
||||||
BENCHMARK(SipHash_32b);
|
BENCHMARK(SipHash_32b);
|
||||||
|
BENCHMARK(FastRandom_32bit);
|
||||||
|
BENCHMARK(FastRandom_1bit);
|
||||||
|
|
180
src/crypto/chacha20.cpp
Normal file
180
src/crypto/chacha20.cpp
Normal file
|
@ -0,0 +1,180 @@
|
||||||
|
// Copyright (c) 2017 The Bitcoin Core developers
|
||||||
|
// Distributed under the MIT software license, see the accompanying
|
||||||
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
|
// Based on the public domain implementation 'merged' by D. J. Bernstein
|
||||||
|
// See https://cr.yp.to/chacha.html.
|
||||||
|
|
||||||
|
#include "crypto/common.h"
|
||||||
|
#include "crypto/chacha20.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
constexpr static inline uint32_t rotl32(uint32_t v, int c) { return (v << c) | (v >> (32 - c)); }
|
||||||
|
|
||||||
|
#define QUARTERROUND(a,b,c,d) \
|
||||||
|
a += b; d = rotl32(d ^ a, 16); \
|
||||||
|
c += d; b = rotl32(b ^ c, 12); \
|
||||||
|
a += b; d = rotl32(d ^ a, 8); \
|
||||||
|
c += d; b = rotl32(b ^ c, 7);
|
||||||
|
|
||||||
|
static const unsigned char sigma[] = "expand 32-byte k";
|
||||||
|
static const unsigned char tau[] = "expand 16-byte k";
|
||||||
|
|
||||||
|
void ChaCha20::SetKey(const unsigned char* k, size_t keylen)
|
||||||
|
{
|
||||||
|
const unsigned char *constants;
|
||||||
|
|
||||||
|
input[4] = ReadLE32(k + 0);
|
||||||
|
input[5] = ReadLE32(k + 4);
|
||||||
|
input[6] = ReadLE32(k + 8);
|
||||||
|
input[7] = ReadLE32(k + 12);
|
||||||
|
if (keylen == 32) { /* recommended */
|
||||||
|
k += 16;
|
||||||
|
constants = sigma;
|
||||||
|
} else { /* keylen == 16 */
|
||||||
|
constants = tau;
|
||||||
|
}
|
||||||
|
input[8] = ReadLE32(k + 0);
|
||||||
|
input[9] = ReadLE32(k + 4);
|
||||||
|
input[10] = ReadLE32(k + 8);
|
||||||
|
input[11] = ReadLE32(k + 12);
|
||||||
|
input[0] = ReadLE32(constants + 0);
|
||||||
|
input[1] = ReadLE32(constants + 4);
|
||||||
|
input[2] = ReadLE32(constants + 8);
|
||||||
|
input[3] = ReadLE32(constants + 12);
|
||||||
|
input[12] = 0;
|
||||||
|
input[13] = 0;
|
||||||
|
input[14] = 0;
|
||||||
|
input[15] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ChaCha20::ChaCha20()
|
||||||
|
{
|
||||||
|
memset(input, 0, sizeof(input));
|
||||||
|
}
|
||||||
|
|
||||||
|
ChaCha20::ChaCha20(const unsigned char* k, size_t keylen)
|
||||||
|
{
|
||||||
|
SetKey(k, keylen);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChaCha20::SetIV(uint64_t iv)
|
||||||
|
{
|
||||||
|
input[14] = iv;
|
||||||
|
input[15] = iv >> 32;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChaCha20::Seek(uint64_t pos)
|
||||||
|
{
|
||||||
|
input[12] = pos;
|
||||||
|
input[13] = pos >> 32;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChaCha20::Output(unsigned char* c, size_t bytes)
|
||||||
|
{
|
||||||
|
uint32_t x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15;
|
||||||
|
uint32_t j0, j1, j2, j3, j4, j5, j6, j7, j8, j9, j10, j11, j12, j13, j14, j15;
|
||||||
|
unsigned char *ctarget = NULL;
|
||||||
|
unsigned char tmp[64];
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
if (!bytes) return;
|
||||||
|
|
||||||
|
j0 = input[0];
|
||||||
|
j1 = input[1];
|
||||||
|
j2 = input[2];
|
||||||
|
j3 = input[3];
|
||||||
|
j4 = input[4];
|
||||||
|
j5 = input[5];
|
||||||
|
j6 = input[6];
|
||||||
|
j7 = input[7];
|
||||||
|
j8 = input[8];
|
||||||
|
j9 = input[9];
|
||||||
|
j10 = input[10];
|
||||||
|
j11 = input[11];
|
||||||
|
j12 = input[12];
|
||||||
|
j13 = input[13];
|
||||||
|
j14 = input[14];
|
||||||
|
j15 = input[15];
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
if (bytes < 64) {
|
||||||
|
ctarget = c;
|
||||||
|
c = tmp;
|
||||||
|
}
|
||||||
|
x0 = j0;
|
||||||
|
x1 = j1;
|
||||||
|
x2 = j2;
|
||||||
|
x3 = j3;
|
||||||
|
x4 = j4;
|
||||||
|
x5 = j5;
|
||||||
|
x6 = j6;
|
||||||
|
x7 = j7;
|
||||||
|
x8 = j8;
|
||||||
|
x9 = j9;
|
||||||
|
x10 = j10;
|
||||||
|
x11 = j11;
|
||||||
|
x12 = j12;
|
||||||
|
x13 = j13;
|
||||||
|
x14 = j14;
|
||||||
|
x15 = j15;
|
||||||
|
for (i = 20;i > 0;i -= 2) {
|
||||||
|
QUARTERROUND( x0, x4, x8,x12)
|
||||||
|
QUARTERROUND( x1, x5, x9,x13)
|
||||||
|
QUARTERROUND( x2, x6,x10,x14)
|
||||||
|
QUARTERROUND( x3, x7,x11,x15)
|
||||||
|
QUARTERROUND( x0, x5,x10,x15)
|
||||||
|
QUARTERROUND( x1, x6,x11,x12)
|
||||||
|
QUARTERROUND( x2, x7, x8,x13)
|
||||||
|
QUARTERROUND( x3, x4, x9,x14)
|
||||||
|
}
|
||||||
|
x0 += j0;
|
||||||
|
x1 += j1;
|
||||||
|
x2 += j2;
|
||||||
|
x3 += j3;
|
||||||
|
x4 += j4;
|
||||||
|
x5 += j5;
|
||||||
|
x6 += j6;
|
||||||
|
x7 += j7;
|
||||||
|
x8 += j8;
|
||||||
|
x9 += j9;
|
||||||
|
x10 += j10;
|
||||||
|
x11 += j11;
|
||||||
|
x12 += j12;
|
||||||
|
x13 += j13;
|
||||||
|
x14 += j14;
|
||||||
|
x15 += j15;
|
||||||
|
|
||||||
|
++j12;
|
||||||
|
if (!j12) ++j13;
|
||||||
|
|
||||||
|
WriteLE32(c + 0, x0);
|
||||||
|
WriteLE32(c + 4, x1);
|
||||||
|
WriteLE32(c + 8, x2);
|
||||||
|
WriteLE32(c + 12, x3);
|
||||||
|
WriteLE32(c + 16, x4);
|
||||||
|
WriteLE32(c + 20, x5);
|
||||||
|
WriteLE32(c + 24, x6);
|
||||||
|
WriteLE32(c + 28, x7);
|
||||||
|
WriteLE32(c + 32, x8);
|
||||||
|
WriteLE32(c + 36, x9);
|
||||||
|
WriteLE32(c + 40, x10);
|
||||||
|
WriteLE32(c + 44, x11);
|
||||||
|
WriteLE32(c + 48, x12);
|
||||||
|
WriteLE32(c + 52, x13);
|
||||||
|
WriteLE32(c + 56, x14);
|
||||||
|
WriteLE32(c + 60, x15);
|
||||||
|
|
||||||
|
if (bytes <= 64) {
|
||||||
|
if (bytes < 64) {
|
||||||
|
for (i = 0;i < bytes;++i) ctarget[i] = c[i];
|
||||||
|
}
|
||||||
|
input[12] = j12;
|
||||||
|
input[13] = j13;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
bytes -= 64;
|
||||||
|
c += 64;
|
||||||
|
}
|
||||||
|
}
|
26
src/crypto/chacha20.h
Normal file
26
src/crypto/chacha20.h
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
// Copyright (c) 2017 The Bitcoin Core developers
|
||||||
|
// Distributed under the MIT software license, see the accompanying
|
||||||
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
|
#ifndef BITCOIN_CRYPTO_CHACHA20_H
|
||||||
|
#define BITCOIN_CRYPTO_CHACHA20_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
/** A PRNG class for ChaCha20. */
|
||||||
|
class ChaCha20
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
uint32_t input[16];
|
||||||
|
|
||||||
|
public:
|
||||||
|
ChaCha20();
|
||||||
|
ChaCha20(const unsigned char* key, size_t keylen);
|
||||||
|
void SetKey(const unsigned char* key, size_t keylen);
|
||||||
|
void SetIV(uint64_t iv);
|
||||||
|
void Seek(uint64_t pos);
|
||||||
|
void Output(unsigned char* output, size_t bytes);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // BITCOIN_CRYPTO_CHACHA20_H
|
|
@ -79,4 +79,25 @@ void static inline WriteBE64(unsigned char* ptr, uint64_t x)
|
||||||
memcpy(ptr, (char*)&v, 8);
|
memcpy(ptr, (char*)&v, 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Return the smallest number n such that (x >> n) == 0 (or 64 if the highest bit in x is set. */
|
||||||
|
uint64_t static inline CountBits(uint64_t x)
|
||||||
|
{
|
||||||
|
#ifdef HAVE_DECL___BUILTIN_CLZL
|
||||||
|
if (sizeof(unsigned long) >= sizeof(uint64_t)) {
|
||||||
|
return x ? 8 * sizeof(unsigned long) - __builtin_clzl(x) : 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_DECL___BUILTIN_CLZLL
|
||||||
|
if (sizeof(unsigned long long) >= sizeof(uint64_t)) {
|
||||||
|
return x ? 8 * sizeof(unsigned long long) - __builtin_clzll(x) : 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
int ret = 0;
|
||||||
|
while (x) {
|
||||||
|
x >>= 1;
|
||||||
|
++ret;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
#endif // BITCOIN_CRYPTO_COMMON_H
|
#endif // BITCOIN_CRYPTO_COMMON_H
|
||||||
|
|
|
@ -758,7 +758,7 @@ public:
|
||||||
// after addresses were pushed.
|
// after addresses were pushed.
|
||||||
if (_addr.IsValid() && !addrKnown.contains(_addr.GetKey())) {
|
if (_addr.IsValid() && !addrKnown.contains(_addr.GetKey())) {
|
||||||
if (vAddrToSend.size() >= MAX_ADDR_TO_SEND) {
|
if (vAddrToSend.size() >= MAX_ADDR_TO_SEND) {
|
||||||
vAddrToSend[insecure_rand.rand32() % vAddrToSend.size()] = _addr;
|
vAddrToSend[insecure_rand.randrange(vAddrToSend.size())] = _addr;
|
||||||
} else {
|
} else {
|
||||||
vAddrToSend.push_back(_addr);
|
vAddrToSend.push_back(_addr);
|
||||||
}
|
}
|
||||||
|
|
|
@ -240,22 +240,16 @@ uint256 GetRandHash()
|
||||||
return hash;
|
return hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
FastRandomContext::FastRandomContext(bool fDeterministic)
|
void FastRandomContext::RandomSeed()
|
||||||
{
|
{
|
||||||
// The seed values have some unlikely fixed points which we avoid.
|
uint256 seed = GetRandHash();
|
||||||
if (fDeterministic) {
|
rng.SetKey(seed.begin(), 32);
|
||||||
Rz = Rw = 11;
|
requires_seed = false;
|
||||||
} else {
|
}
|
||||||
uint32_t tmp;
|
|
||||||
do {
|
FastRandomContext::FastRandomContext(const uint256& seed) : requires_seed(false), bytebuf_size(0), bitbuf_size(0)
|
||||||
GetRandBytes((unsigned char*)&tmp, 4);
|
{
|
||||||
} while (tmp == 0 || tmp == 0x9068ffffU);
|
rng.SetKey(seed.begin(), 32);
|
||||||
Rz = tmp;
|
|
||||||
do {
|
|
||||||
GetRandBytes((unsigned char*)&tmp, 4);
|
|
||||||
} while (tmp == 0 || tmp == 0x464fffffU);
|
|
||||||
Rw = tmp;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Random_SanityCheck()
|
bool Random_SanityCheck()
|
||||||
|
@ -288,3 +282,12 @@ bool Random_SanityCheck()
|
||||||
} while (num_overwritten < NUM_OS_RANDOM_BYTES && tries < MAX_TRIES);
|
} 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 */
|
return (num_overwritten == NUM_OS_RANDOM_BYTES); /* If this failed, bailed out after too many tries */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FastRandomContext::FastRandomContext(bool fDeterministic) : requires_seed(!fDeterministic), bytebuf_size(0), bitbuf_size(0)
|
||||||
|
{
|
||||||
|
if (!fDeterministic) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
uint256 seed;
|
||||||
|
rng.SetKey(seed.begin(), 32);
|
||||||
|
}
|
||||||
|
|
80
src/random.h
80
src/random.h
|
@ -6,6 +6,8 @@
|
||||||
#ifndef BITCOIN_RANDOM_H
|
#ifndef BITCOIN_RANDOM_H
|
||||||
#define BITCOIN_RANDOM_H
|
#define BITCOIN_RANDOM_H
|
||||||
|
|
||||||
|
#include "crypto/chacha20.h"
|
||||||
|
#include "crypto/common.h"
|
||||||
#include "uint256.h"
|
#include "uint256.h"
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
@ -33,17 +35,79 @@ void GetStrongRandBytes(unsigned char* buf, int num);
|
||||||
* This class is not thread-safe.
|
* This class is not thread-safe.
|
||||||
*/
|
*/
|
||||||
class FastRandomContext {
|
class FastRandomContext {
|
||||||
public:
|
private:
|
||||||
explicit FastRandomContext(bool fDeterministic=false);
|
bool requires_seed;
|
||||||
|
ChaCha20 rng;
|
||||||
|
|
||||||
uint32_t rand32() {
|
unsigned char bytebuf[64];
|
||||||
Rz = 36969 * (Rz & 65535) + (Rz >> 16);
|
int bytebuf_size;
|
||||||
Rw = 18000 * (Rw & 65535) + (Rw >> 16);
|
|
||||||
return (Rw << 16) + Rz;
|
uint64_t bitbuf;
|
||||||
|
int bitbuf_size;
|
||||||
|
|
||||||
|
void RandomSeed();
|
||||||
|
|
||||||
|
void FillByteBuffer()
|
||||||
|
{
|
||||||
|
if (requires_seed) {
|
||||||
|
RandomSeed();
|
||||||
|
}
|
||||||
|
rng.Output(bytebuf, sizeof(bytebuf));
|
||||||
|
bytebuf_size = sizeof(bytebuf);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t Rz;
|
void FillBitBuffer()
|
||||||
uint32_t Rw;
|
{
|
||||||
|
bitbuf = rand64();
|
||||||
|
bitbuf_size = 64;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit FastRandomContext(bool fDeterministic = false);
|
||||||
|
|
||||||
|
/** Initialize with explicit seed (only for testing) */
|
||||||
|
explicit FastRandomContext(const uint256& seed);
|
||||||
|
|
||||||
|
/** Generate a random 64-bit integer. */
|
||||||
|
uint64_t rand64()
|
||||||
|
{
|
||||||
|
if (bytebuf_size < 8) FillByteBuffer();
|
||||||
|
uint64_t ret = ReadLE64(bytebuf + 64 - bytebuf_size);
|
||||||
|
bytebuf_size -= 8;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Generate a random (bits)-bit integer. */
|
||||||
|
uint64_t randbits(int bits) {
|
||||||
|
if (bits == 0) {
|
||||||
|
return 0;
|
||||||
|
} else if (bits > 32) {
|
||||||
|
return rand64() >> (64 - bits);
|
||||||
|
} else {
|
||||||
|
if (bitbuf_size < bits) FillBitBuffer();
|
||||||
|
uint64_t ret = bitbuf & (~(uint64_t)0 >> (64 - bits));
|
||||||
|
bitbuf >>= bits;
|
||||||
|
bitbuf_size -= bits;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Generate a random integer in the range [0..range). */
|
||||||
|
uint64_t randrange(uint64_t range)
|
||||||
|
{
|
||||||
|
--range;
|
||||||
|
int bits = CountBits(range);
|
||||||
|
while (true) {
|
||||||
|
uint64_t ret = randbits(bits);
|
||||||
|
if (ret <= range) return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Generate a random 32-bit integer. */
|
||||||
|
uint32_t rand32() { return randbits(32); }
|
||||||
|
|
||||||
|
/** Generate a random boolean. */
|
||||||
|
bool randbool() { return randbits(1); }
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Number of random bytes returned by GetOSRand.
|
/* Number of random bytes returned by GetOSRand.
|
||||||
|
|
|
@ -203,10 +203,11 @@ BOOST_AUTO_TEST_CASE(addrman_select)
|
||||||
BOOST_CHECK(addrman.size() == 7);
|
BOOST_CHECK(addrman.size() == 7);
|
||||||
|
|
||||||
// Test 12: Select pulls from new and tried regardless of port number.
|
// Test 12: Select pulls from new and tried regardless of port number.
|
||||||
BOOST_CHECK(addrman.Select().ToString() == "250.4.6.6:8333");
|
std::set<uint16_t> ports;
|
||||||
BOOST_CHECK(addrman.Select().ToString() == "250.3.2.2:9999");
|
for (int i = 0; i < 20; ++i) {
|
||||||
BOOST_CHECK(addrman.Select().ToString() == "250.3.3.3:9999");
|
ports.insert(addrman.Select().GetPort());
|
||||||
BOOST_CHECK(addrman.Select().ToString() == "250.4.4.4:8333");
|
}
|
||||||
|
BOOST_CHECK_EQUAL(ports.size(), 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(addrman_new_collisions)
|
BOOST_AUTO_TEST_CASE(addrman_new_collisions)
|
||||||
|
|
|
@ -3,12 +3,14 @@
|
||||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
#include "crypto/aes.h"
|
#include "crypto/aes.h"
|
||||||
|
#include "crypto/chacha20.h"
|
||||||
#include "crypto/ripemd160.h"
|
#include "crypto/ripemd160.h"
|
||||||
#include "crypto/sha1.h"
|
#include "crypto/sha1.h"
|
||||||
#include "crypto/sha256.h"
|
#include "crypto/sha256.h"
|
||||||
#include "crypto/sha512.h"
|
#include "crypto/sha512.h"
|
||||||
#include "crypto/hmac_sha256.h"
|
#include "crypto/hmac_sha256.h"
|
||||||
#include "crypto/hmac_sha512.h"
|
#include "crypto/hmac_sha512.h"
|
||||||
|
#include "random.h"
|
||||||
#include "utilstrencodings.h"
|
#include "utilstrencodings.h"
|
||||||
#include "test/test_bitcoin.h"
|
#include "test/test_bitcoin.h"
|
||||||
#include "test/test_random.h"
|
#include "test/test_random.h"
|
||||||
|
@ -187,6 +189,19 @@ void TestAES256CBC(const std::string &hexkey, const std::string &hexiv, bool pad
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TestChaCha20(const std::string &hexkey, uint64_t nonce, uint64_t seek, const std::string& hexout)
|
||||||
|
{
|
||||||
|
std::vector<unsigned char> key = ParseHex(hexkey);
|
||||||
|
ChaCha20 rng(key.data(), key.size());
|
||||||
|
rng.SetIV(nonce);
|
||||||
|
rng.Seek(seek);
|
||||||
|
std::vector<unsigned char> out = ParseHex(hexout);
|
||||||
|
std::vector<unsigned char> outres;
|
||||||
|
outres.resize(out.size());
|
||||||
|
rng.Output(outres.data(), outres.size());
|
||||||
|
BOOST_CHECK(out == outres);
|
||||||
|
}
|
||||||
|
|
||||||
std::string LongTestString(void) {
|
std::string LongTestString(void) {
|
||||||
std::string ret;
|
std::string ret;
|
||||||
for (int i=0; i<200000; i++) {
|
for (int i=0; i<200000; i++) {
|
||||||
|
@ -439,4 +454,57 @@ BOOST_AUTO_TEST_CASE(aes_cbc_testvectors) {
|
||||||
"b2eb05e2c39be9fcda6c19078c6a9d1b3f461796d6b0d6b2e0c2a72b4d80e644");
|
"b2eb05e2c39be9fcda6c19078c6a9d1b3f461796d6b0d6b2e0c2a72b4d80e644");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(chacha20_testvector)
|
||||||
|
{
|
||||||
|
// Test vector from RFC 7539
|
||||||
|
TestChaCha20("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", 0x4a000000UL, 1,
|
||||||
|
"224f51f3401bd9e12fde276fb8631ded8c131f823d2c06e27e4fcaec9ef3cf788a3b0aa372600a92b57974cded2b9334794cb"
|
||||||
|
"a40c63e34cdea212c4cf07d41b769a6749f3f630f4122cafe28ec4dc47e26d4346d70b98c73f3e9c53ac40c5945398b6eda1a"
|
||||||
|
"832c89c167eacd901d7e2bf363");
|
||||||
|
|
||||||
|
// Test vectors from https://tools.ietf.org/html/draft-agl-tls-chacha20poly1305-04#section-7
|
||||||
|
TestChaCha20("0000000000000000000000000000000000000000000000000000000000000000", 0, 0,
|
||||||
|
"76b8e0ada0f13d90405d6ae55386bd28bdd219b8a08ded1aa836efcc8b770dc7da41597c5157488d7724e03fb8d84a376a43b"
|
||||||
|
"8f41518a11cc387b669b2ee6586");
|
||||||
|
TestChaCha20("0000000000000000000000000000000000000000000000000000000000000001", 0, 0,
|
||||||
|
"4540f05a9f1fb296d7736e7b208e3c96eb4fe1834688d2604f450952ed432d41bbe2a0b6ea7566d2a5d1e7e20d42af2c53d79"
|
||||||
|
"2b1c43fea817e9ad275ae546963");
|
||||||
|
TestChaCha20("0000000000000000000000000000000000000000000000000000000000000000", 0x0100000000000000ULL, 0,
|
||||||
|
"de9cba7bf3d69ef5e786dc63973f653a0b49e015adbff7134fcb7df137821031e85a050278a7084527214f73efc7fa5b52770"
|
||||||
|
"62eb7a0433e445f41e3");
|
||||||
|
TestChaCha20("0000000000000000000000000000000000000000000000000000000000000000", 1, 0,
|
||||||
|
"ef3fdfd6c61578fbf5cf35bd3dd33b8009631634d21e42ac33960bd138e50d32111e4caf237ee53ca8ad6426194a88545ddc4"
|
||||||
|
"97a0b466e7d6bbdb0041b2f586b");
|
||||||
|
TestChaCha20("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", 0x0706050403020100ULL, 0,
|
||||||
|
"f798a189f195e66982105ffb640bb7757f579da31602fc93ec01ac56f85ac3c134a4547b733b46413042c9440049176905d3b"
|
||||||
|
"e59ea1c53f15916155c2be8241a38008b9a26bc35941e2444177c8ade6689de95264986d95889fb60e84629c9bd9a5acb1cc1"
|
||||||
|
"18be563eb9b3a4a472f82e09a7e778492b562ef7130e88dfe031c79db9d4f7c7a899151b9a475032b63fc385245fe054e3dd5"
|
||||||
|
"a97a5f576fe064025d3ce042c566ab2c507b138db853e3d6959660996546cc9c4a6eafdc777c040d70eaf46f76dad3979e5c5"
|
||||||
|
"360c3317166a1c894c94a371876a94df7628fe4eaaf2ccb27d5aaae0ad7ad0f9d4b6ad3b54098746d4524d38407a6deb3ab78"
|
||||||
|
"fab78c9");
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(countbits_tests)
|
||||||
|
{
|
||||||
|
FastRandomContext ctx;
|
||||||
|
for (int i = 0; i <= 64; ++i) {
|
||||||
|
if (i == 0) {
|
||||||
|
// Check handling of zero.
|
||||||
|
BOOST_CHECK_EQUAL(CountBits(0), 0);
|
||||||
|
} else if (i < 10) {
|
||||||
|
for (uint64_t j = 1 << (i - 1); (j >> i) == 0; ++j) {
|
||||||
|
// Exhaustively test up to 10 bits
|
||||||
|
BOOST_CHECK_EQUAL(CountBits(j), i);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (int k = 0; k < 1000; k++) {
|
||||||
|
// Randomly test 1000 samples of each length above 10 bits.
|
||||||
|
uint64_t j = ((uint64_t)1) << (i - 1) | ctx.randbits(i - 1);
|
||||||
|
BOOST_CHECK_EQUAL(CountBits(j), i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_SUITE_END()
|
BOOST_AUTO_TEST_SUITE_END()
|
||||||
|
|
|
@ -28,6 +28,7 @@ class prevector_tester {
|
||||||
typedef typename pretype::size_type Size;
|
typedef typename pretype::size_type Size;
|
||||||
bool passed = true;
|
bool passed = true;
|
||||||
FastRandomContext rand_cache;
|
FastRandomContext rand_cache;
|
||||||
|
uint256 rand_seed;
|
||||||
|
|
||||||
|
|
||||||
template <typename A, typename B>
|
template <typename A, typename B>
|
||||||
|
@ -183,13 +184,12 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
~prevector_tester() {
|
~prevector_tester() {
|
||||||
BOOST_CHECK_MESSAGE(passed, "insecure_rand_Rz: "
|
BOOST_CHECK_MESSAGE(passed, "insecure_rand: " + rand_seed.ToString());
|
||||||
<< rand_cache.Rz
|
|
||||||
<< ", insecure_rand_Rw: "
|
|
||||||
<< rand_cache.Rw);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
prevector_tester() {
|
prevector_tester() {
|
||||||
seed_insecure_rand();
|
seed_insecure_rand();
|
||||||
|
rand_seed = insecure_rand_seed;
|
||||||
rand_cache = insecure_rand_ctx;
|
rand_cache = insecure_rand_ctx;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -15,5 +15,39 @@ BOOST_AUTO_TEST_CASE(osrandom_tests)
|
||||||
BOOST_CHECK(Random_SanityCheck());
|
BOOST_CHECK(Random_SanityCheck());
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_SUITE_END()
|
BOOST_AUTO_TEST_CASE(fastrandom_tests)
|
||||||
|
{
|
||||||
|
// Check that deterministic FastRandomContexts are deterministic
|
||||||
|
FastRandomContext ctx1(true);
|
||||||
|
FastRandomContext ctx2(true);
|
||||||
|
|
||||||
|
BOOST_CHECK_EQUAL(ctx1.rand32(), ctx2.rand32());
|
||||||
|
BOOST_CHECK_EQUAL(ctx1.rand32(), ctx2.rand32());
|
||||||
|
BOOST_CHECK_EQUAL(ctx1.rand64(), ctx2.rand64());
|
||||||
|
BOOST_CHECK_EQUAL(ctx1.randbits(3), ctx2.randbits(3));
|
||||||
|
BOOST_CHECK_EQUAL(ctx1.randbits(7), ctx2.randbits(7));
|
||||||
|
BOOST_CHECK_EQUAL(ctx1.rand32(), ctx2.rand32());
|
||||||
|
BOOST_CHECK_EQUAL(ctx1.randbits(3), ctx2.randbits(3));
|
||||||
|
|
||||||
|
// Check that a nondeterministic ones are not
|
||||||
|
FastRandomContext ctx3;
|
||||||
|
FastRandomContext ctx4;
|
||||||
|
BOOST_CHECK(ctx3.rand64() != ctx4.rand64()); // extremely unlikely to be equal
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(fastrandom_randbits)
|
||||||
|
{
|
||||||
|
FastRandomContext ctx1;
|
||||||
|
FastRandomContext ctx2;
|
||||||
|
for (int bits = 0; bits < 63; ++bits) {
|
||||||
|
for (int j = 0; j < 1000; ++j) {
|
||||||
|
uint64_t rangebits = ctx1.randbits(bits);
|
||||||
|
BOOST_CHECK_EQUAL(rangebits >> bits, 0);
|
||||||
|
uint64_t range = ((uint64_t)1) << bits | rangebits;
|
||||||
|
uint64_t rand = ctx2.randrange(range);
|
||||||
|
BOOST_CHECK(rand < range);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_SUITE_END()
|
||||||
|
|
|
@ -27,7 +27,8 @@
|
||||||
|
|
||||||
#include <boost/thread.hpp>
|
#include <boost/thread.hpp>
|
||||||
|
|
||||||
FastRandomContext insecure_rand_ctx(true);
|
uint256 insecure_rand_seed = GetRandHash();
|
||||||
|
FastRandomContext insecure_rand_ctx(insecure_rand_seed);
|
||||||
|
|
||||||
extern bool fPrintToConsole;
|
extern bool fPrintToConsole;
|
||||||
extern void noui_connect();
|
extern void noui_connect();
|
||||||
|
|
|
@ -8,11 +8,17 @@
|
||||||
|
|
||||||
#include "random.h"
|
#include "random.h"
|
||||||
|
|
||||||
|
extern uint256 insecure_rand_seed;
|
||||||
extern FastRandomContext insecure_rand_ctx;
|
extern FastRandomContext insecure_rand_ctx;
|
||||||
|
|
||||||
static inline void seed_insecure_rand(bool fDeterministic = false)
|
static inline void seed_insecure_rand(bool fDeterministic = false)
|
||||||
{
|
{
|
||||||
insecure_rand_ctx = FastRandomContext(fDeterministic);
|
if (fDeterministic) {
|
||||||
|
insecure_rand_seed = uint256();
|
||||||
|
} else {
|
||||||
|
insecure_rand_seed = GetRandHash();
|
||||||
|
}
|
||||||
|
insecure_rand_ctx = FastRandomContext(insecure_rand_seed);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline uint32_t insecure_rand(void)
|
static inline uint32_t insecure_rand(void)
|
||||||
|
|
|
@ -2095,7 +2095,7 @@ static void ApproximateBestSubset(const std::vector<CInputCoin>& vValue, const C
|
||||||
//that the rng is fast. We do not use a constant random sequence,
|
//that the rng is fast. We do not use a constant random sequence,
|
||||||
//because there may be some privacy improvement by making
|
//because there may be some privacy improvement by making
|
||||||
//the selection random.
|
//the selection random.
|
||||||
if (nPass == 0 ? insecure_rand.rand32()&1 : !vfIncluded[i])
|
if (nPass == 0 ? insecure_rand.randbool() : !vfIncluded[i])
|
||||||
{
|
{
|
||||||
nTotal += vValue[i].txout.nValue;
|
nTotal += vValue[i].txout.nValue;
|
||||||
vfIncluded[i] = true;
|
vfIncluded[i] = true;
|
||||||
|
|
Loading…
Reference in a new issue