]>
Commit | Line | Data |
---|---|---|
1a4d82fc JJ |
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 { | |
c34b1796 | 18 | use prelude::v1::*; |
1a4d82fc JJ |
19 | use self::OsRngInner::*; |
20 | ||
9346a6ac AL |
21 | use fs::File; |
22 | use io; | |
c34b1796 AL |
23 | use libc; |
24 | use mem; | |
1a4d82fc JJ |
25 | use rand::Rng; |
26 | use rand::reader::ReaderRng; | |
c34b1796 | 27 | use sys::os::errno; |
1a4d82fc JJ |
28 | |
29 | #[cfg(all(target_os = "linux", | |
30 | any(target_arch = "x86_64", | |
31 | target_arch = "x86", | |
32 | target_arch = "arm", | |
85aaf69f SL |
33 | target_arch = "aarch64", |
34 | target_arch = "powerpc")))] | |
1a4d82fc JJ |
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; | |
85aaf69f SL |
46 | #[cfg(target_arch = "powerpc")] |
47 | const NR_GETRANDOM: libc::c_long = 384; | |
1a4d82fc JJ |
48 | |
49 | unsafe { | |
85aaf69f | 50 | syscall(NR_GETRANDOM, buf.as_mut_ptr(), buf.len(), 0) |
1a4d82fc JJ |
51 | } |
52 | } | |
53 | ||
54 | #[cfg(not(all(target_os = "linux", | |
55 | any(target_arch = "x86_64", | |
56 | target_arch = "x86", | |
57 | target_arch = "arm", | |
85aaf69f SL |
58 | target_arch = "aarch64", |
59 | target_arch = "powerpc"))))] | |
1a4d82fc JJ |
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 { | |
85aaf69f | 66 | let result = getrandom(&mut v[read..]); |
1a4d82fc JJ |
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 { | |
85aaf69f | 75 | read += result as usize; |
1a4d82fc JJ |
76 | } |
77 | } | |
78 | } | |
79 | ||
80 | fn getrandom_next_u32() -> u32 { | |
c34b1796 | 81 | let mut buf: [u8; 4] = [0; 4]; |
1a4d82fc JJ |
82 | getrandom_fill_bytes(&mut buf); |
83 | unsafe { mem::transmute::<[u8; 4], u32>(buf) } | |
84 | } | |
85 | ||
86 | fn getrandom_next_u64() -> u64 { | |
c34b1796 | 87 | let mut buf: [u8; 8] = [0; 8]; |
1a4d82fc JJ |
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", | |
85aaf69f SL |
96 | target_arch = "aarch64", |
97 | target_arch = "powerpc")))] | |
1a4d82fc JJ |
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", | |
85aaf69f SL |
125 | target_arch = "aarch64", |
126 | target_arch = "powerpc"))))] | |
1a4d82fc JJ |
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`. | |
9346a6ac | 150 | pub fn new() -> io::Result<OsRng> { |
1a4d82fc JJ |
151 | if is_getrandom_available() { |
152 | return Ok(OsRng { inner: OsGetrandomRng }); | |
153 | } | |
154 | ||
9346a6ac | 155 | let reader = try!(File::open("/dev/urandom")); |
1a4d82fc JJ |
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 { | |
c34b1796 | 186 | use prelude::v1::*; |
1a4d82fc | 187 | |
c34b1796 | 188 | use io; |
1a4d82fc | 189 | use mem; |
1a4d82fc | 190 | use rand::Rng; |
c34b1796 | 191 | use libc::{c_int, size_t}; |
1a4d82fc JJ |
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. | |
1a4d82fc | 203 | pub struct OsRng { |
9346a6ac AL |
204 | // dummy field to ensure that this struct cannot be constructed outside |
205 | // of this module | |
1a4d82fc JJ |
206 | _dummy: (), |
207 | } | |
208 | ||
209 | #[repr(C)] | |
210 | struct SecRandom; | |
211 | ||
1a4d82fc | 212 | #[allow(non_upper_case_globals)] |
c34b1796 | 213 | const kSecRandomDefault: *const SecRandom = 0 as *const SecRandom; |
1a4d82fc JJ |
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`. | |
9346a6ac | 223 | pub fn new() -> io::Result<OsRng> { |
1a4d82fc JJ |
224 | Ok(OsRng { _dummy: () }) |
225 | } | |
226 | } | |
227 | ||
228 | impl Rng for OsRng { | |
229 | fn next_u32(&mut self) -> u32 { | |
c34b1796 | 230 | let mut v = [0; 4]; |
1a4d82fc JJ |
231 | self.fill_bytes(&mut v); |
232 | unsafe { mem::transmute(v) } | |
233 | } | |
234 | fn next_u64(&mut self) -> u64 { | |
c34b1796 | 235 | let mut v = [0; 8]; |
1a4d82fc JJ |
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 { | |
9346a6ac AL |
241 | SecRandomCopyBytes(kSecRandomDefault, v.len() as size_t, |
242 | v.as_mut_ptr()) | |
1a4d82fc JJ |
243 | }; |
244 | if ret == -1 { | |
9346a6ac AL |
245 | panic!("couldn't generate random bytes: {}", |
246 | io::Error::last_os_error()); | |
1a4d82fc JJ |
247 | } |
248 | } | |
249 | } | |
250 | } | |
251 | ||
252 | #[cfg(windows)] | |
253 | mod imp { | |
c34b1796 | 254 | use prelude::v1::*; |
1a4d82fc | 255 | |
c34b1796 | 256 | use io; |
1a4d82fc | 257 | use mem; |
1a4d82fc | 258 | use rand::Rng; |
c34b1796 AL |
259 | use libc::types::os::arch::extra::{LONG_PTR}; |
260 | use libc::{DWORD, BYTE, LPCSTR, BOOL}; | |
1a4d82fc JJ |
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 | ||
c34b1796 AL |
278 | const PROV_RSA_FULL: DWORD = 1; |
279 | const CRYPT_SILENT: DWORD = 64; | |
280 | const CRYPT_VERIFYCONTEXT: DWORD = 0xF0000000; | |
1a4d82fc JJ |
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`. | |
9346a6ac | 297 | pub fn new() -> io::Result<OsRng> { |
1a4d82fc JJ |
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 { | |
9346a6ac | 306 | Err(io::Error::last_os_error()) |
1a4d82fc JJ |
307 | } else { |
308 | Ok(OsRng { hcryptprov: hcp }) | |
309 | } | |
310 | } | |
311 | } | |
312 | ||
313 | impl Rng for OsRng { | |
314 | fn next_u32(&mut self) -> u32 { | |
c34b1796 | 315 | let mut v = [0; 4]; |
1a4d82fc JJ |
316 | self.fill_bytes(&mut v); |
317 | unsafe { mem::transmute(v) } | |
318 | } | |
319 | fn next_u64(&mut self) -> u64 { | |
c34b1796 | 320 | let mut v = [0; 8]; |
1a4d82fc JJ |
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 { | |
c34b1796 AL |
330 | panic!("couldn't generate random bytes: {}", |
331 | io::Error::last_os_error()); | |
1a4d82fc JJ |
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 { | |
c34b1796 AL |
342 | panic!("couldn't release context: {}", |
343 | io::Error::last_os_error()); | |
1a4d82fc JJ |
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; | |
85aaf69f | 356 | use thread; |
1a4d82fc JJ |
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 | ||
c34b1796 | 365 | let mut v = [0; 1000]; |
1a4d82fc JJ |
366 | r.fill_bytes(&mut v); |
367 | } | |
368 | ||
369 | #[test] | |
370 | fn test_os_rng_tasks() { | |
371 | ||
372 | let mut txs = vec!(); | |
85aaf69f | 373 | for _ in 0..20 { |
1a4d82fc JJ |
374 | let (tx, rx) = channel(); |
375 | txs.push(tx); | |
376 | ||
85aaf69f | 377 | thread::spawn(move|| { |
1a4d82fc JJ |
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(); | |
85aaf69f | 384 | thread::yield_now(); |
c34b1796 | 385 | let mut v = [0; 1000]; |
1a4d82fc | 386 | |
85aaf69f | 387 | for _ in 0..100 { |
1a4d82fc | 388 | r.next_u32(); |
85aaf69f | 389 | thread::yield_now(); |
1a4d82fc | 390 | r.next_u64(); |
85aaf69f | 391 | thread::yield_now(); |
1a4d82fc | 392 | r.fill_bytes(&mut v); |
85aaf69f | 393 | thread::yield_now(); |
1a4d82fc JJ |
394 | } |
395 | }); | |
396 | } | |
397 | ||
398 | // start all the tasks | |
85aaf69f | 399 | for tx in &txs { |
1a4d82fc JJ |
400 | tx.send(()).unwrap(); |
401 | } | |
402 | } | |
403 | } |