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