]> git.lizzy.rs Git - rust.git/blob - src/libstd/sys/unix/rand.rs
Rollup merge of #39604 - est31:i128_tests, r=alexcrichton
[rust.git] / src / libstd / sys / unix / rand.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 pub use self::imp::OsRng;
12
13 use mem;
14
15 fn next_u32(mut fill_buf: &mut FnMut(&mut [u8])) -> u32 {
16     let mut buf: [u8; 4] = [0; 4];
17     fill_buf(&mut buf);
18     unsafe { mem::transmute::<[u8; 4], u32>(buf) }
19 }
20
21 fn next_u64(mut fill_buf: &mut FnMut(&mut [u8])) -> u64 {
22     let mut buf: [u8; 8] = [0; 8];
23     fill_buf(&mut buf);
24     unsafe { mem::transmute::<[u8; 8], u64>(buf) }
25 }
26
27 #[cfg(all(unix,
28           not(target_os = "ios"),
29           not(target_os = "openbsd"),
30           not(target_os = "freebsd"),
31           not(target_os = "fuchsia")))]
32 mod imp {
33     use self::OsRngInner::*;
34     use super::{next_u32, next_u64};
35
36     use fs::File;
37     use io;
38     use libc;
39     use rand::Rng;
40     use rand::reader::ReaderRng;
41     use sys::os::errno;
42
43     #[cfg(all(target_os = "linux",
44               any(target_arch = "x86_64",
45                   target_arch = "x86",
46                   target_arch = "arm",
47                   target_arch = "aarch64",
48                   target_arch = "powerpc",
49                   target_arch = "powerpc64",
50                   target_arch = "s390x")))]
51     fn getrandom(buf: &mut [u8]) -> libc::c_long {
52         #[cfg(target_arch = "x86_64")]
53         const NR_GETRANDOM: libc::c_long = 318;
54         #[cfg(target_arch = "x86")]
55         const NR_GETRANDOM: libc::c_long = 355;
56         #[cfg(target_arch = "arm")]
57         const NR_GETRANDOM: libc::c_long = 384;
58         #[cfg(target_arch = "s390x")]
59         const NR_GETRANDOM: libc::c_long = 349;
60         #[cfg(any(target_arch = "powerpc", target_arch = "powerpc64"))]
61         const NR_GETRANDOM: libc::c_long = 359;
62         #[cfg(target_arch = "aarch64")]
63         const NR_GETRANDOM: libc::c_long = 278;
64
65         const GRND_NONBLOCK: libc::c_uint = 0x0001;
66
67         unsafe {
68             libc::syscall(NR_GETRANDOM, buf.as_mut_ptr(), buf.len(), GRND_NONBLOCK)
69         }
70     }
71
72     #[cfg(not(all(target_os = "linux",
73                   any(target_arch = "x86_64",
74                       target_arch = "x86",
75                       target_arch = "arm",
76                       target_arch = "aarch64",
77                       target_arch = "powerpc",
78                       target_arch = "powerpc64",
79                       target_arch = "s390x"))))]
80     fn getrandom(_buf: &mut [u8]) -> libc::c_long { -1 }
81
82     fn getrandom_fill_bytes(v: &mut [u8]) {
83         let mut read = 0;
84         while read < v.len() {
85             let result = getrandom(&mut v[read..]);
86             if result == -1 {
87                 let err = errno() as libc::c_int;
88                 if err == libc::EINTR {
89                     continue;
90                 } else if err == libc::EAGAIN {
91                     // if getrandom() returns EAGAIN it would have blocked
92                     // because the non-blocking pool (urandom) has not
93                     // initialized in the kernel yet due to a lack of entropy
94                     // the fallback we do here is to avoid blocking applications
95                     // which could depend on this call without ever knowing
96                     // they do and don't have a work around. The PRNG of
97                     // /dev/urandom will still be used but not over a completely
98                     // full entropy pool
99                     let reader = File::open("/dev/urandom").expect("Unable to open /dev/urandom");
100                     let mut reader_rng = ReaderRng::new(reader);
101                     reader_rng.fill_bytes(&mut v[read..]);
102                     read += v.len();
103                 } else {
104                     panic!("unexpected getrandom error: {}", err);
105                 }
106             } else {
107                 read += result as usize;
108             }
109         }
110     }
111
112     #[cfg(all(target_os = "linux",
113               any(target_arch = "x86_64",
114                   target_arch = "x86",
115                   target_arch = "arm",
116                   target_arch = "aarch64",
117                   target_arch = "powerpc",
118                   target_arch = "powerpc64",
119                   target_arch = "s390x")))]
120     fn is_getrandom_available() -> bool {
121         use sync::atomic::{AtomicBool, Ordering};
122         use sync::Once;
123
124         static CHECKER: Once = Once::new();
125         static AVAILABLE: AtomicBool = AtomicBool::new(false);
126
127         CHECKER.call_once(|| {
128             let mut buf: [u8; 0] = [];
129             let result = getrandom(&mut buf);
130             let available = if result == -1 {
131                 let err = io::Error::last_os_error().raw_os_error();
132                 err != Some(libc::ENOSYS)
133             } else {
134                 true
135             };
136             AVAILABLE.store(available, Ordering::Relaxed);
137         });
138
139         AVAILABLE.load(Ordering::Relaxed)
140     }
141
142     #[cfg(not(all(target_os = "linux",
143                   any(target_arch = "x86_64",
144                       target_arch = "x86",
145                       target_arch = "arm",
146                       target_arch = "aarch64",
147                       target_arch = "powerpc",
148                       target_arch = "powerpc64",
149                       target_arch = "s390x"))))]
150     fn is_getrandom_available() -> bool { false }
151
152     pub struct OsRng {
153         inner: OsRngInner,
154     }
155
156     enum OsRngInner {
157         OsGetrandomRng,
158         OsReaderRng(ReaderRng<File>),
159     }
160
161     impl OsRng {
162         /// Create a new `OsRng`.
163         pub fn new() -> io::Result<OsRng> {
164             if is_getrandom_available() {
165                 return Ok(OsRng { inner: OsGetrandomRng });
166             }
167
168             let reader = File::open("/dev/urandom")?;
169             let reader_rng = ReaderRng::new(reader);
170
171             Ok(OsRng { inner: OsReaderRng(reader_rng) })
172         }
173     }
174
175     impl Rng for OsRng {
176         fn next_u32(&mut self) -> u32 {
177             match self.inner {
178                 OsGetrandomRng => next_u32(&mut getrandom_fill_bytes),
179                 OsReaderRng(ref mut rng) => rng.next_u32(),
180             }
181         }
182         fn next_u64(&mut self) -> u64 {
183             match self.inner {
184                 OsGetrandomRng => next_u64(&mut getrandom_fill_bytes),
185                 OsReaderRng(ref mut rng) => rng.next_u64(),
186             }
187         }
188         fn fill_bytes(&mut self, v: &mut [u8]) {
189             match self.inner {
190                 OsGetrandomRng => getrandom_fill_bytes(v),
191                 OsReaderRng(ref mut rng) => rng.fill_bytes(v)
192             }
193         }
194     }
195 }
196
197 #[cfg(target_os = "openbsd")]
198 mod imp {
199     use super::{next_u32, next_u64};
200
201     use io;
202     use libc;
203     use sys::os::errno;
204     use rand::Rng;
205
206     pub struct OsRng {
207         // dummy field to ensure that this struct cannot be constructed outside
208         // of this module
209         _dummy: (),
210     }
211
212     impl OsRng {
213         /// Create a new `OsRng`.
214         pub fn new() -> io::Result<OsRng> {
215             Ok(OsRng { _dummy: () })
216         }
217     }
218
219     impl Rng for OsRng {
220         fn next_u32(&mut self) -> u32 {
221             next_u32(&mut |v| self.fill_bytes(v))
222         }
223         fn next_u64(&mut self) -> u64 {
224             next_u64(&mut |v| self.fill_bytes(v))
225         }
226         fn fill_bytes(&mut self, v: &mut [u8]) {
227             // getentropy(2) permits a maximum buffer size of 256 bytes
228             for s in v.chunks_mut(256) {
229                 let ret = unsafe {
230                     libc::getentropy(s.as_mut_ptr() as *mut libc::c_void, s.len())
231                 };
232                 if ret == -1 {
233                     panic!("unexpected getentropy error: {}", errno());
234                 }
235             }
236         }
237     }
238 }
239
240 #[cfg(target_os = "ios")]
241 mod imp {
242     use super::{next_u32, next_u64};
243
244     use io;
245     use ptr;
246     use rand::Rng;
247     use libc::{c_int, size_t};
248
249     pub struct OsRng {
250         // dummy field to ensure that this struct cannot be constructed outside
251         // of this module
252         _dummy: (),
253     }
254
255     enum SecRandom {}
256
257     #[allow(non_upper_case_globals)]
258     const kSecRandomDefault: *const SecRandom = ptr::null();
259
260     extern {
261         fn SecRandomCopyBytes(rnd: *const SecRandom,
262                               count: size_t, bytes: *mut u8) -> c_int;
263     }
264
265     impl OsRng {
266         /// Create a new `OsRng`.
267         pub fn new() -> io::Result<OsRng> {
268             Ok(OsRng { _dummy: () })
269         }
270     }
271
272     impl Rng for OsRng {
273         fn next_u32(&mut self) -> u32 {
274             next_u32(&mut |v| self.fill_bytes(v))
275         }
276         fn next_u64(&mut self) -> u64 {
277             next_u64(&mut |v| self.fill_bytes(v))
278         }
279         fn fill_bytes(&mut self, v: &mut [u8]) {
280             let ret = unsafe {
281                 SecRandomCopyBytes(kSecRandomDefault, v.len(),
282                                    v.as_mut_ptr())
283             };
284             if ret == -1 {
285                 panic!("couldn't generate random bytes: {}",
286                        io::Error::last_os_error());
287             }
288         }
289     }
290 }
291
292 #[cfg(target_os = "freebsd")]
293 mod imp {
294     use super::{next_u32, next_u64};
295
296     use io;
297     use libc;
298     use rand::Rng;
299     use ptr;
300
301     pub struct OsRng {
302         // dummy field to ensure that this struct cannot be constructed outside
303         // of this module
304         _dummy: (),
305     }
306
307     impl OsRng {
308         /// Create a new `OsRng`.
309         pub fn new() -> io::Result<OsRng> {
310             Ok(OsRng { _dummy: () })
311         }
312     }
313
314     impl Rng for OsRng {
315         fn next_u32(&mut self) -> u32 {
316             next_u32(&mut |v| self.fill_bytes(v))
317         }
318         fn next_u64(&mut self) -> u64 {
319             next_u64(&mut |v| self.fill_bytes(v))
320         }
321         fn fill_bytes(&mut self, v: &mut [u8]) {
322             let mib = [libc::CTL_KERN, libc::KERN_ARND];
323             // kern.arandom permits a maximum buffer size of 256 bytes
324             for s in v.chunks_mut(256) {
325                 let mut s_len = s.len();
326                 let ret = unsafe {
327                     libc::sysctl(mib.as_ptr(), mib.len() as libc::c_uint,
328                                  s.as_mut_ptr() as *mut _, &mut s_len,
329                                  ptr::null(), 0)
330                 };
331                 if ret == -1 || s_len != s.len() {
332                     panic!("kern.arandom sysctl failed! (returned {}, s.len() {}, oldlenp {})",
333                            ret, s.len(), s_len);
334                 }
335             }
336         }
337     }
338 }
339
340 #[cfg(target_os = "fuchsia")]
341 mod imp {
342     use super::{next_u32, next_u64};
343
344     use io;
345     use rand::Rng;
346
347     #[link(name = "magenta")]
348     extern {
349         fn mx_cprng_draw(buffer: *mut u8, len: usize, actual: *mut usize) -> i32;
350     }
351
352     fn getrandom(buf: &mut [u8]) -> Result<usize, i32> {
353         unsafe {
354             let mut actual = 0;
355             let status = mx_cprng_draw(buf.as_mut_ptr(), buf.len(), &mut actual);
356             if status == 0 {
357                 Ok(actual)
358             } else {
359                 Err(status)
360             }
361         }
362     }
363
364     pub struct OsRng {
365         // dummy field to ensure that this struct cannot be constructed outside
366         // of this module
367         _dummy: (),
368     }
369
370     impl OsRng {
371         /// Create a new `OsRng`.
372         pub fn new() -> io::Result<OsRng> {
373             Ok(OsRng { _dummy: () })
374         }
375     }
376
377     impl Rng for OsRng {
378         fn next_u32(&mut self) -> u32 {
379             next_u32(&mut |v| self.fill_bytes(v))
380         }
381         fn next_u64(&mut self) -> u64 {
382             next_u64(&mut |v| self.fill_bytes(v))
383         }
384         fn fill_bytes(&mut self, v: &mut [u8]) {
385             let mut buf = v;
386             while !buf.is_empty() {
387                 let ret = getrandom(buf);
388                 match ret {
389                     Err(err) => {
390                         panic!("kernel mx_cprng_draw call failed! (returned {}, buf.len() {})",
391                             err, buf.len())
392                     }
393                     Ok(actual) => {
394                         let move_buf = buf;
395                         buf = &mut move_buf[(actual as usize)..];
396                     }
397                 }
398             }
399         }
400     }
401 }