x86/
random.rs

1//! Instructions to generate random bits directly from the hardware (RDRAND and RDSEED).
2//!
3//! The decision process for which instruction to use is based on what
4//! the output will be used for. If you wish to seed another pseudorandom
5//! number generator (PRNG), use RDSEED. For all other purposes, use RDRAND
6//!
7//! See also: https://software.intel.com/en-us/blogs/2012/11/17/the-difference-between-rdrand-and-rdseed
8//!
9//! * RDRAND: Cryptographically secure pseudorandom number generator NIST:SP 800-90A
10//! * RDSEED: Non-deterministic random bit generator NIST: SP 800-90B & C (drafts)
11#[cfg(target_arch = "x86_64")]
12use core::arch::x86_64::{
13    _rdrand16_step, _rdrand32_step, _rdrand64_step, _rdseed16_step, _rdseed32_step, _rdseed64_step,
14};
15
16#[cfg(target_arch = "x86")]
17use core::arch::x86::{_rdrand16_step, _rdrand32_step, _rdseed16_step, _rdseed32_step};
18
19/// Generates a 16-bit random value and stores it in `e`.
20///
21/// # Safety
22/// Will crash if RDRAND instructions are not supported.
23#[inline(always)]
24pub unsafe fn rdrand16(e: &mut u16) -> bool {
25    _rdrand16_step(e) == 1
26}
27
28/// Generates a 32-bit random value and stores it in `e`.
29///
30/// # Safety
31/// Will crash if RDRAND instructions are not supported.
32#[inline(always)]
33pub unsafe fn rdrand32(e: &mut u32) -> bool {
34    _rdrand32_step(e) == 1
35}
36
37/// Generates a 64-bit random value and stores it in `e`.
38///
39/// # Safety
40/// Will crash if RDRAND instructions are not supported.
41#[cfg(target_arch = "x86_64")]
42#[inline(always)]
43pub unsafe fn rdrand64(e: &mut u64) -> bool {
44    _rdrand64_step(e) == 1
45}
46
47/// RdRand trait to implement the generic rdrand_slice function.
48pub trait RdRand {
49    /// Fills `self` with random bits. Returns true on success or false otherwise
50    ///
51    /// # Safety
52    /// RDRAND is not supported on all architctures, so using this may crash you.
53    unsafe fn fill_random(&mut self) -> bool;
54}
55
56impl RdRand for u8 {
57    /// Fills the 16-bit value with a random bit string
58    ///
59    /// # Safety
60    /// Will crash if RDSEED instructions are not supported.
61    unsafe fn fill_random(&mut self) -> bool {
62        let mut r: u16 = 0;
63        let ret = rdrand16(&mut r);
64        *self = r as u8;
65        ret
66    }
67}
68
69impl RdRand for u16 {
70    /// Fills the 16-bit value with a random bit string
71    ///
72    /// # Safety
73    /// Will crash if RDRAND instructions are not supported.
74    unsafe fn fill_random(&mut self) -> bool {
75        rdrand16(self)
76    }
77}
78
79impl RdRand for u32 {
80    /// Fills the 32-bit value with a random bit string
81    ///
82    /// # Safety
83    /// Will crash if RDRAND instructions are not supported.
84    unsafe fn fill_random(&mut self) -> bool {
85        rdrand32(self)
86    }
87}
88
89#[cfg(target_arch = "x86_64")]
90impl RdRand for u64 {
91    /// Fills the 64-bit value with a random bit string
92    ///
93    /// # Safety
94    /// Will crash if RDRAND instructions are not supported.
95    unsafe fn fill_random(&mut self) -> bool {
96        rdrand64(self)
97    }
98}
99
100/// Fill a slice with random values.
101///
102/// Returns true if the iterator was successfully filled with
103/// random values, otherwise false.
104/// # Safety
105/// Will crash if RDRAND instructions are not supported.
106pub unsafe fn rdrand_slice<T: RdRand>(buffer: &mut [T]) -> bool {
107    let mut worked = true;
108    for element in buffer {
109        worked &= element.fill_random();
110    }
111    worked
112}
113
114/// Generates a 16-bit random value and stores it in `e`.
115///
116/// # Safety
117/// Will crash if RDSEED instructions are not supported.
118#[inline(always)]
119pub unsafe fn rdseed16(e: &mut u16) -> bool {
120    _rdseed16_step(e) == 1
121}
122
123/// Generates a 32-bit random value and stores it in `e`.
124///
125/// # Safety
126/// Will crash if RDSEED instructions are not supported.
127#[inline(always)]
128pub unsafe fn rdseed32(e: &mut u32) -> bool {
129    _rdseed32_step(e) == 1
130}
131
132/// Generates a 64-bit random value and stores it in `e`.
133///
134/// # Safety
135/// Will crash if RDSEED instructions are not supported.
136#[cfg(target_arch = "x86_64")]
137#[inline(always)]
138pub unsafe fn rdseed64(e: &mut u64) -> bool {
139    _rdseed64_step(e) == 1
140}
141
142/// RdSeed trait to implement the generic rdseed_slice function.
143pub trait RdSeed {
144    /// Fills `self` with random bits. Returns true on success or false otherwise
145    ///
146    /// # Safety
147    /// RDSEED is not supported on all architctures, so using this may crash you.
148    unsafe fn fill_random(&mut self) -> bool;
149}
150
151impl RdSeed for u8 {
152    /// Fills the 16-bit value with a random bit string
153    ///
154    /// # Safety
155    /// Will crash if RDSEED instructions are not supported.
156    unsafe fn fill_random(&mut self) -> bool {
157        let mut r: u16 = 0;
158        let ret = rdseed16(&mut r);
159        *self = r as u8;
160        ret
161    }
162}
163
164impl RdSeed for u16 {
165    /// Fills the 16-bit value with a random bit string
166    ///
167    /// # Safety
168    /// Will crash if RDSEED instructions are not supported.
169    unsafe fn fill_random(&mut self) -> bool {
170        rdseed16(self)
171    }
172}
173
174impl RdSeed for u32 {
175    /// Fills the 32-bit value with a random bit string
176    ///
177    /// # Safety
178    /// Will crash if RDSEED instructions are not supported.
179    unsafe fn fill_random(&mut self) -> bool {
180        rdseed32(self)
181    }
182}
183
184#[cfg(target_arch = "x86_64")]
185impl RdSeed for u64 {
186    /// Fills the 64-bit value with a random bit string
187    ///
188    /// # Safety
189    /// Will crash if RDSEED instructions are not supported.
190    unsafe fn fill_random(&mut self) -> bool {
191        rdseed64(self)
192    }
193}
194
195/// Fill a slice with random values.
196///
197/// Returns true if the iterator was successfully filled with
198/// random values, otherwise false.
199///
200/// # Safety
201/// Will crash if RDSEED instructions are not supported.
202pub unsafe fn rdseed_slice<T: RdSeed>(buffer: &mut [T]) -> bool {
203    let mut worked = true;
204    for element in buffer {
205        worked &= element.fill_random();
206    }
207    worked
208}
209
210#[cfg(all(test, feature = "utest"))]
211mod test {
212    use super::*;
213
214    #[test]
215    fn rdrand_u64() {
216        let has_rdrand = crate::cpuid::CpuId::new()
217            .get_feature_info()
218            .map_or(false, |finfo| finfo.has_rdrand());
219        if !has_rdrand {
220            return;
221        }
222
223        unsafe {
224            let mut buf: [u64; 4] = [0, 0, 0, 0];
225            rdrand_slice(&mut buf);
226
227            assert_ne!(buf[0], 0);
228            assert_ne!(buf[1], 0);
229            assert_ne!(buf[2], 0);
230            assert_ne!(buf[3], 0);
231        }
232    }
233
234    #[test]
235    fn rdrand_u32() {
236        let has_rdrand = crate::cpuid::CpuId::new()
237            .get_feature_info()
238            .map_or(false, |finfo| finfo.has_rdrand());
239        if !has_rdrand {
240            return;
241        }
242
243        unsafe {
244            let mut buf: [u32; 4] = [0, 0, 0, 0];
245            rdrand_slice(&mut buf);
246
247            assert_ne!(buf[0], 0);
248            assert_ne!(buf[1], 0);
249            assert_ne!(buf[2], 0);
250            assert_ne!(buf[3], 0);
251        }
252    }
253
254    #[test]
255    fn rdrand_u16() {
256        let has_rdrand = crate::cpuid::CpuId::new()
257            .get_feature_info()
258            .map_or(false, |finfo| finfo.has_rdrand());
259        if !has_rdrand {
260            return;
261        }
262
263        unsafe {
264            let mut buf: [u16; 4] = [0, 0, 0, 0];
265            rdrand_slice(&mut buf);
266            assert_ne!(buf[0], 0);
267            assert_ne!(buf[1], 0);
268            assert_ne!(buf[2], 0);
269            assert_ne!(buf[3], 0);
270        }
271    }
272
273    #[test]
274    fn rdseed_u64() {
275        let has_rdseed = crate::cpuid::CpuId::new()
276            .get_extended_feature_info()
277            .map_or(false, |efinfo| efinfo.has_rdseed());
278        if !has_rdseed {
279            return;
280        }
281
282        unsafe {
283            let mut buf: [u64; 4] = [0, 0, 0, 0];
284            rdseed_slice(&mut buf);
285
286            assert_ne!(buf[0], 0);
287            assert_ne!(buf[1], 0);
288            assert_ne!(buf[2], 0);
289            assert_ne!(buf[3], 0);
290        }
291    }
292
293    #[test]
294    fn rdseed_u32() {
295        let has_rdseed = crate::cpuid::CpuId::new()
296            .get_extended_feature_info()
297            .map_or(false, |efinfo| efinfo.has_rdseed());
298        if !has_rdseed {
299            return;
300        }
301
302        unsafe {
303            let mut buf: [u32; 4] = [0, 0, 0, 0];
304            rdseed_slice(&mut buf);
305
306            assert_ne!(buf[0], 0);
307            assert_ne!(buf[1], 0);
308            assert_ne!(buf[2], 0);
309            assert_ne!(buf[3], 0);
310        }
311    }
312
313    #[test]
314    fn rdseed_u16() {
315        let has_rdseed = crate::cpuid::CpuId::new()
316            .get_extended_feature_info()
317            .map_or(false, |efinfo| efinfo.has_rdseed());
318        if !has_rdseed {
319            return;
320        }
321
322        unsafe {
323            let mut buf: [u16; 4] = [0, 0, 0, 0];
324            rdseed_slice(&mut buf);
325            // Not the best test in the world, but unlikely enough to fail...
326            assert!(buf[0] > 0 || buf[1] > 0 || buf[2] > 0 || buf[3] > 0);
327        }
328    }
329
330    #[test]
331    fn rdrand_u8() {
332        let has_rdseed = crate::cpuid::CpuId::new()
333            .get_extended_feature_info()
334            .map_or(false, |efinfo| efinfo.has_rdseed());
335        if !has_rdseed {
336            return;
337        }
338
339        unsafe {
340            let mut buf: [u8; 4] = [0, 0, 0, 0];
341            rdrand_slice(&mut buf);
342            // Not the best test in the world, but unlikely enough to fail...
343            assert!(buf[0] > 0 || buf[1] > 0 || buf[2] > 0 || buf[3] > 0);
344        }
345    }
346
347    #[test]
348    fn rdseed_u8() {
349        let has_rdseed = crate::cpuid::CpuId::new()
350            .get_extended_feature_info()
351            .map_or(false, |efinfo| efinfo.has_rdseed());
352        if !has_rdseed {
353            return;
354        }
355
356        unsafe {
357            let mut buf: [u8; 4] = [0, 0, 0, 0];
358            rdseed_slice(&mut buf);
359            // Not the best test in the world, but unlikely enough to fail...
360            assert!(buf[0] > 0 || buf[1] > 0 || buf[2] > 0 || buf[3] > 0);
361        }
362    }
363}