From d942b87f1a4bb5b4c634171180fb47924f2645cc Mon Sep 17 00:00:00 2001 From: William Conner Date: Mon, 16 Dec 2024 08:57:49 -0800 Subject: [PATCH] Add internal proto serialization library for PRF-based key derivation keys. PiperOrigin-RevId: 706726904 Change-Id: I403bb959f42235b0e9fb8aab1b3f6ace8f5b74e4 --- tink/keyderivation/internal/BUILD.bazel | 22 + tink/keyderivation/internal/CMakeLists.txt | 22 + ...key_derivation_proto_serialization_impl.cc | 215 +++++++++- ...erivation_proto_serialization_impl_test.cc | 399 ++++++++++++++++++ 4 files changed, 656 insertions(+), 2 deletions(-) diff --git a/tink/keyderivation/internal/BUILD.bazel b/tink/keyderivation/internal/BUILD.bazel index d661839e..7f22a63a 100644 --- a/tink/keyderivation/internal/BUILD.bazel +++ b/tink/keyderivation/internal/BUILD.bazel @@ -294,21 +294,35 @@ cc_library( deps = [ "//proto:prf_based_deriver_cc_proto", "//proto:tink_cc_proto", + "//tink:key", "//tink:parameters", + "//tink:partial_key_access", + "//tink:restricted_data", + "//tink:secret_key_access_token", + "//tink/internal:call_with_core_dump_protection", "//tink/internal:global_serialization_registry", + "//tink/internal:key_parser", + "//tink/internal:key_serializer", "//tink/internal:mutable_serialization_registry", "//tink/internal:parameters_parser", "//tink/internal:parameters_serializer", + "//tink/internal:proto_key_serialization", "//tink/internal:proto_parameters_serialization", "//tink/internal:serialization", "//tink/internal:serialization_registry", + "//tink/keyderivation:prf_based_key_derivation_key", "//tink/keyderivation:prf_based_key_derivation_parameters", + "//tink/prf:prf_key", "//tink/prf:prf_parameters", + "//tink/util:secret_data", + "//tink/util:secret_proto", "//tink/util:status", "//tink/util:statusor", "@com_google_absl//absl/log:check", + "@com_google_absl//absl/memory", "@com_google_absl//absl/status", "@com_google_absl//absl/strings:string_view", + "@com_google_absl//absl/types:optional", ], ) @@ -321,19 +335,27 @@ cc_test( "//proto:prf_based_deriver_cc_proto", "//proto:tink_cc_proto", "//proto:xchacha20_poly1305_cc_proto", + "//tink:key", "//tink:parameters", + "//tink:partial_key_access", + "//tink:restricted_data", "//tink/aead:xchacha20_poly1305_parameters", + "//tink/internal:internal_insecure_secret_key_access", "//tink/internal:mutable_serialization_registry", + "//tink/internal:proto_key_serialization", "//tink/internal:proto_parameters_serialization", "//tink/internal:serialization", "//tink/internal:serialization_registry", + "//tink/keyderivation:prf_based_key_derivation_key", "//tink/keyderivation:prf_based_key_derivation_parameters", + "//tink/prf:aes_cmac_prf_key", "//tink/prf:aes_cmac_prf_parameters", "//tink/util:statusor", "//tink/util:test_matchers", "@com_google_absl//absl/log:check", "@com_google_absl//absl/status", "@com_google_absl//absl/strings:string_view", + "@com_google_absl//absl/types:optional", "@com_google_googletest//:gtest_main", ], ) diff --git a/tink/keyderivation/internal/CMakeLists.txt b/tink/keyderivation/internal/CMakeLists.txt index 0c874d54..a1ac63cd 100644 --- a/tink/keyderivation/internal/CMakeLists.txt +++ b/tink/keyderivation/internal/CMakeLists.txt @@ -287,18 +287,32 @@ tink_cc_library( prf_based_key_derivation_proto_serialization_impl.h DEPS absl::check + absl::memory absl::status absl::string_view + absl::optional + tink::core::key tink::core::parameters + tink::core::partial_key_access + tink::core::restricted_data + tink::core::secret_key_access_token + tink::internal::call_with_core_dump_protection tink::internal::global_serialization_registry + tink::internal::key_parser + tink::internal::key_serializer tink::internal::mutable_serialization_registry tink::internal::parameters_parser tink::internal::parameters_serializer + tink::internal::proto_key_serialization tink::internal::proto_parameters_serialization tink::internal::serialization tink::internal::serialization_registry + tink::keyderivation::prf_based_key_derivation_key tink::keyderivation::prf_based_key_derivation_parameters + tink::prf::prf_key tink::prf::prf_parameters + tink::util::secret_data + tink::util::secret_proto tink::util::status tink::util::statusor tink::proto::prf_based_deriver_cc_proto @@ -315,13 +329,21 @@ tink_cc_test( absl::check absl::status absl::string_view + absl::optional + tink::core::key tink::core::parameters + tink::core::partial_key_access + tink::core::restricted_data tink::aead::xchacha20_poly1305_parameters + tink::internal::internal_insecure_secret_key_access tink::internal::mutable_serialization_registry + tink::internal::proto_key_serialization tink::internal::proto_parameters_serialization tink::internal::serialization tink::internal::serialization_registry + tink::keyderivation::prf_based_key_derivation_key tink::keyderivation::prf_based_key_derivation_parameters + tink::prf::aes_cmac_prf_key tink::prf::aes_cmac_prf_parameters tink::util::statusor tink::util::test_matchers diff --git a/tink/keyderivation/internal/prf_based_key_derivation_proto_serialization_impl.cc b/tink/keyderivation/internal/prf_based_key_derivation_proto_serialization_impl.cc index 2b0f5f80..b46bfd26 100644 --- a/tink/keyderivation/internal/prf_based_key_derivation_proto_serialization_impl.cc +++ b/tink/keyderivation/internal/prf_based_key_derivation_proto_serialization_impl.cc @@ -17,20 +17,35 @@ #include "tink/keyderivation/internal/prf_based_key_derivation_proto_serialization_impl.h" #include +#include #include "absl/log/check.h" +#include "absl/memory/memory.h" #include "absl/status/status.h" #include "absl/strings/string_view.h" +#include "absl/types/optional.h" +#include "tink/internal/call_with_core_dump_protection.h" #include "tink/internal/global_serialization_registry.h" +#include "tink/internal/key_parser.h" +#include "tink/internal/key_serializer.h" #include "tink/internal/mutable_serialization_registry.h" #include "tink/internal/parameters_parser.h" #include "tink/internal/parameters_serializer.h" +#include "tink/internal/proto_key_serialization.h" #include "tink/internal/proto_parameters_serialization.h" #include "tink/internal/serialization.h" #include "tink/internal/serialization_registry.h" +#include "tink/key.h" +#include "tink/keyderivation/prf_based_key_derivation_key.h" #include "tink/keyderivation/prf_based_key_derivation_parameters.h" #include "tink/parameters.h" +#include "tink/partial_key_access.h" +#include "tink/prf/prf_key.h" #include "tink/prf/prf_parameters.h" +#include "tink/restricted_data.h" +#include "tink/secret_key_access_token.h" +#include "tink/util/secret_data.h" +#include "tink/util/secret_proto.h" #include "tink/util/status.h" #include "tink/util/statusor.h" #include "proto/prf_based_deriver.pb.h" @@ -41,7 +56,12 @@ namespace tink { namespace internal { namespace { +using ::crypto::tink::util::SecretData; +using ::crypto::tink::util::SecretProto; +using ::google::crypto::tink::KeyData; using ::google::crypto::tink::KeyTemplate; +using ::google::crypto::tink::OutputPrefixType; +using ::google::crypto::tink::PrfBasedDeriverKey; using ::google::crypto::tink::PrfBasedDeriverKeyFormat; using PrfBasedKeyDerivationProtoParametersParserImpl = @@ -50,6 +70,10 @@ using PrfBasedKeyDerivationProtoParametersParserImpl = using PrfBasedKeyDerivationProtoParametersSerializerImpl = ParametersSerializerImpl; +using PrfBasedKeyDerivationProtoKeyParserImpl = + KeyParserImpl; +using PrfBasedKeyDerivationProtoKeySerializerImpl = + KeySerializerImpl; const absl::string_view kTypeUrl = "type.googleapis.com/google.crypto.tink.PrfBasedDeriverKey"; @@ -84,6 +108,57 @@ util::StatusOr ParametersToKeyTemplate( return proto_serialization->GetKeyTemplate(); } +util::StatusOr> PrfKeyFromKeyData( + const KeyData& key_data, SecretKeyAccessToken token) { + util::StatusOr proto_key_serialization = + ProtoKeySerialization::Create( + key_data.type_url(), RestrictedData(key_data.value(), token), + key_data.key_material_type(), OutputPrefixType::RAW, + /*id_requirement=*/absl::nullopt); + if (!proto_key_serialization.ok()) { + return proto_key_serialization.status(); + } + + util::StatusOr> key = + GlobalSerializationRegistry().ParseKey(*proto_key_serialization, token); + if (!key.ok()) { + return key.status(); + } + + const PrfKey* prf_key = dynamic_cast(key->get()); + if (prf_key == nullptr) { + return absl::Status(absl::StatusCode::kInvalidArgument, + "Non-PRF key stored in the `prf_key` field."); + } + + return absl::WrapUnique(dynamic_cast(key->release())); +} + +util::StatusOr PrfKeyToKeyData(const PrfKey& prf_key, + SecretKeyAccessToken token) { + util::StatusOr> serialization = + GlobalSerializationRegistry().SerializeKey(prf_key, + token); + if (!serialization.ok()) { + return serialization.status(); + } + + const ProtoKeySerialization* proto_serialization = + dynamic_cast(serialization->get()); + if (proto_serialization == nullptr) { + return util::Status(absl::StatusCode::kInternal, + "Failed to serialize proto key."); + } + + KeyData key_data; + key_data.set_value(util::SecretDataAsStringView( + proto_serialization->SerializedKeyProto().Get(token))); + key_data.set_type_url(proto_serialization->TypeUrl()); + key_data.set_key_material_type(proto_serialization->KeyMaterialType()); + + return key_data; +} + util::StatusOr ParseParameters( const ProtoParametersSerialization& serialization) { if (serialization.GetKeyTemplate().type_url() != kTypeUrl) { @@ -157,6 +232,107 @@ util::StatusOr SerializeParameters( proto_key_format.SerializeAsString()); } +util::StatusOr ParseKey( + const ProtoKeySerialization& serialization, + absl::optional token) { + if (serialization.TypeUrl() != kTypeUrl) { + return util::Status( + absl::StatusCode::kInvalidArgument, + "Wrong type URL when parsing PrfBasedKeyDerivationKey."); + } + if (!token.has_value()) { + return util::Status(absl::StatusCode::kPermissionDenied, + "SecretKeyAccess is required."); + } + + util::StatusOr> + proto_key = SecretProto:: + ParseFromSecretData(serialization.SerializedKeyProto().Get(*token)); + if (!proto_key.ok()) { + return util::Status(absl::StatusCode::kInvalidArgument, + "Failed to parse PrfBasedDeriverKey proto"); + } + if ((*proto_key)->version() != 0) { + return util::Status(absl::StatusCode::kInvalidArgument, + "Only version 0 keys are accepted."); + } + + if (serialization.GetOutputPrefixType() != + (*proto_key)->params().derived_key_template().output_prefix_type()) { + return util::Status( + absl::StatusCode::kInvalidArgument, + "Parsed output prefix type must match derived key output prefix type."); + } + + util::StatusOr> derived_key_parameters = + ParametersFromKeyTemplate((*proto_key)->params().derived_key_template()); + if (!derived_key_parameters.ok()) { + return derived_key_parameters.status(); + } + + util::StatusOr> prf_key = + CallWithCoreDumpProtection( + [&]() { return PrfKeyFromKeyData((*proto_key)->prf_key(), *token); }); + if (!prf_key.ok()) { + return prf_key.status(); + } + + util::StatusOr parameters = + PrfBasedKeyDerivationParameters::Builder() + .SetPrfParameters((*prf_key)->GetParameters()) + .SetDerivedKeyParameters(**derived_key_parameters) + .Build(); + if (!derived_key_parameters.ok()) { + return derived_key_parameters.status(); + } + + return PrfBasedKeyDerivationKey::Create(*parameters, **prf_key, + serialization.IdRequirement(), + GetPartialKeyAccess()); +} + +util::StatusOr SerializeKey( + const PrfBasedKeyDerivationKey& key, + absl::optional token) { + if (!token.has_value()) { + return util::Status(absl::StatusCode::kPermissionDenied, + "SecretKeyAccess is required."); + } + + util::StatusOr derived_key_template = + ParametersToKeyTemplate(key.GetParameters().GetDerivedKeyParameters()); + if (!derived_key_template.ok()) { + return derived_key_template.status(); + } + + SecretProto proto_key; + proto_key->set_version(0); + util::Status status = CallWithCoreDumpProtection([&]() { + util::StatusOr prf_key_data = + PrfKeyToKeyData(key.GetPrfKey(), *token); + if (!prf_key_data.ok()) { + return prf_key_data.status(); + } + *proto_key->mutable_prf_key() = *prf_key_data; + return util::OkStatus(); + }); + if (!status.ok()) { + return status; + } + *proto_key->mutable_params()->mutable_derived_key_template() = + *derived_key_template; + + util::StatusOr serialized_key = proto_key.SerializeAsSecretData(); + if (!serialized_key.ok()) { + return serialized_key.status(); + } + RestrictedData restricted_output = + RestrictedData(*std::move(serialized_key), *token); + return ProtoKeySerialization::Create( + kTypeUrl, restricted_output, google::crypto::tink::KeyData::SYMMETRIC, + derived_key_template->output_prefix_type(), key.GetIdRequirement()); +} + PrfBasedKeyDerivationProtoParametersParserImpl* PrfBasedKeyDerivationProtoParametersParser() { static auto* parser = new PrfBasedKeyDerivationProtoParametersParserImpl( @@ -172,6 +348,19 @@ PrfBasedKeyDerivationProtoParametersSerializer() { return serializer; } +PrfBasedKeyDerivationProtoKeyParserImpl* PrfBasedKeyDerivationProtoKeyParser() { + static auto* parser = + new PrfBasedKeyDerivationProtoKeyParserImpl(kTypeUrl, ParseKey); + return parser; +} + +PrfBasedKeyDerivationProtoKeySerializerImpl* +PrfBasedKeyDerivationProtoKeySerializer() { + static auto* serializer = + new PrfBasedKeyDerivationProtoKeySerializerImpl(SerializeKey); + return serializer; +} + } // namespace util::Status RegisterPrfBasedKeyDerivationProtoSerializationWithMutableRegistry( @@ -182,8 +371,19 @@ util::Status RegisterPrfBasedKeyDerivationProtoSerializationWithMutableRegistry( return status; } - return registry.RegisterParametersSerializer( + status = registry.RegisterParametersSerializer( PrfBasedKeyDerivationProtoParametersSerializer()); + if (!status.ok()) { + return status; + } + + status = registry.RegisterKeyParser(PrfBasedKeyDerivationProtoKeyParser()); + if (!status.ok()) { + return status; + } + + return registry.RegisterKeySerializer( + PrfBasedKeyDerivationProtoKeySerializer()); } util::Status RegisterPrfBasedKeyDerivationProtoSerializationWithRegistryBuilder( @@ -194,8 +394,19 @@ util::Status RegisterPrfBasedKeyDerivationProtoSerializationWithRegistryBuilder( return status; } - return builder.RegisterParametersSerializer( + status = builder.RegisterParametersSerializer( PrfBasedKeyDerivationProtoParametersSerializer()); + if (!status.ok()) { + return status; + } + + status = builder.RegisterKeyParser(PrfBasedKeyDerivationProtoKeyParser()); + if (!status.ok()) { + return status; + } + + return builder.RegisterKeySerializer( + PrfBasedKeyDerivationProtoKeySerializer()); } } // namespace internal diff --git a/tink/keyderivation/internal/prf_based_key_derivation_proto_serialization_impl_test.cc b/tink/keyderivation/internal/prf_based_key_derivation_proto_serialization_impl_test.cc index a1776364..a0d6e0b3 100644 --- a/tink/keyderivation/internal/prf_based_key_derivation_proto_serialization_impl_test.cc +++ b/tink/keyderivation/internal/prf_based_key_derivation_proto_serialization_impl_test.cc @@ -17,6 +17,7 @@ #include "tink/keyderivation/internal/prf_based_key_derivation_proto_serialization_impl.h" #include +#include #include #include "gmock/gmock.h" @@ -24,14 +25,22 @@ #include "absl/log/check.h" #include "absl/status/status.h" #include "absl/strings/string_view.h" +#include "absl/types/optional.h" #include "tink/aead/xchacha20_poly1305_parameters.h" +#include "tink/internal/internal_insecure_secret_key_access.h" #include "tink/internal/mutable_serialization_registry.h" +#include "tink/internal/proto_key_serialization.h" #include "tink/internal/proto_parameters_serialization.h" #include "tink/internal/serialization.h" #include "tink/internal/serialization_registry.h" +#include "tink/key.h" +#include "tink/keyderivation/prf_based_key_derivation_key.h" #include "tink/keyderivation/prf_based_key_derivation_parameters.h" #include "tink/parameters.h" +#include "tink/partial_key_access.h" +#include "tink/prf/aes_cmac_prf_key.h" #include "tink/prf/aes_cmac_prf_parameters.h" +#include "tink/restricted_data.h" #include "tink/util/statusor.h" #include "tink/util/test_matchers.h" #include "proto/aes_cmac_prf.pb.h" @@ -47,6 +56,7 @@ namespace { using ::crypto::tink::test::IsOk; using ::crypto::tink::test::StatusIs; using ::google::crypto::tink::AesCmacPrfKeyFormat; +using ::google::crypto::tink::KeyData; using ::google::crypto::tink::KeyTemplate; using ::google::crypto::tink::OutputPrefixType; using ::google::crypto::tink::PrfBasedDeriverKeyFormat; @@ -101,6 +111,25 @@ XChaCha20Poly1305Parameters GetXChaCha20Poly1305Parameters() { return *parameters; } +AesCmacPrfKey GetAesCmacPrfKey() { + util::StatusOr key = AesCmacPrfKey::Create( + RestrictedData(kPrfKeyValue, GetInsecureSecretKeyAccessInternal()), + GetPartialKeyAccess()); + CHECK_OK(key); + return *key; +} + +KeyData GetAesCmacPrfKeyData() { + google::crypto::tink::AesCmacPrfKey key; + key.set_version(0); + key.set_key_value(std::string("0123456789abcdef")); + KeyData key_data; + key_data.set_type_url(kPrfKeyTypeUrl); + key_data.set_value(key.SerializeAsString()); + key_data.set_key_material_type(KeyData::SYMMETRIC); + return key_data; +} + TEST(PrfBasedKeyDerivationProtoSerializationTest, RegisterTwiceSucceedsWithMutableRegistry) { MutableSerializationRegistry registry; @@ -397,6 +426,376 @@ TEST(PrfBasedKeyDerivationProtoSerializationTest, Eq(kDerivedKeyTypeUrl)); } +TEST(PrfBasedKeyDerivationProtoSerializationTest, ParseKeyWithMutableRegistry) { + MutableSerializationRegistry registry; + ASSERT_THAT( + RegisterPrfBasedKeyDerivationProtoSerializationWithMutableRegistry( + registry), + IsOk()); + + KeyTemplate derived_key_template = GetXChaCha20Poly1305KeyTemplate(); + + google::crypto::tink::PrfBasedDeriverKey key_proto; + key_proto.set_version(0); + *key_proto.mutable_prf_key() = GetAesCmacPrfKeyData(); + *key_proto.mutable_params()->mutable_derived_key_template() = + derived_key_template; + + RestrictedData serialized_key = RestrictedData( + key_proto.SerializeAsString(), GetInsecureSecretKeyAccessInternal()); + util::StatusOr serialization = + internal::ProtoKeySerialization::Create( + kTypeUrl, serialized_key, KeyData::SYMMETRIC, + derived_key_template.output_prefix_type(), /*id_requirement=*/123); + ASSERT_THAT(serialization, IsOk()); + + util::StatusOr> key = + registry.ParseKey(*serialization, GetInsecureSecretKeyAccessInternal()); + ASSERT_THAT(key, IsOk()); + EXPECT_THAT((*key)->GetIdRequirement(), Eq(123)); + EXPECT_THAT( + (*key)->GetParameters().HasIdRequirement(), + derived_key_template.output_prefix_type() != OutputPrefixType::RAW); + + util::StatusOr expected_parameters = + PrfBasedKeyDerivationParameters::Builder() + .SetPrfParameters(GetAesCmacPrfParameters()) + .SetDerivedKeyParameters(GetXChaCha20Poly1305Parameters()) + .Build(); + ASSERT_THAT(expected_parameters, IsOk()); + + util::StatusOr expected_key = + PrfBasedKeyDerivationKey::Create(*expected_parameters, GetAesCmacPrfKey(), + /*id_requirement=*/123, + GetPartialKeyAccess()); + ASSERT_THAT(expected_key, IsOk()); + + EXPECT_THAT(**key, Eq(*expected_key)); +} + +TEST(PrfBasedKeyDerivationProtoSerializationTest, ParseKeyWithRegistryBuilder) { + SerializationRegistry::Builder builder; + ASSERT_THAT( + RegisterPrfBasedKeyDerivationProtoSerializationWithRegistryBuilder( + builder), + IsOk()); + SerializationRegistry registry = std::move(builder).Build(); + + KeyTemplate derived_key_template = GetXChaCha20Poly1305KeyTemplate(); + + google::crypto::tink::PrfBasedDeriverKey key_proto; + key_proto.set_version(0); + *key_proto.mutable_prf_key() = GetAesCmacPrfKeyData(); + *key_proto.mutable_params()->mutable_derived_key_template() = + derived_key_template; + + RestrictedData serialized_key = RestrictedData( + key_proto.SerializeAsString(), GetInsecureSecretKeyAccessInternal()); + util::StatusOr serialization = + internal::ProtoKeySerialization::Create( + kTypeUrl, serialized_key, KeyData::SYMMETRIC, + derived_key_template.output_prefix_type(), /*id_requirement=*/123); + ASSERT_THAT(serialization, IsOk()); + + util::StatusOr> key = + registry.ParseKey(*serialization, GetInsecureSecretKeyAccessInternal()); + ASSERT_THAT(key, IsOk()); + EXPECT_THAT((*key)->GetIdRequirement(), Eq(123)); + EXPECT_THAT( + (*key)->GetParameters().HasIdRequirement(), + derived_key_template.output_prefix_type() != OutputPrefixType::RAW); + + util::StatusOr expected_parameters = + PrfBasedKeyDerivationParameters::Builder() + .SetPrfParameters(GetAesCmacPrfParameters()) + .SetDerivedKeyParameters(GetXChaCha20Poly1305Parameters()) + .Build(); + ASSERT_THAT(expected_parameters, IsOk()); + + util::StatusOr expected_key = + PrfBasedKeyDerivationKey::Create(*expected_parameters, GetAesCmacPrfKey(), + /*id_requirement=*/123, + GetPartialKeyAccess()); + ASSERT_THAT(expected_key, IsOk()); + + EXPECT_THAT(**key, Eq(*expected_key)); +} + +TEST(PrfBasedKeyDerivationProtoSerializationTest, + ParseKeyWithInvalidSerialization) { + MutableSerializationRegistry registry; + ASSERT_THAT( + RegisterPrfBasedKeyDerivationProtoSerializationWithMutableRegistry( + registry), + IsOk()); + + RestrictedData serialized_key = RestrictedData( + "invalid_serialization", GetInsecureSecretKeyAccessInternal()); + + util::StatusOr serialization = + internal::ProtoKeySerialization::Create( + kTypeUrl, serialized_key, KeyData::SYMMETRIC, OutputPrefixType::TINK, + /*id_requirement=*/123); + ASSERT_THAT(serialization, IsOk()); + + util::StatusOr> key = + registry.ParseKey(*serialization, GetInsecureSecretKeyAccessInternal()); + EXPECT_THAT(key.status(), + StatusIs(absl::StatusCode::kInvalidArgument, + HasSubstr("Failed to parse PrfBasedDeriverKey proto"))); +} + +TEST(PrfBasedKeyDerivationProtoSerializationTest, + ParseKeyWithMismatchedOutputPrefix) { + MutableSerializationRegistry registry; + ASSERT_THAT( + RegisterPrfBasedKeyDerivationProtoSerializationWithMutableRegistry( + registry), + IsOk()); + + KeyTemplate derived_key_template = GetXChaCha20Poly1305KeyTemplate(); + + google::crypto::tink::PrfBasedDeriverKey key_proto; + key_proto.set_version(0); + *key_proto.mutable_prf_key() = GetAesCmacPrfKeyData(); + *key_proto.mutable_params()->mutable_derived_key_template() = + derived_key_template; + + RestrictedData serialized_key = RestrictedData( + key_proto.SerializeAsString(), GetInsecureSecretKeyAccessInternal()); + util::StatusOr serialization = + internal::ProtoKeySerialization::Create( + kTypeUrl, serialized_key, KeyData::SYMMETRIC, OutputPrefixType::RAW, + /*id_requirement=*/absl::nullopt); + ASSERT_THAT(serialization, IsOk()); + + util::StatusOr> key = + registry.ParseKey(*serialization, GetInsecureSecretKeyAccessInternal()); + EXPECT_THAT(key, StatusIs(absl::StatusCode::kInvalidArgument, + HasSubstr("Parsed output prefix type must match " + "derived key output prefix type"))); +} + +TEST(PrfBasedKeyDerivationProtoSerializationTest, ParseKeyNoSecretKeyAccess) { + MutableSerializationRegistry registry; + ASSERT_THAT( + RegisterPrfBasedKeyDerivationProtoSerializationWithMutableRegistry( + registry), + IsOk()); + + KeyTemplate derived_key_template = GetXChaCha20Poly1305KeyTemplate(); + + google::crypto::tink::PrfBasedDeriverKey key_proto; + key_proto.set_version(0); + *key_proto.mutable_prf_key() = GetAesCmacPrfKeyData(); + *key_proto.mutable_params()->mutable_derived_key_template() = + derived_key_template; + + RestrictedData serialized_key = RestrictedData( + key_proto.SerializeAsString(), GetInsecureSecretKeyAccessInternal()); + util::StatusOr serialization = + internal::ProtoKeySerialization::Create( + kTypeUrl, serialized_key, KeyData::SYMMETRIC, + derived_key_template.output_prefix_type(), /*id_requirement=*/123); + ASSERT_THAT(serialization, IsOk()); + + util::StatusOr> key = + registry.ParseKey(*serialization, /*token=*/absl::nullopt); + EXPECT_THAT(key, StatusIs(absl::StatusCode::kPermissionDenied)); +} + +TEST(PrfBasedKeyDerivationProtoSerializationTest, + ParseKeyWithInvalidPrfKeyData) { + MutableSerializationRegistry registry; + ASSERT_THAT( + RegisterPrfBasedKeyDerivationProtoSerializationWithMutableRegistry( + registry), + IsOk()); + + KeyTemplate derived_key_template = GetXChaCha20Poly1305KeyTemplate(); + + google::crypto::tink::XChaCha20Poly1305Key invalid_prf_key; + invalid_prf_key.set_version(0); + invalid_prf_key.set_key_value("0123456789abcdef0123456789abcdef"); + KeyData invalid_prf_key_data; + invalid_prf_key_data.set_type_url(kDerivedKeyTypeUrl); + invalid_prf_key_data.set_value(invalid_prf_key.SerializeAsString()); + invalid_prf_key_data.set_key_material_type(KeyData::SYMMETRIC); + + google::crypto::tink::PrfBasedDeriverKey key_proto; + key_proto.set_version(0); + *key_proto.mutable_prf_key() = invalid_prf_key_data; + *key_proto.mutable_params()->mutable_derived_key_template() = + derived_key_template; + + RestrictedData serialized_key = RestrictedData( + key_proto.SerializeAsString(), GetInsecureSecretKeyAccessInternal()); + util::StatusOr serialization = + internal::ProtoKeySerialization::Create( + kTypeUrl, serialized_key, KeyData::SYMMETRIC, + derived_key_template.output_prefix_type(), /*id_requirement=*/123); + ASSERT_THAT(serialization, IsOk()); + + util::StatusOr> key = + registry.ParseKey(*serialization, GetInsecureSecretKeyAccessInternal()); + EXPECT_THAT(key, + StatusIs(absl::StatusCode::kInvalidArgument, + HasSubstr("Non-PRF key stored in the `prf_key` field"))); +} + +TEST(PrfBasedKeyDerivationProtoSerializationTest, ParseKeyWithInvalidVersion) { + MutableSerializationRegistry registry; + ASSERT_THAT( + RegisterPrfBasedKeyDerivationProtoSerializationWithMutableRegistry( + registry), + IsOk()); + + KeyTemplate derived_key_template = GetXChaCha20Poly1305KeyTemplate(); + + google::crypto::tink::PrfBasedDeriverKey key_proto; + key_proto.set_version(1); + *key_proto.mutable_prf_key() = GetAesCmacPrfKeyData(); + *key_proto.mutable_params()->mutable_derived_key_template() = + derived_key_template; + + RestrictedData serialized_key = RestrictedData( + key_proto.SerializeAsString(), GetInsecureSecretKeyAccessInternal()); + util::StatusOr serialization = + internal::ProtoKeySerialization::Create( + kTypeUrl, serialized_key, KeyData::SYMMETRIC, + derived_key_template.output_prefix_type(), /*id_requirement=*/123); + ASSERT_THAT(serialization, IsOk()); + + util::StatusOr> key = + registry.ParseKey(*serialization, GetInsecureSecretKeyAccessInternal()); + EXPECT_THAT(key, StatusIs(absl::StatusCode::kInvalidArgument, + HasSubstr("Only version 0 keys are accepted"))); +} + +TEST(PrfBasedKeyDerivationProtoSerializationTest, + SerializeKeyWithMutableRegistry) { + MutableSerializationRegistry registry; + ASSERT_THAT( + RegisterPrfBasedKeyDerivationProtoSerializationWithMutableRegistry( + registry), + IsOk()); + + util::StatusOr parameters = + PrfBasedKeyDerivationParameters::Builder() + .SetPrfParameters(GetAesCmacPrfParameters()) + .SetDerivedKeyParameters(GetXChaCha20Poly1305Parameters()) + .Build(); + ASSERT_THAT(parameters, IsOk()); + + util::StatusOr key = + PrfBasedKeyDerivationKey::Create(*parameters, GetAesCmacPrfKey(), + /*id_requirement=*/123, + GetPartialKeyAccess()); + ASSERT_THAT(key, IsOk()); + + util::StatusOr> serialization = + registry.SerializeKey( + *key, GetInsecureSecretKeyAccessInternal()); + ASSERT_THAT(serialization, IsOk()); + EXPECT_THAT((*serialization)->ObjectIdentifier(), Eq(kTypeUrl)); + + const internal::ProtoKeySerialization* proto_serialization = + dynamic_cast( + serialization->get()); + ASSERT_THAT(proto_serialization, NotNull()); + EXPECT_THAT(proto_serialization->TypeUrl(), Eq(kTypeUrl)); + EXPECT_THAT(proto_serialization->KeyMaterialType(), Eq(KeyData::SYMMETRIC)); + EXPECT_THAT(proto_serialization->GetOutputPrefixType(), + Eq(GetXChaCha20Poly1305KeyTemplate().output_prefix_type())); + EXPECT_THAT(proto_serialization->IdRequirement(), Eq(123)); + + google::crypto::tink::PrfBasedDeriverKey key_proto; + key_proto.set_version(0); + ASSERT_THAT(key_proto.ParseFromString( + proto_serialization->SerializedKeyProto().GetSecret( + GetInsecureSecretKeyAccessInternal())), + IsTrue()); + EXPECT_THAT(key_proto.prf_key().type_url(), Eq(kPrfKeyTypeUrl)); + EXPECT_THAT(key_proto.params().derived_key_template().type_url(), + Eq(kDerivedKeyTypeUrl)); +} + +TEST(PrfBasedKeyDerivationProtoSerializationTest, + SerializeKeyWithRegistryBuilder) { + SerializationRegistry::Builder builder; + ASSERT_THAT( + RegisterPrfBasedKeyDerivationProtoSerializationWithRegistryBuilder( + builder), + IsOk()); + SerializationRegistry registry = std::move(builder).Build(); + + util::StatusOr parameters = + PrfBasedKeyDerivationParameters::Builder() + .SetPrfParameters(GetAesCmacPrfParameters()) + .SetDerivedKeyParameters(GetXChaCha20Poly1305Parameters()) + .Build(); + ASSERT_THAT(parameters, IsOk()); + + util::StatusOr key = + PrfBasedKeyDerivationKey::Create(*parameters, GetAesCmacPrfKey(), + /*id_requirement=*/123, + GetPartialKeyAccess()); + ASSERT_THAT(key, IsOk()); + + util::StatusOr> serialization = + registry.SerializeKey( + *key, GetInsecureSecretKeyAccessInternal()); + ASSERT_THAT(serialization, IsOk()); + EXPECT_THAT((*serialization)->ObjectIdentifier(), Eq(kTypeUrl)); + + const internal::ProtoKeySerialization* proto_serialization = + dynamic_cast( + serialization->get()); + ASSERT_THAT(proto_serialization, NotNull()); + EXPECT_THAT(proto_serialization->TypeUrl(), Eq(kTypeUrl)); + EXPECT_THAT(proto_serialization->KeyMaterialType(), Eq(KeyData::SYMMETRIC)); + EXPECT_THAT(proto_serialization->GetOutputPrefixType(), + Eq(GetXChaCha20Poly1305KeyTemplate().output_prefix_type())); + EXPECT_THAT(proto_serialization->IdRequirement(), Eq(123)); + + google::crypto::tink::PrfBasedDeriverKey key_proto; + key_proto.set_version(0); + ASSERT_THAT(key_proto.ParseFromString( + proto_serialization->SerializedKeyProto().GetSecret( + GetInsecureSecretKeyAccessInternal())), + IsTrue()); + EXPECT_THAT(key_proto.prf_key().type_url(), Eq(kPrfKeyTypeUrl)); + EXPECT_THAT(key_proto.params().derived_key_template().type_url(), + Eq(kDerivedKeyTypeUrl)); +} + +TEST(PrfBasedKeyDerivationProtoSerializationTest, + SerializeKeyNoSecretKeyAccess) { + MutableSerializationRegistry registry; + ASSERT_THAT( + RegisterPrfBasedKeyDerivationProtoSerializationWithMutableRegistry( + registry), + IsOk()); + + util::StatusOr parameters = + PrfBasedKeyDerivationParameters::Builder() + .SetPrfParameters(GetAesCmacPrfParameters()) + .SetDerivedKeyParameters(GetXChaCha20Poly1305Parameters()) + .Build(); + ASSERT_THAT(parameters, IsOk()); + + util::StatusOr key = + PrfBasedKeyDerivationKey::Create(*parameters, GetAesCmacPrfKey(), + /*id_requirement=*/123, + GetPartialKeyAccess()); + ASSERT_THAT(key, IsOk()); + + util::StatusOr> serialization = + registry.SerializeKey( + *key, /*token=*/absl::nullopt); + ASSERT_THAT(serialization, StatusIs(absl::StatusCode::kPermissionDenied)); +} + } // namespace } // namespace internal } // namespace tink