├── .github └── workflows │ └── rdrand.yml ├── .gitignore ├── Cargo.toml ├── LICENSE ├── README.mkd ├── benches └── rdrand.rs └── src ├── changelog.rs ├── errors.rs └── lib.rs /.github/workflows/rdrand.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: 4 | push: 5 | paths-ignore: 6 | - '*.mkd' 7 | - 'LICENSE' 8 | pull_request: 9 | types: [opened, repoened, synchronize] 10 | 11 | jobs: 12 | test: 13 | runs-on: ${{ matrix.os }} 14 | strategy: 15 | fail-fast: false 16 | matrix: 17 | rust_toolchain: [nightly, stable, 1.46.0] 18 | os: [ubuntu-latest, windows-latest, macOS-latest] 19 | flags: ["", "--no-default-features", "--release", "--release --no-default-features"] 20 | timeout-minutes: 20 21 | steps: 22 | - uses: actions/checkout@v2 23 | - uses: actions-rs/toolchain@v1 24 | with: 25 | toolchain: ${{ matrix.rust_toolchain }} 26 | profile: minimal 27 | default: true 28 | - uses: actions-rs/cargo@v1 29 | with: 30 | command: test 31 | args: --manifest-path=Cargo.toml ${{ matrix.flags }} -- --nocapture 32 | 33 | bench: 34 | runs-on: ubuntu-latest 35 | timeout-minutes: 20 36 | steps: 37 | - uses: actions/checkout@v2 38 | - uses: actions-rs/toolchain@v1 39 | with: 40 | toolchain: stable 41 | profile: minimal 42 | default: true 43 | - uses: actions-rs/cargo@v1 44 | with: 45 | command: bench 46 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /Cargo.lock 3 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rdrand" 3 | version = "0.8.3" 4 | authors = ["Simonas Kazlauskas "] 5 | description = "An implementation of random number generator based on rdrand and rdseed instructions" 6 | keywords = ["rand", "rdrand", "rdseed", "random"] 7 | license = "ISC" 8 | repository = "https://github.com/nagisa/rust_rdrand/" 9 | documentation = "https://docs.rs/rdrand/0.8.2/" 10 | edition = "2018" 11 | 12 | [[bench]] 13 | name = "rdrand" 14 | harness = false 15 | 16 | [dependencies] 17 | rand_core = { version = "0.6", default-features = false } 18 | 19 | [dev-dependencies] 20 | criterion = "0.3" 21 | 22 | [features] 23 | default = ["std"] 24 | std = ["rand_core/std"] 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright © 2014, Simonas Kazlauskas 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 appear 5 | 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 OF 12 | THIS SOFTWARE. 13 | -------------------------------------------------------------------------------- /README.mkd: -------------------------------------------------------------------------------- 1 | An implementation of random number generators based on `rdrand` and `rdseed` instructions. 2 | 3 | The random number generators provided by this crate are fairly slow (the latency for these 4 | instructions is pretty high), but provide high quality random bits. Caveat is: neither AMD’s 5 | nor Intel’s designs are public and therefore are not verifiable for lack of backdoors. 6 | 7 | Unless you know what you are doing, use the random number generators provided by the `rand` 8 | crate (such as `EntropyRng`) instead. 9 | -------------------------------------------------------------------------------- /benches/rdrand.rs: -------------------------------------------------------------------------------- 1 | use criterion::{criterion_group, criterion_main, Criterion, Throughput}; 2 | use rand_core::RngCore; 3 | 4 | fn bench_rdrand(c: &mut Criterion) { 5 | let mut gen = match rdrand::RdRand::new() { 6 | Ok(g) => g, 7 | Err(_) => return, 8 | }; 9 | let mut group = c.benchmark_group("rdrand"); 10 | 11 | group 12 | .throughput(Throughput::Bytes(2)) 13 | .bench_function("try_next/u16", move |b| { 14 | b.iter(move || gen.try_next_u16().unwrap()) 15 | }); 16 | group 17 | .throughput(Throughput::Bytes(4)) 18 | .bench_function("try_next/u32", move |b| { 19 | b.iter(move || gen.try_next_u32().unwrap()) 20 | }); 21 | group 22 | .throughput(Throughput::Bytes(4)) 23 | .bench_function("next/u32", move |b| b.iter(move || gen.next_u32())); 24 | group 25 | .throughput(Throughput::Bytes(8)) 26 | .bench_function("try_next/u64", move |b| { 27 | b.iter(move || gen.try_next_u64().unwrap()) 28 | }); 29 | group 30 | .throughput(Throughput::Bytes(8)) 31 | .bench_function("next/u64", move |b| b.iter(move || gen.next_u64())); 32 | let mut buffer = [0; 128]; 33 | group 34 | .throughput(Throughput::Bytes(128)) 35 | .bench_function("try_next/fill128", |b| { 36 | b.iter(|| gen.try_fill_bytes(&mut buffer).unwrap()) 37 | }); 38 | group 39 | .throughput(Throughput::Bytes(128)) 40 | .bench_function("next/fill128", |b| b.iter(|| gen.fill_bytes(&mut buffer))); 41 | 42 | group.finish(); 43 | } 44 | 45 | fn bench_rdseed(c: &mut Criterion) { 46 | let mut gen = match rdrand::RdSeed::new() { 47 | Ok(g) => g, 48 | Err(_) => return, 49 | }; 50 | let mut group = c.benchmark_group("rdseed"); 51 | 52 | group 53 | .throughput(Throughput::Bytes(2)) 54 | .bench_function("try_next/u16", move |b| { 55 | b.iter(move || gen.try_next_u16().unwrap()) 56 | }); 57 | group 58 | .throughput(Throughput::Bytes(4)) 59 | .bench_function("try_next/u32", move |b| { 60 | b.iter(move || gen.try_next_u32().unwrap()) 61 | }); 62 | group 63 | .throughput(Throughput::Bytes(4)) 64 | .bench_function("next/u32", move |b| b.iter(move || gen.next_u32())); 65 | group 66 | .throughput(Throughput::Bytes(8)) 67 | .bench_function("try_next/u64", move |b| { 68 | b.iter(move || gen.try_next_u64().unwrap()) 69 | }); 70 | group 71 | .throughput(Throughput::Bytes(8)) 72 | .bench_function("next/u64", move |b| b.iter(move || gen.next_u64())); 73 | let mut buffer = [0; 128]; 74 | group 75 | .throughput(Throughput::Bytes(128)) 76 | .bench_function("try_next/fill128", |b| { 77 | b.iter(|| gen.try_fill_bytes(&mut buffer).unwrap()) 78 | }); 79 | group 80 | .throughput(Throughput::Bytes(128)) 81 | .bench_function("next/fill128", |b| b.iter(|| gen.fill_bytes(&mut buffer))); 82 | 83 | group.finish(); 84 | } 85 | 86 | criterion_group!(benches, bench_rdrand, bench_rdseed); 87 | criterion_main!(benches); 88 | -------------------------------------------------------------------------------- /src/changelog.rs: -------------------------------------------------------------------------------- 1 | //! Project changelog 2 | 3 | 4 | /// Fix the implementation of `try_fill_bytes` when the buffer is aligned but the size is not a 5 | /// multiple of a word size. 6 | pub mod r0_8_2 {} 7 | 8 | /// Disallow use of `rdrand`/`rdseed` on known-broken AMD systems. 9 | pub mod r0_8_1 {} 10 | 11 | /// ## Breaking changes 12 | /// 13 | /// * Upgrade to `rand_core = ^0.6.0`. 14 | pub mod r0_8_0 {} 15 | 16 | /// ## Breaking changes 17 | /// 18 | /// Upgrade to `rand_core = ^0.5.1`. This involves a major change to how errors are handled. See 19 | /// [`ErrorCode`](crate::ErrorCode). 20 | /// 21 | /// rustc version 1.42 is now required to build the library (up from 1.32). 22 | pub mod r0_7_0 {} 23 | 24 | /// Fix unsound mutable reference aliasing in the implementation of `try_fill_bytes`. 25 | /// 26 | /// The affected code has been replaced with safer one where the scope of `unsafe` is reduced to 27 | /// the loop which obtains a random word via a native instruction. 28 | /// 29 | /// ## Breaking changes 30 | /// 31 | /// rustc version 1.32 is now required to build the library (up from 1.30). 32 | pub mod r0_6_0 {} 33 | 34 | /// Replaced likely unsound use of `core::mem::uninitialized()`. 35 | pub mod r0_5_1 {} 36 | 37 | /// ## Breaking changes 38 | /// 39 | /// Updated rand_core dependency from `0.3` to `0.4`. 40 | pub mod r0_5_0 {} 41 | 42 | /// ## Breaking changes 43 | /// 44 | /// Crate gained an enabled-by-default `std` feature. If you relied on rdrand being `core`-able 45 | /// change your dependency to appear as such: 46 | /// 47 | /// ```toml 48 | /// rdrand = { version = "0.4", default-features = false } 49 | /// ``` 50 | /// 51 | /// This is done so that an advantage of the common feature detection functionality could be 52 | /// employed by users that are not constrained by `core`. This functionality is faster, caches the 53 | /// results and is shared between all users of the functionality. 54 | /// 55 | /// For `core` usage the feature detection has also been improved and will not be done if e.g. 56 | /// crate is built with `rdrand` instructions enabled globally. 57 | pub mod r0_4_0 {} 58 | 59 | /// Crate now works on stable! 60 | /// 61 | /// ## Breaking changes 62 | /// 63 | /// * Updated to `rand_core = ^0.3`. 64 | pub mod r0_3_0 {} 65 | -------------------------------------------------------------------------------- /src/errors.rs: -------------------------------------------------------------------------------- 1 | use core::{ 2 | convert::TryFrom, 3 | fmt::{self, Display, Formatter}, 4 | }; 5 | 6 | /// Errors in this library 7 | #[repr(u8)] 8 | #[derive(Debug, Copy, Clone)] 9 | pub enum ErrorCode { 10 | /// The hardware instruction is not supported 11 | UnsupportedInstruction, 12 | /// There was a hardware failure 13 | HardwareFailure, 14 | } 15 | 16 | impl ErrorCode { 17 | #[cfg(not(feature = "std"))] 18 | const fn as_randcore_code(self) -> core::num::NonZeroU32 { 19 | /// Arbitrary, off top of head bitmask for error codes that come from rdrand 20 | const RDRAND_TAG: u32 = rand_core::Error::CUSTOM_START + 0x3D34_7D00; 21 | unsafe { core::num::NonZeroU32::new_unchecked(RDRAND_TAG + self as u32) } 22 | } 23 | } 24 | 25 | #[cfg(not(feature = "std"))] 26 | impl From for rand_core::Error { 27 | fn from(code: ErrorCode) -> rand_core::Error { 28 | code.as_randcore_code().into() 29 | } 30 | } 31 | 32 | #[cfg(feature = "std")] 33 | impl From for rand_core::Error { 34 | fn from(code: ErrorCode) -> rand_core::Error { 35 | rand_core::Error::new(code) 36 | } 37 | } 38 | 39 | #[cfg(feature = "std")] 40 | impl std::error::Error for ErrorCode {} 41 | 42 | impl Display for ErrorCode { 43 | fn fmt(&self, f: &mut Formatter) -> fmt::Result { 44 | f.write_str(match self { 45 | ErrorCode::UnsupportedInstruction => "the hardware instruction is not supported", 46 | ErrorCode::HardwareFailure => "hardware generator failure", 47 | }) 48 | } 49 | } 50 | 51 | #[derive(Copy, Clone, Debug)] 52 | pub struct NotAnErrorCode; 53 | 54 | #[cfg(feature = "std")] 55 | impl std::error::Error for NotAnErrorCode {} 56 | 57 | impl Display for NotAnErrorCode { 58 | fn fmt(&self, f: &mut Formatter) -> fmt::Result { 59 | f.write_str("the error is not an rdrand error") 60 | } 61 | } 62 | 63 | impl TryFrom<&rand_core::Error> for ErrorCode { 64 | type Error = NotAnErrorCode; 65 | #[cfg(feature = "std")] 66 | fn try_from(error: &rand_core::Error) -> Result { 67 | error 68 | .inner() 69 | .downcast_ref::() 70 | .copied() 71 | .ok_or(NotAnErrorCode) 72 | } 73 | #[cfg(not(feature = "std"))] 74 | fn try_from(error: &rand_core::Error) -> Result { 75 | let code = error.code().ok_or(NotAnErrorCode)?; 76 | if code == ErrorCode::UnsupportedInstruction.as_randcore_code() { 77 | Ok(ErrorCode::UnsupportedInstruction) 78 | } else if code == ErrorCode::HardwareFailure.as_randcore_code() { 79 | Ok(ErrorCode::HardwareFailure) 80 | } else { 81 | Err(NotAnErrorCode) 82 | } 83 | } 84 | } 85 | 86 | impl TryFrom for ErrorCode { 87 | type Error = NotAnErrorCode; 88 | fn try_from(error: rand_core::Error) -> Result { 89 | >::try_from(&error) 90 | } 91 | } 92 | 93 | #[cfg(test)] 94 | mod test { 95 | use super::ErrorCode; 96 | use core::convert::TryInto; 97 | use rand_core::Error; 98 | 99 | #[test] 100 | fn error_code_send() { 101 | fn assert_send() {} 102 | assert_send::(); 103 | } 104 | 105 | #[test] 106 | fn error_code_sync() { 107 | fn assert_sync() {} 108 | assert_sync::(); 109 | } 110 | 111 | #[test] 112 | fn error_code_copy() { 113 | fn assert_copy() {} 114 | assert_copy::(); 115 | } 116 | 117 | #[test] 118 | fn error_code_clone() { 119 | fn assert_clone() {} 120 | assert_clone::(); 121 | } 122 | 123 | #[test] 124 | #[cfg(feature = "std")] 125 | fn error_code_error() { 126 | fn assert_error() {} 127 | assert_error::(); 128 | } 129 | 130 | #[test] 131 | fn conversion_roundtrip_unsupported_hardware() { 132 | let core_rand: Error = ErrorCode::UnsupportedInstruction.into(); 133 | let code: ErrorCode = core_rand.try_into().expect("should convert back"); 134 | assert!(matches!(code, ErrorCode::UnsupportedInstruction)); 135 | } 136 | 137 | #[test] 138 | fn conversion_roundtrip_hardware_failure() { 139 | let core_rand: Error = ErrorCode::HardwareFailure.into(); 140 | let code: ErrorCode = core_rand.try_into().expect("should convert back"); 141 | assert!(matches!(code, ErrorCode::HardwareFailure)); 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright © 2014, Simonas Kazlauskas 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 | //! 26 | //! 27 | //! 28 | //! 29 | //! 30 | //! 31 | //! 32 | //! 33 | //! 34 | //! 35 | //! 36 | //! 37 | //! 38 | //! 39 | //! 40 | //! 41 | //! 42 | //! 43 | //! 44 | //! 45 | //! 46 | //! 47 | //! 48 | //! 49 | //! 50 | //! 51 | //! 52 | //! 53 | //! 54 | //! 55 | //! 56 | //! 57 | //! 58 | //! 59 | //!
ArchitectureLatency (cycles)Maximum throughput (per core)
u16u32u64
AMD Ryzen~1200~1200~2500~12MB/s @ 3.7GHz
Intel Skylake460460460~72MB/s @ 4.2GHz
Intel Haswell320320320~110MB/s @ 4.4GHz
60 | //! 61 | //! [Agner’s instruction tables]: http://agner.org/optimize/ 62 | #![cfg_attr(not(feature = "std"), no_std)] 63 | 64 | pub mod changelog; 65 | mod errors; 66 | 67 | pub use errors::ErrorCode; 68 | use rand_core::{CryptoRng, Error, RngCore}; 69 | 70 | #[cold] 71 | #[inline(never)] 72 | pub(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)] 88 | pub 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)] 100 | pub struct RdSeed(()); 101 | 102 | impl CryptoRng for RdRand {} 103 | impl CryptoRng for RdSeed {} 104 | 105 | mod 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 133 | macro_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)] 162 | fn 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)] 171 | fn amd_family(cpuid1: &arch::CpuidResult) -> u32 { 172 | ((cpuid1.eax >> 8) & 0xF) + ((cpuid1.eax >> 20) & 0xFF) 173 | } 174 | 175 | #[inline(always)] 176 | fn has_rdrand(cpuid1: &arch::CpuidResult) -> bool { 177 | const FLAG: u32 = 1 << 30; 178 | cpuid1.ecx & FLAG == FLAG 179 | } 180 | 181 | #[inline(always)] 182 | fn 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. 197 | const FIRST_GOOD_AMD_FAMILY: u32 = 0x17; 198 | 199 | macro_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 | 217 | macro_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 { 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 { 263 | #[target_feature(enable = $feat)] 264 | unsafe fn imp() -> Result { 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 { 283 | #[target_feature(enable = $feat)] 284 | unsafe fn imp() -> Result { 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 { 306 | #[target_feature(enable = $feat)] 307 | unsafe fn imp() -> Result { 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")] 461 | impl_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")] 471 | impl_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")] 481 | impl_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")] 491 | impl_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)] 502 | mod 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 | } 568 | --------------------------------------------------------------------------------