blob: ae5cdbfb19f52274aea31760fd1aae3ae1669d68 [file] [log] [blame]
// Copyright 2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/rand_util.h"
#include <fcntl.h>
#include <string.h>
#include <algorithm>
#include <cmath>
#include <limits>
#include "base/files/file_util.h"
#include "base/logging.h"
#include "build/build_config.h"
#if defined(OS_FUCHSIA)
#include <zircon/syscalls.h>
#include "base/fuchsia/fuchsia_logging.h"
#elif defined(OS_POSIX)
#include "base/posix/eintr_wrapper.h"
#elif defined(OS_WIN)
#include <windows.h>
// #define needed to link in RtlGenRandom(), a.k.a. SystemFunction036. See the
// "Community Additions" comment on MSDN here:
// http://msdn.microsoft.com/en-us/library/windows/desktop/aa387694.aspx
#define SystemFunction036 NTAPI SystemFunction036
#include <NTSecAPI.h>
#undef SystemFunction036
#endif // OS_WIN
#if defined(OS_POSIX) && !defined(OS_FUCHSIA)
namespace {
int GetUrandomFDInternal() {
int fd = HANDLE_EINTR(open("/dev/urandom", O_RDONLY | O_NOCTTY | O_CLOEXEC));
PCHECK(fd >= 0) << "open /dev/urandom";
return fd;
}
int GetUrandomFD() {
static int fd = GetUrandomFDInternal();
return fd;
}
} // namespace
#endif // OS_POSIX && !OS_FUCHSIA
namespace base {
uint64_t RandUint64() {
uint64_t number;
RandBytes(&number, sizeof(number));
return number;
}
int RandInt(int min, int max) {
DCHECK_LE(min, max);
uint64_t range = static_cast<uint64_t>(max) - min + 1;
int result = min + static_cast<int>(base::RandGenerator(range));
DCHECK_GE(result, min);
DCHECK_LE(result, max);
return result;
}
uint64_t RandGenerator(uint64_t range) {
DCHECK_GT(range, 0u);
uint64_t max_acceptable_value =
(std::numeric_limits<uint64_t>::max() / range) * range - 1;
uint64_t value;
do {
value = base::RandUint64();
} while (value > max_acceptable_value);
return value % range;
}
double RandDouble() {
static_assert(std::numeric_limits<double>::radix == 2,
"otherwise use scalbn");
static_assert(std::numeric_limits<double>::digits <
std::numeric_limits<uint64_t>::digits,
"integer type must be wider than floating-point mantissa");
uint64_t random_bits = RandUint64();
const int kMantissaBits = std::numeric_limits<double>::digits;
uint64_t mantissa = random_bits & ((UINT64_C(1) << kMantissaBits) - 1);
double result = std::ldexp(mantissa, -1 * kMantissaBits);
DCHECK_GE(result, 0.0);
DCHECK_LT(result, 1.0);
return result;
}
void RandBytes(void* output, size_t output_length) {
if (output_length == 0) {
return;
}
#if defined(OS_FUCHSIA)
zx_cprng_draw(output, output_length);
#elif defined(OS_POSIX)
int fd = GetUrandomFD();
bool success = ReadFromFD(fd, static_cast<char*>(output), output_length);
CHECK(success);
#elif defined(OS_WIN)
char* output_ptr = static_cast<char*>(output);
while (output_length > 0) {
const ULONG output_bytes_this_pass = static_cast<ULONG>(std::min(
output_length, static_cast<size_t>(std::numeric_limits<ULONG>::max())));
const bool success =
RtlGenRandom(output_ptr, output_bytes_this_pass) != FALSE;
CHECK(success);
output_length -= output_bytes_this_pass;
output_ptr += output_bytes_this_pass;
}
#endif
}
std::string RandBytesAsString(size_t length) {
if (length == 0) {
return std::string();
}
std::string result(length, std::string::value_type());
RandBytes(&result[0], length);
return result;
}
} // namespace base