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 |