blob: d1921e23d55aad9c0c518b4447d24c394bf9ce3f [file] [log] [blame]
// Copyright 2013 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 "services/preferences/tracked/pref_hash_calculator.h"
#include <memory>
#include <string>
#include <utility>
#include "base/macros.h"
#include "base/strings/string_util.h"
#include "base/values.h"
#include "testing/gtest/include/gtest/gtest.h"
TEST(PrefHashCalculatorTest, TestCurrentAlgorithm) {
base::Value string_value_1("string value 1");
base::Value string_value_2("string value 2");
base::DictionaryValue dictionary_value_1;
dictionary_value_1.SetInteger("int value", 1);
dictionary_value_1.Set("nested empty map",
std::make_unique<base::DictionaryValue>());
base::DictionaryValue dictionary_value_1_equivalent;
dictionary_value_1_equivalent.SetInteger("int value", 1);
base::DictionaryValue dictionary_value_2;
dictionary_value_2.SetInteger("int value", 2);
PrefHashCalculator calc1("seed1", "deviceid", "legacydeviceid");
PrefHashCalculator calc1_dup("seed1", "deviceid", "legacydeviceid");
PrefHashCalculator calc2("seed2", "deviceid", "legacydeviceid");
PrefHashCalculator calc3("seed1", "deviceid2", "legacydeviceid");
// Two calculators with same seed produce same hash.
ASSERT_EQ(calc1.Calculate("pref_path", &string_value_1),
calc1_dup.Calculate("pref_path", &string_value_1));
ASSERT_EQ(PrefHashCalculator::VALID,
calc1_dup.Validate("pref_path", &string_value_1,
calc1.Calculate("pref_path", &string_value_1)));
// Different seeds, different hashes.
ASSERT_NE(calc1.Calculate("pref_path", &string_value_1),
calc2.Calculate("pref_path", &string_value_1));
ASSERT_EQ(PrefHashCalculator::INVALID,
calc2.Validate("pref_path", &string_value_1,
calc1.Calculate("pref_path", &string_value_1)));
// Different device IDs, different hashes.
ASSERT_NE(calc1.Calculate("pref_path", &string_value_1),
calc3.Calculate("pref_path", &string_value_1));
// Different values, different hashes.
ASSERT_NE(calc1.Calculate("pref_path", &string_value_1),
calc1.Calculate("pref_path", &string_value_2));
// Different paths, different hashes.
ASSERT_NE(calc1.Calculate("pref_path", &string_value_1),
calc1.Calculate("pref_path_2", &string_value_1));
// Works for dictionaries.
ASSERT_EQ(calc1.Calculate("pref_path", &dictionary_value_1),
calc1.Calculate("pref_path", &dictionary_value_1));
ASSERT_NE(calc1.Calculate("pref_path", &dictionary_value_1),
calc1.Calculate("pref_path", &dictionary_value_2));
// Empty dictionary children are pruned.
ASSERT_EQ(calc1.Calculate("pref_path", &dictionary_value_1),
calc1.Calculate("pref_path", &dictionary_value_1_equivalent));
// NULL value is supported.
ASSERT_FALSE(calc1.Calculate("pref_path", NULL).empty());
}
// Tests the output against a known value to catch unexpected algorithm changes.
// The test hashes below must NEVER be updated, the serialization algorithm used
// must always be able to generate data that will produce these exact hashes.
TEST(PrefHashCalculatorTest, CatchHashChanges) {
static const char kSeed[] = "0123456789ABCDEF0123456789ABCDEF";
static const char kDeviceId[] = "test_device_id1";
auto null_value = std::make_unique<base::Value>();
auto bool_value = std::make_unique<base::Value>(false);
auto int_value = std::make_unique<base::Value>(1234567890);
auto double_value = std::make_unique<base::Value>(123.0987654321);
auto string_value = std::make_unique<base::Value>(
"testing with special chars:\n<>{}:^^@#$\\/");
// For legacy reasons, we have to support pruning of empty lists/dictionaries
// and nested empty ists/dicts in the hash generation algorithm.
auto nested_empty_dict = std::make_unique<base::DictionaryValue>();
nested_empty_dict->Set("a", std::make_unique<base::DictionaryValue>());
nested_empty_dict->Set("b", std::make_unique<base::ListValue>());
auto nested_empty_list = std::make_unique<base::ListValue>();
nested_empty_list->Append(std::make_unique<base::DictionaryValue>());
nested_empty_list->Append(std::make_unique<base::ListValue>());
nested_empty_list->Append(
std::make_unique<base::Value>(nested_empty_dict->Clone()));
// A dictionary with an empty dictionary, an empty list, and nested empty
// dictionaries/lists in it.
auto dict_value = std::make_unique<base::DictionaryValue>();
dict_value->SetString("a", "foo");
dict_value->Set("d", std::make_unique<base::ListValue>());
dict_value->Set("b", std::make_unique<base::DictionaryValue>());
dict_value->SetString("c", "baz");
dict_value->Set("e", std::move(nested_empty_dict));
dict_value->Set("f", std::move(nested_empty_list));
auto list_value = std::make_unique<base::ListValue>();
list_value->AppendBoolean(true);
list_value->AppendInteger(100);
list_value->AppendDouble(1.0);
ASSERT_EQ(base::Value::Type::NONE, null_value->type());
ASSERT_EQ(base::Value::Type::BOOLEAN, bool_value->type());
ASSERT_EQ(base::Value::Type::INTEGER, int_value->type());
ASSERT_EQ(base::Value::Type::DOUBLE, double_value->type());
ASSERT_EQ(base::Value::Type::STRING, string_value->type());
ASSERT_EQ(base::Value::Type::DICTIONARY, dict_value->type());
ASSERT_EQ(base::Value::Type::LIST, list_value->type());
// Test every value type independently. Intentionally omits Type::BINARY which
// isn't even allowed in JSONWriter's input.
static const char kExpectedNullValue[] =
"82A9F3BBC7F9FF84C76B033C854E79EEB162783FA7B3E99FF9372FA8E12C44F7";
EXPECT_EQ(PrefHashCalculator::VALID,
PrefHashCalculator(kSeed, kDeviceId, "legacydeviceid")
.Validate("pref.path", null_value.get(), kExpectedNullValue));
static const char kExpectedBooleanValue[] =
"A520D8F43EA307B0063736DC9358C330539D0A29417580514C8B9862632C4CCC";
EXPECT_EQ(
PrefHashCalculator::VALID,
PrefHashCalculator(kSeed, kDeviceId, "legacydeviceid")
.Validate("pref.path", bool_value.get(), kExpectedBooleanValue));
static const char kExpectedIntegerValue[] =
"8D60DA1F10BF5AA29819D2D66D7CCEF9AABC5DA93C11A0D2BD21078D63D83682";
EXPECT_EQ(PrefHashCalculator::VALID,
PrefHashCalculator(kSeed, kDeviceId, "legacydeviceid")
.Validate("pref.path", int_value.get(), kExpectedIntegerValue));
static const char kExpectedDoubleValue[] =
"C9D94772516125BEEDAE68C109D44BC529E719EE020614E894CC7FB4098C545D";
EXPECT_EQ(
PrefHashCalculator::VALID,
PrefHashCalculator(kSeed, kDeviceId, "legacydeviceid")
.Validate("pref.path", double_value.get(), kExpectedDoubleValue));
static const char kExpectedStringValue[] =
"05ACCBD3B05C45C36CD06190F63EC577112311929D8380E26E5F13182EB68318";
EXPECT_EQ(
PrefHashCalculator::VALID,
PrefHashCalculator(kSeed, kDeviceId, "legacydeviceid")
.Validate("pref.path", string_value.get(), kExpectedStringValue));
static const char kExpectedDictValue[] =
"7A84DCC710D796C771F789A4DA82C952095AA956B6F1667EE42D0A19ECAA3C4A";
EXPECT_EQ(PrefHashCalculator::VALID,
PrefHashCalculator(kSeed, kDeviceId, "legacydeviceid")
.Validate("pref.path", dict_value.get(), kExpectedDictValue));
static const char kExpectedListValue[] =
"8D5A25972DF5AE20D041C780E7CA54E40F614AD53513A0724EE8D62D4F992740";
EXPECT_EQ(PrefHashCalculator::VALID,
PrefHashCalculator(kSeed, kDeviceId, "legacydeviceid")
.Validate("pref.path", list_value.get(), kExpectedListValue));
// Also test every value type together in the same dictionary.
base::DictionaryValue everything;
everything.Set("null", std::move(null_value));
everything.Set("bool", std::move(bool_value));
everything.Set("int", std::move(int_value));
everything.Set("double", std::move(double_value));
everything.Set("string", std::move(string_value));
everything.Set("list", std::move(list_value));
everything.Set("dict", std::move(dict_value));
static const char kExpectedEverythingValue[] =
"B97D09BE7005693574DCBDD03D8D9E44FB51F4008B73FB56A49A9FA671A1999B";
EXPECT_EQ(PrefHashCalculator::VALID,
PrefHashCalculator(kSeed, kDeviceId, "legacydeviceid")
.Validate("pref.path", &everything, kExpectedEverythingValue));
}
TEST(PrefHashCalculatorTest, TestCompatibilityWithLegacyDeviceId) {
static const char kSeed[] = "0123456789ABCDEF0123456789ABCDEF";
static const char kNewDeviceId[] = "new_test_device_id1";
static const char kLegacyDeviceId[] = "test_device_id1";
// As in PrefHashCalculatorTest.CatchHashChanges.
const base::Value string_value("testing with special chars:\n<>{}:^^@#$\\/");
static const char kExpectedValue[] =
"05ACCBD3B05C45C36CD06190F63EC577112311929D8380E26E5F13182EB68318";
EXPECT_EQ(PrefHashCalculator::VALID_SECURE_LEGACY,
PrefHashCalculator(kSeed, kNewDeviceId, kLegacyDeviceId)
.Validate("pref.path", &string_value, kExpectedValue));
}
TEST(PrefHashCalculatorTest, TestNotCompatibleWithEmptyLegacyDeviceId) {
static const char kSeed[] = "0123456789ABCDEF0123456789ABCDEF";
static const char kNewDeviceId[] = "unused";
static const char kLegacyDeviceId[] = "";
const base::Value string_value("testing with special chars:\n<>{}:^^@#$\\/");
static const char kExpectedValue[] =
"F14F989B7CAABF3B36ECAE34492C4D8094D2500E7A86D9A3203E54B274C27CB5";
EXPECT_EQ(PrefHashCalculator::INVALID,
PrefHashCalculator(kSeed, kNewDeviceId, kLegacyDeviceId)
.Validate("pref.path", &string_value, kExpectedValue));
}