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
8
9from cryptography.hazmat.bindings._rust import openssl as rust_openssl
10from cryptography.utils import Buffer
11
12__all__ = [
13 "MD5",
14 "SHA1",
15 "SHA3_224",
16 "SHA3_256",
17 "SHA3_384",
18 "SHA3_512",
19 "SHA224",
20 "SHA256",
21 "SHA384",
22 "SHA512",
23 "SHA512_224",
24 "SHA512_256",
25 "SHAKE128",
26 "SHAKE256",
27 "SM3",
28 "BLAKE2b",
29 "BLAKE2s",
30 "ExtendableOutputFunction",
31 "Hash",
32 "HashAlgorithm",
33 "HashContext",
34 "XOFHash",
35]
36
37
38class HashAlgorithm(metaclass=abc.ABCMeta):
39 @property
40 @abc.abstractmethod
41 def name(self) -> str:
42 """
43 A string naming this algorithm (e.g. "sha256", "md5").
44 """
45
46 @property
47 @abc.abstractmethod
48 def digest_size(self) -> int:
49 """
50 The size of the resulting digest in bytes.
51 """
52
53 @property
54 @abc.abstractmethod
55 def block_size(self) -> int | None:
56 """
57 The internal block size of the hash function, or None if the hash
58 function does not use blocks internally (e.g. SHA3).
59 """
60
61
62class HashContext(metaclass=abc.ABCMeta):
63 @property
64 @abc.abstractmethod
65 def algorithm(self) -> HashAlgorithm:
66 """
67 A HashAlgorithm that will be used by this context.
68 """
69
70 @abc.abstractmethod
71 def update(self, data: Buffer) -> None:
72 """
73 Processes the provided bytes through the hash.
74 """
75
76 @abc.abstractmethod
77 def finalize(self) -> bytes:
78 """
79 Finalizes the hash context and returns the hash digest as bytes.
80 """
81
82 @abc.abstractmethod
83 def copy(self) -> HashContext:
84 """
85 Return a HashContext that is a copy of the current context.
86 """
87
88
89Hash = rust_openssl.hashes.Hash
90HashContext.register(Hash)
91
92XOFHash = rust_openssl.hashes.XOFHash
93
94
95class ExtendableOutputFunction(metaclass=abc.ABCMeta):
96 """
97 An interface for extendable output functions.
98 """
99
100
101class SHA1(HashAlgorithm):
102 name = "sha1"
103 digest_size = 20
104 block_size = 64
105
106
107class SHA512_224(HashAlgorithm): # noqa: N801
108 name = "sha512-224"
109 digest_size = 28
110 block_size = 128
111
112
113class SHA512_256(HashAlgorithm): # noqa: N801
114 name = "sha512-256"
115 digest_size = 32
116 block_size = 128
117
118
119class SHA224(HashAlgorithm):
120 name = "sha224"
121 digest_size = 28
122 block_size = 64
123
124
125class SHA256(HashAlgorithm):
126 name = "sha256"
127 digest_size = 32
128 block_size = 64
129
130
131class SHA384(HashAlgorithm):
132 name = "sha384"
133 digest_size = 48
134 block_size = 128
135
136
137class SHA512(HashAlgorithm):
138 name = "sha512"
139 digest_size = 64
140 block_size = 128
141
142
143class SHA3_224(HashAlgorithm): # noqa: N801
144 name = "sha3-224"
145 digest_size = 28
146 block_size = None
147
148
149class SHA3_256(HashAlgorithm): # noqa: N801
150 name = "sha3-256"
151 digest_size = 32
152 block_size = None
153
154
155class SHA3_384(HashAlgorithm): # noqa: N801
156 name = "sha3-384"
157 digest_size = 48
158 block_size = None
159
160
161class SHA3_512(HashAlgorithm): # noqa: N801
162 name = "sha3-512"
163 digest_size = 64
164 block_size = None
165
166
167class SHAKE128(HashAlgorithm, ExtendableOutputFunction):
168 name = "shake128"
169 block_size = None
170
171 def __init__(self, digest_size: int):
172 if not isinstance(digest_size, int):
173 raise TypeError("digest_size must be an integer")
174
175 if digest_size < 1:
176 raise ValueError("digest_size must be a positive integer")
177
178 self._digest_size = digest_size
179
180 @property
181 def digest_size(self) -> int:
182 return self._digest_size
183
184
185class SHAKE256(HashAlgorithm, ExtendableOutputFunction):
186 name = "shake256"
187 block_size = None
188
189 def __init__(self, digest_size: int):
190 if not isinstance(digest_size, int):
191 raise TypeError("digest_size must be an integer")
192
193 if digest_size < 1:
194 raise ValueError("digest_size must be a positive integer")
195
196 self._digest_size = digest_size
197
198 @property
199 def digest_size(self) -> int:
200 return self._digest_size
201
202
203class MD5(HashAlgorithm):
204 name = "md5"
205 digest_size = 16
206 block_size = 64
207
208
209class BLAKE2b(HashAlgorithm):
210 name = "blake2b"
211 _max_digest_size = 64
212 _min_digest_size = 1
213 block_size = 128
214
215 def __init__(self, digest_size: int):
216 if digest_size != 64:
217 raise ValueError("Digest size must be 64")
218
219 self._digest_size = digest_size
220
221 @property
222 def digest_size(self) -> int:
223 return self._digest_size
224
225
226class BLAKE2s(HashAlgorithm):
227 name = "blake2s"
228 block_size = 64
229 _max_digest_size = 32
230 _min_digest_size = 1
231
232 def __init__(self, digest_size: int):
233 if digest_size != 32:
234 raise ValueError("Digest size must be 32")
235
236 self._digest_size = digest_size
237
238 @property
239 def digest_size(self) -> int:
240 return self._digest_size
241
242
243class SM3(HashAlgorithm):
244 name = "sm3"
245 digest_size = 32
246 block_size = 64