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 "base/base64.h" | 7 #include "base/base64.h" |
8 #include "base/macros.h" | 8 #include "base/macros.h" |
| 9 #include "base/test/histogram_tester.h" |
9 #include "build/build_config.h" | 10 #include "build/build_config.h" |
10 #include "components/prefs/testing_pref_service.h" | 11 #include "components/prefs/testing_pref_service.h" |
11 #include "components/variations/pref_names.h" | 12 #include "components/variations/pref_names.h" |
12 #include "components/variations/proto/study.pb.h" | 13 #include "components/variations/proto/study.pb.h" |
13 #include "components/variations/proto/variations_seed.pb.h" | 14 #include "components/variations/proto/variations_seed.pb.h" |
14 #include "testing/gtest/include/gtest/gtest.h" | 15 #include "testing/gtest/include/gtest/gtest.h" |
15 #include "third_party/zlib/google/compression_utils.h" | 16 #include "third_party/zlib/google/compression_utils.h" |
16 | 17 |
17 #if defined(OS_ANDROID) | 18 #if defined(OS_ANDROID) |
18 #include "components/variations/android/variations_seed_bridge.h" | 19 #include "components/variations/android/variations_seed_bridge.h" |
19 #endif // OS_ANDROID | 20 #endif // OS_ANDROID |
20 | 21 |
21 namespace variations { | 22 namespace variations { |
22 | 23 |
23 namespace { | 24 namespace { |
24 | 25 |
25 class TestVariationsSeedStore : public VariationsSeedStore { | 26 class TestVariationsSeedStore : public VariationsSeedStore { |
26 public: | 27 public: |
27 explicit TestVariationsSeedStore(PrefService* local_state) | 28 explicit TestVariationsSeedStore(PrefService* local_state) |
28 : VariationsSeedStore(local_state) {} | 29 : VariationsSeedStore(local_state) {} |
29 ~TestVariationsSeedStore() override {} | 30 ~TestVariationsSeedStore() override {} |
30 | 31 |
31 bool StoreSeedForTesting(const std::string& seed_data) { | 32 bool StoreSeedForTesting(const std::string& seed_data) { |
32 return StoreSeedData(seed_data, std::string(), std::string(), | 33 return StoreSeedData(seed_data, std::string(), std::string(), |
33 base::Time::Now(), false, false, nullptr); | 34 base::Time::Now(), false, false, nullptr); |
34 } | 35 } |
35 | 36 |
36 VariationsSeedStore::VerifySignatureResult VerifySeedSignature( | 37 bool SignatureVerificationEnabled() override { return false; } |
37 const std::string& seed_bytes, | |
38 const std::string& base64_seed_signature) override { | |
39 return VariationsSeedStore::VARIATIONS_SEED_SIGNATURE_ENUM_SIZE; | |
40 } | |
41 | 38 |
42 private: | 39 private: |
43 DISALLOW_COPY_AND_ASSIGN(TestVariationsSeedStore); | 40 DISALLOW_COPY_AND_ASSIGN(TestVariationsSeedStore); |
44 }; | 41 }; |
45 | 42 |
| 43 // Signature verification is disabled on Android and iOS for performance |
| 44 // reasons. This class re-enables it for tests, which don't mind the (small) |
| 45 // performance penalty. |
| 46 class SignatureVerifyingVariationsSeedStore : public VariationsSeedStore { |
| 47 public: |
| 48 explicit SignatureVerifyingVariationsSeedStore(PrefService* local_state) |
| 49 : VariationsSeedStore(local_state) {} |
| 50 ~SignatureVerifyingVariationsSeedStore() override {} |
| 51 |
| 52 bool SignatureVerificationEnabled() override { return true; } |
| 53 |
| 54 private: |
| 55 DISALLOW_COPY_AND_ASSIGN(SignatureVerifyingVariationsSeedStore); |
| 56 }; |
46 | 57 |
47 // Populates |seed| with simple test data. The resulting seed will contain one | 58 // Populates |seed| with simple test data. The resulting seed will contain one |
48 // study called "test", which contains one experiment called "abc" with | 59 // study called "test", which contains one experiment called "abc" with |
49 // probability weight 100. |seed|'s study field will be cleared before adding | 60 // probability weight 100. |seed|'s study field will be cleared before adding |
50 // the new study. | 61 // the new study. |
51 VariationsSeed CreateTestSeed() { | 62 VariationsSeed CreateTestSeed() { |
52 VariationsSeed seed; | 63 VariationsSeed seed; |
53 Study* study = seed.add_study(); | 64 Study* study = seed.add_study(); |
54 study->set_name("test"); | 65 study->set_name("test"); |
55 study->set_default_experiment_name("abc"); | 66 study->set_default_experiment_name("abc"); |
(...skipping 96 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
152 base::Base64Encode(Compress(uncompressed_seed_data), | 163 base::Base64Encode(Compress(uncompressed_seed_data), |
153 &compressed_base64_seed_data); | 164 &compressed_base64_seed_data); |
154 | 165 |
155 // Set seed and valid signature in prefs. | 166 // Set seed and valid signature in prefs. |
156 prefs.SetString(prefs::kVariationsCompressedSeed, | 167 prefs.SetString(prefs::kVariationsCompressedSeed, |
157 compressed_base64_seed_data); | 168 compressed_base64_seed_data); |
158 prefs.SetString(prefs::kVariationsSeedSignature, base64_seed_signature); | 169 prefs.SetString(prefs::kVariationsSeedSignature, base64_seed_signature); |
159 | 170 |
160 VariationsSeedStore seed_store(&prefs); | 171 VariationsSeedStore seed_store(&prefs); |
161 VariationsSeed loaded_seed; | 172 VariationsSeed loaded_seed; |
162 seed_store.LoadSeed(&loaded_seed); | 173 EXPECT_TRUE(seed_store.LoadSeed(&loaded_seed)); |
163 std::string invalid_signature = seed_store.GetInvalidSignature(); | 174 std::string invalid_signature = seed_store.GetInvalidSignature(); |
164 // Valid signature so we get an empty string. | 175 // Valid signature so we get an empty string. |
165 EXPECT_EQ(std::string(), invalid_signature); | 176 EXPECT_EQ(std::string(), invalid_signature); |
166 | 177 |
167 prefs.SetString(prefs::kVariationsSeedSignature, | 178 prefs.SetString(prefs::kVariationsSeedSignature, |
168 base64_seed_signature_invalid); | 179 base64_seed_signature_invalid); |
169 seed_store.LoadSeed(&loaded_seed); | 180 #if defined(OS_IOS) || defined(OS_ANDROID) |
| 181 EXPECT_TRUE(seed_store.LoadSeed(&loaded_seed)); |
| 182 #else |
| 183 EXPECT_FALSE(seed_store.LoadSeed(&loaded_seed)); |
| 184 #endif |
170 // Invalid signature, so we should get the signature itself, except on mobile | 185 // Invalid signature, so we should get the signature itself, except on mobile |
171 // where we should get an empty string because verification is not enabled. | 186 // where we should get an empty string because verification is not enabled. |
172 invalid_signature = seed_store.GetInvalidSignature(); | 187 invalid_signature = seed_store.GetInvalidSignature(); |
173 #if defined(OS_IOS) || defined(OS_ANDROID) | 188 #if defined(OS_IOS) || defined(OS_ANDROID) |
174 EXPECT_EQ(std::string(), invalid_signature); | 189 EXPECT_EQ(std::string(), invalid_signature); |
175 #else | 190 #else |
176 EXPECT_EQ(base64_seed_signature_invalid, invalid_signature); | 191 EXPECT_EQ(base64_seed_signature_invalid, invalid_signature); |
177 #endif | 192 #endif |
178 | 193 |
179 prefs.SetString(prefs::kVariationsSeedSignature, std::string()); | 194 prefs.SetString(prefs::kVariationsSeedSignature, std::string()); |
180 seed_store.LoadSeed(&loaded_seed); | 195 #if defined(OS_IOS) || defined(OS_ANDROID) |
| 196 EXPECT_TRUE(seed_store.LoadSeed(&loaded_seed)); |
| 197 #else |
| 198 EXPECT_FALSE(seed_store.LoadSeed(&loaded_seed)); |
| 199 #endif |
181 invalid_signature = seed_store.GetInvalidSignature(); | 200 invalid_signature = seed_store.GetInvalidSignature(); |
182 // Empty signature, not considered invalid. | 201 // Empty signature, not considered invalid. |
183 EXPECT_EQ(std::string(), invalid_signature); | 202 EXPECT_EQ(std::string(), invalid_signature); |
184 } | 203 } |
185 | 204 |
186 TEST(VariationsSeedStoreTest, StoreSeedData) { | 205 TEST(VariationsSeedStoreTest, StoreSeedData) { |
187 const VariationsSeed seed = CreateTestSeed(); | 206 const VariationsSeed seed = CreateTestSeed(); |
188 const std::string serialized_seed = SerializeSeed(seed); | 207 const std::string serialized_seed = SerializeSeed(seed); |
189 | 208 |
190 TestingPrefServiceSimple prefs; | 209 TestingPrefServiceSimple prefs; |
(...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
272 | 291 |
273 VariationsSeed parsed_seed; | 292 VariationsSeed parsed_seed; |
274 EXPECT_FALSE(seed_store.StoreSeedData(compressed_seed, std::string(), | 293 EXPECT_FALSE(seed_store.StoreSeedData(compressed_seed, std::string(), |
275 std::string(), base::Time::Now(), false, | 294 std::string(), base::Time::Now(), false, |
276 true, &parsed_seed)); | 295 true, &parsed_seed)); |
277 } | 296 } |
278 | 297 |
279 TEST(VariationsSeedStoreTest, VerifySeedSignature) { | 298 TEST(VariationsSeedStoreTest, VerifySeedSignature) { |
280 // The below seed and signature pair were generated using the server's | 299 // The below seed and signature pair were generated using the server's |
281 // private key. | 300 // private key. |
282 const std::string base64_seed_data = | 301 const std::string uncompressed_base64_seed_data = |
283 "CigxZDI5NDY0ZmIzZDc4ZmYxNTU2ZTViNTUxYzY0NDdjYmM3NGU1ZmQwEr0BCh9VTUEtVW5p" | 302 "CigxZDI5NDY0ZmIzZDc4ZmYxNTU2ZTViNTUxYzY0NDdjYmM3NGU1ZmQwEr0BCh9VTUEtVW5p" |
284 "Zm9ybWl0eS1UcmlhbC0xMC1QZXJjZW50GICckqUFOAFCB2RlZmF1bHRKCwoHZGVmYXVsdBAB" | 303 "Zm9ybWl0eS1UcmlhbC0xMC1QZXJjZW50GICckqUFOAFCB2RlZmF1bHRKCwoHZGVmYXVsdBAB" |
285 "SgwKCGdyb3VwXzAxEAFKDAoIZ3JvdXBfMDIQAUoMCghncm91cF8wMxABSgwKCGdyb3VwXzA0" | 304 "SgwKCGdyb3VwXzAxEAFKDAoIZ3JvdXBfMDIQAUoMCghncm91cF8wMxABSgwKCGdyb3VwXzA0" |
286 "EAFKDAoIZ3JvdXBfMDUQAUoMCghncm91cF8wNhABSgwKCGdyb3VwXzA3EAFKDAoIZ3JvdXBf" | 305 "EAFKDAoIZ3JvdXBfMDUQAUoMCghncm91cF8wNhABSgwKCGdyb3VwXzA3EAFKDAoIZ3JvdXBf" |
287 "MDgQAUoMCghncm91cF8wORAB"; | 306 "MDgQAUoMCghncm91cF8wORAB"; |
288 const std::string base64_seed_signature = | 307 const std::string base64_seed_signature = |
289 "MEQCIDD1IVxjzWYncun+9IGzqYjZvqxxujQEayJULTlbTGA/AiAr0oVmEgVUQZBYq5VLOSvy" | 308 "MEQCIDD1IVxjzWYncun+9IGzqYjZvqxxujQEayJULTlbTGA/AiAr0oVmEgVUQZBYq5VLOSvy" |
290 "96JkMYgzTkHPwbv7K/CmgA=="; | 309 "96JkMYgzTkHPwbv7K/CmgA=="; |
291 | 310 |
292 std::string seed_data; | 311 std::string seed_data; |
293 EXPECT_TRUE(base::Base64Decode(base64_seed_data, &seed_data)); | 312 ASSERT_TRUE(base::Base64Decode(uncompressed_base64_seed_data, &seed_data)); |
| 313 VariationsSeed seed; |
| 314 ASSERT_TRUE(seed.ParseFromString(seed_data)); |
| 315 std::string base64_seed_data = SerializeSeedBase64(seed); |
294 | 316 |
295 VariationsSeedStore seed_store(NULL); | 317 TestingPrefServiceSimple prefs; |
296 | 318 VariationsSeedStore::RegisterPrefs(prefs.registry()); |
297 #if defined(OS_IOS) || defined(OS_ANDROID) | |
298 // Signature verification is not enabled on mobile. | |
299 if (seed_store.VerifySeedSignature(seed_data, base64_seed_signature) == | |
300 VariationsSeedStore::VARIATIONS_SEED_SIGNATURE_ENUM_SIZE) { | |
301 return; | |
302 } | |
303 #endif | |
304 | 319 |
305 // The above inputs should be valid. | 320 // The above inputs should be valid. |
306 EXPECT_EQ(VariationsSeedStore::VARIATIONS_SEED_SIGNATURE_VALID, | 321 { |
307 seed_store.VerifySeedSignature(seed_data, base64_seed_signature)); | 322 prefs.SetString(prefs::kVariationsCompressedSeed, base64_seed_data); |
| 323 prefs.SetString(prefs::kVariationsSeedSignature, base64_seed_signature); |
| 324 SignatureVerifyingVariationsSeedStore seed_store(&prefs); |
| 325 |
| 326 base::HistogramTester histogram_tester; |
| 327 VariationsSeed seed; |
| 328 EXPECT_TRUE(seed_store.LoadSeed(&seed)); |
| 329 histogram_tester.ExpectUniqueSample( |
| 330 "Variations.LoadSeedSignature", |
| 331 static_cast<base::HistogramBase::Sample>( |
| 332 VerifySignatureResult::VALID_SIGNATURE), |
| 333 1); |
| 334 } |
308 | 335 |
309 // If there's no signature, the corresponding result should be returned. | 336 // If there's no signature, the corresponding result should be returned. |
310 EXPECT_EQ(VariationsSeedStore::VARIATIONS_SEED_SIGNATURE_MISSING, | 337 { |
311 seed_store.VerifySeedSignature(seed_data, std::string())); | 338 prefs.SetString(prefs::kVariationsCompressedSeed, base64_seed_data); |
| 339 prefs.SetString(prefs::kVariationsSeedSignature, std::string()); |
| 340 SignatureVerifyingVariationsSeedStore seed_store(&prefs); |
312 | 341 |
313 // Using non-base64 encoded value as signature (e.g. seed data) should fail. | 342 base::HistogramTester histogram_tester; |
314 EXPECT_EQ(VariationsSeedStore::VARIATIONS_SEED_SIGNATURE_DECODE_FAILED, | 343 VariationsSeed seed; |
315 seed_store.VerifySeedSignature(seed_data, seed_data)); | 344 EXPECT_FALSE(seed_store.LoadSeed(&seed)); |
| 345 histogram_tester.ExpectUniqueSample( |
| 346 "Variations.LoadSeedSignature", |
| 347 static_cast<base::HistogramBase::Sample>( |
| 348 VerifySignatureResult::MISSING_SIGNATURE), |
| 349 1); |
| 350 } |
| 351 |
| 352 // Using non-base64 encoded value as signature should fail. |
| 353 { |
| 354 prefs.SetString(prefs::kVariationsCompressedSeed, base64_seed_data); |
| 355 prefs.SetString(prefs::kVariationsSeedSignature, |
| 356 "not a base64-encoded string"); |
| 357 SignatureVerifyingVariationsSeedStore seed_store(&prefs); |
| 358 |
| 359 base::HistogramTester histogram_tester; |
| 360 VariationsSeed seed; |
| 361 EXPECT_FALSE(seed_store.LoadSeed(&seed)); |
| 362 histogram_tester.ExpectUniqueSample( |
| 363 "Variations.LoadSeedSignature", |
| 364 static_cast<base::HistogramBase::Sample>( |
| 365 VerifySignatureResult::DECODE_FAILED), |
| 366 1); |
| 367 } |
316 | 368 |
317 // Using a different signature (e.g. the base64 seed data) should fail. | 369 // Using a different signature (e.g. the base64 seed data) should fail. |
318 // OpenSSL doesn't distinguish signature decode failure from the | 370 // OpenSSL doesn't distinguish signature decode failure from the |
319 // signature not matching. | 371 // signature not matching. |
320 EXPECT_EQ(VariationsSeedStore::VARIATIONS_SEED_SIGNATURE_INVALID_SEED, | 372 { |
321 seed_store.VerifySeedSignature(seed_data, base64_seed_data)); | 373 prefs.SetString(prefs::kVariationsCompressedSeed, base64_seed_data); |
| 374 prefs.SetString(prefs::kVariationsSeedSignature, base64_seed_data); |
| 375 SignatureVerifyingVariationsSeedStore seed_store(&prefs); |
| 376 |
| 377 base::HistogramTester histogram_tester; |
| 378 VariationsSeed seed; |
| 379 EXPECT_FALSE(seed_store.LoadSeed(&seed)); |
| 380 histogram_tester.ExpectUniqueSample( |
| 381 "Variations.LoadSeedSignature", |
| 382 static_cast<base::HistogramBase::Sample>( |
| 383 VerifySignatureResult::INVALID_SEED), |
| 384 1); |
| 385 } |
322 | 386 |
323 // Using a different seed should not match the signature. | 387 // Using a different seed should not match the signature. |
324 seed_data[0] = 'x'; | 388 { |
325 EXPECT_EQ(VariationsSeedStore::VARIATIONS_SEED_SIGNATURE_INVALID_SEED, | 389 VariationsSeed wrong_seed; |
326 seed_store.VerifySeedSignature(seed_data, base64_seed_signature)); | 390 ASSERT_TRUE(wrong_seed.ParseFromString(seed_data)); |
| 391 (*wrong_seed.mutable_study(0)->mutable_name())[0] = 'x'; |
| 392 std::string base64_wrong_seed_data = SerializeSeedBase64(wrong_seed); |
| 393 |
| 394 prefs.SetString(prefs::kVariationsCompressedSeed, base64_wrong_seed_data); |
| 395 prefs.SetString(prefs::kVariationsSeedSignature, base64_seed_signature); |
| 396 SignatureVerifyingVariationsSeedStore seed_store(&prefs); |
| 397 |
| 398 base::HistogramTester histogram_tester; |
| 399 VariationsSeed seed; |
| 400 EXPECT_FALSE(seed_store.LoadSeed(&seed)); |
| 401 histogram_tester.ExpectUniqueSample( |
| 402 "Variations.LoadSeedSignature", |
| 403 static_cast<base::HistogramBase::Sample>( |
| 404 VerifySignatureResult::INVALID_SEED), |
| 405 1); |
| 406 } |
327 } | 407 } |
328 | 408 |
329 TEST(VariationsSeedStoreTest, ApplyDeltaPatch) { | 409 TEST(VariationsSeedStoreTest, ApplyDeltaPatch) { |
330 // Sample seeds and the server produced delta between them to verify that the | 410 // Sample seeds and the server produced delta between them to verify that the |
331 // client code is able to decode the deltas produced by the server. | 411 // client code is able to decode the deltas produced by the server. |
332 const std::string base64_before_seed_data = | 412 const std::string base64_before_seed_data = |
333 "CigxN2E4ZGJiOTI4ODI0ZGU3ZDU2MGUyODRlODY1ZDllYzg2NzU1MTE0ElgKDFVNQVN0YWJp" | 413 "CigxN2E4ZGJiOTI4ODI0ZGU3ZDU2MGUyODRlODY1ZDllYzg2NzU1MTE0ElgKDFVNQVN0YWJp" |
334 "bGl0eRjEyomgBTgBQgtTZXBhcmF0ZUxvZ0oLCgdEZWZhdWx0EABKDwoLU2VwYXJhdGVMb2cQ" | 414 "bGl0eRjEyomgBTgBQgtTZXBhcmF0ZUxvZ0oLCgdEZWZhdWx0EABKDwoLU2VwYXJhdGVMb2cQ" |
335 "ZFIVEgszNC4wLjE4MDEuMCAAIAEgAiADEkQKIFVNQS1Vbmlmb3JtaXR5LVRyaWFsLTEwMC1Q" | 415 "ZFIVEgszNC4wLjE4MDEuMCAAIAEgAiADEkQKIFVNQS1Vbmlmb3JtaXR5LVRyaWFsLTEwMC1Q" |
336 "ZXJjZW50GIDjhcAFOAFCCGdyb3VwXzAxSgwKCGdyb3VwXzAxEAFgARJPCh9VTUEtVW5pZm9y" | 416 "ZXJjZW50GIDjhcAFOAFCCGdyb3VwXzAxSgwKCGdyb3VwXzAxEAFgARJPCh9VTUEtVW5pZm9y" |
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
394 &response_date, &is_gzip_compressed); | 474 &response_date, &is_gzip_compressed); |
395 EXPECT_EQ("", seed_data); | 475 EXPECT_EQ("", seed_data); |
396 EXPECT_EQ("", seed_signature); | 476 EXPECT_EQ("", seed_signature); |
397 EXPECT_EQ("", seed_country); | 477 EXPECT_EQ("", seed_country); |
398 EXPECT_EQ("", response_date); | 478 EXPECT_EQ("", response_date); |
399 EXPECT_FALSE(is_gzip_compressed); | 479 EXPECT_FALSE(is_gzip_compressed); |
400 } | 480 } |
401 #endif // OS_ANDROID | 481 #endif // OS_ANDROID |
402 | 482 |
403 } // namespace variations | 483 } // namespace variations |
OLD | NEW |