1 // Copyright 2022 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/mac/internal/chunked_mac_wrapper.h"
18
19 #include <memory>
20 #include <string>
21 #include <utility>
22
23 #include "gmock/gmock.h"
24 #include "gtest/gtest.h"
25 #include "absl/strings/str_cat.h"
26 #include "tink/chunked_mac.h"
27 #include "tink/mac/internal/chunked_mac_impl.h"
28 #include "tink/subtle/mac/stateful_mac.h"
29 #include "tink/util/status.h"
30 #include "tink/util/statusor.h"
31 #include "tink/util/test_matchers.h"
32 #include "proto/tink.pb.h"
33
34 namespace crypto {
35 namespace tink {
36 namespace internal {
37 namespace {
38
39 using ::crypto::tink::test::IsOk;
40 using ::crypto::tink::test::IsOkAndHolds;
41 using ::crypto::tink::test::StatusIs;
42 using ::google::crypto::tink::KeysetInfo;
43 using ::google::crypto::tink::KeyStatusType;
44 using ::google::crypto::tink::OutputPrefixType;
45 using ::testing::Values;
46
47 class FakeStatefulMac : public subtle::StatefulMac {
48 public:
FakeStatefulMac(absl::string_view name)49 explicit FakeStatefulMac(absl::string_view name) : name_(name) {}
50
Update(absl::string_view data)51 util::Status Update(absl::string_view data) override {
52 absl::StrAppend(&buffer_, data);
53 return util::OkStatus();
54 }
55
Finalize()56 util::StatusOr<std::string> Finalize() override {
57 return absl::StrCat(name_, buffer_);
58 }
59
60 private:
61 const std::string name_;
62 std::string buffer_ = "";
63 };
64
65 class FakeStatefulMacFactory : public subtle::StatefulMacFactory {
66 public:
FakeStatefulMacFactory(absl::string_view name)67 explicit FakeStatefulMacFactory(absl::string_view name) : name_(name) {}
68
Create() const69 util::StatusOr<std::unique_ptr<subtle::StatefulMac>> Create() const override {
70 return std::unique_ptr<subtle::StatefulMac>(
71 absl::make_unique<FakeStatefulMac>(name_));
72 }
73
74 private:
75 std::string name_;
76 };
77
TEST(ChunkedMacWrapperTest,WrapNullptr)78 TEST(ChunkedMacWrapperTest, WrapNullptr) {
79 EXPECT_THAT(ChunkedMacWrapper().Wrap(nullptr).status(),
80 StatusIs(absl::StatusCode::kInternal));
81 }
82
TEST(ChunkedMacWrapperTest,WrapEmpty)83 TEST(ChunkedMacWrapperTest, WrapEmpty) {
84 std::unique_ptr<PrimitiveSet<ChunkedMac>> mac_set(
85 new PrimitiveSet<ChunkedMac>());
86 EXPECT_THAT(ChunkedMacWrapper().Wrap(std::move(mac_set)).status(),
87 StatusIs(absl::StatusCode::kInvalidArgument));
88 }
89
CreateFakeChunkedMac(absl::string_view name)90 std::unique_ptr<ChunkedMac> CreateFakeChunkedMac(absl::string_view name) {
91 return absl::make_unique<ChunkedMacImpl>(
92 absl::make_unique<FakeStatefulMacFactory>(name));
93 }
94
AddPrimitiveToSet(uint32_t key_id,bool set_primary,OutputPrefixType output_prefix_type,std::unique_ptr<ChunkedMac> mac,KeysetInfo & keyset_info,PrimitiveSet<ChunkedMac> & mac_set)95 util::Status AddPrimitiveToSet(uint32_t key_id, bool set_primary,
96 OutputPrefixType output_prefix_type,
97 std::unique_ptr<ChunkedMac> mac,
98 KeysetInfo& keyset_info,
99 PrimitiveSet<ChunkedMac>& mac_set) {
100 int index = keyset_info.key_info_size();
101 KeysetInfo::KeyInfo* key_info = keyset_info.add_key_info();
102 key_info->set_output_prefix_type(output_prefix_type);
103 key_info->set_key_id(key_id);
104 key_info->set_status(KeyStatusType::ENABLED);
105
106 auto entry =
107 mac_set.AddPrimitive(std::move(mac), keyset_info.key_info(index));
108 if (!entry.ok()) {
109 return entry.status();
110 }
111 if (set_primary) {
112 util::Status set_primary_status = mac_set.set_primary(*entry);
113 if (!set_primary_status.ok()) {
114 return set_primary_status;
115 }
116 }
117 return util::OkStatus();
118 }
119
TEST(ChunkedMacWrapperTest,ComputeMac)120 TEST(ChunkedMacWrapperTest, ComputeMac) {
121 KeysetInfo keyset_info;
122 auto mac_set = absl::make_unique<PrimitiveSet<ChunkedMac>>();
123
124 // Add primitives to the primitive set.
125 ASSERT_THAT(
126 AddPrimitiveToSet(
127 /*key_id=*/0x12d66f, /*set_primary=*/false, OutputPrefixType::TINK,
128 CreateFakeChunkedMac("chunkedmac0:"), keyset_info, *mac_set),
129 IsOk());
130 ASSERT_THAT(
131 AddPrimitiveToSet(
132 /*key_id=*/0xb1539, /*set_primary=*/false, OutputPrefixType::LEGACY,
133 CreateFakeChunkedMac("chunkedmac1:"), keyset_info, *mac_set),
134 IsOk());
135 ASSERT_THAT(
136 AddPrimitiveToSet(
137 /*key_id=*/0x6e12af, /*set_primary=*/true, OutputPrefixType::TINK,
138 CreateFakeChunkedMac("chunkedmac2:"), keyset_info, *mac_set),
139 IsOk());
140
141 // Wrap primitive set into a ChunkedMac.
142 util::StatusOr<std::unique_ptr<crypto::tink::ChunkedMac>> chunked_mac =
143 ChunkedMacWrapper().Wrap(std::move(mac_set));
144 ASSERT_THAT(chunked_mac.status(), IsOk());
145
146 util::StatusOr<std::unique_ptr<ChunkedMacComputation>> computation =
147 (*chunked_mac)->CreateComputation();
148 EXPECT_THAT(computation.status(), IsOk());
149 EXPECT_THAT((*computation)->Update("inputdata"), IsOk());
150 util::StatusOr<std::string> tag = (*computation)->ComputeMac();
151 const std::string output_prefix = std::string("\x01\x00\x6e\x12\xaf", 5);
152 const std::string raw_tag = "chunkedmac2:inputdata";
153 EXPECT_THAT(tag, IsOkAndHolds(absl::StrCat(output_prefix, raw_tag)));
154 }
155
TEST(ChunkedMacWrapperTest,VerifyMacWithUniquePrefix)156 TEST(ChunkedMacWrapperTest, VerifyMacWithUniquePrefix) {
157 KeysetInfo keyset_info;
158 auto mac_set = absl::make_unique<PrimitiveSet<ChunkedMac>>();
159
160 // Add primitives to primitive set.
161 ASSERT_THAT(
162 AddPrimitiveToSet(
163 /*key_id=*/0x12d66f, /*set_primary=*/false, OutputPrefixType::TINK,
164 CreateFakeChunkedMac("chunkedmac0:"), keyset_info, *mac_set),
165 IsOk());
166 ASSERT_THAT(
167 AddPrimitiveToSet(
168 /*key_id=*/0xb1539, /*set_primary=*/false, OutputPrefixType::LEGACY,
169 CreateFakeChunkedMac("chunkedmac1:"), keyset_info, *mac_set),
170 IsOk());
171 ASSERT_THAT(
172 AddPrimitiveToSet(
173 /*key_id=*/0x6e12af, /*set_primary=*/true, OutputPrefixType::TINK,
174 CreateFakeChunkedMac("chunkedmac2:"), keyset_info, *mac_set),
175 IsOk());
176
177 // Wrap primitive set into a ChunkedMac.
178 util::StatusOr<std::unique_ptr<crypto::tink::ChunkedMac>>
179 chunked_mac = ChunkedMacWrapper().Wrap(std::move(mac_set));
180 ASSERT_THAT(chunked_mac.status(), IsOk());
181
182 const std::string output_prefix = std::string("\x01\x00\x6e\x12\xaf", 5);
183 const std::string raw_tag = "chunkedmac2:inputdata";
184 util::StatusOr<std::unique_ptr<ChunkedMacVerification>> verification =
185 (*chunked_mac)
186 ->CreateVerification(absl::StrCat(output_prefix, raw_tag));
187 EXPECT_THAT(verification.status(), IsOk());
188 EXPECT_THAT((*verification)->Update("inputdata"), IsOk());
189 EXPECT_THAT((*verification)->VerifyMac(), IsOk());
190 }
191
TEST(ChunkedMacWrapperTest,VerifyMacWithDuplicatePrefix)192 TEST(ChunkedMacWrapperTest, VerifyMacWithDuplicatePrefix) {
193 KeysetInfo keyset_info;
194 auto mac_set = absl::make_unique<PrimitiveSet<ChunkedMac>>();
195
196 // Add primitives to primitive set.
197 ASSERT_THAT(
198 AddPrimitiveToSet(
199 /*key_id=*/0x12d66f, /*set_primary=*/false, OutputPrefixType::LEGACY,
200 CreateFakeChunkedMac("chunkedmac0:"), keyset_info, *mac_set),
201 IsOk());
202 ASSERT_THAT(
203 AddPrimitiveToSet(
204 /*key_id=*/0x6e12af, /*set_primary=*/false, OutputPrefixType::TINK,
205 CreateFakeChunkedMac("chunkedmac1:"), keyset_info, *mac_set),
206 IsOk());
207 ASSERT_THAT(
208 AddPrimitiveToSet(
209 /*key_id=*/0x6e12af, /*set_primary=*/true, OutputPrefixType::TINK,
210 CreateFakeChunkedMac("chunkedmac2:"), keyset_info, *mac_set),
211 IsOk());
212
213 // Wrap primitive set into a ChunkedMac.
214 util::StatusOr<std::unique_ptr<crypto::tink::ChunkedMac>>
215 chunked_mac = ChunkedMacWrapper().Wrap(std::move(mac_set));
216 ASSERT_THAT(chunked_mac.status(), IsOk());
217
218 const std::string output_prefix = std::string("\x01\x00\x6e\x12\xaf", 5);
219 const std::string raw_tag = "chunkedmac1:inputdata";
220 util::StatusOr<std::unique_ptr<ChunkedMacVerification>> verification =
221 (*chunked_mac)
222 ->CreateVerification(absl::StrCat(output_prefix, raw_tag));
223 EXPECT_THAT(verification.status(), IsOk());
224 EXPECT_THAT((*verification)->Update("inputdata"), IsOk());
225 EXPECT_THAT((*verification)->VerifyMac(), IsOk());
226 }
227
TEST(ChunkedMacWrapperTest,VerifyMacWithRawTagStartingWithKeyId)228 TEST(ChunkedMacWrapperTest, VerifyMacWithRawTagStartingWithKeyId) {
229 KeysetInfo keyset_info;
230 auto mac_set = absl::make_unique<PrimitiveSet<ChunkedMac>>();
231
232 const std::string key_id0 = std::string("\x01\x00\x12\xd6\x6f", 5);
233
234 // Add primitives to primitive set.
235 ASSERT_THAT(
236 AddPrimitiveToSet(
237 /*key_id=*/0x12d66f, /*set_primary=*/false, OutputPrefixType::TINK,
238 CreateFakeChunkedMac("chunkedmac0:"), keyset_info, *mac_set),
239 IsOk());
240 ASSERT_THAT(
241 AddPrimitiveToSet(
242 /*key_id=*/0x6e12af, /*set_primary=*/true, OutputPrefixType::RAW,
243 CreateFakeChunkedMac(/*name=*/absl::StrCat(key_id0, ":chunkedmac1:")),
244 keyset_info, *mac_set),
245 IsOk());
246
247 // Wrap primitive set into a ChunkedMac.
248 util::StatusOr<std::unique_ptr<crypto::tink::ChunkedMac>>
249 chunked_mac = ChunkedMacWrapper().Wrap(std::move(mac_set));
250 ASSERT_THAT(chunked_mac.status(), IsOk());
251
252 const std::string raw_tag = absl::StrCat(key_id0, ":chunkedmac1:inputdata");
253 util::StatusOr<std::unique_ptr<ChunkedMacVerification>> verification =
254 (*chunked_mac)->CreateVerification(raw_tag);
255 EXPECT_THAT(verification.status(), IsOk());
256 EXPECT_THAT((*verification)->Update("inputdata"), IsOk());
257 EXPECT_THAT((*verification)->VerifyMac(), IsOk());
258 }
259
260 class ChunkedMacWrapperOutputPrefixTest
261 : public testing::TestWithParam<OutputPrefixType> {};
262
263 INSTANTIATE_TEST_SUITE_P(
264 ChunkedMacWrapperOutputPrefixTestSuite, ChunkedMacWrapperOutputPrefixTest,
265 Values(OutputPrefixType::LEGACY, OutputPrefixType::RAW,
266 OutputPrefixType::CRUNCHY, OutputPrefixType::TINK));
267
TEST_P(ChunkedMacWrapperOutputPrefixTest,ComputeVerifyMac)268 TEST_P(ChunkedMacWrapperOutputPrefixTest, ComputeVerifyMac) {
269 OutputPrefixType output_prefix_type = GetParam();
270
271 KeysetInfo keyset_info;
272 auto mac_set = absl::make_unique<PrimitiveSet<ChunkedMac>>();
273
274 // Add primitives to primitive set.
275 ASSERT_THAT(
276 AddPrimitiveToSet(
277 /*key_id=*/0x12d66f, /*set_primary=*/true, output_prefix_type,
278 CreateFakeChunkedMac("chunkedmac:"), keyset_info, *mac_set),
279 IsOk());
280
281 // Wrap primitive set into a ChunkedMac.
282 util::StatusOr<std::unique_ptr<crypto::tink::ChunkedMac>> chunked_mac =
283 ChunkedMacWrapper().Wrap(std::move(mac_set));
284 ASSERT_THAT(chunked_mac.status(), IsOk());
285
286 // Compute MAC via wrapper.
287 util::StatusOr<std::unique_ptr<ChunkedMacComputation>> mac_computation =
288 (*chunked_mac)->CreateComputation();
289 ASSERT_THAT(mac_computation.status(), IsOk());
290 ASSERT_THAT((*mac_computation)->Update("inputdata"), IsOk());
291 util::StatusOr<std::string> tag = (*mac_computation)->ComputeMac();
292 ASSERT_THAT(tag.status(), IsOk());
293
294 // Verify MAC via wrapper.
295 util::StatusOr<std::unique_ptr<ChunkedMacVerification>> mac_verification =
296 (*chunked_mac)->CreateVerification(*tag);
297 ASSERT_THAT(mac_verification.status(), IsOk());
298 ASSERT_THAT((*mac_verification)->Update("inputdata"), IsOk());
299 ASSERT_THAT((*mac_verification)->VerifyMac(), IsOk());
300 }
301
302 } // namespace
303 } // namespace internal
304 } // namespace tink
305 } // namespace crypto
306