@@ -78,25 +78,119 @@ static inline int64_t GetPerformanceCounter() noexcept
78
78
}
79
79
80
80
#if defined(__x86_64__) || defined(__amd64__) || defined(__i386__)
81
- static bool rdrand_supported = false ;
81
+ static bool g_rdrand_supported = false ;
82
+ static bool g_rdseed_supported = false ;
82
83
static constexpr uint32_t CPUID_F1_ECX_RDRAND = 0x40000000 ;
84
+ static constexpr uint32_t CPUID_F7_EBX_RDSEED = 0x00040000 ;
85
+ #ifdef bit_RDRND
86
+ static_assert (CPUID_F1_ECX_RDRAND == bit_RDRND, " Unexpected value for bit_RDRND" );
87
+ #endif
88
+ #ifdef bit_RDSEED
89
+ static_assert (CPUID_F7_EBX_RDSEED == bit_RDSEED, " Unexpected value for bit_RDSEED" );
90
+ #endif
91
+ static void inline GetCPUID (uint32_t leaf, uint32_t subleaf, uint32_t & a, uint32_t & b, uint32_t & c, uint32_t & d)
92
+ {
93
+ // We can't use __get_cpuid as it doesn't support subleafs.
94
+ #ifdef __GNUC__
95
+ __cpuid_count (leaf, subleaf, a, b, c, d);
96
+ #else
97
+ __asm__ (" cpuid" : " =a" (a), " =b" (b), " =c" (c), " =d" (d) : " 0" (leaf), " 2" (subleaf));
98
+ #endif
99
+ }
100
+
83
101
static void InitHardwareRand ()
84
102
{
85
103
uint32_t eax, ebx, ecx, edx;
86
- if (__get_cpuid (1 , &eax, &ebx, &ecx, &edx) && (ecx & CPUID_F1_ECX_RDRAND)) {
87
- rdrand_supported = true ;
104
+ GetCPUID (1 , 0 , eax, ebx, ecx, edx);
105
+ if (ecx & CPUID_F1_ECX_RDRAND) {
106
+ g_rdrand_supported = true ;
107
+ }
108
+ GetCPUID (7 , 0 , eax, ebx, ecx, edx);
109
+ if (ebx & CPUID_F7_EBX_RDSEED) {
110
+ g_rdseed_supported = true ;
88
111
}
89
112
}
90
113
91
114
static void ReportHardwareRand ()
92
115
{
93
- if (rdrand_supported) {
94
- // This must be done in a separate function, as HWRandInit() may be indirectly called
95
- // from global constructors, before logging is initialized.
116
+ // This must be done in a separate function, as HWRandInit() may be indirectly called
117
+ // from global constructors, before logging is initialized.
118
+ if (g_rdseed_supported) {
119
+ LogPrintf (" Using RdSeed as additional entropy source\n " );
120
+ }
121
+ if (g_rdrand_supported) {
96
122
LogPrintf (" Using RdRand as an additional entropy source\n " );
97
123
}
98
124
}
99
125
126
+ /* * Read 64 bits of entropy using rdrand.
127
+ *
128
+ * Must only be called when RdRand is supported.
129
+ */
130
+ static uint64_t GetRdRand () noexcept
131
+ {
132
+ // RdRand may very rarely fail. Invoke it up to 10 times in a loop to reduce this risk.
133
+ #ifdef __i386__
134
+ uint8_t ok;
135
+ uint32_t r1, r2;
136
+ for (int i = 0 ; i < 10 ; ++i) {
137
+ __asm__ volatile (" .byte 0x0f, 0xc7, 0xf0; setc %1" : " =a" (r1), " =q" (ok) :: " cc" ); // rdrand %eax
138
+ if (ok) break ;
139
+ }
140
+ for (int i = 0 ; i < 10 ; ++i) {
141
+ __asm__ volatile (" .byte 0x0f, 0xc7, 0xf0; setc %1" : " =a" (r2), " =q" (ok) :: " cc" ); // rdrand %eax
142
+ if (ok) break ;
143
+ }
144
+ return (((uint64_t )r2) << 32 ) | r1;
145
+ #elif defined(__x86_64__) || defined(__amd64__)
146
+ uint8_t ok;
147
+ uint64_t r1;
148
+ for (int i = 0 ; i < 10 ; ++i) {
149
+ __asm__ volatile (" .byte 0x48, 0x0f, 0xc7, 0xf0; setc %1" : " =a" (r1), " =q" (ok) :: " cc" ); // rdrand %rax
150
+ if (ok) break ;
151
+ }
152
+ return r1;
153
+ #else
154
+ #error "RdRand is only supported on x86 and x86_64"
155
+ #endif
156
+ }
157
+
158
+ /* * Read 64 bits of entropy using rdseed.
159
+ *
160
+ * Must only be called when RdSeed is supported.
161
+ */
162
+ static uint64_t GetRdSeed () noexcept
163
+ {
164
+ // RdSeed may fail when the HW RNG is overloaded. Loop indefinitely until enough entropy is gathered,
165
+ // but pause after every failure.
166
+ #ifdef __i386__
167
+ uint8_t ok;
168
+ uint32_t r1, r2;
169
+ do {
170
+ __asm__ volatile (" .byte 0x0f, 0xc7, 0xf8; setc %1" : " =a" (r1), " =q" (ok) :: " cc" ); // rdseed %eax
171
+ if (ok) break ;
172
+ __asm__ volatile (" pause" );
173
+ } while (true );
174
+ do {
175
+ __asm__ volatile (" .byte 0x0f, 0xc7, 0xf8; setc %1" : " =a" (r2), " =q" (ok) :: " cc" ); // rdseed %eax
176
+ if (ok) break ;
177
+ __asm__ volatile (" pause" );
178
+ } while (true );
179
+ return (((uint64_t )r2) << 32 ) | r1;
180
+ #elif defined(__x86_64__) || defined(__amd64__)
181
+ uint8_t ok;
182
+ uint64_t r1;
183
+ do {
184
+ __asm__ volatile (" .byte 0x48, 0x0f, 0xc7, 0xf8; setc %1" : " =a" (r1), " =q" (ok) :: " cc" ); // rdseed %rax
185
+ if (ok) break ;
186
+ __asm__ volatile (" pause" );
187
+ } while (true );
188
+ return r1;
189
+ #else
190
+ #error "RdSeed is only supported on x86 and x86_64"
191
+ #endif
192
+ }
193
+
100
194
#else
101
195
/* Access to other hardware random number generators could be added here later,
102
196
* assuming it is sufficiently fast (in the order of a few hundred CPU cycles).
@@ -107,40 +201,40 @@ static void InitHardwareRand() {}
107
201
static void ReportHardwareRand () {}
108
202
#endif
109
203
110
- static bool GetHardwareRand (unsigned char * ent32) noexcept {
204
+ /* * Add 64 bits of entropy gathered from hardware to hasher. Do nothing if not supported. */
205
+ static void SeedHardwareFast (CSHA512& hasher) noexcept {
111
206
#if defined(__x86_64__) || defined(__amd64__) || defined(__i386__)
112
- if (rdrand_supported) {
113
- uint8_t ok;
114
- // Not all assemblers support the rdrand instruction, write it in hex.
115
- #ifdef __i386__
116
- for (int iter = 0 ; iter < 4 ; ++iter) {
117
- uint32_t r1, r2;
118
- __asm__ volatile (" .byte 0x0f, 0xc7, 0xf0;" // rdrand %eax
119
- " .byte 0x0f, 0xc7, 0xf2;" // rdrand %edx
120
- " setc %2" :
121
- " =a" (r1), " =d" (r2), " =q" (ok) :: " cc" );
122
- if (!ok) return false ;
123
- WriteLE32 (ent32 + 8 * iter, r1);
124
- WriteLE32 (ent32 + 8 * iter + 4 , r2);
125
- }
126
- #else
127
- uint64_t r1, r2, r3, r4;
128
- __asm__ volatile (" .byte 0x48, 0x0f, 0xc7, 0xf0, " // rdrand %rax
129
- " 0x48, 0x0f, 0xc7, 0xf3, " // rdrand %rbx
130
- " 0x48, 0x0f, 0xc7, 0xf1, " // rdrand %rcx
131
- " 0x48, 0x0f, 0xc7, 0xf2; " // rdrand %rdx
132
- " setc %4" :
133
- " =a" (r1), " =b" (r2), " =c" (r3), " =d" (r4), " =q" (ok) :: " cc" );
134
- if (!ok) return false ;
135
- WriteLE64 (ent32, r1);
136
- WriteLE64 (ent32 + 8 , r2);
137
- WriteLE64 (ent32 + 16 , r3);
138
- WriteLE64 (ent32 + 24 , r4);
207
+ if (g_rdrand_supported) {
208
+ uint64_t out = GetRdRand ();
209
+ hasher.Write ((const unsigned char *)&out, sizeof (out));
210
+ return ;
211
+ }
139
212
#endif
140
- return true ;
213
+ }
214
+
215
+ /* * Add 256 bits of entropy gathered from hardware to hasher. Do nothing if not supported. */
216
+ static void SeedHardwareSlow (CSHA512& hasher) noexcept {
217
+ #if defined(__x86_64__) || defined(__amd64__) || defined(__i386__)
218
+ // When we want 256 bits of entropy, prefer RdSeed over RdRand, as it's
219
+ // guaranteed to produce independent randomness on every call.
220
+ if (g_rdseed_supported) {
221
+ for (int i = 0 ; i < 4 ; ++i) {
222
+ uint64_t out = GetRdSeed ();
223
+ hasher.Write ((const unsigned char *)&out, sizeof (out));
224
+ }
225
+ return ;
226
+ }
227
+ // When falling back to RdRand, XOR the result of 1024 results.
228
+ // This guarantees a reseeding occurs between each.
229
+ if (g_rdrand_supported) {
230
+ for (int i = 0 ; i < 4 ; ++i) {
231
+ uint64_t out = 0 ;
232
+ for (int j = 0 ; j < 1024 ; ++j) out ^= GetRdRand ();
233
+ hasher.Write ((const unsigned char *)&out, sizeof (out));
234
+ }
235
+ return ;
141
236
}
142
237
#endif
143
- return false ;
144
238
}
145
239
146
240
static void RandAddSeedPerfmon (CSHA512& hasher)
@@ -407,8 +501,7 @@ static void SeedFast(CSHA512& hasher) noexcept
407
501
hasher.Write ((const unsigned char *)&ptr, sizeof (ptr));
408
502
409
503
// Hardware randomness is very fast when available; use it always.
410
- bool have_hw_rand = GetHardwareRand (buffer);
411
- if (have_hw_rand) hasher.Write (buffer, sizeof (buffer));
504
+ SeedHardwareFast (hasher);
412
505
413
506
// High-precision timestamp
414
507
SeedTimestamp (hasher);
@@ -460,6 +553,9 @@ static void SeedStartup(CSHA512& hasher) noexcept
460
553
RAND_screen ();
461
554
#endif
462
555
556
+ // Gather 256 bits of hardware randomness, if available
557
+ SeedHardwareSlow (hasher);
558
+
463
559
// Everything that the 'slow' seeder includes.
464
560
SeedSlow (hasher);
465
561
0 commit comments