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/aead/aes_gcm_proto_serialization.h"
18
19 #include <string>
20
21 #include "absl/status/status.h"
22 #include "absl/types/optional.h"
23 #include "tink/aead/aes_gcm_key.h"
24 #include "tink/aead/aes_gcm_parameters.h"
25 #include "tink/internal/key_parser.h"
26 #include "tink/internal/key_serializer.h"
27 #include "tink/internal/mutable_serialization_registry.h"
28 #include "tink/internal/parameters_parser.h"
29 #include "tink/internal/parameters_serializer.h"
30 #include "tink/internal/proto_key_serialization.h"
31 #include "tink/internal/proto_parameters_serialization.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_gcm.pb.h"
38 #include "proto/tink.pb.h"
39
40 namespace crypto {
41 namespace tink {
42 namespace {
43
44 using ::google::crypto::tink::AesGcmKeyFormat;
45 using ::google::crypto::tink::OutputPrefixType;
46
47 using AesGcmProtoParametersParserImpl =
48 internal::ParametersParserImpl<internal::ProtoParametersSerialization,
49 AesGcmParameters>;
50 using AesGcmProtoParametersSerializerImpl =
51 internal::ParametersSerializerImpl<AesGcmParameters,
52 internal::ProtoParametersSerialization>;
53 using AesGcmProtoKeyParserImpl =
54 internal::KeyParserImpl<internal::ProtoKeySerialization, AesGcmKey>;
55 using AesGcmProtoKeySerializerImpl =
56 internal::KeySerializerImpl<AesGcmKey, internal::ProtoKeySerialization>;
57
58 const absl::string_view kTypeUrl =
59 "type.googleapis.com/google.crypto.tink.AesGcmKey";
60
ToVariant(OutputPrefixType output_prefix_type)61 util::StatusOr<AesGcmParameters::Variant> ToVariant(
62 OutputPrefixType output_prefix_type) {
63 switch (output_prefix_type) {
64 case OutputPrefixType::LEGACY:
65 ABSL_FALLTHROUGH_INTENDED; // Parse LEGACY output prefix as CRUNCHY.
66 case OutputPrefixType::CRUNCHY:
67 return AesGcmParameters::Variant::kCrunchy;
68 case OutputPrefixType::RAW:
69 return AesGcmParameters::Variant::kNoPrefix;
70 case OutputPrefixType::TINK:
71 return AesGcmParameters::Variant::kTink;
72 default:
73 return util::Status(absl::StatusCode::kInvalidArgument,
74 "Could not determine AesGcmParameters::Variant");
75 }
76 }
77
ToOutputPrefixType(AesGcmParameters::Variant variant)78 util::StatusOr<OutputPrefixType> ToOutputPrefixType(
79 AesGcmParameters::Variant variant) {
80 switch (variant) {
81 case AesGcmParameters::Variant::kCrunchy:
82 return OutputPrefixType::CRUNCHY;
83 case AesGcmParameters::Variant::kNoPrefix:
84 return OutputPrefixType::RAW;
85 case AesGcmParameters::Variant::kTink:
86 return OutputPrefixType::TINK;
87 default:
88 return util::Status(absl::StatusCode::kInvalidArgument,
89 "Could not determine output prefix type");
90 }
91 }
92
93 // Legacy Tink AES-GCM key proto format assumes 12-byte random IVs and 16-byte
94 // tags.
ValidateParamsForProto(const AesGcmParameters & params)95 util::Status ValidateParamsForProto(const AesGcmParameters& params) {
96 if (params.IvSizeInBytes() != 12) {
97 return util::Status(
98 absl::StatusCode::kInvalidArgument,
99 "Tink currently restricts AES-GCM IV size to 12 bytes.");
100 }
101 if (params.TagSizeInBytes() != 16) {
102 return util::Status(
103 absl::StatusCode::kInvalidArgument,
104 "Tink currently restricts AES-GCM tag size to 16 bytes.");
105 }
106 return util::OkStatus();
107 }
108
ParseParameters(const internal::ProtoParametersSerialization & serialization)109 util::StatusOr<AesGcmParameters> ParseParameters(
110 const internal::ProtoParametersSerialization& serialization) {
111 if (serialization.GetKeyTemplate().type_url() != kTypeUrl) {
112 return util::Status(absl::StatusCode::kInvalidArgument,
113 "Wrong type URL when parsing AesGcmParameters.");
114 }
115
116 AesGcmKeyFormat proto_key_format;
117 if (!proto_key_format.ParseFromString(
118 serialization.GetKeyTemplate().value())) {
119 return util::Status(absl::StatusCode::kInvalidArgument,
120 "Failed to parse AesGcmKeyFormat proto");
121 }
122 if (proto_key_format.version() != 0) {
123 return util::Status(absl::StatusCode::kInvalidArgument,
124 "Only version 0 keys are accepted.");
125 }
126
127 util::StatusOr<AesGcmParameters::Variant> variant =
128 ToVariant(serialization.GetKeyTemplate().output_prefix_type());
129 if (!variant.ok()) return variant.status();
130
131 // Legacy Tink AES-GCM key proto format assumes 12-byte random IVs and 16-byte
132 // tags.
133 return AesGcmParameters::Builder()
134 .SetVariant(*variant)
135 .SetKeySizeInBytes(proto_key_format.key_size())
136 .SetIvSizeInBytes(12)
137 .SetTagSizeInBytes(16)
138 .Build();
139 }
140
SerializeParameters(const AesGcmParameters & parameters)141 util::StatusOr<internal::ProtoParametersSerialization> SerializeParameters(
142 const AesGcmParameters& parameters) {
143 util::Status valid_params = ValidateParamsForProto(parameters);
144 if (!valid_params.ok()) return valid_params;
145
146 util::StatusOr<OutputPrefixType> output_prefix_type =
147 ToOutputPrefixType(parameters.GetVariant());
148 if (!output_prefix_type.ok()) return output_prefix_type.status();
149
150 AesGcmKeyFormat proto_key_format;
151 proto_key_format.set_key_size(parameters.KeySizeInBytes());
152
153 return internal::ProtoParametersSerialization::Create(
154 kTypeUrl, *output_prefix_type, proto_key_format.SerializeAsString());
155 }
156
ParseKey(const internal::ProtoKeySerialization & serialization,absl::optional<SecretKeyAccessToken> token)157 util::StatusOr<AesGcmKey> ParseKey(
158 const internal::ProtoKeySerialization& serialization,
159 absl::optional<SecretKeyAccessToken> token) {
160 if (serialization.TypeUrl() != kTypeUrl) {
161 return util::Status(absl::StatusCode::kInvalidArgument,
162 "Wrong type URL when parsing AesGcmKey.");
163 }
164 if (!token.has_value()) {
165 return util::Status(absl::StatusCode::kInvalidArgument,
166 "SecretKeyAccess is required");
167 }
168 google::crypto::tink::AesGcmKey proto_key;
169 RestrictedData restricted_data = serialization.SerializedKeyProto();
170 // OSS proto library complains if input is not converted to a string.
171 if (!proto_key.ParseFromString(
172 std::string(restricted_data.GetSecret(*token)))) {
173 return util::Status(absl::StatusCode::kInvalidArgument,
174 "Failed to parse AesGcmKey proto");
175 }
176 if (proto_key.version() != 0) {
177 return util::Status(absl::StatusCode::kInvalidArgument,
178 "Only version 0 keys are accepted.");
179 }
180
181 util::StatusOr<AesGcmParameters::Variant> variant =
182 ToVariant(serialization.GetOutputPrefixType());
183 if (!variant.ok()) return variant.status();
184
185 // Legacy AES-GCM key proto format assumes 12-byte random IVs and 16-byte
186 // tags.
187 util::StatusOr<AesGcmParameters> parameters =
188 AesGcmParameters::Builder()
189 .SetVariant(*variant)
190 .SetKeySizeInBytes(proto_key.key_value().length())
191 .SetIvSizeInBytes(12)
192 .SetTagSizeInBytes(16)
193 .Build();
194 if (!parameters.ok()) return parameters.status();
195
196 return AesGcmKey::Create(
197 *parameters, RestrictedData(proto_key.key_value(), *token),
198 serialization.IdRequirement(), GetPartialKeyAccess());
199 }
200
SerializeKey(const AesGcmKey & key,absl::optional<SecretKeyAccessToken> token)201 util::StatusOr<internal::ProtoKeySerialization> SerializeKey(
202 const AesGcmKey& key, absl::optional<SecretKeyAccessToken> token) {
203 util::Status valid_params = ValidateParamsForProto(key.GetParameters());
204 if (!valid_params.ok()) return valid_params;
205
206 util::StatusOr<RestrictedData> restricted_input =
207 key.GetKeyBytes(GetPartialKeyAccess());
208 if (!restricted_input.ok()) return restricted_input.status();
209 if (!token.has_value()) {
210 return util::Status(absl::StatusCode::kInvalidArgument,
211 "SecretKeyAccess is required");
212 }
213
214 google::crypto::tink::AesGcmKey proto_key;
215 proto_key.set_version(0);
216 // OSS proto library complains if input is not converted to a string.
217 proto_key.set_key_value(std::string(restricted_input->GetSecret(*token)));
218
219 util::StatusOr<OutputPrefixType> output_prefix_type =
220 ToOutputPrefixType(key.GetParameters().GetVariant());
221 if (!output_prefix_type.ok()) return output_prefix_type.status();
222
223 RestrictedData restricted_output =
224 RestrictedData(proto_key.SerializeAsString(), *token);
225 return internal::ProtoKeySerialization::Create(
226 kTypeUrl, restricted_output, google::crypto::tink::KeyData::SYMMETRIC,
227 *output_prefix_type, key.GetIdRequirement());
228 }
229
AesGcmProtoParametersParser()230 AesGcmProtoParametersParserImpl* AesGcmProtoParametersParser() {
231 static auto* parser =
232 new AesGcmProtoParametersParserImpl(kTypeUrl, ParseParameters);
233 return parser;
234 }
235
AesGcmProtoParametersSerializer()236 AesGcmProtoParametersSerializerImpl* AesGcmProtoParametersSerializer() {
237 static auto* serializer =
238 new AesGcmProtoParametersSerializerImpl(kTypeUrl, SerializeParameters);
239 return serializer;
240 }
241
AesGcmProtoKeyParser()242 AesGcmProtoKeyParserImpl* AesGcmProtoKeyParser() {
243 static auto* parser = new AesGcmProtoKeyParserImpl(kTypeUrl, ParseKey);
244 return parser;
245 }
246
AesGcmProtoKeySerializer()247 AesGcmProtoKeySerializerImpl* AesGcmProtoKeySerializer() {
248 static auto* serializer = new AesGcmProtoKeySerializerImpl(SerializeKey);
249 return serializer;
250 }
251
252 } // namespace
253
RegisterAesGcmProtoSerialization()254 util::Status RegisterAesGcmProtoSerialization() {
255 util::Status status =
256 internal::MutableSerializationRegistry::GlobalInstance()
257 .RegisterParametersParser(AesGcmProtoParametersParser());
258 if (!status.ok()) return status;
259
260 status = internal::MutableSerializationRegistry::GlobalInstance()
261 .RegisterParametersSerializer(AesGcmProtoParametersSerializer());
262 if (!status.ok()) return status;
263
264 status = internal::MutableSerializationRegistry::GlobalInstance()
265 .RegisterKeyParser(AesGcmProtoKeyParser());
266 if (!status.ok()) return status;
267
268 return internal::MutableSerializationRegistry::GlobalInstance()
269 .RegisterKeySerializer(AesGcmProtoKeySerializer());
270 }
271
272 } // namespace tink
273 } // namespace crypto
274