// Copyright 2017 The Abseil Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "absl/random/internal/seed_material.h" #include <fcntl.h> #ifndef _WIN32 #include <unistd.h> #else #include <io.h> #endif #include <algorithm> #include <cerrno> #include <cstdint> #include <cstdlib> #include <cstring> #include "absl/base/internal/raw_logging.h" #include "absl/strings/ascii.h" #include "absl/strings/escaping.h" #include "absl/strings/string_view.h" #include "absl/strings/strip.h" #if defined(__native_client__) #include <nacl/nacl_random.h> #define ABSL_RANDOM_USE_NACL_SECURE_RANDOM 1 #elif defined(_WIN32) #include <windows.h> #define ABSL_RANDOM_USE_BCRYPT 1 #pragma comment(lib, "bcrypt.lib") #elif defined(__Fuchsia__) #include <zircon/syscalls.h> #endif #if defined(ABSL_RANDOM_USE_BCRYPT) #include <bcrypt.h> #ifndef BCRYPT_SUCCESS #define BCRYPT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0) #endif // Also link bcrypt; this can be done via linker options or: // #pragma comment(lib, "bcrypt.lib") #endif namespace absl { ABSL_NAMESPACE_BEGIN namespace random_internal { namespace { // Read OS Entropy for random number seeds. // TODO(absl-team): Possibly place a cap on how much entropy may be read at a // time. #if defined(ABSL_RANDOM_USE_BCRYPT) // On Windows potentially use the BCRYPT CNG API to read available entropy. bool ReadSeedMaterialFromOSEntropyImpl(absl::Span<uint32_t> values) { BCRYPT_ALG_HANDLE hProvider; NTSTATUS ret; ret = BCryptOpenAlgorithmProvider(&hProvider, BCRYPT_RNG_ALGORITHM, MS_PRIMITIVE_PROVIDER, 0); if (!(BCRYPT_SUCCESS(ret))) { ABSL_RAW_LOG(ERROR, "Failed to open crypto provider."); return false; } ret = BCryptGenRandom( hProvider, // provider reinterpret_cast<UCHAR*>(values.data()), // buffer static_cast<ULONG>(sizeof(uint32_t) * values.size()), // bytes 0); // flags BCryptCloseAlgorithmProvider(hProvider, 0); return BCRYPT_SUCCESS(ret); } #elif defined(ABSL_RANDOM_USE_NACL_SECURE_RANDOM) // On NaCL use nacl_secure_random to acquire bytes. bool ReadSeedMaterialFromOSEntropyImpl(absl::Span<uint32_t> values) { auto buffer = reinterpret_cast<uint8_t*>(values.data()); size_t buffer_size = sizeof(uint32_t) * values.size(); uint8_t* output_ptr = buffer; while (buffer_size > 0) { size_t nread = 0; const int error = nacl_secure_random(output_ptr, buffer_size, &nread); if (error != 0 || nread > buffer_size) { ABSL_RAW_LOG(ERROR, "Failed to read secure_random seed data: %d", error); return false; } output_ptr += nread; buffer_size -= nread; } return true; } #elif defined(__Fuchsia__) bool ReadSeedMaterialFromOSEntropyImpl(absl::Span<uint32_t> values) { auto buffer = reinterpret_cast<uint8_t*>(values.data()); size_t buffer_size = sizeof(uint32_t) * values.size(); zx_cprng_draw(buffer, buffer_size); return true; } #else // On *nix, read entropy from /dev/urandom. bool ReadSeedMaterialFromOSEntropyImpl(absl::Span<uint32_t> values) { const char kEntropyFile[] = "/dev/urandom"; auto buffer = reinterpret_cast<uint8_t*>(values.data()); size_t buffer_size = sizeof(uint32_t) * values.size(); int dev_urandom = open(kEntropyFile, O_RDONLY); bool success = (-1 != dev_urandom); if (!success) { return false; } while (success && buffer_size > 0) { int bytes_read = read(dev_urandom, buffer, buffer_size); int read_error = errno; success = (bytes_read > 0); if (success) { buffer += bytes_read; buffer_size -= bytes_read; } else if (bytes_read == -1 && read_error == EINTR) { success = true; // Need to try again. } } close(dev_urandom); return success; } #endif } // namespace bool ReadSeedMaterialFromOSEntropy(absl::Span<uint32_t> values) { assert(values.data() != nullptr); if (values.data() == nullptr) { return false; } if (values.empty()) { return true; } return ReadSeedMaterialFromOSEntropyImpl(values); } void MixIntoSeedMaterial(absl::Span<const uint32_t> sequence, absl::Span<uint32_t> seed_material) { // Algorithm is based on code available at // https://gist.github.com/imneme/540829265469e673d045 constexpr uint32_t kInitVal = 0x43b0d7e5; constexpr uint32_t kHashMul = 0x931e8875; constexpr uint32_t kMixMulL = 0xca01f9dd; constexpr uint32_t kMixMulR = 0x4973f715; constexpr uint32_t kShiftSize = sizeof(uint32_t) * 8 / 2; uint32_t hash_const = kInitVal; auto hash = [&](uint32_t value) { value ^= hash_const; hash_const *= kHashMul; value *= hash_const; value ^= value >> kShiftSize; return value; }; auto mix = [&](uint32_t x, uint32_t y) { uint32_t result = kMixMulL * x - kMixMulR * y; result ^= result >> kShiftSize; return result; }; for (const auto& seq_val : sequence) { for (auto& elem : seed_material) { elem = mix(elem, hash(seq_val)); } } } absl::optional<uint32_t> GetSaltMaterial() { // Salt must be common for all generators within the same process so read it // only once and store in static variable. static const auto salt_material = []() -> absl::optional<uint32_t> { uint32_t salt_value = 0; if (random_internal::ReadSeedMaterialFromOSEntropy( MakeSpan(&salt_value, 1))) { return salt_value; } return absl::nullopt; }(); return salt_material; } } // namespace random_internal ABSL_NAMESPACE_END } // namespace absl