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