// 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" "github.com/google/tink/go/signature/subtle" "github.com/google/tink/go/tink" ) func createTinkECVerifier() (tink.Verifier, error) { // Public key from: https://datatracker.ietf.org/doc/html/rfc7515#appendix-A.3 x, err := base64Decode("f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU") if err != nil { return nil, fmt.Errorf("base64 decoding x coordinate of public key: %v", err) } y, err := base64Decode("x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0") if err != nil { return nil, fmt.Errorf("base64 decoding y coordinate of public key: %v", err) } tv, err := subtle.NewECDSAVerifier("SHA256", "NIST_P256", "IEEE_P1363", x, y) if err != nil { return nil, fmt.Errorf("subtle.NewECDSAVerifier() err = %v, want nil", err) } return tv, nil } func createTinkECSigner() (tink.Signer, error) { // Private key from: https://datatracker.ietf.org/doc/html/rfc7515#appendix-A.3 k, err := base64Decode("jpsQnnGQmL-YBIffH1136cspYG6-0iY7X1fCE9-E9LI") if err != nil { return nil, fmt.Errorf("base64 decoding private key: %v", err) } ts, err := subtle.NewECDSASigner("SHA256", "NIST_P256", "IEEE_P1363", k) if err != nil { return nil, fmt.Errorf("subtle.NewECDSASigner() err = %v, want nil", err) } return ts, nil } func createESVerifier(kid *string) (*verifierWithKID, error) { tv, err := createTinkECVerifier() if err != nil { return nil, err } v, err := newVerifierWithKID(tv, "ES256", kid) if err != nil { return nil, fmt.Errorf("newVerifierWithKID(algorithm = 'ES256') err = %v, want nil", err) } return v, nil } func createESSigner(kid *string) (*signerWithKID, error) { ts, err := createTinkECSigner() if err != nil { return nil, err } s, err := newSignerWithKID(ts, "ES256", kid) if err != nil { return nil, fmt.Errorf("newSignerWithKID(algorithm = 'ES256') err = %v, want nil", err) } return s, nil } func TestVerifierWithFixedToken(t *testing.T) { // compact from: https://datatracker.ietf.org/doc/html/rfc7515#appendix-A.3 compact := "eyJhbGciOiJFUzI1NiJ9.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.DtEhU3ljbEg8L38VWAfUAqOyKAM6-Xx-F4GawxaepmXFCgfTjDxw5djxLa8ISlSApmWQxfKTUJqPP3-Kg6NU1Q" v, err := createESVerifier(nil) if err != nil { t.Fatal(err) } validator, err := NewValidator(&ValidatorOpts{ExpectedIssuer: refString("joe"), FixedNow: time.Unix(1300819300, 0)}) if err != nil { t.Fatalf("NewValidator() err = %v, want nil", err) } verified, err := v.VerifyAndDecodeWithKID(compact, validator, nil) if err != nil { t.Errorf("VerifyAndDecodeWithKID() err = %v, want nil", err) } issuer, err := verified.Issuer() if err != nil { t.Errorf("verified.Issuer() err = %v, want nil", err) } if issuer != "joe" { t.Errorf("verified.Issuer() = %q, want joe", issuer) } expiration, err := verified.ExpiresAt() if err != nil { t.Errorf("verified.ExpiresAt() err = %v, want nil", err) } wantExp := time.Unix(1300819380, 0) if !expiration.Equal(wantExp) { t.Errorf("verified.ExpiresAt() = %q, want %q", expiration, wantExp) } boolClaim, err := verified.BooleanClaim("http://example.com/is_root") if err != nil { t.Errorf("verified.BooleanClaim('http://example.com/is_root') err = %v, want nil", err) } if boolClaim != true { t.Errorf("verified.BooleanClaim('http://example.com/is_root') = %v, want false", boolClaim) } } func TestCreateSignValidateToken(t *testing.T) { rawJWT, err := NewRawJWT(&RawJWTOptions{TypeHeader: refString("JWT"), WithoutExpiration: true}) if err != nil { t.Fatalf("NewRawJWT() err = %v, want nil", err) } validator, err := NewValidator(&ValidatorOpts{ExpectedTypeHeader: refString("JWT"), AllowMissingExpiration: true}) if err != nil { t.Fatalf("NewValidator() err = %v, want nil", err) } s, err := createESSigner(nil) if err != nil { t.Fatal(err) } v, err := createESVerifier(nil) if err != nil { t.Fatal(err) } compact, err := s.SignAndEncodeWithKID(rawJWT, nil) if err != nil { t.Fatalf("s.SignAndEncodeWithKID() err = %v, want nil", err) } verified, err := v.VerifyAndDecodeWithKID(compact, validator, nil) if err != nil { t.Fatalf("v.VerifyAndDecodeWithKID() err = %v, want nil", err) } typeHeader, err := verified.TypeHeader() if err != nil { t.Errorf("verified.TypeHeader() err = %v, want nil", err) } if typeHeader != "JWT" { t.Errorf("verified.TypeHeader() = %q, want 'JWT'", typeHeader) } } func TestSignerWithTinkAndCustomKIDFails(t *testing.T) { rawJWT, err := NewRawJWT(&RawJWTOptions{TypeHeader: refString("JWT"), WithoutExpiration: true}) if err != nil { t.Fatalf("NewRawJWT() err = %v, want nil", err) } s, err := createESSigner(refString("1234")) if err != nil { t.Fatal(err) } if _, err := s.SignAndEncodeWithKID(rawJWT, refString("123")); err == nil { t.Errorf("s.SignAndEncodeWithKID(kid = 123) err = nil, want error") } } type signerVerifierKIDTestCase struct { tag string signerCustomKID *string verifierCustomKID *string // calculated from the Tink Key ID. signerKID *string // calculated from the Tink Key ID. verifierKID *string } func TestSignVerifyWithKID(t *testing.T) { rawJWT, err := NewRawJWT(&RawJWTOptions{TypeHeader: refString("JWT"), WithoutExpiration: true}) if err != nil { t.Fatalf("NewRawJWT() err = %v, want nil", err) } validator, err := NewValidator(&ValidatorOpts{ExpectedTypeHeader: refString("JWT"), AllowMissingExpiration: true}) if err != nil { t.Fatalf("NewValidator() err = %v, want nil", err) } for _, tc := range []signerVerifierKIDTestCase{ { tag: "verifier with custom kid matches kid in header generated with custom kid", signerCustomKID: refString("1234"), verifierCustomKID: refString("1234"), }, { tag: "verifier with tink kid matches kid in header generated with custom kid", signerCustomKID: refString("1234"), verifierKID: refString("1234"), }, { tag: "verifier with tink kid matches kid in header generated with tink kid", signerKID: refString("1234"), verifierKID: refString("1234"), }, { tag: "no kid in verifier ignores kid when present in header", signerKID: refString("1234"), }, { tag: "verifier with custom kid ignores when no kid present in header", verifierCustomKID: refString("1234"), }, } { t.Run(tc.tag, func(t *testing.T) { s, err := createESSigner(tc.signerCustomKID) if err != nil { t.Fatal(err) } v, err := createESVerifier(tc.verifierCustomKID) if err != nil { t.Fatal(err) } compact, err := s.SignAndEncodeWithKID(rawJWT, tc.signerKID) if err != nil { t.Fatalf("s.SignAndEncodeWithKID(kid = %v) err = %v, want nil", tc.signerKID, err) } verified, err := v.VerifyAndDecodeWithKID(compact, validator, tc.verifierKID) if err != nil { t.Fatalf("s.VerifyAndDecodeWithKID(kid = %v) err = %v, want nil", tc.verifierKID, err) } typeHeader, err := verified.TypeHeader() if err != nil { t.Errorf("verified.TypeHeader() err = %v, want nil", err) } if typeHeader != "JWT" { t.Errorf("verified.TypeHeader() = %q, want 'JWT'", typeHeader) } }) } } func TestSignVerifyWithKIDFailure(t *testing.T) { rawJWT, err := NewRawJWT(&RawJWTOptions{TypeHeader: refString("JWT"), WithoutExpiration: true}) if err != nil { t.Fatalf("NewRawJWT() err = %v, want nil", err) } validator, err := NewValidator(&ValidatorOpts{ExpectedTypeHeader: refString("JWT"), AllowMissingExpiration: true}) if err != nil { t.Fatalf("NewValidator() err = %v, want nil", err) } for _, tc := range []signerVerifierKIDTestCase{ { tag: "verifier with custom kid different from header generated with custom kid", signerCustomKID: refString("1234"), verifierCustomKID: refString("123"), }, { tag: "verifier with custom kid different from header generated with tink kid", signerKID: refString("5678"), verifierCustomKID: refString("1234"), }, { tag: "verifier with both tink and custom kid", verifierCustomKID: refString("1234"), verifierKID: refString("1234"), }, { tag: "verifier with tink kid and token without kid in header", verifierKID: refString("1234"), }, } { t.Run(tc.tag, func(t *testing.T) { s, err := createESSigner(tc.signerCustomKID) if err != nil { t.Fatal(err) } v, err := createESVerifier(tc.verifierCustomKID) if err != nil { t.Fatal(err) } compact, err := s.SignAndEncodeWithKID(rawJWT, tc.signerKID) if err != nil { t.Fatalf("s.SignAndEncodeWithKID(kid = %v) err = %v, want nil", tc.signerKID, err) } if _, err := v.VerifyAndDecodeWithKID(compact, validator, tc.verifierKID); err == nil { t.Fatalf("s.VerifyAndDecodeWithKID(kid = %v) err = nil, want error", tc.verifierKID) } }) } } func TestVerifierModifiedCompact(t *testing.T) { rawJWT, err := NewRawJWT(&RawJWTOptions{TypeHeader: refString("JWT"), WithoutExpiration: true}) if err != nil { t.Fatalf("NewRawJWT() err = %v, want nil", err) } validator, err := NewValidator(&ValidatorOpts{ExpectedTypeHeader: refString("JWT"), AllowMissingExpiration: true}) if err != nil { t.Fatalf("NewValidator() err = %v, want nil", err) } s, err := createESSigner(nil) if err != nil { t.Fatal(err) } v, err := createESVerifier(nil) if err != nil { t.Fatal(err) } compact, err := s.SignAndEncodeWithKID(rawJWT, nil) if err != nil { t.Fatalf("s.SignAndEncodeWithKID() err = %v, want nil", err) } if _, err := v.VerifyAndDecodeWithKID(compact, validator, nil); err != nil { t.Errorf("VerifyAndDecodeWithKID() err = %v, want nil", err) } for _, invalid := range []string{ compact + "x", compact + " ", "x" + compact, " " + compact, } { if _, err := v.VerifyAndDecodeWithKID(invalid, validator, nil); err == nil { t.Errorf("VerifyAndDecodeWithKID() err = nil, want error") } } } func TestVerifierInvalidInputs(t *testing.T) { validator, err := NewValidator(&ValidatorOpts{AllowMissingExpiration: true}) if err != nil { t.Fatalf("NewValidator() err = %v, want nil", err) } v, err := createESVerifier(nil) if err != nil { t.Fatal(err) } for _, invalid := range []string{ "eyJhbGciOiJUzI1NiJ9.e30.YWJj.", "eyJhbGciOiJUzI1NiJ9?.e30.YWJj", "eyJhbGciOiJUzI1NiJ9.e30?.YWJj", "eyJhbGciOiJUzI1NiJ9.e30.YWJj?", "eyJhbGciOiJUzI1NiJ9.YWJj", } { if _, err := v.VerifyAndDecodeWithKID(invalid, validator, nil); err == nil { t.Errorf("v.VerifyAndDecodeWithKID(compact = %q) err = nil, want error", invalid) } } } func TestNewSignerWithNilTinkSignerFails(t *testing.T) { if _, err := newSignerWithKID(nil, "ES256", nil); err == nil { t.Errorf("newSignerWithKID(nil, 'ES256', nil) err = nil, want error") } } func TestNewVerifierWithNilTinkVerifierFails(t *testing.T) { if _, err := newVerifierWithKID(nil, "ES256", nil); err == nil { t.Errorf("newVerifierWithKID(nil, 'ES256', nil) err = nil, want error") } }