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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: CacheToolsUtils
3
- Version: 10.1
3
+ Version: 10.2
4
4
  Summary: Cachetools Utilities
5
5
  Author-email: Fabien Coelho <cachetools.utils@coelho.net>
6
6
  License: CC0
@@ -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
- from Crypto.Cipher import Salsa20
400
- self._cipher = Salsa20
443
+ self._cipher = _Cipher(cipher)
401
444
 
402
- def _keydev(self, key) -> tuple[bytes, bytes, bytes]:
403
- """Compute hash, key and nonce from initial key."""
404
- hkey = hashlib.sha3_512(key + self._secret).digest()
405
- # NOTE hash and nonce may overlap, which is not an issue
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._keydev(key)[0]
451
+ return self._derive(key)[0]
410
452
 
411
453
  def __setitem__(self, key, val):
412
- hkey, vkey, vnonce = self._keydev(key)
413
- xval = self._cipher.new(key=vkey, nonce=vnonce).encrypt(val)
454
+ hkey, derived = self._derive(key)
455
+ xval = self._cipher.new(derived).encrypt(self._cipher._pad(val))
414
456
  if self._csize:
415
- cs = hashlib.sha3_256(val).digest()[:self._csize]
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, vkey, vnonce = self._keydev(key)
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(key=vkey, nonce=vnonce).decrypt(xval)
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,,