chromium/components/variations/variations_seed_store.cc

// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#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  // BUILDFLAG(IS_ANDROID)

#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  // BUILDFLAG(IS_CHROMEOS_ASH)

#if BUILDFLAG(IS_IOS)
#include "components/variations/metrics.h"
#endif  // BUILDFLAG(IS_IOS)

namespace variations {
namespace {

// The ECDSA public key of the variations server for verifying variations seed
// signatures.
const uint8_t kPublicKey[] =;

// A sentinel value that may be stored as the latest variations seed value in
// prefs to indicate that the latest seed is identical to the safe seed. Used to
// avoid duplicating storage space.
constexpr char kIdenticalToSafeSeedSentinel[] =;

#if BUILDFLAG(IS_CHROMEOS_ASH)
// Number of attempts to send the safe seed from Chrome to CrOS platforms before
// giving up.
constexpr int kSendPlatformSafeSeedMaxAttempts = 2;
#endif  // BUILDFLAG(IS_CHROMEOS_ASH)

// Returns true if |signature| is empty and if the command-line flag to accept
// empty seed signature is specified.
bool AcceptEmptySeedSignatureForTesting(const std::string& signature) {}

// Verifies a variations seed (the serialized proto bytes) with the specified
// base-64 encoded signature that was received from the server and returns the
// result. The signature is assumed to be an "ECDSA with SHA-256" signature
// (see kECDSAWithSHA256AlgorithmID in the .cc file). Returns the result of
// signature verification.
VerifySignatureResult VerifySeedSignature(
    const std::string& seed_bytes,
    const std::string& base64_seed_signature) {}

// Truncates a time to the start of the day in UTC. If given a time representing
// 2014-03-11 10:18:03.1 UTC, it will return a time representing
// 2014-03-11 00:00:00.0 UTC.
base::Time TruncateToUTCDay(base::Time time) {}

UpdateSeedDateResult GetSeedDateChangeState(base::Time server_seed_date,
                                            base::Time stored_seed_date) {}

// Remove gzip compression from |data|.
// Returns success or error, populating result on success.
StoreSeedResult Uncompress(const std::string& compressed, std::string* result) {}

}  // namespace

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() {}

// static
void VariationsSeedStore::RegisterPrefs(PrefRegistrySimple* registry) {}

// static
base::Time VariationsSeedStore::GetLastFetchTimeFromPrefService(
    PrefService* prefs) {}

// static
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;

// It is intentional that country-related prefs are retained for regular seeds
// and cleared for safe seeds.
//
// For regular seeds, the prefs are kept for two reasons. First, it's better to
// have some idea of a country recently associated with the device. Second, some
// past, country-gated launches started relying on the VariationsService-
// provided country when they retired server-side configs.
//
// The safe seed prefs are needed to correctly apply a safe seed, so if the safe
// seed is cleared, there's no reason to retain them as they may be incorrect
// for the next safe seed.
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()) {
    // Note: This is an expected case on non-first run starts.
    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,
                /*is_delta_compressed=*/false, initial_seed->is_gzip_compressed,
                std::move(done_callback),
                /*require_synchronous=*/true);
}
#endif  // BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS)

// static
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) {}

// static
VariationsSeedStore::SeedProcessingResult VariationsSeedStore::ProcessSeedData(
    bool signature_verification_enabled,
    SeedData seed_data) {}

// static
StoreSeedResult VariationsSeedStore::ValidateSeedBytes(
    const std::string& seed_bytes,
    const std::string& base64_seed_signature,
    SeedType seed_type,
    bool signature_verification_enabled,
    ValidatedSeed* result) {}

// static
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) {
  // Do not retry after two failed attempts.
  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  // BUILDFLAG(IS_CHROMEOS_ASH)

}  // namespace variations