Crypto++  5.6.3
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. A lazy throw strategy is used in case the CPU does not support
21 // the instruction. I.e., the 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 // According to Wei, CRYPTOPP_DISABLE_ASM is a failsafe due to the assembler.
49 // We sidestep it because it does not limit us. The assembler does not limit
50 // us because we emit out own byte codes as needed. To diasble RDRAND or
51 // RDSEED, set CRYPTOPP_BOOL_RDRAND_ASM or CRYPTOPP_BOOL_RDSEED_ASM to 0.
52 #ifndef CRYPTOPP_CPUID_AVAILABLE
53 # if (CRYPTOPP_BOOL_X86 || CRYPTOPP_BOOL_X32 || CRYPTOPP_BOOL_X64)
54 # define CRYPTOPP_CPUID_AVAILABLE
55 # endif
56 #endif
57 
58 #if defined(CRYPTOPP_CPUID_AVAILABLE) && !defined(CRYPTOPP_BOOL_RDRAND_ASM)
59 # define CRYPTOPP_BOOL_RDRAND_ASM 1
60 #else
61 # define CRYPTOPP_BOOL_RDRAND_ASM 0
62 #endif
63 #if defined(CRYPTOPP_CPUID_AVAILABLE) && !defined(CRYPTOPP_BOOL_RDSEED_ASM)
64 # define CRYPTOPP_BOOL_RDSEED_ASM 1
65 #else
66 # define CRYPTOPP_BOOL_RDSEED_ASM 0
67 #endif
68 
69 #if defined(CRYPTOPP_CPUID_AVAILABLE)
70 # define MSC_INTRIN_COMPILER ((CRYPTOPP_MSC_VERSION >= 1700) || (CRYPTOPP_CLANG_VERSION >= 30200) || (_INTEL_COMPILER >= 1210))
71 # define GCC_INTRIN_COMPILER ((CRYPTOPP_GCC_VERSION >= 40600) || (CRYPTOPP_CLANG_VERSION >= 30200) || (_INTEL_COMPILER >= 1210))
72 #else
73 # define MSC_INTRIN_COMPILER 0
74 # define GCC_INTRIN_COMPILER 0
75 #endif
76 
77 // In general, the library's ASM code is best on Windows, and Intrinsics is
78 // the best code under GCC and compatibles. We favor them accordingly.
79 // The NASM code is optimized well on Linux, but its not easy to cut-in.
80 #if defined(CRYPTOPP_CPUID_AVAILABLE) && (CRYPTOPP_MSC_VERSION >= 1200)
81 # if CRYPTOPP_BOOL_RDRAND_ASM
82 # define MASM_RDRAND_ASM_AVAILABLE 1
83 # elif MSC_INTRIN_COMPILER
84 # define ALL_RDRAND_INTRIN_AVAILABLE 1
85 # endif
86 # if CRYPTOPP_BOOL_RDSEED_ASM
87 # define MASM_RDSEED_ASM_AVAILABLE 1
88 # elif MSC_INTRIN_COMPILER
89 # define ALL_RDSEED_INTRIN_AVAILABLE 1
90 # endif
91 #elif defined(CRYPTOPP_CPUID_AVAILABLE) && (CRYPTOPP_GCC_VERSION >= 30200)
92 # if GCC_INTRIN_COMPILER && defined(__RDRND__)
93 # define ALL_RDRAND_INTRIN_AVAILABLE 1
94 # elif CRYPTOPP_BOOL_RDRAND_ASM
95 # define GCC_RDRAND_ASM_AVAILABLE 1
96 # endif
97 # if GCC_INTRIN_COMPILER && defined(__RDSEED__)
98 # define ALL_RDSEED_INTRIN_AVAILABLE 1
99 # elif CRYPTOPP_BOOL_RDSEED_ASM
100 # define GCC_RDSEED_ASM_AVAILABLE 1
101 # endif
102 #endif
103 
104 // Debug diagnostics
105 #if 0
106 # if MASM_RDRAND_ASM_AVAILABLE
107 # pragma message ("MASM_RDRAND_ASM_AVAILABLE is 1")
108 # elif NASM_RDRAND_ASM_AVAILABLE
109 # pragma message ("NASM_RDRAND_ASM_AVAILABLE is 1")
110 # elif GCC_RDRAND_ASM_AVAILABLE
111 # pragma message ("GCC_RDRAND_ASM_AVAILABLE is 1")
112 # elif ALL_RDRAND_INTRIN_AVAILABLE
113 # pragma message ("ALL_RDRAND_INTRIN_AVAILABLE is 1")
114 # else
115 # pragma message ("RDRAND is not available")
116 # endif
117 # if MASM_RDSEED_ASM_AVAILABLE
118 # pragma message ("MASM_RDSEED_ASM_AVAILABLE is 1")
119 # elif NASM_RDSEED_ASM_AVAILABLE
120 # pragma message ("NASM_RDSEED_ASM_AVAILABLE is 1")
121 # elif GCC_RDSEED_ASM_AVAILABLE
122 # pragma message ("GCC_RDSEED_ASM_AVAILABLE is 1")
123 # elif ALL_RDSEED_INTRIN_AVAILABLE
124 # pragma message ("ALL_RDSEED_INTRIN_AVAILABLE is 1")
125 # else
126 # pragma message ("RDSEED is not available")
127 # endif
128 #endif
129 
130 /////////////////////////////////////////////////////////////////////
131 /////////////////////////////////////////////////////////////////////
132 
133 #if (ALL_RDRAND_INTRIN_AVAILABLE || ALL_RDSEED_INTRIN_AVAILABLE)
134 # include <immintrin.h> // rdrand, MSC, ICC, and GCC
135 # if defined(__has_include)
136 # if __has_include(<x86intrin.h>)
137 # include <x86intrin.h> // rdseed for some compilers, like GCC
138 # endif
139 # endif
140 #endif
141 
142 #if MASM_RDRAND_ASM_AVAILABLE
143 # ifdef _M_X64
144 extern "C" int CRYPTOPP_FASTCALL MASM_RRA_GenerateBlock(byte*, size_t, unsigned int);
145 // # pragma comment(lib, "rdrand-x64.lib")
146 # else
147 extern "C" int MASM_RRA_GenerateBlock(byte*, size_t, unsigned int);
148 // # pragma comment(lib, "rdrand-x86.lib")
149 # endif
150 #endif
151 
152 #if MASM_RDSEED_ASM_AVAILABLE
153 # ifdef _M_X64
154 extern "C" int CRYPTOPP_FASTCALL MASM_RSA_GenerateBlock(byte*, size_t, unsigned int);
155 // # pragma comment(lib, "rdrand-x64.lib")
156 # else
157 extern "C" int MASM_RSA_GenerateBlock(byte*, size_t, unsigned int);
158 // # pragma comment(lib, "rdrand-x86.lib")
159 # endif
160 #endif
161 
162 #if NASM_RDRAND_ASM_AVAILABLE
163 extern "C" int NASM_RRA_GenerateBlock(byte*, size_t, unsigned int);
164 #endif
165 
166 #if NASM_RDSEED_ASM_AVAILABLE
167 extern "C" int NASM_RSA_GenerateBlock(byte*, size_t, unsigned int);
168 #endif
169 
170 /////////////////////////////////////////////////////////////////////
171 /////////////////////////////////////////////////////////////////////
172 
173 NAMESPACE_BEGIN(CryptoPP)
174 
175 #if ALL_RDRAND_INTRIN_AVAILABLE
176 static int ALL_RRI_GenerateBlock(byte *output, size_t size, unsigned int safety)
177 {
178  assert((output && size) || !(output || size));
179 #if CRYPTOPP_BOOL_X64 || CRYTPOPP_BOOL_X32
180  word64 val;
181 #else
182  word32 val;
183 #endif
184 
185  while (size >= sizeof(val))
186  {
187 #if CRYPTOPP_BOOL_X64 || CRYTPOPP_BOOL_X32
188  if (_rdrand64_step((word64*)output))
189 #else
190  if (_rdrand32_step((word32*)output))
191 #endif
192  {
193  output += sizeof(val);
194  size -= sizeof(val);
195  }
196  else
197  {
198  if (!safety--)
199  return 0;
200  }
201  }
202 
203  if (size)
204  {
205 #if CRYPTOPP_BOOL_X64 || CRYTPOPP_BOOL_X32
206  if (_rdrand64_step(&val))
207 #else
208  if (_rdrand32_step(&val))
209 #endif
210  {
211  memcpy(output, &val, size);
212  size = 0;
213  }
214  else
215  {
216  if (!safety--)
217  return 0;
218  }
219  }
220 
221 #if CRYPTOPP_BOOL_X64 || CRYTPOPP_BOOL_X32
222  *((volatile word64*)&val) = 0;
223 #else
224  *((volatile word32*)&val) = 0;
225 #endif
226 
227  return int(size == 0);
228 }
229 #endif // ALL_RDRAND_INTRINSIC_AVAILABLE
230 
231 #if GCC_RDRAND_ASM_AVAILABLE
232 static int GCC_RRA_GenerateBlock(byte *output, size_t size, unsigned int safety)
233 {
234  assert((output && size) || !(output || size));
235 #if CRYPTOPP_BOOL_X64 || CRYPTOPP_BOOL_X32
236  word64 val;
237 #else
238  word32 val;
239 #endif
240  char rc;
241  while (size)
242  {
243  __asm__ volatile(
244 #if CRYPTOPP_BOOL_X64 || CRYPTOPP_BOOL_X32
245  ".byte 0x48, 0x0f, 0xc7, 0xf0;\n" // rdrand rax
246 #else
247  ".byte 0x0f, 0xc7, 0xf0;\n" // rdrand eax
248 #endif
249  "setc %1; "
250  : "=a" (val), "=qm" (rc)
251  :
252  : "cc"
253  );
254 
255  if (rc)
256  {
257  if (size >= sizeof(val))
258  {
259 #if defined(CRYPTOPP_ALLOW_UNALIGNED_DATA_ACCESS) && (CRYPTOPP_BOOL_X64 || CRYPTOPP_BOOL_X32)
260  *((word64*)output) = val;
261 #elif defined(CRYPTOPP_ALLOW_UNALIGNED_DATA_ACCESS) && (CRYPTOPP_BOOL_X86)
262  *((word32*)output) = val;
263 #else
264  memcpy(output, &val, sizeof(val));
265 #endif
266  output += sizeof(val);
267  size -= sizeof(val);
268  }
269  else
270  {
271  memcpy(output, &val, size);
272  size = 0;
273  }
274  }
275  else
276  {
277  if (!safety--)
278  break;
279  }
280  }
281 
282 #if CRYPTOPP_BOOL_X64 || CRYPTOPP_BOOL_X32
283  *((volatile word64*)&val) = 0;
284 #else
285  *((volatile word32*)&val) = 0;
286 #endif
287 
288  return int(size == 0);
289 }
290 
291 #endif // GCC_RDRAND_ASM_AVAILABLE
292 
293 #if (CRYPTOPP_BOOL_X86 || CRYPTOPP_BOOL_X32 || CRYPTOPP_BOOL_X64)
294 void RDRAND::GenerateBlock(byte *output, size_t size)
295 {
296  CRYPTOPP_UNUSED(output), CRYPTOPP_UNUSED(size);
297  assert((output && size) || !(output || size));
298 
299  if(!HasRDRAND())
300  throw NotImplemented("RDRAND: rdrand is not available on this platform");
301 
302  int rc; CRYPTOPP_UNUSED(rc);
303 #if MASM_RDRAND_ASM_AVAILABLE
304  rc = MASM_RRA_GenerateBlock(output, size, m_retries);
305  if (!rc) { throw RDRAND_Err("MASM_RRA_GenerateBlock"); }
306 #elif NASM_RDRAND_ASM_AVAILABLE
307  rc = NASM_RRA_GenerateBlock(output, size, m_retries);
308  if (!rc) { throw RDRAND_Err("NASM_RRA_GenerateBlock"); }
309 #elif ALL_RDRAND_INTRIN_AVAILABLE
310  rc = ALL_RRI_GenerateBlock(output, size, m_retries);
311  if (!rc) { throw RDRAND_Err("ALL_RRI_GenerateBlock"); }
312 #elif GCC_RDRAND_ASM_AVAILABLE
313  rc = GCC_RRA_GenerateBlock(output, size, m_retries);
314  if (!rc) { throw RDRAND_Err("GCC_RRA_GenerateBlock"); }
315 #else
316  // RDRAND not detected at compile time, and no suitable compiler found
317  throw NotImplemented("RDRAND: failed to find a suitable implementation???");
318 #endif // CRYPTOPP_CPUID_AVAILABLE
319 }
320 
321 void RDRAND::DiscardBytes(size_t n)
322 {
323  // RoundUpToMultipleOf is used because a full word is read, and its cheaper
324  // to discard full words. There's no sense in dealing with tail bytes.
325  assert(HasRDRAND());
326 #if CRYPTOPP_BOOL_X64 || CRYPTOPP_BOOL_X32
328  n = RoundUpToMultipleOf(n, sizeof(word64));
329 #else
331  n = RoundUpToMultipleOf(n, sizeof(word32));
332 #endif
333 
334  size_t count = STDMIN(n, discard.SizeInBytes());
335  while (count)
336  {
337  GenerateBlock(discard.BytePtr(), count);
338  n -= count;
339  count = STDMIN(n, discard.SizeInBytes());
340  }
341 }
342 #endif // CRYPTOPP_BOOL_X86 || CRYPTOPP_BOOL_X32 || CRYPTOPP_BOOL_X64
343 
344 /////////////////////////////////////////////////////////////////////
345 /////////////////////////////////////////////////////////////////////
346 
347 #if ALL_RDSEED_INTRIN_AVAILABLE
348 static int ALL_RSI_GenerateBlock(byte *output, size_t size, unsigned int safety)
349 {
350  assert((output && size) || !(output || size));
351 #if CRYPTOPP_BOOL_X64 || CRYPTOPP_BOOL_X32
352  word64 val;
353 #else
354  word32 val;
355 #endif
356 
357  while (size >= sizeof(val))
358  {
359 #if CRYPTOPP_BOOL_X64 || CRYPTOPP_BOOL_X32
360  if (_rdseed64_step((word64*)output))
361 #else
362  if (_rdseed32_step((word32*)output))
363 #endif
364  {
365  output += sizeof(val);
366  size -= sizeof(val);
367  }
368  else
369  {
370  if (!safety--)
371  return 0;
372  }
373  }
374 
375  if (size)
376  {
377 #if CRYPTOPP_BOOL_X64 || CRYPTOPP_BOOL_X32
378  if (_rdseed64_step(&val))
379 #else
380  if (_rdseed32_step(&val))
381 #endif
382  {
383  memcpy(output, &val, size);
384  size = 0;
385  }
386  else
387  {
388  if (!safety--)
389  return 0;
390  }
391  }
392 
393 #if CRYPTOPP_BOOL_X64 || CRYPTOPP_BOOL_X32
394  *((volatile word64*)&val) = 0;
395 #else
396  *((volatile word32*)&val) = 0;
397 #endif
398 
399  return int(size == 0);
400 }
401 #endif // ALL_RDSEED_INTRIN_AVAILABLE
402 
403 #if GCC_RDSEED_ASM_AVAILABLE
404 static int GCC_RSA_GenerateBlock(byte *output, size_t size, unsigned int safety)
405 {
406  assert((output && size) || !(output || size));
407 #if CRYPTOPP_BOOL_X64 || CRYPTOPP_BOOL_X32
408  word64 val;
409 #else
410  word32 val;
411 #endif
412  char rc;
413  while (size)
414  {
415  __asm__ volatile(
416 #if CRYPTOPP_BOOL_X64 || CRYPTOPP_BOOL_X32
417  ".byte 0x48, 0x0f, 0xc7, 0xf8;\n" // rdseed rax
418 #else
419  ".byte 0x0f, 0xc7, 0xf8;\n" // rdseed eax
420 #endif
421  "setc %1; "
422  : "=a" (val), "=qm" (rc)
423  :
424  : "cc"
425  );
426 
427  if (rc)
428  {
429  if (size >= sizeof(val))
430  {
431 #if defined(CRYPTOPP_ALLOW_UNALIGNED_DATA_ACCESS) && (CRYPTOPP_BOOL_X64 || CRYPTOPP_BOOL_X32)
432  *((word64*)output) = val;
433 #elif defined(CRYPTOPP_ALLOW_UNALIGNED_DATA_ACCESS) && (CRYPTOPP_BOOL_X86)
434  *((word32*)output) = val;
435 #else
436  memcpy(output, &val, sizeof(val));
437 #endif
438  output += sizeof(val);
439  size -= sizeof(val);
440  }
441  else
442  {
443  memcpy(output, &val, size);
444  size = 0;
445  }
446  }
447  else
448  {
449  if (!safety--)
450  break;
451  }
452  }
453 
454 #if CRYPTOPP_BOOL_X64 || CRYPTOPP_BOOL_X32
455  *((volatile word64*)&val) = 0;
456 #else
457  *((volatile word32*)&val) = 0;
458 #endif
459 
460  return int(size == 0);
461 }
462 #endif // GCC_RDSEED_ASM_AVAILABLE
463 
464 #if (CRYPTOPP_BOOL_X86 || CRYPTOPP_BOOL_X32 || CRYPTOPP_BOOL_X64)
465 void RDSEED::GenerateBlock(byte *output, size_t size)
466 {
467  CRYPTOPP_UNUSED(output), CRYPTOPP_UNUSED(size);
468  assert((output && size) || !(output || size));
469 
470  if(!HasRDSEED())
471  throw NotImplemented("RDSEED: rdseed is not available on this platform");
472 
473  int rc; CRYPTOPP_UNUSED(rc);
474 #if MASM_RDSEED_ASM_AVAILABLE
475  rc = MASM_RSA_GenerateBlock(output, size, m_retries);
476  if (!rc) { throw RDSEED_Err("MASM_RSA_GenerateBlock"); }
477 #elif NASM_RDSEED_ASM_AVAILABLE
478  rc = NASM_RSA_GenerateBlock(output, size, m_retries);
479  if (!rc) { throw RDRAND_Err("NASM_RSA_GenerateBlock"); }
480 #elif ALL_RDSEED_INTRIN_AVAILABLE
481  rc = ALL_RSI_GenerateBlock(output, size, m_retries);
482  if (!rc) { throw RDSEED_Err("ALL_RSI_GenerateBlock"); }
483 #elif GCC_RDSEED_ASM_AVAILABLE
484  rc = GCC_RSA_GenerateBlock(output, size, m_retries);
485  if (!rc) { throw RDSEED_Err("GCC_RSA_GenerateBlock"); }
486 #else
487  // RDSEED not detected at compile time, and no suitable compiler found
488  throw NotImplemented("RDSEED: failed to find a suitable implementation???");
489 #endif
490 }
491 
492 void RDSEED::DiscardBytes(size_t n)
493 {
494  // RoundUpToMultipleOf is used because a full word is read, and its cheaper
495  // to discard full words. There's no sense in dealing with tail bytes.
496  assert(HasRDSEED());
497 #if CRYPTOPP_BOOL_X64 || CRYPTOPP_BOOL_X32
499  n = RoundUpToMultipleOf(n, sizeof(word64));
500 #else
502  n = RoundUpToMultipleOf(n, sizeof(word32));
503 #endif
504 
505  size_t count = STDMIN(n, discard.SizeInBytes());
506  while (count)
507  {
508  GenerateBlock(discard.BytePtr(), count);
509  n -= count;
510  count = STDMIN(n, discard.SizeInBytes());
511  }
512 }
513 #endif // CRYPTOPP_BOOL_X86 || CRYPTOPP_BOOL_X32 || CRYPTOPP_BOOL_X64
514 
515 NAMESPACE_END
virtual void GenerateBlock(byte *output, size_t size)
Generate random array of bytes.
Definition: rdrand.h:149
virtual void GenerateBlock(byte *output, size_t size)
Generate random array of bytes.
Definition: rdrand.h:70
Abstract base classes that provide a uniform interface to this library.
Library configuration file.
Classes for RDRAND and RDSEED.
Classes and functions for secure memory allocations.
Exception thrown when a RDRAND generator encounters a generator related error.
Definition: rdrand.h:28
virtual void DiscardBytes(size_t n)
Generate and discard n bytes.
Definition: rdrand.h:163
A method was called which was not implemented.
Definition: cryptlib.h:187
virtual void DiscardBytes(size_t n)
Generate and discard n bytes.
Definition: rdrand.h:84
Fixed size stack-based SecBlock.
Definition: secblock.h:741
const T & STDMIN(const T &a, const T &b)
Replacement function for std::min.
Definition: misc.h:386
Classes, functions, intrinsics and features for X86, X32 nd X64 assembly.
T1 RoundUpToMultipleOf(const T1 &n, const T2 &m)
Rounds a value up to a multiple of a second value.
Definition: misc.h:759
Crypto++ library namespace.
size_type SizeInBytes() const
Provides the number of bytes in the SecBlock.
Definition: secblock.h:523
Exception thrown when a RDSEED generator encounters a generator related error.
Definition: rdrand.h:107
byte * BytePtr()
Provides a byte pointer to the first element in the memory block.
Definition: secblock.h:516