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