1 // Copyright 2019 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 //
15 ///////////////////////////////////////////////////////////////////////////////
16
17 #include "tink/subtle/streaming_mac_impl.h"
18
19 #include <memory>
20 #include <string>
21 #include <utility>
22 #include <vector>
23
24 #include "gtest/gtest.h"
25 #include "absl/status/status.h"
26 #include "tink/subtle/random.h"
27 #include "tink/subtle/test_util.h"
28 #include "tink/util/status.h"
29 #include "tink/util/statusor.h"
30 #include "tink/util/test_matchers.h"
31 #include "tink/util/test_util.h"
32
33 namespace crypto {
34 namespace tink {
35 namespace subtle {
36 namespace {
37
38 using ::crypto::tink::test::DummyStatefulMac;
39 using ::crypto::tink::test::IsOk;
40 using ::crypto::tink::test::StatusIs;
41 using ::testing::HasSubstr;
42
43 class DummyStatefulMacFactory : public StatefulMacFactory {
44 public:
45 DummyStatefulMacFactory() = default;
46 ~DummyStatefulMacFactory() override = default;
47
48 // Constructs a StatefulMac using the DummyStatefulMac, which creates
49 // returns a MAC of the header concatenated with the plaintext.
Create() const50 util::StatusOr<std::unique_ptr<StatefulMac>> Create() const override {
51 return std::unique_ptr<StatefulMac>(
52 absl::make_unique<DummyStatefulMac>("streaming mac:"));
53 }
54 };
55
56 // A helper for creating an OutputStreamWithResult<std::string>,
57 // used for test validation for mac computation.
58 std::unique_ptr<OutputStreamWithResult<std::string>>
GetComputeMacOutputStream()59 GetComputeMacOutputStream() {
60 auto mac_factory = std::unique_ptr<StatefulMacFactory>(
61 absl::make_unique<DummyStatefulMacFactory>());
62 auto streaming_mac =
63 absl::make_unique<StreamingMacImpl>(std::move(mac_factory));
64 util::StatusOr<std::unique_ptr<OutputStreamWithResult<std::string>>>
65 stream_status = streaming_mac->NewComputeMacOutputStream();
66 EXPECT_THAT(stream_status, IsOk());
67 return std::move(*stream_status);
68 }
69
70 // A helper for creating an OutputStreamWithResult<util::Status>,
71 // used for test validation for mac verification.
GetVerifyMacOutputStream(std::string expected_mac)72 std::unique_ptr<OutputStreamWithResult<util::Status>> GetVerifyMacOutputStream(
73 std::string expected_mac) {
74 auto mac_factory = std::unique_ptr<StatefulMacFactory>(
75 absl::make_unique<DummyStatefulMacFactory>());
76 auto streaming_mac =
77 absl::make_unique<StreamingMacImpl>(std::move(mac_factory));
78 util::StatusOr<std::unique_ptr<OutputStreamWithResult<util::Status>>>
79 stream_status = streaming_mac->NewVerifyMacOutputStream(expected_mac);
80 EXPECT_THAT(stream_status, IsOk());
81 return std::move(*stream_status);
82 }
83
TEST(StreamingMacImplTest,ComputeEmptyMac)84 TEST(StreamingMacImplTest, ComputeEmptyMac) {
85 std::string expected_mac = "23:0:DummyMac:streaming mac:";
86 auto output_stream = GetComputeMacOutputStream();
87
88 // Close stream and check result
89 auto close_status = output_stream->CloseAndGetResult();
90 EXPECT_THAT(close_status, IsOk());
91 EXPECT_EQ(*close_status, expected_mac);
92 }
93
TEST(StreamingMacImplTest,ComputeSmallMac)94 TEST(StreamingMacImplTest, ComputeSmallMac) {
95 std::string text = "I am a small message";
96 std::string expected_mac =
97 "23:20:DummyMac:streaming mac:I am a small message";
98 auto output_stream = GetComputeMacOutputStream();
99
100 // Write to the ComputeMacOutputStream
101 auto status = test::WriteToStream(output_stream.get(), text, false);
102 EXPECT_THAT(status, IsOk());
103 EXPECT_EQ(output_stream->Position(), text.size());
104
105 // Close stream and check result
106 auto close_status = output_stream->CloseAndGetResult();
107 EXPECT_THAT(close_status, IsOk());
108 EXPECT_EQ(*close_status, expected_mac);
109 }
110
TEST(StreamingMacImplTest,ComputeRandMac)111 TEST(StreamingMacImplTest, ComputeRandMac) {
112 std::vector<int> text_sizes = {0, 10, 100, 1000, 10000, 1000000};
113
114 for (auto text_size : text_sizes) {
115 std::string text = Random::GetRandomBytes(text_size);
116 std::string expected_mac =
117 "23:" + std::to_string(text_size) + ":DummyMac:streaming mac:" + text;
118 auto output_stream = GetComputeMacOutputStream();
119
120 // Write to the ComputeMacOutputStream
121 auto status = test::WriteToStream(output_stream.get(), text, false);
122 EXPECT_THAT(status, IsOk());
123 EXPECT_EQ(output_stream->Position(), text.size());
124
125 // Close stream and check result
126 auto close_status = output_stream->CloseAndGetResult();
127 EXPECT_THAT(close_status, IsOk());
128 EXPECT_EQ(*close_status, expected_mac);
129 }
130 }
131
TEST(StreamingMacImplTest,ComputeCheckStreamPosition)132 TEST(StreamingMacImplTest, ComputeCheckStreamPosition) {
133 std::string text = "I am a small message";
134 auto output_stream = GetComputeMacOutputStream();
135
136 // Check position in first buffer returned by Next();
137 void* buffer;
138 util::StatusOr<int> next_result = output_stream->Next(&buffer);
139 EXPECT_THAT(next_result, IsOk());
140 int buffer_size = *next_result;
141 EXPECT_EQ(buffer_size, output_stream->Position());
142
143 // Check position after calling BackUp
144 output_stream->BackUp(10);
145 EXPECT_EQ(buffer_size - 10, output_stream->Position());
146 }
147
TEST(StreamingMacImplTest,ComputeCloseTwiceError)148 TEST(StreamingMacImplTest, ComputeCloseTwiceError) {
149 auto output_stream = GetComputeMacOutputStream();
150
151 // Close stream
152 auto close_status = output_stream->CloseAndGetResult();
153
154 // Try closing the stream again.
155 auto reclose_status = output_stream->Close();
156 EXPECT_FALSE(reclose_status.ok());
157 EXPECT_EQ(absl::StatusCode::kFailedPrecondition, reclose_status.code());
158 }
159
TEST(StreamingMacImplTest,VerifyEmptyMac)160 TEST(StreamingMacImplTest, VerifyEmptyMac) {
161 std::string expected_mac = "23:0:DummyMac:streaming mac:";
162 auto output_stream = GetVerifyMacOutputStream(expected_mac);
163
164 // Close stream and check result
165 auto close_status = output_stream->CloseAndGetResult();
166 EXPECT_THAT(close_status, IsOk());
167 }
168
TEST(StreamingMacImplTest,VerifySmallMac)169 TEST(StreamingMacImplTest, VerifySmallMac) {
170 std::string text = "I am a small message";
171 std::string expected_mac =
172 "23:20:DummyMac:streaming mac:I am a small message";
173 auto output_stream = GetVerifyMacOutputStream(expected_mac);
174
175 // Write to the VerifyMacOutputStream
176 auto status = test::WriteToStream(output_stream.get(), text, false);
177 EXPECT_THAT(status, IsOk());
178 EXPECT_EQ(output_stream->Position(), text.size());
179
180 // Close stream and check result
181 auto close_status = output_stream->CloseAndGetResult();
182 EXPECT_THAT(close_status, IsOk());
183 }
184
TEST(StreamingMacImplTest,VerifyEmptyMacFail)185 TEST(StreamingMacImplTest, VerifyEmptyMacFail) {
186 std::string expected_mac = "23:1:DummyMac:streaming mac:";
187 auto output_stream = GetVerifyMacOutputStream(expected_mac);
188
189 // Close stream and check result
190 EXPECT_THAT(
191 output_stream->CloseAndGetResult(),
192 StatusIs(absl::StatusCode::kInvalidArgument, HasSubstr("Incorrect MAC")));
193 }
194
TEST(StreamingMacImplTest,VerifySmallMacFail)195 TEST(StreamingMacImplTest, VerifySmallMacFail) {
196 std::string text = "I am a small message";
197 std::string expected_mac = "23:20:DummyMac:streaming mac:I am wrong message";
198 auto output_stream = GetVerifyMacOutputStream(expected_mac);
199
200 // Write to the VerifyMacOutputStream
201 auto status = test::WriteToStream(output_stream.get(), text, false);
202 EXPECT_THAT(status, IsOk());
203 EXPECT_EQ(output_stream->Position(), text.size());
204
205 // Close stream and check result
206 EXPECT_THAT(
207 output_stream->CloseAndGetResult(),
208 StatusIs(absl::StatusCode::kInvalidArgument, HasSubstr("Invalid MAC")));
209 }
210
TEST(StreamingMacImplTest,VerifyRandMac)211 TEST(StreamingMacImplTest, VerifyRandMac) {
212 std::vector<int> text_sizes = {0, 10, 100, 1000, 10000, 1000000};
213
214 for (auto text_size : text_sizes) {
215 std::string text = Random::GetRandomBytes(text_size);
216 std::string expected_mac =
217 "23:" + std::to_string(text_size) + ":DummyMac:streaming mac:" + text;
218 auto output_stream = GetVerifyMacOutputStream(expected_mac);
219
220 // Write to the VerifyMacOutputStream
221 auto status = test::WriteToStream(output_stream.get(), text, false);
222 EXPECT_THAT(status, IsOk());
223 EXPECT_EQ(output_stream->Position(), text.size());
224
225 // Close stream and check result
226 auto close_status = output_stream->CloseAndGetResult();
227 EXPECT_THAT(close_status, IsOk());
228 }
229 }
230
TEST(StreamingMacImplTest,VerifyCheckStreamPosition)231 TEST(StreamingMacImplTest, VerifyCheckStreamPosition) {
232 std::string text = "I am a small message";
233 std::string expected_mac = "23:1:DummyMac:streaming mac:";
234 auto output_stream = GetVerifyMacOutputStream(expected_mac);
235
236 // Check position in first buffer returned by Next();
237 void* buffer;
238 util::StatusOr<int> next_result = output_stream->Next(&buffer);
239 EXPECT_THAT(next_result, IsOk());
240 int buffer_size = *next_result;
241 EXPECT_EQ(buffer_size, output_stream->Position());
242
243 // Check position after calling BackUp
244 output_stream->BackUp(10);
245 EXPECT_EQ(buffer_size - 10, output_stream->Position());
246 }
247
TEST(StreamingMacImplTest,VerifyCloseTwiceError)248 TEST(StreamingMacImplTest, VerifyCloseTwiceError) {
249 std::string expected_mac = "23:0:DummyMac:streaming mac:";
250 auto output_stream = GetVerifyMacOutputStream(expected_mac);
251
252 // Close stream
253 auto close_status = output_stream->CloseAndGetResult();
254
255 // Try closing the stream again.
256 auto reclose_status = output_stream->Close();
257 EXPECT_FALSE(reclose_status.ok());
258 EXPECT_EQ(absl::StatusCode::kFailedPrecondition, reclose_status.code());
259 }
260
261 } // namespace
262 } // namespace subtle
263 } // namespace tink
264 } // namespace crypto
265