]> git.lizzy.rs Git - rust.git/blob - src/libstd/rand/os.rs
unbreak openbsd code
[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     fn getrandom(buf: &mut [u8]) -> libc::c_long {
35         extern "C" {
36             fn syscall(number: libc::c_long, ...) -> libc::c_long;
37         }
38
39         #[cfg(target_arch = "x86_64")]
40         const NR_GETRANDOM: libc::c_long = 318;
41         #[cfg(target_arch = "x86")]
42         const NR_GETRANDOM: libc::c_long = 355;
43         #[cfg(any(target_arch = "arm", target_arch = "powerpc"))]
44         const NR_GETRANDOM: libc::c_long = 384;
45         #[cfg(target_arch = "aarch64")]
46         const NR_GETRANDOM: libc::c_long = 278;
47
48         unsafe {
49             syscall(NR_GETRANDOM, buf.as_mut_ptr(), buf.len(), 0)
50         }
51     }
52
53     #[cfg(not(all(target_os = "linux",
54                   any(target_arch = "x86_64",
55                       target_arch = "x86",
56                       target_arch = "arm",
57                       target_arch = "aarch64",
58                       target_arch = "powerpc"))))]
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     fn is_getrandom_available() -> bool {
97         use sync::atomic::{AtomicBool, Ordering};
98         use sync::Once;
99
100         static CHECKER: Once = Once::new();
101         static AVAILABLE: AtomicBool = AtomicBool::new(false);
102
103         CHECKER.call_once(|| {
104             let mut buf: [u8; 0] = [];
105             let result = getrandom(&mut buf);
106             let available = if result == -1 {
107                 let err = io::Error::last_os_error().raw_os_error();
108                 err != Some(libc::ENOSYS)
109             } else {
110                 true
111             };
112             AVAILABLE.store(available, Ordering::Relaxed);
113         });
114
115         AVAILABLE.load(Ordering::Relaxed)
116     }
117
118     #[cfg(not(all(target_os = "linux",
119                   any(target_arch = "x86_64",
120                       target_arch = "x86",
121                       target_arch = "arm",
122                       target_arch = "aarch64",
123                       target_arch = "powerpc"))))]
124     fn is_getrandom_available() -> bool { false }
125
126     /// A random number generator that retrieves randomness straight from
127     /// the operating system. Platform sources:
128     ///
129     /// - Unix-like systems (Linux, Android, Mac OSX): read directly from
130     ///   `/dev/urandom`, or from `getrandom(2)` system call if available.
131     /// - Windows: calls `CryptGenRandom`, using the default cryptographic
132     ///   service provider with the `PROV_RSA_FULL` type.
133     /// - iOS: calls SecRandomCopyBytes as /dev/(u)random is sandboxed.
134     /// - OpenBSD: uses the `getentropy(2)` system call.
135     ///
136     /// This does not block.
137     pub struct OsRng {
138         inner: OsRngInner,
139     }
140
141     enum OsRngInner {
142         OsGetrandomRng,
143         OsReaderRng(ReaderRng<File>),
144     }
145
146     impl OsRng {
147         /// Create a new `OsRng`.
148         pub fn new() -> io::Result<OsRng> {
149             if is_getrandom_available() {
150                 return Ok(OsRng { inner: OsGetrandomRng });
151             }
152
153             let reader = try!(File::open("/dev/urandom"));
154             let reader_rng = ReaderRng::new(reader);
155
156             Ok(OsRng { inner: OsReaderRng(reader_rng) })
157         }
158     }
159
160     impl Rng for OsRng {
161         fn next_u32(&mut self) -> u32 {
162             match self.inner {
163                 OsGetrandomRng => getrandom_next_u32(),
164                 OsReaderRng(ref mut rng) => rng.next_u32(),
165             }
166         }
167         fn next_u64(&mut self) -> u64 {
168             match self.inner {
169                 OsGetrandomRng => getrandom_next_u64(),
170                 OsReaderRng(ref mut rng) => rng.next_u64(),
171             }
172         }
173         fn fill_bytes(&mut self, v: &mut [u8]) {
174             match self.inner {
175                 OsGetrandomRng => getrandom_fill_bytes(v),
176                 OsReaderRng(ref mut rng) => rng.fill_bytes(v)
177             }
178         }
179     }
180 }
181
182 #[cfg(target_os = "openbsd")]
183 mod imp {
184     use io;
185     use libc;
186     use mem;
187     use sys::os::errno;
188     use rand::Rng;
189
190     /// A random number generator that retrieves randomness straight from
191     /// the operating system. Platform sources:
192     ///
193     /// - Unix-like systems (Linux, Android, Mac OSX): read directly from
194     ///   `/dev/urandom`, or from `getrandom(2)` system call if available.
195     /// - Windows: calls `CryptGenRandom`, using the default cryptographic
196     ///   service provider with the `PROV_RSA_FULL` type.
197     /// - iOS: calls SecRandomCopyBytes as /dev/(u)random is sandboxed.
198     /// - OpenBSD: uses the `getentropy(2)` system call.
199     ///
200     /// This does not block.
201     pub struct OsRng {
202         // dummy field to ensure that this struct cannot be constructed outside
203         // of this module
204         _dummy: (),
205     }
206
207     impl OsRng {
208         /// Create a new `OsRng`.
209         pub fn new() -> io::Result<OsRng> {
210             Ok(OsRng { _dummy: () })
211         }
212     }
213
214     impl Rng for OsRng {
215         fn next_u32(&mut self) -> u32 {
216             let mut v = [0; 4];
217             self.fill_bytes(&mut v);
218             unsafe { mem::transmute(v) }
219         }
220         fn next_u64(&mut self) -> u64 {
221             let mut v = [0; 8];
222             self.fill_bytes(&mut v);
223             unsafe { mem::transmute(v) }
224         }
225         fn fill_bytes(&mut self, v: &mut [u8]) {
226             // getentropy(2) permits a maximum buffer size of 256 bytes
227             for s in v.chunks_mut(256) {
228                 let ret = unsafe {
229                     libc::syscall(libc::NR_GETENTROPY, s.as_mut_ptr(), s.len())
230                 };
231                 if ret == -1 {
232                     panic!("unexpected getentropy error: {}", errno());
233                 }
234             }
235         }
236     }
237 }
238
239 #[cfg(target_os = "ios")]
240 mod imp {
241     #[cfg(stage0)] use prelude::v1::*;
242
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 prelude::v1::*;
387
388     use sync::mpsc::channel;
389     use rand::Rng;
390     use super::OsRng;
391     use thread;
392
393     #[test]
394     fn test_os_rng() {
395         let mut r = OsRng::new().unwrap();
396
397         r.next_u32();
398         r.next_u64();
399
400         let mut v = [0; 1000];
401         r.fill_bytes(&mut v);
402     }
403
404     #[test]
405     fn test_os_rng_tasks() {
406
407         let mut txs = vec!();
408         for _ in 0..20 {
409             let (tx, rx) = channel();
410             txs.push(tx);
411
412             thread::spawn(move|| {
413                 // wait until all the threads are ready to go.
414                 rx.recv().unwrap();
415
416                 // deschedule to attempt to interleave things as much
417                 // as possible (XXX: is this a good test?)
418                 let mut r = OsRng::new().unwrap();
419                 thread::yield_now();
420                 let mut v = [0; 1000];
421
422                 for _ in 0..100 {
423                     r.next_u32();
424                     thread::yield_now();
425                     r.next_u64();
426                     thread::yield_now();
427                     r.fill_bytes(&mut v);
428                     thread::yield_now();
429                 }
430             });
431         }
432
433         // start all the threads
434         for tx in &txs {
435             tx.send(()).unwrap();
436         }
437     }
438 }