]> git.proxmox.com Git - rustc.git/blob - src/libstd/rand/os.rs
Imported Upstream version 1.6.0+dfsg1
[rustc.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")))]
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(any(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 let len = v.len();
64 while read < len {
65 let result = getrandom(&mut v[read..]);
66 if result == -1 {
67 let err = errno() as libc::c_int;
68 if err == libc::EINTR {
69 continue;
70 } else {
71 panic!("unexpected getrandom error: {}", err);
72 }
73 } else {
74 read += result as usize;
75 }
76 }
77 }
78
79 fn getrandom_next_u32() -> u32 {
80 let mut buf: [u8; 4] = [0; 4];
81 getrandom_fill_bytes(&mut buf);
82 unsafe { mem::transmute::<[u8; 4], u32>(buf) }
83 }
84
85 fn getrandom_next_u64() -> u64 {
86 let mut buf: [u8; 8] = [0; 8];
87 getrandom_fill_bytes(&mut buf);
88 unsafe { mem::transmute::<[u8; 8], u64>(buf) }
89 }
90
91 #[cfg(all(target_os = "linux",
92 any(target_arch = "x86_64",
93 target_arch = "x86",
94 target_arch = "arm",
95 target_arch = "aarch64",
96 target_arch = "powerpc")))]
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 fn is_getrandom_available() -> bool { false }
126
127 /// A random number generator that retrieves randomness straight from
128 /// the operating system. Platform sources:
129 ///
130 /// - Unix-like systems (Linux, Android, Mac OSX): read directly from
131 /// `/dev/urandom`, or from `getrandom(2)` system call if available.
132 /// - Windows: calls `CryptGenRandom`, using the default cryptographic
133 /// service provider with the `PROV_RSA_FULL` type.
134 /// - iOS: calls SecRandomCopyBytes as /dev/(u)random is sandboxed.
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 = "ios")]
183 mod imp {
184 #[cfg(stage0)] use prelude::v1::*;
185
186 use io;
187 use mem;
188 use ptr;
189 use rand::Rng;
190 use libc::{c_int, size_t};
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 ///
201 /// This does not block.
202 pub struct OsRng {
203 // dummy field to ensure that this struct cannot be constructed outside
204 // of this module
205 _dummy: (),
206 }
207
208 enum SecRandom {}
209
210 #[allow(non_upper_case_globals)]
211 const kSecRandomDefault: *const SecRandom = ptr::null();
212
213 #[link(name = "Security", kind = "framework")]
214 extern "C" {
215 fn SecRandomCopyBytes(rnd: *const SecRandom,
216 count: size_t, bytes: *mut u8) -> c_int;
217 }
218
219 impl OsRng {
220 /// Create a new `OsRng`.
221 pub fn new() -> io::Result<OsRng> {
222 Ok(OsRng { _dummy: () })
223 }
224 }
225
226 impl Rng for OsRng {
227 fn next_u32(&mut self) -> u32 {
228 let mut v = [0; 4];
229 self.fill_bytes(&mut v);
230 unsafe { mem::transmute(v) }
231 }
232 fn next_u64(&mut self) -> u64 {
233 let mut v = [0; 8];
234 self.fill_bytes(&mut v);
235 unsafe { mem::transmute(v) }
236 }
237 fn fill_bytes(&mut self, v: &mut [u8]) {
238 let ret = unsafe {
239 SecRandomCopyBytes(kSecRandomDefault, v.len() as size_t,
240 v.as_mut_ptr())
241 };
242 if ret == -1 {
243 panic!("couldn't generate random bytes: {}",
244 io::Error::last_os_error());
245 }
246 }
247 }
248 }
249
250 #[cfg(windows)]
251 mod imp {
252 use io;
253 use mem;
254 use rand::Rng;
255 use sys::c;
256
257 /// A random number generator that retrieves randomness straight from
258 /// the operating system. Platform sources:
259 ///
260 /// - Unix-like systems (Linux, Android, Mac OSX): read directly from
261 /// `/dev/urandom`, or from `getrandom(2)` system call if available.
262 /// - Windows: calls `CryptGenRandom`, using the default cryptographic
263 /// service provider with the `PROV_RSA_FULL` type.
264 /// - iOS: calls SecRandomCopyBytes as /dev/(u)random is sandboxed.
265 ///
266 /// This does not block.
267 pub struct OsRng {
268 hcryptprov: c::HCRYPTPROV
269 }
270
271 impl OsRng {
272 /// Create a new `OsRng`.
273 pub fn new() -> io::Result<OsRng> {
274 let mut hcp = 0;
275 let ret = unsafe {
276 c::CryptAcquireContextA(&mut hcp, 0 as c::LPCSTR, 0 as c::LPCSTR,
277 c::PROV_RSA_FULL,
278 c::CRYPT_VERIFYCONTEXT | c::CRYPT_SILENT)
279 };
280
281 if ret == 0 {
282 Err(io::Error::last_os_error())
283 } else {
284 Ok(OsRng { hcryptprov: hcp })
285 }
286 }
287 }
288
289 impl Rng for OsRng {
290 fn next_u32(&mut self) -> u32 {
291 let mut v = [0; 4];
292 self.fill_bytes(&mut v);
293 unsafe { mem::transmute(v) }
294 }
295 fn next_u64(&mut self) -> u64 {
296 let mut v = [0; 8];
297 self.fill_bytes(&mut v);
298 unsafe { mem::transmute(v) }
299 }
300 fn fill_bytes(&mut self, v: &mut [u8]) {
301 let ret = unsafe {
302 c::CryptGenRandom(self.hcryptprov, v.len() as c::DWORD,
303 v.as_mut_ptr())
304 };
305 if ret == 0 {
306 panic!("couldn't generate random bytes: {}",
307 io::Error::last_os_error());
308 }
309 }
310 }
311
312 impl Drop for OsRng {
313 fn drop(&mut self) {
314 let ret = unsafe {
315 c::CryptReleaseContext(self.hcryptprov, 0)
316 };
317 if ret == 0 {
318 panic!("couldn't release context: {}",
319 io::Error::last_os_error());
320 }
321 }
322 }
323 }
324
325 #[cfg(test)]
326 mod tests {
327 use prelude::v1::*;
328
329 use sync::mpsc::channel;
330 use rand::Rng;
331 use super::OsRng;
332 use thread;
333
334 #[test]
335 fn test_os_rng() {
336 let mut r = OsRng::new().unwrap();
337
338 r.next_u32();
339 r.next_u64();
340
341 let mut v = [0; 1000];
342 r.fill_bytes(&mut v);
343 }
344
345 #[test]
346 fn test_os_rng_tasks() {
347
348 let mut txs = vec!();
349 for _ in 0..20 {
350 let (tx, rx) = channel();
351 txs.push(tx);
352
353 thread::spawn(move|| {
354 // wait until all the threads are ready to go.
355 rx.recv().unwrap();
356
357 // deschedule to attempt to interleave things as much
358 // as possible (XXX: is this a good test?)
359 let mut r = OsRng::new().unwrap();
360 thread::yield_now();
361 let mut v = [0; 1000];
362
363 for _ in 0..100 {
364 r.next_u32();
365 thread::yield_now();
366 r.next_u64();
367 thread::yield_now();
368 r.fill_bytes(&mut v);
369 thread::yield_now();
370 }
371 });
372 }
373
374 // start all the threads
375 for tx in &txs {
376 tx.send(()).unwrap();
377 }
378 }
379 }