blob: 5a669783e2bdaf5ca707273a6c73b41d1fc4cfcd [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 services_test
import (
"context"
"errors"
"fmt"
"testing"
dpb "google.golang.org/protobuf/types/known/durationpb"
spb "google.golang.org/protobuf/types/known/structpb"
tpb "google.golang.org/protobuf/types/known/timestamppb"
wpb "google.golang.org/protobuf/types/known/wrapperspb"
"github.com/google/go-cmp/cmp"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/testing/protocmp"
"github.com/google/tink/go/aead"
"github.com/google/tink/go/jwt"
"github.com/google/tink/go/signature"
"github.com/google/tink/testing/go/services"
pb "github.com/google/tink/testing/go/protos/testing_api_go_grpc"
)
func verifiedJWTFromResponse(response *pb.JwtVerifyResponse) (*pb.JwtToken, error) {
switch r := response.Result.(type) {
case *pb.JwtVerifyResponse_VerifiedJwt:
return r.VerifiedJwt, nil
case *pb.JwtVerifyResponse_Err:
return nil, errors.New(r.Err)
default:
return nil, fmt.Errorf("response.Result has unexpected type %T", r)
}
}
func signedCompactJWTFromResponse(response *pb.JwtSignResponse) (string, error) {
switch r := response.Result.(type) {
case *pb.JwtSignResponse_SignedCompactJwt:
return r.SignedCompactJwt, nil
case *pb.JwtSignResponse_Err:
return "", errors.New(r.Err)
default:
return "", fmt.Errorf("response.Result has unexpected type %T", r)
}
}
func jwkSetFromResponse(response *pb.JwtToJwkSetResponse) (string, error) {
switch r := response.Result.(type) {
case *pb.JwtToJwkSetResponse_JwkSet:
return r.JwkSet, nil
case *pb.JwtToJwkSetResponse_Err:
return "", errors.New(r.Err)
default:
return "", fmt.Errorf("response.Result has unexpected type %T", r)
}
}
func keysetFromResponse(response *pb.JwtFromJwkSetResponse) ([]byte, error) {
switch r := response.Result.(type) {
case *pb.JwtFromJwkSetResponse_Keyset:
return r.Keyset, nil
case *pb.JwtFromJwkSetResponse_Err:
return nil, errors.New(r.Err)
default:
return nil, fmt.Errorf("response.Result has unexpected type %T", r)
}
}
type jwtTestCase struct {
tag string
rawJWT *pb.JwtToken
validator *pb.JwtValidator
}
func TestJWTComputeInvalidJWT(t *testing.T) {
for _, tc := range []jwtTestCase{
{
tag: "nil rawJWT",
rawJWT: nil,
},
{
tag: "invalid json array string",
rawJWT: &pb.JwtToken{
CustomClaims: map[string]*pb.JwtClaimValue{
"cc-array": &pb.JwtClaimValue{Kind: &pb.JwtClaimValue_JsonArrayValue{JsonArrayValue: "{35}"}},
},
},
},
{
tag: "invalid json object string",
rawJWT: &pb.JwtToken{
CustomClaims: map[string]*pb.JwtClaimValue{
"cc-object": &pb.JwtClaimValue{Kind: &pb.JwtClaimValue_JsonObjectValue{JsonObjectValue: `["o":"a"]`}},
},
},
},
} {
t.Run(tc.tag, func(t *testing.T) {
keysetService := &services.KeysetService{}
jwtService := &services.JWTService{}
ctx := context.Background()
template, err := proto.Marshal(jwt.HS256Template())
if err != nil {
t.Fatalf("proto.Marshal(jwt.HS256Template()) failed: %v", err)
}
keyset, err := genKeyset(ctx, keysetService, template)
if err != nil {
t.Fatalf("genKeyset failed: %v", err)
}
signResponse, err := jwtService.ComputeMacAndEncode(ctx, &pb.JwtSignRequest{AnnotatedKeyset: &pb.AnnotatedKeyset{SerializedKeyset: keyset}, RawJwt: tc.rawJWT})
if err != nil {
t.Fatalf("jwtService.ComputeMacAndEncode() err = %v, want nil", err)
}
if _, err := signedCompactJWTFromResponse(signResponse); err == nil {
t.Fatalf("JwtSignResponse: error = nil, want error")
}
})
}
}
func TestSuccessfulJwtMacCreation(t *testing.T) {
keysetService := &services.KeysetService{}
jwtService := &services.JWTService{}
ctx := context.Background()
template, err := proto.Marshal(jwt.HS256Template())
if err != nil {
t.Fatalf("proto.Marshal(jwt.HS256Template()) failed: %v, want nil", err)
}
keyset, err := genKeyset(ctx, keysetService, template)
if err != nil {
t.Fatalf("genKeyset failed: %v", err)
}
result, err := jwtService.CreateJwtMac(ctx, &pb.CreationRequest{AnnotatedKeyset: &pb.AnnotatedKeyset{SerializedKeyset: keyset}})
if err != nil {
t.Fatalf("CreateJwtMac with good keyset failed with gRPC error: %v, want nil", err)
}
if result.GetErr() != "" {
t.Fatalf("CreateJwtMac with good keyset failed with result.GetErr() = %q, want empty string", result.GetErr())
}
}
func TestFailingJwtMacCreation(t *testing.T) {
keysetService := &services.KeysetService{}
jwtService := &services.JWTService{}
ctx := context.Background()
// We use signature keys -- then we cannot create a JwtMac
template, err := proto.Marshal(aead.AES128GCMKeyTemplate())
if err != nil {
t.Fatalf("proto.Marshal(signature.ECDSAP256KeyTemplate()) failed: %v", err)
}
badKeyset, err := genKeyset(ctx, keysetService, template)
if err != nil {
t.Fatalf("genKeyset failed: %v", err)
}
result, err := jwtService.CreateJwtMac(ctx, &pb.CreationRequest{AnnotatedKeyset: &pb.AnnotatedKeyset{SerializedKeyset: badKeyset}})
if err != nil {
t.Fatalf("CreateJwtMac with bad keyset failed with gRPC error: %v", err)
}
if result.GetErr() == "" {
t.Fatalf("result.GetErr() of bad keyset after CreateJwtMac is empty, want not empty")
}
}
func TestJWTComputeMACWithInvalidKeysetFails(t *testing.T) {
keysetService := &services.KeysetService{}
jwtService := &services.JWTService{}
ctx := context.Background()
template, err := proto.Marshal(aead.AES256GCMKeyTemplate())
if err != nil {
t.Fatalf("proto.Marshal(jwt.AES256GCMKeyTemplate()) failed: %v", err)
}
keyset, err := genKeyset(ctx, keysetService, template)
if err != nil {
t.Fatalf("genKeyset failed: %v", err)
}
rawJWT := &pb.JwtToken{
TypeHeader: &wpb.StringValue{Value: "JWT"},
Issuer: &wpb.StringValue{Value: "issuer"},
}
signResponse, err := jwtService.ComputeMacAndEncode(ctx, &pb.JwtSignRequest{AnnotatedKeyset: &pb.AnnotatedKeyset{SerializedKeyset: keyset}, RawJwt: rawJWT})
if err != nil {
t.Fatalf("jwtService.ComputeMacAndEncode() err = %v, want nil", err)
}
if _, err := signedCompactJWTFromResponse(signResponse); err == nil {
t.Fatalf("JwtSignResponse: error = nil, want error")
}
}
func TestJWTComputeAndVerifyMac(t *testing.T) {
for _, tc := range []jwtTestCase{
{
tag: "all claims and custom claims",
rawJWT: &pb.JwtToken{
TypeHeader: &wpb.StringValue{Value: "JWT"},
Issuer: &wpb.StringValue{Value: "issuer"},
Subject: &wpb.StringValue{Value: "subject"},
JwtId: &wpb.StringValue{Value: "tink"},
Audiences: []string{"audience"},
Expiration: &tpb.Timestamp{Seconds: 123456},
NotBefore: &tpb.Timestamp{Seconds: 12345},
IssuedAt: &tpb.Timestamp{Seconds: 1234},
CustomClaims: map[string]*pb.JwtClaimValue{
"cc-null": &pb.JwtClaimValue{Kind: &pb.JwtClaimValue_NullValue{}},
"cc-num": &pb.JwtClaimValue{Kind: &pb.JwtClaimValue_NumberValue{NumberValue: 5.67}},
"cc-bool": &pb.JwtClaimValue{Kind: &pb.JwtClaimValue_BoolValue{BoolValue: true}},
"cc-string": &pb.JwtClaimValue{Kind: &pb.JwtClaimValue_StringValue{StringValue: "foo bar"}},
"cc-array": &pb.JwtClaimValue{Kind: &pb.JwtClaimValue_JsonArrayValue{JsonArrayValue: "[35]"}},
"cc-object": &pb.JwtClaimValue{Kind: &pb.JwtClaimValue_JsonObjectValue{JsonObjectValue: `{"key":"val"}`}},
},
},
validator: &pb.JwtValidator{
ExpectedTypeHeader: &wpb.StringValue{Value: "JWT"},
ExpectedIssuer: &wpb.StringValue{Value: "issuer"},
ExpectedAudience: &wpb.StringValue{Value: "audience"},
Now: &tpb.Timestamp{Seconds: 12345},
ClockSkew: &dpb.Duration{Seconds: 0},
},
},
{
tag: "without custom claims",
rawJWT: &pb.JwtToken{
TypeHeader: &wpb.StringValue{Value: "JWT"},
Issuer: &wpb.StringValue{Value: "issuer"},
Subject: &wpb.StringValue{Value: "subject"},
Audiences: []string{"audience"},
},
validator: &pb.JwtValidator{
ExpectedTypeHeader: &wpb.StringValue{Value: "JWT"},
ExpectedIssuer: &wpb.StringValue{Value: "issuer"},
ExpectedAudience: &wpb.StringValue{Value: "audience"},
AllowMissingExpiration: true,
},
},
{
tag: "without expiration",
rawJWT: &pb.JwtToken{
Subject: &wpb.StringValue{Value: "subject"},
},
validator: &pb.JwtValidator{
AllowMissingExpiration: true,
},
},
{
tag: "clock skew",
rawJWT: &pb.JwtToken{
Expiration: &tpb.Timestamp{Seconds: 1234},
},
validator: &pb.JwtValidator{
Now: &tpb.Timestamp{Seconds: 1235},
ClockSkew: &dpb.Duration{Seconds: 2},
},
},
} {
t.Run(tc.tag, func(t *testing.T) {
keysetService := &services.KeysetService{}
jwtService := &services.JWTService{}
ctx := context.Background()
template, err := proto.Marshal(jwt.HS256Template())
if err != nil {
t.Fatalf("proto.Marshal(jwt.HS256Template()) failed: %v", err)
}
keyset, err := genKeyset(ctx, keysetService, template)
if err != nil {
t.Fatalf("genKeyset failed: %v", err)
}
signResponse, err := jwtService.ComputeMacAndEncode(ctx, &pb.JwtSignRequest{AnnotatedKeyset: &pb.AnnotatedKeyset{SerializedKeyset: keyset}, RawJwt: tc.rawJWT})
if err != nil {
t.Fatalf("jwtService.ComputeMacAndEncode() err = %v, want nil", err)
}
compact, err := signedCompactJWTFromResponse(signResponse)
if err != nil {
t.Fatalf("JwtSignResponse_Err: %v", err)
}
verifyResponse, err := jwtService.VerifyMacAndDecode(ctx, &pb.JwtVerifyRequest{AnnotatedKeyset: &pb.AnnotatedKeyset{SerializedKeyset: keyset}, SignedCompactJwt: compact, Validator: tc.validator})
if err != nil {
t.Fatalf("jwtService.VerifyMacAndDecode() err = %v, want nil", err)
}
verifiedJWT, err := verifiedJWTFromResponse(verifyResponse)
if err != nil {
t.Fatalf("JwtVerifyResponse_Err: %v", err)
}
if !cmp.Equal(verifiedJWT, tc.rawJWT, protocmp.Transform()) {
t.Errorf("verifiedJWT doesn't match expected value: (+ got, - want) %v", cmp.Diff(verifiedJWT, tc.rawJWT, protocmp.Transform()))
}
})
}
}
func TestJWTVerifyMACFailures(t *testing.T) {
keysetService := &services.KeysetService{}
jwtService := &services.JWTService{}
ctx := context.Background()
template, err := proto.Marshal(jwt.HS256Template())
if err != nil {
t.Fatalf("proto.Marshal(jwt.HS256Template()) failed: %v", err)
}
keyset, err := genKeyset(ctx, keysetService, template)
if err != nil {
t.Fatalf("genKeyset failed: %v", err)
}
rawJWT := &pb.JwtToken{
TypeHeader: &wpb.StringValue{Value: "JWT"},
Expiration: &tpb.Timestamp{Seconds: 123456},
NotBefore: &tpb.Timestamp{Seconds: 12345},
IssuedAt: &tpb.Timestamp{Seconds: 1234},
}
signResponse, err := jwtService.ComputeMacAndEncode(ctx, &pb.JwtSignRequest{AnnotatedKeyset: &pb.AnnotatedKeyset{SerializedKeyset: keyset}, RawJwt: rawJWT})
if err != nil {
t.Fatalf("jwtService.ComputeMacAndEncode() err = %v, want nil", err)
}
compact, err := signedCompactJWTFromResponse(signResponse)
if err != nil {
t.Fatalf("JwtSignResponse_Err: %v", err)
}
validator := &pb.JwtValidator{
ExpectedTypeHeader: &wpb.StringValue{Value: "JWT"},
Now: &tpb.Timestamp{Seconds: 12345},
}
verifyResponse, err := jwtService.VerifyMacAndDecode(ctx, &pb.JwtVerifyRequest{AnnotatedKeyset: &pb.AnnotatedKeyset{SerializedKeyset: keyset}, SignedCompactJwt: compact, Validator: validator})
if err != nil {
t.Fatalf("jwtService.VerifyMacAndDecode() err = %v, want nil", err)
}
if _, err := verifiedJWTFromResponse(verifyResponse); err != nil {
t.Fatalf("JwtVerifyResponse_Err: %v", err)
}
for _, tc := range []jwtTestCase{
{
tag: "unexpected type header",
validator: &pb.JwtValidator{
ExpectedTypeHeader: &wpb.StringValue{Value: "unexpected"},
Now: &tpb.Timestamp{Seconds: 12345},
},
},
{
tag: "expired token",
validator: &pb.JwtValidator{
ExpectedTypeHeader: &wpb.StringValue{Value: "JWT"},
Now: &tpb.Timestamp{Seconds: 999999999999},
},
},
{
tag: "expect issued in the past",
validator: &pb.JwtValidator{
ExpectedTypeHeader: &wpb.StringValue{Value: "JWT"},
Now: &tpb.Timestamp{Seconds: 1233},
ExpectIssuedInThePast: true,
},
},
} {
t.Run(tc.tag, func(t *testing.T) {
verifyResponse, err := jwtService.VerifyMacAndDecode(ctx, &pb.JwtVerifyRequest{AnnotatedKeyset: &pb.AnnotatedKeyset{SerializedKeyset: keyset}, SignedCompactJwt: compact, Validator: tc.validator})
if err != nil {
t.Fatalf("jwtService.VerifyMacAndDecode() err = %v, want nil", err)
}
if _, err := verifiedJWTFromResponse(verifyResponse); err == nil {
t.Fatalf("JwtVerifyResponse_Err: nil, want error")
}
})
}
}
func TestSuccessfulJwtSignVerifyCreation(t *testing.T) {
keysetService := &services.KeysetService{}
jwtService := &services.JWTService{}
ctx := context.Background()
template, err := proto.Marshal(jwt.ES256Template())
if err != nil {
t.Fatalf("proto.Marshal(hybrid.ES256Template()) failed: %v", err)
}
privateKeyset, err := genKeyset(ctx, keysetService, template)
if err != nil {
t.Fatalf("genKeyset failed: %v", err)
}
result, err := jwtService.CreateJwtPublicKeySign(ctx, &pb.CreationRequest{AnnotatedKeyset: &pb.AnnotatedKeyset{SerializedKeyset: privateKeyset}})
if err != nil {
t.Fatalf("CreateJwtPublicKeySign with good keyset failed with gRPC error: %v, want nil", err)
}
if result.GetErr() != "" {
t.Fatalf("CreateJwtPublicKeySign with good keyset failed with result.GetErr() = %q, want empty string", result.GetErr())
}
}
func TestSuccessfulJwtVerifyCreation(t *testing.T) {
keysetService := &services.KeysetService{}
jwtService := &services.JWTService{}
ctx := context.Background()
template, err := proto.Marshal(jwt.ES256Template())
if err != nil {
t.Fatalf("proto.Marshal(hybrid.ES256Template()) failed: %v", err)
}
privateKeyset, err := genKeyset(ctx, keysetService, template)
if err != nil {
t.Fatalf("genKeyset failed: %v", err)
}
publicKeyset, err := pubKeyset(ctx, keysetService, privateKeyset)
if err != nil {
t.Fatalf("pubKeyset failed: %v", err)
}
result, err := jwtService.CreateJwtPublicKeyVerify(ctx, &pb.CreationRequest{AnnotatedKeyset: &pb.AnnotatedKeyset{SerializedKeyset: publicKeyset}})
if err != nil {
t.Fatalf("CreateJwtPublicKeyVerify with good keyset failed with gRPC error: %v", err)
}
if result.GetErr() != "" {
t.Fatalf("CreateJwtPublicKeyVerify with good keyset failed with result.GetErr() = %q, want empty string", result.GetErr())
}
}
func TestFailingJwtSignCreation(t *testing.T) {
keysetService := &services.KeysetService{}
jwtService := &services.JWTService{}
ctx := context.Background()
// We use signature keys -- then we cannot create a hybrid encrypt
template, err := proto.Marshal(signature.ECDSAP256KeyTemplate())
if err != nil {
t.Fatalf("proto.Marshal(signature.ECDSAP256KeyTemplate()) failed: %v", err)
}
privateKeyset, err := genKeyset(ctx, keysetService, template)
if err != nil {
t.Fatalf("genKeyset failed: %v", err)
}
result, err := jwtService.CreateJwtPublicKeySign(ctx, &pb.CreationRequest{AnnotatedKeyset: &pb.AnnotatedKeyset{SerializedKeyset: privateKeyset}})
if err != nil {
t.Fatalf("CreateJwtPublicKeySign with bad keyset failed with gRPC error: %v", err)
}
if result.GetErr() == "" {
t.Fatalf("CreateJwtPublicKeySign with bad keyset succeeded")
}
}
func TestFailingJwtVerifyCreation(t *testing.T) {
keysetService := &services.KeysetService{}
jwtService := &services.JWTService{}
ctx := context.Background()
// We use signature keys -- then we cannot create a hybrid encrypt
template, err := proto.Marshal(signature.ECDSAP256KeyTemplate())
if err != nil {
t.Fatalf("proto.Marshal(signature.ECDSAP256KeyTemplate()) failed: %v", err)
}
privateKeyset, err := genKeyset(ctx, keysetService, template)
if err != nil {
t.Fatalf("genKeyset failed: %v", err)
}
publicKeyset, err := pubKeyset(ctx, keysetService, privateKeyset)
if err != nil {
t.Fatalf("pubKeyset failed: %v", err)
}
result, err := jwtService.CreateJwtPublicKeyVerify(ctx, &pb.CreationRequest{AnnotatedKeyset: &pb.AnnotatedKeyset{SerializedKeyset: publicKeyset}})
if err != nil {
t.Fatalf("CreateJwtPublicKeyVerify with good keyset failed with gRPC error: %v", err)
}
if result.GetErr() == "" {
t.Fatalf("CreateJwtPublicKeyVerify with bad keyset succeeded")
}
}
func TestJWTPublicKeySignWithInvalidKeysetFails(t *testing.T) {
keysetService := &services.KeysetService{}
jwtService := &services.JWTService{}
ctx := context.Background()
template, err := proto.Marshal(aead.AES256GCMKeyTemplate())
if err != nil {
t.Fatalf("proto.Marshal(aead.AES256GCMKeyTemplate()) failed: %v", err)
}
privateKeyset, err := genKeyset(ctx, keysetService, template)
if err != nil {
t.Fatalf("genKeyset failed: %v", err)
}
rawJWT := &pb.JwtToken{
Subject: &wpb.StringValue{Value: "tink-subject"},
}
signResponse, err := jwtService.PublicKeySignAndEncode(ctx, &pb.JwtSignRequest{AnnotatedKeyset: &pb.AnnotatedKeyset{SerializedKeyset: privateKeyset}, RawJwt: rawJWT})
if err != nil {
t.Fatalf("jwtService.PublicKeySignAndEncode() err = %v", err)
}
if _, err := signedCompactJWTFromResponse(signResponse); err == nil {
t.Fatalf("JwtSignResponse_Err: nil want error")
}
}
func TestJWTPublicKeySignInvalidTokenFails(t *testing.T) {
keysetService := &services.KeysetService{}
jwtService := &services.JWTService{}
ctx := context.Background()
template, err := proto.Marshal(jwt.ES256Template())
if err != nil {
t.Fatalf("proto.Marshal(jwt.ES256Template()) failed: %v", err)
}
privateKeyset, err := genKeyset(ctx, keysetService, template)
if err != nil {
t.Fatalf("genKeyset failed: %v", err)
}
for _, tc := range []jwtTestCase{
{
tag: "nil rawJWT",
rawJWT: nil,
},
{
tag: "invalid json array string",
rawJWT: &pb.JwtToken{
CustomClaims: map[string]*pb.JwtClaimValue{
"cc-array": &pb.JwtClaimValue{Kind: &pb.JwtClaimValue_JsonArrayValue{JsonArrayValue: "{35}"}},
},
},
},
{
tag: "invalid json object string",
rawJWT: &pb.JwtToken{
CustomClaims: map[string]*pb.JwtClaimValue{
"cc-object": &pb.JwtClaimValue{Kind: &pb.JwtClaimValue_JsonObjectValue{JsonObjectValue: `["o":"a"]`}},
},
},
},
} {
t.Run(tc.tag, func(t *testing.T) {
signResponse, err := jwtService.PublicKeySignAndEncode(ctx, &pb.JwtSignRequest{AnnotatedKeyset: &pb.AnnotatedKeyset{SerializedKeyset: privateKeyset}, RawJwt: tc.rawJWT})
if err != nil {
t.Fatalf("jwtService.PublicKeySignAndEncode() err = %v", err)
}
if _, err := signedCompactJWTFromResponse(signResponse); err == nil {
t.Fatalf("JwtSignResponse_Err: nil want error")
}
})
}
}
func TestJWTPublicKeyVerifyFails(t *testing.T) {
keysetService := &services.KeysetService{}
jwtService := &services.JWTService{}
ctx := context.Background()
template, err := proto.Marshal(jwt.ES256Template())
if err != nil {
t.Fatalf("proto.Marshal(jwt.ES256Template()) failed: %v", err)
}
privateKeyset, err := genKeyset(ctx, keysetService, template)
if err != nil {
t.Fatalf("genKeyset failed: %v", err)
}
publicKeyset, err := pubKeyset(ctx, keysetService, privateKeyset)
if err != nil {
t.Fatalf("pubKeyset failed: %v", err)
}
rawJWT := &pb.JwtToken{
Subject: &wpb.StringValue{Value: "tink-subject"},
}
signResponse, err := jwtService.PublicKeySignAndEncode(ctx, &pb.JwtSignRequest{AnnotatedKeyset: &pb.AnnotatedKeyset{SerializedKeyset: privateKeyset}, RawJwt: rawJWT})
if err != nil {
t.Fatalf("jwtService.PublicKeySignAndEncode() err = %v", err)
}
compact, err := signedCompactJWTFromResponse(signResponse)
if err != nil {
t.Fatalf("JwtSignResponse_Err failed: %v", err)
}
validator := &pb.JwtValidator{
ExpectedTypeHeader: &wpb.StringValue{Value: "JWT"},
}
verifyResponse, err := jwtService.PublicKeyVerifyAndDecode(ctx, &pb.JwtVerifyRequest{AnnotatedKeyset: &pb.AnnotatedKeyset{SerializedKeyset: publicKeyset}, SignedCompactJwt: compact, Validator: validator})
if err != nil {
t.Fatalf("jwtVerifySignature failed: %v", err)
}
if _, err := verifiedJWTFromResponse(verifyResponse); err == nil {
t.Fatalf("JwtVerifyResponse_Err: nil want error")
}
}
func TestJWTPublicKeySignAndEncodeVerifyAndDecode(t *testing.T) {
keysetService := &services.KeysetService{}
jwtService := &services.JWTService{}
ctx := context.Background()
template, err := proto.Marshal(jwt.ES256Template())
if err != nil {
t.Fatalf("proto.Marshal(jwt.ES256Template()) failed: %v", err)
}
privateKeyset, err := genKeyset(ctx, keysetService, template)
if err != nil {
t.Fatalf("genKeyset failed: %v", err)
}
publicKeyset, err := pubKeyset(ctx, keysetService, privateKeyset)
if err != nil {
t.Fatalf("pubKeyset failed: %v", err)
}
rawJWT := &pb.JwtToken{
Subject: &wpb.StringValue{Value: "tink-subject"},
}
signResponse, err := jwtService.PublicKeySignAndEncode(ctx, &pb.JwtSignRequest{AnnotatedKeyset: &pb.AnnotatedKeyset{SerializedKeyset: privateKeyset}, RawJwt: rawJWT})
if err != nil {
t.Fatalf("jwtService.PublicKeySignAndEncode() err = %v", err)
}
compact, err := signedCompactJWTFromResponse(signResponse)
if err != nil {
t.Fatalf("JwtSignResponse_Err failed: %v", err)
}
validator := &pb.JwtValidator{
AllowMissingExpiration: true,
}
verifyResponse, err := jwtService.PublicKeyVerifyAndDecode(ctx, &pb.JwtVerifyRequest{AnnotatedKeyset: &pb.AnnotatedKeyset{SerializedKeyset: publicKeyset}, SignedCompactJwt: compact, Validator: validator})
if err != nil {
t.Fatalf("jwtVerifySignature failed: %v", err)
}
verifiedJWT, err := verifiedJWTFromResponse(verifyResponse)
if err != nil {
t.Fatalf("JwtVerifyResponse_Err: %v", err)
}
if !cmp.Equal(verifiedJWT, rawJWT, protocmp.Transform()) {
t.Errorf("verifiedJWT doesn't match expected value: (+ got, - want) %v", cmp.Diff(verifiedJWT, rawJWT, protocmp.Transform()))
}
}
func TestToJwkSetWithPrivateKeyFails(t *testing.T) {
keysetService := &services.KeysetService{}
jwtService := &services.JWTService{}
ctx := context.Background()
template, err := proto.Marshal(jwt.ES256Template())
if err != nil {
t.Fatalf("proto.Marshal(jwt.ES256Template()) failed: %v", err)
}
privateKeyset, err := genKeyset(ctx, keysetService, template)
if err != nil {
t.Fatalf("genKeyset failed: %v", err)
}
toJWKResponse, err := jwtService.ToJwkSet(ctx, &pb.JwtToJwkSetRequest{Keyset: privateKeyset})
if err != nil {
t.Fatalf("jwtService.ToJwkSet() err = %v, want nil", err)
}
if _, err := jwkSetFromResponse(toJWKResponse); err == nil {
t.Fatalf("JwtToJwkSetResponse_Err: = nil, want error")
}
}
func TestFromJwkSetPrivateKeyFails(t *testing.T) {
jwtService := &services.JWTService{}
ctx := context.Background()
jwkES256PublicKey := `{
"keys":[{
"kty":"EC",
"crv":"P-256",
"x":"wO6uIxh8SkKOO8VjZXNRTteRcwCPE4_4JElKyaa0fcQ",
"y":"7oRiYhnmkP6nqrdXWgtsWUWq5uFRLJkhyVFiWPRB278",
"d":"8oRinhnmkYjkqrdXWgtsWUWq5uFRLJkhyVFiWPRB278",
"use":"sig","alg":"ES256","key_ops":["verify"],
"kid":"EhuduQ"}]
}`
fromJWKResponse, err := jwtService.FromJwkSet(ctx, &pb.JwtFromJwkSetRequest{JwkSet: jwkES256PublicKey})
if err != nil {
t.Fatalf("jwtService.FromJwkSet() err = %v, want nil", err)
}
if _, err := keysetFromResponse(fromJWKResponse); err == nil {
t.Fatalf("JwtFromJwkSetResponse_Err = nil, want error")
}
}
func TestFromJwkToJwkSet(t *testing.T) {
jwtService := &services.JWTService{}
ctx := context.Background()
jwkES256PublicKey := `{
"keys":[{
"kty":"EC",
"crv":"P-256",
"x":"wO6uIxh8SkKOO8VjZXNRTteRcwCPE4_4JElKyaa0fcQ",
"y":"7oRiYhnmkP6nqrdXWgtsWUWq5uFRLJkhyVFiWPRB278",
"use":"sig","alg":"ES256","key_ops":["verify"],
"kid":"EhuduQ"}]
}`
fromJWKResponse, err := jwtService.FromJwkSet(ctx, &pb.JwtFromJwkSetRequest{JwkSet: jwkES256PublicKey})
if err != nil {
t.Fatalf("jwtService.FromJwkSet() err = %v, want nil", err)
}
ks, err := keysetFromResponse(fromJWKResponse)
if err != nil {
t.Fatalf("JwtFromJwkSetResponse_Err: = %v, want nil", err)
}
toJWKResponse, err := jwtService.ToJwkSet(ctx, &pb.JwtToJwkSetRequest{Keyset: ks})
if err != nil {
t.Fatalf("jwtService.ToJwkSet() err = %v, want nil", err)
}
jwkSet, err := jwkSetFromResponse(toJWKResponse)
if err != nil {
t.Fatalf("JwtToJwkSetResponse_Err: = %v, want nil", err)
}
got := &spb.Struct{}
if err := got.UnmarshalJSON([]byte(jwkSet)); err != nil {
t.Fatalf("got.UnmarshalJSON() err = %v, want nil", err)
}
want := &spb.Struct{}
if err := want.UnmarshalJSON([]byte(jwkES256PublicKey)); err != nil {
t.Fatalf("want.UnmarshalJSON() err = %v, want nil", err)
}
if !cmp.Equal(want, got, protocmp.Transform()) {
t.Errorf("mismatch in jwk sets: diff (-want,+got): %v", cmp.Diff(want, got, protocmp.Transform()))
}
}