diff options
author | wconner <wconner@google.com> | 2023-08-07 04:16:35 -0700 |
---|---|---|
committer | Copybara-Service <copybara-worker@google.com> | 2023-08-07 04:18:05 -0700 |
commit | b8fa494529f005263f2c0c369b64139d6421cb40 (patch) | |
tree | c720c7c545d3ac865274c5bca68f09a49848e9f2 | |
parent | cf0e5d94c306c290f346fe8cf45282b176c64920 (diff) | |
download | tink-b8fa494529f005263f2c0c369b64139d6421cb40.tar.gz |
Add Ed25519 private key proto parsers and serializers.
PiperOrigin-RevId: 554433002
-rw-r--r-- | cc/signature/BUILD.bazel | 3 | ||||
-rw-r--r-- | cc/signature/CMakeLists.txt | 3 | ||||
-rw-r--r-- | cc/signature/ed25519_proto_serialization.cc | 124 | ||||
-rw-r--r-- | cc/signature/ed25519_proto_serialization_test.cc | 228 |
4 files changed, 352 insertions, 6 deletions
diff --git a/cc/signature/BUILD.bazel b/cc/signature/BUILD.bazel index 6d16ccf28..fed82d4d4 100644 --- a/cc/signature/BUILD.bazel +++ b/cc/signature/BUILD.bazel @@ -486,6 +486,7 @@ cc_library( include_prefix = "tink/signature", deps = [ ":ed25519_parameters", + ":ed25519_private_key", ":ed25519_public_key", "//:insecure_secret_key_access", "//:partial_key_access", @@ -925,6 +926,7 @@ cc_test( srcs = ["ed25519_proto_serialization_test.cc"], deps = [ ":ed25519_parameters", + ":ed25519_private_key", ":ed25519_proto_serialization", ":ed25519_public_key", "//:insecure_secret_key_access", @@ -932,6 +934,7 @@ cc_test( "//:parameters", "//:partial_key_access", "//:restricted_data", + "//internal:ec_util", "//internal:mutable_serialization_registry", "//internal:proto_key_serialization", "//internal:proto_parameters_serialization", diff --git a/cc/signature/CMakeLists.txt b/cc/signature/CMakeLists.txt index 686ad21a9..571eb4481 100644 --- a/cc/signature/CMakeLists.txt +++ b/cc/signature/CMakeLists.txt @@ -463,6 +463,7 @@ tink_cc_library( ed25519_proto_serialization.h DEPS tink::signature::ed25519_parameters + tink::signature::ed25519_private_key tink::signature::ed25519_public_key absl::status absl::strings @@ -886,6 +887,7 @@ tink_cc_test( ed25519_proto_serialization_test.cc DEPS tink::signature::ed25519_parameters + tink::signature::ed25519_private_key tink::signature::ed25519_proto_serialization tink::signature::ed25519_public_key gmock @@ -896,6 +898,7 @@ tink_cc_test( tink::core::parameters tink::core::partial_key_access tink::core::restricted_data + tink::internal::ec_util tink::internal::mutable_serialization_registry tink::internal::proto_key_serialization tink::internal::proto_parameters_serialization diff --git a/cc/signature/ed25519_proto_serialization.cc b/cc/signature/ed25519_proto_serialization.cc index df5103cce..0809b318d 100644 --- a/cc/signature/ed25519_proto_serialization.cc +++ b/cc/signature/ed25519_proto_serialization.cc @@ -33,6 +33,7 @@ #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" @@ -44,6 +45,7 @@ namespace tink { namespace { using ::google::crypto::tink::Ed25519KeyFormat; +using ::google::crypto::tink::KeyData; using ::google::crypto::tink::OutputPrefixType; using Ed25519ProtoParametersParserImpl = @@ -57,6 +59,11 @@ using Ed25519ProtoPublicKeyParserImpl = using Ed25519ProtoPublicKeySerializerImpl = internal::KeySerializerImpl<Ed25519PublicKey, internal::ProtoKeySerialization>; +using Ed25519ProtoPrivateKeyParserImpl = + internal::KeyParserImpl<internal::ProtoKeySerialization, Ed25519PrivateKey>; +using Ed25519ProtoPrivateKeySerializerImpl = + internal::KeySerializerImpl<Ed25519PrivateKey, + internal::ProtoKeySerialization>; const absl::string_view kPublicTypeUrl = "type.googleapis.com/google.crypto.tink.Ed25519PublicKey"; @@ -162,6 +169,54 @@ util::StatusOr<Ed25519PublicKey> ParsePublicKey( GetPartialKeyAccess()); } +util::StatusOr<Ed25519PrivateKey> ParsePrivateKey( + const internal::ProtoKeySerialization& serialization, + absl::optional<SecretKeyAccessToken> 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<Ed25519Parameters::Variant> variant = + ToVariant(serialization.GetOutputPrefixType()); + if (!variant.ok()) { + return variant.status(); + } + + util::StatusOr<Ed25519Parameters> parameters = + Ed25519Parameters::Create(*variant); + if (!parameters.ok()) { + return parameters.status(); + } + + util::StatusOr<Ed25519PublicKey> 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<internal::ProtoParametersSerialization> SerializeParameters( const Ed25519Parameters& parameters) { util::StatusOr<OutputPrefixType> output_prefix_type = @@ -195,9 +250,46 @@ util::StatusOr<internal::ProtoKeySerialization> SerializePublicKey( RestrictedData restricted_output = RestrictedData( proto_key.SerializeAsString(), InsecureSecretKeyAccess::Get()); return internal::ProtoKeySerialization::Create( - kPublicTypeUrl, restricted_output, - google::crypto::tink::KeyData::ASYMMETRIC_PUBLIC, *output_prefix_type, - key.GetIdRequirement()); + kPublicTypeUrl, restricted_output, KeyData::ASYMMETRIC_PUBLIC, + *output_prefix_type, key.GetIdRequirement()); +} + +util::StatusOr<internal::ProtoKeySerialization> SerializePrivateKey( + const Ed25519PrivateKey& key, absl::optional<SecretKeyAccessToken> token) { + util::StatusOr<RestrictedData> 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<OutputPrefixType> 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() { @@ -224,6 +316,18 @@ Ed25519ProtoPublicKeySerializerImpl* Ed25519ProtoPublicKeySerializer() { 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() { @@ -247,8 +351,20 @@ util::Status RegisterEd25519ProtoSerialization() { 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(Ed25519ProtoPublicKeySerializer()); + .RegisterKeySerializer(Ed25519ProtoPrivateKeySerializer()); } } // namespace tink diff --git a/cc/signature/ed25519_proto_serialization_test.cc b/cc/signature/ed25519_proto_serialization_test.cc index 04a839ed2..b51b13de0 100644 --- a/cc/signature/ed25519_proto_serialization_test.cc +++ b/cc/signature/ed25519_proto_serialization_test.cc @@ -24,6 +24,7 @@ #include "absl/status/status.h" #include "absl/types/optional.h" #include "tink/insecure_secret_key_access.h" +#include "tink/internal/ec_util.h" #include "tink/internal/mutable_serialization_registry.h" #include "tink/internal/proto_key_serialization.h" #include "tink/internal/proto_parameters_serialization.h" @@ -33,6 +34,7 @@ #include "tink/partial_key_access.h" #include "tink/restricted_data.h" #include "tink/signature/ed25519_parameters.h" +#include "tink/signature/ed25519_private_key.h" #include "tink/signature/ed25519_public_key.h" #include "tink/subtle/random.h" #include "tink/util/statusor.h" @@ -53,7 +55,6 @@ using ::google::crypto::tink::OutputPrefixType; using ::testing::Eq; using ::testing::IsTrue; using ::testing::NotNull; -using ::testing::SizeIs; using ::testing::TestWithParam; using ::testing::Values; @@ -314,7 +315,230 @@ TEST_P(Ed25519ProtoSerializationTest, SerializePublicKey) { InsecureSecretKeyAccess::Get()))), IsTrue()); EXPECT_THAT(proto_key.version(), Eq(0)); - EXPECT_THAT(proto_key.key_value(), SizeIs(32)); + EXPECT_THAT(proto_key.key_value(), Eq(raw_key_bytes)); +} + +TEST_P(Ed25519ProtoSerializationTest, ParsePrivateKey) { + TestCase test_case = GetParam(); + ASSERT_THAT(RegisterEd25519ProtoSerialization(), IsOk()); + + util::StatusOr<std::unique_ptr<internal::Ed25519Key>> key_pair = + internal::NewEd25519Key(); + ASSERT_THAT(key_pair, IsOk()); + + google::crypto::tink::Ed25519PublicKey public_key_proto; + public_key_proto.set_version(0); + public_key_proto.set_key_value((*key_pair)->public_key); + + google::crypto::tink::Ed25519PrivateKey private_key_proto; + private_key_proto.set_version(0); + *private_key_proto.mutable_public_key() = public_key_proto; + private_key_proto.set_key_value((*key_pair)->private_key); + + RestrictedData serialized_key = RestrictedData( + private_key_proto.SerializeAsString(), InsecureSecretKeyAccess::Get()); + + util::StatusOr<internal::ProtoKeySerialization> serialization = + internal::ProtoKeySerialization::Create( + "type.googleapis.com/google.crypto.tink.Ed25519PrivateKey", + serialized_key, KeyData::ASYMMETRIC_PRIVATE, + test_case.output_prefix_type, test_case.id); + ASSERT_THAT(serialization, IsOk()); + + util::StatusOr<std::unique_ptr<Key>> key = + internal::MutableSerializationRegistry::GlobalInstance().ParseKey( + *serialization, InsecureSecretKeyAccess::Get()); + ASSERT_THAT(key, IsOk()); + EXPECT_THAT((*key)->GetIdRequirement(), Eq(test_case.id)); + EXPECT_THAT((*key)->GetParameters().HasIdRequirement(), + test_case.id.has_value()); + + util::StatusOr<Ed25519Parameters> expected_parameters = + Ed25519Parameters::Create(test_case.variant); + ASSERT_THAT(expected_parameters, IsOk()); + + util::StatusOr<Ed25519PublicKey> expected_public_key = + Ed25519PublicKey::Create(*expected_parameters, (*key_pair)->public_key, + test_case.id, GetPartialKeyAccess()); + ASSERT_THAT(expected_public_key, IsOk()); + + util::StatusOr<Ed25519PrivateKey> expected_private_key = + Ed25519PrivateKey::Create(*expected_public_key, + RestrictedData((*key_pair)->private_key, + InsecureSecretKeyAccess::Get()), + GetPartialKeyAccess()); + ASSERT_THAT(expected_private_key, IsOk()); + + EXPECT_THAT(**key, Eq(*expected_private_key)); +} + +TEST_F(Ed25519ProtoSerializationTest, ParsePrivateKeyWithInvalidSerialization) { + ASSERT_THAT(RegisterEd25519ProtoSerialization(), IsOk()); + + RestrictedData serialized_key = + RestrictedData("invalid_serialization", InsecureSecretKeyAccess::Get()); + + util::StatusOr<internal::ProtoKeySerialization> serialization = + internal::ProtoKeySerialization::Create( + "type.googleapis.com/google.crypto.tink.Ed25519PrivateKey", + serialized_key, KeyData::ASYMMETRIC_PRIVATE, OutputPrefixType::TINK, + /*id_requirement=*/0x23456789); + ASSERT_THAT(serialization, IsOk()); + + util::StatusOr<std::unique_ptr<Key>> key = + internal::MutableSerializationRegistry::GlobalInstance().ParseKey( + *serialization, InsecureSecretKeyAccess::Get()); + EXPECT_THAT(key.status(), StatusIs(absl::StatusCode::kInvalidArgument)); +} + +TEST_F(Ed25519ProtoSerializationTest, ParsePrivateKeyWithInvalidVersion) { + ASSERT_THAT(RegisterEd25519ProtoSerialization(), IsOk()); + + util::StatusOr<std::unique_ptr<internal::Ed25519Key>> key_pair = + internal::NewEd25519Key(); + ASSERT_THAT(key_pair, IsOk()); + + google::crypto::tink::Ed25519PublicKey public_key_proto; + public_key_proto.set_version(0); + public_key_proto.set_key_value((*key_pair)->public_key); + + google::crypto::tink::Ed25519PrivateKey private_key_proto; + private_key_proto.set_version(1); + *private_key_proto.mutable_public_key() = public_key_proto; + private_key_proto.set_key_value((*key_pair)->private_key); + + RestrictedData serialized_key = RestrictedData( + private_key_proto.SerializeAsString(), InsecureSecretKeyAccess::Get()); + + util::StatusOr<internal::ProtoKeySerialization> serialization = + internal::ProtoKeySerialization::Create( + "type.googleapis.com/google.crypto.tink.Ed25519PrivateKey", + serialized_key, KeyData::ASYMMETRIC_PRIVATE, OutputPrefixType::TINK, + /*id_requirement=*/0x23456789); + ASSERT_THAT(serialization, IsOk()); + + util::StatusOr<std::unique_ptr<Key>> key = + internal::MutableSerializationRegistry::GlobalInstance().ParseKey( + *serialization, InsecureSecretKeyAccess::Get()); + EXPECT_THAT(key.status(), StatusIs(absl::StatusCode::kInvalidArgument)); +} + +TEST_F(Ed25519ProtoSerializationTest, ParsePrivateKeyNoSecretKeyAccess) { + ASSERT_THAT(RegisterEd25519ProtoSerialization(), IsOk()); + + util::StatusOr<std::unique_ptr<internal::Ed25519Key>> key_pair = + internal::NewEd25519Key(); + ASSERT_THAT(key_pair, IsOk()); + + google::crypto::tink::Ed25519PublicKey public_key_proto; + public_key_proto.set_version(0); + public_key_proto.set_key_value((*key_pair)->public_key); + + google::crypto::tink::Ed25519PrivateKey private_key_proto; + private_key_proto.set_version(0); + *private_key_proto.mutable_public_key() = public_key_proto; + private_key_proto.set_key_value((*key_pair)->private_key); + + RestrictedData serialized_key = RestrictedData( + private_key_proto.SerializeAsString(), InsecureSecretKeyAccess::Get()); + + util::StatusOr<internal::ProtoKeySerialization> serialization = + internal::ProtoKeySerialization::Create( + "type.googleapis.com/google.crypto.tink.Ed25519PrivateKey", + serialized_key, KeyData::ASYMMETRIC_PRIVATE, OutputPrefixType::TINK, + /*id_requirement=*/0x23456789); + ASSERT_THAT(serialization, IsOk()); + + util::StatusOr<std::unique_ptr<Key>> key = + internal::MutableSerializationRegistry::GlobalInstance().ParseKey( + *serialization, /*token=*/absl::nullopt); + EXPECT_THAT(key.status(), StatusIs(absl::StatusCode::kPermissionDenied)); +} + +TEST_P(Ed25519ProtoSerializationTest, SerializePrivateKey) { + 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<internal::Ed25519Key>> key_pair = + internal::NewEd25519Key(); + ASSERT_THAT(key_pair, IsOk()); + + util::StatusOr<Ed25519PublicKey> public_key = + Ed25519PublicKey::Create(*parameters, (*key_pair)->public_key, + test_case.id, GetPartialKeyAccess()); + ASSERT_THAT(public_key, IsOk()); + + util::StatusOr<Ed25519PrivateKey> private_key = Ed25519PrivateKey::Create( + *public_key, + RestrictedData((*key_pair)->private_key, InsecureSecretKeyAccess::Get()), + GetPartialKeyAccess()); + ASSERT_THAT(private_key, IsOk()); + + util::StatusOr<std::unique_ptr<Serialization>> serialization = + internal::MutableSerializationRegistry::GlobalInstance() + .SerializeKey<internal::ProtoKeySerialization>( + *private_key, InsecureSecretKeyAccess::Get()); + ASSERT_THAT(serialization, IsOk()); + EXPECT_THAT((*serialization)->ObjectIdentifier(), + Eq("type.googleapis.com/google.crypto.tink.Ed25519PrivateKey")); + + const internal::ProtoKeySerialization* proto_serialization = + dynamic_cast<const internal::ProtoKeySerialization*>( + serialization->get()); + ASSERT_THAT(proto_serialization, NotNull()); + EXPECT_THAT(proto_serialization->TypeUrl(), + Eq("type.googleapis.com/google.crypto.tink.Ed25519PrivateKey")); + EXPECT_THAT(proto_serialization->KeyMaterialType(), + Eq(KeyData::ASYMMETRIC_PRIVATE)); + EXPECT_THAT(proto_serialization->GetOutputPrefixType(), + Eq(test_case.output_prefix_type)); + EXPECT_THAT(proto_serialization->IdRequirement(), Eq(test_case.id)); + + google::crypto::tink::Ed25519PrivateKey proto_key; + // OSS proto library complains if input is not converted to a string. + ASSERT_THAT(proto_key.ParseFromString(std::string( + proto_serialization->SerializedKeyProto().GetSecret( + InsecureSecretKeyAccess::Get()))), + IsTrue()); + EXPECT_THAT(proto_key.version(), Eq(0)); + EXPECT_THAT(proto_key.key_value(), Eq((*key_pair)->private_key)); + EXPECT_THAT(proto_key.has_public_key(), IsTrue()); + EXPECT_THAT(proto_key.public_key().version(), Eq(0)); + EXPECT_THAT(proto_key.public_key().key_value(), Eq((*key_pair)->public_key)); +} + +TEST_F(Ed25519ProtoSerializationTest, SerializePrivateKeyNoSecretKeyAccess) { + ASSERT_THAT(RegisterEd25519ProtoSerialization(), IsOk()); + + util::StatusOr<Ed25519Parameters> parameters = + Ed25519Parameters::Create(Ed25519Parameters::Variant::kTink); + ASSERT_THAT(parameters, IsOk()); + + util::StatusOr<std::unique_ptr<internal::Ed25519Key>> key_pair = + internal::NewEd25519Key(); + ASSERT_THAT(key_pair, IsOk()); + + util::StatusOr<Ed25519PublicKey> public_key = + Ed25519PublicKey::Create(*parameters, (*key_pair)->public_key, + /*id_requirement=*/123, GetPartialKeyAccess()); + ASSERT_THAT(public_key, IsOk()); + + util::StatusOr<Ed25519PrivateKey> private_key = Ed25519PrivateKey::Create( + *public_key, + RestrictedData((*key_pair)->private_key, InsecureSecretKeyAccess::Get()), + GetPartialKeyAccess()); + ASSERT_THAT(private_key, IsOk()); + + util::StatusOr<std::unique_ptr<Serialization>> serialization = + internal::MutableSerializationRegistry::GlobalInstance() + .SerializeKey<internal::ProtoKeySerialization>( + *private_key, /*token=*/absl::nullopt); + ASSERT_THAT(serialization.status(), + StatusIs(absl::StatusCode::kPermissionDenied)); } } // namespace |