padding.py 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  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 AlreadyFinalized
  9. from cryptography.hazmat.bindings._padding import lib
  10. @six.add_metaclass(abc.ABCMeta)
  11. class PaddingContext(object):
  12. @abc.abstractmethod
  13. def update(self, data):
  14. """
  15. Pads the provided bytes and returns any available data as bytes.
  16. """
  17. @abc.abstractmethod
  18. def finalize(self):
  19. """
  20. Finalize the padding, returns bytes.
  21. """
  22. def _byte_padding_check(block_size):
  23. if not (0 <= block_size <= 2040):
  24. raise ValueError("block_size must be in range(0, 2041).")
  25. if block_size % 8 != 0:
  26. raise ValueError("block_size must be a multiple of 8.")
  27. def _byte_padding_update(buffer_, data, block_size):
  28. if buffer_ is None:
  29. raise AlreadyFinalized("Context was already finalized.")
  30. utils._check_byteslike("data", data)
  31. buffer_ += bytes(data)
  32. finished_blocks = len(buffer_) // (block_size // 8)
  33. result = buffer_[: finished_blocks * (block_size // 8)]
  34. buffer_ = buffer_[finished_blocks * (block_size // 8) :]
  35. return buffer_, result
  36. def _byte_padding_pad(buffer_, block_size, paddingfn):
  37. if buffer_ is None:
  38. raise AlreadyFinalized("Context was already finalized.")
  39. pad_size = block_size // 8 - len(buffer_)
  40. return buffer_ + paddingfn(pad_size)
  41. def _byte_unpadding_update(buffer_, data, block_size):
  42. if buffer_ is None:
  43. raise AlreadyFinalized("Context was already finalized.")
  44. utils._check_byteslike("data", data)
  45. buffer_ += bytes(data)
  46. finished_blocks = max(len(buffer_) // (block_size // 8) - 1, 0)
  47. result = buffer_[: finished_blocks * (block_size // 8)]
  48. buffer_ = buffer_[finished_blocks * (block_size // 8) :]
  49. return buffer_, result
  50. def _byte_unpadding_check(buffer_, block_size, checkfn):
  51. if buffer_ is None:
  52. raise AlreadyFinalized("Context was already finalized.")
  53. if len(buffer_) != block_size // 8:
  54. raise ValueError("Invalid padding bytes.")
  55. valid = checkfn(buffer_, block_size // 8)
  56. if not valid:
  57. raise ValueError("Invalid padding bytes.")
  58. pad_size = six.indexbytes(buffer_, -1)
  59. return buffer_[:-pad_size]
  60. class PKCS7(object):
  61. def __init__(self, block_size):
  62. _byte_padding_check(block_size)
  63. self.block_size = block_size
  64. def padder(self):
  65. return _PKCS7PaddingContext(self.block_size)
  66. def unpadder(self):
  67. return _PKCS7UnpaddingContext(self.block_size)
  68. @utils.register_interface(PaddingContext)
  69. class _PKCS7PaddingContext(object):
  70. def __init__(self, block_size):
  71. self.block_size = block_size
  72. # TODO: more copies than necessary, we should use zero-buffer (#193)
  73. self._buffer = b""
  74. def update(self, data):
  75. self._buffer, result = _byte_padding_update(
  76. self._buffer, data, self.block_size
  77. )
  78. return result
  79. def _padding(self, size):
  80. return six.int2byte(size) * size
  81. def finalize(self):
  82. result = _byte_padding_pad(
  83. self._buffer, self.block_size, self._padding
  84. )
  85. self._buffer = None
  86. return result
  87. @utils.register_interface(PaddingContext)
  88. class _PKCS7UnpaddingContext(object):
  89. def __init__(self, block_size):
  90. self.block_size = block_size
  91. # TODO: more copies than necessary, we should use zero-buffer (#193)
  92. self._buffer = b""
  93. def update(self, data):
  94. self._buffer, result = _byte_unpadding_update(
  95. self._buffer, data, self.block_size
  96. )
  97. return result
  98. def finalize(self):
  99. result = _byte_unpadding_check(
  100. self._buffer, self.block_size, lib.Cryptography_check_pkcs7_padding
  101. )
  102. self._buffer = None
  103. return result
  104. class ANSIX923(object):
  105. def __init__(self, block_size):
  106. _byte_padding_check(block_size)
  107. self.block_size = block_size
  108. def padder(self):
  109. return _ANSIX923PaddingContext(self.block_size)
  110. def unpadder(self):
  111. return _ANSIX923UnpaddingContext(self.block_size)
  112. @utils.register_interface(PaddingContext)
  113. class _ANSIX923PaddingContext(object):
  114. def __init__(self, block_size):
  115. self.block_size = block_size
  116. # TODO: more copies than necessary, we should use zero-buffer (#193)
  117. self._buffer = b""
  118. def update(self, data):
  119. self._buffer, result = _byte_padding_update(
  120. self._buffer, data, self.block_size
  121. )
  122. return result
  123. def _padding(self, size):
  124. return six.int2byte(0) * (size - 1) + six.int2byte(size)
  125. def finalize(self):
  126. result = _byte_padding_pad(
  127. self._buffer, self.block_size, self._padding
  128. )
  129. self._buffer = None
  130. return result
  131. @utils.register_interface(PaddingContext)
  132. class _ANSIX923UnpaddingContext(object):
  133. def __init__(self, block_size):
  134. self.block_size = block_size
  135. # TODO: more copies than necessary, we should use zero-buffer (#193)
  136. self._buffer = b""
  137. def update(self, data):
  138. self._buffer, result = _byte_unpadding_update(
  139. self._buffer, data, self.block_size
  140. )
  141. return result
  142. def finalize(self):
  143. result = _byte_unpadding_check(
  144. self._buffer,
  145. self.block_size,
  146. lib.Cryptography_check_ansix923_padding,
  147. )
  148. self._buffer = None
  149. return result