| // Copyright 2022 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/mac/internal/chunked_mac_wrapper.h" |
| |
| #include <memory> |
| #include <string> |
| #include <utility> |
| |
| #include "gmock/gmock.h" |
| #include "gtest/gtest.h" |
| #include "absl/strings/str_cat.h" |
| #include "tink/chunked_mac.h" |
| #include "tink/mac/internal/chunked_mac_impl.h" |
| #include "tink/subtle/mac/stateful_mac.h" |
| #include "tink/util/status.h" |
| #include "tink/util/statusor.h" |
| #include "tink/util/test_matchers.h" |
| #include "proto/tink.pb.h" |
| |
| namespace crypto { |
| namespace tink { |
| namespace internal { |
| namespace { |
| |
| using ::crypto::tink::test::IsOk; |
| using ::crypto::tink::test::IsOkAndHolds; |
| using ::crypto::tink::test::StatusIs; |
| using ::google::crypto::tink::KeysetInfo; |
| using ::google::crypto::tink::KeyStatusType; |
| using ::google::crypto::tink::OutputPrefixType; |
| using ::testing::Values; |
| |
| class FakeStatefulMac : public subtle::StatefulMac { |
| public: |
| explicit FakeStatefulMac(absl::string_view name) : name_(name) {} |
| |
| util::Status Update(absl::string_view data) override { |
| absl::StrAppend(&buffer_, data); |
| return util::OkStatus(); |
| } |
| |
| util::StatusOr<std::string> Finalize() override { |
| return absl::StrCat(name_, buffer_); |
| } |
| |
| private: |
| const std::string name_; |
| std::string buffer_ = ""; |
| }; |
| |
| class FakeStatefulMacFactory : public subtle::StatefulMacFactory { |
| public: |
| explicit FakeStatefulMacFactory(absl::string_view name) : name_(name) {} |
| |
| util::StatusOr<std::unique_ptr<subtle::StatefulMac>> Create() const override { |
| return std::unique_ptr<subtle::StatefulMac>( |
| absl::make_unique<FakeStatefulMac>(name_)); |
| } |
| |
| private: |
| std::string name_; |
| }; |
| |
| TEST(ChunkedMacWrapperTest, WrapNullptr) { |
| EXPECT_THAT(ChunkedMacWrapper().Wrap(nullptr).status(), |
| StatusIs(absl::StatusCode::kInternal)); |
| } |
| |
| TEST(ChunkedMacWrapperTest, WrapEmpty) { |
| std::unique_ptr<PrimitiveSet<ChunkedMac>> mac_set( |
| new PrimitiveSet<ChunkedMac>()); |
| EXPECT_THAT(ChunkedMacWrapper().Wrap(std::move(mac_set)).status(), |
| StatusIs(absl::StatusCode::kInvalidArgument)); |
| } |
| |
| std::unique_ptr<ChunkedMac> CreateFakeChunkedMac(absl::string_view name) { |
| return absl::make_unique<ChunkedMacImpl>( |
| absl::make_unique<FakeStatefulMacFactory>(name)); |
| } |
| |
| util::Status AddPrimitiveToSet(uint32_t key_id, bool set_primary, |
| OutputPrefixType output_prefix_type, |
| std::unique_ptr<ChunkedMac> mac, |
| KeysetInfo& keyset_info, |
| PrimitiveSet<ChunkedMac>& mac_set) { |
| int index = keyset_info.key_info_size(); |
| KeysetInfo::KeyInfo* key_info = keyset_info.add_key_info(); |
| key_info->set_output_prefix_type(output_prefix_type); |
| key_info->set_key_id(key_id); |
| key_info->set_status(KeyStatusType::ENABLED); |
| |
| auto entry = |
| mac_set.AddPrimitive(std::move(mac), keyset_info.key_info(index)); |
| if (!entry.ok()) { |
| return entry.status(); |
| } |
| if (set_primary) { |
| util::Status set_primary_status = mac_set.set_primary(*entry); |
| if (!set_primary_status.ok()) { |
| return set_primary_status; |
| } |
| } |
| return util::OkStatus(); |
| } |
| |
| TEST(ChunkedMacWrapperTest, ComputeMac) { |
| KeysetInfo keyset_info; |
| auto mac_set = absl::make_unique<PrimitiveSet<ChunkedMac>>(); |
| |
| // Add primitives to the primitive set. |
| ASSERT_THAT( |
| AddPrimitiveToSet( |
| /*key_id=*/0x12d66f, /*set_primary=*/false, OutputPrefixType::TINK, |
| CreateFakeChunkedMac("chunkedmac0:"), keyset_info, *mac_set), |
| IsOk()); |
| ASSERT_THAT( |
| AddPrimitiveToSet( |
| /*key_id=*/0xb1539, /*set_primary=*/false, OutputPrefixType::LEGACY, |
| CreateFakeChunkedMac("chunkedmac1:"), keyset_info, *mac_set), |
| IsOk()); |
| ASSERT_THAT( |
| AddPrimitiveToSet( |
| /*key_id=*/0x6e12af, /*set_primary=*/true, OutputPrefixType::TINK, |
| CreateFakeChunkedMac("chunkedmac2:"), keyset_info, *mac_set), |
| IsOk()); |
| |
| // Wrap primitive set into a ChunkedMac. |
| util::StatusOr<std::unique_ptr<crypto::tink::ChunkedMac>> chunked_mac = |
| ChunkedMacWrapper().Wrap(std::move(mac_set)); |
| ASSERT_THAT(chunked_mac.status(), IsOk()); |
| |
| util::StatusOr<std::unique_ptr<ChunkedMacComputation>> computation = |
| (*chunked_mac)->CreateComputation(); |
| EXPECT_THAT(computation.status(), IsOk()); |
| EXPECT_THAT((*computation)->Update("inputdata"), IsOk()); |
| util::StatusOr<std::string> tag = (*computation)->ComputeMac(); |
| const std::string output_prefix = std::string("\x01\x00\x6e\x12\xaf", 5); |
| const std::string raw_tag = "chunkedmac2:inputdata"; |
| EXPECT_THAT(tag, IsOkAndHolds(absl::StrCat(output_prefix, raw_tag))); |
| } |
| |
| TEST(ChunkedMacWrapperTest, VerifyMacWithUniquePrefix) { |
| KeysetInfo keyset_info; |
| auto mac_set = absl::make_unique<PrimitiveSet<ChunkedMac>>(); |
| |
| // Add primitives to primitive set. |
| ASSERT_THAT( |
| AddPrimitiveToSet( |
| /*key_id=*/0x12d66f, /*set_primary=*/false, OutputPrefixType::TINK, |
| CreateFakeChunkedMac("chunkedmac0:"), keyset_info, *mac_set), |
| IsOk()); |
| ASSERT_THAT( |
| AddPrimitiveToSet( |
| /*key_id=*/0xb1539, /*set_primary=*/false, OutputPrefixType::LEGACY, |
| CreateFakeChunkedMac("chunkedmac1:"), keyset_info, *mac_set), |
| IsOk()); |
| ASSERT_THAT( |
| AddPrimitiveToSet( |
| /*key_id=*/0x6e12af, /*set_primary=*/true, OutputPrefixType::TINK, |
| CreateFakeChunkedMac("chunkedmac2:"), keyset_info, *mac_set), |
| IsOk()); |
| |
| // Wrap primitive set into a ChunkedMac. |
| util::StatusOr<std::unique_ptr<crypto::tink::ChunkedMac>> |
| chunked_mac = ChunkedMacWrapper().Wrap(std::move(mac_set)); |
| ASSERT_THAT(chunked_mac.status(), IsOk()); |
| |
| const std::string output_prefix = std::string("\x01\x00\x6e\x12\xaf", 5); |
| const std::string raw_tag = "chunkedmac2:inputdata"; |
| util::StatusOr<std::unique_ptr<ChunkedMacVerification>> verification = |
| (*chunked_mac) |
| ->CreateVerification(absl::StrCat(output_prefix, raw_tag)); |
| EXPECT_THAT(verification.status(), IsOk()); |
| EXPECT_THAT((*verification)->Update("inputdata"), IsOk()); |
| EXPECT_THAT((*verification)->VerifyMac(), IsOk()); |
| } |
| |
| TEST(ChunkedMacWrapperTest, VerifyMacWithDuplicatePrefix) { |
| KeysetInfo keyset_info; |
| auto mac_set = absl::make_unique<PrimitiveSet<ChunkedMac>>(); |
| |
| // Add primitives to primitive set. |
| ASSERT_THAT( |
| AddPrimitiveToSet( |
| /*key_id=*/0x12d66f, /*set_primary=*/false, OutputPrefixType::LEGACY, |
| CreateFakeChunkedMac("chunkedmac0:"), keyset_info, *mac_set), |
| IsOk()); |
| ASSERT_THAT( |
| AddPrimitiveToSet( |
| /*key_id=*/0x6e12af, /*set_primary=*/false, OutputPrefixType::TINK, |
| CreateFakeChunkedMac("chunkedmac1:"), keyset_info, *mac_set), |
| IsOk()); |
| ASSERT_THAT( |
| AddPrimitiveToSet( |
| /*key_id=*/0x6e12af, /*set_primary=*/true, OutputPrefixType::TINK, |
| CreateFakeChunkedMac("chunkedmac2:"), keyset_info, *mac_set), |
| IsOk()); |
| |
| // Wrap primitive set into a ChunkedMac. |
| util::StatusOr<std::unique_ptr<crypto::tink::ChunkedMac>> |
| chunked_mac = ChunkedMacWrapper().Wrap(std::move(mac_set)); |
| ASSERT_THAT(chunked_mac.status(), IsOk()); |
| |
| const std::string output_prefix = std::string("\x01\x00\x6e\x12\xaf", 5); |
| const std::string raw_tag = "chunkedmac1:inputdata"; |
| util::StatusOr<std::unique_ptr<ChunkedMacVerification>> verification = |
| (*chunked_mac) |
| ->CreateVerification(absl::StrCat(output_prefix, raw_tag)); |
| EXPECT_THAT(verification.status(), IsOk()); |
| EXPECT_THAT((*verification)->Update("inputdata"), IsOk()); |
| EXPECT_THAT((*verification)->VerifyMac(), IsOk()); |
| } |
| |
| TEST(ChunkedMacWrapperTest, VerifyMacWithRawTagStartingWithKeyId) { |
| KeysetInfo keyset_info; |
| auto mac_set = absl::make_unique<PrimitiveSet<ChunkedMac>>(); |
| |
| const std::string key_id0 = std::string("\x01\x00\x12\xd6\x6f", 5); |
| |
| // Add primitives to primitive set. |
| ASSERT_THAT( |
| AddPrimitiveToSet( |
| /*key_id=*/0x12d66f, /*set_primary=*/false, OutputPrefixType::TINK, |
| CreateFakeChunkedMac("chunkedmac0:"), keyset_info, *mac_set), |
| IsOk()); |
| ASSERT_THAT( |
| AddPrimitiveToSet( |
| /*key_id=*/0x6e12af, /*set_primary=*/true, OutputPrefixType::RAW, |
| CreateFakeChunkedMac(/*name=*/absl::StrCat(key_id0, ":chunkedmac1:")), |
| keyset_info, *mac_set), |
| IsOk()); |
| |
| // Wrap primitive set into a ChunkedMac. |
| util::StatusOr<std::unique_ptr<crypto::tink::ChunkedMac>> |
| chunked_mac = ChunkedMacWrapper().Wrap(std::move(mac_set)); |
| ASSERT_THAT(chunked_mac.status(), IsOk()); |
| |
| const std::string raw_tag = absl::StrCat(key_id0, ":chunkedmac1:inputdata"); |
| util::StatusOr<std::unique_ptr<ChunkedMacVerification>> verification = |
| (*chunked_mac)->CreateVerification(raw_tag); |
| EXPECT_THAT(verification.status(), IsOk()); |
| EXPECT_THAT((*verification)->Update("inputdata"), IsOk()); |
| EXPECT_THAT((*verification)->VerifyMac(), IsOk()); |
| } |
| |
| class ChunkedMacWrapperOutputPrefixTest |
| : public testing::TestWithParam<OutputPrefixType> {}; |
| |
| INSTANTIATE_TEST_SUITE_P( |
| ChunkedMacWrapperOutputPrefixTestSuite, ChunkedMacWrapperOutputPrefixTest, |
| Values(OutputPrefixType::LEGACY, OutputPrefixType::RAW, |
| OutputPrefixType::CRUNCHY, OutputPrefixType::TINK)); |
| |
| TEST_P(ChunkedMacWrapperOutputPrefixTest, ComputeVerifyMac) { |
| OutputPrefixType output_prefix_type = GetParam(); |
| |
| KeysetInfo keyset_info; |
| auto mac_set = absl::make_unique<PrimitiveSet<ChunkedMac>>(); |
| |
| // Add primitives to primitive set. |
| ASSERT_THAT( |
| AddPrimitiveToSet( |
| /*key_id=*/0x12d66f, /*set_primary=*/true, output_prefix_type, |
| CreateFakeChunkedMac("chunkedmac:"), keyset_info, *mac_set), |
| IsOk()); |
| |
| // Wrap primitive set into a ChunkedMac. |
| util::StatusOr<std::unique_ptr<crypto::tink::ChunkedMac>> chunked_mac = |
| ChunkedMacWrapper().Wrap(std::move(mac_set)); |
| ASSERT_THAT(chunked_mac.status(), IsOk()); |
| |
| // Compute MAC via wrapper. |
| util::StatusOr<std::unique_ptr<ChunkedMacComputation>> mac_computation = |
| (*chunked_mac)->CreateComputation(); |
| ASSERT_THAT(mac_computation.status(), IsOk()); |
| ASSERT_THAT((*mac_computation)->Update("inputdata"), IsOk()); |
| util::StatusOr<std::string> tag = (*mac_computation)->ComputeMac(); |
| ASSERT_THAT(tag.status(), IsOk()); |
| |
| // Verify MAC via wrapper. |
| util::StatusOr<std::unique_ptr<ChunkedMacVerification>> mac_verification = |
| (*chunked_mac)->CreateVerification(*tag); |
| ASSERT_THAT(mac_verification.status(), IsOk()); |
| ASSERT_THAT((*mac_verification)->Update("inputdata"), IsOk()); |
| ASSERT_THAT((*mac_verification)->VerifyMac(), IsOk()); |
| } |
| |
| } // namespace |
| } // namespace internal |
| } // namespace tink |
| } // namespace crypto |