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