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