blob: fc279d6d27d70fe5c997845f49ca0dd300f6c4e4 [file] [log] [blame] [edit]
// Copyright 2022 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <base/dcheck_is_on.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "featured/hmac.h"
namespace {
using ::testing::SizeIs;
} // namespace
namespace featured {
// Check that basic verification works as expected.
TEST(HMAC, SignAndVerify) {
HMAC hmacer(HMAC::SHA256);
ASSERT_TRUE(hmacer.Init());
std::optional<std::string> hmac_maybe = hmacer.Sign("data");
ASSERT_TRUE(hmac_maybe.has_value());
std::string hmac = hmac_maybe.value();
EXPECT_THAT(hmac, SizeIs(32)); // SHA256 should give a 32-byte digest.
// Same data should verify (even if not identical by pointer)
char other_data[] = "Aata"; // force compiler to give a different address.
other_data[0] = 'd';
EXPECT_TRUE(hmacer.Verify(other_data, hmac));
// Different data should *not* verify.
EXPECT_FALSE(hmacer.Verify("not data", hmac));
// Should generate a different key and thus not verify.
HMAC hmacer_2(HMAC::SHA256);
ASSERT_TRUE(hmacer_2.Init());
EXPECT_FALSE(hmacer_2.Verify("data", hmac));
}
// HMAC should only verify if length matches.
TEST(HMAC, VerifyBadSize) {
HMAC hmacer(HMAC::SHA256);
ASSERT_TRUE(hmacer.Init());
std::optional<std::string> hmac_maybe = hmacer.Sign("data");
ASSERT_TRUE(hmac_maybe.has_value());
std::string hmac = hmac_maybe.value();
std::string hmac_long = hmac + "2";
EXPECT_FALSE(hmacer.Verify("data", hmac_long));
std::string hmac_short = hmac.substr(0, hmac.size() - 1);
ASSERT_EQ(hmac_short.size(), hmac.size() - 1);
EXPECT_FALSE(hmacer.Verify("data", hmac_short));
}
// Check that specifying a constant test key produces consistent results.
TEST(HMAC, SignAndVerify_FakeKey) {
HMAC hmacer(HMAC::SHA256);
ASSERT_TRUE(hmacer.Init("fakekey"));
std::optional<std::string> hmac_maybe = hmacer.Sign("data");
ASSERT_TRUE(hmac_maybe.has_value());
std::string hmac = hmac_maybe.value();
// Key should be as set.
EXPECT_EQ(hmacer.GetKey(), "fakekey");
// Different data should *not* verify.
EXPECT_FALSE(hmacer.Verify("not data", hmac));
// Should use the same key and thus verify.
HMAC hmacer_2(HMAC::SHA256);
ASSERT_TRUE(hmacer_2.Init("fakekey"));
EXPECT_TRUE(hmacer_2.Verify("data", hmac));
}
// Test that Init() generates an appropriate-length key, and that it is
// not all 0.
TEST(HMAC, Init) {
std::string bad_key(32, '\0');
ASSERT_THAT(bad_key, SizeIs(32));
HMAC hmacer(HMAC::SHA256);
ASSERT_TRUE(hmacer.Init());
std::string actual = hmacer.GetKey();
EXPECT_THAT(actual, SizeIs(32));
EXPECT_NE(actual, bad_key);
}
// Check that we can create a class using a key with null bytes and other
// non-ASCII bytes.
TEST(HMAC, FakeKey_ArbitraryBytes) {
std::string key("\xff\x00\xca\xfe", 4);
HMAC hmacer(HMAC::SHA256);
ASSERT_TRUE(hmacer.Init(key));
// Key should be as set.
EXPECT_THAT(hmacer.GetKey(), SizeIs(4));
EXPECT_EQ(hmacer.GetKey(), key);
}
// If Init is called twice, the new key should be different.
TEST(HMAC, GenerateTwice) {
HMAC hmacer(HMAC::SHA256);
ASSERT_TRUE(hmacer.Init());
std::string k1 = hmacer.GetKey();
ASSERT_TRUE(hmacer.Init());
EXPECT_NE(hmacer.GetKey(), k1);
}
#if DCHECK_IS_ON()
// Verify that sign DCHECKs if key isn't initialized.
TEST(HMACDeath, SignDieEmptyKey) {
HMAC hmacer(HMAC::SHA256);
EXPECT_DEATH(hmacer.Sign("data"), "Class not initialized");
}
// Verify that verify DCHECKs if key isn't initialized.
TEST(HMACDeath, VerifyDieEmptyKey) {
HMAC hmacer(HMAC::SHA256);
EXPECT_DEATH(hmacer.Verify("data", "hmac"), "Class not initialized");
}
#else // DCHECK_IS_ON()
// Verify that sign returns nullopt if DCHECK is off and key is empty.
TEST(HMAC, SignFailEmptyKey) {
HMAC hmacer(HMAC::SHA256);
EXPECT_EQ(hmacer.Sign("data"), std::nullopt);
}
// Verify that verification fails if DCHECK is off and key is empty.
TEST(HMAC, VerifyFailEmptyKey) {
HMAC hmacer(HMAC::SHA256);
EXPECT_FALSE(hmacer.Verify("data", "hmac"));
}
#endif // DCHECK_IS_ON()
} // namespace featured