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