Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,8 @@ This backend uses [pickle](https://docs.python.org/3/library/pickle.html) module
values, but the cashes can store values with md5-keyed hash.

Use `secret` and `digestmod` parameters to protect your application from security vulnerabilities.
The `digestmod` is a hashing algorithm that can be used: `sum`, `md5` (default), `sha1` and `sha256`
The `digestmod` is a hashing algorithm that can be used: `sum`, `md5` (default), `sha1` and `sha256`.
To use [xxhash](https://pypi.org/project/xxhash/) algorithms (digestmods: `xxh3_64`, `xxh3_128`, `xxh32` and `xxh64`) install `xxhash` package or setup cashews with speedup requirements (`pip install cashews[speedup]`))
The `secret` is a salt for a hash.

Pickle can't serialize any type of object. In case you need to store more complex types
Expand Down
4 changes: 4 additions & 0 deletions cashews/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ class UnsupportedPicklerError(CacheError):
"""Unknown or unsupported pickle type."""


class UnsupportedDigestmod(CacheError):
"""Unknown or unsupported digestmod type."""


class UnsupportedCompressorError(CacheError):
"""Unknown or unsupported compress type."""

Expand Down
48 changes: 43 additions & 5 deletions cashews/serialize.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,58 @@
from typing import TYPE_CHECKING

from .compresors import Compressor, CompressType, get_compressor
from .exceptions import DecompressionError, SignIsMissingError, UnSecureDataError
from .exceptions import DecompressionError, SignIsMissingError, UnSecureDataError, UnsupportedDigestmod
from .picklers import Pickler, PicklerType, get_pickler

try:
import xxhash
except ImportError:
_XXHASH = False
else:
_XXHASH = True


if TYPE_CHECKING: # pragma: no cover
from ._typing import ICustomDecoder, ICustomEncoder, Key, Value
from .backends.interface import Backend


def _seal(digestmod):
def sign(key: bytes, value: bytes) -> bytes:
return hmac.new(key, value, digestmod).hexdigest().encode()
def sign(secret: bytes, value: bytes) -> bytes:
return hmac.new(secret, value, digestmod).hexdigest().encode()

return sign


def simple_sign(key: bytes, value: bytes) -> bytes:
s = sum(key) + sum(value)
def simple_sign(secret: bytes, value: bytes) -> bytes:
s = sum(secret) + sum(value)
return f"{s:x}".encode()


def _xxhash_xxh32_sign(secret: bytes, value: bytes) -> bytes:
if not _XXHASH:
raise UnsupportedDigestmod("xxhash is not installed")
return xxhash.xxh32_hexdigest(secret + value).encode()


def _xxhash_xxh64_sign(secret: bytes, value: bytes) -> bytes:
if not _XXHASH:
raise UnsupportedDigestmod("xxhash is not installed")
return xxhash.xxh64_hexdigest(secret + value).encode()


def _xxhash_xxh3_64_sign(secret: bytes, value: bytes) -> bytes:
if not _XXHASH:
raise UnsupportedDigestmod("xxhash is not installed")
return xxhash.xxh3_64_hexdigest(secret + value).encode()


def _xxhash_xxh3_128_sign(secret: bytes, value: bytes) -> bytes:
if not _XXHASH:
raise UnsupportedDigestmod("xxhash is not installed")
return xxhash.xxh3_128_hexdigest(secret + value).encode()


def _to_bytes(value: str | bytes) -> bytes:
if isinstance(value, str):
value = value.encode()
Expand All @@ -46,11 +78,17 @@ class HashSigner(Signer):
b"md5": _seal(hashlib.md5),
b"sha256": _seal(hashlib.sha256),
b"sum": simple_sign,
b"xxh32": _xxhash_xxh32_sign,
b"xxh64": _xxhash_xxh64_sign,
b"xxh3_64": _xxhash_xxh3_64_sign,
b"xxh3_128": _xxhash_xxh3_128_sign,
}

def __init__(self, secret: str | bytes, digestmod: str | bytes = b"md5"):
self._secret = _to_bytes(secret)
self._digestmod = _to_bytes(digestmod)
if self._digestmod in (b"xxh32", b"xxh64") and not _XXHASH:
raise UnsupportedDigestmod()

def sign(self, key: Key, value: bytes) -> bytes:
sign = self._gen_sign(key, value, self._digestmod)
Expand Down
Loading