• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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