|
| 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 | +//! Interface to the operating system provided random number |
| 12 | +//! generators: |
| 13 | +//! |
| 14 | +//! - Unix-like systems (Linux, Android, Mac OSX): read directly from |
| 15 | +//! `/dev/urandom`, or from `getrandom(2)` system call if available. |
| 16 | +//! - Windows: calls `CryptGenRandom`, using the default cryptographic |
| 17 | +//! service provider with the `PROV_RSA_FULL` type. |
| 18 | +//! - iOS: calls SecRandomCopyBytes as /dev/(u)random is sandboxed. |
| 19 | +//! - OpenBSD: uses the `getentropy(2)` system call. |
| 20 | +
|
| 21 | + |
| 22 | +pub use self::imp::fill_bytes; |
| 23 | + |
| 24 | +#[cfg(all(unix, not(target_os = "ios"), not(target_os = "openbsd")))] |
| 25 | +mod imp { |
| 26 | + use fs::File; |
| 27 | + use io::{self, Read}; |
| 28 | + use libc; |
| 29 | + use sys::os::errno; |
| 30 | + |
| 31 | + #[cfg(all(target_os = "linux", |
| 32 | + any(target_arch = "x86_64", |
| 33 | + target_arch = "x86", |
| 34 | + target_arch = "arm", |
| 35 | + target_arch = "aarch64", |
| 36 | + target_arch = "powerpc", |
| 37 | + target_arch = "powerpc64", |
| 38 | + target_arch = "powerpc64le")))] |
| 39 | + fn getrandom(buf: &mut [u8]) -> libc::c_long { |
| 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(target_arch = "arm")] |
| 45 | + const NR_GETRANDOM: libc::c_long = 384; |
| 46 | + #[cfg(any(target_arch = "powerpc", target_arch = "powerpc64", |
| 47 | + target_arch = "powerpc64le"))] |
| 48 | + const NR_GETRANDOM: libc::c_long = 359; |
| 49 | + #[cfg(target_arch = "aarch64")] |
| 50 | + const NR_GETRANDOM: libc::c_long = 278; |
| 51 | + |
| 52 | + unsafe { |
| 53 | + libc::syscall(NR_GETRANDOM, buf.as_mut_ptr(), buf.len(), 0) |
| 54 | + } |
| 55 | + } |
| 56 | + |
| 57 | + #[cfg(not(all(target_os = "linux", |
| 58 | + any(target_arch = "x86_64", |
| 59 | + target_arch = "x86", |
| 60 | + target_arch = "arm", |
| 61 | + target_arch = "aarch64", |
| 62 | + target_arch = "powerpc", |
| 63 | + target_arch = "powerpc64", |
| 64 | + target_arch = "powerpc64le"))))] |
| 65 | + fn getrandom(_buf: &mut [u8]) -> libc::c_long { -1 } |
| 66 | + |
| 67 | + fn getrandom_fill_bytes(v: &mut [u8]) { |
| 68 | + let mut read = 0; |
| 69 | + while read < v.len() { |
| 70 | + let result = getrandom(&mut v[read..]); |
| 71 | + if result == -1 { |
| 72 | + let err = errno() as libc::c_int; |
| 73 | + if err == libc::EINTR { |
| 74 | + continue; |
| 75 | + } else { |
| 76 | + panic!("unexpected getrandom error: {}", err); |
| 77 | + } |
| 78 | + } else { |
| 79 | + read += result as usize; |
| 80 | + } |
| 81 | + } |
| 82 | + } |
| 83 | + |
| 84 | + #[cfg(all(target_os = "linux", |
| 85 | + any(target_arch = "x86_64", |
| 86 | + target_arch = "x86", |
| 87 | + target_arch = "arm", |
| 88 | + target_arch = "aarch64", |
| 89 | + target_arch = "powerpc", |
| 90 | + target_arch = "powerpc64", |
| 91 | + target_arch = "powerpc64le")))] |
| 92 | + fn is_getrandom_available() -> bool { |
| 93 | + use sync::atomic::{AtomicBool, Ordering}; |
| 94 | + use sync::Once; |
| 95 | + |
| 96 | + static CHECKER: Once = Once::new(); |
| 97 | + static AVAILABLE: AtomicBool = AtomicBool::new(false); |
| 98 | + |
| 99 | + CHECKER.call_once(|| { |
| 100 | + let mut buf: [u8; 0] = []; |
| 101 | + let result = getrandom(&mut buf); |
| 102 | + let available = if result == -1 { |
| 103 | + let err = io::Error::last_os_error().raw_os_error(); |
| 104 | + err != Some(libc::ENOSYS) |
| 105 | + } else { |
| 106 | + true |
| 107 | + }; |
| 108 | + AVAILABLE.store(available, Ordering::Relaxed); |
| 109 | + }); |
| 110 | + |
| 111 | + AVAILABLE.load(Ordering::Relaxed) |
| 112 | + } |
| 113 | + |
| 114 | + #[cfg(not(all(target_os = "linux", |
| 115 | + any(target_arch = "x86_64", |
| 116 | + target_arch = "x86", |
| 117 | + target_arch = "arm", |
| 118 | + target_arch = "aarch64", |
| 119 | + target_arch = "powerpc", |
| 120 | + target_arch = "powerpc64", |
| 121 | + target_arch = "powerpc64le"))))] |
| 122 | + fn is_getrandom_available() -> bool { false } |
| 123 | + |
| 124 | + pub fn fill_bytes(b: &mut [u8]) { |
| 125 | + if is_getrandom_available() { |
| 126 | + getrandom_fill_bytes(b) |
| 127 | + } else { |
| 128 | + let mut reader = File::open("/dev/urandom").expect("failed to open /dev/urandom"); |
| 129 | + reader.read_exact(b).expect("failed to read bytes from /dev/urandom"); |
| 130 | + } |
| 131 | + } |
| 132 | +} |
| 133 | + |
| 134 | +#[cfg(target_os = "openbsd")] |
| 135 | +mod imp { |
| 136 | + use libc; |
| 137 | + use sys::os::errno; |
| 138 | + |
| 139 | + pub fn fill_bytes(v: &mut [u8]) { |
| 140 | + // getentropy(2) permits a maximum buffer size of 256 bytes |
| 141 | + for s in v.chunks_mut(256) { |
| 142 | + let ret = unsafe { |
| 143 | + libc::getentropy(s.as_mut_ptr() as *mut libc::c_void, s.len()) |
| 144 | + }; |
| 145 | + if ret == -1 { |
| 146 | + panic!("unexpected getentropy error: {}", errno()); |
| 147 | + } |
| 148 | + } |
| 149 | + } |
| 150 | +} |
| 151 | + |
| 152 | +#[cfg(target_os = "ios")] |
| 153 | +mod imp { |
| 154 | + use io; |
| 155 | + use ptr; |
| 156 | + use libc::{c_int, size_t}; |
| 157 | + |
| 158 | + enum SecRandom {} |
| 159 | + |
| 160 | + #[allow(non_upper_case_globals)] |
| 161 | + const kSecRandomDefault: *const SecRandom = ptr::null(); |
| 162 | + |
| 163 | + #[link(name = "Security", kind = "framework")] |
| 164 | + extern "C" { |
| 165 | + fn SecRandomCopyBytes(rnd: *const SecRandom, |
| 166 | + count: size_t, bytes: *mut u8) -> c_int; |
| 167 | + } |
| 168 | + |
| 169 | + pub fn fill_bytes(v: &mut [u8]) { |
| 170 | + let ret = unsafe { |
| 171 | + SecRandomCopyBytes(kSecRandomDefault, v.len() as size_t, |
| 172 | + v.as_mut_ptr()) |
| 173 | + }; |
| 174 | + if ret == -1 { |
| 175 | + panic!("couldn't generate random bytes: {}", |
| 176 | + io::Error::last_os_error()); |
| 177 | + } |
| 178 | + } |
| 179 | +} |
| 180 | + |
| 181 | +#[cfg(windows)] |
| 182 | +mod imp { |
| 183 | + use io; |
| 184 | + use sys::c; |
| 185 | + |
| 186 | + // SBRM struct to ensure the cryptography context gets cleaned up |
| 187 | + // properly |
| 188 | + struct OsRng { |
| 189 | + hcryptprov: c::HCRYPTPROV |
| 190 | + } |
| 191 | + |
| 192 | + impl OsRng { |
| 193 | + /// Create a new `OsRng`. |
| 194 | + pub fn new() -> io::Result<OsRng> { |
| 195 | + let mut hcp = 0; |
| 196 | + let ret = unsafe { |
| 197 | + c::CryptAcquireContextA(&mut hcp, 0 as c::LPCSTR, 0 as c::LPCSTR, |
| 198 | + c::PROV_RSA_FULL, |
| 199 | + c::CRYPT_VERIFYCONTEXT | c::CRYPT_SILENT) |
| 200 | + }; |
| 201 | + |
| 202 | + if ret == 0 { |
| 203 | + Err(io::Error::last_os_error()) |
| 204 | + } else { |
| 205 | + Ok(OsRng { hcryptprov: hcp }) |
| 206 | + } |
| 207 | + } |
| 208 | + } |
| 209 | + impl Drop for OsRng { |
| 210 | + fn drop(&mut self) { |
| 211 | + let ret = unsafe { |
| 212 | + c::CryptReleaseContext(self.hcryptprov, 0) |
| 213 | + }; |
| 214 | + if ret == 0 { |
| 215 | + panic!("couldn't release context: {}", |
| 216 | + io::Error::last_os_error()); |
| 217 | + } |
| 218 | + } |
| 219 | + } |
| 220 | + |
| 221 | + pub fn fill_bytes(v: &mut [u8]) { |
| 222 | + let os = OsRng::new().expect("failed to acquire crypt context for randomness"); |
| 223 | + let ret = unsafe { |
| 224 | + c::CryptGenRandom(self.hcryptprov, v.len() as c::DWORD, |
| 225 | + v.as_mut_ptr()) |
| 226 | + }; |
| 227 | + if ret == 0 { |
| 228 | + panic!("couldn't generate random bytes: {}", |
| 229 | + io::Error::last_os_error()); |
| 230 | + } |
| 231 | + } |
| 232 | + |
| 233 | +} |
| 234 | + |
| 235 | +#[cfg(test)] |
| 236 | +mod tests { |
| 237 | + use sync::mpsc::channel; |
| 238 | + use super::fill_bytes; |
| 239 | + use thread; |
| 240 | + |
| 241 | + #[test] |
| 242 | + fn test_fill_bytes() { |
| 243 | + let mut v = [0; 1000]; |
| 244 | + fill_bytes(&mut v); |
| 245 | + } |
| 246 | + |
| 247 | + #[test] |
| 248 | + fn test_fill_bytes_tasks() { |
| 249 | + |
| 250 | + let mut txs = vec!(); |
| 251 | + for _ in 0..20 { |
| 252 | + let (tx, rx) = channel(); |
| 253 | + txs.push(tx); |
| 254 | + |
| 255 | + thread::spawn(move|| { |
| 256 | + // wait until all the threads are ready to go. |
| 257 | + rx.recv().unwrap(); |
| 258 | + |
| 259 | + let mut v = [0; 1000]; |
| 260 | + |
| 261 | + for _ in 0..100 { |
| 262 | + fill_bytes(&mut v); |
| 263 | + // deschedule to attempt to interleave things as much |
| 264 | + // as possible (XXX: is this a good test?) |
| 265 | + thread::yield_now(); |
| 266 | + } |
| 267 | + }); |
| 268 | + } |
| 269 | + |
| 270 | + // start all the threads |
| 271 | + for tx in &txs { |
| 272 | + tx.send(()).unwrap(); |
| 273 | + } |
| 274 | + } |
| 275 | +} |
0 commit comments