base.py 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. # This file is dual licensed under the terms of the Apache License, Version
  2. # 2.0, and the BSD License. See the LICENSE file in the root of this repository
  3. # for complete details.
  4. from __future__ import absolute_import, division, print_function
  5. import abc
  6. import six
  7. from cryptography import utils
  8. from cryptography.exceptions import (
  9. AlreadyFinalized,
  10. AlreadyUpdated,
  11. NotYetFinalized,
  12. UnsupportedAlgorithm,
  13. _Reasons,
  14. )
  15. from cryptography.hazmat.backends import _get_backend
  16. from cryptography.hazmat.backends.interfaces import CipherBackend
  17. from cryptography.hazmat.primitives.ciphers import modes
  18. @six.add_metaclass(abc.ABCMeta)
  19. class CipherAlgorithm(object):
  20. @abc.abstractproperty
  21. def name(self):
  22. """
  23. A string naming this mode (e.g. "AES", "Camellia").
  24. """
  25. @abc.abstractproperty
  26. def key_size(self):
  27. """
  28. The size of the key being used as an integer in bits (e.g. 128, 256).
  29. """
  30. @six.add_metaclass(abc.ABCMeta)
  31. class BlockCipherAlgorithm(object):
  32. @abc.abstractproperty
  33. def block_size(self):
  34. """
  35. The size of a block as an integer in bits (e.g. 64, 128).
  36. """
  37. @six.add_metaclass(abc.ABCMeta)
  38. class CipherContext(object):
  39. @abc.abstractmethod
  40. def update(self, data):
  41. """
  42. Processes the provided bytes through the cipher and returns the results
  43. as bytes.
  44. """
  45. @abc.abstractmethod
  46. def update_into(self, data, buf):
  47. """
  48. Processes the provided bytes and writes the resulting data into the
  49. provided buffer. Returns the number of bytes written.
  50. """
  51. @abc.abstractmethod
  52. def finalize(self):
  53. """
  54. Returns the results of processing the final block as bytes.
  55. """
  56. @six.add_metaclass(abc.ABCMeta)
  57. class AEADCipherContext(object):
  58. @abc.abstractmethod
  59. def authenticate_additional_data(self, data):
  60. """
  61. Authenticates the provided bytes.
  62. """
  63. @six.add_metaclass(abc.ABCMeta)
  64. class AEADDecryptionContext(object):
  65. @abc.abstractmethod
  66. def finalize_with_tag(self, tag):
  67. """
  68. Returns the results of processing the final block as bytes and allows
  69. delayed passing of the authentication tag.
  70. """
  71. @six.add_metaclass(abc.ABCMeta)
  72. class AEADEncryptionContext(object):
  73. @abc.abstractproperty
  74. def tag(self):
  75. """
  76. Returns tag bytes. This is only available after encryption is
  77. finalized.
  78. """
  79. class Cipher(object):
  80. def __init__(self, algorithm, mode, backend=None):
  81. backend = _get_backend(backend)
  82. if not isinstance(backend, CipherBackend):
  83. raise UnsupportedAlgorithm(
  84. "Backend object does not implement CipherBackend.",
  85. _Reasons.BACKEND_MISSING_INTERFACE,
  86. )
  87. if not isinstance(algorithm, CipherAlgorithm):
  88. raise TypeError("Expected interface of CipherAlgorithm.")
  89. if mode is not None:
  90. mode.validate_for_algorithm(algorithm)
  91. self.algorithm = algorithm
  92. self.mode = mode
  93. self._backend = backend
  94. def encryptor(self):
  95. if isinstance(self.mode, modes.ModeWithAuthenticationTag):
  96. if self.mode.tag is not None:
  97. raise ValueError(
  98. "Authentication tag must be None when encrypting."
  99. )
  100. ctx = self._backend.create_symmetric_encryption_ctx(
  101. self.algorithm, self.mode
  102. )
  103. return self._wrap_ctx(ctx, encrypt=True)
  104. def decryptor(self):
  105. ctx = self._backend.create_symmetric_decryption_ctx(
  106. self.algorithm, self.mode
  107. )
  108. return self._wrap_ctx(ctx, encrypt=False)
  109. def _wrap_ctx(self, ctx, encrypt):
  110. if isinstance(self.mode, modes.ModeWithAuthenticationTag):
  111. if encrypt:
  112. return _AEADEncryptionContext(ctx)
  113. else:
  114. return _AEADCipherContext(ctx)
  115. else:
  116. return _CipherContext(ctx)
  117. @utils.register_interface(CipherContext)
  118. class _CipherContext(object):
  119. def __init__(self, ctx):
  120. self._ctx = ctx
  121. def update(self, data):
  122. if self._ctx is None:
  123. raise AlreadyFinalized("Context was already finalized.")
  124. return self._ctx.update(data)
  125. def update_into(self, data, buf):
  126. if self._ctx is None:
  127. raise AlreadyFinalized("Context was already finalized.")
  128. return self._ctx.update_into(data, buf)
  129. def finalize(self):
  130. if self._ctx is None:
  131. raise AlreadyFinalized("Context was already finalized.")
  132. data = self._ctx.finalize()
  133. self._ctx = None
  134. return data
  135. @utils.register_interface(AEADCipherContext)
  136. @utils.register_interface(CipherContext)
  137. @utils.register_interface(AEADDecryptionContext)
  138. class _AEADCipherContext(object):
  139. def __init__(self, ctx):
  140. self._ctx = ctx
  141. self._bytes_processed = 0
  142. self._aad_bytes_processed = 0
  143. self._tag = None
  144. self._updated = False
  145. def _check_limit(self, data_size):
  146. if self._ctx is None:
  147. raise AlreadyFinalized("Context was already finalized.")
  148. self._updated = True
  149. self._bytes_processed += data_size
  150. if self._bytes_processed > self._ctx._mode._MAX_ENCRYPTED_BYTES:
  151. raise ValueError(
  152. "{} has a maximum encrypted byte limit of {}".format(
  153. self._ctx._mode.name, self._ctx._mode._MAX_ENCRYPTED_BYTES
  154. )
  155. )
  156. def update(self, data):
  157. self._check_limit(len(data))
  158. return self._ctx.update(data)
  159. def update_into(self, data, buf):
  160. self._check_limit(len(data))
  161. return self._ctx.update_into(data, buf)
  162. def finalize(self):
  163. if self._ctx is None:
  164. raise AlreadyFinalized("Context was already finalized.")
  165. data = self._ctx.finalize()
  166. self._tag = self._ctx.tag
  167. self._ctx = None
  168. return data
  169. def finalize_with_tag(self, tag):
  170. if self._ctx is None:
  171. raise AlreadyFinalized("Context was already finalized.")
  172. data = self._ctx.finalize_with_tag(tag)
  173. self._tag = self._ctx.tag
  174. self._ctx = None
  175. return data
  176. def authenticate_additional_data(self, data):
  177. if self._ctx is None:
  178. raise AlreadyFinalized("Context was already finalized.")
  179. if self._updated:
  180. raise AlreadyUpdated("Update has been called on this context.")
  181. self._aad_bytes_processed += len(data)
  182. if self._aad_bytes_processed > self._ctx._mode._MAX_AAD_BYTES:
  183. raise ValueError(
  184. "{} has a maximum AAD byte limit of {}".format(
  185. self._ctx._mode.name, self._ctx._mode._MAX_AAD_BYTES
  186. )
  187. )
  188. self._ctx.authenticate_additional_data(data)
  189. @utils.register_interface(AEADEncryptionContext)
  190. class _AEADEncryptionContext(_AEADCipherContext):
  191. @property
  192. def tag(self):
  193. if self._ctx is not None:
  194. raise NotYetFinalized(
  195. "You must finalize encryption before " "getting the tag."
  196. )
  197. return self._tag