1 //===----------------------------------------------------------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include <__config> 10 11 #if defined(_LIBCPP_USING_WIN32_RANDOM) 12 // Must be defined before including stdlib.h to enable rand_s(). 13 # define _CRT_RAND_S 14 #endif // defined(_LIBCPP_USING_WIN32_RANDOM) 15 16 #include <__system_error/throw_system_error.h> 17 #include <limits> 18 #include <random> 19 #include <string> 20 21 #include <errno.h> 22 #include <stdio.h> 23 #include <stdlib.h> 24 25 #if defined(_LIBCPP_USING_GETENTROPY) 26 # include <sys/random.h> 27 #elif defined(_LIBCPP_USING_DEV_RANDOM) 28 # include <fcntl.h> 29 # include <unistd.h> 30 # if __has_include(<sys/ioctl.h>) && __has_include(<linux/random.h>) 31 # include <linux/random.h> 32 # include <sys/ioctl.h> 33 # endif 34 #elif defined(_LIBCPP_USING_NACL_RANDOM) 35 # include <nacl/nacl_random.h> 36 #elif defined(_LIBCPP_USING_FUCHSIA_CPRNG) 37 # include <zircon/syscalls.h> 38 #endif 39 40 _LIBCPP_BEGIN_NAMESPACE_STD 41 42 #if defined(_LIBCPP_USING_GETENTROPY) 43 44 random_device::random_device(const string& __token) { 45 if (__token != "/dev/urandom") 46 std::__throw_system_error(ENOENT, ("random device not supported " + __token).c_str()); 47 } 48 49 random_device::~random_device() {} 50 51 unsigned random_device::operator()() { 52 unsigned r; 53 size_t n = sizeof(r); 54 int err = getentropy(&r, n); 55 if (err) 56 std::__throw_system_error(errno, "random_device getentropy failed"); 57 return r; 58 } 59 60 #elif defined(_LIBCPP_USING_ARC4_RANDOM) 61 62 random_device::random_device(const string&) {} 63 64 random_device::~random_device() {} 65 66 unsigned random_device::operator()() { return arc4random(); } 67 68 #elif defined(_LIBCPP_USING_DEV_RANDOM) 69 70 random_device::random_device(const string& __token) : __f_(open(__token.c_str(), O_RDONLY)) { 71 if (__f_ < 0) 72 std::__throw_system_error(errno, ("random_device failed to open " + __token).c_str()); 73 } 74 75 random_device::~random_device() { close(__f_); } 76 77 unsigned random_device::operator()() { 78 unsigned r; 79 size_t n = sizeof(r); 80 char* p = reinterpret_cast<char*>(&r); 81 while (n > 0) { 82 ssize_t s = read(__f_, p, n); 83 if (s == 0) 84 std::__throw_system_error(ENOMSG, "random_device got EOF"); 85 if (s == -1) { 86 if (errno != EINTR) 87 std::__throw_system_error(errno, "random_device got an unexpected error"); 88 continue; 89 } 90 n -= static_cast<size_t>(s); 91 p += static_cast<size_t>(s); 92 } 93 return r; 94 } 95 96 #elif defined(_LIBCPP_USING_NACL_RANDOM) 97 98 random_device::random_device(const string& __token) { 99 if (__token != "/dev/urandom") 100 std::__throw_system_error(ENOENT, ("random device not supported " + __token).c_str()); 101 int error = nacl_secure_random_init(); 102 if (error) 103 std::__throw_system_error(error, ("random device failed to open " + __token).c_str()); 104 } 105 106 random_device::~random_device() {} 107 108 unsigned random_device::operator()() { 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 std::__throw_system_error(error, "random_device failed getting bytes"); 115 else if (bytes_written != n) 116 std::__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 if (__token != "/dev/urandom") 124 std::__throw_system_error(ENOENT, ("random device not supported " + __token).c_str()); 125 } 126 127 random_device::~random_device() {} 128 129 unsigned random_device::operator()() { 130 unsigned r; 131 errno_t err = rand_s(&r); 132 if (err) 133 std::__throw_system_error(err, "random_device rand_s failed."); 134 return r; 135 } 136 137 #elif defined(_LIBCPP_USING_FUCHSIA_CPRNG) 138 139 random_device::random_device(const string& __token) { 140 if (__token != "/dev/urandom") 141 std::__throw_system_error(ENOENT, ("random device not supported " + __token).c_str()); 142 } 143 144 random_device::~random_device() {} 145 146 unsigned random_device::operator()() { 147 // Implicitly link against the vDSO system call ABI without 148 // requiring the final link to specify -lzircon explicitly when 149 // statically linking libc++. 150 # pragma comment(lib, "zircon") 151 152 // The system call cannot fail. It returns only when the bits are ready. 153 unsigned r; 154 _zx_cprng_draw(&r, sizeof(r)); 155 return r; 156 } 157 158 #else 159 # error "Random device not implemented for this architecture" 160 #endif 161 162 double random_device::entropy() const noexcept { 163 #if defined(_LIBCPP_USING_DEV_RANDOM) && defined(RNDGETENTCNT) 164 int ent; 165 if (::ioctl(__f_, RNDGETENTCNT, &ent) < 0) 166 return 0; 167 168 if (ent < 0) 169 return 0; 170 171 if (ent > std::numeric_limits<result_type>::digits) 172 return std::numeric_limits<result_type>::digits; 173 174 return ent; 175 #elif defined(_LIBCPP_USING_ARC4_RANDOM) || defined(_LIBCPP_USING_FUCHSIA_CPRNG) 176 return std::numeric_limits<result_type>::digits; 177 #else 178 return 0; 179 #endif 180 } 181 182 _LIBCPP_END_NAMESPACE_STD 183