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"
10 #include "base/strings/string_number_conversions.h"
11 #include "base/strings/string_util.h"
12 #include "chrome/common/pref_names.h"
13 #include "components/variations/proto/study.pb.h"
14 #include "components/variations/proto/variations_seed.pb.h"
15 #include "testing/gtest/include/gtest/gtest.h"
17 namespace chrome_variations
{
21 class TestVariationsSeedStore
: public VariationsSeedStore
{
23 explicit TestVariationsSeedStore(PrefService
* local_state
)
24 : VariationsSeedStore(local_state
) {}
25 virtual ~TestVariationsSeedStore() {}
27 bool StoreSeedForTesting(const std::string
& seed_data
) {
28 return StoreSeedData(seed_data
, std::string(), base::Time::Now(), NULL
);
31 virtual VariationsSeedStore::VerifySignatureResult
VerifySeedSignature(
32 const std::string
& seed_bytes
,
33 const std::string
& base64_seed_signature
) OVERRIDE
{
34 return VariationsSeedStore::VARIATIONS_SEED_SIGNATURE_ENUM_SIZE
;
38 DISALLOW_COPY_AND_ASSIGN(TestVariationsSeedStore
);
42 // Populates |seed| with simple test data. The resulting seed will contain one
43 // study called "test", which contains one experiment called "abc" with
44 // probability weight 100. |seed|'s study field will be cleared before adding
46 variations::VariationsSeed
CreateTestSeed() {
47 variations::VariationsSeed seed
;
48 variations::Study
* study
= seed
.add_study();
49 study
->set_name("test");
50 study
->set_default_experiment_name("abc");
51 variations::Study_Experiment
* experiment
= study
->add_experiment();
52 experiment
->set_name("abc");
53 experiment
->set_probability_weight(100);
54 seed
.set_serial_number("123");
58 // Serializes |seed| to protobuf binary format.
59 std::string
SerializeSeed(const variations::VariationsSeed
& seed
) {
60 std::string serialized_seed
;
61 seed
.SerializeToString(&serialized_seed
);
62 return serialized_seed
;
65 // Serializes |seed| to base64-encoded protobuf binary format.
66 std::string
SerializeSeedBase64(const variations::VariationsSeed
& seed
,
68 std::string serialized_seed
= SerializeSeed(seed
);
70 std::string sha1
= base::SHA1HashString(serialized_seed
);
71 *hash
= base::HexEncode(sha1
.data(), sha1
.size());
73 std::string base64_serialized_seed
;
74 base::Base64Encode(serialized_seed
, &base64_serialized_seed
);
75 return base64_serialized_seed
;
78 // Checks whether the pref with name |pref_name| is at its default value in
80 bool PrefHasDefaultValue(const TestingPrefServiceSimple
& prefs
,
81 const char* pref_name
) {
82 return prefs
.FindPreference(pref_name
)->IsDefaultValue();
87 TEST(VariationsSeedStoreTest
, LoadSeed
) {
88 // Store good seed data to test if loading from prefs works.
89 const variations::VariationsSeed seed
= CreateTestSeed();
90 std::string seed_hash
;
91 const std::string base64_seed
= SerializeSeedBase64(seed
, &seed_hash
);
93 TestingPrefServiceSimple prefs
;
94 VariationsSeedStore::RegisterPrefs(prefs
.registry());
95 prefs
.SetString(prefs::kVariationsSeed
, base64_seed
);
97 TestVariationsSeedStore
seed_store(&prefs
);
99 variations::VariationsSeed loaded_seed
;
100 // Check that loading a seed without a hash pref set 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::kVariationsSeed
));
107 EXPECT_EQ(base64_seed
, prefs
.GetString(prefs::kVariationsSeed
));
109 // Check that loading a seed with the correct hash works.
110 prefs
.SetString(prefs::kVariationsSeedHash
, seed_hash
);
112 EXPECT_TRUE(seed_store
.LoadSeed(&loaded_seed
));
113 EXPECT_EQ(SerializeSeed(seed
), SerializeSeed(loaded_seed
));
115 // Check that loading a bad seed returns false and clears the pref.
116 prefs
.ClearPref(prefs::kVariationsSeed
);
117 prefs
.SetString(prefs::kVariationsSeed
, "this should fail");
118 EXPECT_FALSE(PrefHasDefaultValue(prefs
, prefs::kVariationsSeed
));
119 EXPECT_FALSE(seed_store
.LoadSeed(&loaded_seed
));
120 EXPECT_TRUE(PrefHasDefaultValue(prefs
, prefs::kVariationsSeed
));
121 EXPECT_TRUE(PrefHasDefaultValue(prefs
, prefs::kVariationsSeedDate
));
122 EXPECT_TRUE(PrefHasDefaultValue(prefs
, prefs::kVariationsSeedSignature
));
124 // Check that having no seed in prefs results in a return value of false.
125 prefs
.ClearPref(prefs::kVariationsSeed
);
126 EXPECT_FALSE(seed_store
.LoadSeed(&loaded_seed
));
129 TEST(VariationsSeedStoreTest
, StoreSeedData
) {
130 const variations::VariationsSeed seed
= CreateTestSeed();
131 const std::string serialized_seed
= SerializeSeed(seed
);
133 TestingPrefServiceSimple prefs
;
134 VariationsSeedStore::RegisterPrefs(prefs
.registry());
136 TestVariationsSeedStore
seed_store(&prefs
);
138 EXPECT_TRUE(seed_store
.StoreSeedForTesting(serialized_seed
));
139 // Make sure the pref was actually set.
140 EXPECT_FALSE(PrefHasDefaultValue(prefs
, prefs::kVariationsSeed
));
142 std::string loaded_serialized_seed
= prefs
.GetString(prefs::kVariationsSeed
);
143 std::string decoded_serialized_seed
;
144 ASSERT_TRUE(base::Base64Decode(loaded_serialized_seed
,
145 &decoded_serialized_seed
));
146 // Make sure the stored seed from pref is the same as the seed we created.
147 EXPECT_EQ(serialized_seed
, decoded_serialized_seed
);
149 // Check if trying to store a bad seed leaves the pref unchanged.
150 prefs
.ClearPref(prefs::kVariationsSeed
);
151 EXPECT_FALSE(seed_store
.StoreSeedForTesting("should fail"));
152 EXPECT_TRUE(PrefHasDefaultValue(prefs
, prefs::kVariationsSeed
));
155 TEST(VariationsSeedStoreTest
, StoreSeedData_ParsedSeed
) {
156 const variations::VariationsSeed seed
= CreateTestSeed();
157 const std::string serialized_seed
= SerializeSeed(seed
);
159 TestingPrefServiceSimple prefs
;
160 VariationsSeedStore::RegisterPrefs(prefs
.registry());
161 TestVariationsSeedStore
seed_store(&prefs
);
163 variations::VariationsSeed parsed_seed
;
164 EXPECT_TRUE(seed_store
.StoreSeedData(serialized_seed
, std::string(),
165 base::Time::Now(), &parsed_seed
));
166 EXPECT_EQ(serialized_seed
, SerializeSeed(parsed_seed
));
169 TEST(VariationsSeedStoreTest
, VerifySeedSignature
) {
170 // The below seed and signature pair were generated using the server's
172 const std::string base64_seed_data
=
173 "CigxZDI5NDY0ZmIzZDc4ZmYxNTU2ZTViNTUxYzY0NDdjYmM3NGU1ZmQwEr0BCh9VTUEtVW5p"
174 "Zm9ybWl0eS1UcmlhbC0xMC1QZXJjZW50GICckqUFOAFCB2RlZmF1bHRKCwoHZGVmYXVsdBAB"
175 "SgwKCGdyb3VwXzAxEAFKDAoIZ3JvdXBfMDIQAUoMCghncm91cF8wMxABSgwKCGdyb3VwXzA0"
176 "EAFKDAoIZ3JvdXBfMDUQAUoMCghncm91cF8wNhABSgwKCGdyb3VwXzA3EAFKDAoIZ3JvdXBf"
177 "MDgQAUoMCghncm91cF8wORAB";
178 const std::string base64_seed_signature
=
179 "MEQCIDD1IVxjzWYncun+9IGzqYjZvqxxujQEayJULTlbTGA/AiAr0oVmEgVUQZBYq5VLOSvy"
180 "96JkMYgzTkHPwbv7K/CmgA==";
182 std::string seed_data
;
183 EXPECT_TRUE(base::Base64Decode(base64_seed_data
, &seed_data
));
185 VariationsSeedStore
seed_store(NULL
);
187 #if defined(OS_IOS) || defined(OS_ANDROID)
188 // Signature verification is not enabled on mobile.
189 if (seed_store
.VerifySeedSignature(seed_data
, base64_seed_signature
) ==
190 VariationsSeedStore::VARIATIONS_SEED_SIGNATURE_ENUM_SIZE
) {
195 // The above inputs should be valid.
196 EXPECT_EQ(VariationsSeedStore::VARIATIONS_SEED_SIGNATURE_VALID
,
197 seed_store
.VerifySeedSignature(seed_data
, base64_seed_signature
));
199 // If there's no signature, the corresponding result should be returned.
200 EXPECT_EQ(VariationsSeedStore::VARIATIONS_SEED_SIGNATURE_MISSING
,
201 seed_store
.VerifySeedSignature(seed_data
, std::string()));
203 // Using non-base64 encoded value as signature (e.g. seed data) should fail.
204 EXPECT_EQ(VariationsSeedStore::VARIATIONS_SEED_SIGNATURE_DECODE_FAILED
,
205 seed_store
.VerifySeedSignature(seed_data
, seed_data
));
207 // Using a different signature (e.g. the base64 seed data) should fail.
208 #if defined(USE_OPENSSL)
209 // OpenSSL doesn't distinguish signature decode failure from the
210 // signature not matching.
211 EXPECT_EQ(VariationsSeedStore::VARIATIONS_SEED_SIGNATURE_INVALID_SEED
,
212 seed_store
.VerifySeedSignature(seed_data
, base64_seed_data
));
214 EXPECT_EQ(VariationsSeedStore::VARIATIONS_SEED_SIGNATURE_INVALID_SIGNATURE
,
215 seed_store
.VerifySeedSignature(seed_data
, base64_seed_data
));
218 // Using a different seed should not match the signature.
220 EXPECT_EQ(VariationsSeedStore::VARIATIONS_SEED_SIGNATURE_INVALID_SEED
,
221 seed_store
.VerifySeedSignature(seed_data
, base64_seed_signature
));
224 } // namespace chrome_variations