blob: 1f204200729eaec2e6fef3ee4366c491360dfa33 [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_test
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"fmt"
"testing"
"google.golang.org/protobuf/proto"
"github.com/google/tink/go/jwt"
"github.com/google/tink/go/keyset"
"github.com/google/tink/go/signature"
"github.com/google/tink/go/testkeyset"
"github.com/google/tink/go/testutil"
jepb "github.com/google/tink/go/proto/jwt_ecdsa_go_proto"
tinkpb "github.com/google/tink/go/proto/tink_go_proto"
)
func TestSignerVerifierFactoryWithInvalidPrimitiveSetType(t *testing.T) {
kh, err := keyset.NewHandle(signature.ECDSAP256KeyTemplate())
if err != nil {
t.Fatalf("failed to build *keyset.Handle: %s", err)
}
if _, err := jwt.NewSigner(kh); err == nil {
t.Errorf("jwt.NewSigner() err = nil, want error")
}
if _, err := jwt.NewVerifier(kh); err == nil {
t.Errorf("jwt.NewVerifier() err = nil, want error")
}
}
func TestSignerVerifierFactoryNilKeyset(t *testing.T) {
if _, err := jwt.NewSigner(nil); err == nil {
t.Errorf("jwt.NewSigner(nil) err = nil, want error")
}
if _, err := jwt.NewVerifier(nil); err == nil {
t.Errorf("jwt.NewVerifier(nil) err = nil, want error")
}
}
func createJWTECDSAKey(kid *string) (*jepb.JwtEcdsaPrivateKey, error) {
k, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
return nil, fmt.Errorf("ecdsa.GenerateKey(curve=P256): %v", err)
}
var customKID *jepb.JwtEcdsaPublicKey_CustomKid = nil
if kid != nil {
customKID = &jepb.JwtEcdsaPublicKey_CustomKid{Value: *kid}
}
return &jepb.JwtEcdsaPrivateKey{
Version: 0,
PublicKey: &jepb.JwtEcdsaPublicKey{
Version: 0,
Algorithm: jepb.JwtEcdsaAlgorithm_ES256,
X: k.X.Bytes(),
Y: k.Y.Bytes(),
CustomKid: customKID,
},
KeyValue: k.D.Bytes(),
}, nil
}
func createKeyData(privKey *jepb.JwtEcdsaPrivateKey) (*tinkpb.KeyData, error) {
serializedPrivKey, err := proto.Marshal(privKey)
if err != nil {
return nil, fmt.Errorf("serializing private key proto: %v", err)
}
return &tinkpb.KeyData{
TypeUrl: "type.googleapis.com/google.crypto.tink.JwtEcdsaPrivateKey",
Value: serializedPrivKey,
KeyMaterialType: tinkpb.KeyData_ASYMMETRIC_PRIVATE,
}, nil
}
func createKeysetHandles(privKey *tinkpb.KeyData, outputPrefixType tinkpb.OutputPrefixType) (*keyset.Handle, *keyset.Handle, error) {
k := testutil.NewKey(privKey, tinkpb.KeyStatusType_ENABLED, 1 /*=keyID*/, outputPrefixType)
privKeyHandle, err := testkeyset.NewHandle(testutil.NewKeyset(k.KeyId, []*tinkpb.Keyset_Key{k}))
if err != nil {
return nil, nil, fmt.Errorf("creating keyset handle for private key: %v", err)
}
pubKeyHandle, err := privKeyHandle.Public()
if err != nil {
return nil, nil, fmt.Errorf("creating keyset handle for public key: %v", err)
}
return privKeyHandle, pubKeyHandle, nil
}
func createKeyHandlesFromKey(t *testing.T, privKey *jepb.JwtEcdsaPrivateKey, outputPrefixType tinkpb.OutputPrefixType) (*keyset.Handle, *keyset.Handle) {
privKeyData, err := createKeyData(privKey)
if err != nil {
t.Fatal(err)
}
privKeyHandle, pubKeyHandle, err := createKeysetHandles(privKeyData, outputPrefixType)
if err != nil {
t.Fatal(err)
}
return privKeyHandle, pubKeyHandle
}
func createKeyAndKeyHandles(t *testing.T, kid *string, outputPrefixType tinkpb.OutputPrefixType) (*jepb.JwtEcdsaPrivateKey, *keyset.Handle, *keyset.Handle) {
privKey, err := createJWTECDSAKey(kid)
if err != nil {
t.Fatal(err)
}
privKeyHandle, pubKeyHandle := createKeyHandlesFromKey(t, privKey, outputPrefixType)
return privKey, privKeyHandle, pubKeyHandle
}
func TestFactoryVerifyWithDifferentKeyFails(t *testing.T) {
_, privKeyHandle, pubKeyHandle := createKeyAndKeyHandles(t, nil /*=kid*/, tinkpb.OutputPrefixType_TINK)
signer, err := jwt.NewSigner(privKeyHandle)
if err != nil {
t.Fatalf("jwt.NewSigner() err = %v, want nil", err)
}
verifier, err := jwt.NewVerifier(pubKeyHandle)
if err != nil {
t.Fatalf("jwt.NewVerifier() err = %v, want nil", err)
}
rawJWT, err := jwt.NewRawJWT(&jwt.RawJWTOptions{WithoutExpiration: true, Audiences: []string{"tink-audience"}})
if err != nil {
t.Fatalf("jwt.NewRawJWT() err = %v, want nil", err)
}
validator, err := jwt.NewValidator(&jwt.ValidatorOpts{AllowMissingExpiration: true, ExpectedAudience: refString("tink-audience")})
if err != nil {
t.Fatalf("jwt.NewValidator() err = %v, want nil", err)
}
compact, err := signer.SignAndEncode(rawJWT)
if err != nil {
t.Errorf("signer.SignAndEncode() err = %v, want nil", err)
}
if _, err := verifier.VerifyAndDecode(compact, validator); err != nil {
t.Errorf("verifier.VerifyAndDecode() err = %v, want nil", err)
}
// verification with different key fails
_, _, pubKeyHandle = createKeyAndKeyHandles(t, nil /*=kid*/, tinkpb.OutputPrefixType_TINK)
verifier, err = jwt.NewVerifier(pubKeyHandle)
if err != nil {
t.Fatalf("jwt.NewVerifier() err = %v, want nil", err)
}
if _, err := verifier.VerifyAndDecode(compact, validator); err == nil {
t.Errorf("verifier.VerifyAndDecode() err = nil, want error")
}
}
func TestFactorySignWithTinkAndCustomKIDFails(t *testing.T) {
_, privKeyHandle, _ := createKeyAndKeyHandles(t, refString("customKID"), tinkpb.OutputPrefixType_TINK)
signer, err := jwt.NewSigner(privKeyHandle)
if err != nil {
t.Fatalf("jwt.NewSigner() err = %v, want nil", err)
}
rawJWT, err := jwt.NewRawJWT(&jwt.RawJWTOptions{WithoutExpiration: true})
if err != nil {
t.Fatalf("jwt.NewRawJWT() err = %v, want nil", err)
}
if _, err := signer.SignAndEncode(rawJWT); err == nil {
t.Errorf("signer.SignAndEncode() err = nil, want error")
}
}
type signerVerifierFactoryKIDTestCase struct {
tag string
signerOutputPrefix tinkpb.OutputPrefixType
signerKID *string
verifierOutputPrefix tinkpb.OutputPrefixType
verifierKID *string
}
func TestFactorySignVerifyWithKIDFailure(t *testing.T) {
rawJWT, err := jwt.NewRawJWT(&jwt.RawJWTOptions{WithoutExpiration: true})
if err != nil {
t.Fatalf("jwt.NewRawJWT() err = %v, want nil", err)
}
validator, err := jwt.NewValidator(&jwt.ValidatorOpts{AllowMissingExpiration: true})
if err != nil {
t.Fatalf("jwt.NewValidator() err = %v, want nil", err)
}
for _, tc := range []signerVerifierFactoryKIDTestCase{
{
tag: "raw output prefix and different custom kid",
signerOutputPrefix: tinkpb.OutputPrefixType_RAW,
signerKID: refString("customKID"),
verifierOutputPrefix: tinkpb.OutputPrefixType_RAW,
verifierKID: refString("OtherCustomKID"),
},
{
tag: "verifier with tink output prefix and custom kid when token has no kid",
signerOutputPrefix: tinkpb.OutputPrefixType_RAW,
signerKID: nil,
verifierOutputPrefix: tinkpb.OutputPrefixType_TINK,
verifierKID: refString("customKID"),
},
{
tag: "verifier with tink output prefix and custom kid when token has kid",
signerOutputPrefix: tinkpb.OutputPrefixType_RAW,
signerKID: refString("customKID"),
verifierOutputPrefix: tinkpb.OutputPrefixType_TINK,
verifierKID: refString("customKid"),
},
{
tag: "token with fixed kid and verifier with tink output prefix",
signerOutputPrefix: tinkpb.OutputPrefixType_RAW,
signerKID: refString("customKID"),
verifierOutputPrefix: tinkpb.OutputPrefixType_TINK,
verifierKID: nil,
},
{
tag: "token missing kid in header when verifier has tink output prefix",
signerOutputPrefix: tinkpb.OutputPrefixType_RAW,
signerKID: nil,
verifierOutputPrefix: tinkpb.OutputPrefixType_TINK,
verifierKID: nil,
},
} {
t.Run(tc.tag, func(t *testing.T) {
key, privKeyHandle, _ := createKeyAndKeyHandles(t, tc.signerKID, tc.signerOutputPrefix)
signer, err := jwt.NewSigner(privKeyHandle)
if err != nil {
t.Fatalf("jwt.NewSigner() err = %v, want nil", err)
}
compact, err := signer.SignAndEncode(rawJWT)
if err != nil {
t.Errorf("signer.SignAndEncode() err = %v, want nil", err)
}
key.PublicKey.CustomKid = nil
if tc.verifierKID != nil {
key.PublicKey.CustomKid = &jepb.JwtEcdsaPublicKey_CustomKid{Value: *tc.verifierKID}
}
_, pubKeyHandle := createKeyHandlesFromKey(t, key, tc.verifierOutputPrefix)
verifier, err := jwt.NewVerifier(pubKeyHandle)
if err != nil {
t.Fatalf("jwt.NewVerifier() err = %v, want nil", err)
}
if _, err := verifier.VerifyAndDecode(compact, validator); err == nil {
t.Errorf("verifier.VerifyAndDecode() err = nil, want error")
}
})
}
}
func TestFactorySignVerifyWithKIDSuccess(t *testing.T) {
rawJWT, err := jwt.NewRawJWT(&jwt.RawJWTOptions{WithoutExpiration: true})
if err != nil {
t.Fatalf("jwt.NewRawJWT() err = %v, want nil", err)
}
validator, err := jwt.NewValidator(&jwt.ValidatorOpts{AllowMissingExpiration: true})
if err != nil {
t.Fatalf("jwt.NewValidator() err = %v, want nil", err)
}
for _, tc := range []signerVerifierFactoryKIDTestCase{
{
tag: "signer verifier without custom kid and with raw output prefix",
signerOutputPrefix: tinkpb.OutputPrefixType_RAW,
signerKID: nil,
verifierOutputPrefix: tinkpb.OutputPrefixType_RAW,
verifierKID: nil,
},
{
tag: "signer with custom kid verifier without custom kid and raw output prefixes",
signerOutputPrefix: tinkpb.OutputPrefixType_RAW,
signerKID: refString("customKID"),
verifierOutputPrefix: tinkpb.OutputPrefixType_RAW,
verifierKID: nil,
},
{
tag: "signer and verifier same custom kid and raw output prefix",
signerOutputPrefix: tinkpb.OutputPrefixType_RAW,
signerKID: refString("customKID"),
verifierOutputPrefix: tinkpb.OutputPrefixType_RAW,
verifierKID: refString("customKID"),
},
{
tag: "signer and verifier with tink output prefix and no custom kid",
signerOutputPrefix: tinkpb.OutputPrefixType_TINK,
signerKID: nil,
verifierOutputPrefix: tinkpb.OutputPrefixType_TINK,
verifierKID: nil,
},
{
tag: "signer with tink output prefix verifier with raw output prefix",
signerOutputPrefix: tinkpb.OutputPrefixType_TINK,
signerKID: nil,
verifierOutputPrefix: tinkpb.OutputPrefixType_RAW,
verifierKID: nil,
},
{
tag: "token missing kid in header when verifier has custom kid",
signerOutputPrefix: tinkpb.OutputPrefixType_RAW,
signerKID: nil,
verifierOutputPrefix: tinkpb.OutputPrefixType_RAW,
verifierKID: refString("customKID"),
},
} {
t.Run(tc.tag, func(t *testing.T) {
key, privKeyHandle, _ := createKeyAndKeyHandles(t, tc.signerKID, tc.signerOutputPrefix)
signer, err := jwt.NewSigner(privKeyHandle)
if err != nil {
t.Fatalf("jwt.NewSigner() err = %v, want nil", err)
}
compact, err := signer.SignAndEncode(rawJWT)
if err != nil {
t.Errorf("signer.SignAndEncode() err = %v, want nil", err)
}
key.GetPublicKey().CustomKid = nil
if tc.verifierKID != nil {
key.GetPublicKey().CustomKid = &jepb.JwtEcdsaPublicKey_CustomKid{Value: *tc.verifierKID}
}
_, pubKeyHandle := createKeyHandlesFromKey(t, key, tc.verifierOutputPrefix)
verifier, err := jwt.NewVerifier(pubKeyHandle)
if err != nil {
t.Fatalf("jwt.NewVerifier() err = %v, want nil", err)
}
if _, err := verifier.VerifyAndDecode(compact, validator); err != nil {
t.Errorf("verifier.VerifyAndDecode() err = %v, want nil", err)
}
})
}
}