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