Crypto++ 8.0 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
93 RDRAND::RDRAND()
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.
150 FixedSizeSecBlock<word64, 16> discard;
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
199 RDSEED::RDSEED()
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.
255 FixedSizeSecBlock<word64, 16> discard;
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
269 RDRAND::RDRAND()
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
286 RDSEED::RDSEED()
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
RDSEED::DiscardBytes
virtual void DiscardBytes(size_t n)
Generate and discard n bytes.
Definition: rdrand.cpp:251
SecBlock::SizeInBytes
size_type SizeInBytes() const
Provides the number of bytes in the SecBlock.
Definition: secblock.h:811
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()
Determines RDRAND availability.
Definition: cpu.h:247
RDSEED::RDSEED
RDSEED()
Construct a RDSEED generator.
Definition: rdrand.cpp:199
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:146
NotImplemented
A method was called which was not implemented.
Definition: cryptlib.h:223
HasRDSEED
bool HasRDSEED()
Determines RDSEED availability.
Definition: cpu.h:258
pch.h
Precompiled header file.
FixedSizeSecBlock
Fixed size stack-based SecBlock.
Definition: secblock.h:1077
STDMIN
const T & STDMIN(const T &a, const T &b)
Replacement function for std::min.
Definition: misc.h:535
CRYPTOPP_ASSERT
#define CRYPTOPP_ASSERT(exp)
Debugging and diagnostic assertion.
Definition: trap.h:69
RDSEED::GenerateBlock
virtual void GenerateBlock(byte *output, size_t size)
Generate random array of bytes.
Definition: rdrand.cpp:205
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:99
RoundUpToMultipleOf
T1 RoundUpToMultipleOf(const T1 &n, const T2 &m)
Rounds a value up to a multiple of a second value.
Definition: misc.h:1053
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:93
SecBlock::BytePtr
byte * BytePtr()
Provides a byte pointer to the first element in the memory block.
Definition: secblock.h:804