def test_jwt_mac_sets_custom_kid_for_raw_keys(self, template_name): keyset = generate_jwt_mac_keyset_with_custom_kid( template_name=template_name, custom_kid='my kid') raw_jwt = jwt.new_raw_jwt(without_expiration=True) for lang in SUPPORTED_LANGUAGES: jwt_mac = testing_servers.jwt_mac(lang, keyset.SerializeToString()) compact = jwt_mac.compute_mac_and_encode(raw_jwt) self.assertEqual(decode_kid(compact), 'my kid')
def test_jwt_mac_does_not_sets_kid_for_raw_templates(self, template_name): key_template = supported_key_types.KEY_TEMPLATE[template_name] keyset = testing_servers.new_keyset('cc', key_template) raw_jwt = jwt.new_raw_jwt(without_expiration=True) for lang in SUPPORTED_LANGUAGES: jwt_mac = testing_servers.jwt_mac(lang, keyset) compact = jwt_mac.compute_mac_and_encode(raw_jwt) self.assertIsNone(decode_kid(compact))
def test_verify_nonempty_crit_header_invalid(self, lang): # See https://tools.ietf.org/html/rfc7515#section-4.1.11 token = generate_token( '{"alg":"HS256","crit":["http://example.invalid/UNDEFINED"],' '"http://example.invalid/UNDEFINED":true}', '{"jti":"123"}') jwt_mac = testing_servers.jwt_mac(lang, KEYSET) with self.assertRaises(tink.TinkError): jwt_mac.verify_mac_and_decode(token, EMPTY_VALIDATOR)
def test_verify_token_with_too_many_recursions_fails(self, lang): # num_recursions has been chosen such that parsing of this token fails # in all languages. We want to make sure that the algorithm does not # hang or crash in this case, but only returns a parsing error. num_recursions = 10000 payload = ('{"a":' * num_recursions) + '""' + ('}' * num_recursions) token = generate_token('{"alg":"HS256"}', payload) jwt_mac = testing_servers.jwt_mac(lang, KEYSET) with self.assertRaises(tink.TinkError): jwt_mac.verify_mac_and_decode(token, EMPTY_VALIDATOR)
def test_verify_audience_string(self, lang): token = generate_token('{"alg":"HS256"}', '{"aud":"joe"}') jwt_mac = testing_servers.jwt_mac(lang, KEYSET) val1 = jwt.new_validator(audience='joe') jwt_mac.verify_mac_and_decode(token, val1) val3 = EMPTY_VALIDATOR with self.assertRaises(tink.TinkError): jwt_mac.verify_mac_and_decode(token, val3)
def test_jwt_mac_without_primary(self, key_template_name, lang): """Unsets the primary key and tries to create and verify JWT MACs.""" template = supported_key_types.KEY_TEMPLATE[key_template_name] keyset = testing_servers.new_keyset(lang, template) jwt_mac = testing_servers.jwt_mac(lang, keyset) now = datetime.datetime.now(tz=datetime.timezone.utc) raw_jwt = jwt.new_raw_jwt(issuer='issuer', expiration=now + datetime.timedelta(seconds=100)) token = jwt_mac.compute_mac_and_encode(raw_jwt) jwt_mac_without_primary = testing_servers.jwt_mac( lang, unset_primary(keyset)) with self.assertRaises(tink.TinkError): jwt_mac_without_primary.compute_mac_and_encode(raw_jwt) validator = jwt.new_validator(expected_issuer='issuer', fixed_now=now) with self.assertRaises(tink.TinkError): jwt_mac_without_primary.verify_mac_and_decode(token, validator)
def test_jwt_mac_fails_for_tink_keys_with_custom_kid(self, template_name): keyset = generate_jwt_mac_keyset_with_custom_kid( template_name=template_name, custom_kid='my kid') raw_jwt = jwt.new_raw_jwt(without_expiration=True) for lang in SUPPORTED_LANGUAGES: with self.assertRaises( tink.TinkError, msg=('%s supports JWT mac keys with TINK output prefix type ' 'and custom_kid set unexpectedly') % lang): jwt_mac = testing_servers.jwt_mac(lang, keyset.SerializeToString()) jwt_mac.compute_mac_and_encode(raw_jwt)
def test_verify_float_not_before(self, lang): token = generate_token('{"alg":"HS256"}', '{"iss":"joe", "nbf":1234.5}') jwt_mac = testing_servers.jwt_mac(lang, KEYSET) val1 = jwt.new_validator( fixed_now=datetime.datetime.fromtimestamp(1234, datetime.timezone.utc)) with self.assertRaises(tink.TinkError): jwt_mac.verify_mac_and_decode(token, val1) val2 = jwt.new_validator( fixed_now=datetime.datetime.fromtimestamp(1235, datetime.timezone.utc)) jwt_mac.verify_mac_and_decode(token, val2)
def test_compute_verify_jwt_mac(self, key_template_name): supported_langs = supported_key_types.SUPPORTED_LANGUAGES_BY_TEMPLATE_NAME[ key_template_name] self.assertNotEmpty(supported_langs) key_template = supported_key_types.KEY_TEMPLATE[key_template_name] # Take the first supported language to generate the keyset. keyset = testing_servers.new_keyset(supported_langs[0], key_template) supported_jwt_macs = [ testing_servers.jwt_mac(lang, keyset) for lang in supported_langs ] unsupported_jwt_macs = [ testing_servers.jwt_mac(lang, keyset) for lang in SUPPORTED_LANGUAGES if lang not in supported_langs ] now = datetime.datetime.now(tz=datetime.timezone.utc) raw_jwt = jwt.new_raw_jwt(issuer='issuer', expiration=now + datetime.timedelta(seconds=100)) for p in supported_jwt_macs: compact = p.compute_mac_and_encode(raw_jwt) validator = jwt.new_validator(expected_issuer='issuer', fixed_now=now) for p2 in supported_jwt_macs: verified_jwt = p2.verify_mac_and_decode(compact, validator) self.assertEqual(verified_jwt.issuer(), 'issuer') for p2 in unsupported_jwt_macs: with self.assertRaises( tink.TinkError, msg= '%s supports verify_mac_and_decode with %s unexpectedly' % (p2.lang, key_template_name)): p2.verify_mac_and_decode(compact, validator) for p in unsupported_jwt_macs: with self.assertRaises( tink.TinkError, msg= '%s supports compute_mac_and_encode with %s unexpectedly' % (p.lang, key_template_name)): p.compute_mac_and_encode(raw_jwt)
def test_verify_float_expiration(self, lang): token = generate_token('{"alg":"HS256"}', '{"jti":"123", "exp":1234.5}') jwt_mac = testing_servers.jwt_mac(lang, KEYSET) validate_after = jwt.new_validator( fixed_now=datetime.datetime.fromtimestamp(1235.5, datetime.timezone.utc)) with self.assertRaises(tink.TinkError): jwt_mac.verify_mac_and_decode(token, validate_after) validate_before = jwt.new_validator( fixed_now=datetime.datetime.fromtimestamp(1233.5, datetime.timezone.utc)) jwt_mac.verify_mac_and_decode(token, validate_before)
def test_verify_issued_at(self, lang): token = generate_token('{"alg":"HS256"}', '{"jti":"123", "iat":1234}') jwt_mac = testing_servers.jwt_mac(lang, KEYSET) # same time as issued-at fine. validator_same_time = jwt.new_validator( expect_issued_in_the_past=True, allow_missing_expiration=True, fixed_now=datetime.datetime.fromtimestamp(1234, datetime.timezone.utc)) jwt_mac.verify_mac_and_decode(token, validator_same_time) # one second before is not yet valid validator_before = jwt.new_validator( expect_issued_in_the_past=True, allow_missing_expiration=True, fixed_now=datetime.datetime.fromtimestamp(1233, datetime.timezone.utc)) with self.assertRaises(tink.TinkError): jwt_mac.verify_mac_and_decode(token, validator_before) # ten second before but without expect_issued_in_the_past is fine validator_without_iat_validation = jwt.new_validator( allow_missing_expiration=True, fixed_now=datetime.datetime.fromtimestamp(1224, datetime.timezone.utc)) jwt_mac.verify_mac_and_decode(token, validator_without_iat_validation) # 3 seconds too early with 3 seconds clock skew is fine validator_ok_with_clockskew = jwt.new_validator( expect_issued_in_the_past=True, allow_missing_expiration=True, fixed_now=datetime.datetime.fromtimestamp(1231, datetime.timezone.utc), clock_skew=datetime.timedelta(seconds=3)) jwt_mac.verify_mac_and_decode(token, validator_ok_with_clockskew) # 3 seconds too early with 2 seconds clock skew is not yet valid. validator_too_early_with_clockskew = jwt.new_validator( expect_issued_in_the_past=True, allow_missing_expiration=True, fixed_now=datetime.datetime.fromtimestamp(1231, datetime.timezone.utc), clock_skew=datetime.timedelta(seconds=2)) with self.assertRaises(tink.TinkError): jwt_mac.verify_mac_and_decode(token, validator_too_early_with_clockskew)
def test_verify_subject(self, lang): token = generate_token('{"alg":"HS256"}', '{"sub":"joe"}') jwt_mac = testing_servers.jwt_mac(lang, KEYSET) val1 = jwt.new_validator(subject='joe') jwt_mac.verify_mac_and_decode(token, val1) val2 = EMPTY_VALIDATOR jwt_mac.verify_mac_and_decode(token, val2) val3 = jwt.new_validator(subject='Joe') with self.assertRaises(tink.TinkError): jwt_mac.verify_mac_and_decode(token, val3) val4 = jwt.new_validator(subject='joe ') with self.assertRaises(tink.TinkError): jwt_mac.verify_mac_and_decode(token, val4)
def test_verify_typ_header(self, lang): token = generate_token( '{"typ":"typeHeader", "alg":"HS256"}', '{"jti":"123"}') jwt_mac = testing_servers.jwt_mac(lang, KEYSET) validator_with_correct_type_header = jwt.new_validator( expected_type_header='typeHeader', allow_missing_expiration=True) jwt_mac.verify_mac_and_decode(token, validator_with_correct_type_header) validator_with_missing_type_header = jwt.new_validator( allow_missing_expiration=True) with self.assertRaises(tink.TinkError): jwt_mac.verify_mac_and_decode(token, validator_with_missing_type_header) validator_that_ignores_type_header = jwt.new_validator( ignore_type_header=True, allow_missing_expiration=True) jwt_mac.verify_mac_and_decode(token, validator_that_ignores_type_header) validator_with_wrong_type_header = jwt.new_validator( expected_type_header='typeHeader', allow_missing_expiration=True) jwt_mac.verify_mac_and_decode(token, validator_with_wrong_type_header)
def test_verify_float_expiration(self, lang): token = generate_token('{"alg":"HS256"}', '{"iss":"joe", "exp":1234.5}') jwt_mac = testing_servers.jwt_mac(lang, KEYSET) # same time is expired. val1 = jwt.new_validator(fixed_now=datetime.datetime.fromtimestamp( 1234.5, datetime.timezone.utc)) with self.assertRaises(tink.TinkError): jwt_mac.verify_mac_and_decode(token, val1) # a fraction of a second after is expired. val1 = jwt.new_validator(fixed_now=datetime.datetime.fromtimestamp( 1234.75, datetime.timezone.utc)) with self.assertRaises(tink.TinkError): jwt_mac.verify_mac_and_decode(token, val1) # a fraction of a second before is fine val2 = jwt.new_validator(fixed_now=datetime.datetime.fromtimestamp( 1234.25, datetime.timezone.utc)) jwt_mac.verify_mac_and_decode(token, val2)
def test_jwt_mac(self, lang): key_format = jwt_hmac_pb2.JwtHmacKeyFormat(hash_type=common_pb2.SHA256, key_size=32) key_template = tink_pb2.KeyTemplate( type_url='type.googleapis.com/google.crypto.tink.JwtHmacKey', value=key_format.SerializeToString(), output_prefix_type=tink_pb2.RAW) keyset = testing_servers.new_keyset(lang, key_template) jwt_mac_primitive = testing_servers.jwt_mac(lang, keyset) now = datetime.datetime.now(tz=datetime.timezone.utc) token = jwt.new_raw_jwt(issuer='issuer', subject='subject', audiences=['audience1', 'audience2'], jwt_id='jwt_id', expiration=now + datetime.timedelta(seconds=10), custom_claims={ 'switch': True, 'pi': 3.14159 }) compact = jwt_mac_primitive.compute_mac_and_encode(token) validator = jwt.new_validator(audience='audience1', fixed_now=now) verified_jwt = jwt_mac_primitive.verify_mac_and_decode( compact, validator) self.assertEqual(verified_jwt.issuer(), 'issuer') self.assertEqual(verified_jwt.subject(), 'subject') self.assertEqual(verified_jwt.jwt_id(), 'jwt_id') self.assertEqual(verified_jwt.custom_claim('switch'), True) self.assertEqual(verified_jwt.custom_claim('pi'), 3.14159) validator2 = jwt.new_validator(audience='wrong_audience', fixed_now=now) with self.assertRaises(tink.TinkError): jwt_mac_primitive.verify_mac_and_decode(compact, validator2)
def test_verify_token_with_many_recursions(self, lang): num_recursions = 10 payload = ('{"a":' * num_recursions) + '""' + ('}' * num_recursions) token = generate_token('{"alg":"HS256"}', payload) jwt_mac = testing_servers.jwt_mac(lang, KEYSET) jwt_mac.verify_mac_and_decode(token, EMPTY_VALIDATOR)
def test_verify_token_with_empty_audiences(self, lang): token = generate_token('{"alg":"HS256"}', '{"aud":[]}') jwt_mac = testing_servers.jwt_mac(lang, KEYSET) with self.assertRaises(tink.TinkError): jwt_mac.verify_mac_and_decode(token, EMPTY_VALIDATOR)
def test_verify_invalid_utf8_in_payload(self, lang): token = generate_token_from_bytes(b'{"alg":"HS256"}', b'{"jti":"joe\xc2"}') jwt_mac = testing_servers.jwt_mac(lang, KEYSET) with self.assertRaises(tink.TinkError): jwt_mac.verify_mac_and_decode(token, EMPTY_VALIDATOR)
def test_verify_issuer_with_wrong_type(self, lang): token = generate_token('{"alg":"HS256"}', '{"iss":123}') jwt_mac = testing_servers.jwt_mac(lang, KEYSET) with self.assertRaises(tink.TinkError): jwt_mac.verify_mac_and_decode(token, EMPTY_VALIDATOR)
def test_verify_empty_string_issuer(self, lang): token = generate_token('{"alg":"HS256"}', '{"iss":""}') jwt_mac = testing_servers.jwt_mac(lang, KEYSET) jwt_mac.verify_mac_and_decode( token, jwt.new_validator(expected_issuer='', allow_missing_expiration=True))
def test_unknown_typ_header_valid(self, lang): token = generate_token('{"typ":"unknown", "alg":"HS256"}', '{"iss":"joe"}') jwt_mac = testing_servers.jwt_mac(lang, KEYSET) verified_jwt = jwt_mac.verify_mac_and_decode(token, EMPTY_VALIDATOR) self.assertEqual(verified_jwt.type_header(), 'unknown')
def test_large_expiration_is_fine(self, lang): token = generate_token('{"alg":"HS256"}', '{"exp":253402300799}') jwt_mac = testing_servers.jwt_mac(lang, KEYSET) jwt_mac.verify_mac_and_decode(token, EMPTY_VALIDATOR)
def test_verify_ignores_typ_header(self, lang): token = generate_token('{"typ":"unknown", "alg":"HS256"}', '{"iss":"joe"}') jwt_mac = testing_servers.jwt_mac(lang, KEYSET) jwt_mac.verify_mac_and_decode(token, EMPTY_VALIDATOR)
def test_verify_unknown_header_valid(self, lang): token = generate_token('{"alg":"HS256", "unknown":{"a":"b"}}', '{"jti":"123"}') jwt_mac = testing_servers.jwt_mac(lang, KEYSET) verified_jwt = jwt_mac.verify_mac_and_decode(token, EMPTY_VALIDATOR) self.assertEqual(verified_jwt.jwt_id(), '123')
def test_verify_valid(self, lang): token = generate_token('{"alg":"HS256"}', '{"iss":"joe"}') jwt_mac = testing_servers.jwt_mac(lang, KEYSET) verified_jwt = jwt_mac.verify_mac_and_decode(token, EMPTY_VALIDATOR) self.assertEqual(verified_jwt.issuer(), 'joe')
def test_verify_empty_crit_header_invalid(self, lang): # See https://tools.ietf.org/html/rfc7515#section-4.1.11 token = generate_token('{"alg":"HS256", "crit":[]}', '{"iss":"joe"}') jwt_mac = testing_servers.jwt_mac(lang, KEYSET) with self.assertRaises(tink.TinkError): jwt_mac.verify_mac_and_decode(token, EMPTY_VALIDATOR)
def test_too_large_expiration_is_invalid(self, lang): token = generate_token('{"alg":"HS256"}', '{"exp":253402300800}') jwt_mac = testing_servers.jwt_mac(lang, KEYSET) with self.assertRaises(tink.TinkError): jwt_mac.verify_mac_and_decode(token, EMPTY_VALIDATOR)
def test_infinity_expiration_is_invalid(self, lang): token = generate_token('{"alg":"HS256"}', '{"jti":"123", "exp":Infinity}') jwt_mac = testing_servers.jwt_mac(lang, KEYSET) with self.assertRaises(tink.TinkError): jwt_mac.verify_mac_and_decode(token, EMPTY_VALIDATOR)
def test_verify_empty_string_subject(self, lang): token = generate_token('{"alg":"HS256"}', '{"sub":""}') jwt_mac = testing_servers.jwt_mac(lang, KEYSET) jwt_mac.verify_mac_and_decode(token, jwt.new_validator(subject=''))