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