1 // Copyright 2023 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/hmac_proto_serialization.h"
18
19 #include <memory>
20 #include <string>
21
22 #include "gmock/gmock.h"
23 #include "gtest/gtest.h"
24 #include "tink/insecure_secret_key_access.h"
25 #include "tink/internal/mutable_serialization_registry.h"
26 #include "tink/internal/proto_key_serialization.h"
27 #include "tink/internal/proto_parameters_serialization.h"
28 #include "tink/mac/hmac_key.h"
29 #include "tink/mac/hmac_parameters.h"
30 #include "tink/partial_key_access.h"
31 #include "tink/restricted_data.h"
32 #include "tink/subtle/random.h"
33 #include "tink/util/test_matchers.h"
34 #include "proto/common.pb.h"
35 #include "proto/hmac.pb.h"
36 #include "proto/tink.pb.h"
37
38 namespace crypto {
39 namespace tink {
40 namespace {
41
42 using ::crypto::tink::subtle::Random;
43 using ::crypto::tink::test::IsOk;
44 using ::crypto::tink::test::IsOkAndHolds;
45 using ::crypto::tink::test::StatusIs;
46 using ::google::crypto::tink::HashType;
47 using ::google::crypto::tink::HmacKeyFormat;
48 using ::google::crypto::tink::KeyData;
49 using ::google::crypto::tink::OutputPrefixType;
50 using ::testing::Eq;
51 using ::testing::IsTrue;
52 using ::testing::NotNull;
53 using ::testing::TestWithParam;
54 using ::testing::Values;
55
56 struct TestCase {
57 HmacParameters::Variant variant;
58 OutputPrefixType output_prefix_type;
59 HmacParameters::HashType hash_type;
60 HashType proto_hash_type;
61 int key_size;
62 int tag_size;
63 int total_size;
64 absl::optional<int> id;
65 std::string output_prefix;
66 };
67
68 class HmacProtoSerializationTest : public TestWithParam<TestCase> {
69 protected:
SetUp()70 void SetUp() override {
71 internal::MutableSerializationRegistry::GlobalInstance().Reset();
72 }
73 };
74
75 INSTANTIATE_TEST_SUITE_P(
76 HmacProtoSerializationTestSuite, HmacProtoSerializationTest,
77 Values(TestCase{HmacParameters::Variant::kTink, OutputPrefixType::TINK,
78 HmacParameters::HashType::kSha1, HashType::SHA1,
79 /*key_size=*/16, /*cryptographic_tag_size=*/10,
80 /*total_size=*/15, /*id=*/0x02030400,
81 /*output_prefix=*/std::string("\x01\x02\x03\x04\x00", 5)},
82 TestCase{HmacParameters::Variant::kCrunchy,
83 OutputPrefixType::CRUNCHY,
84 HmacParameters::HashType::kSha224, HashType::SHA224,
85 /*key_size=*/16, /*tag_size=*/12, /*total_size=*/17,
86 /*id=*/0x01030005,
87 /*output_prefix=*/std::string("\x00\x01\x03\x00\x05", 5)},
88 TestCase{HmacParameters::Variant::kLegacy, OutputPrefixType::LEGACY,
89 HmacParameters::HashType::kSha256, HashType::SHA256,
90 /*key_size=*/32, /*cryptographic_tag_size=*/14,
91 /*total_tag_size=*/19, /*id=*/0x01020304,
92 /*output_prefix=*/std::string("\x00\x01\x02\x03\x04", 5)},
93 TestCase{HmacParameters::Variant::kNoPrefix, OutputPrefixType::RAW,
94 HmacParameters::HashType::kSha384, HashType::SHA384,
95 /*key_size=*/32, /*cryptographic_tag_size=*/16,
96 /*total_tag_size=*/16, /*id=*/absl::nullopt,
97 /*output_prefix=*/""},
98 TestCase{HmacParameters::Variant::kNoPrefix, OutputPrefixType::RAW,
99 HmacParameters::HashType::kSha512, HashType::SHA512,
100 /*key_size=*/32, /*cryptographic_tag_size=*/20,
101 /*total_tag_size=*/20, /*id=*/absl::nullopt,
102 /*output_prefix=*/""}));
103
TEST_P(HmacProtoSerializationTest,ParseParameters)104 TEST_P(HmacProtoSerializationTest, ParseParameters) {
105 TestCase test_case = GetParam();
106 ASSERT_THAT(RegisterHmacProtoSerialization(), IsOk());
107
108 HmacKeyFormat key_format_proto;
109 key_format_proto.set_key_size(test_case.key_size);
110 key_format_proto.mutable_params()->set_tag_size(test_case.tag_size);
111 key_format_proto.mutable_params()->set_hash(test_case.proto_hash_type);
112
113 util::StatusOr<internal::ProtoParametersSerialization> serialization =
114 internal::ProtoParametersSerialization::Create(
115 "type.googleapis.com/google.crypto.tink.HmacKey",
116 test_case.output_prefix_type, key_format_proto.SerializeAsString());
117 ASSERT_THAT(serialization, IsOk());
118
119 util::StatusOr<std::unique_ptr<Parameters>> parsed_parameters =
120 internal::MutableSerializationRegistry::GlobalInstance().ParseParameters(
121 *serialization);
122 ASSERT_THAT(parsed_parameters, IsOk());
123 EXPECT_THAT((*parsed_parameters)->HasIdRequirement(),
124 test_case.id.has_value());
125
126 util::StatusOr<HmacParameters> expected_parameters =
127 HmacParameters::Create(test_case.key_size, test_case.tag_size,
128 test_case.hash_type, test_case.variant);
129 ASSERT_THAT(expected_parameters, IsOk());
130 ASSERT_THAT(**parsed_parameters, Eq(*expected_parameters));
131 }
132
TEST_F(HmacProtoSerializationTest,ParseParametersWithInvalidSerialization)133 TEST_F(HmacProtoSerializationTest, ParseParametersWithInvalidSerialization) {
134 ASSERT_THAT(RegisterHmacProtoSerialization(), IsOk());
135
136 util::StatusOr<internal::ProtoParametersSerialization> serialization =
137 internal::ProtoParametersSerialization::Create(
138 "type.googleapis.com/google.crypto.tink.HmacKey",
139 OutputPrefixType::RAW, "invalid_serialization");
140 ASSERT_THAT(serialization, IsOk());
141
142 util::StatusOr<std::unique_ptr<Parameters>> params =
143 internal::MutableSerializationRegistry::GlobalInstance().ParseParameters(
144 *serialization);
145 ASSERT_THAT(params.status(), StatusIs(absl::StatusCode::kInvalidArgument));
146 }
147
TEST_F(HmacProtoSerializationTest,ParseParametersWithInvalidVersion)148 TEST_F(HmacProtoSerializationTest, ParseParametersWithInvalidVersion) {
149 ASSERT_THAT(RegisterHmacProtoSerialization(), IsOk());
150
151 HmacKeyFormat key_format_proto;
152 key_format_proto.set_key_size(16);
153 key_format_proto.set_version(1); // Invalid version.
154 key_format_proto.mutable_params()->set_tag_size(10);
155 key_format_proto.mutable_params()->set_hash(HashType::SHA256);
156
157 util::StatusOr<internal::ProtoParametersSerialization> serialization =
158 internal::ProtoParametersSerialization::Create(
159 "type.googleapis.com/google.crypto.tink.HmacKey",
160 OutputPrefixType::RAW, key_format_proto.SerializeAsString());
161 ASSERT_THAT(serialization, IsOk());
162
163 util::StatusOr<std::unique_ptr<Parameters>> params =
164 internal::MutableSerializationRegistry::GlobalInstance().ParseParameters(
165 *serialization);
166 ASSERT_THAT(params.status(), StatusIs(absl::StatusCode::kInvalidArgument));
167 }
168
TEST_F(HmacProtoSerializationTest,ParseParametersWithUnkownOutputPrefix)169 TEST_F(HmacProtoSerializationTest, ParseParametersWithUnkownOutputPrefix) {
170 ASSERT_THAT(RegisterHmacProtoSerialization(), IsOk());
171
172 HmacKeyFormat key_format_proto;
173 key_format_proto.set_key_size(16);
174 key_format_proto.mutable_params()->set_tag_size(10);
175
176 util::StatusOr<internal::ProtoParametersSerialization> serialization =
177 internal::ProtoParametersSerialization::Create(
178 "type.googleapis.com/google.crypto.tink.HmacKey",
179 OutputPrefixType::UNKNOWN_PREFIX,
180 key_format_proto.SerializeAsString());
181 ASSERT_THAT(serialization, IsOk());
182
183 util::StatusOr<std::unique_ptr<Parameters>> params =
184 internal::MutableSerializationRegistry::GlobalInstance().ParseParameters(
185 *serialization);
186 ASSERT_THAT(params.status(), StatusIs(absl::StatusCode::kInvalidArgument));
187 }
188
TEST_P(HmacProtoSerializationTest,SerializeParameters)189 TEST_P(HmacProtoSerializationTest, SerializeParameters) {
190 TestCase test_case = GetParam();
191 ASSERT_THAT(RegisterHmacProtoSerialization(), IsOk());
192
193 util::StatusOr<HmacParameters> parameters =
194 HmacParameters::Create(test_case.key_size, test_case.tag_size,
195 test_case.hash_type, test_case.variant);
196 ASSERT_THAT(parameters, IsOk());
197
198 util::StatusOr<std::unique_ptr<Serialization>> serialization =
199 internal::MutableSerializationRegistry::GlobalInstance()
200 .SerializeParameters<internal::ProtoParametersSerialization>(
201 *parameters);
202 ASSERT_THAT(serialization, IsOk());
203 EXPECT_THAT((*serialization)->ObjectIdentifier(),
204 Eq("type.googleapis.com/google.crypto.tink.HmacKey"));
205
206 const internal::ProtoParametersSerialization* proto_serialization =
207 dynamic_cast<const internal::ProtoParametersSerialization*>(
208 serialization->get());
209 ASSERT_THAT(proto_serialization, NotNull());
210 EXPECT_THAT(proto_serialization->GetKeyTemplate().type_url(),
211 Eq("type.googleapis.com/google.crypto.tink.HmacKey"));
212 EXPECT_THAT(proto_serialization->GetKeyTemplate().output_prefix_type(),
213 Eq(test_case.output_prefix_type));
214
215 HmacKeyFormat key_format;
216 ASSERT_THAT(
217 key_format.ParseFromString(proto_serialization->GetKeyTemplate().value()),
218 IsTrue());
219 ASSERT_THAT(key_format.key_size(), Eq(test_case.key_size));
220 ASSERT_THAT(key_format.params().tag_size(), Eq(test_case.tag_size));
221 ASSERT_THAT(key_format.params().hash(), Eq(test_case.proto_hash_type));
222 }
223
TEST_P(HmacProtoSerializationTest,ParseKey)224 TEST_P(HmacProtoSerializationTest, ParseKey) {
225 TestCase test_case = GetParam();
226 ASSERT_THAT(RegisterHmacProtoSerialization(), IsOk());
227
228 std::string raw_key_bytes = Random::GetRandomBytes(test_case.key_size);
229 google::crypto::tink::HmacKey key_proto;
230 key_proto.set_version(0);
231 key_proto.set_key_value(raw_key_bytes);
232 key_proto.mutable_params()->set_tag_size(test_case.tag_size);
233 key_proto.mutable_params()->set_hash(test_case.proto_hash_type);
234 RestrictedData serialized_key = RestrictedData(
235 key_proto.SerializeAsString(), InsecureSecretKeyAccess::Get());
236
237 util::StatusOr<internal::ProtoKeySerialization> serialization =
238 internal::ProtoKeySerialization::Create(
239 "type.googleapis.com/google.crypto.tink.HmacKey", serialized_key,
240 KeyData::SYMMETRIC, test_case.output_prefix_type, test_case.id);
241 ASSERT_THAT(serialization, IsOk());
242
243 util::StatusOr<std::unique_ptr<Key>> parsed_key =
244 internal::MutableSerializationRegistry::GlobalInstance().ParseKey(
245 *serialization, InsecureSecretKeyAccess::Get());
246 ASSERT_THAT(parsed_key, IsOk());
247 EXPECT_THAT((*parsed_key)->GetParameters().HasIdRequirement(),
248 test_case.id.has_value());
249 EXPECT_THAT((*parsed_key)->GetIdRequirement(), Eq(test_case.id));
250
251 util::StatusOr<HmacParameters> expected_parameters =
252 HmacParameters::Create(test_case.key_size, test_case.tag_size,
253 test_case.hash_type, test_case.variant);
254 ASSERT_THAT(expected_parameters, IsOk());
255 util::StatusOr<HmacKey> expected_key = HmacKey::Create(
256 *expected_parameters,
257 RestrictedData(raw_key_bytes, InsecureSecretKeyAccess::Get()),
258 test_case.id, GetPartialKeyAccess());
259
260 ASSERT_THAT(expected_key, IsOk());
261 ASSERT_THAT(**parsed_key, Eq(*expected_key));
262 }
263
TEST_F(HmacProtoSerializationTest,ParseKeyWithInvalidSerialization)264 TEST_F(HmacProtoSerializationTest, ParseKeyWithInvalidSerialization) {
265 ASSERT_THAT(RegisterHmacProtoSerialization(), IsOk());
266
267 std::string raw_key_bytes = Random::GetRandomBytes(16);
268 google::crypto::tink::HmacKey key_proto;
269 key_proto.set_version(0);
270 key_proto.set_key_value(raw_key_bytes);
271 key_proto.mutable_params()->set_tag_size(10);
272 key_proto.mutable_params()->set_hash(HashType::SHA256);
273 RestrictedData serialized_key =
274 RestrictedData("invalid_serialization", InsecureSecretKeyAccess::Get());
275
276 util::StatusOr<internal::ProtoKeySerialization> serialization =
277 internal::ProtoKeySerialization::Create(
278 "type.googleapis.com/google.crypto.tink.HmacKey", serialized_key,
279 KeyData::SYMMETRIC, OutputPrefixType::TINK,
280 /*id_requirement=*/0x23456789);
281 ASSERT_THAT(serialization, IsOk());
282
283 util::StatusOr<std::unique_ptr<Key>> key =
284 internal::MutableSerializationRegistry::GlobalInstance().ParseKey(
285 *serialization, InsecureSecretKeyAccess::Get());
286 ASSERT_THAT(key.status(), StatusIs(absl::StatusCode::kInvalidArgument));
287 }
288
TEST_F(HmacProtoSerializationTest,ParseKeyWithInvalidVersion)289 TEST_F(HmacProtoSerializationTest, ParseKeyWithInvalidVersion) {
290 ASSERT_THAT(RegisterHmacProtoSerialization(), IsOk());
291
292 std::string raw_key_bytes = Random::GetRandomBytes(16);
293 google::crypto::tink::HmacKey key_proto;
294 key_proto.set_version(1); // Invalid version number.
295 key_proto.set_key_value(raw_key_bytes);
296 key_proto.mutable_params()->set_tag_size(10);
297 key_proto.mutable_params()->set_hash(HashType::SHA256);
298 RestrictedData serialized_key = RestrictedData(
299 key_proto.SerializeAsString(), InsecureSecretKeyAccess::Get());
300
301 util::StatusOr<internal::ProtoKeySerialization> serialization =
302 internal::ProtoKeySerialization::Create(
303 "type.googleapis.com/google.crypto.tink.HmacKey", serialized_key,
304 KeyData::SYMMETRIC, OutputPrefixType::TINK,
305 /*id_requirement=*/0x23456789);
306 ASSERT_THAT(serialization, IsOk());
307
308 util::StatusOr<std::unique_ptr<Key>> key =
309 internal::MutableSerializationRegistry::GlobalInstance().ParseKey(
310 *serialization, InsecureSecretKeyAccess::Get());
311 ASSERT_THAT(key.status(), StatusIs(absl::StatusCode::kInvalidArgument));
312 }
313
TEST_F(HmacProtoSerializationTest,ParseKeyWithoutSecretKeyAccess)314 TEST_F(HmacProtoSerializationTest, ParseKeyWithoutSecretKeyAccess) {
315 ASSERT_THAT(RegisterHmacProtoSerialization(), IsOk());
316
317 std::string raw_key_bytes = Random::GetRandomBytes(16);
318 google::crypto::tink::HmacKey key_proto;
319 key_proto.set_version(0);
320 key_proto.set_key_value(raw_key_bytes);
321 key_proto.mutable_params()->set_tag_size(10);
322 key_proto.mutable_params()->set_hash(HashType::SHA256);
323 RestrictedData serialized_key = RestrictedData(
324 key_proto.SerializeAsString(), InsecureSecretKeyAccess::Get());
325
326 util::StatusOr<internal::ProtoKeySerialization> serialization =
327 internal::ProtoKeySerialization::Create(
328 "type.googleapis.com/google.crypto.tink.HmacKey", serialized_key,
329 KeyData::SYMMETRIC, OutputPrefixType::TINK,
330 /*id_requirement=*/0x23456789);
331 ASSERT_THAT(serialization, IsOk());
332
333 util::StatusOr<std::unique_ptr<Key>> key =
334 internal::MutableSerializationRegistry::GlobalInstance().ParseKey(
335 *serialization, absl::nullopt);
336 ASSERT_THAT(key.status(), StatusIs(absl::StatusCode::kInvalidArgument));
337 }
338
TEST_P(HmacProtoSerializationTest,SerializeKey)339 TEST_P(HmacProtoSerializationTest, SerializeKey) {
340 TestCase test_case = GetParam();
341 ASSERT_THAT(RegisterHmacProtoSerialization(), IsOk());
342
343 util::StatusOr<HmacParameters> parameters =
344 HmacParameters::Create(test_case.key_size, test_case.tag_size,
345 test_case.hash_type, test_case.variant);
346 ASSERT_THAT(parameters, IsOk());
347
348 std::string raw_key_bytes = Random::GetRandomBytes(test_case.key_size);
349 util::StatusOr<HmacKey> key = HmacKey::Create(
350 *parameters,
351 RestrictedData(raw_key_bytes, InsecureSecretKeyAccess::Get()),
352 test_case.id, GetPartialKeyAccess());
353 ASSERT_THAT(key, IsOk());
354
355 util::StatusOr<std::unique_ptr<Serialization>> serialization =
356 internal::MutableSerializationRegistry::GlobalInstance()
357 .SerializeKey<internal::ProtoKeySerialization>(
358 *key, InsecureSecretKeyAccess::Get());
359 ASSERT_THAT(serialization, IsOk());
360 EXPECT_THAT((*serialization)->ObjectIdentifier(),
361 Eq("type.googleapis.com/google.crypto.tink.HmacKey"));
362
363 const internal::ProtoKeySerialization* proto_serialization =
364 dynamic_cast<const internal::ProtoKeySerialization*>(
365 serialization->get());
366 ASSERT_THAT(proto_serialization, NotNull());
367 EXPECT_THAT(proto_serialization->TypeUrl(),
368 Eq("type.googleapis.com/google.crypto.tink.HmacKey"));
369 EXPECT_THAT(proto_serialization->KeyMaterialType(), Eq(KeyData::SYMMETRIC));
370 EXPECT_THAT(proto_serialization->GetOutputPrefixType(),
371 Eq(test_case.output_prefix_type));
372 EXPECT_THAT(proto_serialization->IdRequirement(), Eq(test_case.id));
373
374 google::crypto::tink::HmacKey proto_key;
375 // OSS proto library complains if input is not converted to a string.
376 ASSERT_THAT(proto_key.ParseFromString(std::string(
377 proto_serialization->SerializedKeyProto().GetSecret(
378 InsecureSecretKeyAccess::Get()))),
379 IsTrue());
380 EXPECT_THAT(proto_key.key_value().size(), Eq(test_case.key_size));
381 EXPECT_THAT(proto_key.params().tag_size(), Eq(test_case.tag_size));
382 EXPECT_THAT(proto_key.params().hash(), Eq(test_case.proto_hash_type));
383 }
384
TEST_F(HmacProtoSerializationTest,SerializeKeyWithoutSecretKeyAccess)385 TEST_F(HmacProtoSerializationTest, SerializeKeyWithoutSecretKeyAccess) {
386 ASSERT_THAT(RegisterHmacProtoSerialization(), IsOk());
387
388 util::StatusOr<HmacParameters> parameters = HmacParameters::Create(
389 /*key_size_in_bytes=*/16, /*cryptographic_tag_size_in_bytes=*/10,
390 HmacParameters::HashType::kSha256, HmacParameters::Variant::kNoPrefix);
391 ASSERT_THAT(parameters, IsOk());
392
393 std::string raw_key_bytes = Random::GetRandomBytes(16);
394 util::StatusOr<HmacKey> key = HmacKey::Create(
395 *parameters,
396 RestrictedData(raw_key_bytes, InsecureSecretKeyAccess::Get()),
397 /*id_requirement=*/absl::nullopt, GetPartialKeyAccess());
398 ASSERT_THAT(key, IsOk());
399
400 util::StatusOr<std::unique_ptr<Serialization>> serialization =
401 internal::MutableSerializationRegistry::GlobalInstance()
402 .SerializeKey<internal::ProtoKeySerialization>(*key, absl::nullopt);
403 ASSERT_THAT(serialization.status(),
404 StatusIs(absl::StatusCode::kInvalidArgument));
405 }
406
407 } // namespace
408 } // namespace tink
409 } // namespace crypto
410