Development of an internal social media platform with personalised dashboards for students
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

serialization.py 3.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  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, dumps, loads
  10. from kombu.utils.encoding import bytes_to_str, str_to_bytes, ensure_bytes
  11. from .certificate import Certificate, FSCertStore
  12. from .key import PrivateKey
  13. from .utils import reraise_errors
  14. __all__ = ['SecureSerializer', 'register_auth']
  15. def b64encode(s):
  16. return bytes_to_str(base64.b64encode(str_to_bytes(s)))
  17. def b64decode(s):
  18. return base64.b64decode(str_to_bytes(s))
  19. class SecureSerializer(object):
  20. def __init__(self, key=None, cert=None, cert_store=None,
  21. digest='sha1', serializer='json'):
  22. self._key = key
  23. self._cert = cert
  24. self._cert_store = cert_store
  25. self._digest = digest
  26. self._serializer = serializer
  27. def serialize(self, data):
  28. """serialize data structure into string"""
  29. assert self._key is not None
  30. assert self._cert is not None
  31. with reraise_errors('Unable to serialize: {0!r}', (Exception, )):
  32. content_type, content_encoding, body = dumps(
  33. bytes_to_str(data), serializer=self._serializer)
  34. # What we sign is the serialized body, not the body itself.
  35. # this way the receiver doesn't have to decode the contents
  36. # to verify the signature (and thus avoiding potential flaws
  37. # in the decoding step).
  38. body = ensure_bytes(body)
  39. return self._pack(body, content_type, content_encoding,
  40. signature=self._key.sign(body, self._digest),
  41. signer=self._cert.get_id())
  42. def deserialize(self, data):
  43. """deserialize data structure from string"""
  44. assert self._cert_store is not None
  45. with reraise_errors('Unable to deserialize: {0!r}', (Exception, )):
  46. payload = self._unpack(data)
  47. signature, signer, body = (payload['signature'],
  48. payload['signer'],
  49. payload['body'])
  50. self._cert_store[signer].verify(body, signature, self._digest)
  51. return loads(bytes_to_str(body), payload['content_type'],
  52. payload['content_encoding'], force=True)
  53. def _pack(self, body, content_type, content_encoding, signer, signature,
  54. sep=str_to_bytes('\x00\x01')):
  55. fields = sep.join(
  56. ensure_bytes(s) for s in [signer, signature, content_type,
  57. content_encoding, body]
  58. )
  59. return b64encode(fields)
  60. def _unpack(self, payload, sep=str_to_bytes('\x00\x01')):
  61. raw_payload = b64decode(ensure_bytes(payload))
  62. first_sep = raw_payload.find(sep)
  63. signer = raw_payload[:first_sep]
  64. signer_cert = self._cert_store[signer]
  65. sig_len = signer_cert._cert.get_pubkey().bits() >> 3
  66. signature = raw_payload[
  67. first_sep + len(sep):first_sep + len(sep) + sig_len
  68. ]
  69. end_of_sig = first_sep + len(sep) + sig_len + len(sep)
  70. v = raw_payload[end_of_sig:].split(sep)
  71. return {
  72. 'signer': signer,
  73. 'signature': signature,
  74. 'content_type': bytes_to_str(v[0]),
  75. 'content_encoding': bytes_to_str(v[1]),
  76. 'body': bytes_to_str(v[2]),
  77. }
  78. def register_auth(key=None, cert=None, store=None, digest='sha1',
  79. serializer='json'):
  80. """register security serializer"""
  81. s = SecureSerializer(key and PrivateKey(key),
  82. cert and Certificate(cert),
  83. store and FSCertStore(store),
  84. digest=digest, serializer=serializer)
  85. registry.register('auth', s.serialize, s.deserialize,
  86. content_type='application/data',
  87. content_encoding='utf-8')