blob: e24a149fd84d78b5c9816bb0ee55f26c61305c62 [file] [log] [blame]
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/attribution_reporting/attribution_insecure_random_generator.h"
#include <stdint.h>
#include <limits>
#include "base/bit_cast.h"
#include "base/check_op.h"
#include "base/memory/raw_ref.h"
#include "base/ranges/algorithm.h"
#include "content/browser/attribution_reporting/attribution_report.h"
#include "third_party/abseil-cpp/absl/numeric/int128.h"
namespace content {
namespace {
// Copied from `v8::base::RandomNumberGenerator::XorShift128()`.
void XorShift128(uint64_t* state0, uint64_t* state1) {
uint64_t s1 = *state0;
uint64_t s0 = *state1;
*state0 = s0;
s1 ^= s1 << 23;
s1 ^= s1 >> 17;
s1 ^= s0;
s1 ^= s0 >> 26;
*state1 = s1;
}
// Copied from `v8::base::RandomNumberGenerator::ToDouble()`.
double ToDouble(uint64_t state0) {
// Exponent for double values for [1.0 .. 2.0)
static const uint64_t kExponentBits = uint64_t{0x3FF0000000000000};
uint64_t random = (state0 >> 12) | kExponentBits;
return base::bit_cast<double>(random) - 1;
}
} // namespace
AttributionInsecureRandomGenerator::AttributionInsecureRandomGenerator(
absl::uint128 seed)
: state0_(absl::Uint128Low64(seed)), state1_(absl::Uint128High64(seed)) {}
// Adapted from `v8::base::RandomNumberGenerator::NextInt64()`.
uint64_t AttributionInsecureRandomGenerator::RandUint64() {
XorShift128(&state0_, &state1_);
return state0_ + state1_;
}
// Copied from `v8::base::RandomNumberGenerator::ToDouble()`.
double AttributionInsecureRandomGenerator::RandDouble() {
XorShift128(&state0_, &state1_);
double result = ToDouble(state0_);
DCHECK_GE(result, 0.0);
DCHECK_LT(result, 1.0);
return result;
}
// Adapted from `base::RandGenerator()`.
uint64_t AttributionInsecureRandomGenerator::RandGenerator(uint64_t range) {
DCHECK_GT(range, 0u);
// We must discard random results above this number, as they would
// make the random generator non-uniform (consider e.g. if
// MAX_UINT64 was 7 and |range| was 5, then a result of 1 would be twice
// as likely as a result of 3 or 4).
uint64_t max_acceptable_value =
(std::numeric_limits<uint64_t>::max() / range) * range - 1;
uint64_t value;
do {
value = RandUint64();
} while (value > max_acceptable_value);
return value % range;
}
// Adapted from `base::RandInt()`.
int AttributionInsecureRandomGenerator::RandInt(int min, int max) {
DCHECK_LE(min, max);
uint64_t range = static_cast<uint64_t>(max) - min + 1;
// |range| is at most UINT_MAX + 1, so the result of `RandGenerator(range)`
// is at most UINT_MAX. Hence it's safe to cast it from uint64_t to int64_t.
int result =
static_cast<int>(min + static_cast<int64_t>(RandGenerator(range)));
DCHECK_GE(result, min);
DCHECK_LE(result, max);
return result;
}
// Adapted from `base::RandomBitGenerator` and `base::RandomShuffle()`.
void AttributionInsecureRandomGenerator::RandomShuffle(
std::vector<AttributionReport>& reports) {
struct RandomBitGenerator {
using result_type = uint64_t;
static constexpr result_type min() { return 0; }
static constexpr result_type max() {
return std::numeric_limits<uint64_t>::max();
}
result_type operator()() const { return gen->RandUint64(); }
const raw_ref<AttributionInsecureRandomGenerator> gen;
};
base::ranges::shuffle(reports, RandomBitGenerator{.gen = raw_ref(*this)});
}
} // namespace content