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 "base/sha1.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"
16
17namespace chrome_variations {
18
19namespace {
20
21class TestVariationsSeedStore : public VariationsSeedStore {
22 public:
23  explicit TestVariationsSeedStore(PrefService* local_state)
24      : VariationsSeedStore(local_state) {}
25  virtual ~TestVariationsSeedStore() {}
26
27  bool StoreSeedForTesting(const std::string& seed_data) {
28    return StoreSeedData(seed_data, std::string(), base::Time::Now(), NULL);
29  }
30
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;
35  }
36
37 private:
38  DISALLOW_COPY_AND_ASSIGN(TestVariationsSeedStore);
39};
40
41
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
45// the new study.
46variations::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");
55  return seed;
56}
57
58// Serializes |seed| to protobuf binary format.
59std::string SerializeSeed(const variations::VariationsSeed& seed) {
60  std::string serialized_seed;
61  seed.SerializeToString(&serialized_seed);
62  return serialized_seed;
63}
64
65// Serializes |seed| to base64-encoded protobuf binary format.
66std::string SerializeSeedBase64(const variations::VariationsSeed& seed,
67                                std::string* hash) {
68  std::string serialized_seed = SerializeSeed(seed);
69  if (hash != NULL) {
70    std::string sha1 = base::SHA1HashString(serialized_seed);
71    *hash = base::HexEncode(sha1.data(), sha1.size());
72  }
73  std::string base64_serialized_seed;
74  base::Base64Encode(serialized_seed, &base64_serialized_seed);
75  return base64_serialized_seed;
76}
77
78// Checks whether the pref with name |pref_name| is at its default value in
79// |prefs|.
80bool PrefHasDefaultValue(const TestingPrefServiceSimple& prefs,
81                         const char* pref_name) {
82  return prefs.FindPreference(pref_name)->IsDefaultValue();
83}
84
85}  // namespace
86
87TEST(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);
92
93  TestingPrefServiceSimple prefs;
94  VariationsSeedStore::RegisterPrefs(prefs.registry());
95  prefs.SetString(prefs::kVariationsSeed, base64_seed);
96
97  TestVariationsSeedStore seed_store(&prefs);
98
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));
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(PrefHasDefaultValue(prefs, prefs::kVariationsSeed));
107  EXPECT_EQ(base64_seed, prefs.GetString(prefs::kVariationsSeed));
108
109  // Check that loading a seed with the correct hash works.
110  prefs.SetString(prefs::kVariationsSeedHash, seed_hash);
111  loaded_seed.Clear();
112  EXPECT_TRUE(seed_store.LoadSeed(&loaded_seed));
113  EXPECT_EQ(SerializeSeed(seed), SerializeSeed(loaded_seed));
114
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));
123
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));
127}
128
129TEST(VariationsSeedStoreTest, StoreSeedData) {
130  const variations::VariationsSeed seed = CreateTestSeed();
131  const std::string serialized_seed = SerializeSeed(seed);
132
133  TestingPrefServiceSimple prefs;
134  VariationsSeedStore::RegisterPrefs(prefs.registry());
135
136  TestVariationsSeedStore seed_store(&prefs);
137
138  EXPECT_TRUE(seed_store.StoreSeedForTesting(serialized_seed));
139  // Make sure the pref was actually set.
140  EXPECT_FALSE(PrefHasDefaultValue(prefs, prefs::kVariationsSeed));
141
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);
148
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));
153}
154
155TEST(VariationsSeedStoreTest, StoreSeedData_ParsedSeed) {
156  const variations::VariationsSeed seed = CreateTestSeed();
157  const std::string serialized_seed = SerializeSeed(seed);
158
159  TestingPrefServiceSimple prefs;
160  VariationsSeedStore::RegisterPrefs(prefs.registry());
161  TestVariationsSeedStore seed_store(&prefs);
162
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));
167}
168
169TEST(VariationsSeedStoreTest, VerifySeedSignature) {
170  // The below seed and signature pair were generated using the server's
171  // private key.
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==";
181
182  std::string seed_data;
183  EXPECT_TRUE(base::Base64Decode(base64_seed_data, &seed_data));
184
185  VariationsSeedStore seed_store(NULL);
186
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) {
191    return;
192  }
193#endif
194
195  // The above inputs should be valid.
196  EXPECT_EQ(VariationsSeedStore::VARIATIONS_SEED_SIGNATURE_VALID,
197            seed_store.VerifySeedSignature(seed_data, base64_seed_signature));
198
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()));
202
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));
206
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));
213#else
214  EXPECT_EQ(VariationsSeedStore::VARIATIONS_SEED_SIGNATURE_INVALID_SIGNATURE,
215            seed_store.VerifySeedSignature(seed_data, base64_seed_data));
216#endif
217
218  // Using a different seed should not match the signature.
219  seed_data[0] = 'x';
220  EXPECT_EQ(VariationsSeedStore::VARIATIONS_SEED_SIGNATURE_INVALID_SEED,
221            seed_store.VerifySeedSignature(seed_data, base64_seed_signature));
222}
223
224}  // namespace chrome_variations
225