aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorwconner <wconner@google.com>2023-08-07 04:16:35 -0700
committerCopybara-Service <copybara-worker@google.com>2023-08-07 04:18:05 -0700
commitb8fa494529f005263f2c0c369b64139d6421cb40 (patch)
treec720c7c545d3ac865274c5bca68f09a49848e9f2
parentcf0e5d94c306c290f346fe8cf45282b176c64920 (diff)
downloadtink-b8fa494529f005263f2c0c369b64139d6421cb40.tar.gz
Add Ed25519 private key proto parsers and serializers.
PiperOrigin-RevId: 554433002
-rw-r--r--cc/signature/BUILD.bazel3
-rw-r--r--cc/signature/CMakeLists.txt3
-rw-r--r--cc/signature/ed25519_proto_serialization.cc124
-rw-r--r--cc/signature/ed25519_proto_serialization_test.cc228
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