// 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/signature/ed25519_proto_serialization.h" #include #include "absl/status/status.h" #include "absl/strings/string_view.h" #include "absl/types/optional.h" #include "tink/insecure_secret_key_access.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/partial_key_access.h" #include "tink/restricted_data.h" #include "tink/secret_key_access_token.h" #include "tink/signature/ed25519_parameters.h" #include "tink/signature/ed25519_private_key.h" #include "tink/signature/ed25519_public_key.h" #include "tink/util/status.h" #include "tink/util/statusor.h" #include "proto/ed25519.pb.h" #include "proto/tink.pb.h" namespace crypto { namespace tink { namespace { using ::google::crypto::tink::Ed25519KeyFormat; using ::google::crypto::tink::KeyData; using ::google::crypto::tink::OutputPrefixType; using Ed25519ProtoParametersParserImpl = internal::ParametersParserImpl; using Ed25519ProtoParametersSerializerImpl = internal::ParametersSerializerImpl; using Ed25519ProtoPublicKeyParserImpl = internal::KeyParserImpl; using Ed25519ProtoPublicKeySerializerImpl = internal::KeySerializerImpl; using Ed25519ProtoPrivateKeyParserImpl = internal::KeyParserImpl; using Ed25519ProtoPrivateKeySerializerImpl = internal::KeySerializerImpl; const absl::string_view kPublicTypeUrl = "type.googleapis.com/google.crypto.tink.Ed25519PublicKey"; const absl::string_view kPrivateTypeUrl = "type.googleapis.com/google.crypto.tink.Ed25519PrivateKey"; util::StatusOr ToVariant( OutputPrefixType output_prefix_type) { switch (output_prefix_type) { case OutputPrefixType::LEGACY: return Ed25519Parameters::Variant::kLegacy; case OutputPrefixType::CRUNCHY: return Ed25519Parameters::Variant::kCrunchy; case OutputPrefixType::RAW: return Ed25519Parameters::Variant::kNoPrefix; case OutputPrefixType::TINK: return Ed25519Parameters::Variant::kTink; default: return util::Status(absl::StatusCode::kInvalidArgument, "Could not determine Ed25519Parameters::Variant"); } } util::StatusOr ToOutputPrefixType( Ed25519Parameters::Variant variant) { switch (variant) { case Ed25519Parameters::Variant::kLegacy: return OutputPrefixType::LEGACY; case Ed25519Parameters::Variant::kCrunchy: return OutputPrefixType::CRUNCHY; case Ed25519Parameters::Variant::kNoPrefix: return OutputPrefixType::RAW; case Ed25519Parameters::Variant::kTink: return OutputPrefixType::TINK; default: return util::Status(absl::StatusCode::kInvalidArgument, "Could not determine output prefix type"); } } util::StatusOr ParseParameters( const internal::ProtoParametersSerialization& serialization) { if (serialization.GetKeyTemplate().type_url() != kPrivateTypeUrl) { return util::Status(absl::StatusCode::kInvalidArgument, "Wrong type URL when parsing Ed25519Parameters."); } Ed25519KeyFormat proto_key_format; if (!proto_key_format.ParseFromString( serialization.GetKeyTemplate().value())) { return util::Status(absl::StatusCode::kInvalidArgument, "Failed to parse Ed25519KeyFormat proto"); } if (proto_key_format.version() != 0) { return util::Status(absl::StatusCode::kInvalidArgument, "Only version 0 keys are accepted."); } util::StatusOr variant = ToVariant(serialization.GetKeyTemplate().output_prefix_type()); if (!variant.ok()) { return variant.status(); } return Ed25519Parameters::Create(*variant); } util::StatusOr ParsePublicKey( const internal::ProtoKeySerialization& serialization, absl::optional token) { if (serialization.TypeUrl() != kPublicTypeUrl) { return util::Status(absl::StatusCode::kInvalidArgument, "Wrong type URL when parsing Ed25519PublicKey."); } google::crypto::tink::Ed25519PublicKey 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(InsecureSecretKeyAccess::Get())))) { return util::Status(absl::StatusCode::kInvalidArgument, "Failed to parse Ed25519PublicKey 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 parameters = Ed25519Parameters::Create(*variant); if (!parameters.ok()) { return parameters.status(); } return Ed25519PublicKey::Create(*parameters, proto_key.key_value(), serialization.IdRequirement(), GetPartialKeyAccess()); } util::StatusOr ParsePrivateKey( const internal::ProtoKeySerialization& serialization, absl::optional token) { if (serialization.TypeUrl() != kPrivateTypeUrl) { return util::Status(absl::StatusCode::kInvalidArgument, "Wrong type URL when parsing Ed25519PrivateKey."); } if (!token.has_value()) { return util::Status(absl::StatusCode::kPermissionDenied, "SecretKeyAccess is required"); } google::crypto::tink::Ed25519PrivateKey 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 Ed25519PrivateKey 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 parameters = Ed25519Parameters::Create(*variant); if (!parameters.ok()) { return parameters.status(); } util::StatusOr public_key = Ed25519PublicKey::Create( *parameters, proto_key.public_key().key_value(), serialization.IdRequirement(), GetPartialKeyAccess()); if (!public_key.ok()) { return public_key.status(); } return Ed25519PrivateKey::Create( *public_key, RestrictedData(proto_key.key_value(), *token), GetPartialKeyAccess()); } util::StatusOr SerializeParameters( const Ed25519Parameters& parameters) { util::StatusOr output_prefix_type = ToOutputPrefixType(parameters.GetVariant()); if (!output_prefix_type.ok()) { return output_prefix_type.status(); } Ed25519KeyFormat proto_key_format; proto_key_format.set_version(0); return internal::ProtoParametersSerialization::Create( kPrivateTypeUrl, *output_prefix_type, proto_key_format.SerializeAsString()); } util::StatusOr SerializePublicKey( const Ed25519PublicKey& key, absl::optional token) { google::crypto::tink::Ed25519PublicKey proto_key; proto_key.set_version(0); // OSS proto library complains if input is not converted to a string. proto_key.set_key_value( std::string(key.GetPublicKeyBytes(GetPartialKeyAccess()))); 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(), InsecureSecretKeyAccess::Get()); return internal::ProtoKeySerialization::Create( kPublicTypeUrl, restricted_output, KeyData::ASYMMETRIC_PUBLIC, *output_prefix_type, key.GetIdRequirement()); } util::StatusOr SerializePrivateKey( const Ed25519PrivateKey& key, absl::optional token) { util::StatusOr restricted_input = key.GetPrivateKeyBytes(GetPartialKeyAccess()); if (!restricted_input.ok()) { return restricted_input.status(); } if (!token.has_value()) { return util::Status(absl::StatusCode::kPermissionDenied, "SecretKeyAccess is required"); } google::crypto::tink::Ed25519PublicKey proto_public_key; proto_public_key.set_version(0); // OSS proto library complains if input is not converted to a string. proto_public_key.set_key_value( std::string(key.GetPublicKey().GetPublicKeyBytes(GetPartialKeyAccess()))); google::crypto::tink::Ed25519PrivateKey proto_private_key; proto_private_key.set_version(0); *proto_private_key.mutable_public_key() = proto_public_key; // OSS proto library complains if input is not converted to a string. proto_private_key.set_key_value( std::string(restricted_input->GetSecret(*token))); util::StatusOr output_prefix_type = ToOutputPrefixType(key.GetPublicKey().GetParameters().GetVariant()); if (!output_prefix_type.ok()) { return output_prefix_type.status(); } RestrictedData restricted_output = RestrictedData(proto_private_key.SerializeAsString(), *token); return internal::ProtoKeySerialization::Create( kPrivateTypeUrl, restricted_output, KeyData::ASYMMETRIC_PRIVATE, *output_prefix_type, key.GetIdRequirement()); } Ed25519ProtoParametersParserImpl* Ed25519ProtoParametersParser() { static auto* parser = new Ed25519ProtoParametersParserImpl(kPrivateTypeUrl, ParseParameters); return parser; } Ed25519ProtoParametersSerializerImpl* Ed25519ProtoParametersSerializer() { static auto* serializer = new Ed25519ProtoParametersSerializerImpl( kPrivateTypeUrl, SerializeParameters); return serializer; } Ed25519ProtoPublicKeyParserImpl* Ed25519ProtoPublicKeyParser() { static auto* parser = new Ed25519ProtoPublicKeyParserImpl(kPublicTypeUrl, ParsePublicKey); return parser; } Ed25519ProtoPublicKeySerializerImpl* Ed25519ProtoPublicKeySerializer() { static auto* serializer = new Ed25519ProtoPublicKeySerializerImpl(SerializePublicKey); return serializer; } Ed25519ProtoPrivateKeyParserImpl* Ed25519ProtoPrivateKeyParser() { static auto* parser = new Ed25519ProtoPrivateKeyParserImpl(kPrivateTypeUrl, ParsePrivateKey); return parser; } Ed25519ProtoPrivateKeySerializerImpl* Ed25519ProtoPrivateKeySerializer() { static auto* serializer = new Ed25519ProtoPrivateKeySerializerImpl(SerializePrivateKey); return serializer; } } // namespace util::Status RegisterEd25519ProtoSerialization() { util::Status status = internal::MutableSerializationRegistry::GlobalInstance() .RegisterParametersParser(Ed25519ProtoParametersParser()); if (!status.ok()) { return status; } status = internal::MutableSerializationRegistry::GlobalInstance() .RegisterParametersSerializer(Ed25519ProtoParametersSerializer()); if (!status.ok()) { return status; } status = internal::MutableSerializationRegistry::GlobalInstance() .RegisterKeyParser(Ed25519ProtoPublicKeyParser()); if (!status.ok()) { return status; } status = internal::MutableSerializationRegistry::GlobalInstance() .RegisterKeySerializer(Ed25519ProtoPublicKeySerializer()); if (!status.ok()) { return status; } status = internal::MutableSerializationRegistry::GlobalInstance() .RegisterKeyParser(Ed25519ProtoPrivateKeyParser()); if (!status.ok()) { return status; } return internal::MutableSerializationRegistry::GlobalInstance() .RegisterKeySerializer(Ed25519ProtoPrivateKeySerializer()); } } // namespace tink } // namespace crypto