#include "components/variations/variations_seed_store.h"
#include <stdint.h>
#include <utility>
#include "base/base64.h"
#include "base/build_time.h"
#include "base/command_line.h"
#include "base/functional/callback_helpers.h"
#include "base/logging.h"
#include "base/metrics/histogram_macros.h"
#include "base/numerics/safe_math.h"
#include "base/strings/string_number_conversions.h"
#include "base/task/task_runner.h"
#include "base/task/thread_pool.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/pref_service.h"
#include "components/variations/client_filterable_state.h"
#include "components/variations/pref_names.h"
#include "components/variations/proto/variations_seed.pb.h"
#include "components/variations/variations_safe_seed_store_local_state.h"
#include "components/variations/variations_switches.h"
#include "components/version_info/version_info.h"
#include "crypto/signature_verifier.h"
#include "third_party/protobuf/src/google/protobuf/io/coded_stream.h"
#include "third_party/zlib/google/compression_utils.h"
#if BUILDFLAG(IS_ANDROID)
#include "components/variations/android/variations_seed_bridge.h"
#include "components/variations/metrics.h"
#endif
#if BUILDFLAG(IS_CHROMEOS_ASH)
#include "base/functional/callback.h"
#include "chromeos/ash/components/dbus/featured/featured.pb.h"
#include "chromeos/ash/components/dbus/featured/featured_client.h"
#endif
#if BUILDFLAG(IS_IOS)
#include "components/variations/metrics.h"
#endif
namespace variations {
namespace {
const uint8_t kPublicKey[] = …;
constexpr char kIdenticalToSafeSeedSentinel[] = …;
#if BUILDFLAG(IS_CHROMEOS_ASH)
constexpr int kSendPlatformSafeSeedMaxAttempts = 2;
#endif
bool AcceptEmptySeedSignatureForTesting(const std::string& signature) { … }
VerifySignatureResult VerifySeedSignature(
const std::string& seed_bytes,
const std::string& base64_seed_signature) { … }
base::Time TruncateToUTCDay(base::Time time) { … }
UpdateSeedDateResult GetSeedDateChangeState(base::Time server_seed_date,
base::Time stored_seed_date) { … }
StoreSeedResult Uncompress(const std::string& compressed, std::string* result) { … }
}
VariationsSeedStore::VariationsSeedStore(
PrefService* local_state,
std::unique_ptr<VariationsSafeSeedStore> safe_seed_store)
: … { … }
VariationsSeedStore::VariationsSeedStore(
PrefService* local_state,
std::unique_ptr<SeedResponse> initial_seed,
bool signature_verification_enabled,
std::unique_ptr<VariationsSafeSeedStore> safe_seed_store,
bool use_first_run_prefs)
: … { … }
VariationsSeedStore::~VariationsSeedStore() = default;
bool VariationsSeedStore::LoadSeed(VariationsSeed* seed,
std::string* seed_data,
std::string* base64_seed_signature) { … }
void VariationsSeedStore::StoreSeedData(
std::string data,
std::string base64_seed_signature,
std::string country_code,
base::Time date_fetched,
bool is_delta_compressed,
bool is_gzip_compressed,
base::OnceCallback<void(bool, VariationsSeed)> done_callback,
bool require_synchronous) { … }
bool VariationsSeedStore::LoadSafeSeed(VariationsSeed* seed,
ClientFilterableState* client_state) { … }
bool VariationsSeedStore::StoreSafeSeed(
const std::string& seed_data,
const std::string& base64_seed_signature,
int seed_milestone,
const ClientFilterableState& client_state,
base::Time seed_fetch_time) { … }
base::Time VariationsSeedStore::GetLastFetchTime() const { … }
base::Time VariationsSeedStore::GetSafeSeedFetchTime() const { … }
int VariationsSeedStore::GetLatestMilestone() const { … }
int VariationsSeedStore::GetSafeSeedMilestone() const { … }
base::Time VariationsSeedStore::GetLatestTimeForStudyDateChecks() const { … }
base::Time VariationsSeedStore::GetSafeSeedTimeForStudyDateChecks() const { … }
base::Time VariationsSeedStore::GetTimeForStudyDateChecks(bool is_safe_seed) { … }
void VariationsSeedStore::RecordLastFetchTime(base::Time fetch_time) { … }
void VariationsSeedStore::UpdateSeedDateAndLogDayChange(
base::Time server_date_fetched) { … }
const std::string& VariationsSeedStore::GetLatestSerialNumber() { … }
void VariationsSeedStore::RegisterPrefs(PrefRegistrySimple* registry) { … }
base::Time VariationsSeedStore::GetLastFetchTimeFromPrefService(
PrefService* prefs) { … }
VerifySignatureResult VariationsSeedStore::VerifySeedSignatureForTesting(
const std::string& seed_bytes,
const std::string& base64_seed_signature) { … }
VariationsSeedStore::SeedData::SeedData() = default;
VariationsSeedStore::SeedData::~SeedData() = default;
VariationsSeedStore::SeedData::SeedData(VariationsSeedStore::SeedData&& other) =
default;
VariationsSeedStore::SeedData& VariationsSeedStore::SeedData::operator=(
VariationsSeedStore::SeedData&& other) = default;
VariationsSeedStore::SeedProcessingResult::SeedProcessingResult(
SeedData seed_data,
StoreSeedResult result)
: … { … }
VariationsSeedStore::SeedProcessingResult::~SeedProcessingResult() = default;
VariationsSeedStore::SeedProcessingResult::SeedProcessingResult(
VariationsSeedStore::SeedProcessingResult&& other) = default;
VariationsSeedStore::SeedProcessingResult&
VariationsSeedStore::SeedProcessingResult::operator=(
VariationsSeedStore::SeedProcessingResult&& other) = default;
void VariationsSeedStore::ClearPrefs(SeedType seed_type) { … }
#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS)
void VariationsSeedStore::ImportInitialSeed(
std::unique_ptr<SeedResponse> initial_seed) {
if (initial_seed->data.empty()) {
RecordFirstRunSeedImportResult(
FirstRunSeedImportResult::FAIL_NO_FIRST_RUN_SEED);
return;
}
if (initial_seed->date.is_null()) {
RecordFirstRunSeedImportResult(
FirstRunSeedImportResult::FAIL_INVALID_RESPONSE_DATE);
LOG(WARNING) << "Missing response date";
return;
}
auto done_callback =
base::BindOnce([](bool store_success, VariationsSeed seed) {
if (store_success) {
RecordFirstRunSeedImportResult(FirstRunSeedImportResult::SUCCESS);
} else {
RecordFirstRunSeedImportResult(
FirstRunSeedImportResult::FAIL_STORE_FAILED);
LOG(WARNING) << "First run variations seed is invalid.";
}
});
StoreSeedData(std::move(initial_seed->data),
std::move(initial_seed->signature),
std::move(initial_seed->country), initial_seed->date,
false, initial_seed->is_gzip_compressed,
std::move(done_callback),
true);
}
#endif
std::optional<std::string> VariationsSeedStore::SeedBytesToCompressedBase64Seed(
const std::string& seed_bytes) { … }
LoadSeedResult VariationsSeedStore::VerifyAndParseSeed(
VariationsSeed* seed,
const std::string& seed_data,
const std::string& base64_seed_signature,
std::optional<VerifySignatureResult>* verify_signature_result) { … }
LoadSeedResult VariationsSeedStore::LoadSeedImpl(
SeedType seed_type,
VariationsSeed* seed,
std::string* seed_data,
std::string* base64_seed_signature) { … }
LoadSeedResult VariationsSeedStore::ReadSeedData(SeedType seed_type,
std::string* seed_data) { … }
StoreSeedResult VariationsSeedStore::ResolveDelta(
const std::string& delta_bytes,
std::string* seed_bytes) { … }
StoreSeedResult VariationsSeedStore::ResolveInstanceManipulations(
const std::string& data,
const InstanceManipulations& im,
std::string* seed_bytes) { … }
void VariationsSeedStore::OnSeedDataProcessed(
base::OnceCallback<void(bool, VariationsSeed)> done_callback,
SeedProcessingResult result) { … }
void VariationsSeedStore::StoreValidatedSeed(const ValidatedSeed& seed,
const std::string& country_code,
base::Time date_fetched) { … }
void VariationsSeedStore::StoreValidatedSafeSeed(
const ValidatedSeed& seed,
int seed_milestone,
const ClientFilterableState& client_state,
base::Time seed_fetch_time) { … }
VariationsSeedStore::SeedProcessingResult VariationsSeedStore::ProcessSeedData(
bool signature_verification_enabled,
SeedData seed_data) { … }
StoreSeedResult VariationsSeedStore::ValidateSeedBytes(
const std::string& seed_bytes,
const std::string& base64_seed_signature,
SeedType seed_type,
bool signature_verification_enabled,
ValidatedSeed* result) { … }
bool VariationsSeedStore::ApplyDeltaPatch(const std::string& existing_data,
const std::string& patch,
std::string* output) { … }
#if BUILDFLAG(IS_CHROMEOS_ASH)
featured::SeedDetails VariationsSeedStore::GetSafeSeedStateForPlatform(
const ValidatedSeed& seed,
const int seed_milestone,
const ClientFilterableState& client_state,
const base::Time seed_fetch_time) {
featured::SeedDetails safe_seed;
safe_seed.set_b64_compressed_data(seed.base64_seed_data);
safe_seed.set_locale(client_state.locale);
safe_seed.set_milestone(seed_milestone);
safe_seed.set_permanent_consistency_country(
client_state.permanent_consistency_country);
safe_seed.set_session_consistency_country(
client_state.session_consistency_country);
safe_seed.set_signature(seed.base64_seed_signature);
safe_seed.set_date(
client_state.reference_date.ToDeltaSinceWindowsEpoch().InMilliseconds());
safe_seed.set_fetch_time(
seed_fetch_time.ToDeltaSinceWindowsEpoch().InMilliseconds());
return safe_seed;
}
void VariationsSeedStore::MaybeRetrySendSafeSeed(
const featured::SeedDetails& safe_seed,
bool success) {
if (!success &&
send_seed_to_platform_attempts_ < kSendPlatformSafeSeedMaxAttempts) {
SendSafeSeedToPlatform(safe_seed);
}
}
void VariationsSeedStore::SendSafeSeedToPlatform(
const featured::SeedDetails& safe_seed) {
send_seed_to_platform_attempts_++;
ash::featured::FeaturedClient* client = ash::featured::FeaturedClient::Get();
if (client) {
client->HandleSeedFetched(
safe_seed, base::BindOnce(&VariationsSeedStore::MaybeRetrySendSafeSeed,
weak_ptr_factory_.GetWeakPtr(), safe_seed));
}
}
#endif
}