Fix the hash generation algorithm to be consistent with prior implementation.

BUG=321680
R=bauerb@chromium.org,gab@chromium.org

Review URL: https://codereview.chromium.org/110523006

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@244512 0039d316-1c4b-4281-b951-d872f2087c98
parent f4671393
...@@ -16,6 +16,32 @@ ...@@ -16,6 +16,32 @@
namespace { namespace {
// Calculates an HMAC of |message| using |key|, encoded as a hexadecimal string.
std::string GetDigestString(const std::string& key,
const std::string& message) {
crypto::HMAC hmac(crypto::HMAC::SHA256);
std::vector<uint8> digest(hmac.DigestLength());
if (!hmac.Init(key) || !hmac.Sign(message, &digest[0], digest.size())) {
NOTREACHED();
return std::string();
}
return base::HexEncode(&digest[0], digest.size());
}
// Verifies that |digest_string| is a valid HMAC of |message| using |key|.
// |digest_string| must be encoded as a hexadecimal string.
bool VerifyDigestString(const std::string& key,
const std::string& message,
const std::string& digest_string) {
crypto::HMAC hmac(crypto::HMAC::SHA256);
std::vector<uint8> digest;
return base::HexStringToBytes(digest_string, &digest) &&
hmac.Init(key) &&
hmac.Verify(message,
base::StringPiece(reinterpret_cast<char*>(&digest[0]),
digest.size()));
}
// Renders |value| as a string. |value| may be NULL, in which case the result // Renders |value| as a string. |value| may be NULL, in which case the result
// is an empty string. // is an empty string.
std::string ValueAsString(const base::Value* value) { std::string ValueAsString(const base::Value* value) {
...@@ -38,53 +64,63 @@ std::string ValueAsString(const base::Value* value) { ...@@ -38,53 +64,63 @@ std::string ValueAsString(const base::Value* value) {
} }
// Common helper for all hash algorithms. // Common helper for all hash algorithms.
std::string CalculateFromValueAndComponents( std::string GetMessageFromValueAndComponents(
const std::string& seed,
const base::Value* value, const base::Value* value,
const std::vector<std::string>& extra_components) { const std::vector<std::string>& extra_components) {
static const size_t kSHA256DigestSize = 32; return JoinString(extra_components, "") + ValueAsString(value);
}
std::string message = JoinString(extra_components, "") + ValueAsString(value);
crypto::HMAC hmac(crypto::HMAC::SHA256); // Generates a device ID based on the input device ID. The derived device ID has
unsigned char digest[kSHA256DigestSize]; // no useful properties beyond those of the input device ID except that it is
if (!hmac.Init(seed) || !hmac.Sign(message, digest, arraysize(digest))) { // consistent with previous implementations.
NOTREACHED(); std::string GenerateDeviceIdLikePrefMetricsServiceDid(
const std::string& original_device_id) {
if (original_device_id.empty())
return std::string(); return std::string();
} return StringToLowerASCII(
GetDigestString(original_device_id, "PrefMetricsService"));
}
return base::HexEncode(digest, arraysize(digest)); // Verifies a hash using a deprecated hash algorithm. For validating old
// hashes during migration.
bool VerifyLegacyHash(const std::string& seed,
const base::Value* value,
const std::string& digest_string) {
return VerifyDigestString(
seed,
GetMessageFromValueAndComponents(value, std::vector<std::string>()),
digest_string);
} }
} // namespace } // namespace
PrefHashCalculator::PrefHashCalculator(const std::string& seed, PrefHashCalculator::PrefHashCalculator(const std::string& seed,
const std::string& device_id) const std::string& device_id)
: seed_(seed), device_id_(device_id) {} : seed_(seed),
device_id_(GenerateDeviceIdLikePrefMetricsServiceDid(device_id)) {}
std::string PrefHashCalculator::Calculate(const std::string& path, std::string PrefHashCalculator::Calculate(const std::string& path,
const base::Value* value) const { const base::Value* value) const {
std::vector<std::string> components; return GetDigestString(seed_, GetMessage(path, value));
if (!device_id_.empty())
components.push_back(device_id_);
components.push_back(path);
return CalculateFromValueAndComponents(seed_, value, components);
} }
PrefHashCalculator::ValidationResult PrefHashCalculator::Validate( PrefHashCalculator::ValidationResult PrefHashCalculator::Validate(
const std::string& path, const std::string& path,
const base::Value* value, const base::Value* value,
const std::string& hash) const { const std::string& digest_string) const {
if (hash == Calculate(path, value)) if (VerifyDigestString(seed_, GetMessage(path, value), digest_string))
return VALID; return VALID;
if (hash == CalculateLegacyHash(path, value)) if (VerifyLegacyHash(seed_, value, digest_string))
return VALID_LEGACY; return VALID_LEGACY;
return INVALID; return INVALID;
} }
std::string PrefHashCalculator::CalculateLegacyHash( std::string PrefHashCalculator::GetMessage(const std::string& path,
const std::string& path, const base::Value* value) const { const base::Value* value) const {
return CalculateFromValueAndComponents(seed_, std::vector<std::string> components;
value, if (!device_id_.empty())
std::vector<std::string>()); components.push_back(device_id_);
components.push_back(path);
return GetMessageFromValueAndComponents(value, components);
} }
...@@ -39,10 +39,9 @@ class PrefHashCalculator { ...@@ -39,10 +39,9 @@ class PrefHashCalculator {
const std::string& hash) const; const std::string& hash) const;
private: private:
// Calculate a hash using a deprecated hash algorithm. For validating old // Concatenates |device_id_|, |path|, and |value| to give the hash input.
// hashes during migration. std::string GetMessage(const std::string& path,
std::string CalculateLegacyHash(const std::string& path, const base::Value* value) const;
const base::Value* value) const;
std::string seed_; std::string seed_;
std::string device_id_; std::string device_id_;
......
...@@ -71,25 +71,24 @@ TEST(PrefHashCalculatorTest, TestCurrentAlgorithm) { ...@@ -71,25 +71,24 @@ TEST(PrefHashCalculatorTest, TestCurrentAlgorithm) {
// Tests the output against a known value to catch unexpected algorithm changes. // Tests the output against a known value to catch unexpected algorithm changes.
TEST(PrefHashCalculatorTest, CatchHashChanges) { TEST(PrefHashCalculatorTest, CatchHashChanges) {
const char* kDeviceId = "test_device_id1"; static const char kSeed[] = "0123456789ABCDEF0123456789ABCDEF";
static const char kDeviceId[] = "test_device_id1";
{ {
static const char kExpectedValue[] = static const char kExpectedValue[] =
"5CE37D7EBCBC9BE510F0F5E7C326CA92C1673713C3717839610AEA1A217D8BB8"; "D8137B8E767D3D910DCD3821CAC61D26ABB042E6EC406AEB0E347ED73A3A4EC1";
base::ListValue list; base::ListValue list;
list.Set(0, new base::FundamentalValue(true)); list.Set(0, new base::FundamentalValue(true));
list.Set(1, new base::FundamentalValue(100)); list.Set(1, new base::FundamentalValue(100));
list.Set(2, new base::FundamentalValue(1.0)); list.Set(2, new base::FundamentalValue(1.0));
// 32 NULL bytes is the seed that was used to generate the hash in old
// tests. Use it again to ensure that we haven't altered the algorithm.
EXPECT_EQ(PrefHashCalculator::VALID, EXPECT_EQ(PrefHashCalculator::VALID,
PrefHashCalculator(std::string(32u, 0), kDeviceId).Validate( PrefHashCalculator(kSeed, kDeviceId).Validate(
"pref.path2", &list, kExpectedValue)); "pref.path2", &list, kExpectedValue));
} }
{ {
static const char kExpectedValue[] = static const char kExpectedValue[] =
"A50FE7EB31BFBC32B8A27E71730AF15421178A9B5815644ACE174B18966735B9"; "3F947A044DE9E421A735525385B4C789693682E6F6E3E4CB4741E58724B28F96";
base::DictionaryValue dict; base::DictionaryValue dict;
dict.Set("a", new base::StringValue("foo")); dict.Set("a", new base::StringValue("foo"));
...@@ -97,18 +96,40 @@ TEST(PrefHashCalculatorTest, CatchHashChanges) { ...@@ -97,18 +96,40 @@ TEST(PrefHashCalculatorTest, CatchHashChanges) {
dict.Set("b", new base::StringValue("bar")); dict.Set("b", new base::StringValue("bar"));
dict.Set("c", new base::StringValue("baz")); dict.Set("c", new base::StringValue("baz"));
// 32 NULL bytes is the seed that was used to generate the hash in old
// tests. Use it again to ensure that we haven't altered the algorithm.
EXPECT_EQ(PrefHashCalculator::VALID, EXPECT_EQ(PrefHashCalculator::VALID,
PrefHashCalculator(std::string(32u, 0), kDeviceId).Validate( PrefHashCalculator(kSeed, kDeviceId).Validate(
"pref.path1", &dict, kExpectedValue)); "pref.path1", &dict, kExpectedValue));
} }
} }
TEST(PrefHashCalculatorTest, TestCompatibilityWithPrefMetricsService) {
static const char kSeed[] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F
};
static const char kDeviceId[] =
"D730D9CBD98C734A4FB097A1922275FE9F7E026A4EA1BE0E84";
static const char kExpectedValue[] =
"845EF34663FF8D32BE6707F40258FBA531C2BFC532E3B014AFB3476115C2A9DE";
base::ListValue startup_urls;
startup_urls.Set(0, new base::StringValue("http://www.chromium.org/"));
EXPECT_EQ(PrefHashCalculator::VALID,
PrefHashCalculator(std::string(kSeed, arraysize(kSeed)), kDeviceId).
Validate("session.startup_urls", &startup_urls, kExpectedValue));
}
TEST(PrefHashCalculatorTest, TestLegacyAlgorithm) { TEST(PrefHashCalculatorTest, TestLegacyAlgorithm) {
const char* kExpectedValue = static const char kExpectedValue[] =
"C503FB7C65EEFD5C07185F616A0AA67923C069909933F362022B1F187E73E9A2"; "C503FB7C65EEFD5C07185F616A0AA67923C069909933F362022B1F187E73E9A2";
const char* kDeviceId = "deviceid"; static const char kDeviceId[] = "not_used";
base::DictionaryValue dict; base::DictionaryValue dict;
dict.Set("a", new base::StringValue("foo")); dict.Set("a", new base::StringValue("foo"));
...@@ -120,5 +141,4 @@ TEST(PrefHashCalculatorTest, TestLegacyAlgorithm) { ...@@ -120,5 +141,4 @@ TEST(PrefHashCalculatorTest, TestLegacyAlgorithm) {
EXPECT_EQ(PrefHashCalculator::VALID_LEGACY, EXPECT_EQ(PrefHashCalculator::VALID_LEGACY,
PrefHashCalculator(std::string(32u, 0), kDeviceId).Validate( PrefHashCalculator(std::string(32u, 0), kDeviceId).Validate(
"pref.path1", &dict, kExpectedValue)); "pref.path1", &dict, kExpectedValue));
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment