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