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