1 // Copyright 2017 The Abseil Authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "absl/random/internal/seed_material.h"
16
17 #include <fcntl.h>
18
19 #ifndef _WIN32
20 #include <unistd.h>
21 #else
22 #include <io.h>
23 #endif
24
25 #include <algorithm>
26 #include <cerrno>
27 #include <cstdint>
28 #include <cstdlib>
29 #include <cstring>
30
31 #include "absl/base/dynamic_annotations.h"
32 #include "absl/base/internal/raw_logging.h"
33 #include "absl/strings/ascii.h"
34 #include "absl/strings/escaping.h"
35 #include "absl/strings/string_view.h"
36 #include "absl/strings/strip.h"
37
38 #if defined(__native_client__)
39
40 #include <nacl/nacl_random.h>
41 #define ABSL_RANDOM_USE_NACL_SECURE_RANDOM 1
42
43 #elif defined(_WIN32)
44
45 #include <windows.h>
46 #define ABSL_RANDOM_USE_BCRYPT 1
47 #pragma comment(lib, "bcrypt.lib")
48
49 #elif defined(__Fuchsia__)
50 #include <zircon/syscalls.h>
51
52 #endif
53
54 #if defined(__GLIBC__) && \
55 (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 25))
56 // glibc >= 2.25 has getentropy()
57 #define ABSL_RANDOM_USE_GET_ENTROPY 1
58 #endif
59
60 #if defined(__EMSCRIPTEN__)
61 #include <sys/random.h>
62 // Emscripten has getentropy, but it resides in a different header.
63 #define ABSL_RANDOM_USE_GET_ENTROPY 1
64 #endif
65
66 #if defined(ABSL_RANDOM_USE_BCRYPT)
67 #include <bcrypt.h>
68
69 #ifndef BCRYPT_SUCCESS
70 #define BCRYPT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
71 #endif
72 // Also link bcrypt; this can be done via linker options or:
73 // #pragma comment(lib, "bcrypt.lib")
74 #endif
75
76 namespace absl {
77 ABSL_NAMESPACE_BEGIN
78 namespace random_internal {
79 namespace {
80
81 // Read OS Entropy for random number seeds.
82 // TODO(absl-team): Possibly place a cap on how much entropy may be read at a
83 // time.
84
85 #if defined(ABSL_RANDOM_USE_BCRYPT)
86
87 // On Windows potentially use the BCRYPT CNG API to read available entropy.
ReadSeedMaterialFromOSEntropyImpl(absl::Span<uint32_t> values)88 bool ReadSeedMaterialFromOSEntropyImpl(absl::Span<uint32_t> values) {
89 BCRYPT_ALG_HANDLE hProvider;
90 NTSTATUS ret;
91 ret = BCryptOpenAlgorithmProvider(&hProvider, BCRYPT_RNG_ALGORITHM,
92 MS_PRIMITIVE_PROVIDER, 0);
93 if (!(BCRYPT_SUCCESS(ret))) {
94 ABSL_RAW_LOG(ERROR, "Failed to open crypto provider.");
95 return false;
96 }
97 ret = BCryptGenRandom(
98 hProvider, // provider
99 reinterpret_cast<UCHAR*>(values.data()), // buffer
100 static_cast<ULONG>(sizeof(uint32_t) * values.size()), // bytes
101 0); // flags
102 BCryptCloseAlgorithmProvider(hProvider, 0);
103 return BCRYPT_SUCCESS(ret);
104 }
105
106 #elif defined(ABSL_RANDOM_USE_NACL_SECURE_RANDOM)
107
108 // On NaCL use nacl_secure_random to acquire bytes.
109 bool ReadSeedMaterialFromOSEntropyImpl(absl::Span<uint32_t> values) {
110 auto buffer = reinterpret_cast<uint8_t*>(values.data());
111 size_t buffer_size = sizeof(uint32_t) * values.size();
112
113 uint8_t* output_ptr = buffer;
114 while (buffer_size > 0) {
115 size_t nread = 0;
116 const int error = nacl_secure_random(output_ptr, buffer_size, &nread);
117 if (error != 0 || nread > buffer_size) {
118 ABSL_RAW_LOG(ERROR, "Failed to read secure_random seed data: %d", error);
119 return false;
120 }
121 output_ptr += nread;
122 buffer_size -= nread;
123 }
124 return true;
125 }
126
127 #elif defined(__Fuchsia__)
128
129 bool ReadSeedMaterialFromOSEntropyImpl(absl::Span<uint32_t> values) {
130 auto buffer = reinterpret_cast<uint8_t*>(values.data());
131 size_t buffer_size = sizeof(uint32_t) * values.size();
132 zx_cprng_draw(buffer, buffer_size);
133 return true;
134 }
135
136 #else
137
138 #if defined(ABSL_RANDOM_USE_GET_ENTROPY)
139 // On *nix, use getentropy() if supported. Note that libc may support
140 // getentropy(), but the kernel may not, in which case this function will return
141 // false.
142 bool ReadSeedMaterialFromGetEntropy(absl::Span<uint32_t> values) {
143 auto buffer = reinterpret_cast<uint8_t*>(values.data());
144 size_t buffer_size = sizeof(uint32_t) * values.size();
145 while (buffer_size > 0) {
146 // getentropy() has a maximum permitted length of 256.
147 size_t to_read = std::min<size_t>(buffer_size, 256);
148 int result = getentropy(buffer, to_read);
149 if (result < 0) {
150 return false;
151 }
152 // https://github.com/google/sanitizers/issues/1173
153 // MemorySanitizer can't see through getentropy().
154 ABSL_ANNOTATE_MEMORY_IS_INITIALIZED(buffer, to_read);
155 buffer += to_read;
156 buffer_size -= to_read;
157 }
158 return true;
159 }
160 #endif // defined(ABSL_RANDOM_GETENTROPY)
161
162 // On *nix, read entropy from /dev/urandom.
163 bool ReadSeedMaterialFromDevURandom(absl::Span<uint32_t> values) {
164 const char kEntropyFile[] = "/dev/urandom";
165
166 auto buffer = reinterpret_cast<uint8_t*>(values.data());
167 size_t buffer_size = sizeof(uint32_t) * values.size();
168
169 int dev_urandom = open(kEntropyFile, O_RDONLY);
170 bool success = (-1 != dev_urandom);
171 if (!success) {
172 return false;
173 }
174
175 while (success && buffer_size > 0) {
176 int bytes_read = read(dev_urandom, buffer, buffer_size);
177 int read_error = errno;
178 success = (bytes_read > 0);
179 if (success) {
180 buffer += bytes_read;
181 buffer_size -= bytes_read;
182 } else if (bytes_read == -1 && read_error == EINTR) {
183 success = true; // Need to try again.
184 }
185 }
186 close(dev_urandom);
187 return success;
188 }
189
190 bool ReadSeedMaterialFromOSEntropyImpl(absl::Span<uint32_t> values) {
191 #if defined(ABSL_RANDOM_USE_GET_ENTROPY)
192 if (ReadSeedMaterialFromGetEntropy(values)) {
193 return true;
194 }
195 #endif
196 // Libc may support getentropy, but the kernel may not, so we still have
197 // to fallback to ReadSeedMaterialFromDevURandom().
198 return ReadSeedMaterialFromDevURandom(values);
199 }
200
201 #endif
202
203 } // namespace
204
ReadSeedMaterialFromOSEntropy(absl::Span<uint32_t> values)205 bool ReadSeedMaterialFromOSEntropy(absl::Span<uint32_t> values) {
206 assert(values.data() != nullptr);
207 if (values.data() == nullptr) {
208 return false;
209 }
210 if (values.empty()) {
211 return true;
212 }
213 return ReadSeedMaterialFromOSEntropyImpl(values);
214 }
215
MixIntoSeedMaterial(absl::Span<const uint32_t> sequence,absl::Span<uint32_t> seed_material)216 void MixIntoSeedMaterial(absl::Span<const uint32_t> sequence,
217 absl::Span<uint32_t> seed_material) {
218 // Algorithm is based on code available at
219 // https://gist.github.com/imneme/540829265469e673d045
220 constexpr uint32_t kInitVal = 0x43b0d7e5;
221 constexpr uint32_t kHashMul = 0x931e8875;
222 constexpr uint32_t kMixMulL = 0xca01f9dd;
223 constexpr uint32_t kMixMulR = 0x4973f715;
224 constexpr uint32_t kShiftSize = sizeof(uint32_t) * 8 / 2;
225
226 uint32_t hash_const = kInitVal;
227 auto hash = [&](uint32_t value) {
228 value ^= hash_const;
229 hash_const *= kHashMul;
230 value *= hash_const;
231 value ^= value >> kShiftSize;
232 return value;
233 };
234
235 auto mix = [&](uint32_t x, uint32_t y) {
236 uint32_t result = kMixMulL * x - kMixMulR * y;
237 result ^= result >> kShiftSize;
238 return result;
239 };
240
241 for (const auto& seq_val : sequence) {
242 for (auto& elem : seed_material) {
243 elem = mix(elem, hash(seq_val));
244 }
245 }
246 }
247
GetSaltMaterial()248 absl::optional<uint32_t> GetSaltMaterial() {
249 // Salt must be common for all generators within the same process so read it
250 // only once and store in static variable.
251 static const auto salt_material = []() -> absl::optional<uint32_t> {
252 uint32_t salt_value = 0;
253
254 if (random_internal::ReadSeedMaterialFromOSEntropy(
255 MakeSpan(&salt_value, 1))) {
256 return salt_value;
257 }
258
259 return absl::nullopt;
260 }();
261
262 return salt_material;
263 }
264
265 } // namespace random_internal
266 ABSL_NAMESPACE_END
267 } // namespace absl
268