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 constexpr int MAX_EINTR_RETRIES = 16;
89 int eintr_retries = 0;
90 // Sample until we fill the buffer
91 while (bytes_left != 0) {
92#if defined(__wasm__) || defined(__APPLE__)
93 // Sample through a "syscall" on wasm. We can't request more than 256, it fails and results in an infinite
94 // loop
95 ssize_t read_bytes =
96 getentropy(current_offset, BYTES_PER_GETENTROPY_READ) == -1 ? -1 : BYTES_PER_GETENTROPY_READ;
97#elif defined(__ANDROID__)
98 // Android API 24 doesn't have getrandom/getentropy, read from /dev/urandom
99 static thread_local int urandom_fd = -1;
100 if (urandom_fd == -1) {
101 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
102 urandom_fd = open("/dev/urandom", O_RDONLY | O_CLOEXEC);
103 }
104 ssize_t read_bytes = ::read(urandom_fd, current_offset, BYTES_PER_GETENTROPY_READ);
105#elif defined(_WIN32)
106 // Use BCryptGenRandom on Windows
107 NTSTATUS status =
108 BCryptGenRandom(NULL, current_offset, static_cast<ULONG>(bytes_left), BCRYPT_USE_SYSTEM_PREFERRED_RNG);
109 ssize_t read_bytes = (status == 0) ? static_cast<ssize_t>(bytes_left) : -1;
110#else
111 // Sample from urandom on native
112 auto read_bytes = getrandom(current_offset, bytes_left, 0);
113#endif
114 if (read_bytes > 0) {
115 current_offset += read_bytes;
116 bytes_left -= static_cast<size_t>(read_bytes);
117 continue;
118 }
119 // read_bytes <= 0: failure or EOF. On platforms that report EINTR via errno, retry a
120 // bounded number of times; any other failure (including read_bytes == 0, e.g. a
121 // sealed/stubbed urandom returning EOF) is fatal.
122#if !defined(_WIN32)
123 if (read_bytes == -1 && errno == EINTR && eintr_retries++ < MAX_EINTR_RETRIES) {
124 continue;
125 }
126#endif
127 throw_or_abort("CSPRNG read failed: cannot retrieve entropy from system source");
128 }
129 random_buffer_wrapper.offset = 0;
130 }
131
132 memcpy(&random_data, random_buffer_wrapper.buffer.get() + random_buffer_wrapper.offset, random_data_buffer_size);
133 random_buffer_wrapper.offset += static_cast<ssize_t>(random_data_buffer_size);
134 return random_data;
135}
136} // namespace
137
138class RandomEngine : public RNG {
139 public:
140 uint8_t get_random_uint8() override
141 {
142 auto buf = generate_random_data<1>();
143 uint32_t out = buf[0];
144 return static_cast<uint8_t>(out);
145 }
146
147 uint16_t get_random_uint16() override
148 {
149 auto buf = generate_random_data<1>();
150 uint32_t out = buf[0];
151 return static_cast<uint16_t>(out);
152 }
153
154 uint32_t get_random_uint32() override
155 {
156 auto buf = generate_random_data<1>();
157 uint32_t out = buf[0];
158 return static_cast<uint32_t>(out);
159 }
160
161 uint64_t get_random_uint64() override
162 {
163 auto buf = generate_random_data<2>();
164 auto lo = static_cast<uint64_t>(buf[0]);
165 auto hi = static_cast<uint64_t>(buf[1]);
166 return (lo + (hi << 32ULL));
167 }
168
170 {
171 const auto get64 = [](const std::array<uint32_t, 4>& buffer, const size_t offset) {
172 auto lo = static_cast<uint64_t>(buffer[0 + offset]);
173 auto hi = static_cast<uint64_t>(buffer[1 + offset]);
174 return (lo + (hi << 32ULL));
175 };
176 auto buf = generate_random_data<4>();
177 auto lo = static_cast<uint128_t>(get64(buf, 0));
178 auto hi = static_cast<uint128_t>(get64(buf, 2));
179
180 return (lo + (hi << static_cast<uint128_t>(64ULL)));
181 }
182
184 {
185 const auto get64 = [](const std::array<uint32_t, 8>& buffer, const size_t offset) {
186 auto lo = static_cast<uint64_t>(buffer[0 + offset]);
187 auto hi = static_cast<uint64_t>(buffer[1 + offset]);
188 return (lo + (hi << 32ULL));
189 };
190 auto buf = generate_random_data<8>();
191 uint64_t lolo = get64(buf, 0);
192 uint64_t lohi = get64(buf, 2);
193 uint64_t hilo = get64(buf, 4);
194 uint64_t hihi = get64(buf, 6);
195 return { lolo, lohi, hilo, hihi };
196 }
197};
198
199class DebugEngine : public RNG {
200 public:
202 // disable linting for this line: we want the DEBUG engine to produce predictable pseudorandom numbers!
203 // NOLINTNEXTLINE(cert-msc32-c, cert-msc51-cpp)
204 : engine(std::mt19937_64(12345))
205 {}
206
208 : engine(std::mt19937_64(seed))
209 {}
210
211 uint8_t get_random_uint8() override { return static_cast<uint8_t>(dist(engine)); }
212
213 uint16_t get_random_uint16() override { return static_cast<uint16_t>(dist(engine)); }
214
215 uint32_t get_random_uint32() override { return static_cast<uint32_t>(dist(engine)); }
216
217 uint64_t get_random_uint64() override { return dist(engine); }
218
220 {
221 uint128_t hi = dist(engine);
222 uint128_t lo = dist(engine);
223 return (hi << 64) | lo;
224 }
225
227 {
228 // Do not inline in constructor call. Evaluation order is important for cross-compiler consistency.
229 auto a = dist(engine);
230 auto b = dist(engine);
231 auto c = dist(engine);
232 auto d = dist(engine);
233 return { a, b, c, d };
234 }
235
236 private:
239};
240
245{
246 // static std::seed_seq seed({ 1, 2, 3, 4, 5 });
247 static DebugEngine debug_engine = DebugEngine();
248 if (reset) {
249 debug_engine = DebugEngine(seed);
250 }
251 return debug_engine;
252}
253
258{
259#ifdef BBERG_DEBUG_LOG
260 // Use determinism for logging
261 return get_debug_randomness();
262#else
263 static RandomEngine engine;
264 return engine;
265#endif
266}
267
268} // namespace bb::numeric
uint8_t get_random_uint8() override
Definition engine.cpp:211
DebugEngine(std::uint_fast64_t seed)
Definition engine.cpp:207
std::mt19937_64 engine
Definition engine.cpp:237
std::uniform_int_distribution< uint64_t > dist
Definition engine.cpp:238
uint32_t get_random_uint32() override
Definition engine.cpp:215
uint16_t get_random_uint16() override
Definition engine.cpp:213
uint64_t get_random_uint64() override
Definition engine.cpp:217
uint128_t get_random_uint128() override
Definition engine.cpp:219
uint256_t get_random_uint256() override
Definition engine.cpp:226
uint32_t get_random_uint32() override
Definition engine.cpp:154
uint8_t get_random_uint8() override
Definition engine.cpp:140
uint16_t get_random_uint16() override
Definition engine.cpp:147
uint256_t get_random_uint256() override
Definition engine.cpp:183
uint64_t get_random_uint64() override
Definition engine.cpp:161
uint128_t get_random_uint128() override
Definition engine.cpp:169
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:244
RNG & get_randomness()
Definition engine.cpp:257
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)