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 CRYPTOPP_MSC_VERSION
28 # pragma warning(disable: 4702)
29 #endif
30
31 #if defined(CRYPTOPP_RDRAND_AVAILABLE)
32 # if defined(CRYPTOPP_MSC_VERSION)
33 # define MASM_RDRAND_ASM_AVAILABLE 1
34 # endif
35 # if (__SUNPRO_CC >= 0x5100) || (CRYPTOPP_APPLE_CLANG_VERSION >= 30000) || \
36 (CRYPTOPP_LLVM_CLANG_VERSION >= 20800) || (CRYPTOPP_GCC_VERSION >= 30200)
37 # define GCC_RDRAND_ASM_AVAILABLE 1
38 # endif
39 #endif // CRYPTOPP_RDRAND_AVAILABLE
40
41 #if defined(CRYPTOPP_RDSEED_AVAILABLE)
42 # if defined(CRYPTOPP_MSC_VERSION)
43 # define MASM_RDSEED_ASM_AVAILABLE 1
44 # endif
45 # if (__SUNPRO_CC >= 0x5100) || (CRYPTOPP_APPLE_CLANG_VERSION >= 30000) || \
46 (CRYPTOPP_LLVM_CLANG_VERSION >= 20800) || (CRYPTOPP_GCC_VERSION >= 30200)
47 # define GCC_RDSEED_ASM_AVAILABLE 1
48 # endif
49 #endif // CRYPTOPP_RDSEED_AVAILABLE
50
51 typedef unsigned char byte;
52
53 #if MASM_RDRAND_ASM_AVAILABLE
54 extern "C" void CRYPTOPP_FASTCALL MASM_RDRAND_GenerateBlock(byte*, size_t);
55 #endif
56
57 #if MASM_RDSEED_ASM_AVAILABLE
58 extern "C" void CRYPTOPP_FASTCALL MASM_RDSEED_GenerateBlock(byte*, size_t);
59 #endif
60
61 /////////////////////////////////////////////////////////////////////
62 /////////////////////////////////////////////////////////////////////
63
64 NAMESPACE_BEGIN(CryptoPP)
65
66 #if defined(CRYPTOPP_RDRAND_AVAILABLE)
67
68 // Fills 4 bytes
69 inline void RDRAND32(void* output)
70 {
71 CRYPTOPP_UNUSED(output); // MSC warning
72 # if defined(GCC_RDRAND_ASM_AVAILABLE)
73 __asm__ __volatile__
74 (
75 "1:\n"
76 ".byte 0x0f, 0xc7, 0xf0;\n"
77 "jnc 1b;\n"
78 : "=a" (*reinterpret_cast<word32*>(output))
79 : : "cc"
80 );
81 # endif
82 }
83
84 # if (CRYPTOPP_BOOL_X64 || CRYPTOPP_BOOL_X32)
85 // Fills 8 bytes
86 inline void RDRAND64(void* output)
87 {
88 CRYPTOPP_UNUSED(output); // MSC warning
89 # if defined(GCC_RDRAND_ASM_AVAILABLE)
90 __asm__ __volatile__
91 (
92 "1:\n"
93 ".byte 0x48, 0x0f, 0xc7, 0xf0;\n"
94 "jnc 1b;\n"
95 : "=a" (*reinterpret_cast<word64*>(output))
96 : : "cc"
97 );
98 # endif
99 }
100 # endif // RDRAND64
101
102 RDRAND::RDRAND()
103 {
104 if (!HasRDRAND())
105 throw RDRAND_Err("HasRDRAND");
106 }
107
108 void RDRAND::GenerateBlock(byte *output, size_t size)
109 {
110 CRYPTOPP_ASSERT((output && size) || !(output || size));
111 if (size == 0) return;
112
113 # if defined(MASM_RDRAND_ASM_AVAILABLE)
114
115 MASM_RDRAND_GenerateBlock(output, size);
116
117 # elif defined(GCC_RDRAND_ASM_AVAILABLE)
118
119 # if (CRYPTOPP_BOOL_X64 || CRYPTOPP_BOOL_X32)
120 size_t i = 0;
121 for (i = 0; i < size/8; i++)
122 RDRAND64(output+i*8);
123
124 output += i*8;
125 size -= i*8;
126
127 if (size)
128 {
129 word64 val;
130 RDRAND64(&val);
131 std::memcpy(output, &val, size);
132 }
133 # else
134 size_t i = 0;
135 for (i = 0; i < size/4; i++)
136 RDRAND32(output+i*4);
137
138 output += i*4;
139 size -= i*4;
140
141 if (size)
142 {
143 word32 val;
144 RDRAND32(&val);
145 std::memcpy(output, &val, size);
146 }
147 # endif
148 # else
149 // No suitable compiler found
150 CRYPTOPP_UNUSED(output);
151 throw NotImplemented("RDRAND: failed to find a suitable implementation");
152 # endif
153 }
154
155 void RDRAND::DiscardBytes(size_t n)
156 {
157 // RoundUpToMultipleOf is used because a full word is read, and its cheaper
158 // to discard full words. There's no sense in dealing with tail bytes.
159 FixedSizeSecBlock<word64, 16> discard;
160 n = RoundUpToMultipleOf(n, sizeof(word64));
161
162 size_t count = STDMIN(n, discard.SizeInBytes());
163 while (count)
164 {
165 GenerateBlock(discard.BytePtr(), count);
166 n -= count;
167 count = STDMIN(n, discard.SizeInBytes());
168 }
169 }
170
171 #endif // CRYPTOPP_RDRAND_AVAILABLE
172
173 /////////////////////////////////////////////////////////////////////
174 /////////////////////////////////////////////////////////////////////
175
176 #if defined(CRYPTOPP_RDSEED_AVAILABLE)
177
178 // Fills 4 bytes
179 inline void RDSEED32(void* output)
180 {
181 CRYPTOPP_UNUSED(output); // MSC warning
182 # if defined(GCC_RDSEED_ASM_AVAILABLE)
183 __asm__ __volatile__
184 (
185 "1:\n"
186 ".byte 0x0f, 0xc7, 0xf8;\n"
187 "jnc 1b;\n"
188 : "=a" (*reinterpret_cast<word32*>(output))
189 : : "cc"
190 );
191 # endif
192 }
193
194 # if (CRYPTOPP_BOOL_X64 || CRYPTOPP_BOOL_X32)
195 // Fills 8 bytes
196 inline void RDSEED64(void* output)
197 {
198 CRYPTOPP_UNUSED(output); // MSC warning
199 # if defined(GCC_RDSEED_ASM_AVAILABLE)
200 __asm__ __volatile__
201 (
202 "1:\n"
203 ".byte 0x48, 0x0f, 0xc7, 0xf8;\n"
204 "jnc 1b;\n"
205 : "=a" (*reinterpret_cast<word64*>(output))
206 : : "cc"
207 );
208 # endif
209 }
210 # endif // RDSEED64
211
212 RDSEED::RDSEED()
213 {
214 if (!HasRDSEED())
215 throw RDSEED_Err("HasRDSEED");
216 }
217
218 void RDSEED::GenerateBlock(byte *output, size_t size)
219 {
220 CRYPTOPP_ASSERT((output && size) || !(output || size));
221 if (size == 0) return;
222
223 # if defined(MASM_RDSEED_ASM_AVAILABLE)
224
225 MASM_RDSEED_GenerateBlock(output, size);
226
227 # elif defined(GCC_RDSEED_ASM_AVAILABLE)
228 # if (CRYPTOPP_BOOL_X64 || CRYPTOPP_BOOL_X32)
229 size_t i = 0;
230 for (i = 0; i < size/8; i++)
231 RDSEED64(output+i*8);
232
233 output += i*8;
234 size -= i*8;
235
236 if (size)
237 {
238 word64 val;
239 RDSEED64(&val);
240 std::memcpy(output, &val, size);
241 }
242 # else
243 size_t i = 0;
244 for (i = 0; i < size/4; i++)
245 RDSEED32(output+i*4);
246
247 output += i*4;
248 size -= i*4;
249
250 if (size)
251 {
252 word32 val;
253 RDSEED32(&val);
254 std::memcpy(output, &val, size);
255 }
256 # endif
257 # else
258 // No suitable compiler found
259 CRYPTOPP_UNUSED(output);
260 throw NotImplemented("RDSEED: failed to find a suitable implementation");
261 # endif // RDSEED64
262 }
263
264 void RDSEED::DiscardBytes(size_t n)
265 {
266 // RoundUpToMultipleOf is used because a full word is read, and its cheaper
267 // to discard full words. There's no sense in dealing with tail bytes.
268 FixedSizeSecBlock<word64, 16> discard;
269 n = RoundUpToMultipleOf(n, sizeof(word64));
270
271 size_t count = STDMIN(n, discard.SizeInBytes());
272 while (count)
273 {
274 GenerateBlock(discard.BytePtr(), count);
275 n -= count;
276 count = STDMIN(n, discard.SizeInBytes());
277 }
278 }
279
280 #endif // CRYPTOPP_RDSEED_AVAILABLE
281
282 /////////////////////////////////////////////////////////////////////
283 /////////////////////////////////////////////////////////////////////
284
285 #if !defined(CRYPTOPP_RDRAND_AVAILABLE)
286
287 RDRAND::RDRAND()
288 {
289 throw RDRAND_Err("HasRDRAND");
290 }
291
292 void RDRAND::GenerateBlock(byte *output, size_t size)
293 {
294 // Constructor will throw, should not get here
295 CRYPTOPP_UNUSED(output); CRYPTOPP_UNUSED(size);
296 }
297
298 void RDRAND::DiscardBytes(size_t n)
299 {
300 // Constructor will throw, should not get here
301 CRYPTOPP_UNUSED(n);
302 }
303
304 #endif // CRYPTOPP_RDRAND_AVAILABLE
305
306 #if !defined(CRYPTOPP_RDSEED_AVAILABLE)
307
308 RDSEED::RDSEED()
309 {
310 throw RDSEED_Err("HasRDSEED");
311 }
312
313 void RDSEED::GenerateBlock(byte *output, size_t size)
314 {
315 // Constructor will throw, should not get here
316 CRYPTOPP_UNUSED(output); CRYPTOPP_UNUSED(size);
317 }
318
319 void RDSEED::DiscardBytes(size_t n)
320 {
321 // Constructor will throw, should not get here
322 CRYPTOPP_UNUSED(n);
323 }
324
325 #endif // CRYPTOPP_RDSEED_AVAILABLE
326
327 NAMESPACE_END
RDSEED::DiscardBytes
virtual void DiscardBytes(size_t n)
Generate and discard n bytes.
Definition: rdrand.cpp:319
SecBlock::SizeInBytes
size_type SizeInBytes() const
Provides the number of bytes in the SecBlock.
Definition: secblock.h:845
cryptlib.h
Abstract base classes that provide a uniform interface to this library.
config.h
Library configuration file.
rdrand.h
Classes for RDRAND and RDSEED.
HasRDRAND
bool HasRDRAND()
Determine RDRAND availability.
Definition: cpu.h:290
RDSEED::RDSEED
RDSEED()
Construct a RDSEED generator.
Definition: rdrand.cpp:308
secblock.h
Classes and functions for secure memory allocations.
RDRAND_Err
Exception thrown when a RDRAND generator encounters a generator related error.
Definition: rdrand.h:38
RDRAND::DiscardBytes
virtual void DiscardBytes(size_t n)
Generate and discard n bytes.
Definition: rdrand.cpp:298
NotImplemented
A method was called which was not implemented.
Definition: cryptlib.h:232
HasRDSEED
bool HasRDSEED()
Determine RDSEED availability.
Definition: cpu.h:305
pch.h
Precompiled header file.
FixedSizeSecBlock
Fixed size stack-based SecBlock.
Definition: secblock.h:1116
STDMIN
const T & STDMIN(const T &a, const T &b)
Replacement function for std::min.
Definition: misc.h:635
CRYPTOPP_ASSERT
#define CRYPTOPP_ASSERT(exp)
Debugging and diagnostic assertion.
Definition: trap.h:68
RDSEED::GenerateBlock
virtual void GenerateBlock(byte *output, size_t size)
Generate random array of bytes.
Definition: rdrand.cpp:313
cpu.h
Functions for CPU features and intrinsics.
RDRAND::GenerateBlock
virtual void GenerateBlock(byte *output, size_t size)
Generate random array of bytes.
Definition: rdrand.cpp:292
RoundUpToMultipleOf
T1 RoundUpToMultipleOf(const T1 &n, const T2 &m)
Rounds a value up to a multiple of a second value.
Definition: misc.h:1154
CryptoPP
Crypto++ library namespace.
RDSEED_Err
Exception thrown when a RDSEED generator encounters a generator related error.
Definition: rdrand.h:92
RDRAND::RDRAND
RDRAND()
Construct a RDRAND generator.
Definition: rdrand.cpp:287
SecBlock::BytePtr
byte * BytePtr()
Provides a byte pointer to the first element in the memory block.
Definition: secblock.h:838