blob: 39238e74020a1731e24c1a949759910e9aed491b [file] [log] [blame] [edit]
// Copyright 2017 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
#include "core/fxcrt/fx_random.h"
#include <stdint.h>
#include <array>
#include "build/build_config.h"
#include "core/fxcrt/fx_string.h"
#include "core/fxcrt/fx_system.h"
#if BUILDFLAG(IS_WIN)
#include <wincrypt.h>
#else
#include <sys/time.h>
#include <unistd.h>
#endif
namespace {
constexpr uint32_t kTwistOffset = 456;
constexpr uint32_t kUpperMask = 0x80000000;
constexpr uint32_t kLowerMask = 0x7fffffff;
#if BUILDFLAG(IS_WIN)
bool GenerateSeedFromCryptoRandom(uint32_t* seed) {
HCRYPTPROV hCP = 0;
if (!::CryptAcquireContext(&hCP, nullptr, nullptr, PROV_RSA_FULL, 0) ||
!hCP) {
return false;
}
::CryptGenRandom(hCP, sizeof(uint32_t), reinterpret_cast<uint8_t*>(seed));
::CryptReleaseContext(hCP, 0);
return true;
}
#endif
uint32_t GenerateSeedFromEnvironment() {
char stack_marker;
uintptr_t stack_address = reinterpret_cast<uintptr_t>(&stack_marker);
uint32_t seed = ~static_cast<uint32_t>(stack_address >> 3);
#if BUILDFLAG(IS_WIN)
SYSTEMTIME system_time;
GetSystemTime(&system_time);
seed ^= static_cast<uint32_t>(system_time.wSecond) * 1000000;
seed ^= static_cast<uint32_t>(system_time.wMilliseconds) * 1000;
seed ^= GetCurrentProcessId();
#else
struct timeval current_time;
gettimeofday(&current_time, nullptr);
seed ^= static_cast<uint32_t>(current_time.tv_sec) * 1000000;
seed ^= static_cast<uint32_t>(current_time.tv_usec);
seed ^= static_cast<uint32_t>(getpid());
#endif
return seed;
}
uint32_t GetNextGlobalSeed() {
static uint32_t global_seed = []() -> uint32_t {
uint32_t initial_seed;
#if BUILDFLAG(IS_WIN)
if (!GenerateSeedFromCryptoRandom(&initial_seed)) {
initial_seed = GenerateSeedFromEnvironment();
}
#else
initial_seed = GenerateSeedFromEnvironment();
#endif
return initial_seed;
}();
return ++global_seed;
}
std::array<uint32_t, FX_Random::kStateSize> InitState(uint32_t seed) {
std::array<uint32_t, FX_Random::kStateSize> state;
state[0] = seed;
for (uint32_t i = 1; i < FX_Random::kStateSize; i++) {
const uint32_t prev = state[i - 1];
state[i] = (1812433253UL * (prev ^ (prev >> 30)) + i);
}
return state;
}
} // namespace
FX_Random::FX_Random(uint32_t seed)
: next_index_(kStateSize), state_(InitState(seed)) {}
FX_Random::~FX_Random() = default;
// static
void FX_Random::Fill(pdfium::span<uint32_t> buffer) {
FX_Random next_generator(GetNextGlobalSeed());
for (uint32_t& val : buffer) {
val = next_generator.Generate();
}
}
uint32_t FX_Random::Generate() {
uint32_t result;
if (next_index_ >= kStateSize) {
static constexpr std::array<uint32_t, 2> kMatrixATable = {{0, 0x9908b0df}};
uint32_t i;
for (i = 0; i < kStateSize - kTwistOffset; ++i) {
result = (state_[i] & kUpperMask) | (state_[i + 1] & kLowerMask);
state_[i] =
state_[i + kTwistOffset] ^ (result >> 1) ^ kMatrixATable[result & 1];
}
for (; i < kStateSize - 1; ++i) {
result = (state_[i] & kUpperMask) | (state_[i + 1] & kLowerMask);
// `kTwistOffset - kStateSize` underflows, but this is safe because
// unsigned underflow is well-defined.
state_[i] = state_[i + (kTwistOffset - kStateSize)] ^ (result >> 1) ^
kMatrixATable[result & 1];
}
result = (state_[kStateSize - 1] & kUpperMask) | (state_[0] & kLowerMask);
state_[kStateSize - 1] =
state_[kTwistOffset - 1] ^ (result >> 1) ^ kMatrixATable[result & 1];
next_index_ = 0;
}
result = state_[next_index_++];
result ^= (result >> 11);
result ^= (result << 7) & 0x9d2c5680UL;
result ^= (result << 15) & 0xefc60000UL;
result ^= (result >> 18);
return result;
}