diff --git a/cc/jwt/BUILD.bazel b/cc/jwt/BUILD.bazel index db361d2b52..4df8baa39d 100644 --- a/cc/jwt/BUILD.bazel +++ b/cc/jwt/BUILD.bazel @@ -225,6 +225,27 @@ cc_library( ], ) +cc_library( + name = "jwt_hmac_key", + srcs = ["jwt_hmac_key.cc"], + hdrs = ["jwt_hmac_key.h"], + include_prefix = "tink/jwt", + deps = [ + ":jwt_hmac_parameters", + ":jwt_mac_key", + "//:key", + "//:partial_key_access_token", + "//:restricted_data", + "//util:status", + "//util:statusor", + "@com_google_absl//absl/base:endian", + "@com_google_absl//absl/status", + "@com_google_absl//absl/strings", + "@com_google_absl//absl/strings:string_view", + "@com_google_absl//absl/types:optional", + ], +) + # tests cc_test( @@ -390,3 +411,19 @@ cc_test( "@com_google_googletest//:gtest_main", ], ) + +cc_test( + name = "jwt_hmac_key_test", + srcs = ["jwt_hmac_key_test.cc"], + deps = [ + ":jwt_hmac_key", + ":jwt_hmac_parameters", + "//:partial_key_access", + "//:restricted_data", + "//util:statusor", + "//util:test_matchers", + "@com_google_absl//absl/status", + "@com_google_absl//absl/types:optional", + "@com_google_googletest//:gtest_main", + ], +) diff --git a/cc/jwt/CMakeLists.txt b/cc/jwt/CMakeLists.txt index 01cc3d09a9..261a99d220 100644 --- a/cc/jwt/CMakeLists.txt +++ b/cc/jwt/CMakeLists.txt @@ -204,6 +204,26 @@ tink_cc_library( tink::util::statusor ) +tink_cc_library( + NAME jwt_hmac_key + SRCS + jwt_hmac_key.cc + jwt_hmac_key.h + DEPS + tink::jwt::jwt_hmac_parameters + tink::jwt::jwt_mac_key + absl::endian + absl::status + absl::strings + absl::string_view + absl::optional + tink::core::key + tink::core::partial_key_access_token + tink::core::restricted_data + tink::util::status + tink::util::statusor +) + # tests tink_cc_test( @@ -364,3 +384,19 @@ tink_cc_test( tink::util::statusor tink::util::test_matchers ) + +tink_cc_test( + NAME jwt_hmac_key_test + SRCS + jwt_hmac_key_test.cc + DEPS + tink::jwt::jwt_hmac_key + tink::jwt::jwt_hmac_parameters + gmock + absl::status + absl::optional + tink::core::partial_key_access + tink::core::restricted_data + tink::util::statusor + tink::util::test_matchers +) diff --git a/cc/jwt/jwt_hmac_key.cc b/cc/jwt/jwt_hmac_key.cc new file mode 100644 index 0000000000..cd191a5196 --- /dev/null +++ b/cc/jwt/jwt_hmac_key.cc @@ -0,0 +1,151 @@ +// 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/jwt/jwt_hmac_key.h" + +#include + +#include "absl/base/internal/endian.h" +#include "absl/status/status.h" +#include "absl/strings/escaping.h" +#include "absl/strings/string_view.h" +#include "absl/types/optional.h" +#include "tink/jwt/jwt_hmac_parameters.h" +#include "tink/key.h" +#include "tink/partial_key_access_token.h" +#include "tink/restricted_data.h" +#include "tink/util/status.h" +#include "tink/util/statusor.h" + +namespace crypto { +namespace tink { + +JwtHmacKey::Builder& JwtHmacKey::Builder::SetParameters( + const JwtHmacParameters& parameters) { + parameters_ = parameters; + return *this; +} + +JwtHmacKey::Builder& JwtHmacKey::Builder::SetKeyBytes( + const RestrictedData& key_bytes) { + key_bytes_ = key_bytes; + return *this; +} + +JwtHmacKey::Builder& JwtHmacKey::Builder::SetIdRequirement(int id_requirement) { + id_requirement_ = id_requirement; + return *this; +} + +JwtHmacKey::Builder& JwtHmacKey::Builder::SetCustomKid( + absl::string_view custom_kid) { + custom_kid_ = custom_kid.data(); + return *this; +} + +util::StatusOr> JwtHmacKey::Builder::ComputeKid() { + switch (parameters_->GetKidStrategy()) { + case JwtHmacParameters::KidStrategy::kBase64EncodedKeyId: { + if (custom_kid_.has_value()) { + return util::Status( + absl::StatusCode::kInvalidArgument, + "Custom kid must not be set for KidStrategy::kBase64EncodedKeyId."); + } + std::string base64_kid; + char buffer[4]; + absl::big_endian::Store32(buffer, *id_requirement_); + absl::WebSafeBase64Escape(absl::string_view(buffer, 4), &base64_kid); + return base64_kid; + } + case JwtHmacParameters::KidStrategy::kCustom: { + if (!custom_kid_.has_value()) { + return util::Status(absl::StatusCode::kInvalidArgument, + "Custom kid must be set for KidStrategy::kCustom."); + } + return custom_kid_; + } + case JwtHmacParameters::KidStrategy::kIgnored: { + if (custom_kid_.has_value()) { + return util::Status( + absl::StatusCode::kInvalidArgument, + "Custom kid must not be set for KidStrategy::kIgnored."); + } + return absl::nullopt; + } + default: + // Should be unreachable if all valid kid strategies have been handled. + return util::Status(absl::StatusCode::kFailedPrecondition, + "Unknown kid strategy."); + } +} + +util::StatusOr JwtHmacKey::Builder::Build( + PartialKeyAccessToken token) { + if (!parameters_.has_value()) { + return util::Status(absl::StatusCode::kInvalidArgument, + "JWT HMAC parameters must be specified."); + } + if (!key_bytes_.has_value()) { + return util::Status(absl::StatusCode::kInvalidArgument, + "JWT HMAC key bytes must be specified."); + } + if (parameters_->KeySizeInBytes() != key_bytes_->size()) { + return util::Status( + absl::StatusCode::kInvalidArgument, + "Actual JWT HMAC key size does not match size specified in " + "the parameters."); + } + if (parameters_->HasIdRequirement() && !id_requirement_.has_value()) { + return util::Status( + absl::StatusCode::kInvalidArgument, + "Cannot create key without ID requirement with parameters with ID " + "requirement"); + } + if (!parameters_->HasIdRequirement() && id_requirement_.has_value()) { + return util::Status( + absl::StatusCode::kInvalidArgument, + "Cannot create key with ID requirement with parameters without ID " + "requirement"); + } + util::StatusOr> kid = ComputeKid(); + if (!kid.ok()) { + return kid.status(); + } + return JwtHmacKey(*parameters_, *key_bytes_, id_requirement_, *kid); +} + +bool JwtHmacKey::operator==(const Key& other) const { + const JwtHmacKey* that = dynamic_cast(&other); + if (that == nullptr) { + return false; + } + if (parameters_ != that->parameters_) { + return false; + } + if (key_bytes_ != that->key_bytes_) { + return false; + } + if (id_requirement_ != that->id_requirement_) { + return false; + } + if (kid_ != that->kid_) { + return false; + } + return true; +} + +} // namespace tink +} // namespace crypto diff --git a/cc/jwt/jwt_hmac_key.h b/cc/jwt/jwt_hmac_key.h new file mode 100644 index 0000000000..7ab3360bb8 --- /dev/null +++ b/cc/jwt/jwt_hmac_key.h @@ -0,0 +1,107 @@ +// 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_JWT_JWT_HMAC_KEY_H_ +#define TINK_JWT_JWT_HMAC_KEY_H_ + +#include + +#include "absl/strings/string_view.h" +#include "absl/types/optional.h" +#include "tink/jwt/jwt_hmac_parameters.h" +#include "tink/jwt/jwt_mac_key.h" +#include "tink/key.h" +#include "tink/partial_key_access_token.h" +#include "tink/restricted_data.h" +#include "tink/util/statusor.h" + +namespace crypto { +namespace tink { + +// Represents functions to authenticate and verify JWTs using HMAC. +class JwtHmacKey : public JwtMacKey { + public: + // Creates JWT HMAC key instances. + class Builder { + public: + // Copyable and movable. + Builder(const Builder& other) = default; + Builder& operator=(const Builder& other) = default; + Builder(Builder&& other) = default; + Builder& operator=(Builder&& other) = default; + + // Creates initially empty parameters builder. + Builder() = default; + + Builder& SetParameters(const JwtHmacParameters& parameters); + Builder& SetKeyBytes(const RestrictedData& key_bytes); + Builder& SetIdRequirement(int id_requirement); + Builder& SetCustomKid(absl::string_view custom_kid); + + // Creates JWT HMAC key object from this builder. + util::StatusOr Build(PartialKeyAccessToken token); + + private: + util::StatusOr> ComputeKid(); + + absl::optional parameters_; + absl::optional key_bytes_; + absl::optional id_requirement_; + absl::optional custom_kid_; + }; + + // Copyable and movable. + JwtHmacKey(const JwtHmacKey& other) = default; + JwtHmacKey& operator=(const JwtHmacKey& other) = default; + JwtHmacKey(JwtHmacKey&& other) = default; + JwtHmacKey& operator=(JwtHmacKey&& other) = default; + + const RestrictedData& GetKeyBytes(PartialKeyAccessToken token) const { + return key_bytes_; + } + + const JwtHmacParameters& GetParameters() const override { + return parameters_; + } + + absl::optional GetIdRequirement() const override { + return id_requirement_; + } + + absl::optional GetKid() const override { return kid_; } + + bool operator==(const Key& other) const override; + + private: + JwtHmacKey(const JwtHmacParameters& parameters, + const RestrictedData& key_bytes, + absl::optional id_requirement, + absl::optional kid) + : parameters_(parameters), + key_bytes_(key_bytes), + id_requirement_(id_requirement), + kid_(kid) {} + + JwtHmacParameters parameters_; + RestrictedData key_bytes_; + absl::optional id_requirement_; + absl::optional kid_; +}; + +} // namespace tink +} // namespace crypto + +#endif // TINK_JWT_JWT_HMAC_KEY_H_ diff --git a/cc/jwt/jwt_hmac_key_test.cc b/cc/jwt/jwt_hmac_key_test.cc new file mode 100644 index 0000000000..e64035c7d2 --- /dev/null +++ b/cc/jwt/jwt_hmac_key_test.cc @@ -0,0 +1,418 @@ +// 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/jwt/jwt_hmac_key.h" + +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/status/status.h" +#include "absl/types/optional.h" +#include "tink/jwt/jwt_hmac_parameters.h" +#include "tink/partial_key_access.h" +#include "tink/restricted_data.h" +#include "tink/util/statusor.h" +#include "tink/util/test_matchers.h" + +namespace crypto { +namespace tink { +namespace { + +using ::crypto::tink::test::IsOk; +using ::crypto::tink::test::StatusIs; +using ::testing::Eq; +using ::testing::HasSubstr; +using ::testing::TestWithParam; +using ::testing::Values; + +struct TestCase { + int key_size_in_bytes; + JwtHmacParameters::KidStrategy kid_strategy; + JwtHmacParameters::Algorithm algorithm; + absl::optional custom_kid; + absl::optional id_requirement; + absl::optional expected_kid; +}; + +using JwtHmacKeyTest = TestWithParam; + +INSTANTIATE_TEST_SUITE_P( + JwtHmacKeyTestSuite, JwtHmacKeyTest, + Values(TestCase{/*key_size_in_bytes=*/16, + JwtHmacParameters::KidStrategy::kBase64EncodedKeyId, + JwtHmacParameters::Algorithm::kHs256, + /*custom_kid=*/absl::nullopt, /*id_requirement=*/123, + /*expected_kid=*/"AAAAew"}, + TestCase{/*key_size_in_bytes=*/32, + JwtHmacParameters::KidStrategy::kCustom, + JwtHmacParameters::Algorithm::kHs384, + /*custom_kid=*/"custom_kid", + /*id_requirement=*/absl::nullopt, + /*expected_kid=*/"custom_kid"}, + TestCase{/*key_size_in_bytes=*/32, + JwtHmacParameters::KidStrategy::kIgnored, + JwtHmacParameters::Algorithm::kHs512, + /*custom_kid=*/absl::nullopt, + /*id_requirement=*/absl::nullopt, + /*expected_kid=*/absl::nullopt})); + +TEST_P(JwtHmacKeyTest, CreateSucceeds) { + TestCase test_case = GetParam(); + + util::StatusOr params = JwtHmacParameters::Create( + test_case.key_size_in_bytes, test_case.kid_strategy, test_case.algorithm); + ASSERT_THAT(params, IsOk()); + + RestrictedData secret = RestrictedData(test_case.key_size_in_bytes); + JwtHmacKey::Builder builder = + JwtHmacKey::Builder().SetParameters(*params).SetKeyBytes(secret); + if (test_case.id_requirement.has_value()) { + builder.SetIdRequirement(*test_case.id_requirement); + } + if (test_case.custom_kid.has_value()) { + builder.SetCustomKid(*test_case.custom_kid); + } + util::StatusOr key = builder.Build(GetPartialKeyAccess()); + ASSERT_THAT(key, IsOk()); + + EXPECT_THAT(key->GetParameters(), Eq(*params)); + EXPECT_THAT(key->GetKeyBytes(GetPartialKeyAccess()), Eq(secret)); + EXPECT_THAT(key->GetIdRequirement(), Eq(test_case.id_requirement)); + EXPECT_THAT(key->GetKid(), Eq(test_case.expected_kid)); +} + +TEST(JwtHmacKeyTest, CreateKeyWithMismatchedKeySizeFails) { + // Key size parameter is 32 bytes. + util::StatusOr params = JwtHmacParameters::Create( + /*key_size_in_bytes=*/32, + JwtHmacParameters::KidStrategy::kBase64EncodedKeyId, + JwtHmacParameters::Algorithm::kHs256); + ASSERT_THAT(params, IsOk()); + + // Key material is 16 bytes (another valid key length). + RestrictedData mismatched_secret = RestrictedData(/*num_random_bytes=*/16); + JwtHmacKey::Builder builder = JwtHmacKey::Builder() + .SetParameters(*params) + .SetKeyBytes(mismatched_secret) + .SetIdRequirement(123); + + EXPECT_THAT(builder.Build(GetPartialKeyAccess()).status(), + StatusIs(absl::StatusCode::kInvalidArgument, + HasSubstr("Actual JWT HMAC key size does not match"))); +} + +TEST(JwtHmacKeyTest, CreateKeyWithoutKeyBytesFails) { + util::StatusOr params = JwtHmacParameters::Create( + /*key_size_in_bytes=*/32, + JwtHmacParameters::KidStrategy::kBase64EncodedKeyId, + JwtHmacParameters::Algorithm::kHs256); + ASSERT_THAT(params, IsOk()); + + JwtHmacKey::Builder builder = + JwtHmacKey::Builder().SetParameters(*params).SetIdRequirement(123); + + EXPECT_THAT(builder.Build(GetPartialKeyAccess()).status(), + StatusIs(absl::StatusCode::kInvalidArgument, + HasSubstr("JWT HMAC key bytes must be specified"))); +} + +TEST(JwtHmacKeyTest, CreateKeyWithoutParametersFails) { + util::StatusOr params = JwtHmacParameters::Create( + /*key_size_in_bytes=*/32, + JwtHmacParameters::KidStrategy::kBase64EncodedKeyId, + JwtHmacParameters::Algorithm::kHs256); + ASSERT_THAT(params, IsOk()); + + RestrictedData secret = RestrictedData(/*num_random_bytes=*/32); + JwtHmacKey::Builder builder = + JwtHmacKey::Builder().SetKeyBytes(secret).SetIdRequirement(123); + + EXPECT_THAT(builder.Build(GetPartialKeyAccess()).status(), + StatusIs(absl::StatusCode::kInvalidArgument, + HasSubstr("JWT HMAC parameters must be specified"))); +} + +TEST(JwtHmacKeyTest, CreateBase64EncodedKidWithoutIdRequirementFails) { + util::StatusOr params = JwtHmacParameters::Create( + /*key_size_in_bytes=*/32, + JwtHmacParameters::KidStrategy::kBase64EncodedKeyId, + JwtHmacParameters::Algorithm::kHs256); + ASSERT_THAT(params, IsOk()); + + RestrictedData secret = RestrictedData(/*num_random_bytes=*/32); + JwtHmacKey::Builder builder = + JwtHmacKey::Builder().SetParameters(*params).SetKeyBytes(secret); + + EXPECT_THAT(builder.Build(GetPartialKeyAccess()).status(), + StatusIs(absl::StatusCode::kInvalidArgument, + HasSubstr("Cannot create key without ID requirement " + "with parameters with ID requirement"))); +} + +TEST(JwtHmacKeyTest, CreateBase64EncodedKidWithCustomKidFails) { + util::StatusOr params = JwtHmacParameters::Create( + /*key_size_in_bytes=*/32, + JwtHmacParameters::KidStrategy::kBase64EncodedKeyId, + JwtHmacParameters::Algorithm::kHs256); + ASSERT_THAT(params, IsOk()); + + RestrictedData secret = RestrictedData(/*num_random_bytes=*/32); + JwtHmacKey::Builder builder = JwtHmacKey::Builder() + .SetParameters(*params) + .SetKeyBytes(secret) + .SetIdRequirement(123) + .SetCustomKid("custom_kid"); + + EXPECT_THAT(builder.Build(GetPartialKeyAccess()).status(), + StatusIs(absl::StatusCode::kInvalidArgument, + HasSubstr("Custom kid must not be set for " + "KidStrategy::kBase64EncodedKeyId"))); +} + +TEST(JwtHmacKeyTest, CreateCustomKidWithIdRequirementFails) { + util::StatusOr params = JwtHmacParameters::Create( + /*key_size_in_bytes=*/32, JwtHmacParameters::KidStrategy::kCustom, + JwtHmacParameters::Algorithm::kHs256); + ASSERT_THAT(params, IsOk()); + + RestrictedData secret = RestrictedData(/*num_random_bytes=*/32); + JwtHmacKey::Builder builder = JwtHmacKey::Builder() + .SetParameters(*params) + .SetKeyBytes(secret) + .SetCustomKid("custom_kid") + .SetIdRequirement(123); + + EXPECT_THAT(builder.Build(GetPartialKeyAccess()).status(), + StatusIs(absl::StatusCode::kInvalidArgument, + HasSubstr("Cannot create key with ID requirement with " + "parameters without ID requirement"))); +} + +TEST(JwtHmacKeyTest, CreateCustomKidWithoutCustomKidFails) { + util::StatusOr params = JwtHmacParameters::Create( + /*key_size_in_bytes=*/32, JwtHmacParameters::KidStrategy::kCustom, + JwtHmacParameters::Algorithm::kHs256); + ASSERT_THAT(params, IsOk()); + + RestrictedData secret = RestrictedData(/*num_random_bytes=*/32); + JwtHmacKey::Builder builder = + JwtHmacKey::Builder().SetParameters(*params).SetKeyBytes(secret); + + EXPECT_THAT(builder.Build(GetPartialKeyAccess()).status(), + StatusIs(absl::StatusCode::kInvalidArgument, + HasSubstr("Custom kid must be set"))); +} + +TEST(JwtHmacKeyTest, CreateIgnoredKidWithIdRequirementFails) { + util::StatusOr params = JwtHmacParameters::Create( + /*key_size_in_bytes=*/32, JwtHmacParameters::KidStrategy::kIgnored, + JwtHmacParameters::Algorithm::kHs256); + ASSERT_THAT(params, IsOk()); + + RestrictedData secret = RestrictedData(/*num_random_bytes=*/32); + JwtHmacKey::Builder builder = JwtHmacKey::Builder() + .SetParameters(*params) + .SetKeyBytes(secret) + .SetIdRequirement(123); + + EXPECT_THAT(builder.Build(GetPartialKeyAccess()).status(), + StatusIs(absl::StatusCode::kInvalidArgument, + HasSubstr("Cannot create key with ID requirement with " + "parameters without ID requirement"))); +} + +TEST(JwtHmacKeyTest, CreateIgnoredKidWithCustomKidFails) { + util::StatusOr params = JwtHmacParameters::Create( + /*key_size_in_bytes=*/32, JwtHmacParameters::KidStrategy::kIgnored, + JwtHmacParameters::Algorithm::kHs256); + ASSERT_THAT(params, IsOk()); + + RestrictedData secret = RestrictedData(/*num_random_bytes=*/32); + JwtHmacKey::Builder builder = JwtHmacKey::Builder() + .SetParameters(*params) + .SetKeyBytes(secret) + .SetCustomKid("custom_kid"); + + EXPECT_THAT( + builder.Build(GetPartialKeyAccess()).status(), + StatusIs( + absl::StatusCode::kInvalidArgument, + HasSubstr("Custom kid must not be set for KidStrategy::kIgnored"))); +} + +TEST_P(JwtHmacKeyTest, KeyEquals) { + TestCase test_case = GetParam(); + + util::StatusOr params = JwtHmacParameters::Create( + test_case.key_size_in_bytes, test_case.kid_strategy, test_case.algorithm); + ASSERT_THAT(params, IsOk()); + + RestrictedData secret = RestrictedData(test_case.key_size_in_bytes); + JwtHmacKey::Builder builder = + JwtHmacKey::Builder().SetParameters(*params).SetKeyBytes(secret); + if (test_case.id_requirement.has_value()) { + builder.SetIdRequirement(*test_case.id_requirement); + } + if (test_case.custom_kid.has_value()) { + builder.SetCustomKid(*test_case.custom_kid); + } + util::StatusOr key = builder.Build(GetPartialKeyAccess()); + ASSERT_THAT(key, IsOk()); + + JwtHmacKey::Builder other_builder = + JwtHmacKey::Builder().SetParameters(*params).SetKeyBytes(secret); + if (test_case.id_requirement.has_value()) { + other_builder.SetIdRequirement(*test_case.id_requirement); + } + if (test_case.custom_kid.has_value()) { + other_builder.SetCustomKid(*test_case.custom_kid); + } + util::StatusOr other_key = + other_builder.Build(GetPartialKeyAccess()); + ASSERT_THAT(other_key, IsOk()); + + EXPECT_TRUE(*key == *other_key); + EXPECT_TRUE(*other_key == *key); + EXPECT_FALSE(*key != *other_key); + EXPECT_FALSE(*other_key != *key); +} + +TEST(JwtHmacKeyTest, DifferentParametersNotEqual) { + util::StatusOr params = JwtHmacParameters::Create( + /*key_size_in_bytes=*/32, + JwtHmacParameters::KidStrategy::kBase64EncodedKeyId, + JwtHmacParameters::Algorithm::kHs256); + ASSERT_THAT(params, IsOk()); + + util::StatusOr other_params = JwtHmacParameters::Create( + /*key_size_in_bytes=*/32, + JwtHmacParameters::KidStrategy::kBase64EncodedKeyId, + JwtHmacParameters::Algorithm::kHs384); + ASSERT_THAT(other_params, IsOk()); + + RestrictedData secret = RestrictedData(/*num_random_bytes=*/32); + + util::StatusOr key = JwtHmacKey::Builder() + .SetParameters(*params) + .SetKeyBytes(secret) + .SetIdRequirement(123) + .Build(GetPartialKeyAccess()); + ASSERT_THAT(key, IsOk()); + + util::StatusOr other_key = JwtHmacKey::Builder() + .SetParameters(*other_params) + .SetKeyBytes(secret) + .SetIdRequirement(123) + .Build(GetPartialKeyAccess()); + ASSERT_THAT(other_key, IsOk()); + + EXPECT_TRUE(*key != *other_key); + EXPECT_TRUE(*other_key != *key); + EXPECT_FALSE(*key == *other_key); + EXPECT_FALSE(*other_key == *key); +} + +TEST(JwtHmacKeyTest, DifferentSecretDataNotEqual) { + util::StatusOr params = JwtHmacParameters::Create( + /*key_size_in_bytes=*/32, + JwtHmacParameters::KidStrategy::kBase64EncodedKeyId, + JwtHmacParameters::Algorithm::kHs256); + ASSERT_THAT(params, IsOk()); + + RestrictedData secret = RestrictedData(/*num_random_bytes=*/32); + RestrictedData other_secret = RestrictedData(/*num_random_bytes=*/32); + + util::StatusOr key = JwtHmacKey::Builder() + .SetParameters(*params) + .SetKeyBytes(secret) + .SetIdRequirement(123) + .Build(GetPartialKeyAccess()); + ASSERT_THAT(key, IsOk()); + + util::StatusOr other_key = JwtHmacKey::Builder() + .SetParameters(*params) + .SetKeyBytes(other_secret) + .SetIdRequirement(123) + .Build(GetPartialKeyAccess()); + ASSERT_THAT(other_key, IsOk()); + + EXPECT_TRUE(*key != *other_key); + EXPECT_TRUE(*other_key != *key); + EXPECT_FALSE(*key == *other_key); + EXPECT_FALSE(*other_key == *key); +} + +TEST(JwtHmacKeyTest, DifferentIdRequirementNotEqual) { + util::StatusOr params = JwtHmacParameters::Create( + /*key_size_in_bytes=*/32, + JwtHmacParameters::KidStrategy::kBase64EncodedKeyId, + JwtHmacParameters::Algorithm::kHs256); + ASSERT_THAT(params, IsOk()); + + RestrictedData secret = RestrictedData(/*num_random_bytes=*/32); + + util::StatusOr key = JwtHmacKey::Builder() + .SetParameters(*params) + .SetKeyBytes(secret) + .SetIdRequirement(123) + .Build(GetPartialKeyAccess()); + ASSERT_THAT(key, IsOk()); + + util::StatusOr other_key = JwtHmacKey::Builder() + .SetParameters(*params) + .SetKeyBytes(secret) + .SetIdRequirement(456) + .Build(GetPartialKeyAccess()); + ASSERT_THAT(other_key, IsOk()); + + EXPECT_TRUE(*key != *other_key); + EXPECT_TRUE(*other_key != *key); + EXPECT_FALSE(*key == *other_key); + EXPECT_FALSE(*other_key == *key); +} + +TEST(JwtHmacKeyTest, DifferentCustomKidNotEqual) { + util::StatusOr params = JwtHmacParameters::Create( + /*key_size_in_bytes=*/32, JwtHmacParameters::KidStrategy::kCustom, + JwtHmacParameters::Algorithm::kHs256); + ASSERT_THAT(params, IsOk()); + + RestrictedData secret = RestrictedData(/*num_random_bytes=*/32); + + util::StatusOr key = JwtHmacKey::Builder() + .SetParameters(*params) + .SetKeyBytes(secret) + .SetCustomKid("custom_kid") + .Build(GetPartialKeyAccess()); + ASSERT_THAT(key, IsOk()); + + util::StatusOr other_key = JwtHmacKey::Builder() + .SetParameters(*params) + .SetKeyBytes(secret) + .SetCustomKid("other_custom_kid") + .Build(GetPartialKeyAccess()); + ASSERT_THAT(other_key, IsOk()); + + EXPECT_TRUE(*key != *other_key); + EXPECT_TRUE(*other_key != *key); + EXPECT_FALSE(*key == *other_key); + EXPECT_FALSE(*other_key == *key); +} + +} // namespace +} // namespace tink +} // namespace crypto