blob: 4c9e957a98aca8ef291f6102a78dee3b4afb6920 [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/variations/variations_seed_store.h"
#include <memory>
#include <utility>
#include "base/base64.h"
#include "base/macros.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/time/time.h"
#include "base/version.h"
#include "build/build_config.h"
#include "components/prefs/testing_pref_service.h"
#include "components/variations/client_filterable_state.h"
#include "components/variations/pref_names.h"
#include "components/variations/proto/study.pb.h"
#include "components/variations/proto/variations_seed.pb.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/zlib/google/compression_utils.h"
#if defined(OS_ANDROID)
#include "components/variations/android/variations_seed_bridge.h"
#endif // OS_ANDROID
namespace variations {
namespace {
// The below seed and signature pair were generated using the server's private
// key.
const char kUncompressedBase64SeedData[] =
"CigxZDI5NDY0ZmIzZDc4ZmYxNTU2ZTViNTUxYzY0NDdjYmM3NGU1ZmQwEr0BCh9VTUEtVW5p"
"Zm9ybWl0eS1UcmlhbC0xMC1QZXJjZW50GICckqUFOAFCB2RlZmF1bHRKCwoHZGVmYXVsdBAB"
"SgwKCGdyb3VwXzAxEAFKDAoIZ3JvdXBfMDIQAUoMCghncm91cF8wMxABSgwKCGdyb3VwXzA0"
"EAFKDAoIZ3JvdXBfMDUQAUoMCghncm91cF8wNhABSgwKCGdyb3VwXzA3EAFKDAoIZ3JvdXBf"
"MDgQAUoMCghncm91cF8wORAB";
const char kBase64SeedSignature[] =
"MEQCIDD1IVxjzWYncun+9IGzqYjZvqxxujQEayJULTlbTGA/AiAr0oVmEgVUQZBYq5VLOSvy"
"96JkMYgzTkHPwbv7K/CmgA==";
// The sentinel value that may be stored as the latest variations seed value in
// prefs to indicate that the latest seed is identical to the safe seed.
// Note: This constant is intentionally duplicated in the test because it is
// persisted to disk. In order to maintain backward-compatibility, it's
// important that code continue to correctly handle this specific constant, even
// if the constant used internally in the implementation changes.
constexpr char kIdenticalToSafeSeedSentinel[] = "safe_seed_content";
class TestVariationsSeedStore : public VariationsSeedStore {
public:
explicit TestVariationsSeedStore(PrefService* local_state)
: VariationsSeedStore(local_state) {}
~TestVariationsSeedStore() override {}
bool StoreSeedForTesting(const std::string& seed_data) {
return StoreSeedData(seed_data, std::string(), std::string(),
base::Time::Now(), false, false, false, nullptr);
}
bool SignatureVerificationEnabled() override { return false; }
private:
DISALLOW_COPY_AND_ASSIGN(TestVariationsSeedStore);
};
// Signature verification is disabled on Android and iOS for performance
// reasons. This class re-enables it for tests, which don't mind the (small)
// performance penalty.
class SignatureVerifyingVariationsSeedStore : public VariationsSeedStore {
public:
explicit SignatureVerifyingVariationsSeedStore(PrefService* local_state)
: VariationsSeedStore(local_state) {}
~SignatureVerifyingVariationsSeedStore() override {}
bool SignatureVerificationEnabled() override { return true; }
private:
DISALLOW_COPY_AND_ASSIGN(SignatureVerifyingVariationsSeedStore);
};
// Creates a base::Time object from the corresponding raw value. The specific
// implementation is not important; it's only important that distinct inputs map
// to distinct outputs.
base::Time WrapTime(int64_t time) {
return base::Time::FromDeltaSinceWindowsEpoch(
base::TimeDelta::FromMicroseconds(time));
}
// Populates |seed| with simple test data. The resulting seed will contain one
// study called "test", which contains one experiment called "abc" with
// probability weight 100. |seed|'s study field will be cleared before adding
// the new study.
VariationsSeed CreateTestSeed() {
VariationsSeed seed;
Study* study = seed.add_study();
study->set_name("test");
study->set_default_experiment_name("abc");
Study_Experiment* experiment = study->add_experiment();
experiment->set_name("abc");
experiment->set_probability_weight(100);
seed.set_serial_number("123");
return seed;
}
// Returns a ClientFilterableState with all fields set to "interesting" values
// for testing.
std::unique_ptr<ClientFilterableState> CreateTestClientFilterableState() {
std::unique_ptr<ClientFilterableState> client_state =
std::make_unique<ClientFilterableState>();
client_state->locale = "es-MX";
client_state->reference_date = WrapTime(1234554321);
client_state->version = base::Version("1.2.3.4");
client_state->channel = Study::CANARY;
client_state->form_factor = Study::PHONE;
client_state->platform = Study::PLATFORM_MAC;
client_state->hardware_class = "mario";
client_state->is_low_end_device = true;
client_state->session_consistency_country = "mx";
client_state->permanent_consistency_country = "br";
return client_state;
}
// Serializes |seed| to protobuf binary format.
std::string SerializeSeed(const VariationsSeed& seed) {
std::string serialized_seed;
seed.SerializeToString(&serialized_seed);
return serialized_seed;
}
// Compresses |data| using Gzip compression and returns the result.
std::string Compress(const std::string& data) {
std::string compressed;
const bool result = compression::GzipCompress(data, &compressed);
EXPECT_TRUE(result);
return compressed;
}
// Serializes |seed| to compressed base64-encoded protobuf binary format.
std::string SerializeSeedBase64(const VariationsSeed& seed) {
std::string serialized_seed = SerializeSeed(seed);
std::string base64_serialized_seed;
base::Base64Encode(Compress(serialized_seed), &base64_serialized_seed);
return base64_serialized_seed;
}
// Sets all seed-related prefs to non-default values. Used to verify whether
// pref values were cleared.
void SetAllSeedPrefsToNonDefaultValues(PrefService* prefs) {
prefs->SetString(prefs::kVariationsCompressedSeed, "a");
prefs->SetString(prefs::kVariationsSafeCompressedSeed, "b");
prefs->SetString(prefs::kVariationsSafeSeedLocale, "c");
prefs->SetString(prefs::kVariationsSafeSeedPermanentConsistencyCountry, "d");
prefs->SetString(prefs::kVariationsSafeSeedSessionConsistencyCountry, "e");
prefs->SetString(prefs::kVariationsSafeSeedSignature, "f");
prefs->SetString(prefs::kVariationsSeedSignature, "g");
const base::Time now = base::Time::Now();
const base::TimeDelta delta = base::TimeDelta::FromDays(1);
prefs->SetTime(prefs::kVariationsSafeSeedDate, now - delta);
prefs->SetTime(prefs::kVariationsSafeSeedFetchTime, now - delta * 2);
prefs->SetTime(prefs::kVariationsSeedDate, now - delta * 3);
}
// Checks whether the pref with name |pref_name| is at its default value in
// |prefs|.
bool PrefHasDefaultValue(const TestingPrefServiceSimple& prefs,
const char* pref_name) {
return prefs.FindPreference(pref_name)->IsDefaultValue();
}
} // namespace
TEST(VariationsSeedStoreTest, LoadSeed_ValidSeed) {
// Store good seed data to test if loading from prefs works.
const VariationsSeed seed = CreateTestSeed();
const std::string base64_seed = SerializeSeedBase64(seed);
const std::string base64_seed_signature = "a test signature, ignored.";
TestingPrefServiceSimple prefs;
VariationsSeedStore::RegisterPrefs(prefs.registry());
prefs.SetString(prefs::kVariationsCompressedSeed, base64_seed);
prefs.SetString(prefs::kVariationsSeedSignature, base64_seed_signature);
TestVariationsSeedStore seed_store(&prefs);
VariationsSeed loaded_seed;
std::string loaded_seed_data;
std::string loaded_base64_seed_signature;
// Check that loading a seed works correctly.
EXPECT_TRUE(seed_store.LoadSeed(&loaded_seed, &loaded_seed_data,
&loaded_base64_seed_signature));
// Check that the loaded data is the same as the original.
EXPECT_EQ(SerializeSeed(seed), SerializeSeed(loaded_seed));
EXPECT_EQ(SerializeSeed(seed), loaded_seed_data);
EXPECT_EQ(base64_seed_signature, loaded_base64_seed_signature);
// Make sure the pref hasn't been changed.
EXPECT_FALSE(PrefHasDefaultValue(prefs, prefs::kVariationsCompressedSeed));
EXPECT_EQ(base64_seed, prefs.GetString(prefs::kVariationsCompressedSeed));
}
TEST(VariationsSeedStoreTest, LoadSeed_InvalidSeed) {
TestingPrefServiceSimple prefs;
VariationsSeedStore::RegisterPrefs(prefs.registry());
SetAllSeedPrefsToNonDefaultValues(&prefs);
prefs.SetString(prefs::kVariationsCompressedSeed, "this should fail");
// Loading an invalid seed should return false and clear all associated prefs.
TestVariationsSeedStore seed_store(&prefs);
VariationsSeed loaded_seed;
std::string loaded_seed_data;
std::string loaded_base64_seed_signature;
EXPECT_FALSE(seed_store.LoadSeed(&loaded_seed, &loaded_seed_data,
&loaded_base64_seed_signature));
EXPECT_TRUE(PrefHasDefaultValue(prefs, prefs::kVariationsCompressedSeed));
EXPECT_TRUE(PrefHasDefaultValue(prefs, prefs::kVariationsSeedDate));
EXPECT_TRUE(PrefHasDefaultValue(prefs, prefs::kVariationsSeedSignature));
// However, only the latest seed prefs should be cleared; the safe seed prefs
// should not be modified.
EXPECT_FALSE(
PrefHasDefaultValue(prefs, prefs::kVariationsSafeCompressedSeed));
EXPECT_FALSE(PrefHasDefaultValue(prefs, prefs::kVariationsSafeSeedDate));
EXPECT_FALSE(PrefHasDefaultValue(prefs, prefs::kVariationsSafeSeedFetchTime));
EXPECT_FALSE(PrefHasDefaultValue(prefs, prefs::kVariationsSafeSeedLocale));
EXPECT_FALSE(PrefHasDefaultValue(
prefs, prefs::kVariationsSafeSeedPermanentConsistencyCountry));
EXPECT_FALSE(PrefHasDefaultValue(
prefs, prefs::kVariationsSafeSeedSessionConsistencyCountry));
EXPECT_FALSE(PrefHasDefaultValue(prefs, prefs::kVariationsSafeSeedSignature));
}
TEST(VariationsSeedStoreTest, LoadSeed_InvalidSignature) {
const VariationsSeed seed = CreateTestSeed();
const std::string base64_seed = SerializeSeedBase64(seed);
const std::string base64_seed_signature = "a deeply compromised signature.";
TestingPrefServiceSimple prefs;
VariationsSeedStore::RegisterPrefs(prefs.registry());
SetAllSeedPrefsToNonDefaultValues(&prefs);
prefs.SetString(prefs::kVariationsCompressedSeed, base64_seed);
prefs.SetString(prefs::kVariationsSeedSignature, base64_seed_signature);
// Loading a valid seed with an invalid signature should return false and
// clear all associated prefs when signature verification is enabled.
SignatureVerifyingVariationsSeedStore seed_store(&prefs);
VariationsSeed loaded_seed;
std::string loaded_seed_data;
std::string loaded_base64_seed_signature;
EXPECT_FALSE(seed_store.LoadSeed(&loaded_seed, &loaded_seed_data,
&loaded_base64_seed_signature));
EXPECT_TRUE(PrefHasDefaultValue(prefs, prefs::kVariationsCompressedSeed));
EXPECT_TRUE(PrefHasDefaultValue(prefs, prefs::kVariationsSeedDate));
EXPECT_TRUE(PrefHasDefaultValue(prefs, prefs::kVariationsSeedSignature));
// However, only the latest seed prefs should be cleared; the safe seed prefs
// should not be modified.
EXPECT_FALSE(
PrefHasDefaultValue(prefs, prefs::kVariationsSafeCompressedSeed));
EXPECT_FALSE(PrefHasDefaultValue(prefs, prefs::kVariationsSafeSeedDate));
EXPECT_FALSE(PrefHasDefaultValue(prefs, prefs::kVariationsSafeSeedFetchTime));
EXPECT_FALSE(PrefHasDefaultValue(prefs, prefs::kVariationsSafeSeedLocale));
EXPECT_FALSE(PrefHasDefaultValue(
prefs, prefs::kVariationsSafeSeedPermanentConsistencyCountry));
EXPECT_FALSE(PrefHasDefaultValue(
prefs, prefs::kVariationsSafeSeedSessionConsistencyCountry));
EXPECT_FALSE(PrefHasDefaultValue(prefs, prefs::kVariationsSafeSeedSignature));
}
TEST(VariationsSeedStoreTest, LoadSeed_EmptySeed) {
TestingPrefServiceSimple prefs;
VariationsSeedStore::RegisterPrefs(prefs.registry());
// Loading an empty seed should return false.
TestVariationsSeedStore seed_store(&prefs);
VariationsSeed loaded_seed;
std::string loaded_seed_data;
std::string loaded_base64_seed_signature;
EXPECT_FALSE(seed_store.LoadSeed(&loaded_seed, &loaded_seed_data,
&loaded_base64_seed_signature));
}
TEST(VariationsSeedStoreTest, LoadSeed_IdenticalToSafeSeed) {
// Store good seed data to the safe seed prefs, and store a sentinel value to
// the latest seed pref, to verify that loading via the alias works.
const VariationsSeed seed = CreateTestSeed();
const std::string base64_seed = SerializeSeedBase64(seed);
const std::string base64_seed_signature = "a test signature, ignored.";
TestingPrefServiceSimple prefs;
VariationsSeedStore::RegisterPrefs(prefs.registry());
prefs.SetString(prefs::kVariationsCompressedSeed,
kIdenticalToSafeSeedSentinel);
prefs.SetString(prefs::kVariationsSafeCompressedSeed, base64_seed);
prefs.SetString(prefs::kVariationsSeedSignature, base64_seed_signature);
TestVariationsSeedStore seed_store(&prefs);
VariationsSeed loaded_seed;
std::string loaded_seed_data;
std::string loaded_base64_seed_signature;
// Check that loading the seed works correctly.
EXPECT_TRUE(seed_store.LoadSeed(&loaded_seed, &loaded_seed_data,
&loaded_base64_seed_signature));
// Check that the loaded data is the same as the original.
EXPECT_EQ(SerializeSeed(seed), SerializeSeed(loaded_seed));
EXPECT_EQ(SerializeSeed(seed), loaded_seed_data);
EXPECT_EQ(base64_seed_signature, loaded_base64_seed_signature);
}
TEST(VariationsSeedStoreTest, StoreSeedData) {
const VariationsSeed seed = CreateTestSeed();
const std::string serialized_seed = SerializeSeed(seed);
TestingPrefServiceSimple prefs;
VariationsSeedStore::RegisterPrefs(prefs.registry());
TestVariationsSeedStore seed_store(&prefs);
EXPECT_TRUE(seed_store.StoreSeedForTesting(serialized_seed));
// Make sure the pref was actually set.
EXPECT_FALSE(PrefHasDefaultValue(prefs, prefs::kVariationsCompressedSeed));
std::string loaded_compressed_seed =
prefs.GetString(prefs::kVariationsCompressedSeed);
std::string decoded_compressed_seed;
ASSERT_TRUE(base::Base64Decode(loaded_compressed_seed,
&decoded_compressed_seed));
// Make sure the stored seed from pref is the same as the seed we created.
EXPECT_EQ(Compress(serialized_seed), decoded_compressed_seed);
// Check if trying to store a bad seed leaves the pref unchanged.
prefs.ClearPref(prefs::kVariationsCompressedSeed);
EXPECT_FALSE(seed_store.StoreSeedForTesting("should fail"));
EXPECT_TRUE(PrefHasDefaultValue(prefs, prefs::kVariationsCompressedSeed));
}
TEST(VariationsSeedStoreTest, StoreSeedData_ParsedSeed) {
const VariationsSeed seed = CreateTestSeed();
const std::string serialized_seed = SerializeSeed(seed);
TestingPrefServiceSimple prefs;
VariationsSeedStore::RegisterPrefs(prefs.registry());
TestVariationsSeedStore seed_store(&prefs);
VariationsSeed parsed_seed;
EXPECT_TRUE(seed_store.StoreSeedData(serialized_seed, std::string(),
std::string(), base::Time::Now(), false,
false, false, &parsed_seed));
EXPECT_EQ(serialized_seed, SerializeSeed(parsed_seed));
}
TEST(VariationsSeedStoreTest, StoreSeedData_CountryCode) {
TestingPrefServiceSimple prefs;
VariationsSeedStore::RegisterPrefs(prefs.registry());
TestVariationsSeedStore seed_store(&prefs);
// Test with a valid header value.
std::string seed = SerializeSeed(CreateTestSeed());
EXPECT_TRUE(seed_store.StoreSeedData(seed, std::string(), "test_country",
base::Time::Now(), false, false, false,
nullptr));
EXPECT_EQ("test_country", prefs.GetString(prefs::kVariationsCountry));
// Test with no country code specified - which should preserve the old value.
EXPECT_TRUE(seed_store.StoreSeedData(seed, std::string(), std::string(),
base::Time::Now(), false, false, false,
nullptr));
EXPECT_EQ("test_country", prefs.GetString(prefs::kVariationsCountry));
}
TEST(VariationsSeedStoreTest, StoreSeedData_GzippedSeed) {
const VariationsSeed seed = CreateTestSeed();
const std::string serialized_seed = SerializeSeed(seed);
std::string compressed_seed;
ASSERT_TRUE(compression::GzipCompress(serialized_seed, &compressed_seed));
TestingPrefServiceSimple prefs;
VariationsSeedStore::RegisterPrefs(prefs.registry());
TestVariationsSeedStore seed_store(&prefs);
VariationsSeed parsed_seed;
EXPECT_TRUE(seed_store.StoreSeedData(compressed_seed, std::string(),
std::string(), base::Time::Now(), false,
true, false, &parsed_seed));
EXPECT_EQ(serialized_seed, SerializeSeed(parsed_seed));
}
TEST(VariationsSeedStoreTest, StoreSeedData_IdenticalToSafeSeed) {
const VariationsSeed seed = CreateTestSeed();
const std::string serialized_seed = SerializeSeed(seed);
const std::string base64_seed = SerializeSeedBase64(seed);
TestingPrefServiceSimple prefs;
VariationsSeedStore::RegisterPrefs(prefs.registry());
prefs.SetString(prefs::kVariationsSafeCompressedSeed, base64_seed);
TestVariationsSeedStore seed_store(&prefs);
EXPECT_TRUE(seed_store.StoreSeedForTesting(serialized_seed));
// Verify that the pref has a sentinel value, rather than the full string.
EXPECT_EQ(kIdenticalToSafeSeedSentinel,
prefs.GetString(prefs::kVariationsCompressedSeed));
// Verify that loading the stored seed returns the original seed value.
VariationsSeed loaded_seed;
std::string loaded_seed_data;
std::string unused_loaded_base64_seed_signature;
EXPECT_TRUE(seed_store.LoadSeed(&loaded_seed, &loaded_seed_data,
&unused_loaded_base64_seed_signature));
EXPECT_EQ(SerializeSeed(seed), SerializeSeed(loaded_seed));
EXPECT_EQ(SerializeSeed(seed), loaded_seed_data);
}
TEST(VariationsSeedStoreTest, LoadSafeSeed_ValidSeed) {
// Store good seed data to test if loading from prefs works.
const VariationsSeed seed = CreateTestSeed();
const std::string base64_seed = SerializeSeedBase64(seed);
const std::string base64_seed_signature = "a test signature, ignored.";
const base::Time reference_date = base::Time::Now();
const base::Time fetch_time = reference_date - base::TimeDelta::FromDays(3);
const std::string locale = "en-US";
const std::string permanent_consistency_country = "us";
const std::string session_consistency_country = "ca";
TestingPrefServiceSimple prefs;
VariationsSeedStore::RegisterPrefs(prefs.registry());
prefs.SetString(prefs::kVariationsSafeCompressedSeed, base64_seed);
prefs.SetString(prefs::kVariationsSafeSeedSignature, base64_seed_signature);
prefs.SetTime(prefs::kVariationsSafeSeedDate, reference_date);
prefs.SetTime(prefs::kVariationsSafeSeedFetchTime, fetch_time);
prefs.SetString(prefs::kVariationsSafeSeedLocale, locale);
prefs.SetString(prefs::kVariationsSafeSeedPermanentConsistencyCountry,
permanent_consistency_country);
prefs.SetString(prefs::kVariationsSafeSeedSessionConsistencyCountry,
session_consistency_country);
TestVariationsSeedStore seed_store(&prefs);
VariationsSeed loaded_seed;
std::unique_ptr<ClientFilterableState> client_state =
CreateTestClientFilterableState();
base::Time loaded_fetch_time;
EXPECT_TRUE(seed_store.LoadSafeSeed(&loaded_seed, client_state.get(),
&loaded_fetch_time));
// Check that the loaded data is the same as the original.
EXPECT_EQ(SerializeSeed(seed), SerializeSeed(loaded_seed));
EXPECT_EQ(locale, client_state->locale);
EXPECT_EQ(reference_date, client_state->reference_date);
EXPECT_EQ(permanent_consistency_country,
client_state->permanent_consistency_country);
EXPECT_EQ(session_consistency_country,
client_state->session_consistency_country);
EXPECT_EQ(fetch_time, loaded_fetch_time);
// Make sure that other data in the |client_state| hasn't been changed.
std::unique_ptr<ClientFilterableState> original_state =
CreateTestClientFilterableState();
EXPECT_EQ(original_state->version, client_state->version);
EXPECT_EQ(original_state->channel, client_state->channel);
EXPECT_EQ(original_state->form_factor, client_state->form_factor);
EXPECT_EQ(original_state->platform, client_state->platform);
EXPECT_EQ(original_state->hardware_class, client_state->hardware_class);
EXPECT_EQ(original_state->is_low_end_device, client_state->is_low_end_device);
// Make sure the pref hasn't been changed.
EXPECT_FALSE(
PrefHasDefaultValue(prefs, prefs::kVariationsSafeCompressedSeed));
EXPECT_EQ(base64_seed, prefs.GetString(prefs::kVariationsSafeCompressedSeed));
}
TEST(VariationsSeedStoreTest, LoadSafeSeed_InvalidSeed) {
TestingPrefServiceSimple prefs;
VariationsSeedStore::RegisterPrefs(prefs.registry());
SetAllSeedPrefsToNonDefaultValues(&prefs);
prefs.SetString(prefs::kVariationsSafeCompressedSeed, "this should fail");
// Loading an invalid seed should return false and clear all associated prefs.
TestVariationsSeedStore seed_store(&prefs);
VariationsSeed loaded_seed;
std::unique_ptr<ClientFilterableState> client_state =
CreateTestClientFilterableState();
base::Time fetch_time;
EXPECT_FALSE(
seed_store.LoadSafeSeed(&loaded_seed, client_state.get(), &fetch_time));
EXPECT_TRUE(PrefHasDefaultValue(prefs, prefs::kVariationsSafeCompressedSeed));
EXPECT_TRUE(PrefHasDefaultValue(prefs, prefs::kVariationsSafeSeedDate));
EXPECT_TRUE(PrefHasDefaultValue(prefs, prefs::kVariationsSafeSeedFetchTime));
EXPECT_TRUE(PrefHasDefaultValue(prefs, prefs::kVariationsSafeSeedLocale));
EXPECT_TRUE(PrefHasDefaultValue(
prefs, prefs::kVariationsSafeSeedPermanentConsistencyCountry));
EXPECT_TRUE(PrefHasDefaultValue(
prefs, prefs::kVariationsSafeSeedSessionConsistencyCountry));
EXPECT_TRUE(PrefHasDefaultValue(prefs, prefs::kVariationsSafeSeedSignature));
// However, only the safe seed prefs should be cleared; the latest seed prefs
// should not be modified.
EXPECT_FALSE(PrefHasDefaultValue(prefs, prefs::kVariationsCompressedSeed));
EXPECT_FALSE(PrefHasDefaultValue(prefs, prefs::kVariationsSeedDate));
EXPECT_FALSE(PrefHasDefaultValue(prefs, prefs::kVariationsSeedSignature));
// Moreover, loading an invalid seed should leave the |client_state|
// unmodified.
std::unique_ptr<ClientFilterableState> original_state =
CreateTestClientFilterableState();
EXPECT_EQ(original_state->locale, client_state->locale);
EXPECT_EQ(original_state->reference_date, client_state->reference_date);
EXPECT_EQ(original_state->session_consistency_country,
client_state->session_consistency_country);
EXPECT_EQ(original_state->permanent_consistency_country,
client_state->permanent_consistency_country);
}
TEST(VariationsSeedStoreTest, LoadSafeSeed_InvalidSignature) {
const VariationsSeed seed = CreateTestSeed();
const std::string base64_seed = SerializeSeedBase64(seed);
const std::string base64_seed_signature = "a deeply compromised signature.";
TestingPrefServiceSimple prefs;
VariationsSeedStore::RegisterPrefs(prefs.registry());
SetAllSeedPrefsToNonDefaultValues(&prefs);
prefs.SetString(prefs::kVariationsSafeCompressedSeed, base64_seed);
prefs.SetString(prefs::kVariationsSafeSeedSignature, base64_seed_signature);
// Loading a valid seed with an invalid signature should return false and
// clear all associated prefs when signature verification is enabled.
SignatureVerifyingVariationsSeedStore seed_store(&prefs);
VariationsSeed loaded_seed;
std::unique_ptr<ClientFilterableState> client_state =
CreateTestClientFilterableState();
base::Time fetch_time;
EXPECT_FALSE(
seed_store.LoadSafeSeed(&loaded_seed, client_state.get(), &fetch_time));
EXPECT_TRUE(PrefHasDefaultValue(prefs, prefs::kVariationsSafeCompressedSeed));
EXPECT_TRUE(PrefHasDefaultValue(prefs, prefs::kVariationsSafeSeedDate));
EXPECT_TRUE(PrefHasDefaultValue(prefs, prefs::kVariationsSafeSeedFetchTime));
EXPECT_TRUE(PrefHasDefaultValue(prefs, prefs::kVariationsSafeSeedLocale));
EXPECT_TRUE(PrefHasDefaultValue(
prefs, prefs::kVariationsSafeSeedPermanentConsistencyCountry));
EXPECT_TRUE(PrefHasDefaultValue(
prefs, prefs::kVariationsSafeSeedSessionConsistencyCountry));
EXPECT_TRUE(PrefHasDefaultValue(prefs, prefs::kVariationsSafeSeedSignature));
// However, only the safe seed prefs should be cleared; the latest seed prefs
// should not be modified.
EXPECT_FALSE(PrefHasDefaultValue(prefs, prefs::kVariationsCompressedSeed));
EXPECT_FALSE(PrefHasDefaultValue(prefs, prefs::kVariationsSeedDate));
EXPECT_FALSE(PrefHasDefaultValue(prefs, prefs::kVariationsSeedSignature));
// Moreover, the passed-in |client_state| should remain unmodified.
std::unique_ptr<ClientFilterableState> original_state =
CreateTestClientFilterableState();
EXPECT_EQ(original_state->locale, client_state->locale);
EXPECT_EQ(original_state->reference_date, client_state->reference_date);
EXPECT_EQ(original_state->session_consistency_country,
client_state->session_consistency_country);
EXPECT_EQ(original_state->permanent_consistency_country,
client_state->permanent_consistency_country);
}
TEST(VariationsSeedStoreTest, LoadSafeSeed_EmptySeed) {
TestingPrefServiceSimple prefs;
VariationsSeedStore::RegisterPrefs(prefs.registry());
// Loading an empty seed should return false.
TestVariationsSeedStore seed_store(&prefs);
VariationsSeed loaded_seed;
ClientFilterableState client_state;
base::Time fetch_time;
EXPECT_FALSE(
seed_store.LoadSafeSeed(&loaded_seed, &client_state, &fetch_time));
}
TEST(VariationsSeedStoreTest, StoreSafeSeed_ValidSeed) {
const VariationsSeed seed = CreateTestSeed();
const std::string serialized_seed = SerializeSeed(seed);
const std::string signature = "a completely ignored signature";
ClientFilterableState client_state;
client_state.locale = "en-US";
client_state.reference_date = WrapTime(12345);
client_state.session_consistency_country = "US";
client_state.permanent_consistency_country = "CA";
const base::Time fetch_time = WrapTime(99999);
TestingPrefServiceSimple prefs;
VariationsSeedStore::RegisterPrefs(prefs.registry());
TestVariationsSeedStore seed_store(&prefs);
base::HistogramTester histogram_tester;
EXPECT_TRUE(seed_store.StoreSafeSeed(serialized_seed, signature, client_state,
fetch_time));
// Verify the stored data.
std::string loaded_compressed_seed =
prefs.GetString(prefs::kVariationsSafeCompressedSeed);
std::string decoded_compressed_seed;
ASSERT_TRUE(
base::Base64Decode(loaded_compressed_seed, &decoded_compressed_seed));
EXPECT_EQ(Compress(serialized_seed), decoded_compressed_seed);
EXPECT_EQ(signature, prefs.GetString(prefs::kVariationsSafeSeedSignature));
EXPECT_EQ("en-US", prefs.GetString(prefs::kVariationsSafeSeedLocale));
EXPECT_EQ(WrapTime(12345), prefs.GetTime(prefs::kVariationsSafeSeedDate));
EXPECT_EQ(WrapTime(99999),
prefs.GetTime(prefs::kVariationsSafeSeedFetchTime));
EXPECT_EQ("US", prefs.GetString(
prefs::kVariationsSafeSeedSessionConsistencyCountry));
EXPECT_EQ("CA", prefs.GetString(
prefs::kVariationsSafeSeedPermanentConsistencyCountry));
// Verify metrics.
histogram_tester.ExpectUniqueSample(
"Variations.SafeMode.StoreSafeSeed.Result", StoreSeedResult::SUCCESS, 1);
}
TEST(VariationsSeedStoreTest, StoreSafeSeed_EmptySeed) {
const std::string serialized_seed;
const std::string signature = "a completely ignored signature";
ClientFilterableState client_state;
client_state.locale = "en-US";
client_state.reference_date = WrapTime(54321);
client_state.session_consistency_country = "US";
client_state.permanent_consistency_country = "CA";
base::Time fetch_time = WrapTime(99999);
TestingPrefServiceSimple prefs;
VariationsSeedStore::RegisterPrefs(prefs.registry());
prefs.SetString(prefs::kVariationsSafeCompressedSeed, "a seed");
prefs.SetString(prefs::kVariationsSafeSeedSignature, "a signature");
prefs.SetString(prefs::kVariationsSafeSeedLocale, "en-US");
prefs.SetString(prefs::kVariationsSafeSeedPermanentConsistencyCountry, "CA");
prefs.SetString(prefs::kVariationsSafeSeedSessionConsistencyCountry, "US");
prefs.SetTime(prefs::kVariationsSafeSeedDate, WrapTime(12345));
prefs.SetTime(prefs::kVariationsSafeSeedFetchTime, WrapTime(34567));
TestVariationsSeedStore seed_store(&prefs);
base::HistogramTester histogram_tester;
EXPECT_FALSE(seed_store.StoreSafeSeed(serialized_seed, signature,
client_state, fetch_time));
// Verify that none of the prefs were overwritten.
EXPECT_EQ("a seed", prefs.GetString(prefs::kVariationsSafeCompressedSeed));
EXPECT_EQ("a signature",
prefs.GetString(prefs::kVariationsSafeSeedSignature));
EXPECT_EQ("en-US", prefs.GetString(prefs::kVariationsSafeSeedLocale));
EXPECT_EQ("CA", prefs.GetString(
prefs::kVariationsSafeSeedPermanentConsistencyCountry));
EXPECT_EQ("US", prefs.GetString(
prefs::kVariationsSafeSeedSessionConsistencyCountry));
EXPECT_EQ(WrapTime(12345), prefs.GetTime(prefs::kVariationsSafeSeedDate));
EXPECT_EQ(WrapTime(34567),
prefs.GetTime(prefs::kVariationsSafeSeedFetchTime));
// Verify metrics.
histogram_tester.ExpectUniqueSample(
"Variations.SafeMode.StoreSafeSeed.Result",
StoreSeedResult::FAILED_EMPTY_GZIP_CONTENTS, 1);
}
TEST(VariationsSeedStoreTest, StoreSafeSeed_InvalidSeed) {
const std::string serialized_seed = "a nonsense seed";
const std::string signature = "a completely ignored signature";
ClientFilterableState client_state;
client_state.locale = "en-US";
client_state.reference_date = WrapTime(12345);
client_state.session_consistency_country = "US";
client_state.permanent_consistency_country = "CA";
base::Time fetch_time = WrapTime(54321);
TestingPrefServiceSimple prefs;
VariationsSeedStore::RegisterPrefs(prefs.registry());
prefs.SetString(prefs::kVariationsSafeCompressedSeed, "a previous seed");
prefs.SetString(prefs::kVariationsSafeSeedSignature, "a previous signature");
prefs.SetString(prefs::kVariationsSafeSeedLocale, "en-CA");
prefs.SetString(prefs::kVariationsSafeSeedPermanentConsistencyCountry, "IN");
prefs.SetString(prefs::kVariationsSafeSeedSessionConsistencyCountry, "MX");
prefs.SetTime(prefs::kVariationsSafeSeedDate, WrapTime(67890));
prefs.SetTime(prefs::kVariationsSafeSeedFetchTime, WrapTime(13579));
SignatureVerifyingVariationsSeedStore seed_store(&prefs);
base::HistogramTester histogram_tester;
EXPECT_FALSE(seed_store.StoreSafeSeed(serialized_seed, signature,
client_state, fetch_time));
// Verify that none of the prefs were overwritten.
EXPECT_EQ("a previous seed",
prefs.GetString(prefs::kVariationsSafeCompressedSeed));
EXPECT_EQ("a previous signature",
prefs.GetString(prefs::kVariationsSafeSeedSignature));
EXPECT_EQ("en-CA", prefs.GetString(prefs::kVariationsSafeSeedLocale));
EXPECT_EQ("IN", prefs.GetString(
prefs::kVariationsSafeSeedPermanentConsistencyCountry));
EXPECT_EQ("MX", prefs.GetString(
prefs::kVariationsSafeSeedSessionConsistencyCountry));
EXPECT_EQ(WrapTime(67890), prefs.GetTime(prefs::kVariationsSafeSeedDate));
EXPECT_EQ(WrapTime(13579),
prefs.GetTime(prefs::kVariationsSafeSeedFetchTime));
// Verify metrics.
histogram_tester.ExpectUniqueSample(
"Variations.SafeMode.StoreSafeSeed.Result", StoreSeedResult::FAILED_PARSE,
1);
}
TEST(VariationsSeedStoreTest, StoreSafeSeed_InvalidSignature) {
const VariationsSeed seed = CreateTestSeed();
const std::string serialized_seed = SerializeSeed(seed);
// A valid signature, but for a different seed.
const std::string signature = kBase64SeedSignature;
ClientFilterableState client_state;
client_state.locale = "en-US";
client_state.reference_date = WrapTime(12345);
client_state.session_consistency_country = "US";
client_state.permanent_consistency_country = "CA";
const base::Time fetch_time = WrapTime(34567);
TestingPrefServiceSimple prefs;
VariationsSeedStore::RegisterPrefs(prefs.registry());
prefs.SetString(prefs::kVariationsSafeCompressedSeed, "a previous seed");
prefs.SetString(prefs::kVariationsSafeSeedSignature, "a previous signature");
prefs.SetString(prefs::kVariationsSafeSeedLocale, "en-CA");
prefs.SetString(prefs::kVariationsSafeSeedPermanentConsistencyCountry, "IN");
prefs.SetString(prefs::kVariationsSafeSeedSessionConsistencyCountry, "MX");
prefs.SetTime(prefs::kVariationsSafeSeedDate, WrapTime(67890));
prefs.SetTime(prefs::kVariationsSafeSeedFetchTime, WrapTime(24680));
SignatureVerifyingVariationsSeedStore seed_store(&prefs);
base::HistogramTester histogram_tester;
EXPECT_FALSE(seed_store.StoreSafeSeed(serialized_seed, signature,
client_state, fetch_time));
// Verify that none of the prefs were overwritten.
EXPECT_EQ("a previous seed",
prefs.GetString(prefs::kVariationsSafeCompressedSeed));
EXPECT_EQ("a previous signature",
prefs.GetString(prefs::kVariationsSafeSeedSignature));
EXPECT_EQ("en-CA", prefs.GetString(prefs::kVariationsSafeSeedLocale));
EXPECT_EQ("IN", prefs.GetString(
prefs::kVariationsSafeSeedPermanentConsistencyCountry));
EXPECT_EQ("MX", prefs.GetString(
prefs::kVariationsSafeSeedSessionConsistencyCountry));
EXPECT_EQ(WrapTime(67890), prefs.GetTime(prefs::kVariationsSafeSeedDate));
EXPECT_EQ(WrapTime(24680),
prefs.GetTime(prefs::kVariationsSafeSeedFetchTime));
// Verify metrics.
histogram_tester.ExpectUniqueSample(
"Variations.SafeMode.StoreSafeSeed.Result",
StoreSeedResult::FAILED_SIGNATURE, 1);
histogram_tester.ExpectUniqueSample(
"Variations.SafeMode.StoreSafeSeed.SignatureValidity",
VerifySignatureResult::INVALID_SEED, 1);
}
TEST(VariationsSeedStoreTest, StoreSafeSeed_ValidSignature) {
std::string serialized_seed;
ASSERT_TRUE(
base::Base64Decode(kUncompressedBase64SeedData, &serialized_seed));
const std::string signature = kBase64SeedSignature;
ClientFilterableState client_state;
client_state.locale = "en-US";
client_state.reference_date = WrapTime(12345);
client_state.session_consistency_country = "US";
client_state.permanent_consistency_country = "CA";
const base::Time fetch_time = WrapTime(34567);
TestingPrefServiceSimple prefs;
VariationsSeedStore::RegisterPrefs(prefs.registry());
SignatureVerifyingVariationsSeedStore seed_store(&prefs);
base::HistogramTester histogram_tester;
EXPECT_TRUE(seed_store.StoreSafeSeed(serialized_seed, signature, client_state,
fetch_time));
// Verify the stored data.
std::string loaded_compressed_seed =
prefs.GetString(prefs::kVariationsSafeCompressedSeed);
std::string decoded_compressed_seed;
ASSERT_TRUE(
base::Base64Decode(loaded_compressed_seed, &decoded_compressed_seed));
EXPECT_EQ(Compress(serialized_seed), decoded_compressed_seed);
EXPECT_EQ(signature, prefs.GetString(prefs::kVariationsSafeSeedSignature));
EXPECT_EQ("en-US", prefs.GetString(prefs::kVariationsSafeSeedLocale));
EXPECT_EQ(WrapTime(12345), prefs.GetTime(prefs::kVariationsSafeSeedDate));
EXPECT_EQ(WrapTime(34567),
prefs.GetTime(prefs::kVariationsSafeSeedFetchTime));
EXPECT_EQ("US", prefs.GetString(
prefs::kVariationsSafeSeedSessionConsistencyCountry));
EXPECT_EQ("CA", prefs.GetString(
prefs::kVariationsSafeSeedPermanentConsistencyCountry));
// Verify metrics.
histogram_tester.ExpectUniqueSample(
"Variations.SafeMode.StoreSafeSeed.Result", StoreSeedResult::SUCCESS, 1);
histogram_tester.ExpectUniqueSample(
"Variations.SafeMode.StoreSafeSeed.SignatureValidity",
VerifySignatureResult::VALID_SIGNATURE, 1);
}
TEST(VariationsSeedStoreTest, StoreSafeSeed_IdenticalToLatestSeed) {
const VariationsSeed seed = CreateTestSeed();
const std::string serialized_seed = SerializeSeed(seed);
const std::string base64_seed = SerializeSeedBase64(seed);
const std::string signature = "a completely ignored signature";
ClientFilterableState unused_client_state;
const base::Time fetch_time = WrapTime(12345);
TestingPrefServiceSimple prefs;
VariationsSeedStore::RegisterPrefs(prefs.registry());
prefs.SetString(prefs::kVariationsCompressedSeed, base64_seed);
prefs.SetTime(prefs::kVariationsLastFetchTime, WrapTime(99999));
TestVariationsSeedStore seed_store(&prefs);
base::HistogramTester histogram_tester;
EXPECT_TRUE(seed_store.StoreSafeSeed(serialized_seed, signature,
unused_client_state, fetch_time));
// Verify the latest seed value was migrated to a sentinel value, rather than
// the full string.
EXPECT_EQ(kIdenticalToSafeSeedSentinel,
prefs.GetString(prefs::kVariationsCompressedSeed));
// Verify that loading the stored seed returns the original seed value.
VariationsSeed loaded_seed;
std::string loaded_seed_data;
std::string unused_loaded_base64_seed_signature;
EXPECT_TRUE(seed_store.LoadSeed(&loaded_seed, &loaded_seed_data,
&unused_loaded_base64_seed_signature));
EXPECT_EQ(SerializeSeed(seed), SerializeSeed(loaded_seed));
EXPECT_EQ(SerializeSeed(seed), loaded_seed_data);
// Verify that the safe seed prefs indeed contain the serialized seed value
// and that the last fetch time was copied from the latest seed.
EXPECT_EQ(base64_seed, prefs.GetString(prefs::kVariationsSafeCompressedSeed));
VariationsSeed loaded_safe_seed;
base::Time loaded_fetch_time;
EXPECT_TRUE(seed_store.LoadSafeSeed(&loaded_safe_seed, &unused_client_state,
&loaded_fetch_time));
EXPECT_EQ(SerializeSeed(seed), SerializeSeed(loaded_safe_seed));
EXPECT_EQ(WrapTime(99999), loaded_fetch_time);
// Verify metrics.
histogram_tester.ExpectUniqueSample(
"Variations.SafeMode.StoreSafeSeed.Result", StoreSeedResult::SUCCESS, 1);
}
TEST(VariationsSeedStoreTest, StoreSafeSeed_PreviouslyIdenticalToLatestSeed) {
// Create two distinct seeds: an old one saved as both the safe and the latest
// seed value, and a new one that should overwrite only the stored safe seed
// value.
const VariationsSeed old_seed = CreateTestSeed();
VariationsSeed new_seed = CreateTestSeed();
new_seed.set_serial_number("12345678");
ASSERT_NE(SerializeSeed(old_seed), SerializeSeed(new_seed));
const std::string serialized_old_seed = SerializeSeed(old_seed);
const std::string base64_old_seed = SerializeSeedBase64(old_seed);
const std::string serialized_new_seed = SerializeSeed(new_seed);
const std::string base64_new_seed = SerializeSeedBase64(new_seed);
const std::string signature = "a completely ignored signature";
const base::Time fetch_time = WrapTime(12345);
ClientFilterableState unused_client_state;
TestingPrefServiceSimple prefs;
VariationsSeedStore::RegisterPrefs(prefs.registry());
prefs.SetString(prefs::kVariationsSafeCompressedSeed, base64_old_seed);
prefs.SetString(prefs::kVariationsCompressedSeed,
kIdenticalToSafeSeedSentinel);
TestVariationsSeedStore seed_store(&prefs);
base::HistogramTester histogram_tester;
EXPECT_TRUE(seed_store.StoreSafeSeed(serialized_new_seed, signature,
unused_client_state, fetch_time));
// Verify the latest seed value was copied before the safe seed was
// overwritten.
EXPECT_EQ(base64_old_seed, prefs.GetString(prefs::kVariationsCompressedSeed));
// Verify that loading the stored seed returns the old seed value.
VariationsSeed loaded_seed;
std::string loaded_seed_data;
std::string unused_loaded_base64_seed_signature;
EXPECT_TRUE(seed_store.LoadSeed(&loaded_seed, &loaded_seed_data,
&unused_loaded_base64_seed_signature));
EXPECT_EQ(SerializeSeed(old_seed), SerializeSeed(loaded_seed));
EXPECT_EQ(SerializeSeed(old_seed), loaded_seed_data);
// Verify that the safe seed prefs indeed contain the new seed's serialized
// value.
EXPECT_EQ(base64_new_seed,
prefs.GetString(prefs::kVariationsSafeCompressedSeed));
VariationsSeed loaded_safe_seed;
base::Time loaded_fetch_time;
EXPECT_TRUE(seed_store.LoadSafeSeed(&loaded_safe_seed, &unused_client_state,
&loaded_fetch_time));
EXPECT_EQ(SerializeSeed(new_seed), SerializeSeed(loaded_safe_seed));
EXPECT_EQ(fetch_time, loaded_fetch_time);
// Verify metrics.
histogram_tester.ExpectUniqueSample(
"Variations.SafeMode.StoreSafeSeed.Result", StoreSeedResult::SUCCESS, 1);
}
TEST(VariationsSeedStoreTest, StoreSeedData_GzippedEmptySeed) {
std::string empty_seed;
std::string compressed_seed;
ASSERT_TRUE(compression::GzipCompress(empty_seed, &compressed_seed));
TestingPrefServiceSimple prefs;
VariationsSeedStore::RegisterPrefs(prefs.registry());
TestVariationsSeedStore seed_store(&prefs);
VariationsSeed parsed_seed;
EXPECT_FALSE(seed_store.StoreSeedData(compressed_seed, std::string(),
std::string(), base::Time::Now(), false,
true, false, &parsed_seed));
}
TEST(VariationsSeedStoreTest, VerifySeedSignature) {
// A valid seed and signature pair generated using the server's private key.
const std::string uncompressed_base64_seed_data = kUncompressedBase64SeedData;
const std::string base64_seed_signature = kBase64SeedSignature;
std::string seed_data;
ASSERT_TRUE(base::Base64Decode(uncompressed_base64_seed_data, &seed_data));
VariationsSeed seed;
ASSERT_TRUE(seed.ParseFromString(seed_data));
std::string base64_seed_data = SerializeSeedBase64(seed);
TestingPrefServiceSimple prefs;
VariationsSeedStore::RegisterPrefs(prefs.registry());
// The above inputs should be valid.
{
prefs.SetString(prefs::kVariationsCompressedSeed, base64_seed_data);
prefs.SetString(prefs::kVariationsSeedSignature, base64_seed_signature);
SignatureVerifyingVariationsSeedStore seed_store(&prefs);
base::HistogramTester histogram_tester;
VariationsSeed seed;
std::string seed_data;
std::string base64_seed_signature;
EXPECT_TRUE(seed_store.LoadSeed(&seed, &seed_data, &base64_seed_signature));
histogram_tester.ExpectUniqueSample(
"Variations.LoadSeedSignature",
static_cast<base::HistogramBase::Sample>(
VerifySignatureResult::VALID_SIGNATURE),
1);
}
// If there's no signature, the corresponding result should be returned.
{
prefs.SetString(prefs::kVariationsCompressedSeed, base64_seed_data);
prefs.SetString(prefs::kVariationsSeedSignature, std::string());
SignatureVerifyingVariationsSeedStore seed_store(&prefs);
base::HistogramTester histogram_tester;
VariationsSeed seed;
std::string seed_data;
std::string base64_seed_signature;
EXPECT_FALSE(
seed_store.LoadSeed(&seed, &seed_data, &base64_seed_signature));
histogram_tester.ExpectUniqueSample(
"Variations.LoadSeedSignature",
static_cast<base::HistogramBase::Sample>(
VerifySignatureResult::MISSING_SIGNATURE),
1);
}
// Using non-base64 encoded value as signature should fail.
{
prefs.SetString(prefs::kVariationsCompressedSeed, base64_seed_data);
prefs.SetString(prefs::kVariationsSeedSignature,
"not a base64-encoded string");
SignatureVerifyingVariationsSeedStore seed_store(&prefs);
base::HistogramTester histogram_tester;
std::string seed_data;
std::string base64_seed_signature;
EXPECT_FALSE(
seed_store.LoadSeed(&seed, &seed_data, &base64_seed_signature));
histogram_tester.ExpectUniqueSample(
"Variations.LoadSeedSignature",
static_cast<base::HistogramBase::Sample>(
VerifySignatureResult::DECODE_FAILED),
1);
}
// Using a different signature (e.g. the base64 seed data) should fail.
// OpenSSL doesn't distinguish signature decode failure from the
// signature not matching.
{
prefs.SetString(prefs::kVariationsCompressedSeed, base64_seed_data);
prefs.SetString(prefs::kVariationsSeedSignature, base64_seed_data);
SignatureVerifyingVariationsSeedStore seed_store(&prefs);
base::HistogramTester histogram_tester;
VariationsSeed seed;
std::string seed_data;
std::string base64_seed_signature;
EXPECT_FALSE(
seed_store.LoadSeed(&seed, &seed_data, &base64_seed_signature));
histogram_tester.ExpectUniqueSample(
"Variations.LoadSeedSignature",
static_cast<base::HistogramBase::Sample>(
VerifySignatureResult::INVALID_SEED),
1);
}
// Using a different seed should not match the signature.
{
VariationsSeed wrong_seed;
ASSERT_TRUE(wrong_seed.ParseFromString(seed_data));
(*wrong_seed.mutable_study(0)->mutable_name())[0] = 'x';
std::string base64_wrong_seed_data = SerializeSeedBase64(wrong_seed);
prefs.SetString(prefs::kVariationsCompressedSeed, base64_wrong_seed_data);
prefs.SetString(prefs::kVariationsSeedSignature, base64_seed_signature);
SignatureVerifyingVariationsSeedStore seed_store(&prefs);
base::HistogramTester histogram_tester;
std::string seed_data;
std::string base64_seed_signature;
EXPECT_FALSE(
seed_store.LoadSeed(&seed, &seed_data, &base64_seed_signature));
histogram_tester.ExpectUniqueSample(
"Variations.LoadSeedSignature",
static_cast<base::HistogramBase::Sample>(
VerifySignatureResult::INVALID_SEED),
1);
}
}
TEST(VariationsSeedStoreTest, ApplyDeltaPatch) {
// Sample seeds and the server produced delta between them to verify that the
// client code is able to decode the deltas produced by the server.
const std::string base64_before_seed_data =
"CigxN2E4ZGJiOTI4ODI0ZGU3ZDU2MGUyODRlODY1ZDllYzg2NzU1MTE0ElgKDFVNQVN0YWJp"
"bGl0eRjEyomgBTgBQgtTZXBhcmF0ZUxvZ0oLCgdEZWZhdWx0EABKDwoLU2VwYXJhdGVMb2cQ"
"ZFIVEgszNC4wLjE4MDEuMCAAIAEgAiADEkQKIFVNQS1Vbmlmb3JtaXR5LVRyaWFsLTEwMC1Q"
"ZXJjZW50GIDjhcAFOAFCCGdyb3VwXzAxSgwKCGdyb3VwXzAxEAFgARJPCh9VTUEtVW5pZm9y"
"bWl0eS1UcmlhbC01MC1QZXJjZW50GIDjhcAFOAFCB2RlZmF1bHRKDAoIZ3JvdXBfMDEQAUoL"
"CgdkZWZhdWx0EAFgAQ==";
const std::string base64_after_seed_data =
"CigyNGQzYTM3ZTAxYmViOWYwNWYzMjM4YjUzNWY3MDg1ZmZlZWI4NzQwElgKDFVNQVN0YWJp"
"bGl0eRjEyomgBTgBQgtTZXBhcmF0ZUxvZ0oLCgdEZWZhdWx0EABKDwoLU2VwYXJhdGVMb2cQ"
"ZFIVEgszNC4wLjE4MDEuMCAAIAEgAiADEpIBCh9VTUEtVW5pZm9ybWl0eS1UcmlhbC0yMC1Q"
"ZXJjZW50GIDjhcAFOAFCB2RlZmF1bHRKEQoIZ3JvdXBfMDEQARijtskBShEKCGdyb3VwXzAy"
"EAEYpLbJAUoRCghncm91cF8wMxABGKW2yQFKEQoIZ3JvdXBfMDQQARimtskBShAKB2RlZmF1"
"bHQQARiitskBYAESWAofVU1BLVVuaWZvcm1pdHktVHJpYWwtNTAtUGVyY2VudBiA44XABTgB"
"QgdkZWZhdWx0Sg8KC25vbl9kZWZhdWx0EAFKCwoHZGVmYXVsdBABUgQoACgBYAE=";
const std::string base64_delta_data =
"KgooMjRkM2EzN2UwMWJlYjlmMDVmMzIzOGI1MzVmNzA4NWZmZWViODc0MAAqW+4BkgEKH1VN"
"QS1Vbmlmb3JtaXR5LVRyaWFsLTIwLVBlcmNlbnQYgOOFwAU4AUIHZGVmYXVsdEoRCghncm91"
"cF8wMRABGKO2yQFKEQoIZ3JvdXBfMDIQARiktskBShEKCGdyb3VwXzAzEAEYpbbJAUoRCghn"
"cm91cF8wNBABGKa2yQFKEAoHZGVmYXVsdBABGKK2yQFgARJYCh9VTUEtVW5pZm9ybWl0eS1U"
"cmlhbC01MC1QZXJjZW50GIDjhcAFOAFCB2RlZmF1bHRKDwoLbm9uX2RlZmF1bHQQAUoLCgdk"
"ZWZhdWx0EAFSBCgAKAFgAQ==";
std::string before_seed_data;
std::string after_seed_data;
std::string delta_data;
EXPECT_TRUE(base::Base64Decode(base64_before_seed_data, &before_seed_data));
EXPECT_TRUE(base::Base64Decode(base64_after_seed_data, &after_seed_data));
EXPECT_TRUE(base::Base64Decode(base64_delta_data, &delta_data));
std::string output;
EXPECT_TRUE(VariationsSeedStore::ApplyDeltaPatch(before_seed_data, delta_data,
&output));
EXPECT_EQ(after_seed_data, output);
}
TEST(VariationsSeedStoreTest, LastFetchTime_DistinctSeeds) {
TestingPrefServiceSimple prefs;
VariationsSeedStore::RegisterPrefs(prefs.registry());
prefs.SetString(prefs::kVariationsCompressedSeed, "one");
prefs.SetString(prefs::kVariationsSafeCompressedSeed, "not one");
prefs.SetTime(prefs::kVariationsLastFetchTime, WrapTime(1));
prefs.SetTime(prefs::kVariationsSafeSeedFetchTime, WrapTime(0));
base::Time start_time = base::Time::Now();
TestVariationsSeedStore seed_store(&prefs);
seed_store.RecordLastFetchTime();
// Verify that the last fetch time was updated.
const base::Time last_fetch_time =
prefs.GetTime(prefs::kVariationsLastFetchTime);
EXPECT_NE(WrapTime(1), last_fetch_time);
EXPECT_GE(last_fetch_time, start_time);
// Verify that the safe seed's fetch time was *not* updated.
EXPECT_EQ(WrapTime(0), prefs.GetTime(prefs::kVariationsSafeSeedFetchTime));
}
TEST(VariationsSeedStoreTest, LastFetchTime_IdenticalSeeds) {
TestingPrefServiceSimple prefs;
VariationsSeedStore::RegisterPrefs(prefs.registry());
prefs.SetString(prefs::kVariationsSafeCompressedSeed, "some seed");
prefs.SetString(prefs::kVariationsCompressedSeed,
kIdenticalToSafeSeedSentinel);
prefs.SetTime(prefs::kVariationsLastFetchTime, WrapTime(1));
prefs.SetTime(prefs::kVariationsSafeSeedFetchTime, WrapTime(0));
base::Time start_time = base::Time::Now();
TestVariationsSeedStore seed_store(&prefs);
seed_store.RecordLastFetchTime();
// Verify that the last fetch time was updated.
const base::Time last_fetch_time =
prefs.GetTime(prefs::kVariationsLastFetchTime);
EXPECT_NE(WrapTime(1), last_fetch_time);
EXPECT_GE(last_fetch_time, start_time);
// Verify that the safe seed's fetch time *was* also updated.
EXPECT_EQ(last_fetch_time,
prefs.GetTime(prefs::kVariationsSafeSeedFetchTime));
}
TEST(VariationsSeedStoreTest, GetLatestSerialNumber_LoadsInitialValue) {
// Store good seed data to test if loading from prefs works.
const VariationsSeed seed = CreateTestSeed();
const std::string base64_seed = SerializeSeedBase64(seed);
const std::string base64_seed_signature = "a completely ignored signature";
TestingPrefServiceSimple prefs;
VariationsSeedStore::RegisterPrefs(prefs.registry());
prefs.SetString(prefs::kVariationsCompressedSeed, base64_seed);
prefs.SetString(prefs::kVariationsSeedSignature, base64_seed_signature);
TestVariationsSeedStore seed_store(&prefs);
EXPECT_EQ("123", seed_store.GetLatestSerialNumber());
}
TEST(VariationsSeedStoreTest, GetLatestSerialNumber_EmptyWhenNoSeedIsSaved) {
// Start with empty prefs.
TestingPrefServiceSimple prefs;
VariationsSeedStore::RegisterPrefs(prefs.registry());
TestVariationsSeedStore seed_store(&prefs);
EXPECT_EQ(std::string(), seed_store.GetLatestSerialNumber());
}
// Verifies that the cached serial number is correctly updated when a new seed
// is saved.
TEST(VariationsSeedStoreTest, GetLatestSerialNumber_UpdatedWithNewStoredSeed) {
// Store good seed data initially.
const VariationsSeed seed = CreateTestSeed();
const std::string base64_seed = SerializeSeedBase64(seed);
const std::string base64_seed_signature = "a completely ignored signature";
TestingPrefServiceSimple prefs;
VariationsSeedStore::RegisterPrefs(prefs.registry());
prefs.SetString(prefs::kVariationsCompressedSeed, base64_seed);
prefs.SetString(prefs::kVariationsSeedSignature, base64_seed_signature);
// Call GetLatestSerialNumber() once to prime the cached value.
TestVariationsSeedStore seed_store(&prefs);
EXPECT_EQ("123", seed_store.GetLatestSerialNumber());
VariationsSeed new_seed = CreateTestSeed();
new_seed.set_serial_number("456");
seed_store.StoreSeedForTesting(SerializeSeed(new_seed));
EXPECT_EQ("456", seed_store.GetLatestSerialNumber());
}
TEST(VariationsSeedStoreTest, GetLatestSerialNumber_ClearsPrefsOnFailure) {
// Store corrupted seed data to test that prefs are cleared when loading
// fails.
TestingPrefServiceSimple prefs;
VariationsSeedStore::RegisterPrefs(prefs.registry());
prefs.SetString(prefs::kVariationsCompressedSeed, "complete garbage");
prefs.SetString(prefs::kVariationsSeedSignature, "an unused signature");
TestVariationsSeedStore seed_store(&prefs);
EXPECT_EQ(std::string(), seed_store.GetLatestSerialNumber());
EXPECT_TRUE(PrefHasDefaultValue(prefs, prefs::kVariationsCompressedSeed));
}
#if defined(OS_ANDROID)
TEST(VariationsSeedStoreTest, ImportFirstRunJavaSeed) {
const std::string test_seed_data = "raw_seed_data_test";
const std::string test_seed_signature = "seed_signature_test";
const std::string test_seed_country = "seed_country_code_test";
const std::string test_response_date = "seed_response_date_test";
const bool test_is_gzip_compressed = true;
android::SetJavaFirstRunPrefsForTesting(test_seed_data, test_seed_signature,
test_seed_country, test_response_date,
test_is_gzip_compressed);
auto seed = android::GetVariationsFirstRunSeed();
EXPECT_EQ(test_seed_data, seed->data);
EXPECT_EQ(test_seed_signature, seed->signature);
EXPECT_EQ(test_seed_country, seed->country);
EXPECT_EQ(test_response_date, seed->date);
EXPECT_EQ(test_is_gzip_compressed, seed->is_gzip_compressed);
android::ClearJavaFirstRunPrefs();
seed = android::GetVariationsFirstRunSeed();
EXPECT_EQ("", seed->data);
EXPECT_EQ("", seed->signature);
EXPECT_EQ("", seed->country);
EXPECT_EQ("", seed->date);
EXPECT_FALSE(seed->is_gzip_compressed);
}
#endif // OS_ANDROID
} // namespace variations