aead.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361
  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. import os
  5. import typing
  6. from cryptography import exceptions, utils
  7. from cryptography.hazmat.backends.openssl import aead
  8. from cryptography.hazmat.backends.openssl.backend import backend
  9. class ChaCha20Poly1305:
  10. _MAX_SIZE = 2**31 - 1
  11. def __init__(self, key: bytes):
  12. if not backend.aead_cipher_supported(self):
  13. raise exceptions.UnsupportedAlgorithm(
  14. "ChaCha20Poly1305 is not supported by this version of OpenSSL",
  15. exceptions._Reasons.UNSUPPORTED_CIPHER,
  16. )
  17. utils._check_byteslike("key", key)
  18. if len(key) != 32:
  19. raise ValueError("ChaCha20Poly1305 key must be 32 bytes.")
  20. self._key = key
  21. @classmethod
  22. def generate_key(cls) -> bytes:
  23. return os.urandom(32)
  24. def encrypt(
  25. self,
  26. nonce: bytes,
  27. data: bytes,
  28. associated_data: typing.Optional[bytes],
  29. ) -> bytes:
  30. if associated_data is None:
  31. associated_data = b""
  32. if len(data) > self._MAX_SIZE or len(associated_data) > self._MAX_SIZE:
  33. # This is OverflowError to match what cffi would raise
  34. raise OverflowError(
  35. "Data or associated data too long. Max 2**31 - 1 bytes"
  36. )
  37. self._check_params(nonce, data, associated_data)
  38. return aead._encrypt(backend, self, nonce, data, [associated_data], 16)
  39. def decrypt(
  40. self,
  41. nonce: bytes,
  42. data: bytes,
  43. associated_data: typing.Optional[bytes],
  44. ) -> bytes:
  45. if associated_data is None:
  46. associated_data = b""
  47. self._check_params(nonce, data, associated_data)
  48. return aead._decrypt(backend, self, nonce, data, [associated_data], 16)
  49. def _check_params(
  50. self,
  51. nonce: bytes,
  52. data: bytes,
  53. associated_data: bytes,
  54. ) -> None:
  55. utils._check_byteslike("nonce", nonce)
  56. utils._check_bytes("data", data)
  57. utils._check_bytes("associated_data", associated_data)
  58. if len(nonce) != 12:
  59. raise ValueError("Nonce must be 12 bytes")
  60. class AESCCM:
  61. _MAX_SIZE = 2**31 - 1
  62. def __init__(self, key: bytes, tag_length: int = 16):
  63. utils._check_byteslike("key", key)
  64. if len(key) not in (16, 24, 32):
  65. raise ValueError("AESCCM key must be 128, 192, or 256 bits.")
  66. self._key = key
  67. if not isinstance(tag_length, int):
  68. raise TypeError("tag_length must be an integer")
  69. if tag_length not in (4, 6, 8, 10, 12, 14, 16):
  70. raise ValueError("Invalid tag_length")
  71. self._tag_length = tag_length
  72. if not backend.aead_cipher_supported(self):
  73. raise exceptions.UnsupportedAlgorithm(
  74. "AESCCM is not supported by this version of OpenSSL",
  75. exceptions._Reasons.UNSUPPORTED_CIPHER,
  76. )
  77. @classmethod
  78. def generate_key(cls, bit_length: int) -> bytes:
  79. if not isinstance(bit_length, int):
  80. raise TypeError("bit_length must be an integer")
  81. if bit_length not in (128, 192, 256):
  82. raise ValueError("bit_length must be 128, 192, or 256")
  83. return os.urandom(bit_length // 8)
  84. def encrypt(
  85. self,
  86. nonce: bytes,
  87. data: bytes,
  88. associated_data: typing.Optional[bytes],
  89. ) -> bytes:
  90. if associated_data is None:
  91. associated_data = b""
  92. if len(data) > self._MAX_SIZE or len(associated_data) > self._MAX_SIZE:
  93. # This is OverflowError to match what cffi would raise
  94. raise OverflowError(
  95. "Data or associated data too long. Max 2**31 - 1 bytes"
  96. )
  97. self._check_params(nonce, data, associated_data)
  98. self._validate_lengths(nonce, len(data))
  99. return aead._encrypt(
  100. backend, self, nonce, data, [associated_data], self._tag_length
  101. )
  102. def decrypt(
  103. self,
  104. nonce: bytes,
  105. data: bytes,
  106. associated_data: typing.Optional[bytes],
  107. ) -> bytes:
  108. if associated_data is None:
  109. associated_data = b""
  110. self._check_params(nonce, data, associated_data)
  111. return aead._decrypt(
  112. backend, self, nonce, data, [associated_data], self._tag_length
  113. )
  114. def _validate_lengths(self, nonce: bytes, data_len: int) -> None:
  115. # For information about computing this, see
  116. # https://tools.ietf.org/html/rfc3610#section-2.1
  117. l_val = 15 - len(nonce)
  118. if 2 ** (8 * l_val) < data_len:
  119. raise ValueError("Data too long for nonce")
  120. def _check_params(
  121. self, nonce: bytes, data: bytes, associated_data: bytes
  122. ) -> None:
  123. utils._check_byteslike("nonce", nonce)
  124. utils._check_bytes("data", data)
  125. utils._check_bytes("associated_data", associated_data)
  126. if not 7 <= len(nonce) <= 13:
  127. raise ValueError("Nonce must be between 7 and 13 bytes")
  128. class AESGCM:
  129. _MAX_SIZE = 2**31 - 1
  130. def __init__(self, key: bytes):
  131. utils._check_byteslike("key", key)
  132. if len(key) not in (16, 24, 32):
  133. raise ValueError("AESGCM key must be 128, 192, or 256 bits.")
  134. self._key = key
  135. @classmethod
  136. def generate_key(cls, bit_length: int) -> bytes:
  137. if not isinstance(bit_length, int):
  138. raise TypeError("bit_length must be an integer")
  139. if bit_length not in (128, 192, 256):
  140. raise ValueError("bit_length must be 128, 192, or 256")
  141. return os.urandom(bit_length // 8)
  142. def encrypt(
  143. self,
  144. nonce: bytes,
  145. data: bytes,
  146. associated_data: typing.Optional[bytes],
  147. ) -> bytes:
  148. if associated_data is None:
  149. associated_data = b""
  150. if len(data) > self._MAX_SIZE or len(associated_data) > self._MAX_SIZE:
  151. # This is OverflowError to match what cffi would raise
  152. raise OverflowError(
  153. "Data or associated data too long. Max 2**31 - 1 bytes"
  154. )
  155. self._check_params(nonce, data, associated_data)
  156. return aead._encrypt(backend, self, nonce, data, [associated_data], 16)
  157. def decrypt(
  158. self,
  159. nonce: bytes,
  160. data: bytes,
  161. associated_data: typing.Optional[bytes],
  162. ) -> bytes:
  163. if associated_data is None:
  164. associated_data = b""
  165. self._check_params(nonce, data, associated_data)
  166. return aead._decrypt(backend, self, nonce, data, [associated_data], 16)
  167. def _check_params(
  168. self,
  169. nonce: bytes,
  170. data: bytes,
  171. associated_data: bytes,
  172. ) -> None:
  173. utils._check_byteslike("nonce", nonce)
  174. utils._check_bytes("data", data)
  175. utils._check_bytes("associated_data", associated_data)
  176. if len(nonce) < 8 or len(nonce) > 128:
  177. raise ValueError("Nonce must be between 8 and 128 bytes")
  178. class AESOCB3:
  179. _MAX_SIZE = 2**31 - 1
  180. def __init__(self, key: bytes):
  181. utils._check_byteslike("key", key)
  182. if len(key) not in (16, 24, 32):
  183. raise ValueError("AESOCB3 key must be 128, 192, or 256 bits.")
  184. self._key = key
  185. if not backend.aead_cipher_supported(self):
  186. raise exceptions.UnsupportedAlgorithm(
  187. "OCB3 is not supported by this version of OpenSSL",
  188. exceptions._Reasons.UNSUPPORTED_CIPHER,
  189. )
  190. @classmethod
  191. def generate_key(cls, bit_length: int) -> bytes:
  192. if not isinstance(bit_length, int):
  193. raise TypeError("bit_length must be an integer")
  194. if bit_length not in (128, 192, 256):
  195. raise ValueError("bit_length must be 128, 192, or 256")
  196. return os.urandom(bit_length // 8)
  197. def encrypt(
  198. self,
  199. nonce: bytes,
  200. data: bytes,
  201. associated_data: typing.Optional[bytes],
  202. ) -> bytes:
  203. if associated_data is None:
  204. associated_data = b""
  205. if len(data) > self._MAX_SIZE or len(associated_data) > self._MAX_SIZE:
  206. # This is OverflowError to match what cffi would raise
  207. raise OverflowError(
  208. "Data or associated data too long. Max 2**31 - 1 bytes"
  209. )
  210. self._check_params(nonce, data, associated_data)
  211. return aead._encrypt(backend, self, nonce, data, [associated_data], 16)
  212. def decrypt(
  213. self,
  214. nonce: bytes,
  215. data: bytes,
  216. associated_data: typing.Optional[bytes],
  217. ) -> bytes:
  218. if associated_data is None:
  219. associated_data = b""
  220. self._check_params(nonce, data, associated_data)
  221. return aead._decrypt(backend, self, nonce, data, [associated_data], 16)
  222. def _check_params(
  223. self,
  224. nonce: bytes,
  225. data: bytes,
  226. associated_data: bytes,
  227. ) -> None:
  228. utils._check_byteslike("nonce", nonce)
  229. utils._check_bytes("data", data)
  230. utils._check_bytes("associated_data", associated_data)
  231. if len(nonce) < 12 or len(nonce) > 15:
  232. raise ValueError("Nonce must be between 12 and 15 bytes")
  233. class AESSIV(object):
  234. _MAX_SIZE = 2**31 - 1
  235. def __init__(self, key: bytes):
  236. utils._check_byteslike("key", key)
  237. if len(key) not in (32, 48, 64):
  238. raise ValueError("AESSIV key must be 256, 384, or 512 bits.")
  239. self._key = key
  240. if not backend.aead_cipher_supported(self):
  241. raise exceptions.UnsupportedAlgorithm(
  242. "AES-SIV is not supported by this version of OpenSSL",
  243. exceptions._Reasons.UNSUPPORTED_CIPHER,
  244. )
  245. @classmethod
  246. def generate_key(cls, bit_length: int) -> bytes:
  247. if not isinstance(bit_length, int):
  248. raise TypeError("bit_length must be an integer")
  249. if bit_length not in (256, 384, 512):
  250. raise ValueError("bit_length must be 256, 384, or 512")
  251. return os.urandom(bit_length // 8)
  252. def encrypt(
  253. self,
  254. data: bytes,
  255. associated_data: typing.Optional[typing.List[bytes]],
  256. ) -> bytes:
  257. if associated_data is None:
  258. associated_data = []
  259. self._check_params(data, associated_data)
  260. if len(data) > self._MAX_SIZE or any(
  261. len(ad) > self._MAX_SIZE for ad in associated_data
  262. ):
  263. # This is OverflowError to match what cffi would raise
  264. raise OverflowError(
  265. "Data or associated data too long. Max 2**31 - 1 bytes"
  266. )
  267. return aead._encrypt(backend, self, b"", data, associated_data, 16)
  268. def decrypt(
  269. self,
  270. data: bytes,
  271. associated_data: typing.Optional[typing.List[bytes]],
  272. ) -> bytes:
  273. if associated_data is None:
  274. associated_data = []
  275. self._check_params(data, associated_data)
  276. return aead._decrypt(backend, self, b"", data, associated_data, 16)
  277. def _check_params(
  278. self,
  279. data: bytes,
  280. associated_data: typing.List,
  281. ) -> None:
  282. utils._check_bytes("data", data)
  283. if not isinstance(associated_data, list) or not all(
  284. isinstance(x, bytes) for x in associated_data
  285. ):
  286. raise TypeError("associated_data must be a list of bytes or None")