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