// Copyright 2022 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.
//
////////////////////////////////////////////////////////////////////////////////

package jwt

import (
	"fmt"
	"testing"
	"time"

	"google.golang.org/protobuf/proto"
	"github.com/tink-crypto/tink-go/v2/core/registry"
	"github.com/tink-crypto/tink-go/v2/subtle/random"
	jrsppb "github.com/tink-crypto/tink-go/v2/proto/jwt_rsa_ssa_pkcs1_go_proto"
)

const testJWTRSVerifierKeyType = "type.googleapis.com/google.crypto.tink.JwtRsaSsaPkcs1PublicKey"

func makeValidRSPublicKey() (*jrsppb.JwtRsaSsaPkcs1PublicKey, error) {
	// Public key taken from: https://datatracker.ietf.org/doc/html/rfc7515#appendix-A.2
	n, err := base64Decode(
		"ofgWCuLjybRlzo0tZWJjNiuSfb4p4fAkd_wWJcyQoTbji9k0l8W26mPddx" +
			"HmfHQp-Vaw-4qPCJrcS2mJPMEzP1Pt0Bm4d4QlL-yRT-SFd2lZS-pCgNMs" +
			"D1W_YpRPEwOWvG6b32690r2jZ47soMZo9wGzjb_7OMg0LOL-bSf63kpaSH" +
			"SXndS5z5rexMdbBYUsLA9e-KXBdQOS-UTo7WTBEMa2R2CapHg665xsmtdV" +
			"MTBQY4uDZlxvb3qCo5ZwKh9kG4LT6_I5IhlJH7aGhyxXFvUK-DWNmoudF8" +
			"NAco9_h9iaGNj8q2ethFkMLs91kzk2PAcDTW9gb54h4FRWyuXpoQ")
	if err != nil {
		return nil, fmt.Errorf("base64 decoding modulus: %v", err)
	}
	e, err := base64Decode("AQAB")
	if err != nil {
		return nil, fmt.Errorf("base64 decoding public exponent: %v", err)
	}
	return &jrsppb.JwtRsaSsaPkcs1PublicKey{
		Algorithm: jrsppb.JwtRsaSsaPkcs1Algorithm_RS256,
		Version:   0,
		N:         n,
		E:         e,
		CustomKid: nil,
	}, nil
}

func TestJWTRSVerifierNotImplemented(t *testing.T) {
	km, err := registry.GetKeyManager(testJWTRSVerifierKeyType)
	if err != nil {
		t.Fatalf("registry.GetKeyManager(%q) err = %v, want nil", testJWTRSVerifierKeyType, err)
	}
	keyFormat := &jrsppb.JwtRsaSsaPkcs1KeyFormat{
		Version:           0,
		Algorithm:         jrsppb.JwtRsaSsaPkcs1Algorithm_RS256,
		ModulusSizeInBits: 3072,
		PublicExponent:    []byte{0x01, 0x00, 0x01}, // 65537 aka F4
	}
	serializedKeyFormat, err := proto.Marshal(keyFormat)
	if err != nil {
		t.Fatalf("proto.Marshal() err = %v, want nil", err)
	}
	if _, err := km.NewKey(serializedKeyFormat); err == nil {
		t.Fatalf("km.NewKey() err = nil, want error")
	}
	if _, err := km.NewKeyData(serializedKeyFormat); err == nil {
		t.Fatalf("km.NewKeyData() err = nil, want error")
	}
}

func TestJWTRSVerifierDoesSupport(t *testing.T) {
	km, err := registry.GetKeyManager(testJWTRSVerifierKeyType)
	if err != nil {
		t.Fatalf("registry.GetKeyManager(%q) err = %v, want nil", testJWTRSVerifierKeyType, err)
	}
	if !km.DoesSupport(testJWTRSVerifierKeyType) {
		t.Errorf("DoesSupport(%q) = false, want true", testJWTRSVerifierKeyType)
	}
	if km.DoesSupport("not.the.actual.key.type") {
		t.Errorf("km.DoesSupport('not.the.actual.key.type') = true, want false")
	}
}

func TestJWTRSVerifierTypeURL(t *testing.T) {
	km, err := registry.GetKeyManager(testJWTRSVerifierKeyType)
	if err != nil {
		t.Fatalf("registry.GetKeyManager(%q) err = %v, want nil", testJWTRSVerifierKeyType, err)
	}
	if km.TypeURL() != testJWTRSVerifierKeyType {
		t.Errorf("km.TypeURL() = %q, want %q", km.TypeURL(), testJWTRSVerifierKeyType)
	}
}

func TestJWTRSVerifierPrimitiveWithInvalidKey(t *testing.T) {
	type testCase struct {
		name   string
		pubKey *jrsppb.JwtRsaSsaPkcs1PublicKey
	}
	km, err := registry.GetKeyManager(testJWTRSVerifierKeyType)
	if err != nil {
		t.Fatalf("registry.GetKeyManager(%q) err = %v, want nil", testJWTRSVerifierKeyType, err)
	}
	validPubKey, err := makeValidRSPublicKey()
	if err != nil {
		t.Fatalf("makeValidRSAPSSPKCS1PrivKey() err = %v, want nil", err)
	}
	for _, tc := range []testCase{
		{
			name:   "nil public key",
			pubKey: nil,
		},
		{
			name:   "empty public key",
			pubKey: &jrsppb.JwtRsaSsaPkcs1PublicKey{},
		},
		{
			name: "invalid version",
			pubKey: &jrsppb.JwtRsaSsaPkcs1PublicKey{
				Version:   validPubKey.Version + 1,
				Algorithm: validPubKey.GetAlgorithm(),
				N:         validPubKey.GetN(),
				E:         validPubKey.GetE(),
				CustomKid: validPubKey.GetCustomKid(),
			},
		},
		{
			name: "invalid algorithm",
			pubKey: &jrsppb.JwtRsaSsaPkcs1PublicKey{
				Algorithm: jrsppb.JwtRsaSsaPkcs1Algorithm_RS_UNKNOWN,
				Version:   validPubKey.Version,
				N:         validPubKey.GetN(),
				E:         validPubKey.GetE(),
				CustomKid: validPubKey.GetCustomKid(),
			},
		},
		{
			name: "invalid modulus",
			pubKey: &jrsppb.JwtRsaSsaPkcs1PublicKey{
				Version:   validPubKey.Version,
				Algorithm: validPubKey.GetAlgorithm(),
				N:         []byte{0x00},
				E:         validPubKey.GetE(),
				CustomKid: validPubKey.GetCustomKid(),
			},
		},
		{
			name: "invalid exponent",
			pubKey: &jrsppb.JwtRsaSsaPkcs1PublicKey{
				Version:   validPubKey.Version,
				Algorithm: validPubKey.GetAlgorithm(),
				N:         validPubKey.GetN(),
				E:         []byte{0x05, 0x04, 0x03},
				CustomKid: validPubKey.GetCustomKid(),
			},
		},
		{
			name: "exponent larger than 64 bits",
			pubKey: &jrsppb.JwtRsaSsaPkcs1PublicKey{
				Version:   validPubKey.Version,
				Algorithm: validPubKey.GetAlgorithm(),
				N:         validPubKey.GetN(),
				E:         random.GetRandomBytes(65),
				CustomKid: validPubKey.GetCustomKid(),
			},
		},
	} {
		t.Run(tc.name, func(t *testing.T) {
			serializedPubKey, err := proto.Marshal(tc.pubKey)
			if err != nil {
				t.Fatalf("proto.Marshal() err = %v, want nil", err)
			}
			if _, err := km.Primitive(serializedPubKey); err == nil {
				t.Errorf("Primitive() err = nil, want error")
			}
		})
	}
}

func TestJWTRSVerifierPrimitiveWithInvalidSerializedKey(t *testing.T) {
	km, err := registry.GetKeyManager(testJWTRSVerifierKeyType)
	if err != nil {
		t.Fatalf("registry.GetKeyManager(%q) err = %v, want nil", testJWTRSVerifierKeyType, err)
	}
	if _, err := km.Primitive([]byte("invalid_serialization")); err == nil {
		t.Errorf("Primitive() err = nil, want error")
	}
}

func TestJWTRSVerifierPrimitiveVerifyFixedToken(t *testing.T) {
	km, err := registry.GetKeyManager(testJWTRSVerifierKeyType)
	if err != nil {
		t.Fatalf("registry.GetKeyManager(%q) err = %v, want nil", testJWTRSVerifierKeyType, err)
	}
	pubKey, err := makeValidRSPublicKey()
	if err != nil {
		t.Fatalf("makeValidRSPublicKey() err = %v, want nil", err)
	}
	serializedPubKey, err := proto.Marshal(pubKey)
	if err != nil {
		t.Fatalf("proto.Marshal() err = %v, want nil", err)
	}
	v, err := km.Primitive(serializedPubKey)
	if err != nil {
		t.Fatalf("km.Primitive() err = %v, want nil", err)
	}
	verifier, ok := v.(*verifierWithKID)
	if !ok {
		t.Fatalf("primitive isn't a JWT Verifier")
	}
	// compact from https://datatracker.ietf.org/doc/html/rfc7515#appendix-A.2
	compact := "eyJhbGciOiJSUzI1NiJ9" +
		"." +
		"eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFt" +
		"cGxlLmNvbS9pc19yb290Ijp0cnVlfQ" +
		"." +
		"cC4hiUPoj9Eetdgtv3hF80EGrhuB__dzERat0XF9g2VtQgr9PJbu3XOiZj5RZmh7" +
		"AAuHIm4Bh-0Qc_lF5YKt_O8W2Fp5jujGbds9uJdbF9CUAr7t1dnZcAcQjbKBYNX4" +
		"BAynRFdiuB--f_nZLgrnbyTyWzO75vRK5h6xBArLIARNPvkSjtQBMHlb1L07Qe7K" +
		"0GarZRmB_eSN9383LcOLn6_dO--xi12jzDwusC-eOkHWEsqtFZESc6BfI7noOPqv" +
		"hJ1phCnvWh6IeYI2w9QOYEUipUTI8np6LbgGY9Fs98rqVt5AXLIhWkWywlVmtVrB" +
		"p0igcN_IoypGlUPQGe77Rw"
	issuer := "joe"
	validator, err := NewValidator(&ValidatorOpts{
		ExpectedIssuer: &issuer,
		FixedNow:       time.Unix(123, 0),
	})
	if err != nil {
		t.Fatalf("NewValidator() err = %v, want nil", err)
	}
	// verification succeeds because token was valid on January 1, 1970 UTC.
	if _, err := verifier.VerifyAndDecodeWithKID(compact, validator, nil); err != nil {
		t.Errorf("verifier.VerifyAndDecodeWithKID(kid = nil) err = %v, want nil", err)
	}
	// verification with KID fails because token contains no KID.
	if _, err := verifier.VerifyAndDecodeWithKID(compact, validator, refString("1234")); err == nil {
		t.Errorf("verifier.VerifyAndDecodeWithKID(kid = '1234') err = nil, want error")
	}
}

func TestJWTRSVerifierPrimitiveWithCustomKID(t *testing.T) {
	km, err := registry.GetKeyManager(testJWTRSVerifierKeyType)
	if err != nil {
		t.Fatalf("registry.GetKeyManager(%q) err = %v, want nil", testJWTRSVerifierKeyType, err)
	}
	pubKey, err := makeValidRSPublicKey()
	if err != nil {
		t.Fatalf("makeValidRSPublicKey() err = %v, want nil", err)
	}
	pubKey.CustomKid = &jrsppb.JwtRsaSsaPkcs1PublicKey_CustomKid{
		Value: "8542",
	}
	serializedPubKey, err := proto.Marshal(pubKey)
	if err != nil {
		t.Fatalf("proto.Marshal() err = %v, want nil", err)
	}
	v, err := km.Primitive(serializedPubKey)
	if err != nil {
		t.Fatalf("km.Primitive() err = %v, want nil", err)
	}
	// similar to https://datatracker.ietf.org/doc/html/rfc7515#appendix-A.2 but with KID 8542
	compact := "eyJhbGciOiJSUzI1NiIsImtpZCI6Ijg1NDIifQ" +
		"." +
		"eyJleHAiOjEzMDA4MTkzODAsImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290I" +
		"jp0cnVlLCJpc3MiOiJqb2UifQ" +
		"." +
		"aoQ4f8U_gpIymZM20rbAG2kjw5H5EKruPqPWf_wsEDeKPSjCXzkW016s5UqTz" +
		"dJ72ZEP05PPZHs4VtZslUXQajLlZNgbK3UJ86QYBrqENq0Pwnhh43TVPi9lrF" +
		"xOLjSQHAqKXYCy4aflqRdZqP9QqpLqaKtB1mAcDNM25Qx01Ix9FV_ngqI5OLD" +
		"OYyDp5HoxgMAV-jNR9yq-r31_EBQmmDFHC8K8NJ5XLa4SybbhNlUWi6b1p7sQ" +
		"NIOcb6RtSGSL73m-FYOo_dOMZ1ZNd7a_JiJe7QZ3-v1Dnw9GBSxvLdtKye2Fu" +
		"ZHietYMJJczj14KeDbBK6TwmbUM8AacLt-JGg"
	verifier, ok := v.(*verifierWithKID)
	if !ok {
		t.Fatalf("primitive isn't a JWT Verifier")
	}
	issuer := "joe"
	validator, err := NewValidator(&ValidatorOpts{
		ExpectedIssuer: &issuer,
		FixedNow:       time.Unix(123, 0),
	})
	if err != nil {
		t.Fatalf("NewValidator() err = %v, want nil", err)
	}
	// verification succeeds because token was valid on January 1, 1970 UTC.
	if _, err := verifier.VerifyAndDecodeWithKID(compact, validator, nil); err != nil {
		t.Errorf("verifier.VerifyAndDecodeWithKID(kid = nil) err = %v, want nil", err)
	}
	// verification with custom KID and Tink KID fails, there can only be one KID set.
	if _, err := verifier.VerifyAndDecodeWithKID(compact, validator, refString("8542")); err == nil {
		t.Errorf("verifier.VerifyAndDecodeWithKID(kid = '8542') err = nil, want error")
	}
	pubKey.CustomKid = &jrsppb.JwtRsaSsaPkcs1PublicKey_CustomKid{
		Value: "1234",
	}
	serializedPubKey, err = proto.Marshal(pubKey)
	if err != nil {
		t.Fatalf("proto.Marshal() err = %v, want nil", err)
	}
	v, err = km.Primitive(serializedPubKey)
	if err != nil {
		t.Fatalf("km.Primitive() err = %v, want nil", err)
	}
	verifierWrongKID, ok := v.(*verifierWithKID)
	if !ok {
		t.Fatalf("primitive isn't a JWT Verifier")
	}
	// primitive contains invalid Custom KID which fails verification.
	if _, err := verifierWrongKID.VerifyAndDecodeWithKID(compact, validator, nil); err == nil {
		t.Errorf("verifier.VerifyAndDecodeWithKID(kid = nil) err = nil, want error")
	}
}

func TestJWTRSVerifierPrimitiveWithTinkKID(t *testing.T) {
	km, err := registry.GetKeyManager(testJWTRSVerifierKeyType)
	if err != nil {
		t.Fatalf("registry.GetKeyManager(%q) err = %v, want nil", testJWTRSVerifierKeyType, err)
	}
	pubKey, err := makeValidRSPublicKey()
	if err != nil {
		t.Fatalf("makeValidRSPublicKey() err = %v, want nil", err)
	}
	pubKey.CustomKid = nil
	serializedPubKey, err := proto.Marshal(pubKey)
	if err != nil {
		t.Fatalf("proto.Marshal() err = %v, want nil", err)
	}
	v, err := km.Primitive(serializedPubKey)
	if err != nil {
		t.Fatalf("km.Primitive() err = %v, want nil", err)
	}
	verifier, ok := v.(*verifierWithKID)
	if !ok {
		t.Fatalf("primitive isn't a JWT Verifier")
	}
	// similar to https://datatracker.ietf.org/doc/html/rfc7515#appendix-A.2 but with KID 8542
	compact := "eyJhbGciOiJSUzI1NiIsImtpZCI6Ijg1NDIifQ" +
		"." +
		"eyJleHAiOjEzMDA4MTkzODAsImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290I" +
		"jp0cnVlLCJpc3MiOiJqb2UifQ" +
		"." +
		"aoQ4f8U_gpIymZM20rbAG2kjw5H5EKruPqPWf_wsEDeKPSjCXzkW016s5UqTz" +
		"dJ72ZEP05PPZHs4VtZslUXQajLlZNgbK3UJ86QYBrqENq0Pwnhh43TVPi9lrF" +
		"xOLjSQHAqKXYCy4aflqRdZqP9QqpLqaKtB1mAcDNM25Qx01Ix9FV_ngqI5OLD" +
		"OYyDp5HoxgMAV-jNR9yq-r31_EBQmmDFHC8K8NJ5XLa4SybbhNlUWi6b1p7sQ" +
		"NIOcb6RtSGSL73m-FYOo_dOMZ1ZNd7a_JiJe7QZ3-v1Dnw9GBSxvLdtKye2Fu" +
		"ZHietYMJJczj14KeDbBK6TwmbUM8AacLt-JGg"
	issuer := "joe"
	validator, err := NewValidator(&ValidatorOpts{
		ExpectedIssuer: &issuer,
		FixedNow:       time.Unix(123, 0),
	})
	if err != nil {
		t.Fatalf("NewValidator() err = %v, want nil", err)
	}
	if _, err := verifier.VerifyAndDecodeWithKID(compact, validator, refString("8542")); err != nil {
		t.Errorf("verifier.VerifyAndDecodeWithKID(kid = '8542') err = %v, want nil", err)
	}
	// verification fails with invalid KID
	if _, err := verifier.VerifyAndDecodeWithKID(compact, validator, refString("2333")); err == nil {
		t.Errorf("verifier.VerifyAndDecodeWithKID(kid = '2333') err = nil, want error")
	}
}
