]> git.lizzy.rs Git - rust.git/blob - src/libstd/rand/os.rs
rollup merge of #18407 : thestinger/arena
[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     use io::{IoResult, File};
19     use path::Path;
20     use rand::Rng;
21     use rand::reader::ReaderRng;
22     use result::{Ok, Err};
23
24     /// A random number generator that retrieves randomness straight from
25     /// the operating system. Platform sources:
26     ///
27     /// - Unix-like systems (Linux, Android, Mac OSX): read directly from
28     ///   `/dev/urandom`.
29     /// - Windows: calls `CryptGenRandom`, using the default cryptographic
30     ///   service provider with the `PROV_RSA_FULL` type.
31     /// - iOS: calls SecRandomCopyBytes as /dev/(u)random is sandboxed
32     /// This does not block.
33     #[cfg(unix)]
34     pub struct OsRng {
35         inner: ReaderRng<File>
36     }
37
38     impl OsRng {
39         /// Create a new `OsRng`.
40         pub fn new() -> IoResult<OsRng> {
41             let reader = try!(File::open(&Path::new("/dev/urandom")));
42             let reader_rng = ReaderRng::new(reader);
43
44             Ok(OsRng { inner: reader_rng })
45         }
46     }
47
48     impl Rng for OsRng {
49         fn next_u32(&mut self) -> u32 {
50             self.inner.next_u32()
51         }
52         fn next_u64(&mut self) -> u64 {
53             self.inner.next_u64()
54         }
55         fn fill_bytes(&mut self, v: &mut [u8]) {
56             self.inner.fill_bytes(v)
57         }
58     }
59 }
60
61 #[cfg(target_os = "ios")]
62 mod imp {
63     extern crate libc;
64
65     use collections::Collection;
66     use io::{IoResult};
67     use kinds::marker;
68     use mem;
69     use os;
70     use rand::Rng;
71     use result::{Ok};
72     use self::libc::{c_int, size_t};
73     use slice::MutableSlice;
74
75     /// A random number generator that retrieves randomness straight from
76     /// the operating system. Platform sources:
77     ///
78     /// - Unix-like systems (Linux, Android, Mac OSX): read directly from
79     ///   `/dev/urandom`.
80     /// - Windows: calls `CryptGenRandom`, using the default cryptographic
81     ///   service provider with the `PROV_RSA_FULL` type.
82     /// - iOS: calls SecRandomCopyBytes as /dev/(u)random is sandboxed
83     /// This does not block.
84     pub struct OsRng {
85         marker: marker::NoCopy
86     }
87
88     #[repr(C)]
89     struct SecRandom;
90
91     #[allow(non_uppercase_statics)]
92     static kSecRandomDefault: *const SecRandom = 0 as *const SecRandom;
93
94     #[link(name = "Security", kind = "framework")]
95     extern "C" {
96         fn SecRandomCopyBytes(rnd: *const SecRandom,
97                               count: size_t, bytes: *mut u8) -> c_int;
98     }
99
100     impl OsRng {
101         /// Create a new `OsRng`.
102         pub fn new() -> IoResult<OsRng> {
103             Ok(OsRng {marker: marker::NoCopy} )
104         }
105     }
106
107     impl Rng for OsRng {
108         fn next_u32(&mut self) -> u32 {
109             let mut v = [0u8, .. 4];
110             self.fill_bytes(v);
111             unsafe { mem::transmute(v) }
112         }
113         fn next_u64(&mut self) -> u64 {
114             let mut v = [0u8, .. 8];
115             self.fill_bytes(v);
116             unsafe { mem::transmute(v) }
117         }
118         fn fill_bytes(&mut self, v: &mut [u8]) {
119             let ret = unsafe {
120                 SecRandomCopyBytes(kSecRandomDefault, v.len() as size_t, v.as_mut_ptr())
121             };
122             if ret == -1 {
123                 panic!("couldn't generate random bytes: {}", os::last_os_error());
124             }
125         }
126     }
127 }
128
129 #[cfg(windows)]
130 mod imp {
131     extern crate libc;
132
133     use core_collections::Collection;
134     use io::{IoResult, IoError};
135     use mem;
136     use ops::Drop;
137     use os;
138     use rand::Rng;
139     use result::{Ok, Err};
140     use self::libc::{DWORD, BYTE, LPCSTR, BOOL};
141     use self::libc::types::os::arch::extra::{LONG_PTR};
142     use slice::MutableSlice;
143
144     type HCRYPTPROV = LONG_PTR;
145
146     /// A random number generator that retrieves randomness straight from
147     /// the operating system. Platform sources:
148     ///
149     /// - Unix-like systems (Linux, Android, Mac OSX): read directly from
150     ///   `/dev/urandom`.
151     /// - Windows: calls `CryptGenRandom`, using the default cryptographic
152     ///   service provider with the `PROV_RSA_FULL` type.
153     ///
154     /// This does not block.
155     pub struct OsRng {
156         hcryptprov: HCRYPTPROV
157     }
158
159     static PROV_RSA_FULL: DWORD = 1;
160     static CRYPT_SILENT: DWORD = 64;
161     static CRYPT_VERIFYCONTEXT: DWORD = 0xF0000000;
162
163     #[allow(non_snake_case)]
164     extern "system" {
165         fn CryptAcquireContextA(phProv: *mut HCRYPTPROV,
166                                 pszContainer: LPCSTR,
167                                 pszProvider: LPCSTR,
168                                 dwProvType: DWORD,
169                                 dwFlags: DWORD) -> BOOL;
170         fn CryptGenRandom(hProv: HCRYPTPROV,
171                           dwLen: DWORD,
172                           pbBuffer: *mut BYTE) -> BOOL;
173         fn CryptReleaseContext(hProv: HCRYPTPROV, dwFlags: DWORD) -> BOOL;
174     }
175
176     impl OsRng {
177         /// Create a new `OsRng`.
178         pub fn new() -> IoResult<OsRng> {
179             let mut hcp = 0;
180             let ret = unsafe {
181                 CryptAcquireContextA(&mut hcp, 0 as LPCSTR, 0 as LPCSTR,
182                                      PROV_RSA_FULL,
183                                      CRYPT_VERIFYCONTEXT | CRYPT_SILENT)
184             };
185
186             if ret == 0 {
187                 Err(IoError::last_error())
188             } else {
189                 Ok(OsRng { hcryptprov: hcp })
190             }
191         }
192     }
193
194     impl Rng for OsRng {
195         fn next_u32(&mut self) -> u32 {
196             let mut v = [0u8, .. 4];
197             self.fill_bytes(v);
198             unsafe { mem::transmute(v) }
199         }
200         fn next_u64(&mut self) -> u64 {
201             let mut v = [0u8, .. 8];
202             self.fill_bytes(v);
203             unsafe { mem::transmute(v) }
204         }
205         fn fill_bytes(&mut self, v: &mut [u8]) {
206             let ret = unsafe {
207                 CryptGenRandom(self.hcryptprov, v.len() as DWORD,
208                                v.as_mut_ptr())
209             };
210             if ret == 0 {
211                 panic!("couldn't generate random bytes: {}", os::last_os_error());
212             }
213         }
214     }
215
216     impl Drop for OsRng {
217         fn drop(&mut self) {
218             let ret = unsafe {
219                 CryptReleaseContext(self.hcryptprov, 0)
220             };
221             if ret == 0 {
222                 panic!("couldn't release context: {}", os::last_os_error());
223             }
224         }
225     }
226 }
227
228 #[cfg(test)]
229 mod test {
230     use prelude::*;
231
232     use super::OsRng;
233     use rand::Rng;
234     use task;
235
236     #[test]
237     fn test_os_rng() {
238         let mut r = OsRng::new().unwrap();
239
240         r.next_u32();
241         r.next_u64();
242
243         let mut v = [0u8, .. 1000];
244         r.fill_bytes(v);
245     }
246
247     #[test]
248     fn test_os_rng_tasks() {
249
250         let mut txs = vec!();
251         for _ in range(0u, 20) {
252             let (tx, rx) = channel();
253             txs.push(tx);
254             task::spawn(proc() {
255                 // wait until all the tasks are ready to go.
256                 rx.recv();
257
258                 // deschedule to attempt to interleave things as much
259                 // as possible (XXX: is this a good test?)
260                 let mut r = OsRng::new().unwrap();
261                 task::deschedule();
262                 let mut v = [0u8, .. 1000];
263
264                 for _ in range(0u, 100) {
265                     r.next_u32();
266                     task::deschedule();
267                     r.next_u64();
268                     task::deschedule();
269                     r.fill_bytes(v);
270                     task::deschedule();
271                 }
272             })
273         }
274
275         // start all the tasks
276         for tx in txs.iter() {
277             tx.send(())
278         }
279     }
280 }