modes.py 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  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. from cryptography import utils
  7. from cryptography.exceptions import UnsupportedAlgorithm, _Reasons
  8. from cryptography.hazmat.primitives._cipheralgorithm import (
  9. BlockCipherAlgorithm,
  10. CipherAlgorithm,
  11. )
  12. from cryptography.hazmat.primitives.ciphers import algorithms
  13. class Mode(metaclass=abc.ABCMeta):
  14. @property
  15. @abc.abstractmethod
  16. def name(self) -> str:
  17. """
  18. A string naming this mode (e.g. "ECB", "CBC").
  19. """
  20. @abc.abstractmethod
  21. def validate_for_algorithm(self, algorithm: CipherAlgorithm) -> None:
  22. """
  23. Checks that all the necessary invariants of this (mode, algorithm)
  24. combination are met.
  25. """
  26. class ModeWithInitializationVector(Mode, metaclass=abc.ABCMeta):
  27. @property
  28. @abc.abstractmethod
  29. def initialization_vector(self) -> bytes:
  30. """
  31. The value of the initialization vector for this mode as bytes.
  32. """
  33. class ModeWithTweak(Mode, metaclass=abc.ABCMeta):
  34. @property
  35. @abc.abstractmethod
  36. def tweak(self) -> bytes:
  37. """
  38. The value of the tweak for this mode as bytes.
  39. """
  40. class ModeWithNonce(Mode, metaclass=abc.ABCMeta):
  41. @property
  42. @abc.abstractmethod
  43. def nonce(self) -> bytes:
  44. """
  45. The value of the nonce for this mode as bytes.
  46. """
  47. class ModeWithAuthenticationTag(Mode, metaclass=abc.ABCMeta):
  48. @property
  49. @abc.abstractmethod
  50. def tag(self) -> bytes | None:
  51. """
  52. The value of the tag supplied to the constructor of this mode.
  53. """
  54. def _check_aes_key_length(self: Mode, algorithm: CipherAlgorithm) -> None:
  55. if algorithm.key_size > 256 and algorithm.name == "AES":
  56. raise ValueError(
  57. "Only 128, 192, and 256 bit keys are allowed for this AES mode"
  58. )
  59. def _check_iv_length(
  60. self: ModeWithInitializationVector, algorithm: BlockCipherAlgorithm
  61. ) -> None:
  62. iv_len = len(self.initialization_vector)
  63. if iv_len * 8 != algorithm.block_size:
  64. raise ValueError(f"Invalid IV size ({iv_len}) for {self.name}.")
  65. def _check_nonce_length(
  66. nonce: bytes, name: str, algorithm: CipherAlgorithm
  67. ) -> None:
  68. if not isinstance(algorithm, BlockCipherAlgorithm):
  69. raise UnsupportedAlgorithm(
  70. f"{name} requires a block cipher algorithm",
  71. _Reasons.UNSUPPORTED_CIPHER,
  72. )
  73. if len(nonce) * 8 != algorithm.block_size:
  74. raise ValueError(f"Invalid nonce size ({len(nonce)}) for {name}.")
  75. def _check_iv_and_key_length(
  76. self: ModeWithInitializationVector, algorithm: CipherAlgorithm
  77. ) -> None:
  78. if not isinstance(algorithm, BlockCipherAlgorithm):
  79. raise UnsupportedAlgorithm(
  80. f"{self} requires a block cipher algorithm",
  81. _Reasons.UNSUPPORTED_CIPHER,
  82. )
  83. _check_aes_key_length(self, algorithm)
  84. _check_iv_length(self, algorithm)
  85. class CBC(ModeWithInitializationVector):
  86. name = "CBC"
  87. def __init__(self, initialization_vector: bytes):
  88. utils._check_byteslike("initialization_vector", initialization_vector)
  89. self._initialization_vector = initialization_vector
  90. @property
  91. def initialization_vector(self) -> bytes:
  92. return self._initialization_vector
  93. validate_for_algorithm = _check_iv_and_key_length
  94. class XTS(ModeWithTweak):
  95. name = "XTS"
  96. def __init__(self, tweak: bytes):
  97. utils._check_byteslike("tweak", tweak)
  98. if len(tweak) != 16:
  99. raise ValueError("tweak must be 128-bits (16 bytes)")
  100. self._tweak = tweak
  101. @property
  102. def tweak(self) -> bytes:
  103. return self._tweak
  104. def validate_for_algorithm(self, algorithm: CipherAlgorithm) -> None:
  105. if isinstance(algorithm, (algorithms.AES128, algorithms.AES256)):
  106. raise TypeError(
  107. "The AES128 and AES256 classes do not support XTS, please use "
  108. "the standard AES class instead."
  109. )
  110. if algorithm.key_size not in (256, 512):
  111. raise ValueError(
  112. "The XTS specification requires a 256-bit key for AES-128-XTS"
  113. " and 512-bit key for AES-256-XTS"
  114. )
  115. class ECB(Mode):
  116. name = "ECB"
  117. validate_for_algorithm = _check_aes_key_length
  118. class OFB(ModeWithInitializationVector):
  119. name = "OFB"
  120. def __init__(self, initialization_vector: bytes):
  121. utils._check_byteslike("initialization_vector", initialization_vector)
  122. self._initialization_vector = initialization_vector
  123. @property
  124. def initialization_vector(self) -> bytes:
  125. return self._initialization_vector
  126. validate_for_algorithm = _check_iv_and_key_length
  127. class CFB(ModeWithInitializationVector):
  128. name = "CFB"
  129. def __init__(self, initialization_vector: bytes):
  130. utils._check_byteslike("initialization_vector", initialization_vector)
  131. self._initialization_vector = initialization_vector
  132. @property
  133. def initialization_vector(self) -> bytes:
  134. return self._initialization_vector
  135. validate_for_algorithm = _check_iv_and_key_length
  136. class CFB8(ModeWithInitializationVector):
  137. name = "CFB8"
  138. def __init__(self, initialization_vector: bytes):
  139. utils._check_byteslike("initialization_vector", initialization_vector)
  140. self._initialization_vector = initialization_vector
  141. @property
  142. def initialization_vector(self) -> bytes:
  143. return self._initialization_vector
  144. validate_for_algorithm = _check_iv_and_key_length
  145. class CTR(ModeWithNonce):
  146. name = "CTR"
  147. def __init__(self, nonce: bytes):
  148. utils._check_byteslike("nonce", nonce)
  149. self._nonce = nonce
  150. @property
  151. def nonce(self) -> bytes:
  152. return self._nonce
  153. def validate_for_algorithm(self, algorithm: CipherAlgorithm) -> None:
  154. _check_aes_key_length(self, algorithm)
  155. _check_nonce_length(self.nonce, self.name, algorithm)
  156. class GCM(ModeWithInitializationVector, ModeWithAuthenticationTag):
  157. name = "GCM"
  158. _MAX_ENCRYPTED_BYTES = (2**39 - 256) // 8
  159. _MAX_AAD_BYTES = (2**64) // 8
  160. def __init__(
  161. self,
  162. initialization_vector: bytes,
  163. tag: bytes | None = None,
  164. min_tag_length: int = 16,
  165. ):
  166. # OpenSSL 3.0.0 constrains GCM IVs to [64, 1024] bits inclusive
  167. # This is a sane limit anyway so we'll enforce it here.
  168. utils._check_byteslike("initialization_vector", initialization_vector)
  169. if len(initialization_vector) < 8 or len(initialization_vector) > 128:
  170. raise ValueError(
  171. "initialization_vector must be between 8 and 128 bytes (64 "
  172. "and 1024 bits)."
  173. )
  174. self._initialization_vector = initialization_vector
  175. if tag is not None:
  176. utils._check_bytes("tag", tag)
  177. if min_tag_length < 4:
  178. raise ValueError("min_tag_length must be >= 4")
  179. if len(tag) < min_tag_length:
  180. raise ValueError(
  181. f"Authentication tag must be {min_tag_length} bytes or "
  182. "longer."
  183. )
  184. self._tag = tag
  185. self._min_tag_length = min_tag_length
  186. @property
  187. def tag(self) -> bytes | None:
  188. return self._tag
  189. @property
  190. def initialization_vector(self) -> bytes:
  191. return self._initialization_vector
  192. def validate_for_algorithm(self, algorithm: CipherAlgorithm) -> None:
  193. _check_aes_key_length(self, algorithm)
  194. if not isinstance(algorithm, BlockCipherAlgorithm):
  195. raise UnsupportedAlgorithm(
  196. "GCM requires a block cipher algorithm",
  197. _Reasons.UNSUPPORTED_CIPHER,
  198. )
  199. block_size_bytes = algorithm.block_size // 8
  200. if self._tag is not None and len(self._tag) > block_size_bytes:
  201. raise ValueError(
  202. f"Authentication tag cannot be more than {block_size_bytes} "
  203. "bytes."
  204. )