master
  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
 44random_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
 49random_device::~random_device() {}
 50
 51unsigned 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
 62random_device::random_device(const string&) {}
 63
 64random_device::~random_device() {}
 65
 66unsigned random_device::operator()() { return arc4random(); }
 67
 68#elif defined(_LIBCPP_USING_DEV_RANDOM)
 69
 70random_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
 75random_device::~random_device() { close(__f_); }
 76
 77unsigned 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
 98random_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
106random_device::~random_device() {}
107
108unsigned 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
122random_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
127random_device::~random_device() {}
128
129unsigned 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
139random_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
144random_device::~random_device() {}
145
146unsigned 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
162double 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