diff --git a/itsdangerous b/itsdangerous deleted file mode 120000 index ac5f0e33..00000000 --- a/itsdangerous +++ /dev/null @@ -1 +0,0 @@ -tails/lib/itsdangerous \ No newline at end of file diff --git a/onionshare/onionshare.py b/onionshare/onionshare.py index c699cfb1..55fb0689 100644 --- a/onionshare/onionshare.py +++ b/onionshare/onionshare.py @@ -8,13 +8,22 @@ from stem import SocketError from flask import Flask, Markup, Response, request, make_response, send_from_directory, render_template_string, abort -# Flask depends on itsdangerous, which needs constant time string comparison -# for the HMAC values in secure cookies. Since we know itsdangerous is -# available, we just use its function. -from itsdangerous import constant_time_compare +class NoTor(Exception): pass -class NoTor(Exception): - pass +def constant_time_compare(val1, val2): + _builtin_constant_time_compare = getattr(hmac, 'compare_digest', None) + if _builtin_constant_time_compare is not None: + return _builtin_constant_time_compare(val1, val2) + len_eq = len(val1) == len(val2) + if len_eq: + result = 0 + left = val1 + else: + result = 1 + left = val2 + for x, y in izip(bytearray(left), bytearray(val2)): + result |= x ^ y + return result == 0 def random_string(num_bytes): b = os.urandom(num_bytes) diff --git a/setup.py b/setup.py index 80a76745..492e7ac4 100644 --- a/setup.py +++ b/setup.py @@ -18,12 +18,6 @@ def file_list(path): packages = ['onionshare', 'onionshare_gui'] -sys.path.remove(os.getcwd()) -try: - import itsdangerous -except ImportError: - packages.append('itsdangerous') - version = open('version').read().strip() setup( diff --git a/tails/lib/itsdangerous/__init__.py b/tails/lib/itsdangerous/__init__.py deleted file mode 100644 index a3411558..00000000 --- a/tails/lib/itsdangerous/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from itsdangerous import * diff --git a/tails/lib/itsdangerous/itsdangerous.py b/tails/lib/itsdangerous/itsdangerous.py deleted file mode 100644 index 228d1012..00000000 --- a/tails/lib/itsdangerous/itsdangerous.py +++ /dev/null @@ -1,872 +0,0 @@ -# -*- coding: utf-8 -*- -""" - itsdangerous - ~~~~~~~~~~~~ - - A module that implements various functions to deal with untrusted - sources. Mainly useful for web applications. - - :copyright: (c) 2014 by Armin Ronacher and the Django Software Foundation. - :license: BSD, see LICENSE for more details. -""" - -import sys -import hmac -import zlib -import time -import base64 -import hashlib -import operator -from datetime import datetime - - -PY2 = sys.version_info[0] == 2 -if PY2: - from itertools import izip - text_type = unicode - int_to_byte = chr - number_types = (int, long, float) -else: - from functools import reduce - izip = zip - text_type = str - int_to_byte = operator.methodcaller('to_bytes', 1, 'big') - number_types = (int, float) - - -try: - import simplejson as json -except ImportError: - import json - - -class _CompactJSON(object): - """Wrapper around simplejson that strips whitespace. - """ - - def loads(self, payload): - return json.loads(payload) - - def dumps(self, obj): - return json.dumps(obj, separators=(',', ':')) - - -compact_json = _CompactJSON() - - -# 2011/01/01 in UTC -EPOCH = 1293840000 - - -def want_bytes(s, encoding='utf-8', errors='strict'): - if isinstance(s, text_type): - s = s.encode(encoding, errors) - return s - - -def is_text_serializer(serializer): - """Checks wheather a serializer generates text or binary.""" - return isinstance(serializer.dumps({}), text_type) - - -# Starting with 3.3 the standard library has a c-implementation for -# constant time string compares. -_builtin_constant_time_compare = getattr(hmac, 'compare_digest', None) - - -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. Do - not use this function for anything else than comparision with known - length targets. - - This is should be implemented in C in order to get it completely right. - """ - if _builtin_constant_time_compare is not None: - return _builtin_constant_time_compare(val1, val2) - len_eq = len(val1) == len(val2) - if len_eq: - result = 0 - left = val1 - else: - result = 1 - left = val2 - for x, y in izip(bytearray(left), bytearray(val2)): - result |= x ^ y - return result == 0 - - -class BadData(Exception): - """Raised if bad data of any sort was encountered. This is the - base for all exceptions that itsdangerous is currently using. - - .. versionadded:: 0.15 - """ - message = None - - def __init__(self, message): - Exception.__init__(self, message) - self.message = message - - def __str__(self): - return text_type(self.message) - - if PY2: - __unicode__ = __str__ - def __str__(self): - return self.__unicode__().encode('utf-8') - - -class BadPayload(BadData): - """This error is raised in situations when payload is loaded without - checking the signature first and an exception happend as a result of - that. The original exception that caused that will be stored on the - exception as :attr:`original_error`. - - This can also happen with a :class:`JSONWebSignatureSerializer` that - is subclassed and uses a different serializer for the payload than - the expected one. - - .. versionadded:: 0.15 - """ - - def __init__(self, message, original_error=None): - BadData.__init__(self, message) - #: If available, the error that indicates why the payload - #: was not valid. This might be `None`. - self.original_error = original_error - - -class BadSignature(BadData): - """This error is raised if a signature does not match. As of - itsdangerous 0.14 there are helpful attributes on the exception - instances. You can also catch down the baseclass :exc:`BadData`. - """ - - def __init__(self, message, payload=None): - BadData.__init__(self, message) - #: The payload that failed the signature test. In some - #: situations you might still want to inspect this, even if - #: you know it was tampered with. - #: - #: .. versionadded:: 0.14 - self.payload = payload - - -class BadTimeSignature(BadSignature): - """Raised for time based signatures that fail. This is a subclass - of :class:`BadSignature` so you can catch those down as well. - """ - - def __init__(self, message, payload=None, date_signed=None): - BadSignature.__init__(self, message, payload) - - #: If the signature expired this exposes the date of when the - #: signature was created. This can be helpful in order to - #: tell the user how long a link has been gone stale. - #: - #: .. versionadded:: 0.14 - self.date_signed = date_signed - - -class BadHeader(BadSignature): - """Raised if a signed header is invalid in some form. This only - happens for serializers that have a header that goes with the - signature. - - .. versionadded:: 0.24 - """ - - def __init__(self, message, payload=None, header=None, - original_error=None): - BadSignature.__init__(self, message, payload) - - #: If the header is actually available but just malformed it - #: might be stored here. - self.header = header - - #: If available, the error that indicates why the payload - #: was not valid. This might be `None`. - self.original_error = original_error - - -class SignatureExpired(BadTimeSignature): - """Signature timestamp is older than required max_age. This is a - subclass of :exc:`BadTimeSignature` so you can use the baseclass for - catching the error. - """ - - -def base64_encode(string): - """base64 encodes a single bytestring (and is tolerant to getting - called with a unicode string). - The resulting bytestring is safe for putting into URLs. - """ - string = want_bytes(string) - return base64.urlsafe_b64encode(string).strip(b'=') - - -def base64_decode(string): - """base64 decodes a single bytestring (and is tolerant to getting - called with a unicode string). - The result is also a bytestring. - """ - string = want_bytes(string, encoding='ascii', errors='ignore') - return base64.urlsafe_b64decode(string + b'=' * (-len(string) % 4)) - - -def int_to_bytes(num): - assert num >= 0 - rv = [] - while num: - rv.append(int_to_byte(num & 0xff)) - num >>= 8 - return b''.join(reversed(rv)) - - -def bytes_to_int(bytestr): - return reduce(lambda a, b: a << 8 | b, bytearray(bytestr), 0) - - -class SigningAlgorithm(object): - """Subclasses of `SigningAlgorithm` have to implement `get_signature` to - provide signature generation functionality. - """ - - def get_signature(self, key, value): - """Returns the signature for the given key and value""" - raise NotImplementedError() - - def verify_signature(self, key, value, sig): - """Verifies the given signature matches the expected signature""" - return constant_time_compare(sig, self.get_signature(key, value)) - - -class NoneAlgorithm(SigningAlgorithm): - """This class provides a algorithm that does not perform any signing and - returns an empty signature. - """ - - def get_signature(self, key, value): - return b'' - - -class HMACAlgorithm(SigningAlgorithm): - """This class provides signature generation using HMACs.""" - - #: The digest method to use with the MAC algorithm. This defaults to sha1 - #: but can be changed for any other function in the hashlib module. - default_digest_method = staticmethod(hashlib.sha1) - - def __init__(self, digest_method=None): - if digest_method is None: - digest_method = self.default_digest_method - self.digest_method = digest_method - - def get_signature(self, key, value): - mac = hmac.new(key, msg=value, digestmod=self.digest_method) - return mac.digest() - - -class Signer(object): - """This class can sign bytes and unsign it and validate the signature - provided. - - Salt can be used to namespace the hash, so that a signed string is only - valid for a given namespace. Leaving this at the default value or re-using - a salt value across different parts of your application where the same - signed value in one part can mean something different in another part - is a security risk. - - See :ref:`the-salt` for an example of what the salt is doing and how you - can utilize it. - - .. versionadded:: 0.14 - `key_derivation` and `digest_method` were added as arguments to the - class constructor. - - .. versionadded:: 0.18 - `algorithm` was added as an argument to the class constructor. - """ - - #: The digest method to use for the signer. This defaults to sha1 but can - #: be changed for any other function in the hashlib module. - #: - #: .. versionchanged:: 0.14 - default_digest_method = staticmethod(hashlib.sha1) - - #: Controls how the key is derived. The default is Django style - #: concatenation. Possible values are ``concat``, ``django-concat`` - #: and ``hmac``. This is used for deriving a key from the secret key - #: with an added salt. - #: - #: .. versionadded:: 0.14 - default_key_derivation = 'django-concat' - - def __init__(self, secret_key, salt=None, sep='.', key_derivation=None, - digest_method=None, algorithm=None): - self.secret_key = want_bytes(secret_key) - self.sep = sep - self.salt = 'itsdangerous.Signer' if salt is None else salt - if key_derivation is None: - key_derivation = self.default_key_derivation - self.key_derivation = key_derivation - if digest_method is None: - digest_method = self.default_digest_method - self.digest_method = digest_method - if algorithm is None: - algorithm = HMACAlgorithm(self.digest_method) - self.algorithm = algorithm - - def derive_key(self): - """This method is called to derive the key. If you're unhappy with - the default key derivation choices you can override them here. - Keep in mind that the key derivation in itsdangerous is not intended - to be used as a security method to make a complex key out of a short - password. Instead you should use large random secret keys. - """ - salt = want_bytes(self.salt) - if self.key_derivation == 'concat': - return self.digest_method(salt + self.secret_key).digest() - elif self.key_derivation == 'django-concat': - return self.digest_method(salt + b'signer' + - self.secret_key).digest() - elif self.key_derivation == 'hmac': - mac = hmac.new(self.secret_key, digestmod=self.digest_method) - mac.update(salt) - return mac.digest() - elif self.key_derivation == 'none': - return self.secret_key - else: - raise TypeError('Unknown key derivation method') - - def get_signature(self, value): - """Returns the signature for the given value""" - value = want_bytes(value) - key = self.derive_key() - sig = self.algorithm.get_signature(key, value) - return base64_encode(sig) - - def sign(self, value): - """Signs the given string.""" - return value + want_bytes(self.sep) + self.get_signature(value) - - def verify_signature(self, value, sig): - """Verifies the signature for the given value.""" - key = self.derive_key() - try: - sig = base64_decode(sig) - except Exception: - return False - return self.algorithm.verify_signature(key, value, sig) - - def unsign(self, signed_value): - """Unsigns the given string.""" - signed_value = want_bytes(signed_value) - sep = want_bytes(self.sep) - if sep not in signed_value: - raise BadSignature('No %r found in value' % self.sep) - value, sig = signed_value.rsplit(sep, 1) - if self.verify_signature(value, sig): - return value - raise BadSignature('Signature %r does not match' % sig, - payload=value) - - def validate(self, signed_value): - """Just validates the given signed value. Returns `True` if the - signature exists and is valid, `False` otherwise.""" - try: - self.unsign(signed_value) - return True - except BadSignature: - return False - - -class TimestampSigner(Signer): - """Works like the regular :class:`Signer` but also records the time - of the signing and can be used to expire signatures. The unsign - method can rause a :exc:`SignatureExpired` method if the unsigning - failed because the signature is expired. This exception is a subclass - of :exc:`BadSignature`. - """ - - def get_timestamp(self): - """Returns the current timestamp. This implementation returns the - seconds since 1/1/2011. The function must return an integer. - """ - return int(time.time() - EPOCH) - - def timestamp_to_datetime(self, ts): - """Used to convert the timestamp from `get_timestamp` into a - datetime object. - """ - return datetime.utcfromtimestamp(ts + EPOCH) - - def sign(self, value): - """Signs the given string and also attaches a time information.""" - value = want_bytes(value) - timestamp = base64_encode(int_to_bytes(self.get_timestamp())) - sep = want_bytes(self.sep) - value = value + sep + timestamp - return value + sep + self.get_signature(value) - - def unsign(self, value, max_age=None, return_timestamp=False): - """Works like the regular :meth:`~Signer.unsign` but can also - validate the time. See the base docstring of the class for - the general behavior. If `return_timestamp` is set to `True` - the timestamp of the signature will be returned as naive - :class:`datetime.datetime` object in UTC. - """ - try: - result = Signer.unsign(self, value) - sig_error = None - except BadSignature as e: - sig_error = e - result = e.payload or b'' - sep = want_bytes(self.sep) - - # If there is no timestamp in the result there is something - # seriously wrong. In case there was a signature error, we raise - # that one directly, otherwise we have a weird situation in which - # we shouldn't have come except someone uses a time-based serializer - # on non-timestamp data, so catch that. - if not sep in result: - if sig_error: - raise sig_error - raise BadTimeSignature('timestamp missing', payload=result) - - value, timestamp = result.rsplit(sep, 1) - try: - timestamp = bytes_to_int(base64_decode(timestamp)) - except Exception: - timestamp = None - - # Signature is *not* okay. Raise a proper error now that we have - # split the value and the timestamp. - if sig_error is not None: - raise BadTimeSignature(text_type(sig_error), payload=value, - date_signed=timestamp) - - # Signature was okay but the timestamp is actually not there or - # malformed. Should not happen, but well. We handle it nonetheless - if timestamp is None: - raise BadTimeSignature('Malformed timestamp', payload=value) - - # Check timestamp is not older than max_age - if max_age is not None: - age = self.get_timestamp() - timestamp - if age > max_age: - raise SignatureExpired( - 'Signature age %s > %s seconds' % (age, max_age), - payload=value, - date_signed=self.timestamp_to_datetime(timestamp)) - - if return_timestamp: - return value, self.timestamp_to_datetime(timestamp) - return value - - def validate(self, signed_value, max_age=None): - """Just validates the given signed value. Returns `True` if the - signature exists and is valid, `False` otherwise.""" - try: - self.unsign(signed_value, max_age=max_age) - return True - except BadSignature: - return False - - -class Serializer(object): - """This class provides a serialization interface on top of the - signer. It provides a similar API to json/pickle and other modules but is - slightly differently structured internally. If you want to change the - underlying implementation for parsing and loading you have to override the - :meth:`load_payload` and :meth:`dump_payload` functions. - - This implementation uses simplejson if available for dumping and loading - and will fall back to the standard library's json module if it's not - available. - - Starting with 0.14 you do not need to subclass this class in order to - switch out or customer the :class:`Signer`. You can instead also pass a - different class to the constructor as well as keyword arguments as - dictionary that should be forwarded:: - - s = Serializer(signer_kwargs={'key_derivation': 'hmac'}) - - .. versionchanged:: 0.14: - The `signer` and `signer_kwargs` parameters were added to the - constructor. - """ - - #: If a serializer module or class is not passed to the constructor - #: this one is picked up. This currently defaults to :mod:`json`. - default_serializer = json - - #: The default :class:`Signer` class that is being used by this - #: serializer. - #: - #: .. versionadded:: 0.14 - default_signer = Signer - - def __init__(self, secret_key, salt=b'itsdangerous', serializer=None, - signer=None, signer_kwargs=None): - self.secret_key = want_bytes(secret_key) - self.salt = want_bytes(salt) - if serializer is None: - serializer = self.default_serializer - self.serializer = serializer - self.is_text_serializer = is_text_serializer(serializer) - if signer is None: - signer = self.default_signer - self.signer = signer - self.signer_kwargs = signer_kwargs or {} - - def load_payload(self, payload, serializer=None): - """Loads the encoded object. This function raises :class:`BadPayload` - if the payload is not valid. The `serializer` parameter can be used to - override the serializer stored on the class. The encoded payload is - always byte based. - """ - if serializer is None: - serializer = self.serializer - is_text = self.is_text_serializer - else: - is_text = is_text_serializer(serializer) - try: - if is_text: - payload = payload.decode('utf-8') - return serializer.loads(payload) - except Exception as e: - raise BadPayload('Could not load the payload because an ' - 'exception occurred on unserializing the data', - original_error=e) - - def dump_payload(self, obj): - """Dumps the encoded object. The return value is always a - bytestring. If the internal serializer is text based the value - will automatically be encoded to utf-8. - """ - return want_bytes(self.serializer.dumps(obj)) - - def make_signer(self, salt=None): - """A method that creates a new instance of the signer to be used. - The default implementation uses the :class:`Signer` baseclass. - """ - if salt is None: - salt = self.salt - return self.signer(self.secret_key, salt=salt, **self.signer_kwargs) - - def dumps(self, obj, salt=None): - """Returns a signed string serialized with the internal serializer. - The return value can be either a byte or unicode string depending - on the format of the internal serializer. - """ - payload = want_bytes(self.dump_payload(obj)) - rv = self.make_signer(salt).sign(payload) - if self.is_text_serializer: - rv = rv.decode('utf-8') - return rv - - def dump(self, obj, f, salt=None): - """Like :meth:`dumps` but dumps into a file. The file handle has - to be compatible with what the internal serializer expects. - """ - f.write(self.dumps(obj, salt)) - - def loads(self, s, salt=None): - """Reverse of :meth:`dumps`, raises :exc:`BadSignature` if the - signature validation fails. - """ - s = want_bytes(s) - return self.load_payload(self.make_signer(salt).unsign(s)) - - def load(self, f, salt=None): - """Like :meth:`loads` but loads from a file.""" - return self.loads(f.read(), salt) - - def loads_unsafe(self, s, salt=None): - """Like :meth:`loads` but without verifying the signature. This is - potentially very dangerous to use depending on how your serializer - works. The return value is ``(signature_okay, payload)`` instead of - just the payload. The first item will be a boolean that indicates - if the signature is okay (``True``) or if it failed. This function - never fails. - - Use it for debugging only and if you know that your serializer module - is not exploitable (eg: do not use it with a pickle serializer). - - .. versionadded:: 0.15 - """ - return self._loads_unsafe_impl(s, salt) - - def _loads_unsafe_impl(self, s, salt, load_kwargs=None, - load_payload_kwargs=None): - """Lowlevel helper function to implement :meth:`loads_unsafe` in - serializer subclasses. - """ - try: - return True, self.loads(s, salt=salt, **(load_kwargs or {})) - except BadSignature as e: - if e.payload is None: - return False, None - try: - return False, self.load_payload(e.payload, - **(load_payload_kwargs or {})) - except BadPayload: - return False, None - - def load_unsafe(self, f, *args, **kwargs): - """Like :meth:`loads_unsafe` but loads from a file. - - .. versionadded:: 0.15 - """ - return self.loads_unsafe(f.read(), *args, **kwargs) - - -class TimedSerializer(Serializer): - """Uses the :class:`TimestampSigner` instead of the default - :meth:`Signer`. - """ - - default_signer = TimestampSigner - - def loads(self, s, max_age=None, return_timestamp=False, salt=None): - """Reverse of :meth:`dumps`, raises :exc:`BadSignature` if the - signature validation fails. If a `max_age` is provided it will - ensure the signature is not older than that time in seconds. In - case the signature is outdated, :exc:`SignatureExpired` is raised - which is a subclass of :exc:`BadSignature`. All arguments are - forwarded to the signer's :meth:`~TimestampSigner.unsign` method. - """ - base64d, timestamp = self.make_signer(salt) \ - .unsign(s, max_age, return_timestamp=True) - payload = self.load_payload(base64d) - if return_timestamp: - return payload, timestamp - return payload - - def loads_unsafe(self, s, max_age=None, salt=None): - load_kwargs = {'max_age': max_age} - load_payload_kwargs = {} - return self._loads_unsafe_impl(s, salt, load_kwargs, load_payload_kwargs) - - -class JSONWebSignatureSerializer(Serializer): - """This serializer implements JSON Web Signature (JWS) support. Only - supports the JWS Compact Serialization. - """ - - jws_algorithms = { - 'HS256': HMACAlgorithm(hashlib.sha256), - 'HS384': HMACAlgorithm(hashlib.sha384), - 'HS512': HMACAlgorithm(hashlib.sha512), - 'none': NoneAlgorithm(), - } - - #: The default algorithm to use for signature generation - default_algorithm = 'HS256' - - default_serializer = compact_json - - def __init__(self, secret_key, salt=None, serializer=None, - signer=None, signer_kwargs=None, algorithm_name=None): - Serializer.__init__(self, secret_key, salt, serializer, - signer, signer_kwargs) - if algorithm_name is None: - algorithm_name = self.default_algorithm - self.algorithm_name = algorithm_name - self.algorithm = self.make_algorithm(algorithm_name) - - def load_payload(self, payload, return_header=False): - payload = want_bytes(payload) - if b'.' not in payload: - raise BadPayload('No "." found in value') - base64d_header, base64d_payload = payload.split(b'.', 1) - try: - json_header = base64_decode(base64d_header) - except Exception as e: - raise BadHeader('Could not base64 decode the header because of ' - 'an exception', original_error=e) - try: - json_payload = base64_decode(base64d_payload) - except Exception as e: - raise BadPayload('Could not base64 decode the payload because of ' - 'an exception', original_error=e) - try: - header = Serializer.load_payload(self, json_header, - serializer=json) - except BadData as e: - raise BadHeader('Could not unserialize header because it was ' - 'malformed', original_error=e) - if not isinstance(header, dict): - raise BadHeader('Header payload is not a JSON object', - header=header) - payload = Serializer.load_payload(self, json_payload) - if return_header: - return payload, header - return payload - - def dump_payload(self, header, obj): - base64d_header = base64_encode(self.serializer.dumps(header)) - base64d_payload = base64_encode(self.serializer.dumps(obj)) - return base64d_header + b'.' + base64d_payload - - def make_algorithm(self, algorithm_name): - try: - return self.jws_algorithms[algorithm_name] - except KeyError: - raise NotImplementedError('Algorithm not supported') - - def make_signer(self, salt=None, algorithm=None): - if salt is None: - salt = self.salt - key_derivation = 'none' if salt is None else None - if algorithm is None: - algorithm = self.algorithm - return self.signer(self.secret_key, salt=salt, sep='.', - key_derivation=key_derivation, algorithm=algorithm) - - def make_header(self, header_fields): - header = header_fields.copy() if header_fields else {} - header['alg'] = self.algorithm_name - return header - - def dumps(self, obj, salt=None, header_fields=None): - """Like :meth:`~Serializer.dumps` but creates a JSON Web Signature. It - also allows for specifying additional fields to be included in the JWS - Header. - """ - header = self.make_header(header_fields) - signer = self.make_signer(salt, self.algorithm) - return signer.sign(self.dump_payload(header, obj)) - - def loads(self, s, salt=None, return_header=False): - """Reverse of :meth:`dumps`. If requested via `return_header` it will - return a tuple of payload and header. - """ - payload, header = self.load_payload( - self.make_signer(salt, self.algorithm).unsign(want_bytes(s)), - return_header=True) - if header.get('alg') != self.algorithm_name: - raise BadHeader('Algorithm mismatch', header=header, - payload=payload) - if return_header: - return payload, header - return payload - - def loads_unsafe(self, s, salt=None, return_header=False): - kwargs = {'return_header': return_header} - return self._loads_unsafe_impl(s, salt, kwargs, kwargs) - - -class TimedJSONWebSignatureSerializer(JSONWebSignatureSerializer): - """Works like the regular :class:`JSONWebSignatureSerializer` but also - records the time of the signing and can be used to expire signatures. - - JWS currently does not specify this behavior but it mentions a possibility - extension like this in the spec. Expiry date is encoded into the header - similarily as specified in `draft-ietf-oauth-json-web-token - 0): - raise BadSignature('expiry date is not an IntDate', - payload=payload) - - if header['exp'] < self.now(): - raise SignatureExpired('Signature expired', payload=payload, - date_signed=self.get_issue_date(header)) - - if return_header: - return payload, header - return payload - - def get_issue_date(self, header): - rv = header.get('iat') - if isinstance(rv, number_types): - return datetime.utcfromtimestamp(int(rv)) - - def now(self): - return int(time.time()) - - -class URLSafeSerializerMixin(object): - """Mixed in with a regular serializer it will attempt to zlib compress - the string to make it shorter if necessary. It will also base64 encode - the string so that it can safely be placed in a URL. - """ - - def load_payload(self, payload): - decompress = False - if payload.startswith(b'.'): - payload = payload[1:] - decompress = True - try: - json = base64_decode(payload) - except Exception as e: - raise BadPayload('Could not base64 decode the payload because of ' - 'an exception', original_error=e) - if decompress: - try: - json = zlib.decompress(json) - except Exception as e: - raise BadPayload('Could not zlib decompress the payload before ' - 'decoding the payload', original_error=e) - return super(URLSafeSerializerMixin, self).load_payload(json) - - def dump_payload(self, obj): - json = super(URLSafeSerializerMixin, self).dump_payload(obj) - is_compressed = False - compressed = zlib.compress(json) - if len(compressed) < (len(json) - 1): - json = compressed - is_compressed = True - base64d = base64_encode(json) - if is_compressed: - base64d = b'.' + base64d - return base64d - - -class URLSafeSerializer(URLSafeSerializerMixin, Serializer): - """Works like :class:`Serializer` but dumps and loads into a URL - safe string consisting of the upper and lowercase character of the - alphabet as well as ``'_'``, ``'-'`` and ``'.'``. - """ - default_serializer = compact_json - - -class URLSafeTimedSerializer(URLSafeSerializerMixin, TimedSerializer): - """Works like :class:`TimedSerializer` but dumps and loads into a URL - safe string consisting of the upper and lowercase character of the - alphabet as well as ``'_'``, ``'-'`` and ``'.'``. - """ - default_serializer = compact_json