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.
5 #include "components/variations/variations_seed_store.h"
7 #include "base/base64.h"
8 #include "base/prefs/testing_pref_service.h"
9 #include "components/compression/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"
15 namespace chrome_variations
{
19 class TestVariationsSeedStore
: public VariationsSeedStore
{
21 explicit TestVariationsSeedStore(PrefService
* local_state
)
22 : VariationsSeedStore(local_state
) {}
23 ~TestVariationsSeedStore() override
{}
25 bool StoreSeedForTesting(const std::string
& seed_data
) {
26 return StoreSeedData(seed_data
, std::string(), std::string(),
27 base::Time::Now(), false, nullptr);
30 VariationsSeedStore::VerifySignatureResult
VerifySeedSignature(
31 const std::string
& seed_bytes
,
32 const std::string
& base64_seed_signature
) override
{
33 return VariationsSeedStore::VARIATIONS_SEED_SIGNATURE_ENUM_SIZE
;
37 DISALLOW_COPY_AND_ASSIGN(TestVariationsSeedStore
);
41 // Populates |seed| with simple test data. The resulting seed will contain one
42 // study called "test", which contains one experiment called "abc" with
43 // probability weight 100. |seed|'s study field will be cleared before adding
45 variations::VariationsSeed
CreateTestSeed() {
46 variations::VariationsSeed seed
;
47 variations::Study
* study
= seed
.add_study();
48 study
->set_name("test");
49 study
->set_default_experiment_name("abc");
50 variations::Study_Experiment
* experiment
= study
->add_experiment();
51 experiment
->set_name("abc");
52 experiment
->set_probability_weight(100);
53 seed
.set_serial_number("123");
57 // Serializes |seed| to protobuf binary format.
58 std::string
SerializeSeed(const variations::VariationsSeed
& seed
) {
59 std::string serialized_seed
;
60 seed
.SerializeToString(&serialized_seed
);
61 return serialized_seed
;
64 // Compresses |data| using Gzip compression and returns the result.
65 std::string
Compress(const std::string
& data
) {
66 std::string compressed
;
67 const bool result
= compression::GzipCompress(data
, &compressed
);
72 // Serializes |seed| to compressed base64-encoded protobuf binary format.
73 std::string
SerializeSeedBase64(const variations::VariationsSeed
& seed
) {
74 std::string serialized_seed
= SerializeSeed(seed
);
75 std::string base64_serialized_seed
;
76 base::Base64Encode(Compress(serialized_seed
), &base64_serialized_seed
);
77 return base64_serialized_seed
;
80 // Checks whether the pref with name |pref_name| is at its default value in
82 bool PrefHasDefaultValue(const TestingPrefServiceSimple
& prefs
,
83 const char* pref_name
) {
84 return prefs
.FindPreference(pref_name
)->IsDefaultValue();
89 TEST(VariationsSeedStoreTest
, LoadSeed
) {
90 // Store good seed data to test if loading from prefs works.
91 const variations::VariationsSeed seed
= CreateTestSeed();
92 const std::string base64_seed
= SerializeSeedBase64(seed
);
94 TestingPrefServiceSimple prefs
;
95 VariationsSeedStore::RegisterPrefs(prefs
.registry());
96 prefs
.SetString(prefs::kVariationsCompressedSeed
, base64_seed
);
98 TestVariationsSeedStore
seed_store(&prefs
);
100 variations::VariationsSeed loaded_seed
;
101 // Check that loading a seed works correctly.
102 EXPECT_TRUE(seed_store
.LoadSeed(&loaded_seed
));
104 // Check that the loaded data is the same as the original.
105 EXPECT_EQ(SerializeSeed(seed
), SerializeSeed(loaded_seed
));
106 // Make sure the pref hasn't been changed.
107 EXPECT_FALSE(PrefHasDefaultValue(prefs
, prefs::kVariationsCompressedSeed
));
108 EXPECT_EQ(base64_seed
, prefs
.GetString(prefs::kVariationsCompressedSeed
));
110 // Check that loading a bad seed returns false and clears the pref.
111 prefs
.SetString(prefs::kVariationsCompressedSeed
, "this should fail");
112 EXPECT_FALSE(PrefHasDefaultValue(prefs
, prefs::kVariationsCompressedSeed
));
113 EXPECT_FALSE(seed_store
.LoadSeed(&loaded_seed
));
114 EXPECT_TRUE(PrefHasDefaultValue(prefs
, prefs::kVariationsCompressedSeed
));
115 EXPECT_TRUE(PrefHasDefaultValue(prefs
, prefs::kVariationsSeedDate
));
116 EXPECT_TRUE(PrefHasDefaultValue(prefs
, prefs::kVariationsSeedSignature
));
118 // Check that having no seed in prefs results in a return value of false.
119 prefs
.ClearPref(prefs::kVariationsCompressedSeed
);
120 EXPECT_FALSE(seed_store
.LoadSeed(&loaded_seed
));
123 TEST(VariationsSeedStoreTest
, GetInvalidSignature
) {
124 const variations::VariationsSeed seed
= CreateTestSeed();
125 const std::string base64_seed
= SerializeSeedBase64(seed
);
127 TestingPrefServiceSimple prefs
;
128 VariationsSeedStore::RegisterPrefs(prefs
.registry());
129 prefs
.SetString(prefs::kVariationsSeed
, base64_seed
);
131 // The below seed and signature pair were generated using the server's
133 const std::string base64_seed_data
=
134 "CigxZDI5NDY0ZmIzZDc4ZmYxNTU2ZTViNTUxYzY0NDdjYmM3NGU1ZmQwEr0BCh9VTUEtVW5p"
135 "Zm9ybWl0eS1UcmlhbC0xMC1QZXJjZW50GICckqUFOAFCB2RlZmF1bHRKCwoHZGVmYXVsdBAB"
136 "SgwKCGdyb3VwXzAxEAFKDAoIZ3JvdXBfMDIQAUoMCghncm91cF8wMxABSgwKCGdyb3VwXzA0"
137 "EAFKDAoIZ3JvdXBfMDUQAUoMCghncm91cF8wNhABSgwKCGdyb3VwXzA3EAFKDAoIZ3JvdXBf"
138 "MDgQAUoMCghncm91cF8wORAB";
139 const std::string base64_seed_signature
=
140 "MEQCIDD1IVxjzWYncun+9IGzqYjZvqxxujQEayJULTlbTGA/AiAr0oVmEgVUQZBYq5VLOSvy"
141 "96JkMYgzTkHPwbv7K/CmgA==";
142 const std::string base64_seed_signature_invalid
=
143 "AEQCIDD1IVxjzWYncun+9IGzqYjZvqxxujQEayJULTlbTGA/AiAr0oVmEgVUQZBYq5VLOSvy"
144 "96JkMYgzTkHPwbv7K/CmgA==";
146 // Set seed and valid signature in prefs.
147 prefs
.SetString(prefs::kVariationsSeed
, base64_seed_data
);
148 prefs
.SetString(prefs::kVariationsSeedSignature
, base64_seed_signature
);
150 VariationsSeedStore
seed_store(&prefs
);
151 variations::VariationsSeed loaded_seed
;
152 seed_store
.LoadSeed(&loaded_seed
);
153 std::string invalid_signature
= seed_store
.GetInvalidSignature();
154 // Valid signature so we get an empty string.
155 EXPECT_EQ(std::string(), invalid_signature
);
157 prefs
.SetString(prefs::kVariationsSeedSignature
,
158 base64_seed_signature_invalid
);
159 seed_store
.LoadSeed(&loaded_seed
);
160 // Invalid signature, so we should get the signature itself, except on mobile
161 // where we should get an empty string because verification is not enabled.
162 invalid_signature
= seed_store
.GetInvalidSignature();
163 #if defined(OS_IOS) || defined(OS_ANDROID)
164 EXPECT_EQ(std::string(), invalid_signature
);
166 EXPECT_EQ(base64_seed_signature_invalid
, invalid_signature
);
169 prefs
.SetString(prefs::kVariationsSeedSignature
, std::string());
170 seed_store
.LoadSeed(&loaded_seed
);
171 invalid_signature
= seed_store
.GetInvalidSignature();
172 // Empty signature, not considered invalid.
173 EXPECT_EQ(std::string(), invalid_signature
);
176 TEST(VariationsSeedStoreTest
, StoreSeedData
) {
177 const variations::VariationsSeed seed
= CreateTestSeed();
178 const std::string serialized_seed
= SerializeSeed(seed
);
180 TestingPrefServiceSimple prefs
;
181 VariationsSeedStore::RegisterPrefs(prefs
.registry());
183 TestVariationsSeedStore
seed_store(&prefs
);
185 EXPECT_TRUE(seed_store
.StoreSeedForTesting(serialized_seed
));
186 // Make sure the pref was actually set.
187 EXPECT_FALSE(PrefHasDefaultValue(prefs
, prefs::kVariationsCompressedSeed
));
189 std::string loaded_compressed_seed
=
190 prefs
.GetString(prefs::kVariationsCompressedSeed
);
191 std::string decoded_compressed_seed
;
192 ASSERT_TRUE(base::Base64Decode(loaded_compressed_seed
,
193 &decoded_compressed_seed
));
194 // Make sure the stored seed from pref is the same as the seed we created.
195 EXPECT_EQ(Compress(serialized_seed
), decoded_compressed_seed
);
197 // Check if trying to store a bad seed leaves the pref unchanged.
198 prefs
.ClearPref(prefs::kVariationsCompressedSeed
);
199 EXPECT_FALSE(seed_store
.StoreSeedForTesting("should fail"));
200 EXPECT_TRUE(PrefHasDefaultValue(prefs
, prefs::kVariationsCompressedSeed
));
203 TEST(VariationsSeedStoreTest
, StoreSeedData_ParsedSeed
) {
204 const variations::VariationsSeed seed
= CreateTestSeed();
205 const std::string serialized_seed
= SerializeSeed(seed
);
207 TestingPrefServiceSimple prefs
;
208 VariationsSeedStore::RegisterPrefs(prefs
.registry());
209 TestVariationsSeedStore
seed_store(&prefs
);
211 variations::VariationsSeed parsed_seed
;
212 EXPECT_TRUE(seed_store
.StoreSeedData(serialized_seed
, std::string(),
213 std::string(), base::Time::Now(), false,
215 EXPECT_EQ(serialized_seed
, SerializeSeed(parsed_seed
));
218 TEST(VariationsSeedStoreTest
, StoreSeedData_CountryCode
) {
219 TestingPrefServiceSimple prefs
;
220 VariationsSeedStore::RegisterPrefs(prefs
.registry());
221 TestVariationsSeedStore
seed_store(&prefs
);
223 // Test with a seed country code and no header value.
224 variations::VariationsSeed seed
= CreateTestSeed();
225 seed
.set_country_code("test_country");
226 EXPECT_TRUE(seed_store
.StoreSeedData(SerializeSeed(seed
), std::string(),
227 std::string(), base::Time::Now(), false,
229 EXPECT_EQ("test_country", prefs
.GetString(prefs::kVariationsCountry
));
231 // Test with a header value and no seed country.
232 prefs
.ClearPref(prefs::kVariationsCountry
);
233 seed
.clear_country_code();
234 EXPECT_TRUE(seed_store
.StoreSeedData(SerializeSeed(seed
), std::string(),
235 "test_country2", base::Time::Now(),
237 EXPECT_EQ("test_country2", prefs
.GetString(prefs::kVariationsCountry
));
239 // Test with a seed country code and header value.
240 prefs
.ClearPref(prefs::kVariationsCountry
);
241 seed
.set_country_code("test_country3");
242 EXPECT_TRUE(seed_store
.StoreSeedData(SerializeSeed(seed
), std::string(),
243 "test_country4", base::Time::Now(),
245 EXPECT_EQ("test_country4", prefs
.GetString(prefs::kVariationsCountry
));
247 // Test with no country code specified - which should preserve the old value.
248 seed
.clear_country_code();
249 EXPECT_TRUE(seed_store
.StoreSeedData(SerializeSeed(seed
), std::string(),
250 std::string(), base::Time::Now(), false,
252 EXPECT_EQ("test_country4", prefs
.GetString(prefs::kVariationsCountry
));
255 TEST(VariationsSeedStoreTest
, VerifySeedSignature
) {
256 // The below seed and signature pair were generated using the server's
258 const std::string base64_seed_data
=
259 "CigxZDI5NDY0ZmIzZDc4ZmYxNTU2ZTViNTUxYzY0NDdjYmM3NGU1ZmQwEr0BCh9VTUEtVW5p"
260 "Zm9ybWl0eS1UcmlhbC0xMC1QZXJjZW50GICckqUFOAFCB2RlZmF1bHRKCwoHZGVmYXVsdBAB"
261 "SgwKCGdyb3VwXzAxEAFKDAoIZ3JvdXBfMDIQAUoMCghncm91cF8wMxABSgwKCGdyb3VwXzA0"
262 "EAFKDAoIZ3JvdXBfMDUQAUoMCghncm91cF8wNhABSgwKCGdyb3VwXzA3EAFKDAoIZ3JvdXBf"
263 "MDgQAUoMCghncm91cF8wORAB";
264 const std::string base64_seed_signature
=
265 "MEQCIDD1IVxjzWYncun+9IGzqYjZvqxxujQEayJULTlbTGA/AiAr0oVmEgVUQZBYq5VLOSvy"
266 "96JkMYgzTkHPwbv7K/CmgA==";
268 std::string seed_data
;
269 EXPECT_TRUE(base::Base64Decode(base64_seed_data
, &seed_data
));
271 VariationsSeedStore
seed_store(NULL
);
273 #if defined(OS_IOS) || defined(OS_ANDROID)
274 // Signature verification is not enabled on mobile.
275 if (seed_store
.VerifySeedSignature(seed_data
, base64_seed_signature
) ==
276 VariationsSeedStore::VARIATIONS_SEED_SIGNATURE_ENUM_SIZE
) {
281 // The above inputs should be valid.
282 EXPECT_EQ(VariationsSeedStore::VARIATIONS_SEED_SIGNATURE_VALID
,
283 seed_store
.VerifySeedSignature(seed_data
, base64_seed_signature
));
285 // If there's no signature, the corresponding result should be returned.
286 EXPECT_EQ(VariationsSeedStore::VARIATIONS_SEED_SIGNATURE_MISSING
,
287 seed_store
.VerifySeedSignature(seed_data
, std::string()));
289 // Using non-base64 encoded value as signature (e.g. seed data) should fail.
290 EXPECT_EQ(VariationsSeedStore::VARIATIONS_SEED_SIGNATURE_DECODE_FAILED
,
291 seed_store
.VerifySeedSignature(seed_data
, seed_data
));
293 // Using a different signature (e.g. the base64 seed data) should fail.
294 #if defined(USE_OPENSSL)
295 // OpenSSL doesn't distinguish signature decode failure from the
296 // signature not matching.
297 EXPECT_EQ(VariationsSeedStore::VARIATIONS_SEED_SIGNATURE_INVALID_SEED
,
298 seed_store
.VerifySeedSignature(seed_data
, base64_seed_data
));
300 EXPECT_EQ(VariationsSeedStore::VARIATIONS_SEED_SIGNATURE_INVALID_SIGNATURE
,
301 seed_store
.VerifySeedSignature(seed_data
, base64_seed_data
));
304 // Using a different seed should not match the signature.
306 EXPECT_EQ(VariationsSeedStore::VARIATIONS_SEED_SIGNATURE_INVALID_SEED
,
307 seed_store
.VerifySeedSignature(seed_data
, base64_seed_signature
));
310 TEST(VariationsSeedStoreTest
, ApplyDeltaPatch
) {
311 // Sample seeds and the server produced delta between them to verify that the
312 // client code is able to decode the deltas produced by the server.
313 const std::string base64_before_seed_data
=
314 "CigxN2E4ZGJiOTI4ODI0ZGU3ZDU2MGUyODRlODY1ZDllYzg2NzU1MTE0ElgKDFVNQVN0YWJp"
315 "bGl0eRjEyomgBTgBQgtTZXBhcmF0ZUxvZ0oLCgdEZWZhdWx0EABKDwoLU2VwYXJhdGVMb2cQ"
316 "ZFIVEgszNC4wLjE4MDEuMCAAIAEgAiADEkQKIFVNQS1Vbmlmb3JtaXR5LVRyaWFsLTEwMC1Q"
317 "ZXJjZW50GIDjhcAFOAFCCGdyb3VwXzAxSgwKCGdyb3VwXzAxEAFgARJPCh9VTUEtVW5pZm9y"
318 "bWl0eS1UcmlhbC01MC1QZXJjZW50GIDjhcAFOAFCB2RlZmF1bHRKDAoIZ3JvdXBfMDEQAUoL"
319 "CgdkZWZhdWx0EAFgAQ==";
320 const std::string base64_after_seed_data
=
321 "CigyNGQzYTM3ZTAxYmViOWYwNWYzMjM4YjUzNWY3MDg1ZmZlZWI4NzQwElgKDFVNQVN0YWJp"
322 "bGl0eRjEyomgBTgBQgtTZXBhcmF0ZUxvZ0oLCgdEZWZhdWx0EABKDwoLU2VwYXJhdGVMb2cQ"
323 "ZFIVEgszNC4wLjE4MDEuMCAAIAEgAiADEpIBCh9VTUEtVW5pZm9ybWl0eS1UcmlhbC0yMC1Q"
324 "ZXJjZW50GIDjhcAFOAFCB2RlZmF1bHRKEQoIZ3JvdXBfMDEQARijtskBShEKCGdyb3VwXzAy"
325 "EAEYpLbJAUoRCghncm91cF8wMxABGKW2yQFKEQoIZ3JvdXBfMDQQARimtskBShAKB2RlZmF1"
326 "bHQQARiitskBYAESWAofVU1BLVVuaWZvcm1pdHktVHJpYWwtNTAtUGVyY2VudBiA44XABTgB"
327 "QgdkZWZhdWx0Sg8KC25vbl9kZWZhdWx0EAFKCwoHZGVmYXVsdBABUgQoACgBYAE=";
328 const std::string base64_delta_data
=
329 "KgooMjRkM2EzN2UwMWJlYjlmMDVmMzIzOGI1MzVmNzA4NWZmZWViODc0MAAqW+4BkgEKH1VN"
330 "QS1Vbmlmb3JtaXR5LVRyaWFsLTIwLVBlcmNlbnQYgOOFwAU4AUIHZGVmYXVsdEoRCghncm91"
331 "cF8wMRABGKO2yQFKEQoIZ3JvdXBfMDIQARiktskBShEKCGdyb3VwXzAzEAEYpbbJAUoRCghn"
332 "cm91cF8wNBABGKa2yQFKEAoHZGVmYXVsdBABGKK2yQFgARJYCh9VTUEtVW5pZm9ybWl0eS1U"
333 "cmlhbC01MC1QZXJjZW50GIDjhcAFOAFCB2RlZmF1bHRKDwoLbm9uX2RlZmF1bHQQAUoLCgdk"
334 "ZWZhdWx0EAFSBCgAKAFgAQ==";
336 std::string before_seed_data
;
337 std::string after_seed_data
;
338 std::string delta_data
;
339 EXPECT_TRUE(base::Base64Decode(base64_before_seed_data
, &before_seed_data
));
340 EXPECT_TRUE(base::Base64Decode(base64_after_seed_data
, &after_seed_data
));
341 EXPECT_TRUE(base::Base64Decode(base64_delta_data
, &delta_data
));
344 EXPECT_TRUE(VariationsSeedStore::ApplyDeltaPatch(before_seed_data
, delta_data
,
346 EXPECT_EQ(after_seed_data
, output
);
349 } // namespace chrome_variations