• 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/internal/raw_logging.h"
32 #include "absl/strings/ascii.h"
33 #include "absl/strings/escaping.h"
34 #include "absl/strings/string_view.h"
35 #include "absl/strings/strip.h"
36 
37 #if defined(__native_client__)
38 
39 #include <nacl/nacl_random.h>
40 #define ABSL_RANDOM_USE_NACL_SECURE_RANDOM 1
41 
42 #elif defined(_WIN32)
43 
44 #include <windows.h>
45 #define ABSL_RANDOM_USE_BCRYPT 1
46 #pragma comment(lib, "bcrypt.lib")
47 
48 #elif defined(__Fuchsia__)
49 #include <zircon/syscalls.h>
50 
51 #endif
52 
53 #if defined(ABSL_RANDOM_USE_BCRYPT)
54 #include <bcrypt.h>
55 
56 #ifndef BCRYPT_SUCCESS
57 #define BCRYPT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
58 #endif
59 // Also link bcrypt; this can be done via linker options or:
60 // #pragma comment(lib, "bcrypt.lib")
61 #endif
62 
63 namespace absl {
64 ABSL_NAMESPACE_BEGIN
65 namespace random_internal {
66 namespace {
67 
68 // Read OS Entropy for random number seeds.
69 // TODO(absl-team): Possibly place a cap on how much entropy may be read at a
70 // time.
71 
72 #if defined(ABSL_RANDOM_USE_BCRYPT)
73 
74 // On Windows potentially use the BCRYPT CNG API to read available entropy.
ReadSeedMaterialFromOSEntropyImpl(absl::Span<uint32_t> values)75 bool ReadSeedMaterialFromOSEntropyImpl(absl::Span<uint32_t> values) {
76   BCRYPT_ALG_HANDLE hProvider;
77   NTSTATUS ret;
78   ret = BCryptOpenAlgorithmProvider(&hProvider, BCRYPT_RNG_ALGORITHM,
79                                     MS_PRIMITIVE_PROVIDER, 0);
80   if (!(BCRYPT_SUCCESS(ret))) {
81     ABSL_RAW_LOG(ERROR, "Failed to open crypto provider.");
82     return false;
83   }
84   ret = BCryptGenRandom(
85       hProvider,                                             // provider
86       reinterpret_cast<UCHAR*>(values.data()),               // buffer
87       static_cast<ULONG>(sizeof(uint32_t) * values.size()),  // bytes
88       0);                                                    // flags
89   BCryptCloseAlgorithmProvider(hProvider, 0);
90   return BCRYPT_SUCCESS(ret);
91 }
92 
93 #elif defined(ABSL_RANDOM_USE_NACL_SECURE_RANDOM)
94 
95 // On NaCL use nacl_secure_random to acquire bytes.
96 bool ReadSeedMaterialFromOSEntropyImpl(absl::Span<uint32_t> values) {
97   auto buffer = reinterpret_cast<uint8_t*>(values.data());
98   size_t buffer_size = sizeof(uint32_t) * values.size();
99 
100   uint8_t* output_ptr = buffer;
101   while (buffer_size > 0) {
102     size_t nread = 0;
103     const int error = nacl_secure_random(output_ptr, buffer_size, &nread);
104     if (error != 0 || nread > buffer_size) {
105       ABSL_RAW_LOG(ERROR, "Failed to read secure_random seed data: %d", error);
106       return false;
107     }
108     output_ptr += nread;
109     buffer_size -= nread;
110   }
111   return true;
112 }
113 
114 #elif defined(__Fuchsia__)
115 
116 bool ReadSeedMaterialFromOSEntropyImpl(absl::Span<uint32_t> values) {
117   auto buffer = reinterpret_cast<uint8_t*>(values.data());
118   size_t buffer_size = sizeof(uint32_t) * values.size();
119   zx_cprng_draw(buffer, buffer_size);
120   return true;
121 }
122 
123 #else
124 
125 // On *nix, read entropy from /dev/urandom.
126 bool ReadSeedMaterialFromOSEntropyImpl(absl::Span<uint32_t> values) {
127   const char kEntropyFile[] = "/dev/urandom";
128 
129   auto buffer = reinterpret_cast<uint8_t*>(values.data());
130   size_t buffer_size = sizeof(uint32_t) * values.size();
131 
132   int dev_urandom = open(kEntropyFile, O_RDONLY);
133   bool success = (-1 != dev_urandom);
134   if (!success) {
135     return false;
136   }
137 
138   while (success && buffer_size > 0) {
139     int bytes_read = read(dev_urandom, buffer, buffer_size);
140     int read_error = errno;
141     success = (bytes_read > 0);
142     if (success) {
143       buffer += bytes_read;
144       buffer_size -= bytes_read;
145     } else if (bytes_read == -1 && read_error == EINTR) {
146       success = true;  // Need to try again.
147     }
148   }
149   close(dev_urandom);
150   return success;
151 }
152 
153 #endif
154 
155 }  // namespace
156 
ReadSeedMaterialFromOSEntropy(absl::Span<uint32_t> values)157 bool ReadSeedMaterialFromOSEntropy(absl::Span<uint32_t> values) {
158   assert(values.data() != nullptr);
159   if (values.data() == nullptr) {
160     return false;
161   }
162   if (values.empty()) {
163     return true;
164   }
165   return ReadSeedMaterialFromOSEntropyImpl(values);
166 }
167 
MixIntoSeedMaterial(absl::Span<const uint32_t> sequence,absl::Span<uint32_t> seed_material)168 void MixIntoSeedMaterial(absl::Span<const uint32_t> sequence,
169                          absl::Span<uint32_t> seed_material) {
170   // Algorithm is based on code available at
171   // https://gist.github.com/imneme/540829265469e673d045
172   constexpr uint32_t kInitVal = 0x43b0d7e5;
173   constexpr uint32_t kHashMul = 0x931e8875;
174   constexpr uint32_t kMixMulL = 0xca01f9dd;
175   constexpr uint32_t kMixMulR = 0x4973f715;
176   constexpr uint32_t kShiftSize = sizeof(uint32_t) * 8 / 2;
177 
178   uint32_t hash_const = kInitVal;
179   auto hash = [&](uint32_t value) {
180     value ^= hash_const;
181     hash_const *= kHashMul;
182     value *= hash_const;
183     value ^= value >> kShiftSize;
184     return value;
185   };
186 
187   auto mix = [&](uint32_t x, uint32_t y) {
188     uint32_t result = kMixMulL * x - kMixMulR * y;
189     result ^= result >> kShiftSize;
190     return result;
191   };
192 
193   for (const auto& seq_val : sequence) {
194     for (auto& elem : seed_material) {
195       elem = mix(elem, hash(seq_val));
196     }
197   }
198 }
199 
GetSaltMaterial()200 absl::optional<uint32_t> GetSaltMaterial() {
201   // Salt must be common for all generators within the same process so read it
202   // only once and store in static variable.
203   static const auto salt_material = []() -> absl::optional<uint32_t> {
204     uint32_t salt_value = 0;
205 
206     if (random_internal::ReadSeedMaterialFromOSEntropy(
207             MakeSpan(&salt_value, 1))) {
208       return salt_value;
209     }
210 
211     return absl::nullopt;
212   }();
213 
214   return salt_material;
215 }
216 
217 }  // namespace random_internal
218 ABSL_NAMESPACE_END
219 }  // namespace absl
220