// Copyright 2020 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // /////////////////////////////////////////////////////////////////////////////// #include "tink/subtle/stateful_hmac_boringssl.h" #include #include #include #include "gmock/gmock.h" #include "gtest/gtest.h" #include "absl/memory/memory.h" #include "absl/status/status.h" #include "tink/subtle/common_enums.h" #include "tink/subtle/wycheproof_util.h" #include "tink/util/secret_data.h" #include "tink/util/status.h" #include "tink/util/statusor.h" #include "tink/util/test_matchers.h" #include "tink/util/test_util.h" namespace crypto { namespace tink { namespace subtle { namespace { constexpr size_t kTagSize = 16; constexpr size_t kSmallTagSize = 10; using ::crypto::tink::test::IsOk; using ::crypto::tink::test::StatusIs; using ::testing::HasSubstr; using ::testing::StrEq; void EmptyHmac(HashType hash_type, uint32_t tag_size, std::string key, std::string expected) { auto hmac_result = StatefulHmacBoringSsl::New( hash_type, tag_size, util::SecretDataFromStringView(key)); EXPECT_THAT(hmac_result, IsOk()); auto hmac = std::move(hmac_result.value()); auto result = hmac->Finalize(); EXPECT_THAT(result, IsOk()); auto tag = result.value(); EXPECT_EQ(tag.size(), tag_size); EXPECT_EQ(tag, expected); } TEST(StatefulHmacBoringSslTest, testEmpty) { std::string key(test::HexDecodeOrDie("000102030405060708090a0b0c0d0e0f")); std::string expected_256( test::HexDecodeOrDie("07eff8b326b7798c9ccfcbdbe579489a")); EmptyHmac(HashType::SHA256, kTagSize, key, expected_256); std::string expected_512( test::HexDecodeOrDie("2fec800ca276c44985a35aec92067e5e")); EmptyHmac(HashType::SHA512, kTagSize, key, expected_512); std::string expected_256_small(test::HexDecodeOrDie("07eff8b326b7798c9ccf")); EmptyHmac(HashType::SHA256, kSmallTagSize, key, expected_256_small); std::string expected_512_small(test::HexDecodeOrDie("2fec800ca276c44985a3")); EmptyHmac(HashType::SHA512, kSmallTagSize, key, expected_512_small); } void BasicHmac(HashType hash_type, uint32_t tag_size, std::string key, std::string data, std::string expected) { auto hmac_result = StatefulHmacBoringSsl::New( hash_type, tag_size, util::SecretDataFromStringView(key)); EXPECT_THAT(hmac_result, IsOk()); auto hmac = std::move(hmac_result.value()); auto update_result = hmac->Update(data); EXPECT_THAT(update_result, IsOk()); auto result = hmac->Finalize(); EXPECT_THAT(result, IsOk()); auto tag = result.value(); EXPECT_EQ(tag.size(), tag_size); EXPECT_EQ(tag, expected); } TEST(StatefulHmacBoringSslTest, testBasic) { std::string key(test::HexDecodeOrDie("000102030405060708090a0b0c0d0e0f")); std::string data = "Some data to test."; std::string expected_256( test::HexDecodeOrDie("1d6eb74bc283f7947e92c72bd985ce6e")); BasicHmac(HashType::SHA256, kTagSize, key, data, expected_256); std::string expected_512( test::HexDecodeOrDie("72b8ff800f57f9aeec41265a29b69b6a")); BasicHmac(HashType::SHA512, kTagSize, key, data, expected_512); std::string expected_256_small(test::HexDecodeOrDie("1d6eb74bc283f7947e92")); BasicHmac(HashType::SHA256, kSmallTagSize, key, data, expected_256_small); std::string expected_512_small(test::HexDecodeOrDie("72b8ff800f57f9aeec41")); BasicHmac(HashType::SHA512, kSmallTagSize, key, data, expected_512_small); } void MultipleUpdateHmac(HashType hash_type, uint32_t tag_size, std::string key, std::string data1, std::string data2, std::string data3, std::string data4, std::string expected) { auto hmac_result = StatefulHmacBoringSsl::New( hash_type, tag_size, util::SecretDataFromStringView(key)); EXPECT_THAT(hmac_result, IsOk()); auto hmac = std::move(hmac_result.value()); auto update_1 = hmac->Update(data1); EXPECT_THAT(update_1, IsOk()); auto update_2 = hmac->Update(data2); EXPECT_THAT(update_2, IsOk()); auto update_3 = hmac->Update(data3); EXPECT_THAT(update_3, IsOk()); auto update_4 = hmac->Update(data4); EXPECT_THAT(update_4, IsOk()); auto result = hmac->Finalize(); EXPECT_THAT(result, IsOk()); auto tag = result.value(); EXPECT_EQ(tag.size(), tag_size); EXPECT_EQ(tag, expected); } TEST(StatefulHmacBoringSslTest, testMultipleUpdates) { std::string key(test::HexDecodeOrDie("000102030405060708090a0b0c0d0e0f")); std::string data1 = "Some ", data2 = "data ", data3 = "to ", data4 = "test."; // The tags are the same as the tags in testBasic, since they have the same // key and the same input, but testMultipleUpdates uses multiple updates. std::string expected_256( test::HexDecodeOrDie("1d6eb74bc283f7947e92c72bd985ce6e")); MultipleUpdateHmac(HashType::SHA256, kTagSize, key, data1, data2, data3, data4, expected_256); std::string expected_512( test::HexDecodeOrDie("72b8ff800f57f9aeec41265a29b69b6a")); MultipleUpdateHmac(HashType::SHA512, kTagSize, key, data1, data2, data3, data4, expected_512); std::string expected_256_small(test::HexDecodeOrDie("1d6eb74bc283f7947e92")); MultipleUpdateHmac(HashType::SHA256, kSmallTagSize, key, data1, data2, data3, data4, expected_256_small); std::string expected_512_small(test::HexDecodeOrDie("72b8ff800f57f9aeec41")); MultipleUpdateHmac(HashType::SHA512, kSmallTagSize, key, data1, data2, data3, data4, expected_512_small); } TEST(StatefulHmacBoringSslTest, testInvalidKeySizes) { size_t tag_size = 16; for (int keysize = 0; keysize < 65; keysize++) { std::string key(keysize, 'x'); auto hmac_result = StatefulHmacBoringSsl::New( HashType::SHA256, tag_size, util::SecretDataFromStringView(key)); if (keysize >= 16) { EXPECT_THAT(hmac_result, IsOk()); } else { EXPECT_THAT(hmac_result.status(), StatusIs(absl::StatusCode::kInvalidArgument, HasSubstr("invalid key size"))); } } } TEST(StatefulCmacFactoryTest, createsObjects) { std::string key(test::HexDecodeOrDie("000102030405060708090a0b0c0d0e0f")); std::string data = "Some data to test."; std::string expected( test::HexDecodeOrDie("1d6eb74bc283f7947e92c72bd985ce6e")); BasicHmac(HashType::SHA256, kTagSize, key, data, expected); auto factory = absl::make_unique( HashType::SHA256, kTagSize, util::SecretDataFromStringView(key)); auto stateful_hmac_or = factory->Create(); ASSERT_THAT(stateful_hmac_or, IsOk()); auto stateful_hmac = std::move(stateful_hmac_or.value()); EXPECT_THAT(stateful_hmac->Update(data), IsOk()); auto output_or = stateful_hmac->Finalize(); ASSERT_THAT(output_or, IsOk()); auto output = output_or.value(); EXPECT_THAT(output, StrEq(expected)); } class StatefulHmacBoringSslTestVectorTest : public ::testing::TestWithParam> { public: // Utility to simplify testing with test vectors. // Arguments and result are hexadecimal. void StatefulHmacVerifyHex(const std::string &key_hex, const std::string &msg_hex, const std::string &tag_hex) { std::string key = test::HexDecodeOrDie(key_hex); std::string tag = test::HexDecodeOrDie(tag_hex); std::string msg = test::HexDecodeOrDie(msg_hex); auto create_result = StatefulHmacBoringSsl::New( HashType::SHA1, tag.size(), util::SecretDataFromStringView(key)); EXPECT_THAT(create_result, IsOk()); auto hmac = std::move(create_result.value()); auto update_result = hmac->Update(msg); EXPECT_THAT(update_result, IsOk()); auto finalize_result = hmac->Finalize(); EXPECT_THAT(finalize_result, IsOk()); auto result = finalize_result.value(); EXPECT_EQ(result, tag); } }; // Wycheproof HMAC tests are not enabled because the test vectors are in // "rc" (release candidate) state, and are not yet exported for use. // TODO(cathieyun): re-enable Wycheproof HMAC tests once vectors are exported. /* // Test with test vectors from Wycheproof project. bool WycheproofTest(const rapidjson::Document &root, HashType hash_type) { int errors = 0; for (const rapidjson::Value &test_group : root["testGroups"].GetArray()) { // Get the key size in bytes. Wycheproof contains tests for keys smaller // than MIN_KEY_SIZE, which is 16, so the test will skip those. if (test_group["keySize"].GetInt() / 8 < 16) { continue; } for (const rapidjson::Value &test : test_group["tests"].GetArray()) { std::string comment = test["comment"].GetString(); std::string key = WycheproofUtil::GetBytes(test["key"]); std::string msg = WycheproofUtil::GetBytes(test["msg"]); std::string tag = WycheproofUtil::GetBytes(test["tag"]); std::string id = absl::StrCat(test["tcId"].GetInt()); std::string expected = test["result"].GetString(); auto create_result = StatefulHmacBoringSsl::New(hash_type, tag.length(), key); EXPECT_THAT(create_result, IsOk()); auto hmac = std::move(create_result.value()); auto update_result = hmac->Update(msg); EXPECT_THAT(update_result, IsOk()); auto finalize_result = hmac->Finalize(); auto result = finalize_result.value(); bool success = result == tag; if (success) { // std::string result_tag = result.value(); if (expected == "invalid") { ADD_FAILURE() << "verified incorrect tag:" << id; errors++; } } else { if (expected == "valid") { ADD_FAILURE() << "Could not create tag for test with tcId:" << id << " tag_size:" << tag.length() << " key_size:" << key.length() << " error:" << result; errors++; } } } } return errors == 0; } TEST_F(StatefulHmacBoringSslTest, TestVectors) { // Test Hmac with SHA256 std::unique_ptr root256 = WycheproofUtil::ReadTestVectors("hmac_sha256_test.json"); ASSERT_TRUE(WycheproofTest(*root256, HashType::SHA256)); // Test Hmac with SHA512 std::unique_ptr root512 = WycheproofUtil::ReadTestVectors("hmac_sha512_test.json"); ASSERT_TRUE(WycheproofTest(*root512, HashType::SHA512)); } */ } // namespace } // namespace subtle } // namespace tink } // namespace crypto