(index<- ) ./librand/os.rs
git branch: * master 5200215 auto merge of #14035 : alexcrichton/rust/experimental, r=huonw
modified: Wed Apr 9 17:27:02 2014
1 // Copyright 2013 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(unix)]
17 mod imp {
18 use Rng;
19 use reader::ReaderRng;
20 use std::io::{IoResult, File};
21
22 /// A random number generator that retrieves randomness straight from
23 /// the operating system. Platform sources:
24 ///
25 /// - Unix-like systems (Linux, Android, Mac OSX): read directly from
26 /// `/dev/urandom`.
27 /// - Windows: calls `CryptGenRandom`, using the default cryptographic
28 /// service provider with the `PROV_RSA_FULL` type.
29 ///
30 /// This does not block.
31 #[cfg(unix)]
32 pub struct OSRng {
33 inner: ReaderRng<File>
34 }
35
36 impl OSRng {
37 /// Create a new `OSRng`.
38 pub fn new() -> IoResult<OSRng> {
39 let reader = try!(File::open(&Path::new("/dev/urandom")));
40 let reader_rng = ReaderRng::new(reader);
41
42 Ok(OSRng { inner: reader_rng })
43 }
44 }
45
46 impl Rng for OSRng {
47 fn next_u32(&mut self) -> u32 {
48 self.inner.next_u32()
49 }
50 fn next_u64(&mut self) -> u64 {
51 self.inner.next_u64()
52 }
53 fn fill_bytes(&mut self, v: &mut [u8]) {
54 self.inner.fill_bytes(v)
55 }
56 }
57 }
58
59 #[cfg(windows)]
60 mod imp {
61 extern crate libc;
62
63 use Rng;
64 use std::cast;
65 use std::io::{IoResult, IoError};
66 use std::os;
67 use std::rt::stack;
68 use self::libc::{c_ulong, DWORD, BYTE, LPCSTR, BOOL};
69
70 type HCRYPTPROV = c_ulong;
71
72 /// A random number generator that retrieves randomness straight from
73 /// the operating system. Platform sources:
74 ///
75 /// - Unix-like systems (Linux, Android, Mac OSX): read directly from
76 /// `/dev/urandom`.
77 /// - Windows: calls `CryptGenRandom`, using the default cryptographic
78 /// service provider with the `PROV_RSA_FULL` type.
79 ///
80 /// This does not block.
81 pub struct OSRng {
82 hcryptprov: HCRYPTPROV
83 }
84
85 static PROV_RSA_FULL: DWORD = 1;
86 static CRYPT_SILENT: DWORD = 64;
87 static CRYPT_VERIFYCONTEXT: DWORD = 0xF0000000;
88 static NTE_BAD_SIGNATURE: DWORD = 0x80090006;
89
90 extern "system" {
91 fn CryptAcquireContextA(phProv: *mut HCRYPTPROV,
92 pszContainer: LPCSTR,
93 pszProvider: LPCSTR,
94 dwProvType: DWORD,
95 dwFlags: DWORD) -> BOOL;
96 fn CryptGenRandom(hProv: HCRYPTPROV,
97 dwLen: DWORD,
98 pbBuffer: *mut BYTE) -> BOOL;
99 fn CryptReleaseContext(hProv: HCRYPTPROV, dwFlags: DWORD) -> BOOL;
100 }
101
102 impl OSRng {
103 /// Create a new `OSRng`.
104 pub fn new() -> IoResult<OSRng> {
105 let mut hcp = 0;
106 let mut ret = unsafe {
107 CryptAcquireContextA(&mut hcp, 0 as LPCSTR, 0 as LPCSTR,
108 PROV_RSA_FULL,
109 CRYPT_VERIFYCONTEXT | CRYPT_SILENT)
110 };
111
112 // It turns out that if we can't acquire a context with the
113 // NTE_BAD_SIGNATURE error code, the documentation states:
114 //
115 // The provider DLL signature could not be verified. Either the
116 // DLL or the digital signature has been tampered with.
117 //
118 // Sounds fishy, no? As it turns out, our signature can be bad
119 // because our Thread Information Block (TIB) isn't exactly what it
120 // expects. As to why, I have no idea. The only data we store in the
121 // TIB is the stack limit for each thread, but apparently that's
122 // enough to make the signature valid.
123 //
124 // Furthermore, this error only happens the *first* time we call
125 // CryptAcquireContext, so we don't have to worry about future
126 // calls.
127 //
128 // Anyway, the fix employed here is that if we see this error, we
129 // pray that we're not close to the end of the stack, temporarily
130 // set the stack limit to 0 (what the TIB originally was), acquire a
131 // context, and then reset the stack limit.
132 //
133 // Again, I'm not sure why this is the fix, nor why we're getting
134 // this error. All I can say is that this seems to allow libnative
135 // to progress where it otherwise would be hindered. Who knew?
136 if ret == 0 && os::errno() as DWORD == NTE_BAD_SIGNATURE {
137 unsafe {
138 let limit = stack::get_sp_limit();
139 stack::record_sp_limit(0);
140 ret = CryptAcquireContextA(&mut hcp, 0 as LPCSTR, 0 as LPCSTR,
141 PROV_RSA_FULL,
142 CRYPT_VERIFYCONTEXT | CRYPT_SILENT);
143 stack::record_sp_limit(limit);
144 }
145 }
146
147 if ret == 0 {
148 Err(IoError::last_error())
149 } else {
150 Ok(OSRng { hcryptprov: hcp })
151 }
152 }
153 }
154
155 impl Rng for OSRng {
156 fn next_u32(&mut self) -> u32 {
157 let mut v = [0u8, .. 4];
158 self.fill_bytes(v);
159 unsafe { cast::transmute(v) }
160 }
161 fn next_u64(&mut self) -> u64 {
162 let mut v = [0u8, .. 8];
163 self.fill_bytes(v);
164 unsafe { cast::transmute(v) }
165 }
166 fn fill_bytes(&mut self, v: &mut [u8]) {
167 let ret = unsafe {
168 CryptGenRandom(self.hcryptprov, v.len() as DWORD,
169 v.as_mut_ptr())
170 };
171 if ret == 0 {
172 fail!("couldn't generate random bytes: {}", os::last_os_error());
173 }
174 }
175 }
176
177 impl Drop for OSRng {
178 fn drop(&mut self) {
179 let ret = unsafe {
180 CryptReleaseContext(self.hcryptprov, 0)
181 };
182 if ret == 0 {
183 fail!("couldn't release context: {}", os::last_os_error());
184 }
185 }
186 }
187 }
188
189 #[cfg(test)]
190 mod test {
191 use super::OSRng;
192 use Rng;
193 use std::task;
194
195 #[test]
196 fn test_os_rng() {
197 let mut r = OSRng::new().unwrap();
198
199 r.next_u32();
200 r.next_u64();
201
202 let mut v = [0u8, .. 1000];
203 r.fill_bytes(v);
204 }
205
206 #[test]
207 fn test_os_rng_tasks() {
208
209 let mut txs = vec!();
210 for _ in range(0, 20) {
211 let (tx, rx) = channel();
212 txs.push(tx);
213 task::spawn(proc() {
214 // wait until all the tasks are ready to go.
215 rx.recv();
216
217 // deschedule to attempt to interleave things as much
218 // as possible (XXX: is this a good test?)
219 let mut r = OSRng::new().unwrap();
220 task::deschedule();
221 let mut v = [0u8, .. 1000];
222
223 for _ in range(0, 100) {
224 r.next_u32();
225 task::deschedule();
226 r.next_u64();
227 task::deschedule();
228 r.fill_bytes(v);
229 task::deschedule();
230 }
231 })
232 }
233
234 // start all the tasks
235 for tx in txs.iter() {
236 tx.send(())
237 }
238 }
239 }