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 "limits" 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_GETENTROPY) 29 # include <sys/random.h> 30 #elif defined(_LIBCPP_USING_DEV_RANDOM) 31 # include <fcntl.h> 32 # include <unistd.h> 33 # if __has_include(<sys/ioctl.h>) && __has_include(<linux/random.h>) 34 # include <sys/ioctl.h> 35 # include <linux/random.h> 36 # endif 37 #elif defined(_LIBCPP_USING_NACL_RANDOM) 38 # include <nacl/nacl_random.h> 39 #elif defined(_LIBCPP_USING_FUCHSIA_CPRNG) 40 # include <zircon/syscalls.h> 41 #endif 42 43 44 _LIBCPP_BEGIN_NAMESPACE_STD 45 46 #if defined(_LIBCPP_USING_GETENTROPY) 47 48 random_device::random_device(const string& __token) 49 { 50 if (__token != "/dev/urandom") 51 __throw_system_error(ENOENT, ("random device not supported " + __token).c_str()); 52 } 53 54 random_device::~random_device() 55 { 56 } 57 58 unsigned 59 random_device::operator()() 60 { 61 unsigned r; 62 size_t n = sizeof(r); 63 int err = getentropy(&r, n); 64 if (err) 65 __throw_system_error(errno, "random_device getentropy failed"); 66 return r; 67 } 68 69 #elif defined(_LIBCPP_USING_ARC4_RANDOM) 70 71 random_device::random_device(const string&) 72 { 73 } 74 75 random_device::~random_device() 76 { 77 } 78 79 unsigned 80 random_device::operator()() 81 { 82 return arc4random(); 83 } 84 85 #elif defined(_LIBCPP_USING_DEV_RANDOM) 86 87 random_device::random_device(const string& __token) 88 : __f_(open(__token.c_str(), O_RDONLY)) 89 { 90 if (__f_ < 0) 91 __throw_system_error(errno, ("random_device failed to open " + __token).c_str()); 92 } 93 94 random_device::~random_device() 95 { 96 close(__f_); 97 } 98 99 unsigned 100 random_device::operator()() 101 { 102 unsigned r; 103 size_t n = sizeof(r); 104 char* p = reinterpret_cast<char*>(&r); 105 while (n > 0) 106 { 107 ssize_t s = read(__f_, p, n); 108 if (s == 0) 109 __throw_system_error(ENODATA, "random_device got EOF"); 110 if (s == -1) 111 { 112 if (errno != EINTR) 113 __throw_system_error(errno, "random_device got an unexpected error"); 114 continue; 115 } 116 n -= static_cast<size_t>(s); 117 p += static_cast<size_t>(s); 118 } 119 return r; 120 } 121 122 #elif defined(_LIBCPP_USING_NACL_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 int error = nacl_secure_random_init(); 129 if (error) 130 __throw_system_error(error, ("random device failed to open " + __token).c_str()); 131 } 132 133 random_device::~random_device() 134 { 135 } 136 137 unsigned 138 random_device::operator()() 139 { 140 unsigned r; 141 size_t n = sizeof(r); 142 size_t bytes_written; 143 int error = nacl_secure_random(&r, n, &bytes_written); 144 if (error != 0) 145 __throw_system_error(error, "random_device failed getting bytes"); 146 else if (bytes_written != n) 147 __throw_runtime_error("random_device failed to obtain enough bytes"); 148 return r; 149 } 150 151 #elif defined(_LIBCPP_USING_WIN32_RANDOM) 152 153 random_device::random_device(const string& __token) 154 { 155 if (__token != "/dev/urandom") 156 __throw_system_error(ENOENT, ("random device not supported " + __token).c_str()); 157 } 158 159 random_device::~random_device() 160 { 161 } 162 163 unsigned 164 random_device::operator()() 165 { 166 unsigned r; 167 errno_t err = rand_s(&r); 168 if (err) 169 __throw_system_error(err, "random_device rand_s failed."); 170 return r; 171 } 172 173 #elif defined(_LIBCPP_USING_FUCHSIA_CPRNG) 174 175 random_device::random_device(const string& __token) { 176 if (__token != "/dev/urandom") 177 __throw_system_error(ENOENT, ("random device not supported " + __token).c_str()); 178 } 179 180 random_device::~random_device() {} 181 182 unsigned random_device::operator()() { 183 // Implicitly link against the vDSO system call ABI without 184 // requiring the final link to specify -lzircon explicitly when 185 // statically linking libc++. 186 # pragma comment(lib, "zircon") 187 188 // The system call cannot fail. It returns only when the bits are ready. 189 unsigned r; 190 _zx_cprng_draw(&r, sizeof(r)); 191 return r; 192 } 193 194 #else 195 #error "Random device not implemented for this architecture" 196 #endif 197 198 double 199 random_device::entropy() const noexcept 200 { 201 #if defined(_LIBCPP_USING_DEV_RANDOM) && defined(RNDGETENTCNT) 202 int ent; 203 if (::ioctl(__f_, RNDGETENTCNT, &ent) < 0) 204 return 0; 205 206 if (ent < 0) 207 return 0; 208 209 if (ent > std::numeric_limits<result_type>::digits) 210 return std::numeric_limits<result_type>::digits; 211 212 return ent; 213 #elif defined(_LIBCPP_USING_ARC4_RANDOM) || defined(_LIBCPP_USING_FUCHSIA_CPRNG) 214 return std::numeric_limits<result_type>::digits; 215 #else 216 return 0; 217 #endif 218 } 219 220 _LIBCPP_END_NAMESPACE_STD 221