diff --git a/tink/prf/BUILD.bazel b/tink/prf/BUILD.bazel index f62c85e6..3cd76b23 100644 --- a/tink/prf/BUILD.bazel +++ b/tink/prf/BUILD.bazel @@ -371,33 +371,12 @@ cc_library( cc_library( name = "hkdf_prf_proto_serialization", - srcs = ["hkdf_prf_proto_serialization.cc"], hdrs = ["hkdf_prf_proto_serialization.h"], include_prefix = "tink/prf", deps = [ - ":hkdf_prf_key", - ":hkdf_prf_parameters", - "//proto:common_cc_proto", - "//proto:hkdf_prf_cc_proto", - "//proto:tink_cc_proto", - "//tink:partial_key_access", - "//tink:restricted_data", - "//tink:secret_key_access_token", - "//tink/internal:call_with_core_dump_protection", - "//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/util:secret_data", - "//tink/util:secret_proto", + "//tink/prf/internal:hkdf_prf_proto_serialization_impl", "//tink/util:status", - "//tink/util:statusor", - "@com_google_absl//absl/status", - "@com_google_absl//absl/strings:string_view", - "@com_google_absl//absl/types:optional", ], ) diff --git a/tink/prf/CMakeLists.txt b/tink/prf/CMakeLists.txt index 620f7f50..77262dc5 100644 --- a/tink/prf/CMakeLists.txt +++ b/tink/prf/CMakeLists.txt @@ -347,32 +347,11 @@ tink_cc_library( tink_cc_library( NAME hkdf_prf_proto_serialization SRCS - hkdf_prf_proto_serialization.cc hkdf_prf_proto_serialization.h DEPS - tink::prf::hkdf_prf_key - tink::prf::hkdf_prf_parameters - absl::status - absl::string_view - absl::optional - tink::core::partial_key_access - tink::core::restricted_data - tink::core::secret_key_access_token - tink::internal::call_with_core_dump_protection - 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::util::secret_data - tink::util::secret_proto + tink::prf::internal::hkdf_prf_proto_serialization_impl tink::util::status - tink::util::statusor - tink::proto::common_cc_proto - tink::proto::hkdf_prf_cc_proto - tink::proto::tink_cc_proto ) # tests diff --git a/tink/prf/hkdf_prf_proto_serialization.h b/tink/prf/hkdf_prf_proto_serialization.h index c702c604..46261b45 100644 --- a/tink/prf/hkdf_prf_proto_serialization.h +++ b/tink/prf/hkdf_prf_proto_serialization.h @@ -17,13 +17,19 @@ #ifndef TINK_PRF_HKDF_PRF_PROTO_SERIALIZATION_H_ #define TINK_PRF_HKDF_PRF_PROTO_SERIALIZATION_H_ +#include "tink/internal/mutable_serialization_registry.h" +#include "tink/prf/internal/hkdf_prf_proto_serialization_impl.h" #include "tink/util/status.h" namespace crypto { namespace tink { -// Registers proto parsers and serializers for HKDF-PRF parameters and keys. -crypto::tink::util::Status RegisterHkdfPrfProtoSerialization(); +// Registers proto parsers and serializers for HKDF-PRF parameters and keys into +// global serialization registry. +inline crypto::tink::util::Status RegisterHkdfPrfProtoSerialization() { + return internal::RegisterHkdfPrfProtoSerializationWithMutableRegistry( + internal::MutableSerializationRegistry::GlobalInstance()); +} } // namespace tink } // namespace crypto diff --git a/tink/prf/internal/BUILD.bazel b/tink/prf/internal/BUILD.bazel index d7f86726..12c243f7 100644 --- a/tink/prf/internal/BUILD.bazel +++ b/tink/prf/internal/BUILD.bazel @@ -67,6 +67,39 @@ cc_library( ], ) +cc_library( + name = "hkdf_prf_proto_serialization_impl", + srcs = ["hkdf_prf_proto_serialization_impl.cc"], + hdrs = ["hkdf_prf_proto_serialization_impl.h"], + include_prefix = "tink/prf/internal", + deps = [ + "//proto:common_cc_proto", + "//proto:hkdf_prf_cc_proto", + "//proto:tink_cc_proto", + "//tink:partial_key_access", + "//tink:restricted_data", + "//tink:secret_key_access_token", + "//tink/internal:call_with_core_dump_protection", + "//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_registry", + "//tink/prf:hkdf_prf_key", + "//tink/prf:hkdf_prf_parameters", + "//tink/util:secret_data", + "//tink/util:secret_proto", + "//tink/util:status", + "//tink/util:statusor", + "@com_google_absl//absl/status", + "@com_google_absl//absl/strings:string_view", + "@com_google_absl//absl/types:optional", + ], +) + # tests cc_test( @@ -122,3 +155,34 @@ cc_test( "@com_google_googletest//:gtest_main", ], ) + +cc_test( + name = "hkdf_prf_proto_serialization_impl_test", + srcs = ["hkdf_prf_proto_serialization_impl_test.cc"], + deps = [ + ":hkdf_prf_proto_serialization_impl", + "//proto:common_cc_proto", + "//proto:hkdf_prf_cc_proto", + "//proto:tink_cc_proto", + "//tink:insecure_secret_key_access_testonly", + "//tink:key", + "//tink:parameters", + "//tink:partial_key_access", + "//tink:restricted_data", + "//tink/internal:mutable_serialization_registry", + "//tink/internal:proto_key_serialization", + "//tink/internal:proto_parameters_serialization", + "//tink/internal:serialization", + "//tink/internal:serialization_registry", + "//tink/prf:hkdf_prf_key", + "//tink/prf:hkdf_prf_parameters", + "//tink/subtle:random", + "//tink/util:statusor", + "//tink/util:test_matchers", + "//tink/util:test_util", + "@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/prf/internal/CMakeLists.txt b/tink/prf/internal/CMakeLists.txt index 490643f0..d9b5848c 100644 --- a/tink/prf/internal/CMakeLists.txt +++ b/tink/prf/internal/CMakeLists.txt @@ -62,6 +62,38 @@ tink_cc_library( tink::proto::tink_cc_proto ) +tink_cc_library( + NAME hkdf_prf_proto_serialization_impl + SRCS + hkdf_prf_proto_serialization_impl.cc + hkdf_prf_proto_serialization_impl.h + DEPS + absl::status + absl::string_view + absl::optional + tink::core::partial_key_access + tink::core::restricted_data + tink::core::secret_key_access_token + tink::internal::call_with_core_dump_protection + 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_registry + tink::prf::hkdf_prf_key + tink::prf::hkdf_prf_parameters + tink::util::secret_data + tink::util::secret_proto + tink::util::status + tink::util::statusor + tink::proto::common_cc_proto + tink::proto::hkdf_prf_cc_proto + tink::proto::tink_cc_proto +) + # tests tink_cc_test( @@ -117,3 +149,34 @@ tink_cc_test( tink::proto::aes_cmac_prf_cc_proto tink::proto::tink_cc_proto ) + +tink_cc_test( + NAME hkdf_prf_proto_serialization_impl_test + SRCS + hkdf_prf_proto_serialization_impl_test.cc + DEPS + tink::prf::internal::hkdf_prf_proto_serialization_impl + gmock + absl::status + absl::string_view + absl::optional + tink::core::insecure_secret_key_access_testonly + tink::core::key + tink::core::parameters + tink::core::partial_key_access + tink::core::restricted_data + tink::internal::mutable_serialization_registry + tink::internal::proto_key_serialization + tink::internal::proto_parameters_serialization + tink::internal::serialization + tink::internal::serialization_registry + tink::prf::hkdf_prf_key + tink::prf::hkdf_prf_parameters + tink::subtle::random + tink::util::statusor + tink::util::test_matchers + tink::util::test_util + tink::proto::common_cc_proto + tink::proto::hkdf_prf_cc_proto + tink::proto::tink_cc_proto +) diff --git a/tink/prf/hkdf_prf_proto_serialization.cc b/tink/prf/internal/hkdf_prf_proto_serialization_impl.cc similarity index 84% rename from tink/prf/hkdf_prf_proto_serialization.cc rename to tink/prf/internal/hkdf_prf_proto_serialization_impl.cc index d4212e27..a6e65e3e 100644 --- a/tink/prf/hkdf_prf_proto_serialization.cc +++ b/tink/prf/internal/hkdf_prf_proto_serialization_impl.cc @@ -14,9 +14,8 @@ // //////////////////////////////////////////////////////////////////////////////// -#include "tink/prf/hkdf_prf_proto_serialization.h" +#include "tink/prf/internal/hkdf_prf_proto_serialization_impl.h" -#include #include #include @@ -31,6 +30,7 @@ #include "tink/internal/parameters_serializer.h" #include "tink/internal/proto_key_serialization.h" #include "tink/internal/proto_parameters_serialization.h" +#include "tink/internal/serialization_registry.h" #include "tink/partial_key_access.h" #include "tink/prf/hkdf_prf_key.h" #include "tink/prf/hkdf_prf_parameters.h" @@ -46,6 +46,7 @@ namespace crypto { namespace tink { +namespace internal { namespace { using ::crypto::tink::util::SecretData; @@ -56,15 +57,13 @@ using ::google::crypto::tink::HkdfPrfParams; using ::google::crypto::tink::OutputPrefixType; using HkdfPrfProtoParametersParserImpl = - internal::ParametersParserImpl; + ParametersParserImpl; using HkdfPrfProtoParametersSerializerImpl = - internal::ParametersSerializerImpl; + ParametersSerializerImpl; using HkdfPrfProtoKeyParserImpl = - internal::KeyParserImpl; + KeyParserImpl; using HkdfPrfProtoKeySerializerImpl = - internal::KeySerializerImpl; + KeySerializerImpl; const absl::string_view kTypeUrl = "type.googleapis.com/google.crypto.tink.HkdfPrfKey"; @@ -107,7 +106,7 @@ util::StatusOr ToProtoHashType( } util::StatusOr ParseParameters( - const internal::ProtoParametersSerialization& serialization) { + const ProtoParametersSerialization& serialization) { if (serialization.GetKeyTemplate().type_url() != kTypeUrl) { return util::Status(absl::StatusCode::kInvalidArgument, "Wrong type URL when parsing HkdfPrfParameters."); @@ -145,7 +144,7 @@ util::StatusOr ParseParameters( absl::nullopt); } -util::StatusOr SerializeParameters( +util::StatusOr SerializeParameters( const HkdfPrfParameters& parameters) { util::StatusOr proto_hash_type = ToProtoHashType(parameters.GetHashType()); @@ -164,12 +163,12 @@ util::StatusOr SerializeParameters( } *proto_key_format.mutable_params() = params; - return internal::ProtoParametersSerialization::Create( + return ProtoParametersSerialization::Create( kTypeUrl, OutputPrefixType::RAW, proto_key_format.SerializeAsString()); } util::StatusOr ParseKey( - const internal::ProtoKeySerialization& serialization, + const ProtoKeySerialization& serialization, absl::optional token) { if (serialization.TypeUrl() != kTypeUrl) { return util::Status(absl::StatusCode::kInvalidArgument, @@ -218,7 +217,7 @@ util::StatusOr ParseKey( GetPartialKeyAccess()); } -util::StatusOr SerializeKey( +util::StatusOr SerializeKey( const HkdfPrfKey& key, absl::optional token) { if (!token.has_value()) { return util::Status(absl::StatusCode::kPermissionDenied, @@ -245,7 +244,7 @@ util::StatusOr SerializeKey( SecretProto proto_key; proto_key->set_version(0); *proto_key->mutable_params() = params; - internal::CallWithCoreDumpProtection( + CallWithCoreDumpProtection( [&]() { proto_key->set_key_value(restricted_input->GetSecret(*token)); }); util::StatusOr serialized_key = proto_key.SerializeAsSecretData(); @@ -255,7 +254,7 @@ util::StatusOr SerializeKey( RestrictedData restricted_output = RestrictedData(*std::move(serialized_key), *token); - return internal::ProtoKeySerialization::Create( + return ProtoKeySerialization::Create( kTypeUrl, restricted_output, google::crypto::tink::KeyData::SYMMETRIC, OutputPrefixType::RAW, key.GetIdRequirement()); } @@ -284,30 +283,50 @@ HkdfPrfProtoKeySerializerImpl& HkdfPrfProtoKeySerializer() { } // namespace -util::Status RegisterHkdfPrfProtoSerialization() { +util::Status RegisterHkdfPrfProtoSerializationWithMutableRegistry( + MutableSerializationRegistry& registry) { util::Status status = - internal::MutableSerializationRegistry::GlobalInstance() - .RegisterParametersParser(&HkdfPrfProtoParametersParser()); + registry.RegisterParametersParser(&HkdfPrfProtoParametersParser()); + if (!status.ok()) { + return status; + } + + status = registry.RegisterParametersSerializer( + &HkdfPrfProtoParametersSerializer()); + if (!status.ok()) { + return status; + } + + status = registry.RegisterKeyParser(&HkdfPrfProtoKeyParser()); + if (!status.ok()) { + return status; + } + + return registry.RegisterKeySerializer(&HkdfPrfProtoKeySerializer()); +} + +util::Status RegisterHkdfPrfProtoSerializationWithRegistryBuilder( + SerializationRegistry::Builder& builder) { + util::Status status = + builder.RegisterParametersParser(&HkdfPrfProtoParametersParser()); if (!status.ok()) { return status; } status = - internal::MutableSerializationRegistry::GlobalInstance() - .RegisterParametersSerializer(&HkdfPrfProtoParametersSerializer()); + builder.RegisterParametersSerializer(&HkdfPrfProtoParametersSerializer()); if (!status.ok()) { return status; } - status = internal::MutableSerializationRegistry::GlobalInstance() - .RegisterKeyParser(&HkdfPrfProtoKeyParser()); + status = builder.RegisterKeyParser(&HkdfPrfProtoKeyParser()); if (!status.ok()) { return status; } - return internal::MutableSerializationRegistry::GlobalInstance() - .RegisterKeySerializer(&HkdfPrfProtoKeySerializer()); + return builder.RegisterKeySerializer(&HkdfPrfProtoKeySerializer()); } +} // namespace internal } // namespace tink } // namespace crypto diff --git a/tink/prf/internal/hkdf_prf_proto_serialization_impl.h b/tink/prf/internal/hkdf_prf_proto_serialization_impl.h new file mode 100644 index 00000000..18a17863 --- /dev/null +++ b/tink/prf/internal/hkdf_prf_proto_serialization_impl.h @@ -0,0 +1,42 @@ +// Copyright 2024 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////////// + +#ifndef TINK_PRF_INTERNAL_HKDF_PRF_PROTO_SERIALIZATION_IMPL_H_ +#define TINK_PRF_INTERNAL_HKDF_PRF_PROTO_SERIALIZATION_IMPL_H_ + +#include "tink/internal/mutable_serialization_registry.h" +#include "tink/internal/serialization_registry.h" +#include "tink/util/status.h" + +namespace crypto { +namespace tink { +namespace internal { + +// Registers proto parsers and serializers for HKDF-PRF parameters and +// keys into specified mutable serialization `registry`. +crypto::tink::util::Status RegisterHkdfPrfProtoSerializationWithMutableRegistry( + MutableSerializationRegistry& registry); + +// Registers proto parsers and serializers for HKDF-PRF parameters and +// keys into specified immutable serialization registry `builder`. +crypto::tink::util::Status RegisterHkdfPrfProtoSerializationWithRegistryBuilder( + SerializationRegistry::Builder& builder); + +} // namespace internal +} // namespace tink +} // namespace crypto + +#endif // TINK_PRF_INTERNAL_HKDF_PRF_PROTO_SERIALIZATION_IMPL_H_ diff --git a/tink/prf/internal/hkdf_prf_proto_serialization_impl_test.cc b/tink/prf/internal/hkdf_prf_proto_serialization_impl_test.cc new file mode 100644 index 00000000..c46d4d98 --- /dev/null +++ b/tink/prf/internal/hkdf_prf_proto_serialization_impl_test.cc @@ -0,0 +1,671 @@ +// Copyright 2024 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////////// + +#include "tink/prf/internal/hkdf_prf_proto_serialization_impl.h" + +#include +#include +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/status/status.h" +#include "absl/strings/string_view.h" +#include "absl/types/optional.h" +#include "tink/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/parameters.h" +#include "tink/partial_key_access.h" +#include "tink/prf/hkdf_prf_key.h" +#include "tink/prf/hkdf_prf_parameters.h" +#include "tink/restricted_data.h" +#include "tink/subtle/random.h" +#include "tink/util/statusor.h" +#include "tink/util/test_matchers.h" +#include "tink/util/test_util.h" +#include "proto/common.pb.h" +#include "proto/hkdf_prf.pb.h" +#include "proto/tink.pb.h" + +namespace crypto { +namespace tink { +namespace internal { +namespace { + +using ::crypto::tink::subtle::Random; +using ::crypto::tink::test::IsOk; +using ::crypto::tink::test::StatusIs; +using ::google::crypto::tink::HashType; +using ::google::crypto::tink::HkdfPrfKeyFormat; +using ::google::crypto::tink::HkdfPrfParams; +using ::google::crypto::tink::KeyData; +using ::google::crypto::tink::OutputPrefixType; +using ::testing::Eq; +using ::testing::HasSubstr; +using ::testing::IsFalse; +using ::testing::IsTrue; +using ::testing::NotNull; +using ::testing::TestWithParam; +using ::testing::Values; + +constexpr absl::string_view kTypeUrl = + "type.googleapis.com/google.crypto.tink.HkdfPrfKey"; + +struct TestCase { + int key_size; + HkdfPrfParameters::HashType hash_type; + HashType proto_hash_type; + absl::optional salt; +}; + +using HkdfPrfProtoSerializationTest = TestWithParam; + +INSTANTIATE_TEST_SUITE_P( + HkdfPrfParametersCreateTestSuite, HkdfPrfProtoSerializationTest, + Values(TestCase{/*key_size=*/16, HkdfPrfParameters::HashType::kSha1, + HashType::SHA1, /*salt=*/absl::nullopt}, + TestCase{/*key_size=*/16, HkdfPrfParameters::HashType::kSha224, + HashType::SHA224, + /*salt=*/test::HexDecodeOrDie("00010203040506")}, + TestCase{/*key_size=*/16, HkdfPrfParameters::HashType::kSha256, + HashType::SHA256, + /*salt=*/test::HexDecodeOrDie("00010203040506070809")}, + TestCase{ + /*key_size=*/32, HkdfPrfParameters::HashType::kSha384, + HashType::SHA384, + /*salt=*/test::HexDecodeOrDie("000102030405060708090a0b0c")}, + TestCase{/*key_size=*/32, HkdfPrfParameters::HashType::kSha512, + HashType::SHA512, + /*salt=*/ + test::HexDecodeOrDie("000102030405060708090a0b0c0d0e0f")})); + +TEST_F(HkdfPrfProtoSerializationTest, + RegisterTwiceSucceedsWithMutableRegistry) { + MutableSerializationRegistry registry; + ASSERT_THAT(RegisterHkdfPrfProtoSerializationWithMutableRegistry(registry), + IsOk()); + ASSERT_THAT(RegisterHkdfPrfProtoSerializationWithMutableRegistry(registry), + IsOk()); +} + +TEST_F(HkdfPrfProtoSerializationTest, + RegisterTwiceSucceedsWithRegistryBuilder) { + // TODO: b/378091229 - Consider disallowing duplicate registrations. + SerializationRegistry::Builder builder; + ASSERT_THAT(RegisterHkdfPrfProtoSerializationWithRegistryBuilder(builder), + IsOk()); + ASSERT_THAT(RegisterHkdfPrfProtoSerializationWithRegistryBuilder(builder), + IsOk()); +} + +TEST_P(HkdfPrfProtoSerializationTest, ParseParametersWithMutableRegistry) { + MutableSerializationRegistry registry; + ASSERT_THAT(RegisterHkdfPrfProtoSerializationWithMutableRegistry(registry), + IsOk()); + + TestCase test_case = GetParam(); + + HkdfPrfKeyFormat proto_key_format; + proto_key_format.set_version(0); + proto_key_format.set_key_size(test_case.key_size); + + HkdfPrfParams params; + params.set_hash(test_case.proto_hash_type); + if (test_case.salt.has_value()) { + params.set_salt(*test_case.salt); + } + *proto_key_format.mutable_params() = params; + + util::StatusOr serialization = + ProtoParametersSerialization::Create( + kTypeUrl, OutputPrefixType::RAW, + proto_key_format.SerializeAsString()); + ASSERT_THAT(serialization, IsOk()); + + util::StatusOr> parsed_parameters = + registry.ParseParameters(*serialization); + ASSERT_THAT(parsed_parameters, IsOk()); + EXPECT_THAT((*parsed_parameters)->HasIdRequirement(), IsFalse()); + + util::StatusOr expected_parameters = + HkdfPrfParameters::Create(test_case.key_size, test_case.hash_type, + test_case.salt); + ASSERT_THAT(expected_parameters, IsOk()); + ASSERT_THAT(**parsed_parameters, Eq(*expected_parameters)); +} + +TEST_P(HkdfPrfProtoSerializationTest, ParseParametersWithRegistryBuilder) { + SerializationRegistry::Builder builder; + ASSERT_THAT(RegisterHkdfPrfProtoSerializationWithRegistryBuilder(builder), + IsOk()); + SerializationRegistry registry = std::move(builder).Build(); + + TestCase test_case = GetParam(); + + HkdfPrfKeyFormat proto_key_format; + proto_key_format.set_version(0); + proto_key_format.set_key_size(test_case.key_size); + + HkdfPrfParams params; + params.set_hash(test_case.proto_hash_type); + if (test_case.salt.has_value()) { + params.set_salt(*test_case.salt); + } + *proto_key_format.mutable_params() = params; + + util::StatusOr serialization = + ProtoParametersSerialization::Create( + kTypeUrl, OutputPrefixType::RAW, + proto_key_format.SerializeAsString()); + ASSERT_THAT(serialization, IsOk()); + + util::StatusOr> parsed_parameters = + registry.ParseParameters(*serialization); + ASSERT_THAT(parsed_parameters, IsOk()); + EXPECT_THAT((*parsed_parameters)->HasIdRequirement(), IsFalse()); + + util::StatusOr expected_parameters = + HkdfPrfParameters::Create(test_case.key_size, test_case.hash_type, + test_case.salt); + ASSERT_THAT(expected_parameters, IsOk()); + ASSERT_THAT(**parsed_parameters, Eq(*expected_parameters)); +} + +TEST_F(HkdfPrfProtoSerializationTest, + ParseParametersWithInvalidSerializationFails) { + MutableSerializationRegistry registry; + ASSERT_THAT(RegisterHkdfPrfProtoSerializationWithMutableRegistry(registry), + IsOk()); + + util::StatusOr serialization = + ProtoParametersSerialization::Create(kTypeUrl, OutputPrefixType::RAW, + "invalid_serialization"); + ASSERT_THAT(serialization, IsOk()); + + util::StatusOr> params = + registry.ParseParameters(*serialization); + EXPECT_THAT(params.status(), + StatusIs(absl::StatusCode::kInvalidArgument, + HasSubstr("Failed to parse HkdfPrfKeyFormat proto"))); +} + +using HkdfPrfParsePrefixTest = TestWithParam; + +INSTANTIATE_TEST_SUITE_P(HkdfPrfParsePrefixTestSuite, HkdfPrfParsePrefixTest, + Values(OutputPrefixType::TINK, + OutputPrefixType::CRUNCHY, + OutputPrefixType::LEGACY, + OutputPrefixType::UNKNOWN_PREFIX)); + +TEST_P(HkdfPrfParsePrefixTest, ParseParametersWithInvalidPrefixFails) { + OutputPrefixType invalid_output_prefix_type = GetParam(); + MutableSerializationRegistry registry; + ASSERT_THAT(RegisterHkdfPrfProtoSerializationWithMutableRegistry(registry), + IsOk()); + + HkdfPrfKeyFormat proto_key_format; + proto_key_format.set_version(0); + proto_key_format.set_key_size(16); + proto_key_format.mutable_params()->set_hash(HashType::SHA256); + + util::StatusOr serialization = + ProtoParametersSerialization::Create( + kTypeUrl, invalid_output_prefix_type, + proto_key_format.SerializeAsString()); + ASSERT_THAT(serialization, IsOk()); + + util::StatusOr> params = + registry.ParseParameters(*serialization); + EXPECT_THAT( + params.status(), + StatusIs( + absl::StatusCode::kInvalidArgument, + HasSubstr("Output prefix type must be RAW for HkdfPrfParameters"))); +} + +TEST_F(HkdfPrfProtoSerializationTest, ParseParametersWithInvalidVersionFails) { + MutableSerializationRegistry registry; + ASSERT_THAT(RegisterHkdfPrfProtoSerializationWithMutableRegistry(registry), + IsOk()); + + HkdfPrfKeyFormat proto_key_format; + proto_key_format.set_version(1); // invalid version + proto_key_format.set_key_size(16); + proto_key_format.mutable_params()->set_hash(HashType::SHA256); + + util::StatusOr serialization = + ProtoParametersSerialization::Create( + kTypeUrl, OutputPrefixType::RAW, + proto_key_format.SerializeAsString()); + ASSERT_THAT(serialization, IsOk()); + + util::StatusOr> params = + registry.ParseParameters(*serialization); + EXPECT_THAT(params.status(), + StatusIs(absl::StatusCode::kInvalidArgument, + HasSubstr("Only version 0 keys are accepted"))); +} + +TEST_F(HkdfPrfProtoSerializationTest, ParseParametersWithUnknownHashTypeFails) { + MutableSerializationRegistry registry; + ASSERT_THAT(RegisterHkdfPrfProtoSerializationWithMutableRegistry(registry), + IsOk()); + + HkdfPrfKeyFormat key_format_proto; + key_format_proto.set_key_size(16); + key_format_proto.set_version(0); + key_format_proto.mutable_params()->set_hash(HashType::UNKNOWN_HASH); + + util::StatusOr serialization = + ProtoParametersSerialization::Create( + kTypeUrl, OutputPrefixType::RAW, + key_format_proto.SerializeAsString()); + ASSERT_THAT(serialization, IsOk()); + + util::StatusOr> params = + registry.ParseParameters(*serialization); + ASSERT_THAT(params.status(), + StatusIs(absl::StatusCode::kInvalidArgument, + HasSubstr("Could not determine HashType"))); +} + +TEST_P(HkdfPrfProtoSerializationTest, SerializeParametersWithMutableRegistry) { + MutableSerializationRegistry registry; + ASSERT_THAT(RegisterHkdfPrfProtoSerializationWithMutableRegistry(registry), + IsOk()); + + TestCase test_case = GetParam(); + util::StatusOr parameters = HkdfPrfParameters::Create( + test_case.key_size, test_case.hash_type, test_case.salt); + ASSERT_THAT(parameters, IsOk()); + + util::StatusOr> serialization = + registry.SerializeParameters(*parameters); + ASSERT_THAT(serialization, IsOk()); + EXPECT_THAT((*serialization)->ObjectIdentifier(), Eq(kTypeUrl)); + + const ProtoParametersSerialization* proto_serialization = + dynamic_cast(serialization->get()); + ASSERT_THAT(proto_serialization, NotNull()); + EXPECT_THAT(proto_serialization->GetKeyTemplate().type_url(), Eq(kTypeUrl)); + EXPECT_THAT(proto_serialization->GetKeyTemplate().output_prefix_type(), + Eq(OutputPrefixType::RAW)); + + HkdfPrfKeyFormat key_format; + ASSERT_THAT( + key_format.ParseFromString(proto_serialization->GetKeyTemplate().value()), + IsTrue()); + EXPECT_THAT(key_format.version(), Eq(0)); + EXPECT_THAT(key_format.key_size(), Eq(test_case.key_size)); + EXPECT_THAT(key_format.params().hash(), Eq(test_case.proto_hash_type)); + if (test_case.salt.has_value()) { + EXPECT_THAT(key_format.params().salt(), Eq(*test_case.salt)); + } +} + +TEST_P(HkdfPrfProtoSerializationTest, SerializeParametersWithRegistryBuilder) { + SerializationRegistry::Builder builder; + ASSERT_THAT(RegisterHkdfPrfProtoSerializationWithRegistryBuilder(builder), + IsOk()); + SerializationRegistry registry = std::move(builder).Build(); + + TestCase test_case = GetParam(); + util::StatusOr parameters = HkdfPrfParameters::Create( + test_case.key_size, test_case.hash_type, test_case.salt); + ASSERT_THAT(parameters, IsOk()); + + util::StatusOr> serialization = + registry.SerializeParameters(*parameters); + ASSERT_THAT(serialization, IsOk()); + EXPECT_THAT((*serialization)->ObjectIdentifier(), Eq(kTypeUrl)); + + const ProtoParametersSerialization* proto_serialization = + dynamic_cast(serialization->get()); + ASSERT_THAT(proto_serialization, NotNull()); + EXPECT_THAT(proto_serialization->GetKeyTemplate().type_url(), Eq(kTypeUrl)); + EXPECT_THAT(proto_serialization->GetKeyTemplate().output_prefix_type(), + Eq(OutputPrefixType::RAW)); + + HkdfPrfKeyFormat key_format; + ASSERT_THAT( + key_format.ParseFromString(proto_serialization->GetKeyTemplate().value()), + IsTrue()); + EXPECT_THAT(key_format.version(), Eq(0)); + EXPECT_THAT(key_format.key_size(), Eq(test_case.key_size)); + EXPECT_THAT(key_format.params().hash(), Eq(test_case.proto_hash_type)); + if (test_case.salt.has_value()) { + EXPECT_THAT(key_format.params().salt(), Eq(*test_case.salt)); + } +} + +TEST_P(HkdfPrfProtoSerializationTest, ParseKeyWithMutableRegistry) { + MutableSerializationRegistry registry; + ASSERT_THAT(RegisterHkdfPrfProtoSerializationWithMutableRegistry(registry), + IsOk()); + + TestCase test_case = GetParam(); + + std::string raw_key_bytes = Random::GetRandomBytes(test_case.key_size); + HkdfPrfParams proto_params; + proto_params.set_hash(test_case.proto_hash_type); + if (test_case.salt.has_value()) { + proto_params.set_salt(*test_case.salt); + } + google::crypto::tink::HkdfPrfKey key_proto; + key_proto.set_version(0); + key_proto.set_key_value(raw_key_bytes); + *key_proto.mutable_params() = proto_params; + RestrictedData serialized_key = RestrictedData( + key_proto.SerializeAsString(), InsecureSecretKeyAccess::Get()); + + util::StatusOr serialization = + ProtoKeySerialization::Create(kTypeUrl, serialized_key, + KeyData::SYMMETRIC, OutputPrefixType::RAW, + /*id_requirement=*/absl::nullopt); + ASSERT_THAT(serialization, IsOk()); + + util::StatusOr> key = + registry.ParseKey(*serialization, InsecureSecretKeyAccess::Get()); + ASSERT_THAT(key, IsOk()); + EXPECT_THAT((*key)->GetIdRequirement(), Eq(absl::nullopt)); + EXPECT_THAT((*key)->GetParameters().HasIdRequirement(), IsFalse()); + + util::StatusOr expected_parameters = + HkdfPrfParameters::Create(test_case.key_size, test_case.hash_type, + test_case.salt); + ASSERT_THAT(expected_parameters, IsOk()); + + util::StatusOr expected_key = HkdfPrfKey::Create( + *expected_parameters, + RestrictedData(raw_key_bytes, InsecureSecretKeyAccess::Get()), + GetPartialKeyAccess()); + ASSERT_THAT(expected_key, IsOk()); + + EXPECT_THAT(**key, Eq(*expected_key)); +} + +TEST_P(HkdfPrfProtoSerializationTest, ParseKeyWithRegistryBuilder) { + SerializationRegistry::Builder builder; + ASSERT_THAT(RegisterHkdfPrfProtoSerializationWithRegistryBuilder(builder), + IsOk()); + SerializationRegistry registry = std::move(builder).Build(); + + TestCase test_case = GetParam(); + + std::string raw_key_bytes = Random::GetRandomBytes(test_case.key_size); + HkdfPrfParams proto_params; + proto_params.set_hash(test_case.proto_hash_type); + if (test_case.salt.has_value()) { + proto_params.set_salt(*test_case.salt); + } + google::crypto::tink::HkdfPrfKey key_proto; + key_proto.set_version(0); + key_proto.set_key_value(raw_key_bytes); + *key_proto.mutable_params() = proto_params; + RestrictedData serialized_key = RestrictedData( + key_proto.SerializeAsString(), InsecureSecretKeyAccess::Get()); + + util::StatusOr serialization = + ProtoKeySerialization::Create(kTypeUrl, serialized_key, + KeyData::SYMMETRIC, OutputPrefixType::RAW, + /*id_requirement=*/absl::nullopt); + ASSERT_THAT(serialization, IsOk()); + + util::StatusOr> key = + registry.ParseKey(*serialization, InsecureSecretKeyAccess::Get()); + ASSERT_THAT(key, IsOk()); + EXPECT_THAT((*key)->GetIdRequirement(), Eq(absl::nullopt)); + EXPECT_THAT((*key)->GetParameters().HasIdRequirement(), IsFalse()); + + util::StatusOr expected_parameters = + HkdfPrfParameters::Create(test_case.key_size, test_case.hash_type, + test_case.salt); + ASSERT_THAT(expected_parameters, IsOk()); + + util::StatusOr expected_key = HkdfPrfKey::Create( + *expected_parameters, + RestrictedData(raw_key_bytes, InsecureSecretKeyAccess::Get()), + GetPartialKeyAccess()); + ASSERT_THAT(expected_key, IsOk()); + + EXPECT_THAT(**key, Eq(*expected_key)); +} + +TEST_F(HkdfPrfProtoSerializationTest, ParseKeyWithInvalidSerializationFails) { + MutableSerializationRegistry registry; + ASSERT_THAT(RegisterHkdfPrfProtoSerializationWithMutableRegistry(registry), + IsOk()); + + RestrictedData serialized_key = + RestrictedData("invalid_serialization", InsecureSecretKeyAccess::Get()); + + util::StatusOr serialization = + ProtoKeySerialization::Create(kTypeUrl, serialized_key, + KeyData::SYMMETRIC, OutputPrefixType::RAW, + /*id_requirement=*/absl::nullopt); + ASSERT_THAT(serialization, IsOk()); + + util::StatusOr> key = + registry.ParseKey(*serialization, InsecureSecretKeyAccess::Get()); + EXPECT_THAT(key.status(), + StatusIs(absl::StatusCode::kInvalidArgument, + HasSubstr("Failed to parse HkdfPrfKey proto"))); +} + +TEST_P(HkdfPrfParsePrefixTest, ParseKeyWithInvalidPrefixFails) { + MutableSerializationRegistry registry; + ASSERT_THAT(RegisterHkdfPrfProtoSerializationWithMutableRegistry(registry), + IsOk()); + + OutputPrefixType invalid_output_prefix_type = GetParam(); + + std::string raw_key_bytes = Random::GetRandomBytes(16); + google::crypto::tink::HkdfPrfKey key_proto; + key_proto.set_version(0); + key_proto.set_key_value(raw_key_bytes); + key_proto.mutable_params()->set_hash(HashType::SHA256); + RestrictedData serialized_key = RestrictedData( + key_proto.SerializeAsString(), InsecureSecretKeyAccess::Get()); + + util::StatusOr serialization = + ProtoKeySerialization::Create(kTypeUrl, serialized_key, + KeyData::SYMMETRIC, + invalid_output_prefix_type, + /*id_requirement=*/123); + ASSERT_THAT(serialization, IsOk()); + util::StatusOr> key = + registry.ParseKey(*serialization, InsecureSecretKeyAccess::Get()); + EXPECT_THAT( + key.status(), + StatusIs(absl::StatusCode::kInvalidArgument, + HasSubstr("Output prefix type must be RAW for HkdfPrfKey"))); +} + +TEST_F(HkdfPrfProtoSerializationTest, ParseKeyNoSecretKeyAccessFails) { + MutableSerializationRegistry registry; + ASSERT_THAT(RegisterHkdfPrfProtoSerializationWithMutableRegistry(registry), + IsOk()); + + std::string raw_key_bytes = Random::GetRandomBytes(16); + google::crypto::tink::HkdfPrfKey key_proto; + key_proto.set_version(0); + key_proto.set_key_value(raw_key_bytes); + key_proto.mutable_params()->set_hash(HashType::SHA256); + RestrictedData serialized_key = RestrictedData( + key_proto.SerializeAsString(), InsecureSecretKeyAccess::Get()); + + util::StatusOr serialization = + ProtoKeySerialization::Create(kTypeUrl, serialized_key, + KeyData::SYMMETRIC, OutputPrefixType::RAW, + /*id_requirement=*/absl::nullopt); + ASSERT_THAT(serialization, IsOk()); + + util::StatusOr> key = + registry.ParseKey(*serialization, /*token=*/absl::nullopt); + EXPECT_THAT(key.status(), StatusIs(absl::StatusCode::kPermissionDenied, + HasSubstr("SecretKeyAccess is required"))); +} + +TEST_F(HkdfPrfProtoSerializationTest, ParseKeyWithInvalidVersionFails) { + MutableSerializationRegistry registry; + ASSERT_THAT(RegisterHkdfPrfProtoSerializationWithMutableRegistry(registry), + IsOk()); + + std::string raw_key_bytes = Random::GetRandomBytes(16); + google::crypto::tink::HkdfPrfKey key_proto; + key_proto.set_version(1); // Invalid version number. + key_proto.set_key_value(raw_key_bytes); + key_proto.mutable_params()->set_hash(HashType::SHA256); + RestrictedData serialized_key = RestrictedData( + key_proto.SerializeAsString(), InsecureSecretKeyAccess::Get()); + + util::StatusOr serialization = + ProtoKeySerialization::Create(kTypeUrl, serialized_key, + KeyData::SYMMETRIC, OutputPrefixType::RAW, + /*id_requirement=*/absl::nullopt); + ASSERT_THAT(serialization, IsOk()); + + util::StatusOr> key = + registry.ParseKey(*serialization, InsecureSecretKeyAccess::Get()); + EXPECT_THAT(key.status(), + StatusIs(absl::StatusCode::kInvalidArgument, + HasSubstr("Only version 0 keys are accepted"))); +} + +TEST_P(HkdfPrfProtoSerializationTest, SerializeKeyWithMutableRegistry) { + MutableSerializationRegistry registry; + ASSERT_THAT(RegisterHkdfPrfProtoSerializationWithMutableRegistry(registry), + IsOk()); + + TestCase test_case = GetParam(); + + std::string raw_key_bytes = Random::GetRandomBytes(test_case.key_size); + util::StatusOr parameters = HkdfPrfParameters::Create( + test_case.key_size, test_case.hash_type, test_case.salt); + ASSERT_THAT(parameters, IsOk()); + util::StatusOr key = HkdfPrfKey::Create( + *parameters, + RestrictedData(raw_key_bytes, InsecureSecretKeyAccess::Get()), + GetPartialKeyAccess()); + ASSERT_THAT(key, IsOk()); + + util::StatusOr> serialization = + registry.SerializeKey( + *key, InsecureSecretKeyAccess::Get()); + ASSERT_THAT(serialization, IsOk()); + EXPECT_THAT((*serialization)->ObjectIdentifier(), Eq(kTypeUrl)); + + const 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(OutputPrefixType::RAW)); + EXPECT_THAT(proto_serialization->IdRequirement(), Eq(absl::nullopt)); + + google::crypto::tink::HkdfPrfKey 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(raw_key_bytes)); + EXPECT_THAT(proto_key.params().hash(), Eq(test_case.proto_hash_type)); + if (test_case.salt.has_value()) { + EXPECT_THAT(proto_key.params().salt(), Eq(*test_case.salt)); + } +} + +TEST_P(HkdfPrfProtoSerializationTest, SerializeKeyWithRegistryBuilder) { + SerializationRegistry::Builder builder; + ASSERT_THAT(RegisterHkdfPrfProtoSerializationWithRegistryBuilder(builder), + IsOk()); + SerializationRegistry registry = std::move(builder).Build(); + + TestCase test_case = GetParam(); + + std::string raw_key_bytes = Random::GetRandomBytes(test_case.key_size); + util::StatusOr parameters = HkdfPrfParameters::Create( + test_case.key_size, test_case.hash_type, test_case.salt); + ASSERT_THAT(parameters, IsOk()); + util::StatusOr key = HkdfPrfKey::Create( + *parameters, + RestrictedData(raw_key_bytes, InsecureSecretKeyAccess::Get()), + GetPartialKeyAccess()); + ASSERT_THAT(key, IsOk()); + + util::StatusOr> serialization = + registry.SerializeKey( + *key, InsecureSecretKeyAccess::Get()); + ASSERT_THAT(serialization, IsOk()); + EXPECT_THAT((*serialization)->ObjectIdentifier(), Eq(kTypeUrl)); + + const 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(OutputPrefixType::RAW)); + EXPECT_THAT(proto_serialization->IdRequirement(), Eq(absl::nullopt)); + + google::crypto::tink::HkdfPrfKey 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(raw_key_bytes)); + EXPECT_THAT(proto_key.params().hash(), Eq(test_case.proto_hash_type)); + if (test_case.salt.has_value()) { + EXPECT_THAT(proto_key.params().salt(), Eq(*test_case.salt)); + } +} + +TEST_F(HkdfPrfProtoSerializationTest, SerializeKeyNoSecretKeyAccessFails) { + MutableSerializationRegistry registry; + ASSERT_THAT(RegisterHkdfPrfProtoSerializationWithMutableRegistry(registry), + IsOk()); + + std::string raw_key_bytes = Random::GetRandomBytes(16); + util::StatusOr parameters = HkdfPrfParameters::Create( + /*key_size_in_bytes=*/16, HkdfPrfParameters::HashType::kSha256, + /*salt=*/absl::nullopt); + ASSERT_THAT(parameters, IsOk()); + util::StatusOr key = HkdfPrfKey::Create( + *parameters, + RestrictedData(raw_key_bytes, InsecureSecretKeyAccess::Get()), + GetPartialKeyAccess()); + ASSERT_THAT(key, IsOk()); + + util::StatusOr> serialization = + registry.SerializeKey(*key, + /*token=*/absl::nullopt); + EXPECT_THAT(serialization.status(), + StatusIs(absl::StatusCode::kPermissionDenied, + HasSubstr("SecretKeyAccess is required"))); +} + +} // namespace +} // namespace internal +} // namespace tink +} // namespace crypto