Crypto++  8.2
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
11 // at Crypto++ 5.6.3. At compile time, it uses CRYPTOPP_BOOL_{X86|X32|X64}
12 // to select an implementation or throws "NotImplemented". Users of the
13 // classes should call HasRDRAND() or HasRDSEED() to determine if a
14 // generator is available at runtime.
15 // The original classes accepted a retry count. Retries were superflous for
16 // RDRAND, and RDSEED encountered a failure about 1 in 256 bytes depending
17 // on the processor. Retries were removed at Crypto++ 6.0 because
18 // GenerateBlock unconditionally retries and always fulfills the request.
19 // Intel recommends using a retry count in case RDRAND or RDSEED circuit
20 // is bad. This implemenation does not follow the advice and requires
21 // good silicon. If the circuit or processor is bad then the user has
22 // bigger problems than generating random numbers.
23 
24 /////////////////////////////////////////////////////////////////////
25 /////////////////////////////////////////////////////////////////////
26 
27 #if defined(CRYPTOPP_CPUID_AVAILABLE) && !defined(CRYPTOPP_DISABLE_ASM)
28 
29 # if defined(CRYPTOPP_MSC_VERSION)
30 # define MASM_RDRAND_ASM_AVAILABLE 1
31 # define MASM_RDSEED_ASM_AVAILABLE 1
32 # endif
33 
34 # if (__SUNPRO_CC >= 0x5100) || (CRYPTOPP_APPLE_CLANG_VERSION >= 30000) || \
35  (CRYPTOPP_CLANG_VERSION >= 20800) || (CRYPTOPP_GCC_VERSION >= 30200)
36 # define GCC_RDRAND_ASM_AVAILABLE 1
37 # define GCC_RDSEED_ASM_AVAILABLE 1
38 # endif
39 
40 #endif // CRYPTOPP_CPUID_AVAILABLE
41 
42 typedef unsigned char byte;
43 
44 #if MASM_RDRAND_ASM_AVAILABLE
45 extern "C" void CRYPTOPP_FASTCALL MASM_RDRAND_GenerateBlock(byte*, size_t);
46 #endif
47 
48 #if MASM_RDSEED_ASM_AVAILABLE
49 extern "C" void CRYPTOPP_FASTCALL MASM_RDSEED_GenerateBlock(byte*, size_t);
50 #endif
51 
52 /////////////////////////////////////////////////////////////////////
53 /////////////////////////////////////////////////////////////////////
54 
55 NAMESPACE_BEGIN(CryptoPP)
56 
57 #if defined(CRYPTOPP_CPUID_AVAILABLE) && !defined(CRYPTOPP_DISABLE_ASM)
58 
59 // Fills 4 bytes
60 inline void RDRAND32(void* output)
61 {
62  CRYPTOPP_UNUSED(output); // MSC warning
63 #if defined(GCC_RDRAND_ASM_AVAILABLE)
64  __asm__ __volatile__
65  (
66  "1:\n"
67  ".byte 0x0f, 0xc7, 0xf0;\n"
68  "jnc 1b;\n"
69  : "=a" (*reinterpret_cast<word32*>(output))
70  : : "cc"
71  );
72 #endif
73 }
74 
75 #if (CRYPTOPP_BOOL_X64 || CRYPTOPP_BOOL_X32)
76 // Fills 8 bytes
77 inline void RDRAND64(void* output)
78 {
79  CRYPTOPP_UNUSED(output); // MSC warning
80 #if defined(GCC_RDRAND_ASM_AVAILABLE)
81  __asm__ __volatile__
82  (
83  "1:\n"
84  ".byte 0x48, 0x0f, 0xc7, 0xf0;\n"
85  "jnc 1b;\n"
86  : "=a" (*reinterpret_cast<word64*>(output))
87  : : "cc"
88  );
89 #endif
90 }
91 #endif // RDRAND64
92 
94 {
95  if (!HasRDRAND())
96  throw RDRAND_Err("HasRDRAND");
97 }
98 
99 void RDRAND::GenerateBlock(byte *output, size_t size)
100 {
101  CRYPTOPP_ASSERT((output && size) || !(output || size));
102  if (size == 0) return;
103 
104 #if defined(MASM_RDRAND_ASM_AVAILABLE)
105 
106  MASM_RDRAND_GenerateBlock(output, size);
107 
108 #elif defined(GCC_RDRAND_ASM_AVAILABLE)
109 
110 # if (CRYPTOPP_BOOL_X64 || CRYPTOPP_BOOL_X32)
111  size_t i = 0;
112  for (i = 0; i < size/8; i++)
113  RDRAND64(output+i*8);
114 
115  output += i*8;
116  size -= i*8;
117 
118  if (size)
119  {
120  word64 val;
121  RDRAND64(&val);
122  std::memcpy(output, &val, size);
123  }
124 # else
125  size_t i = 0;
126  for (i = 0; i < size/4; i++)
127  RDRAND32(output+i*4);
128 
129  output += i*4;
130  size -= i*4;
131 
132  if (size)
133  {
134  word32 val;
135  RDRAND32(&val);
136  std::memcpy(output, &val, size);
137  }
138 # endif
139 #else
140  // No suitable compiler found
141  CRYPTOPP_UNUSED(output);
142  throw NotImplemented("RDRAND: failed to find a suitable implementation");
143 #endif
144 }
145 
146 void RDRAND::DiscardBytes(size_t n)
147 {
148  // RoundUpToMultipleOf is used because a full word is read, and its cheaper
149  // to discard full words. There's no sense in dealing with tail bytes.
151  n = RoundUpToMultipleOf(n, sizeof(word64));
152 
153  size_t count = STDMIN(n, discard.SizeInBytes());
154  while (count)
155  {
156  GenerateBlock(discard.BytePtr(), count);
157  n -= count;
158  count = STDMIN(n, discard.SizeInBytes());
159  }
160 }
161 
162 /////////////////////////////////////////////////////////////////////
163 /////////////////////////////////////////////////////////////////////
164 
165 // Fills 4 bytes
166 inline void RDSEED32(void* output)
167 {
168  CRYPTOPP_UNUSED(output); // MSC warning
169 #if defined(GCC_RDSEED_ASM_AVAILABLE)
170  __asm__ __volatile__
171  (
172  "1:\n"
173  ".byte 0x0f, 0xc7, 0xf8;\n"
174  "jnc 1b;\n"
175  : "=a" (*reinterpret_cast<word32*>(output))
176  : : "cc"
177  );
178 #endif
179 }
180 
181 #if (CRYPTOPP_BOOL_X64 || CRYPTOPP_BOOL_X32)
182 // Fills 8 bytes
183 inline void RDSEED64(void* output)
184 {
185  CRYPTOPP_UNUSED(output); // MSC warning
186 #if defined(GCC_RDSEED_ASM_AVAILABLE)
187  __asm__ __volatile__
188  (
189  "1:\n"
190  ".byte 0x48, 0x0f, 0xc7, 0xf8;\n"
191  "jnc 1b;\n"
192  : "=a" (*reinterpret_cast<word64*>(output))
193  : : "cc"
194  );
195 #endif
196 }
197 #endif // RDSEED64
198 
200 {
201  if (!HasRDSEED())
202  throw RDSEED_Err("HasRDSEED");
203 }
204 
205 void RDSEED::GenerateBlock(byte *output, size_t size)
206 {
207  CRYPTOPP_ASSERT((output && size) || !(output || size));
208  if (size == 0) return;
209 
210 #if defined(MASM_RDSEED_ASM_AVAILABLE)
211 
212  MASM_RDSEED_GenerateBlock(output, size);
213 
214 #elif defined(GCC_RDSEED_ASM_AVAILABLE)
215 # if (CRYPTOPP_BOOL_X64 || CRYPTOPP_BOOL_X32)
216  size_t i = 0;
217  for (i = 0; i < size/8; i++)
218  RDSEED64(output+i*8);
219 
220  output += i*8;
221  size -= i*8;
222 
223  if (size)
224  {
225  word64 val;
226  RDSEED64(&val);
227  std::memcpy(output, &val, size);
228  }
229 # else
230  size_t i = 0;
231  for (i = 0; i < size/4; i++)
232  RDSEED32(output+i*4);
233 
234  output += i*4;
235  size -= i*4;
236 
237  if (size)
238  {
239  word32 val;
240  RDSEED32(&val);
241  std::memcpy(output, &val, size);
242  }
243 # endif
244 #else
245  // No suitable compiler found
246  CRYPTOPP_UNUSED(output);
247  throw NotImplemented("RDSEED: failed to find a suitable implementation");
248 #endif // RDSEED64
249 }
250 
251 void RDSEED::DiscardBytes(size_t n)
252 {
253  // RoundUpToMultipleOf is used because a full word is read, and its cheaper
254  // to discard full words. There's no sense in dealing with tail bytes.
256  n = RoundUpToMultipleOf(n, sizeof(word64));
257 
258  size_t count = STDMIN(n, discard.SizeInBytes());
259  while (count)
260  {
261  GenerateBlock(discard.BytePtr(), count);
262  n -= count;
263  count = STDMIN(n, discard.SizeInBytes());
264  }
265 }
266 
267 #else // not CRYPTOPP_CPUID_AVAILABLE
268 
270 {
271  throw RDRAND_Err("HasRDRAND");
272 }
273 
274 void RDRAND::GenerateBlock(byte *output, size_t size)
275 {
276  // Constructor will throw, should not get here
277  CRYPTOPP_UNUSED(output); CRYPTOPP_UNUSED(size);
278 }
279 
280 void RDRAND::DiscardBytes(size_t n)
281 {
282  // Constructor will throw, should not get here
283  CRYPTOPP_UNUSED(n);
284 }
285 
287 {
288  throw RDSEED_Err("HasRDSEED");
289 }
290 
291 void RDSEED::GenerateBlock(byte *output, size_t size)
292 {
293  // Constructor will throw, should not get here
294  CRYPTOPP_UNUSED(output); CRYPTOPP_UNUSED(size);
295 }
296 
297 void RDSEED::DiscardBytes(size_t n)
298 {
299  // Constructor will throw, should not get here
300  CRYPTOPP_UNUSED(n);
301 }
302 
303 #endif // CRYPTOPP_CPUID_AVAILABLE
304 
305 NAMESPACE_END
virtual void DiscardBytes(size_t n)
Generate and discard n bytes.
Definition: rdrand.cpp:251
size_type SizeInBytes() const
Provides the number of bytes in the SecBlock.
Definition: secblock.h:811
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:247
RDSEED()
Construct a RDSEED generator.
Definition: rdrand.cpp:199
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:146
A method was called which was not implemented.
Definition: cryptlib.h:223
bool HasRDSEED()
Determines RDSEED availability.
Definition: cpu.h:258
Precompiled header file.
Fixed size stack-based SecBlock.
Definition: secblock.h:1077
const T & STDMIN(const T &a, const T &b)
Replacement function for std::min.
Definition: misc.h:567
#define CRYPTOPP_ASSERT(exp)
Debugging and diagnostic assertion.
Definition: trap.h:69
virtual void GenerateBlock(byte *output, size_t size)
Generate random array of bytes.
Definition: rdrand.cpp:205
Functions for CPU features and intrinsics.
virtual void GenerateBlock(byte *output, size_t size)
Generate random array of bytes.
Definition: rdrand.cpp:99
T1 RoundUpToMultipleOf(const T1 &n, const T2 &m)
Rounds a value up to a multiple of a second value.
Definition: misc.h:1085
Crypto++ library namespace.
Exception thrown when a RDSEED generator encounters a generator related error.
Definition: rdrand.h:92
RDRAND()
Construct a RDRAND generator.
Definition: rdrand.cpp:93
byte * BytePtr()
Provides a byte pointer to the first element in the memory block.
Definition: secblock.h:804