Funktionierender Prototyp des Serious Games zur Vermittlung von Wissen zu Software-Engineering-Arbeitsmodellen.
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.

padding.py 6.1KB

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