PyJWT-1.5.3/0000755000076500000240000000000013153565454013413 5ustar jpadillastaff00000000000000PyJWT-1.5.3/AUTHORS0000644000076500000240000000100512532343527014452 0ustar jpadillastaff00000000000000PyJWT lead developer --------------------- - jpadilla Original author ------------------ - progrium Patches and Suggestions ----------------------- - Boris Feld - Åsmund Ødegård Adding support for RSA-SHA256 privat/public signature. - Mark Adams - Wouter Bolsterlee - Michael Davis PyJWT-1.5.3/CHANGELOG.md0000644000076500000240000002043313153564676015233 0ustar jpadillastaff00000000000000Change Log ========================================================================= All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). [Unreleased][unreleased] ------------------------------------------------------------------------- ### Changed ### Fixed ### Added [v1.5.3][1.5.3] ------------------------------------------------------------------------- ### Changed - Increase required version of the cryptography package to >=1.4.0. ### Fixed - Remove uses of deprecated functions from the cryptography package. - Warn about missing `algorithms` param to `decode()` only when `verify` param is `True` [#281][281] [v1.5.2][1.5.2] ------------------------------------------------------------------------- ### Fixed - Ensure correct arguments order in decode super call [7c1e61d][7c1e61d] [v1.5.1][1.5.1] ------------------------------------------------------------------------- ### Changed - Change optparse for argparse. [#238][238] ### Fixed - Guard against PKCS1 PEM encododed public keys [#277][277] - Add deprecation warning when decoding without specifying `algorithms` [#277][277] - Improve deprecation messages [#270][270] - PyJWT.decode: move verify param into options [#271][271] ### Added - Support for Python 3.6 [#262][262] - Expose jwt.InvalidAlgorithmError [#264][264] [v1.5.0][1.5.0] ------------------------------------------------------------------------- ### Changed - Add support for ECDSA public keys in RFC 4253 (OpenSSH) format [#244][244] - Renamed commandline script `jwt` to `jwt-cli` to avoid issues with the script clobbering the `jwt` module in some circumstances. [#187][187] - Better error messages when using an algorithm that requires the cryptography package, but it isn't available [#230][230] - Tokens with future 'iat' values are no longer rejected [#190][190] - Non-numeric 'iat' values now raise InvalidIssuedAtError instead of DecodeError - Remove rejection of future 'iat' claims [#252][252] ### Fixed - Add back 'ES512' for backward compatibility (for now) [#225][225] - Fix incorrectly named ECDSA algorithm [#219][219] - Fix rpm build [#196][196] ### Added - Add JWK support for HMAC and RSA keys [#202][202] [v1.4.2][1.4.2] ------------------------------------------------------------------------- ### Fixed - A PEM-formatted key encoded as bytes could cause a `TypeError` to be raised [#213][213] [v1.4.1][1.4.1] ------------------------------------------------------------------------- ### Fixed - Newer versions of Pytest could not detect warnings properly [#182][182] - Non-string 'kid' value now raises `InvalidTokenError` [#174][174] - `jwt.decode(None)` now gracefully fails with `InvalidTokenError` [#183][183] [v1.4][1.4.0] ------------------------------------------------------------------------- ### Fixed - Exclude Python cache files from PyPI releases. ### Added - Added new options to require certain claims (require_nbf, require_iat, require_exp) and raise `MissingRequiredClaimError` if they are not present. - If `audience=` or `issuer=` is specified but the claim is not present, `MissingRequiredClaimError` is now raised instead of `InvalidAudienceError` and `InvalidIssuerError` [v1.3][1.3.0] ------------------------------------------------------------------------- ### Fixed - ECDSA (ES256, ES384, ES512) signatures are now being properly serialized [#158][158] - RSA-PSS (PS256, PS384, PS512) signatures now use the proper salt length for PSS padding. [#163][163] ### Added - Added a new `jwt.get_unverified_header()` to parse and return the header portion of a token prior to signature verification. ### Removed - Python 3.2 is no longer a supported platform. This version of Python is rarely used. Users affected by this should upgrade to 3.3+. [v1.2.0][1.2.0] ------------------------------------------------------------------------- ### Fixed - Added back `verify_expiration=` argument to `jwt.decode()` that was erroneously removed in [v1.1.0][1.1.0]. ### Changed - Refactored JWS-specific logic out of PyJWT and into PyJWS superclass. [#141][141] ### Deprecated - `verify_expiration=` argument to `jwt.decode()` is now deprecated and will be removed in a future version. Use the `option=` argument instead. [v1.1.0][1.1.0] ------------------------------------------------------------------------- ### Added - Added support for PS256, PS384, and PS512 algorithms. [#132][132] - Added flexible and complete verification options during decode. [#131][131] - Added this CHANGELOG.md file. ### Deprecated - Deprecated usage of the .decode(..., verify=False) parameter. ### Fixed - Fixed command line encoding. [#128][128] [v1.0.1][1.0.1] ------------------------------------------------------------------------- ### Fixed - Include jwt/contrib' and jwt/contrib/algorithms` in setup.py so that they will actually be included when installing. [882524d][882524d] - Fix bin/jwt after removing jwt.header(). [bd57b02][bd57b02] [v1.0.0][1.0.0] ------------------------------------------------------------------------- ### Changed - Moved `jwt.api.header` out of the public API. [#85][85] - Added README details how to extract public / private keys from an x509 certificate. [#100][100] - Refactor api.py functions into an object (`PyJWT`). [#101][101] - Added support for PyCrypto and ecdsa when cryptography isn't available. [#101][103] ### Fixed - Fixed a security vulnerability where `alg=None` header could bypass signature verification. [#109][109] - Fixed a security vulnerability by adding support for a whitelist of allowed `alg` values `jwt.decode(algorithms=[])`. [#110][110] [unreleased]: https://github.com/jpadilla/pyjwt/compare/1.4.2...HEAD [1.0.0]: https://github.com/jpadilla/pyjwt/compare/0.4.3...1.0.0 [1.0.1]: https://github.com/jpadilla/pyjwt/compare/1.0.0...1.0.1 [1.0.1]: https://github.com/jpadilla/pyjwt/compare/1.0.0...1.0.1 [1.0.1]: https://github.com/jpadilla/pyjwt/compare/1.0.0...1.0.1 [1.1.0]: https://github.com/jpadilla/pyjwt/compare/1.0.1...1.1.0 [1.2.0]: https://github.com/jpadilla/pyjwt/compare/1.1.0...1.2.0 [1.3.0]: https://github.com/jpadilla/pyjwt/compare/1.2.0...1.3.0 [1.4.0]: https://github.com/jpadilla/pyjwt/compare/1.3.0...1.4.0 [1.4.1]: https://github.com/jpadilla/pyjwt/compare/1.4.0...1.4.1 [1.4.2]: https://github.com/jpadilla/pyjwt/compare/1.4.1...1.4.2 [1.5.0]: https://github.com/jpadilla/pyjwt/compare/1.4.2...1.5.0 [1.5.1]: https://github.com/jpadilla/pyjwt/compare/1.5.0...1.5.1 [1.5.2]: https://github.com/jpadilla/pyjwt/compare/1.5.1...1.5.2 [1.5.3]: https://github.com/jpadilla/pyjwt/compare/1.5.2...1.5.3 [109]: https://github.com/jpadilla/pyjwt/pull/109 [110]: https://github.com/jpadilla/pyjwt/pull/110 [100]: https://github.com/jpadilla/pyjwt/pull/100 [101]: https://github.com/jpadilla/pyjwt/pull/101 [103]: https://github.com/jpadilla/pyjwt/pull/103 [85]: https://github.com/jpadilla/pyjwt/pull/85 [882524d]: https://github.com/jpadilla/pyjwt/commit/882524d [bd57b02]: https://github.com/jpadilla/pyjwt/commit/bd57b02 [131]: https://github.com/jpadilla/pyjwt/pull/131 [132]: https://github.com/jpadilla/pyjwt/pull/132 [128]: https://github.com/jpadilla/pyjwt/pull/128 [141]: https://github.com/jpadilla/pyjwt/pull/141 [158]: https://github.com/jpadilla/pyjwt/pull/158 [163]: https://github.com/jpadilla/pyjwt/pull/163 [174]: https://github.com/jpadilla/pyjwt/pull/174 [182]: https://github.com/jpadilla/pyjwt/pull/182 [183]: https://github.com/jpadilla/pyjwt/pull/183 [190]: https://github.com/jpadilla/pyjwt/pull/190 [213]: https://github.com/jpadilla/pyjwt/pull/214 [244]: https://github.com/jpadilla/pyjwt/pull/244 [202]: https://github.com/jpadilla/pyjwt/pull/202 [252]: https://github.com/jpadilla/pyjwt/pull/252 [225]: https://github.com/jpadilla/pyjwt/pull/225 [219]: https://github.com/jpadilla/pyjwt/pull/219 [196]: https://github.com/jpadilla/pyjwt/pull/196 [187]: https://github.com/jpadilla/pyjwt/pull/187 [230]: https://github.com/jpadilla/pyjwt/pull/230 [238]: https://github.com/jpadilla/pyjwt/pull/238 [262]: https://github.com/jpadilla/pyjwt/pull/262 [264]: https://github.com/jpadilla/pyjwt/pull/264 [270]: https://github.com/jpadilla/pyjwt/pull/270 [271]: https://github.com/jpadilla/pyjwt/pull/271 [277]: https://github.com/jpadilla/pyjwt/pull/277 [281]: https://github.com/jpadilla/pyjwt/pull/281 [7c1e61d]: https://github.com/jpadilla/pyjwt/commit/7c1e61dde27bafe16e7d1bb6e35199e778962742 PyJWT-1.5.3/jwt/0000755000076500000240000000000013153565454014217 5ustar jpadillastaff00000000000000PyJWT-1.5.3/jwt/__init__.py0000644000076500000240000000137113153564716016332 0ustar jpadillastaff00000000000000# -*- 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.5.3' __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, InvalidAlgorithmError, InvalidAudienceError, ExpiredSignatureError, ImmatureSignatureError, InvalidIssuedAtError, InvalidIssuerError, ExpiredSignature, InvalidAudience, InvalidIssuer, MissingRequiredClaimError ) PyJWT-1.5.3/jwt/__main__.py0000644000076500000240000001006713122544212016276 0ustar jpadillastaff00000000000000#!/usr/bin/env python from __future__ import absolute_import, print_function import argparse import json import sys import time from . import DecodeError, __version__, decode, encode def encode_payload(args): # Try to encode if args.key is None: raise ValueError('Key is required when encoding. See --help for usage.') # Build payload object to encode payload = {} for arg in args.payload: 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 token = encode( payload, key=args.key, algorithm=args.algorithm ) return token.decode('utf-8') def decode_payload(args): try: if sys.stdin.isatty(): token = sys.stdin.read() else: token = args.token token = token.encode('utf-8') data = decode(token, key=args.key, verify=args.verify) return json.dumps(data) except DecodeError as e: raise DecodeError('There was an error decoding the token: %s' % e) def build_argparser(): usage = ''' Encodes or decodes JSON Web Tokens based on input. %(prog)s [options] [options] input Decoding examples: %(prog)s --key=secret decode json.web.token %(prog)s decode --no-verify json.web.token Encoding requires the key option and takes space separated key/value pairs separated by equals (=) as input. Examples: %(prog)s --key=secret encode iss=me exp=1302049071 %(prog)s --key=secret encode foo=bar exp=+10 The exp key is special and can take an offset to current Unix time. ''' arg_parser = argparse.ArgumentParser( prog='pyjwt', usage=usage ) arg_parser.add_argument( '-v', '--version', action='version', version='%(prog)s ' + __version__ ) arg_parser.add_argument( '--key', dest='key', metavar='KEY', default=None, help='set the secret key to sign with' ) arg_parser.add_argument( '--alg', dest='algorithm', metavar='ALG', default='HS256', help='set crypto algorithm to sign with. default=HS256' ) subparsers = arg_parser.add_subparsers( title='PyJWT subcommands', description='valid subcommands', help='additional help' ) # Encode subcommand encode_parser = subparsers.add_parser('encode', help='use to encode a supplied payload') payload_help = """Payload to encode. Must be a space separated list of key/value pairs separated by equals (=) sign.""" encode_parser.add_argument('payload', nargs='+', help=payload_help) encode_parser.set_defaults(func=encode_payload) # Decode subcommand decode_parser = subparsers.add_parser('decode', help='use to decode a supplied JSON web token') decode_parser.add_argument('token', help='JSON web token to decode.') decode_parser.add_argument( '-n', '--no-verify', action='store_false', dest='verify', default=True, help='ignore signature and claims verification on decode' ) decode_parser.set_defaults(func=decode_payload) return arg_parser def main(): arg_parser = build_argparser() try: arguments = arg_parser.parse_args(sys.argv[1:]) output = arguments.func(arguments) print(output) except Exception as e: print('There was an unforseen error: ', e) arg_parser.print_help() PyJWT-1.5.3/jwt/algorithms.py0000644000076500000240000003203013153564550016734 0ustar jpadillastaff00000000000000import hashlib import hmac import json from .compat import constant_time_compare, string_types from .exceptions import InvalidKeyError from .utils import ( base64url_decode, base64url_encode, der_to_raw_signature, force_bytes, force_unicode, from_base64url_uint, raw_to_der_signature, to_base64url_uint ) 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, RSAPrivateNumbers, RSAPublicNumbers, rsa_recover_prime_factors, rsa_crt_dmp1, rsa_crt_dmq1, rsa_crt_iqmp ) 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 requires_cryptography = set(['RS256', 'RS384', 'RS512', 'ES256', 'ES384', 'ES521', 'ES512', 'PS256', 'PS384', 'PS512']) 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), 'ES521': ECAlgorithm(ECAlgorithm.SHA512), 'ES512': ECAlgorithm(ECAlgorithm.SHA512), # Backward compat for #219 fix '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 @staticmethod def to_jwk(key_obj): """ Serializes a given RSA key into a JWK """ raise NotImplementedError @staticmethod def from_jwk(jwk): """ Deserializes a given RSA key from JWK back into a PublicKey or PrivateKey object """ 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): key = force_bytes(key) invalid_strings = [ b'-----BEGIN PUBLIC KEY-----', b'-----BEGIN CERTIFICATE-----', b'-----BEGIN RSA PUBLIC KEY-----', 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 @staticmethod def to_jwk(key_obj): return json.dumps({ 'k': force_unicode(base64url_encode(force_bytes(key_obj))), 'kty': 'oct' }) @staticmethod def from_jwk(jwk): obj = json.loads(jwk) if obj.get('kty') != 'oct': raise InvalidKeyError('Not an HMAC key') return base64url_decode(obj['k']) 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): key = force_bytes(key) 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 @staticmethod def to_jwk(key_obj): obj = None if getattr(key_obj, 'private_numbers', None): # Private key numbers = key_obj.private_numbers() obj = { 'kty': 'RSA', 'key_ops': ['sign'], 'n': force_unicode(to_base64url_uint(numbers.public_numbers.n)), 'e': force_unicode(to_base64url_uint(numbers.public_numbers.e)), 'd': force_unicode(to_base64url_uint(numbers.d)), 'p': force_unicode(to_base64url_uint(numbers.p)), 'q': force_unicode(to_base64url_uint(numbers.q)), 'dp': force_unicode(to_base64url_uint(numbers.dmp1)), 'dq': force_unicode(to_base64url_uint(numbers.dmq1)), 'qi': force_unicode(to_base64url_uint(numbers.iqmp)) } elif getattr(key_obj, 'verify', None): # Public key numbers = key_obj.public_numbers() obj = { 'kty': 'RSA', 'key_ops': ['verify'], 'n': force_unicode(to_base64url_uint(numbers.n)), 'e': force_unicode(to_base64url_uint(numbers.e)) } else: raise InvalidKeyError('Not a public or private key') return json.dumps(obj) @staticmethod def from_jwk(jwk): try: obj = json.loads(jwk) except ValueError: raise InvalidKeyError('Key is not valid JSON') if obj.get('kty') != 'RSA': raise InvalidKeyError('Not an RSA key') if 'd' in obj and 'e' in obj and 'n' in obj: # Private key if 'oth' in obj: raise InvalidKeyError('Unsupported RSA private key: > 2 primes not supported') other_props = ['p', 'q', 'dp', 'dq', 'qi'] props_found = [prop in obj for prop in other_props] any_props_found = any(props_found) if any_props_found and not all(props_found): raise InvalidKeyError('RSA key must include all parameters if any are present besides d') public_numbers = RSAPublicNumbers( from_base64url_uint(obj['e']), from_base64url_uint(obj['n']) ) if any_props_found: numbers = RSAPrivateNumbers( d=from_base64url_uint(obj['d']), p=from_base64url_uint(obj['p']), q=from_base64url_uint(obj['q']), dmp1=from_base64url_uint(obj['dp']), dmq1=from_base64url_uint(obj['dq']), iqmp=from_base64url_uint(obj['qi']), public_numbers=public_numbers ) else: d = from_base64url_uint(obj['d']) p, q = rsa_recover_prime_factors( public_numbers.n, d, public_numbers.e ) numbers = RSAPrivateNumbers( d=d, p=p, q=q, dmp1=rsa_crt_dmp1(d, p), dmq1=rsa_crt_dmq1(d, q), iqmp=rsa_crt_iqmp(p, q), public_numbers=public_numbers ) return numbers.private_key(default_backend()) elif 'n' in obj and 'e' in obj: # Public key numbers = RSAPublicNumbers( from_base64url_uint(obj['e']), from_base64url_uint(obj['n']) ) return numbers.public_key(default_backend()) else: raise InvalidKeyError('Not a public or private key') def sign(self, msg, key): return key.sign(msg, padding.PKCS1v15(), self.hash_alg()) def verify(self, msg, key, sig): try: key.verify(sig, msg, padding.PKCS1v15(), self.hash_alg()) 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): key = force_bytes(key) # 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: if key.startswith(b'ecdsa-sha2-'): key = load_ssh_public_key(key, backend=default_backend()) else: 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): der_sig = key.sign(msg, ec.ECDSA(self.hash_alg())) 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 try: key.verify(der_sig, msg, ec.ECDSA(self.hash_alg())) return True except InvalidSignature: return False class RSAPSSAlgorithm(RSAAlgorithm): """ Performs a signature using RSASSA-PSS with MGF1 """ def sign(self, msg, key): return key.sign( msg, padding.PSS( mgf=padding.MGF1(self.hash_alg()), salt_length=self.hash_alg.digest_size ), self.hash_alg() ) def verify(self, msg, key, sig): try: key.verify( sig, msg, padding.PSS( mgf=padding.MGF1(self.hash_alg()), salt_length=self.hash_alg.digest_size ), self.hash_alg() ) return True except InvalidSignature: return False PyJWT-1.5.3/jwt/api_jws.py0000644000076500000240000001660313153564550016227 0ustar jpadillastaff00000000000000import binascii import json import warnings from collections import Mapping from .algorithms import ( Algorithm, get_default_algorithms, has_crypto, requires_cryptography # NOQA ) from .compat import binary_type, string_types, text_type from .exceptions import DecodeError, InvalidAlgorithmError, InvalidTokenError from .utils import base64url_decode, base64url_encode, force_bytes, 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 = force_bytes( json.dumps( header, separators=(',', ':'), cls=json_encoder ) ) 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: if not has_crypto and algorithm in requires_cryptography: raise NotImplementedError( "Algorithm '%s' could not be found. Do you have cryptography " "installed?" % algorithm ) else: 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): merged_options = merge_dict(self.options, options) verify_signature = merged_options['verify_signature'] if verify_signature and not algorithms: warnings.warn( 'It is strongly recommended that you pass in a ' + 'value for the "algorithms" argument when calling decode(). ' + 'This argument will be mandatory in a future version.', DeprecationWarning ) payload, signing_input, header, signature = self._load(jws) if not verify: warnings.warn('The verify parameter is deprecated. ' 'Please use verify_signature in options instead.', DeprecationWarning, stacklevel=2) elif verify_signature: self._verify_signature(payload, signing_input, header, signature, key, algorithms) 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 PyJWT-1.5.3/jwt/api_jwt.py0000644000076500000240000001563413153564550016233 0ustar jpadillastaff00000000000000import 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): if verify and not algorithms: warnings.warn( 'It is strongly recommended that you pass in a ' + 'value for the "algorithms" argument when calling decode(). ' + 'This argument will be mandatory in a future version.', DeprecationWarning ) payload, signing_input, header, signature = self._load(jwt) if options is None: options = {'verify_signature': verify} else: options.setdefault('verify_signature', verify) decoded = super(PyJWT, self).decode( jwt, key=key, algorithms=algorithms, options=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 verify_exp in 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: int(payload['iat']) except ValueError: raise InvalidIssuedAtError('Issued At claim (iat) must be an integer.') 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 PyJWT-1.5.3/jwt/compat.py0000644000076500000240000000337013060030443016035 0ustar jpadillastaff00000000000000""" The `compat` module provides support for backwards compatibility with older versions of python, and compatibility wrappers around optional packages. """ # flake8: noqa import hmac import struct import sys PY3 = sys.version_info[0] == 3 if PY3: text_type = str binary_type = bytes else: text_type = unicode binary_type = str string_types = (text_type, binary_type) 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 # Use int.to_bytes if it exists (Python 3) if getattr(int, 'to_bytes', None): def bytes_from_int(val): remaining = val byte_length = 0 while remaining != 0: remaining = remaining >> 8 byte_length += 1 return val.to_bytes(byte_length, 'big', signed=False) else: def bytes_from_int(val): buf = [] while val: val, remainder = divmod(val, 256) buf.append(remainder) buf.reverse() return struct.pack('%sB' % len(buf), *buf) PyJWT-1.5.3/jwt/contrib/0000755000076500000240000000000013153565454015657 5ustar jpadillastaff00000000000000PyJWT-1.5.3/jwt/contrib/__init__.py0000644000076500000240000000000012532343527017751 0ustar jpadillastaff00000000000000PyJWT-1.5.3/jwt/contrib/algorithms/0000755000076500000240000000000013153565454020030 5ustar jpadillastaff00000000000000PyJWT-1.5.3/jwt/contrib/algorithms/__init__.py0000644000076500000240000000000012532343527022122 0ustar jpadillastaff00000000000000PyJWT-1.5.3/jwt/contrib/algorithms/py_ecdsa.py0000644000076500000240000000335312532343527022170 0ustar jpadillastaff00000000000000# 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 PyJWT-1.5.3/jwt/contrib/algorithms/pycrypto.py0000644000076500000240000000234212532343527022267 0ustar jpadillastaff00000000000000import 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) PyJWT-1.5.3/jwt/exceptions.py0000644000076500000240000000151112552053647016746 0ustar jpadillastaff00000000000000class 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 PyJWT-1.5.3/jwt/utils.py0000644000076500000240000000510513060030443015710 0ustar jpadillastaff00000000000000import base64 import binascii import struct from .compat import binary_type, bytes_from_int, text_type try: from cryptography.hazmat.primitives.asymmetric.utils import ( decode_dss_signature, encode_dss_signature ) except ImportError: pass def force_unicode(value): if isinstance(value, binary_type): return value.decode('utf-8') elif isinstance(value, text_type): return value else: raise TypeError('Expected a string value') def force_bytes(value): if isinstance(value, text_type): return value.encode('utf-8') elif isinstance(value, binary_type): return value else: raise TypeError('Expected a string value') def base64url_decode(input): if isinstance(input, text_type): input = input.encode('ascii') 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 to_base64url_uint(val): if val < 0: raise ValueError('Must be a positive integer') int_bytes = bytes_from_int(val) if len(int_bytes) == 0: int_bytes = b'\x00' return base64url_encode(int_bytes) def from_base64url_uint(val): if isinstance(val, text_type): val = val.encode('ascii') data = base64url_decode(val) buf = struct.unpack('%sB' % len(data), data) return int(''.join(["%02x" % byte for byte in buf]), 16) 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_dss_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_dss_signature(r, s) PyJWT-1.5.3/LICENSE0000644000076500000240000000207012532343527014412 0ustar jpadillastaff00000000000000The MIT License (MIT) Copyright (c) 2015 José Padilla Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. PyJWT-1.5.3/MANIFEST.in0000644000076500000240000000024113060030443015124 0ustar jpadillastaff00000000000000include README.rst include CHANGELOG.md include LICENSE include AUTHORS include tox.ini recursive-exclude * __pycache__ recursive-exclude * *.py[co] graft tests PyJWT-1.5.3/PKG-INFO0000644000076500000240000000605313153565454014514 0ustar jpadillastaff00000000000000Metadata-Version: 1.1 Name: PyJWT Version: 1.5.3 Summary: JSON Web Token implementation in Python Home-page: http://github.com/jpadilla/pyjwt Author: Jose Padilla Author-email: hello@jpadilla.com License: MIT Description-Content-Type: UNKNOWN Description: PyJWT ===== .. image:: https://secure.travis-ci.org/jpadilla/pyjwt.svg?branch=master :target: http://travis-ci.org/jpadilla/pyjwt?branch=master .. image:: https://ci.appveyor.com/api/projects/status/h8nt70aqtwhht39t?svg=true :target: https://ci.appveyor.com/project/jpadilla/pyjwt .. image:: https://img.shields.io/pypi/v/pyjwt.svg :target: https://pypi.python.org/pypi/pyjwt .. image:: https://coveralls.io/repos/jpadilla/pyjwt/badge.svg?branch=master :target: https://coveralls.io/r/jpadilla/pyjwt?branch=master .. image:: https://readthedocs.org/projects/pyjwt/badge/?version=latest :target: https://pyjwt.readthedocs.io A Python implementation of `RFC 7519 `_. Original implementation was written by `@progrium `_. Installing ---------- Install with **pip**: .. code-block:: sh $ pip install PyJWT Usage ----- .. code:: python >>> import jwt >>> encoded = jwt.encode({'some': 'payload'}, 'secret', algorithm='HS256') 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzb21lIjoicGF5bG9hZCJ9.4twFt5NiznN84AWoo1d7KO1T_yoc0Z6XOpOVswacPZg' >>> jwt.decode(encoded, 'secret', algorithms=['HS256']) {'some': 'payload'} Command line ------------ Usage:: pyjwt [options] INPUT Decoding examples:: pyjwt --key=secret TOKEN pyjwt --no-verify TOKEN See more options executing ``pyjwt --help``. Documentation ------------- View the full docs online at https://pyjwt.readthedocs.io/en/latest/ Tests ----- You can run tests from the project root after cloning with: .. code-block:: sh $ python setup.py test 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: Programming Language :: Python :: 3.6 Classifier: Topic :: Utilities PyJWT-1.5.3/PyJWT.egg-info/0000755000076500000240000000000013153565454016062 5ustar jpadillastaff00000000000000PyJWT-1.5.3/PyJWT.egg-info/dependency_links.txt0000644000076500000240000000000113153565454022130 0ustar jpadillastaff00000000000000 PyJWT-1.5.3/PyJWT.egg-info/entry_points.txt0000644000076500000240000000005513153565454021360 0ustar jpadillastaff00000000000000[console_scripts] pyjwt = jwt.__main__:main PyJWT-1.5.3/PyJWT.egg-info/PKG-INFO0000644000076500000240000000605313153565454017163 0ustar jpadillastaff00000000000000Metadata-Version: 1.1 Name: PyJWT Version: 1.5.3 Summary: JSON Web Token implementation in Python Home-page: http://github.com/jpadilla/pyjwt Author: Jose Padilla Author-email: hello@jpadilla.com License: MIT Description-Content-Type: UNKNOWN Description: PyJWT ===== .. image:: https://secure.travis-ci.org/jpadilla/pyjwt.svg?branch=master :target: http://travis-ci.org/jpadilla/pyjwt?branch=master .. image:: https://ci.appveyor.com/api/projects/status/h8nt70aqtwhht39t?svg=true :target: https://ci.appveyor.com/project/jpadilla/pyjwt .. image:: https://img.shields.io/pypi/v/pyjwt.svg :target: https://pypi.python.org/pypi/pyjwt .. image:: https://coveralls.io/repos/jpadilla/pyjwt/badge.svg?branch=master :target: https://coveralls.io/r/jpadilla/pyjwt?branch=master .. image:: https://readthedocs.org/projects/pyjwt/badge/?version=latest :target: https://pyjwt.readthedocs.io A Python implementation of `RFC 7519 `_. Original implementation was written by `@progrium `_. Installing ---------- Install with **pip**: .. code-block:: sh $ pip install PyJWT Usage ----- .. code:: python >>> import jwt >>> encoded = jwt.encode({'some': 'payload'}, 'secret', algorithm='HS256') 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzb21lIjoicGF5bG9hZCJ9.4twFt5NiznN84AWoo1d7KO1T_yoc0Z6XOpOVswacPZg' >>> jwt.decode(encoded, 'secret', algorithms=['HS256']) {'some': 'payload'} Command line ------------ Usage:: pyjwt [options] INPUT Decoding examples:: pyjwt --key=secret TOKEN pyjwt --no-verify TOKEN See more options executing ``pyjwt --help``. Documentation ------------- View the full docs online at https://pyjwt.readthedocs.io/en/latest/ Tests ----- You can run tests from the project root after cloning with: .. code-block:: sh $ python setup.py test 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: Programming Language :: Python :: 3.6 Classifier: Topic :: Utilities PyJWT-1.5.3/PyJWT.egg-info/requires.txt0000644000076500000240000000017213153565454020462 0ustar jpadillastaff00000000000000 [crypto] cryptography>=1.4 [flake8] flake8 flake8-import-order pep8-naming [test] pytest<4,>3 pytest-cov pytest-runner PyJWT-1.5.3/PyJWT.egg-info/SOURCES.txt0000644000076500000240000000332713153565454017753 0ustar jpadillastaff00000000000000AUTHORS CHANGELOG.md LICENSE MANIFEST.in README.rst 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/__init__.pyc tests/compat.py tests/compat.pyc tests/test_algorithms.py tests/test_api_jws.py tests/test_api_jwt.py tests/test_cli.py tests/test_compat.py tests/test_exceptions.py tests/test_jwt.py tests/test_utils.py tests/utils.py tests/utils.pyc tests/__pycache__/test_algorithms.cpython-27-PYTEST.pyc tests/__pycache__/test_api_jws.cpython-27-PYTEST.pyc tests/__pycache__/test_api_jwt.cpython-27-PYTEST.pyc tests/__pycache__/test_cli.cpython-27-PYTEST.pyc tests/__pycache__/test_compat.cpython-27-PYTEST.pyc tests/__pycache__/test_exceptions.cpython-27-PYTEST.pyc tests/__pycache__/test_jwt.cpython-27-PYTEST.pyc tests/__pycache__/test_utils.cpython-27-PYTEST.pyc tests/contrib/__init__.py tests/contrib/__init__.pyc tests/contrib/test_algorithms.py tests/contrib/__pycache__/test_algorithms.cpython-27-PYTEST.pyc tests/keys/__init__.py tests/keys/__init__.pyc 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_ec_ssh.pub tests/keys/testkey_pkcs1.pub.pem tests/keys/testkey_rsa tests/keys/testkey_rsa.cer tests/keys/testkey_rsa.pubPyJWT-1.5.3/PyJWT.egg-info/top_level.txt0000644000076500000240000000000413153565454020606 0ustar jpadillastaff00000000000000jwt PyJWT-1.5.3/README.rst0000644000076500000240000000314513107201014015057 0ustar jpadillastaff00000000000000PyJWT ===== .. image:: https://secure.travis-ci.org/jpadilla/pyjwt.svg?branch=master :target: http://travis-ci.org/jpadilla/pyjwt?branch=master .. image:: https://ci.appveyor.com/api/projects/status/h8nt70aqtwhht39t?svg=true :target: https://ci.appveyor.com/project/jpadilla/pyjwt .. image:: https://img.shields.io/pypi/v/pyjwt.svg :target: https://pypi.python.org/pypi/pyjwt .. image:: https://coveralls.io/repos/jpadilla/pyjwt/badge.svg?branch=master :target: https://coveralls.io/r/jpadilla/pyjwt?branch=master .. image:: https://readthedocs.org/projects/pyjwt/badge/?version=latest :target: https://pyjwt.readthedocs.io A Python implementation of `RFC 7519 `_. Original implementation was written by `@progrium `_. Installing ---------- Install with **pip**: .. code-block:: sh $ pip install PyJWT Usage ----- .. code:: python >>> import jwt >>> encoded = jwt.encode({'some': 'payload'}, 'secret', algorithm='HS256') 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzb21lIjoicGF5bG9hZCJ9.4twFt5NiznN84AWoo1d7KO1T_yoc0Z6XOpOVswacPZg' >>> jwt.decode(encoded, 'secret', algorithms=['HS256']) {'some': 'payload'} Command line ------------ Usage:: pyjwt [options] INPUT Decoding examples:: pyjwt --key=secret TOKEN pyjwt --no-verify TOKEN See more options executing ``pyjwt --help``. Documentation ------------- View the full docs online at https://pyjwt.readthedocs.io/en/latest/ Tests ----- You can run tests from the project root after cloning with: .. code-block:: sh $ python setup.py test PyJWT-1.5.3/setup.cfg0000644000076500000240000000034413153565454015235 0ustar jpadillastaff00000000000000[flake8] max-line-length = 119 exclude = docs/, .tox/ [wheel] universal = 1 [tool:pytest] addopts = --cov-report term-missing --cov-config=.coveragerc --cov . [aliases] test = pytest [egg_info] tag_build = tag_date = 0 PyJWT-1.5.3/setup.py0000755000076500000240000000506113153565310015121 0ustar jpadillastaff00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- import os import re import sys from setuptools import find_packages, setup def get_version(package): """ Return package version as listed in `__version__` in `init.py`. """ with open(os.path.join(package, '__init__.py'), 'rb') as init_py: src = init_py.read().decode('utf-8') return re.search("__version__ = ['\"]([^'\"]+)['\"]", src).group(1) version = get_version('jwt') with open(os.path.join(os.path.dirname(__file__), 'README.rst')) as readme: long_description = readme.read() if sys.argv[-1] == 'publish': if os.system("pip freeze | grep twine"): print("twine not installed.\nUse `pip install twine`.\nExiting.") sys.exit() os.system("python setup.py sdist bdist_wheel") os.system("twine upload dist/*") print('You probably want to also tag the version now:') print(" git tag -a {0} -m 'version {0}'".format(version)) print(' git push --tags') sys.exit() tests_require = [ 'pytest >3,<4', 'pytest-cov', 'pytest-runner', ] needs_pytest = set(('pytest', 'test', 'ptr')).intersection(sys.argv) pytest_runner = ['pytest-runner'] if needs_pytest else [] setup( name='PyJWT', version=version, author='Jose Padilla', author_email='hello@jpadilla.com', description='JSON Web Token implementation in Python', license='MIT', keywords='jwt json web token security signing', url='http://github.com/jpadilla/pyjwt', packages=find_packages( exclude=["*.tests", "*.tests.*", "tests.*", "tests"] ), long_description=long_description, classifiers=[ 'Development Status :: 5 - Production/Stable', 'Intended Audience :: Developers', 'Natural Language :: English', 'License :: OSI Approved :: MIT License', 'Programming Language :: Python', 'Programming Language :: Python :: 2.6', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3.3', 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', 'Topic :: Utilities', ], test_suite='tests', setup_requires=pytest_runner, tests_require=tests_require, extras_require=dict( test=tests_require, crypto=['cryptography >= 1.4'], flake8=[ 'flake8', 'flake8-import-order', 'pep8-naming' ] ), entry_points={ 'console_scripts': [ 'pyjwt = jwt.__main__:main' ] } ) PyJWT-1.5.3/tests/0000755000076500000240000000000013153565454014555 5ustar jpadillastaff00000000000000PyJWT-1.5.3/tests/__init__.py0000644000076500000240000000000012532343527016647 0ustar jpadillastaff00000000000000PyJWT-1.5.3/tests/__pycache__/0000755000076500000240000000000013153565454016765 5ustar jpadillastaff00000000000000PyJWT-1.5.3/tests/compat.py0000644000076500000240000000025512532343527016407 0ustar jpadillastaff00000000000000# flake8: noqa import sys PY3 = sys.version_info[0] == 3 if PY3: string_types = str, text_type = str else: string_types = basestring, text_type = unicode PyJWT-1.5.3/tests/contrib/0000755000076500000240000000000013153565454016215 5ustar jpadillastaff00000000000000PyJWT-1.5.3/tests/contrib/__init__.py0000644000076500000240000000000012532343527020307 0ustar jpadillastaff00000000000000PyJWT-1.5.3/tests/contrib/__pycache__/0000755000076500000240000000000013153565454020425 5ustar jpadillastaff00000000000000PyJWT-1.5.3/tests/contrib/test_algorithms.py0000644000076500000240000001626713060030443021771 0ustar jpadillastaff00000000000000import base64 from jwt.utils import force_bytes, force_unicode import pytest from ..utils import key_path try: from jwt.contrib.algorithms.pycrypto import RSAAlgorithm has_pycrypto = True except ImportError: has_pycrypto = False try: from jwt.contrib.algorithms.py_ecdsa import ECAlgorithm has_ecdsa = True except ImportError: has_ecdsa = False @pytest.mark.skipif(not has_pycrypto, reason='Not supported without PyCrypto library') class TestPycryptoAlgorithms: def test_rsa_should_parse_pem_public_key(self): algo = RSAAlgorithm(RSAAlgorithm.SHA256) with open(key_path('testkey2_rsa.pub.pem'), 'r') as pem_key: algo.prepare_key(pem_key.read()) def test_rsa_should_accept_unicode_key(self): algo = RSAAlgorithm(RSAAlgorithm.SHA256) with open(key_path('testkey_rsa'), 'r') as rsa_key: algo.prepare_key(force_unicode(rsa_key.read())) def test_rsa_should_reject_non_string_key(self): algo = RSAAlgorithm(RSAAlgorithm.SHA256) with pytest.raises(TypeError): algo.prepare_key(None) def test_rsa_sign_should_generate_correct_signature_value(self): algo = RSAAlgorithm(RSAAlgorithm.SHA256) jwt_message = force_bytes('Hello World!') expected_sig = base64.b64decode(force_bytes( 'yS6zk9DBkuGTtcBzLUzSpo9gGJxJFOGvUqN01iLhWHrzBQ9ZEz3+Ae38AXp' '10RWwscp42ySC85Z6zoN67yGkLNWnfmCZSEv+xqELGEvBJvciOKsrhiObUl' '2mveSc1oeO/2ujkGDkkkJ2epn0YliacVjZF5+/uDmImUfAAj8lzjnHlzYix' 'sn5jGz1H07jYYbi9diixN8IUhXeTafwFg02IcONhum29V40Wu6O5tAKWlJX' 'fHJnNUzAEUOXS0WahHVb57D30pcgIji9z923q90p5c7E2cU8V+E1qe8NdCA' 'APCDzZZ9zQ/dgcMVaBrGrgimrcLbPjueOKFgSO+SSjIElKA==')) with open(key_path('testkey_rsa'), 'r') as keyfile: jwt_key = algo.prepare_key(keyfile.read()) with open(key_path('testkey_rsa.pub'), 'r') as keyfile: jwt_pub_key = algo.prepare_key(keyfile.read()) algo.sign(jwt_message, jwt_key) result = algo.verify(jwt_message, jwt_pub_key, expected_sig) assert result def test_rsa_verify_should_return_false_if_signature_invalid(self): algo = RSAAlgorithm(RSAAlgorithm.SHA256) jwt_message = force_bytes('Hello World!') jwt_sig = base64.b64decode(force_bytes( 'yS6zk9DBkuGTtcBzLUzSpo9gGJxJFOGvUqN01iLhWHrzBQ9ZEz3+Ae38AXp' '10RWwscp42ySC85Z6zoN67yGkLNWnfmCZSEv+xqELGEvBJvciOKsrhiObUl' '2mveSc1oeO/2ujkGDkkkJ2epn0YliacVjZF5+/uDmImUfAAj8lzjnHlzYix' 'sn5jGz1H07jYYbi9diixN8IUhXeTafwFg02IcONhum29V40Wu6O5tAKWlJX' 'fHJnNUzAEUOXS0WahHVb57D30pcgIji9z923q90p5c7E2cU8V+E1qe8NdCA' 'APCDzZZ9zQ/dgcMVaBrGrgimrcLbPjueOKFgSO+SSjIElKA==')) jwt_sig += force_bytes('123') # Signature is now invalid with open(key_path('testkey_rsa.pub'), 'r') as keyfile: jwt_pub_key = algo.prepare_key(keyfile.read()) result = algo.verify(jwt_message, jwt_pub_key, jwt_sig) assert not result def test_rsa_verify_should_return_true_if_signature_valid(self): algo = RSAAlgorithm(RSAAlgorithm.SHA256) jwt_message = force_bytes('Hello World!') jwt_sig = base64.b64decode(force_bytes( 'yS6zk9DBkuGTtcBzLUzSpo9gGJxJFOGvUqN01iLhWHrzBQ9ZEz3+Ae38AXp' '10RWwscp42ySC85Z6zoN67yGkLNWnfmCZSEv+xqELGEvBJvciOKsrhiObUl' '2mveSc1oeO/2ujkGDkkkJ2epn0YliacVjZF5+/uDmImUfAAj8lzjnHlzYix' 'sn5jGz1H07jYYbi9diixN8IUhXeTafwFg02IcONhum29V40Wu6O5tAKWlJX' 'fHJnNUzAEUOXS0WahHVb57D30pcgIji9z923q90p5c7E2cU8V+E1qe8NdCA' 'APCDzZZ9zQ/dgcMVaBrGrgimrcLbPjueOKFgSO+SSjIElKA==')) with open(key_path('testkey_rsa.pub'), 'r') as keyfile: jwt_pub_key = algo.prepare_key(keyfile.read()) result = algo.verify(jwt_message, jwt_pub_key, jwt_sig) assert result def test_rsa_prepare_key_should_be_idempotent(self): algo = RSAAlgorithm(RSAAlgorithm.SHA256) with open(key_path('testkey_rsa.pub'), 'r') as keyfile: jwt_pub_key_first = algo.prepare_key(keyfile.read()) jwt_pub_key_second = algo.prepare_key(jwt_pub_key_first) assert jwt_pub_key_first == jwt_pub_key_second @pytest.mark.skipif(not has_ecdsa, reason='Not supported without ecdsa library') class TestEcdsaAlgorithms: def test_ec_should_reject_non_string_key(self): algo = ECAlgorithm(ECAlgorithm.SHA256) with pytest.raises(TypeError): algo.prepare_key(None) def test_ec_should_accept_unicode_key(self): algo = ECAlgorithm(ECAlgorithm.SHA256) with open(key_path('testkey_ec'), 'r') as ec_key: algo.prepare_key(force_unicode(ec_key.read())) def test_ec_sign_should_generate_correct_signature_value(self): algo = ECAlgorithm(ECAlgorithm.SHA256) jwt_message = force_bytes('Hello World!') expected_sig = base64.b64decode(force_bytes( 'AC+m4Jf/xI3guAC6w0w37t5zRpSCF6F4udEz5LiMiTIjCS4vcVe6dDOxK+M' 'mvkF8PxJuvqxP2CO3TR3okDPCl/NjATTO1jE+qBZ966CRQSSzcCM+tzcHzw' 'LZS5kbvKu0Acd/K6Ol2/W3B1NeV5F/gjvZn/jOwaLgWEUYsg0o4XVrAg65')) with open(key_path('testkey_ec'), 'r') as keyfile: jwt_key = algo.prepare_key(keyfile.read()) with open(key_path('testkey_ec.pub'), 'r') as keyfile: jwt_pub_key = algo.prepare_key(keyfile.read()) algo.sign(jwt_message, jwt_key) result = algo.verify(jwt_message, jwt_pub_key, expected_sig) assert result def test_ec_verify_should_return_false_if_signature_invalid(self): algo = ECAlgorithm(ECAlgorithm.SHA256) jwt_message = force_bytes('Hello World!') jwt_sig = base64.b64decode(force_bytes( 'AC+m4Jf/xI3guAC6w0w37t5zRpSCF6F4udEz5LiMiTIjCS4vcVe6dDOxK+M' 'mvkF8PxJuvqxP2CO3TR3okDPCl/NjATTO1jE+qBZ966CRQSSzcCM+tzcHzw' 'LZS5kbvKu0Acd/K6Ol2/W3B1NeV5F/gjvZn/jOwaLgWEUYsg0o4XVrAg65')) jwt_sig += force_bytes('123') # Signature is now invalid with open(key_path('testkey_ec.pub'), 'r') as keyfile: jwt_pub_key = algo.prepare_key(keyfile.read()) result = algo.verify(jwt_message, jwt_pub_key, jwt_sig) assert not result def test_ec_verify_should_return_true_if_signature_valid(self): algo = ECAlgorithm(ECAlgorithm.SHA256) jwt_message = force_bytes('Hello World!') jwt_sig = base64.b64decode(force_bytes( 'AC+m4Jf/xI3guAC6w0w37t5zRpSCF6F4udEz5LiMiTIjCS4vcVe6dDOxK+M' 'mvkF8PxJuvqxP2CO3TR3okDPCl/NjATTO1jE+qBZ966CRQSSzcCM+tzcHzw' 'LZS5kbvKu0Acd/K6Ol2/W3B1NeV5F/gjvZn/jOwaLgWEUYsg0o4XVrAg65')) with open(key_path('testkey_ec.pub'), 'r') as keyfile: jwt_pub_key = algo.prepare_key(keyfile.read()) result = algo.verify(jwt_message, jwt_pub_key, jwt_sig) assert result def test_ec_prepare_key_should_be_idempotent(self): algo = ECAlgorithm(ECAlgorithm.SHA256) with open(key_path('testkey_ec.pub'), 'r') as keyfile: jwt_pub_key_first = algo.prepare_key(keyfile.read()) jwt_pub_key_second = algo.prepare_key(jwt_pub_key_first) assert jwt_pub_key_first == jwt_pub_key_second PyJWT-1.5.3/tests/keys/0000755000076500000240000000000013153565454015530 5ustar jpadillastaff00000000000000PyJWT-1.5.3/tests/keys/__init__.py0000644000076500000240000000325613060030443017625 0ustar jpadillastaff00000000000000import json import os from jwt.utils import base64url_decode, force_bytes from tests.utils import int_from_bytes BASE_PATH = os.path.dirname(os.path.abspath(__file__)) def decode_value(val): decoded = base64url_decode(force_bytes(val)) return int_from_bytes(decoded, 'big') def load_hmac_key(): with open(os.path.join(BASE_PATH, 'jwk_hmac.json'), 'r') as infile: keyobj = json.load(infile) return base64url_decode(force_bytes(keyobj['k'])) try: from cryptography.hazmat.primitives.asymmetric import ec from cryptography.hazmat.backends import default_backend from jwt.algorithms import RSAAlgorithm has_crypto = True except ImportError: has_crypto = False if has_crypto: def load_rsa_key(): with open(os.path.join(BASE_PATH, 'jwk_rsa_key.json'), 'r') as infile: return RSAAlgorithm.from_jwk(infile.read()) def load_rsa_pub_key(): with open(os.path.join(BASE_PATH, 'jwk_rsa_pub.json'), 'r') as infile: return RSAAlgorithm.from_jwk(infile.read()) def load_ec_key(): with open(os.path.join(BASE_PATH, 'jwk_ec_key.json'), 'r') as infile: keyobj = json.load(infile) return ec.EllipticCurvePrivateNumbers( private_value=decode_value(keyobj['d']), public_numbers=load_ec_pub_key().public_numbers() ) def load_ec_pub_key(): with open(os.path.join(BASE_PATH, 'jwk_ec_pub.json'), 'r') as infile: keyobj = json.load(infile) return ec.EllipticCurvePublicNumbers( x=decode_value(keyobj['x']), y=decode_value(keyobj['y']), curve=ec.SECP521R1() ).public_key(default_backend()) PyJWT-1.5.3/tests/keys/jwk_ec_key.json0000644000076500000240000000063512532343527020534 0ustar jpadillastaff00000000000000{ "kty": "EC", "kid": "bilbo.baggins@hobbiton.example", "use": "sig", "crv": "P-521", "x": "AHKZLLOsCOzz5cY97ewNUajB957y-C-U88c3v13nmGZx6sYl_oJXu9A5RkTKqjqvjyekWF-7ytDyRXYgCF5cj0Kt", "y": "AdymlHvOiLxXkEhayXQnNCvDX4h9htZaCJN34kfmC6pV5OhQHiraVySsUdaQkAgDPrwQrJmbnX9cwlGfP-HqHZR1", "d": "AAhRON2r9cqXX1hg-RoI6R1tX5p2rUAYdmpHZoC1XNM56KtscrX6zbKipQrCW9CGZH3T4ubpnoTKLDYJ_fF3_rJt" } PyJWT-1.5.3/tests/keys/jwk_ec_pub.json0000644000076500000240000000046712532343527020535 0ustar jpadillastaff00000000000000{ "kty": "EC", "kid": "bilbo.baggins@hobbiton.example", "use": "sig", "crv": "P-521", "x": "AHKZLLOsCOzz5cY97ewNUajB957y-C-U88c3v13nmGZx6sYl_oJXu9A5RkTKqjqvjyekWF-7ytDyRXYgCF5cj0Kt", "y": "AdymlHvOiLxXkEhayXQnNCvDX4h9htZaCJN34kfmC6pV5OhQHiraVySsUdaQkAgDPrwQrJmbnX9cwlGfP-HqHZR1" } PyJWT-1.5.3/tests/keys/jwk_hmac.json0000644000076500000240000000025312532343527020201 0ustar jpadillastaff00000000000000{ "kty": "oct", "kid": "018c0ae5-4d9b-471b-bfd6-eef314bc7037", "use": "sig", "alg": "HS256", "k": "hJtXIZ2uSN5kbQfbtTNWbpdmhkV8FJG-Onbc6mxCcYg" } PyJWT-1.5.3/tests/keys/jwk_rsa_key.json0000644000076500000240000000332112532343527020725 0ustar jpadillastaff00000000000000{ "kty": "RSA", "kid": "bilbo.baggins@hobbiton.example", "use": "sig", "n": "n4EPtAOCc9AlkeQHPzHStgAbgs7bTZLwUBZdR8_KuKPEHLd4rHVTeT-O-XV2jRojdNhxJWTDvNd7nqQ0VEiZQHz_AJmSCpMaJMRBSFKrKb2wqVwGU_NsYOYL-QtiWN2lbzcEe6XC0dApr5ydQLrHqkHHig3RBordaZ6Aj-oBHqFEHYpPe7Tpe-OfVfHd1E6cS6M1FZcD1NNLYD5lFHpPI9bTwJlsde3uhGqC0ZCuEHg8lhzwOHrtIQbS0FVbb9k3-tVTU4fg_3L_vniUFAKwuCLqKnS2BYwdq_mzSnbLY7h_qixoR7jig3__kRhuaxwUkRz5iaiQkqgc5gHdrNP5zw", "e": "AQAB", "d": "bWUC9B-EFRIo8kpGfh0ZuyGPvMNKvYWNtB_ikiH9k20eT-O1q_I78eiZkpXxXQ0UTEs2LsNRS-8uJbvQ-A1irkwMSMkK1J3XTGgdrhCku9gRldY7sNA_AKZGh-Q661_42rINLRCe8W-nZ34ui_qOfkLnK9QWDDqpaIsA-bMwWWSDFu2MUBYwkHTMEzLYGqOe04noqeq1hExBTHBOBdkMXiuFhUq1BU6l-DqEiWxqg82sXt2h-LMnT3046AOYJoRioz75tSUQfGCshWTBnP5uDjd18kKhyv07lhfSJdrPdM5Plyl21hsFf4L_mHCuoFau7gdsPfHPxxjVOcOpBrQzwQ", "p": "3Slxg_DwTXJcb6095RoXygQCAZ5RnAvZlno1yhHtnUex_fp7AZ_9nRaO7HX_-SFfGQeutao2TDjDAWU4Vupk8rw9JR0AzZ0N2fvuIAmr_WCsmGpeNqQnev1T7IyEsnh8UMt-n5CafhkikzhEsrmndH6LxOrvRJlsPp6Zv8bUq0k", "q": "uKE2dh-cTf6ERF4k4e_jy78GfPYUIaUyoSSJuBzp3Cubk3OCqs6grT8bR_cu0Dm1MZwWmtdqDyI95HrUeq3MP15vMMON8lHTeZu2lmKvwqW7anV5UzhM1iZ7z4yMkuUwFWoBvyY898EXvRD-hdqRxHlSqAZ192zB3pVFJ0s7pFc", "dp": "B8PVvXkvJrj2L-GYQ7v3y9r6Kw5g9SahXBwsWUzp19TVlgI-YV85q1NIb1rxQtD-IsXXR3-TanevuRPRt5OBOdiMGQp8pbt26gljYfKU_E9xn-RULHz0-ed9E9gXLKD4VGngpz-PfQ_q29pk5xWHoJp009Qf1HvChixRX59ehik", "dq": "CLDmDGduhylc9o7r84rEUVn7pzQ6PF83Y-iBZx5NT-TpnOZKF1pErAMVeKzFEl41DlHHqqBLSM0W1sOFbwTxYWZDm6sI6og5iTbwQGIC3gnJKbi_7k_vJgGHwHxgPaX2PnvP-zyEkDERuf-ry4c_Z11Cq9AqC2yeL6kdKT1cYF8", "qi": "3PiqvXQN0zwMeE-sBvZgi289XP9XCQF3VWqPzMKnIgQp7_Tugo6-NZBKCQsMf3HaEGBjTVJs_jcK8-TRXvaKe-7ZMaQj8VfBdYkssbu0NKDDhjJ-GtiseaDVWt7dcH0cfwxgFUHpQh7FoCrjFJ6h6ZEpMF6xmujs4qMpPz8aaI4" } PyJWT-1.5.3/tests/keys/jwk_rsa_pub.json0000644000076500000240000000071512532343527020727 0ustar jpadillastaff00000000000000{ "kty": "RSA", "kid": "bilbo.baggins@hobbiton.example", "use": "sig", "n": "n4EPtAOCc9AlkeQHPzHStgAbgs7bTZLwUBZdR8_KuKPEHLd4rHVTeT-O-XV2jRojdNhxJWTDvNd7nqQ0VEiZQHz_AJmSCpMaJMRBSFKrKb2wqVwGU_NsYOYL-QtiWN2lbzcEe6XC0dApr5ydQLrHqkHHig3RBordaZ6Aj-oBHqFEHYpPe7Tpe-OfVfHd1E6cS6M1FZcD1NNLYD5lFHpPI9bTwJlsde3uhGqC0ZCuEHg8lhzwOHrtIQbS0FVbb9k3-tVTU4fg_3L_vniUFAKwuCLqKnS2BYwdq_mzSnbLY7h_qixoR7jig3__kRhuaxwUkRz5iaiQkqgc5gHdrNP5zw", "e": "AQAB" } PyJWT-1.5.3/tests/keys/testkey2_rsa.pub.pem0000644000076500000240000000070312532343527021432 0ustar jpadillastaff00000000000000-----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1tUH3/0v8fvLensHO1g2 6+U4r7jBg43DVOgqmXAWQa8ArAb4NfTrsYX8YkVhZZYwuLmKczRj0GhXUVY9iDbT sIGmgG+ySj6eiREz5VLqofFkAvRZ6y7yNv8PIGgXEhQTiDDNIkHGaFNMvn/eZ54H is70pdTjR5Ko+/y/wg71df1nb/5KwttSvy0YsTu/XpkduonPruYfAVRG3HK+3GZd xTygLcdamwe9jj+kjxtXRlrXVMQiXGFSU8U6bjafWnQiQ9XzjxvygBt0ZD0kRorr p74XGyQY5ThkN8DlpJbTTFsxOnBUAQz4zhohjobIGBRimi5yVlyLOwTlpaKGFC7O 7wIDAQAB -----END PUBLIC KEY----- PyJWT-1.5.3/tests/keys/testkey_ec0000644000076500000240000000055112532343527017606 0ustar jpadillastaff00000000000000-----BEGIN EC PRIVATE KEY----- MIHbAgEBBEG4xN/z6gk7bPkEzs1hHOsbs+Gi2lku8YH4LkS4E1q9U9jSOjvEcFNH m/CQjKi1rtpAb0/WL3p/wXsc26e7zmAA5KAHBgUrgQQAI6GBiQOBhgAEAVnCcDxA J0v5OJBYFIcTReydEkEIWRvpzYMvv5l8IUOT2SFJiHdWtU45DV4is7+g6bbQanbh 28/1dBLR/kH1stAeAYWeTJ08gxo3M9Q0KinXsXm4c6G24UiGY6WHeWlOPKPa16fz pwJ62o3XaRrCdGzX+K7TCwahWCTeizrJQAe8UwUY -----END EC PRIVATE KEY----- PyJWT-1.5.3/tests/keys/testkey_ec.pub0000644000076500000240000000041412532343527020371 0ustar jpadillastaff00000000000000-----BEGIN PUBLIC KEY----- MIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQBWcJwPEAnS/k4kFgUhxNF7J0SQQhZ G+nNgy+/mXwhQ5PZIUmId1a1TjkNXiKzv6DpttBqduHbz/V0EtH+QfWy0B4BhZ5M nTyDGjcz1DQqKdexebhzobbhSIZjpYd5aU48o9rXp/OnAnrajddpGsJ0bNf4rtML BqFYJN6LOslAB7xTBRg= -----END PUBLIC KEY----- PyJWT-1.5.3/tests/keys/testkey_ec_ssh.pub0000644000076500000240000000037513075663746021267 0ustar jpadillastaff00000000000000ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBAFZwnA8QCdL+TiQWBSHE0XsnRJBCFkb6c2DL7+ZfCFDk9khSYh3VrVOOQ1eIrO/oOm20Gp24dvP9XQS0f5B9bLQHgGFnkydPIMaNzPUNCop17F5uHOhtuFIhmOlh3lpTjyj2ten86cCetqN12kawnRs1/iu0wsGoVgk3os6yUAHvFMFGA== PyJWT-1.5.3/tests/keys/testkey_pkcs1.pub.pem0000644000076500000240000000036713122746474021616 0ustar jpadillastaff00000000000000-----BEGIN RSA PUBLIC KEY----- MIGHAoGBAOV/0Vl/5VdHcYpnILYzBGWo5JQVzo9wBkbxzjAStcAnTwvv1ZJTMXs6 fjz91f9hiMM4Z/5qNTE/EHlDWxVdj1pyRaQulZPUs0r9qJ02ogRRGLG3jjrzzbzF yj/pdNBwym0UJYC/Jmn/kMLwGiWI2nfa9vM5SovqZiAy2FD7eOtVAgED -----END RSA PUBLIC KEY----- PyJWT-1.5.3/tests/keys/testkey_rsa0000644000076500000240000000321712532343527020006 0ustar jpadillastaff00000000000000-----BEGIN RSA PRIVATE KEY----- MIIEpQIBAAKCAQEA1HgzBfJv2cOjQryCwe8NEelriOTNFWKZUivevUrRhlqcmZJd CvuCJRr+xCN+OmO8qwgJJR98feNujxVg+J9Ls3/UOA4HcF9nYH6aqVXELAE8Hk/A Lvxi96ms1DDuAvQGaYZ+lANxlvxeQFOZSbjkz/9mh8aLeGKwqJLp3p+OhUBQpwvA UAPg82+OUtgTW3nSljjeFr14B8qAneGSc/wl0ni++1SRZUXFSovzcqQOkla3W27r rLfrD6LXgj/TsDs4vD1PnIm1zcVenKT7TfYI17bsG/O/Wecwz2Nl19pL7gDosNru F3ogJWNq1Lyn/ijPQnkPLpZHyhvuiycYcI3DiQIDAQABAoIBAQCt9uzwBZ0HVGQs lGULnUu6SsC9iXlR9TVMTpdFrij4NODb7Tc5cs0QzJWkytrjvB4Se7XhK3KnMLyp cvu/Fc7J3fRJIVN98t+V5pOD6rGAxlIPD4Vv8z6lQcw8wQNgb6WAaZriXh93XJNf YBO2hSj0FU5CBZLUsxmqLQBIQ6RR/OUGAvThShouE9K4N0vKB2UPOCu5U+d5zS3W 44Q5uatxYiSHBTYIZDN4u27Nfo5WA+GTvFyeNsO6tNNWlYfRHSBtnm6SZDY/5i4J fxP2JY0waM81KRvuHTazY571lHM/TTvFDRUX5nvHIu7GToBKahfVLf26NJuTZYXR 5c09GAXBAoGBAO7a9M/dvS6eDhyESYyCjP6w61jD7UYJ1fudaYFrDeqnaQ857Pz4 BcKx3KMmLFiDvuMgnVVj8RToBGfMV0zP7sDnuFRJnWYcOeU8e2sWGbZmWGWzv0SD +AhppSZThU4mJ8aa/tgsepCHkJnfoX+3wN7S9NfGhM8GDGxTHJwBpxINAoGBAOO4 ZVtn9QEblmCX/Q5ejInl43Y9nRsfTy9lB9Lp1cyWCJ3eep6lzT60K3OZGVOuSgKQ vZ/aClMCMbqsAAG4fKBjREA6p7k4/qaMApHQum8APCh9WPsKLaavxko8ZDc41kZt hgKyUs2XOhW/BLjmzqwGryidvOfszDwhH7rNVmRtAoGBALYGdvrSaRHVsbtZtRM3 imuuOCx1Y6U0abZOx9Cw3PIukongAxLlkL5G/XX36WOrQxWkDUK930OnbXQM7ZrD +5dW/8p8L09Zw2VHKmb5eK7gYA1hZim4yJTgrdL/Y1+jBDz+cagcfWsXZMNfAZxr VLh628x0pVF/sof67pqVR9UhAoGBAMcQiLoQ9GJVhW1HMBYBnQVnCyJv1gjBo+0g emhrtVQ0y6+FrtdExVjNEzboXPWD5Hq9oKY+aswJnQM8HH1kkr16SU2EeN437pQU zKI/PtqN8AjNGp3JVgLioYp/pHOJofbLA10UGcJTMpmT9ELWsVA8P55X1a1AmYDu y9f2bFE5AoGAdjo95mB0LVYikNPa+NgyDwLotLqrueb9IviMmn6zKHCwiOXReqXD X9slB8RA15uv56bmN04O//NyVFcgJ2ef169GZHiRFIgIy0Pl8LYkMhCYKKhyqM7g xN+SqGqDTKDC22j00S7jcvCaa1qadn1qbdfukZ4NXv7E2d/LO0Y2Kkc= -----END RSA PRIVATE KEY----- PyJWT-1.5.3/tests/keys/testkey_rsa.cer0000644000076500000240000000240112532343527020550 0ustar jpadillastaff00000000000000-----BEGIN CERTIFICATE----- MIIDhTCCAm2gAwIBAgIJANE4sir3EkX8MA0GCSqGSIb3DQEBCwUAMFkxCzAJBgNV BAYTAlVTMQ4wDAYDVQQIDAVUZXhhczEPMA0GA1UEBwwGQXVzdGluMQ4wDAYDVQQK DAVQeUpXVDEZMBcGA1UECwwQVGVzdCBDZXJ0aWZpY2F0ZTAeFw0xNTAzMTgwMTE2 MTRaFw0xODAzMTcwMTE2MTRaMFkxCzAJBgNVBAYTAlVTMQ4wDAYDVQQIDAVUZXhh czEPMA0GA1UEBwwGQXVzdGluMQ4wDAYDVQQKDAVQeUpXVDEZMBcGA1UECwwQVGVz dCBDZXJ0aWZpY2F0ZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANR4 MwXyb9nDo0K8gsHvDRHpa4jkzRVimVIr3r1K0YZanJmSXQr7giUa/sQjfjpjvKsI CSUffH3jbo8VYPifS7N/1DgOB3BfZ2B+mqlVxCwBPB5PwC78YveprNQw7gL0BmmG fpQDcZb8XkBTmUm45M//ZofGi3hisKiS6d6fjoVAUKcLwFAD4PNvjlLYE1t50pY4 3ha9eAfKgJ3hknP8JdJ4vvtUkWVFxUqL83KkDpJWt1tu66y36w+i14I/07A7OLw9 T5yJtc3FXpyk+032CNe27Bvzv1nnMM9jZdfaS+4A6LDa7hd6ICVjatS8p/4oz0J5 Dy6WR8ob7osnGHCNw4kCAwEAAaNQME4wHQYDVR0OBBYEFDR6fVdFxZED6YMmD62W LlBW+qEBMB8GA1UdIwQYMBaAFDR6fVdFxZED6YMmD62WLlBW+qEBMAwGA1UdEwQF MAMBAf8wDQYJKoZIhvcNAQELBQADggEBAFwDNwm+lU/kGfWwiWM0Lv2aosXotoiG TsBSWIn2iYphq0vzlgChcNocN9zkaOz3zc9pcREP6lyqHpE0OEbNucHHDdU1L2he lLFOLOmkpP5fyPDXs9nKYhO8ygMByEonHm3K/VvCgrsSgJ3JuxMLUxnE55jQXGWV OqYQNo2J5h93Zd2HTTe19jCz+bbWnRBP5VvLAAAo5YSmk3iroWSPWAKkWOOecJ2Q /xnRyuWERsfvZiF/m9q7yDJ55LXVVm3Rufmy76SoTnJ2acap+XQNXBH/AxayeLUS OYmHWH61dUcsQtwXYHYRB8TTtMIwUCXGmthXkDJydEfrGcD0y6APIh8= -----END CERTIFICATE----- PyJWT-1.5.3/tests/keys/testkey_rsa.pub0000644000076500000240000000062112532343527020567 0ustar jpadillastaff00000000000000ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDUeDMF8m/Zw6NCvILB7w0R6WuI5M0VYplSK969StGGWpyZkl0K+4IlGv7EI346Y7yrCAklH3x9426PFWD4n0uzf9Q4DgdwX2dgfpqpVcQsATweT8Au/GL3qazUMO4C9AZphn6UA3GW/F5AU5lJuOTP/2aHxot4YrCokunen46FQFCnC8BQA+Dzb45S2BNbedKWON4WvXgHyoCd4ZJz/CXSeL77VJFlRcVKi/NypA6SVrdbbuust+sPoteCP9OwOzi8PU+cibXNxV6cpPtN9gjXtuwb879Z5zDPY2XX2kvuAOiw2u4XeiAlY2rUvKf+KM9CeQ8ulkfKG+6LJxhwjcOJ aasmundo@mair.local PyJWT-1.5.3/tests/test_algorithms.py0000644000076500000240000006037713122746474020353 0ustar jpadillastaff00000000000000import base64 import json from jwt.algorithms import Algorithm, HMACAlgorithm, NoneAlgorithm from jwt.exceptions import InvalidKeyError from jwt.utils import base64url_decode, force_bytes, force_unicode import pytest from .keys import load_hmac_key from .utils import key_path try: from jwt.algorithms import RSAAlgorithm, ECAlgorithm, RSAPSSAlgorithm from .keys import load_rsa_pub_key, load_ec_pub_key has_crypto = True except ImportError: has_crypto = False class TestAlgorithms: def test_algorithm_should_throw_exception_if_prepare_key_not_impl(self): algo = Algorithm() with pytest.raises(NotImplementedError): algo.prepare_key('test') def test_algorithm_should_throw_exception_if_sign_not_impl(self): algo = Algorithm() with pytest.raises(NotImplementedError): algo.sign('message', 'key') def test_algorithm_should_throw_exception_if_verify_not_impl(self): algo = Algorithm() with pytest.raises(NotImplementedError): algo.verify('message', 'key', 'signature') def test_algorithm_should_throw_exception_if_to_jwk_not_impl(self): algo = Algorithm() with pytest.raises(NotImplementedError): algo.from_jwk('value') def test_algorithm_should_throw_exception_if_from_jwk_not_impl(self): algo = Algorithm() with pytest.raises(NotImplementedError): algo.to_jwk('value') def test_none_algorithm_should_throw_exception_if_key_is_not_none(self): algo = NoneAlgorithm() with pytest.raises(InvalidKeyError): algo.prepare_key('123') def test_hmac_should_reject_nonstring_key(self): algo = HMACAlgorithm(HMACAlgorithm.SHA256) with pytest.raises(TypeError) as context: algo.prepare_key(object()) exception = context.value assert str(exception) == 'Expected a string value' def test_hmac_should_accept_unicode_key(self): algo = HMACAlgorithm(HMACAlgorithm.SHA256) algo.prepare_key(force_unicode('awesome')) def test_hmac_should_throw_exception_if_key_is_pem_public_key(self): algo = HMACAlgorithm(HMACAlgorithm.SHA256) with pytest.raises(InvalidKeyError): with open(key_path('testkey2_rsa.pub.pem'), 'r') as keyfile: algo.prepare_key(keyfile.read()) def test_hmac_should_throw_exception_if_key_is_x509_certificate(self): algo = HMACAlgorithm(HMACAlgorithm.SHA256) with pytest.raises(InvalidKeyError): with open(key_path('testkey_rsa.cer'), 'r') as keyfile: algo.prepare_key(keyfile.read()) def test_hmac_should_throw_exception_if_key_is_ssh_public_key(self): algo = HMACAlgorithm(HMACAlgorithm.SHA256) with pytest.raises(InvalidKeyError): with open(key_path('testkey_rsa.pub'), 'r') as keyfile: algo.prepare_key(keyfile.read()) def test_hmac_should_throw_exception_if_key_is_x509_cert(self): algo = HMACAlgorithm(HMACAlgorithm.SHA256) with pytest.raises(InvalidKeyError): with open(key_path('testkey2_rsa.pub.pem'), 'r') as keyfile: algo.prepare_key(keyfile.read()) def test_hmac_should_throw_exception_if_key_is_pkcs1_pem_public(self): algo = HMACAlgorithm(HMACAlgorithm.SHA256) with pytest.raises(InvalidKeyError): with open(key_path('testkey_pkcs1.pub.pem'), 'r') as keyfile: algo.prepare_key(keyfile.read()) def test_hmac_jwk_should_parse_and_verify(self): algo = HMACAlgorithm(HMACAlgorithm.SHA256) with open(key_path('jwk_hmac.json'), 'r') as keyfile: key = algo.from_jwk(keyfile.read()) signature = algo.sign(b'Hello World!', key) assert algo.verify(b'Hello World!', key, signature) def test_hmac_to_jwk_returns_correct_values(self): algo = HMACAlgorithm(HMACAlgorithm.SHA256) key = algo.to_jwk('secret') assert json.loads(key) == {'kty': 'oct', 'k': 'c2VjcmV0'} def test_hmac_from_jwk_should_raise_exception_if_not_hmac_key(self): algo = HMACAlgorithm(HMACAlgorithm.SHA256) with open(key_path('jwk_rsa_pub.json'), 'r') as keyfile: with pytest.raises(InvalidKeyError): algo.from_jwk(keyfile.read()) @pytest.mark.skipif(not has_crypto, reason='Not supported without cryptography library') def test_rsa_should_parse_pem_public_key(self): algo = RSAAlgorithm(RSAAlgorithm.SHA256) with open(key_path('testkey2_rsa.pub.pem'), 'r') as pem_key: algo.prepare_key(pem_key.read()) @pytest.mark.skipif(not has_crypto, reason='Not supported without cryptography library') def test_rsa_should_accept_pem_private_key_bytes(self): algo = RSAAlgorithm(RSAAlgorithm.SHA256) with open(key_path('testkey_rsa'), 'rb') as pem_key: algo.prepare_key(pem_key.read()) @pytest.mark.skipif(not has_crypto, reason='Not supported without cryptography library') def test_rsa_should_accept_unicode_key(self): algo = RSAAlgorithm(RSAAlgorithm.SHA256) with open(key_path('testkey_rsa'), 'r') as rsa_key: algo.prepare_key(force_unicode(rsa_key.read())) @pytest.mark.skipif(not has_crypto, reason='Not supported without cryptography library') def test_rsa_should_reject_non_string_key(self): algo = RSAAlgorithm(RSAAlgorithm.SHA256) with pytest.raises(TypeError): algo.prepare_key(None) @pytest.mark.skipif(not has_crypto, reason='Not supported without cryptography library') def test_rsa_verify_should_return_false_if_signature_invalid(self): algo = RSAAlgorithm(RSAAlgorithm.SHA256) message = force_bytes('Hello World!') sig = base64.b64decode(force_bytes( 'yS6zk9DBkuGTtcBzLUzSpo9gGJxJFOGvUqN01iLhWHrzBQ9ZEz3+Ae38AXp' '10RWwscp42ySC85Z6zoN67yGkLNWnfmCZSEv+xqELGEvBJvciOKsrhiObUl' '2mveSc1oeO/2ujkGDkkkJ2epn0YliacVjZF5+/uDmImUfAAj8lzjnHlzYix' 'sn5jGz1H07jYYbi9diixN8IUhXeTafwFg02IcONhum29V40Wu6O5tAKWlJX' 'fHJnNUzAEUOXS0WahHVb57D30pcgIji9z923q90p5c7E2cU8V+E1qe8NdCA' 'APCDzZZ9zQ/dgcMVaBrGrgimrcLbPjueOKFgSO+SSjIElKA==')) sig += force_bytes('123') # Signature is now invalid with open(key_path('testkey_rsa.pub'), 'r') as keyfile: pub_key = algo.prepare_key(keyfile.read()) result = algo.verify(message, pub_key, sig) assert not result @pytest.mark.skipif(not has_crypto, reason='Not supported without cryptography library') def test_rsa_jwk_public_and_private_keys_should_parse_and_verify(self): algo = RSAAlgorithm(RSAAlgorithm.SHA256) with open(key_path('jwk_rsa_pub.json'), 'r') as keyfile: pub_key = algo.from_jwk(keyfile.read()) with open(key_path('jwk_rsa_key.json'), 'r') as keyfile: priv_key = algo.from_jwk(keyfile.read()) signature = algo.sign(force_bytes('Hello World!'), priv_key) assert algo.verify(force_bytes('Hello World!'), pub_key, signature) @pytest.mark.skipif(not has_crypto, reason='Not supported without cryptography library') def test_rsa_private_key_to_jwk_works_with_from_jwk(self): algo = RSAAlgorithm(RSAAlgorithm.SHA256) with open(key_path('testkey_rsa'), 'r') as rsa_key: orig_key = algo.prepare_key(force_unicode(rsa_key.read())) parsed_key = algo.from_jwk(algo.to_jwk(orig_key)) assert parsed_key.private_numbers() == orig_key.private_numbers() assert parsed_key.private_numbers().public_numbers == orig_key.private_numbers().public_numbers @pytest.mark.skipif(not has_crypto, reason='Not supported without cryptography library') def test_rsa_public_key_to_jwk_works_with_from_jwk(self): algo = RSAAlgorithm(RSAAlgorithm.SHA256) with open(key_path('testkey_rsa.pub'), 'r') as rsa_key: orig_key = algo.prepare_key(force_unicode(rsa_key.read())) parsed_key = algo.from_jwk(algo.to_jwk(orig_key)) assert parsed_key.public_numbers() == orig_key.public_numbers() @pytest.mark.skipif(not has_crypto, reason='Not supported without cryptography library') def test_rsa_jwk_private_key_with_other_primes_is_invalid(self): algo = RSAAlgorithm(RSAAlgorithm.SHA256) with open(key_path('jwk_rsa_key.json'), 'r') as keyfile: with pytest.raises(InvalidKeyError): keydata = json.loads(keyfile.read()) keydata['oth'] = [] algo.from_jwk(json.dumps(keydata)) @pytest.mark.skipif(not has_crypto, reason='Not supported without cryptography library') def test_rsa_jwk_private_key_with_missing_values_is_invalid(self): algo = RSAAlgorithm(RSAAlgorithm.SHA256) with open(key_path('jwk_rsa_key.json'), 'r') as keyfile: with pytest.raises(InvalidKeyError): keydata = json.loads(keyfile.read()) del keydata['p'] algo.from_jwk(json.dumps(keydata)) @pytest.mark.skipif(not has_crypto, reason='Not supported without cryptography library') def test_rsa_jwk_private_key_can_recover_prime_factors(self): algo = RSAAlgorithm(RSAAlgorithm.SHA256) with open(key_path('jwk_rsa_key.json'), 'r') as keyfile: keybytes = keyfile.read() control_key = algo.from_jwk(keybytes).private_numbers() keydata = json.loads(keybytes) delete_these = ['p', 'q', 'dp', 'dq', 'qi'] for field in delete_these: del keydata[field] parsed_key = algo.from_jwk(json.dumps(keydata)).private_numbers() assert control_key.d == parsed_key.d assert control_key.p == parsed_key.p assert control_key.q == parsed_key.q assert control_key.dmp1 == parsed_key.dmp1 assert control_key.dmq1 == parsed_key.dmq1 assert control_key.iqmp == parsed_key.iqmp @pytest.mark.skipif(not has_crypto, reason='Not supported without cryptography library') def test_rsa_jwk_private_key_with_missing_required_values_is_invalid(self): algo = RSAAlgorithm(RSAAlgorithm.SHA256) with open(key_path('jwk_rsa_key.json'), 'r') as keyfile: with pytest.raises(InvalidKeyError): keydata = json.loads(keyfile.read()) del keydata['p'] algo.from_jwk(json.dumps(keydata)) @pytest.mark.skipif(not has_crypto, reason='Not supported without cryptography library') def test_rsa_jwk_raises_exception_if_not_a_valid_key(self): algo = RSAAlgorithm(RSAAlgorithm.SHA256) # Invalid JSON with pytest.raises(InvalidKeyError): algo.from_jwk('{not-a-real-key') # Missing key parts with pytest.raises(InvalidKeyError): algo.from_jwk('{"kty": "RSA"}') @pytest.mark.skipif(not has_crypto, reason='Not supported without cryptography library') def test_rsa_to_jwk_returns_correct_values_for_public_key(self): algo = RSAAlgorithm(RSAAlgorithm.SHA256) with open(key_path('testkey_rsa.pub'), 'r') as keyfile: pub_key = algo.prepare_key(keyfile.read()) key = algo.to_jwk(pub_key) expected = { 'e': 'AQAB', 'key_ops': ['verify'], 'kty': 'RSA', 'n': ( '1HgzBfJv2cOjQryCwe8NEelriOTNFWKZUivevUrRhlqcmZJdCvuCJRr-xCN-' 'OmO8qwgJJR98feNujxVg-J9Ls3_UOA4HcF9nYH6aqVXELAE8Hk_ALvxi96ms' '1DDuAvQGaYZ-lANxlvxeQFOZSbjkz_9mh8aLeGKwqJLp3p-OhUBQpwvAUAPg' '82-OUtgTW3nSljjeFr14B8qAneGSc_wl0ni--1SRZUXFSovzcqQOkla3W27r' 'rLfrD6LXgj_TsDs4vD1PnIm1zcVenKT7TfYI17bsG_O_Wecwz2Nl19pL7gDo' 'sNruF3ogJWNq1Lyn_ijPQnkPLpZHyhvuiycYcI3DiQ' ), } assert json.loads(key) == expected @pytest.mark.skipif(not has_crypto, reason='Not supported without cryptography library') def test_rsa_to_jwk_returns_correct_values_for_private_key(self): algo = RSAAlgorithm(RSAAlgorithm.SHA256) with open(key_path('testkey_rsa'), 'r') as keyfile: priv_key = algo.prepare_key(keyfile.read()) key = algo.to_jwk(priv_key) expected = { 'key_ops': [u'sign'], 'kty': 'RSA', 'e': 'AQAB', 'n': ( '1HgzBfJv2cOjQryCwe8NEelriOTNFWKZUivevUrRhlqcmZJdCvuCJRr-xCN-' 'OmO8qwgJJR98feNujxVg-J9Ls3_UOA4HcF9nYH6aqVXELAE8Hk_ALvxi96ms' '1DDuAvQGaYZ-lANxlvxeQFOZSbjkz_9mh8aLeGKwqJLp3p-OhUBQpwvAUAPg' '82-OUtgTW3nSljjeFr14B8qAneGSc_wl0ni--1SRZUXFSovzcqQOkla3W27r' 'rLfrD6LXgj_TsDs4vD1PnIm1zcVenKT7TfYI17bsG_O_Wecwz2Nl19pL7gDo' 'sNruF3ogJWNq1Lyn_ijPQnkPLpZHyhvuiycYcI3DiQ' ), 'd': ('rfbs8AWdB1RkLJRlC51LukrAvYl5UfU1TE6XRa4o-DTg2-03OXLNEMyVpMr' 'a47weEnu14StypzC8qXL7vxXOyd30SSFTffLfleaTg-qxgMZSDw-Fb_M-pU' 'HMPMEDYG-lgGma4l4fd1yTX2ATtoUo9BVOQgWS1LMZqi0ASEOkUfzlBgL04' 'UoaLhPSuDdLygdlDzgruVPnec0t1uOEObmrcWIkhwU2CGQzeLtuzX6OVgPh' 'k7xcnjbDurTTVpWH0R0gbZ5ukmQ2P-YuCX8T9iWNMGjPNSkb7h02s2Oe9ZR' 'zP007xQ0VF-Z7xyLuxk6ASmoX1S39ujSbk2WF0eXNPRgFwQ'), 'q': ('47hlW2f1ARuWYJf9Dl6MieXjdj2dGx9PL2UH0unVzJYInd56nqXNPrQrc5k' 'ZU65KApC9n9oKUwIxuqwAAbh8oGNEQDqnuTj-powCkdC6bwA8KH1Y-wotpq' '_GSjxkNzjWRm2GArJSzZc6Fb8EuObOrAavKJ285-zMPCEfus1WZG0'), 'p': ('7tr0z929Lp4OHIRJjIKM_rDrWMPtRgnV-51pgWsN6qdpDzns_PgFwrHcoyY' 'sWIO-4yCdVWPxFOgEZ8xXTM_uwOe4VEmdZhw55Tx7axYZtmZYZbO_RIP4CG' 'mlJlOFTiYnxpr-2Cx6kIeQmd-hf7fA3tL018aEzwYMbFMcnAGnEg0'), 'qi': ('djo95mB0LVYikNPa-NgyDwLotLqrueb9IviMmn6zKHCwiOXReqXDX9slB8' 'RA15uv56bmN04O__NyVFcgJ2ef169GZHiRFIgIy0Pl8LYkMhCYKKhyqM7g' 'xN-SqGqDTKDC22j00S7jcvCaa1qadn1qbdfukZ4NXv7E2d_LO0Y2Kkc'), 'dp': ('tgZ2-tJpEdWxu1m1EzeKa644LHVjpTRptk7H0LDc8i6SieADEuWQvkb9df' 'fpY6tDFaQNQr3fQ6dtdAztmsP7l1b_ynwvT1nDZUcqZvl4ruBgDWFmKbjI' 'lOCt0v9jX6MEPP5xqBx9axdkw18BnGtUuHrbzHSlUX-yh_rumpVH1SE'), 'dq': ('xxCIuhD0YlWFbUcwFgGdBWcLIm_WCMGj7SB6aGu1VDTLr4Wu10TFWM0TNu' 'hc9YPker2gpj5qzAmdAzwcfWSSvXpJTYR43jfulBTMoj8-2o3wCM0anclW' 'AuKhin-kc4mh9ssDXRQZwlMymZP0QtaxUDw_nlfVrUCZgO7L1_ZsUTk') } assert json.loads(key) == expected @pytest.mark.skipif(not has_crypto, reason='Not supported without cryptography library') def test_rsa_to_jwk_raises_exception_on_invalid_key(self): algo = RSAAlgorithm(RSAAlgorithm.SHA256) with pytest.raises(InvalidKeyError): algo.to_jwk({'not': 'a valid key'}) @pytest.mark.skipif(not has_crypto, reason='Not supported without cryptography library') def test_rsa_from_jwk_raises_exception_on_invalid_key(self): algo = RSAAlgorithm(RSAAlgorithm.SHA256) with open(key_path('jwk_hmac.json'), 'r') as keyfile: with pytest.raises(InvalidKeyError): algo.from_jwk(keyfile.read()) @pytest.mark.skipif(not has_crypto, reason='Not supported without cryptography library') def test_ec_should_reject_non_string_key(self): algo = ECAlgorithm(ECAlgorithm.SHA256) with pytest.raises(TypeError): algo.prepare_key(None) @pytest.mark.skipif(not has_crypto, reason='Not supported without cryptography library') def test_ec_should_accept_unicode_key(self): algo = ECAlgorithm(ECAlgorithm.SHA256) with open(key_path('testkey_ec'), 'r') as ec_key: algo.prepare_key(force_unicode(ec_key.read())) @pytest.mark.skipif(not has_crypto, reason='Not supported without cryptography library') def test_ec_should_accept_pem_private_key_bytes(self): algo = ECAlgorithm(ECAlgorithm.SHA256) with open(key_path('testkey_ec'), 'rb') as ec_key: algo.prepare_key(ec_key.read()) @pytest.mark.skipif(not has_crypto, reason='Not supported without cryptography library') def test_ec_should_accept_ssh_public_key_bytes(self): algo = ECAlgorithm(ECAlgorithm.SHA256) with open(key_path('testkey_ec_ssh.pub'), 'r') as ec_key: algo.prepare_key(ec_key.read()) @pytest.mark.skipif(not has_crypto, reason='Not supported without cryptography library') def test_ec_verify_should_return_false_if_signature_invalid(self): algo = ECAlgorithm(ECAlgorithm.SHA256) message = force_bytes('Hello World!') # Mess up the signature by replacing a known byte sig = base64.b64decode(force_bytes( 'AC+m4Jf/xI3guAC6w0w37t5zRpSCF6F4udEz5LiMiTIjCS4vcVe6dDOxK+M' 'mvkF8PxJuvqxP2CO3TR3okDPCl/NjATTO1jE+qBZ966CRQSSzcCM+tzcHzw' 'LZS5kbvKu0Acd/K6Ol2/W3B1NeV5F/gjvZn/jOwaLgWEUYsg0o4XVrAg65'.replace('r', 's'))) with open(key_path('testkey_ec.pub'), 'r') as keyfile: pub_key = algo.prepare_key(keyfile.read()) result = algo.verify(message, pub_key, sig) assert not result @pytest.mark.skipif(not has_crypto, reason='Not supported without cryptography library') def test_ec_verify_should_return_false_if_signature_wrong_length(self): algo = ECAlgorithm(ECAlgorithm.SHA256) message = force_bytes('Hello World!') sig = base64.b64decode(force_bytes('AC+m4Jf/xI3guAC6w0w3')) with open(key_path('testkey_ec.pub'), 'r') as keyfile: pub_key = algo.prepare_key(keyfile.read()) result = algo.verify(message, pub_key, sig) assert not result @pytest.mark.skipif(not has_crypto, reason='Not supported without cryptography library') def test_rsa_pss_sign_then_verify_should_return_true(self): algo = RSAPSSAlgorithm(RSAPSSAlgorithm.SHA256) message = force_bytes('Hello World!') with open(key_path('testkey_rsa'), 'r') as keyfile: priv_key = algo.prepare_key(keyfile.read()) sig = algo.sign(message, priv_key) with open(key_path('testkey_rsa.pub'), 'r') as keyfile: pub_key = algo.prepare_key(keyfile.read()) result = algo.verify(message, pub_key, sig) assert result @pytest.mark.skipif(not has_crypto, reason='Not supported without cryptography library') def test_rsa_pss_verify_should_return_false_if_signature_invalid(self): algo = RSAPSSAlgorithm(RSAPSSAlgorithm.SHA256) jwt_message = force_bytes('Hello World!') jwt_sig = base64.b64decode(force_bytes( 'ywKAUGRIDC//6X+tjvZA96yEtMqpOrSppCNfYI7NKyon3P7doud5v65oWNu' 'vQsz0fzPGfF7mQFGo9Cm9Vn0nljm4G6PtqZRbz5fXNQBH9k10gq34AtM02c' '/cveqACQ8gF3zxWh6qr9jVqIpeMEaEBIkvqG954E0HT9s9ybHShgHX9mlWk' '186/LopP4xe5c/hxOQjwhv6yDlTiwJFiqjNCvj0GyBKsc4iECLGIIO+4mC4' 'daOCWqbpZDuLb1imKpmm8Nsm56kAxijMLZnpCcnPgyb7CqG+B93W9GHglA5' 'drUeR1gRtO7vqbZMsCAQ4bpjXxwbYyjQlEVuMl73UL6sOWg==')) jwt_sig += force_bytes('123') # Signature is now invalid with open(key_path('testkey_rsa.pub'), 'r') as keyfile: jwt_pub_key = algo.prepare_key(keyfile.read()) result = algo.verify(jwt_message, jwt_pub_key, jwt_sig) assert not result class TestAlgorithmsRFC7520: """ These test vectors were taken from RFC 7520 (https://tools.ietf.org/html/rfc7520) """ def test_hmac_verify_should_return_true_for_test_vector(self): """ This test verifies that HMAC verification works with a known good signature and key. Reference: https://tools.ietf.org/html/rfc7520#section-4.4 """ signing_input = force_bytes( 'eyJhbGciOiJIUzI1NiIsImtpZCI6IjAxOGMwYWU1LTRkOWItNDcxYi1iZmQ2LWVlZ' 'jMxNGJjNzAzNyJ9.SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ' '29pbmcgb3V0IHlvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9hZCwgYW5kIG' 'lmIHlvdSBkb24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXigJlzIG5vIGtub3dpbmc' 'gd2hlcmUgeW91IG1pZ2h0IGJlIHN3ZXB0IG9mZiB0by4' ) signature = base64url_decode(force_bytes( 's0h6KThzkfBBBkLspW1h84VsJZFTsPPqMDA7g1Md7p0' )) algo = HMACAlgorithm(HMACAlgorithm.SHA256) key = algo.prepare_key(load_hmac_key()) result = algo.verify(signing_input, key, signature) assert result @pytest.mark.skipif(not has_crypto, reason='Not supported without cryptography library') def test_rsa_verify_should_return_true_for_test_vector(self): """ This test verifies that RSA PKCS v1.5 verification works with a known good signature and key. Reference: https://tools.ietf.org/html/rfc7520#section-4.1 """ signing_input = force_bytes( 'eyJhbGciOiJSUzI1NiIsImtpZCI6ImJpbGJvLmJhZ2dpbnNAaG9iYml0b24uZXhhb' 'XBsZSJ9.SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb' '3V0IHlvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9hZCwgYW5kIGlmIHlvdS' 'Bkb24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXigJlzIG5vIGtub3dpbmcgd2hlcmU' 'geW91IG1pZ2h0IGJlIHN3ZXB0IG9mZiB0by4' ) signature = base64url_decode(force_bytes( 'MRjdkly7_-oTPTS3AXP41iQIGKa80A0ZmTuV5MEaHoxnW2e5CZ5NlKtainoFmKZop' 'dHM1O2U4mwzJdQx996ivp83xuglII7PNDi84wnB-BDkoBwA78185hX-Es4JIwmDLJ' 'K3lfWRa-XtL0RnltuYv746iYTh_qHRD68BNt1uSNCrUCTJDt5aAE6x8wW1Kt9eRo4' 'QPocSadnHXFxnt8Is9UzpERV0ePPQdLuW3IS_de3xyIrDaLGdjluPxUAhb6L2aXic' '1U12podGU0KLUQSE_oI-ZnmKJ3F4uOZDnd6QZWJushZ41Axf_fcIe8u9ipH84ogor' 'ee7vjbU5y18kDquDg' )) algo = RSAAlgorithm(RSAAlgorithm.SHA256) key = algo.prepare_key(load_rsa_pub_key()) result = algo.verify(signing_input, key, signature) assert result @pytest.mark.skipif(not has_crypto, reason='Not supported without cryptography library') def test_rsapss_verify_should_return_true_for_test_vector(self): """ This test verifies that RSA-PSS verification works with a known good signature and key. Reference: https://tools.ietf.org/html/rfc7520#section-4.2 """ signing_input = force_bytes( 'eyJhbGciOiJQUzM4NCIsImtpZCI6ImJpbGJvLmJhZ2dpbnNAaG9iYml0b24uZXhhb' 'XBsZSJ9.SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb' '3V0IHlvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9hZCwgYW5kIGlmIHlvdS' 'Bkb24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXigJlzIG5vIGtub3dpbmcgd2hlcmU' 'geW91IG1pZ2h0IGJlIHN3ZXB0IG9mZiB0by4' ) signature = base64url_decode(force_bytes( 'cu22eBqkYDKgIlTpzDXGvaFfz6WGoz7fUDcfT0kkOy42miAh2qyBzk1xEsnk2IpN6' '-tPid6VrklHkqsGqDqHCdP6O8TTB5dDDItllVo6_1OLPpcbUrhiUSMxbbXUvdvWXz' 'g-UD8biiReQFlfz28zGWVsdiNAUf8ZnyPEgVFn442ZdNqiVJRmBqrYRXe8P_ijQ7p' '8Vdz0TTrxUeT3lm8d9shnr2lfJT8ImUjvAA2Xez2Mlp8cBE5awDzT0qI0n6uiP1aC' 'N_2_jLAeQTlqRHtfa64QQSUmFAAjVKPbByi7xho0uTOcbH510a6GYmJUAfmWjwZ6o' 'D4ifKo8DYM-X72Eaw' )) algo = RSAPSSAlgorithm(RSAPSSAlgorithm.SHA384) key = algo.prepare_key(load_rsa_pub_key()) result = algo.verify(signing_input, key, signature) assert result @pytest.mark.skipif(not has_crypto, reason='Not supported without cryptography library') def test_ec_verify_should_return_true_for_test_vector(self): """ This test verifies that ECDSA verification works with a known good signature and key. Reference: https://tools.ietf.org/html/rfc7520#section-4.3 """ signing_input = force_bytes( 'eyJhbGciOiJFUzUxMiIsImtpZCI6ImJpbGJvLmJhZ2dpbnNAaG9iYml0b24uZXhhb' 'XBsZSJ9.SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb' '3V0IHlvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9hZCwgYW5kIGlmIHlvdS' 'Bkb24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXigJlzIG5vIGtub3dpbmcgd2hlcmU' 'geW91IG1pZ2h0IGJlIHN3ZXB0IG9mZiB0by4' ) signature = base64url_decode(force_bytes( 'AE_R_YZCChjn4791jSQCrdPZCNYqHXCTZH0-JZGYNlaAjP2kqaluUIIUnC9qvbu9P' 'lon7KRTzoNEuT4Va2cmL1eJAQy3mtPBu_u_sDDyYjnAMDxXPn7XrT0lw-kvAD890j' 'l8e2puQens_IEKBpHABlsbEPX6sFY8OcGDqoRuBomu9xQ2' )) algo = ECAlgorithm(ECAlgorithm.SHA512) key = algo.prepare_key(load_ec_pub_key()) result = algo.verify(signing_input, key, signature) assert result PyJWT-1.5.3/tests/test_api_jws.py0000644000076500000240000006336613153564550017634 0ustar jpadillastaff00000000000000 import json from decimal import Decimal from jwt.algorithms import Algorithm from jwt.api_jws import PyJWS from jwt.exceptions import ( DecodeError, InvalidAlgorithmError, InvalidTokenError ) from jwt.utils import base64url_decode, force_bytes, force_unicode import pytest from .compat import string_types, text_type try: from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives.serialization import ( load_pem_private_key, load_pem_public_key, load_ssh_public_key ) has_crypto = True except ImportError: has_crypto = False @pytest.fixture def jws(): return PyJWS() @pytest.fixture def payload(): """ Creates a sample jws claimset for use as a payload during tests """ return force_bytes('hello world') class TestJWS: def test_register_algo_does_not_allow_duplicate_registration(self, jws): jws.register_algorithm('AAA', Algorithm()) with pytest.raises(ValueError): jws.register_algorithm('AAA', Algorithm()) def test_register_algo_rejects_non_algorithm_obj(self, jws): with pytest.raises(TypeError): jws.register_algorithm('AAA123', {}) def test_unregister_algo_removes_algorithm(self, jws): supported = jws.get_algorithms() assert 'none' in supported assert 'HS256' in supported jws.unregister_algorithm('HS256') supported = jws.get_algorithms() assert 'HS256' not in supported def test_unregister_algo_throws_error_if_not_registered(self, jws): with pytest.raises(KeyError): jws.unregister_algorithm('AAA') def test_algo_parameter_removes_alg_from_algorithms_list(self, jws): assert 'none' in jws.get_algorithms() assert 'HS256' in jws.get_algorithms() jws = PyJWS(algorithms=['HS256']) assert 'none' not in jws.get_algorithms() assert 'HS256' in jws.get_algorithms() def test_override_options(self): jws = PyJWS(options={'verify_signature': False}) assert not jws.options['verify_signature'] def test_non_object_options_dont_persist(self, jws, payload): token = jws.encode(payload, 'secret') jws.decode(token, 'secret', options={'verify_signature': False}) assert jws.options['verify_signature'] def test_options_must_be_dict(self, jws): pytest.raises(TypeError, PyJWS, options=object()) pytest.raises(TypeError, PyJWS, options=('something')) def test_encode_decode(self, jws, payload): secret = 'secret' jws_message = jws.encode(payload, secret) decoded_payload = jws.decode(jws_message, secret) assert decoded_payload == payload def test_decode_fails_when_alg_is_not_on_method_algorithms_param(self, jws, payload): secret = 'secret' jws_token = jws.encode(payload, secret, algorithm='HS256') jws.decode(jws_token, secret) with pytest.raises(InvalidAlgorithmError): jws.decode(jws_token, secret, algorithms=['HS384']) def test_decode_works_with_unicode_token(self, jws): secret = 'secret' unicode_jws = text_type( 'eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9' '.eyJoZWxsbyI6ICJ3b3JsZCJ9' '.tvagLDLoaiJKxOKqpBXSEGy7SYSifZhjntgm9ctpyj8') jws.decode(unicode_jws, secret) def test_decode_missing_segments_throws_exception(self, jws): secret = 'secret' example_jws = ('eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9' '.eyJoZWxsbyI6ICJ3b3JsZCJ9' '') # Missing segment with pytest.raises(DecodeError) as context: jws.decode(example_jws, secret) exception = context.value assert str(exception) == 'Not enough segments' def test_decode_invalid_token_type_is_none(self, jws): example_jws = None example_secret = 'secret' with pytest.raises(DecodeError) as context: jws.decode(example_jws, example_secret) exception = context.value assert 'Invalid token type' in str(exception) def test_decode_invalid_token_type_is_int(self, jws): example_jws = 123 example_secret = 'secret' with pytest.raises(DecodeError) as context: jws.decode(example_jws, example_secret) exception = context.value assert 'Invalid token type' in str(exception) def test_decode_with_non_mapping_header_throws_exception(self, jws): secret = 'secret' example_jws = ('MQ' # == 1 '.eyJoZWxsbyI6ICJ3b3JsZCJ9' '.tvagLDLoaiJKxOKqpBXSEGy7SYSifZhjntgm9ctpyj8') with pytest.raises(DecodeError) as context: jws.decode(example_jws, secret) exception = context.value assert str(exception) == 'Invalid header string: must be a json object' def test_encode_algorithm_param_should_be_case_sensitive(self, jws, payload): jws.encode(payload, 'secret', algorithm='HS256') with pytest.raises(NotImplementedError) as context: jws.encode(payload, None, algorithm='hs256') exception = context.value assert str(exception) == 'Algorithm not supported' def test_decode_algorithm_param_should_be_case_sensitive(self, jws): example_jws = ('eyJhbGciOiJoczI1NiIsInR5cCI6IkpXVCJ9' # alg = hs256 '.eyJoZWxsbyI6IndvcmxkIn0' '.5R_FEPE7SW2dT9GgIxPgZATjFGXfUDOSwo7TtO_Kd_g') with pytest.raises(InvalidAlgorithmError) as context: jws.decode(example_jws, 'secret') exception = context.value assert str(exception) == 'Algorithm not supported' def test_bad_secret(self, jws, payload): right_secret = 'foo' bad_secret = 'bar' jws_message = jws.encode(payload, right_secret) with pytest.raises(DecodeError): jws.decode(jws_message, bad_secret) def test_decodes_valid_jws(self, jws, payload): example_secret = 'secret' example_jws = ( b'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.' b'aGVsbG8gd29ybGQ.' b'gEW0pdU4kxPthjtehYdhxB9mMOGajt1xCKlGGXDJ8PM') decoded_payload = jws.decode(example_jws, example_secret) assert decoded_payload == payload # 'Control' Elliptic Curve jws created by another library. # Used to test for regressions that could affect both # encoding / decoding operations equally (causing tests # to still pass). @pytest.mark.skipif(not has_crypto, reason="Can't run without cryptography library") def test_decodes_valid_es384_jws(self, jws): example_payload = {'hello': 'world'} with open('tests/keys/testkey_ec.pub', 'r') as fp: example_pubkey = fp.read() example_jws = ( b'eyJhbGciOiJFUzM4NCIsInR5cCI6IkpXVCJ9' b'.eyJoZWxsbyI6IndvcmxkIn0' b'.AGtlemKghaIaYh1yeeekFH9fRuNY7hCaw5hUgZ5aG1N' b'2F8FIbiKLaZKr8SiFdTimXFVTEmxpBQ9sRmdsDsnrM-1' b'HAG0_zxxu0JyINOFT2iqF3URYl9HZ8kZWMeZAtXmn6Cw' b'PXRJD2f7N-f7bJ5JeL9VT5beI2XD3FlK3GgRvI-eE-2Ik') decoded_payload = jws.decode(example_jws, example_pubkey) json_payload = json.loads(force_unicode(decoded_payload)) assert json_payload == example_payload # 'Control' RSA jws created by another library. # Used to test for regressions that could affect both # encoding / decoding operations equally (causing tests # to still pass). @pytest.mark.skipif(not has_crypto, reason="Can't run without cryptography library") def test_decodes_valid_rs384_jws(self, jws): example_payload = {'hello': 'world'} with open('tests/keys/testkey_rsa.pub', 'r') as fp: example_pubkey = fp.read() example_jws = ( b'eyJhbGciOiJSUzM4NCIsInR5cCI6IkpXVCJ9' b'.eyJoZWxsbyI6IndvcmxkIn0' b'.yNQ3nI9vEDs7lEh-Cp81McPuiQ4ZRv6FL4evTYYAh1X' b'lRTTR3Cz8pPA9Stgso8Ra9xGB4X3rlra1c8Jz10nTUju' b'O06OMm7oXdrnxp1KIiAJDerWHkQ7l3dlizIk1bmMA457' b'W2fNzNfHViuED5ISM081dgf_a71qBwJ_yShMMrSOfxDx' b'mX9c4DjRogRJG8SM5PvpLqI_Cm9iQPGMvmYK7gzcq2cJ' b'urHRJDJHTqIdpLWXkY7zVikeen6FhuGyn060Dz9gYq9t' b'uwmrtSWCBUjiN8sqJ00CDgycxKqHfUndZbEAOjcCAhBr' b'qWW3mSVivUfubsYbwUdUG3fSRPjaUPcpe8A') decoded_payload = jws.decode(example_jws, example_pubkey) json_payload = json.loads(force_unicode(decoded_payload)) assert json_payload == example_payload def test_load_verify_valid_jws(self, jws, payload): example_secret = 'secret' example_jws = ( b'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.' b'aGVsbG8gd29ybGQ.' b'SIr03zM64awWRdPrAM_61QWsZchAtgDV3pphfHPPWkI' ) decoded_payload = jws.decode(example_jws, key=example_secret) assert decoded_payload == payload def test_allow_skip_verification(self, jws, payload): right_secret = 'foo' jws_message = jws.encode(payload, right_secret) decoded_payload = jws.decode(jws_message, verify=False) assert decoded_payload == payload def test_verify_false_deprecated(self, jws, recwarn): example_jws = ( b'eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9' b'.eyJoZWxsbyI6ICJ3b3JsZCJ9' b'.tvagLDLoaiJKxOKqpBXSEGy7SYSifZhjntgm9ctpyj8') pytest.deprecated_call(jws.decode, example_jws, verify=False) def test_decode_with_optional_algorithms(self, jws): example_secret = 'secret' example_jws = ( b'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.' b'aGVsbG8gd29ybGQ.' b'SIr03zM64awWRdPrAM_61QWsZchAtgDV3pphfHPPWkI' ) pytest.deprecated_call(jws.decode, example_jws, key=example_secret) def test_decode_no_algorithms_verify_signature_false(self, jws): example_secret = 'secret' example_jws = ( b'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.' b'aGVsbG8gd29ybGQ.' b'SIr03zM64awWRdPrAM_61QWsZchAtgDV3pphfHPPWkI' ) try: pytest.deprecated_call( jws.decode, example_jws, key=example_secret, options={'verify_signature': False}, ) except AssertionError: pass else: assert False, "Unexpected DeprecationWarning raised." def test_load_no_verification(self, jws, payload): right_secret = 'foo' jws_message = jws.encode(payload, right_secret) decoded_payload = jws.decode(jws_message, key=None, verify=False) assert decoded_payload == payload def test_no_secret(self, jws, payload): right_secret = 'foo' jws_message = jws.encode(payload, right_secret) with pytest.raises(DecodeError): jws.decode(jws_message) def test_verify_signature_with_no_secret(self, jws, payload): right_secret = 'foo' jws_message = jws.encode(payload, right_secret) with pytest.raises(DecodeError) as exc: jws.decode(jws_message) assert 'Signature verification' in str(exc.value) def test_verify_signature_with_no_algo_header_throws_exception(self, jws, payload): example_jws = ( b'e30' b'.eyJhIjo1fQ' b'.KEh186CjVw_Q8FadjJcaVnE7hO5Z9nHBbU8TgbhHcBY' ) with pytest.raises(InvalidAlgorithmError): jws.decode(example_jws, 'secret') def test_invalid_crypto_alg(self, jws, payload): with pytest.raises(NotImplementedError): jws.encode(payload, 'secret', algorithm='HS1024') @pytest.mark.skipif(has_crypto, reason='Scenario requires cryptography to not be installed') def test_missing_crypto_library_better_error_messages(self, jws, payload): with pytest.raises(NotImplementedError) as excinfo: jws.encode(payload, 'secret', algorithm='RS256') assert 'cryptography' in str(excinfo.value) def test_unicode_secret(self, jws, payload): secret = '\xc2' jws_message = jws.encode(payload, secret) decoded_payload = jws.decode(jws_message, secret) assert decoded_payload == payload def test_nonascii_secret(self, jws, payload): secret = '\xc2' # char value that ascii codec cannot decode jws_message = jws.encode(payload, secret) decoded_payload = jws.decode(jws_message, secret) assert decoded_payload == payload def test_bytes_secret(self, jws, payload): secret = b'\xc2' # char value that ascii codec cannot decode jws_message = jws.encode(payload, secret) decoded_payload = jws.decode(jws_message, secret) assert decoded_payload == payload def test_decode_invalid_header_padding(self, jws): example_jws = ( 'aeyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9' '.eyJoZWxsbyI6ICJ3b3JsZCJ9' '.tvagLDLoaiJKxOKqpBXSEGy7SYSifZhjntgm9ctpyj8') example_secret = 'secret' with pytest.raises(DecodeError) as exc: jws.decode(example_jws, example_secret) assert 'header padding' in str(exc.value) def test_decode_invalid_header_string(self, jws): example_jws = ( 'eyJhbGciOiAiSFMyNTbpIiwgInR5cCI6ICJKV1QifQ==' '.eyJoZWxsbyI6ICJ3b3JsZCJ9' '.tvagLDLoaiJKxOKqpBXSEGy7SYSifZhjntgm9ctpyj8') example_secret = 'secret' with pytest.raises(DecodeError) as exc: jws.decode(example_jws, example_secret) assert 'Invalid header' in str(exc.value) def test_decode_invalid_payload_padding(self, jws): example_jws = ( 'eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9' '.aeyJoZWxsbyI6ICJ3b3JsZCJ9' '.tvagLDLoaiJKxOKqpBXSEGy7SYSifZhjntgm9ctpyj8') example_secret = 'secret' with pytest.raises(DecodeError) as exc: jws.decode(example_jws, example_secret) assert 'Invalid payload padding' in str(exc.value) def test_decode_invalid_crypto_padding(self, jws): example_jws = ( 'eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9' '.eyJoZWxsbyI6ICJ3b3JsZCJ9' '.aatvagLDLoaiJKxOKqpBXSEGy7SYSifZhjntgm9ctpyj8') example_secret = 'secret' with pytest.raises(DecodeError) as exc: jws.decode(example_jws, example_secret) assert 'Invalid crypto padding' in str(exc.value) def test_decode_with_algo_none_should_fail(self, jws, payload): jws_message = jws.encode(payload, key=None, algorithm=None) with pytest.raises(DecodeError): jws.decode(jws_message) def test_decode_with_algo_none_and_verify_false_should_pass(self, jws, payload): jws_message = jws.encode(payload, key=None, algorithm=None) jws.decode(jws_message, verify=False) def test_get_unverified_header_returns_header_values(self, jws, payload): jws_message = jws.encode(payload, key='secret', algorithm='HS256', headers={'kid': 'toomanysecrets'}) header = jws.get_unverified_header(jws_message) assert 'kid' in header assert header['kid'] == 'toomanysecrets' def test_get_unverified_header_fails_on_bad_header_types(self, jws, payload): # Contains a bad kid value (int 123 instead of string) example_jws = ( 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6MTIzfQ' '.eyJzdWIiOiIxMjM0NTY3ODkwIn0' '.vs2WY54jfpKP3JGC73Vq5YlMsqM5oTZ1ZydT77SiZSk') with pytest.raises(InvalidTokenError) as exc: jws.get_unverified_header(example_jws) assert 'Key ID header parameter must be a string' == str(exc.value) @pytest.mark.skipif(not has_crypto, reason='Not supported without cryptography library') def test_encode_decode_with_rsa_sha256(self, jws, payload): # PEM-formatted RSA key with open('tests/keys/testkey_rsa', 'r') as rsa_priv_file: priv_rsakey = load_pem_private_key(force_bytes(rsa_priv_file.read()), password=None, backend=default_backend()) jws_message = jws.encode(payload, priv_rsakey, algorithm='RS256') with open('tests/keys/testkey_rsa.pub', 'r') as rsa_pub_file: pub_rsakey = load_ssh_public_key(force_bytes(rsa_pub_file.read()), backend=default_backend()) jws.decode(jws_message, pub_rsakey) # string-formatted key with open('tests/keys/testkey_rsa', 'r') as rsa_priv_file: priv_rsakey = rsa_priv_file.read() jws_message = jws.encode(payload, priv_rsakey, algorithm='RS256') with open('tests/keys/testkey_rsa.pub', 'r') as rsa_pub_file: pub_rsakey = rsa_pub_file.read() jws.decode(jws_message, pub_rsakey) @pytest.mark.skipif(not has_crypto, reason='Not supported without cryptography library') def test_encode_decode_with_rsa_sha384(self, jws, payload): # PEM-formatted RSA key with open('tests/keys/testkey_rsa', 'r') as rsa_priv_file: priv_rsakey = load_pem_private_key(force_bytes(rsa_priv_file.read()), password=None, backend=default_backend()) jws_message = jws.encode(payload, priv_rsakey, algorithm='RS384') with open('tests/keys/testkey_rsa.pub', 'r') as rsa_pub_file: pub_rsakey = load_ssh_public_key(force_bytes(rsa_pub_file.read()), backend=default_backend()) jws.decode(jws_message, pub_rsakey) # string-formatted key with open('tests/keys/testkey_rsa', 'r') as rsa_priv_file: priv_rsakey = rsa_priv_file.read() jws_message = jws.encode(payload, priv_rsakey, algorithm='RS384') with open('tests/keys/testkey_rsa.pub', 'r') as rsa_pub_file: pub_rsakey = rsa_pub_file.read() jws.decode(jws_message, pub_rsakey) @pytest.mark.skipif(not has_crypto, reason='Not supported without cryptography library') def test_encode_decode_with_rsa_sha512(self, jws, payload): # PEM-formatted RSA key with open('tests/keys/testkey_rsa', 'r') as rsa_priv_file: priv_rsakey = load_pem_private_key(force_bytes(rsa_priv_file.read()), password=None, backend=default_backend()) jws_message = jws.encode(payload, priv_rsakey, algorithm='RS512') with open('tests/keys/testkey_rsa.pub', 'r') as rsa_pub_file: pub_rsakey = load_ssh_public_key(force_bytes(rsa_pub_file.read()), backend=default_backend()) jws.decode(jws_message, pub_rsakey) # string-formatted key with open('tests/keys/testkey_rsa', 'r') as rsa_priv_file: priv_rsakey = rsa_priv_file.read() jws_message = jws.encode(payload, priv_rsakey, algorithm='RS512') with open('tests/keys/testkey_rsa.pub', 'r') as rsa_pub_file: pub_rsakey = rsa_pub_file.read() jws.decode(jws_message, pub_rsakey) def test_rsa_related_algorithms(self, jws): jws = PyJWS() jws_algorithms = jws.get_algorithms() if has_crypto: assert 'RS256' in jws_algorithms assert 'RS384' in jws_algorithms assert 'RS512' in jws_algorithms assert 'PS256' in jws_algorithms assert 'PS384' in jws_algorithms assert 'PS512' in jws_algorithms else: assert 'RS256' not in jws_algorithms assert 'RS384' not in jws_algorithms assert 'RS512' not in jws_algorithms assert 'PS256' not in jws_algorithms assert 'PS384' not in jws_algorithms assert 'PS512' not in jws_algorithms @pytest.mark.skipif(not has_crypto, reason="Can't run without cryptography library") def test_encode_decode_with_ecdsa_sha256(self, jws, payload): # PEM-formatted EC key with open('tests/keys/testkey_ec', 'r') as ec_priv_file: priv_eckey = load_pem_private_key(force_bytes(ec_priv_file.read()), password=None, backend=default_backend()) jws_message = jws.encode(payload, priv_eckey, algorithm='ES256') with open('tests/keys/testkey_ec.pub', 'r') as ec_pub_file: pub_eckey = load_pem_public_key(force_bytes(ec_pub_file.read()), backend=default_backend()) jws.decode(jws_message, pub_eckey) # string-formatted key with open('tests/keys/testkey_ec', 'r') as ec_priv_file: priv_eckey = ec_priv_file.read() jws_message = jws.encode(payload, priv_eckey, algorithm='ES256') with open('tests/keys/testkey_ec.pub', 'r') as ec_pub_file: pub_eckey = ec_pub_file.read() jws.decode(jws_message, pub_eckey) @pytest.mark.skipif(not has_crypto, reason="Can't run without cryptography library") def test_encode_decode_with_ecdsa_sha384(self, jws, payload): # PEM-formatted EC key with open('tests/keys/testkey_ec', 'r') as ec_priv_file: priv_eckey = load_pem_private_key(force_bytes(ec_priv_file.read()), password=None, backend=default_backend()) jws_message = jws.encode(payload, priv_eckey, algorithm='ES384') with open('tests/keys/testkey_ec.pub', 'r') as ec_pub_file: pub_eckey = load_pem_public_key(force_bytes(ec_pub_file.read()), backend=default_backend()) jws.decode(jws_message, pub_eckey) # string-formatted key with open('tests/keys/testkey_ec', 'r') as ec_priv_file: priv_eckey = ec_priv_file.read() jws_message = jws.encode(payload, priv_eckey, algorithm='ES384') with open('tests/keys/testkey_ec.pub', 'r') as ec_pub_file: pub_eckey = ec_pub_file.read() jws.decode(jws_message, pub_eckey) @pytest.mark.skipif(not has_crypto, reason="Can't run without cryptography library") def test_encode_decode_with_ecdsa_sha512(self, jws, payload): # PEM-formatted EC key with open('tests/keys/testkey_ec', 'r') as ec_priv_file: priv_eckey = load_pem_private_key(force_bytes(ec_priv_file.read()), password=None, backend=default_backend()) jws_message = jws.encode(payload, priv_eckey, algorithm='ES521') with open('tests/keys/testkey_ec.pub', 'r') as ec_pub_file: pub_eckey = load_pem_public_key(force_bytes(ec_pub_file.read()), backend=default_backend()) jws.decode(jws_message, pub_eckey) # string-formatted key with open('tests/keys/testkey_ec', 'r') as ec_priv_file: priv_eckey = ec_priv_file.read() jws_message = jws.encode(payload, priv_eckey, algorithm='ES521') with open('tests/keys/testkey_ec.pub', 'r') as ec_pub_file: pub_eckey = ec_pub_file.read() jws.decode(jws_message, pub_eckey) def test_ecdsa_related_algorithms(self, jws): jws = PyJWS() jws_algorithms = jws.get_algorithms() if has_crypto: assert 'ES256' in jws_algorithms assert 'ES384' in jws_algorithms assert 'ES521' in jws_algorithms else: assert 'ES256' not in jws_algorithms assert 'ES384' not in jws_algorithms assert 'ES521' not in jws_algorithms def test_skip_check_signature(self, jws): token = ("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9" ".eyJzb21lIjoicGF5bG9hZCJ9" ".4twFt5NiznN84AWoo1d7KO1T_yoc0Z6XOpOVswacPZA") jws.decode(token, 'secret', options={'verify_signature': False}) def test_decode_options_must_be_dict(self, jws, payload): token = jws.encode(payload, 'secret') with pytest.raises(TypeError): jws.decode(token, 'secret', options=object()) with pytest.raises(TypeError): jws.decode(token, 'secret', options='something') def test_custom_json_encoder(self, jws, payload): class CustomJSONEncoder(json.JSONEncoder): def default(self, o): if isinstance(o, Decimal): return 'it worked' return super(CustomJSONEncoder, self).default(o) data = { 'some_decimal': Decimal('2.2') } with pytest.raises(TypeError): jws.encode(payload, 'secret', headers=data) token = jws.encode(payload, 'secret', headers=data, json_encoder=CustomJSONEncoder) header = force_bytes(force_unicode(token).split('.')[0]) header = json.loads(force_unicode(base64url_decode(header))) assert 'some_decimal' in header assert header['some_decimal'] == 'it worked' def test_encode_headers_parameter_adds_headers(self, jws, payload): headers = {'testheader': True} token = jws.encode(payload, 'secret', headers=headers) if not isinstance(token, string_types): token = token.decode() header = token[0:token.index('.')].encode() header = base64url_decode(header) if not isinstance(header, text_type): header = header.decode() header_obj = json.loads(header) assert 'testheader' in header_obj assert header_obj['testheader'] == headers['testheader'] def test_encode_fails_on_invalid_kid_types(self, jws, payload): with pytest.raises(InvalidTokenError) as exc: jws.encode(payload, 'secret', headers={'kid': 123}) assert 'Key ID header parameter must be a string' == str(exc.value) with pytest.raises(InvalidTokenError) as exc: jws.encode(payload, 'secret', headers={'kid': None}) assert 'Key ID header parameter must be a string' == str(exc.value) PyJWT-1.5.3/tests/test_api_jwt.py0000644000076500000240000004147613153564550017633 0ustar jpadillastaff00000000000000 import json import time from calendar import timegm from datetime import datetime, timedelta from decimal import Decimal from jwt.api_jwt import PyJWT from jwt.exceptions import ( DecodeError, ExpiredSignatureError, ImmatureSignatureError, InvalidAudienceError, InvalidIssuedAtError, InvalidIssuerError, MissingRequiredClaimError ) import pytest from .test_api_jws import has_crypto from .utils import utc_timestamp @pytest.fixture def jwt(): return PyJWT() @pytest.fixture def payload(): """ Creates a sample JWT claimset for use as a payload during tests """ return { 'iss': 'jeff', 'exp': utc_timestamp() + 15, 'claim': 'insanity' } class TestJWT: def test_decodes_valid_jwt(self, jwt): example_payload = {'hello': 'world'} example_secret = 'secret' example_jwt = ( b'eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9' b'.eyJoZWxsbyI6ICJ3b3JsZCJ9' b'.tvagLDLoaiJKxOKqpBXSEGy7SYSifZhjntgm9ctpyj8') decoded_payload = jwt.decode(example_jwt, example_secret) assert decoded_payload == example_payload def test_load_verify_valid_jwt(self, jwt): example_payload = {'hello': 'world'} example_secret = 'secret' example_jwt = ( b'eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9' b'.eyJoZWxsbyI6ICJ3b3JsZCJ9' b'.tvagLDLoaiJKxOKqpBXSEGy7SYSifZhjntgm9ctpyj8') decoded_payload = jwt.decode(example_jwt, key=example_secret) assert decoded_payload == example_payload def test_decode_invalid_payload_string(self, jwt): example_jwt = ( 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.aGVsb' 'G8gd29ybGQ.SIr03zM64awWRdPrAM_61QWsZchAtgDV' '3pphfHPPWkI') example_secret = 'secret' with pytest.raises(DecodeError) as exc: jwt.decode(example_jwt, example_secret) assert 'Invalid payload string' in str(exc.value) def test_decode_with_non_mapping_payload_throws_exception(self, jwt): secret = 'secret' example_jwt = ('eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.' 'MQ.' # == 1 'AbcSR3DWum91KOgfKxUHm78rLs_DrrZ1CrDgpUFFzls') with pytest.raises(DecodeError) as context: jwt.decode(example_jwt, secret) exception = context.value assert str(exception) == 'Invalid payload string: must be a json object' def test_decode_with_invalid_audience_param_throws_exception(self, jwt): secret = 'secret' example_jwt = ('eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9' '.eyJoZWxsbyI6ICJ3b3JsZCJ9' '.tvagLDLoaiJKxOKqpBXSEGy7SYSifZhjntgm9ctpyj8') with pytest.raises(TypeError) as context: jwt.decode(example_jwt, secret, audience=1) exception = context.value assert str(exception) == 'audience must be a string or None' def test_decode_with_nonlist_aud_claim_throws_exception(self, jwt): secret = 'secret' example_jwt = ('eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9' '.eyJoZWxsbyI6IndvcmxkIiwiYXVkIjoxfQ' # aud = 1 '.Rof08LBSwbm8Z_bhA2N3DFY-utZR1Gi9rbIS5Zthnnc') with pytest.raises(InvalidAudienceError) as context: jwt.decode(example_jwt, secret, audience='my_audience') exception = context.value assert str(exception) == 'Invalid claim format in token' def test_decode_with_invalid_aud_list_member_throws_exception(self, jwt): secret = 'secret' example_jwt = ('eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9' '.eyJoZWxsbyI6IndvcmxkIiwiYXVkIjpbMV19' '.iQgKpJ8shetwNMIosNXWBPFB057c2BHs-8t1d2CCM2A') with pytest.raises(InvalidAudienceError) as context: jwt.decode(example_jwt, secret, audience='my_audience') exception = context.value assert str(exception) == 'Invalid claim format in token' def test_encode_bad_type(self, jwt): types = ['string', tuple(), list(), 42, set()] for t in types: pytest.raises(TypeError, lambda: jwt.encode(t, 'secret')) def test_decode_raises_exception_if_exp_is_not_int(self, jwt): # >>> jwt.encode({'exp': 'not-an-int'}, 'secret') example_jwt = ('eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.' 'eyJleHAiOiJub3QtYW4taW50In0.' 'P65iYgoHtBqB07PMtBSuKNUEIPPPfmjfJG217cEE66s') with pytest.raises(DecodeError) as exc: jwt.decode(example_jwt, 'secret') assert 'exp' in str(exc.value) def test_decode_raises_exception_if_iat_is_not_int(self, jwt): # >>> jwt.encode({'iat': 'not-an-int'}, 'secret') example_jwt = ('eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.' 'eyJpYXQiOiJub3QtYW4taW50In0.' 'H1GmcQgSySa5LOKYbzGm--b1OmRbHFkyk8pq811FzZM') with pytest.raises(InvalidIssuedAtError): jwt.decode(example_jwt, 'secret') def test_decode_raises_exception_if_nbf_is_not_int(self, jwt): # >>> jwt.encode({'nbf': 'not-an-int'}, 'secret') example_jwt = ('eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.' 'eyJuYmYiOiJub3QtYW4taW50In0.' 'c25hldC8G2ZamC8uKpax9sYMTgdZo3cxrmzFHaAAluw') with pytest.raises(DecodeError): jwt.decode(example_jwt, 'secret') def test_encode_datetime(self, jwt): secret = 'secret' current_datetime = datetime.utcnow() payload = { 'exp': current_datetime, 'iat': current_datetime, 'nbf': current_datetime } jwt_message = jwt.encode(payload, secret) decoded_payload = jwt.decode(jwt_message, secret, leeway=1) assert (decoded_payload['exp'] == timegm(current_datetime.utctimetuple())) assert (decoded_payload['iat'] == timegm(current_datetime.utctimetuple())) assert (decoded_payload['nbf'] == timegm(current_datetime.utctimetuple())) # 'Control' Elliptic Curve JWT created by another library. # Used to test for regressions that could affect both # encoding / decoding operations equally (causing tests # to still pass). @pytest.mark.skipif(not has_crypto, reason="Can't run without cryptography library") def test_decodes_valid_es384_jwt(self, jwt): example_payload = {'hello': 'world'} with open('tests/keys/testkey_ec.pub', 'r') as fp: example_pubkey = fp.read() example_jwt = ( b'eyJhbGciOiJFUzM4NCIsInR5cCI6IkpXVCJ9' b'.eyJoZWxsbyI6IndvcmxkIn0' b'.AddMgkmRhzqptDYqlmy_f2dzM6O9YZmVo-txs_CeAJD' b'NoD8LN7YiPeLmtIhkO5_VZeHHKvtQcGc4lsq-Y72c4dK' b'pANr1f6HEYhjpBc03u_bv06PYMcr5N2-9k97-qf-JCSb' b'zqW6R250Q7gNCX5R7NrCl7MTM4DTBZkGbUlqsFUleiGlj') decoded_payload = jwt.decode(example_jwt, example_pubkey) assert decoded_payload == example_payload # 'Control' RSA JWT created by another library. # Used to test for regressions that could affect both # encoding / decoding operations equally (causing tests # to still pass). @pytest.mark.skipif(not has_crypto, reason="Can't run without cryptography library") def test_decodes_valid_rs384_jwt(self, jwt): example_payload = {'hello': 'world'} with open('tests/keys/testkey_rsa.pub', 'r') as fp: example_pubkey = fp.read() example_jwt = ( b'eyJhbGciOiJSUzM4NCIsInR5cCI6IkpXVCJ9' b'.eyJoZWxsbyI6IndvcmxkIn0' b'.yNQ3nI9vEDs7lEh-Cp81McPuiQ4ZRv6FL4evTYYAh1X' b'lRTTR3Cz8pPA9Stgso8Ra9xGB4X3rlra1c8Jz10nTUju' b'O06OMm7oXdrnxp1KIiAJDerWHkQ7l3dlizIk1bmMA457' b'W2fNzNfHViuED5ISM081dgf_a71qBwJ_yShMMrSOfxDx' b'mX9c4DjRogRJG8SM5PvpLqI_Cm9iQPGMvmYK7gzcq2cJ' b'urHRJDJHTqIdpLWXkY7zVikeen6FhuGyn060Dz9gYq9t' b'uwmrtSWCBUjiN8sqJ00CDgycxKqHfUndZbEAOjcCAhBr' b'qWW3mSVivUfubsYbwUdUG3fSRPjaUPcpe8A') decoded_payload = jwt.decode(example_jwt, example_pubkey) assert decoded_payload == example_payload def test_decode_with_expiration(self, jwt, payload): payload['exp'] = utc_timestamp() - 1 secret = 'secret' jwt_message = jwt.encode(payload, secret) with pytest.raises(ExpiredSignatureError): jwt.decode(jwt_message, secret) def test_decode_with_notbefore(self, jwt, payload): payload['nbf'] = utc_timestamp() + 10 secret = 'secret' jwt_message = jwt.encode(payload, secret) with pytest.raises(ImmatureSignatureError): jwt.decode(jwt_message, secret) def test_decode_skip_expiration_verification(self, jwt, payload): payload['exp'] = time.time() - 1 secret = 'secret' jwt_message = jwt.encode(payload, secret) jwt.decode(jwt_message, secret, options={'verify_exp': False}) def test_decode_skip_notbefore_verification(self, jwt, payload): payload['nbf'] = time.time() + 10 secret = 'secret' jwt_message = jwt.encode(payload, secret) jwt.decode(jwt_message, secret, options={'verify_nbf': False}) def test_decode_with_expiration_with_leeway(self, jwt, payload): payload['exp'] = utc_timestamp() - 2 secret = 'secret' jwt_message = jwt.encode(payload, secret) decoded_payload, signing, header, signature = jwt._load(jwt_message) # With 3 seconds leeway, should be ok for leeway in (3, timedelta(seconds=3)): jwt.decode(jwt_message, secret, leeway=leeway) # With 1 seconds, should fail for leeway in (1, timedelta(seconds=1)): with pytest.raises(ExpiredSignatureError): jwt.decode(jwt_message, secret, leeway=leeway) def test_decode_with_notbefore_with_leeway(self, jwt, payload): payload['nbf'] = utc_timestamp() + 10 secret = 'secret' jwt_message = jwt.encode(payload, secret) # With 13 seconds leeway, should be ok jwt.decode(jwt_message, secret, leeway=13) with pytest.raises(ImmatureSignatureError): jwt.decode(jwt_message, secret, leeway=1) def test_check_audience_when_valid(self, jwt): payload = { 'some': 'payload', 'aud': 'urn:me' } token = jwt.encode(payload, 'secret') jwt.decode(token, 'secret', audience='urn:me') def test_check_audience_in_array_when_valid(self, jwt): payload = { 'some': 'payload', 'aud': ['urn:me', 'urn:someone-else'] } token = jwt.encode(payload, 'secret') jwt.decode(token, 'secret', audience='urn:me') def test_raise_exception_invalid_audience(self, jwt): payload = { 'some': 'payload', 'aud': 'urn:someone-else' } token = jwt.encode(payload, 'secret') with pytest.raises(InvalidAudienceError): jwt.decode(token, 'secret', audience='urn-me') def test_raise_exception_invalid_audience_in_array(self, jwt): payload = { 'some': 'payload', 'aud': ['urn:someone', 'urn:someone-else'] } token = jwt.encode(payload, 'secret') with pytest.raises(InvalidAudienceError): jwt.decode(token, 'secret', audience='urn:me') def test_raise_exception_token_without_issuer(self, jwt): issuer = 'urn:wrong' payload = { 'some': 'payload' } token = jwt.encode(payload, 'secret') with pytest.raises(MissingRequiredClaimError) as exc: jwt.decode(token, 'secret', issuer=issuer) assert exc.value.claim == 'iss' def test_raise_exception_token_without_audience(self, jwt): payload = { 'some': 'payload', } token = jwt.encode(payload, 'secret') with pytest.raises(MissingRequiredClaimError) as exc: jwt.decode(token, 'secret', audience='urn:me') assert exc.value.claim == 'aud' def test_check_issuer_when_valid(self, jwt): issuer = 'urn:foo' payload = { 'some': 'payload', 'iss': 'urn:foo' } token = jwt.encode(payload, 'secret') jwt.decode(token, 'secret', issuer=issuer) def test_raise_exception_invalid_issuer(self, jwt): issuer = 'urn:wrong' payload = { 'some': 'payload', 'iss': 'urn:foo' } token = jwt.encode(payload, 'secret') with pytest.raises(InvalidIssuerError): jwt.decode(token, 'secret', issuer=issuer) def test_skip_check_audience(self, jwt): payload = { 'some': 'payload', 'aud': 'urn:me', } token = jwt.encode(payload, 'secret') jwt.decode(token, 'secret', options={'verify_aud': False}) def test_skip_check_exp(self, jwt): payload = { 'some': 'payload', 'exp': datetime.utcnow() - timedelta(days=1) } token = jwt.encode(payload, 'secret') jwt.decode(token, 'secret', options={'verify_exp': False}) def test_decode_should_raise_error_if_exp_required_but_not_present(self, jwt): payload = { 'some': 'payload', # exp not present } token = jwt.encode(payload, 'secret') with pytest.raises(MissingRequiredClaimError) as exc: jwt.decode(token, 'secret', options={'require_exp': True}) assert exc.value.claim == 'exp' def test_decode_should_raise_error_if_iat_required_but_not_present(self, jwt): payload = { 'some': 'payload', # iat not present } token = jwt.encode(payload, 'secret') with pytest.raises(MissingRequiredClaimError) as exc: jwt.decode(token, 'secret', options={'require_iat': True}) assert exc.value.claim == 'iat' def test_decode_should_raise_error_if_nbf_required_but_not_present(self, jwt): payload = { 'some': 'payload', # nbf not present } token = jwt.encode(payload, 'secret') with pytest.raises(MissingRequiredClaimError) as exc: jwt.decode(token, 'secret', options={'require_nbf': True}) assert exc.value.claim == 'nbf' def test_skip_check_signature(self, jwt): token = ("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9" ".eyJzb21lIjoicGF5bG9hZCJ9" ".4twFt5NiznN84AWoo1d7KO1T_yoc0Z6XOpOVswacPZA") jwt.decode(token, 'secret', options={'verify_signature': False}) def test_skip_check_iat(self, jwt): payload = { 'some': 'payload', 'iat': datetime.utcnow() + timedelta(days=1) } token = jwt.encode(payload, 'secret') jwt.decode(token, 'secret', options={'verify_iat': False}) def test_skip_check_nbf(self, jwt): payload = { 'some': 'payload', 'nbf': datetime.utcnow() + timedelta(days=1) } token = jwt.encode(payload, 'secret') jwt.decode(token, 'secret', options={'verify_nbf': False}) def test_custom_json_encoder(self, jwt): class CustomJSONEncoder(json.JSONEncoder): def default(self, o): if isinstance(o, Decimal): return 'it worked' return super(CustomJSONEncoder, self).default(o) data = { 'some_decimal': Decimal('2.2') } with pytest.raises(TypeError): jwt.encode(data, 'secret') token = jwt.encode(data, 'secret', json_encoder=CustomJSONEncoder) payload = jwt.decode(token, 'secret') assert payload == {'some_decimal': 'it worked'} def test_decode_with_verify_expiration_kwarg(self, jwt, payload): payload['exp'] = utc_timestamp() - 1 secret = 'secret' jwt_message = jwt.encode(payload, secret) pytest.deprecated_call( jwt.decode, jwt_message, secret, verify_expiration=False ) with pytest.raises(ExpiredSignatureError): pytest.deprecated_call( jwt.decode, jwt_message, secret, verify_expiration=True ) def test_decode_with_optional_algorithms(self, jwt, payload): secret = 'secret' jwt_message = jwt.encode(payload, secret) pytest.deprecated_call( jwt.decode, jwt_message, secret ) def test_decode_no_algorithms_verify_false(self, jwt, payload): secret = 'secret' jwt_message = jwt.encode(payload, secret) try: pytest.deprecated_call( jwt.decode, jwt_message, secret, verify=False, ) except AssertionError: pass else: assert False, "Unexpected DeprecationWarning raised." PyJWT-1.5.3/tests/test_cli.py0000644000076500000240000000763513122544212016731 0ustar jpadillastaff00000000000000 import argparse import json import sys import jwt from jwt.__main__ import build_argparser, decode_payload, encode_payload, main import pytest class TestCli: def test_build_argparse(self): args = ['--key', '1234', 'encode', 'name=Vader'] parser = build_argparser() parsed_args = parser.parse_args(args) assert parsed_args.key == '1234' def test_encode_payload_raises_value_error_key_is_required(self): encode_args = ['encode', 'name=Vader', 'job=Sith'] parser = build_argparser() args = parser.parse_args(encode_args) with pytest.raises(ValueError) as excinfo: encode_payload(args) assert 'Key is required when encoding' in str(excinfo.value) def test_decode_payload_raises_decoded_error(self): decode_args = ['--key', '1234', 'decode', 'wrong-token'] parser = build_argparser() args = parser.parse_args(decode_args) with pytest.raises(jwt.DecodeError) as excinfo: decode_payload(args) assert 'There was an error decoding the token' in str(excinfo.value) def test_decode_payload_raises_decoded_error_isatty(self, monkeypatch): def patched_sys_stdin_read(): raise jwt.DecodeError() decode_args = ['--key', '1234', 'decode', 'wrong-token'] parser = build_argparser() args = parser.parse_args(decode_args) monkeypatch.setattr(sys.stdin, 'isatty', lambda: True) monkeypatch.setattr(sys.stdin, 'read', patched_sys_stdin_read) with pytest.raises(jwt.DecodeError) as excinfo: decode_payload(args) assert 'There was an error decoding the token' in str(excinfo.value) @pytest.mark.parametrize('key,name,job,exp,verify', [ ('1234', 'Vader', 'Sith', None, None), ('4567', 'Anakin', 'Jedi', '+1', None), ('4321', 'Padme', 'Queen', '4070926800', 'true'), ]) def test_encode_decode(self, key, name, job, exp, verify): encode_args = [ '--key={0}'.format(key), 'encode', 'name={0}'.format(name), 'job={0}'.format(job), ] if exp: encode_args.append('exp={0}'.format(exp)) if verify: encode_args.append('verify={0}'.format(verify)) parser = build_argparser() parsed_encode_args = parser.parse_args(encode_args) token = encode_payload(parsed_encode_args) assert token is not None assert token is not '' decode_args = [ '--key={0}'.format(key), 'decode', token ] parser = build_argparser() parsed_decode_args = parser.parse_args(decode_args) actual = json.loads(decode_payload(parsed_decode_args)) expected = { 'job': job, 'name': name, } assert actual['name'] == expected['name'] assert actual['job'] == expected['job'] @pytest.mark.parametrize('key,name,job,exp,verify', [ ('1234', 'Vader', 'Sith', None, None), ('4567', 'Anakin', 'Jedi', '+1', None), ('4321', 'Padme', 'Queen', '4070926800', 'true'), ]) def test_main(self, monkeypatch, key, name, job, exp, verify): args = [ 'test_cli.py', '--key={0}'.format(key), 'encode', 'name={0}'.format(name), 'job={0}'.format(job), ] if exp: args.append('exp={0}'.format(exp)) if verify: args.append('verify={0}'.format(verify)) monkeypatch.setattr(sys, 'argv', args) main() def test_main_throw_exception(self, monkeypatch, capsys): def patched_argparser_parse_args(self, args): raise Exception('NOOOOOOOOOOO!') monkeypatch.setattr(argparse.ArgumentParser, 'parse_args', patched_argparser_parse_args) main() out, _ = capsys.readouterr() assert 'NOOOOOOOOOOO!' in out PyJWT-1.5.3/tests/test_compat.py0000644000076500000240000000115513060030443017431 0ustar jpadillastaff00000000000000from jwt.compat import constant_time_compare from jwt.utils import force_bytes class TestCompat: def test_constant_time_compare_returns_true_if_same(self): assert constant_time_compare( force_bytes('abc'), force_bytes('abc') ) def test_constant_time_compare_returns_false_if_diff_lengths(self): assert not constant_time_compare( force_bytes('abc'), force_bytes('abcd') ) def test_constant_time_compare_returns_false_if_totally_different(self): assert not constant_time_compare( force_bytes('abcd'), force_bytes('efgh') ) PyJWT-1.5.3/tests/test_exceptions.py0000644000076500000240000000032512552053647020345 0ustar jpadillastaff00000000000000from jwt.exceptions import MissingRequiredClaimError def test_missing_required_claim_error_has_proper_str(): exc = MissingRequiredClaimError('abc') assert str(exc) == 'Token is missing the "abc" claim' PyJWT-1.5.3/tests/test_jwt.py0000644000076500000240000000114612532343527016767 0ustar jpadillastaff00000000000000import jwt from .utils import utc_timestamp def test_encode_decode(): """ This test exists primarily to ensure that calls to jwt.encode and jwt.decode don't explode. Most functionality is tested by the PyJWT class tests. This is primarily a sanity check to make sure we don't break the public global functions. """ payload = { 'iss': 'jeff', 'exp': utc_timestamp() + 15, 'claim': 'insanity' } secret = 'secret' jwt_message = jwt.encode(payload, secret) decoded_payload = jwt.decode(jwt_message, secret) assert decoded_payload == payload PyJWT-1.5.3/tests/test_utils.py0000644000076500000240000000166513060030443017314 0ustar jpadillastaff00000000000000from jwt.utils import ( force_bytes, force_unicode, from_base64url_uint, to_base64url_uint ) import pytest @pytest.mark.parametrize("inputval,expected", [ (0, b'AA'), (1, b'AQ'), (255, b'_w'), (65537, b'AQAB'), (123456789, b'B1vNFQ'), pytest.mark.xfail((-1, ''), raises=ValueError) ]) def test_to_base64url_uint(inputval, expected): actual = to_base64url_uint(inputval) assert actual == expected @pytest.mark.parametrize("inputval,expected", [ (b'AA', 0), (b'AQ', 1), (b'_w', 255), (b'AQAB', 65537), (b'B1vNFQ', 123456789, ), ]) def test_from_base64url_uint(inputval, expected): actual = from_base64url_uint(inputval) assert actual == expected def test_force_unicode_raises_error_on_invalid_object(): with pytest.raises(TypeError): force_unicode({}) def test_force_bytes_raises_error_on_invalid_object(): with pytest.raises(TypeError): force_bytes({}) PyJWT-1.5.3/tests/utils.py0000644000076500000240000000144413060030443016250 0ustar jpadillastaff00000000000000import os import struct from calendar import timegm from datetime import datetime def utc_timestamp(): return timegm(datetime.utcnow().utctimetuple()) def key_path(key_name): return os.path.join(os.path.dirname(os.path.realpath(__file__)), 'keys', key_name) # Borrowed from `cryptography` if hasattr(int, "from_bytes"): int_from_bytes = int.from_bytes else: def int_from_bytes(data, byteorder, signed=False): assert byteorder == 'big' assert not signed if len(data) % 4 != 0: data = (b'\x00' * (4 - (len(data) % 4))) + data result = 0 while len(data) > 0: digit, = struct.unpack('>I', data[:4]) result = (result << 32) + digit data = data[4:] return result PyJWT-1.5.3/tox.ini0000644000076500000240000000052313107201014014700 0ustar jpadillastaff00000000000000[tox] envlist = py{26,27,33,34,35,36}-crypto, py{27,35,36}-contrib_crypto, py{27,35,36}-nocrypto, flake8 [testenv] commands = python setup.py pytest deps = crypto: cryptography contrib_crypto: pycrypto contrib_crypto: ecdsa [testenv:flake8] commands = flake8 deps = flake8 flake8-import-order pep8-naming