def gen_token(json_header: str, json_payload: str) -> str: cc_mac = _cc_mac() unsigned_token = ( _jwt_format.encode_header(json_header) + b'.' + _jwt_format.encode_payload(json_payload)) return _jwt_format.create_signed_compact(unsigned_token, cc_mac.compute_mac(unsigned_token))
def generate_token(header: Text, payload: Text) -> Text: """Generates tokens with valid MACs.""" unsigned_compact = (_jwt_format.encode_header(header) + b'.' + _jwt_format.encode_payload(payload)) mac_value = MAC.compute_mac(unsigned_compact) return (unsigned_compact + b'.' + _jwt_format.encode_signature(mac_value)).decode('utf8')
def test_decodeencode_header_rs256(self): # Example from https://tools.ietf.org/html/rfc7515#appendix-A.2 encoded_header = b'eyJhbGciOiJSUzI1NiJ9' json_header = _jwt_format.decode_header(encoded_header) self.assertEqual(json_header['alg'], 'RS256') self.assertEqual( _jwt_format.decode_header(_jwt_format.encode_header(json_header)), json_header)
def test_decodeencode_header_hs256(self): # Example from https://tools.ietf.org/html/rfc7515#appendix-A.1 encoded_header = b'eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9' json_header = _jwt_format.decode_header(encoded_header) self.assertEqual(json_header['alg'], 'HS256') self.assertEqual(json_header['typ'], 'JWT') self.assertEqual( _jwt_format.decode_header(_jwt_format.encode_header(json_header)), json_header)
def create_signed_token(json_header: Text, json_payload: Text) -> Text: key_data = _fixed_key_data() cc_key_manager = tink_bindings.MacKeyManager.from_cc_registry( 'type.googleapis.com/google.crypto.tink.JwtHmacKey') cc_mac = cc_key_manager.primitive(key_data.SerializeToString()) unsigned_token = (_jwt_format.encode_header(json_header) + b'.' + _jwt_format.encode_payload(json_payload)) return _jwt_format.create_signed_compact( unsigned_token, cc_mac.compute_mac(unsigned_token))
def test_encode_decode_header(self): encoded_header = _jwt_format.encode_header('{ "alg": "RS256"} ') json_header = _jwt_format.decode_header(encoded_header) self.assertEqual(json_header, '{ "alg": "RS256"} ')
def test_encode_header_with_invalid_utf16_character(self): with self.assertRaises(_jwt_error.JwtInvalidError): _jwt_format.encode_header('{"alg": "RS256", "a":"\uD834"}')
def test_encode_header_with_utf16_surrogate(self): self.assertEqual( _jwt_format.encode_header('{"alg": "RS256", "a":"\U0001d11e"}'), b'eyJhbGciOiAiUlMyNTYiLCAiYSI6IvCdhJ4ifQ')
def test_validate_header_with_unknown_algorithm_fails(self): header = _jwt_format.encode_header({}) with self.assertRaises(_jwt_error.JwtInvalidError): _jwt_format.validate_header(header, 'HS123')
def test_verify_empty_header_fails(self): header = _jwt_format.encode_header({}) with self.assertRaises(_jwt_error.JwtInvalidError): _jwt_format.validate_header(header, 'ES256')
def test_validate_header_with_bad_typ_fails(self): header = _jwt_format.encode_header({'alg': 'HS256', 'typ': 'IWT'}) with self.assertRaises(_jwt_error.JwtInvalidError): _jwt_format.validate_header(header, 'HS256')
def test_validate_header_with_lowercase_typ_success(self): header = _jwt_format.encode_header({'alg': 'HS256', 'typ': 'jwt'}) _jwt_format.validate_header(header, 'HS256')
def gen_compact(json_header: str, json_payload: str, raw_sign) -> str: unsigned_compact = (_jwt_format.encode_header(json_header) + b'.' + _jwt_format.encode_payload(json_payload)) signature = raw_sign.sign(unsigned_compact) return _jwt_format.create_signed_compact(unsigned_compact, signature)
def test_weird_tokens_with_valid_signatures(self): handle = tink.new_keyset_handle(jwt.raw_jwt_es256_template()) sign = handle.primitive(jwt.JwtPublicKeySign) # Get the internal PublicKeySign primitive to create valid signatures. wrapped = cast(_jwt_signature_wrappers._WrappedJwtPublicKeySign, sign) raw_sign = cast( _jwt_signature_key_manager._JwtPublicKeySign, wrapped._primitive_set.primary().primitive)._public_key_sign verify = handle.public_keyset_handle().primitive( jwt.JwtPublicKeyVerify) validator = jwt.new_validator(expected_issuer='issuer', allow_missing_expiration=True) # Normal token. valid = gen_compact('{"alg":"ES256"}', '{"iss":"issuer"}', raw_sign) verified_jwt = verify.verify_and_decode(valid, validator) self.assertEqual(verified_jwt.issuer(), 'issuer') # Token with unknown header is valid. unknown_header = gen_compact( '{"alg":"ES256","unknown_header":"abc"} \n ', '{"iss":"issuer" }', raw_sign) verified_jwt = verify.verify_and_decode(unknown_header, validator) self.assertEqual(verified_jwt.issuer(), 'issuer') # Token with unknown kid is valid, since primitives with output prefix type # RAW ignore kid headers. unknown_header = gen_compact('{"alg":"ES256","kid":"unknown"} \n ', '{"iss":"issuer" }', raw_sign) verified_jwt = verify.verify_and_decode(unknown_header, validator) self.assertEqual(verified_jwt.issuer(), 'issuer') # Token with invalid alg header alg_invalid = gen_compact('{"alg":"ES384"}', '{"iss":"issuer"}', raw_sign) with self.assertRaises(tink.TinkError): verify.verify_and_decode(alg_invalid, validator) # Token with empty header empty_header = gen_compact('{}', '{"iss":"issuer"}', raw_sign) with self.assertRaises(tink.TinkError): verify.verify_and_decode(empty_header, validator) # Token header is not valid JSON header_invalid = gen_compact('{"alg":"ES256"', '{"iss":"issuer"}', raw_sign) with self.assertRaises(tink.TinkError): verify.verify_and_decode(header_invalid, validator) # Token payload is not valid JSON payload_invalid = gen_compact('{"alg":"ES256"}', '{"iss":"issuer"', raw_sign) with self.assertRaises(tink.TinkError): verify.verify_and_decode(payload_invalid, validator) # Token with whitespace in header JSON string is valid. whitespace_in_header = gen_compact(' {"alg": \n "ES256"} \n ', '{"iss":"issuer" }', raw_sign) verified_jwt = verify.verify_and_decode(whitespace_in_header, validator) self.assertEqual(verified_jwt.issuer(), 'issuer') # Token with whitespace in payload JSON string is valid. whitespace_in_payload = gen_compact('{"alg":"ES256"}', ' {"iss": \n"issuer" } \n', raw_sign) verified_jwt = verify.verify_and_decode(whitespace_in_payload, validator) self.assertEqual(verified_jwt.issuer(), 'issuer') # Token with whitespace in base64-encoded header is invalid. with_whitespace = (_jwt_format.encode_header('{"alg":"ES256"}') + b' .' + _jwt_format.encode_payload('{"iss":"issuer"}')) token_with_whitespace = _jwt_format.create_signed_compact( with_whitespace, raw_sign.sign(with_whitespace)) with self.assertRaises(tink.TinkError): verify.verify_and_decode(token_with_whitespace, validator) # Token with invalid character is invalid. with_invalid_char = (_jwt_format.encode_header('{"alg":"ES256"}') + b'.?' + _jwt_format.encode_payload('{"iss":"issuer"}')) token_with_invalid_char = _jwt_format.create_signed_compact( with_invalid_char, raw_sign.sign(with_invalid_char)) with self.assertRaises(tink.TinkError): verify.verify_and_decode(token_with_invalid_char, validator) # Token with additional '.' is invalid. with_dot = (_jwt_format.encode_header('{"alg":"ES256"}') + b'.' + _jwt_format.encode_payload('{"iss":"issuer"}') + b'.') token_with_dot = _jwt_format.create_signed_compact( with_dot, raw_sign.sign(with_dot)) with self.assertRaises(tink.TinkError): verify.verify_and_decode(token_with_dot, validator) # 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 rec_payload = ('{"a":' * num_recursions) + '""' + ('}' * num_recursions) rec_token = gen_compact('{"alg":"ES256"}', rec_payload, raw_sign) with self.assertRaises(tink.TinkError): verify.verify_and_decode( rec_token, validator=jwt.new_validator(allow_missing_expiration=True)) # test wrong types with self.assertRaises(tink.TinkError): verify.verify_and_decode(cast(str, None), validator) with self.assertRaises(tink.TinkError): verify.verify_and_decode(cast(str, 123), validator) with self.assertRaises(tink.TinkError): valid_bytes = valid.encode('utf8') verify.verify_and_decode(cast(str, valid_bytes), validator)
def test_weird_tokens_with_valid_macs(self): jwt_hmac = create_fixed_jwt_hmac() validator = jwt.new_validator( expected_issuer='joe', allow_missing_expiration=True) cc_mac = _cc_mac() # Normal token. valid_token = gen_token('{"alg":"HS256"}', '{"iss":"joe"}') verified = jwt_hmac.verify_mac_and_decode_with_kid( valid_token, validator, kid=None) self.assertEqual(verified.issuer(), 'joe') # Token with unknown header is valid. token_with_unknown_header = gen_token( '{"unknown_header":"123","alg":"HS256"}', '{"iss":"joe"}') verified2 = jwt_hmac.verify_mac_and_decode_with_kid( token_with_unknown_header, validator, kid=None) self.assertEqual(verified2.issuer(), 'joe') # Token with unknown kid is valid, since primitives with output prefix type # RAW ignore kid headers. token_with_unknown_kid = gen_token('{"kid":"unknown","alg":"HS256"}', '{"iss":"joe"}') verified2 = jwt_hmac.verify_mac_and_decode_with_kid( token_with_unknown_kid, validator, kid=None) self.assertEqual(verified2.issuer(), 'joe') # Token with invalid alg header alg_invalid = gen_token('{"alg":"HS384"}', '{"iss":"joe"}') with self.assertRaises(tink.TinkError): jwt_hmac.verify_mac_and_decode_with_kid(alg_invalid, validator, kid=None) # Token with empty header empty_header = gen_token('{}', '{"iss":"joe"}') with self.assertRaises(tink.TinkError): jwt_hmac.verify_mac_and_decode_with_kid(empty_header, validator, kid=None) # Token header is not valid JSON header_invalid = gen_token('{"alg":"HS256"', '{"iss":"joe"}') with self.assertRaises(tink.TinkError): jwt_hmac.verify_mac_and_decode_with_kid( header_invalid, validator, kid=None) # Token payload is not valid JSON payload_invalid = gen_token('{"alg":"HS256"}', '{"iss":"joe"') with self.assertRaises(tink.TinkError): jwt_hmac.verify_mac_and_decode_with_kid( payload_invalid, validator, kid=None) # Token with whitespace in header JSON string is valid. whitespace_in_header = gen_token(' {"alg": \n "HS256"} \n ', '{"iss":"joe" }') verified_jwt = jwt_hmac.verify_mac_and_decode_with_kid( whitespace_in_header, validator, kid=None) self.assertEqual(verified_jwt.issuer(), 'joe') # Token with whitespace in payload JSON string is valid. whitespace_in_payload = gen_token('{"alg":"HS256"}', ' {"iss": \n"joe" } \n') verified_jwt = jwt_hmac.verify_mac_and_decode_with_kid( whitespace_in_payload, validator, kid=None) self.assertEqual(verified_jwt.issuer(), 'joe') # Token with whitespace in base64-encoded header is invalid. with_whitespace_in_encoding = ( _jwt_format.encode_header('{"alg":"HS256"}') + b' .' + _jwt_format.encode_payload('{"iss":"joe"}')) token_with_whitespace_in_encoding = _jwt_format.create_signed_compact( with_whitespace_in_encoding, cc_mac.compute_mac(with_whitespace_in_encoding)) with self.assertRaises(tink.TinkError): jwt_hmac.verify_mac_and_decode_with_kid( token_with_whitespace_in_encoding, validator, kid=None) # Token with invalid character is invalid. with_invalid_char = ( _jwt_format.encode_header('{"alg":"HS256"}') + b'.?' + _jwt_format.encode_payload('{"iss":"joe"}')) token_with_invalid_char = _jwt_format.create_signed_compact( with_invalid_char, cc_mac.compute_mac(with_invalid_char)) with self.assertRaises(tink.TinkError): jwt_hmac.verify_mac_and_decode_with_kid( token_with_invalid_char, validator, kid=None) # Token with additional '.' is invalid. with_dot = ( _jwt_format.encode_header('{"alg":"HS256"}') + b'.' + _jwt_format.encode_payload('{"iss":"joe"}') + b'.') token_with_dot = _jwt_format.create_signed_compact( with_dot, cc_mac.compute_mac(with_dot)) with self.assertRaises(tink.TinkError): jwt_hmac.verify_mac_and_decode_with_kid( token_with_dot, validator, kid=None) # 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 rec_payload = ('{"a":' * num_recursions) + '""' + ('}' * num_recursions) rec_token = gen_token('{"alg":"HS256"}', rec_payload) with self.assertRaises(tink.TinkError): jwt_hmac.verify_mac_and_decode_with_kid( rec_token, validator=jwt.new_validator(allow_missing_expiration=True), kid=None) # test wrong types with self.assertRaises(tink.TinkError): jwt_hmac.verify_mac_and_decode_with_kid( cast(str, None), validator, kid=None) with self.assertRaises(tink.TinkError): jwt_hmac.verify_mac_and_decode_with_kid( cast(str, 123), validator, kid=None) with self.assertRaises(tink.TinkError): valid_token_bytes = valid_token.encode('utf8') jwt_hmac.verify_mac_and_decode_with_kid( cast(str, valid_token_bytes), validator, kid=None)