(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  }