| // 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 <utility> |
| |
| #include "gtest/gtest.h" |
| #include "absl/status/status.h" |
| #include "absl/strings/escaping.h" |
| #include "tink/internal/fips_utils.h" |
| #include "tink/mac.h" |
| #include "tink/subtle/common_enums.h" |
| #include "tink/util/secret_data.h" |
| #include "tink/util/status.h" |
| #include "tink/util/statusor.h" |
| #include "tink/util/test_matchers.h" |
| |
| namespace crypto { |
| namespace tink { |
| namespace subtle { |
| namespace { |
| |
| using ::crypto::tink::test::StatusIs; |
| |
| 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) { |
| util::SecretData key = |
| util::SecretDataFromStringView(absl::HexStringToBytes(key_hex)); |
| std::string tag = absl::HexStringToBytes(tag_hex); |
| std::string data = absl::HexStringToBytes(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.value()); |
| auto result = hmac->VerifyMac(tag, data); |
| return result.ok(); |
| } |
| }; |
| |
| TEST_F(HmacBoringSslTest, testBasic) { |
| if (internal::IsFipsModeEnabled() && !internal::IsFipsEnabledInSsl()) { |
| GTEST_SKIP() |
| << "Test should not run in FIPS mode when BoringCrypto is unavailable."; |
| } |
| |
| util::SecretData key = util::SecretDataFromStringView( |
| absl::HexStringToBytes("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.value()); |
| { // Test with some example data. |
| std::string data = "Some data to test."; |
| auto res = hmac->ComputeMac(data); |
| EXPECT_TRUE(res.ok()) << res.status(); |
| std::string tag = res.value(); |
| EXPECT_EQ(tag_size, tag.size()); |
| EXPECT_EQ(tag, absl::HexStringToBytes("9ccdca5b7fffb690df396e4ac49b9cd4")); |
| auto status = hmac->VerifyMac(tag, data); |
| EXPECT_TRUE(status.ok()) |
| << "tag:" << absl::BytesToHexString(tag) << " status:" << status; |
| } |
| { // Test with empty example data. |
| absl::string_view data; |
| auto res = hmac->ComputeMac(data); |
| EXPECT_TRUE(res.ok()) << res.status(); |
| std::string tag = res.value(); |
| EXPECT_EQ(tag_size, tag.size()); |
| EXPECT_EQ(tag, absl::HexStringToBytes("5433122f77bcf8a4d9b874b4149823ef")); |
| auto status = hmac->VerifyMac(tag, data); |
| EXPECT_TRUE(status.ok()) |
| << "tag:" << absl::BytesToHexString(tag) << " status:" << status; |
| } |
| } |
| |
| TEST_F(HmacBoringSslTest, testModification) { |
| if (internal::IsFipsModeEnabled() && !internal::IsFipsEnabledInSsl()) { |
| GTEST_SKIP() |
| << "Test should not run in FIPS mode when BoringCrypto is unavailable."; |
| } |
| |
| util::SecretData key = util::SecretDataFromStringView( |
| absl::HexStringToBytes("000102030405060708090a0b0c0d0e0f")); |
| auto hmac_result = HmacBoringSsl::New(HashType::SHA1, 16, key); |
| EXPECT_TRUE(hmac_result.ok()) << hmac_result.status(); |
| auto hmac = std::move(hmac_result.value()); |
| std::string data = "Some data to test"; |
| std::string tag = hmac->ComputeMac(data).value(); |
| auto status = hmac->VerifyMac(tag, data); |
| EXPECT_TRUE(status.ok()) << status; |
| 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:" << absl::BytesToHexString(tag) |
| << " modified:" << absl::BytesToHexString(modified_tag); |
| } |
| } |
| |
| TEST_F(HmacBoringSslTest, testTruncation) { |
| if (internal::IsFipsModeEnabled() && !internal::IsFipsEnabledInSsl()) { |
| GTEST_SKIP() |
| << "Test should not run in FIPS mode when BoringCrypto is unavailable."; |
| } |
| |
| util::SecretData key = util::SecretDataFromStringView( |
| absl::HexStringToBytes("000102030405060708090a0b0c0d0e0f")); |
| auto hmac_result = HmacBoringSsl::New(HashType::SHA1, 20, key); |
| EXPECT_TRUE(hmac_result.ok()) << hmac_result.status(); |
| auto hmac = std::move(hmac_result.value()); |
| std::string data = "Some data to test"; |
| std::string tag = hmac->ComputeMac(data).value(); |
| auto status = hmac->VerifyMac(tag, data); |
| EXPECT_TRUE(status.ok()) << status; |
| 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:" << absl::BytesToHexString(tag) |
| << " modified:" << absl::BytesToHexString(modified_tag); |
| } |
| } |
| |
| TEST_F(HmacBoringSslTest, testInvalidKeySizes) { |
| if (internal::IsFipsModeEnabled() && !internal::IsFipsEnabledInSsl()) { |
| GTEST_SKIP() |
| << "Test should not run in FIPS mode when BoringCrypto is unavailable."; |
| } |
| |
| size_t tag_size = 16; |
| |
| for (int keysize = 0; keysize < 65; keysize++) { |
| util::SecretData 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()); |
| } |
| } |
| } |
| |
| TEST_F(HmacBoringSslTest, TestFipsFailWithoutBoringCrypto) { |
| if (!internal::IsFipsModeEnabled() || internal::IsFipsEnabledInSsl()) { |
| GTEST_SKIP() |
| << "Test assumes kOnlyUseFips but BoringCrypto is unavailable."; |
| } |
| |
| util::SecretData key128 = util::SecretDataFromStringView( |
| absl::HexStringToBytes("000102030405060708090a0b0c0d0e0f")); |
| util::SecretData key256 = |
| util::SecretDataFromStringView(absl::HexStringToBytes( |
| "000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f")); |
| |
| EXPECT_THAT(subtle::HmacBoringSsl::New(HashType::SHA1, 16, key128).status(), |
| StatusIs(absl::StatusCode::kInternal)); |
| EXPECT_THAT(subtle::HmacBoringSsl::New(HashType::SHA224, 16, key128).status(), |
| StatusIs(absl::StatusCode::kInternal)); |
| EXPECT_THAT(subtle::HmacBoringSsl::New(HashType::SHA256, 16, key128).status(), |
| StatusIs(absl::StatusCode::kInternal)); |
| EXPECT_THAT(subtle::HmacBoringSsl::New(HashType::SHA384, 16, key128).status(), |
| StatusIs(absl::StatusCode::kInternal)); |
| EXPECT_THAT(subtle::HmacBoringSsl::New(HashType::SHA512, 16, key128).status(), |
| StatusIs(absl::StatusCode::kInternal)); |
| } |
| // 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 |
| |