Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
engine.cpp
Go to the documentation of this file.
1// === AUDIT STATUS ===
2// internal: { status: Complete, auditors: [Luke], commit: }
3// external_1: { status: not started, auditors: [], commit: }
4// external_2: { status: not started, auditors: [], commit: }
5// =====================
6
7#include "engine.hpp"
10#include <array>
11#include <cerrno>
12#include <cstring>
13#include <functional>
14#include <memory>
15#include <random>
16#if defined(__APPLE__)
17#include <TargetConditionals.h>
18#if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR
19#include <unistd.h>
20extern "C" int getentropy(void* buffer, size_t length); // getentropy on iOS
21#else
22#include <sys/random.h> // getentropy on macOS
23#endif
24#elif defined(__ANDROID__)
25// Android API 24 doesn't have getrandom/getentropy, use /dev/urandom
26#include <fcntl.h>
27#include <unistd.h>
28#elif defined(_WIN32)
29#include <bcrypt.h>
30#include <windows.h>
31#else
32#include <sys/random.h>
33#endif
34
35namespace bb::numeric {
36
37namespace {
38
39#if defined(__wasm__) || defined(__APPLE__) || defined(__ANDROID__)
40
41// In wasm, on mac os, and on Android the API we are using can only give 256 bytes per call,
42// so there is no point in creating a larger buffer
43constexpr size_t RANDOM_BUFFER_SIZE = 256;
44constexpr size_t BYTES_PER_GETENTROPY_READ = 256;
45
46#elif defined(_WIN32)
47
48// BCryptGenRandom can fill arbitrary sizes, but keep a reasonable buffer
49constexpr size_t RANDOM_BUFFER_SIZE = 1UL << 20;
50
51#else
52
53// When working on native we allocate 1M of memory to sample randomness from urandom
54constexpr size_t RANDOM_BUFFER_SIZE = 1UL << 20;
55
56#endif
57struct RandomBufferWrapper {
58 // Buffer with randomness sampled from a CSPRNG (heap-allocated on first use to avoid
59 // bloating TLS — a 1 MiB inline array adds ~0.6 ms per thread creation)
61 // Offset into the unused part of the buffer
62 ssize_t offset = -1;
63};
64thread_local RandomBufferWrapper random_buffer_wrapper;
71template <size_t size_in_unsigned_ints> std::array<unsigned int, size_in_unsigned_ints> generate_random_data()
72{
73 static_assert(size_in_unsigned_ints > 0);
74 static_assert(size_in_unsigned_ints <= 32);
76 constexpr size_t random_data_buffer_size = sizeof(random_data);
77
78 // if the buffer is not initialized or doesn't contain enough bytes, sample randomness
79 // We could preserve the leftover bytes, but it's a bit messy
80 if (random_buffer_wrapper.offset == -1 ||
81 (static_cast<size_t>(random_buffer_wrapper.offset) + random_data_buffer_size) > RANDOM_BUFFER_SIZE) {
82 if (!random_buffer_wrapper.buffer) {
83 random_buffer_wrapper.buffer = std::make_unique<uint8_t[]>(RANDOM_BUFFER_SIZE);
84 }
85 size_t bytes_left = RANDOM_BUFFER_SIZE;
86 uint8_t* current_offset = random_buffer_wrapper.buffer.get();
87 // Bound EINTR retries so a pathological signal storm cannot mask a genuine fault.
88 // Unused on Windows (BCryptGenRandom is not interruptible).
89 [[maybe_unused]] constexpr int MAX_EINTR_RETRIES = 16;
90 [[maybe_unused]] int eintr_retries = 0;
91 // Sample until we fill the buffer
92 while (bytes_left != 0) {
93#if defined(__wasm__) || defined(__APPLE__)
94 // Sample through a "syscall" on wasm. We can't request more than 256, it fails and results in an infinite
95 // loop
96 ssize_t read_bytes =
97 getentropy(current_offset, BYTES_PER_GETENTROPY_READ) == -1 ? -1 : BYTES_PER_GETENTROPY_READ;
98#elif defined(__ANDROID__)
99 // Android API 24 doesn't have getrandom/getentropy, read from /dev/urandom
100 static thread_local int urandom_fd = -1;
101 if (urandom_fd == -1) {
102 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
103 urandom_fd = open("/dev/urandom", O_RDONLY | O_CLOEXEC);
104 }
105 ssize_t read_bytes = ::read(urandom_fd, current_offset, BYTES_PER_GETENTROPY_READ);
106#elif defined(_WIN32)
107 // Use BCryptGenRandom on Windows
108 NTSTATUS status =
109 BCryptGenRandom(NULL, current_offset, static_cast<ULONG>(bytes_left), BCRYPT_USE_SYSTEM_PREFERRED_RNG);
110 ssize_t read_bytes = (status == 0) ? static_cast<ssize_t>(bytes_left) : -1;
111#else
112 // Sample from urandom on native
113 auto read_bytes = getrandom(current_offset, bytes_left, 0);
114#endif
115 if (read_bytes > 0) {
116 current_offset += read_bytes;
117 bytes_left -= static_cast<size_t>(read_bytes);
118 continue;
119 }
120 // read_bytes <= 0: failure or EOF. On platforms that report EINTR via errno, retry a
121 // bounded number of times; any other failure (including read_bytes == 0, e.g. a
122 // sealed/stubbed urandom returning EOF) is fatal.
123#if !defined(_WIN32)
124 if (read_bytes == -1 && errno == EINTR && eintr_retries++ < MAX_EINTR_RETRIES) {
125 continue;
126 }
127#endif
128 throw_or_abort("CSPRNG read failed: cannot retrieve entropy from system source");
129 }
130 random_buffer_wrapper.offset = 0;
131 }
132
133 memcpy(&random_data, random_buffer_wrapper.buffer.get() + random_buffer_wrapper.offset, random_data_buffer_size);
134 random_buffer_wrapper.offset += static_cast<ssize_t>(random_data_buffer_size);
135 return random_data;
136}
137} // namespace
138
139class RandomEngine : public RNG {
140 public:
141 uint8_t get_random_uint8() override
142 {
143 auto buf = generate_random_data<1>();
144 uint32_t out = buf[0];
145 return static_cast<uint8_t>(out);
146 }
147
148 uint16_t get_random_uint16() override
149 {
150 auto buf = generate_random_data<1>();
151 uint32_t out = buf[0];
152 return static_cast<uint16_t>(out);
153 }
154
155 uint32_t get_random_uint32() override
156 {
157 auto buf = generate_random_data<1>();
158 uint32_t out = buf[0];
159 return static_cast<uint32_t>(out);
160 }
161
162 uint64_t get_random_uint64() override
163 {
164 auto buf = generate_random_data<2>();
165 auto lo = static_cast<uint64_t>(buf[0]);
166 auto hi = static_cast<uint64_t>(buf[1]);
167 return (lo + (hi << 32ULL));
168 }
169
171 {
172 const auto get64 = [](const std::array<uint32_t, 4>& buffer, const size_t offset) {
173 auto lo = static_cast<uint64_t>(buffer[0 + offset]);
174 auto hi = static_cast<uint64_t>(buffer[1 + offset]);
175 return (lo + (hi << 32ULL));
176 };
177 auto buf = generate_random_data<4>();
178 auto lo = static_cast<uint128_t>(get64(buf, 0));
179 auto hi = static_cast<uint128_t>(get64(buf, 2));
180
181 return (lo + (hi << static_cast<uint128_t>(64ULL)));
182 }
183
185 {
186 const auto get64 = [](const std::array<uint32_t, 8>& buffer, const size_t offset) {
187 auto lo = static_cast<uint64_t>(buffer[0 + offset]);
188 auto hi = static_cast<uint64_t>(buffer[1 + offset]);
189 return (lo + (hi << 32ULL));
190 };
191 auto buf = generate_random_data<8>();
192 uint64_t lolo = get64(buf, 0);
193 uint64_t lohi = get64(buf, 2);
194 uint64_t hilo = get64(buf, 4);
195 uint64_t hihi = get64(buf, 6);
196 return { lolo, lohi, hilo, hihi };
197 }
198};
199
200class DebugEngine : public RNG {
201 public:
203 // disable linting for this line: we want the DEBUG engine to produce predictable pseudorandom numbers!
204 // NOLINTNEXTLINE(cert-msc32-c, cert-msc51-cpp)
205 : engine(std::mt19937_64(12345))
206 {}
207
209 : engine(std::mt19937_64(seed))
210 {}
211
212 uint8_t get_random_uint8() override { return static_cast<uint8_t>(dist(engine)); }
213
214 uint16_t get_random_uint16() override { return static_cast<uint16_t>(dist(engine)); }
215
216 uint32_t get_random_uint32() override { return static_cast<uint32_t>(dist(engine)); }
217
218 uint64_t get_random_uint64() override { return dist(engine); }
219
221 {
222 uint128_t hi = dist(engine);
223 uint128_t lo = dist(engine);
224 return (hi << 64) | lo;
225 }
226
228 {
229 // Do not inline in constructor call. Evaluation order is important for cross-compiler consistency.
230 auto a = dist(engine);
231 auto b = dist(engine);
232 auto c = dist(engine);
233 auto d = dist(engine);
234 return { a, b, c, d };
235 }
236
237 private:
240};
241
246{
247 // static std::seed_seq seed({ 1, 2, 3, 4, 5 });
248 static DebugEngine debug_engine = DebugEngine();
249 if (reset) {
250 debug_engine = DebugEngine(seed);
251 }
252 return debug_engine;
253}
254
259{
260#ifdef BBERG_DEBUG_LOG
261 // Use determinism for logging
262 return get_debug_randomness();
263#else
264 static RandomEngine engine;
265 return engine;
266#endif
267}
268
269} // namespace bb::numeric
uint8_t get_random_uint8() override
Definition engine.cpp:212
DebugEngine(std::uint_fast64_t seed)
Definition engine.cpp:208
std::mt19937_64 engine
Definition engine.cpp:238
std::uniform_int_distribution< uint64_t > dist
Definition engine.cpp:239
uint32_t get_random_uint32() override
Definition engine.cpp:216
uint16_t get_random_uint16() override
Definition engine.cpp:214
uint64_t get_random_uint64() override
Definition engine.cpp:218
uint128_t get_random_uint128() override
Definition engine.cpp:220
uint256_t get_random_uint256() override
Definition engine.cpp:227
uint32_t get_random_uint32() override
Definition engine.cpp:155
uint8_t get_random_uint8() override
Definition engine.cpp:141
uint16_t get_random_uint16() override
Definition engine.cpp:148
uint256_t get_random_uint256() override
Definition engine.cpp:184
uint64_t get_random_uint64() override
Definition engine.cpp:162
uint128_t get_random_uint128() override
Definition engine.cpp:170
FF a
FF b
numeric::RNG & engine
ssize_t offset
Definition engine.cpp:62
std::unique_ptr< uint8_t[]> buffer
Definition engine.cpp:60
void read(B &it, uint256_t &value)
Definition uint256.hpp:267
RNG & get_debug_randomness(bool reset, std::uint_fast64_t seed)
Definition engine.cpp:245
RNG & get_randomness()
Definition engine.cpp:258
STL namespace.
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
unsigned __int128 uint128_t
Definition serialize.hpp:45
void throw_or_abort(std::string const &err)