aboutsummaryrefslogtreecommitdiffstats
path: root/python/itsdangerous
diff options
context:
space:
mode:
Diffstat (limited to 'python/itsdangerous')
-rw-r--r--python/itsdangerous/__init__.py22
-rw-r--r--python/itsdangerous/_compat.py46
-rw-r--r--python/itsdangerous/_json.py18
-rw-r--r--python/itsdangerous/encoding.py49
-rw-r--r--python/itsdangerous/exc.py98
-rw-r--r--python/itsdangerous/jws.py218
-rw-r--r--python/itsdangerous/serializer.py233
-rw-r--r--python/itsdangerous/signer.py179
-rw-r--r--python/itsdangerous/timed.py147
-rw-r--r--python/itsdangerous/url_safe.py65
10 files changed, 0 insertions, 1075 deletions
diff --git a/python/itsdangerous/__init__.py b/python/itsdangerous/__init__.py
deleted file mode 100644
index 0fcd8c1..0000000
--- a/python/itsdangerous/__init__.py
+++ /dev/null
@@ -1,22 +0,0 @@
-from ._json import json
-from .encoding import base64_decode
-from .encoding import base64_encode
-from .encoding import want_bytes
-from .exc import BadData
-from .exc import BadHeader
-from .exc import BadPayload
-from .exc import BadSignature
-from .exc import BadTimeSignature
-from .exc import SignatureExpired
-from .jws import JSONWebSignatureSerializer
-from .jws import TimedJSONWebSignatureSerializer
-from .serializer import Serializer
-from .signer import HMACAlgorithm
-from .signer import NoneAlgorithm
-from .signer import Signer
-from .timed import TimedSerializer
-from .timed import TimestampSigner
-from .url_safe import URLSafeSerializer
-from .url_safe import URLSafeTimedSerializer
-
-__version__ = "1.1.0"
diff --git a/python/itsdangerous/_compat.py b/python/itsdangerous/_compat.py
deleted file mode 100644
index 2291bce..0000000
--- a/python/itsdangerous/_compat.py
+++ /dev/null
@@ -1,46 +0,0 @@
-import decimal
-import hmac
-import numbers
-import sys
-
-PY2 = sys.version_info[0] == 2
-
-if PY2:
- from itertools import izip
-
- text_type = unicode # noqa: 821
-else:
- izip = zip
- text_type = str
-
-number_types = (numbers.Real, decimal.Decimal)
-
-
-def _constant_time_compare(val1, val2):
- """Return ``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.
-
- This is an alias of :func:`hmac.compare_digest` on Python>=2.7,3.3.
- """
- 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
-
-
-# Starting with 2.7/3.3 the standard library has a c-implementation for
-# constant time string compares.
-constant_time_compare = getattr(hmac, "compare_digest", _constant_time_compare)
diff --git a/python/itsdangerous/_json.py b/python/itsdangerous/_json.py
deleted file mode 100644
index 426b36e..0000000
--- a/python/itsdangerous/_json.py
+++ /dev/null
@@ -1,18 +0,0 @@
-try:
- import simplejson as json
-except ImportError:
- import json
-
-
-class _CompactJSON(object):
- """Wrapper around json module that strips whitespace."""
-
- @staticmethod
- def loads(payload):
- return json.loads(payload)
-
- @staticmethod
- def dumps(obj, **kwargs):
- kwargs.setdefault("ensure_ascii", False)
- kwargs.setdefault("separators", (",", ":"))
- return json.dumps(obj, **kwargs)
diff --git a/python/itsdangerous/encoding.py b/python/itsdangerous/encoding.py
deleted file mode 100644
index 1e28969..0000000
--- a/python/itsdangerous/encoding.py
+++ /dev/null
@@ -1,49 +0,0 @@
-import base64
-import string
-import struct
-
-from ._compat import text_type
-from .exc import BadData
-
-
-def want_bytes(s, encoding="utf-8", errors="strict"):
- if isinstance(s, text_type):
- s = s.encode(encoding, errors)
- return s
-
-
-def base64_encode(string):
- """Base64 encode a string of bytes or text. The resulting bytes are
- safe to use in URLs.
- """
- string = want_bytes(string)
- return base64.urlsafe_b64encode(string).rstrip(b"=")
-
-
-def base64_decode(string):
- """Base64 decode a URL-safe string of bytes or text. The result is
- bytes.
- """
- string = want_bytes(string, encoding="ascii", errors="ignore")
- string += b"=" * (-len(string) % 4)
-
- try:
- return base64.urlsafe_b64decode(string)
- except (TypeError, ValueError):
- raise BadData("Invalid base64-encoded data")
-
-
-# The alphabet used by base64.urlsafe_*
-_base64_alphabet = (string.ascii_letters + string.digits + "-_=").encode("ascii")
-
-_int64_struct = struct.Struct(">Q")
-_int_to_bytes = _int64_struct.pack
-_bytes_to_int = _int64_struct.unpack
-
-
-def int_to_bytes(num):
- return _int_to_bytes(num).lstrip(b"\x00")
-
-
-def bytes_to_int(bytestr):
- return _bytes_to_int(bytestr.rjust(8, b"\x00"))[0]
diff --git a/python/itsdangerous/exc.py b/python/itsdangerous/exc.py
deleted file mode 100644
index 287d691..0000000
--- a/python/itsdangerous/exc.py
+++ /dev/null
@@ -1,98 +0,0 @@
-from ._compat import PY2
-from ._compat import text_type
-
-
-class BadData(Exception):
- """Raised if bad data of any sort was encountered. This is the base
- for all exceptions that itsdangerous defines.
-
- .. versionadded:: 0.15
- """
-
- message = None
-
- def __init__(self, message):
- super(BadData, self).__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 BadSignature(BadData):
- """Raised if a signature does not match."""
-
- 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 if a time-based signature is invalid. This is a subclass
- of :class:`BadSignature`.
- """
-
- 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 SignatureExpired(BadTimeSignature):
- """Raised if a signature timestamp is older than ``max_age``. This
- is a subclass of :exc:`BadTimeSignature`.
- """
-
-
-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 BadPayload(BadData):
- """Raised if a payload is invalid. This could happen if the payload
- is loaded despite an invalid signature, or if there is a mismatch
- between the serializer and deserializer. The original exception
- that occurred during loading is stored on as :attr:`original_error`.
-
- .. 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
diff --git a/python/itsdangerous/jws.py b/python/itsdangerous/jws.py
deleted file mode 100644
index 92e9ec8..0000000
--- a/python/itsdangerous/jws.py
+++ /dev/null
@@ -1,218 +0,0 @@
-import hashlib
-import time
-from datetime import datetime
-
-from ._compat import number_types
-from ._json import _CompactJSON
-from ._json import json
-from .encoding import base64_decode
-from .encoding import base64_encode
-from .encoding import want_bytes
-from .exc import BadData
-from .exc import BadHeader
-from .exc import BadPayload
-from .exc import BadSignature
-from .exc import SignatureExpired
-from .serializer import Serializer
-from .signer import HMACAlgorithm
-from .signer import NoneAlgorithm
-
-
-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 = "HS512"
-
- default_serializer = _CompactJSON
-
- def __init__(
- self,
- secret_key,
- salt=None,
- serializer=None,
- serializer_kwargs=None,
- signer=None,
- signer_kwargs=None,
- algorithm_name=None,
- ):
- Serializer.__init__(
- self,
- secret_key=secret_key,
- salt=salt,
- serializer=serializer,
- serializer_kwargs=serializer_kwargs,
- signer=signer,
- signer_kwargs=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, serializer=None, 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, serializer=serializer)
- if return_header:
- return payload, header
- return payload
-
- def dump_payload(self, header, obj):
- base64d_header = base64_encode(
- self.serializer.dumps(header, **self.serializer_kwargs)
- )
- base64d_payload = base64_encode(
- self.serializer.dumps(obj, **self.serializer_kwargs)
- )
- 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
- possible extension like this in the spec. Expiry date is encoded
- into the header similar to what's specified in `draft-ietf-oauth
- -json-web-token <http://self-issued.info/docs/draft-ietf-oauth-json
- -web-token.html#expDef>`_.
- """
-
- DEFAULT_EXPIRES_IN = 3600
-
- def __init__(self, secret_key, expires_in=None, **kwargs):
- JSONWebSignatureSerializer.__init__(self, secret_key, **kwargs)
- if expires_in is None:
- expires_in = self.DEFAULT_EXPIRES_IN
- self.expires_in = expires_in
-
- def make_header(self, header_fields):
- header = JSONWebSignatureSerializer.make_header(self, header_fields)
- iat = self.now()
- exp = iat + self.expires_in
- header["iat"] = iat
- header["exp"] = exp
- return header
-
- def loads(self, s, salt=None, return_header=False):
- payload, header = JSONWebSignatureSerializer.loads(
- self, s, salt, return_header=True
- )
-
- if "exp" not in header:
- raise BadSignature("Missing expiry date", payload=payload)
-
- int_date_error = BadHeader("Expiry date is not an IntDate", payload=payload)
- try:
- header["exp"] = int(header["exp"])
- except ValueError:
- raise int_date_error
- if header["exp"] < 0:
- raise int_date_error
-
- 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())
diff --git a/python/itsdangerous/serializer.py b/python/itsdangerous/serializer.py
deleted file mode 100644
index 12c20f4..0000000
--- a/python/itsdangerous/serializer.py
+++ /dev/null
@@ -1,233 +0,0 @@
-import hashlib
-
-from ._compat import text_type
-from ._json import json
-from .encoding import want_bytes
-from .exc import BadPayload
-from .exc import BadSignature
-from .signer import Signer
-
-
-def is_text_serializer(serializer):
- """Checks whether a serializer generates text or binary."""
- return isinstance(serializer.dumps({}), text_type)
-
-
-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 structured differently 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.
-
- You do not need to subclass this class in order to switch out or
- customize the :class:`.Signer`. You can instead pass a different
- class to the constructor as well as keyword arguments as a dict that
- should be forwarded.
-
- .. code-block:: python
-
- s = Serializer(signer_kwargs={'key_derivation': 'hmac'})
-
- You may want to upgrade the signing parameters without invalidating
- existing signatures that are in use. Fallback signatures can be
- given that will be tried if unsigning with the current signer fails.
-
- Fallback signers can be defined by providing a list of
- ``fallback_signers``. Each item can be one of the following: a
- signer class (which is instantiated with ``signer_kwargs``,
- ``salt``, and ``secret_key``), a tuple
- ``(signer_class, signer_kwargs)``, or a dict of ``signer_kwargs``.
-
- For example, this is a serializer that signs using SHA-512, but will
- unsign using either SHA-512 or SHA1:
-
- .. code-block:: python
-
- s = Serializer(
- signer_kwargs={"digest_method": hashlib.sha512},
- fallback_signers=[{"digest_method": hashlib.sha1}]
- )
-
- .. versionchanged:: 0.14:
- The ``signer`` and ``signer_kwargs`` parameters were added to
- the constructor.
-
- .. versionchanged:: 1.1.0:
- Added support for ``fallback_signers`` and configured a default
- SHA-512 fallback. This fallback is for users who used the yanked
- 1.0.0 release which defaulted to SHA-512.
- """
-
- #: 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
-
- #: The default fallback signers.
- default_fallback_signers = [{"digest_method": hashlib.sha512}]
-
- def __init__(
- self,
- secret_key,
- salt=b"itsdangerous",
- serializer=None,
- serializer_kwargs=None,
- signer=None,
- signer_kwargs=None,
- fallback_signers=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 {}
- if fallback_signers is None:
- fallback_signers = list(self.default_fallback_signers or ())
- self.fallback_signers = fallback_signers
- self.serializer_kwargs = serializer_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`` should always be
- bytes.
- """
- 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 bytes.
- If the internal serializer returns text, the value will be
- encoded as UTF-8.
- """
- return want_bytes(self.serializer.dumps(obj, **self.serializer_kwargs))
-
- def make_signer(self, salt=None):
- """Creates a new instance of the signer to be used. The default
- implementation uses the :class:`.Signer` base class.
- """
- if salt is None:
- salt = self.salt
- return self.signer(self.secret_key, salt=salt, **self.signer_kwargs)
-
- def iter_unsigners(self, salt=None):
- """Iterates over all signers to be tried for unsigning. Starts
- with the configured signer, then constructs each signer
- specified in ``fallback_signers``.
- """
- if salt is None:
- salt = self.salt
- yield self.make_signer(salt)
- for fallback in self.fallback_signers:
- if type(fallback) is dict:
- kwargs = fallback
- fallback = self.signer
- elif type(fallback) is tuple:
- fallback, kwargs = fallback
- else:
- kwargs = self.signer_kwargs
- yield fallback(self.secret_key, salt=salt, **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)
- last_exception = None
- for signer in self.iter_unsigners(salt):
- try:
- return self.load_payload(signer.unsign(s))
- except BadSignature as err:
- last_exception = err
- raise last_exception
-
- 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_valid,
- payload)`` instead of just the payload. The first item will be a
- boolean that indicates if the signature is valid. This function
- never fails.
-
- Use it for debugging only and if you know that your serializer
- module is not exploitable (for example, 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):
- """Low level 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)
diff --git a/python/itsdangerous/signer.py b/python/itsdangerous/signer.py
deleted file mode 100644
index 6bddc03..0000000
--- a/python/itsdangerous/signer.py
+++ /dev/null
@@ -1,179 +0,0 @@
-import hashlib
-import hmac
-
-from ._compat import constant_time_compare
-from .encoding import _base64_alphabet
-from .encoding import base64_decode
-from .encoding import base64_encode
-from .encoding import want_bytes
-from .exc import BadSignature
-
-
-class SigningAlgorithm(object):
- """Subclasses must implement :meth:`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):
- """Provides an algorithm that does not perform any signing and
- returns an empty signature.
- """
-
- def get_signature(self, key, value):
- return b""
-
-
-class HMACAlgorithm(SigningAlgorithm):
- """Provides signature generation using HMACs."""
-
- #: The digest method to use with the MAC algorithm. This defaults to
- #: SHA1, but can be changed to 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 and unsign bytes, validating 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 to any other function in the hashlib
- #: module.
- #:
- #: .. versionadded:: 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 = want_bytes(sep)
- if self.sep in _base64_alphabet:
- raise ValueError(
- "The given separator cannot be used because it may be"
- " contained in the signature itself. Alphanumeric"
- " characters and `-_=` must not be used."
- )
- 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. The default key
- derivation choices can be overridden here. Key derivation 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 want_bytes(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):
- """Only validates the given signed value. Returns ``True`` if
- the signature exists and is valid.
- """
- try:
- self.unsign(signed_value)
- return True
- except BadSignature:
- return False
diff --git a/python/itsdangerous/timed.py b/python/itsdangerous/timed.py
deleted file mode 100644
index 4c117e4..0000000
--- a/python/itsdangerous/timed.py
+++ /dev/null
@@ -1,147 +0,0 @@
-import time
-from datetime import datetime
-
-from ._compat import text_type
-from .encoding import base64_decode
-from .encoding import base64_encode
-from .encoding import bytes_to_int
-from .encoding import int_to_bytes
-from .encoding import want_bytes
-from .exc import BadSignature
-from .exc import BadTimeSignature
-from .exc import SignatureExpired
-from .serializer import Serializer
-from .signer import Signer
-
-
-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
- :meth:`unsign` method can raise :exc:`.SignatureExpired` if the
- unsigning failed because the signature is expired.
- """
-
- def get_timestamp(self):
- """Returns the current timestamp. The function must return an
- integer.
- """
- return int(time.time())
-
- def timestamp_to_datetime(self, ts):
- """Used to convert the timestamp from :meth:`get_timestamp` into
- a datetime object.
- """
- return datetime.utcfromtimestamp(ts)
-
- def sign(self, value):
- """Signs the given string and also attaches 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 ``True`` the
- timestamp of the signature will be returned as a 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 sep not 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 we handle it anyway.
- 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):
- """Only validates the given signed value. Returns ``True`` if
- the signature exists and is valid."""
- try:
- self.unsign(signed_value, max_age=max_age)
- return True
- except BadSignature:
- return False
-
-
-class TimedSerializer(Serializer):
- """Uses :class:`TimestampSigner` instead of the default
- :class:`.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. All arguments are forwarded to the signer's
- :meth:`~TimestampSigner.unsign` method.
- """
- s = want_bytes(s)
- last_exception = None
- for signer in self.iter_unsigners(salt):
- try:
- base64d, timestamp = signer.unsign(s, max_age, return_timestamp=True)
- payload = self.load_payload(base64d)
- if return_timestamp:
- return payload, timestamp
- return payload
- # If we get a signature expired it means we could read the
- # signature but it's invalid. In that case we do not want to
- # try the next signer.
- except SignatureExpired:
- raise
- except BadSignature as err:
- last_exception = err
- raise last_exception
-
- 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)
diff --git a/python/itsdangerous/url_safe.py b/python/itsdangerous/url_safe.py
deleted file mode 100644
index fcaa011..0000000
--- a/python/itsdangerous/url_safe.py
+++ /dev/null
@@ -1,65 +0,0 @@
-import zlib
-
-from ._json import _CompactJSON
-from .encoding import base64_decode
-from .encoding import base64_encode
-from .exc import BadPayload
-from .serializer import Serializer
-from .timed import TimedSerializer
-
-
-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.
- """
-
- default_serializer = _CompactJSON
-
- def load_payload(self, payload, *args, **kwargs):
- 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, *args, **kwargs)
-
- 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 ``'.'``.
- """
-
-
-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 ``'.'``.
- """