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 17namespace chrome_variations { 18 19namespace { 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). 23bool 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. 43const 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. 51const 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. 62enum 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 70void 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. 76enum 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. 87base::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 98VariationsSeedDateChangeState 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 115VariationsSeedStore::VariationsSeedStore(PrefService* local_state) 116 : local_state_(local_state) { 117} 118 119VariationsSeedStore::~VariationsSeedStore() { 120} 121 122bool 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 162bool 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 210void 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 231void 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 239void 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 246VariationsSeedStore::VerifySignatureResult 247VariationsSeedStore::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