Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/cryptography/hazmat/primitives/ciphers/aead.py: 80%
122 statements
« prev ^ index » next coverage.py v7.3.2, created at 2023-12-08 07:26 +0000
« prev ^ index » next coverage.py v7.3.2, created at 2023-12-08 07:26 +0000
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.
5from __future__ import annotations
7import os
9from cryptography import exceptions, utils
10from cryptography.hazmat.backends.openssl import aead
11from cryptography.hazmat.backends.openssl.backend import backend
12from cryptography.hazmat.bindings._rust import FixedPool
13from cryptography.hazmat.bindings._rust import openssl as rust_openssl
15__all__ = [
16 "ChaCha20Poly1305",
17 "AESCCM",
18 "AESGCM",
19 "AESGCMSIV",
20 "AESOCB3",
21 "AESSIV",
22]
24AESSIV = rust_openssl.aead.AESSIV
25AESOCB3 = rust_openssl.aead.AESOCB3
26AESGCMSIV = rust_openssl.aead.AESGCMSIV
29class ChaCha20Poly1305:
30 _MAX_SIZE = 2**31 - 1
32 def __init__(self, key: bytes):
33 if not backend.aead_cipher_supported(self):
34 raise exceptions.UnsupportedAlgorithm(
35 "ChaCha20Poly1305 is not supported by this version of OpenSSL",
36 exceptions._Reasons.UNSUPPORTED_CIPHER,
37 )
38 utils._check_byteslike("key", key)
40 if len(key) != 32:
41 raise ValueError("ChaCha20Poly1305 key must be 32 bytes.")
43 self._key = key
44 self._pool = FixedPool(self._create_fn)
46 @classmethod
47 def generate_key(cls) -> bytes:
48 return os.urandom(32)
50 def _create_fn(self):
51 return aead._aead_create_ctx(backend, self, self._key)
53 def encrypt(
54 self,
55 nonce: bytes,
56 data: bytes,
57 associated_data: bytes | None,
58 ) -> bytes:
59 if associated_data is None:
60 associated_data = b""
62 if len(data) > self._MAX_SIZE or len(associated_data) > self._MAX_SIZE:
63 # This is OverflowError to match what cffi would raise
64 raise OverflowError(
65 "Data or associated data too long. Max 2**31 - 1 bytes"
66 )
68 self._check_params(nonce, data, associated_data)
69 with self._pool.acquire() as ctx:
70 return aead._encrypt(
71 backend, self, nonce, data, [associated_data], 16, ctx
72 )
74 def decrypt(
75 self,
76 nonce: bytes,
77 data: bytes,
78 associated_data: bytes | None,
79 ) -> bytes:
80 if associated_data is None:
81 associated_data = b""
83 self._check_params(nonce, data, associated_data)
84 with self._pool.acquire() as ctx:
85 return aead._decrypt(
86 backend, self, nonce, data, [associated_data], 16, ctx
87 )
89 def _check_params(
90 self,
91 nonce: bytes,
92 data: bytes,
93 associated_data: bytes,
94 ) -> None:
95 utils._check_byteslike("nonce", nonce)
96 utils._check_byteslike("data", data)
97 utils._check_byteslike("associated_data", associated_data)
98 if len(nonce) != 12:
99 raise ValueError("Nonce must be 12 bytes")
102class AESCCM:
103 _MAX_SIZE = 2**31 - 1
105 def __init__(self, key: bytes, tag_length: int = 16):
106 utils._check_byteslike("key", key)
107 if len(key) not in (16, 24, 32):
108 raise ValueError("AESCCM key must be 128, 192, or 256 bits.")
110 self._key = key
111 if not isinstance(tag_length, int):
112 raise TypeError("tag_length must be an integer")
114 if tag_length not in (4, 6, 8, 10, 12, 14, 16):
115 raise ValueError("Invalid tag_length")
117 self._tag_length = tag_length
119 if not backend.aead_cipher_supported(self):
120 raise exceptions.UnsupportedAlgorithm(
121 "AESCCM is not supported by this version of OpenSSL",
122 exceptions._Reasons.UNSUPPORTED_CIPHER,
123 )
125 @classmethod
126 def generate_key(cls, bit_length: int) -> bytes:
127 if not isinstance(bit_length, int):
128 raise TypeError("bit_length must be an integer")
130 if bit_length not in (128, 192, 256):
131 raise ValueError("bit_length must be 128, 192, or 256")
133 return os.urandom(bit_length // 8)
135 def encrypt(
136 self,
137 nonce: bytes,
138 data: bytes,
139 associated_data: bytes | None,
140 ) -> bytes:
141 if associated_data is None:
142 associated_data = b""
144 if len(data) > self._MAX_SIZE or len(associated_data) > self._MAX_SIZE:
145 # This is OverflowError to match what cffi would raise
146 raise OverflowError(
147 "Data or associated data too long. Max 2**31 - 1 bytes"
148 )
150 self._check_params(nonce, data, associated_data)
151 self._validate_lengths(nonce, len(data))
152 return aead._encrypt(
153 backend, self, nonce, data, [associated_data], self._tag_length
154 )
156 def decrypt(
157 self,
158 nonce: bytes,
159 data: bytes,
160 associated_data: bytes | None,
161 ) -> bytes:
162 if associated_data is None:
163 associated_data = b""
165 self._check_params(nonce, data, associated_data)
166 return aead._decrypt(
167 backend, self, nonce, data, [associated_data], self._tag_length
168 )
170 def _validate_lengths(self, nonce: bytes, data_len: int) -> None:
171 # For information about computing this, see
172 # https://tools.ietf.org/html/rfc3610#section-2.1
173 l_val = 15 - len(nonce)
174 if 2 ** (8 * l_val) < data_len:
175 raise ValueError("Data too long for nonce")
177 def _check_params(
178 self, nonce: bytes, data: bytes, associated_data: bytes
179 ) -> None:
180 utils._check_byteslike("nonce", nonce)
181 utils._check_byteslike("data", data)
182 utils._check_byteslike("associated_data", associated_data)
183 if not 7 <= len(nonce) <= 13:
184 raise ValueError("Nonce must be between 7 and 13 bytes")
187class AESGCM:
188 _MAX_SIZE = 2**31 - 1
190 def __init__(self, key: bytes):
191 utils._check_byteslike("key", key)
192 if len(key) not in (16, 24, 32):
193 raise ValueError("AESGCM key must be 128, 192, or 256 bits.")
195 self._key = key
197 @classmethod
198 def generate_key(cls, bit_length: int) -> bytes:
199 if not isinstance(bit_length, int):
200 raise TypeError("bit_length must be an integer")
202 if bit_length not in (128, 192, 256):
203 raise ValueError("bit_length must be 128, 192, or 256")
205 return os.urandom(bit_length // 8)
207 def encrypt(
208 self,
209 nonce: bytes,
210 data: bytes,
211 associated_data: bytes | None,
212 ) -> bytes:
213 if associated_data is None:
214 associated_data = b""
216 if len(data) > self._MAX_SIZE or len(associated_data) > self._MAX_SIZE:
217 # This is OverflowError to match what cffi would raise
218 raise OverflowError(
219 "Data or associated data too long. Max 2**31 - 1 bytes"
220 )
222 self._check_params(nonce, data, associated_data)
223 return aead._encrypt(backend, self, nonce, data, [associated_data], 16)
225 def decrypt(
226 self,
227 nonce: bytes,
228 data: bytes,
229 associated_data: bytes | None,
230 ) -> bytes:
231 if associated_data is None:
232 associated_data = b""
234 self._check_params(nonce, data, associated_data)
235 return aead._decrypt(backend, self, nonce, data, [associated_data], 16)
237 def _check_params(
238 self,
239 nonce: bytes,
240 data: bytes,
241 associated_data: bytes,
242 ) -> None:
243 utils._check_byteslike("nonce", nonce)
244 utils._check_byteslike("data", data)
245 utils._check_byteslike("associated_data", associated_data)
246 if len(nonce) < 8 or len(nonce) > 128:
247 raise ValueError("Nonce must be between 8 and 128 bytes")