Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(213)

Side by Side Diff: chrome/browser/metrics/variations/variations_seed_store.cc

Issue 1200233005: Support delta-compressed variations server responses. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Fixing missing return in !is_compressed case. Created 5 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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 // DELTA_COUNT is not so much a result of the seed store, but rather counting
88 // the number of delta-compressed seeds the SeedStore() function saw. Kept in
89 // the same histogram for convenience of comparing against the other values.
90 VARIATIONS_SEED_STORE_DELTA_COUNT,
91 VARIATIONS_SEED_STORE_FAILED_DELTA_READ_SEED,
92 VARIATIONS_SEED_STORE_FAILED_DELTA_APPLY,
93 VARIATIONS_SEED_STORE_FAILED_DELTA_STORE,
85 VARIATIONS_SEED_STORE_RESULT_ENUM_SIZE, 94 VARIATIONS_SEED_STORE_RESULT_ENUM_SIZE,
86 }; 95 };
87 96
88 void RecordVariationsSeedStoreHistogram(VariationsSeedStoreResult result) { 97 void RecordSeedStoreHistogram(VariationsSeedStoreResult result) {
89 UMA_HISTOGRAM_ENUMERATION("Variations.SeedStoreResult", result, 98 UMA_HISTOGRAM_ENUMERATION("Variations.SeedStoreResult", result,
90 VARIATIONS_SEED_STORE_RESULT_ENUM_SIZE); 99 VARIATIONS_SEED_STORE_RESULT_ENUM_SIZE);
91 } 100 }
92 101
93 // Note: UMA histogram enum - don't re-order or remove entries. 102 // Note: UMA histogram enum - don't re-order or remove entries.
94 enum VariationsSeedDateChangeState { 103 enum VariationsSeedDateChangeState {
95 SEED_DATE_NO_OLD_DATE, 104 SEED_DATE_NO_OLD_DATE,
96 SEED_DATE_NEW_DATE_OLDER, 105 SEED_DATE_NEW_DATE_OLDER,
97 SEED_DATE_SAME_DAY, 106 SEED_DATE_SAME_DAY,
98 SEED_DATE_NEW_DAY, 107 SEED_DATE_NEW_DAY,
(...skipping 25 matching lines...) Expand all
124 // The server date is earlier than the stored date, and they are from 133 // 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. 134 // different UTC days, so |server_seed_date| is a valid new day.
126 return SEED_DATE_NEW_DAY; 135 return SEED_DATE_NEW_DAY;
127 } 136 }
128 return SEED_DATE_SAME_DAY; 137 return SEED_DATE_SAME_DAY;
129 } 138 }
130 139
131 } // namespace 140 } // namespace
132 141
133 VariationsSeedStore::VariationsSeedStore(PrefService* local_state) 142 VariationsSeedStore::VariationsSeedStore(PrefService* local_state)
134 : local_state_(local_state) { 143 : local_state_(local_state), seed_has_country_code_(false) {
135 } 144 }
136 145
137 VariationsSeedStore::~VariationsSeedStore() { 146 VariationsSeedStore::~VariationsSeedStore() {
138 } 147 }
139 148
140 bool VariationsSeedStore::LoadSeed(variations::VariationsSeed* seed) { 149 bool VariationsSeedStore::LoadSeed(variations::VariationsSeed* seed) {
141 invalid_base64_signature_.clear(); 150 invalid_base64_signature_.clear();
142 151
143 std::string seed_data; 152 std::string seed_data;
144 if (!ReadSeedData(&seed_data)) 153 if (!ReadSeedData(&seed_data))
(...skipping 22 matching lines...) Expand all
167 } 176 }
168 177
169 // Migrate any existing country code from the seed to the pref, if the pref is 178 // 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 179 // empty. TODO(asvitkine): Clean up the code in M50+ when sufficient number
171 // of clients have migrated. 180 // of clients have migrated.
172 if (seed->has_country_code() && 181 if (seed->has_country_code() &&
173 local_state_->GetString(prefs::kVariationsCountry).empty()) { 182 local_state_->GetString(prefs::kVariationsCountry).empty()) {
174 local_state_->SetString(prefs::kVariationsCountry, seed->country_code()); 183 local_state_->SetString(prefs::kVariationsCountry, seed->country_code());
175 } 184 }
176 variations_serial_number_ = seed->serial_number(); 185 variations_serial_number_ = seed->serial_number();
186 seed_has_country_code_ = seed->has_country_code();
177 RecordVariationSeedEmptyHistogram(VARIATIONS_SEED_NOT_EMPTY); 187 RecordVariationSeedEmptyHistogram(VARIATIONS_SEED_NOT_EMPTY);
178 return true; 188 return true;
179 } 189 }
180 190
181 bool VariationsSeedStore::StoreSeedData( 191 bool VariationsSeedStore::StoreSeedData(
192 const std::string& data,
193 const std::string& base64_seed_signature,
194 const std::string& country_code,
195 const base::Time& date_fetched,
196 bool is_delta_compressed,
197 variations::VariationsSeed* parsed_seed) {
198 if (!is_delta_compressed) {
199 const bool result =
200 StoreSeedDataNoDelta(data, base64_seed_signature, country_code,
201 date_fetched, parsed_seed);
202 if (result) {
203 UMA_HISTOGRAM_COUNTS_1000("Variations.StoreSeed.Size",
204 data.length() / 1024);
205 }
206 return result;
207 }
208
209 // If the data is delta compressed, first decode it.
210 DCHECK(invalid_base64_signature_.empty());
211 RecordSeedStoreHistogram(VARIATIONS_SEED_STORE_DELTA_COUNT);
212
213 std::string existing_seed_data;
214 std::string updated_seed_data;
215 if (!ReadSeedData(&existing_seed_data)) {
216 RecordSeedStoreHistogram(VARIATIONS_SEED_STORE_FAILED_DELTA_READ_SEED);
217 return false;
218 }
219 if (!ApplyDeltaPatch(existing_seed_data, data, &updated_seed_data)) {
220 RecordSeedStoreHistogram(VARIATIONS_SEED_STORE_FAILED_DELTA_APPLY);
221 return false;
222 }
223
224 const bool result =
225 StoreSeedDataNoDelta(updated_seed_data, base64_seed_signature,
226 country_code, date_fetched, parsed_seed);
227 if (result) {
228 // Note: |updated_seed_data.length()| is guaranteed to be non-zero, else
229 // result would be false.
230 int size_reduction = updated_seed_data.length() - data.length();
231 UMA_HISTOGRAM_PERCENTAGE("Variations.StoreSeed.DeltaSize.ReductionPercent",
232 100 * size_reduction / updated_seed_data.length());
233 UMA_HISTOGRAM_COUNTS_1000("Variations.StoreSeed.DeltaSize",
234 data.length() / 1024);
235 } else {
236 RecordSeedStoreHistogram(VARIATIONS_SEED_STORE_FAILED_DELTA_STORE);
237 }
238 return result;
239 }
240
241 // TODO(asvitkine): Move this method down to match declaration order in a
242 // follow-up CL.
243 bool VariationsSeedStore::StoreSeedDataNoDelta(
182 const std::string& seed_data, 244 const std::string& seed_data,
183 const std::string& base64_seed_signature, 245 const std::string& base64_seed_signature,
246 const std::string& country_code,
184 const base::Time& date_fetched, 247 const base::Time& date_fetched,
185 variations::VariationsSeed* parsed_seed) { 248 variations::VariationsSeed* parsed_seed) {
186 if (seed_data.empty()) { 249 if (seed_data.empty()) {
187 RecordVariationsSeedStoreHistogram(VARIATIONS_SEED_STORE_FAILED_EMPTY); 250 RecordSeedStoreHistogram(VARIATIONS_SEED_STORE_FAILED_EMPTY);
188 return false; 251 return false;
189 } 252 }
190 253
191 // Only store the seed data if it parses correctly. 254 // Only store the seed data if it parses correctly.
192 variations::VariationsSeed seed; 255 variations::VariationsSeed seed;
193 if (!seed.ParseFromString(seed_data)) { 256 if (!seed.ParseFromString(seed_data)) {
194 RecordVariationsSeedStoreHistogram(VARIATIONS_SEED_STORE_FAILED_PARSE); 257 RecordSeedStoreHistogram(VARIATIONS_SEED_STORE_FAILED_PARSE);
195 return false; 258 return false;
196 } 259 }
197 260
198 const VerifySignatureResult result = 261 const VerifySignatureResult result =
199 VerifySeedSignature(seed_data, base64_seed_signature); 262 VerifySeedSignature(seed_data, base64_seed_signature);
200 if (result != VARIATIONS_SEED_SIGNATURE_ENUM_SIZE) { 263 if (result != VARIATIONS_SEED_SIGNATURE_ENUM_SIZE) {
201 UMA_HISTOGRAM_ENUMERATION("Variations.StoreSeedSignature", result, 264 UMA_HISTOGRAM_ENUMERATION("Variations.StoreSeedSignature", result,
202 VARIATIONS_SEED_SIGNATURE_ENUM_SIZE); 265 VARIATIONS_SEED_SIGNATURE_ENUM_SIZE);
203 if (result != VARIATIONS_SEED_SIGNATURE_VALID) { 266 if (result != VARIATIONS_SEED_SIGNATURE_VALID) {
204 RecordVariationsSeedStoreHistogram( 267 RecordSeedStoreHistogram(VARIATIONS_SEED_STORE_FAILED_SIGNATURE);
205 VARIATIONS_SEED_STORE_FAILED_SIGNATURE);
206 return false; 268 return false;
207 } 269 }
208 } 270 }
209 271
210 // Compress the seed before base64-encoding and storing. 272 // Compress the seed before base64-encoding and storing.
211 std::string compressed_seed_data; 273 std::string compressed_seed_data;
212 if (!metrics::GzipCompress(seed_data, &compressed_seed_data)) { 274 if (!metrics::GzipCompress(seed_data, &compressed_seed_data)) {
213 RecordVariationsSeedStoreHistogram(VARIATIONS_SEED_STORE_FAILED_GZIP); 275 RecordSeedStoreHistogram(VARIATIONS_SEED_STORE_FAILED_GZIP);
214 return false; 276 return false;
215 } 277 }
216 278
217 std::string base64_seed_data; 279 std::string base64_seed_data;
218 base::Base64Encode(compressed_seed_data, &base64_seed_data); 280 base::Base64Encode(compressed_seed_data, &base64_seed_data);
219 281
220 // TODO(asvitkine): This pref is no longer being used. Remove it completely 282 // TODO(asvitkine): This pref is no longer being used. Remove it completely
221 // in M45+. 283 // in M45+.
222 local_state_->ClearPref(prefs::kVariationsSeed); 284 local_state_->ClearPref(prefs::kVariationsSeed);
223 285
224 // Update the saved country code only if one was returned from the server. 286 // Update the saved country code only if one was returned from the server.
225 if (seed.has_country_code()) 287 // Prefer the country code that was transmitted in the header over the one in
288 // the seed (which is deprecated).
289 if (!country_code.empty())
290 local_state_->SetString(prefs::kVariationsCountry, country_code);
291 else if (seed.has_country_code())
226 local_state_->SetString(prefs::kVariationsCountry, seed.country_code()); 292 local_state_->SetString(prefs::kVariationsCountry, seed.country_code());
227 293
228 local_state_->SetString(prefs::kVariationsCompressedSeed, base64_seed_data); 294 local_state_->SetString(prefs::kVariationsCompressedSeed, base64_seed_data);
229 UpdateSeedDateAndLogDayChange(date_fetched); 295 UpdateSeedDateAndLogDayChange(date_fetched);
230 local_state_->SetString(prefs::kVariationsSeedSignature, 296 local_state_->SetString(prefs::kVariationsSeedSignature,
231 base64_seed_signature); 297 base64_seed_signature);
232 variations_serial_number_ = seed.serial_number(); 298 variations_serial_number_ = seed.serial_number();
233 if (parsed_seed) 299 if (parsed_seed)
234 seed.Swap(parsed_seed); 300 seed.Swap(parsed_seed);
235 301
236 RecordVariationsSeedStoreHistogram(VARIATIONS_SEED_STORE_SUCCESS); 302 RecordSeedStoreHistogram(VARIATIONS_SEED_STORE_SUCCESS);
237 return true; 303 return true;
238 } 304 }
239 305
240 void VariationsSeedStore::UpdateSeedDateAndLogDayChange( 306 void VariationsSeedStore::UpdateSeedDateAndLogDayChange(
241 const base::Time& server_date_fetched) { 307 const base::Time& server_date_fetched) {
242 VariationsSeedDateChangeState date_change = SEED_DATE_NO_OLD_DATE; 308 VariationsSeedDateChangeState date_change = SEED_DATE_NO_OLD_DATE;
243 309
244 if (local_state_->HasPrefPath(prefs::kVariationsSeedDate)) { 310 if (local_state_->HasPrefPath(prefs::kVariationsSeedDate)) {
245 const int64 stored_date_value = 311 const int64 stored_date_value =
246 local_state_->GetInt64(prefs::kVariationsSeedDate); 312 local_state_->GetInt64(prefs::kVariationsSeedDate);
247 const base::Time stored_date = 313 const base::Time stored_date =
248 base::Time::FromInternalValue(stored_date_value); 314 base::Time::FromInternalValue(stored_date_value);
249 315
250 date_change = GetSeedDateChangeState(server_date_fetched, stored_date); 316 date_change = GetSeedDateChangeState(server_date_fetched, stored_date);
251 } 317 }
252 318
253 UMA_HISTOGRAM_ENUMERATION("Variations.SeedDateChange", date_change, 319 UMA_HISTOGRAM_ENUMERATION("Variations.SeedDateChange", date_change,
254 SEED_DATE_ENUM_SIZE); 320 SEED_DATE_ENUM_SIZE);
255 321
256 local_state_->SetInt64(prefs::kVariationsSeedDate, 322 local_state_->SetInt64(prefs::kVariationsSeedDate,
257 server_date_fetched.ToInternalValue()); 323 server_date_fetched.ToInternalValue());
258 } 324 }
259 325
326
327 std::string VariationsSeedStore::GetInvalidSignature() const {
328 return invalid_base64_signature_;
329 }
330
260 // static 331 // static
261 void VariationsSeedStore::RegisterPrefs(PrefRegistrySimple* registry) { 332 void VariationsSeedStore::RegisterPrefs(PrefRegistrySimple* registry) {
262 registry->RegisterStringPref(prefs::kVariationsCompressedSeed, std::string()); 333 registry->RegisterStringPref(prefs::kVariationsCompressedSeed, std::string());
263 registry->RegisterStringPref(prefs::kVariationsSeed, std::string()); 334 registry->RegisterStringPref(prefs::kVariationsSeed, std::string());
264 registry->RegisterInt64Pref(prefs::kVariationsSeedDate, 335 registry->RegisterInt64Pref(prefs::kVariationsSeedDate,
265 base::Time().ToInternalValue()); 336 base::Time().ToInternalValue());
266 registry->RegisterStringPref(prefs::kVariationsSeedSignature, std::string()); 337 registry->RegisterStringPref(prefs::kVariationsSeedSignature, std::string());
267 registry->RegisterStringPref(prefs::kVariationsCountry, std::string()); 338 registry->RegisterStringPref(prefs::kVariationsCountry, std::string());
268 } 339 }
269 340
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after
328 return VARIATIONS_SEED_SIGNATURE_INVALID_SIGNATURE; 399 return VARIATIONS_SEED_SIGNATURE_INVALID_SIGNATURE;
329 } 400 }
330 401
331 verifier.VerifyUpdate(reinterpret_cast<const uint8*>(seed_bytes.data()), 402 verifier.VerifyUpdate(reinterpret_cast<const uint8*>(seed_bytes.data()),
332 seed_bytes.size()); 403 seed_bytes.size());
333 if (verifier.VerifyFinal()) 404 if (verifier.VerifyFinal())
334 return VARIATIONS_SEED_SIGNATURE_VALID; 405 return VARIATIONS_SEED_SIGNATURE_VALID;
335 return VARIATIONS_SEED_SIGNATURE_INVALID_SEED; 406 return VARIATIONS_SEED_SIGNATURE_INVALID_SEED;
336 } 407 }
337 408
338 std::string VariationsSeedStore::GetInvalidSignature() const { 409 // static
339 return invalid_base64_signature_; 410 bool VariationsSeedStore::ApplyDeltaPatch(const std::string& existing_data,
411 const std::string& patch,
412 std::string* output) {
413 output->clear();
414
415 google::protobuf::io::CodedInputStream in(
416 reinterpret_cast<const uint8*>(patch.data()), patch.length());
417 // Temporary string declared outside the loop so it can be re-used between
418 // different iterations (rather than allocating new ones).
419 std::string temp;
420
421 const uint32 existing_data_size = static_cast<uint32>(existing_data.size());
422 while (in.CurrentPosition() != static_cast<int>(patch.length())) {
423 uint32 value;
424 if (!in.ReadVarint32(&value))
425 return false;
426
427 if (value != 0) {
428 // A non-zero value indicates the number of bytes to copy from the patch
429 // stream to the output.
430
431 // No need to guard against bad data (i.e. very large |value|) because the
432 // call below will fail if |value| is greater than the size of the patch.
433 if (!in.ReadString(&temp, value))
434 return false;
435 output->append(temp);
436 } else {
437 // Otherwise, when it's zero, it indicates that it's followed by a pair of
438 // numbers - |offset| and |length| that specify a range of data to copy
439 // from |existing_data|.
440 uint32 offset;
441 uint32 length;
442 if (!in.ReadVarint32(&offset) || !in.ReadVarint32(&length))
443 return false;
444
445 // Check for |offset + length| being out of range and for overflow.
446 base::CheckedNumeric<uint32> end_offset(offset);
447 end_offset += length;
448 if (!end_offset.IsValid() || end_offset.ValueOrDie() > existing_data_size)
449 return false;
450 output->append(existing_data, offset, length);
451 }
452 }
453 return true;
340 } 454 }
341 455
342 } // namespace chrome_variations 456 } // namespace chrome_variations
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698