]> git.lizzy.rs Git - rust.git/blob - src/libstd/rand/os.rs
Auto merge of #24865 - bluss:range-size, r=alexcrichton
[rust.git] / src / libstd / rand / os.rs
1 // Copyright 2013-2015 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 //! Interfaces to the operating system provided random number
12 //! generators.
13
14 pub use self::imp::OsRng;
15
16 #[cfg(all(unix, not(target_os = "ios")))]
17 mod imp {
18     use prelude::v1::*;
19     use self::OsRngInner::*;
20
21     use fs::File;
22     use io;
23     use libc;
24     use mem;
25     use rand::Rng;
26     use rand::reader::ReaderRng;
27     use sys::os::errno;
28
29     #[cfg(all(target_os = "linux",
30               any(target_arch = "x86_64",
31                   target_arch = "x86",
32                   target_arch = "arm",
33                   target_arch = "aarch64",
34                   target_arch = "powerpc")))]
35     fn getrandom(buf: &mut [u8]) -> libc::c_long {
36         extern "C" {
37             fn syscall(number: libc::c_long, ...) -> libc::c_long;
38         }
39
40         #[cfg(target_arch = "x86_64")]
41         const NR_GETRANDOM: libc::c_long = 318;
42         #[cfg(target_arch = "x86")]
43         const NR_GETRANDOM: libc::c_long = 355;
44         #[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
45         const NR_GETRANDOM: libc::c_long = 384;
46         #[cfg(target_arch = "powerpc")]
47         const NR_GETRANDOM: libc::c_long = 384;
48
49         unsafe {
50             syscall(NR_GETRANDOM, buf.as_mut_ptr(), buf.len(), 0)
51         }
52     }
53
54     #[cfg(not(all(target_os = "linux",
55                   any(target_arch = "x86_64",
56                       target_arch = "x86",
57                       target_arch = "arm",
58                       target_arch = "aarch64",
59                       target_arch = "powerpc"))))]
60     fn getrandom(_buf: &mut [u8]) -> libc::c_long { -1 }
61
62     fn getrandom_fill_bytes(v: &mut [u8]) {
63         let mut read = 0;
64         let len = v.len();
65         while read < len {
66             let result = getrandom(&mut v[read..]);
67             if result == -1 {
68                 let err = errno() as libc::c_int;
69                 if err == libc::EINTR {
70                     continue;
71                 } else {
72                     panic!("unexpected getrandom error: {}", err);
73                 }
74             } else {
75                 read += result as usize;
76             }
77         }
78     }
79
80     fn getrandom_next_u32() -> u32 {
81         let mut buf: [u8; 4] = [0; 4];
82         getrandom_fill_bytes(&mut buf);
83         unsafe { mem::transmute::<[u8; 4], u32>(buf) }
84     }
85
86     fn getrandom_next_u64() -> u64 {
87         let mut buf: [u8; 8] = [0; 8];
88         getrandom_fill_bytes(&mut buf);
89         unsafe { mem::transmute::<[u8; 8], u64>(buf) }
90     }
91
92     #[cfg(all(target_os = "linux",
93               any(target_arch = "x86_64",
94                   target_arch = "x86",
95                   target_arch = "arm",
96                   target_arch = "aarch64",
97                   target_arch = "powerpc")))]
98     fn is_getrandom_available() -> bool {
99         use sync::atomic::{AtomicBool, ATOMIC_BOOL_INIT, Ordering};
100         use sync::{Once, ONCE_INIT};
101
102         static CHECKER: Once = ONCE_INIT;
103         static AVAILABLE: AtomicBool = ATOMIC_BOOL_INIT;
104
105         CHECKER.call_once(|| {
106             let mut buf: [u8; 0] = [];
107             let result = getrandom(&mut buf);
108             let available = if result == -1 {
109                 let err = io::Error::last_os_error().raw_os_error();
110                 err != Some(libc::ENOSYS)
111             } else {
112                 true
113             };
114             AVAILABLE.store(available, Ordering::Relaxed);
115         });
116
117         AVAILABLE.load(Ordering::Relaxed)
118     }
119
120     #[cfg(not(all(target_os = "linux",
121                   any(target_arch = "x86_64",
122                       target_arch = "x86",
123                       target_arch = "arm",
124                       target_arch = "aarch64",
125                       target_arch = "powerpc"))))]
126     fn is_getrandom_available() -> bool { false }
127
128     /// A random number generator that retrieves randomness straight from
129     /// the operating system. Platform sources:
130     ///
131     /// - Unix-like systems (Linux, Android, Mac OSX): read directly from
132     ///   `/dev/urandom`, or from `getrandom(2)` system call if available.
133     /// - Windows: calls `CryptGenRandom`, using the default cryptographic
134     ///   service provider with the `PROV_RSA_FULL` type.
135     /// - iOS: calls SecRandomCopyBytes as /dev/(u)random is sandboxed.
136     ///
137     /// This does not block.
138     pub struct OsRng {
139         inner: OsRngInner,
140     }
141
142     enum OsRngInner {
143         OsGetrandomRng,
144         OsReaderRng(ReaderRng<File>),
145     }
146
147     impl OsRng {
148         /// Create a new `OsRng`.
149         pub fn new() -> io::Result<OsRng> {
150             if is_getrandom_available() {
151                 return Ok(OsRng { inner: OsGetrandomRng });
152             }
153
154             let reader = try!(File::open("/dev/urandom"));
155             let reader_rng = ReaderRng::new(reader);
156
157             Ok(OsRng { inner: OsReaderRng(reader_rng) })
158         }
159     }
160
161     impl Rng for OsRng {
162         fn next_u32(&mut self) -> u32 {
163             match self.inner {
164                 OsGetrandomRng => getrandom_next_u32(),
165                 OsReaderRng(ref mut rng) => rng.next_u32(),
166             }
167         }
168         fn next_u64(&mut self) -> u64 {
169             match self.inner {
170                 OsGetrandomRng => getrandom_next_u64(),
171                 OsReaderRng(ref mut rng) => rng.next_u64(),
172             }
173         }
174         fn fill_bytes(&mut self, v: &mut [u8]) {
175             match self.inner {
176                 OsGetrandomRng => getrandom_fill_bytes(v),
177                 OsReaderRng(ref mut rng) => rng.fill_bytes(v)
178             }
179         }
180     }
181 }
182
183 #[cfg(target_os = "ios")]
184 mod imp {
185     use prelude::v1::*;
186
187     use io;
188     use mem;
189     use rand::Rng;
190     use libc::{c_int, size_t};
191
192     /// A random number generator that retrieves randomness straight from
193     /// the operating system. Platform sources:
194     ///
195     /// - Unix-like systems (Linux, Android, Mac OSX): read directly from
196     ///   `/dev/urandom`, or from `getrandom(2)` system call if available.
197     /// - Windows: calls `CryptGenRandom`, using the default cryptographic
198     ///   service provider with the `PROV_RSA_FULL` type.
199     /// - iOS: calls SecRandomCopyBytes as /dev/(u)random is sandboxed.
200     ///
201     /// This does not block.
202     pub struct OsRng {
203         // dummy field to ensure that this struct cannot be constructed outside
204         // of this module
205         _dummy: (),
206     }
207
208     #[repr(C)]
209     struct SecRandom;
210
211     #[allow(non_upper_case_globals)]
212     const kSecRandomDefault: *const SecRandom = 0 as *const SecRandom;
213
214     #[link(name = "Security", kind = "framework")]
215     extern "C" {
216         fn SecRandomCopyBytes(rnd: *const SecRandom,
217                               count: size_t, bytes: *mut u8) -> c_int;
218     }
219
220     impl OsRng {
221         /// Create a new `OsRng`.
222         pub fn new() -> io::Result<OsRng> {
223             Ok(OsRng { _dummy: () })
224         }
225     }
226
227     impl Rng for OsRng {
228         fn next_u32(&mut self) -> u32 {
229             let mut v = [0; 4];
230             self.fill_bytes(&mut v);
231             unsafe { mem::transmute(v) }
232         }
233         fn next_u64(&mut self) -> u64 {
234             let mut v = [0; 8];
235             self.fill_bytes(&mut v);
236             unsafe { mem::transmute(v) }
237         }
238         fn fill_bytes(&mut self, v: &mut [u8]) {
239             let ret = unsafe {
240                 SecRandomCopyBytes(kSecRandomDefault, v.len() as size_t,
241                                    v.as_mut_ptr())
242             };
243             if ret == -1 {
244                 panic!("couldn't generate random bytes: {}",
245                        io::Error::last_os_error());
246             }
247         }
248     }
249 }
250
251 #[cfg(windows)]
252 mod imp {
253     use prelude::v1::*;
254
255     use io;
256     use mem;
257     use rand::Rng;
258     use libc::types::os::arch::extra::{LONG_PTR};
259     use libc::{DWORD, BYTE, LPCSTR, BOOL};
260
261     type HCRYPTPROV = LONG_PTR;
262
263     /// A random number generator that retrieves randomness straight from
264     /// the operating system. Platform sources:
265     ///
266     /// - Unix-like systems (Linux, Android, Mac OSX): read directly from
267     ///   `/dev/urandom`, or from `getrandom(2)` system call if available.
268     /// - Windows: calls `CryptGenRandom`, using the default cryptographic
269     ///   service provider with the `PROV_RSA_FULL` type.
270     /// - iOS: calls SecRandomCopyBytes as /dev/(u)random is sandboxed.
271     ///
272     /// This does not block.
273     pub struct OsRng {
274         hcryptprov: HCRYPTPROV
275     }
276
277     const PROV_RSA_FULL: DWORD = 1;
278     const CRYPT_SILENT: DWORD = 64;
279     const CRYPT_VERIFYCONTEXT: DWORD = 0xF0000000;
280
281     #[allow(non_snake_case)]
282     extern "system" {
283         fn CryptAcquireContextA(phProv: *mut HCRYPTPROV,
284                                 pszContainer: LPCSTR,
285                                 pszProvider: LPCSTR,
286                                 dwProvType: DWORD,
287                                 dwFlags: DWORD) -> BOOL;
288         fn CryptGenRandom(hProv: HCRYPTPROV,
289                           dwLen: DWORD,
290                           pbBuffer: *mut BYTE) -> BOOL;
291         fn CryptReleaseContext(hProv: HCRYPTPROV, dwFlags: DWORD) -> BOOL;
292     }
293
294     impl OsRng {
295         /// Create a new `OsRng`.
296         pub fn new() -> io::Result<OsRng> {
297             let mut hcp = 0;
298             let ret = unsafe {
299                 CryptAcquireContextA(&mut hcp, 0 as LPCSTR, 0 as LPCSTR,
300                                      PROV_RSA_FULL,
301                                      CRYPT_VERIFYCONTEXT | CRYPT_SILENT)
302             };
303
304             if ret == 0 {
305                 Err(io::Error::last_os_error())
306             } else {
307                 Ok(OsRng { hcryptprov: hcp })
308             }
309         }
310     }
311
312     impl Rng for OsRng {
313         fn next_u32(&mut self) -> u32 {
314             let mut v = [0; 4];
315             self.fill_bytes(&mut v);
316             unsafe { mem::transmute(v) }
317         }
318         fn next_u64(&mut self) -> u64 {
319             let mut v = [0; 8];
320             self.fill_bytes(&mut v);
321             unsafe { mem::transmute(v) }
322         }
323         fn fill_bytes(&mut self, v: &mut [u8]) {
324             let ret = unsafe {
325                 CryptGenRandom(self.hcryptprov, v.len() as DWORD,
326                                v.as_mut_ptr())
327             };
328             if ret == 0 {
329                 panic!("couldn't generate random bytes: {}",
330                        io::Error::last_os_error());
331             }
332         }
333     }
334
335     impl Drop for OsRng {
336         fn drop(&mut self) {
337             let ret = unsafe {
338                 CryptReleaseContext(self.hcryptprov, 0)
339             };
340             if ret == 0 {
341                 panic!("couldn't release context: {}",
342                        io::Error::last_os_error());
343             }
344         }
345     }
346 }
347
348 #[cfg(test)]
349 mod tests {
350     use prelude::v1::*;
351
352     use sync::mpsc::channel;
353     use rand::Rng;
354     use super::OsRng;
355     use thread;
356
357     #[test]
358     fn test_os_rng() {
359         let mut r = OsRng::new().unwrap();
360
361         r.next_u32();
362         r.next_u64();
363
364         let mut v = [0; 1000];
365         r.fill_bytes(&mut v);
366     }
367
368     #[test]
369     fn test_os_rng_tasks() {
370
371         let mut txs = vec!();
372         for _ in 0..20 {
373             let (tx, rx) = channel();
374             txs.push(tx);
375
376             thread::spawn(move|| {
377                 // wait until all the tasks are ready to go.
378                 rx.recv().unwrap();
379
380                 // deschedule to attempt to interleave things as much
381                 // as possible (XXX: is this a good test?)
382                 let mut r = OsRng::new().unwrap();
383                 thread::yield_now();
384                 let mut v = [0; 1000];
385
386                 for _ in 0..100 {
387                     r.next_u32();
388                     thread::yield_now();
389                     r.next_u64();
390                     thread::yield_now();
391                     r.fill_bytes(&mut v);
392                     thread::yield_now();
393                 }
394             });
395         }
396
397         // start all the tasks
398         for tx in &txs {
399             tx.send(()).unwrap();
400         }
401     }
402 }