• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2021 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"""Cross-language tests for JWT validation.
15
16These tests test the non-cryptographic JWT validation. The tokens are MACed
17with the same key and the MAC is always valid. We test how the validation
18handles weird headers or payloads.
19"""
20
21import base64
22import datetime
23
24from absl.testing import absltest
25from absl.testing import parameterized
26import tink
27from tink import cleartext_keyset_handle
28from tink import jwt
29from tink import mac
30
31from tink.proto import common_pb2
32from tink.proto import hmac_pb2
33from tink.proto import jwt_hmac_pb2
34from tink.proto import tink_pb2
35from util import testing_servers
36
37SUPPORTED_LANGUAGES = testing_servers.SUPPORTED_LANGUAGES_BY_PRIMITIVE['jwt']
38
39# Example from https://tools.ietf.org/html/rfc7519#section-3.1
40EXAMPLE_TOKEN = ('eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9.'
41                 'eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQo'
42                 'gImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.'
43                 'dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk')
44KEY_VALUE = (b'AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-'
45             b'1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow==')
46KEYSET = None
47MAC = None
48EMPTY_VALIDATOR = jwt.new_validator(allow_missing_expiration=True)
49
50
51def _base64_encode(data: bytes) -> bytes:
52  """Does a URL-safe base64 encoding without padding."""
53  return base64.urlsafe_b64encode(data).rstrip(b'=')
54
55
56def _keyset() -> bytes:
57  jwt_hmac_key = jwt_hmac_pb2.JwtHmacKey(
58      version=0,
59      algorithm=jwt_hmac_pb2.HS256,
60      key_value=base64.urlsafe_b64decode(KEY_VALUE))
61  keyset = tink_pb2.Keyset()
62  key = keyset.key.add()
63  key.key_data.type_url = ('type.googleapis.com/google.crypto.tink.JwtHmacKey')
64  key.key_data.value = jwt_hmac_key.SerializeToString()
65  key.key_data.key_material_type = tink_pb2.KeyData.SYMMETRIC
66  key.status = tink_pb2.ENABLED
67  key.key_id = 123
68  key.output_prefix_type = tink_pb2.RAW
69  keyset.primary_key_id = 123
70  return keyset.SerializeToString()
71
72
73def _mac() -> mac.Mac:
74  hmac_key = hmac_pb2.HmacKey(
75      version=0, key_value=base64.urlsafe_b64decode(KEY_VALUE))
76  hmac_key.params.hash = common_pb2.SHA256
77  hmac_key.params.tag_size = 32
78  keyset = tink_pb2.Keyset()
79  key = keyset.key.add()
80  key.key_data.type_url = ('type.googleapis.com/google.crypto.tink.HmacKey')
81  key.key_data.value = hmac_key.SerializeToString()
82  key.key_data.key_material_type = tink_pb2.KeyData.SYMMETRIC
83  key.status = tink_pb2.ENABLED
84  key.key_id = 123
85  key.output_prefix_type = tink_pb2.RAW
86  keyset.primary_key_id = 123
87  keyset_handle = cleartext_keyset_handle.from_keyset(keyset)
88  return keyset_handle.primitive(mac.Mac)
89
90
91def setUpModule():
92  global KEYSET, MAC
93  jwt.register_jwt_mac()
94  mac.register()
95  testing_servers.start('jwt')
96  KEYSET = _keyset()
97  MAC = _mac()
98
99
100def tearDownModule():
101  testing_servers.stop()
102
103
104def generate_token_from_bytes(header: bytes, payload: bytes) -> str:
105  """Generates tokens from bytes with valid MACs."""
106  unsigned_compact = (_base64_encode(header) + b'.' + _base64_encode(payload))
107  mac_value = MAC.compute_mac(unsigned_compact)
108  return (unsigned_compact + b'.' + _base64_encode(mac_value)).decode('utf8')
109
110
111def generate_token(header: str, payload: str) -> str:
112  """Generates tokens with valid MACs."""
113  return generate_token_from_bytes(
114      header.encode('utf8'), payload.encode('utf8'))
115
116
117class JwtTest(parameterized.TestCase):
118
119  def test_genenerate_token_generates_example(self):
120    token = generate_token(
121        '{"typ":"JWT",\r\n "alg":"HS256"}',
122        '{"iss":"joe",\r\n "exp":1300819380,\r\n '
123        '"http://example.com/is_root":true}')
124    self.assertEqual(token, EXAMPLE_TOKEN)
125
126  @parameterized.parameters(SUPPORTED_LANGUAGES)
127  def test_verify_valid(self, lang):
128    token = generate_token('{"alg":"HS256"}', '{"jti":"123"}')
129    jwt_mac = testing_servers.remote_primitive(lang, KEYSET, jwt.JwtMac)
130    verified_jwt = jwt_mac.verify_mac_and_decode(token, EMPTY_VALIDATOR)
131    self.assertEqual(verified_jwt.jwt_id(), '123')
132
133  @parameterized.parameters(SUPPORTED_LANGUAGES)
134  def test_verify_unknown_header_valid(self, lang):
135    token = generate_token('{"alg":"HS256", "unknown":{"a":"b"}}',
136                           '{"jti":"123"}')
137    jwt_mac = testing_servers.remote_primitive(lang, KEYSET, jwt.JwtMac)
138    verified_jwt = jwt_mac.verify_mac_and_decode(token, EMPTY_VALIDATOR)
139    self.assertEqual(verified_jwt.jwt_id(), '123')
140
141  @parameterized.parameters(SUPPORTED_LANGUAGES)
142  def test_verify_empty_crit_header_invalid(self, lang):
143    # See https://tools.ietf.org/html/rfc7515#section-4.1.11
144    token = generate_token('{"alg":"HS256", "crit":[]}', '{"jti":"123"}')
145    jwt_mac = testing_servers.remote_primitive(lang, KEYSET, jwt.JwtMac)
146    with self.assertRaises(tink.TinkError):
147      jwt_mac.verify_mac_and_decode(token, EMPTY_VALIDATOR)
148
149  @parameterized.parameters(SUPPORTED_LANGUAGES)
150  def test_verify_nonempty_crit_header_invalid(self, lang):
151    # See https://tools.ietf.org/html/rfc7515#section-4.1.11
152    token = generate_token(
153        '{"alg":"HS256","crit":["http://example.invalid/UNDEFINED"],'
154        '"http://example.invalid/UNDEFINED":true}', '{"jti":"123"}')
155    jwt_mac = testing_servers.remote_primitive(lang, KEYSET, jwt.JwtMac)
156    with self.assertRaises(tink.TinkError):
157      jwt_mac.verify_mac_and_decode(token, EMPTY_VALIDATOR)
158
159  @parameterized.parameters(SUPPORTED_LANGUAGES)
160  def test_verify_typ_header(self, lang):
161    token = generate_token(
162        '{"typ":"typeHeader", "alg":"HS256"}', '{"jti":"123"}')
163    jwt_mac = testing_servers.remote_primitive(lang, KEYSET, jwt.JwtMac)
164
165    validator_with_correct_type_header = jwt.new_validator(
166        expected_type_header='typeHeader', allow_missing_expiration=True)
167    jwt_mac.verify_mac_and_decode(token, validator_with_correct_type_header)
168
169    validator_with_missing_type_header = jwt.new_validator(
170        allow_missing_expiration=True)
171    with self.assertRaises(tink.TinkError):
172      jwt_mac.verify_mac_and_decode(token, validator_with_missing_type_header)
173
174    validator_that_ignores_type_header = jwt.new_validator(
175        ignore_type_header=True, allow_missing_expiration=True)
176    jwt_mac.verify_mac_and_decode(token, validator_that_ignores_type_header)
177
178    validator_with_wrong_type_header = jwt.new_validator(
179        expected_type_header='typeHeader', allow_missing_expiration=True)
180    jwt_mac.verify_mac_and_decode(token, validator_with_wrong_type_header)
181
182  @parameterized.parameters(SUPPORTED_LANGUAGES)
183  def test_verify_expiration(self, lang):
184    token = generate_token('{"alg":"HS256"}', '{"jti":"123", "exp":1234}')
185    jwt_mac = testing_servers.remote_primitive(lang, KEYSET, jwt.JwtMac)
186
187    # same time is expired.
188    validator_with_same_time = jwt.new_validator(
189        fixed_now=datetime.datetime.fromtimestamp(1234, datetime.timezone.utc))
190    with self.assertRaises(tink.TinkError):
191      jwt_mac.verify_mac_and_decode(token, validator_with_same_time)
192
193    # a second before is fine
194    validator_before = jwt.new_validator(
195        fixed_now=datetime.datetime.fromtimestamp(1233,
196                                                  datetime.timezone.utc))
197    jwt_mac.verify_mac_and_decode(token, validator_before)
198
199    # 3 seconds too late with 3 seconds clock skew is expired.
200    validator_too_late_with_clockskew = jwt.new_validator(
201        fixed_now=datetime.datetime.fromtimestamp(1237, datetime.timezone.utc),
202        clock_skew=datetime.timedelta(seconds=3))
203    with self.assertRaises(tink.TinkError):
204      jwt_mac.verify_mac_and_decode(token, validator_too_late_with_clockskew)
205
206    # 2 seconds too late with 3 seconds clock skew is fine.
207    validator_still_ok_with_clockskew = jwt.new_validator(
208        fixed_now=datetime.datetime.fromtimestamp(1236, datetime.timezone.utc),
209        clock_skew=datetime.timedelta(seconds=3))
210    jwt_mac.verify_mac_and_decode(token, validator_still_ok_with_clockskew)
211
212  @parameterized.parameters(SUPPORTED_LANGUAGES)
213  def test_verify_float_expiration(self, lang):
214    token = generate_token('{"alg":"HS256"}', '{"jti":"123", "exp":1234.5}')
215    jwt_mac = testing_servers.remote_primitive(lang, KEYSET, jwt.JwtMac)
216
217    validate_after = jwt.new_validator(
218        fixed_now=datetime.datetime.fromtimestamp(1235.5,
219                                                  datetime.timezone.utc))
220    with self.assertRaises(tink.TinkError):
221      jwt_mac.verify_mac_and_decode(token, validate_after)
222
223    validate_before = jwt.new_validator(
224        fixed_now=datetime.datetime.fromtimestamp(1233.5,
225                                                  datetime.timezone.utc))
226    jwt_mac.verify_mac_and_decode(token, validate_before)
227
228  @parameterized.parameters(SUPPORTED_LANGUAGES)
229  def test_exp_expiration_is_fine(self, lang):
230    token = generate_token('{"alg":"HS256"}', '{"exp":1e10}')
231    jwt_mac = testing_servers.remote_primitive(lang, KEYSET, jwt.JwtMac)
232    jwt_mac.verify_mac_and_decode(token, EMPTY_VALIDATOR)
233
234  @parameterized.parameters(SUPPORTED_LANGUAGES)
235  def test_large_expiration_is_fine(self, lang):
236    token = generate_token('{"alg":"HS256"}', '{"exp":253402300799}')
237    jwt_mac = testing_servers.remote_primitive(lang, KEYSET, jwt.JwtMac)
238    jwt_mac.verify_mac_and_decode(token, EMPTY_VALIDATOR)
239
240  @parameterized.parameters(SUPPORTED_LANGUAGES)
241  def test_too_large_expiration_is_invalid(self, lang):
242    token = generate_token('{"alg":"HS256"}', '{"exp":253402300800}')
243    jwt_mac = testing_servers.remote_primitive(lang, KEYSET, jwt.JwtMac)
244    with self.assertRaises(tink.TinkError):
245      jwt_mac.verify_mac_and_decode(token, EMPTY_VALIDATOR)
246
247  @parameterized.parameters(SUPPORTED_LANGUAGES)
248  def test_way_too_large_expiration_is_invalid(self, lang):
249    token = generate_token('{"alg":"HS256"}', '{"exp":1e30}')
250    jwt_mac = testing_servers.remote_primitive(lang, KEYSET, jwt.JwtMac)
251    with self.assertRaises(tink.TinkError):
252      jwt_mac.verify_mac_and_decode(token, EMPTY_VALIDATOR)
253
254  @parameterized.parameters(SUPPORTED_LANGUAGES)
255  def test_infinity_expiration_is_invalid(self, lang):
256    token = generate_token('{"alg":"HS256"}', '{"jti":"123", "exp":Infinity}')
257    jwt_mac = testing_servers.remote_primitive(lang, KEYSET, jwt.JwtMac)
258
259    with self.assertRaises(tink.TinkError):
260      jwt_mac.verify_mac_and_decode(token, EMPTY_VALIDATOR)
261
262  @parameterized.parameters(SUPPORTED_LANGUAGES)
263  def test_verify_not_before(self, lang):
264    token = generate_token('{"alg":"HS256"}', '{"jti":"123", "nbf":1234}')
265    jwt_mac = testing_servers.remote_primitive(lang, KEYSET, jwt.JwtMac)
266
267    # same time as not-before fine.
268    validator_same_time = jwt.new_validator(
269        allow_missing_expiration=True,
270        fixed_now=datetime.datetime.fromtimestamp(1234, datetime.timezone.utc))
271    jwt_mac.verify_mac_and_decode(token, validator_same_time)
272
273    # one second before is not yet valid
274    validator_before = jwt.new_validator(
275        allow_missing_expiration=True,
276        fixed_now=datetime.datetime.fromtimestamp(1233, datetime.timezone.utc))
277    with self.assertRaises(tink.TinkError):
278      jwt_mac.verify_mac_and_decode(token, validator_before)
279
280    # 3 seconds too early with 3 seconds clock skew is fine
281    validator_ok_with_clockskew = jwt.new_validator(
282        allow_missing_expiration=True,
283        fixed_now=datetime.datetime.fromtimestamp(1231, datetime.timezone.utc),
284        clock_skew=datetime.timedelta(seconds=3))
285    jwt_mac.verify_mac_and_decode(token, validator_ok_with_clockskew)
286
287    # 3 seconds too early with 2 seconds clock skew is not yet valid.
288    validator_too_early_with_clockskew = jwt.new_validator(
289        allow_missing_expiration=True,
290        fixed_now=datetime.datetime.fromtimestamp(1231, datetime.timezone.utc),
291        clock_skew=datetime.timedelta(seconds=2))
292    with self.assertRaises(tink.TinkError):
293      jwt_mac.verify_mac_and_decode(token, validator_too_early_with_clockskew)
294
295  @parameterized.parameters(SUPPORTED_LANGUAGES)
296  def test_verify_float_not_before(self, lang):
297    token = generate_token('{"alg":"HS256"}', '{"jti":"123", "nbf":1234.5}')
298    jwt_mac = testing_servers.remote_primitive(lang, KEYSET, jwt.JwtMac)
299
300    validator_before = jwt.new_validator(
301        allow_missing_expiration=True,
302        fixed_now=datetime.datetime.fromtimestamp(1233.5,
303                                                  datetime.timezone.utc))
304    with self.assertRaises(tink.TinkError):
305      jwt_mac.verify_mac_and_decode(token, validator_before)
306
307    validator_after = jwt.new_validator(
308        allow_missing_expiration=True,
309        fixed_now=datetime.datetime.fromtimestamp(1235.5,
310                                                  datetime.timezone.utc))
311    jwt_mac.verify_mac_and_decode(token, validator_after)
312
313  @parameterized.parameters(SUPPORTED_LANGUAGES)
314  def test_verify_issued_at(self, lang):
315    token = generate_token('{"alg":"HS256"}', '{"jti":"123", "iat":1234}')
316    jwt_mac = testing_servers.remote_primitive(lang, KEYSET, jwt.JwtMac)
317
318    # same time as issued-at fine.
319    validator_same_time = jwt.new_validator(
320        expect_issued_in_the_past=True,
321        allow_missing_expiration=True,
322        fixed_now=datetime.datetime.fromtimestamp(1234, datetime.timezone.utc))
323    jwt_mac.verify_mac_and_decode(token, validator_same_time)
324
325    # one second before is not yet valid
326    validator_before = jwt.new_validator(
327        expect_issued_in_the_past=True,
328        allow_missing_expiration=True,
329        fixed_now=datetime.datetime.fromtimestamp(1233, datetime.timezone.utc))
330    with self.assertRaises(tink.TinkError):
331      jwt_mac.verify_mac_and_decode(token, validator_before)
332
333    # ten second before but without expect_issued_in_the_past is fine
334    validator_without_iat_validation = jwt.new_validator(
335        allow_missing_expiration=True,
336        fixed_now=datetime.datetime.fromtimestamp(1224, datetime.timezone.utc))
337    jwt_mac.verify_mac_and_decode(token, validator_without_iat_validation)
338
339    # 3 seconds too early with 3 seconds clock skew is fine
340    validator_ok_with_clockskew = jwt.new_validator(
341        expect_issued_in_the_past=True,
342        allow_missing_expiration=True,
343        fixed_now=datetime.datetime.fromtimestamp(1231, datetime.timezone.utc),
344        clock_skew=datetime.timedelta(seconds=3))
345    jwt_mac.verify_mac_and_decode(token, validator_ok_with_clockskew)
346
347    # 3 seconds too early with 2 seconds clock skew is not yet valid.
348    validator_too_early_with_clockskew = jwt.new_validator(
349        expect_issued_in_the_past=True,
350        allow_missing_expiration=True,
351        fixed_now=datetime.datetime.fromtimestamp(1231, datetime.timezone.utc),
352        clock_skew=datetime.timedelta(seconds=2))
353    with self.assertRaises(tink.TinkError):
354      jwt_mac.verify_mac_and_decode(token, validator_too_early_with_clockskew)
355
356  @parameterized.parameters(SUPPORTED_LANGUAGES)
357  def test_verify_issuer(self, lang):
358    token = generate_token('{"alg":"HS256"}', '{"iss":"joe"}')
359    jwt_mac = testing_servers.remote_primitive(lang, KEYSET, jwt.JwtMac)
360
361    validator_with_correct_issuer = jwt.new_validator(
362        expected_issuer='joe', allow_missing_expiration=True)
363    jwt_mac.verify_mac_and_decode(token, validator_with_correct_issuer)
364
365    validator_without_issuer = jwt.new_validator(allow_missing_expiration=True)
366    with self.assertRaises(tink.TinkError):
367      jwt_mac.verify_mac_and_decode(token, validator_without_issuer)
368
369    validator_that_ignores_issuer = jwt.new_validator(
370        ignore_issuer=True, allow_missing_expiration=True)
371    jwt_mac.verify_mac_and_decode(token, validator_that_ignores_issuer)
372
373    validator_with_wrong_issuer = jwt.new_validator(
374        expected_issuer='Joe', allow_missing_expiration=True)
375    with self.assertRaises(tink.TinkError):
376      jwt_mac.verify_mac_and_decode(token, validator_with_wrong_issuer)
377
378    val4 = jwt.new_validator(
379        expected_issuer='joe ', allow_missing_expiration=True)
380    with self.assertRaises(tink.TinkError):
381      jwt_mac.verify_mac_and_decode(token, val4)
382
383  @parameterized.parameters(SUPPORTED_LANGUAGES)
384  def test_duplicated_issuer(self, lang):
385    token = generate_token('{"alg":"HS256"}', '{"iss":"joe", "iss":"jane"}')
386    jwt_mac = testing_servers.remote_primitive(lang, KEYSET, jwt.JwtMac)
387
388    validator_with_second_issuer = jwt.new_validator(
389        ignore_issuer=True, allow_missing_expiration=True)
390    with self.assertRaises(tink.TinkError):
391      jwt_mac.verify_mac_and_decode(token, validator_with_second_issuer)
392
393  @parameterized.parameters(SUPPORTED_LANGUAGES)
394  def test_verify_empty_string_issuer(self, lang):
395    token = generate_token('{"alg":"HS256"}', '{"iss":""}')
396    jwt_mac = testing_servers.remote_primitive(lang, KEYSET, jwt.JwtMac)
397    jwt_mac.verify_mac_and_decode(
398        token,
399        jwt.new_validator(expected_issuer='', allow_missing_expiration=True))
400
401  @parameterized.parameters(SUPPORTED_LANGUAGES)
402  def test_verify_issuer_with_wrong_type(self, lang):
403    token = generate_token('{"alg":"HS256"}', '{"iss":123}')
404    jwt_mac = testing_servers.remote_primitive(lang, KEYSET, jwt.JwtMac)
405
406    with self.assertRaises(tink.TinkError):
407      jwt_mac.verify_mac_and_decode(token, EMPTY_VALIDATOR)
408
409  @parameterized.parameters(SUPPORTED_LANGUAGES)
410  def test_verify_invalid_utf8_in_header(self, lang):
411    token = generate_token_from_bytes(b'{"alg":"HS256", "a":"\xc2"}',
412                                      b'{"iss":"joe"}')
413    jwt_mac = testing_servers.remote_primitive(lang, KEYSET, jwt.JwtMac)
414    with self.assertRaises(tink.TinkError):
415      jwt_mac.verify_mac_and_decode(token, EMPTY_VALIDATOR)
416
417  @parameterized.parameters(SUPPORTED_LANGUAGES)
418  def test_verify_invalid_utf8_in_payload(self, lang):
419    token = generate_token_from_bytes(b'{"alg":"HS256"}', b'{"jti":"joe\xc2"}')
420    jwt_mac = testing_servers.remote_primitive(lang, KEYSET, jwt.JwtMac)
421    with self.assertRaises(tink.TinkError):
422      jwt_mac.verify_mac_and_decode(token, EMPTY_VALIDATOR)
423
424  @parameterized.parameters(SUPPORTED_LANGUAGES)
425  def test_verify_with_utf16_surrogate_in_payload(self, lang):
426    # The JSON string contains the G clef character (U+1D11E) in UTF8.
427    token = generate_token_from_bytes(b'{"alg":"HS256"}',
428                                      b'{"jti":"\xF0\x9D\x84\x9E"}')
429    jwt_mac = testing_servers.remote_primitive(lang, KEYSET, jwt.JwtMac)
430    token = jwt_mac.verify_mac_and_decode(token, EMPTY_VALIDATOR)
431    self.assertEqual(token.jwt_id(), u'\U0001d11e')
432
433  @parameterized.parameters(SUPPORTED_LANGUAGES)
434  def test_verify_with_json_escaped_utf16_surrogate_in_payload(self, lang):
435    # The JSON string contains "\uD834\uDD1E", which should decode to
436    # the G clef character (U+1D11E).
437    token = generate_token('{"alg":"HS256"}', '{"jti":"\\uD834\\uDD1E"}')
438    jwt_mac = testing_servers.remote_primitive(lang, KEYSET, jwt.JwtMac)
439    token = jwt_mac.verify_mac_and_decode(token, EMPTY_VALIDATOR)
440    self.assertEqual(token.jwt_id(), u'\U0001d11e')
441
442  @parameterized.parameters(SUPPORTED_LANGUAGES)
443  def test_verify_with_invalid_json_escaped_utf16_in_payload(self, lang):
444    # The JSON string contains "\uD834", which gets decoded into an invalid
445    # UTF16 character.
446    token = generate_token('{"alg":"HS256"}', '{"jti":"\\uD834"}')
447    jwt_mac = testing_servers.remote_primitive(lang, KEYSET, jwt.JwtMac)
448    with self.assertRaises(tink.TinkError):
449      jwt_mac.verify_mac_and_decode(token, EMPTY_VALIDATOR)
450
451  @parameterized.parameters(SUPPORTED_LANGUAGES)
452  def test_verify_with_invalid_json_escaped_utf16_in_claim_name(self, lang):
453    token = generate_token('{"alg":"HS256"}',
454                           '{"\\uD800\\uD800claim":"value"}')
455    jwt_mac = testing_servers.remote_primitive(lang, KEYSET, jwt.JwtMac)
456    with self.assertRaises(tink.TinkError):
457      jwt_mac.verify_mac_and_decode(token, EMPTY_VALIDATOR)
458
459  @parameterized.parameters(SUPPORTED_LANGUAGES)
460  def test_verify_audience(self, lang):
461    token = generate_token('{"alg":"HS256"}', '{"aud":["joe", "jane"]}')
462    jwt_mac = testing_servers.remote_primitive(lang, KEYSET, jwt.JwtMac)
463
464    validator_with_correct_audience = jwt.new_validator(
465        expected_audience='joe', allow_missing_expiration=True)
466    jwt_mac.verify_mac_and_decode(token, validator_with_correct_audience)
467
468    validator_with_correct_audience2 = jwt.new_validator(
469        expected_audience='jane', allow_missing_expiration=True)
470    jwt_mac.verify_mac_and_decode(token, validator_with_correct_audience2)
471
472    validator_without_audience = jwt.new_validator(
473        allow_missing_expiration=True)
474    with self.assertRaises(tink.TinkError):
475      jwt_mac.verify_mac_and_decode(token, validator_without_audience)
476
477    validator_that_ignores_audience = jwt.new_validator(
478        ignore_audiences=True, allow_missing_expiration=True)
479    jwt_mac.verify_mac_and_decode(token, validator_that_ignores_audience)
480
481    validator_with_wrong_audience = jwt.new_validator(
482        expected_audience='Joe', allow_missing_expiration=True)
483    with self.assertRaises(tink.TinkError):
484      jwt_mac.verify_mac_and_decode(token, validator_with_wrong_audience)
485
486    val5 = jwt.new_validator(
487        expected_audience='jane ', allow_missing_expiration=True)
488    with self.assertRaises(tink.TinkError):
489      jwt_mac.verify_mac_and_decode(token, val5)
490
491  @parameterized.parameters(SUPPORTED_LANGUAGES)
492  def test_verify_audience_string(self, lang):
493    token = generate_token('{"alg":"HS256"}', '{"aud":"joe"}')
494    jwt_mac = testing_servers.remote_primitive(lang, KEYSET, jwt.JwtMac)
495
496    val1 = jwt.new_validator(
497        expected_audience='joe', allow_missing_expiration=True)
498    jwt_mac.verify_mac_and_decode(token, val1)
499
500    val3 = EMPTY_VALIDATOR
501    with self.assertRaises(tink.TinkError):
502      jwt_mac.verify_mac_and_decode(token, val3)
503
504  @parameterized.parameters(SUPPORTED_LANGUAGES)
505  def test_verify_audiences_with_wrong_type(self, lang):
506    token = generate_token('{"alg":"HS256"}', '{"aud":["joe", 123]}')
507    jwt_mac = testing_servers.remote_primitive(lang, KEYSET, jwt.JwtMac)
508
509    with self.assertRaises(tink.TinkError):
510      jwt_mac.verify_mac_and_decode(token, EMPTY_VALIDATOR)
511
512  @parameterized.parameters(SUPPORTED_LANGUAGES)
513  def test_verify_token_with_empty_audiences(self, lang):
514    token = generate_token('{"alg":"HS256"}', '{"aud":[]}')
515    jwt_mac = testing_servers.remote_primitive(lang, KEYSET, jwt.JwtMac)
516
517    with self.assertRaises(tink.TinkError):
518      jwt_mac.verify_mac_and_decode(token, EMPTY_VALIDATOR)
519
520  @parameterized.parameters(SUPPORTED_LANGUAGES)
521  def test_verify_token_with_utf_16_encoded_payload_fails(self, lang):
522    token = generate_token_from_bytes('{"alg":"HS256"}'.encode('utf-8'),
523                                      '{"iss":"joe"}'.encode('utf-16'))
524    jwt_mac = testing_servers.remote_primitive(lang, KEYSET, jwt.JwtMac)
525
526    with self.assertRaises(tink.TinkError):
527      jwt_mac.verify_mac_and_decode(token, EMPTY_VALIDATOR)
528
529  @parameterized.parameters(SUPPORTED_LANGUAGES)
530  def test_verify_token_with_utf_32_encoded_payload_fails(self, lang):
531    token = generate_token_from_bytes('{"alg":"HS256"}'.encode('utf-8'),
532                                      '{"iss":"joe"}'.encode('utf-32'))
533    jwt_mac = testing_servers.remote_primitive(lang, KEYSET, jwt.JwtMac)
534
535    with self.assertRaises(tink.TinkError):
536      jwt_mac.verify_mac_and_decode(token, EMPTY_VALIDATOR)
537
538  @parameterized.parameters(SUPPORTED_LANGUAGES)
539  def test_verify_token_with_many_recursions(self, lang):
540    num_recursions = 10
541    payload = ('{"a":' * num_recursions) + '""' + ('}' * num_recursions)
542    token = generate_token('{"alg":"HS256"}', payload)
543    jwt_mac = testing_servers.remote_primitive(lang, KEYSET, jwt.JwtMac)
544    jwt_mac.verify_mac_and_decode(token, EMPTY_VALIDATOR)
545
546  @parameterized.parameters(SUPPORTED_LANGUAGES)
547  def test_verify_token_with_too_many_recursions_fails(self, lang):
548    # TODO(b/220810178): enable test for golang once depth limit is enabled.
549    if lang == 'go': return
550    # num_recursions has been chosen such that parsing of this token fails
551    # in all languages. We want to make sure that the algorithm does not
552    # hang or crash in this case, but only returns a parsing error.
553    num_recursions = 10000
554    payload = ('{"a":' * num_recursions) + '""' + ('}' * num_recursions)
555    token = generate_token('{"alg":"HS256"}', payload)
556    jwt_mac = testing_servers.remote_primitive(lang, KEYSET, jwt.JwtMac)
557    with self.assertRaises(tink.TinkError):
558      jwt_mac.verify_mac_and_decode(token, EMPTY_VALIDATOR)
559
560if __name__ == '__main__':
561  absltest.main()
562