Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F13115184
D490.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
25 KB
Subscribers
None
D490.diff
View Options
diff --git a/configure.ac b/configure.ac
--- a/configure.ac
+++ b/configure.ac
@@ -606,6 +606,8 @@
#include <byteswap.h>
#endif])
+AC_CHECK_DECLS([__builtin_clz, __builtin_clzl, __builtin_clzll])
+
dnl Check for MSG_NOSIGNAL
AC_MSG_CHECKING(for MSG_NOSIGNAL)
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <sys/socket.h>]],
diff --git a/src/Makefile.am b/src/Makefile.am
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -250,6 +250,8 @@
crypto_libbitcoin_crypto_a_SOURCES = \
crypto/aes.cpp \
crypto/aes.h \
+ crypto/chacha20.h \
+ crypto/chacha20.cpp \
crypto/common.h \
crypto/hmac_sha256.cpp \
crypto/hmac_sha256.h \
diff --git a/src/addrman.h b/src/addrman.h
--- a/src/addrman.h
+++ b/src/addrman.h
@@ -143,13 +143,13 @@
*/
//! 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
-#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
-#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
@@ -181,6 +181,11 @@
//! the maximum number of nodes to return in a getaddr call
#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
*/
diff --git a/src/addrman.cpp b/src/addrman.cpp
--- a/src/addrman.cpp
+++ b/src/addrman.cpp
@@ -349,17 +349,22 @@
int nKBucket = RandomInt(ADDRMAN_TRIED_BUCKET_COUNT);
int nKBucketPos = RandomInt(ADDRMAN_BUCKET_SIZE);
while (vvTried[nKBucket][nKBucketPos] == -1) {
- nKBucket = (nKBucket + insecure_rand.rand32()) %
- ADDRMAN_TRIED_BUCKET_COUNT;
- nKBucketPos = (nKBucketPos + insecure_rand.rand32()) %
- ADDRMAN_BUCKET_SIZE;
+ nKBucket =
+ (nKBucket +
+ insecure_rand.randbits(ADDRMAN_TRIED_BUCKET_COUNT_LOG2)) %
+ ADDRMAN_TRIED_BUCKET_COUNT;
+ nKBucketPos =
+ (nKBucketPos +
+ insecure_rand.randbits(ADDRMAN_BUCKET_SIZE_LOG2)) %
+ ADDRMAN_BUCKET_SIZE;
}
int nId = vvTried[nKBucket][nKBucketPos];
assert(mapInfo.count(nId) == 1);
CAddrInfo &info = mapInfo[nId];
if (RandomInt(1 << 30) <
- fChanceFactor * info.GetChance() * (1 << 30))
+ fChanceFactor * info.GetChance() * (1 << 30)) {
return info;
+ }
fChanceFactor *= 1.2;
}
} else {
@@ -369,10 +374,14 @@
int nUBucket = RandomInt(ADDRMAN_NEW_BUCKET_COUNT);
int nUBucketPos = RandomInt(ADDRMAN_BUCKET_SIZE);
while (vvNew[nUBucket][nUBucketPos] == -1) {
- nUBucket = (nUBucket + insecure_rand.rand32()) %
- ADDRMAN_NEW_BUCKET_COUNT;
- nUBucketPos = (nUBucketPos + insecure_rand.rand32()) %
- ADDRMAN_BUCKET_SIZE;
+ nUBucket =
+ (nUBucket +
+ insecure_rand.randbits(ADDRMAN_NEW_BUCKET_COUNT_LOG2)) %
+ ADDRMAN_NEW_BUCKET_COUNT;
+ nUBucketPos =
+ (nUBucketPos +
+ insecure_rand.randbits(ADDRMAN_BUCKET_SIZE_LOG2)) %
+ ADDRMAN_BUCKET_SIZE;
}
int nId = vvNew[nUBucket][nUBucketPos];
assert(mapInfo.count(nId) == 1);
diff --git a/src/bench/checkqueue.cpp b/src/bench/checkqueue.cpp
--- a/src/bench/checkqueue.cpp
+++ b/src/bench/checkqueue.cpp
@@ -62,7 +62,7 @@
prevector<PREVECTOR_SIZE, uint8_t> p;
PrevectorJob() {}
PrevectorJob(FastRandomContext &insecure_rand) {
- p.resize(insecure_rand.rand32() % (PREVECTOR_SIZE * 2));
+ p.resize(insecure_rand.randrange(PREVECTOR_SIZE * 2));
}
bool operator()() { return true; }
void swap(PrevectorJob &x) { p.swap(x.p); };
diff --git a/src/bench/crypto_hash.cpp b/src/bench/crypto_hash.cpp
--- a/src/bench/crypto_hash.cpp
+++ b/src/bench/crypto_hash.cpp
@@ -11,6 +11,7 @@
#include "crypto/sha256.h"
#include "crypto/sha512.h"
#include "hash.h"
+#include "random.h"
#include "uint256.h"
#include "utiltime.h"
@@ -63,6 +64,26 @@
}
}
+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(SHA1);
BENCHMARK(SHA256);
@@ -70,3 +91,5 @@
BENCHMARK(SHA256_32b);
BENCHMARK(SipHash_32b);
+BENCHMARK(FastRandom_32bit);
+BENCHMARK(FastRandom_1bit);
diff --git a/src/crypto/chacha20.h b/src/crypto/chacha20.h
new file mode 100644
--- /dev/null
+++ b/src/crypto/chacha20.h
@@ -0,0 +1,25 @@
+// 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 <cstdint>
+#include <cstdlib>
+
+/** A PRNG class for ChaCha20. */
+class ChaCha20 {
+private:
+ uint32_t input[16];
+
+public:
+ ChaCha20();
+ ChaCha20(const uint8_t *key, size_t keylen);
+ void SetKey(const uint8_t *key, size_t keylen);
+ void SetIV(uint64_t iv);
+ void Seek(uint64_t pos);
+ void Output(uint8_t *output, size_t bytes);
+};
+
+#endif // BITCOIN_CRYPTO_CHACHA20_H
diff --git a/src/crypto/chacha20.cpp b/src/crypto/chacha20.cpp
new file mode 100644
--- /dev/null
+++ b/src/crypto/chacha20.cpp
@@ -0,0 +1,190 @@
+// 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/chacha20.h"
+#include "crypto/common.h"
+
+#include <cstring>
+
+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 uint8_t sigma[] = "expand 32-byte k";
+static const uint8_t tau[] = "expand 16-byte k";
+
+void ChaCha20::SetKey(const uint8_t *k, size_t keylen) {
+ const uint8_t *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 uint8_t *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(uint8_t *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;
+ uint8_t *ctarget = nullptr;
+ uint8_t 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;
+ }
+}
diff --git a/src/crypto/common.h b/src/crypto/common.h
--- a/src/crypto/common.h
+++ b/src/crypto/common.h
@@ -69,4 +69,27 @@
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
diff --git a/src/net.h b/src/net.h
--- a/src/net.h
+++ b/src/net.h
@@ -778,7 +778,7 @@
// after addresses were pushed.
if (_addr.IsValid() && !addrKnown.contains(_addr.GetKey())) {
if (vAddrToSend.size() >= MAX_ADDR_TO_SEND) {
- vAddrToSend[insecure_rand.rand32() % vAddrToSend.size()] =
+ vAddrToSend[insecure_rand.randrange(vAddrToSend.size())] =
_addr;
} else {
vAddrToSend.push_back(_addr);
diff --git a/src/random.h b/src/random.h
--- a/src/random.h
+++ b/src/random.h
@@ -6,6 +6,8 @@
#ifndef BITCOIN_RANDOM_H
#define BITCOIN_RANDOM_H
+#include "crypto/chacha20.h"
+#include "crypto/common.h"
#include "uint256.h"
#include <cstdint>
@@ -33,17 +35,81 @@
* This class is not thread-safe.
*/
class FastRandomContext {
+private:
+ bool requires_seed;
+ ChaCha20 rng;
+
+ uint8_t 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:
explicit FastRandomContext(bool fDeterministic = false);
- uint32_t rand32() {
- Rz = 36969 * (Rz & 65535) + (Rz >> 16);
- Rw = 18000 * (Rw & 65535) + (Rw >> 16);
- return (Rw << 16) + Rz;
+ /** 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;
+ }
}
- uint32_t Rz;
- uint32_t Rw;
+ /** 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); }
};
/**
diff --git a/src/random.cpp b/src/random.cpp
--- a/src/random.cpp
+++ b/src/random.cpp
@@ -239,21 +239,15 @@
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((uint8_t *)&tmp, 4);
- } while (tmp == 0 || tmp == 0x9068ffffU);
- Rz = tmp;
- do {
- GetRandBytes((uint8_t *)&tmp, 4);
- } while (tmp == 0 || tmp == 0x464fffffU);
- Rw = tmp;
- }
+void FastRandomContext::RandomSeed() {
+ uint256 seed = GetRandHash();
+ rng.SetKey(seed.begin(), 32);
+ requires_seed = false;
+}
+
+FastRandomContext::FastRandomContext(const uint256 &seed)
+ : requires_seed(false), bytebuf_size(0), bitbuf_size(0) {
+ rng.SetKey(seed.begin(), 32);
}
bool Random_SanityCheck() {
@@ -288,3 +282,12 @@
/* If this failed, bailed out after too many tries */
return (num_overwritten == NUM_OS_RANDOM_BYTES);
}
+
+FastRandomContext::FastRandomContext(bool fDeterministic)
+ : requires_seed(!fDeterministic), bytebuf_size(0), bitbuf_size(0) {
+ if (!fDeterministic) {
+ return;
+ }
+ uint256 seed;
+ rng.SetKey(seed.begin(), 32);
+}
diff --git a/src/test/addrman_tests.cpp b/src/test/addrman_tests.cpp
--- a/src/test/addrman_tests.cpp
+++ b/src/test/addrman_tests.cpp
@@ -185,10 +185,11 @@
BOOST_CHECK(addrman.size() == 7);
// Test 12: Select pulls from new and tried regardless of port number.
- BOOST_CHECK(addrman.Select().ToString() == "250.4.6.6:8333");
- BOOST_CHECK(addrman.Select().ToString() == "250.3.2.2:9999");
- BOOST_CHECK(addrman.Select().ToString() == "250.3.3.3:9999");
- BOOST_CHECK(addrman.Select().ToString() == "250.4.4.4:8333");
+ std::set<uint16_t> ports;
+ for (int i = 0; i < 20; ++i) {
+ ports.insert(addrman.Select().GetPort());
+ }
+ BOOST_CHECK_EQUAL(ports.size(), 3);
}
BOOST_AUTO_TEST_CASE(addrman_new_collisions) {
diff --git a/src/test/crypto_tests.cpp b/src/test/crypto_tests.cpp
--- a/src/test/crypto_tests.cpp
+++ b/src/test/crypto_tests.cpp
@@ -3,12 +3,14 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "crypto/aes.h"
+#include "crypto/chacha20.h"
#include "crypto/hmac_sha256.h"
#include "crypto/hmac_sha512.h"
#include "crypto/ripemd160.h"
#include "crypto/sha1.h"
#include "crypto/sha256.h"
#include "crypto/sha512.h"
+#include "random.h"
#include "test/test_bitcoin.h"
#include "test/test_random.h"
#include "utilstrencodings.h"
@@ -210,6 +212,19 @@
}
}
+void TestChaCha20(const std::string &hexkey, uint64_t nonce, uint64_t seek,
+ const std::string &hexout) {
+ std::vector<uint8_t> key = ParseHex(hexkey);
+ ChaCha20 rng(key.data(), key.size());
+ rng.SetIV(nonce);
+ rng.Seek(seek);
+ std::vector<uint8_t> out = ParseHex(hexout);
+ std::vector<uint8_t> outres;
+ outres.resize(out.size());
+ rng.Output(outres.data(), outres.size());
+ BOOST_CHECK(out == outres);
+}
+
std::string LongTestString(void) {
std::string ret;
for (int i = 0; i < 200000; i++) {
@@ -550,4 +565,67 @@
"b2eb05e2c39be9fcda6c19078c6a9d1b3f461796d6b0d6b2e0c2a72b4d80e644");
}
+BOOST_AUTO_TEST_CASE(chacha20_testvector) {
+ // Test vector from RFC 7539
+ TestChaCha20(
+ "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f",
+ 0x4a000000UL, 1, "224f51f3401bd9e12fde276fb8631ded8c131f823d2c06e27e4fc"
+ "aec9ef3cf788a3b0aa372600a92b57974cded2b9334794cba40c6"
+ "3e34cdea212c4cf07d41b769a6749f3f630f4122cafe28ec4dc47"
+ "e26d4346d70b98c73f3e9c53ac40c5945398b6eda1a832c89c167"
+ "eacd901d7e2bf363");
+
+ // Test vectors from
+ // https://tools.ietf.org/html/draft-agl-tls-chacha20poly1305-04#section-7
+ TestChaCha20(
+ "0000000000000000000000000000000000000000000000000000000000000000", 0,
+ 0, "76b8e0ada0f13d90405d6ae55386bd28bdd219b8a08ded1aa836efcc8b770dc7da4"
+ "1597c5157488d7724e03fb8d84a376a43b8f41518a11cc387b669b2ee6586");
+ TestChaCha20(
+ "0000000000000000000000000000000000000000000000000000000000000001", 0,
+ 0, "4540f05a9f1fb296d7736e7b208e3c96eb4fe1834688d2604f450952ed432d41bbe"
+ "2a0b6ea7566d2a5d1e7e20d42af2c53d792b1c43fea817e9ad275ae546963");
+ TestChaCha20(
+ "0000000000000000000000000000000000000000000000000000000000000000",
+ 0x0100000000000000ULL, 0, "de9cba7bf3d69ef5e786dc63973f653a0b49e015adbf"
+ "f7134fcb7df137821031e85a050278a7084527214f73"
+ "efc7fa5b5277062eb7a0433e445f41e3");
+ TestChaCha20(
+ "0000000000000000000000000000000000000000000000000000000000000000", 1,
+ 0, "ef3fdfd6c61578fbf5cf35bd3dd33b8009631634d21e42ac33960bd138e50d32111"
+ "e4caf237ee53ca8ad6426194a88545ddc497a0b466e7d6bbdb0041b2f586b");
+ TestChaCha20(
+ "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f",
+ 0x0706050403020100ULL, 0,
+ "f798a189f195e66982105ffb640bb7757f579da31602fc93ec01ac56f85ac3c134a454"
+ "7b733b46413042c9440049176905d3be59ea1c53f15916155c2be8241a38008b9a26bc"
+ "35941e2444177c8ade6689de95264986d95889fb60e84629c9bd9a5acb1cc118be563e"
+ "b9b3a4a472f82e09a7e778492b562ef7130e88dfe031c79db9d4f7c7a899151b9a4750"
+ "32b63fc385245fe054e3dd5a97a5f576fe064025d3ce042c566ab2c507b138db853e3d"
+ "6959660996546cc9c4a6eafdc777c040d70eaf46f76dad3979e5c5360c3317166a1c89"
+ "4c94a371876a94df7628fe4eaaf2ccb27d5aaae0ad7ad0f9d4b6ad3b54098746d4524d"
+ "38407a6deb3ab78fab78c9");
+}
+
+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()
diff --git a/src/test/prevector_tests.cpp b/src/test/prevector_tests.cpp
--- a/src/test/prevector_tests.cpp
+++ b/src/test/prevector_tests.cpp
@@ -28,6 +28,7 @@
typedef typename pretype::size_type Size;
bool passed = true;
FastRandomContext rand_cache;
+ uint256 rand_seed;
template <typename A, typename B> void local_check_equal(A a, B b) {
local_check(a == b);
@@ -178,13 +179,12 @@
}
~prevector_tester() {
- BOOST_CHECK_MESSAGE(passed,
- "insecure_rand_Rz: " << rand_cache.Rz
- << ", insecure_rand_Rw: "
- << rand_cache.Rw);
+ BOOST_CHECK_MESSAGE(passed, "insecure_rand: " + rand_seed.ToString());
}
+
prevector_tester() {
seed_insecure_rand();
+ rand_seed = insecure_rand_seed;
rand_cache = insecure_rand_ctx;
}
};
diff --git a/src/test/random_tests.cpp b/src/test/random_tests.cpp
--- a/src/test/random_tests.cpp
+++ b/src/test/random_tests.cpp
@@ -14,4 +14,38 @@
BOOST_CHECK(Random_SanityCheck());
}
+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;
+ // extremely unlikely to be equal
+ BOOST_CHECK(ctx3.rand64() != ctx4.rand64());
+}
+
+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()
diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp
--- a/src/test/test_bitcoin.cpp
+++ b/src/test/test_bitcoin.cpp
@@ -32,7 +32,8 @@
#include <boost/thread.hpp>
std::unique_ptr<CConnman> g_connman;
-FastRandomContext insecure_rand_ctx(true);
+uint256 insecure_rand_seed = GetRandHash();
+FastRandomContext insecure_rand_ctx(insecure_rand_seed);
extern bool fPrintToConsole;
extern void noui_connect();
diff --git a/src/test/test_random.h b/src/test/test_random.h
--- a/src/test/test_random.h
+++ b/src/test/test_random.h
@@ -8,10 +8,16 @@
#include "random.h"
+extern uint256 insecure_rand_seed;
extern FastRandomContext insecure_rand_ctx;
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) {
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -2320,7 +2320,7 @@
// degenerate behavior and it is important that the rng is fast.
// We do not use a constant random sequence, because there may
// be some privacy improvement by making the selection random.
- if (nPass == 0 ? insecure_rand.rand32() & 1 : !vfIncluded[i]) {
+ if (nPass == 0 ? insecure_rand.randbool() : !vfIncluded[i]) {
nTotal += vValue[i].first;
vfIncluded[i] = true;
if (nTotal >= nTargetValue) {
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sat, Mar 1, 10:11 (3 h, 42 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
5183032
Default Alt Text
D490.diff (25 KB)
Attached To
D490: FastRandomContext improvements and switch to ChaCha20
Event Timeline
Log In to Comment