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 "chrome/browser/metrics/variations/variations_seed_store.h"
7 #include "base/base64.h"
8 #include "base/prefs/testing_pref_service.h"
9 #include "chrome/common/pref_names.h"
10 #include "components/metrics/compression_utils.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(), base::Time::Now(), NULL
);
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
;
36 DISALLOW_COPY_AND_ASSIGN(TestVariationsSeedStore
);
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
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");
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
;
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
);
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
;
79 // Checks whether the pref with name |pref_name| is at its default value in
81 bool PrefHasDefaultValue(const TestingPrefServiceSimple
& prefs
,
82 const char* pref_name
) {
83 return prefs
.FindPreference(pref_name
)->IsDefaultValue();
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
);
93 TestingPrefServiceSimple prefs
;
94 VariationsSeedStore::RegisterPrefs(prefs
.registry());
95 prefs
.SetString(prefs::kVariationsCompressedSeed
, base64_seed
);
97 TestVariationsSeedStore
seed_store(&prefs
);
99 variations::VariationsSeed loaded_seed
;
100 // Check that loading a seed works correctly.
101 EXPECT_TRUE(seed_store
.LoadSeed(&loaded_seed
));
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(PrefHasDefaultValue(prefs
, prefs::kVariationsCompressedSeed
));
107 EXPECT_EQ(base64_seed
, prefs
.GetString(prefs::kVariationsCompressedSeed
));
109 // Check that loading a bad seed returns false and clears the pref.
110 prefs
.SetString(prefs::kVariationsCompressedSeed
, "this should fail");
111 EXPECT_FALSE(PrefHasDefaultValue(prefs
, prefs::kVariationsCompressedSeed
));
112 EXPECT_FALSE(seed_store
.LoadSeed(&loaded_seed
));
113 EXPECT_TRUE(PrefHasDefaultValue(prefs
, prefs::kVariationsCompressedSeed
));
114 EXPECT_TRUE(PrefHasDefaultValue(prefs
, prefs::kVariationsSeedDate
));
115 EXPECT_TRUE(PrefHasDefaultValue(prefs
, prefs::kVariationsSeedSignature
));
117 // Check that having no seed in prefs results in a return value of false.
118 prefs
.ClearPref(prefs::kVariationsCompressedSeed
);
119 EXPECT_FALSE(seed_store
.LoadSeed(&loaded_seed
));
122 TEST(VariationsSeedStoreTest
, GetInvalidSignature
) {
123 const variations::VariationsSeed seed
= CreateTestSeed();
124 const std::string base64_seed
= SerializeSeedBase64(seed
);
126 TestingPrefServiceSimple prefs
;
127 VariationsSeedStore::RegisterPrefs(prefs
.registry());
128 prefs
.SetString(prefs::kVariationsSeed
, base64_seed
);
130 // The below seed and signature pair were generated using the server's
132 const std::string base64_seed_data
=
133 "CigxZDI5NDY0ZmIzZDc4ZmYxNTU2ZTViNTUxYzY0NDdjYmM3NGU1ZmQwEr0BCh9VTUEtVW5p"
134 "Zm9ybWl0eS1UcmlhbC0xMC1QZXJjZW50GICckqUFOAFCB2RlZmF1bHRKCwoHZGVmYXVsdBAB"
135 "SgwKCGdyb3VwXzAxEAFKDAoIZ3JvdXBfMDIQAUoMCghncm91cF8wMxABSgwKCGdyb3VwXzA0"
136 "EAFKDAoIZ3JvdXBfMDUQAUoMCghncm91cF8wNhABSgwKCGdyb3VwXzA3EAFKDAoIZ3JvdXBf"
137 "MDgQAUoMCghncm91cF8wORAB";
138 const std::string base64_seed_signature
=
139 "MEQCIDD1IVxjzWYncun+9IGzqYjZvqxxujQEayJULTlbTGA/AiAr0oVmEgVUQZBYq5VLOSvy"
140 "96JkMYgzTkHPwbv7K/CmgA==";
141 const std::string base64_seed_signature_invalid
=
142 "AEQCIDD1IVxjzWYncun+9IGzqYjZvqxxujQEayJULTlbTGA/AiAr0oVmEgVUQZBYq5VLOSvy"
143 "96JkMYgzTkHPwbv7K/CmgA==";
145 // Set seed and valid signature in prefs.
146 prefs
.SetString(prefs::kVariationsSeed
, base64_seed_data
);
147 prefs
.SetString(prefs::kVariationsSeedSignature
, base64_seed_signature
);
149 VariationsSeedStore
seed_store(&prefs
);
150 variations::VariationsSeed loaded_seed
;
151 seed_store
.LoadSeed(&loaded_seed
);
152 std::string invalid_signature
= seed_store
.GetInvalidSignature();
153 // Valid signature so we get an empty string.
154 EXPECT_EQ(std::string(), invalid_signature
);
156 prefs
.SetString(prefs::kVariationsSeedSignature
,
157 base64_seed_signature_invalid
);
158 seed_store
.LoadSeed(&loaded_seed
);
159 // Invalid signature, so we should get the signature itself, except on mobile
160 // where we should get an empty string because verification is not enabled.
161 invalid_signature
= seed_store
.GetInvalidSignature();
162 #if defined(OS_IOS) || defined(OS_ANDROID)
163 EXPECT_EQ(std::string(), invalid_signature
);
165 EXPECT_EQ(base64_seed_signature_invalid
, invalid_signature
);
168 prefs
.SetString(prefs::kVariationsSeedSignature
, std::string());
169 seed_store
.LoadSeed(&loaded_seed
);
170 invalid_signature
= seed_store
.GetInvalidSignature();
171 // Empty signature, not considered invalid.
172 EXPECT_EQ(std::string(), invalid_signature
);
175 TEST(VariationsSeedStoreTest
, StoreSeedData
) {
176 const variations::VariationsSeed seed
= CreateTestSeed();
177 const std::string serialized_seed
= SerializeSeed(seed
);
179 TestingPrefServiceSimple prefs
;
180 VariationsSeedStore::RegisterPrefs(prefs
.registry());
182 TestVariationsSeedStore
seed_store(&prefs
);
184 EXPECT_TRUE(seed_store
.StoreSeedForTesting(serialized_seed
));
185 // Make sure the pref was actually set.
186 EXPECT_FALSE(PrefHasDefaultValue(prefs
, prefs::kVariationsCompressedSeed
));
188 std::string loaded_compressed_seed
=
189 prefs
.GetString(prefs::kVariationsCompressedSeed
);
190 std::string decoded_compressed_seed
;
191 ASSERT_TRUE(base::Base64Decode(loaded_compressed_seed
,
192 &decoded_compressed_seed
));
193 // Make sure the stored seed from pref is the same as the seed we created.
194 EXPECT_EQ(Compress(serialized_seed
), decoded_compressed_seed
);
196 // Check if trying to store a bad seed leaves the pref unchanged.
197 prefs
.ClearPref(prefs::kVariationsCompressedSeed
);
198 EXPECT_FALSE(seed_store
.StoreSeedForTesting("should fail"));
199 EXPECT_TRUE(PrefHasDefaultValue(prefs
, prefs::kVariationsCompressedSeed
));
202 TEST(VariationsSeedStoreTest
, StoreSeedData_ParsedSeed
) {
203 const variations::VariationsSeed seed
= CreateTestSeed();
204 const std::string serialized_seed
= SerializeSeed(seed
);
206 TestingPrefServiceSimple prefs
;
207 VariationsSeedStore::RegisterPrefs(prefs
.registry());
208 TestVariationsSeedStore
seed_store(&prefs
);
210 variations::VariationsSeed parsed_seed
;
211 EXPECT_TRUE(seed_store
.StoreSeedData(serialized_seed
, std::string(),
212 base::Time::Now(), &parsed_seed
));
213 EXPECT_EQ(serialized_seed
, SerializeSeed(parsed_seed
));
216 TEST(VariationsSeedStoreTest
, VerifySeedSignature
) {
217 // The below seed and signature pair were generated using the server's
219 const std::string base64_seed_data
=
220 "CigxZDI5NDY0ZmIzZDc4ZmYxNTU2ZTViNTUxYzY0NDdjYmM3NGU1ZmQwEr0BCh9VTUEtVW5p"
221 "Zm9ybWl0eS1UcmlhbC0xMC1QZXJjZW50GICckqUFOAFCB2RlZmF1bHRKCwoHZGVmYXVsdBAB"
222 "SgwKCGdyb3VwXzAxEAFKDAoIZ3JvdXBfMDIQAUoMCghncm91cF8wMxABSgwKCGdyb3VwXzA0"
223 "EAFKDAoIZ3JvdXBfMDUQAUoMCghncm91cF8wNhABSgwKCGdyb3VwXzA3EAFKDAoIZ3JvdXBf"
224 "MDgQAUoMCghncm91cF8wORAB";
225 const std::string base64_seed_signature
=
226 "MEQCIDD1IVxjzWYncun+9IGzqYjZvqxxujQEayJULTlbTGA/AiAr0oVmEgVUQZBYq5VLOSvy"
227 "96JkMYgzTkHPwbv7K/CmgA==";
229 std::string seed_data
;
230 EXPECT_TRUE(base::Base64Decode(base64_seed_data
, &seed_data
));
232 VariationsSeedStore
seed_store(NULL
);
234 #if defined(OS_IOS) || defined(OS_ANDROID)
235 // Signature verification is not enabled on mobile.
236 if (seed_store
.VerifySeedSignature(seed_data
, base64_seed_signature
) ==
237 VariationsSeedStore::VARIATIONS_SEED_SIGNATURE_ENUM_SIZE
) {
242 // The above inputs should be valid.
243 EXPECT_EQ(VariationsSeedStore::VARIATIONS_SEED_SIGNATURE_VALID
,
244 seed_store
.VerifySeedSignature(seed_data
, base64_seed_signature
));
246 // If there's no signature, the corresponding result should be returned.
247 EXPECT_EQ(VariationsSeedStore::VARIATIONS_SEED_SIGNATURE_MISSING
,
248 seed_store
.VerifySeedSignature(seed_data
, std::string()));
250 // Using non-base64 encoded value as signature (e.g. seed data) should fail.
251 EXPECT_EQ(VariationsSeedStore::VARIATIONS_SEED_SIGNATURE_DECODE_FAILED
,
252 seed_store
.VerifySeedSignature(seed_data
, seed_data
));
254 // Using a different signature (e.g. the base64 seed data) should fail.
255 #if defined(USE_OPENSSL)
256 // OpenSSL doesn't distinguish signature decode failure from the
257 // signature not matching.
258 EXPECT_EQ(VariationsSeedStore::VARIATIONS_SEED_SIGNATURE_INVALID_SEED
,
259 seed_store
.VerifySeedSignature(seed_data
, base64_seed_data
));
261 EXPECT_EQ(VariationsSeedStore::VARIATIONS_SEED_SIGNATURE_INVALID_SIGNATURE
,
262 seed_store
.VerifySeedSignature(seed_data
, base64_seed_data
));
265 // Using a different seed should not match the signature.
267 EXPECT_EQ(VariationsSeedStore::VARIATIONS_SEED_SIGNATURE_INVALID_SEED
,
268 seed_store
.VerifySeedSignature(seed_data
, base64_seed_signature
));
271 } // namespace chrome_variations