xref: /aosp_15_r20/external/tink/go/jwt/jwt_encoding_test.go (revision e7b1675dde1b92d52ec075b0a92829627f2c52a5)
1*e7b1675dSTing-Kang Chang// Copyright 2022 Google LLC
2*e7b1675dSTing-Kang Chang//
3*e7b1675dSTing-Kang Chang// Licensed under the Apache License, Version 2.0 (the "License");
4*e7b1675dSTing-Kang Chang// you may not use this file except in compliance with the License.
5*e7b1675dSTing-Kang Chang// You may obtain a copy of the License at
6*e7b1675dSTing-Kang Chang//
7*e7b1675dSTing-Kang Chang//      http://www.apache.org/licenses/LICENSE-2.0
8*e7b1675dSTing-Kang Chang//
9*e7b1675dSTing-Kang Chang// Unless required by applicable law or agreed to in writing, software
10*e7b1675dSTing-Kang Chang// distributed under the License is distributed on an "AS IS" BASIS,
11*e7b1675dSTing-Kang Chang// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*e7b1675dSTing-Kang Chang// See the License for the specific language governing permissions and
13*e7b1675dSTing-Kang Chang// limitations under the License.
14*e7b1675dSTing-Kang Chang//
15*e7b1675dSTing-Kang Chang////////////////////////////////////////////////////////////////////////////////
16*e7b1675dSTing-Kang Chang
17*e7b1675dSTing-Kang Changpackage jwt
18*e7b1675dSTing-Kang Chang
19*e7b1675dSTing-Kang Changimport (
20*e7b1675dSTing-Kang Chang	"strings"
21*e7b1675dSTing-Kang Chang	"testing"
22*e7b1675dSTing-Kang Chang	"time"
23*e7b1675dSTing-Kang Chang
24*e7b1675dSTing-Kang Chang	"github.com/google/go-cmp/cmp"
25*e7b1675dSTing-Kang Chang
26*e7b1675dSTing-Kang Chang	tpb "github.com/google/tink/go/proto/tink_go_proto"
27*e7b1675dSTing-Kang Chang)
28*e7b1675dSTing-Kang Chang
29*e7b1675dSTing-Kang Changfunc TestKIDForNonTinkKeysIsNil(t *testing.T) {
30*e7b1675dSTing-Kang Chang	for _, op := range []tpb.OutputPrefixType{
31*e7b1675dSTing-Kang Chang		tpb.OutputPrefixType_LEGACY,
32*e7b1675dSTing-Kang Chang		tpb.OutputPrefixType_RAW,
33*e7b1675dSTing-Kang Chang		tpb.OutputPrefixType_CRUNCHY} {
34*e7b1675dSTing-Kang Chang		if kid := keyID(1234, op); kid != nil {
35*e7b1675dSTing-Kang Chang			t.Errorf("keyID(1234, %q) = %q, want nil", op, *kid)
36*e7b1675dSTing-Kang Chang		}
37*e7b1675dSTing-Kang Chang	}
38*e7b1675dSTing-Kang Chang}
39*e7b1675dSTing-Kang Chang
40*e7b1675dSTing-Kang Changfunc TestKeyIDForTinkKey(t *testing.T) {
41*e7b1675dSTing-Kang Chang	want := "GsapRA"
42*e7b1675dSTing-Kang Chang	kid := keyID(0x1ac6a944, tpb.OutputPrefixType_TINK)
43*e7b1675dSTing-Kang Chang	if kid == nil {
44*e7b1675dSTing-Kang Chang		t.Errorf("KeyID(0x1ac6a944, %q) = nil, want %q", tpb.OutputPrefixType_TINK, want)
45*e7b1675dSTing-Kang Chang	}
46*e7b1675dSTing-Kang Chang	if kid != nil && !cmp.Equal(*kid, want) {
47*e7b1675dSTing-Kang Chang		t.Errorf("KeyID(0x1ac6a944, %q) = %q, want %q", tpb.OutputPrefixType_TINK, *kid, want)
48*e7b1675dSTing-Kang Chang	}
49*e7b1675dSTing-Kang Chang}
50*e7b1675dSTing-Kang Chang
51*e7b1675dSTing-Kang Changtype payloadTestCase struct {
52*e7b1675dSTing-Kang Chang	tag       string
53*e7b1675dSTing-Kang Chang	rawJWT    *RawJWT
54*e7b1675dSTing-Kang Chang	opts      *RawJWTOptions
55*e7b1675dSTing-Kang Chang	tinkKID   *string
56*e7b1675dSTing-Kang Chang	customKID *string
57*e7b1675dSTing-Kang Chang	algorithm string
58*e7b1675dSTing-Kang Chang}
59*e7b1675dSTing-Kang Chang
60*e7b1675dSTing-Kang Changfunc refString(a string) *string {
61*e7b1675dSTing-Kang Chang	return &a
62*e7b1675dSTing-Kang Chang}
63*e7b1675dSTing-Kang Chang
64*e7b1675dSTing-Kang Changfunc refTime(ts int64) *time.Time {
65*e7b1675dSTing-Kang Chang	t := time.Unix(ts, 0)
66*e7b1675dSTing-Kang Chang	return &t
67*e7b1675dSTing-Kang Chang}
68*e7b1675dSTing-Kang Chang
69*e7b1675dSTing-Kang Changfunc TestBase64Encode(t *testing.T) {
70*e7b1675dSTing-Kang Chang	// Examples from: https://datatracker.ietf.org/doc/html/rfc7515#appendix-A.1.1
71*e7b1675dSTing-Kang Chang	want := "eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ"
72*e7b1675dSTing-Kang Chang	payload := []byte{123, 34, 105, 115, 115, 34, 58, 34, 106, 111, 101, 34, 44, 13, 10, 32, 34, 101, 120, 112, 34, 58, 49, 51, 48, 48, 56, 49, 57, 51, 56,
73*e7b1675dSTing-Kang Chang		48, 44, 13, 10, 32, 34, 104, 116, 116, 112, 58, 47, 47, 101, 120, 97, 109, 112, 108, 101, 46, 99, 111, 109, 47, 105, 115, 95, 114, 111, 111, 116, 34, 58, 116, 114, 117, 101, 125}
74*e7b1675dSTing-Kang Chang	got := base64Encode(payload)
75*e7b1675dSTing-Kang Chang	if got != want {
76*e7b1675dSTing-Kang Chang		t.Errorf("base64Encode() got %q want %q", got, want)
77*e7b1675dSTing-Kang Chang	}
78*e7b1675dSTing-Kang Chang}
79*e7b1675dSTing-Kang Chang
80*e7b1675dSTing-Kang Changfunc TestBase64Decode(t *testing.T) {
81*e7b1675dSTing-Kang Chang	// Examples from: https://datatracker.ietf.org/doc/html/rfc7515#appendix-A.1.1
82*e7b1675dSTing-Kang Chang	want := []byte{123, 34, 105, 115, 115, 34, 58, 34, 106, 111, 101, 34, 44, 13, 10, 32, 34, 101, 120, 112, 34, 58, 49, 51, 48, 48, 56, 49, 57, 51, 56,
83*e7b1675dSTing-Kang Chang		48, 44, 13, 10, 32, 34, 104, 116, 116, 112, 58, 47, 47, 101, 120, 97, 109, 112, 108, 101, 46, 99, 111, 109, 47, 105, 115, 95, 114, 111, 111, 116, 34, 58, 116, 114, 117, 101, 125}
84*e7b1675dSTing-Kang Chang	got, err := base64Decode("eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ")
85*e7b1675dSTing-Kang Chang	if err != nil {
86*e7b1675dSTing-Kang Chang		t.Errorf("base64Decode() err = %v, want nil", err)
87*e7b1675dSTing-Kang Chang	}
88*e7b1675dSTing-Kang Chang	if !cmp.Equal(got, want) {
89*e7b1675dSTing-Kang Chang		t.Errorf("base64Decode() got %q, want %q", got, want)
90*e7b1675dSTing-Kang Chang	}
91*e7b1675dSTing-Kang Chang}
92*e7b1675dSTing-Kang Chang
93*e7b1675dSTing-Kang Changfunc TestInvalidCharactersFailBase64Decode(t *testing.T) {
94*e7b1675dSTing-Kang Chang	if _, err := base64Decode("iLA0KIC&hD"); err == nil {
95*e7b1675dSTing-Kang Chang		t.Errorf("base64Decode() err = nil, want error")
96*e7b1675dSTing-Kang Chang	}
97*e7b1675dSTing-Kang Chang}
98*e7b1675dSTing-Kang Chang
99*e7b1675dSTing-Kang Changfunc TestEncodeStaticHeaderWithPayloadIssuerTokenForSigning(t *testing.T) {
100*e7b1675dSTing-Kang Chang	opts := &RawJWTOptions{
101*e7b1675dSTing-Kang Chang		WithoutExpiration: true,
102*e7b1675dSTing-Kang Chang		Issuer:            refString("tink-issuer"),
103*e7b1675dSTing-Kang Chang	}
104*e7b1675dSTing-Kang Chang	// Header 'RS256' alg from: https://datatracker.ietf.org/doc/html/rfc7515#appendix-A.2.1
105*e7b1675dSTing-Kang Chang	// Payload: `{"iss":"tink-issuer"}`
106*e7b1675dSTing-Kang Chang	wantUnsigned := "eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJ0aW5rLWlzc3VlciJ9"
107*e7b1675dSTing-Kang Chang	rawJWT, err := NewRawJWT(opts)
108*e7b1675dSTing-Kang Chang	if err != nil {
109*e7b1675dSTing-Kang Chang		t.Fatalf("generating valid RawJWT: %v", err)
110*e7b1675dSTing-Kang Chang	}
111*e7b1675dSTing-Kang Chang	unsigned, err := createUnsigned(rawJWT, "RS256", nil, nil)
112*e7b1675dSTing-Kang Chang	if err != nil {
113*e7b1675dSTing-Kang Chang		t.Errorf("createUnsigned() err = %v, want nil", err)
114*e7b1675dSTing-Kang Chang	}
115*e7b1675dSTing-Kang Chang
116*e7b1675dSTing-Kang Chang	if unsigned != wantUnsigned {
117*e7b1675dSTing-Kang Chang		t.Errorf("got unsigned %q, want %q", unsigned, wantUnsigned)
118*e7b1675dSTing-Kang Chang	}
119*e7b1675dSTing-Kang Chang}
120*e7b1675dSTing-Kang Chang
121*e7b1675dSTing-Kang Changfunc TestEncodeHeaderWithHeaderFieldsAndEmptyPayload(t *testing.T) {
122*e7b1675dSTing-Kang Chang	type testCase struct {
123*e7b1675dSTing-Kang Chang		tag                 string
124*e7b1675dSTing-Kang Chang		opts                *RawJWTOptions
125*e7b1675dSTing-Kang Chang		wantHeaderSubstring string
126*e7b1675dSTing-Kang Chang		customKID           *string
127*e7b1675dSTing-Kang Chang		tinkKID             *string
128*e7b1675dSTing-Kang Chang	}
129*e7b1675dSTing-Kang Chang	for _, tc := range []testCase{
130*e7b1675dSTing-Kang Chang		{
131*e7b1675dSTing-Kang Chang			tag: "type header",
132*e7b1675dSTing-Kang Chang			opts: &RawJWTOptions{
133*e7b1675dSTing-Kang Chang				WithoutExpiration: true,
134*e7b1675dSTing-Kang Chang				TypeHeader:        refString("JWT"),
135*e7b1675dSTing-Kang Chang			},
136*e7b1675dSTing-Kang Chang			wantHeaderSubstring: `"typ":"JWT"`,
137*e7b1675dSTing-Kang Chang		},
138*e7b1675dSTing-Kang Chang		{
139*e7b1675dSTing-Kang Chang			tag: "custom kid",
140*e7b1675dSTing-Kang Chang			opts: &RawJWTOptions{
141*e7b1675dSTing-Kang Chang				WithoutExpiration: true,
142*e7b1675dSTing-Kang Chang			},
143*e7b1675dSTing-Kang Chang			customKID:           refString("custom"),
144*e7b1675dSTing-Kang Chang			wantHeaderSubstring: `"kid":"custom"`,
145*e7b1675dSTing-Kang Chang		},
146*e7b1675dSTing-Kang Chang		{
147*e7b1675dSTing-Kang Chang			tag: "tink kid",
148*e7b1675dSTing-Kang Chang			opts: &RawJWTOptions{
149*e7b1675dSTing-Kang Chang				WithoutExpiration: true,
150*e7b1675dSTing-Kang Chang			},
151*e7b1675dSTing-Kang Chang			tinkKID:             refString("tink"),
152*e7b1675dSTing-Kang Chang			wantHeaderSubstring: `"kid":"tink"`,
153*e7b1675dSTing-Kang Chang		},
154*e7b1675dSTing-Kang Chang	} {
155*e7b1675dSTing-Kang Chang		rawJWT, err := NewRawJWT(tc.opts)
156*e7b1675dSTing-Kang Chang		if err != nil {
157*e7b1675dSTing-Kang Chang			t.Fatalf("generating valid RawJWT: %v", err)
158*e7b1675dSTing-Kang Chang		}
159*e7b1675dSTing-Kang Chang		unsigned, err := createUnsigned(rawJWT, "RS256", tc.tinkKID, tc.customKID)
160*e7b1675dSTing-Kang Chang		if err != nil {
161*e7b1675dSTing-Kang Chang			t.Errorf("createUnsigned() err = %v, want nil", err)
162*e7b1675dSTing-Kang Chang		}
163*e7b1675dSTing-Kang Chang		token := strings.Split(unsigned, ".")
164*e7b1675dSTing-Kang Chang		if len(token) != 2 {
165*e7b1675dSTing-Kang Chang			t.Errorf("token[0] not encoded in compact serialization format")
166*e7b1675dSTing-Kang Chang		}
167*e7b1675dSTing-Kang Chang		header, err := base64Decode(token[0])
168*e7b1675dSTing-Kang Chang		if err != nil {
169*e7b1675dSTing-Kang Chang			t.Errorf("base64Decode(token[0] = %q)", token[0])
170*e7b1675dSTing-Kang Chang		}
171*e7b1675dSTing-Kang Chang		if !strings.Contains(string(header), tc.wantHeaderSubstring) {
172*e7b1675dSTing-Kang Chang			t.Errorf("header %q, doesn't contain: %q", string(header), tc.wantHeaderSubstring)
173*e7b1675dSTing-Kang Chang		}
174*e7b1675dSTing-Kang Chang		wantPayload := "e30" // `{}`
175*e7b1675dSTing-Kang Chang		if string(token[1]) != wantPayload {
176*e7b1675dSTing-Kang Chang			t.Errorf("token[1] = %q, want %q", token[1], wantPayload)
177*e7b1675dSTing-Kang Chang		}
178*e7b1675dSTing-Kang Chang	}
179*e7b1675dSTing-Kang Chang}
180*e7b1675dSTing-Kang Chang
181*e7b1675dSTing-Kang Changfunc TestCreateUnsignedWithNilRawJWTFails(t *testing.T) {
182*e7b1675dSTing-Kang Chang	if _, err := createUnsigned(nil, "HS256", nil, nil); err == nil {
183*e7b1675dSTing-Kang Chang		t.Errorf("createUnsigned(rawJWT = nil) err = nil, want error")
184*e7b1675dSTing-Kang Chang	}
185*e7b1675dSTing-Kang Chang}
186*e7b1675dSTing-Kang Chang
187*e7b1675dSTing-Kang Changfunc TestCreateUnsignedCustomAndTinkKIDFail(t *testing.T) {
188*e7b1675dSTing-Kang Chang	rawJWT, err := NewRawJWT(&RawJWTOptions{WithoutExpiration: true})
189*e7b1675dSTing-Kang Chang	if err != nil {
190*e7b1675dSTing-Kang Chang		t.Fatalf("generating valid RawJWT: %v", err)
191*e7b1675dSTing-Kang Chang	}
192*e7b1675dSTing-Kang Chang	if _, err := createUnsigned(rawJWT, "HS256", refString("123"), refString("456")); err == nil {
193*e7b1675dSTing-Kang Chang		t.Errorf("createUnsigned(tinkKID = 456, customKID = 123) err = nil, want error")
194*e7b1675dSTing-Kang Chang	}
195*e7b1675dSTing-Kang Chang}
196*e7b1675dSTing-Kang Chang
197*e7b1675dSTing-Kang Changfunc TestCombineTokenAndSignature(t *testing.T) {
198*e7b1675dSTing-Kang Chang	// https://datatracker.ietf.org/doc/html/rfc7515#appendix-A.2.1
199*e7b1675dSTing-Kang Chang	payload := "eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ"
200*e7b1675dSTing-Kang Chang	signature := []byte{116, 24, 223, 180, 151, 153, 224, 37, 79, 250, 96, 125, 216, 173, 187, 186, 22, 212, 37, 77, 105, 214, 191, 240, 91, 88, 5, 88, 83, 132, 141, 121}
201*e7b1675dSTing-Kang Chang	token := combineUnsignedAndSignature(payload, signature)
202*e7b1675dSTing-Kang Chang	want := "eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk"
203*e7b1675dSTing-Kang Chang	if !cmp.Equal(token, want) {
204*e7b1675dSTing-Kang Chang		t.Errorf("combineUnsignedAndSignature(%q, %q) = %q, want %q", payload, signature, token, want)
205*e7b1675dSTing-Kang Chang	}
206*e7b1675dSTing-Kang Chang}
207*e7b1675dSTing-Kang Chang
208*e7b1675dSTing-Kang Changfunc TestSplitSignedCompactInvalidInputs(t *testing.T) {
209*e7b1675dSTing-Kang Chang	type testCases struct {
210*e7b1675dSTing-Kang Chang		tag   string
211*e7b1675dSTing-Kang Chang		token string
212*e7b1675dSTing-Kang Chang	}
213*e7b1675dSTing-Kang Chang	for _, tc := range []testCases{
214*e7b1675dSTing-Kang Chang		{
215*e7b1675dSTing-Kang Chang			tag:   "empty payload",
216*e7b1675dSTing-Kang Chang			token: "",
217*e7b1675dSTing-Kang Chang		},
218*e7b1675dSTing-Kang Chang		{
219*e7b1675dSTing-Kang Chang			tag:   "not in compact serialization missing separators",
220*e7b1675dSTing-Kang Chang			token: "Zm9vYmFyIVRpbms",
221*e7b1675dSTing-Kang Chang		},
222*e7b1675dSTing-Kang Chang		{
223*e7b1675dSTing-Kang Chang			tag:   "not in compact serialization additional separators",
224*e7b1675dSTing-Kang Chang			token: "Zm9vYmFyIVRpbms.Zm9vYmFyGVRpbms.Zm9vYmFyIVRpbms.Zm9vYmFyINRpbms",
225*e7b1675dSTing-Kang Chang		},
226*e7b1675dSTing-Kang Chang		{
227*e7b1675dSTing-Kang Chang			tag:   "non web safe URL encoding character",
228*e7b1675dSTing-Kang Chang			token: "Zm9vYmFyIVRpbms.m9vYmFy.Zm&mFyIVRpbms",
229*e7b1675dSTing-Kang Chang		},
230*e7b1675dSTing-Kang Chang		{
231*e7b1675dSTing-Kang Chang			tag:   "no content",
232*e7b1675dSTing-Kang Chang			token: ".Zm9vYmFyIVRpbms",
233*e7b1675dSTing-Kang Chang		},
234*e7b1675dSTing-Kang Chang		{
235*e7b1675dSTing-Kang Chang			tag:   "no signature",
236*e7b1675dSTing-Kang Chang			token: "Zm9vYmFyIVRpbms.Zm9vYmFyIVRpbms.",
237*e7b1675dSTing-Kang Chang		},
238*e7b1675dSTing-Kang Chang		{
239*e7b1675dSTing-Kang Chang			tag:   "no signature and no content",
240*e7b1675dSTing-Kang Chang			token: "..",
241*e7b1675dSTing-Kang Chang		},
242*e7b1675dSTing-Kang Chang	} {
243*e7b1675dSTing-Kang Chang		t.Run(tc.tag, func(t *testing.T) {
244*e7b1675dSTing-Kang Chang			if _, _, err := splitSignedCompact(tc.token); err == nil {
245*e7b1675dSTing-Kang Chang				t.Errorf("splitSignedCompact(%q) err = nil, want error", tc.token)
246*e7b1675dSTing-Kang Chang			}
247*e7b1675dSTing-Kang Chang		})
248*e7b1675dSTing-Kang Chang	}
249*e7b1675dSTing-Kang Chang}
250*e7b1675dSTing-Kang Chang
251*e7b1675dSTing-Kang Changfunc TestSplitSignedCompact(t *testing.T) {
252*e7b1675dSTing-Kang Chang	// signed token from: https://datatracker.ietf.org/doc/html/rfc7515#appendix-A.1.1
253*e7b1675dSTing-Kang Chang	signedToken := "eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk"
254*e7b1675dSTing-Kang Chang	wantSig := []byte{116, 24, 223, 180, 151, 153, 224, 37, 79, 250, 96, 125, 216, 173, 187, 186, 22, 212, 37, 77, 105, 214, 191, 240, 91, 88, 5, 88, 83, 132, 141, 121}
255*e7b1675dSTing-Kang Chang	wantToken := "eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ"
256*e7b1675dSTing-Kang Chang	sig, token, err := splitSignedCompact(signedToken)
257*e7b1675dSTing-Kang Chang	if err != nil {
258*e7b1675dSTing-Kang Chang		t.Errorf("splitSignedCompact(%q) err = %v, want nil", signedToken, err)
259*e7b1675dSTing-Kang Chang	}
260*e7b1675dSTing-Kang Chang	if !cmp.Equal(sig, wantSig) {
261*e7b1675dSTing-Kang Chang		t.Errorf("splitSignedCompact() sig = %q, want %q", sig, wantSig)
262*e7b1675dSTing-Kang Chang	}
263*e7b1675dSTing-Kang Chang	if token != wantToken {
264*e7b1675dSTing-Kang Chang		t.Errorf("splitSignedCompact() token = %q, want %q", token, wantToken)
265*e7b1675dSTing-Kang Chang	}
266*e7b1675dSTing-Kang Chang}
267*e7b1675dSTing-Kang Chang
268*e7b1675dSTing-Kang Changfunc TestDecodeValidateInvalidHeaderFailures(t *testing.T) {
269*e7b1675dSTing-Kang Chang	type testCases struct {
270*e7b1675dSTing-Kang Chang		tag       string
271*e7b1675dSTing-Kang Chang		header    string
272*e7b1675dSTing-Kang Chang		alg       string
273*e7b1675dSTing-Kang Chang		tinkKID   *string
274*e7b1675dSTing-Kang Chang		customKID *string
275*e7b1675dSTing-Kang Chang	}
276*e7b1675dSTing-Kang Chang	for _, tc := range []testCases{
277*e7b1675dSTing-Kang Chang		{
278*e7b1675dSTing-Kang Chang			tag:    "invalid JSON header",
279*e7b1675dSTing-Kang Chang			header: `JiVeQCo`,
280*e7b1675dSTing-Kang Chang		},
281*e7b1675dSTing-Kang Chang		{
282*e7b1675dSTing-Kang Chang			tag:    "contains line feed",
283*e7b1675dSTing-Kang Chang			header: "eyJ0eXAiOiJKV1Qi\nLA0KICJhbGciOiJIUzI1NiJ9",
284*e7b1675dSTing-Kang Chang			alg:    "HS256",
285*e7b1675dSTing-Kang Chang		},
286*e7b1675dSTing-Kang Chang		{
287*e7b1675dSTing-Kang Chang			tag:    "header contains no fields",
288*e7b1675dSTing-Kang Chang			header: base64Encode([]byte(`{}`)),
289*e7b1675dSTing-Kang Chang		},
290*e7b1675dSTing-Kang Chang		{
291*e7b1675dSTing-Kang Chang			tag:    "type header not a string",
292*e7b1675dSTing-Kang Chang			header: base64Encode([]byte(`{"alg":"HS256", "typ":5}`)),
293*e7b1675dSTing-Kang Chang			alg:    "HS256",
294*e7b1675dSTing-Kang Chang		},
295*e7b1675dSTing-Kang Chang		{
296*e7b1675dSTing-Kang Chang			tag:    "wrong algorithm",
297*e7b1675dSTing-Kang Chang			header: base64Encode([]byte(`{"alg":"HS256"}`)),
298*e7b1675dSTing-Kang Chang			alg:    "HS512",
299*e7b1675dSTing-Kang Chang		},
300*e7b1675dSTing-Kang Chang		{
301*e7b1675dSTing-Kang Chang			tag:       "specyfing custom and tink kid",
302*e7b1675dSTing-Kang Chang			header:    base64Encode([]byte(`{"alg":"HS256", "kid":"tink"}`)),
303*e7b1675dSTing-Kang Chang			alg:       "HS256",
304*e7b1675dSTing-Kang Chang			tinkKID:   refString("tink"),
305*e7b1675dSTing-Kang Chang			customKID: refString("custom"),
306*e7b1675dSTing-Kang Chang		},
307*e7b1675dSTing-Kang Chang		{
308*e7b1675dSTing-Kang Chang			tag:       "invalid custom kid",
309*e7b1675dSTing-Kang Chang			header:    base64Encode([]byte(`{"alg":"HS256", "kid":"custom"}`)),
310*e7b1675dSTing-Kang Chang			customKID: refString("notCustom"),
311*e7b1675dSTing-Kang Chang			alg:       "HS256",
312*e7b1675dSTing-Kang Chang		},
313*e7b1675dSTing-Kang Chang		{
314*e7b1675dSTing-Kang Chang			tag:     "invalid tink kid",
315*e7b1675dSTing-Kang Chang			header:  base64Encode([]byte(`{"alg":"HS256", "kid":"tink"}`)),
316*e7b1675dSTing-Kang Chang			tinkKID: refString("notTink"),
317*e7b1675dSTing-Kang Chang			alg:     "HS256",
318*e7b1675dSTing-Kang Chang		},
319*e7b1675dSTing-Kang Chang		{
320*e7b1675dSTing-Kang Chang			tag:     "specify tink kid and token without kig",
321*e7b1675dSTing-Kang Chang			header:  base64Encode([]byte(`{"alg":"HS256"}`)),
322*e7b1675dSTing-Kang Chang			tinkKID: refString("notTink"),
323*e7b1675dSTing-Kang Chang			alg:     "HS256",
324*e7b1675dSTing-Kang Chang		},
325*e7b1675dSTing-Kang Chang		{
326*e7b1675dSTing-Kang Chang			tag:    "crit header",
327*e7b1675dSTing-Kang Chang			header: base64Encode([]byte(`{"alg":"HS256", "crit":"fooBar"}`)),
328*e7b1675dSTing-Kang Chang			alg:    "HS256",
329*e7b1675dSTing-Kang Chang		},
330*e7b1675dSTing-Kang Chang		{
331*e7b1675dSTing-Kang Chang			tag:    "no compact serialization",
332*e7b1675dSTing-Kang Chang			header: "asd.asd",
333*e7b1675dSTing-Kang Chang		},
334*e7b1675dSTing-Kang Chang		{
335*e7b1675dSTing-Kang Chang			tag:    "invalid UTF16 encoding",
336*e7b1675dSTing-Kang Chang			header: base64Encode([]byte(`{"alg":"HS256", "typ":"\uD834"}`)),
337*e7b1675dSTing-Kang Chang		},
338*e7b1675dSTing-Kang Chang	} {
339*e7b1675dSTing-Kang Chang		t.Run(tc.tag, func(t *testing.T) {
340*e7b1675dSTing-Kang Chang			if _, err := decodeUnsignedTokenAndValidateHeader(dotConcat(tc.header, base64Encode([]byte("{}"))), tc.alg, tc.tinkKID, tc.customKID); err == nil {
341*e7b1675dSTing-Kang Chang				t.Errorf("decodeUnsignedTokenAndValidateHeader() err = nil, want error")
342*e7b1675dSTing-Kang Chang			}
343*e7b1675dSTing-Kang Chang		})
344*e7b1675dSTing-Kang Chang	}
345*e7b1675dSTing-Kang Chang}
346*e7b1675dSTing-Kang Chang
347*e7b1675dSTing-Kang Changfunc TestDecodeValidateKIDHeader(t *testing.T) {
348*e7b1675dSTing-Kang Chang	type testCases struct {
349*e7b1675dSTing-Kang Chang		tag       string
350*e7b1675dSTing-Kang Chang		header    string
351*e7b1675dSTing-Kang Chang		tinkKID   *string
352*e7b1675dSTing-Kang Chang		customKID *string
353*e7b1675dSTing-Kang Chang	}
354*e7b1675dSTing-Kang Chang	for _, tc := range []testCases{
355*e7b1675dSTing-Kang Chang		{
356*e7b1675dSTing-Kang Chang			tag:    "not kid header field",
357*e7b1675dSTing-Kang Chang			header: base64Encode([]byte(`{"alg":"HS256"}`)),
358*e7b1675dSTing-Kang Chang		},
359*e7b1675dSTing-Kang Chang		{
360*e7b1675dSTing-Kang Chang			tag:       "validates custom kid",
361*e7b1675dSTing-Kang Chang			header:    base64Encode([]byte(`{"alg":"HS256", "kid":"custom"}`)),
362*e7b1675dSTing-Kang Chang			customKID: refString("custom"),
363*e7b1675dSTing-Kang Chang		},
364*e7b1675dSTing-Kang Chang		{
365*e7b1675dSTing-Kang Chang			tag:     "validates tink kid",
366*e7b1675dSTing-Kang Chang			header:  base64Encode([]byte(`{"alg":"HS256", "kid":"tink"}`)),
367*e7b1675dSTing-Kang Chang			tinkKID: refString("tink"),
368*e7b1675dSTing-Kang Chang		},
369*e7b1675dSTing-Kang Chang		{
370*e7b1675dSTing-Kang Chang			tag:    "ignores kid if exists and tink kid isn't specified",
371*e7b1675dSTing-Kang Chang			header: base64Encode([]byte(`{"alg":"HS256", "kid":"random"}`)),
372*e7b1675dSTing-Kang Chang		},
373*e7b1675dSTing-Kang Chang		{
374*e7b1675dSTing-Kang Chang			tag:    "unkown headers are accepted",
375*e7b1675dSTing-Kang Chang			header: base64Encode([]byte(`{"alg":"HS256","unknown":"header"}`)),
376*e7b1675dSTing-Kang Chang		},
377*e7b1675dSTing-Kang Chang	} {
378*e7b1675dSTing-Kang Chang		t.Run(tc.tag, func(t *testing.T) {
379*e7b1675dSTing-Kang Chang			_, err := decodeUnsignedTokenAndValidateHeader(dotConcat(tc.header, base64Encode([]byte("{}"))), "HS256", tc.tinkKID, tc.customKID)
380*e7b1675dSTing-Kang Chang			if err != nil {
381*e7b1675dSTing-Kang Chang				t.Errorf("decodeUnsignedTokenAndValidateHeader() err = %v, want nil", err)
382*e7b1675dSTing-Kang Chang			}
383*e7b1675dSTing-Kang Chang		})
384*e7b1675dSTing-Kang Chang	}
385*e7b1675dSTing-Kang Chang}
386*e7b1675dSTing-Kang Chang
387*e7b1675dSTing-Kang Changfunc TestDecodeVerifyTokenFixedValues(t *testing.T) {
388*e7b1675dSTing-Kang Chang	header := "eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9"                                                        // Header example from https://tools.ietf.org/html/rfc7519#section-3.1
389*e7b1675dSTing-Kang Chang	payload := "eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ" // Payload example from https://tools.ietf.org/html/rfc7519#section-3.1
390*e7b1675dSTing-Kang Chang	rawJWT, err := decodeUnsignedTokenAndValidateHeader(dotConcat(header, payload), "HS256", nil, nil)
391*e7b1675dSTing-Kang Chang	if err != nil {
392*e7b1675dSTing-Kang Chang		t.Errorf("decodeUnsignedTokenAndValidateHeader() err = %v, want nil", err)
393*e7b1675dSTing-Kang Chang	}
394*e7b1675dSTing-Kang Chang	iss, err := rawJWT.Issuer()
395*e7b1675dSTing-Kang Chang	if err != nil {
396*e7b1675dSTing-Kang Chang		t.Errorf("rawJWT.Issuer() err = %v, want nil", err)
397*e7b1675dSTing-Kang Chang	}
398*e7b1675dSTing-Kang Chang	if iss != "joe" {
399*e7b1675dSTing-Kang Chang		t.Errorf("rawJWT.Issuer() = %q, want joe", iss)
400*e7b1675dSTing-Kang Chang	}
401*e7b1675dSTing-Kang Chang	exp, err := rawJWT.ExpiresAt()
402*e7b1675dSTing-Kang Chang	if err != nil {
403*e7b1675dSTing-Kang Chang		t.Errorf("rawJWT.ExpiresAt() err = %v, want nil", err)
404*e7b1675dSTing-Kang Chang	}
405*e7b1675dSTing-Kang Chang	wantExp := time.Unix(1300819380, 0)
406*e7b1675dSTing-Kang Chang	if !exp.Equal(wantExp) {
407*e7b1675dSTing-Kang Chang		t.Errorf("rawJWT.ExpiresAt() = %q, want %q", exp, wantExp)
408*e7b1675dSTing-Kang Chang	}
409*e7b1675dSTing-Kang Chang	cc, err := rawJWT.BooleanClaim("http://example.com/is_root")
410*e7b1675dSTing-Kang Chang	if err != nil {
411*e7b1675dSTing-Kang Chang		t.Errorf("rawJWT.BooleanClaim('http://example.com/is_root') err = %v want nil", err)
412*e7b1675dSTing-Kang Chang	}
413*e7b1675dSTing-Kang Chang	if cc != true {
414*e7b1675dSTing-Kang Chang		t.Errorf("rawJWT.BooleanClaim('http://example.com/is_root') = %v, want true", cc)
415*e7b1675dSTing-Kang Chang	}
416*e7b1675dSTing-Kang Chang}
417*e7b1675dSTing-Kang Chang
418*e7b1675dSTing-Kang Changfunc TestDecodeVerifyTokenPaylodWithInvalidEndcoding(t *testing.T) {
419*e7b1675dSTing-Kang Chang	if _, err := decodeUnsignedTokenAndValidateHeader(dotConcat(base64Encode([]byte(`{"alg":"HS256"}`)), "_aSL&%"), "HS256", nil, nil); err == nil {
420*e7b1675dSTing-Kang Chang		t.Errorf("decodeUnsignedTokenAndValidateHeader() err = nil, want error")
421*e7b1675dSTing-Kang Chang	}
422*e7b1675dSTing-Kang Chang}
423