diff options
author | wconner <wconner@google.com> | 2023-08-03 11:43:13 -0700 |
---|---|---|
committer | Copybara-Service <copybara-worker@google.com> | 2023-08-03 11:44:16 -0700 |
commit | 89464905f119362ea232f2e094cccb5eb80150bc (patch) | |
tree | 89da1370f55168a3b1ebffc507bb19c35655fb84 | |
parent | 205beaacc61da00252f5047220dd527e3128a98e (diff) | |
download | tink-89464905f119362ea232f2e094cccb5eb80150bc.tar.gz |
Add Ed25519 parameters proto parser and serializer.
PiperOrigin-RevId: 553543550
-rw-r--r-- | cc/signature/BUILD.bazel | 40 | ||||
-rw-r--r-- | cc/signature/CMakeLists.txt | 39 | ||||
-rw-r--r-- | cc/signature/ed25519_proto_serialization.cc | 152 | ||||
-rw-r--r-- | cc/signature/ed25519_proto_serialization.h | 31 | ||||
-rw-r--r-- | cc/signature/ed25519_proto_serialization_test.cc | 209 |
5 files changed, 471 insertions, 0 deletions
diff --git a/cc/signature/BUILD.bazel b/cc/signature/BUILD.bazel index 5dd6b13ec..b5a549e31 100644 --- a/cc/signature/BUILD.bazel +++ b/cc/signature/BUILD.bazel @@ -479,6 +479,26 @@ cc_library( ], ) +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 @@ cc_test( "@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 4e3864862..778fd63e4 100644 --- a/cc/signature/CMakeLists.txt +++ b/cc/signature/CMakeLists.txt @@ -456,6 +456,25 @@ tink_cc_library( 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_cc_test( 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 000000000..fc022c0cb --- /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 000000000..6f2527e19 --- /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 000000000..a2730c57d --- /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 |