Crypto++  5.6.4
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 // Copyright assigned to Crypto++ project.
3 
4 #include "pch.h"
5 #include "config.h"
6 #include "cryptlib.h"
7 #include "secblock.h"
8 #include "rdrand.h"
9 #include "cpu.h"
10 
11 #if CRYPTOPP_MSC_VERSION
12 # pragma warning(disable: 4100)
13 #endif
14 
15 // This file (and friends) provides both RDRAND and RDSEED, but its somewhat
16 // experimental. They were added at Crypto++ 5.6.3. At compile time, it
17 // indirectly uses CRYPTOPP_BOOL_{X86|X32|X64} (via CRYPTOPP_CPUID_AVAILABLE)
18 // to select an implementation or "throw NotImplemented". At runtime, the
19 // class uses the result of CPUID to determine if RDRAND or RDSEED are
20 // available. If not available, a lazy throw strategy is used. I.e., the
21 // throw is deferred until GenerateBlock() is called.
22 
23 // Here's the naming convention for the functions....
24 // MSC = Microsoft Compiler (and compatibles)
25 // GCC = GNU Compiler (and compatibles)
26 // ALL = MSC and GCC (and compatibles)
27 // RRA = RDRAND, Assembly
28 // RSA = RDSEED, Assembly
29 // RRI = RDRAND, Intrinsic
30 // RSA = RDSEED, Intrinsic
31 
32 /////////////////////////////////////////////////////////////////////
33 /////////////////////////////////////////////////////////////////////
34 
35 // For Linux, install NASM, run rdrand-nasm.asm, add the apppropriate
36 // object file to the Makefile's LIBOBJS (rdrand-x{86|32|64}.o). After
37 // that, define these. They are not enabled by default because they
38 // are not easy to cut-in in the Makefile.
39 
40 #if 0
41 #define NASM_RDRAND_ASM_AVAILABLE 1
42 #define NASM_RDSEED_ASM_AVAILABLE 1
43 #endif
44 
45 /////////////////////////////////////////////////////////////////////
46 /////////////////////////////////////////////////////////////////////
47 
48 // In general, the library's ASM code is best on Windows, and Intrinsics is
49 // the best code under GCC. Clang is missing symbols, so it gets ASM.
50 // The NASM code is optimized well on Linux, but its not easy to cut-in.
51 #if (CRYPTOPP_BOOL_X86 || CRYPTOPP_BOOL_X32 || CRYPTOPP_BOOL_X64)
52 # ifndef CRYPTOPP_CPUID_AVAILABLE
53 # define CRYPTOPP_CPUID_AVAILABLE
54 # endif
55 #endif
56 
57 #if defined(CRYPTOPP_CPUID_AVAILABLE)
58 # if defined(CRYPTOPP_MSC_VERSION)
59 # define MASM_RDRAND_ASM_AVAILABLE 1
60 # define MASM_RDSEED_ASM_AVAILABLE 1
61 # elif defined(CRYPTOPP_LLVM_CLANG_VERSION) || defined(CRYPTOPP_APPLE_CLANG_VERSION)
62 # define GCC_RDRAND_ASM_AVAILABLE 1
63 # define GCC_RDSEED_ASM_AVAILABLE 1
64 # elif defined(__SUNPRO_CC)
65 # if defined(__RDRND__) && (__SUNPRO_CC >= 0x5130)
66 # define ALL_RDRAND_INTRIN_AVAILABLE 1
67 # elif (__SUNPRO_CC >= 0x5100)
68 # define GCC_RDRAND_ASM_AVAILABLE 1
69 # endif
70 # if defined(__RDSEED__) && (__SUNPRO_CC >= 0x5140)
71 # define ALL_RDSEED_INTRIN_AVAILABLE 1
72 # elif (__SUNPRO_CC >= 0x5100)
73 # define GCC_RDSEED_ASM_AVAILABLE 1
74 # endif
75 # elif defined(CRYPTOPP_GCC_VERSION)
76 # if defined(__RDRND__) && (CRYPTOPP_GCC_VERSION >= 30200)
77 # define ALL_RDRAND_INTRIN_AVAILABLE 1
78 # else
79 # define GCC_RDRAND_ASM_AVAILABLE 1
80 # endif
81 # if defined(__RDSEED__) && (CRYPTOPP_GCC_VERSION >= 30200)
82 # define ALL_RDSEED_INTRIN_AVAILABLE 1
83 # else
84 # define GCC_RDSEED_ASM_AVAILABLE 1
85 # endif
86 # endif
87 #endif
88 
89 // Debug diagnostics
90 #if 0
91 # if MASM_RDRAND_ASM_AVAILABLE
92 # pragma message ("MASM_RDRAND_ASM_AVAILABLE is 1")
93 # elif NASM_RDRAND_ASM_AVAILABLE
94 # pragma message ("NASM_RDRAND_ASM_AVAILABLE is 1")
95 # elif GCC_RDRAND_ASM_AVAILABLE
96 # pragma message ("GCC_RDRAND_ASM_AVAILABLE is 1")
97 # elif ALL_RDRAND_INTRIN_AVAILABLE
98 # pragma message ("ALL_RDRAND_INTRIN_AVAILABLE is 1")
99 # else
100 # pragma message ("RDRAND is not available")
101 # endif
102 # if MASM_RDSEED_ASM_AVAILABLE
103 # pragma message ("MASM_RDSEED_ASM_AVAILABLE is 1")
104 # elif NASM_RDSEED_ASM_AVAILABLE
105 # pragma message ("NASM_RDSEED_ASM_AVAILABLE is 1")
106 # elif GCC_RDSEED_ASM_AVAILABLE
107 # pragma message ("GCC_RDSEED_ASM_AVAILABLE is 1")
108 # elif ALL_RDSEED_INTRIN_AVAILABLE
109 # pragma message ("ALL_RDSEED_INTRIN_AVAILABLE is 1")
110 # else
111 # pragma message ("RDSEED is not available")
112 # endif
113 #endif
114 
115 /////////////////////////////////////////////////////////////////////
116 /////////////////////////////////////////////////////////////////////
117 
118 #if (ALL_RDRAND_INTRIN_AVAILABLE || ALL_RDSEED_INTRIN_AVAILABLE)
119 # include <immintrin.h> // rdrand, MSC, ICC, GCC, and SunCC
120 # if defined(__GNUC__) && (CRYPTOPP_GCC_VERSION >= 40600)
121 # include <x86intrin.h> // rdseed for some compilers, like GCC
122 # endif
123 # if defined(__has_include)
124 # if __has_include(<x86intrin.h>)
125 # include <x86intrin.h> // rdrand for Clang (immintrin.h); rdseed for Clang (rdseedintrin.h)
126 # endif
127 # endif
128 #endif
129 
130 #if MASM_RDRAND_ASM_AVAILABLE
131 # ifdef _M_X64
132 extern "C" int CRYPTOPP_FASTCALL MASM_RRA_GenerateBlock(byte*, size_t, unsigned int);
133 // # pragma comment(lib, "rdrand-x64.lib")
134 # else
135 extern "C" int MASM_RRA_GenerateBlock(byte*, size_t, unsigned int);
136 // # pragma comment(lib, "rdrand-x86.lib")
137 # endif
138 #endif
139 
140 #if MASM_RDSEED_ASM_AVAILABLE
141 # ifdef _M_X64
142 extern "C" int CRYPTOPP_FASTCALL MASM_RSA_GenerateBlock(byte*, size_t, unsigned int);
143 // # pragma comment(lib, "rdrand-x64.lib")
144 # else
145 extern "C" int MASM_RSA_GenerateBlock(byte*, size_t, unsigned int);
146 // # pragma comment(lib, "rdrand-x86.lib")
147 # endif
148 #endif
149 
150 #if NASM_RDRAND_ASM_AVAILABLE
151 extern "C" int NASM_RRA_GenerateBlock(byte*, size_t, unsigned int);
152 #endif
153 
154 #if NASM_RDSEED_ASM_AVAILABLE
155 extern "C" int NASM_RSA_GenerateBlock(byte*, size_t, unsigned int);
156 #endif
157 
158 /////////////////////////////////////////////////////////////////////
159 /////////////////////////////////////////////////////////////////////
160 
161 NAMESPACE_BEGIN(CryptoPP)
162 
163 #if ALL_RDRAND_INTRIN_AVAILABLE
164 static int ALL_RRI_GenerateBlock(byte *output, size_t size, unsigned int safety)
165 {
166  assert((output && size) || !(output || size));
167 #if CRYPTOPP_BOOL_X86 || CRYPTOPP_BOOL_X32
168  word32 val;
169 #else
170  word64 val;
171 #endif
172 
173  while (size >= sizeof(val))
174  {
175 #if CRYPTOPP_BOOL_X86 || CRYPTOPP_BOOL_X32
176  if (_rdrand32_step((word32*)output))
177 #else
178  // Cast due to GCC, http://github.com/weidai11/cryptopp/issues/236
179  if (_rdrand64_step(reinterpret_cast<unsigned long long*>(output)))
180 #endif
181  {
182  output += sizeof(val);
183  size -= sizeof(val);
184  }
185  else
186  {
187  if (!safety--)
188  {
189  assert(0);
190  return 0;
191  }
192  }
193  }
194 
195  if (size)
196  {
197 #if CRYPTOPP_BOOL_X86 || CRYPTOPP_BOOL_X32
198  if (_rdrand32_step(&val))
199 #else
200  // Cast due to GCC, http://github.com/weidai11/cryptopp/issues/236
201  if (_rdrand64_step(reinterpret_cast<unsigned long long*>(&val)))
202 #endif
203  {
204  memcpy(output, &val, size);
205  size = 0;
206  }
207  else
208  {
209  if (!safety--)
210  {
211  assert(0);
212  return 0;
213  }
214  }
215  }
216 
217  SecureWipeBuffer(&val, 1);
218 
219  return int(size == 0);
220 }
221 #endif // ALL_RDRAND_INTRINSIC_AVAILABLE
222 
223 #if GCC_RDRAND_ASM_AVAILABLE
224 static int GCC_RRA_GenerateBlock(byte *output, size_t size, unsigned int safety)
225 {
226  assert((output && size) || !(output || size));
227 #if CRYPTOPP_BOOL_X64 || CRYPTOPP_BOOL_X32
228  word64 val;
229 #else
230  word32 val;
231 #endif
232  char rc;
233  while (size)
234  {
235  __asm__ volatile(
236 #if CRYPTOPP_BOOL_X64 || CRYPTOPP_BOOL_X32
237  ".byte 0x48, 0x0f, 0xc7, 0xf0;\n" // rdrand rax
238 #else
239  ".byte 0x0f, 0xc7, 0xf0;\n" // rdrand eax
240 #endif
241  "setc %1; "
242  : "=a" (val), "=qm" (rc)
243  :
244  : "cc"
245  );
246 
247  if (rc)
248  {
249  if (size >= sizeof(val))
250  {
251 #if defined(CRYPTOPP_ALLOW_UNALIGNED_DATA_ACCESS) && (CRYPTOPP_BOOL_X64 || CRYPTOPP_BOOL_X32)
252  *((word64*)(void *)output) = val;
253 #elif defined(CRYPTOPP_ALLOW_UNALIGNED_DATA_ACCESS) && (CRYPTOPP_BOOL_X86)
254  *((word32*)(void *)output) = val;
255 #else
256  memcpy(output, &val, sizeof(val));
257 #endif
258  output += sizeof(val);
259  size -= sizeof(val);
260  }
261  else
262  {
263  memcpy(output, &val, size);
264  size = 0;
265  }
266  }
267  else
268  {
269  if (!safety--)
270  {
271  assert(0);
272  return 0;
273  }
274  }
275  }
276 
277  SecureWipeBuffer(&val, 1);
278 
279  return int(size == 0);
280 }
281 
282 #endif // GCC_RDRAND_ASM_AVAILABLE
283 
284 #if (CRYPTOPP_BOOL_X86 || CRYPTOPP_BOOL_X32 || CRYPTOPP_BOOL_X64)
285 void RDRAND::GenerateBlock(byte *output, size_t size)
286 {
287  CRYPTOPP_UNUSED(output), CRYPTOPP_UNUSED(size);
288  assert((output && size) || !(output || size));
289 
290  if(!HasRDRAND())
291  throw NotImplemented("RDRAND: rdrand is not available on this platform");
292 
293  int rc; CRYPTOPP_UNUSED(rc);
294 #if MASM_RDRAND_ASM_AVAILABLE
295  rc = MASM_RRA_GenerateBlock(output, size, m_retries);
296  if (!rc) { throw RDRAND_Err("MASM_RRA_GenerateBlock"); }
297 #elif NASM_RDRAND_ASM_AVAILABLE
298  rc = NASM_RRA_GenerateBlock(output, size, m_retries);
299  if (!rc) { throw RDRAND_Err("NASM_RRA_GenerateBlock"); }
300 #elif ALL_RDRAND_INTRIN_AVAILABLE
301  rc = ALL_RRI_GenerateBlock(output, size, m_retries);
302  if (!rc) { throw RDRAND_Err("ALL_RRI_GenerateBlock"); }
303 #elif GCC_RDRAND_ASM_AVAILABLE
304  rc = GCC_RRA_GenerateBlock(output, size, m_retries);
305  if (!rc) { throw RDRAND_Err("GCC_RRA_GenerateBlock"); }
306 #else
307  // RDRAND not detected at compile time, and no suitable compiler found
308  throw NotImplemented("RDRAND: failed to find a suitable implementation???");
309 #endif // CRYPTOPP_CPUID_AVAILABLE
310 }
311 
312 void RDRAND::DiscardBytes(size_t n)
313 {
314  // RoundUpToMultipleOf is used because a full word is read, and its cheaper
315  // to discard full words. There's no sense in dealing with tail bytes.
316  assert(HasRDRAND());
317 #if CRYPTOPP_BOOL_X64 || CRYPTOPP_BOOL_X32
319  n = RoundUpToMultipleOf(n, sizeof(word64));
320 #else
322  n = RoundUpToMultipleOf(n, sizeof(word32));
323 #endif
324 
325  size_t count = STDMIN(n, discard.SizeInBytes());
326  while (count)
327  {
328  GenerateBlock(discard.BytePtr(), count);
329  n -= count;
330  count = STDMIN(n, discard.SizeInBytes());
331  }
332 }
333 #endif // CRYPTOPP_BOOL_X86 || CRYPTOPP_BOOL_X32 || CRYPTOPP_BOOL_X64
334 
335 /////////////////////////////////////////////////////////////////////
336 /////////////////////////////////////////////////////////////////////
337 
338 #if ALL_RDSEED_INTRIN_AVAILABLE
339 static int ALL_RSI_GenerateBlock(byte *output, size_t size, unsigned int safety)
340 {
341  assert((output && size) || !(output || size));
342 #if CRYPTOPP_BOOL_X86 || CRYPTOPP_BOOL_X32
343  word32 val;
344 #else
345  word64 val;
346 #endif
347 
348  while (size >= sizeof(val))
349  {
350 #if CRYPTOPP_BOOL_X86 || CRYPTOPP_BOOL_X32
351  if (_rdseed32_step((word32*)output))
352 #else
353  // Cast due to GCC, http://github.com/weidai11/cryptopp/issues/236
354  if (_rdseed64_step(reinterpret_cast<unsigned long long*>(output)))
355 #endif
356  {
357  output += sizeof(val);
358  size -= sizeof(val);
359  }
360  else
361  {
362  if (!safety--)
363  {
364  assert(0);
365  return 0;
366  }
367  }
368  }
369 
370  if (size)
371  {
372 #if CRYPTOPP_BOOL_X86 || CRYPTOPP_BOOL_X32
373  if (_rdseed32_step(&val))
374 #else
375  // Cast due to GCC, http://github.com/weidai11/cryptopp/issues/236
376  if (_rdseed64_step(reinterpret_cast<unsigned long long*>(&val)))
377 #endif
378  {
379  memcpy(output, &val, size);
380  size = 0;
381  }
382  else
383  {
384  if (!safety--)
385  {
386  assert(0);
387  return 0;
388  }
389  }
390  }
391 
392  SecureWipeBuffer(&val, 1);
393 
394  return int(size == 0);
395 }
396 #endif // ALL_RDSEED_INTRIN_AVAILABLE
397 
398 #if GCC_RDSEED_ASM_AVAILABLE
399 static int GCC_RSA_GenerateBlock(byte *output, size_t size, unsigned int safety)
400 {
401  assert((output && size) || !(output || size));
402 #if CRYPTOPP_BOOL_X64 || CRYPTOPP_BOOL_X32
403  word64 val;
404 #else
405  word32 val;
406 #endif
407  char rc;
408  while (size)
409  {
410  __asm__ volatile(
411 #if CRYPTOPP_BOOL_X64 || CRYPTOPP_BOOL_X32
412  ".byte 0x48, 0x0f, 0xc7, 0xf8;\n" // rdseed rax
413 #else
414  ".byte 0x0f, 0xc7, 0xf8;\n" // rdseed eax
415 #endif
416  "setc %1; "
417  : "=a" (val), "=qm" (rc)
418  :
419  : "cc"
420  );
421 
422  if (rc)
423  {
424  if (size >= sizeof(val))
425  {
426 #if defined(CRYPTOPP_ALLOW_UNALIGNED_DATA_ACCESS) && (CRYPTOPP_BOOL_X64 || CRYPTOPP_BOOL_X32)
427  *((word64*)(void *)output) = val;
428 #elif defined(CRYPTOPP_ALLOW_UNALIGNED_DATA_ACCESS) && (CRYPTOPP_BOOL_X86)
429  *((word32*)(void *)output) = val;
430 #else
431  memcpy(output, &val, sizeof(val));
432 #endif
433  output += sizeof(val);
434  size -= sizeof(val);
435  }
436  else
437  {
438  memcpy(output, &val, size);
439  size = 0;
440  }
441  }
442  else
443  {
444  if (!safety--)
445  {
446  assert(0);
447  return 0;
448  }
449  }
450  }
451 
452  SecureWipeBuffer(&val, 1);
453 
454  return int(size == 0);
455 }
456 #endif // GCC_RDSEED_ASM_AVAILABLE
457 
458 #if (CRYPTOPP_BOOL_X86 || CRYPTOPP_BOOL_X32 || CRYPTOPP_BOOL_X64)
459 void RDSEED::GenerateBlock(byte *output, size_t size)
460 {
461  CRYPTOPP_UNUSED(output), CRYPTOPP_UNUSED(size);
462  assert((output && size) || !(output || size));
463 
464  if(!HasRDSEED())
465  throw NotImplemented("RDSEED: rdseed is not available on this platform");
466 
467  int rc; CRYPTOPP_UNUSED(rc);
468 #if MASM_RDSEED_ASM_AVAILABLE
469  rc = MASM_RSA_GenerateBlock(output, size, m_retries);
470  if (!rc) { throw RDSEED_Err("MASM_RSA_GenerateBlock"); }
471 #elif NASM_RDSEED_ASM_AVAILABLE
472  rc = NASM_RSA_GenerateBlock(output, size, m_retries);
473  if (!rc) { throw RDRAND_Err("NASM_RSA_GenerateBlock"); }
474 #elif ALL_RDSEED_INTRIN_AVAILABLE
475  rc = ALL_RSI_GenerateBlock(output, size, m_retries);
476  if (!rc) { throw RDSEED_Err("ALL_RSI_GenerateBlock"); }
477 #elif GCC_RDSEED_ASM_AVAILABLE
478  rc = GCC_RSA_GenerateBlock(output, size, m_retries);
479  if (!rc) { throw RDSEED_Err("GCC_RSA_GenerateBlock"); }
480 #else
481  // RDSEED not detected at compile time, and no suitable compiler found
482  throw NotImplemented("RDSEED: failed to find a suitable implementation???");
483 #endif
484 }
485 
486 void RDSEED::DiscardBytes(size_t n)
487 {
488  // RoundUpToMultipleOf is used because a full word is read, and its cheaper
489  // to discard full words. There's no sense in dealing with tail bytes.
490  assert(HasRDSEED());
491 #if CRYPTOPP_BOOL_X64 || CRYPTOPP_BOOL_X32
493  n = RoundUpToMultipleOf(n, sizeof(word64));
494 #else
496  n = RoundUpToMultipleOf(n, sizeof(word32));
497 #endif
498 
499  size_t count = STDMIN(n, discard.SizeInBytes());
500  while (count)
501  {
502  GenerateBlock(discard.BytePtr(), count);
503  n -= count;
504  count = STDMIN(n, discard.SizeInBytes());
505  }
506 }
507 #endif // CRYPTOPP_BOOL_X86 || CRYPTOPP_BOOL_X32 || CRYPTOPP_BOOL_X64
508 
509 NAMESPACE_END
NotImplemented
A method was called which was not implemented.
Definition: cryptlib.h:204
RoundUpToMultipleOf
T1 RoundUpToMultipleOf(const T1 &n, const T2 &m)
Rounds a value up to a multiple of a second value.
Definition: misc.h:865
rdrand.h
Classes for RDRAND and RDSEED.
secblock.h
Classes and functions for secure memory allocations.
RDSEED::GenerateBlock
virtual void GenerateBlock(byte *output, size_t size)
Generate random array of bytes.
Definition: rdrand.h:162
RDRAND::DiscardBytes
virtual void DiscardBytes(size_t n)
Generate and discard n bytes.
Definition: rdrand.h:92
SecBlock< T, FixedSizeAllocatorWithCleanup< T, S > >::BytePtr
byte * BytePtr()
Provides a byte pointer to the first element in the memory block.
Definition: secblock.h:531
RDSEED::DiscardBytes
virtual void DiscardBytes(size_t n)
Generate and discard n bytes.
Definition: rdrand.h:176
SecBlock< T, FixedSizeAllocatorWithCleanup< T, S > >::SizeInBytes
size_type SizeInBytes() const
Provides the number of bytes in the SecBlock.
Definition: secblock.h:538
HasRDSEED
bool HasRDSEED()
Determines RDSEED availability.
Definition: cpu.h:311
RDRAND_Err
Exception thrown when a RDRAND generator encounters a generator related error.
Definition: rdrand.h:30
STDMIN
const T & STDMIN(const T &a, const T &b)
Replacement function for std::min.
Definition: misc.h:467
cpu.h
Functions for CPU features and intrinsics.
HasRDRAND
bool HasRDRAND()
Determines RDRAND availability.
Definition: cpu.h:301
RDSEED_Err
Exception thrown when a RDSEED generator encounters a generator related error.
Definition: rdrand.h:117
FixedSizeSecBlock
Fixed size stack-based SecBlock.
Definition: secblock.h:754
RDRAND::GenerateBlock
virtual void GenerateBlock(byte *output, size_t size)
Generate random array of bytes.
Definition: rdrand.h:78
CryptoPP
Crypto++ library namespace.
config.h
Library configuration file.
SecureWipeBuffer
void SecureWipeBuffer(T *buf, size_t n)
Sets each element of an array to 0.
Definition: misc.h:1069
cryptlib.h
Abstract base classes that provide a uniform interface to this library.