CCF
Loading...
Searching...
No Matches
entropy.h
Go to the documentation of this file.
1// Copyright (c) Microsoft Corporation. All rights reserved.
2// Licensed under the Apache 2.0 License.
3#pragma once
4
6
7#include <cassert>
8#include <cstdint>
9#include <cstring>
10#include <memory>
11#include <stdexcept>
12#include <utility>
13#include <vector>
14
15// Adapted from:
16// https://software.intel.com/en-us/articles/intel-digital-random-number-generator-drng-software-implementation-guide
17
18#define DRNG_NO_SUPPORT 0x0
19#define DRNG_HAS_RDRAND 0x1
20#define DRNG_HAS_RDSEED 0x2
21
22// `It is recommended that applications attempt 10 retries in a tight loop in
23// the unlikely event that the RDRAND instruction does not return a random
24// number. This number is based on a binomial probability argument: given
25// the design margins of the DRNG, the odds of ten failures in a row are
26// astronomically small and would in fact be an indication of a larger CPU
27// issue.`
28#define RDRAND_RETRIES 10
29
30namespace ccf::crypto
31{
32 using rng_func_t = int (*)(void* ctx, unsigned char* output, size_t len);
33
34 class Entropy
35 {
36 public:
37 Entropy() = default;
38 virtual ~Entropy() = default;
39
43 virtual std::vector<uint8_t> random(size_t len) = 0;
44
48 virtual void random(unsigned char* data, size_t len) = 0;
49
52 virtual uint64_t random64() = 0;
53 };
54
55 class IntelDRNG : public Entropy
56 {
57 private:
58 static int get_drng_support()
59 {
60 thread_local int drng_features = -1;
61
62 /* So we don't call cpuid multiple times for the same information */
63 if (drng_features == -1)
64 {
65 drng_features = DRNG_NO_SUPPORT;
66
67 if (ccf::pal::is_intel_cpu())
68 {
70
71 ccf::pal::cpuid(&info, 1, 0);
72
73 if ((info.ecx & 0x40000000) == 0x40000000)
74 drng_features |= DRNG_HAS_RDRAND;
75
76 cpuid(&info, 7, 0);
77
78 if ((info.ebx & 0x40000) == 0x40000)
79 drng_features |= DRNG_HAS_RDSEED;
80 }
81 }
82
83 return drng_features;
84 }
85
86 static bool rdrand16_step(uint16_t* rand)
87 {
88 unsigned char ok;
89 // @ccc allows placing a constraint on the carry flag ('@cc c') without
90 // having to write to a separate register, see
91 // https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html for more details.
92 asm volatile("rdrand %0" : "=r"(*rand), "=@ccc"(ok));
93 return ok;
94 }
95
96 static bool rdrand32_step(uint32_t* rand)
97 {
98 unsigned char ok;
99 asm volatile("rdrand %0" : "=r"(*rand), "=@ccc"(ok));
100 return ok;
101 }
102
103 static bool rdrand64_step(uint64_t* rand)
104 {
105 unsigned char ok;
106 asm volatile("rdrand %0" : "=r"(*rand), "=@ccc"(ok));
107 return ok;
108 }
109
110 static int rdrand16_retry(unsigned int retries, uint16_t* rand)
111 {
112 unsigned int count = 0;
113
114 while (count <= retries)
115 {
116 if (rdrand16_step(rand))
117 return 1;
118 ++count;
119 }
120
121 return 0;
122 }
123
124 static int rdrand32_retry(unsigned int retries, uint32_t* rand)
125 {
126 unsigned int count = 0;
127
128 while (count <= retries)
129 {
130 if (rdrand32_step(rand))
131 return 1;
132
133 ++count;
134 }
135
136 return 0;
137 }
138
139 static int rdrand64_retry(unsigned int retries, uint64_t* rand)
140 {
141 unsigned int count = 0;
142
143 while (count <= retries)
144 {
145 if (rdrand64_step(rand))
146 return 1;
147
148 ++count;
149 }
150
151 return 0;
152 }
153
154 static unsigned int rdrand_get_bytes(unsigned int n, unsigned char* dest)
155 {
156 unsigned char *headstart, *tailstart = nullptr;
157 uint64_t* blockstart;
158 unsigned int count, ltail, lhead, lblock;
159 uint64_t i, temprand;
160
161 /* Get the address of the first 64-bit aligned block in the
162 * destination buffer. */
163 headstart = dest;
164 if (((uint64_t)headstart % (uint64_t)8) == 0)
165 {
166 blockstart = (uint64_t*)headstart;
167 lblock = n;
168 lhead = 0;
169 }
170 else
171 {
172 blockstart =
173 (uint64_t*)(((uint64_t)headstart & ~(uint64_t)7) + (uint64_t)8);
174 lhead = (unsigned int)((uint64_t)blockstart - (uint64_t)headstart);
175 lblock =
176 ((n - lhead) & ~(unsigned int)7); // cwinter: this bit is/as buggy in
177 // the Intel examples.
178 }
179
180 /* Compute the number of 64-bit blocks and the remaining number
181 * of bytes (the tail) */
182 ltail = n - lblock - lhead;
183 count = lblock / 8; /* The number 64-bit rands needed */
184
185 assert(lhead < 8);
186 assert(lblock <= n);
187 assert(ltail < 8);
188
189 if (ltail)
190 tailstart = (unsigned char*)((uint64_t)blockstart + (uint64_t)lblock);
191
192 /* Populate the starting, mis-aligned section (the head) */
193 if (lhead)
194 {
195 if (!rdrand64_retry(RDRAND_RETRIES, &temprand))
196 return 0;
197 memcpy(headstart, &temprand, lhead);
198 }
199
200 /* Populate the central, aligned block */
201 for (i = 0; i < count; ++i, ++blockstart)
202 {
203 if (!rdrand64_retry(RDRAND_RETRIES, blockstart))
204 return i * 8 + lhead;
205 }
206
207 /* Populate the tail */
208 if (ltail)
209 {
210 if (!rdrand64_retry(RDRAND_RETRIES, &temprand))
211 return count * 8 + lhead;
212 memcpy(tailstart, &temprand, ltail);
213 }
214
215 return n;
216 }
217
218 // The following three functions should be used to generate
219 // randomness that will be used as seed for another RNG
220 static bool rdseed16_step(uint16_t* seed)
221 {
222 unsigned char ok;
223 asm volatile("rdseed %0" : "=r"(*seed), "=@ccc"(ok));
224 return ok;
225 }
226
227 static bool rdseed32_step(uint32_t* seed)
228 {
229 unsigned char ok;
230 asm volatile("rdseed %0" : "=r"(*seed), "=@ccc"(ok));
231 return ok;
232 }
233
234 static bool rdseed64_step(uint64_t* seed)
235 {
236 unsigned char ok;
237 asm volatile("rdseed %0" : "=r"(*seed), "=@ccc"(ok));
238 return ok;
239 }
240
241 public:
243 {
244 if (!is_drng_supported())
245 throw std::logic_error("No support for RDRAND / RDSEED on this CPU.");
246 }
247
252 std::vector<uint8_t> random(size_t len) override
253 {
254 std::vector<uint8_t> buf(len);
255
256 if (rdrand_get_bytes(buf.size(), buf.data()) < buf.size())
257 throw std::logic_error("Couldn't create random data");
258
259 return buf;
260 }
261
265 uint64_t random64() override
266 {
267 uint64_t rnd;
268 uint64_t len = sizeof(uint64_t);
269
270 if (rdrand_get_bytes(len, reinterpret_cast<unsigned char*>(&rnd)) < len)
271 {
272 throw std::logic_error("Couldn't create random data");
273 }
274
275 return rnd;
276 }
277
282 void random(unsigned char* data, size_t len) override
283 {
284 if (rdrand_get_bytes(len, data) < len)
285 throw std::logic_error("Couldn't create random data");
286 }
287
288 static int rng(void*, unsigned char* output, size_t len)
289 {
290 if (rdrand_get_bytes(len, output) < len)
291 throw std::logic_error("Couldn't create random data");
292 return 0;
293 }
294
295 static bool is_drng_supported()
296 {
297 return (get_drng_support() & (DRNG_HAS_RDRAND | DRNG_HAS_RDSEED)) ==
299 }
300 };
301
302 static bool use_drng = IntelDRNG::is_drng_supported();
303 using EntropyPtr = std::shared_ptr<Entropy>;
304 static EntropyPtr intel_drng_ptr;
305
307}
Definition entropy.h:35
virtual void random(unsigned char *data, size_t len)=0
virtual std::vector< uint8_t > random(size_t len)=0
virtual ~Entropy()=default
virtual uint64_t random64()=0
Definition entropy.h:56
static bool is_drng_supported()
Definition entropy.h:295
IntelDRNG()
Definition entropy.h:242
uint64_t random64() override
Definition entropy.h:265
static int rng(void *, unsigned char *output, size_t len)
Definition entropy.h:288
void random(unsigned char *data, size_t len) override
Definition entropy.h:282
std::vector< uint8_t > random(size_t len) override
Definition entropy.h:252
#define DRNG_HAS_RDRAND
Definition entropy.h:19
#define DRNG_HAS_RDSEED
Definition entropy.h:20
#define DRNG_NO_SUPPORT
Definition entropy.h:18
#define RDRAND_RETRIES
Definition entropy.h:28
Definition base64.h:9
int(*)(void *ctx, unsigned char *output, size_t len) rng_func_t
Definition entropy.h:32
EntropyPtr get_entropy()
Definition entropy.cpp:10
std::shared_ptr< Entropy > EntropyPtr
Definition entropy.h:303
Definition hardware_info.h:12
uint64_t ecx
Definition hardware_info.h:15
uint64_t ebx
Definition hardware_info.h:14