| Index: chrome/browser/metrics/variations/variations_seed_store.cc
|
| diff --git a/chrome/browser/metrics/variations/variations_seed_store.cc b/chrome/browser/metrics/variations/variations_seed_store.cc
|
| deleted file mode 100644
|
| index 262c6574a01cb4f866f98c096c5ba43e655738cd..0000000000000000000000000000000000000000
|
| --- a/chrome/browser/metrics/variations/variations_seed_store.cc
|
| +++ /dev/null
|
| @@ -1,348 +0,0 @@
|
| -// Copyright 2014 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 "chrome/browser/metrics/variations/variations_seed_store.h"
|
| -
|
| -#include "base/base64.h"
|
| -#include "base/metrics/histogram_macros.h"
|
| -#include "base/prefs/pref_registry_simple.h"
|
| -#include "base/prefs/pref_service.h"
|
| -#include "base/sha1.h"
|
| -#include "base/strings/string_number_conversions.h"
|
| -#include "components/metrics/compression_utils.h"
|
| -#include "components/variations/pref_names.h"
|
| -#include "components/variations/proto/variations_seed.pb.h"
|
| -#include "crypto/signature_verifier.h"
|
| -
|
| -namespace chrome_variations {
|
| -
|
| -namespace {
|
| -
|
| -// Signature verification is disabled on mobile platforms for now, since it
|
| -// adds about ~15ms to the startup time on mobile (vs. a couple ms on desktop).
|
| -bool SignatureVerificationEnabled() {
|
| -#if defined(OS_IOS) || defined(OS_ANDROID)
|
| - return false;
|
| -#else
|
| - return true;
|
| -#endif
|
| -}
|
| -
|
| -// This is the algorithm ID for ECDSA with SHA-256. Parameters are ABSENT.
|
| -// RFC 5758:
|
| -// ecdsa-with-SHA256 OBJECT IDENTIFIER ::= { iso(1) member-body(2)
|
| -// us(840) ansi-X9-62(10045) signatures(4) ecdsa-with-SHA2(3) 2 }
|
| -// ...
|
| -// When the ecdsa-with-SHA224, ecdsa-with-SHA256, ecdsa-with-SHA384, or
|
| -// ecdsa-with-SHA512 algorithm identifier appears in the algorithm field
|
| -// as an AlgorithmIdentifier, the encoding MUST omit the parameters
|
| -// field. That is, the AlgorithmIdentifier SHALL be a SEQUENCE of one
|
| -// component, the OID ecdsa-with-SHA224, ecdsa-with-SHA256, ecdsa-with-
|
| -// SHA384, or ecdsa-with-SHA512.
|
| -// See also RFC 5480, Appendix A.
|
| -const uint8 kECDSAWithSHA256AlgorithmID[] = {
|
| - 0x30, 0x0a,
|
| - 0x06, 0x08,
|
| - 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02,
|
| -};
|
| -
|
| -// The ECDSA public key of the variations server for verifying variations seed
|
| -// signatures.
|
| -const uint8_t kPublicKey[] = {
|
| - 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01,
|
| - 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00,
|
| - 0x04, 0x51, 0x7c, 0x31, 0x4b, 0x50, 0x42, 0xdd, 0x59, 0xda, 0x0b, 0xfa, 0x43,
|
| - 0x44, 0x33, 0x7c, 0x5f, 0xa1, 0x0b, 0xd5, 0x82, 0xf6, 0xac, 0x04, 0x19, 0x72,
|
| - 0x6c, 0x40, 0xd4, 0x3e, 0x56, 0xe2, 0xa0, 0x80, 0xa0, 0x41, 0xb3, 0x23, 0x7b,
|
| - 0x71, 0xc9, 0x80, 0x87, 0xde, 0x35, 0x0d, 0x25, 0x71, 0x09, 0x7f, 0xb4, 0x15,
|
| - 0x2b, 0xff, 0x82, 0x4d, 0xd3, 0xfe, 0xc5, 0xef, 0x20, 0xc6, 0xa3, 0x10, 0xbf,
|
| -};
|
| -
|
| -// Note: UMA histogram enum - don't re-order or remove entries.
|
| -enum VariationSeedEmptyState {
|
| - VARIATIONS_SEED_NOT_EMPTY,
|
| - VARIATIONS_SEED_EMPTY,
|
| - VARIATIONS_SEED_CORRUPT,
|
| - VARIATIONS_SEED_INVALID_SIGNATURE,
|
| - VARIATIONS_SEED_CORRUPT_BASE64,
|
| - VARIATIONS_SEED_CORRUPT_PROTOBUF,
|
| - VARIATIONS_SEED_CORRUPT_GZIP,
|
| - VARIATIONS_SEED_EMPTY_ENUM_SIZE,
|
| -};
|
| -
|
| -void RecordVariationSeedEmptyHistogram(VariationSeedEmptyState state) {
|
| - UMA_HISTOGRAM_ENUMERATION("Variations.SeedEmpty", state,
|
| - VARIATIONS_SEED_EMPTY_ENUM_SIZE);
|
| -}
|
| -
|
| -enum VariationsSeedStoreResult {
|
| - VARIATIONS_SEED_STORE_SUCCESS,
|
| - VARIATIONS_SEED_STORE_FAILED_EMPTY,
|
| - VARIATIONS_SEED_STORE_FAILED_PARSE,
|
| - VARIATIONS_SEED_STORE_FAILED_SIGNATURE,
|
| - VARIATIONS_SEED_STORE_FAILED_GZIP,
|
| - VARIATIONS_SEED_STORE_RESULT_ENUM_SIZE,
|
| -};
|
| -
|
| -void RecordVariationsSeedStoreHistogram(VariationsSeedStoreResult result) {
|
| - UMA_HISTOGRAM_ENUMERATION("Variations.SeedStoreResult", result,
|
| - VARIATIONS_SEED_STORE_RESULT_ENUM_SIZE);
|
| -}
|
| -
|
| -// Note: UMA histogram enum - don't re-order or remove entries.
|
| -enum VariationsSeedDateChangeState {
|
| - SEED_DATE_NO_OLD_DATE,
|
| - SEED_DATE_NEW_DATE_OLDER,
|
| - SEED_DATE_SAME_DAY,
|
| - SEED_DATE_NEW_DAY,
|
| - SEED_DATE_ENUM_SIZE,
|
| -};
|
| -
|
| -// 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(const base::Time& time) {
|
| - base::Time::Exploded exploded;
|
| - time.UTCExplode(&exploded);
|
| - exploded.hour = 0;
|
| - exploded.minute = 0;
|
| - exploded.second = 0;
|
| - exploded.millisecond = 0;
|
| -
|
| - return base::Time::FromUTCExploded(exploded);
|
| -}
|
| -
|
| -VariationsSeedDateChangeState GetSeedDateChangeState(
|
| - const base::Time& server_seed_date,
|
| - const base::Time& stored_seed_date) {
|
| - if (server_seed_date < stored_seed_date)
|
| - return SEED_DATE_NEW_DATE_OLDER;
|
| -
|
| - if (TruncateToUTCDay(server_seed_date) !=
|
| - TruncateToUTCDay(stored_seed_date)) {
|
| - // The server date is earlier than the stored date, and they are from
|
| - // different UTC days, so |server_seed_date| is a valid new day.
|
| - return SEED_DATE_NEW_DAY;
|
| - }
|
| - return SEED_DATE_SAME_DAY;
|
| -}
|
| -
|
| -} // namespace
|
| -
|
| -VariationsSeedStore::VariationsSeedStore(PrefService* local_state)
|
| - : local_state_(local_state) {
|
| -}
|
| -
|
| -VariationsSeedStore::~VariationsSeedStore() {
|
| -}
|
| -
|
| -bool VariationsSeedStore::LoadSeed(variations::VariationsSeed* seed) {
|
| - invalid_base64_signature_.clear();
|
| -
|
| - std::string seed_data;
|
| - if (!ReadSeedData(&seed_data))
|
| - return false;
|
| -
|
| - const std::string base64_seed_signature =
|
| - local_state_->GetString(metrics::prefs::kVariationsSeedSignature);
|
| - const VerifySignatureResult result =
|
| - VerifySeedSignature(seed_data, base64_seed_signature);
|
| - if (result != VARIATIONS_SEED_SIGNATURE_ENUM_SIZE) {
|
| - UMA_HISTOGRAM_ENUMERATION("Variations.LoadSeedSignature", result,
|
| - VARIATIONS_SEED_SIGNATURE_ENUM_SIZE);
|
| - if (result != VARIATIONS_SEED_SIGNATURE_VALID) {
|
| - ClearPrefs();
|
| - RecordVariationSeedEmptyHistogram(VARIATIONS_SEED_INVALID_SIGNATURE);
|
| - // Record the invalid signature.
|
| - invalid_base64_signature_ = base64_seed_signature;
|
| - return false;
|
| - }
|
| - }
|
| -
|
| - if (!seed->ParseFromString(seed_data)) {
|
| - ClearPrefs();
|
| - RecordVariationSeedEmptyHistogram(VARIATIONS_SEED_CORRUPT_PROTOBUF);
|
| - return false;
|
| - }
|
| -
|
| - // Migrate any existing country code from the seed to the pref, if the pref is
|
| - // empty. TODO(asvitkine): Clean up the code in M50+ when sufficient number
|
| - // of clients have migrated.
|
| - if (seed->has_country_code() &&
|
| - local_state_->GetString(metrics::prefs::kVariationsCountry).empty()) {
|
| - local_state_->SetString(metrics::prefs::kVariationsCountry,
|
| - seed->country_code());
|
| - }
|
| - variations_serial_number_ = seed->serial_number();
|
| - RecordVariationSeedEmptyHistogram(VARIATIONS_SEED_NOT_EMPTY);
|
| - return true;
|
| -}
|
| -
|
| -bool VariationsSeedStore::StoreSeedData(
|
| - const std::string& seed_data,
|
| - const std::string& base64_seed_signature,
|
| - const base::Time& date_fetched,
|
| - variations::VariationsSeed* parsed_seed) {
|
| - if (seed_data.empty()) {
|
| - RecordVariationsSeedStoreHistogram(VARIATIONS_SEED_STORE_FAILED_EMPTY);
|
| - return false;
|
| - }
|
| -
|
| - // Only store the seed data if it parses correctly.
|
| - variations::VariationsSeed seed;
|
| - if (!seed.ParseFromString(seed_data)) {
|
| - RecordVariationsSeedStoreHistogram(VARIATIONS_SEED_STORE_FAILED_PARSE);
|
| - return false;
|
| - }
|
| -
|
| - const VerifySignatureResult result =
|
| - VerifySeedSignature(seed_data, base64_seed_signature);
|
| - if (result != VARIATIONS_SEED_SIGNATURE_ENUM_SIZE) {
|
| - UMA_HISTOGRAM_ENUMERATION("Variations.StoreSeedSignature", result,
|
| - VARIATIONS_SEED_SIGNATURE_ENUM_SIZE);
|
| - if (result != VARIATIONS_SEED_SIGNATURE_VALID) {
|
| - RecordVariationsSeedStoreHistogram(
|
| - VARIATIONS_SEED_STORE_FAILED_SIGNATURE);
|
| - return false;
|
| - }
|
| - }
|
| -
|
| - // Compress the seed before base64-encoding and storing.
|
| - std::string compressed_seed_data;
|
| - if (!metrics::GzipCompress(seed_data, &compressed_seed_data)) {
|
| - RecordVariationsSeedStoreHistogram(VARIATIONS_SEED_STORE_FAILED_GZIP);
|
| - return false;
|
| - }
|
| -
|
| - std::string base64_seed_data;
|
| - base::Base64Encode(compressed_seed_data, &base64_seed_data);
|
| -
|
| - // TODO(asvitkine): This pref is no longer being used. Remove it completely
|
| - // in M45+.
|
| - local_state_->ClearPref(metrics::prefs::kVariationsSeed);
|
| -
|
| - // Update the saved country code only if one was returned from the server.
|
| - if (seed.has_country_code())
|
| - local_state_->SetString(metrics::prefs::kVariationsCountry,
|
| - seed.country_code());
|
| -
|
| - local_state_->SetString(metrics::prefs::kVariationsCompressedSeed,
|
| - base64_seed_data);
|
| - UpdateSeedDateAndLogDayChange(date_fetched);
|
| - local_state_->SetString(metrics::prefs::kVariationsSeedSignature,
|
| - base64_seed_signature);
|
| - variations_serial_number_ = seed.serial_number();
|
| - if (parsed_seed)
|
| - seed.Swap(parsed_seed);
|
| -
|
| - RecordVariationsSeedStoreHistogram(VARIATIONS_SEED_STORE_SUCCESS);
|
| - return true;
|
| -}
|
| -
|
| -void VariationsSeedStore::UpdateSeedDateAndLogDayChange(
|
| - const base::Time& server_date_fetched) {
|
| - VariationsSeedDateChangeState date_change = SEED_DATE_NO_OLD_DATE;
|
| -
|
| - if (local_state_->HasPrefPath(metrics::prefs::kVariationsSeedDate)) {
|
| - const int64 stored_date_value =
|
| - local_state_->GetInt64(metrics::prefs::kVariationsSeedDate);
|
| - const base::Time stored_date =
|
| - base::Time::FromInternalValue(stored_date_value);
|
| -
|
| - date_change = GetSeedDateChangeState(server_date_fetched, stored_date);
|
| - }
|
| -
|
| - UMA_HISTOGRAM_ENUMERATION("Variations.SeedDateChange", date_change,
|
| - SEED_DATE_ENUM_SIZE);
|
| -
|
| - local_state_->SetInt64(metrics::prefs::kVariationsSeedDate,
|
| - server_date_fetched.ToInternalValue());
|
| -}
|
| -
|
| -// static
|
| -void VariationsSeedStore::RegisterPrefs(PrefRegistrySimple* registry) {
|
| - registry->RegisterStringPref(metrics::prefs::kVariationsCompressedSeed,
|
| - std::string());
|
| - registry->RegisterStringPref(metrics::prefs::kVariationsSeed, std::string());
|
| - registry->RegisterInt64Pref(metrics::prefs::kVariationsSeedDate,
|
| - base::Time().ToInternalValue());
|
| - registry->RegisterStringPref(metrics::prefs::kVariationsSeedSignature,
|
| - std::string());
|
| - registry->RegisterStringPref(metrics::prefs::kVariationsCountry,
|
| - std::string());
|
| -}
|
| -
|
| -void VariationsSeedStore::ClearPrefs() {
|
| - local_state_->ClearPref(metrics::prefs::kVariationsCompressedSeed);
|
| - local_state_->ClearPref(metrics::prefs::kVariationsSeed);
|
| - local_state_->ClearPref(metrics::prefs::kVariationsSeedDate);
|
| - local_state_->ClearPref(metrics::prefs::kVariationsSeedSignature);
|
| -}
|
| -
|
| -bool VariationsSeedStore::ReadSeedData(std::string* seed_data) {
|
| - std::string base64_seed_data =
|
| - local_state_->GetString(metrics::prefs::kVariationsCompressedSeed);
|
| - const bool is_compressed = !base64_seed_data.empty();
|
| - // If there's no compressed seed, fall back to the uncompressed one.
|
| - if (!is_compressed)
|
| - base64_seed_data = local_state_->GetString(metrics::prefs::kVariationsSeed);
|
| -
|
| - if (base64_seed_data.empty()) {
|
| - RecordVariationSeedEmptyHistogram(VARIATIONS_SEED_EMPTY);
|
| - return false;
|
| - }
|
| -
|
| - // If the decode process fails, assume the pref value is corrupt and clear it.
|
| - std::string decoded_data;
|
| - if (!base::Base64Decode(base64_seed_data, &decoded_data)) {
|
| - ClearPrefs();
|
| - RecordVariationSeedEmptyHistogram(VARIATIONS_SEED_CORRUPT_BASE64);
|
| - return false;
|
| - }
|
| -
|
| - if (!is_compressed) {
|
| - seed_data->swap(decoded_data);
|
| - } else if (!metrics::GzipUncompress(decoded_data, seed_data)) {
|
| - ClearPrefs();
|
| - RecordVariationSeedEmptyHistogram(VARIATIONS_SEED_CORRUPT_GZIP);
|
| - return false;
|
| - }
|
| -
|
| - return true;
|
| -}
|
| -
|
| -VariationsSeedStore::VerifySignatureResult
|
| -VariationsSeedStore::VerifySeedSignature(
|
| - const std::string& seed_bytes,
|
| - const std::string& base64_seed_signature) {
|
| - if (!SignatureVerificationEnabled())
|
| - return VARIATIONS_SEED_SIGNATURE_ENUM_SIZE;
|
| -
|
| - if (base64_seed_signature.empty())
|
| - return VARIATIONS_SEED_SIGNATURE_MISSING;
|
| -
|
| - std::string signature;
|
| - if (!base::Base64Decode(base64_seed_signature, &signature))
|
| - return VARIATIONS_SEED_SIGNATURE_DECODE_FAILED;
|
| -
|
| - crypto::SignatureVerifier verifier;
|
| - if (!verifier.VerifyInit(
|
| - kECDSAWithSHA256AlgorithmID, sizeof(kECDSAWithSHA256AlgorithmID),
|
| - reinterpret_cast<const uint8*>(signature.data()), signature.size(),
|
| - kPublicKey, arraysize(kPublicKey))) {
|
| - return VARIATIONS_SEED_SIGNATURE_INVALID_SIGNATURE;
|
| - }
|
| -
|
| - verifier.VerifyUpdate(reinterpret_cast<const uint8*>(seed_bytes.data()),
|
| - seed_bytes.size());
|
| - if (verifier.VerifyFinal())
|
| - return VARIATIONS_SEED_SIGNATURE_VALID;
|
| - return VARIATIONS_SEED_SIGNATURE_INVALID_SEED;
|
| -}
|
| -
|
| -std::string VariationsSeedStore::GetInvalidSignature() const {
|
| - return invalid_base64_signature_;
|
| -}
|
| -
|
| -} // namespace chrome_variations
|
|
|