CacheToolsUtils 10.1__py3-none-any.whl → 10.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {CacheToolsUtils-10.1.dist-info → CacheToolsUtils-10.2.dist-info}/METADATA +1 -1
- CacheToolsUtils-10.2.dist-info/RECORD +6 -0
- CacheToolsUtils.py +60 -17
- CacheToolsUtils-10.1.dist-info/RECORD +0 -6
- {CacheToolsUtils-10.1.dist-info → CacheToolsUtils-10.2.dist-info}/LICENSE +0 -0
- {CacheToolsUtils-10.1.dist-info → CacheToolsUtils-10.2.dist-info}/WHEEL +0 -0
- {CacheToolsUtils-10.1.dist-info → CacheToolsUtils-10.2.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
CacheToolsUtils.py,sha256=etJrt34OkZtSMXK1wGJIg9VcDYxH3pXzpJy6OowAMnY,26120
|
|
2
|
+
CacheToolsUtils-10.2.dist-info/LICENSE,sha256=y_yFW1bmBAUFCF-ZDyDiq10Ykns_19If5pFnZUzYgeU,33
|
|
3
|
+
CacheToolsUtils-10.2.dist-info/METADATA,sha256=rEY3PJdfjL-E-QVqAt36vb7voJqxZYaeqze-Lk7YuQ0,4759
|
|
4
|
+
CacheToolsUtils-10.2.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
|
|
5
|
+
CacheToolsUtils-10.2.dist-info/top_level.txt,sha256=qCyE4jGot5mOmvIQrZueCnPtbYtRe2hqke4sGojm3zI,16
|
|
6
|
+
CacheToolsUtils-10.2.dist-info/RECORD,,
|
CacheToolsUtils.py
CHANGED
|
@@ -367,6 +367,48 @@ class TwoLevelCache(_MutMapMix, MutableMapping):
|
|
|
367
367
|
self._cache2.reset() # type: ignore
|
|
368
368
|
|
|
369
369
|
|
|
370
|
+
# NOTE this is quite costly, a Cipher object is created to encrypt/decrypt each value.
|
|
371
|
+
# another design could keep a persistant cipher.
|
|
372
|
+
class _Cipher:
|
|
373
|
+
"""Not so convincing internal class to abstract cipher algorithms."""
|
|
374
|
+
|
|
375
|
+
def __init__(self, name):
|
|
376
|
+
self._name = name
|
|
377
|
+
if name == "Salsa20":
|
|
378
|
+
from Crypto.Cipher import Salsa20 as cipher
|
|
379
|
+
# NOTE hash and nonce may overlap, which is not an issue
|
|
380
|
+
params = {"key": (32, 64), "nonce": (24, 32)}
|
|
381
|
+
kwargs = {}
|
|
382
|
+
size = 0
|
|
383
|
+
elif name == "ChaCha20":
|
|
384
|
+
from Crypto.Cipher import ChaCha20 as cipher
|
|
385
|
+
# NOTE hash and nonce may overlap, which is not an issue
|
|
386
|
+
params = {"key": (32, 64), "nonce": (24, 32)}
|
|
387
|
+
kwargs = {}
|
|
388
|
+
size = 0
|
|
389
|
+
elif name in ("AES", "AES-128", "AES-128-CBC"):
|
|
390
|
+
from Crypto.Cipher import AES as cipher
|
|
391
|
+
params = {"key": (48, 64), "iv": (32, 48)}
|
|
392
|
+
kwargs = {"mode": cipher.MODE_CBC}
|
|
393
|
+
size = 16
|
|
394
|
+
else:
|
|
395
|
+
raise Exception(f"unexpected cipher: {name}")
|
|
396
|
+
self._cipher = cipher
|
|
397
|
+
self._params = params
|
|
398
|
+
self._kwargs = kwargs
|
|
399
|
+
if size:
|
|
400
|
+
from Crypto.Util.Padding import pad, unpad
|
|
401
|
+
self._pad = lambda s: pad(s, size)
|
|
402
|
+
self._unpad = lambda s: unpad(s, size)
|
|
403
|
+
else:
|
|
404
|
+
self._pad = lambda s: s
|
|
405
|
+
self._unpad = lambda s: s
|
|
406
|
+
|
|
407
|
+
def new(self, derived: bytes):
|
|
408
|
+
kwargs = {k: derived[v[0]: v[1]] for k, v in self._params.items()}
|
|
409
|
+
return self._cipher.new(**kwargs, **self._kwargs) # type: ignore
|
|
410
|
+
|
|
411
|
+
|
|
370
412
|
#
|
|
371
413
|
# Encrypted Cache
|
|
372
414
|
#
|
|
@@ -376,6 +418,7 @@ class EncryptedCache(_KeyMutMapMix, _StatsMix, MutableMapping):
|
|
|
376
418
|
:param secret: bytes of secret, at least 16 bytes.
|
|
377
419
|
:param hsize: size of hashed key, default is *16*.
|
|
378
420
|
:param csize: value checksum size, default is *0*.
|
|
421
|
+
:param cipher: chose cipher from "Salsa20", "AES-128-CBC" or "ChaCha20".
|
|
379
422
|
|
|
380
423
|
The key is *not* encrypted but simply hashed, thus they are
|
|
381
424
|
fixed size with a very low collision probability.
|
|
@@ -384,11 +427,12 @@ class EncryptedCache(_KeyMutMapMix, _StatsMix, MutableMapping):
|
|
|
384
427
|
as each value is encrypted with its own key and nonce.
|
|
385
428
|
|
|
386
429
|
Algorithms:
|
|
430
|
+
|
|
387
431
|
- SHA3: hash/key/nonce derivation and checksum.
|
|
388
|
-
- Salsa20: value encryption.
|
|
432
|
+
- Salsa20, AES-128-CBC or ChaCha20: value encryption.
|
|
389
433
|
"""
|
|
390
434
|
|
|
391
|
-
def __init__(self, cache: MutableMapping, secret: bytes, hsize: int = 16, csize: int = 0):
|
|
435
|
+
def __init__(self, cache: MutableMapping, secret: bytes, hsize: int = 16, csize: int = 0, cipher: str = "Salsa20"):
|
|
392
436
|
self._cache = cache
|
|
393
437
|
assert len(secret) >= 16
|
|
394
438
|
self._secret = secret
|
|
@@ -396,34 +440,31 @@ class EncryptedCache(_KeyMutMapMix, _StatsMix, MutableMapping):
|
|
|
396
440
|
self._hsize = hsize
|
|
397
441
|
assert 0 <= csize <= 32
|
|
398
442
|
self._csize = csize
|
|
399
|
-
|
|
400
|
-
self._cipher = Salsa20
|
|
443
|
+
self._cipher = _Cipher(cipher)
|
|
401
444
|
|
|
402
|
-
def
|
|
403
|
-
"""
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
return (hkey[:self._hsize], hkey[32:], hkey[24:32])
|
|
445
|
+
def _derive(self, key) -> tuple[bytes, bytes]:
|
|
446
|
+
"""Derive hash and stuff from initial key and secret."""
|
|
447
|
+
derived = hashlib.sha3_512(key + self._secret).digest()
|
|
448
|
+
return (derived[:self._hsize], derived)
|
|
407
449
|
|
|
408
450
|
def _key(self, key):
|
|
409
|
-
return self.
|
|
451
|
+
return self._derive(key)[0]
|
|
410
452
|
|
|
411
453
|
def __setitem__(self, key, val):
|
|
412
|
-
hkey,
|
|
413
|
-
xval = self._cipher.new(
|
|
454
|
+
hkey, derived = self._derive(key)
|
|
455
|
+
xval = self._cipher.new(derived).encrypt(self._cipher._pad(val))
|
|
414
456
|
if self._csize:
|
|
415
|
-
|
|
416
|
-
xval = cs + xval
|
|
457
|
+
xval = hashlib.sha3_256(val).digest()[:self._csize] + xval
|
|
417
458
|
self._cache[hkey] = xval
|
|
418
459
|
|
|
419
460
|
def __getitem__(self, key):
|
|
420
|
-
hkey,
|
|
461
|
+
hkey, derived = self._derive(key)
|
|
421
462
|
xval = self._cache[hkey]
|
|
422
463
|
if self._csize: # split cs from encrypted value
|
|
423
464
|
cs, xval = xval[:self._csize], xval[self._csize:]
|
|
424
465
|
else:
|
|
425
466
|
cs = None
|
|
426
|
-
val = self._cipher.new(
|
|
467
|
+
val = self._cipher._unpad(self._cipher.new(derived).decrypt(xval))
|
|
427
468
|
if self._csize and cs != hashlib.sha3_256(val).digest()[:self._csize]:
|
|
428
469
|
raise KeyError(f"invalid encrypted value for key {key}")
|
|
429
470
|
return val
|
|
@@ -597,7 +638,9 @@ class _HashJsonKey:
|
|
|
597
638
|
return self._key
|
|
598
639
|
|
|
599
640
|
|
|
641
|
+
# NOTE not very useful, behaves as _HashJsonKey.
|
|
600
642
|
def hash_json_key(*args, **kwargs):
|
|
643
|
+
"""Function handling of above class, to keep it hidden."""
|
|
601
644
|
return _HashJsonKey(*args, **kwargs)
|
|
602
645
|
|
|
603
646
|
|
|
@@ -639,7 +682,7 @@ class JsonSerde:
|
|
|
639
682
|
elif flag == 3:
|
|
640
683
|
return json.loads(value.decode("utf-8"))
|
|
641
684
|
else:
|
|
642
|
-
raise Exception("Unknown serialization format")
|
|
685
|
+
raise Exception(f"Unknown serialization format: {flag}")
|
|
643
686
|
|
|
644
687
|
|
|
645
688
|
class MemCached(_KeyMutMapMix, MutableMapping):
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
CacheToolsUtils.py,sha256=vFzAsKANXs-VdsW5vHMTjaSlRbZ230Yt5KhCVNanU8k,24360
|
|
2
|
-
CacheToolsUtils-10.1.dist-info/LICENSE,sha256=y_yFW1bmBAUFCF-ZDyDiq10Ykns_19If5pFnZUzYgeU,33
|
|
3
|
-
CacheToolsUtils-10.1.dist-info/METADATA,sha256=iNx4pKMTm34ot0u_vWQOo2-GXfcTw9DR_C3OwH7Lkk8,4759
|
|
4
|
-
CacheToolsUtils-10.1.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
|
|
5
|
-
CacheToolsUtils-10.1.dist-info/top_level.txt,sha256=qCyE4jGot5mOmvIQrZueCnPtbYtRe2hqke4sGojm3zI,16
|
|
6
|
-
CacheToolsUtils-10.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|