// Copyright (c) 2012 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 "components/variations/entropy_provider.h" #include #include #include #include "base/logging.h" #include "base/rand_util.h" #include "base/sha1.h" #include "base/sys_byteorder.h" #include "components/variations/metrics_util.h" namespace metrics { namespace internal { SeededRandGenerator::SeededRandGenerator(uint32_t seed) { mersenne_twister_.init_genrand(seed); } SeededRandGenerator::~SeededRandGenerator() { } uint32_t SeededRandGenerator::operator()(uint32_t range) { // Based on base::RandGenerator(). 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). uint32_t max_acceptable_value = (std::numeric_limits::max() / range) * range - 1; uint32_t value; do { value = mersenne_twister_.genrand_int32(); } while (value > max_acceptable_value); return value % range; } void PermuteMappingUsingRandomizationSeed(uint32_t randomization_seed, std::vector* mapping) { for (size_t i = 0; i < mapping->size(); ++i) (*mapping)[i] = static_cast(i); SeededRandGenerator generator(randomization_seed); // Do a deterministic random shuffle of the mapping using |generator|. // // Note: This logic is identical to the following call with libstdc++ and VS: // // std::random_shuffle(mapping->begin(), mapping->end(), generator); // // However, this is not guaranteed by the spec and some implementations (e.g. // libc++) use a different algorithm. To ensure results are consistent // regardless of the compiler toolchain used, use our own version. for (size_t i = 1; i < mapping->size(); ++i) { // Pick an element in mapping[:i+1] with which to exchange mapping[i]. size_t j = generator(i + 1); std::swap((*mapping)[i], (*mapping)[j]); } } } // namespace internal SHA1EntropyProvider::SHA1EntropyProvider(const std::string& entropy_source) : entropy_source_(entropy_source) { } SHA1EntropyProvider::~SHA1EntropyProvider() { } double SHA1EntropyProvider::GetEntropyForTrial( const std::string& trial_name, uint32_t randomization_seed) const { // Given enough input entropy, SHA-1 will produce a uniformly random spread // in its output space. In this case, the input entropy that is used is the // combination of the original |entropy_source_| and the |trial_name|. // // Note: If |entropy_source_| has very low entropy, such as 13 bits or less, // it has been observed that this method does not result in a uniform // distribution given the same |trial_name|. When using such a low entropy // source, PermutedEntropyProvider should be used instead. std::string input(entropy_source_ + trial_name); unsigned char sha1_hash[base::kSHA1Length]; base::SHA1HashBytes(reinterpret_cast(input.c_str()), input.size(), sha1_hash); uint64_t bits; static_assert(sizeof(bits) < sizeof(sha1_hash), "more data required"); memcpy(&bits, sha1_hash, sizeof(bits)); bits = base::ByteSwapToLE64(bits); return base::BitsToOpenEndedUnitInterval(bits); } PermutedEntropyProvider::PermutedEntropyProvider(uint16_t low_entropy_source, size_t low_entropy_source_max) : low_entropy_source_(low_entropy_source), low_entropy_source_max_(low_entropy_source_max) { DCHECK_LT(low_entropy_source, low_entropy_source_max); DCHECK_LE(low_entropy_source_max, std::numeric_limits::max()); } PermutedEntropyProvider::~PermutedEntropyProvider() { } double PermutedEntropyProvider::GetEntropyForTrial( const std::string& trial_name, uint32_t randomization_seed) const { if (randomization_seed == 0) randomization_seed = HashName(trial_name); return GetPermutedValue(randomization_seed) / static_cast(low_entropy_source_max_); } uint16_t PermutedEntropyProvider::GetPermutedValue( uint32_t randomization_seed) const { std::vector mapping(low_entropy_source_max_); internal::PermuteMappingUsingRandomizationSeed(randomization_seed, &mapping); return mapping[low_entropy_source_]; } } // namespace metrics