| OLD | NEW |
| (Empty) |
| 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 | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "chrome/browser/metrics/variations/variations_seed_store.h" | |
| 6 | |
| 7 #include "base/base64.h" | |
| 8 #include "base/prefs/testing_pref_service.h" | |
| 9 #include "components/metrics/compression_utils.h" | |
| 10 #include "components/variations/pref_names.h" | |
| 11 #include "components/variations/proto/study.pb.h" | |
| 12 #include "components/variations/proto/variations_seed.pb.h" | |
| 13 #include "testing/gtest/include/gtest/gtest.h" | |
| 14 | |
| 15 namespace chrome_variations { | |
| 16 | |
| 17 namespace { | |
| 18 | |
| 19 class TestVariationsSeedStore : public VariationsSeedStore { | |
| 20 public: | |
| 21 explicit TestVariationsSeedStore(PrefService* local_state) | |
| 22 : VariationsSeedStore(local_state) {} | |
| 23 ~TestVariationsSeedStore() override {} | |
| 24 | |
| 25 bool StoreSeedForTesting(const std::string& seed_data) { | |
| 26 return StoreSeedData(seed_data, std::string(), base::Time::Now(), NULL); | |
| 27 } | |
| 28 | |
| 29 VariationsSeedStore::VerifySignatureResult VerifySeedSignature( | |
| 30 const std::string& seed_bytes, | |
| 31 const std::string& base64_seed_signature) override { | |
| 32 return VariationsSeedStore::VARIATIONS_SEED_SIGNATURE_ENUM_SIZE; | |
| 33 } | |
| 34 | |
| 35 private: | |
| 36 DISALLOW_COPY_AND_ASSIGN(TestVariationsSeedStore); | |
| 37 }; | |
| 38 | |
| 39 | |
| 40 // Populates |seed| with simple test data. The resulting seed will contain one | |
| 41 // study called "test", which contains one experiment called "abc" with | |
| 42 // probability weight 100. |seed|'s study field will be cleared before adding | |
| 43 // the new study. | |
| 44 variations::VariationsSeed CreateTestSeed() { | |
| 45 variations::VariationsSeed seed; | |
| 46 variations::Study* study = seed.add_study(); | |
| 47 study->set_name("test"); | |
| 48 study->set_default_experiment_name("abc"); | |
| 49 variations::Study_Experiment* experiment = study->add_experiment(); | |
| 50 experiment->set_name("abc"); | |
| 51 experiment->set_probability_weight(100); | |
| 52 seed.set_serial_number("123"); | |
| 53 return seed; | |
| 54 } | |
| 55 | |
| 56 // Serializes |seed| to protobuf binary format. | |
| 57 std::string SerializeSeed(const variations::VariationsSeed& seed) { | |
| 58 std::string serialized_seed; | |
| 59 seed.SerializeToString(&serialized_seed); | |
| 60 return serialized_seed; | |
| 61 } | |
| 62 | |
| 63 // Compresses |data| using Gzip compression and returns the result. | |
| 64 std::string Compress(const std::string& data) { | |
| 65 std::string compressed; | |
| 66 const bool result = metrics::GzipCompress(data, &compressed); | |
| 67 EXPECT_TRUE(result); | |
| 68 return compressed; | |
| 69 } | |
| 70 | |
| 71 // Serializes |seed| to compressed base64-encoded protobuf binary format. | |
| 72 std::string SerializeSeedBase64(const variations::VariationsSeed& seed) { | |
| 73 std::string serialized_seed = SerializeSeed(seed); | |
| 74 std::string base64_serialized_seed; | |
| 75 base::Base64Encode(Compress(serialized_seed), &base64_serialized_seed); | |
| 76 return base64_serialized_seed; | |
| 77 } | |
| 78 | |
| 79 // Checks whether the pref with name |pref_name| is at its default value in | |
| 80 // |prefs|. | |
| 81 bool PrefHasDefaultValue(const TestingPrefServiceSimple& prefs, | |
| 82 const char* pref_name) { | |
| 83 return prefs.FindPreference(pref_name)->IsDefaultValue(); | |
| 84 } | |
| 85 | |
| 86 } // namespace | |
| 87 | |
| 88 TEST(VariationsSeedStoreTest, LoadSeed) { | |
| 89 // Store good seed data to test if loading from prefs works. | |
| 90 const variations::VariationsSeed seed = CreateTestSeed(); | |
| 91 const std::string base64_seed = SerializeSeedBase64(seed); | |
| 92 | |
| 93 TestingPrefServiceSimple prefs; | |
| 94 VariationsSeedStore::RegisterPrefs(prefs.registry()); | |
| 95 prefs.SetString(metrics::prefs::kVariationsCompressedSeed, base64_seed); | |
| 96 | |
| 97 TestVariationsSeedStore seed_store(&prefs); | |
| 98 | |
| 99 variations::VariationsSeed loaded_seed; | |
| 100 // Check that loading a seed works correctly. | |
| 101 EXPECT_TRUE(seed_store.LoadSeed(&loaded_seed)); | |
| 102 | |
| 103 // Check that the loaded data is the same as the original. | |
| 104 EXPECT_EQ(SerializeSeed(seed), SerializeSeed(loaded_seed)); | |
| 105 // Make sure the pref hasn't been changed. | |
| 106 EXPECT_FALSE( | |
| 107 PrefHasDefaultValue(prefs, metrics::prefs::kVariationsCompressedSeed)); | |
| 108 EXPECT_EQ(base64_seed, | |
| 109 prefs.GetString(metrics::prefs::kVariationsCompressedSeed)); | |
| 110 | |
| 111 // Check that loading a bad seed returns false and clears the pref. | |
| 112 prefs.SetString(metrics::prefs::kVariationsCompressedSeed, | |
| 113 "this should fail"); | |
| 114 EXPECT_FALSE( | |
| 115 PrefHasDefaultValue(prefs, metrics::prefs::kVariationsCompressedSeed)); | |
| 116 EXPECT_FALSE(seed_store.LoadSeed(&loaded_seed)); | |
| 117 EXPECT_TRUE( | |
| 118 PrefHasDefaultValue(prefs, metrics::prefs::kVariationsCompressedSeed)); | |
| 119 EXPECT_TRUE(PrefHasDefaultValue(prefs, metrics::prefs::kVariationsSeedDate)); | |
| 120 EXPECT_TRUE( | |
| 121 PrefHasDefaultValue(prefs, metrics::prefs::kVariationsSeedSignature)); | |
| 122 | |
| 123 // Check that having no seed in prefs results in a return value of false. | |
| 124 prefs.ClearPref(metrics::prefs::kVariationsCompressedSeed); | |
| 125 EXPECT_FALSE(seed_store.LoadSeed(&loaded_seed)); | |
| 126 } | |
| 127 | |
| 128 TEST(VariationsSeedStoreTest, GetInvalidSignature) { | |
| 129 const variations::VariationsSeed seed = CreateTestSeed(); | |
| 130 const std::string base64_seed = SerializeSeedBase64(seed); | |
| 131 | |
| 132 TestingPrefServiceSimple prefs; | |
| 133 VariationsSeedStore::RegisterPrefs(prefs.registry()); | |
| 134 prefs.SetString(metrics::prefs::kVariationsSeed, base64_seed); | |
| 135 | |
| 136 // The below seed and signature pair were generated using the server's | |
| 137 // private key. | |
| 138 const std::string base64_seed_data = | |
| 139 "CigxZDI5NDY0ZmIzZDc4ZmYxNTU2ZTViNTUxYzY0NDdjYmM3NGU1ZmQwEr0BCh9VTUEtVW5p" | |
| 140 "Zm9ybWl0eS1UcmlhbC0xMC1QZXJjZW50GICckqUFOAFCB2RlZmF1bHRKCwoHZGVmYXVsdBAB" | |
| 141 "SgwKCGdyb3VwXzAxEAFKDAoIZ3JvdXBfMDIQAUoMCghncm91cF8wMxABSgwKCGdyb3VwXzA0" | |
| 142 "EAFKDAoIZ3JvdXBfMDUQAUoMCghncm91cF8wNhABSgwKCGdyb3VwXzA3EAFKDAoIZ3JvdXBf" | |
| 143 "MDgQAUoMCghncm91cF8wORAB"; | |
| 144 const std::string base64_seed_signature = | |
| 145 "MEQCIDD1IVxjzWYncun+9IGzqYjZvqxxujQEayJULTlbTGA/AiAr0oVmEgVUQZBYq5VLOSvy" | |
| 146 "96JkMYgzTkHPwbv7K/CmgA=="; | |
| 147 const std::string base64_seed_signature_invalid = | |
| 148 "AEQCIDD1IVxjzWYncun+9IGzqYjZvqxxujQEayJULTlbTGA/AiAr0oVmEgVUQZBYq5VLOSvy" | |
| 149 "96JkMYgzTkHPwbv7K/CmgA=="; | |
| 150 | |
| 151 // Set seed and valid signature in prefs. | |
| 152 prefs.SetString(metrics::prefs::kVariationsSeed, base64_seed_data); | |
| 153 prefs.SetString(metrics::prefs::kVariationsSeedSignature, | |
| 154 base64_seed_signature); | |
| 155 | |
| 156 VariationsSeedStore seed_store(&prefs); | |
| 157 variations::VariationsSeed loaded_seed; | |
| 158 seed_store.LoadSeed(&loaded_seed); | |
| 159 std::string invalid_signature = seed_store.GetInvalidSignature(); | |
| 160 // Valid signature so we get an empty string. | |
| 161 EXPECT_EQ(std::string(), invalid_signature); | |
| 162 | |
| 163 prefs.SetString(metrics::prefs::kVariationsSeedSignature, | |
| 164 base64_seed_signature_invalid); | |
| 165 seed_store.LoadSeed(&loaded_seed); | |
| 166 // Invalid signature, so we should get the signature itself, except on mobile | |
| 167 // where we should get an empty string because verification is not enabled. | |
| 168 invalid_signature = seed_store.GetInvalidSignature(); | |
| 169 #if defined(OS_IOS) || defined(OS_ANDROID) | |
| 170 EXPECT_EQ(std::string(), invalid_signature); | |
| 171 #else | |
| 172 EXPECT_EQ(base64_seed_signature_invalid, invalid_signature); | |
| 173 #endif | |
| 174 | |
| 175 prefs.SetString(metrics::prefs::kVariationsSeedSignature, std::string()); | |
| 176 seed_store.LoadSeed(&loaded_seed); | |
| 177 invalid_signature = seed_store.GetInvalidSignature(); | |
| 178 // Empty signature, not considered invalid. | |
| 179 EXPECT_EQ(std::string(), invalid_signature); | |
| 180 } | |
| 181 | |
| 182 TEST(VariationsSeedStoreTest, StoreSeedData) { | |
| 183 const variations::VariationsSeed seed = CreateTestSeed(); | |
| 184 const std::string serialized_seed = SerializeSeed(seed); | |
| 185 | |
| 186 TestingPrefServiceSimple prefs; | |
| 187 VariationsSeedStore::RegisterPrefs(prefs.registry()); | |
| 188 | |
| 189 TestVariationsSeedStore seed_store(&prefs); | |
| 190 | |
| 191 EXPECT_TRUE(seed_store.StoreSeedForTesting(serialized_seed)); | |
| 192 // Make sure the pref was actually set. | |
| 193 EXPECT_FALSE( | |
| 194 PrefHasDefaultValue(prefs, metrics::prefs::kVariationsCompressedSeed)); | |
| 195 | |
| 196 std::string loaded_compressed_seed = | |
| 197 prefs.GetString(metrics::prefs::kVariationsCompressedSeed); | |
| 198 std::string decoded_compressed_seed; | |
| 199 ASSERT_TRUE(base::Base64Decode(loaded_compressed_seed, | |
| 200 &decoded_compressed_seed)); | |
| 201 // Make sure the stored seed from pref is the same as the seed we created. | |
| 202 EXPECT_EQ(Compress(serialized_seed), decoded_compressed_seed); | |
| 203 | |
| 204 // Check if trying to store a bad seed leaves the pref unchanged. | |
| 205 prefs.ClearPref(metrics::prefs::kVariationsCompressedSeed); | |
| 206 EXPECT_FALSE(seed_store.StoreSeedForTesting("should fail")); | |
| 207 EXPECT_TRUE( | |
| 208 PrefHasDefaultValue(prefs, metrics::prefs::kVariationsCompressedSeed)); | |
| 209 } | |
| 210 | |
| 211 TEST(VariationsSeedStoreTest, StoreSeedData_ParsedSeed) { | |
| 212 const variations::VariationsSeed seed = CreateTestSeed(); | |
| 213 const std::string serialized_seed = SerializeSeed(seed); | |
| 214 | |
| 215 TestingPrefServiceSimple prefs; | |
| 216 VariationsSeedStore::RegisterPrefs(prefs.registry()); | |
| 217 TestVariationsSeedStore seed_store(&prefs); | |
| 218 | |
| 219 variations::VariationsSeed parsed_seed; | |
| 220 EXPECT_TRUE(seed_store.StoreSeedData(serialized_seed, std::string(), | |
| 221 base::Time::Now(), &parsed_seed)); | |
| 222 EXPECT_EQ(serialized_seed, SerializeSeed(parsed_seed)); | |
| 223 } | |
| 224 | |
| 225 TEST(VariationsSeedStoreTest, StoreSeedData_CountryCode) { | |
| 226 TestingPrefServiceSimple prefs; | |
| 227 VariationsSeedStore::RegisterPrefs(prefs.registry()); | |
| 228 TestVariationsSeedStore seed_store(&prefs); | |
| 229 | |
| 230 variations::VariationsSeed seed = CreateTestSeed(); | |
| 231 seed.set_country_code("test_country"); | |
| 232 EXPECT_TRUE(seed_store.StoreSeedData(SerializeSeed(seed), std::string(), | |
| 233 base::Time::Now(), nullptr)); | |
| 234 EXPECT_EQ("test_country", | |
| 235 prefs.GetString(metrics::prefs::kVariationsCountry)); | |
| 236 } | |
| 237 | |
| 238 TEST(VariationsSeedStoreTest, VerifySeedSignature) { | |
| 239 // The below seed and signature pair were generated using the server's | |
| 240 // private key. | |
| 241 const std::string base64_seed_data = | |
| 242 "CigxZDI5NDY0ZmIzZDc4ZmYxNTU2ZTViNTUxYzY0NDdjYmM3NGU1ZmQwEr0BCh9VTUEtVW5p" | |
| 243 "Zm9ybWl0eS1UcmlhbC0xMC1QZXJjZW50GICckqUFOAFCB2RlZmF1bHRKCwoHZGVmYXVsdBAB" | |
| 244 "SgwKCGdyb3VwXzAxEAFKDAoIZ3JvdXBfMDIQAUoMCghncm91cF8wMxABSgwKCGdyb3VwXzA0" | |
| 245 "EAFKDAoIZ3JvdXBfMDUQAUoMCghncm91cF8wNhABSgwKCGdyb3VwXzA3EAFKDAoIZ3JvdXBf" | |
| 246 "MDgQAUoMCghncm91cF8wORAB"; | |
| 247 const std::string base64_seed_signature = | |
| 248 "MEQCIDD1IVxjzWYncun+9IGzqYjZvqxxujQEayJULTlbTGA/AiAr0oVmEgVUQZBYq5VLOSvy" | |
| 249 "96JkMYgzTkHPwbv7K/CmgA=="; | |
| 250 | |
| 251 std::string seed_data; | |
| 252 EXPECT_TRUE(base::Base64Decode(base64_seed_data, &seed_data)); | |
| 253 | |
| 254 VariationsSeedStore seed_store(NULL); | |
| 255 | |
| 256 #if defined(OS_IOS) || defined(OS_ANDROID) | |
| 257 // Signature verification is not enabled on mobile. | |
| 258 if (seed_store.VerifySeedSignature(seed_data, base64_seed_signature) == | |
| 259 VariationsSeedStore::VARIATIONS_SEED_SIGNATURE_ENUM_SIZE) { | |
| 260 return; | |
| 261 } | |
| 262 #endif | |
| 263 | |
| 264 // The above inputs should be valid. | |
| 265 EXPECT_EQ(VariationsSeedStore::VARIATIONS_SEED_SIGNATURE_VALID, | |
| 266 seed_store.VerifySeedSignature(seed_data, base64_seed_signature)); | |
| 267 | |
| 268 // If there's no signature, the corresponding result should be returned. | |
| 269 EXPECT_EQ(VariationsSeedStore::VARIATIONS_SEED_SIGNATURE_MISSING, | |
| 270 seed_store.VerifySeedSignature(seed_data, std::string())); | |
| 271 | |
| 272 // Using non-base64 encoded value as signature (e.g. seed data) should fail. | |
| 273 EXPECT_EQ(VariationsSeedStore::VARIATIONS_SEED_SIGNATURE_DECODE_FAILED, | |
| 274 seed_store.VerifySeedSignature(seed_data, seed_data)); | |
| 275 | |
| 276 // Using a different signature (e.g. the base64 seed data) should fail. | |
| 277 #if defined(USE_OPENSSL) | |
| 278 // OpenSSL doesn't distinguish signature decode failure from the | |
| 279 // signature not matching. | |
| 280 EXPECT_EQ(VariationsSeedStore::VARIATIONS_SEED_SIGNATURE_INVALID_SEED, | |
| 281 seed_store.VerifySeedSignature(seed_data, base64_seed_data)); | |
| 282 #else | |
| 283 EXPECT_EQ(VariationsSeedStore::VARIATIONS_SEED_SIGNATURE_INVALID_SIGNATURE, | |
| 284 seed_store.VerifySeedSignature(seed_data, base64_seed_data)); | |
| 285 #endif | |
| 286 | |
| 287 // Using a different seed should not match the signature. | |
| 288 seed_data[0] = 'x'; | |
| 289 EXPECT_EQ(VariationsSeedStore::VARIATIONS_SEED_SIGNATURE_INVALID_SEED, | |
| 290 seed_store.VerifySeedSignature(seed_data, base64_seed_signature)); | |
| 291 } | |
| 292 | |
| 293 } // namespace chrome_variations | |
| OLD | NEW |