Crypto++  6.1
Free C++ class library of cryptographic schemes
rdrand.cpp
1 // rdrand.cpp - written and placed in public domain by Jeffrey Walton and Uri Blumenthal.
2 
3 #include "pch.h"
4 #include "config.h"
5 #include "cryptlib.h"
6 #include "secblock.h"
7 #include "rdrand.h"
8 #include "cpu.h"
9 
10 // This file (and friends) provides both RDRAND and RDSEED. They were added at
11 // Crypto++ 5.6.3. At compile time, it uses CRYPTOPP_BOOL_{X86|X32|X64}
12 // to select an implementation or "throw NotImplemented". The class does not
13 // determine if RDRAND or RDSEED are available at runtime. If not available,
14 // then a SIGILL will result. Users of the classes should call HasRDRAND()
15 // or HasRDSEED() to determine if a generator is available.
16 // The original classes accepted a retry count. Retries were superflous for
17 // RDRAND, and RDSEED encountered a failure about 1 in 256 bytes depending
18 // on the processor. Retries were removed at Crypto++ 6.0 because
19 // GenerateBlock unconditionally retries and always fulfills the request.
20 
21 /////////////////////////////////////////////////////////////////////
22 /////////////////////////////////////////////////////////////////////
23 
24 // For Linux, NASM is optional. Run rdrand-nasm.sh, and then make
25 // with "USE_NASM" like so: USE_NASM=1 make -j 4. The makefile
26 // will add the appropriate defines when building rdrand.cpp,
27 // and add the appropriate object file during link.
28 
29 #if 0
30 #define NASM_RDRAND_ASM_AVAILABLE 1
31 #define NASM_RDSEED_ASM_AVAILABLE 1
32 #endif
33 
34 /////////////////////////////////////////////////////////////////////
35 /////////////////////////////////////////////////////////////////////
36 
37 #if defined(CRYPTOPP_CPUID_AVAILABLE)
38 # if defined(CRYPTOPP_MSC_VERSION)
39 # if (CRYPTOPP_MSC_VERSION >= 1700)
40 # define ALL_RDRAND_INTRIN_AVAILABLE 1
41 # else
42 # define MASM_RDRAND_ASM_AVAILABLE 1
43 # endif
44 # if (CRYPTOPP_MSC_VERSION >= 1800)
45 # define ALL_RDSEED_INTRIN_AVAILABLE 1
46 # else
47 # define MASM_RDSEED_ASM_AVAILABLE 1
48 # endif
49 # elif defined(CRYPTOPP_LLVM_CLANG_VERSION) || defined(CRYPTOPP_APPLE_CLANG_VERSION)
50 # if defined(__RDRND__)
51 # define ALL_RDRAND_INTRIN_AVAILABLE 1
52 # else
53 # define GCC_RDRAND_ASM_AVAILABLE 1
54 # endif
55 # if defined(__RDSEED__)
56 # define ALL_RDSEED_INTRIN_AVAILABLE 1
57 # else
58 # define GCC_RDSEED_ASM_AVAILABLE 1
59 # endif
60 # elif defined(__SUNPRO_CC)
61 # if defined(__RDRND__) && (__SUNPRO_CC >= 0x5130)
62 # define ALL_RDRAND_INTRIN_AVAILABLE 1
63 # elif (__SUNPRO_CC >= 0x5100)
64 # define GCC_RDRAND_ASM_AVAILABLE 1
65 # endif
66 # if defined(__RDSEED__) && (__SUNPRO_CC >= 0x5140)
67 # define ALL_RDSEED_INTRIN_AVAILABLE 1
68 # elif (__SUNPRO_CC >= 0x5100)
69 # define GCC_RDSEED_ASM_AVAILABLE 1
70 # endif
71 # elif defined(CRYPTOPP_GCC_VERSION)
72 # if defined(__RDRND__) && (CRYPTOPP_GCC_VERSION >= 40700) && !defined(__OPTIMIZE__)
73 # define ALL_RDRAND_INTRIN_AVAILABLE 1
74 # else
75 # define GCC_RDRAND_ASM_AVAILABLE 1
76 # endif
77 # if defined(__RDSEED__) && (CRYPTOPP_GCC_VERSION >= 40700) && !defined(__OPTIMIZE__)
78 # define ALL_RDSEED_INTRIN_AVAILABLE 1
79 # else
80 # define GCC_RDSEED_ASM_AVAILABLE 1
81 # endif
82 # endif
83 #endif
84 
85 /////////////////////////////////////////////////////////////////////
86 /////////////////////////////////////////////////////////////////////
87 
88 #if (ALL_RDRAND_INTRIN_AVAILABLE || ALL_RDSEED_INTRIN_AVAILABLE)
89 # include <immintrin.h> // rdrand, MSC, ICC, GCC, and SunCC
90 # if defined(__GNUC__) && (CRYPTOPP_GCC_VERSION >= 40700)
91 # include <x86intrin.h> // rdseed for some compilers, like GCC
92 # endif
93 # if defined(__has_include)
94 # if __has_include(<x86intrin.h>)
95 # include <x86intrin.h>
96 # endif
97 # endif
98 #endif
99 
100 typedef unsigned char byte;
101 
102 #if MASM_RDRAND_ASM_AVAILABLE
103 extern "C" void CRYPTOPP_FASTCALL MASM_RDRAND_GenerateBlock(byte*, size_t);
104 #endif
105 
106 #if MASM_RDSEED_ASM_AVAILABLE
107 extern "C" void CRYPTOPP_FASTCALL MASM_RDSEED_GenerateBlock(byte*, size_t);
108 #endif
109 
110 #if NASM_RDRAND_ASM_AVAILABLE
111 extern "C" void NASM_RDRAND_GenerateBlock(byte*, size_t);
112 #endif
113 
114 #if NASM_RDSEED_ASM_AVAILABLE
115 extern "C" void NASM_RDSEED_GenerateBlock(byte*, size_t);
116 #endif
117 
118 /////////////////////////////////////////////////////////////////////
119 /////////////////////////////////////////////////////////////////////
120 
121 NAMESPACE_BEGIN(CryptoPP)
122 
123 #if defined(CRYPTOPP_CPUID_AVAILABLE)
124 
125 // Fills 4 bytes
126 inline void RDRAND32(void* output)
127 {
128 #if defined(__SUNPRO_CC)
129  __asm__ __volatile__
130  (
131  "1:\n"
132  ".byte 0x0f, 0xc7, 0xf0;\n"
133  "jnc 1b;\n"
134  : "=a" (*reinterpret_cast<word32*>(output))
135  : : "cc"
136  );
137 #elif defined(GCC_RDRAND_ASM_AVAILABLE) && (CRYPTOPP_GCC_VERSION >= 40700)
138  __asm__ __volatile__
139  (
140  INTEL_NOPREFIX
141  ASL(1)
142  AS1(rdrand eax)
143  ASJ(jnc, 1, b)
144  ATT_NOPREFIX
145  : "=a" (*reinterpret_cast<word32*>(output))
146  : : "cc"
147  );
148 #elif defined(GCC_RDRAND_ASM_AVAILABLE) && (CRYPTOPP_GCC_VERSION >= 30200)
149  __asm__ __volatile__
150  (
151  "1:\n"
152  ".byte 0x0f, 0xc7, 0xf0;\n"
153  "jnc 1b;\n"
154  : "=a" (*reinterpret_cast<word32*>(output))
155  : : "cc"
156  );
157 #elif defined(ALL_RDRAND_INTRIN_AVAILABLE)
158  while(!_rdrand32_step(reinterpret_cast<word32*>(output))) {}
159 #else
160  // RDRAND not detected at compile time, or no suitable compiler found
161  CRYPTOPP_UNUSED(output);
162  throw NotImplemented("RDRAND: failed to find an implementation");
163 #endif
164 }
165 
166 #if CRYPTOPP_BOOL_X64 || CRYPTOPP_BOOL_X32
167 // Fills 8 bytes
168 inline void RDRAND64(void* output)
169 {
170 #if defined(__SUNPRO_CC) && (__SUNPRO_CC >= 0x5100)
171  __asm__ __volatile__
172  (
173  "1:\n"
174  ".byte 0x48, 0x0f, 0xc7, 0xf0;\n"
175  "jnc 1b;\n"
176  : "=a" (*reinterpret_cast<word64*>(output))
177  : : "cc"
178  );
179 #elif defined(GCC_RDRAND_ASM_AVAILABLE) && (CRYPTOPP_GCC_VERSION >= 40700)
180  __asm__ __volatile__
181  (
182  INTEL_NOPREFIX
183  ASL(1)
184  AS1(rdrand rax)
185  ASJ(jnc, 1, b)
186  ATT_NOPREFIX
187  : "=a" (*reinterpret_cast<word64*>(output))
188  : : "cc"
189  );
190 #elif defined(GCC_RDRAND_ASM_AVAILABLE) && (CRYPTOPP_GCC_VERSION >= 30200)
191  __asm__ __volatile__
192  (
193  "1:\n"
194  ".byte 0x48, 0x0f, 0xc7, 0xf0;\n"
195  "jnc 1b;\n"
196  : "=a" (*reinterpret_cast<word64*>(output))
197  : : "cc"
198  );
199 #elif defined(ALL_RDRAND_INTRIN_AVAILABLE)
200  while(!_rdrand64_step(reinterpret_cast<unsigned long long*>(output))) {}
201 #else
202  // RDRAND not detected at compile time, or no suitable compiler found
203  CRYPTOPP_UNUSED(output);
204  throw NotImplemented("RDRAND: failed to find an implementation");
205 #endif
206 }
207 #endif // CRYPTOPP_BOOL_X64, CRYPTOPP_BOOL_X32 and RDRAND64
208 
210 {
211  if (!HasRDRAND())
212  throw RDRAND_Err("HasRDRAND");
213 }
214 
215 void RDRAND::GenerateBlock(byte *output, size_t size)
216 {
217  CRYPTOPP_ASSERT((output && size) || !(output || size));
218  if (size == 0) return;
219 
220 #if defined(NASM_RDRAND_ASM_AVAILABLE)
221 
222  NASM_RDRAND_GenerateBlock(output, size);
223 
224 #elif defined(MASM_RDRAND_ASM_AVAILABLE)
225 
226  MASM_RDRAND_GenerateBlock(output, size);
227 
228 #elif CRYPTOPP_BOOL_X64 || CRYPTOPP_BOOL_X32
229  size_t i = 0;
230  for (i = 0; i < size/8; i++)
231  RDRAND64(reinterpret_cast<word64*>(output)+i);
232 
233  output += i*8;
234  size -= i*8;
235 
236  if (size)
237  {
238  word64 val;
239  RDRAND64(&val);
240  ::memcpy(output, &val, size);
241  }
242 #elif CRYPTOPP_BOOL_X86
243  size_t i = 0;
244  for (i = 0; i < size/4; i++)
245  RDRAND32(reinterpret_cast<word32*>(output)+i);
246 
247  output += i*4;
248  size -= i*4;
249 
250  if (size)
251  {
252  word32 val;
253  RDRAND32(&val);
254  ::memcpy(output, &val, size);
255  }
256 #else
257  // RDRAND not detected at compile time, or no suitable compiler found
258  CRYPTOPP_UNUSED(output);
259  throw NotImplemented("RDRAND: failed to find a suitable implementation");
260 #endif
261 }
262 
263 void RDRAND::DiscardBytes(size_t n)
264 {
265  // RoundUpToMultipleOf is used because a full word is read, and its cheaper
266  // to discard full words. There's no sense in dealing with tail bytes.
268  n = RoundUpToMultipleOf(n, sizeof(word64));
269 
270  size_t count = STDMIN(n, discard.SizeInBytes());
271  while (count)
272  {
273  GenerateBlock(discard.BytePtr(), count);
274  n -= count;
275  count = STDMIN(n, discard.SizeInBytes());
276  }
277 }
278 
279 /////////////////////////////////////////////////////////////////////
280 /////////////////////////////////////////////////////////////////////
281 
282 // Fills 4 bytes
283 inline void RDSEED32(void* output)
284 {
285 #if defined(__SUNPRO_CC)
286  __asm__ __volatile__
287  (
288  "1:\n"
289  ".byte 0x0f, 0xc7, 0xf8;\n"
290  "jnc 1b;\n"
291  : "=a" (*reinterpret_cast<word32*>(output))
292  : : "cc"
293  );
294 #elif defined(GCC_RDSEED_ASM_AVAILABLE) && (CRYPTOPP_GCC_VERSION >= 40700)
295  __asm__ __volatile__
296  (
297  INTEL_NOPREFIX
298  ASL(1)
299  AS1(rdseed eax)
300  ASJ(jnc, 1, b)
301  ATT_NOPREFIX
302  : "=a" (*reinterpret_cast<word32*>(output))
303  : : "cc"
304  );
305 #elif defined(GCC_RDSEED_ASM_AVAILABLE) && (CRYPTOPP_GCC_VERSION >= 30200)
306  __asm__ __volatile__
307  (
308  "1:\n"
309  ".byte 0x0f, 0xc7, 0xf8;\n"
310  "jnc 1b;\n"
311  : "=a" (*reinterpret_cast<word32*>(output))
312  : : "cc"
313  );
314 #elif defined(ALL_RDSEED_INTRIN_AVAILABLE)
315  while(!_rdseed32_step(reinterpret_cast<word32*>(output))) {}
316 #else
317  // RDSEED not detected at compile time, or no suitable compiler found
318  CRYPTOPP_UNUSED(output);
319  throw NotImplemented("RDSEED: failed to find an implementation");
320 #endif
321 }
322 
323 #if CRYPTOPP_BOOL_X64 || CRYPTOPP_BOOL_X32
324 // Fills 8 bytes
325 inline void RDSEED64(void* output)
326 {
327 #if defined(__SUNPRO_CC) && (__SUNPRO_CC >= 0x5100)
328  __asm__ __volatile__
329  (
330  "1:\n"
331  ".byte 0x48, 0x0f, 0xc7, 0xf8;\n"
332  "jnc 1b;\n"
333  : "=a" (*reinterpret_cast<word64*>(output))
334  : : "cc"
335  );
336 #elif defined(GCC_RDSEED_ASM_AVAILABLE) && (CRYPTOPP_GCC_VERSION >= 40700)
337  __asm__ __volatile__
338  (
339  INTEL_NOPREFIX
340  ASL(1)
341  AS1(rdseed rax)
342  ASJ(jnc, 1, b)
343  ATT_NOPREFIX
344  : "=a" (*reinterpret_cast<word64*>(output))
345  : : "cc"
346  );
347 #elif defined(GCC_RDSEED_ASM_AVAILABLE) && (CRYPTOPP_GCC_VERSION >= 30200)
348  __asm__ __volatile__
349  (
350  "1:\n"
351  ".byte 0x48, 0x0f, 0xc7, 0xf8;\n"
352  "jnc 1b;\n"
353  : "=a" (*reinterpret_cast<word64*>(output))
354  : : "cc"
355  );
356 #elif defined(ALL_RDSEED_INTRIN_AVAILABLE)
357  while(!_rdseed64_step(reinterpret_cast<unsigned long long*>(output))) {}
358 #else
359  // RDSEED not detected at compile time, or no suitable compiler found
360  CRYPTOPP_UNUSED(output);
361  throw NotImplemented("RDSEED: failed to find an implementation");
362 #endif
363 }
364 #endif // CRYPTOPP_BOOL_X64 and RDSEED64
365 
367 {
368  if (!HasRDSEED())
369  throw RDSEED_Err("HasRDSEED");
370 }
371 
372 void RDSEED::GenerateBlock(byte *output, size_t size)
373 {
374  CRYPTOPP_ASSERT((output && size) || !(output || size));
375  if (size == 0) return;
376 
377 #if defined(NASM_RDSEED_ASM_AVAILABLE)
378 
379  NASM_RDSEED_GenerateBlock(output, size);
380 
381 #elif defined(MASM_RDSEED_ASM_AVAILABLE)
382 
383  MASM_RDSEED_GenerateBlock(output, size);
384 
385 #elif CRYPTOPP_BOOL_X64 || CRYPTOPP_BOOL_X32
386  size_t i = 0;
387  for (i = 0; i < size/8; i++)
388  RDSEED64(reinterpret_cast<word64*>(output)+i);
389 
390  output += i*8;
391  size -= i*8;
392 
393  if (size)
394  {
395  word64 val;
396  RDSEED64(&val);
397  ::memcpy(output, &val, size);
398  }
399 #elif CRYPTOPP_BOOL_X86
400  size_t i = 0;
401  for (i = 0; i < size/4; i++)
402  RDSEED32(reinterpret_cast<word32*>(output)+i);
403 
404  output += i*4;
405  size -= i*4;
406 
407  if (size)
408  {
409  word32 val;
410  RDSEED32(&val);
411  ::memcpy(output, &val, size);
412  }
413 #endif // CRYPTOPP_BOOL_X64, CRYPTOPP_BOOL_X32 and RDSEED64
414 }
415 
416 void RDSEED::DiscardBytes(size_t n)
417 {
418  // RoundUpToMultipleOf is used because a full word is read, and its cheaper
419  // to discard full words. There's no sense in dealing with tail bytes.
421  n = RoundUpToMultipleOf(n, sizeof(word64));
422 
423  size_t count = STDMIN(n, discard.SizeInBytes());
424  while (count)
425  {
426  GenerateBlock(discard.BytePtr(), count);
427  n -= count;
428  count = STDMIN(n, discard.SizeInBytes());
429  }
430 }
431 
432 #else // CRYPTOPP_CPUID_AVAILABLE
433 
435 {
436  throw RDRAND_Err("HasRDRAND");
437 }
438 
439 void RDRAND::GenerateBlock(byte *output, size_t size)
440 {
441  CRYPTOPP_UNUSED(output); CRYPTOPP_UNUSED(size);
442 }
443 
444 void RDRAND::DiscardBytes(size_t n)
445 {
446  CRYPTOPP_UNUSED(n);
447 }
448 
450 {
451  throw RDSEED_Err("HasRDSEED");
452 }
453 
454 void RDSEED::GenerateBlock(byte *output, size_t size)
455 {
456  CRYPTOPP_UNUSED(output); CRYPTOPP_UNUSED(size);
457 }
458 
459 void RDSEED::DiscardBytes(size_t n)
460 {
461  CRYPTOPP_UNUSED(n);
462 }
463 
464 #endif
465 
466 NAMESPACE_END
virtual void DiscardBytes(size_t n)
Generate and discard n bytes.
Definition: rdrand.cpp:416
size_type SizeInBytes() const
Provides the number of bytes in the SecBlock.
Definition: secblock.h:575
Abstract base classes that provide a uniform interface to this library.
Library configuration file.
Classes for RDRAND and RDSEED.
bool HasRDRAND()
Determines RDRAND availability.
Definition: cpu.h:217
RDSEED()
Construct a RDSEED generator.
Definition: rdrand.cpp:366
Classes and functions for secure memory allocations.
Exception thrown when a RDRAND generator encounters a generator related error.
Definition: rdrand.h:38
virtual void DiscardBytes(size_t n)
Generate and discard n bytes.
Definition: rdrand.cpp:263
A method was called which was not implemented.
Definition: cryptlib.h:220
bool HasRDSEED()
Determines RDSEED availability.
Definition: cpu.h:228
Precompiled header file.
Fixed size stack-based SecBlock.
Definition: secblock.h:841
const T & STDMIN(const T &a, const T &b)
Replacement function for std::min.
Definition: misc.h:507
#define CRYPTOPP_ASSERT(exp)
Debugging and diagnostic assertion.
Definition: trap.h:60
virtual void GenerateBlock(byte *output, size_t size)
Generate random array of bytes.
Definition: rdrand.cpp:372
Functions for CPU features and intrinsics.
virtual void GenerateBlock(byte *output, size_t size)
Generate random array of bytes.
Definition: rdrand.cpp:215
T1 RoundUpToMultipleOf(const T1 &n, const T2 &m)
Rounds a value up to a multiple of a second value.
Definition: misc.h:971
Crypto++ library namespace.
Exception thrown when a RDSEED generator encounters a generator related error.
Definition: rdrand.h:88
RDRAND()
Construct a RDRAND generator.
Definition: rdrand.cpp:209
byte * BytePtr()
Provides a byte pointer to the first element in the memory block.
Definition: secblock.h:568