Add Ed25519 parameters proto parser and serializer.

PiperOrigin-RevId: 553543550
diff --git a/cc/signature/BUILD.bazel b/cc/signature/BUILD.bazel
index 5dd6b13..b5a549e 100644
--- a/cc/signature/BUILD.bazel
+++ b/cc/signature/BUILD.bazel
@@ -479,6 +479,26 @@
     ],
 )
 
+cc_library(
+    name = "ed25519_proto_serialization",
+    srcs = ["ed25519_proto_serialization.cc"],
+    hdrs = ["ed25519_proto_serialization.h"],
+    include_prefix = "tink/signature",
+    deps = [
+        ":ed25519_parameters",
+        "//internal:mutable_serialization_registry",
+        "//internal:parameters_parser",
+        "//internal:parameters_serializer",
+        "//internal:proto_parameters_serialization",
+        "//proto:ed25519_cc_proto",
+        "//proto:tink_cc_proto",
+        "//util:status",
+        "//util:statusor",
+        "@com_google_absl//absl/status",
+        "@com_google_absl//absl/strings",
+    ],
+)
+
 # tests
 
 cc_test(
@@ -890,3 +910,23 @@
         "@com_google_googletest//:gtest_main",
     ],
 )
+
+cc_test(
+    name = "ed25519_proto_serialization_test",
+    srcs = ["ed25519_proto_serialization_test.cc"],
+    deps = [
+        ":ed25519_parameters",
+        ":ed25519_proto_serialization",
+        "//:parameters",
+        "//internal:mutable_serialization_registry",
+        "//internal:proto_parameters_serialization",
+        "//internal:serialization",
+        "//proto:ed25519_cc_proto",
+        "//proto:tink_cc_proto",
+        "//util:statusor",
+        "//util:test_matchers",
+        "@com_google_absl//absl/status",
+        "@com_google_absl//absl/types:optional",
+        "@com_google_googletest//:gtest_main",
+    ],
+)
diff --git a/cc/signature/CMakeLists.txt b/cc/signature/CMakeLists.txt
index 4e38648..778fd63 100644
--- a/cc/signature/CMakeLists.txt
+++ b/cc/signature/CMakeLists.txt
@@ -456,6 +456,25 @@
     tink::util::statusor
 )
 
+tink_cc_library(
+  NAME ed25519_proto_serialization
+  SRCS
+    ed25519_proto_serialization.cc
+    ed25519_proto_serialization.h
+  DEPS
+    tink::signature::ed25519_parameters
+    absl::status
+    absl::strings
+    tink::internal::mutable_serialization_registry
+    tink::internal::parameters_parser
+    tink::internal::parameters_serializer
+    tink::internal::proto_parameters_serialization
+    tink::util::status
+    tink::util::statusor
+    tink::proto::ed25519_cc_proto
+    tink::proto::tink_cc_proto
+)
+
 # tests
 
 tink_cc_test(
@@ -851,3 +870,23 @@
     tink::util::statusor
     tink::util::test_matchers
 )
+
+tink_cc_test(
+  NAME ed25519_proto_serialization_test
+  SRCS
+    ed25519_proto_serialization_test.cc
+  DEPS
+    tink::signature::ed25519_parameters
+    tink::signature::ed25519_proto_serialization
+    gmock
+    absl::status
+    absl::optional
+    tink::core::parameters
+    tink::internal::mutable_serialization_registry
+    tink::internal::proto_parameters_serialization
+    tink::internal::serialization
+    tink::util::statusor
+    tink::util::test_matchers
+    tink::proto::ed25519_cc_proto
+    tink::proto::tink_cc_proto
+)
diff --git a/cc/signature/ed25519_proto_serialization.cc b/cc/signature/ed25519_proto_serialization.cc
new file mode 100644
index 0000000..fc022c0
--- /dev/null
+++ b/cc/signature/ed25519_proto_serialization.cc
@@ -0,0 +1,152 @@
+// 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 "absl/status/status.h"
+#include "absl/strings/string_view.h"
+#include "tink/internal/mutable_serialization_registry.h"
+#include "tink/internal/parameters_parser.h"
+#include "tink/internal/parameters_serializer.h"
+#include "tink/internal/proto_parameters_serialization.h"
+#include "tink/signature/ed25519_parameters.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::OutputPrefixType;
+
+using Ed25519ProtoParametersParserImpl =
+    internal::ParametersParserImpl<internal::ProtoParametersSerialization,
+                                   Ed25519Parameters>;
+using Ed25519ProtoParametersSerializerImpl =
+    internal::ParametersSerializerImpl<Ed25519Parameters,
+                                       internal::ProtoParametersSerialization>;
+
+const absl::string_view kPrivateTypeUrl =
+    "type.googleapis.com/google.crypto.tink.Ed25519PrivateKey";
+
+util::StatusOr<Ed25519Parameters::Variant> 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<OutputPrefixType> 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<Ed25519Parameters> 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<Ed25519Parameters::Variant> variant =
+      ToVariant(serialization.GetKeyTemplate().output_prefix_type());
+  if (!variant.ok()) {
+    return variant.status();
+  }
+
+  return Ed25519Parameters::Create(*variant);
+}
+
+util::StatusOr<internal::ProtoParametersSerialization> SerializeParameters(
+    const Ed25519Parameters& parameters) {
+  util::StatusOr<OutputPrefixType> 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());
+}
+
+Ed25519ProtoParametersParserImpl* Ed25519ProtoParametersParser() {
+  static auto* parser =
+      new Ed25519ProtoParametersParserImpl(kPrivateTypeUrl, ParseParameters);
+  return parser;
+}
+
+Ed25519ProtoParametersSerializerImpl* Ed25519ProtoParametersSerializer() {
+  static auto* serializer = new Ed25519ProtoParametersSerializerImpl(
+      kPrivateTypeUrl, SerializeParameters);
+  return serializer;
+}
+
+}  // namespace
+
+util::Status RegisterEd25519ProtoSerialization() {
+  util::Status status =
+      internal::MutableSerializationRegistry::GlobalInstance()
+          .RegisterParametersParser(Ed25519ProtoParametersParser());
+  if (!status.ok()) {
+    return status;
+  }
+
+  return internal::MutableSerializationRegistry::GlobalInstance()
+      .RegisterParametersSerializer(Ed25519ProtoParametersSerializer());
+}
+
+}  // namespace tink
+}  // namespace crypto
diff --git a/cc/signature/ed25519_proto_serialization.h b/cc/signature/ed25519_proto_serialization.h
new file mode 100644
index 0000000..6f2527e
--- /dev/null
+++ b/cc/signature/ed25519_proto_serialization.h
@@ -0,0 +1,31 @@
+// 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.
+//
+////////////////////////////////////////////////////////////////////////////////
+
+#ifndef TINK_SIGNATURE_ED25519_PROTO_SERIALIZATION_H_
+#define TINK_SIGNATURE_ED25519_PROTO_SERIALIZATION_H_
+
+#include "tink/util/status.h"
+
+namespace crypto {
+namespace tink {
+
+// Registers proto parsers and serializers for Ed25519 parameters and keys.
+crypto::tink::util::Status RegisterEd25519ProtoSerialization();
+
+}  // namespace tink
+}  // namespace crypto
+
+#endif  // TINK_SIGNATURE_ED25519_PROTO_SERIALIZATION_H_
diff --git a/cc/signature/ed25519_proto_serialization_test.cc b/cc/signature/ed25519_proto_serialization_test.cc
new file mode 100644
index 0000000..a2730c5
--- /dev/null
+++ b/cc/signature/ed25519_proto_serialization_test.cc
@@ -0,0 +1,209 @@
+// 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 <memory>
+#include <string>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "absl/status/status.h"
+#include "absl/types/optional.h"
+#include "tink/internal/mutable_serialization_registry.h"
+#include "tink/internal/proto_parameters_serialization.h"
+#include "tink/internal/serialization.h"
+#include "tink/parameters.h"
+#include "tink/signature/ed25519_parameters.h"
+#include "tink/util/statusor.h"
+#include "tink/util/test_matchers.h"
+#include "proto/ed25519.pb.h"
+#include "proto/tink.pb.h"
+
+namespace crypto {
+namespace tink {
+namespace {
+
+using ::crypto::tink::test::IsOk;
+using ::crypto::tink::test::StatusIs;
+using ::google::crypto::tink::Ed25519KeyFormat;
+using ::google::crypto::tink::OutputPrefixType;
+using ::testing::Eq;
+using ::testing::IsTrue;
+using ::testing::NotNull;
+using ::testing::TestWithParam;
+using ::testing::Values;
+
+struct TestCase {
+  Ed25519Parameters::Variant variant;
+  OutputPrefixType output_prefix_type;
+  absl::optional<int> id;
+  std::string output_prefix;
+};
+
+class Ed25519ProtoSerializationTest : public TestWithParam<TestCase> {
+ protected:
+  void SetUp() override {
+    internal::MutableSerializationRegistry::GlobalInstance().Reset();
+  }
+};
+
+INSTANTIATE_TEST_SUITE_P(
+    Ed25519ProtoSerializationTestSuite, Ed25519ProtoSerializationTest,
+    Values(TestCase{Ed25519Parameters::Variant::kTink, OutputPrefixType::TINK,
+                    /*id=*/0x02030400,
+                    /*output_prefix=*/std::string("\x01\x02\x03\x04\x00", 5)},
+           TestCase{Ed25519Parameters::Variant::kCrunchy,
+                    OutputPrefixType::CRUNCHY, /*id=*/0x01030005,
+                    /*output_prefix=*/std::string("\x00\x01\x03\x00\x05", 5)},
+           TestCase{Ed25519Parameters::Variant::kLegacy,
+                    OutputPrefixType::LEGACY, /*id=*/0x07080910,
+                    /*output_prefix=*/std::string("\x00\x07\x08\x09\x10", 5)},
+           TestCase{Ed25519Parameters::Variant::kNoPrefix,
+                    OutputPrefixType::RAW, /*id=*/absl::nullopt,
+                    /*output_prefix=*/""}));
+
+TEST_P(Ed25519ProtoSerializationTest, ParseParameters) {
+  TestCase test_case = GetParam();
+  ASSERT_THAT(RegisterEd25519ProtoSerialization(), IsOk());
+
+  Ed25519KeyFormat key_format_proto;
+  key_format_proto.set_version(0);
+
+  util::StatusOr<internal::ProtoParametersSerialization> serialization =
+      internal::ProtoParametersSerialization::Create(
+          "type.googleapis.com/google.crypto.tink.Ed25519PrivateKey",
+          test_case.output_prefix_type, key_format_proto.SerializeAsString());
+  ASSERT_THAT(serialization, IsOk());
+
+  util::StatusOr<std::unique_ptr<Parameters>> params =
+      internal::MutableSerializationRegistry::GlobalInstance().ParseParameters(
+          *serialization);
+  ASSERT_THAT(params, IsOk());
+  EXPECT_THAT((*params)->HasIdRequirement(), test_case.id.has_value());
+
+  const Ed25519Parameters* ed25519_params =
+      dynamic_cast<const Ed25519Parameters*>(params->get());
+  ASSERT_THAT(ed25519_params, NotNull());
+  EXPECT_THAT(ed25519_params->GetVariant(), Eq(test_case.variant));
+}
+
+TEST_F(Ed25519ProtoSerializationTest, ParseParametersWithInvalidSerialization) {
+  ASSERT_THAT(RegisterEd25519ProtoSerialization(), IsOk());
+
+  util::StatusOr<internal::ProtoParametersSerialization> serialization =
+      internal::ProtoParametersSerialization::Create(
+          "type.googleapis.com/google.crypto.tink.Ed25519PrivateKey",
+          OutputPrefixType::RAW, "invalid_serialization");
+  ASSERT_THAT(serialization, IsOk());
+
+  util::StatusOr<std::unique_ptr<Parameters>> params =
+      internal::MutableSerializationRegistry::GlobalInstance().ParseParameters(
+          *serialization);
+  EXPECT_THAT(params.status(), StatusIs(absl::StatusCode::kInvalidArgument));
+}
+
+TEST_F(Ed25519ProtoSerializationTest, ParseParametersWithUnkownOutputPrefix) {
+  ASSERT_THAT(RegisterEd25519ProtoSerialization(), IsOk());
+
+  Ed25519KeyFormat key_format_proto;
+  key_format_proto.set_version(0);
+
+  util::StatusOr<internal::ProtoParametersSerialization> serialization =
+      internal::ProtoParametersSerialization::Create(
+          "type.googleapis.com/google.crypto.tink.Ed25519PrivateKey",
+          OutputPrefixType::UNKNOWN_PREFIX,
+          key_format_proto.SerializeAsString());
+  ASSERT_THAT(serialization, IsOk());
+
+  util::StatusOr<std::unique_ptr<Parameters>> params =
+      internal::MutableSerializationRegistry::GlobalInstance().ParseParameters(
+          *serialization);
+  EXPECT_THAT(params.status(), StatusIs(absl::StatusCode::kInvalidArgument));
+}
+
+TEST_F(Ed25519ProtoSerializationTest, ParseParametersWithInvalidVersion) {
+  ASSERT_THAT(RegisterEd25519ProtoSerialization(), IsOk());
+
+  Ed25519KeyFormat key_format_proto;
+  key_format_proto.set_version(1);
+
+  util::StatusOr<internal::ProtoParametersSerialization> serialization =
+      internal::ProtoParametersSerialization::Create(
+          "type.googleapis.com/google.crypto.tink.Ed25519PrivateKey",
+          OutputPrefixType::RAW, key_format_proto.SerializeAsString());
+  ASSERT_THAT(serialization, IsOk());
+
+  util::StatusOr<std::unique_ptr<Parameters>> params =
+      internal::MutableSerializationRegistry::GlobalInstance().ParseParameters(
+          *serialization);
+  EXPECT_THAT(params.status(), StatusIs(absl::StatusCode::kInvalidArgument));
+}
+
+TEST_F(Ed25519ProtoSerializationTest, ParseParametersWithWrongTypeUrl) {
+  ASSERT_THAT(RegisterEd25519ProtoSerialization(), IsOk());
+
+  Ed25519KeyFormat key_format_proto;
+  key_format_proto.set_version(0);
+
+  // TODO(b/280321781): Switch to public key URL and expect invalid argument.
+  util::StatusOr<internal::ProtoParametersSerialization> serialization =
+      internal::ProtoParametersSerialization::Create(
+          "type.googleapis.com/google.crypto.tink.WrongTypeUrl",
+          OutputPrefixType::RAW, key_format_proto.SerializeAsString());
+  ASSERT_THAT(serialization, IsOk());
+
+  util::StatusOr<std::unique_ptr<Parameters>> params =
+      internal::MutableSerializationRegistry::GlobalInstance().ParseParameters(
+          *serialization);
+  EXPECT_THAT(params.status(), StatusIs(absl::StatusCode::kNotFound));
+}
+
+TEST_P(Ed25519ProtoSerializationTest, SerializeParameters) {
+  TestCase test_case = GetParam();
+  ASSERT_THAT(RegisterEd25519ProtoSerialization(), IsOk());
+
+  util::StatusOr<Ed25519Parameters> parameters =
+      Ed25519Parameters::Create(test_case.variant);
+  ASSERT_THAT(parameters, IsOk());
+
+  util::StatusOr<std::unique_ptr<Serialization>> serialization =
+      internal::MutableSerializationRegistry::GlobalInstance()
+          .SerializeParameters<internal::ProtoParametersSerialization>(
+              *parameters);
+  ASSERT_THAT(serialization, IsOk());
+  EXPECT_THAT((*serialization)->ObjectIdentifier(),
+              Eq("type.googleapis.com/google.crypto.tink.Ed25519PrivateKey"));
+
+  const internal::ProtoParametersSerialization* proto_serialization =
+      dynamic_cast<const internal::ProtoParametersSerialization*>(
+          serialization->get());
+  ASSERT_THAT(proto_serialization, NotNull());
+  EXPECT_THAT(proto_serialization->GetKeyTemplate().type_url(),
+              Eq("type.googleapis.com/google.crypto.tink.Ed25519PrivateKey"));
+  EXPECT_THAT(proto_serialization->GetKeyTemplate().output_prefix_type(),
+              Eq(test_case.output_prefix_type));
+
+  Ed25519KeyFormat key_format;
+  ASSERT_THAT(
+      key_format.ParseFromString(proto_serialization->GetKeyTemplate().value()),
+      IsTrue());
+  EXPECT_THAT(key_format.version(), Eq(0));
+}
+
+}  // namespace
+}  // namespace tink
+}  // namespace crypto