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
5from __future__ import annotations
6
7import abc
8import sys
9
10from cryptography.hazmat.bindings._rust import openssl as rust_openssl
11from cryptography.utils import Buffer
12
13__all__ = [
14 "MD5",
15 "SHA1",
16 "SHA3_224",
17 "SHA3_256",
18 "SHA3_384",
19 "SHA3_512",
20 "SHA224",
21 "SHA256",
22 "SHA384",
23 "SHA512",
24 "SHA512_224",
25 "SHA512_256",
26 "SHAKE128",
27 "SHAKE256",
28 "SM3",
29 "BLAKE2b",
30 "BLAKE2s",
31 "ExtendableOutputFunction",
32 "Hash",
33 "HashAlgorithm",
34 "HashContext",
35 "XOFHash",
36]
37
38
39class HashAlgorithm(metaclass=abc.ABCMeta):
40 @property
41 @abc.abstractmethod
42 def name(self) -> str:
43 """
44 A string naming this algorithm (e.g. "sha256", "md5").
45 """
46
47 @property
48 @abc.abstractmethod
49 def digest_size(self) -> int:
50 """
51 The size of the resulting digest in bytes.
52 """
53
54 @property
55 @abc.abstractmethod
56 def block_size(self) -> int | None:
57 """
58 The internal block size of the hash function, or None if the hash
59 function does not use blocks internally (e.g. SHA3).
60 """
61
62
63class HashContext(metaclass=abc.ABCMeta):
64 @property
65 @abc.abstractmethod
66 def algorithm(self) -> HashAlgorithm:
67 """
68 A HashAlgorithm that will be used by this context.
69 """
70
71 @abc.abstractmethod
72 def update(self, data: Buffer) -> None:
73 """
74 Processes the provided bytes through the hash.
75 """
76
77 @abc.abstractmethod
78 def finalize(self) -> bytes:
79 """
80 Finalizes the hash context and returns the hash digest as bytes.
81 """
82
83 @abc.abstractmethod
84 def copy(self) -> HashContext:
85 """
86 Return a HashContext that is a copy of the current context.
87 """
88
89
90Hash = rust_openssl.hashes.Hash
91HashContext.register(Hash)
92
93XOFHash = rust_openssl.hashes.XOFHash
94
95
96class ExtendableOutputFunction(metaclass=abc.ABCMeta):
97 """
98 An interface for extendable output functions.
99 """
100
101
102class SHA1(HashAlgorithm):
103 name = "sha1"
104 digest_size = 20
105 block_size = 64
106
107 def __eq__(self, other: object) -> bool:
108 if not isinstance(other, SHA1):
109 return NotImplemented
110
111 return True
112
113
114class SHA512_224(HashAlgorithm): # noqa: N801
115 name = "sha512-224"
116 digest_size = 28
117 block_size = 128
118
119 def __eq__(self, other: object) -> bool:
120 if not isinstance(other, SHA512_224):
121 return NotImplemented
122
123 return True
124
125
126class SHA512_256(HashAlgorithm): # noqa: N801
127 name = "sha512-256"
128 digest_size = 32
129 block_size = 128
130
131 def __eq__(self, other: object) -> bool:
132 if not isinstance(other, SHA512_256):
133 return NotImplemented
134
135 return True
136
137
138class SHA224(HashAlgorithm):
139 name = "sha224"
140 digest_size = 28
141 block_size = 64
142
143 def __eq__(self, other: object) -> bool:
144 if not isinstance(other, SHA224):
145 return NotImplemented
146
147 return True
148
149
150class SHA256(HashAlgorithm):
151 name = "sha256"
152 digest_size = 32
153 block_size = 64
154
155 def __eq__(self, other: object) -> bool:
156 if not isinstance(other, SHA256):
157 return NotImplemented
158
159 return True
160
161
162class SHA384(HashAlgorithm):
163 name = "sha384"
164 digest_size = 48
165 block_size = 128
166
167 def __eq__(self, other: object) -> bool:
168 if not isinstance(other, SHA384):
169 return NotImplemented
170
171 return True
172
173
174class SHA512(HashAlgorithm):
175 name = "sha512"
176 digest_size = 64
177 block_size = 128
178
179 def __eq__(self, other: object) -> bool:
180 if not isinstance(other, SHA512):
181 return NotImplemented
182
183 return True
184
185
186class SHA3_224(HashAlgorithm): # noqa: N801
187 name = "sha3-224"
188 digest_size = 28
189 block_size = None
190
191 def __eq__(self, other: object) -> bool:
192 if not isinstance(other, SHA3_224):
193 return NotImplemented
194
195 return True
196
197
198class SHA3_256(HashAlgorithm): # noqa: N801
199 name = "sha3-256"
200 digest_size = 32
201 block_size = None
202
203 def __eq__(self, other: object) -> bool:
204 if not isinstance(other, SHA3_256):
205 return NotImplemented
206
207 return True
208
209
210class SHA3_384(HashAlgorithm): # noqa: N801
211 name = "sha3-384"
212 digest_size = 48
213 block_size = None
214
215 def __eq__(self, other: object) -> bool:
216 if not isinstance(other, SHA3_384):
217 return NotImplemented
218
219 return True
220
221
222class SHA3_512(HashAlgorithm): # noqa: N801
223 name = "sha3-512"
224 digest_size = 64
225 block_size = None
226
227 def __eq__(self, other: object) -> bool:
228 if not isinstance(other, SHA3_512):
229 return NotImplemented
230
231 return True
232
233
234class SHAKE128(HashAlgorithm, ExtendableOutputFunction):
235 name = "shake128"
236 block_size = None
237
238 def __init__(self, digest_size: int):
239 if not isinstance(digest_size, int):
240 raise TypeError("digest_size must be an integer")
241
242 if digest_size < 1:
243 raise ValueError("digest_size must be a positive integer")
244
245 self._digest_size = digest_size
246
247 def __eq__(self, other: object) -> bool:
248 if not isinstance(other, SHAKE128):
249 return NotImplemented
250
251 return self._digest_size == other._digest_size
252
253 @property
254 def digest_size(self) -> int:
255 return self._digest_size
256
257 @classmethod
258 def xof(cls):
259 return cls(sys.maxsize)
260
261
262class SHAKE256(HashAlgorithm, ExtendableOutputFunction):
263 name = "shake256"
264 block_size = None
265
266 def __init__(self, digest_size: int):
267 if not isinstance(digest_size, int):
268 raise TypeError("digest_size must be an integer")
269
270 if digest_size < 1:
271 raise ValueError("digest_size must be a positive integer")
272
273 self._digest_size = digest_size
274
275 def __eq__(self, other: object) -> bool:
276 if not isinstance(other, SHAKE256):
277 return NotImplemented
278
279 return self._digest_size == other._digest_size
280
281 @property
282 def digest_size(self) -> int:
283 return self._digest_size
284
285 @classmethod
286 def xof(cls):
287 return cls(sys.maxsize)
288
289
290class MD5(HashAlgorithm):
291 name = "md5"
292 digest_size = 16
293 block_size = 64
294
295 def __eq__(self, other: object) -> bool:
296 if not isinstance(other, MD5):
297 return NotImplemented
298
299 return True
300
301
302class BLAKE2b(HashAlgorithm):
303 name = "blake2b"
304 _max_digest_size = 64
305 _min_digest_size = 1
306 block_size = 128
307
308 def __init__(self, digest_size: int):
309 if digest_size != 64:
310 raise ValueError("Digest size must be 64")
311
312 self._digest_size = digest_size
313
314 def __eq__(self, other: object) -> bool:
315 if not isinstance(other, BLAKE2b):
316 return NotImplemented
317
318 return self._digest_size == other._digest_size
319
320 @property
321 def digest_size(self) -> int:
322 return self._digest_size
323
324
325class BLAKE2s(HashAlgorithm):
326 name = "blake2s"
327 block_size = 64
328 _max_digest_size = 32
329 _min_digest_size = 1
330
331 def __init__(self, digest_size: int):
332 if digest_size != 32:
333 raise ValueError("Digest size must be 32")
334
335 self._digest_size = digest_size
336
337 def __eq__(self, other: object) -> bool:
338 if not isinstance(other, BLAKE2s):
339 return NotImplemented
340
341 return self._digest_size == other._digest_size
342
343 @property
344 def digest_size(self) -> int:
345 return self._digest_size
346
347
348class SM3(HashAlgorithm):
349 name = "sm3"
350 digest_size = 32
351 block_size = 64
352
353 def __eq__(self, other: object) -> bool:
354 if not isinstance(other, SM3):
355 return NotImplemented
356
357 return True