diff options
author | wconner <wconner@google.com> | 2023-06-21 09:08:35 -0700 |
---|---|---|
committer | Copybara-Service <copybara-worker@google.com> | 2023-06-21 09:10:25 -0700 |
commit | 1578d8712483251f75572269f3f0470e68bc93b2 (patch) | |
tree | 3accf6759974a6c1fe9ff5a1034ce844ac07d6c7 /cc/daead | |
parent | 18dc8ea58250ad1a9eeb5e94645adae2e9775b13 (diff) | |
download | tink-1578d8712483251f75572269f3f0470e68bc93b2.tar.gz |
Add AES-SIV proto serialization.
PiperOrigin-RevId: 542273923
Diffstat (limited to 'cc/daead')
-rw-r--r-- | cc/daead/BUILD.bazel | 49 | ||||
-rw-r--r-- | cc/daead/CMakeLists.txt | 47 | ||||
-rw-r--r-- | cc/daead/aes_siv_proto_serialization.cc | 237 | ||||
-rw-r--r-- | cc/daead/aes_siv_proto_serialization.h | 31 | ||||
-rw-r--r-- | cc/daead/aes_siv_proto_serialization_test.cc | 395 |
5 files changed, 759 insertions, 0 deletions
diff --git a/cc/daead/BUILD.bazel b/cc/daead/BUILD.bazel index 97427828a..f5ac9691d 100644 --- a/cc/daead/BUILD.bazel +++ b/cc/daead/BUILD.bazel @@ -155,6 +155,33 @@ cc_library( ], ) +cc_library( + name = "aes_siv_proto_serialization", + srcs = ["aes_siv_proto_serialization.cc"], + hdrs = ["aes_siv_proto_serialization.h"], + include_prefix = "tink/daead", + deps = [ + ":aes_siv_key", + ":aes_siv_parameters", + "//:partial_key_access", + "//:restricted_data", + "//:secret_key_access_token", + "//internal:key_parser", + "//internal:key_serializer", + "//internal:mutable_serialization_registry", + "//internal:parameters_parser", + "//internal:parameters_serializer", + "//internal:proto_key_serialization", + "//internal:proto_parameters_serialization", + "//proto:aes_siv_cc_proto", + "//proto:tink_cc_proto", + "//util:status", + "//util:statusor", + "@com_google_absl//absl/status", + "@com_google_absl//absl/types:optional", + ], +) + # tests cc_test( @@ -288,3 +315,25 @@ cc_test( "@com_google_googletest//:gtest_main", ], ) + +cc_test( + name = "aes_siv_proto_serialization_test", + size = "small", + srcs = ["aes_siv_proto_serialization_test.cc"], + deps = [ + ":aes_siv_key", + ":aes_siv_parameters", + ":aes_siv_proto_serialization", + "//:insecure_secret_key_access", + "//:partial_key_access", + "//:restricted_data", + "//internal:mutable_serialization_registry", + "//internal:proto_key_serialization", + "//internal:proto_parameters_serialization", + "//proto:aes_siv_cc_proto", + "//proto:tink_cc_proto", + "//subtle:random", + "//util:test_matchers", + "@com_google_googletest//:gtest_main", + ], +) diff --git a/cc/daead/CMakeLists.txt b/cc/daead/CMakeLists.txt index 3a1efc047..39210717b 100644 --- a/cc/daead/CMakeLists.txt +++ b/cc/daead/CMakeLists.txt @@ -145,6 +145,32 @@ tink_cc_library( tink::util::statusor ) +tink_cc_library( + NAME aes_siv_proto_serialization + SRCS + aes_siv_proto_serialization.cc + aes_siv_proto_serialization.h + DEPS + tink::daead::aes_siv_key + tink::daead::aes_siv_parameters + absl::status + absl::optional + tink::core::partial_key_access + tink::core::restricted_data + tink::core::secret_key_access_token + 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::status + tink::util::statusor + tink::proto::aes_siv_cc_proto + tink::proto::tink_cc_proto +) + # tests tink_cc_test( @@ -272,3 +298,24 @@ tink_cc_test( tink::util::statusor tink::util::test_matchers ) + +tink_cc_test( + NAME aes_siv_proto_serialization_test + SRCS + aes_siv_proto_serialization_test.cc + DEPS + tink::daead::aes_siv_key + tink::daead::aes_siv_parameters + tink::daead::aes_siv_proto_serialization + gmock + tink::core::insecure_secret_key_access + 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::subtle::random + tink::util::test_matchers + tink::proto::aes_siv_cc_proto + tink::proto::tink_cc_proto +) diff --git a/cc/daead/aes_siv_proto_serialization.cc b/cc/daead/aes_siv_proto_serialization.cc new file mode 100644 index 000000000..c1711c5ac --- /dev/null +++ b/cc/daead/aes_siv_proto_serialization.cc @@ -0,0 +1,237 @@ +// Copyright 2023 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/daead/aes_siv_proto_serialization.h" + +#include <string> + +#include "absl/status/status.h" +#include "absl/types/optional.h" +#include "tink/daead/aes_siv_key.h" +#include "tink/daead/aes_siv_parameters.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/partial_key_access.h" +#include "tink/restricted_data.h" +#include "tink/secret_key_access_token.h" +#include "tink/util/status.h" +#include "tink/util/statusor.h" +#include "proto/aes_siv.pb.h" +#include "proto/tink.pb.h" + +namespace crypto { +namespace tink { +namespace { + +using ::google::crypto::tink::AesSivKeyFormat; +using ::google::crypto::tink::OutputPrefixType; + +using AesSivProtoParametersParserImpl = + internal::ParametersParserImpl<internal::ProtoParametersSerialization, + AesSivParameters>; +using AesSivProtoParametersSerializerImpl = + internal::ParametersSerializerImpl<AesSivParameters, + internal::ProtoParametersSerialization>; +using AesSivProtoKeyParserImpl = + internal::KeyParserImpl<internal::ProtoKeySerialization, AesSivKey>; +using AesSivProtoKeySerializerImpl = + internal::KeySerializerImpl<AesSivKey, internal::ProtoKeySerialization>; + +const absl::string_view kTypeUrl = + "type.googleapis.com/google.crypto.tink.AesSivKey"; + +util::StatusOr<AesSivParameters::Variant> ToVariant( + OutputPrefixType output_prefix_type) { + switch (output_prefix_type) { + case OutputPrefixType::LEGACY: + ABSL_FALLTHROUGH_INTENDED; // Parse LEGACY output prefix as CRUNCHY. + case OutputPrefixType::CRUNCHY: + return AesSivParameters::Variant::kCrunchy; + case OutputPrefixType::RAW: + return AesSivParameters::Variant::kNoPrefix; + case OutputPrefixType::TINK: + return AesSivParameters::Variant::kTink; + default: + return util::Status(absl::StatusCode::kInvalidArgument, + "Could not determine AesSivParameters::Variant"); + } +} + +util::StatusOr<OutputPrefixType> ToOutputPrefixType( + AesSivParameters::Variant variant) { + switch (variant) { + case AesSivParameters::Variant::kCrunchy: + return OutputPrefixType::CRUNCHY; + case AesSivParameters::Variant::kNoPrefix: + return OutputPrefixType::RAW; + case AesSivParameters::Variant::kTink: + return OutputPrefixType::TINK; + default: + return util::Status(absl::StatusCode::kInvalidArgument, + "Could not determine output prefix type"); + } +} + +util::StatusOr<AesSivParameters> ParseParameters( + const internal::ProtoParametersSerialization& serialization) { + if (serialization.GetKeyTemplate().type_url() != kTypeUrl) { + return util::Status(absl::StatusCode::kInvalidArgument, + "Wrong type URL when parsing AesSivParameters."); + } + + AesSivKeyFormat proto_key_format; + if (!proto_key_format.ParseFromString( + serialization.GetKeyTemplate().value())) { + return util::Status(absl::StatusCode::kInvalidArgument, + "Failed to parse AesSivKeyFormat proto"); + } + if (proto_key_format.version() != 0) { + return util::Status(absl::StatusCode::kInvalidArgument, + "Only version 0 keys are accepted."); + } + + util::StatusOr<AesSivParameters::Variant> variant = + ToVariant(serialization.GetKeyTemplate().output_prefix_type()); + if (!variant.ok()) return variant.status(); + + return AesSivParameters::Create(proto_key_format.key_size(), *variant); +} + +util::StatusOr<internal::ProtoParametersSerialization> SerializeParameters( + const AesSivParameters& parameters) { + util::StatusOr<OutputPrefixType> output_prefix_type = + ToOutputPrefixType(parameters.GetVariant()); + if (!output_prefix_type.ok()) return output_prefix_type.status(); + + AesSivKeyFormat proto_key_format; + proto_key_format.set_key_size(parameters.KeySizeInBytes()); + + return internal::ProtoParametersSerialization::Create( + kTypeUrl, *output_prefix_type, proto_key_format.SerializeAsString()); +} + +util::StatusOr<AesSivKey> ParseKey( + const internal::ProtoKeySerialization& serialization, + absl::optional<SecretKeyAccessToken> token) { + if (serialization.TypeUrl() != kTypeUrl) { + return util::Status(absl::StatusCode::kInvalidArgument, + "Wrong type URL when parsing AesSivKey."); + } + if (!token.has_value()) { + return util::Status(absl::StatusCode::kInvalidArgument, + "SecretKeyAccess is required"); + } + google::crypto::tink::AesSivKey proto_key; + RestrictedData restricted_data = serialization.SerializedKeyProto(); + // OSS proto library complains if input is not converted to a string. + if (!proto_key.ParseFromString( + std::string(restricted_data.GetSecret(*token)))) { + return util::Status(absl::StatusCode::kInvalidArgument, + "Failed to parse AesSivKey proto"); + } + if (proto_key.version() != 0) { + return util::Status(absl::StatusCode::kInvalidArgument, + "Only version 0 keys are accepted."); + } + + util::StatusOr<AesSivParameters::Variant> variant = + ToVariant(serialization.GetOutputPrefixType()); + if (!variant.ok()) return variant.status(); + + util::StatusOr<AesSivParameters> parameters = + AesSivParameters::Create(proto_key.key_value().length(), *variant); + if (!parameters.ok()) return parameters.status(); + + return AesSivKey::Create( + *parameters, RestrictedData(proto_key.key_value(), *token), + serialization.IdRequirement(), GetPartialKeyAccess()); +} + +util::StatusOr<internal::ProtoKeySerialization> SerializeKey( + const AesSivKey& key, absl::optional<SecretKeyAccessToken> token) { + util::StatusOr<RestrictedData> restricted_input = + key.GetKeyBytes(GetPartialKeyAccess()); + if (!restricted_input.ok()) return restricted_input.status(); + if (!token.has_value()) { + return util::Status(absl::StatusCode::kInvalidArgument, + "SecretKeyAccess is required"); + } + + google::crypto::tink::AesSivKey proto_key; + proto_key.set_version(0); + // OSS proto library complains if input is not converted to a string. + proto_key.set_key_value(std::string(restricted_input->GetSecret(*token))); + + util::StatusOr<OutputPrefixType> output_prefix_type = + ToOutputPrefixType(key.GetParameters().GetVariant()); + if (!output_prefix_type.ok()) return output_prefix_type.status(); + + RestrictedData restricted_output = + RestrictedData(proto_key.SerializeAsString(), *token); + return internal::ProtoKeySerialization::Create( + kTypeUrl, restricted_output, google::crypto::tink::KeyData::SYMMETRIC, + *output_prefix_type, key.GetIdRequirement()); +} + +AesSivProtoParametersParserImpl* AesSivProtoParametersParser() { + static auto* parser = + new AesSivProtoParametersParserImpl(kTypeUrl, ParseParameters); + return parser; +} + +AesSivProtoParametersSerializerImpl* AesSivProtoParametersSerializer() { + static auto* serializer = + new AesSivProtoParametersSerializerImpl(kTypeUrl, SerializeParameters); + return serializer; +} + +AesSivProtoKeyParserImpl* AesSivProtoKeyParser() { + static auto* parser = new AesSivProtoKeyParserImpl(kTypeUrl, ParseKey); + return parser; +} + +AesSivProtoKeySerializerImpl* AesSivProtoKeySerializer() { + static auto* serializer = new AesSivProtoKeySerializerImpl(SerializeKey); + return serializer; +} + +} // namespace + +util::Status RegisterAesSivProtoSerialization() { + util::Status status = + internal::MutableSerializationRegistry::GlobalInstance() + .RegisterParametersParser(AesSivProtoParametersParser()); + if (!status.ok()) return status; + + status = internal::MutableSerializationRegistry::GlobalInstance() + .RegisterParametersSerializer(AesSivProtoParametersSerializer()); + if (!status.ok()) return status; + + status = internal::MutableSerializationRegistry::GlobalInstance() + .RegisterKeyParser(AesSivProtoKeyParser()); + if (!status.ok()) return status; + + return internal::MutableSerializationRegistry::GlobalInstance() + .RegisterKeySerializer(AesSivProtoKeySerializer()); +} + +} // namespace tink +} // namespace crypto diff --git a/cc/daead/aes_siv_proto_serialization.h b/cc/daead/aes_siv_proto_serialization.h new file mode 100644 index 000000000..daff86719 --- /dev/null +++ b/cc/daead/aes_siv_proto_serialization.h @@ -0,0 +1,31 @@ +// Copyright 2023 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_DAEAD_AES_SIV_PROTO_SERIALIZATION_H_ +#define TINK_DAEAD_AES_SIV_PROTO_SERIALIZATION_H_ + +#include "tink/util/status.h" + +namespace crypto { +namespace tink { + +// Registers proto parsers and serializers for AES-SIV parameters and keys. +crypto::tink::util::Status RegisterAesSivProtoSerialization(); + +} // namespace tink +} // namespace crypto + +#endif // TINK_DAEAD_AES_SIV_PROTO_SERIALIZATION_H_ diff --git a/cc/daead/aes_siv_proto_serialization_test.cc b/cc/daead/aes_siv_proto_serialization_test.cc new file mode 100644 index 000000000..1f6a45a3d --- /dev/null +++ b/cc/daead/aes_siv_proto_serialization_test.cc @@ -0,0 +1,395 @@ +// Copyright 2023 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/daead/aes_siv_proto_serialization.h" + +#include <memory> +#include <string> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "tink/daead/aes_siv_key.h" +#include "tink/daead/aes_siv_parameters.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/partial_key_access.h" +#include "tink/restricted_data.h" +#include "tink/subtle/random.h" +#include "tink/util/test_matchers.h" +#include "proto/aes_siv.pb.h" +#include "proto/tink.pb.h" + +namespace crypto { +namespace tink { +namespace { + +using ::crypto::tink::subtle::Random; +using ::crypto::tink::test::IsOk; +using ::crypto::tink::test::IsOkAndHolds; +using ::crypto::tink::test::StatusIs; +using ::google::crypto::tink::AesSivKeyFormat; +using ::google::crypto::tink::KeyData; +using ::google::crypto::tink::OutputPrefixType; +using ::testing::Eq; +using ::testing::IsTrue; +using ::testing::NotNull; +using ::testing::TestWithParam; +using ::testing::Values; + +struct TestCase { + AesSivParameters::Variant variant; + OutputPrefixType output_prefix_type; + int key_size; + absl::optional<int> id; + std::string output_prefix; +}; + +class AesSivProtoSerializationTest : public TestWithParam<TestCase> { + protected: + void SetUp() override { + internal::MutableSerializationRegistry::GlobalInstance().Reset(); + } +}; + +INSTANTIATE_TEST_SUITE_P( + AesSivProtoSerializationTestSuite, AesSivProtoSerializationTest, + Values(TestCase{AesSivParameters::Variant::kTink, OutputPrefixType::TINK, + /*key_size=*/32, /*id=*/0x02030400, + /*output_prefix=*/std::string("\x01\x02\x03\x04\x00", 5)}, + TestCase{AesSivParameters::Variant::kCrunchy, + OutputPrefixType::CRUNCHY, /*key_size=*/48, + /*id=*/0x01030005, + /*output_prefix=*/std::string("\x00\x01\x03\x00\x05", 5)}, + TestCase{AesSivParameters::Variant::kNoPrefix, OutputPrefixType::RAW, + /*key_size=*/64, /*id=*/absl::nullopt, + /*output_prefix=*/""})); + +TEST_P(AesSivProtoSerializationTest, ParseParameters) { + TestCase test_case = GetParam(); + ASSERT_THAT(RegisterAesSivProtoSerialization(), IsOk()); + + AesSivKeyFormat key_format_proto; + key_format_proto.set_version(0); + key_format_proto.set_key_size(test_case.key_size); + + util::StatusOr<internal::ProtoParametersSerialization> serialization = + internal::ProtoParametersSerialization::Create( + "type.googleapis.com/google.crypto.tink.AesSivKey", + test_case.output_prefix_type, key_format_proto.SerializeAsString()); + ASSERT_THAT(serialization, IsOk()); + + util::StatusOr<std::unique_ptr<Parameters>> params = + internal::MutableSerializationRegistry::GlobalInstance().ParseParameters( + *serialization); + ASSERT_THAT(params, IsOk()); + EXPECT_THAT((*params)->HasIdRequirement(), test_case.id.has_value()); + + const AesSivParameters* siv_params = + dynamic_cast<const AesSivParameters*>(params->get()); + ASSERT_THAT(siv_params, NotNull()); + EXPECT_THAT(siv_params->GetVariant(), Eq(test_case.variant)); + EXPECT_THAT(siv_params->KeySizeInBytes(), Eq(test_case.key_size)); +} + +TEST_F(AesSivProtoSerializationTest, ParseParametersWithInvalidSerialization) { + ASSERT_THAT(RegisterAesSivProtoSerialization(), IsOk()); + + AesSivKeyFormat key_format_proto; + key_format_proto.set_version(0); + key_format_proto.set_key_size(64); + + util::StatusOr<internal::ProtoParametersSerialization> serialization = + internal::ProtoParametersSerialization::Create( + "type.googleapis.com/google.crypto.tink.AesSivKey", + OutputPrefixType::RAW, "invalid_serialization"); + ASSERT_THAT(serialization, IsOk()); + + util::StatusOr<std::unique_ptr<Parameters>> params = + internal::MutableSerializationRegistry::GlobalInstance().ParseParameters( + *serialization); + EXPECT_THAT(params.status(), StatusIs(absl::StatusCode::kInvalidArgument)); +} + +TEST_F(AesSivProtoSerializationTest, ParseParametersWithUnkownOutputPrefix) { + ASSERT_THAT(RegisterAesSivProtoSerialization(), IsOk()); + + AesSivKeyFormat key_format_proto; + key_format_proto.set_version(0); + key_format_proto.set_key_size(64); + + util::StatusOr<internal::ProtoParametersSerialization> serialization = + internal::ProtoParametersSerialization::Create( + "type.googleapis.com/google.crypto.tink.AesSivKey", + OutputPrefixType::UNKNOWN_PREFIX, + key_format_proto.SerializeAsString()); + ASSERT_THAT(serialization, IsOk()); + + util::StatusOr<std::unique_ptr<Parameters>> params = + internal::MutableSerializationRegistry::GlobalInstance().ParseParameters( + *serialization); + EXPECT_THAT(params.status(), StatusIs(absl::StatusCode::kInvalidArgument)); +} + +TEST_F(AesSivProtoSerializationTest, ParseParametersWithInvalidVersion) { + ASSERT_THAT(RegisterAesSivProtoSerialization(), IsOk()); + + AesSivKeyFormat key_format_proto; + key_format_proto.set_version(1); + key_format_proto.set_key_size(64); + + util::StatusOr<internal::ProtoParametersSerialization> serialization = + internal::ProtoParametersSerialization::Create( + "type.googleapis.com/google.crypto.tink.AesSivKey", + OutputPrefixType::RAW, key_format_proto.SerializeAsString()); + ASSERT_THAT(serialization, IsOk()); + + util::StatusOr<std::unique_ptr<Parameters>> params = + internal::MutableSerializationRegistry::GlobalInstance().ParseParameters( + *serialization); + EXPECT_THAT(params.status(), StatusIs(absl::StatusCode::kInvalidArgument)); +} + +TEST_P(AesSivProtoSerializationTest, SerializeParameters) { + TestCase test_case = GetParam(); + ASSERT_THAT(RegisterAesSivProtoSerialization(), IsOk()); + + util::StatusOr<AesSivParameters> parameters = + AesSivParameters::Create(test_case.key_size, test_case.variant); + ASSERT_THAT(parameters, IsOk()); + + util::StatusOr<std::unique_ptr<Serialization>> serialization = + internal::MutableSerializationRegistry::GlobalInstance() + .SerializeParameters<internal::ProtoParametersSerialization>( + *parameters); + ASSERT_THAT(serialization, IsOk()); + EXPECT_THAT((*serialization)->ObjectIdentifier(), + Eq("type.googleapis.com/google.crypto.tink.AesSivKey")); + + const internal::ProtoParametersSerialization* proto_serialization = + dynamic_cast<const internal::ProtoParametersSerialization*>( + serialization->get()); + ASSERT_THAT(proto_serialization, NotNull()); + EXPECT_THAT(proto_serialization->GetKeyTemplate().type_url(), + Eq("type.googleapis.com/google.crypto.tink.AesSivKey")); + EXPECT_THAT(proto_serialization->GetKeyTemplate().output_prefix_type(), + Eq(test_case.output_prefix_type)); + + AesSivKeyFormat key_format; + ASSERT_THAT( + key_format.ParseFromString(proto_serialization->GetKeyTemplate().value()), + IsTrue()); + EXPECT_THAT(key_format.key_size(), Eq(test_case.key_size)); +} + +TEST_P(AesSivProtoSerializationTest, ParseKey) { + TestCase test_case = GetParam(); + ASSERT_THAT(RegisterAesSivProtoSerialization(), IsOk()); + + std::string raw_key_bytes = Random::GetRandomBytes(test_case.key_size); + google::crypto::tink::AesSivKey key_proto; + key_proto.set_version(0); + key_proto.set_key_value(raw_key_bytes); + RestrictedData serialized_key = RestrictedData( + key_proto.SerializeAsString(), InsecureSecretKeyAccess::Get()); + + util::StatusOr<internal::ProtoKeySerialization> serialization = + internal::ProtoKeySerialization::Create( + "type.googleapis.com/google.crypto.tink.AesSivKey", serialized_key, + KeyData::SYMMETRIC, test_case.output_prefix_type, test_case.id); + ASSERT_THAT(serialization, IsOk()); + + util::StatusOr<std::unique_ptr<Key>> key = + internal::MutableSerializationRegistry::GlobalInstance().ParseKey( + *serialization, InsecureSecretKeyAccess::Get()); + ASSERT_THAT(key, IsOk()); + EXPECT_THAT((*key)->GetIdRequirement(), Eq(test_case.id)); + EXPECT_THAT((*key)->GetParameters().HasIdRequirement(), + test_case.id.has_value()); + + util::StatusOr<AesSivParameters> expected_parameters = + AesSivParameters::Create(test_case.key_size, test_case.variant); + ASSERT_THAT(expected_parameters, IsOk()); + + util::StatusOr<AesSivKey> expected_key = AesSivKey::Create( + *expected_parameters, + RestrictedData(raw_key_bytes, InsecureSecretKeyAccess::Get()), + test_case.id, GetPartialKeyAccess()); + ASSERT_THAT(expected_key, IsOk()); + + EXPECT_THAT(**key, Eq(*expected_key)); +} + +TEST_F(AesSivProtoSerializationTest, ParseLegacyKeyAsCrunchy) { + ASSERT_THAT(RegisterAesSivProtoSerialization(), IsOk()); + + std::string raw_key_bytes = Random::GetRandomBytes(64); + google::crypto::tink::AesSivKey key_proto; + key_proto.set_version(0); + key_proto.set_key_value(raw_key_bytes); + RestrictedData serialized_key = RestrictedData( + key_proto.SerializeAsString(), InsecureSecretKeyAccess::Get()); + + util::StatusOr<internal::ProtoKeySerialization> serialization = + internal::ProtoKeySerialization::Create( + "type.googleapis.com/google.crypto.tink.AesSivKey", serialized_key, + KeyData::SYMMETRIC, OutputPrefixType::LEGACY, /*id_requirement=*/123); + ASSERT_THAT(serialization, IsOk()); + + util::StatusOr<std::unique_ptr<Key>> key = + internal::MutableSerializationRegistry::GlobalInstance().ParseKey( + *serialization, InsecureSecretKeyAccess::Get()); + ASSERT_THAT(key, IsOk()); + + const AesSivKey* aes_siv_key = dynamic_cast<const AesSivKey*>(key->get()); + ASSERT_THAT(aes_siv_key, NotNull()); + EXPECT_THAT(aes_siv_key->GetParameters().GetVariant(), + Eq(AesSivParameters::Variant::kCrunchy)); +} + +TEST_F(AesSivProtoSerializationTest, ParseKeyWithInvalidSerialization) { + ASSERT_THAT(RegisterAesSivProtoSerialization(), IsOk()); + + RestrictedData serialized_key = + RestrictedData("invalid_serialization", InsecureSecretKeyAccess::Get()); + + util::StatusOr<internal::ProtoKeySerialization> serialization = + internal::ProtoKeySerialization::Create( + "type.googleapis.com/google.crypto.tink.AesSivKey", serialized_key, + KeyData::SYMMETRIC, OutputPrefixType::TINK, + /*id_requirement=*/0x23456789); + ASSERT_THAT(serialization, IsOk()); + + util::StatusOr<std::unique_ptr<Key>> key = + internal::MutableSerializationRegistry::GlobalInstance().ParseKey( + *serialization, InsecureSecretKeyAccess::Get()); + EXPECT_THAT(key.status(), StatusIs(absl::StatusCode::kInvalidArgument)); +} + +TEST_F(AesSivProtoSerializationTest, ParseKeyNoSecretKeyAccess) { + ASSERT_THAT(RegisterAesSivProtoSerialization(), IsOk()); + + std::string raw_key_bytes = Random::GetRandomBytes(64); + google::crypto::tink::AesSivKey key_proto; + key_proto.set_version(0); + key_proto.set_key_value(raw_key_bytes); + RestrictedData serialized_key = RestrictedData( + key_proto.SerializeAsString(), InsecureSecretKeyAccess::Get()); + + util::StatusOr<internal::ProtoKeySerialization> serialization = + internal::ProtoKeySerialization::Create( + "type.googleapis.com/google.crypto.tink.AesSivKey", serialized_key, + KeyData::SYMMETRIC, 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::kInvalidArgument)); +} + +TEST_F(AesSivProtoSerializationTest, ParseKeyWithInvalidVersion) { + ASSERT_THAT(RegisterAesSivProtoSerialization(), IsOk()); + + std::string raw_key_bytes = Random::GetRandomBytes(64); + google::crypto::tink::AesSivKey key_proto; + key_proto.set_version(1); // Invalid version number. + key_proto.set_key_value(raw_key_bytes); + RestrictedData serialized_key = RestrictedData( + key_proto.SerializeAsString(), InsecureSecretKeyAccess::Get()); + + util::StatusOr<internal::ProtoKeySerialization> serialization = + internal::ProtoKeySerialization::Create( + "type.googleapis.com/google.crypto.tink.AesSivKey", serialized_key, + KeyData::SYMMETRIC, OutputPrefixType::TINK, + /*id_requirement=*/0x23456789); + ASSERT_THAT(serialization, IsOk()); + + util::StatusOr<std::unique_ptr<Key>> key = + internal::MutableSerializationRegistry::GlobalInstance().ParseKey( + *serialization, InsecureSecretKeyAccess::Get()); + EXPECT_THAT(key.status(), StatusIs(absl::StatusCode::kInvalidArgument)); +} + +TEST_P(AesSivProtoSerializationTest, SerializeKey) { + TestCase test_case = GetParam(); + ASSERT_THAT(RegisterAesSivProtoSerialization(), IsOk()); + + util::StatusOr<AesSivParameters> parameters = + AesSivParameters::Create(test_case.key_size, test_case.variant); + ASSERT_THAT(parameters, IsOk()); + + std::string raw_key_bytes = Random::GetRandomBytes(test_case.key_size); + util::StatusOr<AesSivKey> key = AesSivKey::Create( + *parameters, + RestrictedData(raw_key_bytes, InsecureSecretKeyAccess::Get()), + test_case.id, GetPartialKeyAccess()); + ASSERT_THAT(key, IsOk()); + + util::StatusOr<std::unique_ptr<Serialization>> serialization = + internal::MutableSerializationRegistry::GlobalInstance() + .SerializeKey<internal::ProtoKeySerialization>( + *key, InsecureSecretKeyAccess::Get()); + ASSERT_THAT(serialization, IsOk()); + EXPECT_THAT((*serialization)->ObjectIdentifier(), + Eq("type.googleapis.com/google.crypto.tink.AesSivKey")); + + const internal::ProtoKeySerialization* proto_serialization = + dynamic_cast<const internal::ProtoKeySerialization*>( + serialization->get()); + ASSERT_THAT(proto_serialization, NotNull()); + EXPECT_THAT(proto_serialization->TypeUrl(), + Eq("type.googleapis.com/google.crypto.tink.AesSivKey")); + EXPECT_THAT(proto_serialization->KeyMaterialType(), Eq(KeyData::SYMMETRIC)); + EXPECT_THAT(proto_serialization->GetOutputPrefixType(), + Eq(test_case.output_prefix_type)); + EXPECT_THAT(proto_serialization->IdRequirement(), Eq(test_case.id)); + + google::crypto::tink::AesSivKey proto_key; + // OSS proto library complains if input is not converted to a string. + ASSERT_THAT(proto_key.ParseFromString(std::string( + proto_serialization->SerializedKeyProto().GetSecret( + InsecureSecretKeyAccess::Get()))), + IsTrue()); + EXPECT_THAT(proto_key.key_value().size(), Eq(test_case.key_size)); +} + +TEST_F(AesSivProtoSerializationTest, SerializeKeyNoSecretKeyAccess) { + ASSERT_THAT(RegisterAesSivProtoSerialization(), IsOk()); + + util::StatusOr<AesSivParameters> parameters = AesSivParameters::Create( + /*key_size_in_bytes=*/64, AesSivParameters::Variant::kNoPrefix); + ASSERT_THAT(parameters, IsOk()); + + std::string raw_key_bytes = Random::GetRandomBytes(64); + util::StatusOr<AesSivKey> key = AesSivKey::Create( + *parameters, + RestrictedData(raw_key_bytes, InsecureSecretKeyAccess::Get()), + /*id_requirement=*/absl::nullopt, GetPartialKeyAccess()); + ASSERT_THAT(key, IsOk()); + + util::StatusOr<std::unique_ptr<Serialization>> serialization = + internal::MutableSerializationRegistry::GlobalInstance() + .SerializeKey<internal::ProtoKeySerialization>(*key, absl::nullopt); + EXPECT_THAT(serialization.status(), + StatusIs(absl::StatusCode::kInvalidArgument)); +} + +} // namespace +} // namespace tink +} // namespace crypto |