• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/primitive_set.h"
18 
19 #include <memory>
20 #include <string>
21 #include <thread>  // NOLINT(build/c++11)
22 #include <utility>
23 #include <vector>
24 
25 #include "gmock/gmock.h"
26 #include "gtest/gtest.h"
27 #include "tink/cleartext_keyset_handle.h"
28 #include "tink/crypto_format.h"
29 #include "tink/keyderivation/keyset_deriver.h"
30 #include "tink/mac.h"
31 #include "tink/util/test_matchers.h"
32 #include "tink/util/test_util.h"
33 #include "proto/tink.pb.h"
34 
35 using ::crypto::tink::test::DummyMac;
36 using ::crypto::tink::test::IsOk;
37 using ::google::crypto::tink::Keyset;
38 using ::google::crypto::tink::KeysetInfo;
39 using ::google::crypto::tink::KeyStatusType;
40 using ::google::crypto::tink::OutputPrefixType;
41 using ::testing::Eq;
42 using ::testing::SizeIs;
43 using ::testing::UnorderedElementsAreArray;
44 
45 namespace crypto {
46 namespace tink {
47 namespace {
48 
49 class PrimitiveSetTest : public ::testing::Test {};
50 
add_primitives(PrimitiveSet<Mac> * primitive_set,int key_id_offset,int primitives_count)51 void add_primitives(PrimitiveSet<Mac>* primitive_set, int key_id_offset,
52                     int primitives_count) {
53   for (int i = 0; i < primitives_count; i++) {
54     int key_id = key_id_offset + i;
55     KeysetInfo::KeyInfo key_info;
56     key_info.set_output_prefix_type(OutputPrefixType::TINK);
57     key_info.set_key_id(key_id);
58     key_info.set_status(KeyStatusType::ENABLED);
59     std::unique_ptr<Mac> mac(new DummyMac("dummy MAC"));
60     auto add_result = primitive_set->AddPrimitive(std::move(mac), key_info);
61     EXPECT_TRUE(add_result.ok()) << add_result.status();
62   }
63 }
64 
add_primitives(PrimitiveSet<Mac>::Builder * primitive_set_builder,int key_id_offset,int primitives_count)65 void add_primitives(PrimitiveSet<Mac>::Builder* primitive_set_builder,
66                     int key_id_offset, int primitives_count) {
67   for (int i = 0; i < primitives_count; i++) {
68     int key_id = key_id_offset + i;
69     KeysetInfo::KeyInfo key_info;
70     key_info.set_output_prefix_type(OutputPrefixType::TINK);
71     key_info.set_key_id(key_id);
72     key_info.set_status(KeyStatusType::ENABLED);
73     std::unique_ptr<Mac> mac(new DummyMac("dummy MAC"));
74     primitive_set_builder->AddPrimitive(std::move(mac), key_info);
75   }
76 }
77 
access_primitives(PrimitiveSet<Mac> * primitive_set,int key_id_offset,int primitives_count)78 void access_primitives(PrimitiveSet<Mac>* primitive_set, int key_id_offset,
79                        int primitives_count) {
80   for (int i = 0; i < primitives_count; i++) {
81     int key_id = key_id_offset + i;
82     KeysetInfo::KeyInfo key_info;
83     key_info.set_output_prefix_type(OutputPrefixType::TINK);
84     key_info.set_key_id(key_id);
85     key_info.set_status(KeyStatusType::ENABLED);
86     std::string prefix = CryptoFormat::GetOutputPrefix(key_info).value();
87     auto get_result = primitive_set->get_primitives(prefix);
88     EXPECT_TRUE(get_result.ok()) << get_result.status();
89     EXPECT_GE(get_result.value()->size(), 1);
90   }
91 }
92 
TEST_F(PrimitiveSetTest,ConcurrentOperations)93 TEST_F(PrimitiveSetTest, ConcurrentOperations) {
94   PrimitiveSet<Mac>::Builder mac_set_builder;
95   int offset_a = 100;
96   int offset_b = 150;
97   int count = 100;
98 
99   // Add some primitives.
100   // See go/totw/133 on why we use a lambda here.
101   std::thread add_primitives_a(
102       [&]() { add_primitives(&mac_set_builder, offset_a, count); });
103   std::thread add_primitives_b(
104       [&]() { add_primitives(&mac_set_builder, offset_b, count); });
105   add_primitives_a.join();
106   add_primitives_b.join();
107 
108   auto mac_set_result = std::move(mac_set_builder).Build();
109   ASSERT_TRUE(mac_set_result.ok()) << mac_set_result.status();
110   PrimitiveSet<Mac> mac_set = std::move(mac_set_result.value());
111 
112   // Access primitives.
113   std::thread access_primitives_a(access_primitives, &mac_set, offset_a, count);
114   std::thread access_primitives_b(access_primitives, &mac_set, offset_b, count);
115   access_primitives_a.join();
116   access_primitives_b.join();
117 
118   // Verify the common key ids added by both threads.
119   for (int key_id = offset_a; key_id < offset_b + count; key_id++) {
120     KeysetInfo::KeyInfo key_info;
121     key_info.set_output_prefix_type(OutputPrefixType::TINK);
122     key_info.set_key_id(key_id);
123     key_info.set_status(KeyStatusType::ENABLED);
124     std::string prefix = CryptoFormat::GetOutputPrefix(key_info).value();
125     auto get_result = mac_set.get_primitives(prefix);
126     EXPECT_TRUE(get_result.ok()) << get_result.status();
127     auto macs = get_result.value();
128     if (key_id >= offset_b && key_id < offset_a + count) {
129       EXPECT_EQ(2, macs->size());  // overlapping key_id range
130     } else {
131       EXPECT_EQ(1, macs->size());
132     }
133   }
134 }
135 
TEST_F(PrimitiveSetTest,Basic)136 TEST_F(PrimitiveSetTest, Basic) {
137   std::string mac_name_1 = "MAC#1";
138   std::unique_ptr<Mac> mac_1(new DummyMac(mac_name_1));
139   std::string mac_name_2 = "MAC#2";
140   std::unique_ptr<Mac> mac_2(new DummyMac(mac_name_2));
141   std::string mac_name_3 = "MAC#3";
142   std::unique_ptr<Mac> mac_3(new DummyMac(mac_name_3));
143   std::string mac_name_4 = "MAC#3";
144   std::unique_ptr<Mac> mac_4(new DummyMac(mac_name_4));
145   std::string mac_name_5 = "MAC#3";
146   std::unique_ptr<Mac> mac_5(new DummyMac(mac_name_5));
147   std::string mac_name_6 = "MAC#3";
148   std::unique_ptr<Mac> mac_6(new DummyMac(mac_name_6));
149 
150   uint32_t key_id_1 = 1234543;
151   KeysetInfo::KeyInfo key_1;
152   key_1.set_output_prefix_type(OutputPrefixType::TINK);
153   key_1.set_key_id(key_id_1);
154   key_1.set_status(KeyStatusType::ENABLED);
155 
156   uint32_t key_id_2 = 7213743;
157   KeysetInfo::KeyInfo key_2;
158   key_2.set_output_prefix_type(OutputPrefixType::LEGACY);
159   key_2.set_key_id(key_id_2);
160   key_2.set_status(KeyStatusType::ENABLED);
161 
162   uint32_t key_id_3 = key_id_2;  // same id as key_2
163   KeysetInfo::KeyInfo key_3;
164   key_3.set_output_prefix_type(OutputPrefixType::TINK);
165   key_3.set_key_id(key_id_3);
166   key_3.set_status(KeyStatusType::ENABLED);
167 
168   uint32_t key_id_4 = 947327;
169   KeysetInfo::KeyInfo key_4;
170   key_4.set_output_prefix_type(OutputPrefixType::RAW);
171   key_4.set_key_id(key_id_4);
172   key_4.set_status(KeyStatusType::ENABLED);
173 
174   uint32_t key_id_5 = 529472;
175   KeysetInfo::KeyInfo key_5;
176   key_5.set_output_prefix_type(OutputPrefixType::RAW);
177   key_5.set_key_id(key_id_5);
178   key_5.set_status(KeyStatusType::ENABLED);
179 
180   uint32_t key_id_6 = key_id_1;  // same id as key_1
181   KeysetInfo::KeyInfo key_6;
182   key_6.set_output_prefix_type(OutputPrefixType::TINK);
183   key_6.set_key_id(key_id_6);
184   key_6.set_status(KeyStatusType::ENABLED);
185 
186   PrimitiveSet<Mac>::Builder primitive_set_builder;
187 
188   // Add all the primitives.
189   auto primitive_set_result = PrimitiveSet<Mac>::Builder{}
190                                   .AddPrimitive(std::move(mac_1), key_1)
191                                   .AddPrimitive(std::move(mac_2), key_2)
192                                   .AddPrimaryPrimitive(std::move(mac_3), key_3)
193                                   .AddPrimitive(std::move(mac_4), key_4)
194                                   .AddPrimitive(std::move(mac_5), key_5)
195                                   .AddPrimitive(std::move(mac_6), key_6)
196                                   .Build();
197 
198   ASSERT_TRUE(primitive_set_result.ok()) << primitive_set_result.status();
199   PrimitiveSet<Mac> primitive_set = std::move(primitive_set_result.value());
200 
201   std::string data = "some data";
202 
203   {  // Check the primary.
204     auto primary = primitive_set.get_primary();
205     EXPECT_FALSE(primary == nullptr);
206     EXPECT_EQ(KeyStatusType::ENABLED, primary->get_status());
207     EXPECT_EQ(DummyMac(mac_name_3).ComputeMac(data).value(),
208               primary->get_primitive().ComputeMac(data).value());
209   }
210 
211   {  // Check raw primitives.
212     auto& primitives = *(primitive_set.get_raw_primitives().value());
213     EXPECT_EQ(2, primitives.size());
214     EXPECT_EQ(DummyMac(mac_name_4).ComputeMac(data).value(),
215               primitives[0]->get_primitive().ComputeMac(data).value());
216     EXPECT_EQ(KeyStatusType::ENABLED, primitives[0]->get_status());
217     EXPECT_EQ(key_4.key_id(), primitives[0]->get_key_id());
218     EXPECT_EQ(OutputPrefixType::RAW, primitives[0]->get_output_prefix_type());
219     EXPECT_EQ(DummyMac(mac_name_5).ComputeMac(data).value(),
220               primitives[1]->get_primitive().ComputeMac(data).value());
221     EXPECT_EQ(KeyStatusType::ENABLED, primitives[1]->get_status());
222     EXPECT_EQ(key_5.key_id(), primitives[1]->get_key_id());
223     EXPECT_EQ(OutputPrefixType::RAW, primitives[1]->get_output_prefix_type());
224   }
225 
226   {  // Check Tink primitives.
227     std::string prefix = CryptoFormat::GetOutputPrefix(key_1).value();
228     auto& primitives = *(primitive_set.get_primitives(prefix).value());
229     EXPECT_EQ(2, primitives.size());
230     EXPECT_EQ(DummyMac(mac_name_1).ComputeMac(data).value(),
231               primitives[0]->get_primitive().ComputeMac(data).value());
232     EXPECT_EQ(KeyStatusType::ENABLED, primitives[0]->get_status());
233     EXPECT_EQ(key_1.key_id(), primitives[0]->get_key_id());
234     EXPECT_EQ(OutputPrefixType::TINK, primitives[0]->get_output_prefix_type());
235     EXPECT_EQ(DummyMac(mac_name_6).ComputeMac(data).value(),
236               primitives[1]->get_primitive().ComputeMac(data).value());
237     EXPECT_EQ(KeyStatusType::ENABLED, primitives[1]->get_status());
238     EXPECT_EQ(key_1.key_id(), primitives[1]->get_key_id());
239     EXPECT_EQ(OutputPrefixType::TINK, primitives[1]->get_output_prefix_type());
240   }
241 
242   {  // Check another Tink primitive.
243     std::string prefix = CryptoFormat::GetOutputPrefix(key_3).value();
244     auto& primitives = *(primitive_set.get_primitives(prefix).value());
245     EXPECT_EQ(1, primitives.size());
246     EXPECT_EQ(DummyMac(mac_name_3).ComputeMac(data).value(),
247               primitives[0]->get_primitive().ComputeMac(data).value());
248     EXPECT_EQ(KeyStatusType::ENABLED, primitives[0]->get_status());
249     EXPECT_EQ(key_3.key_id(), primitives[0]->get_key_id());
250     EXPECT_EQ(OutputPrefixType::TINK, primitives[0]->get_output_prefix_type());
251   }
252 
253   {  // Check legacy primitive.
254     std::string prefix = CryptoFormat::GetOutputPrefix(key_2).value();
255     auto& primitives = *(primitive_set.get_primitives(prefix).value());
256     EXPECT_EQ(1, primitives.size());
257     EXPECT_EQ(DummyMac(mac_name_2).ComputeMac(data).value(),
258               primitives[0]->get_primitive().ComputeMac(data).value());
259     EXPECT_EQ(KeyStatusType::ENABLED, primitives[0]->get_status());
260     EXPECT_EQ(key_2.key_id(), primitives[0]->get_key_id());
261     EXPECT_EQ(OutputPrefixType::LEGACY,
262               primitives[0]->get_output_prefix_type());
263   }
264 }
265 
TEST_F(PrimitiveSetTest,PrimaryKeyWithIdCollisions)266 TEST_F(PrimitiveSetTest, PrimaryKeyWithIdCollisions) {
267   std::string mac_name_1 = "MAC#1";
268   std::string mac_name_2 = "MAC#2";
269 
270   uint32_t key_id_1 = 1234543;
271   KeysetInfo::KeyInfo key_info_1;
272   key_info_1.set_key_id(key_id_1);
273   key_info_1.set_status(KeyStatusType::ENABLED);
274 
275   uint32_t key_id_2 = key_id_1;  // same id as key_2
276   KeysetInfo::KeyInfo key_info_2;
277   key_info_2.set_key_id(key_id_2);
278   key_info_2.set_status(KeyStatusType::ENABLED);
279 
280   {  // Test with RAW-keys.
281     std::unique_ptr<Mac> mac_1(new DummyMac(mac_name_1));
282     std::unique_ptr<Mac> mac_2(new DummyMac(mac_name_2));
283     key_info_1.set_output_prefix_type(OutputPrefixType::RAW);
284     key_info_2.set_output_prefix_type(OutputPrefixType::RAW);
285     PrimitiveSet<Mac>::Builder primitive_set_builder;
286 
287     // Add the first primitive, and set it as primary.
288     primitive_set_builder.AddPrimaryPrimitive(std::move(mac_1), key_info_1);
289 
290     auto primitive_set_result = std::move(primitive_set_builder).Build();
291     ASSERT_TRUE(primitive_set_result.ok()) << primitive_set_result.status();
292     PrimitiveSet<Mac> primitive_set = std::move(primitive_set_result.value());
293 
294     std::string identifier = "";
295     const auto& primitives =
296         *(primitive_set.get_primitives(identifier).value());
297     EXPECT_EQ(1, primitives.size());
298     EXPECT_EQ(primitive_set.get_primary(), primitives[0].get());
299   }
300 
301   {  // Test with TINK-keys.
302     std::unique_ptr<Mac> mac_1(new DummyMac(mac_name_1));
303     std::unique_ptr<Mac> mac_2(new DummyMac(mac_name_2));
304     key_info_1.set_output_prefix_type(OutputPrefixType::TINK);
305     key_info_2.set_output_prefix_type(OutputPrefixType::TINK);
306     PrimitiveSet<Mac>::Builder primitive_set_builder;
307 
308     // Add the first primitive, and set it as primary.
309     primitive_set_builder.AddPrimaryPrimitive(std::move(mac_1), key_info_1);
310 
311     auto primitive_set_result = std::move(primitive_set_builder).Build();
312     ASSERT_TRUE(primitive_set_result.ok()) << primitive_set_result.status();
313     PrimitiveSet<Mac> primitive_set = std::move(primitive_set_result.value());
314     std::string identifier = CryptoFormat::GetOutputPrefix(key_info_1).value();
315     const auto& primitives =
316         *(primitive_set.get_primitives(identifier).value());
317     EXPECT_EQ(1, primitives.size());
318     EXPECT_EQ(primitive_set.get_primary(), primitives[0].get());
319   }
320 
321   {  // Test with LEGACY-keys.
322     std::unique_ptr<Mac> mac_1(new DummyMac(mac_name_1));
323     std::unique_ptr<Mac> mac_2(new DummyMac(mac_name_2));
324     key_info_1.set_output_prefix_type(OutputPrefixType::LEGACY);
325     key_info_2.set_output_prefix_type(OutputPrefixType::LEGACY);
326     PrimitiveSet<Mac>::Builder primitive_set_builder;
327 
328     // Add the first primitive, and set it as primary.
329     primitive_set_builder.AddPrimaryPrimitive(std::move(mac_1), key_info_1);
330 
331     auto primitive_set_result = std::move(primitive_set_builder).Build();
332     ASSERT_TRUE(primitive_set_result.ok()) << primitive_set_result.status();
333     PrimitiveSet<Mac> primitive_set = std::move(primitive_set_result.value());
334     std::string identifier = CryptoFormat::GetOutputPrefix(key_info_1).value();
335     const auto& primitives =
336         *(primitive_set.get_primitives(identifier).value());
337     EXPECT_EQ(1, primitives.size());
338     EXPECT_EQ(primitive_set.get_primary(), primitives[0].get());
339   }
340 }
341 
TEST_F(PrimitiveSetTest,DisabledKey)342 TEST_F(PrimitiveSetTest, DisabledKey) {
343   std::string mac_name_1 = "MAC#1";
344   std::unique_ptr<Mac> mac_1(new DummyMac(mac_name_1));
345 
346   uint32_t key_id_1 = 1234543;
347   KeysetInfo::KeyInfo key_info_1;
348   key_info_1.set_output_prefix_type(OutputPrefixType::TINK);
349   key_info_1.set_key_id(key_id_1);
350   key_info_1.set_status(KeyStatusType::DISABLED);
351 
352   // Add all the primitives.
353   auto add_primitive_result = PrimitiveSet<Mac>::Builder{}
354                                   .AddPrimitive(std::move(mac_1), key_info_1)
355                                   .Build();
356   EXPECT_FALSE(add_primitive_result.ok());
357 }
358 
CreateKey(uint32_t key_id,OutputPrefixType output_prefix_type,KeyStatusType key_status,absl::string_view type_url)359 KeysetInfo::KeyInfo CreateKey(uint32_t key_id,
360                               OutputPrefixType output_prefix_type,
361                               KeyStatusType key_status,
362                               absl::string_view type_url) {
363   KeysetInfo::KeyInfo key_info;
364   key_info.set_output_prefix_type(output_prefix_type);
365   key_info.set_key_id(key_id);
366   key_info.set_status(key_status);
367   std::string type_url_str(type_url);
368   key_info.set_type_url(type_url_str);
369   return key_info;
370 }
371 
372 // Struct to hold MAC, Id and type_url.
373 struct MacIdAndTypeUrl {
374   std::string mac;
375   std::string id;
376   std::string type_url;
377 };
378 
operator ==(const MacIdAndTypeUrl & first,const MacIdAndTypeUrl & other)379 bool operator==(const MacIdAndTypeUrl& first, const MacIdAndTypeUrl& other) {
380   return first.mac == other.mac && first.id == other.id &&
381          first.type_url == other.type_url;
382 }
383 
TEST_F(PrimitiveSetTest,GetAll)384 TEST_F(PrimitiveSetTest, GetAll) {
385   auto pset_result =
386       PrimitiveSet<Mac>::Builder{}
387           .AddPrimitive(
388               absl::make_unique<DummyMac>("MAC1"),
389               CreateKey(0x01010101, OutputPrefixType::TINK,
390                         KeyStatusType::ENABLED, /*type_url=*/
391                         "type.googleapis.com/google.crypto.tink.HmacKey"))
392           .AddPrimitive(
393               absl::make_unique<DummyMac>("MAC2"),
394               CreateKey(0x02020202, OutputPrefixType::TINK,
395                         KeyStatusType::ENABLED, /*type_url=*/
396                         "type.googleapis.com/google.crypto.tink.HmacKey"))
397           // Add primitive and make it primary.
398           .AddPrimaryPrimitive(
399               absl::make_unique<DummyMac>("MAC3"),
400               CreateKey(0x02020202, OutputPrefixType::TINK,
401                         KeyStatusType::ENABLED, /*type_url=*/
402                         "type.googleapis.com/google.crypto.tink.AesCmacKey"))
403           .AddPrimitive(
404               absl::make_unique<DummyMac>("MAC4"),
405               CreateKey(0x02020202, OutputPrefixType::RAW,
406                         KeyStatusType::ENABLED, /*type_url=*/
407                         "type.googleapis.com/google.crypto.tink.AesCmacKey"))
408           .AddPrimitive(
409               absl::make_unique<DummyMac>("MAC5"),
410               CreateKey(0x01010101, OutputPrefixType::TINK,
411                         KeyStatusType::ENABLED, /*type_url=*/
412                         "type.googleapis.com/google.crypto.tink.AesCmacKey"))
413           .Build();
414 
415   ASSERT_TRUE(pset_result.ok()) << pset_result.status();
416   PrimitiveSet<Mac> pset = std::move(pset_result.value());
417 
418   std::vector<MacIdAndTypeUrl> mac_id_and_type;
419   for (auto* entry : pset.get_all()) {
420     auto mac_or = entry->get_primitive().ComputeMac("");
421     ASSERT_THAT(mac_or, IsOk());
422     mac_id_and_type.push_back({mac_or.value(), entry->get_identifier(),
423                                std::string(entry->get_key_type_url())});
424   }
425 
426   // In the following id part, the first byte is 1 for Tink.
427   std::vector<MacIdAndTypeUrl> expected_result = {
428       {/*mac=*/"13:0:DummyMac:MAC1", /*id=*/absl::StrCat("\1\1\1\1\1"),
429        /*type_url=*/"type.googleapis.com/google.crypto.tink.HmacKey"},
430       {/*mac=*/"13:0:DummyMac:MAC2", /*id=*/absl::StrCat("\1\2\2\2\2"),
431        /*type_url=*/"type.googleapis.com/google.crypto.tink.HmacKey"},
432       {/*mac=*/"13:0:DummyMac:MAC3", /*id=*/absl::StrCat("\1\2\2\2\2"),
433        /*type_url=*/"type.googleapis.com/google.crypto.tink.AesCmacKey"},
434       {/*mac=*/"13:0:DummyMac:MAC4", /*id=*/"",
435        /*type_url=*/"type.googleapis.com/google.crypto.tink.AesCmacKey"},
436       {/*mac=*/"13:0:DummyMac:MAC5", /*id=*/absl::StrCat("\1\1\1\1\1"),
437        /*type_url=*/"type.googleapis.com/google.crypto.tink.AesCmacKey"}};
438 
439   EXPECT_THAT(mac_id_and_type, UnorderedElementsAreArray(expected_result));
440 }
441 
442 class FakeDeriver : public KeysetDeriver {
443  public:
444   explicit FakeDeriver() = default;
DeriveKeyset(absl::string_view salt) const445   crypto::tink::util::StatusOr<std::unique_ptr<KeysetHandle>> DeriveKeyset(
446       absl::string_view salt) const override {
447     Keyset keyset;
448     return CleartextKeysetHandle::GetKeysetHandle(keyset);
449   }
450 };
451 
TEST_F(PrimitiveSetTest,GetAllInKeysetOrder)452 TEST_F(PrimitiveSetTest, GetAllInKeysetOrder) {
453   auto pset = absl::make_unique<PrimitiveSet<KeysetDeriver>>();
454   std::vector<KeysetInfo::KeyInfo> key_infos;
455 
456   KeysetInfo::KeyInfo key_info;
457   key_info.set_key_id(1010101);
458   key_info.set_status(KeyStatusType::ENABLED);
459   key_info.set_output_prefix_type(OutputPrefixType::RAW);
460   key_info.set_type_url(
461       "type.googleapis.com/google.crypto.tink.PrfBasedDeriverKey");
462   ASSERT_THAT(pset->AddPrimitive(absl::make_unique<FakeDeriver>(), key_info),
463               IsOk());
464   key_infos.push_back(key_info);
465 
466   key_info.set_key_id(2020202);
467   key_info.set_status(KeyStatusType::ENABLED);
468   key_info.set_output_prefix_type(OutputPrefixType::LEGACY);
469   key_info.set_type_url(
470       "type.googleapis.com/google.crypto.tink.PrfBasedDeriverKey");
471   ASSERT_THAT(pset->AddPrimitive(absl::make_unique<FakeDeriver>(), key_info),
472               IsOk());
473   key_infos.push_back(key_info);
474 
475   key_info.set_key_id(3030303);
476   key_info.set_status(KeyStatusType::ENABLED);
477   key_info.set_output_prefix_type(OutputPrefixType::TINK);
478   key_info.set_type_url(
479       "type.googleapis.com/google.crypto.tink.PrfBasedDeriverKey");
480   ASSERT_THAT(pset->AddPrimitive(absl::make_unique<FakeDeriver>(), key_info),
481               IsOk());
482   key_infos.push_back(key_info);
483 
484   std::vector<PrimitiveSet<KeysetDeriver>::Entry<KeysetDeriver>*> entries =
485       pset->get_all_in_keyset_order();
486   ASSERT_THAT(entries, SizeIs(key_infos.size()));
487 
488   for (int i = 0; i < entries.size(); i++) {
489     EXPECT_THAT(entries[i]->get_identifier(),
490                 Eq(*CryptoFormat::GetOutputPrefix(key_infos[i])));
491     EXPECT_THAT(entries[i]->get_status(), Eq(KeyStatusType::ENABLED));
492     EXPECT_THAT(entries[i]->get_key_id(), Eq(key_infos[i].key_id()));
493     EXPECT_THAT(entries[i]->get_output_prefix_type(),
494                 Eq(key_infos[i].output_prefix_type()));
495     EXPECT_THAT(entries[i]->get_key_type_url(), Eq(key_infos[i].type_url()));
496   }
497 }
498 
TEST_F(PrimitiveSetTest,LegacyConcurrentOperations)499 TEST_F(PrimitiveSetTest, LegacyConcurrentOperations) {
500   PrimitiveSet<Mac> mac_set;
501   int offset_a = 100;
502   int offset_b = 150;
503   int count = 100;
504 
505   // Add some primitives.
506   std::thread add_primitives_a(
507       [&]() { add_primitives(&mac_set, offset_a, count); });
508   std::thread add_primitives_b(
509       [&]() { add_primitives(&mac_set, offset_b, count); });
510   add_primitives_a.join();
511   add_primitives_b.join();
512 
513   // Access primitives.
514   std::thread access_primitives_a(access_primitives, &mac_set, offset_a, count);
515   std::thread access_primitives_b(access_primitives, &mac_set, offset_b, count);
516   access_primitives_a.join();
517   access_primitives_b.join();
518 
519   // Verify the common key ids added by both threads.
520   for (int key_id = offset_a; key_id < offset_b + count; key_id++) {
521     KeysetInfo::KeyInfo key_info;
522     key_info.set_output_prefix_type(OutputPrefixType::TINK);
523     key_info.set_key_id(key_id);
524     key_info.set_status(KeyStatusType::ENABLED);
525     std::string prefix = CryptoFormat::GetOutputPrefix(key_info).value();
526     auto get_result = mac_set.get_primitives(prefix);
527     EXPECT_TRUE(get_result.ok()) << get_result.status();
528     auto macs = get_result.value();
529     if (key_id >= offset_b && key_id < offset_a + count) {
530       EXPECT_EQ(2, macs->size());  // overlapping key_id range
531     } else {
532       EXPECT_EQ(1, macs->size());
533     }
534   }
535 }
536 
TEST_F(PrimitiveSetTest,LegacyBasic)537 TEST_F(PrimitiveSetTest, LegacyBasic) {
538   std::string mac_name_1 = "MAC#1";
539   std::unique_ptr<Mac> mac_1(new DummyMac(mac_name_1));
540   std::string mac_name_2 = "MAC#2";
541   std::unique_ptr<Mac> mac_2(new DummyMac(mac_name_2));
542   std::string mac_name_3 = "MAC#3";
543   std::unique_ptr<Mac> mac_3(new DummyMac(mac_name_3));
544   std::string mac_name_4 = "MAC#3";
545   std::unique_ptr<Mac> mac_4(new DummyMac(mac_name_4));
546   std::string mac_name_5 = "MAC#3";
547   std::unique_ptr<Mac> mac_5(new DummyMac(mac_name_5));
548   std::string mac_name_6 = "MAC#3";
549   std::unique_ptr<Mac> mac_6(new DummyMac(mac_name_6));
550 
551   uint32_t key_id_1 = 1234543;
552   KeysetInfo::KeyInfo key_1;
553   key_1.set_output_prefix_type(OutputPrefixType::TINK);
554   key_1.set_key_id(key_id_1);
555   key_1.set_status(KeyStatusType::ENABLED);
556 
557   uint32_t key_id_2 = 7213743;
558   KeysetInfo::KeyInfo key_2;
559   key_2.set_output_prefix_type(OutputPrefixType::LEGACY);
560   key_2.set_key_id(key_id_2);
561   key_2.set_status(KeyStatusType::ENABLED);
562 
563   uint32_t key_id_3 = key_id_2;  // same id as key_2
564   KeysetInfo::KeyInfo key_3;
565   key_3.set_output_prefix_type(OutputPrefixType::TINK);
566   key_3.set_key_id(key_id_3);
567   key_3.set_status(KeyStatusType::ENABLED);
568 
569   uint32_t key_id_4 = 947327;
570   KeysetInfo::KeyInfo key_4;
571   key_4.set_output_prefix_type(OutputPrefixType::RAW);
572   key_4.set_key_id(key_id_4);
573   key_4.set_status(KeyStatusType::ENABLED);
574 
575   uint32_t key_id_5 = 529472;
576   KeysetInfo::KeyInfo key_5;
577   key_5.set_output_prefix_type(OutputPrefixType::RAW);
578   key_5.set_key_id(key_id_5);
579   key_5.set_status(KeyStatusType::ENABLED);
580 
581   uint32_t key_id_6 = key_id_1;  // same id as key_1
582   KeysetInfo::KeyInfo key_6;
583   key_6.set_output_prefix_type(OutputPrefixType::TINK);
584   key_6.set_key_id(key_id_6);
585   key_6.set_status(KeyStatusType::ENABLED);
586 
587   PrimitiveSet<Mac> primitive_set;
588   EXPECT_TRUE(primitive_set.get_primary() == nullptr);
589   EXPECT_EQ(absl::StatusCode::kNotFound,
590             primitive_set.get_raw_primitives().status().code());
591   EXPECT_EQ(absl::StatusCode::kNotFound,
592             primitive_set.get_primitives("prefix").status().code());
593 
594   // Add all the primitives.
595   auto add_primitive_result =
596       primitive_set.AddPrimitive(std::move(mac_1), key_1);
597   EXPECT_TRUE(add_primitive_result.ok()) << add_primitive_result.status();
598 
599   add_primitive_result = primitive_set.AddPrimitive(std::move(mac_2), key_2);
600   EXPECT_TRUE(add_primitive_result.ok()) << add_primitive_result.status();
601 
602   add_primitive_result = primitive_set.AddPrimitive(std::move(mac_3), key_3);
603   EXPECT_TRUE(add_primitive_result.ok()) << add_primitive_result.status();
604   EXPECT_THAT(primitive_set.set_primary(add_primitive_result.value()), IsOk());
605 
606   add_primitive_result = primitive_set.AddPrimitive(std::move(mac_4), key_4);
607   EXPECT_TRUE(add_primitive_result.ok()) << add_primitive_result.status();
608 
609   add_primitive_result = primitive_set.AddPrimitive(std::move(mac_5), key_5);
610   EXPECT_TRUE(add_primitive_result.ok()) << add_primitive_result.status();
611 
612   add_primitive_result = primitive_set.AddPrimitive(std::move(mac_6), key_6);
613   EXPECT_TRUE(add_primitive_result.ok()) << add_primitive_result.status();
614 
615   // Try adding a "consumed" unique_ptr as a primitive.
616   add_primitive_result = primitive_set.AddPrimitive(std::move(mac_6), key_6);
617   EXPECT_FALSE(add_primitive_result.ok());
618   EXPECT_EQ(absl::StatusCode::kInvalidArgument,
619             add_primitive_result.status().code());
620 
621   std::string data = "some data";
622 
623   {  // Check the primary.
624     auto primary = primitive_set.get_primary();
625     EXPECT_FALSE(primary == nullptr);
626     EXPECT_EQ(KeyStatusType::ENABLED, primary->get_status());
627     EXPECT_EQ(DummyMac(mac_name_3).ComputeMac(data).value(),
628               primary->get_primitive().ComputeMac(data).value());
629   }
630 
631   {  // Check raw primitives.
632     auto& primitives = *(primitive_set.get_raw_primitives().value());
633     EXPECT_EQ(2, primitives.size());
634     EXPECT_EQ(DummyMac(mac_name_4).ComputeMac(data).value(),
635               primitives[0]->get_primitive().ComputeMac(data).value());
636     EXPECT_EQ(KeyStatusType::ENABLED, primitives[0]->get_status());
637     EXPECT_EQ(key_4.key_id(), primitives[0]->get_key_id());
638     EXPECT_EQ(OutputPrefixType::RAW, primitives[0]->get_output_prefix_type());
639     EXPECT_EQ(DummyMac(mac_name_5).ComputeMac(data).value(),
640               primitives[1]->get_primitive().ComputeMac(data).value());
641     EXPECT_EQ(KeyStatusType::ENABLED, primitives[1]->get_status());
642     EXPECT_EQ(key_5.key_id(), primitives[1]->get_key_id());
643     EXPECT_EQ(OutputPrefixType::RAW, primitives[1]->get_output_prefix_type());
644   }
645 
646   {  // Check Tink primitives.
647     std::string prefix = CryptoFormat::GetOutputPrefix(key_1).value();
648     auto& primitives = *(primitive_set.get_primitives(prefix).value());
649     EXPECT_EQ(2, primitives.size());
650     EXPECT_EQ(DummyMac(mac_name_1).ComputeMac(data).value(),
651               primitives[0]->get_primitive().ComputeMac(data).value());
652     EXPECT_EQ(KeyStatusType::ENABLED, primitives[0]->get_status());
653     EXPECT_EQ(key_1.key_id(), primitives[0]->get_key_id());
654     EXPECT_EQ(OutputPrefixType::TINK, primitives[0]->get_output_prefix_type());
655     EXPECT_EQ(DummyMac(mac_name_6).ComputeMac(data).value(),
656               primitives[1]->get_primitive().ComputeMac(data).value());
657     EXPECT_EQ(KeyStatusType::ENABLED, primitives[1]->get_status());
658     EXPECT_EQ(key_1.key_id(), primitives[1]->get_key_id());
659     EXPECT_EQ(OutputPrefixType::TINK, primitives[1]->get_output_prefix_type());
660   }
661 
662   {  // Check another Tink primitive.
663     std::string prefix = CryptoFormat::GetOutputPrefix(key_3).value();
664     auto& primitives = *(primitive_set.get_primitives(prefix).value());
665     EXPECT_EQ(1, primitives.size());
666     EXPECT_EQ(DummyMac(mac_name_3).ComputeMac(data).value(),
667               primitives[0]->get_primitive().ComputeMac(data).value());
668     EXPECT_EQ(KeyStatusType::ENABLED, primitives[0]->get_status());
669     EXPECT_EQ(key_3.key_id(), primitives[0]->get_key_id());
670     EXPECT_EQ(OutputPrefixType::TINK, primitives[0]->get_output_prefix_type());
671   }
672 
673   {  // Check legacy primitive.
674     std::string prefix = CryptoFormat::GetOutputPrefix(key_2).value();
675     auto& primitives = *(primitive_set.get_primitives(prefix).value());
676     EXPECT_EQ(1, primitives.size());
677     EXPECT_EQ(DummyMac(mac_name_2).ComputeMac(data).value(),
678               primitives[0]->get_primitive().ComputeMac(data).value());
679     EXPECT_EQ(KeyStatusType::ENABLED, primitives[0]->get_status());
680     EXPECT_EQ(key_2.key_id(), primitives[0]->get_key_id());
681     EXPECT_EQ(OutputPrefixType::LEGACY,
682               primitives[0]->get_output_prefix_type());
683   }
684 }
685 
TEST_F(PrimitiveSetTest,LegacyPrimaryKeyWithIdCollisions)686 TEST_F(PrimitiveSetTest, LegacyPrimaryKeyWithIdCollisions) {
687   std::string mac_name_1 = "MAC#1";
688   std::string mac_name_2 = "MAC#2";
689 
690   uint32_t key_id_1 = 1234543;
691   KeysetInfo::KeyInfo key_info_1;
692   key_info_1.set_key_id(key_id_1);
693   key_info_1.set_status(KeyStatusType::ENABLED);
694 
695   uint32_t key_id_2 = key_id_1;  // same id as key_2
696   KeysetInfo::KeyInfo key_info_2;
697   key_info_2.set_key_id(key_id_2);
698   key_info_2.set_status(KeyStatusType::ENABLED);
699 
700   {  // Test with RAW-keys.
701     std::unique_ptr<Mac> mac_1(new DummyMac(mac_name_1));
702     std::unique_ptr<Mac> mac_2(new DummyMac(mac_name_2));
703     key_info_1.set_output_prefix_type(OutputPrefixType::RAW);
704     key_info_2.set_output_prefix_type(OutputPrefixType::RAW);
705     PrimitiveSet<Mac> primitive_set;
706     EXPECT_TRUE(primitive_set.get_primary() == nullptr);
707 
708     // Add the first primitive, and set it as primary.
709     auto add_primitive_result =
710         primitive_set.AddPrimitive(std::move(mac_1), key_info_1);
711     EXPECT_TRUE(add_primitive_result.ok()) << add_primitive_result.status();
712     ASSERT_THAT(primitive_set.set_primary(add_primitive_result.value()),
713                 IsOk());
714 
715     std::string identifier = "";
716     const auto& primitives =
717         *(primitive_set.get_primitives(identifier).value());
718     EXPECT_EQ(1, primitives.size());
719     EXPECT_EQ(primitive_set.get_primary(), primitives[0].get());
720 
721     //  Adding another primitive should not invalidate the primary.
722     add_primitive_result =
723         primitive_set.AddPrimitive(std::move(mac_2), key_info_2);
724     EXPECT_TRUE(add_primitive_result.ok()) << add_primitive_result.status();
725     EXPECT_EQ(2, primitives.size());
726     EXPECT_EQ(primitive_set.get_primary(), primitives[0].get());
727   }
728 
729   {  // Test with TINK-keys.
730     std::unique_ptr<Mac> mac_1(new DummyMac(mac_name_1));
731     std::unique_ptr<Mac> mac_2(new DummyMac(mac_name_2));
732     key_info_1.set_output_prefix_type(OutputPrefixType::TINK);
733     key_info_2.set_output_prefix_type(OutputPrefixType::TINK);
734     PrimitiveSet<Mac> primitive_set;
735     EXPECT_TRUE(primitive_set.get_primary() == nullptr);
736 
737     // Add the first primitive, and set it as primary.
738     auto add_primitive_result =
739         primitive_set.AddPrimitive(std::move(mac_1), key_info_1);
740     EXPECT_TRUE(add_primitive_result.ok()) << add_primitive_result.status();
741     ASSERT_THAT(primitive_set.set_primary(add_primitive_result.value()),
742                 IsOk());
743 
744     std::string identifier = CryptoFormat::GetOutputPrefix(key_info_1).value();
745     const auto& primitives =
746         *(primitive_set.get_primitives(identifier).value());
747     EXPECT_EQ(1, primitives.size());
748     EXPECT_EQ(primitive_set.get_primary(), primitives[0].get());
749 
750     //  Adding another primitive should not invalidate the primary.
751     add_primitive_result =
752         primitive_set.AddPrimitive(std::move(mac_2), key_info_2);
753     EXPECT_TRUE(add_primitive_result.ok()) << add_primitive_result.status();
754     EXPECT_EQ(2, primitives.size());
755     EXPECT_EQ(primitive_set.get_primary(), primitives[0].get());
756   }
757 
758   {  // Test with LEGACY-keys.
759     std::unique_ptr<Mac> mac_1(new DummyMac(mac_name_1));
760     std::unique_ptr<Mac> mac_2(new DummyMac(mac_name_2));
761     key_info_1.set_output_prefix_type(OutputPrefixType::LEGACY);
762     key_info_2.set_output_prefix_type(OutputPrefixType::LEGACY);
763     PrimitiveSet<Mac> primitive_set;
764     EXPECT_TRUE(primitive_set.get_primary() == nullptr);
765 
766     // Add the first primitive, and set it as primary.
767     auto add_primitive_result =
768         primitive_set.AddPrimitive(std::move(mac_1), key_info_1);
769     EXPECT_TRUE(add_primitive_result.ok()) << add_primitive_result.status();
770     ASSERT_THAT(primitive_set.set_primary(add_primitive_result.value()),
771                 IsOk());
772 
773     std::string identifier = CryptoFormat::GetOutputPrefix(key_info_1).value();
774     const auto& primitives =
775         *(primitive_set.get_primitives(identifier).value());
776     EXPECT_EQ(1, primitives.size());
777     EXPECT_EQ(primitive_set.get_primary(), primitives[0].get());
778 
779     //  Adding another primitive should not invalidate the primary.
780     add_primitive_result =
781         primitive_set.AddPrimitive(std::move(mac_2), key_info_2);
782     EXPECT_TRUE(add_primitive_result.ok()) << add_primitive_result.status();
783     EXPECT_EQ(2, primitives.size());
784     EXPECT_EQ(primitive_set.get_primary(), primitives[0].get());
785   }
786 }
787 
TEST_F(PrimitiveSetTest,LegacyDisabledKey)788 TEST_F(PrimitiveSetTest, LegacyDisabledKey) {
789   std::string mac_name_1 = "MAC#1";
790   std::unique_ptr<Mac> mac_1(new DummyMac(mac_name_1));
791 
792   uint32_t key_id_1 = 1234543;
793   KeysetInfo::KeyInfo key_info_1;
794   key_info_1.set_output_prefix_type(OutputPrefixType::TINK);
795   key_info_1.set_key_id(key_id_1);
796   key_info_1.set_status(KeyStatusType::DISABLED);
797 
798   PrimitiveSet<Mac> primitive_set;
799   // Add all the primitives.
800   auto add_primitive_result =
801       primitive_set.AddPrimitive(std::move(mac_1), key_info_1);
802   EXPECT_FALSE(add_primitive_result.ok());
803 }
804 
TEST_F(PrimitiveSetTest,LegacyGetAll)805 TEST_F(PrimitiveSetTest, LegacyGetAll) {
806   PrimitiveSet<Mac> pset;
807   EXPECT_THAT(
808       pset.AddPrimitive(
809               absl::make_unique<DummyMac>("MAC1"),
810               CreateKey(0x01010101, OutputPrefixType::TINK,
811                         KeyStatusType::ENABLED, /*type_url=*/
812                         "type.googleapis.com/google.crypto.tink.HmacKey"))
813           .status(),
814       IsOk());
815 
816   EXPECT_THAT(
817       pset.AddPrimitive(
818               absl::make_unique<DummyMac>("MAC2"),
819               CreateKey(0x02020202, OutputPrefixType::TINK,
820                         KeyStatusType::ENABLED, /*type_url=*/
821                         "type.googleapis.com/google.crypto.tink.HmacKey"))
822           .status(),
823       IsOk());
824   // Add primitive and make it primary.
825   auto entry_or = pset.AddPrimitive(
826       absl::make_unique<DummyMac>("MAC3"),
827       CreateKey(0x02020202, OutputPrefixType::TINK,
828                 KeyStatusType::ENABLED, /*type_url=*/
829                 "type.googleapis.com/google.crypto.tink.AesCmacKey"));
830   ASSERT_THAT(entry_or, IsOk());
831   EXPECT_THAT(pset.set_primary(entry_or.value()), IsOk());
832 
833   EXPECT_THAT(
834       pset.AddPrimitive(
835               absl::make_unique<DummyMac>("MAC4"),
836               CreateKey(0x02020202, OutputPrefixType::RAW,
837                         KeyStatusType::ENABLED, /*type_url=*/
838                         "type.googleapis.com/google.crypto.tink.AesCmacKey"))
839           .status(),
840       IsOk());
841 
842   EXPECT_THAT(
843       pset.AddPrimitive(
844               absl::make_unique<DummyMac>("MAC5"),
845               CreateKey(0x01010101, OutputPrefixType::TINK,
846                         KeyStatusType::ENABLED, /*type_url=*/
847                         "type.googleapis.com/google.crypto.tink.AesCmacKey"))
848           .status(),
849       IsOk());
850 
851   std::vector<MacIdAndTypeUrl> mac_id_and_type;
852   for (auto* entry : pset.get_all()) {
853     auto mac_or = entry->get_primitive().ComputeMac("");
854     ASSERT_THAT(mac_or, IsOk());
855     mac_id_and_type.push_back({mac_or.value(), entry->get_identifier(),
856                                std::string(entry->get_key_type_url())});
857   }
858 
859   // In the following id part, the first byte is 1 for Tink.
860   std::vector<MacIdAndTypeUrl> expected_result = {
861       {/*mac=*/"13:0:DummyMac:MAC1", /*id=*/absl::StrCat("\1\1\1\1\1"),
862        /*type_url=*/"type.googleapis.com/google.crypto.tink.HmacKey"},
863       {/*mac=*/"13:0:DummyMac:MAC2", /*id=*/absl::StrCat("\1\2\2\2\2"),
864        /*type_url=*/"type.googleapis.com/google.crypto.tink.HmacKey"},
865       {/*mac=*/"13:0:DummyMac:MAC3", /*id=*/absl::StrCat("\1\2\2\2\2"),
866        /*type_url=*/"type.googleapis.com/google.crypto.tink.AesCmacKey"},
867       {/*mac=*/"13:0:DummyMac:MAC4", /*id=*/"",
868        /*type_url=*/"type.googleapis.com/google.crypto.tink.AesCmacKey"},
869       {/*mac=*/"13:0:DummyMac:MAC5", /*id=*/absl::StrCat("\1\1\1\1\1"),
870        /*type_url=*/"type.googleapis.com/google.crypto.tink.AesCmacKey"}};
871 
872   EXPECT_THAT(mac_id_and_type, UnorderedElementsAreArray(expected_result));
873 }
874 
875 }  // namespace
876 }  // namespace tink
877 }  // namespace crypto
878