variations_seed_store.cc revision cedac228d2dd51db4b79ea1e72c7f249408ee061
15d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// Copyright 2014 The Chromium Authors. All rights reserved.
25d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// found in the LICENSE file.
45d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
55d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "chrome/browser/metrics/variations/variations_seed_store.h"
65d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
75d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "base/base64.h"
85d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "base/metrics/histogram.h"
95d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "base/prefs/pref_registry_simple.h"
105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "base/prefs/pref_service.h"
115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "base/sha1.h"
125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "base/strings/string_number_conversions.h"
135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "chrome/common/pref_names.h"
145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "components/variations/proto/variations_seed.pb.h"
155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "crypto/signature_verifier.h"
165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)namespace chrome_variations {
185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)namespace {
205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// Signature verification is disabled on mobile platforms for now, since it
225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// adds about ~15ms to the startup time on mobile (vs. a couple ms on desktop).
235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)bool SignatureVerificationEnabled() {
245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#if defined(OS_IOS) || defined(OS_ANDROID)
255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return false;
265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#else
275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return true;
285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#endif
295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// This is the algorithm ID for ECDSA with SHA-256. Parameters are ABSENT.
325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// RFC 5758:
335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)//   ecdsa-with-SHA256 OBJECT IDENTIFIER ::= { iso(1) member-body(2)
345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)//        us(840) ansi-X9-62(10045) signatures(4) ecdsa-with-SHA2(3) 2 }
355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)//   ...
365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)//   When the ecdsa-with-SHA224, ecdsa-with-SHA256, ecdsa-with-SHA384, or
375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)//   ecdsa-with-SHA512 algorithm identifier appears in the algorithm field
385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)//   as an AlgorithmIdentifier, the encoding MUST omit the parameters
395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)//   field.  That is, the AlgorithmIdentifier SHALL be a SEQUENCE of one
405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)//   component, the OID ecdsa-with-SHA224, ecdsa-with-SHA256, ecdsa-with-
415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)//   SHA384, or ecdsa-with-SHA512.
425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// See also RFC 5480, Appendix A.
435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)const uint8 kECDSAWithSHA256AlgorithmID[] = {
445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  0x30, 0x0a,
455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    0x06, 0x08,
465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02,
475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)};
485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// The ECDSA public key of the variations server for verifying variations seed
505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// signatures.
515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)const uint8_t kPublicKey[] = {
525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01,
535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00,
545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  0x04, 0x51, 0x7c, 0x31, 0x4b, 0x50, 0x42, 0xdd, 0x59, 0xda, 0x0b, 0xfa, 0x43,
555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  0x44, 0x33, 0x7c, 0x5f, 0xa1, 0x0b, 0xd5, 0x82, 0xf6, 0xac, 0x04, 0x19, 0x72,
565d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  0x6c, 0x40, 0xd4, 0x3e, 0x56, 0xe2, 0xa0, 0x80, 0xa0, 0x41, 0xb3, 0x23, 0x7b,
575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  0x71, 0xc9, 0x80, 0x87, 0xde, 0x35, 0x0d, 0x25, 0x71, 0x09, 0x7f, 0xb4, 0x15,
585d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  0x2b, 0xff, 0x82, 0x4d, 0xd3, 0xfe, 0xc5, 0xef, 0x20, 0xc6, 0xa3, 0x10, 0xbf,
595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)};
605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
615d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// Note: UMA histogram enum - don't re-order or remove entries.
625d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)enum VariationSeedEmptyState {
635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  VARIATIONS_SEED_NOT_EMPTY,
645d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  VARIATIONS_SEED_EMPTY,
655d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  VARIATIONS_SEED_CORRUPT,
66a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  VARIATIONS_SEED_INVALID_SIGNATURE,
675d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  VARIATIONS_SEED_EMPTY_ENUM_SIZE,
685d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)};
695d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
705d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void RecordVariationSeedEmptyHistogram(VariationSeedEmptyState state) {
715d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  UMA_HISTOGRAM_ENUMERATION("Variations.SeedEmpty", state,
725d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                            VARIATIONS_SEED_EMPTY_ENUM_SIZE);
735d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
745d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
75a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)// Note: UMA histogram enum - don't re-order or remove entries.
76a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)enum VariationsSeedDateChangeState {
77a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  SEED_DATE_NO_OLD_DATE,
78a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  SEED_DATE_NEW_DATE_OLDER,
79a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  SEED_DATE_SAME_DAY,
80a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  SEED_DATE_NEW_DAY,
81a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  SEED_DATE_ENUM_SIZE,
82a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)};
83a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
84a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)// Truncates a time to the start of the day in UTC. If given a time representing
85a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)// 2014-03-11 10:18:03.1 UTC, it will return a time representing
86a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)// 2014-03-11 00:00:00.0 UTC.
87a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)base::Time TruncateToUTCDay(const base::Time& time) {
88a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  base::Time::Exploded exploded;
89a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  time.UTCExplode(&exploded);
90a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  exploded.hour = 0;
91a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  exploded.minute = 0;
92a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  exploded.second = 0;
93a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  exploded.millisecond = 0;
94a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
95a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  return base::Time::FromUTCExploded(exploded);
96a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}
97a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
98a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)VariationsSeedDateChangeState GetSeedDateChangeState(
99a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    const base::Time& server_seed_date,
100a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    const base::Time& stored_seed_date) {
101a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (server_seed_date < stored_seed_date)
102a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    return SEED_DATE_NEW_DATE_OLDER;
103a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
104a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (TruncateToUTCDay(server_seed_date) !=
105a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      TruncateToUTCDay(stored_seed_date)) {
106a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    // The server date is earlier than the stored date, and they are from
107a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    // different UTC days, so |server_seed_date| is a valid new day.
108a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    return SEED_DATE_NEW_DAY;
109a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  }
110a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  return SEED_DATE_SAME_DAY;
111a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}
112a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
1135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}  // namespace
1145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)VariationsSeedStore::VariationsSeedStore(PrefService* local_state)
1165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    : local_state_(local_state) {
1175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
1185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)VariationsSeedStore::~VariationsSeedStore() {
1205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
1215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)bool VariationsSeedStore::LoadSeed(VariationsSeed* seed) {
1235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  const std::string base64_seed_data =
1245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      local_state_->GetString(prefs::kVariationsSeed);
1255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (base64_seed_data.empty()) {
1265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    RecordVariationSeedEmptyHistogram(VARIATIONS_SEED_EMPTY);
1275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return false;
1285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
1295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // If the decode process fails, assume the pref value is corrupt and clear it.
1315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  std::string seed_data;
1325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (!base::Base64Decode(base64_seed_data, &seed_data) ||
1335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      !seed->ParseFromString(seed_data)) {
1345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    VLOG(1) << "Variations seed data in local pref is corrupt, clearing the "
1355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            << "pref.";
1365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    ClearPrefs();
1375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    RecordVariationSeedEmptyHistogram(VARIATIONS_SEED_CORRUPT);
1385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return false;
1395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
1405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
141a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  const std::string base64_seed_signature =
142a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      local_state_->GetString(prefs::kVariationsSeedSignature);
143a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  const VerifySignatureResult result =
144a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      VerifySeedSignature(seed_data, base64_seed_signature);
145a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (result != VARIATIONS_SEED_SIGNATURE_ENUM_SIZE) {
146a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    UMA_HISTOGRAM_ENUMERATION("Variations.LoadSeedSignature", result,
1475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                              VARIATIONS_SEED_SIGNATURE_ENUM_SIZE);
148a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    if (result != VARIATIONS_SEED_SIGNATURE_VALID) {
149a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      VLOG(1) << "Variations seed signature in local pref missing or invalid "
150a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)              << "with result: " << result << ". Clearing the pref.";
151a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      ClearPrefs();
152a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      RecordVariationSeedEmptyHistogram(VARIATIONS_SEED_INVALID_SIGNATURE);
153a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      return false;
154a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    }
1555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
1565d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  variations_serial_number_ = seed->serial_number();
1585d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  RecordVariationSeedEmptyHistogram(VARIATIONS_SEED_NOT_EMPTY);
1595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return true;
1605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
1615d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1625d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)bool VariationsSeedStore::StoreSeedData(
1635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    const std::string& seed_data,
1645d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    const std::string& base64_seed_signature,
165cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    const base::Time& date_fetched,
166cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    VariationsSeed* parsed_seed) {
1675d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (seed_data.empty()) {
1685d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    VLOG(1) << "Variations seed data is empty, rejecting the seed.";
1695d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return false;
1705d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
1715d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1725d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // Only store the seed data if it parses correctly.
1735d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  VariationsSeed seed;
1745d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (!seed.ParseFromString(seed_data)) {
1755d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    VLOG(1) << "Variations seed data is not in valid proto format, "
1765d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            << "rejecting the seed.";
1775d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return false;
1785d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
1795d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
180a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  const VerifySignatureResult result =
181a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      VerifySeedSignature(seed_data, base64_seed_signature);
182a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (result != VARIATIONS_SEED_SIGNATURE_ENUM_SIZE) {
183a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    UMA_HISTOGRAM_ENUMERATION("Variations.StoreSeedSignature", result,
1845d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                              VARIATIONS_SEED_SIGNATURE_ENUM_SIZE);
185a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    if (result != VARIATIONS_SEED_SIGNATURE_VALID) {
186a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      VLOG(1) << "Variations seed signature missing or invalid with result: "
187a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)              << result << ". Rejecting the seed.";
188a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      return false;
189a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    }
1905d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
1915d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1925d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  std::string base64_seed_data;
1935d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  base::Base64Encode(seed_data, &base64_seed_data);
1945d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
195a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // TODO(asvitkine): This pref is no longer being used. Remove it completely
196a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // in a couple of releases.
197a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  local_state_->ClearPref(prefs::kVariationsSeedHash);
198a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
1995d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  local_state_->SetString(prefs::kVariationsSeed, base64_seed_data);
200a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  UpdateSeedDateAndLogDayChange(date_fetched);
2015d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  local_state_->SetString(prefs::kVariationsSeedSignature,
2025d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                          base64_seed_signature);
2035d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  variations_serial_number_ = seed.serial_number();
204cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  if (parsed_seed)
205cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    seed.Swap(parsed_seed);
2065d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
2075d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return true;
2085d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
2095d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
210a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)void VariationsSeedStore::UpdateSeedDateAndLogDayChange(
211a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    const base::Time& server_date_fetched) {
212a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  VariationsSeedDateChangeState date_change = SEED_DATE_NO_OLD_DATE;
213a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
214a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (local_state_->HasPrefPath(prefs::kVariationsSeedDate)) {
215a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    const int64 stored_date_value =
216a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        local_state_->GetInt64(prefs::kVariationsSeedDate);
217a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    const base::Time stored_date =
218a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        base::Time::FromInternalValue(stored_date_value);
219a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
220a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    date_change = GetSeedDateChangeState(server_date_fetched, stored_date);
221a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  }
222a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
223a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  UMA_HISTOGRAM_ENUMERATION("Variations.SeedDateChange", date_change,
224a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                            SEED_DATE_ENUM_SIZE);
225a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
226a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  local_state_->SetInt64(prefs::kVariationsSeedDate,
227a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                         server_date_fetched.ToInternalValue());
228a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}
229a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
2305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// static
2315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void VariationsSeedStore::RegisterPrefs(PrefRegistrySimple* registry) {
2325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  registry->RegisterStringPref(prefs::kVariationsSeed, std::string());
2335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  registry->RegisterStringPref(prefs::kVariationsSeedHash, std::string());
2345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  registry->RegisterInt64Pref(prefs::kVariationsSeedDate,
2355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                              base::Time().ToInternalValue());
2365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  registry->RegisterStringPref(prefs::kVariationsSeedSignature, std::string());
2375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
2385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
2395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void VariationsSeedStore::ClearPrefs() {
2405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  local_state_->ClearPref(prefs::kVariationsSeed);
2415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  local_state_->ClearPref(prefs::kVariationsSeedDate);
2425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  local_state_->ClearPref(prefs::kVariationsSeedHash);
2435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  local_state_->ClearPref(prefs::kVariationsSeedSignature);
2445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
2455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
246a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)VariationsSeedStore::VerifySignatureResult
247a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)VariationsSeedStore::VerifySeedSignature(
248a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    const std::string& seed_bytes,
249a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    const std::string& base64_seed_signature) {
250a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (!SignatureVerificationEnabled())
251a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    return VARIATIONS_SEED_SIGNATURE_ENUM_SIZE;
252a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
253a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (base64_seed_signature.empty())
254a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    return VARIATIONS_SEED_SIGNATURE_MISSING;
255a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
256a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  std::string signature;
257a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (!base::Base64Decode(base64_seed_signature, &signature))
258a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    return VARIATIONS_SEED_SIGNATURE_DECODE_FAILED;
259a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
260a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  crypto::SignatureVerifier verifier;
261a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (!verifier.VerifyInit(
262a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)          kECDSAWithSHA256AlgorithmID, sizeof(kECDSAWithSHA256AlgorithmID),
263a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)          reinterpret_cast<const uint8*>(signature.data()), signature.size(),
264a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)          kPublicKey, arraysize(kPublicKey))) {
265a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    return VARIATIONS_SEED_SIGNATURE_INVALID_SIGNATURE;
266a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  }
267a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
268a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  verifier.VerifyUpdate(reinterpret_cast<const uint8*>(seed_bytes.data()),
269a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                        seed_bytes.size());
270a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (verifier.VerifyFinal())
271a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    return VARIATIONS_SEED_SIGNATURE_VALID;
272a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  return VARIATIONS_SEED_SIGNATURE_INVALID_SEED;
273a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}
274a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
2755d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}  // namespace chrome_variations
276