| 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 "components/variations/variations_seed_store.h" | 5 #include "components/variations/variations_seed_store.h" |
| 6 | 6 |
| 7 #include <stdint.h> | 7 #include <stdint.h> |
| 8 | 8 |
| 9 #include "base/base64.h" | 9 #include "base/base64.h" |
| 10 #include "base/macros.h" | 10 #include "base/macros.h" |
| (...skipping 11 matching lines...) Expand all Loading... |
| 22 #include "third_party/zlib/google/compression_utils.h" | 22 #include "third_party/zlib/google/compression_utils.h" |
| 23 | 23 |
| 24 #if defined(OS_ANDROID) | 24 #if defined(OS_ANDROID) |
| 25 #include "components/variations/android/variations_seed_bridge.h" | 25 #include "components/variations/android/variations_seed_bridge.h" |
| 26 #endif // OS_ANDROID | 26 #endif // OS_ANDROID |
| 27 | 27 |
| 28 namespace variations { | 28 namespace variations { |
| 29 | 29 |
| 30 namespace { | 30 namespace { |
| 31 | 31 |
| 32 // Signature verification is disabled on mobile platforms for now, since it | |
| 33 // adds about ~15ms to the startup time on mobile (vs. a couple ms on desktop). | |
| 34 bool SignatureVerificationEnabled() { | |
| 35 #if defined(OS_IOS) || defined(OS_ANDROID) | |
| 36 return false; | |
| 37 #else | |
| 38 return true; | |
| 39 #endif | |
| 40 } | |
| 41 | |
| 42 // The ECDSA public key of the variations server for verifying variations seed | 32 // The ECDSA public key of the variations server for verifying variations seed |
| 43 // signatures. | 33 // signatures. |
| 44 const uint8_t kPublicKey[] = { | 34 const uint8_t kPublicKey[] = { |
| 45 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, | 35 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, |
| 46 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00, | 36 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00, |
| 47 0x04, 0x51, 0x7c, 0x31, 0x4b, 0x50, 0x42, 0xdd, 0x59, 0xda, 0x0b, 0xfa, 0x43, | 37 0x04, 0x51, 0x7c, 0x31, 0x4b, 0x50, 0x42, 0xdd, 0x59, 0xda, 0x0b, 0xfa, 0x43, |
| 48 0x44, 0x33, 0x7c, 0x5f, 0xa1, 0x0b, 0xd5, 0x82, 0xf6, 0xac, 0x04, 0x19, 0x72, | 38 0x44, 0x33, 0x7c, 0x5f, 0xa1, 0x0b, 0xd5, 0x82, 0xf6, 0xac, 0x04, 0x19, 0x72, |
| 49 0x6c, 0x40, 0xd4, 0x3e, 0x56, 0xe2, 0xa0, 0x80, 0xa0, 0x41, 0xb3, 0x23, 0x7b, | 39 0x6c, 0x40, 0xd4, 0x3e, 0x56, 0xe2, 0xa0, 0x80, 0xa0, 0x41, 0xb3, 0x23, 0x7b, |
| 50 0x71, 0xc9, 0x80, 0x87, 0xde, 0x35, 0x0d, 0x25, 0x71, 0x09, 0x7f, 0xb4, 0x15, | 40 0x71, 0xc9, 0x80, 0x87, 0xde, 0x35, 0x0d, 0x25, 0x71, 0x09, 0x7f, 0xb4, 0x15, |
| 51 0x2b, 0xff, 0x82, 0x4d, 0xd3, 0xfe, 0xc5, 0xef, 0x20, 0xc6, 0xa3, 0x10, 0xbf, | 41 0x2b, 0xff, 0x82, 0x4d, 0xd3, 0xfe, 0xc5, 0xef, 0x20, 0xc6, 0xa3, 0x10, 0xbf, |
| 52 }; | 42 }; |
| 53 | 43 |
| 54 // Note: UMA histogram enum - don't re-order or remove entries. | 44 // Verifies a variations seed (the serialized proto bytes) with the specified |
| 55 enum VariationSeedEmptyState { | 45 // base-64 encoded signature that was received from the server and returns the |
| 56 VARIATIONS_SEED_NOT_EMPTY, | 46 // result. The signature is assumed to be an "ECDSA with SHA-256" signature |
| 57 VARIATIONS_SEED_EMPTY, | 47 // (see kECDSAWithSHA256AlgorithmID in the .cc file). Returns the result of |
| 58 VARIATIONS_SEED_CORRUPT, | 48 // signature verification. |
| 59 VARIATIONS_SEED_INVALID_SIGNATURE, | 49 VerifySignatureResult VerifySeedSignature( |
| 60 VARIATIONS_SEED_CORRUPT_BASE64, | 50 const std::string& seed_bytes, |
| 61 VARIATIONS_SEED_CORRUPT_PROTOBUF, | 51 const std::string& base64_seed_signature) { |
| 62 VARIATIONS_SEED_CORRUPT_GZIP, | 52 if (base64_seed_signature.empty()) |
| 63 VARIATIONS_SEED_EMPTY_ENUM_SIZE, | 53 return VerifySignatureResult::MISSING_SIGNATURE; |
| 64 }; | |
| 65 | 54 |
| 66 void RecordVariationSeedEmptyHistogram(VariationSeedEmptyState state) { | 55 std::string signature; |
| 67 UMA_HISTOGRAM_ENUMERATION("Variations.SeedEmpty", state, | 56 if (!base::Base64Decode(base64_seed_signature, &signature)) |
| 68 VARIATIONS_SEED_EMPTY_ENUM_SIZE); | 57 return VerifySignatureResult::DECODE_FAILED; |
| 58 |
| 59 crypto::SignatureVerifier verifier; |
| 60 if (!verifier.VerifyInit(crypto::SignatureVerifier::ECDSA_SHA256, |
| 61 reinterpret_cast<const uint8_t*>(signature.data()), |
| 62 signature.size(), kPublicKey, |
| 63 arraysize(kPublicKey))) { |
| 64 return VerifySignatureResult::INVALID_SIGNATURE; |
| 65 } |
| 66 |
| 67 verifier.VerifyUpdate(reinterpret_cast<const uint8_t*>(seed_bytes.data()), |
| 68 seed_bytes.size()); |
| 69 if (!verifier.VerifyFinal()) |
| 70 return VerifySignatureResult::INVALID_SEED; |
| 71 |
| 72 return VerifySignatureResult::VALID_SIGNATURE; |
| 69 } | 73 } |
| 70 | 74 |
| 71 enum VariationsSeedStoreResult { | |
| 72 VARIATIONS_SEED_STORE_SUCCESS, | |
| 73 VARIATIONS_SEED_STORE_FAILED_EMPTY, | |
| 74 VARIATIONS_SEED_STORE_FAILED_PARSE, | |
| 75 VARIATIONS_SEED_STORE_FAILED_SIGNATURE, | |
| 76 VARIATIONS_SEED_STORE_FAILED_GZIP, | |
| 77 // DELTA_COUNT is not so much a result of the seed store, but rather counting | |
| 78 // the number of delta-compressed seeds the SeedStore() function saw. Kept in | |
| 79 // the same histogram for convenience of comparing against the other values. | |
| 80 VARIATIONS_SEED_STORE_DELTA_COUNT, | |
| 81 VARIATIONS_SEED_STORE_FAILED_DELTA_READ_SEED, | |
| 82 VARIATIONS_SEED_STORE_FAILED_DELTA_APPLY, | |
| 83 VARIATIONS_SEED_STORE_FAILED_DELTA_STORE, | |
| 84 VARIATIONS_SEED_STORE_FAILED_UNGZIP, | |
| 85 VARIATIONS_SEED_STORE_FAILED_EMPTY_GZIP_CONTENTS, | |
| 86 VARIATIONS_SEED_STORE_FAILED_UNSUPPORTED_SEED_FORMAT, | |
| 87 VARIATIONS_SEED_STORE_RESULT_ENUM_SIZE, | |
| 88 }; | |
| 89 | |
| 90 void RecordSeedStoreHistogram(VariationsSeedStoreResult result) { | |
| 91 UMA_HISTOGRAM_ENUMERATION("Variations.SeedStoreResult", result, | |
| 92 VARIATIONS_SEED_STORE_RESULT_ENUM_SIZE); | |
| 93 } | |
| 94 | |
| 95 // Note: UMA histogram enum - don't re-order or remove entries. | |
| 96 enum VariationsSeedDateChangeState { | |
| 97 SEED_DATE_NO_OLD_DATE, | |
| 98 SEED_DATE_NEW_DATE_OLDER, | |
| 99 SEED_DATE_SAME_DAY, | |
| 100 SEED_DATE_NEW_DAY, | |
| 101 SEED_DATE_ENUM_SIZE, | |
| 102 }; | |
| 103 | |
| 104 // Truncates a time to the start of the day in UTC. If given a time representing | 75 // Truncates a time to the start of the day in UTC. If given a time representing |
| 105 // 2014-03-11 10:18:03.1 UTC, it will return a time representing | 76 // 2014-03-11 10:18:03.1 UTC, it will return a time representing |
| 106 // 2014-03-11 00:00:00.0 UTC. | 77 // 2014-03-11 00:00:00.0 UTC. |
| 107 base::Time TruncateToUTCDay(const base::Time& time) { | 78 base::Time TruncateToUTCDay(const base::Time& time) { |
| 108 base::Time::Exploded exploded; | 79 base::Time::Exploded exploded; |
| 109 time.UTCExplode(&exploded); | 80 time.UTCExplode(&exploded); |
| 110 exploded.hour = 0; | 81 exploded.hour = 0; |
| 111 exploded.minute = 0; | 82 exploded.minute = 0; |
| 112 exploded.second = 0; | 83 exploded.second = 0; |
| 113 exploded.millisecond = 0; | 84 exploded.millisecond = 0; |
| 114 | 85 |
| 115 base::Time out_time; | 86 base::Time out_time; |
| 116 bool conversion_success = base::Time::FromUTCExploded(exploded, &out_time); | 87 bool conversion_success = base::Time::FromUTCExploded(exploded, &out_time); |
| 117 DCHECK(conversion_success); | 88 DCHECK(conversion_success); |
| 118 return out_time; | 89 return out_time; |
| 119 } | 90 } |
| 120 | 91 |
| 121 VariationsSeedDateChangeState GetSeedDateChangeState( | 92 UpdateSeedDateResult GetSeedDateChangeState( |
| 122 const base::Time& server_seed_date, | 93 const base::Time& server_seed_date, |
| 123 const base::Time& stored_seed_date) { | 94 const base::Time& stored_seed_date) { |
| 124 if (server_seed_date < stored_seed_date) | 95 if (server_seed_date < stored_seed_date) |
| 125 return SEED_DATE_NEW_DATE_OLDER; | 96 return UpdateSeedDateResult::NEW_DATE_IS_OLDER; |
| 126 | 97 |
| 127 if (TruncateToUTCDay(server_seed_date) != | 98 if (TruncateToUTCDay(server_seed_date) != |
| 128 TruncateToUTCDay(stored_seed_date)) { | 99 TruncateToUTCDay(stored_seed_date)) { |
| 129 // The server date is earlier than the stored date, and they are from | 100 // The server date is later than the stored date, and they are from |
| 130 // different UTC days, so |server_seed_date| is a valid new day. | 101 // different UTC days, so |server_seed_date| is a valid new day. |
| 131 return SEED_DATE_NEW_DAY; | 102 return UpdateSeedDateResult::NEW_DAY; |
| 132 } | 103 } |
| 133 return SEED_DATE_SAME_DAY; | 104 return UpdateSeedDateResult::SAME_DAY; |
| 134 } | 105 } |
| 135 | 106 |
| 136 #if defined(OS_ANDROID) | |
| 137 enum FirstRunResult { | |
| 138 FIRST_RUN_SEED_IMPORT_SUCCESS, | |
| 139 FIRST_RUN_SEED_IMPORT_FAIL_NO_CALLBACK, | |
| 140 FIRST_RUN_SEED_IMPORT_FAIL_NO_FIRST_RUN_SEED, | |
| 141 FIRST_RUN_SEED_IMPORT_FAIL_STORE_FAILED, | |
| 142 FIRST_RUN_SEED_IMPORT_FAIL_INVALID_RESPONSE_DATE, | |
| 143 FIRST_RUN_RESULT_ENUM_SIZE, | |
| 144 }; | |
| 145 | |
| 146 void RecordFirstRunResult(FirstRunResult result) { | |
| 147 UMA_HISTOGRAM_ENUMERATION("Variations.FirstRunResult", result, | |
| 148 FIRST_RUN_RESULT_ENUM_SIZE); | |
| 149 } | |
| 150 #endif // OS_ANDROID | |
| 151 | |
| 152 } // namespace | 107 } // namespace |
| 153 | 108 |
| 154 VariationsSeedStore::VariationsSeedStore(PrefService* local_state) | 109 VariationsSeedStore::VariationsSeedStore(PrefService* local_state) |
| 155 : local_state_(local_state) {} | 110 : local_state_(local_state) {} |
| 156 | 111 |
| 157 VariationsSeedStore::~VariationsSeedStore() { | 112 VariationsSeedStore::~VariationsSeedStore() { |
| 158 } | 113 } |
| 159 | 114 |
| 160 bool VariationsSeedStore::LoadSeed(VariationsSeed* seed) { | 115 bool VariationsSeedStore::LoadSeed(VariationsSeed* seed) { |
| 161 invalid_base64_signature_.clear(); | 116 invalid_base64_signature_.clear(); |
| 162 | 117 |
| 163 #if defined(OS_ANDROID) | 118 #if defined(OS_ANDROID) |
| 164 if (!local_state_->HasPrefPath(prefs::kVariationsSeedSignature)) | 119 if (!local_state_->HasPrefPath(prefs::kVariationsSeedSignature)) |
| 165 ImportFirstRunJavaSeed(); | 120 ImportFirstRunJavaSeed(); |
| 166 #endif // OS_ANDROID | 121 #endif // OS_ANDROID |
| 167 | 122 |
| 168 std::string seed_data; | 123 std::string seed_data; |
| 169 if (!ReadSeedData(&seed_data)) | 124 LoadSeedResult read_result = ReadSeedData(&seed_data); |
| 125 if (read_result != LoadSeedResult::SUCCESS) { |
| 126 RecordLoadSeedResult(read_result); |
| 170 return false; | 127 return false; |
| 128 } |
| 171 | 129 |
| 172 const std::string base64_seed_signature = | 130 if (SignatureVerificationEnabled()) { |
| 173 local_state_->GetString(prefs::kVariationsSeedSignature); | 131 const std::string base64_signature = |
| 174 const VerifySignatureResult result = | 132 local_state_->GetString(prefs::kVariationsSeedSignature); |
| 175 VerifySeedSignature(seed_data, base64_seed_signature); | 133 |
| 176 if (result != VARIATIONS_SEED_SIGNATURE_ENUM_SIZE) { | 134 const VerifySignatureResult result = |
| 135 VerifySeedSignature(seed_data, base64_signature); |
| 177 UMA_HISTOGRAM_ENUMERATION("Variations.LoadSeedSignature", result, | 136 UMA_HISTOGRAM_ENUMERATION("Variations.LoadSeedSignature", result, |
| 178 VARIATIONS_SEED_SIGNATURE_ENUM_SIZE); | 137 VerifySignatureResult::ENUM_SIZE); |
| 179 if (result != VARIATIONS_SEED_SIGNATURE_VALID) { | 138 if (result != VerifySignatureResult::VALID_SIGNATURE) { |
| 139 // Record the invalid signature. |
| 140 invalid_base64_signature_ = base64_signature; |
| 180 ClearPrefs(); | 141 ClearPrefs(); |
| 181 RecordVariationSeedEmptyHistogram(VARIATIONS_SEED_INVALID_SIGNATURE); | 142 RecordLoadSeedResult(LoadSeedResult::INVALID_SIGNATURE); |
| 182 // Record the invalid signature. | |
| 183 invalid_base64_signature_ = base64_seed_signature; | |
| 184 return false; | 143 return false; |
| 185 } | 144 } |
| 186 } | 145 } |
| 187 | 146 |
| 188 if (!seed->ParseFromString(seed_data)) { | 147 if (!seed->ParseFromString(seed_data)) { |
| 189 ClearPrefs(); | 148 ClearPrefs(); |
| 190 RecordVariationSeedEmptyHistogram(VARIATIONS_SEED_CORRUPT_PROTOBUF); | 149 RecordLoadSeedResult(LoadSeedResult::CORRUPT_PROTOBUF); |
| 191 return false; | 150 return false; |
| 192 } | 151 } |
| 193 | 152 |
| 194 variations_serial_number_ = seed->serial_number(); | 153 variations_serial_number_ = seed->serial_number(); |
| 195 RecordVariationSeedEmptyHistogram(VARIATIONS_SEED_NOT_EMPTY); | 154 RecordLoadSeedResult(LoadSeedResult::SUCCESS); |
| 196 return true; | 155 return true; |
| 197 } | 156 } |
| 198 | 157 |
| 199 bool VariationsSeedStore::StoreSeedData( | 158 bool VariationsSeedStore::StoreSeedData( |
| 200 const std::string& data, | 159 const std::string& data, |
| 201 const std::string& base64_seed_signature, | 160 const std::string& base64_seed_signature, |
| 202 const std::string& country_code, | 161 const std::string& country_code, |
| 203 const base::Time& date_fetched, | 162 const base::Time& date_fetched, |
| 204 bool is_delta_compressed, | 163 bool is_delta_compressed, |
| 205 bool is_gzip_compressed, | 164 bool is_gzip_compressed, |
| 206 VariationsSeed* parsed_seed) { | 165 VariationsSeed* parsed_seed) { |
| 207 // If the data is gzip compressed, first uncompress it. | 166 // If the data is gzip compressed, first uncompress it. |
| 208 std::string ungzipped_data; | 167 std::string ungzipped_data; |
| 209 if (is_gzip_compressed) { | 168 if (is_gzip_compressed) { |
| 210 if (compression::GzipUncompress(data, &ungzipped_data)) { | 169 if (compression::GzipUncompress(data, &ungzipped_data)) { |
| 211 if (ungzipped_data.empty()) { | 170 if (ungzipped_data.empty()) { |
| 212 RecordSeedStoreHistogram( | 171 RecordStoreSeedResult(StoreSeedResult::FAILED_EMPTY_GZIP_CONTENTS); |
| 213 VARIATIONS_SEED_STORE_FAILED_EMPTY_GZIP_CONTENTS); | |
| 214 return false; | 172 return false; |
| 215 } | 173 } |
| 216 | 174 |
| 217 int size_reduction = ungzipped_data.length() - data.length(); | 175 int size_reduction = ungzipped_data.length() - data.length(); |
| 218 UMA_HISTOGRAM_PERCENTAGE("Variations.StoreSeed.GzipSize.ReductionPercent", | 176 UMA_HISTOGRAM_PERCENTAGE("Variations.StoreSeed.GzipSize.ReductionPercent", |
| 219 100 * size_reduction / ungzipped_data.length()); | 177 100 * size_reduction / ungzipped_data.length()); |
| 220 UMA_HISTOGRAM_COUNTS_1000("Variations.StoreSeed.GzipSize", | 178 UMA_HISTOGRAM_COUNTS_1000("Variations.StoreSeed.GzipSize", |
| 221 data.length() / 1024); | 179 data.length() / 1024); |
| 222 } else { | 180 } else { |
| 223 RecordSeedStoreHistogram(VARIATIONS_SEED_STORE_FAILED_UNGZIP); | 181 RecordStoreSeedResult(StoreSeedResult::FAILED_UNGZIP); |
| 224 return false; | 182 return false; |
| 225 } | 183 } |
| 226 } else { | 184 } else { |
| 227 ungzipped_data = data; | 185 ungzipped_data = data; |
| 228 } | 186 } |
| 229 | 187 |
| 230 if (!is_delta_compressed) { | 188 if (!is_delta_compressed) { |
| 231 const bool result = | 189 const bool result = |
| 232 StoreSeedDataNoDelta(ungzipped_data, base64_seed_signature, | 190 StoreSeedDataNoDelta(ungzipped_data, base64_seed_signature, |
| 233 country_code, date_fetched, parsed_seed); | 191 country_code, date_fetched, parsed_seed); |
| 234 if (result) { | 192 if (result) { |
| 235 UMA_HISTOGRAM_COUNTS_1000("Variations.StoreSeed.Size", | 193 UMA_HISTOGRAM_COUNTS_1000("Variations.StoreSeed.Size", |
| 236 ungzipped_data.length() / 1024); | 194 ungzipped_data.length() / 1024); |
| 237 } | 195 } |
| 238 return result; | 196 return result; |
| 239 } | 197 } |
| 240 | 198 |
| 241 // If the data is delta compressed, first decode it. | 199 // If the data is delta compressed, first decode it. |
| 242 DCHECK(invalid_base64_signature_.empty()); | 200 DCHECK(invalid_base64_signature_.empty()); |
| 243 RecordSeedStoreHistogram(VARIATIONS_SEED_STORE_DELTA_COUNT); | 201 RecordStoreSeedResult(StoreSeedResult::DELTA_COUNT); |
| 244 | 202 |
| 245 std::string existing_seed_data; | 203 std::string existing_seed_data; |
| 246 std::string updated_seed_data; | 204 std::string updated_seed_data; |
| 247 if (!ReadSeedData(&existing_seed_data)) { | 205 LoadSeedResult read_result = ReadSeedData(&existing_seed_data); |
| 248 RecordSeedStoreHistogram(VARIATIONS_SEED_STORE_FAILED_DELTA_READ_SEED); | 206 if (read_result != LoadSeedResult::SUCCESS) { |
| 207 RecordStoreSeedResult(StoreSeedResult::FAILED_DELTA_READ_SEED); |
| 249 return false; | 208 return false; |
| 250 } | 209 } |
| 251 if (!ApplyDeltaPatch(existing_seed_data, ungzipped_data, | 210 if (!ApplyDeltaPatch(existing_seed_data, ungzipped_data, |
| 252 &updated_seed_data)) { | 211 &updated_seed_data)) { |
| 253 RecordSeedStoreHistogram(VARIATIONS_SEED_STORE_FAILED_DELTA_APPLY); | 212 RecordStoreSeedResult(StoreSeedResult::FAILED_DELTA_APPLY); |
| 254 return false; | 213 return false; |
| 255 } | 214 } |
| 256 | 215 |
| 257 const bool result = | 216 const bool result = |
| 258 StoreSeedDataNoDelta(updated_seed_data, base64_seed_signature, | 217 StoreSeedDataNoDelta(updated_seed_data, base64_seed_signature, |
| 259 country_code, date_fetched, parsed_seed); | 218 country_code, date_fetched, parsed_seed); |
| 260 if (result) { | 219 if (result) { |
| 261 // Note: |updated_seed_data.length()| is guaranteed to be non-zero, else | 220 // Note: |updated_seed_data.length()| is guaranteed to be non-zero, else |
| 262 // result would be false. | 221 // result would be false. |
| 263 int size_reduction = updated_seed_data.length() - ungzipped_data.length(); | 222 int size_reduction = updated_seed_data.length() - ungzipped_data.length(); |
| 264 UMA_HISTOGRAM_PERCENTAGE("Variations.StoreSeed.DeltaSize.ReductionPercent", | 223 UMA_HISTOGRAM_PERCENTAGE("Variations.StoreSeed.DeltaSize.ReductionPercent", |
| 265 100 * size_reduction / updated_seed_data.length()); | 224 100 * size_reduction / updated_seed_data.length()); |
| 266 UMA_HISTOGRAM_COUNTS_1000("Variations.StoreSeed.DeltaSize", | 225 UMA_HISTOGRAM_COUNTS_1000("Variations.StoreSeed.DeltaSize", |
| 267 ungzipped_data.length() / 1024); | 226 ungzipped_data.length() / 1024); |
| 268 } else { | 227 } else { |
| 269 RecordSeedStoreHistogram(VARIATIONS_SEED_STORE_FAILED_DELTA_STORE); | 228 RecordStoreSeedResult(StoreSeedResult::FAILED_DELTA_STORE); |
| 270 } | 229 } |
| 271 return result; | 230 return result; |
| 272 } | 231 } |
| 273 | 232 |
| 274 void VariationsSeedStore::UpdateSeedDateAndLogDayChange( | 233 void VariationsSeedStore::UpdateSeedDateAndLogDayChange( |
| 275 const base::Time& server_date_fetched) { | 234 const base::Time& server_date_fetched) { |
| 276 VariationsSeedDateChangeState date_change = SEED_DATE_NO_OLD_DATE; | 235 UpdateSeedDateResult result = UpdateSeedDateResult::NO_OLD_DATE; |
| 277 | 236 |
| 278 if (local_state_->HasPrefPath(prefs::kVariationsSeedDate)) { | 237 if (local_state_->HasPrefPath(prefs::kVariationsSeedDate)) { |
| 279 const int64_t stored_date_value = | 238 const int64_t stored_date_value = |
| 280 local_state_->GetInt64(prefs::kVariationsSeedDate); | 239 local_state_->GetInt64(prefs::kVariationsSeedDate); |
| 281 const base::Time stored_date = | 240 const base::Time stored_date = |
| 282 base::Time::FromInternalValue(stored_date_value); | 241 base::Time::FromInternalValue(stored_date_value); |
| 283 | 242 |
| 284 date_change = GetSeedDateChangeState(server_date_fetched, stored_date); | 243 result = GetSeedDateChangeState(server_date_fetched, stored_date); |
| 285 } | 244 } |
| 286 | 245 |
| 287 UMA_HISTOGRAM_ENUMERATION("Variations.SeedDateChange", date_change, | 246 UMA_HISTOGRAM_ENUMERATION("Variations.SeedDateChange", result, |
| 288 SEED_DATE_ENUM_SIZE); | 247 UpdateSeedDateResult::ENUM_SIZE); |
| 289 | 248 |
| 290 local_state_->SetInt64(prefs::kVariationsSeedDate, | 249 local_state_->SetInt64(prefs::kVariationsSeedDate, |
| 291 server_date_fetched.ToInternalValue()); | 250 server_date_fetched.ToInternalValue()); |
| 292 } | 251 } |
| 293 | 252 |
| 294 std::string VariationsSeedStore::GetInvalidSignature() const { | 253 std::string VariationsSeedStore::GetInvalidSignature() const { |
| 295 return invalid_base64_signature_; | 254 return invalid_base64_signature_; |
| 296 } | 255 } |
| 297 | 256 |
| 298 // static | 257 // static |
| 299 void VariationsSeedStore::RegisterPrefs(PrefRegistrySimple* registry) { | 258 void VariationsSeedStore::RegisterPrefs(PrefRegistrySimple* registry) { |
| 300 registry->RegisterStringPref(prefs::kVariationsCompressedSeed, std::string()); | 259 registry->RegisterStringPref(prefs::kVariationsCompressedSeed, std::string()); |
| 301 registry->RegisterStringPref(prefs::kVariationsCountry, std::string()); | 260 registry->RegisterStringPref(prefs::kVariationsCountry, std::string()); |
| 302 registry->RegisterInt64Pref(prefs::kVariationsSeedDate, | 261 registry->RegisterInt64Pref(prefs::kVariationsSeedDate, |
| 303 base::Time().ToInternalValue()); | 262 base::Time().ToInternalValue()); |
| 304 registry->RegisterStringPref(prefs::kVariationsSeedSignature, std::string()); | 263 registry->RegisterStringPref(prefs::kVariationsSeedSignature, std::string()); |
| 305 } | 264 } |
| 306 | 265 |
| 307 VariationsSeedStore::VerifySignatureResult | 266 bool VariationsSeedStore::SignatureVerificationEnabled() { |
| 308 VariationsSeedStore::VerifySeedSignature( | 267 #if defined(OS_IOS) || defined(OS_ANDROID) |
| 309 const std::string& seed_bytes, | 268 // Signature verification is disabled on mobile platforms for now, since it |
| 310 const std::string& base64_seed_signature) { | 269 // adds about ~15ms to the startup time on mobile (vs. a couple ms on |
| 311 if (!SignatureVerificationEnabled()) | 270 // desktop). |
| 312 return VARIATIONS_SEED_SIGNATURE_ENUM_SIZE; | 271 return false; |
| 313 | 272 #else |
| 314 if (base64_seed_signature.empty()) | 273 return true; |
| 315 return VARIATIONS_SEED_SIGNATURE_MISSING; | 274 #endif |
| 316 | |
| 317 std::string signature; | |
| 318 if (!base::Base64Decode(base64_seed_signature, &signature)) | |
| 319 return VARIATIONS_SEED_SIGNATURE_DECODE_FAILED; | |
| 320 | |
| 321 crypto::SignatureVerifier verifier; | |
| 322 if (!verifier.VerifyInit(crypto::SignatureVerifier::ECDSA_SHA256, | |
| 323 reinterpret_cast<const uint8_t*>(signature.data()), | |
| 324 signature.size(), kPublicKey, | |
| 325 arraysize(kPublicKey))) { | |
| 326 return VARIATIONS_SEED_SIGNATURE_INVALID_SIGNATURE; | |
| 327 } | |
| 328 | |
| 329 verifier.VerifyUpdate(reinterpret_cast<const uint8_t*>(seed_bytes.data()), | |
| 330 seed_bytes.size()); | |
| 331 if (verifier.VerifyFinal()) | |
| 332 return VARIATIONS_SEED_SIGNATURE_VALID; | |
| 333 return VARIATIONS_SEED_SIGNATURE_INVALID_SEED; | |
| 334 } | 275 } |
| 335 | 276 |
| 336 void VariationsSeedStore::ClearPrefs() { | 277 void VariationsSeedStore::ClearPrefs() { |
| 337 local_state_->ClearPref(prefs::kVariationsCompressedSeed); | 278 local_state_->ClearPref(prefs::kVariationsCompressedSeed); |
| 338 local_state_->ClearPref(prefs::kVariationsSeedDate); | 279 local_state_->ClearPref(prefs::kVariationsSeedDate); |
| 339 local_state_->ClearPref(prefs::kVariationsSeedSignature); | 280 local_state_->ClearPref(prefs::kVariationsSeedSignature); |
| 340 } | 281 } |
| 341 | 282 |
| 342 #if defined(OS_ANDROID) | 283 #if defined(OS_ANDROID) |
| 343 void VariationsSeedStore::ImportFirstRunJavaSeed() { | 284 void VariationsSeedStore::ImportFirstRunJavaSeed() { |
| 344 DVLOG(1) << "Importing first run seed from Java preferences."; | 285 DVLOG(1) << "Importing first run seed from Java preferences."; |
| 345 | 286 |
| 346 std::string seed_data; | 287 std::string seed_data; |
| 347 std::string seed_signature; | 288 std::string seed_signature; |
| 348 std::string seed_country; | 289 std::string seed_country; |
| 349 std::string response_date; | 290 std::string response_date; |
| 350 bool is_gzip_compressed; | 291 bool is_gzip_compressed; |
| 351 | 292 |
| 352 android::GetVariationsFirstRunSeed(&seed_data, &seed_signature, &seed_country, | 293 android::GetVariationsFirstRunSeed(&seed_data, &seed_signature, &seed_country, |
| 353 &response_date, &is_gzip_compressed); | 294 &response_date, &is_gzip_compressed); |
| 354 if (seed_data.empty()) { | 295 if (seed_data.empty()) { |
| 355 RecordFirstRunResult(FIRST_RUN_SEED_IMPORT_FAIL_NO_FIRST_RUN_SEED); | 296 RecordFirstRunSeedImportResult( |
| 297 FirstRunSeedImportResult::FAIL_NO_FIRST_RUN_SEED); |
| 356 return; | 298 return; |
| 357 } | 299 } |
| 358 | 300 |
| 359 base::Time current_date; | 301 base::Time current_date; |
| 360 if (!base::Time::FromUTCString(response_date.c_str(), ¤t_date)) { | 302 if (!base::Time::FromUTCString(response_date.c_str(), ¤t_date)) { |
| 361 RecordFirstRunResult(FIRST_RUN_SEED_IMPORT_FAIL_INVALID_RESPONSE_DATE); | 303 RecordFirstRunSeedImportResult( |
| 304 FirstRunSeedImportResult::FAIL_INVALID_RESPONSE_DATE); |
| 362 LOG(WARNING) << "Invalid response date: " << response_date; | 305 LOG(WARNING) << "Invalid response date: " << response_date; |
| 363 return; | 306 return; |
| 364 } | 307 } |
| 365 | 308 |
| 366 if (!StoreSeedData(seed_data, seed_signature, seed_country, current_date, | 309 if (!StoreSeedData(seed_data, seed_signature, seed_country, current_date, |
| 367 false, is_gzip_compressed, nullptr)) { | 310 false, is_gzip_compressed, nullptr)) { |
| 368 RecordFirstRunResult(FIRST_RUN_SEED_IMPORT_FAIL_STORE_FAILED); | 311 RecordFirstRunSeedImportResult(FirstRunSeedImportResult::FAIL_STORE_FAILED); |
| 369 LOG(WARNING) << "First run variations seed is invalid."; | 312 LOG(WARNING) << "First run variations seed is invalid."; |
| 370 return; | 313 return; |
| 371 } | 314 } |
| 372 RecordFirstRunResult(FIRST_RUN_SEED_IMPORT_SUCCESS); | 315 RecordFirstRunSeedImportResult(FirstRunSeedImportResult::SUCCESS); |
| 373 } | 316 } |
| 374 #endif // OS_ANDROID | 317 #endif // OS_ANDROID |
| 375 | 318 |
| 376 bool VariationsSeedStore::ReadSeedData(std::string* seed_data) { | 319 LoadSeedResult VariationsSeedStore::ReadSeedData(std::string* seed_data) { |
| 377 std::string base64_seed_data = | 320 std::string base64_seed_data = |
| 378 local_state_->GetString(prefs::kVariationsCompressedSeed); | 321 local_state_->GetString(prefs::kVariationsCompressedSeed); |
| 379 if (base64_seed_data.empty()) { | 322 if (base64_seed_data.empty()) |
| 380 RecordVariationSeedEmptyHistogram(VARIATIONS_SEED_EMPTY); | 323 return LoadSeedResult::EMPTY; |
| 381 return false; | |
| 382 } | |
| 383 | 324 |
| 384 // If the decode process fails, assume the pref value is corrupt and clear it. | 325 // If the decode process fails, assume the pref value is corrupt and clear it. |
| 385 std::string decoded_data; | 326 std::string decoded_data; |
| 386 if (!base::Base64Decode(base64_seed_data, &decoded_data)) { | 327 if (!base::Base64Decode(base64_seed_data, &decoded_data)) { |
| 387 ClearPrefs(); | 328 ClearPrefs(); |
| 388 RecordVariationSeedEmptyHistogram(VARIATIONS_SEED_CORRUPT_BASE64); | 329 return LoadSeedResult::CORRUPT_BASE64; |
| 389 return false; | |
| 390 } | 330 } |
| 391 | 331 |
| 392 if (!compression::GzipUncompress(decoded_data, seed_data)) { | 332 if (!compression::GzipUncompress(decoded_data, seed_data)) { |
| 393 ClearPrefs(); | 333 ClearPrefs(); |
| 394 RecordVariationSeedEmptyHistogram(VARIATIONS_SEED_CORRUPT_GZIP); | 334 return LoadSeedResult::CORRUPT_GZIP; |
| 395 return false; | |
| 396 } | 335 } |
| 397 | 336 |
| 398 return true; | 337 return LoadSeedResult::SUCCESS; |
| 399 } | 338 } |
| 400 | 339 |
| 401 bool VariationsSeedStore::StoreSeedDataNoDelta( | 340 bool VariationsSeedStore::StoreSeedDataNoDelta( |
| 402 const std::string& seed_data, | 341 const std::string& seed_data, |
| 403 const std::string& base64_seed_signature, | 342 const std::string& base64_seed_signature, |
| 404 const std::string& country_code, | 343 const std::string& country_code, |
| 405 const base::Time& date_fetched, | 344 const base::Time& date_fetched, |
| 406 VariationsSeed* parsed_seed) { | 345 VariationsSeed* parsed_seed) { |
| 407 if (seed_data.empty()) { | 346 if (seed_data.empty()) { |
| 408 RecordSeedStoreHistogram(VARIATIONS_SEED_STORE_FAILED_EMPTY_GZIP_CONTENTS); | 347 RecordStoreSeedResult(StoreSeedResult::FAILED_EMPTY_GZIP_CONTENTS); |
| 409 return false; | 348 return false; |
| 410 } | 349 } |
| 411 | 350 |
| 412 // Only store the seed data if it parses correctly. | 351 // Only store the seed data if it parses correctly. |
| 413 VariationsSeed seed; | 352 VariationsSeed seed; |
| 414 if (!seed.ParseFromString(seed_data)) { | 353 if (!seed.ParseFromString(seed_data)) { |
| 415 RecordSeedStoreHistogram(VARIATIONS_SEED_STORE_FAILED_PARSE); | 354 RecordStoreSeedResult(StoreSeedResult::FAILED_PARSE); |
| 416 return false; | 355 return false; |
| 417 } | 356 } |
| 418 | 357 |
| 419 const VerifySignatureResult result = | 358 if (SignatureVerificationEnabled()) { |
| 420 VerifySeedSignature(seed_data, base64_seed_signature); | 359 const VerifySignatureResult result = |
| 421 if (result != VARIATIONS_SEED_SIGNATURE_ENUM_SIZE) { | 360 VerifySeedSignature(seed_data, base64_seed_signature); |
| 422 UMA_HISTOGRAM_ENUMERATION("Variations.StoreSeedSignature", result, | 361 UMA_HISTOGRAM_ENUMERATION("Variations.StoreSeedSignature", result, |
| 423 VARIATIONS_SEED_SIGNATURE_ENUM_SIZE); | 362 VerifySignatureResult::ENUM_SIZE); |
| 424 if (result != VARIATIONS_SEED_SIGNATURE_VALID) { | 363 if (result != VerifySignatureResult::VALID_SIGNATURE) { |
| 425 RecordSeedStoreHistogram(VARIATIONS_SEED_STORE_FAILED_SIGNATURE); | 364 RecordStoreSeedResult(StoreSeedResult::FAILED_SIGNATURE); |
| 426 return false; | 365 return false; |
| 427 } | 366 } |
| 428 } | 367 } |
| 429 | 368 |
| 430 // Compress the seed before base64-encoding and storing. | 369 // Compress the seed before base64-encoding and storing. |
| 431 std::string compressed_seed_data; | 370 std::string compressed_seed_data; |
| 432 if (!compression::GzipCompress(seed_data, &compressed_seed_data)) { | 371 if (!compression::GzipCompress(seed_data, &compressed_seed_data)) { |
| 433 RecordSeedStoreHistogram(VARIATIONS_SEED_STORE_FAILED_GZIP); | 372 RecordStoreSeedResult(StoreSeedResult::FAILED_GZIP); |
| 434 return false; | 373 return false; |
| 435 } | 374 } |
| 436 | 375 |
| 437 std::string base64_seed_data; | 376 std::string base64_seed_data; |
| 438 base::Base64Encode(compressed_seed_data, &base64_seed_data); | 377 base::Base64Encode(compressed_seed_data, &base64_seed_data); |
| 439 | 378 |
| 440 #if defined(OS_ANDROID) | 379 #if defined(OS_ANDROID) |
| 441 // If currently we do not have any stored pref then we mark seed storing as | 380 // If currently we do not have any stored pref then we mark seed storing as |
| 442 // successful on the Java side of Chrome for Android to avoid repeated seed | 381 // successful on the Java side of Chrome for Android to avoid repeated seed |
| 443 // fetches and clear preferences on the Java side. | 382 // fetches and clear preferences on the Java side. |
| 444 if (local_state_->GetString(prefs::kVariationsCompressedSeed).empty()) { | 383 if (local_state_->GetString(prefs::kVariationsCompressedSeed).empty()) { |
| 445 android::MarkVariationsSeedAsStored(); | 384 android::MarkVariationsSeedAsStored(); |
| 446 android::ClearJavaFirstRunPrefs(); | 385 android::ClearJavaFirstRunPrefs(); |
| 447 } | 386 } |
| 448 #endif | 387 #endif |
| 449 | 388 |
| 450 // Update the saved country code only if one was returned from the server. | 389 // Update the saved country code only if one was returned from the server. |
| 451 if (!country_code.empty()) | 390 if (!country_code.empty()) |
| 452 local_state_->SetString(prefs::kVariationsCountry, country_code); | 391 local_state_->SetString(prefs::kVariationsCountry, country_code); |
| 453 | 392 |
| 454 local_state_->SetString(prefs::kVariationsCompressedSeed, base64_seed_data); | 393 local_state_->SetString(prefs::kVariationsCompressedSeed, base64_seed_data); |
| 455 UpdateSeedDateAndLogDayChange(date_fetched); | 394 UpdateSeedDateAndLogDayChange(date_fetched); |
| 456 local_state_->SetString(prefs::kVariationsSeedSignature, | 395 local_state_->SetString(prefs::kVariationsSeedSignature, |
| 457 base64_seed_signature); | 396 base64_seed_signature); |
| 458 variations_serial_number_ = seed.serial_number(); | 397 variations_serial_number_ = seed.serial_number(); |
| 459 if (parsed_seed) | 398 if (parsed_seed) |
| 460 seed.Swap(parsed_seed); | 399 seed.Swap(parsed_seed); |
| 461 | 400 |
| 462 RecordSeedStoreHistogram(VARIATIONS_SEED_STORE_SUCCESS); | 401 RecordStoreSeedResult(StoreSeedResult::SUCCESS); |
| 463 return true; | 402 return true; |
| 464 } | 403 } |
| 465 | 404 |
| 466 // static | 405 // static |
| 467 bool VariationsSeedStore::ApplyDeltaPatch(const std::string& existing_data, | 406 bool VariationsSeedStore::ApplyDeltaPatch(const std::string& existing_data, |
| 468 const std::string& patch, | 407 const std::string& patch, |
| 469 std::string* output) { | 408 std::string* output) { |
| 470 output->clear(); | 409 output->clear(); |
| 471 | 410 |
| 472 google::protobuf::io::CodedInputStream in( | 411 google::protobuf::io::CodedInputStream in( |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 505 end_offset += length; | 444 end_offset += length; |
| 506 if (!end_offset.IsValid() || end_offset.ValueOrDie() > existing_data_size) | 445 if (!end_offset.IsValid() || end_offset.ValueOrDie() > existing_data_size) |
| 507 return false; | 446 return false; |
| 508 output->append(existing_data, offset, length); | 447 output->append(existing_data, offset, length); |
| 509 } | 448 } |
| 510 } | 449 } |
| 511 return true; | 450 return true; |
| 512 } | 451 } |
| 513 | 452 |
| 514 void VariationsSeedStore::ReportUnsupportedSeedFormatError() { | 453 void VariationsSeedStore::ReportUnsupportedSeedFormatError() { |
| 515 RecordSeedStoreHistogram( | 454 RecordStoreSeedResult(StoreSeedResult::FAILED_UNSUPPORTED_SEED_FORMAT); |
| 516 VARIATIONS_SEED_STORE_FAILED_UNSUPPORTED_SEED_FORMAT); | |
| 517 } | 455 } |
| 518 | 456 |
| 519 } // namespace variations | 457 } // namespace variations |
| OLD | NEW |