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