Add proto parser and serializer for SLH-DSA private key.
PiperOrigin-RevId: 625599407
diff --git a/cc/experimental/pqcrypto/signature/slh_dsa_proto_serialization.cc b/cc/experimental/pqcrypto/signature/slh_dsa_proto_serialization.cc
index c9221dd..57931f8 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 SlhDsaProtoPublicKeySerializerImpl =
internal::KeySerializerImpl<SlhDsaPublicKey,
internal::ProtoKeySerialization>;
+using SlhDsaProtoPrivateKeyParserImpl =
+ internal::KeyParserImpl<internal::ProtoKeySerialization, SlhDsaPrivateKey>;
+using SlhDsaProtoPrivateKeySerializerImpl =
+ internal::KeySerializerImpl<SlhDsaPrivateKey,
+ internal::ProtoKeySerialization>;
const absl::string_view kPrivateTypeUrl =
"type.googleapis.com/google.crypto.tink.SlhDsaPrivateKey";
@@ -249,6 +255,46 @@
GetPartialKeyAccess());
}
+util::StatusOr<SlhDsaPrivateKey> ParsePrivateKey(
+ const internal::ProtoKeySerialization& serialization,
+ absl::optional<SecretKeyAccessToken> 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<SlhDsaParameters> parameters = ToParameters(
+ serialization.GetOutputPrefixType(), proto_key.public_key().params());
+ if (!parameters.ok()) {
+ return parameters.status();
+ }
+
+ util::StatusOr<SlhDsaPublicKey> 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<internal::ProtoParametersSerialization> SerializeParameters(
const SlhDsaParameters& parameters) {
util::StatusOr<OutputPrefixType> output_prefix_type =
@@ -295,6 +341,48 @@
*output_prefix_type, key.GetIdRequirement());
}
+util::StatusOr<internal::ProtoKeySerialization> SerializePrivateKey(
+ const SlhDsaPrivateKey& key, absl::optional<SecretKeyAccessToken> token) {
+ if (!token.has_value()) {
+ return util::Status(absl::StatusCode::kPermissionDenied,
+ "SecretKeyAccess is required");
+ }
+ util::StatusOr<RestrictedData> restricted_input =
+ key.GetPrivateKeyBytes(GetPartialKeyAccess());
+ if (!restricted_input.ok()) {
+ return restricted_input.status();
+ }
+
+ util::StatusOr<SlhDsaParams> 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<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());
+}
+
SlhDsaProtoParametersParserImpl& SlhDsaProtoParametersParser() {
static auto parser =
new SlhDsaProtoParametersParserImpl(kPrivateTypeUrl, ParseParameters);
@@ -319,6 +407,18 @@
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 @@
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 8ca54df..b516ac6 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 <cstdint>
#include <memory>
#include <string>
@@ -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 @@
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<uint8_t*>(public_key_bytes.data()),
+ reinterpret_cast<uint8_t*>(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<internal::ProtoKeySerialization> 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<std::unique_ptr<Key>> 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<SlhDsaParameters> 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<SlhDsaPublicKey> expected_public_key =
+ SlhDsaPublicKey::Create(*expected_parameters, public_key_bytes,
+ test_case.id_requirement, GetPartialKeyAccess());
+ ASSERT_THAT(expected_public_key, IsOk());
+
+ util::StatusOr<SlhDsaPrivateKey> 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<internal::ProtoKeySerialization> serialization =
+ internal::ProtoKeySerialization::Create(kPrivateTypeUrl, 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,
+ 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<uint8_t*>(public_key_bytes.data()),
+ reinterpret_cast<uint8_t*>(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<internal::ProtoKeySerialization> serialization =
+ internal::ProtoKeySerialization::Create(kPrivateTypeUrl, 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,
+ 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<uint8_t*>(public_key_bytes.data()),
+ reinterpret_cast<uint8_t*>(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<internal::ProtoKeySerialization> serialization =
+ internal::ProtoKeySerialization::Create(kPrivateTypeUrl, 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(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<uint8_t*>(public_key_bytes.data()),
+ reinterpret_cast<uint8_t*>(private_key_bytes.data()));
+
+ util::StatusOr<SlhDsaParameters> parameters = SlhDsaParameters::Create(
+ SlhDsaParameters::HashType::kSha2, /*private_key_size_in_bytes=*/64,
+ SlhDsaParameters::SignatureType::kSmallSignature, test_case.variant);
+ ASSERT_THAT(parameters, IsOk());
+
+ util::StatusOr<SlhDsaPublicKey> public_key =
+ SlhDsaPublicKey::Create(*parameters, public_key_bytes,
+ test_case.id_requirement, GetPartialKeyAccess());
+ ASSERT_THAT(public_key, IsOk());
+
+ util::StatusOr<SlhDsaPrivateKey> private_key = SlhDsaPrivateKey::Create(
+ *public_key,
+ RestrictedData(private_key_bytes, 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(kPrivateTypeUrl));
+
+ const internal::ProtoKeySerialization* proto_serialization =
+ dynamic_cast<const internal::ProtoKeySerialization*>(
+ 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<uint8_t*>(public_key_bytes.data()),
+ reinterpret_cast<uint8_t*>(private_key_bytes.data()));
+
+ util::StatusOr<SlhDsaParameters> parameters = SlhDsaParameters::Create(
+ SlhDsaParameters::HashType::kSha2, /*private_key_size_in_bytes=*/64,
+ SlhDsaParameters::SignatureType::kSmallSignature,
+ SlhDsaParameters::Variant::kTink);
+ ASSERT_THAT(parameters, IsOk());
+
+ util::StatusOr<SlhDsaPublicKey> public_key =
+ SlhDsaPublicKey::Create(*parameters, public_key_bytes,
+ /*id_requirement=*/123, GetPartialKeyAccess());
+ ASSERT_THAT(public_key, IsOk());
+
+ util::StatusOr<SlhDsaPrivateKey> private_key = SlhDsaPrivateKey::Create(
+ *public_key,
+ RestrictedData(private_key_bytes, 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,
+ HasSubstr("SecretKeyAccess is required")));
+}
+
} // namespace
} // namespace tink
} // namespace crypto