blob: 55b3efb716879d13390c8c12bbc50923b0c55c9a [file] [log] [blame]
// 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")
}
}