xref: /aosp_15_r20/external/tink/cc/aead/aes_gcm_proto_serialization.cc (revision e7b1675dde1b92d52ec075b0a92829627f2c52a5)
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