xref: /aosp_15_r20/external/tink/go/jwt/jwt_mac_factory_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_test
18
19import (
20	"fmt"
21	"testing"
22
23	"google.golang.org/protobuf/proto"
24	"github.com/google/tink/go/jwt"
25	"github.com/google/tink/go/keyset"
26	"github.com/google/tink/go/signature"
27	"github.com/google/tink/go/subtle/random"
28	"github.com/google/tink/go/testkeyset"
29	"github.com/google/tink/go/testutil"
30
31	jwtmacpb "github.com/google/tink/go/proto/jwt_hmac_go_proto"
32	tinkpb "github.com/google/tink/go/proto/tink_go_proto"
33)
34
35func newJWTHMACKey(algorithm jwtmacpb.JwtHmacAlgorithm, kid *jwtmacpb.JwtHmacKey_CustomKid) *jwtmacpb.JwtHmacKey {
36	return &jwtmacpb.JwtHmacKey{
37		Version:   0,
38		Algorithm: algorithm,
39		KeyValue:  random.GetRandomBytes(32),
40		CustomKid: kid,
41	}
42}
43
44func newKeyData(key *jwtmacpb.JwtHmacKey) (*tinkpb.KeyData, error) {
45	serializedKey, err := proto.Marshal(key)
46	if err != nil {
47		return nil, err
48	}
49	return &tinkpb.KeyData{
50		TypeUrl:         "type.googleapis.com/google.crypto.tink.JwtHmacKey",
51		Value:           serializedKey,
52		KeyMaterialType: tinkpb.KeyData_SYMMETRIC,
53	}, nil
54}
55
56func createJWTMAC(keyData *tinkpb.KeyData, prefixType tinkpb.OutputPrefixType) (jwt.MAC, error) {
57	handle, err := testkeyset.NewHandle(testutil.NewTestKeyset(keyData, prefixType))
58	if err != nil {
59		return nil, fmt.Errorf("creating keyset handle: %v", err)
60	}
61	return jwt.NewMAC(handle)
62}
63
64func verifyMACCompareSubject(p jwt.MAC, compact string, validator *jwt.Validator, wantSubject string) error {
65	verifiedJWT, err := p.VerifyMACAndDecode(compact, validator)
66	if err != nil {
67		return fmt.Errorf("p.VerifyMACAndDecode() err = %v, want nil", err)
68	}
69	subject, err := verifiedJWT.Subject()
70	if err != nil {
71		return fmt.Errorf("verifiedJWT.Subject() err = %v, want nil", err)
72	}
73	if subject != wantSubject {
74		return fmt.Errorf("verifiedJWT.Subject() = %q, want %q", subject, wantSubject)
75	}
76	return nil
77}
78
79func TestNilKeyHandle(t *testing.T) {
80	if _, err := jwt.NewMAC(nil); err == nil {
81		t.Errorf("TestNilKeyHandle(nil) err = nil, want error")
82	}
83}
84
85func TestFactorySameKeyMaterialWithRawPrefixAndNoKIDShouldIgnoreHeader(t *testing.T) {
86	keyData, err := newKeyData(newJWTHMACKey(jwtmacpb.JwtHmacAlgorithm_HS256, nil))
87	if err != nil {
88		t.Fatalf("creating NewKeyData: %v", err)
89	}
90	p, err := createJWTMAC(keyData, tinkpb.OutputPrefixType_TINK)
91	if err != nil {
92		t.Fatalf("creating New JWT MAC: %v", err)
93	}
94
95	rawJWT, err := jwt.NewRawJWT(&jwt.RawJWTOptions{WithoutExpiration: true, Subject: refString("tink-subject")})
96	if err != nil {
97		t.Errorf("jwt.NewRawJWT() err = %v, want nil", err)
98	}
99	validator, err := jwt.NewValidator(&jwt.ValidatorOpts{AllowMissingExpiration: true})
100	if err != nil {
101		t.Errorf("jwt.NewValidator() err = %v, want nil", err)
102	}
103	compact, err := p.ComputeMACAndEncode(rawJWT)
104	if err != nil {
105		t.Errorf("p.ComputeMACAndEncode() err = %v, want nil", err)
106	}
107	if err := verifyMACCompareSubject(p, compact, validator, "tink-subject"); err != nil {
108		t.Error(err)
109	}
110	p, err = createJWTMAC(keyData, tinkpb.OutputPrefixType_RAW)
111	if err != nil {
112		t.Fatalf("creating New JWT MAC: %v", err)
113	}
114	if _, err := p.VerifyMACAndDecode(compact, validator); err != nil {
115		t.Errorf("VerifyMACAndDecode() with a RAW key err = %v, want nil", err)
116	}
117}
118
119func TestFactorySameKeyMaterialWithDifferentPrefixAndKIDShouldFailVerification(t *testing.T) {
120	key := newJWTHMACKey(jwtmacpb.JwtHmacAlgorithm_HS256, nil)
121	keyData, err := newKeyData(key)
122	if err != nil {
123		t.Fatalf("creating NewKeyData: %v", err)
124	}
125	p, err := createJWTMAC(keyData, tinkpb.OutputPrefixType_TINK)
126	if err != nil {
127		t.Fatalf("creating New JWT MAC: %v", err)
128	}
129
130	rawJWT, err := jwt.NewRawJWT(&jwt.RawJWTOptions{WithoutExpiration: true, Subject: refString("tink-subject")})
131	if err != nil {
132		t.Errorf("jwt.NewRawJWT() err = %v, want nil", err)
133	}
134	validator, err := jwt.NewValidator(&jwt.ValidatorOpts{AllowMissingExpiration: true})
135	if err != nil {
136		t.Errorf("jwt.NewValidator() err = %v, want nil", err)
137	}
138	compact, err := p.ComputeMACAndEncode(rawJWT)
139	if err != nil {
140		t.Errorf("p.ComputeMACAndEncode() err = %v, want nil", err)
141	}
142	if err := verifyMACCompareSubject(p, compact, validator, "tink-subject"); err != nil {
143		t.Error(err)
144	}
145	key.CustomKid = &jwtmacpb.JwtHmacKey_CustomKid{
146		Value: "custom-kid",
147	}
148	rawKeyData, err := newKeyData(key)
149	if err != nil {
150		t.Fatalf("creating NewKeyData: %v", err)
151	}
152	p, err = createJWTMAC(rawKeyData, tinkpb.OutputPrefixType_RAW)
153	if err != nil {
154		t.Fatalf("creating New JWT MAC: %v", err)
155	}
156	if _, err := p.VerifyMACAndDecode(compact, validator); err == nil {
157		t.Errorf("VerifyMACAndDecode() with a different KID = nil, want error")
158	}
159}
160
161func TestFactoryDifferentKeyShouldFailValidation(t *testing.T) {
162	keyData, err := newKeyData(newJWTHMACKey(jwtmacpb.JwtHmacAlgorithm_HS256, nil))
163	if err != nil {
164		t.Fatalf("creating NewKeyData: %v", err)
165	}
166	p, err := createJWTMAC(keyData, tinkpb.OutputPrefixType_TINK)
167	if err != nil {
168		t.Fatalf("creating New JWT MAC: %v", err)
169	}
170
171	rawJWT, err := jwt.NewRawJWT(&jwt.RawJWTOptions{WithoutExpiration: true, Subject: refString("tink-subject")})
172	if err != nil {
173		t.Errorf("jwt.NewRawJWT() err = %v, want nil", err)
174	}
175	validator, err := jwt.NewValidator(&jwt.ValidatorOpts{AllowMissingExpiration: true})
176	if err != nil {
177		t.Errorf("jwt.NewValidator() err = %v, want nil", err)
178	}
179	compact, err := p.ComputeMACAndEncode(rawJWT)
180	if err != nil {
181		t.Errorf("p.ComputeMACAndEncode() err = %v, want nil", err)
182	}
183	if err := verifyMACCompareSubject(p, compact, validator, "tink-subject"); err != nil {
184		t.Error(err)
185	}
186	diffKey := newJWTHMACKey(jwtmacpb.JwtHmacAlgorithm_HS256, nil)
187	diffKeyData, err := newKeyData(diffKey)
188	if err != nil {
189		t.Fatalf("creating NewKeyData: %v", err)
190	}
191	p, err = createJWTMAC(diffKeyData, tinkpb.OutputPrefixType_TINK)
192	if err != nil {
193		t.Fatalf("creating New JWT MAC: %v", err)
194	}
195	if _, err := p.VerifyMACAndDecode(compact, validator); err == nil {
196		t.Errorf("VerifyMACAndDecode() with a different key = nil, want error")
197	}
198}
199
200func TestFactoryWithRAWKeyAndKID(t *testing.T) {
201	key := newJWTHMACKey(jwtmacpb.JwtHmacAlgorithm_HS256, &jwtmacpb.JwtHmacKey_CustomKid{Value: "custom-123"})
202	keyData, err := newKeyData(key)
203	if err != nil {
204		t.Fatalf("creating NewKeyData: %v", err)
205	}
206	ks := testutil.NewTestKeyset(keyData, tinkpb.OutputPrefixType_RAW)
207	handle, err := testkeyset.NewHandle(ks)
208	if err != nil {
209		t.Fatalf("creating keyset handle: %v", err)
210	}
211	p, err := jwt.NewMAC(handle)
212	if err != nil {
213		t.Fatalf("creating New JWT MAC: %v", err)
214	}
215	rawJWT, err := jwt.NewRawJWT(&jwt.RawJWTOptions{WithoutExpiration: true, Subject: refString("tink-subject")})
216	if err != nil {
217		t.Errorf("NewRawJWT() err = %v, want nil", err)
218	}
219
220	compact, err := p.ComputeMACAndEncode(rawJWT)
221	if err != nil {
222		t.Errorf("p.ComputeMACAndEncode() err = %v, want nil", err)
223	}
224	validator, err := jwt.NewValidator(&jwt.ValidatorOpts{AllowMissingExpiration: true})
225	if err != nil {
226		t.Errorf("NewValidator() err = %v, want nil", err)
227	}
228	if _, err := p.VerifyMACAndDecode(compact, validator); err != nil {
229		t.Errorf("p.VerifyMACAndDecode() err = %v, want nil", err)
230	}
231}
232
233func TestFactoryWithInvalidPrimitiveSetType(t *testing.T) {
234	kh, err := keyset.NewHandle(signature.ECDSAP256KeyTemplate())
235	if err != nil {
236		t.Fatalf("failed to build *keyset.Handle: %s", err)
237	}
238	if _, err = jwt.NewMAC(kh); err == nil {
239		t.Fatal("calling NewMAC() err = nil, want error")
240	}
241}
242
243func TestVerifyMACAndDecodeReturnsValidationError(t *testing.T) {
244	keyData, err := newKeyData(newJWTHMACKey(jwtmacpb.JwtHmacAlgorithm_HS256, nil))
245	if err != nil {
246		t.Fatalf("creating NewKeyData: %v", err)
247	}
248	p, err := createJWTMAC(keyData, tinkpb.OutputPrefixType_TINK)
249	if err != nil {
250		t.Fatalf("creating New JWT MAC: %v", err)
251	}
252
253	audience := "audience"
254	rawJWT, err := jwt.NewRawJWT(&jwt.RawJWTOptions{Audience: &audience, WithoutExpiration: true})
255	if err != nil {
256		t.Fatalf("jwt.NewRawJWT() err = %v, want nil", err)
257	}
258	token, err := p.ComputeMACAndEncode(rawJWT)
259	if err != nil {
260		t.Errorf("p.ComputeMACAndEncode() err = %v, want nil", err)
261	}
262
263	otherAudience := "otherAudience"
264	validator, err := jwt.NewValidator(
265		&jwt.ValidatorOpts{ExpectedAudience: &otherAudience, AllowMissingExpiration: true})
266	if err != nil {
267		t.Fatalf("jwt.NewValidator() err = %v, want nil", err)
268	}
269
270	_, err = p.VerifyMACAndDecode(token, validator)
271	wantErr := "validating audience claim: otherAudience not found"
272	if err == nil {
273		t.Errorf("p.VerifyMACAndDecode() err = nil, want %q", wantErr)
274	}
275	if err.Error() != wantErr {
276		t.Errorf("p.VerifyMACAndDecode() err = %q, want %q", err.Error(), wantErr)
277	}
278}
279