// Copyright 2019 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/streaming_mac_impl.h" #include #include #include #include #include "gtest/gtest.h" #include "absl/status/status.h" #include "tink/subtle/random.h" #include "tink/subtle/test_util.h" #include "tink/util/status.h" #include "tink/util/statusor.h" #include "tink/util/test_matchers.h" #include "tink/util/test_util.h" namespace crypto { namespace tink { namespace subtle { namespace { using ::crypto::tink::test::DummyStatefulMac; using ::crypto::tink::test::IsOk; using ::crypto::tink::test::StatusIs; using ::testing::HasSubstr; class DummyStatefulMacFactory : public StatefulMacFactory { public: DummyStatefulMacFactory() = default; ~DummyStatefulMacFactory() override = default; // Constructs a StatefulMac using the DummyStatefulMac, which creates // returns a MAC of the header concatenated with the plaintext. util::StatusOr> Create() const override { return std::unique_ptr( absl::make_unique("streaming mac:")); } }; // A helper for creating an OutputStreamWithResult, // used for test validation for mac computation. std::unique_ptr> GetComputeMacOutputStream() { auto mac_factory = std::unique_ptr( absl::make_unique()); auto streaming_mac = absl::make_unique(std::move(mac_factory)); util::StatusOr>> stream_status = streaming_mac->NewComputeMacOutputStream(); EXPECT_THAT(stream_status, IsOk()); return std::move(*stream_status); } // A helper for creating an OutputStreamWithResult, // used for test validation for mac verification. std::unique_ptr> GetVerifyMacOutputStream( std::string expected_mac) { auto mac_factory = std::unique_ptr( absl::make_unique()); auto streaming_mac = absl::make_unique(std::move(mac_factory)); util::StatusOr>> stream_status = streaming_mac->NewVerifyMacOutputStream(expected_mac); EXPECT_THAT(stream_status, IsOk()); return std::move(*stream_status); } TEST(StreamingMacImplTest, ComputeEmptyMac) { std::string expected_mac = "23:0:DummyMac:streaming mac:"; auto output_stream = GetComputeMacOutputStream(); // Close stream and check result auto close_status = output_stream->CloseAndGetResult(); EXPECT_THAT(close_status, IsOk()); EXPECT_EQ(*close_status, expected_mac); } TEST(StreamingMacImplTest, ComputeSmallMac) { std::string text = "I am a small message"; std::string expected_mac = "23:20:DummyMac:streaming mac:I am a small message"; auto output_stream = GetComputeMacOutputStream(); // Write to the ComputeMacOutputStream auto status = test::WriteToStream(output_stream.get(), text, false); EXPECT_THAT(status, IsOk()); EXPECT_EQ(output_stream->Position(), text.size()); // Close stream and check result auto close_status = output_stream->CloseAndGetResult(); EXPECT_THAT(close_status, IsOk()); EXPECT_EQ(*close_status, expected_mac); } TEST(StreamingMacImplTest, ComputeRandMac) { std::vector text_sizes = {0, 10, 100, 1000, 10000, 1000000}; for (auto text_size : text_sizes) { std::string text = Random::GetRandomBytes(text_size); std::string expected_mac = "23:" + std::to_string(text_size) + ":DummyMac:streaming mac:" + text; auto output_stream = GetComputeMacOutputStream(); // Write to the ComputeMacOutputStream auto status = test::WriteToStream(output_stream.get(), text, false); EXPECT_THAT(status, IsOk()); EXPECT_EQ(output_stream->Position(), text.size()); // Close stream and check result auto close_status = output_stream->CloseAndGetResult(); EXPECT_THAT(close_status, IsOk()); EXPECT_EQ(*close_status, expected_mac); } } TEST(StreamingMacImplTest, ComputeCheckStreamPosition) { std::string text = "I am a small message"; auto output_stream = GetComputeMacOutputStream(); // Check position in first buffer returned by Next(); void* buffer; util::StatusOr next_result = output_stream->Next(&buffer); EXPECT_THAT(next_result, IsOk()); int buffer_size = *next_result; EXPECT_EQ(buffer_size, output_stream->Position()); // Check position after calling BackUp output_stream->BackUp(10); EXPECT_EQ(buffer_size - 10, output_stream->Position()); } TEST(StreamingMacImplTest, ComputeCloseTwiceError) { auto output_stream = GetComputeMacOutputStream(); // Close stream auto close_status = output_stream->CloseAndGetResult(); // Try closing the stream again. auto reclose_status = output_stream->Close(); EXPECT_FALSE(reclose_status.ok()); EXPECT_EQ(absl::StatusCode::kFailedPrecondition, reclose_status.code()); } TEST(StreamingMacImplTest, VerifyEmptyMac) { std::string expected_mac = "23:0:DummyMac:streaming mac:"; auto output_stream = GetVerifyMacOutputStream(expected_mac); // Close stream and check result auto close_status = output_stream->CloseAndGetResult(); EXPECT_THAT(close_status, IsOk()); } TEST(StreamingMacImplTest, VerifySmallMac) { std::string text = "I am a small message"; std::string expected_mac = "23:20:DummyMac:streaming mac:I am a small message"; auto output_stream = GetVerifyMacOutputStream(expected_mac); // Write to the VerifyMacOutputStream auto status = test::WriteToStream(output_stream.get(), text, false); EXPECT_THAT(status, IsOk()); EXPECT_EQ(output_stream->Position(), text.size()); // Close stream and check result auto close_status = output_stream->CloseAndGetResult(); EXPECT_THAT(close_status, IsOk()); } TEST(StreamingMacImplTest, VerifyEmptyMacFail) { std::string expected_mac = "23:1:DummyMac:streaming mac:"; auto output_stream = GetVerifyMacOutputStream(expected_mac); // Close stream and check result EXPECT_THAT( output_stream->CloseAndGetResult(), StatusIs(absl::StatusCode::kInvalidArgument, HasSubstr("Incorrect MAC"))); } TEST(StreamingMacImplTest, VerifySmallMacFail) { std::string text = "I am a small message"; std::string expected_mac = "23:20:DummyMac:streaming mac:I am wrong message"; auto output_stream = GetVerifyMacOutputStream(expected_mac); // Write to the VerifyMacOutputStream auto status = test::WriteToStream(output_stream.get(), text, false); EXPECT_THAT(status, IsOk()); EXPECT_EQ(output_stream->Position(), text.size()); // Close stream and check result EXPECT_THAT( output_stream->CloseAndGetResult(), StatusIs(absl::StatusCode::kInvalidArgument, HasSubstr("Invalid MAC"))); } TEST(StreamingMacImplTest, VerifyRandMac) { std::vector text_sizes = {0, 10, 100, 1000, 10000, 1000000}; for (auto text_size : text_sizes) { std::string text = Random::GetRandomBytes(text_size); std::string expected_mac = "23:" + std::to_string(text_size) + ":DummyMac:streaming mac:" + text; auto output_stream = GetVerifyMacOutputStream(expected_mac); // Write to the VerifyMacOutputStream auto status = test::WriteToStream(output_stream.get(), text, false); EXPECT_THAT(status, IsOk()); EXPECT_EQ(output_stream->Position(), text.size()); // Close stream and check result auto close_status = output_stream->CloseAndGetResult(); EXPECT_THAT(close_status, IsOk()); } } TEST(StreamingMacImplTest, VerifyCheckStreamPosition) { std::string text = "I am a small message"; std::string expected_mac = "23:1:DummyMac:streaming mac:"; auto output_stream = GetVerifyMacOutputStream(expected_mac); // Check position in first buffer returned by Next(); void* buffer; util::StatusOr next_result = output_stream->Next(&buffer); EXPECT_THAT(next_result, IsOk()); int buffer_size = *next_result; EXPECT_EQ(buffer_size, output_stream->Position()); // Check position after calling BackUp output_stream->BackUp(10); EXPECT_EQ(buffer_size - 10, output_stream->Position()); } TEST(StreamingMacImplTest, VerifyCloseTwiceError) { std::string expected_mac = "23:0:DummyMac:streaming mac:"; auto output_stream = GetVerifyMacOutputStream(expected_mac); // Close stream auto close_status = output_stream->CloseAndGetResult(); // Try closing the stream again. auto reclose_status = output_stream->Close(); EXPECT_FALSE(reclose_status.ok()); EXPECT_EQ(absl::StatusCode::kFailedPrecondition, reclose_status.code()); } } // namespace } // namespace subtle } // namespace tink } // namespace crypto