]> git.proxmox.com Git - rustc.git/blob - src/libstd/rand/os.rs
Imported Upstream version 1.1.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 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 use sync::{Once, ONCE_INIT};
101
102 static CHECKER: Once = ONCE_INIT;
103 static AVAILABLE: AtomicBool = ATOMIC_BOOL_INIT;
104
105 CHECKER.call_once(|| {
106 let mut buf: [u8; 0] = [];
107 let result = getrandom(&mut buf);
108 let available = if result == -1 {
109 let err = io::Error::last_os_error().raw_os_error();
110 err != Some(libc::ENOSYS)
111 } else {
112 true
113 };
114 AVAILABLE.store(available, Ordering::Relaxed);
115 });
116
117 AVAILABLE.load(Ordering::Relaxed)
118 }
119
120 #[cfg(not(all(target_os = "linux",
121 any(target_arch = "x86_64",
122 target_arch = "x86",
123 target_arch = "arm",
124 target_arch = "aarch64",
125 target_arch = "powerpc"))))]
126 fn is_getrandom_available() -> bool { false }
127
128 /// A random number generator that retrieves randomness straight from
129 /// the operating system. Platform sources:
130 ///
131 /// - Unix-like systems (Linux, Android, Mac OSX): read directly from
132 /// `/dev/urandom`, or from `getrandom(2)` system call if available.
133 /// - Windows: calls `CryptGenRandom`, using the default cryptographic
134 /// service provider with the `PROV_RSA_FULL` type.
135 /// - iOS: calls SecRandomCopyBytes as /dev/(u)random is sandboxed.
136 ///
137 /// This does not block.
138 pub struct OsRng {
139 inner: OsRngInner,
140 }
141
142 enum OsRngInner {
143 OsGetrandomRng,
144 OsReaderRng(ReaderRng<File>),
145 }
146
147 impl OsRng {
148 /// Create a new `OsRng`.
149 pub fn new() -> io::Result<OsRng> {
150 if is_getrandom_available() {
151 return Ok(OsRng { inner: OsGetrandomRng });
152 }
153
154 let reader = try!(File::open("/dev/urandom"));
155 let reader_rng = ReaderRng::new(reader);
156
157 Ok(OsRng { inner: OsReaderRng(reader_rng) })
158 }
159 }
160
161 impl Rng for OsRng {
162 fn next_u32(&mut self) -> u32 {
163 match self.inner {
164 OsGetrandomRng => getrandom_next_u32(),
165 OsReaderRng(ref mut rng) => rng.next_u32(),
166 }
167 }
168 fn next_u64(&mut self) -> u64 {
169 match self.inner {
170 OsGetrandomRng => getrandom_next_u64(),
171 OsReaderRng(ref mut rng) => rng.next_u64(),
172 }
173 }
174 fn fill_bytes(&mut self, v: &mut [u8]) {
175 match self.inner {
176 OsGetrandomRng => getrandom_fill_bytes(v),
177 OsReaderRng(ref mut rng) => rng.fill_bytes(v)
178 }
179 }
180 }
181 }
182
183 #[cfg(target_os = "ios")]
184 mod imp {
185 use prelude::v1::*;
186
187 use io;
188 use mem;
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 #[repr(C)]
209 struct SecRandom;
210
211 #[allow(non_upper_case_globals)]
212 const kSecRandomDefault: *const SecRandom = 0 as *const SecRandom;
213
214 #[link(name = "Security", kind = "framework")]
215 extern "C" {
216 fn SecRandomCopyBytes(rnd: *const SecRandom,
217 count: size_t, bytes: *mut u8) -> c_int;
218 }
219
220 impl OsRng {
221 /// Create a new `OsRng`.
222 pub fn new() -> io::Result<OsRng> {
223 Ok(OsRng { _dummy: () })
224 }
225 }
226
227 impl Rng for OsRng {
228 fn next_u32(&mut self) -> u32 {
229 let mut v = [0; 4];
230 self.fill_bytes(&mut v);
231 unsafe { mem::transmute(v) }
232 }
233 fn next_u64(&mut self) -> u64 {
234 let mut v = [0; 8];
235 self.fill_bytes(&mut v);
236 unsafe { mem::transmute(v) }
237 }
238 fn fill_bytes(&mut self, v: &mut [u8]) {
239 let ret = unsafe {
240 SecRandomCopyBytes(kSecRandomDefault, v.len() as size_t,
241 v.as_mut_ptr())
242 };
243 if ret == -1 {
244 panic!("couldn't generate random bytes: {}",
245 io::Error::last_os_error());
246 }
247 }
248 }
249 }
250
251 #[cfg(windows)]
252 mod imp {
253 use prelude::v1::*;
254
255 use io;
256 use mem;
257 use rand::Rng;
258 use libc::types::os::arch::extra::{LONG_PTR};
259 use libc::{DWORD, BYTE, LPCSTR, BOOL};
260
261 type HCRYPTPROV = LONG_PTR;
262
263 /// A random number generator that retrieves randomness straight from
264 /// the operating system. Platform sources:
265 ///
266 /// - Unix-like systems (Linux, Android, Mac OSX): read directly from
267 /// `/dev/urandom`, or from `getrandom(2)` system call if available.
268 /// - Windows: calls `CryptGenRandom`, using the default cryptographic
269 /// service provider with the `PROV_RSA_FULL` type.
270 /// - iOS: calls SecRandomCopyBytes as /dev/(u)random is sandboxed.
271 ///
272 /// This does not block.
273 pub struct OsRng {
274 hcryptprov: HCRYPTPROV
275 }
276
277 const PROV_RSA_FULL: DWORD = 1;
278 const CRYPT_SILENT: DWORD = 64;
279 const CRYPT_VERIFYCONTEXT: DWORD = 0xF0000000;
280
281 #[allow(non_snake_case)]
282 extern "system" {
283 fn CryptAcquireContextA(phProv: *mut HCRYPTPROV,
284 pszContainer: LPCSTR,
285 pszProvider: LPCSTR,
286 dwProvType: DWORD,
287 dwFlags: DWORD) -> BOOL;
288 fn CryptGenRandom(hProv: HCRYPTPROV,
289 dwLen: DWORD,
290 pbBuffer: *mut BYTE) -> BOOL;
291 fn CryptReleaseContext(hProv: HCRYPTPROV, dwFlags: DWORD) -> BOOL;
292 }
293
294 impl OsRng {
295 /// Create a new `OsRng`.
296 pub fn new() -> io::Result<OsRng> {
297 let mut hcp = 0;
298 let ret = unsafe {
299 CryptAcquireContextA(&mut hcp, 0 as LPCSTR, 0 as LPCSTR,
300 PROV_RSA_FULL,
301 CRYPT_VERIFYCONTEXT | CRYPT_SILENT)
302 };
303
304 if ret == 0 {
305 Err(io::Error::last_os_error())
306 } else {
307 Ok(OsRng { hcryptprov: hcp })
308 }
309 }
310 }
311
312 impl Rng for OsRng {
313 fn next_u32(&mut self) -> u32 {
314 let mut v = [0; 4];
315 self.fill_bytes(&mut v);
316 unsafe { mem::transmute(v) }
317 }
318 fn next_u64(&mut self) -> u64 {
319 let mut v = [0; 8];
320 self.fill_bytes(&mut v);
321 unsafe { mem::transmute(v) }
322 }
323 fn fill_bytes(&mut self, v: &mut [u8]) {
324 let ret = unsafe {
325 CryptGenRandom(self.hcryptprov, v.len() as DWORD,
326 v.as_mut_ptr())
327 };
328 if ret == 0 {
329 panic!("couldn't generate random bytes: {}",
330 io::Error::last_os_error());
331 }
332 }
333 }
334
335 impl Drop for OsRng {
336 fn drop(&mut self) {
337 let ret = unsafe {
338 CryptReleaseContext(self.hcryptprov, 0)
339 };
340 if ret == 0 {
341 panic!("couldn't release context: {}",
342 io::Error::last_os_error());
343 }
344 }
345 }
346 }
347
348 #[cfg(test)]
349 mod tests {
350 use prelude::v1::*;
351
352 use sync::mpsc::channel;
353 use rand::Rng;
354 use super::OsRng;
355 use thread;
356
357 #[test]
358 fn test_os_rng() {
359 let mut r = OsRng::new().unwrap();
360
361 r.next_u32();
362 r.next_u64();
363
364 let mut v = [0; 1000];
365 r.fill_bytes(&mut v);
366 }
367
368 #[test]
369 fn test_os_rng_tasks() {
370
371 let mut txs = vec!();
372 for _ in 0..20 {
373 let (tx, rx) = channel();
374 txs.push(tx);
375
376 thread::spawn(move|| {
377 // wait until all the threads are ready to go.
378 rx.recv().unwrap();
379
380 // deschedule to attempt to interleave things as much
381 // as possible (XXX: is this a good test?)
382 let mut r = OsRng::new().unwrap();
383 thread::yield_now();
384 let mut v = [0; 1000];
385
386 for _ in 0..100 {
387 r.next_u32();
388 thread::yield_now();
389 r.next_u64();
390 thread::yield_now();
391 r.fill_bytes(&mut v);
392 thread::yield_now();
393 }
394 });
395 }
396
397 // start all the threads
398 for tx in &txs {
399 tx.send(()).unwrap();
400 }
401 }
402 }