xref: /llvm-project-15.0.7/libcxx/src/random.cpp (revision 39ea676d)
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