xref: /aosp_15_r20/external/tink/go/jwt/jwt_mac_kid_test.go (revision e7b1675dde1b92d52ec075b0a92829627f2c52a5)
1// Copyright 2022 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//      http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14//
15////////////////////////////////////////////////////////////////////////////////
16
17package jwt
18
19import (
20	"encoding/base64"
21	"fmt"
22	"testing"
23	"time"
24
25	"github.com/google/tink/go/mac/subtle"
26)
27
28func TestNewMACwithNilMACFails(t *testing.T) {
29	if _, err := newMACWithKID(nil, "", nil); err == nil {
30		t.Errorf("NewMACWithKID(nil, '', nil) err = nil, want error")
31	}
32}
33
34func createMACwithKID(customKID *string) (*macWithKID, error) {
35	// https://datatracker.ietf.org/doc/html/rfc7515#appendix-A.1.1
36	key, err := base64.URLEncoding.WithPadding(base64.NoPadding).DecodeString("AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow")
37	if err != nil {
38		return nil, fmt.Errorf("failed parsing test key: %v", err)
39	}
40	mac, err := subtle.NewHMAC("SHA256", key, 32)
41	if err != nil {
42		return nil, err
43	}
44	return newMACWithKID(mac, "HS256", customKID)
45}
46
47func TestCreateAndValidateToken(t *testing.T) {
48	m, err := createMACwithKID(nil)
49	if err != nil {
50		t.Fatalf("creating JWTMACwithKID primitive: %v", err)
51	}
52	rawOpts := &RawJWTOptions{
53		TypeHeader:        refString("typeHeader"),
54		JWTID:             refString("123"),
55		WithoutExpiration: true,
56	}
57	rawJWT, err := NewRawJWT(rawOpts)
58	if err != nil {
59		t.Errorf("NewRawJWT() err = %v, want nil", err)
60	}
61	compact, err := m.ComputeMACAndEncodeWithKID(rawJWT, nil)
62	if err != nil {
63		t.Errorf("m.ComputeMACAndEncodeWithKID err = %v, want nil", err)
64	}
65	validatorOps := &ValidatorOpts{
66		ExpectedTypeHeader:     refString("typeHeader"),
67		AllowMissingExpiration: true,
68	}
69	validator, err := NewValidator(validatorOps)
70	if err != nil {
71		t.Errorf("NewValidator err = %v, want nil", err)
72	}
73	verifiedJWT, err := m.VerifyMACAndDecodeWithKID(compact, validator, nil)
74	if err != nil {
75		t.Errorf("m.VerifyMACAndDecodeWithKID() err = %v, want nil", err)
76	}
77	typeHeader, err := verifiedJWT.TypeHeader()
78	if err != nil {
79		t.Errorf("verifiedJWT.TypeHeader() err = %v, want nil", err)
80	}
81	if typeHeader != "typeHeader" {
82		t.Errorf("verifiedJWT.TypeHeader() = %q, want 'typeHeader'", typeHeader)
83	}
84	jwtID, err := verifiedJWT.JWTID()
85	if err != nil {
86		t.Errorf("verifiedJWT.JWTID() err = %v, want nil", err)
87	}
88	if jwtID != "123" {
89		t.Errorf("verifiedJWT.JWTID() = %q, want '123'", jwtID)
90	}
91
92	validatorOps = &ValidatorOpts{
93		ExpectedTypeHeader:     refString("notTypeHeader"),
94		AllowMissingExpiration: true,
95	}
96	validator, err = NewValidator(validatorOps)
97	if err != nil {
98		t.Errorf("NewValidator err = %v, want nil", err)
99	}
100	if _, err := m.VerifyMACAndDecodeWithKID(compact, validator, nil); err == nil {
101		t.Errorf("m.VerifyMACAndDecodeWithKID() err = nil, want error")
102	}
103}
104
105func TestCreateAndValidateTokenWithKID(t *testing.T) {
106	m, err := createMACwithKID(nil)
107	if err != nil {
108		t.Fatalf("creating JWTMACwithKID primitive: %v", err)
109	}
110	rawOpts := &RawJWTOptions{
111		TypeHeader:        refString("typeHeader"),
112		JWTID:             refString("123"),
113		WithoutExpiration: true,
114	}
115	rawJWT, err := NewRawJWT(rawOpts)
116	if err != nil {
117		t.Errorf("NewRawJWT() err = %v, want nil", err)
118	}
119	compact, err := m.ComputeMACAndEncodeWithKID(rawJWT, refString("kid-123"))
120	if err != nil {
121		t.Errorf("m.ComputeMACAndEncodeWithKID err = %v, want nil", err)
122	}
123	opts := &ValidatorOpts{
124		ExpectedTypeHeader:     refString("typeHeader"),
125		AllowMissingExpiration: true,
126	}
127	validator, err := NewValidator(opts)
128	if err != nil {
129		t.Fatalf("creating JWT validator, NewValidator: %v", err)
130	}
131	verifiedJWT, err := m.VerifyMACAndDecodeWithKID(compact, validator, refString("kid-123"))
132	if err != nil {
133		t.Errorf("m.VerifyMACAndDecodeWithKID(kid = kid-123) err = %v, want nil", err)
134	}
135
136	typeHeader, err := verifiedJWT.TypeHeader()
137	if err != nil {
138		t.Errorf("verifiedJWT.TypeHeader() err = %v, want nil", err)
139	}
140	if typeHeader != *rawOpts.TypeHeader {
141		t.Errorf("verifiedJWT.TypeHeader() = %q, want %q", typeHeader, *rawOpts.TypeHeader)
142	}
143	jwtID, err := verifiedJWT.JWTID()
144	if err != nil {
145		t.Errorf("verifiedJWT.JWTID() err = %v, want nil", err)
146	}
147	if jwtID != *rawOpts.JWTID {
148		t.Errorf("verifiedJWT.JWTID() = %q, want %q", jwtID, *rawOpts.JWTID)
149	}
150
151	if _, err := m.VerifyMACAndDecodeWithKID(compact, validator, nil); err != nil {
152		t.Errorf("m.VerifyMACAndDecodeWithKID(kid = nil) err = %v, want nil", err)
153	}
154	if _, err := m.VerifyMACAndDecodeWithKID(compact, validator, refString("other-kid")); err == nil {
155		t.Errorf("m.VerifyMACAndDecodeWithKID(kid = 'other-kid') err = nil, want error")
156	}
157}
158
159func TestValidateFixedToken(t *testing.T) {
160	// Key and Token are examples from: https://datatracker.ietf.org/doc/html/rfc7515#appendix-A.1.1
161	compact := "eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk"
162	m, err := createMACwithKID(nil)
163	if err != nil {
164		t.Fatalf("creating JWTMACwithKID primitive: %v", err)
165	}
166	opts := &ValidatorOpts{
167		ExpectedTypeHeader: refString("JWT"),
168		ExpectedIssuer:     refString("joe"),
169		FixedNow:           time.Unix(12345, 0),
170	}
171	pastValidator, err := NewValidator(opts)
172	if err != nil {
173		t.Fatalf("creating JWTValidator: %v", err)
174	}
175	// verification succeeds because token was valid valid on January 1, 1970 UTC.
176	verifiedJWT, err := m.VerifyMACAndDecodeWithKID(compact, pastValidator, nil)
177	if err != nil {
178		t.Fatalf("m.VerifyMACAndDecodeWithKID() err = %v, want nil", err)
179	}
180	typeHeader, err := verifiedJWT.TypeHeader()
181	if err != nil {
182		t.Errorf("verifiedJWT.TypeHeader() err = %v, want nil", err)
183	}
184	if typeHeader != *opts.ExpectedTypeHeader {
185		t.Errorf("verifiedJWT.TypeHeader() = %q, want %q", typeHeader, *opts.ExpectedTypeHeader)
186	}
187	issuer, err := verifiedJWT.Issuer()
188	if err != nil {
189		t.Errorf("verifiedJWT.Issuer() err = %v, want nil", err)
190	}
191	if issuer != *opts.ExpectedIssuer {
192		t.Errorf("verifiedJWT.Issuer() = %q, want %q", issuer, *opts.ExpectedIssuer)
193	}
194	boolClaim, err := verifiedJWT.BooleanClaim("http://example.com/is_root")
195	if err != nil {
196		t.Errorf("verifiedJWT.BooleanClaim('http://example.com/is_root') err = %v, want nil", err)
197	}
198	if boolClaim != true {
199		t.Errorf("verifiedJWT.BooleanClaim('http://example.com/is_root') = %q, want true", issuer)
200	}
201
202	// expired token fails verification
203	opts.FixedNow = time.Now()
204	presentValidator, err := NewValidator(opts)
205	if err != nil {
206		t.Fatalf("creating JWTValidator: %v", err)
207	}
208	_, err = m.VerifyMACAndDecodeWithKID(compact, presentValidator, nil)
209	if err == nil {
210		t.Fatalf("m.VerifyMACAndDecodeWithKID() with expired token err = nil, want error")
211	}
212	if !IsExpirationErr(err) {
213		t.Fatalf("IsExpirationErr(err) for err = %q is false, want true", err)
214	}
215
216	// tampered token verification fails
217	tamperedCompact := "eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXx"
218	if _, err := m.VerifyMACAndDecodeWithKID(tamperedCompact, pastValidator, nil); err == nil {
219		t.Fatalf("m.VerifyMACAndDecodeWithKID() with expired tampered token err = nil, want error")
220	}
221}
222
223func TestInvalidTokens(t *testing.T) {
224	m, err := createMACwithKID(nil)
225	if err != nil {
226		t.Fatalf("creating JWTMACwithKID primitive: %v", err)
227	}
228	validator, err := NewValidator(&ValidatorOpts{})
229	if err != nil {
230		t.Fatalf("creating JWTValidator: %v", err)
231	}
232	for _, compact := range []string{
233		"eyJhbGciOiJIUzI1NiJ9.e30.abc.",
234		"eyJhbGciOiJIUzI1NiJ9?.e30.abc",
235		"eyJhbGciOiJIUzI1NiJ9.e30?.abc",
236		"eyJhbGciOiJIUzI1NiJ9.e30.abc?",
237		"eyJhbGciOiJIUzI1NiJ9.e30",
238		"eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9.eyJpc3MiOi&Jqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk",
239	} {
240		if _, err := m.VerifyMACAndDecodeWithKID(compact, validator, nil); err == nil {
241			t.Errorf("m.VerifyMACAndDecodeWithKID(%q) err = nil, want error", compact)
242		}
243	}
244}
245
246func TestCustomKIDAndTinkPrefixKeyFail(t *testing.T) {
247	m, err := createMACwithKID(refString("custom-kid"))
248	if err != nil {
249		t.Fatalf("creating JWTMACwithKID primitive: %v", err)
250	}
251	rawJWT, err := NewRawJWT(&RawJWTOptions{WithoutExpiration: true})
252	if err != nil {
253		t.Fatalf("NewRawJWT() err = %v, want nil", err)
254	}
255	if _, err := m.ComputeMACAndEncodeWithKID(rawJWT, refString("tink-kid")); err == nil {
256		t.Errorf("specifying kid when primitive contains kid to ComputeMACAndEncodeWithKID() err = nil, want error")
257	}
258}
259