]> git.proxmox.com Git - rustc.git/blob - src/libstd/rand/os.rs
Imported Upstream version 1.0.0~beta.3
[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 prelude::v1::*;
19 use self::OsRngInner::*;
20
21 use fs::File;
22 use io;
23 use libc;
24 use mem;
25 use rand::Rng;
26 use rand::reader::ReaderRng;
27 use sys::os::errno;
28
29 #[cfg(all(target_os = "linux",
30 any(target_arch = "x86_64",
31 target_arch = "x86",
32 target_arch = "arm",
33 target_arch = "aarch64",
34 target_arch = "powerpc")))]
35 fn getrandom(buf: &mut [u8]) -> libc::c_long {
36 extern "C" {
37 fn syscall(number: libc::c_long, ...) -> libc::c_long;
38 }
39
40 #[cfg(target_arch = "x86_64")]
41 const NR_GETRANDOM: libc::c_long = 318;
42 #[cfg(target_arch = "x86")]
43 const NR_GETRANDOM: libc::c_long = 355;
44 #[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
45 const NR_GETRANDOM: libc::c_long = 384;
46 #[cfg(target_arch = "powerpc")]
47 const NR_GETRANDOM: libc::c_long = 384;
48
49 unsafe {
50 syscall(NR_GETRANDOM, buf.as_mut_ptr(), buf.len(), 0)
51 }
52 }
53
54 #[cfg(not(all(target_os = "linux",
55 any(target_arch = "x86_64",
56 target_arch = "x86",
57 target_arch = "arm",
58 target_arch = "aarch64",
59 target_arch = "powerpc"))))]
60 fn getrandom(_buf: &mut [u8]) -> libc::c_long { -1 }
61
62 fn getrandom_fill_bytes(v: &mut [u8]) {
63 let mut read = 0;
64 let len = v.len();
65 while read < len {
66 let result = getrandom(&mut v[read..]);
67 if result == -1 {
68 let err = errno() as libc::c_int;
69 if err == libc::EINTR {
70 continue;
71 } else {
72 panic!("unexpected getrandom error: {}", err);
73 }
74 } else {
75 read += result as usize;
76 }
77 }
78 }
79
80 fn getrandom_next_u32() -> u32 {
81 let mut buf: [u8; 4] = [0; 4];
82 getrandom_fill_bytes(&mut buf);
83 unsafe { mem::transmute::<[u8; 4], u32>(buf) }
84 }
85
86 fn getrandom_next_u64() -> u64 {
87 let mut buf: [u8; 8] = [0; 8];
88 getrandom_fill_bytes(&mut buf);
89 unsafe { mem::transmute::<[u8; 8], u64>(buf) }
90 }
91
92 #[cfg(all(target_os = "linux",
93 any(target_arch = "x86_64",
94 target_arch = "x86",
95 target_arch = "arm",
96 target_arch = "aarch64",
97 target_arch = "powerpc")))]
98 fn is_getrandom_available() -> bool {
99 use sync::atomic::{AtomicBool, ATOMIC_BOOL_INIT, Ordering};
100
101 static GETRANDOM_CHECKED: AtomicBool = ATOMIC_BOOL_INIT;
102 static GETRANDOM_AVAILABLE: AtomicBool = ATOMIC_BOOL_INIT;
103
104 if !GETRANDOM_CHECKED.load(Ordering::Relaxed) {
105 let mut buf: [u8; 0] = [];
106 let result = getrandom(&mut buf);
107 let available = if result == -1 {
108 let err = errno() as libc::c_int;
109 err != libc::ENOSYS
110 } else {
111 true
112 };
113 GETRANDOM_AVAILABLE.store(available, Ordering::Relaxed);
114 GETRANDOM_CHECKED.store(true, Ordering::Relaxed);
115 available
116 } else {
117 GETRANDOM_AVAILABLE.load(Ordering::Relaxed)
118 }
119 }
120
121 #[cfg(not(all(target_os = "linux",
122 any(target_arch = "x86_64",
123 target_arch = "x86",
124 target_arch = "arm",
125 target_arch = "aarch64",
126 target_arch = "powerpc"))))]
127 fn is_getrandom_available() -> bool { false }
128
129 /// A random number generator that retrieves randomness straight from
130 /// the operating system. Platform sources:
131 ///
132 /// - Unix-like systems (Linux, Android, Mac OSX): read directly from
133 /// `/dev/urandom`, or from `getrandom(2)` system call if available.
134 /// - Windows: calls `CryptGenRandom`, using the default cryptographic
135 /// service provider with the `PROV_RSA_FULL` type.
136 /// - iOS: calls SecRandomCopyBytes as /dev/(u)random is sandboxed.
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 = "ios")]
185 mod imp {
186 use prelude::v1::*;
187
188 use io;
189 use mem;
190 use rand::Rng;
191 use libc::{c_int, size_t};
192
193 /// A random number generator that retrieves randomness straight from
194 /// the operating system. Platform sources:
195 ///
196 /// - Unix-like systems (Linux, Android, Mac OSX): read directly from
197 /// `/dev/urandom`, or from `getrandom(2)` system call if available.
198 /// - Windows: calls `CryptGenRandom`, using the default cryptographic
199 /// service provider with the `PROV_RSA_FULL` type.
200 /// - iOS: calls SecRandomCopyBytes as /dev/(u)random is sandboxed.
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 #[repr(C)]
210 struct SecRandom;
211
212 #[allow(non_upper_case_globals)]
213 const kSecRandomDefault: *const SecRandom = 0 as *const SecRandom;
214
215 #[link(name = "Security", kind = "framework")]
216 extern "C" {
217 fn SecRandomCopyBytes(rnd: *const SecRandom,
218 count: size_t, bytes: *mut u8) -> c_int;
219 }
220
221 impl OsRng {
222 /// Create a new `OsRng`.
223 pub fn new() -> io::Result<OsRng> {
224 Ok(OsRng { _dummy: () })
225 }
226 }
227
228 impl Rng for OsRng {
229 fn next_u32(&mut self) -> u32 {
230 let mut v = [0; 4];
231 self.fill_bytes(&mut v);
232 unsafe { mem::transmute(v) }
233 }
234 fn next_u64(&mut self) -> u64 {
235 let mut v = [0; 8];
236 self.fill_bytes(&mut v);
237 unsafe { mem::transmute(v) }
238 }
239 fn fill_bytes(&mut self, v: &mut [u8]) {
240 let ret = unsafe {
241 SecRandomCopyBytes(kSecRandomDefault, v.len() as size_t,
242 v.as_mut_ptr())
243 };
244 if ret == -1 {
245 panic!("couldn't generate random bytes: {}",
246 io::Error::last_os_error());
247 }
248 }
249 }
250 }
251
252 #[cfg(windows)]
253 mod imp {
254 use prelude::v1::*;
255
256 use io;
257 use mem;
258 use rand::Rng;
259 use libc::types::os::arch::extra::{LONG_PTR};
260 use libc::{DWORD, BYTE, LPCSTR, BOOL};
261
262 type HCRYPTPROV = LONG_PTR;
263
264 /// A random number generator that retrieves randomness straight from
265 /// the operating system. Platform sources:
266 ///
267 /// - Unix-like systems (Linux, Android, Mac OSX): read directly from
268 /// `/dev/urandom`, or from `getrandom(2)` system call if available.
269 /// - Windows: calls `CryptGenRandom`, using the default cryptographic
270 /// service provider with the `PROV_RSA_FULL` type.
271 /// - iOS: calls SecRandomCopyBytes as /dev/(u)random is sandboxed.
272 ///
273 /// This does not block.
274 pub struct OsRng {
275 hcryptprov: HCRYPTPROV
276 }
277
278 const PROV_RSA_FULL: DWORD = 1;
279 const CRYPT_SILENT: DWORD = 64;
280 const CRYPT_VERIFYCONTEXT: DWORD = 0xF0000000;
281
282 #[allow(non_snake_case)]
283 extern "system" {
284 fn CryptAcquireContextA(phProv: *mut HCRYPTPROV,
285 pszContainer: LPCSTR,
286 pszProvider: LPCSTR,
287 dwProvType: DWORD,
288 dwFlags: DWORD) -> BOOL;
289 fn CryptGenRandom(hProv: HCRYPTPROV,
290 dwLen: DWORD,
291 pbBuffer: *mut BYTE) -> BOOL;
292 fn CryptReleaseContext(hProv: HCRYPTPROV, dwFlags: DWORD) -> BOOL;
293 }
294
295 impl OsRng {
296 /// Create a new `OsRng`.
297 pub fn new() -> io::Result<OsRng> {
298 let mut hcp = 0;
299 let ret = unsafe {
300 CryptAcquireContextA(&mut hcp, 0 as LPCSTR, 0 as LPCSTR,
301 PROV_RSA_FULL,
302 CRYPT_VERIFYCONTEXT | CRYPT_SILENT)
303 };
304
305 if ret == 0 {
306 Err(io::Error::last_os_error())
307 } else {
308 Ok(OsRng { hcryptprov: hcp })
309 }
310 }
311 }
312
313 impl Rng for OsRng {
314 fn next_u32(&mut self) -> u32 {
315 let mut v = [0; 4];
316 self.fill_bytes(&mut v);
317 unsafe { mem::transmute(v) }
318 }
319 fn next_u64(&mut self) -> u64 {
320 let mut v = [0; 8];
321 self.fill_bytes(&mut v);
322 unsafe { mem::transmute(v) }
323 }
324 fn fill_bytes(&mut self, v: &mut [u8]) {
325 let ret = unsafe {
326 CryptGenRandom(self.hcryptprov, v.len() as DWORD,
327 v.as_mut_ptr())
328 };
329 if ret == 0 {
330 panic!("couldn't generate random bytes: {}",
331 io::Error::last_os_error());
332 }
333 }
334 }
335
336 impl Drop for OsRng {
337 fn drop(&mut self) {
338 let ret = unsafe {
339 CryptReleaseContext(self.hcryptprov, 0)
340 };
341 if ret == 0 {
342 panic!("couldn't release context: {}",
343 io::Error::last_os_error());
344 }
345 }
346 }
347 }
348
349 #[cfg(test)]
350 mod test {
351 use prelude::v1::*;
352
353 use sync::mpsc::channel;
354 use rand::Rng;
355 use super::OsRng;
356 use thread;
357
358 #[test]
359 fn test_os_rng() {
360 let mut r = OsRng::new().unwrap();
361
362 r.next_u32();
363 r.next_u64();
364
365 let mut v = [0; 1000];
366 r.fill_bytes(&mut v);
367 }
368
369 #[test]
370 fn test_os_rng_tasks() {
371
372 let mut txs = vec!();
373 for _ in 0..20 {
374 let (tx, rx) = channel();
375 txs.push(tx);
376
377 thread::spawn(move|| {
378 // wait until all the tasks are ready to go.
379 rx.recv().unwrap();
380
381 // deschedule to attempt to interleave things as much
382 // as possible (XXX: is this a good test?)
383 let mut r = OsRng::new().unwrap();
384 thread::yield_now();
385 let mut v = [0; 1000];
386
387 for _ in 0..100 {
388 r.next_u32();
389 thread::yield_now();
390 r.next_u64();
391 thread::yield_now();
392 r.fill_bytes(&mut v);
393 thread::yield_now();
394 }
395 });
396 }
397
398 // start all the tasks
399 for tx in &txs {
400 tx.send(()).unwrap();
401 }
402 }
403 }