[go: up one dir, main page]

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}