rdrand/lib.rs
1// Copyright © 2014, Simonas Kazlauskas <rdrand@kazlauskas.me>
2//
3// Permission to use, copy, modify, and/or distribute this software for any purpose with or without
4// fee is hereby granted, provided that the above copyright notice and this permission notice
5// appear in all copies.
6//
7// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
8// SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
9// AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
10// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
11// NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
12// OF THIS SOFTWARE.
13//! An implementation of random number generators based on `rdrand` and `rdseed` instructions.
14//!
15//! The random number generators provided by this crate are fairly slow (the latency for these
16//! instructions is pretty high), but provide high quality random bits. Caveat is: neither AMD’s
17//! nor Intel’s designs are public and therefore are not verifiable for lack of backdoors.
18//!
19//! Unless you know what you are doing, use the random number generators provided by the `rand`
20//! crate (such as `OsRng`) instead.
21//!
22//! Here are a measurements for select processor architectures. Check [Agner’s instruction tables]
23//! for up-to-date listings.
24//!
25//! <table>
26//! <tr>
27//! <th>Architecture</th>
28//! <th colspan="3">Latency (cycles)</th>
29//! <th>Maximum throughput (per core)</th>
30//! </tr>
31//! <tr>
32//! <td></td>
33//! <td>u16</td>
34//! <td>u32</td>
35//! <td>u64</td>
36//! <td></td>
37//! </tr>
38//! <tr>
39//! <td>AMD Ryzen</td>
40//! <td>~1200</td>
41//! <td>~1200</td>
42//! <td>~2500</td>
43//! <td>~12MB/s @ 3.7GHz</td>
44//! </tr>
45//! <tr>
46//! <td>Intel Skylake</td>
47//! <td>460</td>
48//! <td>460</td>
49//! <td>460</td>
50//! <td>~72MB/s @ 4.2GHz</td>
51//! </tr>
52//! <tr>
53//! <td>Intel Haswell</td>
54//! <td>320</td>
55//! <td>320</td>
56//! <td>320</td>
57//! <td>~110MB/s @ 4.4GHz</td>
58//! </tr>
59//! </table>
60//!
61//! [Agner’s instruction tables]: http://agner.org/optimize/
62#![cfg_attr(not(feature = "std"), no_std)]
63
64pub mod changelog;
65mod errors;
66
67pub use errors::ErrorCode;
68use rand_core::{CryptoRng, Error, RngCore};
69
70#[cold]
71#[inline(never)]
72pub(crate) fn busy_loop_fail(code: ErrorCode) -> ! {
73 panic!("{}", code);
74}
75
76/// A cryptographically secure statistically uniform, non-periodic and non-deterministic random bit
77/// generator.
78///
79/// Note that this generator may be implemented using a deterministic algorithm that is reseeded
80/// routinely from a non-deterministic entropy source to achieve the desirable properties.
81///
82/// This generator is a viable replacement to any generator, however, since nobody has audited
83/// this hardware implementation yet, the usual disclaimers as to their suitability apply.
84///
85/// It is potentially faster than `OsRng`, but is only supported by more recent architectures such
86/// as Intel Ivy Bridge and AMD Zen.
87#[derive(Clone, Copy)]
88pub struct RdRand(());
89
90/// A cryptographically secure non-deterministic random bit generator.
91///
92/// This generator produces high-entropy output and is suited to seed other pseudo-random
93/// generators.
94///
95/// This instruction is only supported by recent architectures such as Intel Broadwell and AMD Zen.
96///
97/// This generator is not intended for general random number generation purposes and should be used
98/// to seed other generators implementing [rand_core::SeedableRng].
99#[derive(Clone, Copy)]
100pub struct RdSeed(());
101
102impl CryptoRng for RdRand {}
103impl CryptoRng for RdSeed {}
104
105mod arch {
106 #[cfg(target_arch = "x86")]
107 pub use core::arch::x86::*;
108 #[cfg(target_arch = "x86_64")]
109 pub use core::arch::x86_64::*;
110
111 #[cfg(target_arch = "x86")]
112 pub(crate) unsafe fn _rdrand64_step(dest: &mut u64) -> i32 {
113 let mut ret1: u32 = 0;
114 let mut ret2: u32 = 0;
115 let ok = _rdrand32_step(&mut ret1) & _rdrand32_step(&mut ret2);
116 *dest = (ret1 as u64) << 32 | (ret2 as u64);
117 ok
118 }
119
120 #[cfg(target_arch = "x86")]
121 pub(crate) unsafe fn _rdseed64_step(dest: &mut u64) -> i32 {
122 let mut ret1: u32 = 0;
123 let mut ret2: u32 = 0;
124 let ok = _rdseed32_step(&mut ret1) & _rdseed32_step(&mut ret2);
125 *dest = (ret1 as u64) << 32 | (ret2 as u64);
126 ok
127 }
128}
129
130// See the following documentation for usage (in particular wrt retries) recommendations:
131//
132// https://software.intel.com/content/www/us/en/develop/articles/intel-digital-random-number-generator-drng-software-implementation-guide.html
133macro_rules! loop_rand {
134 ("rdrand", $el: ty, $step: path) => {{
135 let mut idx = 0;
136 loop {
137 let mut el: $el = 0;
138 if $step(&mut el) != 0 {
139 break Ok(el);
140 } else if idx == 10 {
141 break Err(ErrorCode::HardwareFailure);
142 }
143 idx += 1;
144 }
145 }};
146 ("rdseed", $el: ty, $step: path) => {{
147 let mut idx = 0;
148 loop {
149 let mut el: $el = 0;
150 if $step(&mut el) != 0 {
151 break Ok(el);
152 } else if idx == 127 {
153 break Err(ErrorCode::HardwareFailure);
154 }
155 idx += 1;
156 arch::_mm_pause();
157 }
158 }};
159}
160
161#[inline(always)]
162fn authentic_amd() -> bool {
163 let cpuid0 = unsafe { arch::__cpuid(0) };
164 matches!(
165 (cpuid0.ebx, cpuid0.ecx, cpuid0.edx),
166 (0x68747541, 0x444D4163, 0x69746E65)
167 )
168}
169
170#[inline(always)]
171fn amd_family(cpuid1: &arch::CpuidResult) -> u32 {
172 ((cpuid1.eax >> 8) & 0xF) + ((cpuid1.eax >> 20) & 0xFF)
173}
174
175#[inline(always)]
176fn has_rdrand(cpuid1: &arch::CpuidResult) -> bool {
177 const FLAG: u32 = 1 << 30;
178 cpuid1.ecx & FLAG == FLAG
179}
180
181#[inline(always)]
182fn has_rdseed() -> bool {
183 const FLAG: u32 = 1 << 18;
184 unsafe { arch::__cpuid(7).ebx & FLAG == FLAG }
185}
186
187/// NB: On AMD processor families < 0x17, we want to unconditionally disable RDRAND
188/// and RDSEED. Executing these instructions on these processors can return
189/// non-random data (0) while also reporting a success.
190///
191/// See:
192/// * https://github.com/systemd/systemd/issues/11810
193/// * https://lore.kernel.org/all/776cb5c2d33e7fd0d2893904724c0e52b394f24a.1565817448.git.thomas.lendacky@amd.com/
194///
195/// We take extra care to do so even if `-Ctarget-features=+rdrand` have been
196/// specified, in order to prevent users from shooting themselves in their feet.
197const FIRST_GOOD_AMD_FAMILY: u32 = 0x17;
198
199macro_rules! is_available {
200 ("rdrand") => {{
201 if authentic_amd() {
202 let cpuid1 = unsafe { arch::__cpuid(1) };
203 has_rdrand(&cpuid1) && amd_family(&cpuid1) >= FIRST_GOOD_AMD_FAMILY
204 } else {
205 cfg!(target_feature = "rdrand") || has_rdrand(&unsafe { arch::__cpuid(1) })
206 }
207 }};
208 ("rdseed") => {{
209 if authentic_amd() {
210 amd_family(&unsafe { arch::__cpuid(1) }) >= FIRST_GOOD_AMD_FAMILY && has_rdseed()
211 } else {
212 cfg!(target_feature = "rdrand") || has_rdseed()
213 }
214 }};
215}
216
217macro_rules! impl_rand {
218 ($gen:ident, $feat:tt, $step16: path, $step32:path, $step64:path,
219 maxstep = $maxstep:path, maxty = $maxty: ty) => {
220 impl $gen {
221 /// Create a new instance of the random number generator.
222 ///
223 /// This constructor checks whether the CPU the program is running on supports the
224 /// instruction necessary for this generator to operate. If the instruction is not
225 /// supported, an error is returned.
226 pub fn new() -> Result<Self, ErrorCode> {
227 if cfg!(target_env = "sgx") {
228 if cfg!(target_feature = $feat) {
229 Ok($gen(()))
230 } else {
231 Err(ErrorCode::UnsupportedInstruction)
232 }
233 } else if is_available!($feat) {
234 Ok($gen(()))
235 } else {
236 Err(ErrorCode::UnsupportedInstruction)
237 }
238 }
239
240 /// Create a new instance of the random number generator.
241 ///
242 /// # Safety
243 ///
244 /// This constructor is unsafe because it doesn't check that the CPU supports the
245 /// instruction, but devolves this responsibility to the caller.
246 pub unsafe fn new_unchecked() -> Self {
247 $gen(())
248 }
249
250 /// Generate a single random `u16` value.
251 ///
252 /// The underlying instruction may fail for variety reasons (such as actual hardware
253 /// failure or exhausted entropy), however the exact reason for the failure is not
254 /// usually exposed.
255 ///
256 /// This method will retry calling the instruction a few times, however if all the
257 /// attempts fail, it will return `None`.
258 ///
259 /// In case `Err` is returned, the caller should assume that a non-recoverable failure
260 /// has occured and use another random number genrator instead.
261 #[inline(always)]
262 pub fn try_next_u16(&self) -> Result<u16, ErrorCode> {
263 #[target_feature(enable = $feat)]
264 unsafe fn imp() -> Result<u16, ErrorCode> {
265 loop_rand!($feat, u16, $step16)
266 }
267 unsafe { imp() }
268 }
269
270 /// Generate a single random `u32` value.
271 ///
272 /// The underlying instruction may fail for variety reasons (such as actual hardware
273 /// failure or exhausted entropy), however the exact reason for the failure is not
274 /// usually exposed.
275 ///
276 /// This method will retry calling the instruction a few times, however if all the
277 /// attempts fail, it will return `None`.
278 ///
279 /// In case `Err` is returned, the caller should assume that a non-recoverable failure
280 /// has occured and use another random number genrator instead.
281 #[inline(always)]
282 pub fn try_next_u32(&self) -> Result<u32, ErrorCode> {
283 #[target_feature(enable = $feat)]
284 unsafe fn imp() -> Result<u32, ErrorCode> {
285 loop_rand!($feat, u32, $step32)
286 }
287 unsafe { imp() }
288 }
289
290 /// Generate a single random `u64` value.
291 ///
292 /// The underlying instruction may fail for variety reasons (such as actual hardware
293 /// failure or exhausted entropy), however the exact reason for the failure is not
294 /// usually exposed.
295 ///
296 /// This method will retry calling the instruction a few times, however if all the
297 /// attempts fail, it will return `None`.
298 ///
299 /// In case `Err` is returned, the caller should assume that a non-recoverable failure
300 /// has occured and use another random number genrator instead.
301 ///
302 /// Note, that on 32-bit targets, there’s no underlying instruction to generate a
303 /// 64-bit number, so it is emulated with the 32-bit version of the instruction.
304 #[inline(always)]
305 pub fn try_next_u64(&self) -> Result<u64, ErrorCode> {
306 #[target_feature(enable = $feat)]
307 unsafe fn imp() -> Result<u64, ErrorCode> {
308 loop_rand!($feat, u64, $step64)
309 }
310 unsafe { imp() }
311 }
312
313 /// Fill a buffer `dest` with random data.
314 ///
315 /// This method will use the most appropriate variant of the instruction available on
316 /// the machine to achieve the greatest single-core throughput, however it has a
317 /// slightly higher setup cost than the plain `next_u32` or `next_u64` methods.
318 ///
319 /// The underlying instruction may fail for variety reasons (such as actual hardware
320 /// failure or exhausted entropy), however the exact reason for the failure is not
321 /// usually exposed.
322 ///
323 /// This method will retry calling the instruction a few times, however if all the
324 /// attempts fail, it will return an error.
325 ///
326 /// If an error is returned, the caller should assume that an non-recoverable hardware
327 /// failure has occured and use another random number genrator instead.
328 #[inline(always)]
329 pub fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), ErrorCode> {
330 #[target_feature(enable = $feat)]
331 unsafe fn imp(dest: &mut [u8]) -> Result<(), ErrorCode> {
332 fn slow_fill_bytes<'a>(
333 mut left: &'a mut [u8],
334 mut right: &'a mut [u8],
335 ) -> Result<(), ErrorCode> {
336 let mut word;
337 let mut buffer: &[u8] = &[];
338 loop {
339 if left.is_empty() {
340 if right.is_empty() {
341 break;
342 }
343 ::core::mem::swap(&mut left, &mut right);
344 }
345 if buffer.is_empty() {
346 word =
347 unsafe { loop_rand!($feat, $maxty, $maxstep) }?.to_ne_bytes();
348 buffer = &word[..];
349 }
350 let len = left.len().min(buffer.len());
351 let (copy_src, leftover) = buffer.split_at(len);
352 let (copy_dest, dest_leftover) = { left }.split_at_mut(len);
353 buffer = leftover;
354 left = dest_leftover;
355 copy_dest.copy_from_slice(copy_src);
356 }
357 Ok(())
358 }
359
360 let destlen = dest.len();
361 if destlen > ::core::mem::size_of::<$maxty>() {
362 let (left, mid, right) = dest.align_to_mut();
363 for el in mid {
364 *el = loop_rand!($feat, $maxty, $maxstep)?;
365 }
366
367 slow_fill_bytes(left, right)
368 } else {
369 slow_fill_bytes(dest, &mut [])
370 }
371 }
372 unsafe { imp(dest) }
373 }
374 }
375
376 impl RngCore for $gen {
377 /// Generate a single random `u32` value.
378 ///
379 /// The underlying instruction may fail for variety reasons (such as actual hardware
380 /// failure or exhausted entropy), however the exact reason for the failure is not
381 /// usually exposed.
382 ///
383 /// # Panic
384 ///
385 /// This method will retry calling the instruction a few times, however if all the
386 /// attempts fail, it will `panic`.
387 ///
388 /// In case `panic` occurs, the caller should assume that an non-recoverable
389 /// hardware failure has occured and use another random number genrator instead.
390 #[inline(always)]
391 fn next_u32(&mut self) -> u32 {
392 match self.try_next_u32() {
393 Ok(result) => result,
394 Err(c) => busy_loop_fail(c),
395 }
396 }
397
398 /// Generate a single random `u64` value.
399 ///
400 /// The underlying instruction may fail for variety reasons (such as actual hardware
401 /// failure or exhausted entropy), however the exact reason for the failure is not
402 /// usually exposed.
403 ///
404 /// Note, that on 32-bit targets, there’s no underlying instruction to generate a
405 /// 64-bit number, so it is emulated with the 32-bit version of the instruction.
406 ///
407 /// # Panic
408 ///
409 /// This method will retry calling the instruction a few times, however if all the
410 /// attempts fail, it will `panic`.
411 ///
412 /// In case `panic` occurs, the caller should assume that an non-recoverable
413 /// hardware failure has occured and use another random number genrator instead.
414 #[inline(always)]
415 fn next_u64(&mut self) -> u64 {
416 match self.try_next_u64() {
417 Ok(result) => result,
418 Err(c) => busy_loop_fail(c),
419 }
420 }
421
422 /// Fill a buffer `dest` with random data.
423 ///
424 /// See `try_fill_bytes` for a more extensive documentation.
425 ///
426 /// # Panic
427 ///
428 /// This method will panic any time `try_fill_bytes` would return an error.
429 #[inline(always)]
430 fn fill_bytes(&mut self, dest: &mut [u8]) {
431 match self.try_fill_bytes(dest) {
432 Ok(result) => result,
433 Err(c) => busy_loop_fail(c),
434 }
435 }
436
437 /// Fill a buffer `dest` with random data.
438 ///
439 /// This method will use the most appropriate variant of the instruction available on
440 /// the machine to achieve the greatest single-core throughput, however it has a
441 /// slightly higher setup cost than the plain `next_u32` or `next_u64` methods.
442 ///
443 /// The underlying instruction may fail for variety reasons (such as actual hardware
444 /// failure or exhausted entropy), however the exact reason for the failure is not
445 /// usually exposed.
446 ///
447 /// This method will retry calling the instruction a few times, however if all the
448 /// attempts fail, it will return an error.
449 ///
450 /// If an error is returned, the caller should assume that an non-recoverable hardware
451 /// failure has occured and use another random number genrator instead.
452 #[inline(always)]
453 fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {
454 self.try_fill_bytes(dest).map_err(Into::into)
455 }
456 }
457 };
458}
459
460#[cfg(target_arch = "x86_64")]
461impl_rand!(
462 RdRand,
463 "rdrand",
464 arch::_rdrand16_step,
465 arch::_rdrand32_step,
466 arch::_rdrand64_step,
467 maxstep = arch::_rdrand64_step,
468 maxty = u64
469);
470#[cfg(target_arch = "x86_64")]
471impl_rand!(
472 RdSeed,
473 "rdseed",
474 arch::_rdseed16_step,
475 arch::_rdseed32_step,
476 arch::_rdseed64_step,
477 maxstep = arch::_rdseed64_step,
478 maxty = u64
479);
480#[cfg(target_arch = "x86")]
481impl_rand!(
482 RdRand,
483 "rdrand",
484 arch::_rdrand16_step,
485 arch::_rdrand32_step,
486 arch::_rdrand64_step,
487 maxstep = arch::_rdrand32_step,
488 maxty = u32
489);
490#[cfg(target_arch = "x86")]
491impl_rand!(
492 RdSeed,
493 "rdseed",
494 arch::_rdseed16_step,
495 arch::_rdseed32_step,
496 arch::_rdseed64_step,
497 maxstep = arch::_rdseed32_step,
498 maxty = u32
499);
500
501#[cfg(test)]
502mod test {
503 use super::{RdRand, RdSeed};
504 use rand_core::RngCore;
505
506 #[test]
507 fn rdrand_works() {
508 let _ = RdRand::new().map(|mut r| {
509 r.next_u32();
510 r.next_u64();
511 });
512 }
513
514 #[repr(C, align(8))]
515 struct FillBuffer([u8; 64]);
516
517 #[test]
518 fn fill_fills_all_bytes() {
519 let _ = RdRand::new().map(|mut r| {
520 let mut test_buffer;
521 let mut fill_buffer = FillBuffer([0; 64]); // make sure buffer is aligned to 8-bytes...
522 let test_cases = [
523 (0, 64), // well aligned
524 (8, 64), // well aligned
525 (0, 64), // well aligned
526 (5, 64), // left is non-empty, right is empty.
527 (0, 63), // left is empty, right is non-empty.
528 (5, 63), // left and right both are non-empty.
529 (5, 61), // left and right both are non-empty.
530 (0, 8), // 1 word-worth of data, aligned.
531 (1, 9), // 1 word-worth of data, misaligned.
532 (0, 7), // less than 1 word of data.
533 (1, 7), // less than 1 word of data.
534 ];
535 'outer: for &(start, end) in &test_cases {
536 test_buffer = [0; 64];
537 for _ in 0..512 {
538 fill_buffer.0 = [0; 64];
539 r.fill_bytes(&mut fill_buffer.0[start..end]);
540 for (b, p) in test_buffer.iter_mut().zip(fill_buffer.0.iter()) {
541 *b = *b | *p;
542 }
543 if (&test_buffer[start..end]).iter().all(|x| *x != 0) {
544 assert!(
545 test_buffer[..start].iter().all(|x| *x == 0),
546 "all other values must be 0"
547 );
548 assert!(
549 test_buffer[end..].iter().all(|x| *x == 0),
550 "all other values must be 0"
551 );
552 continue 'outer;
553 }
554 }
555 panic!("wow, we broke it? {} {} {:?}", start, end, &test_buffer[..])
556 }
557 });
558 }
559
560 #[test]
561 fn rdseed_works() {
562 let _ = RdSeed::new().map(|mut r| {
563 r.next_u32();
564 r.next_u64();
565 });
566 }
567}