]> git.lizzy.rs Git - rust.git/blob - src/libstd/rand/os.rs
rollup merge of #20482: kmcallister/macro-reform
[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     extern crate libc;
19
20     use self::OsRngInner::*;
21
22     use io::{IoResult, File};
23     use path::Path;
24     use rand::Rng;
25     use rand::reader::ReaderRng;
26     use result::Result::Ok;
27     use slice::SliceExt;
28     use mem;
29     use os::errno;
30
31     // NOTE: for old macros; remove after the next snapshot
32     #[cfg(stage0)] use result::Result::Err;
33
34     #[cfg(all(target_os = "linux",
35               any(target_arch = "x86_64",
36                   target_arch = "x86",
37                   target_arch = "arm",
38                   target_arch = "aarch64")))]
39     fn getrandom(buf: &mut [u8]) -> libc::c_long {
40         extern "C" {
41             fn syscall(number: libc::c_long, ...) -> libc::c_long;
42         }
43
44         #[cfg(target_arch = "x86_64")]
45         const NR_GETRANDOM: libc::c_long = 318;
46         #[cfg(target_arch = "x86")]
47         const NR_GETRANDOM: libc::c_long = 355;
48         #[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
49         const NR_GETRANDOM: libc::c_long = 384;
50
51         unsafe {
52             syscall(NR_GETRANDOM, buf.as_mut_ptr(), buf.len(), 0u)
53         }
54     }
55
56     #[cfg(not(all(target_os = "linux",
57                   any(target_arch = "x86_64",
58                       target_arch = "x86",
59                       target_arch = "arm",
60                       target_arch = "aarch64"))))]
61     fn getrandom(_buf: &mut [u8]) -> libc::c_long { -1 }
62
63     fn getrandom_fill_bytes(v: &mut [u8]) {
64         let mut read = 0;
65         let len = v.len();
66         while read < len {
67             let result = getrandom(v.slice_from_mut(read));
68             if result == -1 {
69                 let err = errno() as libc::c_int;
70                 if err == libc::EINTR {
71                     continue;
72                 } else {
73                     panic!("unexpected getrandom error: {}", err);
74                 }
75             } else {
76                 read += result as uint;
77             }
78         }
79     }
80
81     fn getrandom_next_u32() -> u32 {
82         let mut buf: [u8; 4] = [0u8; 4];
83         getrandom_fill_bytes(&mut buf);
84         unsafe { mem::transmute::<[u8; 4], u32>(buf) }
85     }
86
87     fn getrandom_next_u64() -> u64 {
88         let mut buf: [u8; 8] = [0u8; 8];
89         getrandom_fill_bytes(&mut buf);
90         unsafe { mem::transmute::<[u8; 8], u64>(buf) }
91     }
92
93     #[cfg(all(target_os = "linux",
94               any(target_arch = "x86_64",
95                   target_arch = "x86",
96                   target_arch = "arm",
97                   target_arch = "aarch64")))]
98     fn is_getrandom_available() -> bool {
99         use sync::atomic::{AtomicBool, ATOMIC_BOOL_INIT, Ordering};
100
101         static GETRANDOM_CHECKED: AtomicBool = ATOMIC_BOOL_INIT;
102         static GETRANDOM_AVAILABLE: AtomicBool = ATOMIC_BOOL_INIT;
103
104         if !GETRANDOM_CHECKED.load(Ordering::Relaxed) {
105             let mut buf: [u8; 0] = [];
106             let result = getrandom(&mut buf);
107             let available = if result == -1 {
108                 let err = errno() as libc::c_int;
109                 err != libc::ENOSYS
110             } else {
111                 true
112             };
113             GETRANDOM_AVAILABLE.store(available, Ordering::Relaxed);
114             GETRANDOM_CHECKED.store(true, Ordering::Relaxed);
115             available
116         } else {
117             GETRANDOM_AVAILABLE.load(Ordering::Relaxed)
118         }
119     }
120
121     #[cfg(not(all(target_os = "linux",
122                   any(target_arch = "x86_64",
123                       target_arch = "x86",
124                       target_arch = "arm",
125                       target_arch = "aarch64"))))]
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() -> IoResult<OsRng> {
150             if is_getrandom_available() {
151                 return Ok(OsRng { inner: OsGetrandomRng });
152             }
153
154             let reader = try!(File::open(&Path::new("/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     extern crate libc;
186
187     use io::{IoResult};
188     use kinds::Sync;
189     use mem;
190     use os;
191     use rand::Rng;
192     use result::Result::{Ok};
193     use self::libc::{c_int, size_t};
194     use slice::SliceExt;
195
196     /// A random number generator that retrieves randomness straight from
197     /// the operating system. Platform sources:
198     ///
199     /// - Unix-like systems (Linux, Android, Mac OSX): read directly from
200     ///   `/dev/urandom`, or from `getrandom(2)` system call if available.
201     /// - Windows: calls `CryptGenRandom`, using the default cryptographic
202     ///   service provider with the `PROV_RSA_FULL` type.
203     /// - iOS: calls SecRandomCopyBytes as /dev/(u)random is sandboxed.
204     ///
205     /// This does not block.
206     #[allow(missing_copy_implementations)]
207     pub struct OsRng {
208         // dummy field to ensure that this struct cannot be constructed outside of this module
209         _dummy: (),
210     }
211
212     #[repr(C)]
213     struct SecRandom;
214
215     unsafe impl Sync for *const SecRandom {}
216
217     #[allow(non_upper_case_globals)]
218     static kSecRandomDefault: *const SecRandom = 0 as *const SecRandom;
219
220     #[link(name = "Security", kind = "framework")]
221     extern "C" {
222         fn SecRandomCopyBytes(rnd: *const SecRandom,
223                               count: size_t, bytes: *mut u8) -> c_int;
224     }
225
226     impl OsRng {
227         /// Create a new `OsRng`.
228         pub fn new() -> IoResult<OsRng> {
229             Ok(OsRng { _dummy: () })
230         }
231     }
232
233     impl Rng for OsRng {
234         fn next_u32(&mut self) -> u32 {
235             let mut v = [0u8; 4];
236             self.fill_bytes(&mut v);
237             unsafe { mem::transmute(v) }
238         }
239         fn next_u64(&mut self) -> u64 {
240             let mut v = [0u8; 8];
241             self.fill_bytes(&mut v);
242             unsafe { mem::transmute(v) }
243         }
244         fn fill_bytes(&mut self, v: &mut [u8]) {
245             let ret = unsafe {
246                 SecRandomCopyBytes(kSecRandomDefault, v.len() as size_t, v.as_mut_ptr())
247             };
248             if ret == -1 {
249                 panic!("couldn't generate random bytes: {}", os::last_os_error());
250             }
251         }
252     }
253 }
254
255 #[cfg(windows)]
256 mod imp {
257     extern crate libc;
258
259     use io::{IoResult, IoError};
260     use mem;
261     use ops::Drop;
262     use os;
263     use rand::Rng;
264     use result::Result::{Ok, Err};
265     use self::libc::{DWORD, BYTE, LPCSTR, BOOL};
266     use self::libc::types::os::arch::extra::{LONG_PTR};
267     use slice::SliceExt;
268
269     type HCRYPTPROV = LONG_PTR;
270
271     /// A random number generator that retrieves randomness straight from
272     /// the operating system. Platform sources:
273     ///
274     /// - Unix-like systems (Linux, Android, Mac OSX): read directly from
275     ///   `/dev/urandom`, or from `getrandom(2)` system call if available.
276     /// - Windows: calls `CryptGenRandom`, using the default cryptographic
277     ///   service provider with the `PROV_RSA_FULL` type.
278     /// - iOS: calls SecRandomCopyBytes as /dev/(u)random is sandboxed.
279     ///
280     /// This does not block.
281     pub struct OsRng {
282         hcryptprov: HCRYPTPROV
283     }
284
285     static PROV_RSA_FULL: DWORD = 1;
286     static CRYPT_SILENT: DWORD = 64;
287     static CRYPT_VERIFYCONTEXT: DWORD = 0xF0000000;
288
289     #[allow(non_snake_case)]
290     extern "system" {
291         fn CryptAcquireContextA(phProv: *mut HCRYPTPROV,
292                                 pszContainer: LPCSTR,
293                                 pszProvider: LPCSTR,
294                                 dwProvType: DWORD,
295                                 dwFlags: DWORD) -> BOOL;
296         fn CryptGenRandom(hProv: HCRYPTPROV,
297                           dwLen: DWORD,
298                           pbBuffer: *mut BYTE) -> BOOL;
299         fn CryptReleaseContext(hProv: HCRYPTPROV, dwFlags: DWORD) -> BOOL;
300     }
301
302     impl OsRng {
303         /// Create a new `OsRng`.
304         pub fn new() -> IoResult<OsRng> {
305             let mut hcp = 0;
306             let ret = unsafe {
307                 CryptAcquireContextA(&mut hcp, 0 as LPCSTR, 0 as LPCSTR,
308                                      PROV_RSA_FULL,
309                                      CRYPT_VERIFYCONTEXT | CRYPT_SILENT)
310             };
311
312             if ret == 0 {
313                 Err(IoError::last_error())
314             } else {
315                 Ok(OsRng { hcryptprov: hcp })
316             }
317         }
318     }
319
320     impl Rng for OsRng {
321         fn next_u32(&mut self) -> u32 {
322             let mut v = [0u8; 4];
323             self.fill_bytes(&mut v);
324             unsafe { mem::transmute(v) }
325         }
326         fn next_u64(&mut self) -> u64 {
327             let mut v = [0u8; 8];
328             self.fill_bytes(&mut v);
329             unsafe { mem::transmute(v) }
330         }
331         fn fill_bytes(&mut self, v: &mut [u8]) {
332             let ret = unsafe {
333                 CryptGenRandom(self.hcryptprov, v.len() as DWORD,
334                                v.as_mut_ptr())
335             };
336             if ret == 0 {
337                 panic!("couldn't generate random bytes: {}", os::last_os_error());
338             }
339         }
340     }
341
342     impl Drop for OsRng {
343         fn drop(&mut self) {
344             let ret = unsafe {
345                 CryptReleaseContext(self.hcryptprov, 0)
346             };
347             if ret == 0 {
348                 panic!("couldn't release context: {}", os::last_os_error());
349             }
350         }
351     }
352 }
353
354 #[cfg(test)]
355 mod test {
356     use prelude::v1::*;
357
358     use sync::mpsc::channel;
359     use rand::Rng;
360     use super::OsRng;
361     use thread::Thread;
362
363     #[test]
364     fn test_os_rng() {
365         let mut r = OsRng::new().unwrap();
366
367         r.next_u32();
368         r.next_u64();
369
370         let mut v = [0u8; 1000];
371         r.fill_bytes(&mut v);
372     }
373
374     #[test]
375     fn test_os_rng_tasks() {
376
377         let mut txs = vec!();
378         for _ in range(0u, 20) {
379             let (tx, rx) = channel();
380             txs.push(tx);
381
382             Thread::spawn(move|| {
383                 // wait until all the tasks are ready to go.
384                 rx.recv().unwrap();
385
386                 // deschedule to attempt to interleave things as much
387                 // as possible (XXX: is this a good test?)
388                 let mut r = OsRng::new().unwrap();
389                 Thread::yield_now();
390                 let mut v = [0u8; 1000];
391
392                 for _ in range(0u, 100) {
393                     r.next_u32();
394                     Thread::yield_now();
395                     r.next_u64();
396                     Thread::yield_now();
397                     r.fill_bytes(&mut v);
398                     Thread::yield_now();
399                 }
400             }).detach();
401         }
402
403         // start all the tasks
404         for tx in txs.iter() {
405             tx.send(()).unwrap();
406         }
407     }
408 }