From d847e39b94a071e15aec8d11eff6dd82fe491149 Mon Sep 17 00:00:00 2001 From: ioannanedelcu Date: Wed, 17 Apr 2024 01:30:49 -0700 Subject: [PATCH] Add proto parser and serializer for SLH-DSA private key. PiperOrigin-RevId: 625599407 --- .../signature/slh_dsa_proto_serialization.cc | 114 ++++++- .../slh_dsa_proto_serialization_test.cc | 280 ++++++++++++++++++ 2 files changed, 393 insertions(+), 1 deletion(-) diff --git a/cc/experimental/pqcrypto/signature/slh_dsa_proto_serialization.cc b/cc/experimental/pqcrypto/signature/slh_dsa_proto_serialization.cc index c9221dd70b..57931f8d01 100644 --- a/cc/experimental/pqcrypto/signature/slh_dsa_proto_serialization.cc +++ b/cc/experimental/pqcrypto/signature/slh_dsa_proto_serialization.cc @@ -20,6 +20,7 @@ #include "absl/strings/string_view.h" #include "absl/types/optional.h" #include "tink/experimental/pqcrypto/signature/slh_dsa_parameters.h" +#include "tink/experimental/pqcrypto/signature/slh_dsa_private_key.h" #include "tink/experimental/pqcrypto/signature/slh_dsa_public_key.h" #include "tink/insecure_secret_key_access.h" #include "tink/internal/key_parser.h" @@ -59,6 +60,11 @@ using SlhDsaProtoPublicKeyParserImpl = using SlhDsaProtoPublicKeySerializerImpl = internal::KeySerializerImpl; +using SlhDsaProtoPrivateKeyParserImpl = + internal::KeyParserImpl; +using SlhDsaProtoPrivateKeySerializerImpl = + internal::KeySerializerImpl; const absl::string_view kPrivateTypeUrl = "type.googleapis.com/google.crypto.tink.SlhDsaPrivateKey"; @@ -249,6 +255,46 @@ util::StatusOr ParsePublicKey( 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 SlhDsaPrivateKey."); + } + if (!token.has_value()) { + return util::Status(absl::StatusCode::kPermissionDenied, + "SecretKeyAccess is required"); + } + google::crypto::tink::SlhDsaPrivateKey proto_key; + const RestrictedData& restricted_data = serialization.SerializedKeyProto(); + if (!proto_key.ParseFromString(restricted_data.GetSecret(*token))) { + return util::Status(absl::StatusCode::kInvalidArgument, + "Failed to parse SlhDsaPrivateKey proto"); + } + if (proto_key.version() != 0) { + return util::Status(absl::StatusCode::kInvalidArgument, + "Only version 0 keys are accepted."); + } + + util::StatusOr parameters = ToParameters( + serialization.GetOutputPrefixType(), proto_key.public_key().params()); + if (!parameters.ok()) { + return parameters.status(); + } + + util::StatusOr public_key = SlhDsaPublicKey::Create( + *parameters, proto_key.public_key().key_value(), + serialization.IdRequirement(), GetPartialKeyAccess()); + if (!public_key.ok()) { + return public_key.status(); + } + + return SlhDsaPrivateKey::Create(*public_key, + RestrictedData(proto_key.key_value(), *token), + GetPartialKeyAccess()); +} + util::StatusOr SerializeParameters( const SlhDsaParameters& parameters) { util::StatusOr output_prefix_type = @@ -295,6 +341,48 @@ util::StatusOr SerializePublicKey( *output_prefix_type, key.GetIdRequirement()); } +util::StatusOr SerializePrivateKey( + const SlhDsaPrivateKey& key, absl::optional token) { + if (!token.has_value()) { + return util::Status(absl::StatusCode::kPermissionDenied, + "SecretKeyAccess is required"); + } + util::StatusOr restricted_input = + key.GetPrivateKeyBytes(GetPartialKeyAccess()); + if (!restricted_input.ok()) { + return restricted_input.status(); + } + + util::StatusOr params = + FromParameters(key.GetPublicKey().GetParameters()); + if (!params.ok()) { + return params.status(); + } + + google::crypto::tink::SlhDsaPublicKey proto_public_key; + proto_public_key.set_version(0); + *proto_public_key.mutable_params() = *params; + proto_public_key.set_key_value( + key.GetPublicKey().GetPublicKeyBytes(GetPartialKeyAccess())); + + google::crypto::tink::SlhDsaPrivateKey proto_private_key; + proto_private_key.set_version(0); + *proto_private_key.mutable_public_key() = proto_public_key; + proto_private_key.set_key_value(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()); +} + SlhDsaProtoParametersParserImpl& SlhDsaProtoParametersParser() { static auto parser = new SlhDsaProtoParametersParserImpl(kPrivateTypeUrl, ParseParameters); @@ -319,6 +407,18 @@ SlhDsaProtoPublicKeySerializerImpl& SlhDsaProtoPublicKeySerializer() { return *serializer; } +SlhDsaProtoPrivateKeyParserImpl& SlhDsaProtoPrivateKeyParser() { + static auto* parser = + new SlhDsaProtoPrivateKeyParserImpl(kPrivateTypeUrl, ParsePrivateKey); + return *parser; +} + +SlhDsaProtoPrivateKeySerializerImpl& SlhDsaProtoPrivateKeySerializer() { + static auto* serializer = + new SlhDsaProtoPrivateKeySerializerImpl(SerializePrivateKey); + return *serializer; +} + } // namespace util::Status RegisterSlhDsaProtoSerialization() { @@ -342,8 +442,20 @@ util::Status RegisterSlhDsaProtoSerialization() { return status; } + status = internal::MutableSerializationRegistry::GlobalInstance() + .RegisterKeySerializer(&SlhDsaProtoPublicKeySerializer()); + if (!status.ok()) { + return status; + } + + status = internal::MutableSerializationRegistry::GlobalInstance() + .RegisterKeyParser(&SlhDsaProtoPrivateKeyParser()); + if (!status.ok()) { + return status; + } + return internal::MutableSerializationRegistry::GlobalInstance() - .RegisterKeySerializer(&SlhDsaProtoPublicKeySerializer()); + .RegisterKeySerializer(&SlhDsaProtoPrivateKeySerializer()); } } // namespace tink diff --git a/cc/experimental/pqcrypto/signature/slh_dsa_proto_serialization_test.cc b/cc/experimental/pqcrypto/signature/slh_dsa_proto_serialization_test.cc index 8ca54df8fa..b516ac6f64 100644 --- a/cc/experimental/pqcrypto/signature/slh_dsa_proto_serialization_test.cc +++ b/cc/experimental/pqcrypto/signature/slh_dsa_proto_serialization_test.cc @@ -16,6 +16,7 @@ #include "tink/experimental/pqcrypto/signature/slh_dsa_proto_serialization.h" +#include #include #include @@ -24,7 +25,11 @@ #include "absl/status/status.h" #include "absl/strings/string_view.h" #include "absl/types/optional.h" +#define OPENSSL_UNSTABLE_EXPERIMENTAL_SPX +#include "openssl/experimental/spx.h" +#undef OPENSSL_UNSTABLE_EXPERIMENTAL_SPX #include "tink/experimental/pqcrypto/signature/slh_dsa_parameters.h" +#include "tink/experimental/pqcrypto/signature/slh_dsa_private_key.h" #include "tink/experimental/pqcrypto/signature/slh_dsa_public_key.h" #include "tink/insecure_secret_key_access.h" #include "tink/internal/mutable_serialization_registry.h" @@ -446,6 +451,281 @@ TEST_P(SlhDsaProtoSerializationTest, SerializePublicKeyWorks) { Eq(SlhDsaSignatureType::SMALL_SIGNATURE)); } +TEST_P(SlhDsaProtoSerializationTest, ParsePrivateKeyWorks) { + TestCase test_case = GetParam(); + ASSERT_THAT(RegisterSlhDsaProtoSerialization(), IsOk()); + + std::string public_key_bytes; + public_key_bytes.resize(SPX_PUBLIC_KEY_BYTES); + std::string private_key_bytes; + private_key_bytes.resize(SPX_SECRET_KEY_BYTES); + + SPX_generate_key(reinterpret_cast(public_key_bytes.data()), + reinterpret_cast(private_key_bytes.data())); + + SlhDsaParams params; + params.set_sig_type(SlhDsaSignatureType::SMALL_SIGNATURE); + params.set_hash_type(SlhDsaHashType::SHA2); + params.set_key_size(64); + + google::crypto::tink::SlhDsaPublicKey public_key_proto; + public_key_proto.set_version(0); + public_key_proto.set_key_value(public_key_bytes); + *public_key_proto.mutable_params() = params; + + google::crypto::tink::SlhDsaPrivateKey private_key_proto; + private_key_proto.set_version(0); + *private_key_proto.mutable_public_key() = public_key_proto; + private_key_proto.set_key_value(private_key_bytes); + + RestrictedData serialized_key = RestrictedData( + private_key_proto.SerializeAsString(), InsecureSecretKeyAccess::Get()); + + util::StatusOr serialization = + internal::ProtoKeySerialization::Create( + kPrivateTypeUrl, serialized_key, KeyData::ASYMMETRIC_PRIVATE, + test_case.output_prefix_type, test_case.id_requirement); + ASSERT_THAT(serialization, IsOk()); + + util::StatusOr> private_key = + internal::MutableSerializationRegistry::GlobalInstance().ParseKey( + *serialization, InsecureSecretKeyAccess::Get()); + ASSERT_THAT(private_key, IsOk()); + + EXPECT_THAT((*private_key)->GetIdRequirement(), Eq(test_case.id_requirement)); + EXPECT_THAT((*private_key)->GetParameters().HasIdRequirement(), + test_case.id_requirement.has_value()); + + util::StatusOr expected_parameters = + SlhDsaParameters::Create( + SlhDsaParameters::HashType::kSha2, /*private_key_size_in_bytes=*/64, + SlhDsaParameters::SignatureType::kSmallSignature, test_case.variant); + ASSERT_THAT(expected_parameters, IsOk()); + + util::StatusOr expected_public_key = + SlhDsaPublicKey::Create(*expected_parameters, public_key_bytes, + test_case.id_requirement, GetPartialKeyAccess()); + ASSERT_THAT(expected_public_key, IsOk()); + + util::StatusOr expected_private_key = + SlhDsaPrivateKey::Create( + *expected_public_key, + RestrictedData(private_key_bytes, InsecureSecretKeyAccess::Get()), + GetPartialKeyAccess()); + ASSERT_THAT(expected_private_key, IsOk()); + + EXPECT_THAT(**private_key, Eq(*expected_private_key)); +} + +TEST_F(SlhDsaProtoSerializationTest, ParsePrivateKeyWithInvalidSerialization) { + ASSERT_THAT(RegisterSlhDsaProtoSerialization(), IsOk()); + + RestrictedData serialized_key = + RestrictedData("invalid_serialization", InsecureSecretKeyAccess::Get()); + + util::StatusOr serialization = + internal::ProtoKeySerialization::Create(kPrivateTypeUrl, serialized_key, + KeyData::ASYMMETRIC_PRIVATE, + OutputPrefixType::TINK, + /*id_requirement=*/0x23456789); + ASSERT_THAT(serialization, IsOk()); + + util::StatusOr> key = + internal::MutableSerializationRegistry::GlobalInstance().ParseKey( + *serialization, InsecureSecretKeyAccess::Get()); + EXPECT_THAT(key.status(), + StatusIs(absl::StatusCode::kInvalidArgument, + HasSubstr("Failed to parse SlhDsaPrivateKey proto"))); +} + +TEST_F(SlhDsaProtoSerializationTest, ParsePrivateKeyWithInvalidVersion) { + ASSERT_THAT(RegisterSlhDsaProtoSerialization(), IsOk()); + + std::string public_key_bytes; + public_key_bytes.resize(SPX_PUBLIC_KEY_BYTES); + std::string private_key_bytes; + private_key_bytes.resize(SPX_SECRET_KEY_BYTES); + + SPX_generate_key(reinterpret_cast(public_key_bytes.data()), + reinterpret_cast(private_key_bytes.data())); + + SlhDsaParams params; + params.set_sig_type(SlhDsaSignatureType::SMALL_SIGNATURE); + params.set_hash_type(SlhDsaHashType::SHA2); + params.set_key_size(64); + + google::crypto::tink::SlhDsaPublicKey public_key_proto; + public_key_proto.set_version(0); + public_key_proto.set_key_value(public_key_bytes); + *public_key_proto.mutable_params() = params; + + google::crypto::tink::SlhDsaPrivateKey private_key_proto; + private_key_proto.set_version(1); + *private_key_proto.mutable_public_key() = public_key_proto; + private_key_proto.set_key_value(private_key_bytes); + + RestrictedData serialized_key = RestrictedData( + private_key_proto.SerializeAsString(), InsecureSecretKeyAccess::Get()); + + util::StatusOr serialization = + internal::ProtoKeySerialization::Create(kPrivateTypeUrl, serialized_key, + KeyData::ASYMMETRIC_PRIVATE, + OutputPrefixType::TINK, + /*id_requirement=*/0x23456789); + ASSERT_THAT(serialization, IsOk()); + + util::StatusOr> key = + internal::MutableSerializationRegistry::GlobalInstance().ParseKey( + *serialization, InsecureSecretKeyAccess::Get()); + EXPECT_THAT(key.status(), + StatusIs(absl::StatusCode::kInvalidArgument, + HasSubstr("Only version 0 keys are accepted"))); +} + +TEST_F(SlhDsaProtoSerializationTest, ParsePrivateKeyNoSecretKeyAccess) { + ASSERT_THAT(RegisterSlhDsaProtoSerialization(), IsOk()); + + std::string public_key_bytes; + public_key_bytes.resize(SPX_PUBLIC_KEY_BYTES); + std::string private_key_bytes; + private_key_bytes.resize(SPX_SECRET_KEY_BYTES); + + SPX_generate_key(reinterpret_cast(public_key_bytes.data()), + reinterpret_cast(private_key_bytes.data())); + + SlhDsaParams params; + params.set_sig_type(SlhDsaSignatureType::SMALL_SIGNATURE); + params.set_hash_type(SlhDsaHashType::SHA2); + params.set_key_size(64); + + google::crypto::tink::SlhDsaPublicKey public_key_proto; + public_key_proto.set_version(0); + public_key_proto.set_key_value(public_key_bytes); + *public_key_proto.mutable_params() = params; + + google::crypto::tink::SlhDsaPrivateKey private_key_proto; + private_key_proto.set_version(0); + *private_key_proto.mutable_public_key() = public_key_proto; + private_key_proto.set_key_value(private_key_bytes); + + RestrictedData serialized_key = RestrictedData( + private_key_proto.SerializeAsString(), InsecureSecretKeyAccess::Get()); + + util::StatusOr serialization = + internal::ProtoKeySerialization::Create(kPrivateTypeUrl, serialized_key, + KeyData::ASYMMETRIC_PRIVATE, + OutputPrefixType::TINK, + /*id_requirement=*/0x23456789); + ASSERT_THAT(serialization, IsOk()); + + util::StatusOr> key = + internal::MutableSerializationRegistry::GlobalInstance().ParseKey( + *serialization, /*token=*/absl::nullopt); + EXPECT_THAT(key.status(), StatusIs(absl::StatusCode::kPermissionDenied)); +} + +TEST_P(SlhDsaProtoSerializationTest, SerializePrivateKey) { + TestCase test_case = GetParam(); + ASSERT_THAT(RegisterSlhDsaProtoSerialization(), IsOk()); + + std::string public_key_bytes; + public_key_bytes.resize(SPX_PUBLIC_KEY_BYTES); + std::string private_key_bytes; + private_key_bytes.resize(SPX_SECRET_KEY_BYTES); + + SPX_generate_key(reinterpret_cast(public_key_bytes.data()), + reinterpret_cast(private_key_bytes.data())); + + util::StatusOr parameters = SlhDsaParameters::Create( + SlhDsaParameters::HashType::kSha2, /*private_key_size_in_bytes=*/64, + SlhDsaParameters::SignatureType::kSmallSignature, test_case.variant); + ASSERT_THAT(parameters, IsOk()); + + util::StatusOr public_key = + SlhDsaPublicKey::Create(*parameters, public_key_bytes, + test_case.id_requirement, GetPartialKeyAccess()); + ASSERT_THAT(public_key, IsOk()); + + util::StatusOr private_key = SlhDsaPrivateKey::Create( + *public_key, + RestrictedData(private_key_bytes, InsecureSecretKeyAccess::Get()), + GetPartialKeyAccess()); + ASSERT_THAT(private_key, IsOk()); + + util::StatusOr> serialization = + internal::MutableSerializationRegistry::GlobalInstance() + .SerializeKey( + *private_key, InsecureSecretKeyAccess::Get()); + ASSERT_THAT(serialization, IsOk()); + EXPECT_THAT((*serialization)->ObjectIdentifier(), Eq(kPrivateTypeUrl)); + + const internal::ProtoKeySerialization* proto_serialization = + dynamic_cast( + serialization->get()); + ASSERT_THAT(proto_serialization, NotNull()); + EXPECT_THAT(proto_serialization->TypeUrl(), Eq(kPrivateTypeUrl)); + 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_requirement)); + + google::crypto::tink::SlhDsaPrivateKey proto_key; + ASSERT_THAT(proto_key.ParseFromString( + proto_serialization->SerializedKeyProto().GetSecret( + InsecureSecretKeyAccess::Get())), + IsTrue()); + EXPECT_THAT(proto_key.version(), Eq(0)); + EXPECT_THAT(proto_key.key_value(), Eq(private_key_bytes)); + 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(public_key_bytes)); + EXPECT_THAT(proto_key.public_key().has_params(), IsTrue()); + EXPECT_THAT(proto_key.public_key().params().key_size(), Eq(64)); + EXPECT_THAT(proto_key.public_key().params().hash_type(), + Eq(SlhDsaHashType::SHA2)); + EXPECT_THAT(proto_key.public_key().params().sig_type(), + Eq(SlhDsaSignatureType::SMALL_SIGNATURE)); +} + +TEST_F(SlhDsaProtoSerializationTest, SerializePrivateKeyNoSecretKeyAccess) { + ASSERT_THAT(RegisterSlhDsaProtoSerialization(), IsOk()); + + std::string public_key_bytes; + public_key_bytes.resize(SPX_PUBLIC_KEY_BYTES); + std::string private_key_bytes; + private_key_bytes.resize(SPX_SECRET_KEY_BYTES); + + SPX_generate_key(reinterpret_cast(public_key_bytes.data()), + reinterpret_cast(private_key_bytes.data())); + + util::StatusOr parameters = SlhDsaParameters::Create( + SlhDsaParameters::HashType::kSha2, /*private_key_size_in_bytes=*/64, + SlhDsaParameters::SignatureType::kSmallSignature, + SlhDsaParameters::Variant::kTink); + ASSERT_THAT(parameters, IsOk()); + + util::StatusOr public_key = + SlhDsaPublicKey::Create(*parameters, public_key_bytes, + /*id_requirement=*/123, GetPartialKeyAccess()); + ASSERT_THAT(public_key, IsOk()); + + util::StatusOr private_key = SlhDsaPrivateKey::Create( + *public_key, + RestrictedData(private_key_bytes, InsecureSecretKeyAccess::Get()), + GetPartialKeyAccess()); + ASSERT_THAT(private_key, IsOk()); + + util::StatusOr> serialization = + internal::MutableSerializationRegistry::GlobalInstance() + .SerializeKey( + *private_key, /*token=*/absl::nullopt); + ASSERT_THAT(serialization.status(), + StatusIs(absl::StatusCode::kPermissionDenied, + HasSubstr("SecretKeyAccess is required"))); +} + } // namespace } // namespace tink } // namespace crypto