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