| // Copyright 2017 Google Inc. |
| // |
| // 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/hmac_boringssl.h" |
| |
| #include <string> |
| |
| #include "tink/mac.h" |
| #include "tink/subtle/common_enums.h" |
| #include "tink/util/status.h" |
| #include "tink/util/statusor.h" |
| #include "tink/util/test_util.h" |
| #include "gtest/gtest.h" |
| |
| namespace crypto { |
| namespace tink { |
| namespace subtle { |
| namespace { |
| |
| class HmacBoringSslTest : public ::testing::Test { |
| public: |
| // Utility to simplify testing with test vectors. |
| // Arguments and result are hexadecimal. |
| bool HmacVerifyHex(HashType hash, |
| uint32_t tag_size, |
| const std::string &key_hex, |
| const std::string &tag_hex, |
| const std::string &data_hex) { |
| std::string key = test::HexDecodeOrDie(key_hex); |
| std::string tag = test::HexDecodeOrDie(tag_hex); |
| std::string data = test::HexDecodeOrDie(data_hex); |
| auto hmac_result = HmacBoringSsl::New(hash, tag_size, key); |
| EXPECT_TRUE(hmac_result.ok()) << hmac_result.status(); |
| auto hmac = std::move(hmac_result.ValueOrDie()); |
| auto result = hmac->VerifyMac(tag, data); |
| return result.ok(); |
| } |
| }; |
| |
| TEST_F(HmacBoringSslTest, testBasic) { |
| std::string key(test::HexDecodeOrDie("000102030405060708090a0b0c0d0e0f")); |
| size_t tag_size = 16; |
| auto hmac_result = HmacBoringSsl::New(HashType::SHA1, tag_size, key); |
| EXPECT_TRUE(hmac_result.ok()) << hmac_result.status(); |
| auto hmac = std::move(hmac_result.ValueOrDie()); |
| { // Test with some example data. |
| std::string data = "Some data to test."; |
| auto res = hmac->ComputeMac(data); |
| EXPECT_TRUE(res.ok()) << res.status().ToString(); |
| std::string tag = res.ValueOrDie(); |
| EXPECT_EQ(tag_size, tag.size()); |
| auto status = hmac->VerifyMac(tag, data); |
| EXPECT_TRUE(status.ok()) << "tag:" << test::HexEncode(tag) |
| << " status:" << status.ToString(); |
| } |
| { // Test with empty example data. |
| absl::string_view data; |
| auto res = hmac->ComputeMac(data); |
| EXPECT_TRUE(res.ok()) << res.status().ToString(); |
| std::string tag = res.ValueOrDie(); |
| EXPECT_EQ(tag_size, tag.size()); |
| auto status = hmac->VerifyMac(tag, data); |
| EXPECT_TRUE(status.ok()) << "tag:" << test::HexEncode(tag) |
| << " status:" << status.ToString(); |
| } |
| } |
| |
| TEST_F(HmacBoringSslTest, testModification) { |
| std::string key(test::HexDecodeOrDie("000102030405060708090a0b0c0d0e0f")); |
| auto hmac_result = HmacBoringSsl::New(HashType::SHA1, 16, key); |
| EXPECT_TRUE(hmac_result.ok()) << hmac_result.status(); |
| auto hmac = std::move(hmac_result.ValueOrDie()); |
| std::string data = "Some data to test"; |
| std::string tag = hmac->ComputeMac(data).ValueOrDie(); |
| auto status = hmac->VerifyMac(tag, data); |
| EXPECT_TRUE(status.ok()) << status.ToString(); |
| size_t bits = tag.size() * 8; |
| for (size_t i = 0; i < bits; i++) { |
| std::string modified_tag = tag; |
| modified_tag[i / 8] ^= 1 << (i % 8); |
| EXPECT_FALSE(hmac->VerifyMac(modified_tag, data).ok()) |
| << "tag:" << test::HexEncode(tag) |
| << " modified:" << test::HexEncode(modified_tag); |
| } |
| } |
| |
| TEST_F(HmacBoringSslTest, testTruncation) { |
| std::string key(test::HexDecodeOrDie("000102030405060708090a0b0c0d0e0f")); |
| auto hmac_result = HmacBoringSsl::New(HashType::SHA1, 20, key); |
| EXPECT_TRUE(hmac_result.ok()) << hmac_result.status(); |
| auto hmac = std::move(hmac_result.ValueOrDie()); |
| std::string data = "Some data to test"; |
| std::string tag = hmac->ComputeMac(data).ValueOrDie(); |
| auto status = hmac->VerifyMac(tag, data); |
| EXPECT_TRUE(status.ok()) << status.ToString(); |
| for (size_t i = 0; i < tag.size(); i++) { |
| std::string modified_tag(tag, 0, i); |
| EXPECT_FALSE(hmac->VerifyMac(modified_tag, data).ok()) |
| << "tag:" << test::HexEncode(tag) |
| << " modified:" << test::HexEncode(modified_tag); |
| } |
| } |
| |
| TEST_F(HmacBoringSslTest, testInvalidKeySizes) { |
| size_t tag_size = 16; |
| |
| for (int keysize = 0; keysize < 65; keysize++) { |
| std::string key(keysize, 'x'); |
| auto hmac_result = HmacBoringSsl::New(HashType::SHA1, tag_size, key); |
| if (keysize >= 16) { |
| EXPECT_TRUE(hmac_result.ok()); |
| } else { |
| EXPECT_FALSE(hmac_result.ok()); |
| } |
| } |
| } |
| |
| // TODO(bleichen): Stuff to test |
| // - Generate test vectors and share with Wycheproof. |
| // - Tag size wrong for construction |
| // - Tag size wrong during verification |
| // - Generate invalid tags with 0s in the middle to catch comparison with |
| // strcmp or similar. |
| // - Generate invalid tags with equal diffs (e.g. to catch broken constant |
| // time comparisons. |
| // - wrong size of tag during verification |
| // - Hmac key size must not be 0 (see RFC) |
| // - Generate test vectors with key sizes larger than the block size of the |
| // hash. (HMAC hashes these keys). |
| |
| } // namespace |
| } // namespace subtle |
| } // namespace tink |
| } // namespace crypto |
| |