]> git.proxmox.com Git - rustc.git/blob - src/libstd/sys/unix/rand.rs
Imported Upstream version 1.9.0+dfsg1
[rustc.git] / src / libstd / sys / unix / rand.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 pub use self::imp::OsRng;
12
13 #[cfg(all(unix, not(target_os = "ios"), not(target_os = "openbsd")))]
14 mod imp {
15 use self::OsRngInner::*;
16
17 use fs::File;
18 use io;
19 use libc;
20 use mem;
21 use rand::Rng;
22 use rand::reader::ReaderRng;
23 use sys::os::errno;
24
25 #[cfg(all(target_os = "linux",
26 any(target_arch = "x86_64",
27 target_arch = "x86",
28 target_arch = "arm",
29 target_arch = "aarch64",
30 target_arch = "powerpc",
31 target_arch = "powerpc64")))]
32 fn getrandom(buf: &mut [u8]) -> libc::c_long {
33 #[cfg(target_arch = "x86_64")]
34 const NR_GETRANDOM: libc::c_long = 318;
35 #[cfg(target_arch = "x86")]
36 const NR_GETRANDOM: libc::c_long = 355;
37 #[cfg(target_arch = "arm")]
38 const NR_GETRANDOM: libc::c_long = 384;
39 #[cfg(any(target_arch = "powerpc", target_arch = "powerpc64"))]
40 const NR_GETRANDOM: libc::c_long = 359;
41 #[cfg(target_arch = "aarch64")]
42 const NR_GETRANDOM: libc::c_long = 278;
43
44 unsafe {
45 libc::syscall(NR_GETRANDOM, buf.as_mut_ptr(), buf.len(), 0)
46 }
47 }
48
49 #[cfg(not(all(target_os = "linux",
50 any(target_arch = "x86_64",
51 target_arch = "x86",
52 target_arch = "arm",
53 target_arch = "aarch64",
54 target_arch = "powerpc",
55 target_arch = "powerpc64"))))]
56 fn getrandom(_buf: &mut [u8]) -> libc::c_long { -1 }
57
58 fn getrandom_fill_bytes(v: &mut [u8]) {
59 let mut read = 0;
60 while read < v.len() {
61 let result = getrandom(&mut v[read..]);
62 if result == -1 {
63 let err = errno() as libc::c_int;
64 if err == libc::EINTR {
65 continue;
66 } else {
67 panic!("unexpected getrandom error: {}", err);
68 }
69 } else {
70 read += result as usize;
71 }
72 }
73 }
74
75 fn getrandom_next_u32() -> u32 {
76 let mut buf: [u8; 4] = [0; 4];
77 getrandom_fill_bytes(&mut buf);
78 unsafe { mem::transmute::<[u8; 4], u32>(buf) }
79 }
80
81 fn getrandom_next_u64() -> u64 {
82 let mut buf: [u8; 8] = [0; 8];
83 getrandom_fill_bytes(&mut buf);
84 unsafe { mem::transmute::<[u8; 8], u64>(buf) }
85 }
86
87 #[cfg(all(target_os = "linux",
88 any(target_arch = "x86_64",
89 target_arch = "x86",
90 target_arch = "arm",
91 target_arch = "aarch64",
92 target_arch = "powerpc",
93 target_arch = "powerpc64")))]
94 fn is_getrandom_available() -> bool {
95 use sync::atomic::{AtomicBool, Ordering};
96 use sync::Once;
97
98 static CHECKER: Once = Once::new();
99 static AVAILABLE: AtomicBool = AtomicBool::new(false);
100
101 CHECKER.call_once(|| {
102 let mut buf: [u8; 0] = [];
103 let result = getrandom(&mut buf);
104 let available = if result == -1 {
105 let err = io::Error::last_os_error().raw_os_error();
106 err != Some(libc::ENOSYS)
107 } else {
108 true
109 };
110 AVAILABLE.store(available, Ordering::Relaxed);
111 });
112
113 AVAILABLE.load(Ordering::Relaxed)
114 }
115
116 #[cfg(not(all(target_os = "linux",
117 any(target_arch = "x86_64",
118 target_arch = "x86",
119 target_arch = "arm",
120 target_arch = "aarch64",
121 target_arch = "powerpc",
122 target_arch = "powerpc64"))))]
123 fn is_getrandom_available() -> bool { false }
124
125 pub struct OsRng {
126 inner: OsRngInner,
127 }
128
129 enum OsRngInner {
130 OsGetrandomRng,
131 OsReaderRng(ReaderRng<File>),
132 }
133
134 impl OsRng {
135 /// Create a new `OsRng`.
136 pub fn new() -> io::Result<OsRng> {
137 if is_getrandom_available() {
138 return Ok(OsRng { inner: OsGetrandomRng });
139 }
140
141 let reader = File::open("/dev/urandom")?;
142 let reader_rng = ReaderRng::new(reader);
143
144 Ok(OsRng { inner: OsReaderRng(reader_rng) })
145 }
146 }
147
148 impl Rng for OsRng {
149 fn next_u32(&mut self) -> u32 {
150 match self.inner {
151 OsGetrandomRng => getrandom_next_u32(),
152 OsReaderRng(ref mut rng) => rng.next_u32(),
153 }
154 }
155 fn next_u64(&mut self) -> u64 {
156 match self.inner {
157 OsGetrandomRng => getrandom_next_u64(),
158 OsReaderRng(ref mut rng) => rng.next_u64(),
159 }
160 }
161 fn fill_bytes(&mut self, v: &mut [u8]) {
162 match self.inner {
163 OsGetrandomRng => getrandom_fill_bytes(v),
164 OsReaderRng(ref mut rng) => rng.fill_bytes(v)
165 }
166 }
167 }
168 }
169
170 #[cfg(target_os = "openbsd")]
171 mod imp {
172 use io;
173 use libc;
174 use mem;
175 use sys::os::errno;
176 use rand::Rng;
177
178 pub struct OsRng {
179 // dummy field to ensure that this struct cannot be constructed outside
180 // of this module
181 _dummy: (),
182 }
183
184 impl OsRng {
185 /// Create a new `OsRng`.
186 pub fn new() -> io::Result<OsRng> {
187 Ok(OsRng { _dummy: () })
188 }
189 }
190
191 impl Rng for OsRng {
192 fn next_u32(&mut self) -> u32 {
193 let mut v = [0; 4];
194 self.fill_bytes(&mut v);
195 unsafe { mem::transmute(v) }
196 }
197 fn next_u64(&mut self) -> u64 {
198 let mut v = [0; 8];
199 self.fill_bytes(&mut v);
200 unsafe { mem::transmute(v) }
201 }
202 fn fill_bytes(&mut self, v: &mut [u8]) {
203 // getentropy(2) permits a maximum buffer size of 256 bytes
204 for s in v.chunks_mut(256) {
205 let ret = unsafe {
206 libc::getentropy(s.as_mut_ptr() as *mut libc::c_void, s.len())
207 };
208 if ret == -1 {
209 panic!("unexpected getentropy error: {}", errno());
210 }
211 }
212 }
213 }
214 }
215
216 #[cfg(target_os = "ios")]
217 mod imp {
218 use io;
219 use mem;
220 use ptr;
221 use rand::Rng;
222 use libc::{c_int, size_t};
223
224 pub struct OsRng {
225 // dummy field to ensure that this struct cannot be constructed outside
226 // of this module
227 _dummy: (),
228 }
229
230 enum SecRandom {}
231
232 #[allow(non_upper_case_globals)]
233 const kSecRandomDefault: *const SecRandom = ptr::null();
234
235 #[link(name = "Security", kind = "framework")]
236 #[cfg(not(cargobuild))]
237 extern {}
238
239 extern {
240 fn SecRandomCopyBytes(rnd: *const SecRandom,
241 count: size_t, bytes: *mut u8) -> c_int;
242 }
243
244 impl OsRng {
245 /// Create a new `OsRng`.
246 pub fn new() -> io::Result<OsRng> {
247 Ok(OsRng { _dummy: () })
248 }
249 }
250
251 impl Rng for OsRng {
252 fn next_u32(&mut self) -> u32 {
253 let mut v = [0; 4];
254 self.fill_bytes(&mut v);
255 unsafe { mem::transmute(v) }
256 }
257 fn next_u64(&mut self) -> u64 {
258 let mut v = [0; 8];
259 self.fill_bytes(&mut v);
260 unsafe { mem::transmute(v) }
261 }
262 fn fill_bytes(&mut self, v: &mut [u8]) {
263 let ret = unsafe {
264 SecRandomCopyBytes(kSecRandomDefault, v.len() as size_t,
265 v.as_mut_ptr())
266 };
267 if ret == -1 {
268 panic!("couldn't generate random bytes: {}",
269 io::Error::last_os_error());
270 }
271 }
272 }
273 }