| // 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_cmac_boringssl.h" |
| |
| #include <cstddef> |
| #include <memory> |
| #include <string> |
| #include <vector> |
| |
| #include "gmock/gmock.h" |
| #include "gtest/gtest.h" |
| #include "absl/memory/memory.h" |
| #include "absl/status/status.h" |
| #include "absl/strings/escaping.h" |
| #include "absl/strings/string_view.h" |
| #include "tink/subtle/common_enums.h" |
| #include "tink/subtle/mac/stateful_mac.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" |
| |
| namespace crypto { |
| namespace tink { |
| namespace subtle { |
| namespace { |
| |
| constexpr size_t kTagSize = 16; |
| constexpr size_t kSmallTagSize = 10; |
| |
| constexpr absl::string_view kKeyHex = "000102030405060708090a0b0c0d0e0f"; |
| constexpr absl::string_view kData = "Some data to test."; |
| constexpr absl::string_view kCmacOnEmptyInputRegularTagSizeHex = |
| "97dd6e5a882cbd564c39ae7d1c5a31aa"; |
| constexpr absl::string_view kCmacOnEmptyInputSmallTagSizeHex = |
| "97dd6e5a882cbd564c39"; |
| constexpr absl::string_view kCmacOnDataRegularTagSizeHex = |
| "c856e183e8dee9bb99402d54c34f3222"; |
| constexpr absl::string_view kCmacOnDataSmallTagSizeHex = "c856e183e8dee9bb9940"; |
| |
| using ::crypto::tink::test::IsOk; |
| using ::crypto::tink::test::IsOkAndHolds; |
| using ::testing::Not; |
| using ::testing::TestWithParam; |
| using ::testing::ValuesIn; |
| |
| TEST(StatefulCmacBoringSslTest, CmacEmptyInputRegularTagSize) { |
| util::SecretData key = |
| util::SecretDataFromStringView(absl::HexStringToBytes(kKeyHex)); |
| util::StatusOr<std::unique_ptr<StatefulMac>> cmac = |
| StatefulCmacBoringSsl::New(kTagSize, key); |
| ASSERT_THAT(cmac, IsOk()); |
| EXPECT_THAT( |
| (*cmac)->Finalize(), |
| IsOkAndHolds(absl::HexStringToBytes(kCmacOnEmptyInputRegularTagSizeHex))); |
| } |
| |
| TEST(StatefulCmacBoringSslTest, CmacEmptyInputSmallTag) { |
| util::SecretData key = |
| util::SecretDataFromStringView(absl::HexStringToBytes(kKeyHex)); |
| util::StatusOr<std::unique_ptr<StatefulMac>> cmac = |
| StatefulCmacBoringSsl::New(kSmallTagSize, key); |
| ASSERT_THAT(cmac, IsOk()); |
| EXPECT_THAT( |
| (*cmac)->Finalize(), |
| IsOkAndHolds(absl::HexStringToBytes(kCmacOnEmptyInputSmallTagSizeHex))); |
| } |
| |
| TEST(StatefulCmacBoringSslTest, CmacSomeDataRegularTagSize) { |
| util::SecretData key = |
| util::SecretDataFromStringView(absl::HexStringToBytes(kKeyHex)); |
| util::StatusOr<std::unique_ptr<StatefulMac>> cmac = |
| StatefulCmacBoringSsl::New(kTagSize, key); |
| ASSERT_THAT(cmac, IsOk()); |
| EXPECT_THAT((*cmac)->Update(kData), IsOk()); |
| EXPECT_THAT( |
| (*cmac)->Finalize(), |
| IsOkAndHolds(absl::HexStringToBytes(kCmacOnDataRegularTagSizeHex))); |
| } |
| |
| TEST(StatefulCmacBoringSslTest, CmacSomeDataSmallTag) { |
| util::SecretData key = |
| util::SecretDataFromStringView(absl::HexStringToBytes(kKeyHex)); |
| util::StatusOr<std::unique_ptr<StatefulMac>> cmac = |
| StatefulCmacBoringSsl::New(kSmallTagSize, key); |
| ASSERT_THAT(cmac, IsOk()); |
| EXPECT_THAT((*cmac)->Update(kData), IsOk()); |
| EXPECT_THAT((*cmac)->Finalize(), |
| IsOkAndHolds(absl::HexStringToBytes(kCmacOnDataSmallTagSizeHex))); |
| } |
| |
| TEST(StatefulCmacBoringSslTest, |
| CmacMultipleUpdatesSameAsOneForWholeInputRegularTagSize) { |
| util::SecretData key = |
| util::SecretDataFromStringView(absl::HexStringToBytes(kKeyHex)); |
| util::StatusOr<std::unique_ptr<StatefulMac>> cmac = |
| StatefulCmacBoringSsl::New(kTagSize, key); |
| ASSERT_THAT(cmac, IsOk()); |
| for (const std::string &token : {"Some ", "data ", "to ", "test."}) { |
| EXPECT_THAT((*cmac)->Update(token), IsOk()); |
| } |
| EXPECT_THAT( |
| (*cmac)->Finalize(), |
| IsOkAndHolds(absl::HexStringToBytes(kCmacOnDataRegularTagSizeHex))); |
| } |
| |
| TEST(StatefulCmacBoringSslTest, |
| CmacMultipleUpdatesSameAsOneForWholeInputSmallTagSize) { |
| util::SecretData key = |
| util::SecretDataFromStringView(absl::HexStringToBytes(kKeyHex)); |
| util::StatusOr<std::unique_ptr<StatefulMac>> cmac = |
| StatefulCmacBoringSsl::New(kSmallTagSize, key); |
| ASSERT_THAT(cmac, IsOk()); |
| for (const std::string &token : {"Some ", "data ", "to ", "test."}) { |
| EXPECT_THAT((*cmac)->Update(token), IsOk()); |
| } |
| EXPECT_THAT((*cmac)->Finalize(), |
| IsOkAndHolds(absl::HexStringToBytes(kCmacOnDataSmallTagSizeHex))); |
| } |
| |
| TEST(StatefulCmacFactoryTest, FactoryGeneratesValidInstances) { |
| auto factory = absl::make_unique<StatefulCmacBoringSslFactory>( |
| kTagSize, |
| util::SecretDataFromStringView(absl::HexStringToBytes(kKeyHex))); |
| util::StatusOr<std::unique_ptr<StatefulMac>> cmac = factory->Create(); |
| ASSERT_THAT(cmac, IsOk()); |
| EXPECT_THAT((*cmac)->Update(kData), IsOk()); |
| EXPECT_THAT( |
| (*cmac)->Finalize(), |
| IsOkAndHolds(absl::HexStringToBytes(kCmacOnDataRegularTagSizeHex))); |
| } |
| |
| struct StatefulCmacTestVector { |
| std::string key; |
| std::string msg; |
| std::string tag; |
| std::string id; |
| std::string expected_result; |
| }; |
| |
| // Reads the Wycheproof test vectors for AES-CMAC. |
| std::vector<StatefulCmacTestVector> GetWycheproofCmakeTestVectors() { |
| std::unique_ptr<rapidjson::Document> root = |
| WycheproofUtil::ReadTestVectors("aes_cmac_test.json"); |
| std::vector<StatefulCmacTestVector> test_vectors; |
| for (const rapidjson::Value &test_group : (*root)["testGroups"].GetArray()) { |
| // Ignore test vectors of invalid key sizes; valid sizes are {16, 32} bytes. |
| int key_size_bits = test_group["keySize"].GetInt(); |
| if (key_size_bits != 128 && key_size_bits != 256) { |
| continue; |
| } |
| for (const rapidjson::Value &test : test_group["tests"].GetArray()) { |
| test_vectors.push_back({ |
| /*key=*/WycheproofUtil::GetBytes(test["key"]), |
| /*msg=*/WycheproofUtil::GetBytes(test["msg"]), |
| /*tag=*/WycheproofUtil::GetBytes(test["tag"]), |
| /*id=*/absl::StrCat(test["tcId"].GetInt()), |
| /*expected_result=*/test["result"].GetString(), |
| }); |
| } |
| } |
| return test_vectors; |
| } |
| |
| using StatefulCmacBoringSslWycheproofTest = |
| TestWithParam<StatefulCmacTestVector>; |
| |
| TEST_P(StatefulCmacBoringSslWycheproofTest, WycheproofTest) { |
| StatefulCmacTestVector test_vector = GetParam(); |
| |
| util::SecretData key = |
| util::SecretDataFromStringView(absl::HexStringToBytes(kKeyHex)); |
| util::StatusOr<std::unique_ptr<StatefulMac>> cmac = |
| StatefulCmacBoringSsl::New( |
| test_vector.tag.length(), |
| util::SecretDataFromStringView(test_vector.key)); |
| ASSERT_THAT(cmac, IsOk()); |
| EXPECT_THAT((*cmac)->Update(test_vector.msg), IsOk()); |
| |
| if (test_vector.expected_result == "invalid") { |
| EXPECT_THAT((*cmac)->Finalize(), Not(IsOkAndHolds(test_vector.tag))); |
| } else { |
| EXPECT_THAT((*cmac)->Finalize(), IsOkAndHolds(test_vector.tag)); |
| } |
| } |
| |
| INSTANTIATE_TEST_SUITE_P(StatefulCmacBoringSslWycheproofTest, |
| StatefulCmacBoringSslWycheproofTest, |
| ValuesIn(GetWycheproofCmakeTestVectors())); |
| |
| } // namespace |
| } // namespace subtle |
| } // namespace tink |
| } // namespace crypto |