// Copyright 2023 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //////////////////////////////////////////////////////////////////////////////// #include "tink/mac/hmac_proto_serialization.h" #include #include "absl/status/status.h" #include "absl/types/optional.h" #include "tink/internal/key_parser.h" #include "tink/internal/key_serializer.h" #include "tink/internal/mutable_serialization_registry.h" #include "tink/internal/parameters_parser.h" #include "tink/internal/parameters_serializer.h" #include "tink/internal/proto_key_serialization.h" #include "tink/internal/proto_parameters_serialization.h" #include "tink/mac/hmac_key.h" #include "tink/mac/hmac_parameters.h" #include "tink/partial_key_access.h" #include "tink/restricted_data.h" #include "tink/secret_key_access_token.h" #include "tink/util/status.h" #include "tink/util/statusor.h" #include "proto/common.pb.h" #include "proto/hmac.pb.h" #include "proto/tink.pb.h" namespace crypto { namespace tink { namespace { using ::google::crypto::tink::HashType; using ::google::crypto::tink::HmacKeyFormat; using ::google::crypto::tink::HmacParams; using ::google::crypto::tink::OutputPrefixType; using HmacProtoParametersParserImpl = internal::ParametersParserImpl; using HmacProtoParametersSerializerImpl = internal::ParametersSerializerImpl; using HmacProtoKeyParserImpl = internal::KeyParserImpl; using HmacProtoKeySerializerImpl = internal::KeySerializerImpl; const absl::string_view kTypeUrl = "type.googleapis.com/google.crypto.tink.HmacKey"; util::StatusOr ToVariant( OutputPrefixType output_prefix_type) { switch (output_prefix_type) { case OutputPrefixType::CRUNCHY: return HmacParameters::Variant::kCrunchy; case OutputPrefixType::LEGACY: return HmacParameters::Variant::kLegacy; case OutputPrefixType::RAW: return HmacParameters::Variant::kNoPrefix; case OutputPrefixType::TINK: return HmacParameters::Variant::kTink; default: return util::Status(absl::StatusCode::kInvalidArgument, "Could not determine HmacParameters::Variant"); } } util::StatusOr ToOutputPrefixType( HmacParameters::Variant variant) { switch (variant) { case HmacParameters::Variant::kCrunchy: return OutputPrefixType::CRUNCHY; case HmacParameters::Variant::kLegacy: return OutputPrefixType::LEGACY; case HmacParameters::Variant::kNoPrefix: return OutputPrefixType::RAW; case HmacParameters::Variant::kTink: return OutputPrefixType::TINK; default: return util::Status(absl::StatusCode::kInvalidArgument, "Could not determine output prefix type"); } } util::StatusOr ToHashType(HashType hash_type) { switch (hash_type) { case HashType::SHA1: return HmacParameters::HashType::kSha1; case HashType::SHA224: return HmacParameters::HashType::kSha224; case HashType::SHA256: return HmacParameters::HashType::kSha256; case HashType::SHA384: return HmacParameters::HashType::kSha384; case HashType::SHA512: return HmacParameters::HashType::kSha512; default: return util::Status(absl::StatusCode::kInvalidArgument, "Could not determine HashType"); } } util::StatusOr ToProtoHashType(HmacParameters::HashType hash_type) { switch (hash_type) { case HmacParameters::HashType::kSha1: return HashType::SHA1; case HmacParameters::HashType::kSha224: return HashType::SHA224; case HmacParameters::HashType::kSha256: return HashType::SHA256; case HmacParameters::HashType::kSha384: return HashType::SHA384; case HmacParameters::HashType::kSha512: return HashType::SHA512; default: return util::Status(absl::StatusCode::kInvalidArgument, "Could not determine HmacParameters::HashType"); } } util::StatusOr ParseParameters( const internal::ProtoParametersSerialization& serialization) { if (serialization.GetKeyTemplate().type_url() != kTypeUrl) { return util::Status(absl::StatusCode::kInvalidArgument, "Wrong type URL when parsing HmacParameters."); } HmacKeyFormat proto_key_format; if (!proto_key_format.ParseFromString( serialization.GetKeyTemplate().value())) { return util::Status(absl::StatusCode::kInvalidArgument, "Failed to parse HmacKeyFormat proto"); } if (proto_key_format.version() != 0) { return util::Status( absl::StatusCode::kInvalidArgument, "Parsing HmacParameters failed: only version 0 is accepted"); } util::StatusOr variant = ToVariant(serialization.GetKeyTemplate().output_prefix_type()); if (!variant.ok()) return variant.status(); util::StatusOr hash_type = ToHashType(proto_key_format.params().hash()); if (!hash_type.ok()) return variant.status(); return HmacParameters::Create(proto_key_format.key_size(), proto_key_format.params().tag_size(), *hash_type, *variant); } util::StatusOr SerializeParameters( const HmacParameters& parameters) { util::StatusOr output_prefix_type = ToOutputPrefixType(parameters.GetVariant()); if (!output_prefix_type.ok()) return output_prefix_type.status(); util::StatusOr proto_hash_type = ToProtoHashType(parameters.GetHashType()); if (!proto_hash_type.ok()) return proto_hash_type.status(); HmacParams proto_params; proto_params.set_tag_size(parameters.CryptographicTagSizeInBytes()); proto_params.set_hash(*proto_hash_type); HmacKeyFormat proto_key_format; proto_key_format.set_key_size(parameters.KeySizeInBytes()); proto_key_format.set_version(0); *proto_key_format.mutable_params() = proto_params; return internal::ProtoParametersSerialization::Create( kTypeUrl, *output_prefix_type, proto_key_format.SerializeAsString()); } util::StatusOr ParseKey( const internal::ProtoKeySerialization& serialization, absl::optional token) { if (serialization.TypeUrl() != kTypeUrl) { return util::Status(absl::StatusCode::kInvalidArgument, "Wrong type URL when parsing HmacKey."); } if (!token.has_value()) { return util::Status(absl::StatusCode::kInvalidArgument, "SecretKeyAccess is required"); } google::crypto::tink::HmacKey proto_key; RestrictedData restricted_data = serialization.SerializedKeyProto(); // OSS proto library complains if input is not converted to a string. if (!proto_key.ParseFromString( std::string(restricted_data.GetSecret(*token)))) { return util::Status(absl::StatusCode::kInvalidArgument, "Failed to parse HmacKey proto"); } if (proto_key.version() != 0) { return util::Status(absl::StatusCode::kInvalidArgument, "Only version 0 keys are accepted."); } util::StatusOr variant = ToVariant(serialization.GetOutputPrefixType()); if (!variant.ok()) return variant.status(); util::StatusOr hash_type = ToHashType(proto_key.params().hash()); if (!hash_type.ok()) return variant.status(); util::StatusOr parameters = HmacParameters::Create( proto_key.key_value().length(), proto_key.params().tag_size(), *hash_type, *variant); if (!parameters.ok()) return parameters.status(); return HmacKey::Create(*parameters, RestrictedData(proto_key.key_value(), *token), serialization.IdRequirement(), GetPartialKeyAccess()); } util::StatusOr SerializeKey( const HmacKey& key, absl::optional token) { util::StatusOr restricted_input = key.GetKeyBytes(GetPartialKeyAccess()); if (!token.has_value()) { return util::Status(absl::StatusCode::kInvalidArgument, "SecretKeyAccess is required"); } if (!restricted_input.ok()) return restricted_input.status(); util::StatusOr proto_hash_type = ToProtoHashType(key.GetParameters().GetHashType()); if (!proto_hash_type.ok()) return proto_hash_type.status(); HmacParams proto_params; proto_params.set_tag_size(key.GetParameters().CryptographicTagSizeInBytes()); proto_params.set_hash(*proto_hash_type); google::crypto::tink::HmacKey proto_key; *proto_key.mutable_params() = proto_params; proto_key.set_version(0); // OSS proto library complains if input is not converted to a string. proto_key.set_key_value(std::string(restricted_input->GetSecret(*token))); util::StatusOr output_prefix_type = ToOutputPrefixType(key.GetParameters().GetVariant()); if (!output_prefix_type.ok()) return output_prefix_type.status(); RestrictedData restricted_output = RestrictedData(proto_key.SerializeAsString(), *token); return internal::ProtoKeySerialization::Create( kTypeUrl, restricted_output, google::crypto::tink::KeyData::SYMMETRIC, *output_prefix_type, key.GetIdRequirement()); } HmacProtoParametersParserImpl* HmacProtoParametersParser() { static auto* parser = new HmacProtoParametersParserImpl(kTypeUrl, ParseParameters); return parser; } HmacProtoParametersSerializerImpl* HmacProtoParametersSerializer() { static auto* serializer = new HmacProtoParametersSerializerImpl(kTypeUrl, SerializeParameters); return serializer; } HmacProtoKeyParserImpl* HmacProtoKeyParser() { static auto* parser = new HmacProtoKeyParserImpl(kTypeUrl, ParseKey); return parser; } HmacProtoKeySerializerImpl* HmacProtoKeySerializer() { static auto* serializer = new HmacProtoKeySerializerImpl(SerializeKey); return serializer; } } // namespace util::Status RegisterHmacProtoSerialization() { util::Status status = internal::MutableSerializationRegistry::GlobalInstance() .RegisterParametersParser(HmacProtoParametersParser()); if (!status.ok()) return status; status = internal::MutableSerializationRegistry::GlobalInstance() .RegisterParametersSerializer(HmacProtoParametersSerializer()); if (!status.ok()) return status; status = internal::MutableSerializationRegistry::GlobalInstance() .RegisterKeyParser(HmacProtoKeyParser()); if (!status.ok()) return status; return internal::MutableSerializationRegistry::GlobalInstance() .RegisterKeySerializer(HmacProtoKeySerializer()); } } // namespace tink } // namespace crypto