aead.py 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  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(object):
  10. _MAX_SIZE = 2 ** 32
  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**32 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(object):
  61. _MAX_SIZE = 2 ** 32
  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. @classmethod
  73. def generate_key(cls, bit_length: int) -> bytes:
  74. if not isinstance(bit_length, int):
  75. raise TypeError("bit_length must be an integer")
  76. if bit_length not in (128, 192, 256):
  77. raise ValueError("bit_length must be 128, 192, or 256")
  78. return os.urandom(bit_length // 8)
  79. def encrypt(
  80. self,
  81. nonce: bytes,
  82. data: bytes,
  83. associated_data: typing.Optional[bytes],
  84. ) -> bytes:
  85. if associated_data is None:
  86. associated_data = b""
  87. if len(data) > self._MAX_SIZE or len(associated_data) > self._MAX_SIZE:
  88. # This is OverflowError to match what cffi would raise
  89. raise OverflowError(
  90. "Data or associated data too long. Max 2**32 bytes"
  91. )
  92. self._check_params(nonce, data, associated_data)
  93. self._validate_lengths(nonce, len(data))
  94. return aead._encrypt(
  95. backend, self, nonce, data, associated_data, self._tag_length
  96. )
  97. def decrypt(
  98. self,
  99. nonce: bytes,
  100. data: bytes,
  101. associated_data: typing.Optional[bytes],
  102. ) -> bytes:
  103. if associated_data is None:
  104. associated_data = b""
  105. self._check_params(nonce, data, associated_data)
  106. return aead._decrypt(
  107. backend, self, nonce, data, associated_data, self._tag_length
  108. )
  109. def _validate_lengths(self, nonce: bytes, data_len: int) -> None:
  110. # For information about computing this, see
  111. # https://tools.ietf.org/html/rfc3610#section-2.1
  112. l_val = 15 - len(nonce)
  113. if 2 ** (8 * l_val) < data_len:
  114. raise ValueError("Data too long for nonce")
  115. def _check_params(
  116. self, nonce: bytes, data: bytes, associated_data: bytes
  117. ) -> None:
  118. utils._check_byteslike("nonce", nonce)
  119. utils._check_bytes("data", data)
  120. utils._check_bytes("associated_data", associated_data)
  121. if not 7 <= len(nonce) <= 13:
  122. raise ValueError("Nonce must be between 7 and 13 bytes")
  123. class AESGCM(object):
  124. _MAX_SIZE = 2 ** 32
  125. def __init__(self, key: bytes):
  126. utils._check_byteslike("key", key)
  127. if len(key) not in (16, 24, 32):
  128. raise ValueError("AESGCM key must be 128, 192, or 256 bits.")
  129. self._key = key
  130. @classmethod
  131. def generate_key(cls, bit_length: int) -> bytes:
  132. if not isinstance(bit_length, int):
  133. raise TypeError("bit_length must be an integer")
  134. if bit_length not in (128, 192, 256):
  135. raise ValueError("bit_length must be 128, 192, or 256")
  136. return os.urandom(bit_length // 8)
  137. def encrypt(
  138. self,
  139. nonce: bytes,
  140. data: bytes,
  141. associated_data: typing.Optional[bytes],
  142. ) -> bytes:
  143. if associated_data is None:
  144. associated_data = b""
  145. if len(data) > self._MAX_SIZE or len(associated_data) > self._MAX_SIZE:
  146. # This is OverflowError to match what cffi would raise
  147. raise OverflowError(
  148. "Data or associated data too long. Max 2**32 bytes"
  149. )
  150. self._check_params(nonce, data, associated_data)
  151. return aead._encrypt(backend, self, nonce, data, associated_data, 16)
  152. def decrypt(
  153. self,
  154. nonce: bytes,
  155. data: bytes,
  156. associated_data: typing.Optional[bytes],
  157. ) -> bytes:
  158. if associated_data is None:
  159. associated_data = b""
  160. self._check_params(nonce, data, associated_data)
  161. return aead._decrypt(backend, self, nonce, data, associated_data, 16)
  162. def _check_params(
  163. self,
  164. nonce: bytes,
  165. data: bytes,
  166. associated_data: bytes,
  167. ) -> None:
  168. utils._check_byteslike("nonce", nonce)
  169. utils._check_bytes("data", data)
  170. utils._check_bytes("associated_data", associated_data)
  171. if len(nonce) < 8 or len(nonce) > 128:
  172. raise ValueError("Nonce must be between 8 and 128 bytes")