serialization.py 3.2 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586
  1. # -*- coding: utf-8 -*-
  2. """
  3. celery.security.serialization
  4. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  5. Secure serializer.
  6. """
  7. from __future__ import absolute_import
  8. import base64
  9. from kombu.serialization import registry, encode, decode
  10. from kombu.utils.encoding import bytes_to_str, str_to_bytes
  11. from .certificate import Certificate, FSCertStore
  12. from .key import PrivateKey
  13. from .utils import reraise_errors
  14. def b64encode(s):
  15. return bytes_to_str(base64.b64encode(str_to_bytes(s)))
  16. def b64decode(s):
  17. return base64.b64decode(str_to_bytes(s))
  18. class SecureSerializer(object):
  19. def __init__(self, key=None, cert=None, cert_store=None,
  20. digest='sha1', serializer='json'):
  21. self._key = key
  22. self._cert = cert
  23. self._cert_store = cert_store
  24. self._digest = digest
  25. self._serializer = serializer
  26. def serialize(self, data):
  27. """serialize data structure into string"""
  28. assert self._key is not None
  29. assert self._cert is not None
  30. with reraise_errors('Unable to serialize: {0!r}', (Exception, )):
  31. content_type, content_encoding, body = encode(
  32. data, serializer=self._serializer)
  33. # What we sign is the serialized body, not the body itself.
  34. # this way the receiver doesn't have to decode the contents
  35. # to verify the signature (and thus avoiding potential flaws
  36. # in the decoding step).
  37. return self._pack(body, content_type, content_encoding,
  38. signature=self._key.sign(body, self._digest),
  39. signer=self._cert.get_id())
  40. def deserialize(self, data):
  41. """deserialize data structure from string"""
  42. assert self._cert_store is not None
  43. with reraise_errors('Unable to deserialize: {0!r}', (Exception, )):
  44. payload = self._unpack(data)
  45. signature, signer, body = (payload['signature'],
  46. payload['signer'],
  47. payload['body'])
  48. self._cert_store[signer].verify(body, signature, self._digest)
  49. return decode(body, payload['content_type'],
  50. payload['content_encoding'], force=True)
  51. def _pack(self, body, content_type, content_encoding, signer, signature,
  52. sep='\x00\x01'):
  53. return b64encode(sep.join([signer, signature,
  54. content_type, content_encoding, body]))
  55. def _unpack(self, payload, sep='\x00\x01',
  56. fields=('signer', 'signature', 'content_type',
  57. 'content_encoding', 'body')):
  58. return dict(zip(fields, b64decode(payload).split(sep)))
  59. def register_auth(key=None, cert=None, store=None, digest='sha1',
  60. serializer='json'):
  61. """register security serializer"""
  62. s = SecureSerializer(key and PrivateKey(key),
  63. cert and Certificate(cert),
  64. store and FSCertStore(store),
  65. digest=digest, serializer=serializer)
  66. registry.register('auth', s.serialize, s.deserialize,
  67. content_type='application/data',
  68. content_encoding='utf-8')