]> git.lizzy.rs Git - rust.git/blob - src/libstd/rand/os.rs
f507011c2b955166e5863b85aee4aa306e8b7444
[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(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::MutableVector;
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     struct SecRandom;
89
90     static kSecRandomDefault: *SecRandom = 0 as *SecRandom;
91
92     #[link(name = "Security", kind = "framework")]
93     extern "C" {
94         fn SecRandomCopyBytes(rnd: *SecRandom, count: size_t, bytes: *mut u8) -> c_int;
95     }
96
97     impl OsRng {
98         /// Create a new `OsRng`.
99         pub fn new() -> IoResult<OsRng> {
100             Ok(OsRng {marker: marker::NoCopy} )
101         }
102     }
103
104     impl Rng for OsRng {
105         fn next_u32(&mut self) -> u32 {
106             let mut v = [0u8, .. 4];
107             self.fill_bytes(v);
108             unsafe { mem::transmute(v) }
109         }
110         fn next_u64(&mut self) -> u64 {
111             let mut v = [0u8, .. 8];
112             self.fill_bytes(v);
113             unsafe { mem::transmute(v) }
114         }
115         fn fill_bytes(&mut self, v: &mut [u8]) {
116             let ret = unsafe {
117                 SecRandomCopyBytes(kSecRandomDefault, v.len() as size_t, v.as_mut_ptr())
118             };
119             if ret == -1 {
120                 fail!("couldn't generate random bytes: {}", os::last_os_error());
121             }
122         }
123     }
124 }
125
126 #[cfg(windows)]
127 mod imp {
128     extern crate libc;
129
130     use core_collections::Collection;
131     use io::{IoResult, IoError};
132     use mem;
133     use ops::Drop;
134     use os;
135     use rand::Rng;
136     use result::{Ok, Err};
137     use rt::stack;
138     use self::libc::{c_ulong, DWORD, BYTE, LPCSTR, BOOL};
139     use slice::MutableVector;
140
141     type HCRYPTPROV = c_ulong;
142
143     /// A random number generator that retrieves randomness straight from
144     /// the operating system. Platform sources:
145     ///
146     /// - Unix-like systems (Linux, Android, Mac OSX): read directly from
147     ///   `/dev/urandom`.
148     /// - Windows: calls `CryptGenRandom`, using the default cryptographic
149     ///   service provider with the `PROV_RSA_FULL` type.
150     ///
151     /// This does not block.
152     pub struct OsRng {
153         hcryptprov: HCRYPTPROV
154     }
155
156     static PROV_RSA_FULL: DWORD = 1;
157     static CRYPT_SILENT: DWORD = 64;
158     static CRYPT_VERIFYCONTEXT: DWORD = 0xF0000000;
159     static NTE_BAD_SIGNATURE: DWORD = 0x80090006;
160
161     #[allow(non_snake_case_functions)]
162     extern "system" {
163         fn CryptAcquireContextA(phProv: *mut HCRYPTPROV,
164                                 pszContainer: LPCSTR,
165                                 pszProvider: LPCSTR,
166                                 dwProvType: DWORD,
167                                 dwFlags: DWORD) -> BOOL;
168         fn CryptGenRandom(hProv: HCRYPTPROV,
169                           dwLen: DWORD,
170                           pbBuffer: *mut BYTE) -> BOOL;
171         fn CryptReleaseContext(hProv: HCRYPTPROV, dwFlags: DWORD) -> BOOL;
172     }
173
174     impl OsRng {
175         /// Create a new `OsRng`.
176         pub fn new() -> IoResult<OsRng> {
177             let mut hcp = 0;
178             let mut ret = unsafe {
179                 CryptAcquireContextA(&mut hcp, 0 as LPCSTR, 0 as LPCSTR,
180                                      PROV_RSA_FULL,
181                                      CRYPT_VERIFYCONTEXT | CRYPT_SILENT)
182             };
183
184             // FIXME #13259:
185             // It turns out that if we can't acquire a context with the
186             // NTE_BAD_SIGNATURE error code, the documentation states:
187             //
188             //     The provider DLL signature could not be verified. Either the
189             //     DLL or the digital signature has been tampered with.
190             //
191             // Sounds fishy, no? As it turns out, our signature can be bad
192             // because our Thread Information Block (TIB) isn't exactly what it
193             // expects. As to why, I have no idea. The only data we store in the
194             // TIB is the stack limit for each thread, but apparently that's
195             // enough to make the signature valid.
196             //
197             // Furthermore, this error only happens the *first* time we call
198             // CryptAcquireContext, so we don't have to worry about future
199             // calls.
200             //
201             // Anyway, the fix employed here is that if we see this error, we
202             // pray that we're not close to the end of the stack, temporarily
203             // set the stack limit to 0 (what the TIB originally was), acquire a
204             // context, and then reset the stack limit.
205             //
206             // Again, I'm not sure why this is the fix, nor why we're getting
207             // this error. All I can say is that this seems to allow libnative
208             // to progress where it otherwise would be hindered. Who knew?
209             if ret == 0 && os::errno() as DWORD == NTE_BAD_SIGNATURE {
210                 unsafe {
211                     let limit = stack::get_sp_limit();
212                     stack::record_sp_limit(0);
213                     ret = CryptAcquireContextA(&mut hcp, 0 as LPCSTR, 0 as LPCSTR,
214                                                PROV_RSA_FULL,
215                                                CRYPT_VERIFYCONTEXT | CRYPT_SILENT);
216                     stack::record_sp_limit(limit);
217                 }
218             }
219
220             if ret == 0 {
221                 Err(IoError::last_error())
222             } else {
223                 Ok(OsRng { hcryptprov: hcp })
224             }
225         }
226     }
227
228     impl Rng for OsRng {
229         fn next_u32(&mut self) -> u32 {
230             let mut v = [0u8, .. 4];
231             self.fill_bytes(v);
232             unsafe { mem::transmute(v) }
233         }
234         fn next_u64(&mut self) -> u64 {
235             let mut v = [0u8, .. 8];
236             self.fill_bytes(v);
237             unsafe { mem::transmute(v) }
238         }
239         fn fill_bytes(&mut self, v: &mut [u8]) {
240             let ret = unsafe {
241                 CryptGenRandom(self.hcryptprov, v.len() as DWORD,
242                                v.as_mut_ptr())
243             };
244             if ret == 0 {
245                 fail!("couldn't generate random bytes: {}", os::last_os_error());
246             }
247         }
248     }
249
250     impl Drop for OsRng {
251         fn drop(&mut self) {
252             let ret = unsafe {
253                 CryptReleaseContext(self.hcryptprov, 0)
254             };
255             if ret == 0 {
256                 fail!("couldn't release context: {}", os::last_os_error());
257             }
258         }
259     }
260 }
261
262 #[cfg(test)]
263 mod test {
264     use prelude::*;
265
266     use super::OsRng;
267     use rand::Rng;
268     use task;
269
270     #[test]
271     fn test_os_rng() {
272         let mut r = OsRng::new().unwrap();
273
274         r.next_u32();
275         r.next_u64();
276
277         let mut v = [0u8, .. 1000];
278         r.fill_bytes(v);
279     }
280
281     #[test]
282     fn test_os_rng_tasks() {
283
284         let mut txs = vec!();
285         for _ in range(0, 20) {
286             let (tx, rx) = channel();
287             txs.push(tx);
288             task::spawn(proc() {
289                 // wait until all the tasks are ready to go.
290                 rx.recv();
291
292                 // deschedule to attempt to interleave things as much
293                 // as possible (XXX: is this a good test?)
294                 let mut r = OsRng::new().unwrap();
295                 task::deschedule();
296                 let mut v = [0u8, .. 1000];
297
298                 for _ in range(0, 100) {
299                     r.next_u32();
300                     task::deschedule();
301                     r.next_u64();
302                     task::deschedule();
303                     r.fill_bytes(v);
304                     task::deschedule();
305                 }
306             })
307         }
308
309         // start all the tasks
310         for tx in txs.iter() {
311             tx.send(())
312         }
313     }
314 }