// 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 #include #include #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 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> Create() const override { return std::unique_ptr( absl::make_unique(name_)); } private: std::string name_; }; TEST(ChunkedMacWrapperTest, WrapNullptr) { EXPECT_THAT(ChunkedMacWrapper().Wrap(nullptr).status(), StatusIs(absl::StatusCode::kInternal)); } TEST(ChunkedMacWrapperTest, WrapEmpty) { std::unique_ptr> mac_set( new PrimitiveSet()); EXPECT_THAT(ChunkedMacWrapper().Wrap(std::move(mac_set)).status(), StatusIs(absl::StatusCode::kInvalidArgument)); } std::unique_ptr CreateFakeChunkedMac(absl::string_view name) { return absl::make_unique( absl::make_unique(name)); } util::Status AddPrimitiveToSet(uint32_t key_id, bool set_primary, OutputPrefixType output_prefix_type, std::unique_ptr mac, KeysetInfo& keyset_info, PrimitiveSet& 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>(); // 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> chunked_mac = ChunkedMacWrapper().Wrap(std::move(mac_set)); ASSERT_THAT(chunked_mac.status(), IsOk()); util::StatusOr> computation = (*chunked_mac)->CreateComputation(); EXPECT_THAT(computation.status(), IsOk()); EXPECT_THAT((*computation)->Update("inputdata"), IsOk()); util::StatusOr 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>(); // 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> 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> 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>(); // 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> 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> 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>(); 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> 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> 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 {}; 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>(); // 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> chunked_mac = ChunkedMacWrapper().Wrap(std::move(mac_set)); ASSERT_THAT(chunked_mac.status(), IsOk()); // Compute MAC via wrapper. util::StatusOr> mac_computation = (*chunked_mac)->CreateComputation(); ASSERT_THAT(mac_computation.status(), IsOk()); ASSERT_THAT((*mac_computation)->Update("inputdata"), IsOk()); util::StatusOr tag = (*mac_computation)->ComputeMac(); ASSERT_THAT(tag.status(), IsOk()); // Verify MAC via wrapper. util::StatusOr> 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