@@ -367,6 +367,92 @@ def reset(self): # pragma: no cover
367
367
self ._cache2 .reset () # type: ignore
368
368
369
369
370
+ #
371
+ # Encrypted Cache
372
+ #
373
+ class EncryptedCache (_KeyMutMapMix , _StatsMix , MutableMapping ):
374
+ """Encrypted Bytes Key-Value Cache.
375
+
376
+ :param secret: bytes of secret, at least 16 bytes.
377
+ :param hsize: size of hashed key, default is 16.
378
+
379
+ The key is *not* encrypted but simply hashed, thus they are
380
+ fixed size with a very low collision probability.
381
+
382
+ By design, the clear-text key is needed to recover the value,
383
+ as each value is encrypted with its own key.
384
+
385
+ There is no integrity check on the value.
386
+
387
+ Algorithms:
388
+ - SHA3: hash/key/nonce derivation.
389
+ - Salsa20: value encryption.
390
+ """
391
+
392
+ def __init__ (self , cache : MutableMapping , secret : bytes , hsize : int = 16 ):
393
+ self ._cache = cache
394
+ assert len (secret ) >= 16
395
+ self ._secret = secret
396
+ assert 8 <= hsize <= 24
397
+ self ._hsize = hsize
398
+ from Crypto .Cipher import Salsa20
399
+ self ._cipher = Salsa20
400
+
401
+ def _keydev (self , key ):
402
+ hkey = hashlib .sha3_512 (key + self ._secret ).digest ()
403
+ sz = self ._hsize
404
+ return (hkey [:sz ], hkey [sz :sz + 32 ], hkey [sz + 32 :sz + 40 ])
405
+
406
+ def _key (self , key ):
407
+ return self ._keydev (key )[0 ]
408
+
409
+ def __setitem__ (self , key , val ):
410
+ hkey , vkey , vnonce = self ._keydev (key )
411
+ self ._cache [hkey ] = self ._cipher .new (key = vkey , nonce = vnonce ).encrypt (val )
412
+
413
+ def __getitem__ (self , key ):
414
+ hkey , vkey , vnonce = self ._keydev (key )
415
+ return self ._cipher .new (key = vkey , nonce = vnonce ).decrypt (self ._cache [hkey ])
416
+
417
+
418
+ class BytesCache (_KeyMutMapMix , _StatsMix , MutableMapping ):
419
+ """Map bytes to strings."""
420
+
421
+ def __init__ (self , cache ):
422
+ self ._cache = cache
423
+
424
+ def _key (self , key ):
425
+ # assert isinstance(key, bytes)
426
+ return base64 .b85encode (key ).decode ("ASCII" )
427
+
428
+ def __setitem__ (self , key , val ):
429
+ self ._cache .__setitem__ (self ._key (key ), self ._key (val ))
430
+
431
+ def __getitem__ (self , key ):
432
+ val = self ._cache .__getitem__ (self ._key (key ))
433
+ # assert isinstance(val, str)
434
+ return base64 .b85decode (val )
435
+
436
+
437
+ class ToBytesCache (_KeyMutMapMix , _StatsMix , MutableMapping ):
438
+ """Map (JSON-serializable) cache keys and values to bytes."""
439
+
440
+ def __init__ (self , cache ):
441
+ self ._cache = cache
442
+
443
+ def _key (self , key ):
444
+ return json .dumps (key , sort_keys = True , separators = ("," , ":" )).encode ("UTF-8" )
445
+
446
+ def __setitem__ (self , key , val ):
447
+ self ._cache .__setitem__ (self ._key (key ), self ._key (val ))
448
+
449
+ def __getitem__ (self , key ):
450
+ return json .loads (self ._cache .__getitem__ (self ._key (key )).decode ("UTF-8" ))
451
+
452
+
453
+ #
454
+ # CACHED DECORATOR AND HELPERS
455
+ #
370
456
def cached (cache , * args , ** kwargs ):
371
457
"""Extended decorator with delete and exists.
372
458
@@ -508,93 +594,9 @@ def full_hash_key(*args, **kwargs) -> str:
508
594
return base64 .b85encode (hkey ).decode ("ASCII" )
509
595
510
596
511
- #
512
- # Encrypted Cache
513
- #
514
- class EncryptedCache (_KeyMutMapMix , _StatsMix , MutableMapping ):
515
- """Encrypted Bytes Key-Value Cache.
516
-
517
- :param secret: bytes of secret, at least 16 bytes.
518
- :param hsize: size of hashed key, default is 16.
519
-
520
- The key is *not* encrypted but simply hashed, thus they are
521
- fixed size with a very low collision probability.
522
-
523
- By design, the clear-text key is needed to recover the value,
524
- as each value is encrypted with its own key.
525
-
526
- There is no integrity check on the value.
527
-
528
- Algorithms:
529
- - SHA3: hash/key/nonce derivation.
530
- - Salsa20: value encryption.
531
- """
532
-
533
- def __init__ (self , cache : MutableMapping , secret : bytes , hsize : int = 16 ):
534
- self ._cache = cache
535
- assert len (secret ) >= 16
536
- self ._secret = secret
537
- assert 8 <= hsize <= 24
538
- self ._hsize = hsize
539
- from Crypto .Cipher import Salsa20
540
- self ._cipher = Salsa20
541
-
542
- def _keydev (self , key ):
543
- hkey = hashlib .sha3_512 (key + self ._secret ).digest ()
544
- sz = self ._hsize
545
- return (hkey [:sz ], hkey [sz :sz + 32 ], hkey [sz + 32 :sz + 40 ])
546
-
547
- def _key (self , key ):
548
- return self ._keydev (key )[0 ]
549
-
550
- def __setitem__ (self , key , val ):
551
- hkey , vkey , vnonce = self ._keydev (key )
552
- self ._cache [hkey ] = self ._cipher .new (key = vkey , nonce = vnonce ).encrypt (val )
553
-
554
- def __getitem__ (self , key ):
555
- hkey , vkey , vnonce = self ._keydev (key )
556
- return self ._cipher .new (key = vkey , nonce = vnonce ).decrypt (self ._cache [hkey ])
557
-
558
-
559
- class BytesCache (_KeyMutMapMix , _StatsMix , MutableMapping ):
560
- """Map bytes to strings."""
561
-
562
- def __init__ (self , cache ):
563
- self ._cache = cache
564
-
565
- def _key (self , key ):
566
- # assert isinstance(key, bytes)
567
- return base64 .b85encode (key ).decode ("ASCII" )
568
-
569
- def __setitem__ (self , key , val ):
570
- self ._cache .__setitem__ (self ._key (key ), self ._key (val ))
571
-
572
- def __getitem__ (self , key ):
573
- val = self ._cache .__getitem__ (self ._key (key ))
574
- # assert isinstance(val, str)
575
- return base64 .b85decode (val )
576
-
577
-
578
- class ToBytesCache (_KeyMutMapMix , _StatsMix , MutableMapping ):
579
- """Map (JSON-serializable) cache keys and values to bytes."""
580
-
581
- def __init__ (self , cache ):
582
- self ._cache = cache
583
-
584
- def _key (self , key ):
585
- return json .dumps (key , sort_keys = True , separators = ("," , ":" )).encode ("UTF-8" )
586
-
587
- def __setitem__ (self , key , val ):
588
- self ._cache .__setitem__ (self ._key (key ), self ._key (val ))
589
-
590
- def __getitem__ (self , key ):
591
- return json .loads (self ._cache .__getitem__ (self ._key (key )).decode ("UTF-8" ))
592
-
593
-
594
597
#
595
598
# MEMCACHED
596
599
#
597
- # FIXME what about bytes?
598
600
class JsonSerde :
599
601
"""JSON serialize/deserialize class for MemCached (``pymemcache``).
600
602
0 commit comments