Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/metrics/variations/variations_seed_store.h" | 5 #include "chrome/browser/metrics/variations/variations_seed_store.h" |
| 6 | 6 |
| 7 #include "base/base64.h" | 7 #include "base/base64.h" |
| 8 #include "base/metrics/histogram_macros.h" | 8 #include "base/metrics/histogram_macros.h" |
| 9 #include "base/numerics/safe_math.h" | |
| 9 #include "base/prefs/pref_registry_simple.h" | 10 #include "base/prefs/pref_registry_simple.h" |
| 10 #include "base/prefs/pref_service.h" | 11 #include "base/prefs/pref_service.h" |
| 11 #include "base/sha1.h" | 12 #include "base/sha1.h" |
| 12 #include "base/strings/string_number_conversions.h" | 13 #include "base/strings/string_number_conversions.h" |
| 13 #include "chrome/common/pref_names.h" | 14 #include "chrome/common/pref_names.h" |
| 14 #include "components/metrics/compression_utils.h" | 15 #include "components/metrics/compression_utils.h" |
| 15 #include "components/variations/proto/variations_seed.pb.h" | 16 #include "components/variations/proto/variations_seed.pb.h" |
| 16 #include "crypto/signature_verifier.h" | 17 #include "crypto/signature_verifier.h" |
| 18 #include "third_party/protobuf/src/google/protobuf/io/coded_stream.h" | |
| 17 | 19 |
| 18 namespace chrome_variations { | 20 namespace chrome_variations { |
| 19 | 21 |
| 20 namespace { | 22 namespace { |
| 21 | 23 |
| 22 // Signature verification is disabled on mobile platforms for now, since it | 24 // Signature verification is disabled on mobile platforms for now, since it |
| 23 // adds about ~15ms to the startup time on mobile (vs. a couple ms on desktop). | 25 // adds about ~15ms to the startup time on mobile (vs. a couple ms on desktop). |
| 24 bool SignatureVerificationEnabled() { | 26 bool SignatureVerificationEnabled() { |
| 25 #if defined(OS_IOS) || defined(OS_ANDROID) | 27 #if defined(OS_IOS) || defined(OS_ANDROID) |
| 26 return false; | 28 return false; |
| (...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 75 UMA_HISTOGRAM_ENUMERATION("Variations.SeedEmpty", state, | 77 UMA_HISTOGRAM_ENUMERATION("Variations.SeedEmpty", state, |
| 76 VARIATIONS_SEED_EMPTY_ENUM_SIZE); | 78 VARIATIONS_SEED_EMPTY_ENUM_SIZE); |
| 77 } | 79 } |
| 78 | 80 |
| 79 enum VariationsSeedStoreResult { | 81 enum VariationsSeedStoreResult { |
| 80 VARIATIONS_SEED_STORE_SUCCESS, | 82 VARIATIONS_SEED_STORE_SUCCESS, |
| 81 VARIATIONS_SEED_STORE_FAILED_EMPTY, | 83 VARIATIONS_SEED_STORE_FAILED_EMPTY, |
| 82 VARIATIONS_SEED_STORE_FAILED_PARSE, | 84 VARIATIONS_SEED_STORE_FAILED_PARSE, |
| 83 VARIATIONS_SEED_STORE_FAILED_SIGNATURE, | 85 VARIATIONS_SEED_STORE_FAILED_SIGNATURE, |
| 84 VARIATIONS_SEED_STORE_FAILED_GZIP, | 86 VARIATIONS_SEED_STORE_FAILED_GZIP, |
| 87 VARIATIONS_SEED_STORE_DELTA_COUNT, | |
|
rkaplow
2015/08/04 22:37:05
DELTA_COUNT is a bit unlike the rest (not really a
Alexei Svitkine (slow)
2015/08/05 15:46:15
Done.
| |
| 88 VARIATIONS_SEED_STORE_FAILED_DELTA_READ_SEED, | |
| 89 VARIATIONS_SEED_STORE_FAILED_DELTA_APPLY, | |
| 90 VARIATIONS_SEED_STORE_FAILED_DELTA_STORE, | |
| 85 VARIATIONS_SEED_STORE_RESULT_ENUM_SIZE, | 91 VARIATIONS_SEED_STORE_RESULT_ENUM_SIZE, |
| 86 }; | 92 }; |
| 87 | 93 |
| 88 void RecordVariationsSeedStoreHistogram(VariationsSeedStoreResult result) { | 94 void RecordSeedStoreHistogram(VariationsSeedStoreResult result) { |
| 89 UMA_HISTOGRAM_ENUMERATION("Variations.SeedStoreResult", result, | 95 UMA_HISTOGRAM_ENUMERATION("Variations.SeedStoreResult", result, |
| 90 VARIATIONS_SEED_STORE_RESULT_ENUM_SIZE); | 96 VARIATIONS_SEED_STORE_RESULT_ENUM_SIZE); |
| 91 } | 97 } |
| 92 | 98 |
| 93 // Note: UMA histogram enum - don't re-order or remove entries. | 99 // Note: UMA histogram enum - don't re-order or remove entries. |
| 94 enum VariationsSeedDateChangeState { | 100 enum VariationsSeedDateChangeState { |
| 95 SEED_DATE_NO_OLD_DATE, | 101 SEED_DATE_NO_OLD_DATE, |
| 96 SEED_DATE_NEW_DATE_OLDER, | 102 SEED_DATE_NEW_DATE_OLDER, |
| 97 SEED_DATE_SAME_DAY, | 103 SEED_DATE_SAME_DAY, |
| 98 SEED_DATE_NEW_DAY, | 104 SEED_DATE_NEW_DAY, |
| (...skipping 25 matching lines...) Expand all Loading... | |
| 124 // The server date is earlier than the stored date, and they are from | 130 // The server date is earlier than the stored date, and they are from |
| 125 // different UTC days, so |server_seed_date| is a valid new day. | 131 // different UTC days, so |server_seed_date| is a valid new day. |
| 126 return SEED_DATE_NEW_DAY; | 132 return SEED_DATE_NEW_DAY; |
| 127 } | 133 } |
| 128 return SEED_DATE_SAME_DAY; | 134 return SEED_DATE_SAME_DAY; |
| 129 } | 135 } |
| 130 | 136 |
| 131 } // namespace | 137 } // namespace |
| 132 | 138 |
| 133 VariationsSeedStore::VariationsSeedStore(PrefService* local_state) | 139 VariationsSeedStore::VariationsSeedStore(PrefService* local_state) |
| 134 : local_state_(local_state) { | 140 : local_state_(local_state), seed_has_country_code_(false) { |
| 135 } | 141 } |
| 136 | 142 |
| 137 VariationsSeedStore::~VariationsSeedStore() { | 143 VariationsSeedStore::~VariationsSeedStore() { |
| 138 } | 144 } |
| 139 | 145 |
| 140 bool VariationsSeedStore::LoadSeed(variations::VariationsSeed* seed) { | 146 bool VariationsSeedStore::LoadSeed(variations::VariationsSeed* seed) { |
| 141 invalid_base64_signature_.clear(); | 147 invalid_base64_signature_.clear(); |
| 142 | 148 |
| 143 std::string seed_data; | 149 std::string seed_data; |
| 144 if (!ReadSeedData(&seed_data)) | 150 if (!ReadSeedData(&seed_data)) |
| (...skipping 22 matching lines...) Expand all Loading... | |
| 167 } | 173 } |
| 168 | 174 |
| 169 // Migrate any existing country code from the seed to the pref, if the pref is | 175 // Migrate any existing country code from the seed to the pref, if the pref is |
| 170 // empty. TODO(asvitkine): Clean up the code in M50+ when sufficient number | 176 // empty. TODO(asvitkine): Clean up the code in M50+ when sufficient number |
| 171 // of clients have migrated. | 177 // of clients have migrated. |
| 172 if (seed->has_country_code() && | 178 if (seed->has_country_code() && |
| 173 local_state_->GetString(prefs::kVariationsCountry).empty()) { | 179 local_state_->GetString(prefs::kVariationsCountry).empty()) { |
| 174 local_state_->SetString(prefs::kVariationsCountry, seed->country_code()); | 180 local_state_->SetString(prefs::kVariationsCountry, seed->country_code()); |
| 175 } | 181 } |
| 176 variations_serial_number_ = seed->serial_number(); | 182 variations_serial_number_ = seed->serial_number(); |
| 183 seed_has_country_code_ = seed->has_country_code(); | |
| 177 RecordVariationSeedEmptyHistogram(VARIATIONS_SEED_NOT_EMPTY); | 184 RecordVariationSeedEmptyHistogram(VARIATIONS_SEED_NOT_EMPTY); |
| 178 return true; | 185 return true; |
| 179 } | 186 } |
| 180 | 187 |
| 181 bool VariationsSeedStore::StoreSeedData( | 188 bool VariationsSeedStore::StoreSeedData( |
| 189 const std::string& data, | |
| 190 const std::string& base64_seed_signature, | |
| 191 const std::string& country_code, | |
| 192 const base::Time& date_fetched, | |
| 193 bool is_delta_compressed, | |
| 194 variations::VariationsSeed* parsed_seed) { | |
| 195 if (!is_delta_compressed) { | |
| 196 return StoreSeedDataNoDelta(data, base64_seed_signature, country_code, | |
| 197 date_fetched, parsed_seed); | |
| 198 } | |
| 199 | |
| 200 // If the data is delta compressed, first decode it. | |
| 201 DCHECK(invalid_base64_signature_.empty()); | |
| 202 RecordSeedStoreHistogram(VARIATIONS_SEED_STORE_DELTA_COUNT); | |
| 203 | |
| 204 std::string existing_seed_data; | |
| 205 std::string updated_seed_data; | |
| 206 if (!ReadSeedData(&existing_seed_data)) { | |
| 207 RecordSeedStoreHistogram(VARIATIONS_SEED_STORE_FAILED_DELTA_READ_SEED); | |
| 208 return false; | |
| 209 } | |
| 210 if (!ApplyDeltaPatch(existing_seed_data, data, &updated_seed_data)) { | |
| 211 RecordSeedStoreHistogram(VARIATIONS_SEED_STORE_FAILED_DELTA_APPLY); | |
| 212 return false; | |
| 213 } | |
| 214 | |
| 215 const bool result = | |
| 216 StoreSeedDataNoDelta(updated_seed_data, base64_seed_signature, | |
| 217 country_code, date_fetched, parsed_seed); | |
| 218 if (result) { | |
| 219 // Note: |updated_seed_data.length()| is guaranteed to be non-zero, else | |
| 220 // result would be false. | |
| 221 int size_reduction = updated_seed_data.length() - data.length(); | |
| 222 UMA_HISTOGRAM_PERCENTAGE("Variations.SeedDelta.SizeReductionPercent", | |
| 223 100 * size_reduction / updated_seed_data.length()); | |
| 224 } else { | |
| 225 RecordSeedStoreHistogram(VARIATIONS_SEED_STORE_FAILED_DELTA_STORE); | |
| 226 } | |
| 227 return result; | |
| 228 } | |
| 229 | |
| 230 // TODO(asvitkine): Move this method down to match declaration order in a | |
|
rkaplow
2015/08/04 22:37:05
did you leave it as is as to make the diff more re
Alexei Svitkine (slow)
2015/08/05 15:46:15
Yep. :)
| |
| 231 // follow-up CL. | |
| 232 bool VariationsSeedStore::StoreSeedDataNoDelta( | |
| 182 const std::string& seed_data, | 233 const std::string& seed_data, |
| 183 const std::string& base64_seed_signature, | 234 const std::string& base64_seed_signature, |
| 235 const std::string& country_code, | |
| 184 const base::Time& date_fetched, | 236 const base::Time& date_fetched, |
| 185 variations::VariationsSeed* parsed_seed) { | 237 variations::VariationsSeed* parsed_seed) { |
| 186 if (seed_data.empty()) { | 238 if (seed_data.empty()) { |
| 187 RecordVariationsSeedStoreHistogram(VARIATIONS_SEED_STORE_FAILED_EMPTY); | 239 RecordSeedStoreHistogram(VARIATIONS_SEED_STORE_FAILED_EMPTY); |
| 188 return false; | 240 return false; |
| 189 } | 241 } |
| 190 | 242 |
| 191 // Only store the seed data if it parses correctly. | 243 // Only store the seed data if it parses correctly. |
| 192 variations::VariationsSeed seed; | 244 variations::VariationsSeed seed; |
| 193 if (!seed.ParseFromString(seed_data)) { | 245 if (!seed.ParseFromString(seed_data)) { |
| 194 RecordVariationsSeedStoreHistogram(VARIATIONS_SEED_STORE_FAILED_PARSE); | 246 RecordSeedStoreHistogram(VARIATIONS_SEED_STORE_FAILED_PARSE); |
| 195 return false; | 247 return false; |
| 196 } | 248 } |
| 197 | 249 |
| 198 const VerifySignatureResult result = | 250 const VerifySignatureResult result = |
| 199 VerifySeedSignature(seed_data, base64_seed_signature); | 251 VerifySeedSignature(seed_data, base64_seed_signature); |
| 200 if (result != VARIATIONS_SEED_SIGNATURE_ENUM_SIZE) { | 252 if (result != VARIATIONS_SEED_SIGNATURE_ENUM_SIZE) { |
| 201 UMA_HISTOGRAM_ENUMERATION("Variations.StoreSeedSignature", result, | 253 UMA_HISTOGRAM_ENUMERATION("Variations.StoreSeedSignature", result, |
| 202 VARIATIONS_SEED_SIGNATURE_ENUM_SIZE); | 254 VARIATIONS_SEED_SIGNATURE_ENUM_SIZE); |
| 203 if (result != VARIATIONS_SEED_SIGNATURE_VALID) { | 255 if (result != VARIATIONS_SEED_SIGNATURE_VALID) { |
| 204 RecordVariationsSeedStoreHistogram( | 256 RecordSeedStoreHistogram(VARIATIONS_SEED_STORE_FAILED_SIGNATURE); |
| 205 VARIATIONS_SEED_STORE_FAILED_SIGNATURE); | |
| 206 return false; | 257 return false; |
| 207 } | 258 } |
| 208 } | 259 } |
| 209 | 260 |
| 210 // Compress the seed before base64-encoding and storing. | 261 // Compress the seed before base64-encoding and storing. |
| 211 std::string compressed_seed_data; | 262 std::string compressed_seed_data; |
| 212 if (!metrics::GzipCompress(seed_data, &compressed_seed_data)) { | 263 if (!metrics::GzipCompress(seed_data, &compressed_seed_data)) { |
| 213 RecordVariationsSeedStoreHistogram(VARIATIONS_SEED_STORE_FAILED_GZIP); | 264 RecordSeedStoreHistogram(VARIATIONS_SEED_STORE_FAILED_GZIP); |
| 214 return false; | 265 return false; |
| 215 } | 266 } |
| 216 | 267 |
| 217 std::string base64_seed_data; | 268 std::string base64_seed_data; |
| 218 base::Base64Encode(compressed_seed_data, &base64_seed_data); | 269 base::Base64Encode(compressed_seed_data, &base64_seed_data); |
| 219 | 270 |
| 220 // TODO(asvitkine): This pref is no longer being used. Remove it completely | 271 // TODO(asvitkine): This pref is no longer being used. Remove it completely |
| 221 // in M45+. | 272 // in M45+. |
| 222 local_state_->ClearPref(prefs::kVariationsSeed); | 273 local_state_->ClearPref(prefs::kVariationsSeed); |
| 223 | 274 |
| 224 // Update the saved country code only if one was returned from the server. | 275 // Update the saved country code only if one was returned from the server. |
| 225 if (seed.has_country_code()) | 276 // Prefer the country code that was transmitted in the header over the one in |
| 277 // the seed (which is deprecated). | |
| 278 if (!country_code.empty()) | |
| 279 local_state_->SetString(prefs::kVariationsCountry, country_code); | |
| 280 else if (seed.has_country_code()) | |
| 226 local_state_->SetString(prefs::kVariationsCountry, seed.country_code()); | 281 local_state_->SetString(prefs::kVariationsCountry, seed.country_code()); |
| 227 | 282 |
| 228 local_state_->SetString(prefs::kVariationsCompressedSeed, base64_seed_data); | 283 local_state_->SetString(prefs::kVariationsCompressedSeed, base64_seed_data); |
| 229 UpdateSeedDateAndLogDayChange(date_fetched); | 284 UpdateSeedDateAndLogDayChange(date_fetched); |
| 230 local_state_->SetString(prefs::kVariationsSeedSignature, | 285 local_state_->SetString(prefs::kVariationsSeedSignature, |
| 231 base64_seed_signature); | 286 base64_seed_signature); |
| 232 variations_serial_number_ = seed.serial_number(); | 287 variations_serial_number_ = seed.serial_number(); |
| 233 if (parsed_seed) | 288 if (parsed_seed) |
| 234 seed.Swap(parsed_seed); | 289 seed.Swap(parsed_seed); |
| 235 | 290 |
| 236 RecordVariationsSeedStoreHistogram(VARIATIONS_SEED_STORE_SUCCESS); | 291 RecordSeedStoreHistogram(VARIATIONS_SEED_STORE_SUCCESS); |
| 237 return true; | 292 return true; |
| 238 } | 293 } |
| 239 | 294 |
| 240 void VariationsSeedStore::UpdateSeedDateAndLogDayChange( | 295 void VariationsSeedStore::UpdateSeedDateAndLogDayChange( |
| 241 const base::Time& server_date_fetched) { | 296 const base::Time& server_date_fetched) { |
| 242 VariationsSeedDateChangeState date_change = SEED_DATE_NO_OLD_DATE; | 297 VariationsSeedDateChangeState date_change = SEED_DATE_NO_OLD_DATE; |
| 243 | 298 |
| 244 if (local_state_->HasPrefPath(prefs::kVariationsSeedDate)) { | 299 if (local_state_->HasPrefPath(prefs::kVariationsSeedDate)) { |
| 245 const int64 stored_date_value = | 300 const int64 stored_date_value = |
| 246 local_state_->GetInt64(prefs::kVariationsSeedDate); | 301 local_state_->GetInt64(prefs::kVariationsSeedDate); |
| 247 const base::Time stored_date = | 302 const base::Time stored_date = |
| 248 base::Time::FromInternalValue(stored_date_value); | 303 base::Time::FromInternalValue(stored_date_value); |
| 249 | 304 |
| 250 date_change = GetSeedDateChangeState(server_date_fetched, stored_date); | 305 date_change = GetSeedDateChangeState(server_date_fetched, stored_date); |
| 251 } | 306 } |
| 252 | 307 |
| 253 UMA_HISTOGRAM_ENUMERATION("Variations.SeedDateChange", date_change, | 308 UMA_HISTOGRAM_ENUMERATION("Variations.SeedDateChange", date_change, |
| 254 SEED_DATE_ENUM_SIZE); | 309 SEED_DATE_ENUM_SIZE); |
| 255 | 310 |
| 256 local_state_->SetInt64(prefs::kVariationsSeedDate, | 311 local_state_->SetInt64(prefs::kVariationsSeedDate, |
| 257 server_date_fetched.ToInternalValue()); | 312 server_date_fetched.ToInternalValue()); |
| 258 } | 313 } |
| 259 | 314 |
| 315 | |
| 316 std::string VariationsSeedStore::GetInvalidSignature() const { | |
| 317 return invalid_base64_signature_; | |
| 318 } | |
| 319 | |
| 260 // static | 320 // static |
| 261 void VariationsSeedStore::RegisterPrefs(PrefRegistrySimple* registry) { | 321 void VariationsSeedStore::RegisterPrefs(PrefRegistrySimple* registry) { |
| 262 registry->RegisterStringPref(prefs::kVariationsCompressedSeed, std::string()); | 322 registry->RegisterStringPref(prefs::kVariationsCompressedSeed, std::string()); |
| 263 registry->RegisterStringPref(prefs::kVariationsSeed, std::string()); | 323 registry->RegisterStringPref(prefs::kVariationsSeed, std::string()); |
| 264 registry->RegisterInt64Pref(prefs::kVariationsSeedDate, | 324 registry->RegisterInt64Pref(prefs::kVariationsSeedDate, |
| 265 base::Time().ToInternalValue()); | 325 base::Time().ToInternalValue()); |
| 266 registry->RegisterStringPref(prefs::kVariationsSeedSignature, std::string()); | 326 registry->RegisterStringPref(prefs::kVariationsSeedSignature, std::string()); |
| 267 } | 327 } |
| 268 | 328 |
| 269 void VariationsSeedStore::ClearPrefs() { | 329 void VariationsSeedStore::ClearPrefs() { |
| (...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 327 return VARIATIONS_SEED_SIGNATURE_INVALID_SIGNATURE; | 387 return VARIATIONS_SEED_SIGNATURE_INVALID_SIGNATURE; |
| 328 } | 388 } |
| 329 | 389 |
| 330 verifier.VerifyUpdate(reinterpret_cast<const uint8*>(seed_bytes.data()), | 390 verifier.VerifyUpdate(reinterpret_cast<const uint8*>(seed_bytes.data()), |
| 331 seed_bytes.size()); | 391 seed_bytes.size()); |
| 332 if (verifier.VerifyFinal()) | 392 if (verifier.VerifyFinal()) |
| 333 return VARIATIONS_SEED_SIGNATURE_VALID; | 393 return VARIATIONS_SEED_SIGNATURE_VALID; |
| 334 return VARIATIONS_SEED_SIGNATURE_INVALID_SEED; | 394 return VARIATIONS_SEED_SIGNATURE_INVALID_SEED; |
| 335 } | 395 } |
| 336 | 396 |
| 337 std::string VariationsSeedStore::GetInvalidSignature() const { | 397 // static |
| 338 return invalid_base64_signature_; | 398 bool VariationsSeedStore::ApplyDeltaPatch(const std::string& existing_data, |
| 399 const std::string& patch, | |
| 400 std::string* output) { | |
| 401 output->clear(); | |
| 402 | |
| 403 google::protobuf::io::CodedInputStream in( | |
| 404 reinterpret_cast<const uint8*>(patch.data()), patch.length()); | |
| 405 // Temporary string declared outside the loop so it can be re-used between | |
| 406 // different iterations (rather than allocating new ones). | |
| 407 std::string temp; | |
| 408 | |
| 409 const uint32 existing_data_size = static_cast<uint32>(existing_data.size()); | |
| 410 while (in.CurrentPosition() != static_cast<int>(patch.length())) { | |
| 411 uint32 value; | |
| 412 if (!in.ReadVarint32(&value)) | |
| 413 return false; | |
| 414 | |
| 415 if (value == 0) { | |
|
rkaplow
2015/08/04 22:37:05
hm, maybe I'm not familiar enough with data diffs,
Alexei Svitkine (slow)
2015/08/05 15:46:15
It's actually just the format of the diff produced
| |
| 416 uint32 offset; | |
| 417 uint32 length; | |
| 418 if (!in.ReadVarint32(&offset) || !in.ReadVarint32(&length)) | |
| 419 return false; | |
| 420 | |
| 421 // Check for |offset + length| being out of range and for overflow. | |
| 422 base::CheckedNumeric<uint32> end_offset(offset); | |
| 423 end_offset += length; | |
| 424 if (!end_offset.IsValid() || end_offset.ValueOrDie() > existing_data_size) | |
| 425 return false; | |
| 426 output->append(existing_data, offset, length); | |
| 427 } else { | |
| 428 // No need to guard against bad data (i.e. very large |value|) because the | |
| 429 // call below will fail if |value| is greater than the size of the patch. | |
| 430 if (!in.ReadString(&temp, value)) | |
| 431 return false; | |
| 432 output->append(temp); | |
| 433 } | |
| 434 } | |
| 435 return true; | |
| 339 } | 436 } |
| 340 | 437 |
| 341 } // namespace chrome_variations | 438 } // namespace chrome_variations |
| OLD | NEW |