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 <string>
20
21 #include "absl/status/status.h"
22 #include "absl/types/optional.h"
23 #include "tink/internal/key_parser.h"
24 #include "tink/internal/key_serializer.h"
25 #include "tink/internal/mutable_serialization_registry.h"
26 #include "tink/internal/parameters_parser.h"
27 #include "tink/internal/parameters_serializer.h"
28 #include "tink/internal/proto_key_serialization.h"
29 #include "tink/internal/proto_parameters_serialization.h"
30 #include "tink/mac/aes_cmac_key.h"
31 #include "tink/mac/aes_cmac_parameters.h"
32 #include "tink/partial_key_access.h"
33 #include "tink/restricted_data.h"
34 #include "tink/secret_key_access_token.h"
35 #include "tink/util/status.h"
36 #include "tink/util/statusor.h"
37 #include "proto/aes_cmac.pb.h"
38 #include "proto/tink.pb.h"
39
40 namespace crypto {
41 namespace tink {
42 namespace {
43
44 using ::google::crypto::tink::AesCmacKeyFormat;
45 using ::google::crypto::tink::AesCmacParams;
46 using ::google::crypto::tink::OutputPrefixType;
47
48 using AesCmacProtoParametersParserImpl =
49 internal::ParametersParserImpl<internal::ProtoParametersSerialization,
50 AesCmacParameters>;
51 using AesCmacProtoParametersSerializerImpl =
52 internal::ParametersSerializerImpl<AesCmacParameters,
53 internal::ProtoParametersSerialization>;
54 using AesCmacProtoKeyParserImpl =
55 internal::KeyParserImpl<internal::ProtoKeySerialization, AesCmacKey>;
56 using AesCmacProtoKeySerializerImpl =
57 internal::KeySerializerImpl<AesCmacKey, internal::ProtoKeySerialization>;
58
59 const absl::string_view kTypeUrl =
60 "type.googleapis.com/google.crypto.tink.AesCmacKey";
61
ToVariant(OutputPrefixType output_prefix_type)62 util::StatusOr<AesCmacParameters::Variant> ToVariant(
63 OutputPrefixType output_prefix_type) {
64 switch (output_prefix_type) {
65 case OutputPrefixType::CRUNCHY:
66 return AesCmacParameters::Variant::kCrunchy;
67 case OutputPrefixType::LEGACY:
68 return AesCmacParameters::Variant::kLegacy;
69 case OutputPrefixType::RAW:
70 return AesCmacParameters::Variant::kNoPrefix;
71 case OutputPrefixType::TINK:
72 return AesCmacParameters::Variant::kTink;
73 default:
74 return util::Status(absl::StatusCode::kInvalidArgument,
75 "Could not determine AesCmacParameters::Variant");
76 }
77 }
78
ToOutputPrefixType(AesCmacParameters::Variant variant)79 util::StatusOr<OutputPrefixType> ToOutputPrefixType(
80 AesCmacParameters::Variant variant) {
81 switch (variant) {
82 case AesCmacParameters::Variant::kCrunchy:
83 return OutputPrefixType::CRUNCHY;
84 case AesCmacParameters::Variant::kLegacy:
85 return OutputPrefixType::LEGACY;
86 case AesCmacParameters::Variant::kNoPrefix:
87 return OutputPrefixType::RAW;
88 case AesCmacParameters::Variant::kTink:
89 return OutputPrefixType::TINK;
90 default:
91 return util::Status(absl::StatusCode::kInvalidArgument,
92 "Could not determine output prefix type");
93 }
94 }
95
ParseParameters(const internal::ProtoParametersSerialization & serialization)96 util::StatusOr<AesCmacParameters> ParseParameters(
97 const internal::ProtoParametersSerialization& serialization) {
98 if (serialization.GetKeyTemplate().type_url() != kTypeUrl) {
99 return util::Status(absl::StatusCode::kInvalidArgument,
100 "Wrong type URL when parsing AesCmacParameters.");
101 }
102
103 AesCmacKeyFormat proto_key_format;
104 if (!proto_key_format.ParseFromString(
105 serialization.GetKeyTemplate().value())) {
106 return util::Status(absl::StatusCode::kInvalidArgument,
107 "Failed to parse AesCmacKeyFormat proto");
108 }
109
110 util::StatusOr<AesCmacParameters::Variant> variant =
111 ToVariant(serialization.GetKeyTemplate().output_prefix_type());
112 if (!variant.ok()) return variant.status();
113
114 return AesCmacParameters::Create(proto_key_format.key_size(),
115 proto_key_format.params().tag_size(),
116 *variant);
117 }
118
SerializeParameters(const AesCmacParameters & parameters)119 util::StatusOr<internal::ProtoParametersSerialization> SerializeParameters(
120 const AesCmacParameters& parameters) {
121 util::StatusOr<OutputPrefixType> output_prefix_type =
122 ToOutputPrefixType(parameters.GetVariant());
123 if (!output_prefix_type.ok()) return output_prefix_type.status();
124
125 AesCmacParams proto_params;
126 proto_params.set_tag_size(parameters.CryptographicTagSizeInBytes());
127 AesCmacKeyFormat proto_key_format;
128 proto_key_format.set_key_size(parameters.KeySizeInBytes());
129 *proto_key_format.mutable_params() = proto_params;
130
131 return internal::ProtoParametersSerialization::Create(
132 kTypeUrl, *output_prefix_type, proto_key_format.SerializeAsString());
133 }
134
ParseKey(const internal::ProtoKeySerialization & serialization,absl::optional<SecretKeyAccessToken> token)135 util::StatusOr<AesCmacKey> ParseKey(
136 const internal::ProtoKeySerialization& serialization,
137 absl::optional<SecretKeyAccessToken> token) {
138 if (serialization.TypeUrl() != kTypeUrl) {
139 return util::Status(absl::StatusCode::kInvalidArgument,
140 "Wrong type URL when parsing AesCmacKey.");
141 }
142 if (!token.has_value()) {
143 return util::Status(absl::StatusCode::kInvalidArgument,
144 "SecretKeyAccess is required");
145 }
146 google::crypto::tink::AesCmacKey proto_key;
147 RestrictedData restricted_data = serialization.SerializedKeyProto();
148 // OSS proto library complains if input is not converted to a string.
149 if (!proto_key.ParseFromString(
150 std::string(restricted_data.GetSecret(*token)))) {
151 return util::Status(absl::StatusCode::kInvalidArgument,
152 "Failed to parse AesCmacKey proto");
153 }
154 if (proto_key.version() != 0) {
155 return util::Status(absl::StatusCode::kInvalidArgument,
156 "Only version 0 keys are accepted.");
157 }
158
159 util::StatusOr<AesCmacParameters::Variant> variant =
160 ToVariant(serialization.GetOutputPrefixType());
161 if (!variant.ok()) return variant.status();
162
163 util::StatusOr<AesCmacParameters> parameters = AesCmacParameters::Create(
164 proto_key.key_value().length(), proto_key.params().tag_size(), *variant);
165 if (!parameters.ok()) return parameters.status();
166
167 util::StatusOr<AesCmacKey> key = AesCmacKey::Create(
168 *parameters, RestrictedData(proto_key.key_value(), *token),
169 serialization.IdRequirement(), GetPartialKeyAccess());
170 if (!key.ok()) return key.status();
171
172 return *key;
173 }
174
SerializeKey(const AesCmacKey & key,absl::optional<SecretKeyAccessToken> token)175 util::StatusOr<internal::ProtoKeySerialization> SerializeKey(
176 const AesCmacKey& key, absl::optional<SecretKeyAccessToken> token) {
177 util::StatusOr<RestrictedData> restricted_input =
178 key.GetKeyBytes(GetPartialKeyAccess());
179 if (!restricted_input.ok()) return restricted_input.status();
180 if (!token.has_value()) {
181 return util::Status(absl::StatusCode::kInvalidArgument,
182 "SecretKeyAccess is required");
183 }
184
185 AesCmacParams proto_params;
186 proto_params.set_tag_size(key.GetParameters().CryptographicTagSizeInBytes());
187 google::crypto::tink::AesCmacKey proto_key;
188 *proto_key.mutable_params() = proto_params;
189 proto_key.set_version(0);
190 // OSS proto library complains if input is not converted to a string.
191 proto_key.set_key_value(std::string(restricted_input->GetSecret(*token)));
192
193 util::StatusOr<OutputPrefixType> output_prefix_type =
194 ToOutputPrefixType(key.GetParameters().GetVariant());
195 if (!output_prefix_type.ok()) return output_prefix_type.status();
196
197 RestrictedData restricted_output =
198 RestrictedData(proto_key.SerializeAsString(), *token);
199 return internal::ProtoKeySerialization::Create(
200 kTypeUrl, restricted_output, google::crypto::tink::KeyData::SYMMETRIC,
201 *output_prefix_type, key.GetIdRequirement());
202 }
203
AesCmacProtoParametersParser()204 AesCmacProtoParametersParserImpl* AesCmacProtoParametersParser() {
205 static auto* parser =
206 new AesCmacProtoParametersParserImpl(kTypeUrl, ParseParameters);
207 return parser;
208 }
209
AesCmacProtoParametersSerializer()210 AesCmacProtoParametersSerializerImpl* AesCmacProtoParametersSerializer() {
211 static auto* serializer =
212 new AesCmacProtoParametersSerializerImpl(kTypeUrl, SerializeParameters);
213 return serializer;
214 }
215
AesCmacProtoKeyParser()216 AesCmacProtoKeyParserImpl* AesCmacProtoKeyParser() {
217 static auto* parser = new AesCmacProtoKeyParserImpl(kTypeUrl, ParseKey);
218 return parser;
219 }
220
AesCmacProtoKeySerializer()221 AesCmacProtoKeySerializerImpl* AesCmacProtoKeySerializer() {
222 static auto* serializer = new AesCmacProtoKeySerializerImpl(SerializeKey);
223 return serializer;
224 }
225
226 } // namespace
227
RegisterAesCmacProtoSerialization()228 util::Status RegisterAesCmacProtoSerialization() {
229 util::Status status =
230 internal::MutableSerializationRegistry::GlobalInstance()
231 .RegisterParametersParser(AesCmacProtoParametersParser());
232 if (!status.ok()) return status;
233
234 status =
235 internal::MutableSerializationRegistry::GlobalInstance()
236 .RegisterParametersSerializer(AesCmacProtoParametersSerializer());
237 if (!status.ok()) return status;
238
239 status = internal::MutableSerializationRegistry::GlobalInstance()
240 .RegisterKeyParser(AesCmacProtoKeyParser());
241 if (!status.ok()) return status;
242
243 return internal::MutableSerializationRegistry::GlobalInstance()
244 .RegisterKeySerializer(AesCmacProtoKeySerializer());
245 }
246
247 } // namespace tink
248 } // namespace crypto
249