Line data Source code
1 : // Copyright (c) 2009-2010 Satoshi Nakamoto
2 : // Copyright (c) 2009-2014 The Bitcoin Core developers
3 : // Distributed under the MIT software license, see the accompanying
4 : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
5 :
6 : #include "random.h"
7 :
8 : #include "support/cleanse.h"
9 : #ifdef WIN32
10 : #include "compat.h" // for Windows API
11 : #endif
12 : #include "serialize.h" // for begin_ptr(vec)
13 : #include "util.h" // for LogPrint()
14 : #include "utilstrencodings.h" // for GetTime()
15 :
16 : #include <limits>
17 :
18 : #ifndef WIN32
19 : #include <sys/time.h>
20 : #endif
21 :
22 : #include <openssl/err.h>
23 : #include <openssl/rand.h>
24 :
25 : static inline int64_t GetPerformanceCounter()
26 : {
27 22743 : int64_t nCounter = 0;
28 : #ifdef WIN32
29 : QueryPerformanceCounter((LARGE_INTEGER*)&nCounter);
30 : #else
31 : timeval t;
32 22743 : gettimeofday(&t, NULL);
33 22743 : nCounter = (int64_t)(t.tv_sec * 1000000 + t.tv_usec);
34 : #endif
35 : return nCounter;
36 : }
37 :
38 22743 : void RandAddSeed()
39 : {
40 : // Seed with CPU performance counter
41 22743 : int64_t nCounter = GetPerformanceCounter();
42 22743 : RAND_add(&nCounter, sizeof(nCounter), 1.5);
43 22743 : memory_cleanse((void*)&nCounter, sizeof(nCounter));
44 22743 : }
45 :
46 22540 : void RandAddSeedPerfmon()
47 : {
48 22540 : RandAddSeed();
49 :
50 : #ifdef WIN32
51 : // Don't need this on Linux, OpenSSL automatically uses /dev/urandom
52 : // Seed with the entire set of perfmon data
53 :
54 : // This can take up to 2 seconds, so only do it every 10 minutes
55 : static int64_t nLastPerfmon;
56 : if (GetTime() < nLastPerfmon + 10 * 60)
57 : return;
58 : nLastPerfmon = GetTime();
59 :
60 : std::vector<unsigned char> vData(250000, 0);
61 : long ret = 0;
62 : unsigned long nSize = 0;
63 : const size_t nMaxSize = 10000000; // Bail out at more than 10MB of performance data
64 : while (true) {
65 : nSize = vData.size();
66 : ret = RegQueryValueExA(HKEY_PERFORMANCE_DATA, "Global", NULL, NULL, begin_ptr(vData), &nSize);
67 : if (ret != ERROR_MORE_DATA || vData.size() >= nMaxSize)
68 : break;
69 : vData.resize(std::max((vData.size() * 3) / 2, nMaxSize)); // Grow size of buffer exponentially
70 : }
71 : RegCloseKey(HKEY_PERFORMANCE_DATA);
72 : if (ret == ERROR_SUCCESS) {
73 : RAND_add(begin_ptr(vData), nSize, nSize / 100.0);
74 : memory_cleanse(begin_ptr(vData), nSize);
75 : LogPrint("rand", "%s: %lu bytes\n", __func__, nSize);
76 : } else {
77 : static bool warned = false; // Warn only once
78 : if (!warned) {
79 : LogPrintf("%s: Warning: RegQueryValueExA(HKEY_PERFORMANCE_DATA) failed with code %i\n", __func__, ret);
80 : warned = true;
81 : }
82 : }
83 : #endif
84 22540 : }
85 :
86 537940 : void GetRandBytes(unsigned char* buf, int num)
87 : {
88 537940 : if (RAND_bytes(buf, num) != 1) {
89 0 : LogPrintf("%s: OpenSSL RAND_bytes() failed with error: %s\n", __func__, ERR_error_string(ERR_get_error(), NULL));
90 0 : assert(false);
91 : }
92 537940 : }
93 :
94 367913 : uint64_t GetRand(uint64_t nMax)
95 : {
96 367913 : if (nMax == 0)
97 : return 0;
98 :
99 : // The range of the random source must be a multiple of the modulus
100 : // to give every possible output value an equal possibility
101 367913 : uint64_t nRange = (std::numeric_limits<uint64_t>::max() / nMax) * nMax;
102 367913 : uint64_t nRand = 0;
103 367913 : do {
104 367913 : GetRandBytes((unsigned char*)&nRand, sizeof(nRand));
105 367913 : } while (nRand >= nRange);
106 367913 : return (nRand % nMax);
107 : }
108 :
109 342008 : int GetRandInt(int nMax)
110 : {
111 342008 : return GetRand(nMax);
112 : }
113 :
114 161612 : uint256 GetRandHash()
115 : {
116 : uint256 hash;
117 161612 : GetRandBytes((unsigned char*)&hash, sizeof(hash));
118 161612 : return hash;
119 : }
120 :
121 : uint32_t insecure_rand_Rz = 11;
122 : uint32_t insecure_rand_Rw = 11;
123 2275 : void seed_insecure_rand(bool fDeterministic)
124 : {
125 : // The seed values have some unlikely fixed points which we avoid.
126 2275 : if (fDeterministic) {
127 1 : insecure_rand_Rz = insecure_rand_Rw = 11;
128 : } else {
129 : uint32_t tmp;
130 2274 : do {
131 2274 : GetRandBytes((unsigned char*)&tmp, 4);
132 2274 : } while (tmp == 0 || tmp == 0x9068ffffU);
133 2274 : insecure_rand_Rz = tmp;
134 2274 : do {
135 2274 : GetRandBytes((unsigned char*)&tmp, 4);
136 2274 : } while (tmp == 0 || tmp == 0x464fffffU);
137 2274 : insecure_rand_Rw = tmp;
138 : }
139 2884 : }
|