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