Home | History | Annotate | Download | only in variations
      1 // Copyright 2014 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "chrome/browser/metrics/variations/variations_seed_store.h"
      6 
      7 #include "base/base64.h"
      8 #include "base/metrics/histogram.h"
      9 #include "base/prefs/pref_registry_simple.h"
     10 #include "base/prefs/pref_service.h"
     11 #include "base/sha1.h"
     12 #include "base/strings/string_number_conversions.h"
     13 #include "chrome/common/pref_names.h"
     14 #include "components/variations/proto/variations_seed.pb.h"
     15 #include "crypto/signature_verifier.h"
     16 
     17 namespace chrome_variations {
     18 
     19 namespace {
     20 
     21 // Signature verification is disabled on mobile platforms for now, since it
     22 // adds about ~15ms to the startup time on mobile (vs. a couple ms on desktop).
     23 bool SignatureVerificationEnabled() {
     24 #if defined(OS_IOS) || defined(OS_ANDROID)
     25   return false;
     26 #else
     27   return true;
     28 #endif
     29 }
     30 
     31 // This is the algorithm ID for ECDSA with SHA-256. Parameters are ABSENT.
     32 // RFC 5758:
     33 //   ecdsa-with-SHA256 OBJECT IDENTIFIER ::= { iso(1) member-body(2)
     34 //        us(840) ansi-X9-62(10045) signatures(4) ecdsa-with-SHA2(3) 2 }
     35 //   ...
     36 //   When the ecdsa-with-SHA224, ecdsa-with-SHA256, ecdsa-with-SHA384, or
     37 //   ecdsa-with-SHA512 algorithm identifier appears in the algorithm field
     38 //   as an AlgorithmIdentifier, the encoding MUST omit the parameters
     39 //   field.  That is, the AlgorithmIdentifier SHALL be a SEQUENCE of one
     40 //   component, the OID ecdsa-with-SHA224, ecdsa-with-SHA256, ecdsa-with-
     41 //   SHA384, or ecdsa-with-SHA512.
     42 // See also RFC 5480, Appendix A.
     43 const uint8 kECDSAWithSHA256AlgorithmID[] = {
     44   0x30, 0x0a,
     45     0x06, 0x08,
     46       0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02,
     47 };
     48 
     49 // The ECDSA public key of the variations server for verifying variations seed
     50 // signatures.
     51 const uint8_t kPublicKey[] = {
     52   0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01,
     53   0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00,
     54   0x04, 0x51, 0x7c, 0x31, 0x4b, 0x50, 0x42, 0xdd, 0x59, 0xda, 0x0b, 0xfa, 0x43,
     55   0x44, 0x33, 0x7c, 0x5f, 0xa1, 0x0b, 0xd5, 0x82, 0xf6, 0xac, 0x04, 0x19, 0x72,
     56   0x6c, 0x40, 0xd4, 0x3e, 0x56, 0xe2, 0xa0, 0x80, 0xa0, 0x41, 0xb3, 0x23, 0x7b,
     57   0x71, 0xc9, 0x80, 0x87, 0xde, 0x35, 0x0d, 0x25, 0x71, 0x09, 0x7f, 0xb4, 0x15,
     58   0x2b, 0xff, 0x82, 0x4d, 0xd3, 0xfe, 0xc5, 0xef, 0x20, 0xc6, 0xa3, 0x10, 0xbf,
     59 };
     60 
     61 // Note: UMA histogram enum - don't re-order or remove entries.
     62 enum VariationSeedEmptyState {
     63   VARIATIONS_SEED_NOT_EMPTY,
     64   VARIATIONS_SEED_EMPTY,
     65   VARIATIONS_SEED_CORRUPT,
     66   VARIATIONS_SEED_INVALID_SIGNATURE,
     67   VARIATIONS_SEED_EMPTY_ENUM_SIZE,
     68 };
     69 
     70 void RecordVariationSeedEmptyHistogram(VariationSeedEmptyState state) {
     71   UMA_HISTOGRAM_ENUMERATION("Variations.SeedEmpty", state,
     72                             VARIATIONS_SEED_EMPTY_ENUM_SIZE);
     73 }
     74 
     75 // Note: UMA histogram enum - don't re-order or remove entries.
     76 enum VariationsSeedDateChangeState {
     77   SEED_DATE_NO_OLD_DATE,
     78   SEED_DATE_NEW_DATE_OLDER,
     79   SEED_DATE_SAME_DAY,
     80   SEED_DATE_NEW_DAY,
     81   SEED_DATE_ENUM_SIZE,
     82 };
     83 
     84 // Truncates a time to the start of the day in UTC. If given a time representing
     85 // 2014-03-11 10:18:03.1 UTC, it will return a time representing
     86 // 2014-03-11 00:00:00.0 UTC.
     87 base::Time TruncateToUTCDay(const base::Time& time) {
     88   base::Time::Exploded exploded;
     89   time.UTCExplode(&exploded);
     90   exploded.hour = 0;
     91   exploded.minute = 0;
     92   exploded.second = 0;
     93   exploded.millisecond = 0;
     94 
     95   return base::Time::FromUTCExploded(exploded);
     96 }
     97 
     98 VariationsSeedDateChangeState GetSeedDateChangeState(
     99     const base::Time& server_seed_date,
    100     const base::Time& stored_seed_date) {
    101   if (server_seed_date < stored_seed_date)
    102     return SEED_DATE_NEW_DATE_OLDER;
    103 
    104   if (TruncateToUTCDay(server_seed_date) !=
    105       TruncateToUTCDay(stored_seed_date)) {
    106     // The server date is earlier than the stored date, and they are from
    107     // different UTC days, so |server_seed_date| is a valid new day.
    108     return SEED_DATE_NEW_DAY;
    109   }
    110   return SEED_DATE_SAME_DAY;
    111 }
    112 
    113 }  // namespace
    114 
    115 VariationsSeedStore::VariationsSeedStore(PrefService* local_state)
    116     : local_state_(local_state) {
    117 }
    118 
    119 VariationsSeedStore::~VariationsSeedStore() {
    120 }
    121 
    122 bool VariationsSeedStore::LoadSeed(variations::VariationsSeed* seed) {
    123   const std::string base64_seed_data =
    124       local_state_->GetString(prefs::kVariationsSeed);
    125   if (base64_seed_data.empty()) {
    126     RecordVariationSeedEmptyHistogram(VARIATIONS_SEED_EMPTY);
    127     return false;
    128   }
    129 
    130   // If the decode process fails, assume the pref value is corrupt and clear it.
    131   std::string seed_data;
    132   if (!base::Base64Decode(base64_seed_data, &seed_data) ||
    133       !seed->ParseFromString(seed_data)) {
    134     VLOG(1) << "Variations seed data in local pref is corrupt, clearing the "
    135             << "pref.";
    136     ClearPrefs();
    137     RecordVariationSeedEmptyHistogram(VARIATIONS_SEED_CORRUPT);
    138     return false;
    139   }
    140 
    141   const std::string base64_seed_signature =
    142       local_state_->GetString(prefs::kVariationsSeedSignature);
    143   const VerifySignatureResult result =
    144       VerifySeedSignature(seed_data, base64_seed_signature);
    145   if (result != VARIATIONS_SEED_SIGNATURE_ENUM_SIZE) {
    146     UMA_HISTOGRAM_ENUMERATION("Variations.LoadSeedSignature", result,
    147                               VARIATIONS_SEED_SIGNATURE_ENUM_SIZE);
    148     if (result != VARIATIONS_SEED_SIGNATURE_VALID) {
    149       VLOG(1) << "Variations seed signature in local pref missing or invalid "
    150               << "with result: " << result << ". Clearing the pref.";
    151       ClearPrefs();
    152       RecordVariationSeedEmptyHistogram(VARIATIONS_SEED_INVALID_SIGNATURE);
    153       return false;
    154     }
    155   }
    156 
    157   variations_serial_number_ = seed->serial_number();
    158   RecordVariationSeedEmptyHistogram(VARIATIONS_SEED_NOT_EMPTY);
    159   return true;
    160 }
    161 
    162 bool VariationsSeedStore::StoreSeedData(
    163     const std::string& seed_data,
    164     const std::string& base64_seed_signature,
    165     const base::Time& date_fetched,
    166     variations::VariationsSeed* parsed_seed) {
    167   if (seed_data.empty()) {
    168     VLOG(1) << "Variations seed data is empty, rejecting the seed.";
    169     return false;
    170   }
    171 
    172   // Only store the seed data if it parses correctly.
    173   variations::VariationsSeed seed;
    174   if (!seed.ParseFromString(seed_data)) {
    175     VLOG(1) << "Variations seed data is not in valid proto format, "
    176             << "rejecting the seed.";
    177     return false;
    178   }
    179 
    180   const VerifySignatureResult result =
    181       VerifySeedSignature(seed_data, base64_seed_signature);
    182   if (result != VARIATIONS_SEED_SIGNATURE_ENUM_SIZE) {
    183     UMA_HISTOGRAM_ENUMERATION("Variations.StoreSeedSignature", result,
    184                               VARIATIONS_SEED_SIGNATURE_ENUM_SIZE);
    185     if (result != VARIATIONS_SEED_SIGNATURE_VALID) {
    186       VLOG(1) << "Variations seed signature missing or invalid with result: "
    187               << result << ". Rejecting the seed.";
    188       return false;
    189     }
    190   }
    191 
    192   std::string base64_seed_data;
    193   base::Base64Encode(seed_data, &base64_seed_data);
    194 
    195   // TODO(asvitkine): This pref is no longer being used. Remove it completely
    196   // in a couple of releases.
    197   local_state_->ClearPref(prefs::kVariationsSeedHash);
    198 
    199   local_state_->SetString(prefs::kVariationsSeed, base64_seed_data);
    200   UpdateSeedDateAndLogDayChange(date_fetched);
    201   local_state_->SetString(prefs::kVariationsSeedSignature,
    202                           base64_seed_signature);
    203   variations_serial_number_ = seed.serial_number();
    204   if (parsed_seed)
    205     seed.Swap(parsed_seed);
    206 
    207   return true;
    208 }
    209 
    210 void VariationsSeedStore::UpdateSeedDateAndLogDayChange(
    211     const base::Time& server_date_fetched) {
    212   VariationsSeedDateChangeState date_change = SEED_DATE_NO_OLD_DATE;
    213 
    214   if (local_state_->HasPrefPath(prefs::kVariationsSeedDate)) {
    215     const int64 stored_date_value =
    216         local_state_->GetInt64(prefs::kVariationsSeedDate);
    217     const base::Time stored_date =
    218         base::Time::FromInternalValue(stored_date_value);
    219 
    220     date_change = GetSeedDateChangeState(server_date_fetched, stored_date);
    221   }
    222 
    223   UMA_HISTOGRAM_ENUMERATION("Variations.SeedDateChange", date_change,
    224                             SEED_DATE_ENUM_SIZE);
    225 
    226   local_state_->SetInt64(prefs::kVariationsSeedDate,
    227                          server_date_fetched.ToInternalValue());
    228 }
    229 
    230 // static
    231 void VariationsSeedStore::RegisterPrefs(PrefRegistrySimple* registry) {
    232   registry->RegisterStringPref(prefs::kVariationsSeed, std::string());
    233   registry->RegisterStringPref(prefs::kVariationsSeedHash, std::string());
    234   registry->RegisterInt64Pref(prefs::kVariationsSeedDate,
    235                               base::Time().ToInternalValue());
    236   registry->RegisterStringPref(prefs::kVariationsSeedSignature, std::string());
    237 }
    238 
    239 void VariationsSeedStore::ClearPrefs() {
    240   local_state_->ClearPref(prefs::kVariationsSeed);
    241   local_state_->ClearPref(prefs::kVariationsSeedDate);
    242   local_state_->ClearPref(prefs::kVariationsSeedHash);
    243   local_state_->ClearPref(prefs::kVariationsSeedSignature);
    244 }
    245 
    246 VariationsSeedStore::VerifySignatureResult
    247 VariationsSeedStore::VerifySeedSignature(
    248     const std::string& seed_bytes,
    249     const std::string& base64_seed_signature) {
    250   if (!SignatureVerificationEnabled())
    251     return VARIATIONS_SEED_SIGNATURE_ENUM_SIZE;
    252 
    253   if (base64_seed_signature.empty())
    254     return VARIATIONS_SEED_SIGNATURE_MISSING;
    255 
    256   std::string signature;
    257   if (!base::Base64Decode(base64_seed_signature, &signature))
    258     return VARIATIONS_SEED_SIGNATURE_DECODE_FAILED;
    259 
    260   crypto::SignatureVerifier verifier;
    261   if (!verifier.VerifyInit(
    262           kECDSAWithSHA256AlgorithmID, sizeof(kECDSAWithSHA256AlgorithmID),
    263           reinterpret_cast<const uint8*>(signature.data()), signature.size(),
    264           kPublicKey, arraysize(kPublicKey))) {
    265     return VARIATIONS_SEED_SIGNATURE_INVALID_SIGNATURE;
    266   }
    267 
    268   verifier.VerifyUpdate(reinterpret_cast<const uint8*>(seed_bytes.data()),
    269                         seed_bytes.size());
    270   if (verifier.VerifyFinal())
    271     return VARIATIONS_SEED_SIGNATURE_VALID;
    272   return VARIATIONS_SEED_SIGNATURE_INVALID_SEED;
    273 }
    274 
    275 }  // namespace chrome_variations
    276