Home | History | Annotate | Download | only in src
      1 //===-------------------------- random.cpp --------------------------------===//
      2 //
      3 //                     The LLVM Compiler Infrastructure
      4 //
      5 // This file is dual licensed under the MIT and the University of Illinois Open
      6 // Source Licenses. See LICENSE.TXT for details.
      7 //
      8 //===----------------------------------------------------------------------===//
      9 
     10 #if defined(_LIBCPP_USING_WIN32_RANDOM)
     11 // Must be defined before including stdlib.h to enable rand_s().
     12 #define _CRT_RAND_S
     13 #endif // defined(_LIBCPP_USING_WIN32_RANDOM)
     14 
     15 #include "random"
     16 #include "system_error"
     17 
     18 #if defined(__sun__)
     19 #define rename solaris_headers_are_broken
     20 #endif // defined(__sun__)
     21 
     22 #include <errno.h>
     23 #include <stdio.h>
     24 #include <stdlib.h>
     25 
     26 #if defined(_LIBCPP_USING_DEV_RANDOM)
     27 #include <fcntl.h>
     28 #include <unistd.h>
     29 #elif defined(_LIBCPP_USING_NACL_RANDOM)
     30 #include <nacl/nacl_random.h>
     31 #endif
     32 
     33 
     34 _LIBCPP_BEGIN_NAMESPACE_STD
     35 
     36 #if defined(_LIBCPP_USING_ARC4_RANDOM)
     37 
     38 random_device::random_device(const string& __token)
     39 {
     40     if (__token != "/dev/urandom")
     41         __throw_system_error(ENOENT, ("random device not supported " + __token).c_str());
     42 }
     43 
     44 random_device::~random_device()
     45 {
     46 }
     47 
     48 unsigned
     49 random_device::operator()()
     50 {
     51     return arc4random();
     52 }
     53 
     54 #elif defined(_LIBCPP_USING_DEV_RANDOM)
     55 
     56 random_device::random_device(const string& __token)
     57     : __f_(open(__token.c_str(), O_RDONLY))
     58 {
     59     if (__f_ < 0)
     60         __throw_system_error(errno, ("random_device failed to open " + __token).c_str());
     61 }
     62 
     63 random_device::~random_device()
     64 {
     65     close(__f_);
     66 }
     67 
     68 unsigned
     69 random_device::operator()()
     70 {
     71     unsigned r;
     72     size_t n = sizeof(r);
     73     char* p = reinterpret_cast<char*>(&r);
     74     while (n > 0)
     75     {
     76         ssize_t s = read(__f_, p, n);
     77         if (s == 0)
     78             __throw_system_error(ENODATA, "random_device got EOF");
     79         if (s == -1)
     80         {
     81             if (errno != EINTR)
     82                 __throw_system_error(errno, "random_device got an unexpected error");
     83             continue;
     84         }
     85         n -= static_cast<size_t>(s);
     86         p += static_cast<size_t>(s);
     87     }
     88     return r;
     89 }
     90 
     91 #elif defined(_LIBCPP_USING_NACL_RANDOM)
     92 
     93 random_device::random_device(const string& __token)
     94 {
     95     if (__token != "/dev/urandom")
     96         __throw_system_error(ENOENT, ("random device not supported " + __token).c_str());
     97     int error = nacl_secure_random_init();
     98     if (error)
     99         __throw_system_error(error, ("random device failed to open " + __token).c_str());
    100 }
    101 
    102 random_device::~random_device()
    103 {
    104 }
    105 
    106 unsigned
    107 random_device::operator()()
    108 {
    109     unsigned r;
    110     size_t n = sizeof(r);
    111     size_t bytes_written;
    112     int error = nacl_secure_random(&r, n, &bytes_written);
    113     if (error != 0)
    114         __throw_system_error(error, "random_device failed getting bytes");
    115     else if (bytes_written != n)
    116         __throw_runtime_error("random_device failed to obtain enough bytes");
    117     return r;
    118 }
    119 
    120 #elif defined(_LIBCPP_USING_WIN32_RANDOM)
    121 
    122 random_device::random_device(const string& __token)
    123 {
    124     if (__token != "/dev/urandom")
    125         __throw_system_error(ENOENT, ("random device not supported " + __token).c_str());
    126 }
    127 
    128 random_device::~random_device()
    129 {
    130 }
    131 
    132 unsigned
    133 random_device::operator()()
    134 {
    135     unsigned r;
    136     errno_t err = rand_s(&r);
    137     if (err)
    138         __throw_system_error(err, "random_device rand_s failed.");
    139     return r;
    140 }
    141 
    142 #else
    143 #error "Random device not implemented for this architecture"
    144 #endif
    145 
    146 double
    147 random_device::entropy() const _NOEXCEPT
    148 {
    149     return 0;
    150 }
    151 
    152 _LIBCPP_END_NAMESPACE_STD
    153