Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/cryptography/hazmat/primitives/ciphers/modes.py: 64%
139 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 abc
9from cryptography import utils
10from cryptography.exceptions import UnsupportedAlgorithm, _Reasons
11from cryptography.hazmat.primitives._cipheralgorithm import (
12 BlockCipherAlgorithm,
13 CipherAlgorithm,
14)
15from cryptography.hazmat.primitives.ciphers import algorithms
18class Mode(metaclass=abc.ABCMeta):
19 @property
20 @abc.abstractmethod
21 def name(self) -> str:
22 """
23 A string naming this mode (e.g. "ECB", "CBC").
24 """
26 @abc.abstractmethod
27 def validate_for_algorithm(self, algorithm: CipherAlgorithm) -> None:
28 """
29 Checks that all the necessary invariants of this (mode, algorithm)
30 combination are met.
31 """
34class ModeWithInitializationVector(Mode, metaclass=abc.ABCMeta):
35 @property
36 @abc.abstractmethod
37 def initialization_vector(self) -> bytes:
38 """
39 The value of the initialization vector for this mode as bytes.
40 """
43class ModeWithTweak(Mode, metaclass=abc.ABCMeta):
44 @property
45 @abc.abstractmethod
46 def tweak(self) -> bytes:
47 """
48 The value of the tweak for this mode as bytes.
49 """
52class ModeWithNonce(Mode, metaclass=abc.ABCMeta):
53 @property
54 @abc.abstractmethod
55 def nonce(self) -> bytes:
56 """
57 The value of the nonce for this mode as bytes.
58 """
61class ModeWithAuthenticationTag(Mode, metaclass=abc.ABCMeta):
62 @property
63 @abc.abstractmethod
64 def tag(self) -> bytes | None:
65 """
66 The value of the tag supplied to the constructor of this mode.
67 """
70def _check_aes_key_length(self: Mode, algorithm: CipherAlgorithm) -> None:
71 if algorithm.key_size > 256 and algorithm.name == "AES":
72 raise ValueError(
73 "Only 128, 192, and 256 bit keys are allowed for this AES mode"
74 )
77def _check_iv_length(
78 self: ModeWithInitializationVector, algorithm: BlockCipherAlgorithm
79) -> None:
80 if len(self.initialization_vector) * 8 != algorithm.block_size:
81 raise ValueError(
82 "Invalid IV size ({}) for {}.".format(
83 len(self.initialization_vector), self.name
84 )
85 )
88def _check_nonce_length(
89 nonce: bytes, name: str, algorithm: CipherAlgorithm
90) -> None:
91 if not isinstance(algorithm, BlockCipherAlgorithm):
92 raise UnsupportedAlgorithm(
93 f"{name} requires a block cipher algorithm",
94 _Reasons.UNSUPPORTED_CIPHER,
95 )
96 if len(nonce) * 8 != algorithm.block_size:
97 raise ValueError(f"Invalid nonce size ({len(nonce)}) for {name}.")
100def _check_iv_and_key_length(
101 self: ModeWithInitializationVector, algorithm: CipherAlgorithm
102) -> None:
103 if not isinstance(algorithm, BlockCipherAlgorithm):
104 raise UnsupportedAlgorithm(
105 f"{self} requires a block cipher algorithm",
106 _Reasons.UNSUPPORTED_CIPHER,
107 )
108 _check_aes_key_length(self, algorithm)
109 _check_iv_length(self, algorithm)
112class CBC(ModeWithInitializationVector):
113 name = "CBC"
115 def __init__(self, initialization_vector: bytes):
116 utils._check_byteslike("initialization_vector", initialization_vector)
117 self._initialization_vector = initialization_vector
119 @property
120 def initialization_vector(self) -> bytes:
121 return self._initialization_vector
123 validate_for_algorithm = _check_iv_and_key_length
126class XTS(ModeWithTweak):
127 name = "XTS"
129 def __init__(self, tweak: bytes):
130 utils._check_byteslike("tweak", tweak)
132 if len(tweak) != 16:
133 raise ValueError("tweak must be 128-bits (16 bytes)")
135 self._tweak = tweak
137 @property
138 def tweak(self) -> bytes:
139 return self._tweak
141 def validate_for_algorithm(self, algorithm: CipherAlgorithm) -> None:
142 if isinstance(algorithm, (algorithms.AES128, algorithms.AES256)):
143 raise TypeError(
144 "The AES128 and AES256 classes do not support XTS, please use "
145 "the standard AES class instead."
146 )
148 if algorithm.key_size not in (256, 512):
149 raise ValueError(
150 "The XTS specification requires a 256-bit key for AES-128-XTS"
151 " and 512-bit key for AES-256-XTS"
152 )
155class ECB(Mode):
156 name = "ECB"
158 validate_for_algorithm = _check_aes_key_length
161class OFB(ModeWithInitializationVector):
162 name = "OFB"
164 def __init__(self, initialization_vector: bytes):
165 utils._check_byteslike("initialization_vector", initialization_vector)
166 self._initialization_vector = initialization_vector
168 @property
169 def initialization_vector(self) -> bytes:
170 return self._initialization_vector
172 validate_for_algorithm = _check_iv_and_key_length
175class CFB(ModeWithInitializationVector):
176 name = "CFB"
178 def __init__(self, initialization_vector: bytes):
179 utils._check_byteslike("initialization_vector", initialization_vector)
180 self._initialization_vector = initialization_vector
182 @property
183 def initialization_vector(self) -> bytes:
184 return self._initialization_vector
186 validate_for_algorithm = _check_iv_and_key_length
189class CFB8(ModeWithInitializationVector):
190 name = "CFB8"
192 def __init__(self, initialization_vector: bytes):
193 utils._check_byteslike("initialization_vector", initialization_vector)
194 self._initialization_vector = initialization_vector
196 @property
197 def initialization_vector(self) -> bytes:
198 return self._initialization_vector
200 validate_for_algorithm = _check_iv_and_key_length
203class CTR(ModeWithNonce):
204 name = "CTR"
206 def __init__(self, nonce: bytes):
207 utils._check_byteslike("nonce", nonce)
208 self._nonce = nonce
210 @property
211 def nonce(self) -> bytes:
212 return self._nonce
214 def validate_for_algorithm(self, algorithm: CipherAlgorithm) -> None:
215 _check_aes_key_length(self, algorithm)
216 _check_nonce_length(self.nonce, self.name, algorithm)
219class GCM(ModeWithInitializationVector, ModeWithAuthenticationTag):
220 name = "GCM"
221 _MAX_ENCRYPTED_BYTES = (2**39 - 256) // 8
222 _MAX_AAD_BYTES = (2**64) // 8
224 def __init__(
225 self,
226 initialization_vector: bytes,
227 tag: bytes | None = None,
228 min_tag_length: int = 16,
229 ):
230 # OpenSSL 3.0.0 constrains GCM IVs to [64, 1024] bits inclusive
231 # This is a sane limit anyway so we'll enforce it here.
232 utils._check_byteslike("initialization_vector", initialization_vector)
233 if len(initialization_vector) < 8 or len(initialization_vector) > 128:
234 raise ValueError(
235 "initialization_vector must be between 8 and 128 bytes (64 "
236 "and 1024 bits)."
237 )
238 self._initialization_vector = initialization_vector
239 if tag is not None:
240 utils._check_bytes("tag", tag)
241 if min_tag_length < 4:
242 raise ValueError("min_tag_length must be >= 4")
243 if len(tag) < min_tag_length:
244 raise ValueError(
245 "Authentication tag must be {} bytes or longer.".format(
246 min_tag_length
247 )
248 )
249 self._tag = tag
250 self._min_tag_length = min_tag_length
252 @property
253 def tag(self) -> bytes | None:
254 return self._tag
256 @property
257 def initialization_vector(self) -> bytes:
258 return self._initialization_vector
260 def validate_for_algorithm(self, algorithm: CipherAlgorithm) -> None:
261 _check_aes_key_length(self, algorithm)
262 if not isinstance(algorithm, BlockCipherAlgorithm):
263 raise UnsupportedAlgorithm(
264 "GCM requires a block cipher algorithm",
265 _Reasons.UNSUPPORTED_CIPHER,
266 )
267 block_size_bytes = algorithm.block_size // 8
268 if self._tag is not None and len(self._tag) > block_size_bytes:
269 raise ValueError(
270 "Authentication tag cannot be more than {} bytes.".format(
271 block_size_bytes
272 )
273 )