1 // Copyright 2017 Google Inc.
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/mac/mac_wrapper.h"
18
19 #include <memory>
20 #include <string>
21 #include <utility>
22
23 #include "gtest/gtest.h"
24 #include "absl/strings/str_cat.h"
25 #include "tink/crypto_format.h"
26 #include "tink/internal/registry_impl.h"
27 #include "tink/mac.h"
28 #include "tink/mac/failing_mac.h"
29 #include "tink/monitoring/monitoring.h"
30 #include "tink/monitoring/monitoring_client_mocks.h"
31 #include "tink/primitive_set.h"
32 #include "tink/util/status.h"
33 #include "tink/util/test_matchers.h"
34 #include "tink/util/test_util.h"
35 #include "proto/tink.pb.h"
36
37 namespace crypto {
38 namespace tink {
39 namespace {
40
41 using ::crypto::tink::test::DummyMac;
42 using ::crypto::tink::test::IsOk;
43 using ::crypto::tink::test::IsOkAndHolds;
44 using ::crypto::tink::test::StatusIs;
45 using ::google::crypto::tink::KeysetInfo;
46 using ::google::crypto::tink::KeyStatusType;
47 using ::google::crypto::tink::OutputPrefixType;
48 using ::testing::_;
49 using ::testing::ByMove;
50 using ::testing::IsNull;
51 using ::testing::NiceMock;
52 using ::testing::Not;
53 using ::testing::NotNull;
54 using ::testing::Return;
55 using ::testing::Test;
56
TEST(MacWrapperTest,WrapNullptr)57 TEST(MacWrapperTest, WrapNullptr) {
58 auto mac_result = MacWrapper().Wrap(nullptr);
59 EXPECT_FALSE(mac_result.ok());
60 EXPECT_EQ(absl::StatusCode::kInternal, mac_result.status().code());
61 EXPECT_PRED_FORMAT2(testing::IsSubstring, "non-NULL",
62 std::string(mac_result.status().message()));
63 }
64
TEST(MacWrapperTest,WrapEmpty)65 TEST(MacWrapperTest, WrapEmpty) {
66 std::unique_ptr<PrimitiveSet<Mac>> mac_set(new PrimitiveSet<Mac>());
67 auto mac_result = MacWrapper().Wrap(std::move(mac_set));
68 EXPECT_FALSE(mac_result.ok());
69 EXPECT_EQ(absl::StatusCode::kInvalidArgument, mac_result.status().code());
70 EXPECT_PRED_FORMAT2(testing::IsSubstring, "no primary",
71 std::string(mac_result.status().message()));
72 }
73
TEST(MacWrapperTest,Basic)74 TEST(MacWrapperTest, Basic) {
75 KeysetInfo::KeyInfo* key_info;
76 KeysetInfo keyset_info;
77
78 uint32_t key_id_0 = 1234543;
79 key_info = keyset_info.add_key_info();
80 key_info->set_output_prefix_type(OutputPrefixType::TINK);
81 key_info->set_key_id(key_id_0);
82 key_info->set_status(KeyStatusType::ENABLED);
83
84 uint32_t key_id_1 = 726329;
85 key_info = keyset_info.add_key_info();
86 key_info->set_output_prefix_type(OutputPrefixType::LEGACY);
87 key_info->set_key_id(key_id_1);
88 key_info->set_status(KeyStatusType::ENABLED);
89
90 uint32_t key_id_2 = 7213743;
91 key_info = keyset_info.add_key_info();
92 key_info->set_output_prefix_type(OutputPrefixType::TINK);
93 key_info->set_key_id(key_id_2);
94 key_info->set_status(KeyStatusType::ENABLED);
95
96 std::string mac_name_0 = "mac0";
97 std::string mac_name_1 = "mac1";
98 std::string mac_name_2 = "mac2";
99 std::unique_ptr<PrimitiveSet<Mac>> mac_set(new PrimitiveSet<Mac>());
100 auto entry_result = mac_set->AddPrimitive(
101 absl::make_unique<DummyMac>(mac_name_0), keyset_info.key_info(0));
102 ASSERT_TRUE(entry_result.ok());
103 entry_result = mac_set->AddPrimitive(absl::make_unique<DummyMac>(mac_name_1),
104 keyset_info.key_info(1));
105 ASSERT_TRUE(entry_result.ok());
106 entry_result = mac_set->AddPrimitive(absl::make_unique<DummyMac>(mac_name_2),
107 keyset_info.key_info(2));
108 ASSERT_TRUE(entry_result.ok());
109 // The last key is the primary.
110 ASSERT_THAT(mac_set->set_primary(entry_result.value()), IsOk());
111
112 // Wrap mac_set and test the resulting Mac.
113 auto mac_result = MacWrapper().Wrap(std::move(mac_set));
114 EXPECT_TRUE(mac_result.ok()) << mac_result.status();
115 std::unique_ptr<Mac> mac = std::move(mac_result.value());
116 std::string data = "some_data_for_mac";
117
118 auto compute_mac_result = mac->ComputeMac(data);
119 EXPECT_TRUE(compute_mac_result.ok()) << compute_mac_result.status();
120 std::string mac_value = compute_mac_result.value();
121 EXPECT_PRED_FORMAT2(testing::IsSubstring, mac_name_2, mac_value);
122
123 util::Status status = mac->VerifyMac(mac_value, data);
124 EXPECT_TRUE(status.ok()) << status;
125
126 status = mac->VerifyMac("some bad mac", data);
127 EXPECT_FALSE(status.ok());
128 EXPECT_EQ(absl::StatusCode::kInvalidArgument, status.code());
129 EXPECT_PRED_FORMAT2(testing::IsSubstring, "verification failed",
130 std::string(status.message()));
131 }
132
TEST(MacWrapperTest,testLegacyAuthentication)133 TEST(MacWrapperTest, testLegacyAuthentication) {
134 // Prepare a set for the wrapper.
135 KeysetInfo::KeyInfo key_info;
136 uint32_t key_id = 1234543;
137 key_info.set_output_prefix_type(OutputPrefixType::LEGACY);
138 key_info.set_key_id(key_id);
139 key_info.set_status(KeyStatusType::ENABLED);
140 std::string mac_name = "SomeLegacyMac";
141
142 std::unique_ptr<PrimitiveSet<Mac>> mac_set(new PrimitiveSet<Mac>());
143 std::unique_ptr<Mac> mac(new DummyMac(mac_name));
144 auto entry_result = mac_set->AddPrimitive(std::move(mac), key_info);
145 ASSERT_TRUE(entry_result.ok());
146 ASSERT_THAT(mac_set->set_primary(entry_result.value()), IsOk());
147
148 // Wrap mac_set and test the resulting Mac.
149 auto mac_result = MacWrapper().Wrap(std::move(mac_set));
150 EXPECT_TRUE(mac_result.ok()) << mac_result.status();
151 mac = std::move(mac_result.value());
152 std::string data = "Some data to authenticate";
153
154 // Compute and verify MAC via wrapper.
155 auto compute_mac_result = mac->ComputeMac(data);
156 EXPECT_TRUE(compute_mac_result.ok()) << compute_mac_result.status();
157 std::string mac_value = compute_mac_result.value();
158 EXPECT_PRED_FORMAT2(testing::IsSubstring, mac_name, mac_value);
159 auto status = mac->VerifyMac(mac_value, data);
160 EXPECT_TRUE(status.ok()) << status;
161
162 // Try verifying on raw Mac-primitive using original data.
163 std::unique_ptr<Mac> raw_mac(new DummyMac(mac_name)); // same as in wrapper
164 std::string raw_mac_value = mac_value.substr(CryptoFormat::kNonRawPrefixSize);
165 status = raw_mac->VerifyMac(raw_mac_value, data);
166 EXPECT_FALSE(status.ok());
167 EXPECT_EQ(absl::StatusCode::kInvalidArgument, status.code());
168
169 // Verify on raw Mac-primitive using legacy-formatted data.
170 std::string legacy_data = data;
171 legacy_data.append(1, CryptoFormat::kLegacyStartByte);
172 status = raw_mac->VerifyMac(raw_mac_value, legacy_data);
173 EXPECT_TRUE(status.ok()) << status;
174 }
175
176 // Produces a mac which starts in the same way as a legacy non-raw signature.
177 class TryBreakLegacyMac : public Mac {
178 public:
ComputeMac(absl::string_view data) const179 crypto::tink::util::StatusOr<std::string> ComputeMac(
180 absl::string_view data) const override {
181 return absl::StrCat(std::string("\x00", 1), "\xff\xff\xff\xff", data);
182 }
183
VerifyMac(absl::string_view mac,absl::string_view data) const184 crypto::tink::util::Status VerifyMac(absl::string_view mac,
185 absl::string_view data) const override {
186 if (mac != ComputeMac(data).value()) {
187 return absl::InvalidArgumentError("Wrong mac");
188 }
189 return util::OkStatus();
190 }
191 };
192
193 // Checks that a raw tag can be verified after a legacy tag is verified with
194 // the same output prefix. (To prevent regression of b/173013224).
TEST(MacWrapperTest,VerifyRawAfterLegacy)195 TEST(MacWrapperTest, VerifyRawAfterLegacy) {
196 std::unique_ptr<PrimitiveSet<Mac>> mac_set(new PrimitiveSet<Mac>());
197
198 KeysetInfo::KeyInfo key_info_0;
199 key_info_0.set_output_prefix_type(OutputPrefixType::RAW);
200 key_info_0.set_key_id(1234);
201 key_info_0.set_status(KeyStatusType::ENABLED);
202 ASSERT_THAT(
203 mac_set->AddPrimitive(absl::make_unique<TryBreakLegacyMac>(), key_info_0)
204 .status(),
205 IsOk());
206
207 KeysetInfo::KeyInfo key_info_1;
208 key_info_1.set_output_prefix_type(OutputPrefixType::LEGACY);
209 key_info_1.set_key_id(0xffffffff);
210 key_info_1.set_status(KeyStatusType::ENABLED);
211
212 auto entry1 =
213 mac_set->AddPrimitive(absl::make_unique<DummyMac>(""), key_info_1);
214 ASSERT_THAT(entry1, IsOk());
215 ASSERT_THAT(mac_set->set_primary(entry1.value()), IsOk());
216
217 // Wrap mac_set and test the resulting Mac.
218 auto wrapped_mac = MacWrapper().Wrap(std::move(mac_set));
219 EXPECT_THAT(wrapped_mac, IsOk());
220
221 std::string data = "some data";
222 std::string mac_tag = TryBreakLegacyMac().ComputeMac(data).value();
223 EXPECT_THAT(wrapped_mac.value()->VerifyMac(mac_tag, data), IsOk());
224 }
225
PopulateKeyInfo(uint32_t key_id,OutputPrefixType out_prefix_type,KeyStatusType status)226 KeysetInfo::KeyInfo PopulateKeyInfo(uint32_t key_id,
227 OutputPrefixType out_prefix_type,
228 KeyStatusType status) {
229 KeysetInfo::KeyInfo key_info;
230 key_info.set_output_prefix_type(out_prefix_type);
231 key_info.set_key_id(key_id);
232 key_info.set_status(status);
233 return key_info;
234 }
235
236 // Creates a test keyset info object.
CreateTestKeysetInfo()237 KeysetInfo CreateTestKeysetInfo() {
238 KeysetInfo keyset_info;
239 *keyset_info.add_key_info() =
240 PopulateKeyInfo(/*key_id=*/1234543, OutputPrefixType::TINK,
241 /*status=*/KeyStatusType::ENABLED);
242 *keyset_info.add_key_info() =
243 PopulateKeyInfo(/*key_id=*/726329, OutputPrefixType::LEGACY,
244 /*status=*/KeyStatusType::ENABLED);
245 *keyset_info.add_key_info() =
246 PopulateKeyInfo(/*key_id=*/7213743, OutputPrefixType::TINK,
247 /*status=*/KeyStatusType::ENABLED);
248 return keyset_info;
249 }
250
251 // Tests for the monitoring behavior.
252 class MacSetWrapperWithMonitoringTest : public Test {
253 protected:
254 // Perform some common initialization: reset the global registry, set expected
255 // calls for the mock monitoring factory and the returned clients.
SetUp()256 void SetUp() override {
257 Registry::Reset();
258
259 // Setup mocks for catching Monitoring calls.
260 auto monitoring_client_factory =
261 absl::make_unique<MockMonitoringClientFactory>();
262 auto compute_monitoring_client =
263 absl::make_unique<NiceMock<MockMonitoringClient>>();
264 compute_monitoring_client_ = compute_monitoring_client.get();
265 auto verify_monitoring_client =
266 absl::make_unique<NiceMock<MockMonitoringClient>>();
267 verify_monitoring_client_ = verify_monitoring_client.get();
268
269 // Monitoring tests expect that the client factory will create the
270 // corresponding MockMonitoringClients.
271 EXPECT_CALL(*monitoring_client_factory, New(_))
272 .WillOnce(
273 Return(ByMove(util::StatusOr<std::unique_ptr<MonitoringClient>>(
274 std::move(compute_monitoring_client)))))
275 .WillOnce(
276 Return(ByMove(util::StatusOr<std::unique_ptr<MonitoringClient>>(
277 std::move(verify_monitoring_client)))));
278
279 ASSERT_THAT(internal::RegistryImpl::GlobalInstance()
280 .RegisterMonitoringClientFactory(
281 std::move(monitoring_client_factory)),
282 IsOk());
283 ASSERT_THAT(
284 internal::RegistryImpl::GlobalInstance().GetMonitoringClientFactory(),
285 Not(IsNull()));
286 }
287
288 // Cleanup the registry to avoid mock leaks.
~MacSetWrapperWithMonitoringTest()289 ~MacSetWrapperWithMonitoringTest() override { Registry::Reset(); }
290
291 MockMonitoringClient* compute_monitoring_client_;
292 MockMonitoringClient* verify_monitoring_client_;
293 };
294
295 // Tests that successful ComputeMac operations are logged.
TEST_F(MacSetWrapperWithMonitoringTest,WrapKeysetWithMonitoringComputeSuccess)296 TEST_F(MacSetWrapperWithMonitoringTest,
297 WrapKeysetWithMonitoringComputeSuccess) {
298 // Create a primitive set and fill it with some entries
299 KeysetInfo keyset_info = CreateTestKeysetInfo();
300 const absl::flat_hash_map<std::string, std::string> annotations = {
301 {"key1", "value1"}, {"key2", "value2"}, {"key3", "value3"}};
302 auto mac_primitive_set = absl::make_unique<PrimitiveSet<Mac>>(annotations);
303 ASSERT_THAT(mac_primitive_set
304 ->AddPrimitive(absl::make_unique<DummyMac>("mac0"),
305 keyset_info.key_info(0))
306 .status(),
307 IsOk());
308 ASSERT_THAT(mac_primitive_set
309 ->AddPrimitive(absl::make_unique<DummyMac>("mac1"),
310 keyset_info.key_info(1))
311 .status(),
312 IsOk());
313 // Set the last as primary.
314 util::StatusOr<PrimitiveSet<Mac>::Entry<Mac>*> last =
315 mac_primitive_set->AddPrimitive(absl::make_unique<DummyMac>("mac2"),
316 keyset_info.key_info(2));
317 ASSERT_THAT(last.status(), IsOk());
318 ASSERT_THAT(mac_primitive_set->set_primary(*last), IsOk());
319 // Record the ID of the primary key.
320 const uint32_t primary_key_id = keyset_info.key_info(2).key_id();
321
322 // Create a MAC and compute an authentication tag
323 util::StatusOr<std::unique_ptr<Mac>> mac =
324 MacWrapper().Wrap(std::move(mac_primitive_set));
325 ASSERT_THAT(mac, IsOkAndHolds(NotNull()));
326
327 constexpr absl::string_view message = "This is some message!";
328
329 // Check that calling ComputeMac triggers a Log() call.
330 EXPECT_CALL(*compute_monitoring_client_, Log(primary_key_id, message.size()));
331 EXPECT_THAT((*mac)->ComputeMac(message).status(), IsOk());
332 }
333
334 // Test that successful VerifyMac operations are logged.
TEST_F(MacSetWrapperWithMonitoringTest,WrapKeysetWithMonitoringVerifySuccess)335 TEST_F(MacSetWrapperWithMonitoringTest,
336 WrapKeysetWithMonitoringVerifySuccess) {
337 // Create a primitive set and fill it with some entries
338 KeysetInfo keyset_info = CreateTestKeysetInfo();
339 const absl::flat_hash_map<std::string, std::string> annotations = {
340 {"key1", "value1"}, {"key2", "value2"}, {"key3", "value3"}};
341 auto mac_primitive_set = absl::make_unique<PrimitiveSet<Mac>>(annotations);
342 ASSERT_THAT(mac_primitive_set
343 ->AddPrimitive(absl::make_unique<DummyMac>("mac0"),
344 keyset_info.key_info(0))
345 .status(),
346 IsOk());
347 ASSERT_THAT(mac_primitive_set
348 ->AddPrimitive(absl::make_unique<DummyMac>("mac1"),
349 keyset_info.key_info(1))
350 .status(),
351 IsOk());
352 // Set the last as primary.
353 util::StatusOr<PrimitiveSet<Mac>::Entry<Mac>*> last =
354 mac_primitive_set->AddPrimitive(absl::make_unique<DummyMac>("mac2"),
355 keyset_info.key_info(2));
356 ASSERT_THAT(last.status(), IsOk());
357 ASSERT_THAT(mac_primitive_set->set_primary(*last), IsOk());
358 // Record the ID of the primary key.
359 const uint32_t primary_key_id = keyset_info.key_info(2).key_id();
360
361 // Create a MAC, compute a Mac and verify it.
362 util::StatusOr<std::unique_ptr<Mac>> mac =
363 MacWrapper().Wrap(std::move(mac_primitive_set));
364 ASSERT_THAT(mac, IsOkAndHolds(NotNull()));
365
366 constexpr absl::string_view message = "This is some message!";
367
368 // Check that calling VerifyMac triggers a Log() call.
369 util::StatusOr<std::string> tag = (*mac)->ComputeMac(message);
370 EXPECT_THAT(tag.status(), IsOk());
371
372 // In the log expect the size of the message without the non-raw prefix.
373 EXPECT_CALL(*verify_monitoring_client_, Log(primary_key_id, message.size()));
374 EXPECT_THAT((*mac)->VerifyMac(*tag, message), IsOk());
375 }
376
TEST_F(MacSetWrapperWithMonitoringTest,WrapKeysetWithMonitoringComputeFailures)377 TEST_F(MacSetWrapperWithMonitoringTest,
378 WrapKeysetWithMonitoringComputeFailures) {
379 // Create a primitive set and fill it with some entries.
380 KeysetInfo keyset_info = CreateTestKeysetInfo();
381 const absl::flat_hash_map<std::string, std::string> annotations = {
382 {"key1", "value1"}, {"key2", "value2"}, {"key3", "value3"}};
383 auto mac_primitive_set = absl::make_unique<PrimitiveSet<Mac>>(annotations);
384 ASSERT_THAT(mac_primitive_set
385 ->AddPrimitive(CreateAlwaysFailingMac("mac "),
386 keyset_info.key_info(0))
387 .status(),
388 IsOk());
389 ASSERT_THAT(mac_primitive_set
390 ->AddPrimitive(CreateAlwaysFailingMac("mac "),
391 keyset_info.key_info(1))
392 .status(),
393 IsOk());
394 // Set the last as primary.
395 util::StatusOr<PrimitiveSet<Mac>::Entry<Mac>*> last =
396 mac_primitive_set->AddPrimitive(CreateAlwaysFailingMac("mac "),
397 keyset_info.key_info(2));
398 ASSERT_THAT(last.status(), IsOk());
399 ASSERT_THAT(mac_primitive_set->set_primary(*last), IsOk());
400
401 // Create a MAC and compute a tag.
402 util::StatusOr<std::unique_ptr<Mac>> mac =
403 MacWrapper().Wrap(std::move(mac_primitive_set));
404 ASSERT_THAT(mac, IsOkAndHolds(NotNull()));
405
406 constexpr absl::string_view message = "This is some message!";
407
408 // Check that calling ComputeMac triggers a LogFailure() call.
409 EXPECT_CALL(*compute_monitoring_client_, LogFailure());
410 EXPECT_THAT((*mac)->ComputeMac(message).status(),
411 StatusIs(absl::StatusCode::kInternal));
412 }
413
414 // Test that monitoring logs verify failures correctly.
TEST_F(MacSetWrapperWithMonitoringTest,WrapKeysetWithMonitoringVerifyFailures)415 TEST_F(MacSetWrapperWithMonitoringTest,
416 WrapKeysetWithMonitoringVerifyFailures) {
417 // Create a primitive set and fill it with some entries.
418 KeysetInfo keyset_info = CreateTestKeysetInfo();
419 const absl::flat_hash_map<std::string, std::string> annotations = {
420 {"key1", "value1"}, {"key2", "value2"}, {"key3", "value3"}};
421 auto mac_primitive_set = absl::make_unique<PrimitiveSet<Mac>>(annotations);
422 ASSERT_THAT(mac_primitive_set
423 ->AddPrimitive(CreateAlwaysFailingMac("mac "),
424 keyset_info.key_info(0))
425 .status(),
426 IsOk());
427 ASSERT_THAT(mac_primitive_set
428 ->AddPrimitive(CreateAlwaysFailingMac("mac "),
429 keyset_info.key_info(1))
430 .status(),
431 IsOk());
432 // Set the last as primary.
433 util::StatusOr<PrimitiveSet<Mac>::Entry<Mac>*> last =
434 mac_primitive_set->AddPrimitive(CreateAlwaysFailingMac("mac "),
435 keyset_info.key_info(2));
436 ASSERT_THAT(last.status(), IsOk());
437 ASSERT_THAT(mac_primitive_set->set_primary(*last), IsOk());
438
439 // Create a MAC and verify a tag.
440 util::StatusOr<std::unique_ptr<Mac>> mac =
441 MacWrapper().Wrap(std::move(mac_primitive_set));
442 ASSERT_THAT(mac, IsOkAndHolds(NotNull()));
443
444 constexpr absl::string_view message = "This is some message!";
445 constexpr absl::string_view tag = "some invalid tag!";
446
447 // Check that calling VerifyMac triggers a LogFailure() call.
448 EXPECT_CALL(*verify_monitoring_client_, LogFailure());
449 EXPECT_THAT((*mac)->VerifyMac(tag, message),
450 StatusIs(absl::StatusCode::kInvalidArgument));
451 }
452
453 } // namespace
454 } // namespace tink
455 } // namespace crypto
456