Line data Source code
1 : //
2 : // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
3 : //
4 : // Distributed under the Boost Software License, Version 1.0. (See accompanying
5 : // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6 : //
7 : // Official repository: https://github.com/cppalliance/capy
8 : //
9 :
10 : #include "random.hpp"
11 : #include <boost/capy/detail/except.hpp>
12 : #include <boost/system/error_code.hpp>
13 :
14 : #if defined(_WIN32)
15 : # ifndef WIN32_LEAN_AND_MEAN
16 : # define WIN32_LEAN_AND_MEAN
17 : # endif
18 : # include <windows.h>
19 : # include <bcrypt.h>
20 : # ifdef _MSC_VER
21 : # pragma comment(lib, "bcrypt.lib")
22 : # endif
23 : #elif defined(__linux__)
24 : # include <sys/random.h>
25 : #elif defined(__APPLE__)
26 : # include <Security/SecRandom.h>
27 : #else
28 : # include <fcntl.h>
29 : # include <unistd.h>
30 : #endif
31 :
32 : namespace boost {
33 : namespace capy {
34 : namespace bcrypt {
35 : namespace detail {
36 :
37 : #if defined(_WIN32)
38 :
39 : namespace {
40 :
41 : class rng_provider
42 : {
43 : BCRYPT_ALG_HANDLE h_ = nullptr;
44 :
45 : public:
46 : rng_provider()
47 : {
48 : NTSTATUS status = BCryptOpenAlgorithmProvider(
49 : &h_,
50 : BCRYPT_RNG_ALGORITHM,
51 : nullptr,
52 : 0);
53 : if (!BCRYPT_SUCCESS(status))
54 : h_ = nullptr;
55 : }
56 :
57 : ~rng_provider()
58 : {
59 : if (h_)
60 : BCryptCloseAlgorithmProvider(h_, 0);
61 : }
62 :
63 : rng_provider(rng_provider const&) = delete;
64 : rng_provider& operator=(rng_provider const&) = delete;
65 :
66 : bool generate(void* buf, std::size_t n) const
67 : {
68 : if (!h_)
69 : return false;
70 : NTSTATUS status = BCryptGenRandom(
71 : h_,
72 : static_cast<PUCHAR>(buf),
73 : static_cast<ULONG>(n),
74 : 0);
75 : return BCRYPT_SUCCESS(status);
76 : }
77 : };
78 :
79 : rng_provider& get_rng()
80 : {
81 : static rng_provider rng;
82 : return rng;
83 : }
84 :
85 : } // namespace
86 :
87 : void
88 : fill_random(void* buf, std::size_t n)
89 : {
90 : if (!get_rng().generate(buf, n))
91 : {
92 : capy::detail::throw_system_error(
93 : system::error_code(
94 : static_cast<int>(GetLastError()),
95 : system::system_category()));
96 : }
97 : }
98 :
99 : #elif defined(__linux__)
100 :
101 : void
102 14 : fill_random(void* buf, std::size_t n)
103 : {
104 14 : auto* p = static_cast<unsigned char*>(buf);
105 28 : while (n > 0)
106 : {
107 14 : ssize_t r = getrandom(p, n, 0);
108 14 : if (r < 0)
109 : {
110 0 : if (errno == EINTR)
111 0 : continue;
112 0 : capy::detail::throw_system_error(
113 0 : system::error_code(
114 0 : errno,
115 : system::system_category()));
116 : }
117 14 : p += r;
118 14 : n -= static_cast<std::size_t>(r);
119 : }
120 14 : }
121 :
122 : #elif defined(__APPLE__)
123 :
124 : void
125 : fill_random(void* buf, std::size_t n)
126 : {
127 : int err = SecRandomCopyBytes(kSecRandomDefault, n, buf);
128 : if (err != errSecSuccess)
129 : {
130 : capy::detail::throw_system_error(
131 : system::error_code(
132 : err,
133 : system::system_category()));
134 : }
135 : }
136 :
137 : #else
138 :
139 : // Fallback: /dev/urandom
140 : void
141 : fill_random(void* buf, std::size_t n)
142 : {
143 : static int fd = -1;
144 : if (fd < 0)
145 : {
146 : fd = open("/dev/urandom", O_RDONLY | O_CLOEXEC);
147 : if (fd < 0)
148 : {
149 : capy::detail::throw_system_error(
150 : system::error_code(
151 : errno,
152 : system::system_category()));
153 : }
154 : }
155 :
156 : auto* p = static_cast<unsigned char*>(buf);
157 : while (n > 0)
158 : {
159 : ssize_t r = read(fd, p, n);
160 : if (r < 0)
161 : {
162 : if (errno == EINTR)
163 : continue;
164 : capy::detail::throw_system_error(
165 : system::error_code(
166 : errno,
167 : system::system_category()));
168 : }
169 : if (r == 0)
170 : {
171 : capy::detail::throw_runtime_error(
172 : "unexpected EOF from /dev/urandom");
173 : }
174 : p += r;
175 : n -= static_cast<std::size_t>(r);
176 : }
177 : }
178 :
179 : #endif
180 :
181 : } // detail
182 : } // bcrypt
183 : } // capy
184 : } // boost
185 :
|