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 <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/hmac_key.h"
31 #include "tink/mac/hmac_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/common.pb.h"
38 #include "proto/hmac.pb.h"
39 #include "proto/tink.pb.h"
40
41 namespace crypto {
42 namespace tink {
43 namespace {
44
45 using ::google::crypto::tink::HashType;
46 using ::google::crypto::tink::HmacKeyFormat;
47 using ::google::crypto::tink::HmacParams;
48 using ::google::crypto::tink::OutputPrefixType;
49
50 using HmacProtoParametersParserImpl =
51 internal::ParametersParserImpl<internal::ProtoParametersSerialization,
52 HmacParameters>;
53 using HmacProtoParametersSerializerImpl =
54 internal::ParametersSerializerImpl<HmacParameters,
55 internal::ProtoParametersSerialization>;
56 using HmacProtoKeyParserImpl =
57 internal::KeyParserImpl<internal::ProtoKeySerialization, HmacKey>;
58 using HmacProtoKeySerializerImpl =
59 internal::KeySerializerImpl<HmacKey, internal::ProtoKeySerialization>;
60
61 const absl::string_view kTypeUrl =
62 "type.googleapis.com/google.crypto.tink.HmacKey";
63
ToVariant(OutputPrefixType output_prefix_type)64 util::StatusOr<HmacParameters::Variant> ToVariant(
65 OutputPrefixType output_prefix_type) {
66 switch (output_prefix_type) {
67 case OutputPrefixType::CRUNCHY:
68 return HmacParameters::Variant::kCrunchy;
69 case OutputPrefixType::LEGACY:
70 return HmacParameters::Variant::kLegacy;
71 case OutputPrefixType::RAW:
72 return HmacParameters::Variant::kNoPrefix;
73 case OutputPrefixType::TINK:
74 return HmacParameters::Variant::kTink;
75 default:
76 return util::Status(absl::StatusCode::kInvalidArgument,
77 "Could not determine HmacParameters::Variant");
78 }
79 }
80
ToOutputPrefixType(HmacParameters::Variant variant)81 util::StatusOr<OutputPrefixType> ToOutputPrefixType(
82 HmacParameters::Variant variant) {
83 switch (variant) {
84 case HmacParameters::Variant::kCrunchy:
85 return OutputPrefixType::CRUNCHY;
86 case HmacParameters::Variant::kLegacy:
87 return OutputPrefixType::LEGACY;
88 case HmacParameters::Variant::kNoPrefix:
89 return OutputPrefixType::RAW;
90 case HmacParameters::Variant::kTink:
91 return OutputPrefixType::TINK;
92 default:
93 return util::Status(absl::StatusCode::kInvalidArgument,
94 "Could not determine output prefix type");
95 }
96 }
97
ToHashType(HashType hash_type)98 util::StatusOr<HmacParameters::HashType> ToHashType(HashType hash_type) {
99 switch (hash_type) {
100 case HashType::SHA1:
101 return HmacParameters::HashType::kSha1;
102 case HashType::SHA224:
103 return HmacParameters::HashType::kSha224;
104 case HashType::SHA256:
105 return HmacParameters::HashType::kSha256;
106 case HashType::SHA384:
107 return HmacParameters::HashType::kSha384;
108 case HashType::SHA512:
109 return HmacParameters::HashType::kSha512;
110 default:
111 return util::Status(absl::StatusCode::kInvalidArgument,
112 "Could not determine HashType");
113 }
114 }
115
ToProtoHashType(HmacParameters::HashType hash_type)116 util::StatusOr<HashType> ToProtoHashType(HmacParameters::HashType hash_type) {
117 switch (hash_type) {
118 case HmacParameters::HashType::kSha1:
119 return HashType::SHA1;
120 case HmacParameters::HashType::kSha224:
121 return HashType::SHA224;
122 case HmacParameters::HashType::kSha256:
123 return HashType::SHA256;
124 case HmacParameters::HashType::kSha384:
125 return HashType::SHA384;
126 case HmacParameters::HashType::kSha512:
127 return HashType::SHA512;
128 default:
129 return util::Status(absl::StatusCode::kInvalidArgument,
130 "Could not determine HmacParameters::HashType");
131 }
132 }
133
ParseParameters(const internal::ProtoParametersSerialization & serialization)134 util::StatusOr<HmacParameters> ParseParameters(
135 const internal::ProtoParametersSerialization& serialization) {
136 if (serialization.GetKeyTemplate().type_url() != kTypeUrl) {
137 return util::Status(absl::StatusCode::kInvalidArgument,
138 "Wrong type URL when parsing HmacParameters.");
139 }
140
141 HmacKeyFormat proto_key_format;
142 if (!proto_key_format.ParseFromString(
143 serialization.GetKeyTemplate().value())) {
144 return util::Status(absl::StatusCode::kInvalidArgument,
145 "Failed to parse HmacKeyFormat proto");
146 }
147 if (proto_key_format.version() != 0) {
148 return util::Status(
149 absl::StatusCode::kInvalidArgument,
150 "Parsing HmacParameters failed: only version 0 is accepted");
151 }
152
153 util::StatusOr<HmacParameters::Variant> variant =
154 ToVariant(serialization.GetKeyTemplate().output_prefix_type());
155 if (!variant.ok()) return variant.status();
156
157 util::StatusOr<HmacParameters::HashType> hash_type =
158 ToHashType(proto_key_format.params().hash());
159 if (!hash_type.ok()) return variant.status();
160
161 return HmacParameters::Create(proto_key_format.key_size(),
162 proto_key_format.params().tag_size(),
163 *hash_type, *variant);
164 }
165
SerializeParameters(const HmacParameters & parameters)166 util::StatusOr<internal::ProtoParametersSerialization> SerializeParameters(
167 const HmacParameters& parameters) {
168 util::StatusOr<OutputPrefixType> output_prefix_type =
169 ToOutputPrefixType(parameters.GetVariant());
170 if (!output_prefix_type.ok()) return output_prefix_type.status();
171 util::StatusOr<HashType> proto_hash_type =
172 ToProtoHashType(parameters.GetHashType());
173 if (!proto_hash_type.ok()) return proto_hash_type.status();
174
175 HmacParams proto_params;
176 proto_params.set_tag_size(parameters.CryptographicTagSizeInBytes());
177 proto_params.set_hash(*proto_hash_type);
178 HmacKeyFormat proto_key_format;
179 proto_key_format.set_key_size(parameters.KeySizeInBytes());
180 proto_key_format.set_version(0);
181 *proto_key_format.mutable_params() = proto_params;
182
183 return internal::ProtoParametersSerialization::Create(
184 kTypeUrl, *output_prefix_type, proto_key_format.SerializeAsString());
185 }
186
ParseKey(const internal::ProtoKeySerialization & serialization,absl::optional<SecretKeyAccessToken> token)187 util::StatusOr<HmacKey> ParseKey(
188 const internal::ProtoKeySerialization& serialization,
189 absl::optional<SecretKeyAccessToken> token) {
190 if (serialization.TypeUrl() != kTypeUrl) {
191 return util::Status(absl::StatusCode::kInvalidArgument,
192 "Wrong type URL when parsing HmacKey.");
193 }
194 if (!token.has_value()) {
195 return util::Status(absl::StatusCode::kInvalidArgument,
196 "SecretKeyAccess is required");
197 }
198
199 google::crypto::tink::HmacKey proto_key;
200 RestrictedData restricted_data = serialization.SerializedKeyProto();
201 // OSS proto library complains if input is not converted to a string.
202 if (!proto_key.ParseFromString(
203 std::string(restricted_data.GetSecret(*token)))) {
204 return util::Status(absl::StatusCode::kInvalidArgument,
205 "Failed to parse HmacKey proto");
206 }
207 if (proto_key.version() != 0) {
208 return util::Status(absl::StatusCode::kInvalidArgument,
209 "Only version 0 keys are accepted.");
210 }
211
212 util::StatusOr<HmacParameters::Variant> variant =
213 ToVariant(serialization.GetOutputPrefixType());
214 if (!variant.ok()) return variant.status();
215 util::StatusOr<HmacParameters::HashType> hash_type =
216 ToHashType(proto_key.params().hash());
217 if (!hash_type.ok()) return variant.status();
218
219 util::StatusOr<HmacParameters> parameters = HmacParameters::Create(
220 proto_key.key_value().length(), proto_key.params().tag_size(), *hash_type,
221 *variant);
222 if (!parameters.ok()) return parameters.status();
223
224 return HmacKey::Create(*parameters,
225 RestrictedData(proto_key.key_value(), *token),
226 serialization.IdRequirement(), GetPartialKeyAccess());
227 }
228
SerializeKey(const HmacKey & key,absl::optional<SecretKeyAccessToken> token)229 util::StatusOr<internal::ProtoKeySerialization> SerializeKey(
230 const HmacKey& key, absl::optional<SecretKeyAccessToken> token) {
231 util::StatusOr<RestrictedData> restricted_input =
232 key.GetKeyBytes(GetPartialKeyAccess());
233 if (!token.has_value()) {
234 return util::Status(absl::StatusCode::kInvalidArgument,
235 "SecretKeyAccess is required");
236 }
237 if (!restricted_input.ok()) return restricted_input.status();
238 util::StatusOr<HashType> proto_hash_type =
239 ToProtoHashType(key.GetParameters().GetHashType());
240 if (!proto_hash_type.ok()) return proto_hash_type.status();
241
242 HmacParams proto_params;
243 proto_params.set_tag_size(key.GetParameters().CryptographicTagSizeInBytes());
244 proto_params.set_hash(*proto_hash_type);
245 google::crypto::tink::HmacKey proto_key;
246 *proto_key.mutable_params() = proto_params;
247 proto_key.set_version(0);
248 // OSS proto library complains if input is not converted to a string.
249 proto_key.set_key_value(std::string(restricted_input->GetSecret(*token)));
250
251 util::StatusOr<OutputPrefixType> output_prefix_type =
252 ToOutputPrefixType(key.GetParameters().GetVariant());
253 if (!output_prefix_type.ok()) return output_prefix_type.status();
254
255 RestrictedData restricted_output =
256 RestrictedData(proto_key.SerializeAsString(), *token);
257 return internal::ProtoKeySerialization::Create(
258 kTypeUrl, restricted_output, google::crypto::tink::KeyData::SYMMETRIC,
259 *output_prefix_type, key.GetIdRequirement());
260 }
261
HmacProtoParametersParser()262 HmacProtoParametersParserImpl* HmacProtoParametersParser() {
263 static auto* parser =
264 new HmacProtoParametersParserImpl(kTypeUrl, ParseParameters);
265 return parser;
266 }
267
HmacProtoParametersSerializer()268 HmacProtoParametersSerializerImpl* HmacProtoParametersSerializer() {
269 static auto* serializer =
270 new HmacProtoParametersSerializerImpl(kTypeUrl, SerializeParameters);
271 return serializer;
272 }
273
HmacProtoKeyParser()274 HmacProtoKeyParserImpl* HmacProtoKeyParser() {
275 static auto* parser = new HmacProtoKeyParserImpl(kTypeUrl, ParseKey);
276 return parser;
277 }
278
HmacProtoKeySerializer()279 HmacProtoKeySerializerImpl* HmacProtoKeySerializer() {
280 static auto* serializer = new HmacProtoKeySerializerImpl(SerializeKey);
281 return serializer;
282 }
283
284 } // namespace
285
RegisterHmacProtoSerialization()286 util::Status RegisterHmacProtoSerialization() {
287 util::Status status =
288 internal::MutableSerializationRegistry::GlobalInstance()
289 .RegisterParametersParser(HmacProtoParametersParser());
290 if (!status.ok()) return status;
291
292 status = internal::MutableSerializationRegistry::GlobalInstance()
293 .RegisterParametersSerializer(HmacProtoParametersSerializer());
294 if (!status.ok()) return status;
295
296 status = internal::MutableSerializationRegistry::GlobalInstance()
297 .RegisterKeyParser(HmacProtoKeyParser());
298 if (!status.ok()) return status;
299
300 return internal::MutableSerializationRegistry::GlobalInstance()
301 .RegisterKeySerializer(HmacProtoKeySerializer());
302 }
303
304 } // namespace tink
305 } // namespace crypto
306