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