Browse Source

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
0.15
Wladimir J. van der Laan 8 years ago
parent
commit
342b9bc390
No known key found for this signature in database
GPG Key ID: 74810B012346C9A6
  1. 2
      configure.ac
  2. 2
      src/Makefile.am
  3. 2
      src/Makefile.test.include
  4. 8
      src/addrman.cpp
  5. 11
      src/addrman.h
  6. 2
      src/bench/checkqueue.cpp
  7. 25
      src/bench/crypto_hash.cpp
  8. 180
      src/crypto/chacha20.cpp
  9. 26
      src/crypto/chacha20.h
  10. 21
      src/crypto/common.h
  11. 2
      src/net.h
  12. 33
      src/random.cpp
  13. 78
      src/random.h
  14. 9
      src/test/addrman_tests.cpp
  15. 68
      src/test/crypto_tests.cpp
  16. 8
      src/test/prevector_tests.cpp
  17. 36
      src/test/random_tests.cpp
  18. 3
      src/test/test_bitcoin.cpp
  19. 8
      src/test/test_random.h
  20. 2
      src/wallet/wallet.cpp

2
configure.ac

@ -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>]],

2
src/Makefile.am

@ -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 \

2
src/Makefile.test.include

@ -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 \

8
src/addrman.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);

11
src/addrman.h

@ -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
*/ */

2
src/bench/checkqueue.cpp

@ -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()()
{ {

25
src/bench/crypto_hash.cpp

@ -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

@ -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

@ -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

21
src/crypto/common.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

2
src/net.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);
} }

33
src/random.cpp

@ -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);
}

78
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 {
private:
bool requires_seed;
ChaCha20 rng;
unsigned char bytebuf[64];
int bytebuf_size;
uint64_t bitbuf;
int bitbuf_size;
void RandomSeed();
void FillByteBuffer()
{
if (requires_seed) {
RandomSeed();
}
rng.Output(bytebuf, sizeof(bytebuf));
bytebuf_size = sizeof(bytebuf);
}
void FillBitBuffer()
{
bitbuf = rand64();
bitbuf_size = 64;
}
public: public:
explicit FastRandomContext(bool fDeterministic=false); explicit FastRandomContext(bool fDeterministic = false);
uint32_t rand32() { /** Initialize with explicit seed (only for testing) */
Rz = 36969 * (Rz & 65535) + (Rz >> 16); explicit FastRandomContext(const uint256& seed);
Rw = 18000 * (Rw & 65535) + (Rw >> 16);
return (Rw << 16) + Rz; /** 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;
}
} }
uint32_t Rz; /** Generate a random integer in the range [0..range). */
uint32_t Rw; 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.

9
src/test/addrman_tests.cpp

@ -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)

68
src/test/crypto_tests.cpp

@ -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()

8
src/test/prevector_tests.cpp

@ -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;
} }
}; };

36
src/test/random_tests.cpp

@ -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()

3
src/test/test_bitcoin.cpp

@ -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
src/test/test_random.h

@ -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)

2
src/wallet/wallet.cpp

@ -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…
Cancel
Save