blob: 14f00dc91622d4e5ca91a33a41e93b34d434aeed [file] [log] [blame]
// Copyright 2021 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 <string>
#include <utility>
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "absl/strings/str_split.h"
#include "openssl/bn.h"
#include "openssl/rsa.h"
#include "tink/internal/bn_util.h"
#include "tink/internal/ssl_unique_ptr.h"
#include "tink/jwt/internal/json_util.h"
#include "tink/jwt/internal/jwt_format.h"
#include "tink/jwt/internal/jwt_rsa_ssa_pkcs1_sign_key_manager.h"
#include "tink/jwt/internal/jwt_rsa_ssa_pkcs1_verify_key_manager.h"
#include "tink/util/status.h"
#include "tink/util/statusor.h"
#include "tink/util/test_matchers.h"
namespace crypto {
namespace tink {
namespace jwt_internal {
using ::crypto::tink::test::IsOk;
using ::crypto::tink::test::IsOkAndHolds;
using ::crypto::tink::util::StatusOr;
using ::google::crypto::tink::JwtRsaSsaPkcs1Algorithm;
using ::google::crypto::tink::JwtRsaSsaPkcs1KeyFormat;
using ::google::crypto::tink::JwtRsaSsaPkcs1PrivateKey;
using ::google::crypto::tink::JwtRsaSsaPkcs1PublicKey;
using ::testing::Eq;
using ::testing::Not;
namespace {
constexpr absl::string_view kTestKid = "kid-123";
JwtRsaSsaPkcs1KeyFormat CreateKeyFormat(JwtRsaSsaPkcs1Algorithm algorithm,
int modulus_size_in_bits,
int public_exponent) {
JwtRsaSsaPkcs1KeyFormat key_format;
key_format.set_algorithm(algorithm);
key_format.set_modulus_size_in_bits(modulus_size_in_bits);
internal::SslUniquePtr<BIGNUM> e(BN_new());
BN_set_word(e.get(), public_exponent);
key_format.set_public_exponent(
internal::BignumToString(e.get(), BN_num_bytes(e.get())).value());
return key_format;
}
TEST(JwtRsaSsaPkcs1SignVerifyKeyManagerTest, BasicsSign) {
EXPECT_EQ(JwtRsaSsaPkcs1SignKeyManager().get_version(), 0);
EXPECT_EQ(JwtRsaSsaPkcs1SignKeyManager().get_key_type(),
"type.googleapis.com/google.crypto.tink.JwtRsaSsaPkcs1PrivateKey");
EXPECT_EQ(JwtRsaSsaPkcs1SignKeyManager().key_material_type(),
google::crypto::tink::KeyData::ASYMMETRIC_PRIVATE);
}
TEST(JwtRsaSsaPkcs1SignVerifyKeyManagerTest, BasicsVerify) {
EXPECT_EQ(JwtRsaSsaPkcs1VerifyKeyManager().get_version(), 0);
EXPECT_EQ(JwtRsaSsaPkcs1VerifyKeyManager().get_key_type(),
"type.googleapis.com/google.crypto.tink.JwtRsaSsaPkcs1PublicKey");
EXPECT_EQ(JwtRsaSsaPkcs1VerifyKeyManager().key_material_type(),
google::crypto::tink::KeyData::ASYMMETRIC_PUBLIC);
}
TEST(JwtRsaSsaPkcs1SignVerifyKeyManagerTest, ValidateEmptyPrivateKey) {
EXPECT_THAT(
JwtRsaSsaPkcs1SignKeyManager().ValidateKey(JwtRsaSsaPkcs1PrivateKey()),
Not(IsOk()));
}
TEST(JwtRsaSsaPkcs1SignVerifyKeyManagerTest, ValidateEmptyPublicKey) {
EXPECT_THAT(
JwtRsaSsaPkcs1VerifyKeyManager().ValidateKey(JwtRsaSsaPkcs1PublicKey()),
Not(IsOk()));
}
TEST(JwtRsaSsaPkcs1SignVerifyKeyManagerTest, ValidateEmptyKeyFormat) {
EXPECT_THAT(JwtRsaSsaPkcs1SignKeyManager().ValidateKeyFormat(
JwtRsaSsaPkcs1KeyFormat()),
Not(IsOk()));
}
TEST(JwtRsaSsaPkcs1SignVerifyKeyManagerTest, ValidKeyFormatRS256) {
JwtRsaSsaPkcs1KeyFormat key_format =
CreateKeyFormat(JwtRsaSsaPkcs1Algorithm::RS256, 2048, RSA_F4);
EXPECT_THAT(JwtRsaSsaPkcs1SignKeyManager().ValidateKeyFormat(key_format),
IsOk());
}
TEST(JwtRsaSsaPkcs1SignVerifyKeyManagerTest, ValidateKeyFormatRS384) {
JwtRsaSsaPkcs1KeyFormat key_format =
CreateKeyFormat(JwtRsaSsaPkcs1Algorithm::RS384, 3072, RSA_F4);
EXPECT_THAT(JwtRsaSsaPkcs1SignKeyManager().ValidateKeyFormat(key_format),
IsOk());
}
TEST(JwtRsaSsaPkcs1SignVerifyKeyManagerTest, ValidateKeyFormatRS512) {
JwtRsaSsaPkcs1KeyFormat key_format =
CreateKeyFormat(JwtRsaSsaPkcs1Algorithm::RS512, 4096, RSA_F4);
EXPECT_THAT(JwtRsaSsaPkcs1SignKeyManager().ValidateKeyFormat(key_format),
IsOk());
}
TEST(JwtRsaSsaPkcs1SignVerifyKeyManagerTest, CreatePrivateKeyAndValidate) {
JwtRsaSsaPkcs1KeyFormat key_format =
CreateKeyFormat(JwtRsaSsaPkcs1Algorithm::RS256, 2048, RSA_F4);
util::StatusOr<google::crypto::tink::JwtRsaSsaPkcs1PrivateKey> key =
JwtRsaSsaPkcs1SignKeyManager().CreateKey(key_format);
ASSERT_THAT(key, IsOk());
EXPECT_EQ(key->version(), 0);
EXPECT_EQ(key->public_key().algorithm(), key_format.algorithm());
EXPECT_THAT(JwtRsaSsaPkcs1SignKeyManager().ValidateKey(*key), IsOk());
// Change key to an invalid algorithm.
key->mutable_public_key()->set_algorithm(JwtRsaSsaPkcs1Algorithm::RS_UNKNOWN);
EXPECT_FALSE(JwtRsaSsaPkcs1SignKeyManager().ValidateKey(*key).ok());
}
TEST(JwtRsaSsaPkcs1SignVerifyKeyManagerTest, CreatePublicKeyAndValidate) {
JwtRsaSsaPkcs1KeyFormat key_format =
CreateKeyFormat(JwtRsaSsaPkcs1Algorithm::RS256, 2048, RSA_F4);
util::StatusOr<google::crypto::tink::JwtRsaSsaPkcs1PrivateKey> key =
JwtRsaSsaPkcs1SignKeyManager().CreateKey(key_format);
ASSERT_THAT(key, IsOk());
util::StatusOr<google::crypto::tink::JwtRsaSsaPkcs1PublicKey> public_key =
JwtRsaSsaPkcs1SignKeyManager().GetPublicKey(*key);
EXPECT_THAT(JwtRsaSsaPkcs1VerifyKeyManager().ValidateKey(*public_key),
IsOk());
// Change key to an invalid algorithm.
public_key->set_algorithm(JwtRsaSsaPkcs1Algorithm::RS_UNKNOWN);
EXPECT_FALSE(JwtRsaSsaPkcs1VerifyKeyManager().ValidateKey(*public_key).ok());
}
TEST(JwtRsaSsaPkcs1SignVerifyKeyManagerTest, GetAndUsePrimitives) {
JwtRsaSsaPkcs1KeyFormat key_format =
CreateKeyFormat(JwtRsaSsaPkcs1Algorithm::RS256, 2048, RSA_F4);
util::StatusOr<google::crypto::tink::JwtRsaSsaPkcs1PrivateKey> key =
JwtRsaSsaPkcs1SignKeyManager().CreateKey(key_format);
ASSERT_THAT(key, IsOk());
util::StatusOr<std::unique_ptr<JwtPublicKeySignInternal>> sign =
JwtRsaSsaPkcs1SignKeyManager().GetPrimitive<JwtPublicKeySignInternal>(
*key);
ASSERT_THAT(sign, IsOk());
util::StatusOr<RawJwt> raw_jwt =
RawJwtBuilder().SetIssuer("issuer").WithoutExpiration().Build();
ASSERT_THAT(raw_jwt, IsOk());
util::StatusOr<std::string> compact =
(*sign)->SignAndEncodeWithKid(*raw_jwt, /*kid=*/absl::nullopt);
ASSERT_THAT(compact, IsOk());
util::StatusOr<JwtValidator> validator = JwtValidatorBuilder()
.ExpectIssuer("issuer")
.AllowMissingExpiration()
.Build();
ASSERT_THAT(validator, IsOk());
util::StatusOr<std::unique_ptr<JwtPublicKeyVerifyInternal>> verify =
JwtRsaSsaPkcs1VerifyKeyManager().GetPrimitive<JwtPublicKeyVerifyInternal>(
key->public_key());
ASSERT_THAT(verify, IsOk());
util::StatusOr<VerifiedJwt> verified_jwt = (*verify)->VerifyAndDecodeWithKid(
*compact, *validator, /*kid=*/absl::nullopt);
ASSERT_THAT(verified_jwt, IsOk());
util::StatusOr<std::string> issuer = verified_jwt->GetIssuer();
EXPECT_THAT(issuer, IsOkAndHolds("issuer"));
EXPECT_THAT((*verify)
->VerifyAndDecodeWithKid(*compact, *validator, kTestKid)
.status(),
Not(IsOk()));
util::StatusOr<JwtValidator> validator2 =
JwtValidatorBuilder().ExpectIssuer("unknown").Build();
ASSERT_THAT(validator2, IsOk());
EXPECT_FALSE(
(*verify)
->VerifyAndDecodeWithKid(*compact, *validator2, /*kid=*/absl::nullopt)
.ok());
// Token with kid header
util::StatusOr<std::string> token_with_kid =
(*sign)->SignAndEncodeWithKid(*raw_jwt, kTestKid);
ASSERT_THAT(compact, IsOk());
EXPECT_THAT((*verify)
->VerifyAndDecodeWithKid(*token_with_kid, *validator,
/*kid=*/absl::nullopt)
.status(),
IsOk());
EXPECT_THAT(
(*verify)
->VerifyAndDecodeWithKid(*token_with_kid, *validator, kTestKid)
.status(),
IsOk());
EXPECT_THAT(
(*verify)
->VerifyAndDecodeWithKid(*token_with_kid, *validator, "other-kid")
.status(),
Not(IsOk()));
}
TEST(JwtRsaSsaPkcs1SignVerifyKeyManagerTest, GetAndUsePrimitivesWithCustomKid) {
JwtRsaSsaPkcs1KeyFormat key_format =
CreateKeyFormat(JwtRsaSsaPkcs1Algorithm::RS256, 2048, RSA_F4);
util::StatusOr<JwtRsaSsaPkcs1PrivateKey> key =
JwtRsaSsaPkcs1SignKeyManager().CreateKey(key_format);
ASSERT_THAT(key, IsOk());
key->mutable_public_key()->mutable_custom_kid()->set_value(
"Lorem ipsum dolor sit amet, consectetur adipiscing elit");
util::StatusOr<std::unique_ptr<JwtPublicKeySignInternal>> sign =
JwtRsaSsaPkcs1SignKeyManager().GetPrimitive<JwtPublicKeySignInternal>(
*key);
ASSERT_THAT(sign, IsOk());
util::StatusOr<RawJwt> raw_jwt =
RawJwtBuilder().SetIssuer("issuer").WithoutExpiration().Build();
ASSERT_THAT(raw_jwt, IsOk());
util::StatusOr<std::string> compact =
(*sign)->SignAndEncodeWithKid(*raw_jwt, /*kid=*/absl::nullopt);
ASSERT_THAT(compact, IsOk());
// parse header and check "kid"
std::vector<absl::string_view> parts = absl::StrSplit(*compact, '.');
ASSERT_THAT(parts.size(), Eq(3));
std::string json_header;
ASSERT_TRUE(DecodeHeader(parts[0], &json_header));
util::StatusOr<google::protobuf::Struct> header =
JsonStringToProtoStruct(json_header);
ASSERT_THAT(header, IsOk());
auto it = header->fields().find("kid");
ASSERT_FALSE(it == header->fields().end());
EXPECT_THAT(it->second.string_value(),
Eq("Lorem ipsum dolor sit amet, consectetur adipiscing elit"));
// validate token
util::StatusOr<JwtValidator> validator = JwtValidatorBuilder()
.ExpectIssuer("issuer")
.AllowMissingExpiration()
.Build();
ASSERT_THAT(validator, IsOk());
util::StatusOr<std::unique_ptr<JwtPublicKeyVerifyInternal>> verify =
JwtRsaSsaPkcs1VerifyKeyManager().GetPrimitive<JwtPublicKeyVerifyInternal>(
key->public_key());
ASSERT_THAT(verify, IsOk());
util::StatusOr<VerifiedJwt> verified_jwt = (*verify)->VerifyAndDecodeWithKid(
*compact, *validator, /*kid=*/absl::nullopt);
ASSERT_THAT(verified_jwt, IsOk());
util::StatusOr<std::string> issuer = verified_jwt->GetIssuer();
ASSERT_THAT(issuer, IsOk());
EXPECT_THAT(*issuer, Eq("issuer"));
// passing a kid when custom_kid is set should fail
EXPECT_THAT((*sign)->SignAndEncodeWithKid(*raw_jwt, kTestKid).status(),
Not(IsOk()));
EXPECT_THAT((*verify)
->VerifyAndDecodeWithKid(*compact, *validator, kTestKid)
.status(),
Not(IsOk()));
// Test that custom kid is verified: validation should fail with other kid.
key->mutable_public_key()->mutable_custom_kid()->set_value("other kid");
ASSERT_THAT(validator, IsOk());
util::StatusOr<std::unique_ptr<JwtPublicKeyVerifyInternal>> other_verify =
JwtRsaSsaPkcs1VerifyKeyManager().GetPrimitive<JwtPublicKeyVerifyInternal>(
key->public_key());
ASSERT_THAT(other_verify, IsOk());
EXPECT_THAT(
(*other_verify)
->VerifyAndDecodeWithKid(*compact, *validator, /*kid=*/absl::nullopt)
.status(),
Not(IsOk()));
}
TEST(JwtRsaSsaPkcs1SignVerifyKeyManagerTest, VerifyFailsWithDifferentKey) {
JwtRsaSsaPkcs1KeyFormat key_format =
CreateKeyFormat(JwtRsaSsaPkcs1Algorithm::RS256, 2048, RSA_F4);
util::StatusOr<google::crypto::tink::JwtRsaSsaPkcs1PrivateKey> key1 =
JwtRsaSsaPkcs1SignKeyManager().CreateKey(key_format);
ASSERT_THAT(key1, IsOk());
util::StatusOr<google::crypto::tink::JwtRsaSsaPkcs1PrivateKey> key2 =
JwtRsaSsaPkcs1SignKeyManager().CreateKey(key_format);
ASSERT_THAT(key2, IsOk());
util::StatusOr<std::unique_ptr<JwtPublicKeySignInternal>> sign1 =
JwtRsaSsaPkcs1SignKeyManager().GetPrimitive<JwtPublicKeySignInternal>(
*key1);
ASSERT_THAT(sign1, IsOk());
util::StatusOr<RawJwt> raw_jwt =
RawJwtBuilder().SetIssuer("issuer").WithoutExpiration().Build();
ASSERT_THAT(raw_jwt, IsOk());
util::StatusOr<std::string> compact =
(*sign1)->SignAndEncodeWithKid(*raw_jwt, /*kid=*/absl::nullopt);
ASSERT_THAT(compact, IsOk());
util::StatusOr<JwtValidator> validator =
JwtValidatorBuilder().AllowMissingExpiration().Build();
ASSERT_THAT(validator, IsOk());
util::StatusOr<std::unique_ptr<JwtPublicKeyVerifyInternal>> verify2 =
JwtRsaSsaPkcs1VerifyKeyManager().GetPrimitive<JwtPublicKeyVerifyInternal>(
key2->public_key());
EXPECT_THAT(verify2, IsOk());
EXPECT_THAT(
(*verify2)
->VerifyAndDecodeWithKid(*compact, *validator, /*kid=*/absl::nullopt)
.status(),
Not(IsOk()));
}
} // namespace
} // namespace jwt_internal
} // namespace tink
} // namespace crypto