4348db7 add PyJWT we use in backend git-subtree-dir: pyextra git-subtree-split: 4348db7e867dafbbcb7ec0302b5528688d1102c6pull/3/head
							parent
							
								
									342bb13bff
								
							
						
					
					
						commit
						7ccae06bce
					
				
				 19 changed files with 1289 additions and 0 deletions
			
			
		@ -0,0 +1,70 @@ | 
				
			|||||||
 | 
					Metadata-Version: 1.1 | 
				
			||||||
 | 
					Name: PyJWT | 
				
			||||||
 | 
					Version: 1.4.1 | 
				
			||||||
 | 
					Summary: JSON Web Token implementation in Python | 
				
			||||||
 | 
					Home-page: http://github.com/jpadilla/pyjwt | 
				
			||||||
 | 
					Author: José Padilla | 
				
			||||||
 | 
					Author-email: hello@jpadilla.com | 
				
			||||||
 | 
					License: MIT | 
				
			||||||
 | 
					Description: # PyJWT | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        [![travis-status-image]][travis] | 
				
			||||||
 | 
					        [![appveyor-status-image]][appveyor] | 
				
			||||||
 | 
					        [![pypi-version-image]][pypi] | 
				
			||||||
 | 
					        [![coveralls-status-image]][coveralls] | 
				
			||||||
 | 
					        [![docs-status-image]][docs] | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        A Python implementation of [RFC 7519][jwt-spec]. | 
				
			||||||
 | 
					        Original implementation was written by [@progrium][progrium]. | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        ## Installing | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        ``` | 
				
			||||||
 | 
					        $ pip install PyJWT | 
				
			||||||
 | 
					        ``` | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        ## Usage | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        ```python | 
				
			||||||
 | 
					        >>> import jwt | 
				
			||||||
 | 
					        >>> encoded = jwt.encode({'some': 'payload'}, 'secret', algorithm='HS256') | 
				
			||||||
 | 
					        'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzb21lIjoicGF5bG9hZCJ9.4twFt5NiznN84AWoo1d7KO1T_yoc0Z6XOpOVswacPZg' | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        >>> jwt.decode(encoded, 'secret', algorithms=['HS256']) | 
				
			||||||
 | 
					        {'some': 'payload'} | 
				
			||||||
 | 
					        ``` | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        ## Tests | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        You can run tests from the project root after cloning with: | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        ``` | 
				
			||||||
 | 
					        $ python setup.py test | 
				
			||||||
 | 
					        ``` | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        [travis-status-image]: https://secure.travis-ci.org/jpadilla/pyjwt.svg?branch=master | 
				
			||||||
 | 
					        [travis]: http://travis-ci.org/jpadilla/pyjwt?branch=master | 
				
			||||||
 | 
					        [appveyor-status-image]: https://ci.appveyor.com/api/projects/status/h8nt70aqtwhht39t?svg=true | 
				
			||||||
 | 
					        [appveyor]: https://ci.appveyor.com/project/jpadilla/pyjwt | 
				
			||||||
 | 
					        [pypi-version-image]: https://img.shields.io/pypi/v/pyjwt.svg | 
				
			||||||
 | 
					        [pypi]: https://pypi.python.org/pypi/pyjwt | 
				
			||||||
 | 
					        [coveralls-status-image]: https://coveralls.io/repos/jpadilla/pyjwt/badge.svg?branch=master | 
				
			||||||
 | 
					        [coveralls]: https://coveralls.io/r/jpadilla/pyjwt?branch=master | 
				
			||||||
 | 
					        [docs-status-image]: https://readthedocs.org/projects/pyjwt/badge/?version=latest | 
				
			||||||
 | 
					        [docs]: http://pyjwt.readthedocs.org | 
				
			||||||
 | 
					        [jwt-spec]: https://tools.ietf.org/html/rfc7519 | 
				
			||||||
 | 
					        [progrium]: https://github.com/progrium | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					Keywords: jwt json web token security signing | 
				
			||||||
 | 
					Platform: UNKNOWN | 
				
			||||||
 | 
					Classifier: Development Status :: 5 - Production/Stable | 
				
			||||||
 | 
					Classifier: Intended Audience :: Developers | 
				
			||||||
 | 
					Classifier: Natural Language :: English | 
				
			||||||
 | 
					Classifier: License :: OSI Approved :: MIT License | 
				
			||||||
 | 
					Classifier: Programming Language :: Python | 
				
			||||||
 | 
					Classifier: Programming Language :: Python :: 2.6 | 
				
			||||||
 | 
					Classifier: Programming Language :: Python :: 2.7 | 
				
			||||||
 | 
					Classifier: Programming Language :: Python :: 3.3 | 
				
			||||||
 | 
					Classifier: Programming Language :: Python :: 3.4 | 
				
			||||||
 | 
					Classifier: Programming Language :: Python :: 3.5 | 
				
			||||||
 | 
					Classifier: Topic :: Utilities | 
				
			||||||
@ -0,0 +1,49 @@ | 
				
			|||||||
 | 
					AUTHORS | 
				
			||||||
 | 
					CHANGELOG.md | 
				
			||||||
 | 
					LICENSE | 
				
			||||||
 | 
					MANIFEST.in | 
				
			||||||
 | 
					README.md | 
				
			||||||
 | 
					setup.cfg | 
				
			||||||
 | 
					setup.py | 
				
			||||||
 | 
					tox.ini | 
				
			||||||
 | 
					PyJWT.egg-info/PKG-INFO | 
				
			||||||
 | 
					PyJWT.egg-info/SOURCES.txt | 
				
			||||||
 | 
					PyJWT.egg-info/dependency_links.txt | 
				
			||||||
 | 
					PyJWT.egg-info/entry_points.txt | 
				
			||||||
 | 
					PyJWT.egg-info/requires.txt | 
				
			||||||
 | 
					PyJWT.egg-info/top_level.txt | 
				
			||||||
 | 
					jwt/__init__.py | 
				
			||||||
 | 
					jwt/__main__.py | 
				
			||||||
 | 
					jwt/algorithms.py | 
				
			||||||
 | 
					jwt/api_jws.py | 
				
			||||||
 | 
					jwt/api_jwt.py | 
				
			||||||
 | 
					jwt/compat.py | 
				
			||||||
 | 
					jwt/exceptions.py | 
				
			||||||
 | 
					jwt/utils.py | 
				
			||||||
 | 
					jwt/contrib/__init__.py | 
				
			||||||
 | 
					jwt/contrib/algorithms/__init__.py | 
				
			||||||
 | 
					jwt/contrib/algorithms/py_ecdsa.py | 
				
			||||||
 | 
					jwt/contrib/algorithms/pycrypto.py | 
				
			||||||
 | 
					tests/__init__.py | 
				
			||||||
 | 
					tests/compat.py | 
				
			||||||
 | 
					tests/test_algorithms.py | 
				
			||||||
 | 
					tests/test_api_jws.py | 
				
			||||||
 | 
					tests/test_api_jwt.py | 
				
			||||||
 | 
					tests/test_compat.py | 
				
			||||||
 | 
					tests/test_exceptions.py | 
				
			||||||
 | 
					tests/test_jwt.py | 
				
			||||||
 | 
					tests/utils.py | 
				
			||||||
 | 
					tests/contrib/__init__.py | 
				
			||||||
 | 
					tests/contrib/test_algorithms.py | 
				
			||||||
 | 
					tests/keys/__init__.py | 
				
			||||||
 | 
					tests/keys/jwk_ec_key.json | 
				
			||||||
 | 
					tests/keys/jwk_ec_pub.json | 
				
			||||||
 | 
					tests/keys/jwk_hmac.json | 
				
			||||||
 | 
					tests/keys/jwk_rsa_key.json | 
				
			||||||
 | 
					tests/keys/jwk_rsa_pub.json | 
				
			||||||
 | 
					tests/keys/testkey2_rsa.pub.pem | 
				
			||||||
 | 
					tests/keys/testkey_ec | 
				
			||||||
 | 
					tests/keys/testkey_ec.pub | 
				
			||||||
 | 
					tests/keys/testkey_rsa | 
				
			||||||
 | 
					tests/keys/testkey_rsa.cer | 
				
			||||||
 | 
					tests/keys/testkey_rsa.pub | 
				
			||||||
@ -0,0 +1 @@ | 
				
			|||||||
 | 
					
 | 
				
			||||||
@ -0,0 +1,3 @@ | 
				
			|||||||
 | 
					[console_scripts] | 
				
			||||||
 | 
					jwt = jwt.__main__:main | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -0,0 +1,31 @@ | 
				
			|||||||
 | 
					../../../../bin/jwt | 
				
			||||||
 | 
					../jwt/__init__.py | 
				
			||||||
 | 
					../jwt/__init__.pyc | 
				
			||||||
 | 
					../jwt/__main__.py | 
				
			||||||
 | 
					../jwt/__main__.pyc | 
				
			||||||
 | 
					../jwt/algorithms.py | 
				
			||||||
 | 
					../jwt/algorithms.pyc | 
				
			||||||
 | 
					../jwt/api_jws.py | 
				
			||||||
 | 
					../jwt/api_jws.pyc | 
				
			||||||
 | 
					../jwt/api_jwt.py | 
				
			||||||
 | 
					../jwt/api_jwt.pyc | 
				
			||||||
 | 
					../jwt/compat.py | 
				
			||||||
 | 
					../jwt/compat.pyc | 
				
			||||||
 | 
					../jwt/contrib/__init__.py | 
				
			||||||
 | 
					../jwt/contrib/__init__.pyc | 
				
			||||||
 | 
					../jwt/contrib/algorithms/__init__.py | 
				
			||||||
 | 
					../jwt/contrib/algorithms/__init__.pyc | 
				
			||||||
 | 
					../jwt/contrib/algorithms/py_ecdsa.py | 
				
			||||||
 | 
					../jwt/contrib/algorithms/py_ecdsa.pyc | 
				
			||||||
 | 
					../jwt/contrib/algorithms/pycrypto.py | 
				
			||||||
 | 
					../jwt/contrib/algorithms/pycrypto.pyc | 
				
			||||||
 | 
					../jwt/exceptions.py | 
				
			||||||
 | 
					../jwt/exceptions.pyc | 
				
			||||||
 | 
					../jwt/utils.py | 
				
			||||||
 | 
					../jwt/utils.pyc | 
				
			||||||
 | 
					PKG-INFO | 
				
			||||||
 | 
					SOURCES.txt | 
				
			||||||
 | 
					dependency_links.txt | 
				
			||||||
 | 
					entry_points.txt | 
				
			||||||
 | 
					requires.txt | 
				
			||||||
 | 
					top_level.txt | 
				
			||||||
@ -0,0 +1,13 @@ | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					[crypto] | 
				
			||||||
 | 
					cryptography | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[flake8] | 
				
			||||||
 | 
					flake8 | 
				
			||||||
 | 
					flake8-import-order | 
				
			||||||
 | 
					pep8-naming | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[test] | 
				
			||||||
 | 
					pytest==2.7.3 | 
				
			||||||
 | 
					pytest-cov | 
				
			||||||
 | 
					pytest-runner | 
				
			||||||
@ -0,0 +1 @@ | 
				
			|||||||
 | 
					jwt | 
				
			||||||
@ -0,0 +1,29 @@ | 
				
			|||||||
 | 
					# -*- coding: utf-8 -*- | 
				
			||||||
 | 
					# flake8: noqa | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					""" | 
				
			||||||
 | 
					JSON Web Token implementation | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Minimum implementation based on this spec: | 
				
			||||||
 | 
					http://self-issued.info/docs/draft-jones-json-web-token-01.html | 
				
			||||||
 | 
					""" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					__title__ = 'pyjwt' | 
				
			||||||
 | 
					__version__ = '1.4.1' | 
				
			||||||
 | 
					__author__ = 'José Padilla' | 
				
			||||||
 | 
					__license__ = 'MIT' | 
				
			||||||
 | 
					__copyright__ = 'Copyright 2015 José Padilla' | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from .api_jwt import ( | 
				
			||||||
 | 
					    encode, decode, register_algorithm, unregister_algorithm, | 
				
			||||||
 | 
					    get_unverified_header, PyJWT | 
				
			||||||
 | 
					) | 
				
			||||||
 | 
					from .api_jws import PyJWS | 
				
			||||||
 | 
					from .exceptions import ( | 
				
			||||||
 | 
					    InvalidTokenError, DecodeError, InvalidAudienceError, | 
				
			||||||
 | 
					    ExpiredSignatureError, ImmatureSignatureError, InvalidIssuedAtError, | 
				
			||||||
 | 
					    InvalidIssuerError, ExpiredSignature, InvalidAudience, InvalidIssuer, | 
				
			||||||
 | 
					    MissingRequiredClaimError | 
				
			||||||
 | 
					) | 
				
			||||||
@ -0,0 +1,135 @@ | 
				
			|||||||
 | 
					#!/usr/bin/env python | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from __future__ import absolute_import, print_function | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import json | 
				
			||||||
 | 
					import optparse | 
				
			||||||
 | 
					import sys | 
				
			||||||
 | 
					import time | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from . import DecodeError, __package__, __version__, decode, encode | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def main(): | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    usage = '''Encodes or decodes JSON Web Tokens based on input. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  %prog [options] input | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Decoding examples: | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  %prog --key=secret json.web.token | 
				
			||||||
 | 
					  %prog --no-verify json.web.token | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Encoding requires the key option and takes space separated key/value pairs | 
				
			||||||
 | 
					separated by equals (=) as input. Examples: | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  %prog --key=secret iss=me exp=1302049071 | 
				
			||||||
 | 
					  %prog --key=secret foo=bar exp=+10 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The exp key is special and can take an offset to current Unix time.\ | 
				
			||||||
 | 
					''' | 
				
			||||||
 | 
					    p = optparse.OptionParser( | 
				
			||||||
 | 
					        usage=usage, | 
				
			||||||
 | 
					        prog=__package__, | 
				
			||||||
 | 
					        version='%s %s' % (__package__, __version__), | 
				
			||||||
 | 
					    ) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    p.add_option( | 
				
			||||||
 | 
					        '-n', '--no-verify', | 
				
			||||||
 | 
					        action='store_false', | 
				
			||||||
 | 
					        dest='verify', | 
				
			||||||
 | 
					        default=True, | 
				
			||||||
 | 
					        help='ignore signature and claims verification on decode' | 
				
			||||||
 | 
					    ) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    p.add_option( | 
				
			||||||
 | 
					        '--key', | 
				
			||||||
 | 
					        dest='key', | 
				
			||||||
 | 
					        metavar='KEY', | 
				
			||||||
 | 
					        default=None, | 
				
			||||||
 | 
					        help='set the secret key to sign with' | 
				
			||||||
 | 
					    ) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    p.add_option( | 
				
			||||||
 | 
					        '--alg', | 
				
			||||||
 | 
					        dest='algorithm', | 
				
			||||||
 | 
					        metavar='ALG', | 
				
			||||||
 | 
					        default='HS256', | 
				
			||||||
 | 
					        help='set crypto algorithm to sign with. default=HS256' | 
				
			||||||
 | 
					    ) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    options, arguments = p.parse_args() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if len(arguments) > 0 or not sys.stdin.isatty(): | 
				
			||||||
 | 
					        if len(arguments) == 1 and (not options.verify or options.key): | 
				
			||||||
 | 
					            # Try to decode | 
				
			||||||
 | 
					            try: | 
				
			||||||
 | 
					                if not sys.stdin.isatty(): | 
				
			||||||
 | 
					                    token = sys.stdin.read() | 
				
			||||||
 | 
					                else: | 
				
			||||||
 | 
					                    token = arguments[0] | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                token = token.encode('utf-8') | 
				
			||||||
 | 
					                data = decode(token, key=options.key, verify=options.verify) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                print(json.dumps(data)) | 
				
			||||||
 | 
					                sys.exit(0) | 
				
			||||||
 | 
					            except DecodeError as e: | 
				
			||||||
 | 
					                print(e) | 
				
			||||||
 | 
					                sys.exit(1) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Try to encode | 
				
			||||||
 | 
					        if options.key is None: | 
				
			||||||
 | 
					            print('Key is required when encoding. See --help for usage.') | 
				
			||||||
 | 
					            sys.exit(1) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Build payload object to encode | 
				
			||||||
 | 
					        payload = {} | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for arg in arguments: | 
				
			||||||
 | 
					            try: | 
				
			||||||
 | 
					                k, v = arg.split('=', 1) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                # exp +offset special case? | 
				
			||||||
 | 
					                if k == 'exp' and v[0] == '+' and len(v) > 1: | 
				
			||||||
 | 
					                    v = str(int(time.time()+int(v[1:]))) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                # Cast to integer? | 
				
			||||||
 | 
					                if v.isdigit(): | 
				
			||||||
 | 
					                    v = int(v) | 
				
			||||||
 | 
					                else: | 
				
			||||||
 | 
					                    # Cast to float? | 
				
			||||||
 | 
					                    try: | 
				
			||||||
 | 
					                        v = float(v) | 
				
			||||||
 | 
					                    except ValueError: | 
				
			||||||
 | 
					                        pass | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                # Cast to true, false, or null? | 
				
			||||||
 | 
					                constants = {'true': True, 'false': False, 'null': None} | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if v in constants: | 
				
			||||||
 | 
					                    v = constants[v] | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                payload[k] = v | 
				
			||||||
 | 
					            except ValueError: | 
				
			||||||
 | 
					                print('Invalid encoding input at {}'.format(arg)) | 
				
			||||||
 | 
					                sys.exit(1) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            token = encode( | 
				
			||||||
 | 
					                payload, | 
				
			||||||
 | 
					                key=options.key, | 
				
			||||||
 | 
					                algorithm=options.algorithm | 
				
			||||||
 | 
					            ) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            print(token) | 
				
			||||||
 | 
					            sys.exit(0) | 
				
			||||||
 | 
					        except Exception as e: | 
				
			||||||
 | 
					            print(e) | 
				
			||||||
 | 
					            sys.exit(1) | 
				
			||||||
 | 
					    else: | 
				
			||||||
 | 
					        p.print_help() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if __name__ == '__main__': | 
				
			||||||
 | 
					    main() | 
				
			||||||
@ -0,0 +1,290 @@ | 
				
			|||||||
 | 
					import hashlib | 
				
			||||||
 | 
					import hmac | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from .compat import constant_time_compare, string_types, text_type | 
				
			||||||
 | 
					from .exceptions import InvalidKeyError | 
				
			||||||
 | 
					from .utils import der_to_raw_signature, raw_to_der_signature | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					try: | 
				
			||||||
 | 
					    from cryptography.hazmat.primitives import hashes | 
				
			||||||
 | 
					    from cryptography.hazmat.primitives.serialization import ( | 
				
			||||||
 | 
					        load_pem_private_key, load_pem_public_key, load_ssh_public_key | 
				
			||||||
 | 
					    ) | 
				
			||||||
 | 
					    from cryptography.hazmat.primitives.asymmetric.rsa import ( | 
				
			||||||
 | 
					        RSAPrivateKey, RSAPublicKey | 
				
			||||||
 | 
					    ) | 
				
			||||||
 | 
					    from cryptography.hazmat.primitives.asymmetric.ec import ( | 
				
			||||||
 | 
					        EllipticCurvePrivateKey, EllipticCurvePublicKey | 
				
			||||||
 | 
					    ) | 
				
			||||||
 | 
					    from cryptography.hazmat.primitives.asymmetric import ec, padding | 
				
			||||||
 | 
					    from cryptography.hazmat.backends import default_backend | 
				
			||||||
 | 
					    from cryptography.exceptions import InvalidSignature | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    has_crypto = True | 
				
			||||||
 | 
					except ImportError: | 
				
			||||||
 | 
					    has_crypto = False | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def get_default_algorithms(): | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					    Returns the algorithms that are implemented by the library. | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					    default_algorithms = { | 
				
			||||||
 | 
					        'none': NoneAlgorithm(), | 
				
			||||||
 | 
					        'HS256': HMACAlgorithm(HMACAlgorithm.SHA256), | 
				
			||||||
 | 
					        'HS384': HMACAlgorithm(HMACAlgorithm.SHA384), | 
				
			||||||
 | 
					        'HS512': HMACAlgorithm(HMACAlgorithm.SHA512) | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if has_crypto: | 
				
			||||||
 | 
					        default_algorithms.update({ | 
				
			||||||
 | 
					            'RS256': RSAAlgorithm(RSAAlgorithm.SHA256), | 
				
			||||||
 | 
					            'RS384': RSAAlgorithm(RSAAlgorithm.SHA384), | 
				
			||||||
 | 
					            'RS512': RSAAlgorithm(RSAAlgorithm.SHA512), | 
				
			||||||
 | 
					            'ES256': ECAlgorithm(ECAlgorithm.SHA256), | 
				
			||||||
 | 
					            'ES384': ECAlgorithm(ECAlgorithm.SHA384), | 
				
			||||||
 | 
					            'ES512': ECAlgorithm(ECAlgorithm.SHA512), | 
				
			||||||
 | 
					            'PS256': RSAPSSAlgorithm(RSAPSSAlgorithm.SHA256), | 
				
			||||||
 | 
					            'PS384': RSAPSSAlgorithm(RSAPSSAlgorithm.SHA384), | 
				
			||||||
 | 
					            'PS512': RSAPSSAlgorithm(RSAPSSAlgorithm.SHA512) | 
				
			||||||
 | 
					        }) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return default_algorithms | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Algorithm(object): | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					    The interface for an algorithm used to sign and verify tokens. | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					    def prepare_key(self, key): | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        Performs necessary validation and conversions on the key and returns | 
				
			||||||
 | 
					        the key value in the proper format for sign() and verify(). | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        raise NotImplementedError | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def sign(self, msg, key): | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        Returns a digital signature for the specified message | 
				
			||||||
 | 
					        using the specified key value. | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        raise NotImplementedError | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def verify(self, msg, key, sig): | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        Verifies that the specified digital signature is valid | 
				
			||||||
 | 
					        for the specified message and key values. | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        raise NotImplementedError | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class NoneAlgorithm(Algorithm): | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					    Placeholder for use when no signing or verification | 
				
			||||||
 | 
					    operations are required. | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					    def prepare_key(self, key): | 
				
			||||||
 | 
					        if key == '': | 
				
			||||||
 | 
					            key = None | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if key is not None: | 
				
			||||||
 | 
					            raise InvalidKeyError('When alg = "none", key value must be None.') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return key | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def sign(self, msg, key): | 
				
			||||||
 | 
					        return b'' | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def verify(self, msg, key, sig): | 
				
			||||||
 | 
					        return False | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class HMACAlgorithm(Algorithm): | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					    Performs signing and verification operations using HMAC | 
				
			||||||
 | 
					    and the specified hash function. | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					    SHA256 = hashlib.sha256 | 
				
			||||||
 | 
					    SHA384 = hashlib.sha384 | 
				
			||||||
 | 
					    SHA512 = hashlib.sha512 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, hash_alg): | 
				
			||||||
 | 
					        self.hash_alg = hash_alg | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def prepare_key(self, key): | 
				
			||||||
 | 
					        if not isinstance(key, string_types) and not isinstance(key, bytes): | 
				
			||||||
 | 
					            raise TypeError('Expecting a string- or bytes-formatted key.') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if isinstance(key, text_type): | 
				
			||||||
 | 
					            key = key.encode('utf-8') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        invalid_strings = [ | 
				
			||||||
 | 
					            b'-----BEGIN PUBLIC KEY-----', | 
				
			||||||
 | 
					            b'-----BEGIN CERTIFICATE-----', | 
				
			||||||
 | 
					            b'ssh-rsa' | 
				
			||||||
 | 
					        ] | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if any([string_value in key for string_value in invalid_strings]): | 
				
			||||||
 | 
					            raise InvalidKeyError( | 
				
			||||||
 | 
					                'The specified key is an asymmetric key or x509 certificate and' | 
				
			||||||
 | 
					                ' should not be used as an HMAC secret.') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return key | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def sign(self, msg, key): | 
				
			||||||
 | 
					        return hmac.new(key, msg, self.hash_alg).digest() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def verify(self, msg, key, sig): | 
				
			||||||
 | 
					        return constant_time_compare(sig, self.sign(msg, key)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if has_crypto: | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class RSAAlgorithm(Algorithm): | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        Performs signing and verification operations using | 
				
			||||||
 | 
					        RSASSA-PKCS-v1_5 and the specified hash function. | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        SHA256 = hashes.SHA256 | 
				
			||||||
 | 
					        SHA384 = hashes.SHA384 | 
				
			||||||
 | 
					        SHA512 = hashes.SHA512 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        def __init__(self, hash_alg): | 
				
			||||||
 | 
					            self.hash_alg = hash_alg | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        def prepare_key(self, key): | 
				
			||||||
 | 
					            if isinstance(key, RSAPrivateKey) or \ | 
				
			||||||
 | 
					               isinstance(key, RSAPublicKey): | 
				
			||||||
 | 
					                return key | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if isinstance(key, string_types): | 
				
			||||||
 | 
					                if isinstance(key, text_type): | 
				
			||||||
 | 
					                    key = key.encode('utf-8') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                try: | 
				
			||||||
 | 
					                    if key.startswith(b'ssh-rsa'): | 
				
			||||||
 | 
					                        key = load_ssh_public_key(key, backend=default_backend()) | 
				
			||||||
 | 
					                    else: | 
				
			||||||
 | 
					                        key = load_pem_private_key(key, password=None, backend=default_backend()) | 
				
			||||||
 | 
					                except ValueError: | 
				
			||||||
 | 
					                    key = load_pem_public_key(key, backend=default_backend()) | 
				
			||||||
 | 
					            else: | 
				
			||||||
 | 
					                raise TypeError('Expecting a PEM-formatted key.') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return key | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        def sign(self, msg, key): | 
				
			||||||
 | 
					            signer = key.signer( | 
				
			||||||
 | 
					                padding.PKCS1v15(), | 
				
			||||||
 | 
					                self.hash_alg() | 
				
			||||||
 | 
					            ) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            signer.update(msg) | 
				
			||||||
 | 
					            return signer.finalize() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        def verify(self, msg, key, sig): | 
				
			||||||
 | 
					            verifier = key.verifier( | 
				
			||||||
 | 
					                sig, | 
				
			||||||
 | 
					                padding.PKCS1v15(), | 
				
			||||||
 | 
					                self.hash_alg() | 
				
			||||||
 | 
					            ) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            verifier.update(msg) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            try: | 
				
			||||||
 | 
					                verifier.verify() | 
				
			||||||
 | 
					                return True | 
				
			||||||
 | 
					            except InvalidSignature: | 
				
			||||||
 | 
					                return False | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class ECAlgorithm(Algorithm): | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        Performs signing and verification operations using | 
				
			||||||
 | 
					        ECDSA and the specified hash function | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        SHA256 = hashes.SHA256 | 
				
			||||||
 | 
					        SHA384 = hashes.SHA384 | 
				
			||||||
 | 
					        SHA512 = hashes.SHA512 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        def __init__(self, hash_alg): | 
				
			||||||
 | 
					            self.hash_alg = hash_alg | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        def prepare_key(self, key): | 
				
			||||||
 | 
					            if isinstance(key, EllipticCurvePrivateKey) or \ | 
				
			||||||
 | 
					               isinstance(key, EllipticCurvePublicKey): | 
				
			||||||
 | 
					                return key | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if isinstance(key, string_types): | 
				
			||||||
 | 
					                if isinstance(key, text_type): | 
				
			||||||
 | 
					                    key = key.encode('utf-8') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                # Attempt to load key. We don't know if it's | 
				
			||||||
 | 
					                # a Signing Key or a Verifying Key, so we try | 
				
			||||||
 | 
					                # the Verifying Key first. | 
				
			||||||
 | 
					                try: | 
				
			||||||
 | 
					                    key = load_pem_public_key(key, backend=default_backend()) | 
				
			||||||
 | 
					                except ValueError: | 
				
			||||||
 | 
					                    key = load_pem_private_key(key, password=None, backend=default_backend()) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            else: | 
				
			||||||
 | 
					                raise TypeError('Expecting a PEM-formatted key.') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return key | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        def sign(self, msg, key): | 
				
			||||||
 | 
					            signer = key.signer(ec.ECDSA(self.hash_alg())) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            signer.update(msg) | 
				
			||||||
 | 
					            der_sig = signer.finalize() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return der_to_raw_signature(der_sig, key.curve) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        def verify(self, msg, key, sig): | 
				
			||||||
 | 
					            try: | 
				
			||||||
 | 
					                der_sig = raw_to_der_signature(sig, key.curve) | 
				
			||||||
 | 
					            except ValueError: | 
				
			||||||
 | 
					                return False | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            verifier = key.verifier(der_sig, ec.ECDSA(self.hash_alg())) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            verifier.update(msg) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            try: | 
				
			||||||
 | 
					                verifier.verify() | 
				
			||||||
 | 
					                return True | 
				
			||||||
 | 
					            except InvalidSignature: | 
				
			||||||
 | 
					                return False | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class RSAPSSAlgorithm(RSAAlgorithm): | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        Performs a signature using RSASSA-PSS with MGF1 | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        def sign(self, msg, key): | 
				
			||||||
 | 
					            signer = key.signer( | 
				
			||||||
 | 
					                padding.PSS( | 
				
			||||||
 | 
					                    mgf=padding.MGF1(self.hash_alg()), | 
				
			||||||
 | 
					                    salt_length=self.hash_alg.digest_size | 
				
			||||||
 | 
					                ), | 
				
			||||||
 | 
					                self.hash_alg() | 
				
			||||||
 | 
					            ) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            signer.update(msg) | 
				
			||||||
 | 
					            return signer.finalize() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        def verify(self, msg, key, sig): | 
				
			||||||
 | 
					            verifier = key.verifier( | 
				
			||||||
 | 
					                sig, | 
				
			||||||
 | 
					                padding.PSS( | 
				
			||||||
 | 
					                    mgf=padding.MGF1(self.hash_alg()), | 
				
			||||||
 | 
					                    salt_length=self.hash_alg.digest_size | 
				
			||||||
 | 
					                ), | 
				
			||||||
 | 
					                self.hash_alg() | 
				
			||||||
 | 
					            ) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            verifier.update(msg) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            try: | 
				
			||||||
 | 
					                verifier.verify() | 
				
			||||||
 | 
					                return True | 
				
			||||||
 | 
					            except InvalidSignature: | 
				
			||||||
 | 
					                return False | 
				
			||||||
@ -0,0 +1,204 @@ | 
				
			|||||||
 | 
					import binascii | 
				
			||||||
 | 
					import json | 
				
			||||||
 | 
					import warnings | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from collections import Mapping | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from .algorithms import Algorithm, get_default_algorithms  # NOQA | 
				
			||||||
 | 
					from .compat import binary_type, string_types, text_type | 
				
			||||||
 | 
					from .exceptions import DecodeError, InvalidAlgorithmError, InvalidTokenError | 
				
			||||||
 | 
					from .utils import base64url_decode, base64url_encode, merge_dict | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class PyJWS(object): | 
				
			||||||
 | 
					    header_typ = 'JWT' | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, algorithms=None, options=None): | 
				
			||||||
 | 
					        self._algorithms = get_default_algorithms() | 
				
			||||||
 | 
					        self._valid_algs = (set(algorithms) if algorithms is not None | 
				
			||||||
 | 
					                            else set(self._algorithms)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Remove algorithms that aren't on the whitelist | 
				
			||||||
 | 
					        for key in list(self._algorithms.keys()): | 
				
			||||||
 | 
					            if key not in self._valid_algs: | 
				
			||||||
 | 
					                del self._algorithms[key] | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if not options: | 
				
			||||||
 | 
					            options = {} | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.options = merge_dict(self._get_default_options(), options) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @staticmethod | 
				
			||||||
 | 
					    def _get_default_options(): | 
				
			||||||
 | 
					        return { | 
				
			||||||
 | 
					            'verify_signature': True | 
				
			||||||
 | 
					        } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def register_algorithm(self, alg_id, alg_obj): | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        Registers a new Algorithm for use when creating and verifying tokens. | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        if alg_id in self._algorithms: | 
				
			||||||
 | 
					            raise ValueError('Algorithm already has a handler.') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if not isinstance(alg_obj, Algorithm): | 
				
			||||||
 | 
					            raise TypeError('Object is not of type `Algorithm`') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self._algorithms[alg_id] = alg_obj | 
				
			||||||
 | 
					        self._valid_algs.add(alg_id) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def unregister_algorithm(self, alg_id): | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        Unregisters an Algorithm for use when creating and verifying tokens | 
				
			||||||
 | 
					        Throws KeyError if algorithm is not registered. | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        if alg_id not in self._algorithms: | 
				
			||||||
 | 
					            raise KeyError('The specified algorithm could not be removed' | 
				
			||||||
 | 
					                           ' because it is not registered.') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        del self._algorithms[alg_id] | 
				
			||||||
 | 
					        self._valid_algs.remove(alg_id) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_algorithms(self): | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        Returns a list of supported values for the 'alg' parameter. | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        return list(self._valid_algs) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def encode(self, payload, key, algorithm='HS256', headers=None, | 
				
			||||||
 | 
					               json_encoder=None): | 
				
			||||||
 | 
					        segments = [] | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if algorithm is None: | 
				
			||||||
 | 
					            algorithm = 'none' | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if algorithm not in self._valid_algs: | 
				
			||||||
 | 
					            pass | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Header | 
				
			||||||
 | 
					        header = {'typ': self.header_typ, 'alg': algorithm} | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if headers: | 
				
			||||||
 | 
					            self._validate_headers(headers) | 
				
			||||||
 | 
					            header.update(headers) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        json_header = json.dumps( | 
				
			||||||
 | 
					            header, | 
				
			||||||
 | 
					            separators=(',', ':'), | 
				
			||||||
 | 
					            cls=json_encoder | 
				
			||||||
 | 
					        ).encode('utf-8') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        segments.append(base64url_encode(json_header)) | 
				
			||||||
 | 
					        segments.append(base64url_encode(payload)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Segments | 
				
			||||||
 | 
					        signing_input = b'.'.join(segments) | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            alg_obj = self._algorithms[algorithm] | 
				
			||||||
 | 
					            key = alg_obj.prepare_key(key) | 
				
			||||||
 | 
					            signature = alg_obj.sign(signing_input, key) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        except KeyError: | 
				
			||||||
 | 
					            raise NotImplementedError('Algorithm not supported') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        segments.append(base64url_encode(signature)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return b'.'.join(segments) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def decode(self, jws, key='', verify=True, algorithms=None, options=None, | 
				
			||||||
 | 
					               **kwargs): | 
				
			||||||
 | 
					        payload, signing_input, header, signature = self._load(jws) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if verify: | 
				
			||||||
 | 
					            merged_options = merge_dict(self.options, options) | 
				
			||||||
 | 
					            if merged_options.get('verify_signature'): | 
				
			||||||
 | 
					                self._verify_signature(payload, signing_input, header, signature, | 
				
			||||||
 | 
					                                       key, algorithms) | 
				
			||||||
 | 
					        else: | 
				
			||||||
 | 
					            warnings.warn('The verify parameter is deprecated. ' | 
				
			||||||
 | 
					                          'Please use options instead.', DeprecationWarning) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return payload | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_unverified_header(self, jwt): | 
				
			||||||
 | 
					        """Returns back the JWT header parameters as a dict() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Note: The signature is not verified so the header parameters | 
				
			||||||
 | 
					        should not be fully trusted until signature verification is complete | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        headers = self._load(jwt)[2] | 
				
			||||||
 | 
					        self._validate_headers(headers) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return headers | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _load(self, jwt): | 
				
			||||||
 | 
					        if isinstance(jwt, text_type): | 
				
			||||||
 | 
					            jwt = jwt.encode('utf-8') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if not issubclass(type(jwt), binary_type): | 
				
			||||||
 | 
					            raise DecodeError("Invalid token type. Token must be a {0}".format( | 
				
			||||||
 | 
					                binary_type)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            signing_input, crypto_segment = jwt.rsplit(b'.', 1) | 
				
			||||||
 | 
					            header_segment, payload_segment = signing_input.split(b'.', 1) | 
				
			||||||
 | 
					        except ValueError: | 
				
			||||||
 | 
					            raise DecodeError('Not enough segments') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            header_data = base64url_decode(header_segment) | 
				
			||||||
 | 
					        except (TypeError, binascii.Error): | 
				
			||||||
 | 
					            raise DecodeError('Invalid header padding') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            header = json.loads(header_data.decode('utf-8')) | 
				
			||||||
 | 
					        except ValueError as e: | 
				
			||||||
 | 
					            raise DecodeError('Invalid header string: %s' % e) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if not isinstance(header, Mapping): | 
				
			||||||
 | 
					            raise DecodeError('Invalid header string: must be a json object') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            payload = base64url_decode(payload_segment) | 
				
			||||||
 | 
					        except (TypeError, binascii.Error): | 
				
			||||||
 | 
					            raise DecodeError('Invalid payload padding') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            signature = base64url_decode(crypto_segment) | 
				
			||||||
 | 
					        except (TypeError, binascii.Error): | 
				
			||||||
 | 
					            raise DecodeError('Invalid crypto padding') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return (payload, signing_input, header, signature) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _verify_signature(self, payload, signing_input, header, signature, | 
				
			||||||
 | 
					                          key='', algorithms=None): | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        alg = header.get('alg') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if algorithms is not None and alg not in algorithms: | 
				
			||||||
 | 
					            raise InvalidAlgorithmError('The specified alg value is not allowed') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            alg_obj = self._algorithms[alg] | 
				
			||||||
 | 
					            key = alg_obj.prepare_key(key) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if not alg_obj.verify(signing_input, key, signature): | 
				
			||||||
 | 
					                raise DecodeError('Signature verification failed') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        except KeyError: | 
				
			||||||
 | 
					            raise InvalidAlgorithmError('Algorithm not supported') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _validate_headers(self, headers): | 
				
			||||||
 | 
					        if 'kid' in headers: | 
				
			||||||
 | 
					            self._validate_kid(headers['kid']) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _validate_kid(self, kid): | 
				
			||||||
 | 
					        if not isinstance(kid, string_types): | 
				
			||||||
 | 
					            raise InvalidTokenError('Key ID header parameter must be a string') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					_jws_global_obj = PyJWS() | 
				
			||||||
 | 
					encode = _jws_global_obj.encode | 
				
			||||||
 | 
					decode = _jws_global_obj.decode | 
				
			||||||
 | 
					register_algorithm = _jws_global_obj.register_algorithm | 
				
			||||||
 | 
					unregister_algorithm = _jws_global_obj.unregister_algorithm | 
				
			||||||
 | 
					get_unverified_header = _jws_global_obj.get_unverified_header | 
				
			||||||
@ -0,0 +1,187 @@ | 
				
			|||||||
 | 
					import json | 
				
			||||||
 | 
					import warnings | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from calendar import timegm | 
				
			||||||
 | 
					from collections import Mapping | 
				
			||||||
 | 
					from datetime import datetime, timedelta | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from .api_jws import PyJWS | 
				
			||||||
 | 
					from .algorithms import Algorithm, get_default_algorithms  # NOQA | 
				
			||||||
 | 
					from .compat import string_types, timedelta_total_seconds | 
				
			||||||
 | 
					from .exceptions import ( | 
				
			||||||
 | 
					    DecodeError, ExpiredSignatureError, ImmatureSignatureError, | 
				
			||||||
 | 
					    InvalidAudienceError, InvalidIssuedAtError, | 
				
			||||||
 | 
					    InvalidIssuerError, MissingRequiredClaimError | 
				
			||||||
 | 
					) | 
				
			||||||
 | 
					from .utils import merge_dict | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class PyJWT(PyJWS): | 
				
			||||||
 | 
					    header_type = 'JWT' | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @staticmethod | 
				
			||||||
 | 
					    def _get_default_options(): | 
				
			||||||
 | 
					        return { | 
				
			||||||
 | 
					            'verify_signature': True, | 
				
			||||||
 | 
					            'verify_exp': True, | 
				
			||||||
 | 
					            'verify_nbf': True, | 
				
			||||||
 | 
					            'verify_iat': True, | 
				
			||||||
 | 
					            'verify_aud': True, | 
				
			||||||
 | 
					            'verify_iss': True, | 
				
			||||||
 | 
					            'require_exp': False, | 
				
			||||||
 | 
					            'require_iat': False, | 
				
			||||||
 | 
					            'require_nbf': False | 
				
			||||||
 | 
					        } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def encode(self, payload, key, algorithm='HS256', headers=None, | 
				
			||||||
 | 
					               json_encoder=None): | 
				
			||||||
 | 
					        # Check that we get a mapping | 
				
			||||||
 | 
					        if not isinstance(payload, Mapping): | 
				
			||||||
 | 
					            raise TypeError('Expecting a mapping object, as JWT only supports ' | 
				
			||||||
 | 
					                            'JSON objects as payloads.') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Payload | 
				
			||||||
 | 
					        for time_claim in ['exp', 'iat', 'nbf']: | 
				
			||||||
 | 
					            # Convert datetime to a intDate value in known time-format claims | 
				
			||||||
 | 
					            if isinstance(payload.get(time_claim), datetime): | 
				
			||||||
 | 
					                payload[time_claim] = timegm(payload[time_claim].utctimetuple()) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        json_payload = json.dumps( | 
				
			||||||
 | 
					            payload, | 
				
			||||||
 | 
					            separators=(',', ':'), | 
				
			||||||
 | 
					            cls=json_encoder | 
				
			||||||
 | 
					        ).encode('utf-8') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return super(PyJWT, self).encode( | 
				
			||||||
 | 
					            json_payload, key, algorithm, headers, json_encoder | 
				
			||||||
 | 
					        ) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def decode(self, jwt, key='', verify=True, algorithms=None, options=None, | 
				
			||||||
 | 
					               **kwargs): | 
				
			||||||
 | 
					        payload, signing_input, header, signature = self._load(jwt) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        decoded = super(PyJWT, self).decode(jwt, key, verify, algorithms, | 
				
			||||||
 | 
					                                            options, **kwargs) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            payload = json.loads(decoded.decode('utf-8')) | 
				
			||||||
 | 
					        except ValueError as e: | 
				
			||||||
 | 
					            raise DecodeError('Invalid payload string: %s' % e) | 
				
			||||||
 | 
					        if not isinstance(payload, Mapping): | 
				
			||||||
 | 
					            raise DecodeError('Invalid payload string: must be a json object') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if verify: | 
				
			||||||
 | 
					            merged_options = merge_dict(self.options, options) | 
				
			||||||
 | 
					            self._validate_claims(payload, merged_options, **kwargs) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return payload | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _validate_claims(self, payload, options, audience=None, issuer=None, | 
				
			||||||
 | 
					                         leeway=0, **kwargs): | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if 'verify_expiration' in kwargs: | 
				
			||||||
 | 
					            options['verify_exp'] = kwargs.get('verify_expiration', True) | 
				
			||||||
 | 
					            warnings.warn('The verify_expiration parameter is deprecated. ' | 
				
			||||||
 | 
					                          'Please use options instead.', DeprecationWarning) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if isinstance(leeway, timedelta): | 
				
			||||||
 | 
					            leeway = timedelta_total_seconds(leeway) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if not isinstance(audience, (string_types, type(None))): | 
				
			||||||
 | 
					            raise TypeError('audience must be a string or None') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self._validate_required_claims(payload, options) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        now = timegm(datetime.utcnow().utctimetuple()) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if 'iat' in payload and options.get('verify_iat'): | 
				
			||||||
 | 
					            self._validate_iat(payload, now, leeway) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if 'nbf' in payload and options.get('verify_nbf'): | 
				
			||||||
 | 
					            self._validate_nbf(payload, now, leeway) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if 'exp' in payload and options.get('verify_exp'): | 
				
			||||||
 | 
					            self._validate_exp(payload, now, leeway) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if options.get('verify_iss'): | 
				
			||||||
 | 
					            self._validate_iss(payload, issuer) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if options.get('verify_aud'): | 
				
			||||||
 | 
					            self._validate_aud(payload, audience) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _validate_required_claims(self, payload, options): | 
				
			||||||
 | 
					        if options.get('require_exp') and payload.get('exp') is None: | 
				
			||||||
 | 
					            raise MissingRequiredClaimError('exp') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if options.get('require_iat') and payload.get('iat') is None: | 
				
			||||||
 | 
					            raise MissingRequiredClaimError('iat') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if options.get('require_nbf') and payload.get('nbf') is None: | 
				
			||||||
 | 
					            raise MissingRequiredClaimError('nbf') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _validate_iat(self, payload, now, leeway): | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            iat = int(payload['iat']) | 
				
			||||||
 | 
					        except ValueError: | 
				
			||||||
 | 
					            raise DecodeError('Issued At claim (iat) must be an integer.') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if iat > (now + leeway): | 
				
			||||||
 | 
					            raise InvalidIssuedAtError('Issued At claim (iat) cannot be in' | 
				
			||||||
 | 
					                                       ' the future.') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _validate_nbf(self, payload, now, leeway): | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            nbf = int(payload['nbf']) | 
				
			||||||
 | 
					        except ValueError: | 
				
			||||||
 | 
					            raise DecodeError('Not Before claim (nbf) must be an integer.') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if nbf > (now + leeway): | 
				
			||||||
 | 
					            raise ImmatureSignatureError('The token is not yet valid (nbf)') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _validate_exp(self, payload, now, leeway): | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            exp = int(payload['exp']) | 
				
			||||||
 | 
					        except ValueError: | 
				
			||||||
 | 
					            raise DecodeError('Expiration Time claim (exp) must be an' | 
				
			||||||
 | 
					                              ' integer.') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if exp < (now - leeway): | 
				
			||||||
 | 
					            raise ExpiredSignatureError('Signature has expired') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _validate_aud(self, payload, audience): | 
				
			||||||
 | 
					        if audience is None and 'aud' not in payload: | 
				
			||||||
 | 
					            return | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if audience is not None and 'aud' not in payload: | 
				
			||||||
 | 
					            # Application specified an audience, but it could not be | 
				
			||||||
 | 
					            # verified since the token does not contain a claim. | 
				
			||||||
 | 
					            raise MissingRequiredClaimError('aud') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        audience_claims = payload['aud'] | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if isinstance(audience_claims, string_types): | 
				
			||||||
 | 
					            audience_claims = [audience_claims] | 
				
			||||||
 | 
					        if not isinstance(audience_claims, list): | 
				
			||||||
 | 
					            raise InvalidAudienceError('Invalid claim format in token') | 
				
			||||||
 | 
					        if any(not isinstance(c, string_types) for c in audience_claims): | 
				
			||||||
 | 
					            raise InvalidAudienceError('Invalid claim format in token') | 
				
			||||||
 | 
					        if audience not in audience_claims: | 
				
			||||||
 | 
					            raise InvalidAudienceError('Invalid audience') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _validate_iss(self, payload, issuer): | 
				
			||||||
 | 
					        if issuer is None: | 
				
			||||||
 | 
					            return | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if 'iss' not in payload: | 
				
			||||||
 | 
					            raise MissingRequiredClaimError('iss') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if payload['iss'] != issuer: | 
				
			||||||
 | 
					            raise InvalidIssuerError('Invalid issuer') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					_jwt_global_obj = PyJWT() | 
				
			||||||
 | 
					encode = _jwt_global_obj.encode | 
				
			||||||
 | 
					decode = _jwt_global_obj.decode | 
				
			||||||
 | 
					register_algorithm = _jwt_global_obj.register_algorithm | 
				
			||||||
 | 
					unregister_algorithm = _jwt_global_obj.unregister_algorithm | 
				
			||||||
 | 
					get_unverified_header = _jwt_global_obj.get_unverified_header | 
				
			||||||
@ -0,0 +1,54 @@ | 
				
			|||||||
 | 
					""" | 
				
			||||||
 | 
					The `compat` module provides support for backwards compatibility with older | 
				
			||||||
 | 
					versions of python, and compatibility wrappers around optional packages. | 
				
			||||||
 | 
					""" | 
				
			||||||
 | 
					# flake8: noqa | 
				
			||||||
 | 
					import sys | 
				
			||||||
 | 
					import hmac | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					PY3 = sys.version_info[0] == 3 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if PY3: | 
				
			||||||
 | 
					    string_types = str, | 
				
			||||||
 | 
					    text_type = str | 
				
			||||||
 | 
					    binary_type = bytes | 
				
			||||||
 | 
					else: | 
				
			||||||
 | 
					    string_types = basestring, | 
				
			||||||
 | 
					    text_type = unicode | 
				
			||||||
 | 
					    binary_type = str | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def timedelta_total_seconds(delta): | 
				
			||||||
 | 
					    try: | 
				
			||||||
 | 
					        delta.total_seconds | 
				
			||||||
 | 
					    except AttributeError: | 
				
			||||||
 | 
					        # On Python 2.6, timedelta instances do not have | 
				
			||||||
 | 
					        # a .total_seconds() method. | 
				
			||||||
 | 
					        total_seconds = delta.days * 24 * 60 * 60 + delta.seconds | 
				
			||||||
 | 
					    else: | 
				
			||||||
 | 
					        total_seconds = delta.total_seconds() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return total_seconds | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					try: | 
				
			||||||
 | 
					    constant_time_compare = hmac.compare_digest | 
				
			||||||
 | 
					except AttributeError: | 
				
			||||||
 | 
					    # Fallback for Python < 2.7 | 
				
			||||||
 | 
					    def constant_time_compare(val1, val2): | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        Returns True if the two strings are equal, False otherwise. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        The time taken is independent of the number of characters that match. | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        if len(val1) != len(val2): | 
				
			||||||
 | 
					            return False | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        result = 0 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for x, y in zip(val1, val2): | 
				
			||||||
 | 
					            result |= ord(x) ^ ord(y) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return result == 0 | 
				
			||||||
@ -0,0 +1,60 @@ | 
				
			|||||||
 | 
					# Note: This file is named py_ecdsa.py because import behavior in Python 2 | 
				
			||||||
 | 
					# would cause ecdsa.py to squash the ecdsa library that it depends upon. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import hashlib | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import ecdsa | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from jwt.algorithms import Algorithm | 
				
			||||||
 | 
					from jwt.compat import string_types, text_type | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ECAlgorithm(Algorithm): | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					    Performs signing and verification operations using | 
				
			||||||
 | 
					    ECDSA and the specified hash function | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    This class requires the ecdsa package to be installed. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    This is based off of the implementation in PyJWT 0.3.2 | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					    SHA256 = hashlib.sha256 | 
				
			||||||
 | 
					    SHA384 = hashlib.sha384 | 
				
			||||||
 | 
					    SHA512 = hashlib.sha512 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, hash_alg): | 
				
			||||||
 | 
					        self.hash_alg = hash_alg | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def prepare_key(self, key): | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if isinstance(key, ecdsa.SigningKey) or \ | 
				
			||||||
 | 
					           isinstance(key, ecdsa.VerifyingKey): | 
				
			||||||
 | 
					            return key | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if isinstance(key, string_types): | 
				
			||||||
 | 
					            if isinstance(key, text_type): | 
				
			||||||
 | 
					                key = key.encode('utf-8') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            # Attempt to load key. We don't know if it's | 
				
			||||||
 | 
					            # a Signing Key or a Verifying Key, so we try | 
				
			||||||
 | 
					            # the Verifying Key first. | 
				
			||||||
 | 
					            try: | 
				
			||||||
 | 
					                key = ecdsa.VerifyingKey.from_pem(key) | 
				
			||||||
 | 
					            except ecdsa.der.UnexpectedDER: | 
				
			||||||
 | 
					                key = ecdsa.SigningKey.from_pem(key) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        else: | 
				
			||||||
 | 
					            raise TypeError('Expecting a PEM-formatted key.') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return key | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def sign(self, msg, key): | 
				
			||||||
 | 
					        return key.sign(msg, hashfunc=self.hash_alg, | 
				
			||||||
 | 
					                        sigencode=ecdsa.util.sigencode_string) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def verify(self, msg, key, sig): | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            return key.verify(sig, msg, hashfunc=self.hash_alg, | 
				
			||||||
 | 
					                              sigdecode=ecdsa.util.sigdecode_string) | 
				
			||||||
 | 
					        except AssertionError: | 
				
			||||||
 | 
					            return False | 
				
			||||||
@ -0,0 +1,47 @@ | 
				
			|||||||
 | 
					import Crypto.Hash.SHA256 | 
				
			||||||
 | 
					import Crypto.Hash.SHA384 | 
				
			||||||
 | 
					import Crypto.Hash.SHA512 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from Crypto.PublicKey import RSA | 
				
			||||||
 | 
					from Crypto.Signature import PKCS1_v1_5 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from jwt.algorithms import Algorithm | 
				
			||||||
 | 
					from jwt.compat import string_types, text_type | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class RSAAlgorithm(Algorithm): | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					    Performs signing and verification operations using | 
				
			||||||
 | 
					    RSASSA-PKCS-v1_5 and the specified hash function. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    This class requires PyCrypto package to be installed. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    This is based off of the implementation in PyJWT 0.3.2 | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					    SHA256 = Crypto.Hash.SHA256 | 
				
			||||||
 | 
					    SHA384 = Crypto.Hash.SHA384 | 
				
			||||||
 | 
					    SHA512 = Crypto.Hash.SHA512 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, hash_alg): | 
				
			||||||
 | 
					        self.hash_alg = hash_alg | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def prepare_key(self, key): | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if isinstance(key, RSA._RSAobj): | 
				
			||||||
 | 
					            return key | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if isinstance(key, string_types): | 
				
			||||||
 | 
					            if isinstance(key, text_type): | 
				
			||||||
 | 
					                key = key.encode('utf-8') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            key = RSA.importKey(key) | 
				
			||||||
 | 
					        else: | 
				
			||||||
 | 
					            raise TypeError('Expecting a PEM- or RSA-formatted key.') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return key | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def sign(self, msg, key): | 
				
			||||||
 | 
					        return PKCS1_v1_5.new(key).sign(self.hash_alg.new(msg)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def verify(self, msg, key, sig): | 
				
			||||||
 | 
					        return PKCS1_v1_5.new(key).verify(self.hash_alg.new(msg), sig) | 
				
			||||||
@ -0,0 +1,48 @@ | 
				
			|||||||
 | 
					class InvalidTokenError(Exception): | 
				
			||||||
 | 
					    pass | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class DecodeError(InvalidTokenError): | 
				
			||||||
 | 
					    pass | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ExpiredSignatureError(InvalidTokenError): | 
				
			||||||
 | 
					    pass | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class InvalidAudienceError(InvalidTokenError): | 
				
			||||||
 | 
					    pass | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class InvalidIssuerError(InvalidTokenError): | 
				
			||||||
 | 
					    pass | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class InvalidIssuedAtError(InvalidTokenError): | 
				
			||||||
 | 
					    pass | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ImmatureSignatureError(InvalidTokenError): | 
				
			||||||
 | 
					    pass | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class InvalidKeyError(Exception): | 
				
			||||||
 | 
					    pass | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class InvalidAlgorithmError(InvalidTokenError): | 
				
			||||||
 | 
					    pass | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class MissingRequiredClaimError(InvalidTokenError): | 
				
			||||||
 | 
					    def __init__(self, claim): | 
				
			||||||
 | 
					        self.claim = claim | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __str__(self): | 
				
			||||||
 | 
					        return 'Token is missing the "%s" claim' % self.claim | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Compatibility aliases (deprecated) | 
				
			||||||
 | 
					ExpiredSignature = ExpiredSignatureError | 
				
			||||||
 | 
					InvalidAudience = InvalidAudienceError | 
				
			||||||
 | 
					InvalidIssuer = InvalidIssuerError | 
				
			||||||
@ -0,0 +1,67 @@ | 
				
			|||||||
 | 
					import base64 | 
				
			||||||
 | 
					import binascii | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					try: | 
				
			||||||
 | 
					    from cryptography.hazmat.primitives.asymmetric.utils import ( | 
				
			||||||
 | 
					        decode_rfc6979_signature, encode_rfc6979_signature | 
				
			||||||
 | 
					    ) | 
				
			||||||
 | 
					except ImportError: | 
				
			||||||
 | 
					    pass | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def base64url_decode(input): | 
				
			||||||
 | 
					    rem = len(input) % 4 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if rem > 0: | 
				
			||||||
 | 
					        input += b'=' * (4 - rem) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return base64.urlsafe_b64decode(input) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def base64url_encode(input): | 
				
			||||||
 | 
					    return base64.urlsafe_b64encode(input).replace(b'=', b'') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def merge_dict(original, updates): | 
				
			||||||
 | 
					    if not updates: | 
				
			||||||
 | 
					        return original | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    try: | 
				
			||||||
 | 
					        merged_options = original.copy() | 
				
			||||||
 | 
					        merged_options.update(updates) | 
				
			||||||
 | 
					    except (AttributeError, ValueError) as e: | 
				
			||||||
 | 
					        raise TypeError('original and updates must be a dictionary: %s' % e) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return merged_options | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def number_to_bytes(num, num_bytes): | 
				
			||||||
 | 
					    padded_hex = '%0*x' % (2 * num_bytes, num) | 
				
			||||||
 | 
					    big_endian = binascii.a2b_hex(padded_hex.encode('ascii')) | 
				
			||||||
 | 
					    return big_endian | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def bytes_to_number(string): | 
				
			||||||
 | 
					    return int(binascii.b2a_hex(string), 16) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def der_to_raw_signature(der_sig, curve): | 
				
			||||||
 | 
					    num_bits = curve.key_size | 
				
			||||||
 | 
					    num_bytes = (num_bits + 7) // 8 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    r, s = decode_rfc6979_signature(der_sig) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return number_to_bytes(r, num_bytes) + number_to_bytes(s, num_bytes) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def raw_to_der_signature(raw_sig, curve): | 
				
			||||||
 | 
					    num_bits = curve.key_size | 
				
			||||||
 | 
					    num_bytes = (num_bits + 7) // 8 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if len(raw_sig) != 2 * num_bytes: | 
				
			||||||
 | 
					        raise ValueError('Invalid signature') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    r = bytes_to_number(raw_sig[:num_bytes]) | 
				
			||||||
 | 
					    s = bytes_to_number(raw_sig[num_bytes:]) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return encode_rfc6979_signature(r, s) | 
				
			||||||
					Loading…
					
					
				
		Reference in new issue