summaryrefslogtreecommitdiffstats
path: root/lib/Crypto/Hash
diff options
context:
space:
mode:
authorxiubuzhe <xiubuzhe@sina.com>2023-10-08 20:59:00 +0800
committerxiubuzhe <xiubuzhe@sina.com>2023-10-08 20:59:00 +0800
commit1dac2263372df2b85db5d029a45721fa158a5c9d (patch)
tree0365f9c57df04178a726d7584ca6a6b955a7ce6a /lib/Crypto/Hash
parentb494be364bb39e1de128ada7dc576a729d99907e (diff)
downloadsunhpc-1dac2263372df2b85db5d029a45721fa158a5c9d.tar.gz
sunhpc-1dac2263372df2b85db5d029a45721fa158a5c9d.tar.bz2
sunhpc-1dac2263372df2b85db5d029a45721fa158a5c9d.zip
first add files
Diffstat (limited to 'lib/Crypto/Hash')
-rw-r--r--lib/Crypto/Hash/BLAKE2b.py247
-rw-r--r--lib/Crypto/Hash/BLAKE2b.pyi32
-rw-r--r--lib/Crypto/Hash/BLAKE2s.py247
-rw-r--r--lib/Crypto/Hash/BLAKE2s.pyi26
-rw-r--r--lib/Crypto/Hash/CMAC.py302
-rw-r--r--lib/Crypto/Hash/CMAC.pyi30
-rw-r--r--lib/Crypto/Hash/HMAC.py213
-rw-r--r--lib/Crypto/Hash/HMAC.pyi25
-rw-r--r--lib/Crypto/Hash/KMAC128.py179
-rw-r--r--lib/Crypto/Hash/KMAC128.pyi33
-rw-r--r--lib/Crypto/Hash/KMAC256.py74
-rw-r--r--lib/Crypto/Hash/KMAC256.pyi10
-rw-r--r--lib/Crypto/Hash/KangarooTwelve.py262
-rw-r--r--lib/Crypto/Hash/KangarooTwelve.pyi16
-rw-r--r--lib/Crypto/Hash/MD2.py166
-rw-r--r--lib/Crypto/Hash/MD2.pyi19
-rw-r--r--lib/Crypto/Hash/MD4.py185
-rw-r--r--lib/Crypto/Hash/MD4.pyi19
-rw-r--r--lib/Crypto/Hash/MD5.py184
-rw-r--r--lib/Crypto/Hash/MD5.pyi19
-rw-r--r--lib/Crypto/Hash/Poly1305.py217
-rw-r--r--lib/Crypto/Hash/Poly1305.pyi24
-rw-r--r--lib/Crypto/Hash/RIPEMD.py26
-rw-r--r--lib/Crypto/Hash/RIPEMD.pyi3
-rw-r--r--lib/Crypto/Hash/RIPEMD160.py169
-rw-r--r--lib/Crypto/Hash/RIPEMD160.pyi19
-rw-r--r--lib/Crypto/Hash/SHA.py24
-rw-r--r--lib/Crypto/Hash/SHA.pyi4
-rw-r--r--lib/Crypto/Hash/SHA1.py185
-rw-r--r--lib/Crypto/Hash/SHA1.pyi19
-rw-r--r--lib/Crypto/Hash/SHA224.py186
-rw-r--r--lib/Crypto/Hash/SHA224.pyi19
-rw-r--r--lib/Crypto/Hash/SHA256.py185
-rw-r--r--lib/Crypto/Hash/SHA256.pyi18
-rw-r--r--lib/Crypto/Hash/SHA384.py186
-rw-r--r--lib/Crypto/Hash/SHA384.pyi19
-rw-r--r--lib/Crypto/Hash/SHA3_224.py174
-rw-r--r--lib/Crypto/Hash/SHA3_224.pyi19
-rw-r--r--lib/Crypto/Hash/SHA3_256.py174
-rw-r--r--lib/Crypto/Hash/SHA3_256.pyi19
-rw-r--r--lib/Crypto/Hash/SHA3_384.py179
-rw-r--r--lib/Crypto/Hash/SHA3_384.pyi19
-rw-r--r--lib/Crypto/Hash/SHA3_512.py174
-rw-r--r--lib/Crypto/Hash/SHA3_512.pyi19
-rw-r--r--lib/Crypto/Hash/SHA512.py204
-rw-r--r--lib/Crypto/Hash/SHA512.pyi22
-rw-r--r--lib/Crypto/Hash/SHAKE128.py129
-rw-r--r--lib/Crypto/Hash/SHAKE128.pyi13
-rw-r--r--lib/Crypto/Hash/SHAKE256.py130
-rw-r--r--lib/Crypto/Hash/SHAKE256.pyi13
-rw-r--r--lib/Crypto/Hash/TupleHash128.py138
-rw-r--r--lib/Crypto/Hash/TupleHash128.pyi22
-rw-r--r--lib/Crypto/Hash/TupleHash256.py73
-rw-r--r--lib/Crypto/Hash/TupleHash256.pyi5
-rwxr-xr-xlib/Crypto/Hash/_BLAKE2b.abi3.sobin0 -> 21888 bytes
-rwxr-xr-xlib/Crypto/Hash/_BLAKE2s.abi3.sobin0 -> 21712 bytes
-rwxr-xr-xlib/Crypto/Hash/_MD2.abi3.sobin0 -> 20128 bytes
-rwxr-xr-xlib/Crypto/Hash/_MD4.abi3.sobin0 -> 25576 bytes
-rwxr-xr-xlib/Crypto/Hash/_MD5.abi3.sobin0 -> 31704 bytes
-rwxr-xr-xlib/Crypto/Hash/_RIPEMD160.abi3.sobin0 -> 55608 bytes
-rwxr-xr-xlib/Crypto/Hash/_SHA1.abi3.sobin0 -> 74416 bytes
-rwxr-xr-xlib/Crypto/Hash/_SHA224.abi3.sobin0 -> 43792 bytes
-rwxr-xr-xlib/Crypto/Hash/_SHA256.abi3.sobin0 -> 43872 bytes
-rwxr-xr-xlib/Crypto/Hash/_SHA384.abi3.sobin0 -> 50520 bytes
-rwxr-xr-xlib/Crypto/Hash/_SHA512.abi3.sobin0 -> 50624 bytes
-rw-r--r--lib/Crypto/Hash/__init__.py24
-rw-r--r--lib/Crypto/Hash/__init__.pyi0
-rwxr-xr-xlib/Crypto/Hash/_ghash_clmul.abi3.sobin0 -> 50160 bytes
-rwxr-xr-xlib/Crypto/Hash/_ghash_portable.abi3.sobin0 -> 17432 bytes
-rwxr-xr-xlib/Crypto/Hash/_keccak.abi3.sobin0 -> 35064 bytes
-rwxr-xr-xlib/Crypto/Hash/_poly1305.abi3.sobin0 -> 33360 bytes
-rw-r--r--lib/Crypto/Hash/cSHAKE128.py187
-rw-r--r--lib/Crypto/Hash/cSHAKE128.pyi14
-rw-r--r--lib/Crypto/Hash/cSHAKE256.py56
-rw-r--r--lib/Crypto/Hash/cSHAKE256.pyi8
-rw-r--r--lib/Crypto/Hash/keccak.py181
-rw-r--r--lib/Crypto/Hash/keccak.pyi23
77 files changed, 5610 insertions, 0 deletions
diff --git a/lib/Crypto/Hash/BLAKE2b.py b/lib/Crypto/Hash/BLAKE2b.py
new file mode 100644
index 0000000..a00e0b4
--- /dev/null
+++ b/lib/Crypto/Hash/BLAKE2b.py
@@ -0,0 +1,247 @@
+# ===================================================================
+#
+# Copyright (c) 2014, Legrandin <helderijs@gmail.com>
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+# ===================================================================
+
+from binascii import unhexlify
+
+from Crypto.Util.py3compat import bord, tobytes
+
+from Crypto.Random import get_random_bytes
+from Crypto.Util._raw_api import (load_pycryptodome_raw_lib,
+ VoidPointer, SmartPointer,
+ create_string_buffer,
+ get_raw_buffer, c_size_t,
+ c_uint8_ptr)
+
+_raw_blake2b_lib = load_pycryptodome_raw_lib("Crypto.Hash._BLAKE2b",
+ """
+ int blake2b_init(void **state,
+ const uint8_t *key,
+ size_t key_size,
+ size_t digest_size);
+ int blake2b_destroy(void *state);
+ int blake2b_update(void *state,
+ const uint8_t *buf,
+ size_t len);
+ int blake2b_digest(const void *state,
+ uint8_t digest[64]);
+ int blake2b_copy(const void *src, void *dst);
+ """)
+
+
+class BLAKE2b_Hash(object):
+ """A BLAKE2b hash object.
+ Do not instantiate directly. Use the :func:`new` function.
+
+ :ivar oid: ASN.1 Object ID
+ :vartype oid: string
+
+ :ivar block_size: the size in bytes of the internal message block,
+ input to the compression function
+ :vartype block_size: integer
+
+ :ivar digest_size: the size in bytes of the resulting hash
+ :vartype digest_size: integer
+ """
+
+ # The internal block size of the hash algorithm in bytes.
+ block_size = 64
+
+ def __init__(self, data, key, digest_bytes, update_after_digest):
+
+ # The size of the resulting hash in bytes.
+ self.digest_size = digest_bytes
+
+ self._update_after_digest = update_after_digest
+ self._digest_done = False
+
+ # See https://tools.ietf.org/html/rfc7693
+ if digest_bytes in (20, 32, 48, 64) and not key:
+ self.oid = "1.3.6.1.4.1.1722.12.2.1." + str(digest_bytes)
+
+ state = VoidPointer()
+ result = _raw_blake2b_lib.blake2b_init(state.address_of(),
+ c_uint8_ptr(key),
+ c_size_t(len(key)),
+ c_size_t(digest_bytes)
+ )
+ if result:
+ raise ValueError("Error %d while instantiating BLAKE2b" % result)
+ self._state = SmartPointer(state.get(),
+ _raw_blake2b_lib.blake2b_destroy)
+ if data:
+ self.update(data)
+
+
+ def update(self, data):
+ """Continue hashing of a message by consuming the next chunk of data.
+
+ Args:
+ data (bytes/bytearray/memoryview): The next chunk of the message being hashed.
+ """
+
+ if self._digest_done and not self._update_after_digest:
+ raise TypeError("You can only call 'digest' or 'hexdigest' on this object")
+
+ result = _raw_blake2b_lib.blake2b_update(self._state.get(),
+ c_uint8_ptr(data),
+ c_size_t(len(data)))
+ if result:
+ raise ValueError("Error %d while hashing BLAKE2b data" % result)
+ return self
+
+
+ def digest(self):
+ """Return the **binary** (non-printable) digest of the message that has been hashed so far.
+
+ :return: The hash digest, computed over the data processed so far.
+ Binary form.
+ :rtype: byte string
+ """
+
+ bfr = create_string_buffer(64)
+ result = _raw_blake2b_lib.blake2b_digest(self._state.get(),
+ bfr)
+ if result:
+ raise ValueError("Error %d while creating BLAKE2b digest" % result)
+
+ self._digest_done = True
+
+ return get_raw_buffer(bfr)[:self.digest_size]
+
+
+ def hexdigest(self):
+ """Return the **printable** digest of the message that has been hashed so far.
+
+ :return: The hash digest, computed over the data processed so far.
+ Hexadecimal encoded.
+ :rtype: string
+ """
+
+ return "".join(["%02x" % bord(x) for x in tuple(self.digest())])
+
+
+ def verify(self, mac_tag):
+ """Verify that a given **binary** MAC (computed by another party)
+ is valid.
+
+ Args:
+ mac_tag (bytes/bytearray/memoryview): the expected MAC of the message.
+
+ Raises:
+ ValueError: if the MAC does not match. It means that the message
+ has been tampered with or that the MAC key is incorrect.
+ """
+
+ secret = get_random_bytes(16)
+
+ mac1 = new(digest_bits=160, key=secret, data=mac_tag)
+ mac2 = new(digest_bits=160, key=secret, data=self.digest())
+
+ if mac1.digest() != mac2.digest():
+ raise ValueError("MAC check failed")
+
+
+ def hexverify(self, hex_mac_tag):
+ """Verify that a given **printable** MAC (computed by another party)
+ is valid.
+
+ Args:
+ hex_mac_tag (string): the expected MAC of the message, as a hexadecimal string.
+
+ Raises:
+ ValueError: if the MAC does not match. It means that the message
+ has been tampered with or that the MAC key is incorrect.
+ """
+
+ self.verify(unhexlify(tobytes(hex_mac_tag)))
+
+
+ def new(self, **kwargs):
+ """Return a new instance of a BLAKE2b hash object.
+ See :func:`new`.
+ """
+
+ if "digest_bytes" not in kwargs and "digest_bits" not in kwargs:
+ kwargs["digest_bytes"] = self.digest_size
+
+ return new(**kwargs)
+
+
+def new(**kwargs):
+ """Create a new hash object.
+
+ Args:
+ data (bytes/bytearray/memoryview):
+ Optional. The very first chunk of the message to hash.
+ It is equivalent to an early call to :meth:`BLAKE2b_Hash.update`.
+ digest_bytes (integer):
+ Optional. The size of the digest, in bytes (1 to 64). Default is 64.
+ digest_bits (integer):
+ Optional and alternative to ``digest_bytes``.
+ The size of the digest, in bits (8 to 512, in steps of 8).
+ Default is 512.
+ key (bytes/bytearray/memoryview):
+ Optional. The key to use to compute the MAC (1 to 64 bytes).
+ If not specified, no key will be used.
+ update_after_digest (boolean):
+ Optional. By default, a hash object cannot be updated anymore after
+ the digest is computed. When this flag is ``True``, such check
+ is no longer enforced.
+
+ Returns:
+ A :class:`BLAKE2b_Hash` hash object
+ """
+
+ data = kwargs.pop("data", None)
+ update_after_digest = kwargs.pop("update_after_digest", False)
+
+ digest_bytes = kwargs.pop("digest_bytes", None)
+ digest_bits = kwargs.pop("digest_bits", None)
+ if None not in (digest_bytes, digest_bits):
+ raise TypeError("Only one digest parameter must be provided")
+ if (None, None) == (digest_bytes, digest_bits):
+ digest_bytes = 64
+ if digest_bytes is not None:
+ if not (1 <= digest_bytes <= 64):
+ raise ValueError("'digest_bytes' not in range 1..64")
+ else:
+ if not (8 <= digest_bits <= 512) or (digest_bits % 8):
+ raise ValueError("'digest_bytes' not in range 8..512, "
+ "with steps of 8")
+ digest_bytes = digest_bits // 8
+
+ key = kwargs.pop("key", b"")
+ if len(key) > 64:
+ raise ValueError("BLAKE2s key cannot exceed 64 bytes")
+
+ if kwargs:
+ raise TypeError("Unknown parameters: " + str(kwargs))
+
+ return BLAKE2b_Hash(data, key, digest_bytes, update_after_digest)
diff --git a/lib/Crypto/Hash/BLAKE2b.pyi b/lib/Crypto/Hash/BLAKE2b.pyi
new file mode 100644
index 0000000..d37c374
--- /dev/null
+++ b/lib/Crypto/Hash/BLAKE2b.pyi
@@ -0,0 +1,32 @@
+from typing import Any, Union
+from types import ModuleType
+
+Buffer = Union[bytes, bytearray, memoryview]
+
+class BLAKE2b_Hash(object):
+ block_size: int
+ digest_size: int
+ oid: str
+
+ def __init__(self,
+ data: Buffer,
+ key: Buffer,
+ digest_bytes: bytes,
+ update_after_digest: bool) -> None: ...
+ def update(self, data: Buffer) -> BLAKE2b_Hash: ...
+ def digest(self) -> bytes: ...
+ def hexdigest(self) -> str: ...
+ def verify(self, mac_tag: Buffer) -> None: ...
+ def hexverify(self, hex_mac_tag: str) -> None: ...
+ def new(self,
+ data: Buffer = ...,
+ digest_bytes: int = ...,
+ digest_bits: int = ...,
+ key: Buffer = ...,
+ update_after_digest: bool = ...) -> BLAKE2b_Hash: ...
+
+def new(data: Buffer = ...,
+ digest_bytes: int = ...,
+ digest_bits: int = ...,
+ key: Buffer = ...,
+ update_after_digest: bool = ...) -> BLAKE2b_Hash: ...
diff --git a/lib/Crypto/Hash/BLAKE2s.py b/lib/Crypto/Hash/BLAKE2s.py
new file mode 100644
index 0000000..9b25c4a
--- /dev/null
+++ b/lib/Crypto/Hash/BLAKE2s.py
@@ -0,0 +1,247 @@
+# ===================================================================
+#
+# Copyright (c) 2014, Legrandin <helderijs@gmail.com>
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+# ===================================================================
+
+from binascii import unhexlify
+
+from Crypto.Util.py3compat import bord, tobytes
+
+from Crypto.Random import get_random_bytes
+from Crypto.Util._raw_api import (load_pycryptodome_raw_lib,
+ VoidPointer, SmartPointer,
+ create_string_buffer,
+ get_raw_buffer, c_size_t,
+ c_uint8_ptr)
+
+_raw_blake2s_lib = load_pycryptodome_raw_lib("Crypto.Hash._BLAKE2s",
+ """
+ int blake2s_init(void **state,
+ const uint8_t *key,
+ size_t key_size,
+ size_t digest_size);
+ int blake2s_destroy(void *state);
+ int blake2s_update(void *state,
+ const uint8_t *buf,
+ size_t len);
+ int blake2s_digest(const void *state,
+ uint8_t digest[32]);
+ int blake2s_copy(const void *src, void *dst);
+ """)
+
+
+class BLAKE2s_Hash(object):
+ """A BLAKE2s hash object.
+ Do not instantiate directly. Use the :func:`new` function.
+
+ :ivar oid: ASN.1 Object ID
+ :vartype oid: string
+
+ :ivar block_size: the size in bytes of the internal message block,
+ input to the compression function
+ :vartype block_size: integer
+
+ :ivar digest_size: the size in bytes of the resulting hash
+ :vartype digest_size: integer
+ """
+
+ # The internal block size of the hash algorithm in bytes.
+ block_size = 32
+
+ def __init__(self, data, key, digest_bytes, update_after_digest):
+
+ # The size of the resulting hash in bytes.
+ self.digest_size = digest_bytes
+
+ self._update_after_digest = update_after_digest
+ self._digest_done = False
+
+ # See https://tools.ietf.org/html/rfc7693
+ if digest_bytes in (16, 20, 28, 32) and not key:
+ self.oid = "1.3.6.1.4.1.1722.12.2.2." + str(digest_bytes)
+
+ state = VoidPointer()
+ result = _raw_blake2s_lib.blake2s_init(state.address_of(),
+ c_uint8_ptr(key),
+ c_size_t(len(key)),
+ c_size_t(digest_bytes)
+ )
+ if result:
+ raise ValueError("Error %d while instantiating BLAKE2s" % result)
+ self._state = SmartPointer(state.get(),
+ _raw_blake2s_lib.blake2s_destroy)
+ if data:
+ self.update(data)
+
+
+ def update(self, data):
+ """Continue hashing of a message by consuming the next chunk of data.
+
+ Args:
+ data (byte string/byte array/memoryview): The next chunk of the message being hashed.
+ """
+
+ if self._digest_done and not self._update_after_digest:
+ raise TypeError("You can only call 'digest' or 'hexdigest' on this object")
+
+ result = _raw_blake2s_lib.blake2s_update(self._state.get(),
+ c_uint8_ptr(data),
+ c_size_t(len(data)))
+ if result:
+ raise ValueError("Error %d while hashing BLAKE2s data" % result)
+ return self
+
+
+ def digest(self):
+ """Return the **binary** (non-printable) digest of the message that has been hashed so far.
+
+ :return: The hash digest, computed over the data processed so far.
+ Binary form.
+ :rtype: byte string
+ """
+
+ bfr = create_string_buffer(32)
+ result = _raw_blake2s_lib.blake2s_digest(self._state.get(),
+ bfr)
+ if result:
+ raise ValueError("Error %d while creating BLAKE2s digest" % result)
+
+ self._digest_done = True
+
+ return get_raw_buffer(bfr)[:self.digest_size]
+
+
+ def hexdigest(self):
+ """Return the **printable** digest of the message that has been hashed so far.
+
+ :return: The hash digest, computed over the data processed so far.
+ Hexadecimal encoded.
+ :rtype: string
+ """
+
+ return "".join(["%02x" % bord(x) for x in tuple(self.digest())])
+
+
+ def verify(self, mac_tag):
+ """Verify that a given **binary** MAC (computed by another party)
+ is valid.
+
+ Args:
+ mac_tag (byte string/byte array/memoryview): the expected MAC of the message.
+
+ Raises:
+ ValueError: if the MAC does not match. It means that the message
+ has been tampered with or that the MAC key is incorrect.
+ """
+
+ secret = get_random_bytes(16)
+
+ mac1 = new(digest_bits=160, key=secret, data=mac_tag)
+ mac2 = new(digest_bits=160, key=secret, data=self.digest())
+
+ if mac1.digest() != mac2.digest():
+ raise ValueError("MAC check failed")
+
+
+ def hexverify(self, hex_mac_tag):
+ """Verify that a given **printable** MAC (computed by another party)
+ is valid.
+
+ Args:
+ hex_mac_tag (string): the expected MAC of the message, as a hexadecimal string.
+
+ Raises:
+ ValueError: if the MAC does not match. It means that the message
+ has been tampered with or that the MAC key is incorrect.
+ """
+
+ self.verify(unhexlify(tobytes(hex_mac_tag)))
+
+
+ def new(self, **kwargs):
+ """Return a new instance of a BLAKE2s hash object.
+ See :func:`new`.
+ """
+
+ if "digest_bytes" not in kwargs and "digest_bits" not in kwargs:
+ kwargs["digest_bytes"] = self.digest_size
+
+ return new(**kwargs)
+
+
+def new(**kwargs):
+ """Create a new hash object.
+
+ Args:
+ data (byte string/byte array/memoryview):
+ Optional. The very first chunk of the message to hash.
+ It is equivalent to an early call to :meth:`BLAKE2s_Hash.update`.
+ digest_bytes (integer):
+ Optional. The size of the digest, in bytes (1 to 32). Default is 32.
+ digest_bits (integer):
+ Optional and alternative to ``digest_bytes``.
+ The size of the digest, in bits (8 to 256, in steps of 8).
+ Default is 256.
+ key (byte string):
+ Optional. The key to use to compute the MAC (1 to 64 bytes).
+ If not specified, no key will be used.
+ update_after_digest (boolean):
+ Optional. By default, a hash object cannot be updated anymore after
+ the digest is computed. When this flag is ``True``, such check
+ is no longer enforced.
+
+ Returns:
+ A :class:`BLAKE2s_Hash` hash object
+ """
+
+ data = kwargs.pop("data", None)
+ update_after_digest = kwargs.pop("update_after_digest", False)
+
+ digest_bytes = kwargs.pop("digest_bytes", None)
+ digest_bits = kwargs.pop("digest_bits", None)
+ if None not in (digest_bytes, digest_bits):
+ raise TypeError("Only one digest parameter must be provided")
+ if (None, None) == (digest_bytes, digest_bits):
+ digest_bytes = 32
+ if digest_bytes is not None:
+ if not (1 <= digest_bytes <= 32):
+ raise ValueError("'digest_bytes' not in range 1..32")
+ else:
+ if not (8 <= digest_bits <= 256) or (digest_bits % 8):
+ raise ValueError("'digest_bytes' not in range 8..256, "
+ "with steps of 8")
+ digest_bytes = digest_bits // 8
+
+ key = kwargs.pop("key", b"")
+ if len(key) > 32:
+ raise ValueError("BLAKE2s key cannot exceed 32 bytes")
+
+ if kwargs:
+ raise TypeError("Unknown parameters: " + str(kwargs))
+
+ return BLAKE2s_Hash(data, key, digest_bytes, update_after_digest)
diff --git a/lib/Crypto/Hash/BLAKE2s.pyi b/lib/Crypto/Hash/BLAKE2s.pyi
new file mode 100644
index 0000000..374b3a4
--- /dev/null
+++ b/lib/Crypto/Hash/BLAKE2s.pyi
@@ -0,0 +1,26 @@
+from typing import Any, Union
+
+Buffer = Union[bytes, bytearray, memoryview]
+
+class BLAKE2s_Hash(object):
+ block_size: int
+ digest_size: int
+ oid: str
+
+ def __init__(self,
+ data: Buffer,
+ key: Buffer,
+ digest_bytes: bytes,
+ update_after_digest: bool) -> None: ...
+ def update(self, data: Buffer) -> BLAKE2s_Hash: ...
+ def digest(self) -> bytes: ...
+ def hexdigest(self) -> str: ...
+ def verify(self, mac_tag: Buffer) -> None: ...
+ def hexverify(self, hex_mac_tag: str) -> None: ...
+ def new(self, **kwargs: Any) -> BLAKE2s_Hash: ...
+
+def new(data: Buffer = ...,
+ digest_bytes: int = ...,
+ digest_bits: int = ...,
+ key: Buffer = ...,
+ update_after_digest: bool = ...) -> BLAKE2s_Hash: ...
diff --git a/lib/Crypto/Hash/CMAC.py b/lib/Crypto/Hash/CMAC.py
new file mode 100644
index 0000000..7585617
--- /dev/null
+++ b/lib/Crypto/Hash/CMAC.py
@@ -0,0 +1,302 @@
+# -*- coding: utf-8 -*-
+#
+# Hash/CMAC.py - Implements the CMAC algorithm
+#
+# ===================================================================
+# The contents of this file are dedicated to the public domain. To
+# the extent that dedication to the public domain is not available,
+# everyone is granted a worldwide, perpetual, royalty-free,
+# non-exclusive license to exercise all rights associated with the
+# contents of this file for any purpose whatsoever.
+# No rights are reserved.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+# ===================================================================
+
+from binascii import unhexlify
+
+from Crypto.Hash import BLAKE2s
+from Crypto.Util.strxor import strxor
+from Crypto.Util.number import long_to_bytes, bytes_to_long
+from Crypto.Util.py3compat import bord, tobytes, _copy_bytes
+from Crypto.Random import get_random_bytes
+
+
+# The size of the authentication tag produced by the MAC.
+digest_size = None
+
+
+def _shift_bytes(bs, xor_lsb=0):
+ num = (bytes_to_long(bs) << 1) ^ xor_lsb
+ return long_to_bytes(num, len(bs))[-len(bs):]
+
+
+class CMAC(object):
+ """A CMAC hash object.
+ Do not instantiate directly. Use the :func:`new` function.
+
+ :ivar digest_size: the size in bytes of the resulting MAC tag
+ :vartype digest_size: integer
+ """
+
+ digest_size = None
+
+ def __init__(self, key, msg, ciphermod, cipher_params, mac_len,
+ update_after_digest):
+
+ self.digest_size = mac_len
+
+ self._key = _copy_bytes(None, None, key)
+ self._factory = ciphermod
+ self._cipher_params = cipher_params
+ self._block_size = bs = ciphermod.block_size
+ self._mac_tag = None
+ self._update_after_digest = update_after_digest
+
+ # Section 5.3 of NIST SP 800 38B and Appendix B
+ if bs == 8:
+ const_Rb = 0x1B
+ self._max_size = 8 * (2 ** 21)
+ elif bs == 16:
+ const_Rb = 0x87
+ self._max_size = 16 * (2 ** 48)
+ else:
+ raise TypeError("CMAC requires a cipher with a block size"
+ " of 8 or 16 bytes, not %d" % bs)
+
+ # Compute sub-keys
+ zero_block = b'\x00' * bs
+ self._ecb = ciphermod.new(key,
+ ciphermod.MODE_ECB,
+ **self._cipher_params)
+ L = self._ecb.encrypt(zero_block)
+ if bord(L[0]) & 0x80:
+ self._k1 = _shift_bytes(L, const_Rb)
+ else:
+ self._k1 = _shift_bytes(L)
+ if bord(self._k1[0]) & 0x80:
+ self._k2 = _shift_bytes(self._k1, const_Rb)
+ else:
+ self._k2 = _shift_bytes(self._k1)
+
+ # Initialize CBC cipher with zero IV
+ self._cbc = ciphermod.new(key,
+ ciphermod.MODE_CBC,
+ zero_block,
+ **self._cipher_params)
+
+ # Cache for outstanding data to authenticate
+ self._cache = bytearray(bs)
+ self._cache_n = 0
+
+ # Last piece of ciphertext produced
+ self._last_ct = zero_block
+
+ # Last block that was encrypted with AES
+ self._last_pt = None
+
+ # Counter for total message size
+ self._data_size = 0
+
+ if msg:
+ self.update(msg)
+
+ def update(self, msg):
+ """Authenticate the next chunk of message.
+
+ Args:
+ data (byte string/byte array/memoryview): The next chunk of data
+ """
+
+ if self._mac_tag is not None and not self._update_after_digest:
+ raise TypeError("update() cannot be called after digest() or verify()")
+
+ self._data_size += len(msg)
+ bs = self._block_size
+
+ if self._cache_n > 0:
+ filler = min(bs - self._cache_n, len(msg))
+ self._cache[self._cache_n:self._cache_n+filler] = msg[:filler]
+ self._cache_n += filler
+
+ if self._cache_n < bs:
+ return self
+
+ msg = memoryview(msg)[filler:]
+ self._update(self._cache)
+ self._cache_n = 0
+
+ remain = len(msg) % bs
+ if remain > 0:
+ self._update(msg[:-remain])
+ self._cache[:remain] = msg[-remain:]
+ else:
+ self._update(msg)
+ self._cache_n = remain
+ return self
+
+ def _update(self, data_block):
+ """Update a block aligned to the block boundary"""
+
+ bs = self._block_size
+ assert len(data_block) % bs == 0
+
+ if len(data_block) == 0:
+ return
+
+ ct = self._cbc.encrypt(data_block)
+ if len(data_block) == bs:
+ second_last = self._last_ct
+ else:
+ second_last = ct[-bs*2:-bs]
+ self._last_ct = ct[-bs:]
+ self._last_pt = strxor(second_last, data_block[-bs:])
+
+ def copy(self):
+ """Return a copy ("clone") of the CMAC object.
+
+ The copy will have the same internal state as the original CMAC
+ object.
+ This can be used to efficiently compute the MAC tag of byte
+ strings that share a common initial substring.
+
+ :return: An :class:`CMAC`
+ """
+
+ obj = self.__new__(CMAC)
+ obj.__dict__ = self.__dict__.copy()
+ obj._cbc = self._factory.new(self._key,
+ self._factory.MODE_CBC,
+ self._last_ct,
+ **self._cipher_params)
+ obj._cache = self._cache[:]
+ obj._last_ct = self._last_ct[:]
+ return obj
+
+ def digest(self):
+ """Return the **binary** (non-printable) MAC tag of the message
+ that has been authenticated so far.
+
+ :return: The MAC tag, computed over the data processed so far.
+ Binary form.
+ :rtype: byte string
+ """
+
+ bs = self._block_size
+
+ if self._mac_tag is not None and not self._update_after_digest:
+ return self._mac_tag
+
+ if self._data_size > self._max_size:
+ raise ValueError("MAC is unsafe for this message")
+
+ if self._cache_n == 0 and self._data_size > 0:
+ # Last block was full
+ pt = strxor(self._last_pt, self._k1)
+ else:
+ # Last block is partial (or message length is zero)
+ partial = self._cache[:]
+ partial[self._cache_n:] = b'\x80' + b'\x00' * (bs - self._cache_n - 1)
+ pt = strxor(strxor(self._last_ct, partial), self._k2)
+
+ self._mac_tag = self._ecb.encrypt(pt)[:self.digest_size]
+
+ return self._mac_tag
+
+ def hexdigest(self):
+ """Return the **printable** MAC tag of the message authenticated so far.
+
+ :return: The MAC tag, computed over the data processed so far.
+ Hexadecimal encoded.
+ :rtype: string
+ """
+
+ return "".join(["%02x" % bord(x)
+ for x in tuple(self.digest())])
+
+ def verify(self, mac_tag):
+ """Verify that a given **binary** MAC (computed by another party)
+ is valid.
+
+ Args:
+ mac_tag (byte string/byte array/memoryview): the expected MAC of the message.
+
+ Raises:
+ ValueError: if the MAC does not match. It means that the message
+ has been tampered with or that the MAC key is incorrect.
+ """
+
+ secret = get_random_bytes(16)
+
+ mac1 = BLAKE2s.new(digest_bits=160, key=secret, data=mac_tag)
+ mac2 = BLAKE2s.new(digest_bits=160, key=secret, data=self.digest())
+
+ if mac1.digest() != mac2.digest():
+ raise ValueError("MAC check failed")
+
+ def hexverify(self, hex_mac_tag):
+ """Return the **printable** MAC tag of the message authenticated so far.
+
+ :return: The MAC tag, computed over the data processed so far.
+ Hexadecimal encoded.
+ :rtype: string
+ """
+
+ self.verify(unhexlify(tobytes(hex_mac_tag)))
+
+
+def new(key, msg=None, ciphermod=None, cipher_params=None, mac_len=None,
+ update_after_digest=False):
+ """Create a new MAC object.
+
+ Args:
+ key (byte string/byte array/memoryview):
+ key for the CMAC object.
+ The key must be valid for the underlying cipher algorithm.
+ For instance, it must be 16 bytes long for AES-128.
+ ciphermod (module):
+ A cipher module from :mod:`Crypto.Cipher`.
+ The cipher's block size has to be 128 bits,
+ like :mod:`Crypto.Cipher.AES`, to reduce the probability
+ of collisions.
+ msg (byte string/byte array/memoryview):
+ Optional. The very first chunk of the message to authenticate.
+ It is equivalent to an early call to `CMAC.update`. Optional.
+ cipher_params (dict):
+ Optional. A set of parameters to use when instantiating a cipher
+ object.
+ mac_len (integer):
+ Length of the MAC, in bytes.
+ It must be at least 4 bytes long.
+ The default (and recommended) length matches the size of a cipher block.
+ update_after_digest (boolean):
+ Optional. By default, a hash object cannot be updated anymore after
+ the digest is computed. When this flag is ``True``, such check
+ is no longer enforced.
+ Returns:
+ A :class:`CMAC` object
+ """
+
+ if ciphermod is None:
+ raise TypeError("ciphermod must be specified (try AES)")
+
+ cipher_params = {} if cipher_params is None else dict(cipher_params)
+
+ if mac_len is None:
+ mac_len = ciphermod.block_size
+
+ if mac_len < 4:
+ raise ValueError("MAC tag length must be at least 4 bytes long")
+
+ if mac_len > ciphermod.block_size:
+ raise ValueError("MAC tag length cannot be larger than a cipher block (%d) bytes" % ciphermod.block_size)
+
+ return CMAC(key, msg, ciphermod, cipher_params, mac_len,
+ update_after_digest)
diff --git a/lib/Crypto/Hash/CMAC.pyi b/lib/Crypto/Hash/CMAC.pyi
new file mode 100644
index 0000000..acdf055
--- /dev/null
+++ b/lib/Crypto/Hash/CMAC.pyi
@@ -0,0 +1,30 @@
+from types import ModuleType
+from typing import Union, Dict, Any
+
+Buffer = Union[bytes, bytearray, memoryview]
+
+digest_size: int
+
+class CMAC(object):
+ digest_size: int
+
+ def __init__(self,
+ key: Buffer,
+ msg: Buffer,
+ ciphermod: ModuleType,
+ cipher_params: Dict[str, Any],
+ mac_len: int, update_after_digest: bool) -> None: ...
+ def update(self, data: Buffer) -> CMAC: ...
+ def copy(self) -> CMAC: ...
+ def digest(self) -> bytes: ...
+ def hexdigest(self) -> str: ...
+ def verify(self, mac_tag: Buffer) -> None: ...
+ def hexverify(self, hex_mac_tag: str) -> None: ...
+
+
+def new(key: Buffer,
+ msg: Buffer = ...,
+ ciphermod: ModuleType = ...,
+ cipher_params: Dict[str, Any] = ...,
+ mac_len: int = ...,
+ update_after_digest: bool = ...) -> CMAC: ...
diff --git a/lib/Crypto/Hash/HMAC.py b/lib/Crypto/Hash/HMAC.py
new file mode 100644
index 0000000..e82bb9d
--- /dev/null
+++ b/lib/Crypto/Hash/HMAC.py
@@ -0,0 +1,213 @@
+#
+# HMAC.py - Implements the HMAC algorithm as described by RFC 2104.
+#
+# ===================================================================
+#
+# Copyright (c) 2014, Legrandin <helderijs@gmail.com>
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+# ===================================================================
+
+from Crypto.Util.py3compat import bord, tobytes
+
+from binascii import unhexlify
+
+from Crypto.Hash import MD5
+from Crypto.Hash import BLAKE2s
+from Crypto.Util.strxor import strxor
+from Crypto.Random import get_random_bytes
+
+__all__ = ['new', 'HMAC']
+
+
+class HMAC(object):
+ """An HMAC hash object.
+ Do not instantiate directly. Use the :func:`new` function.
+
+ :ivar digest_size: the size in bytes of the resulting MAC tag
+ :vartype digest_size: integer
+ """
+
+ def __init__(self, key, msg=b"", digestmod=None):
+
+ if digestmod is None:
+ digestmod = MD5
+
+ if msg is None:
+ msg = b""
+
+ # Size of the MAC tag
+ self.digest_size = digestmod.digest_size
+
+ self._digestmod = digestmod
+
+ if isinstance(key, memoryview):
+ key = key.tobytes()
+
+ try:
+ if len(key) <= digestmod.block_size:
+ # Step 1 or 2
+ key_0 = key + b"\x00" * (digestmod.block_size - len(key))
+ else:
+ # Step 3
+ hash_k = digestmod.new(key).digest()
+ key_0 = hash_k + b"\x00" * (digestmod.block_size - len(hash_k))
+ except AttributeError:
+ # Not all hash types have "block_size"
+ raise ValueError("Hash type incompatible to HMAC")
+
+ # Step 4
+ key_0_ipad = strxor(key_0, b"\x36" * len(key_0))
+
+ # Start step 5 and 6
+ self._inner = digestmod.new(key_0_ipad)
+ self._inner.update(msg)
+
+ # Step 7
+ key_0_opad = strxor(key_0, b"\x5c" * len(key_0))
+
+ # Start step 8 and 9
+ self._outer = digestmod.new(key_0_opad)
+
+ def update(self, msg):
+ """Authenticate the next chunk of message.
+
+ Args:
+ data (byte string/byte array/memoryview): The next chunk of data
+ """
+
+ self._inner.update(msg)
+ return self
+
+ def _pbkdf2_hmac_assist(self, first_digest, iterations):
+ """Carry out the expensive inner loop for PBKDF2-HMAC"""
+
+ result = self._digestmod._pbkdf2_hmac_assist(
+ self._inner,
+ self._outer,
+ first_digest,
+ iterations)
+ return result
+
+ def copy(self):
+ """Return a copy ("clone") of the HMAC object.
+
+ The copy will have the same internal state as the original HMAC
+ object.
+ This can be used to efficiently compute the MAC tag of byte
+ strings that share a common initial substring.
+
+ :return: An :class:`HMAC`
+ """
+
+ new_hmac = HMAC(b"fake key", digestmod=self._digestmod)
+
+ # Syncronize the state
+ new_hmac._inner = self._inner.copy()
+ new_hmac._outer = self._outer.copy()
+
+ return new_hmac
+
+ def digest(self):
+ """Return the **binary** (non-printable) MAC tag of the message
+ authenticated so far.
+
+ :return: The MAC tag digest, computed over the data processed so far.
+ Binary form.
+ :rtype: byte string
+ """
+
+ frozen_outer_hash = self._outer.copy()
+ frozen_outer_hash.update(self._inner.digest())
+ return frozen_outer_hash.digest()
+
+ def verify(self, mac_tag):
+ """Verify that a given **binary** MAC (computed by another party)
+ is valid.
+
+ Args:
+ mac_tag (byte string/byte string/memoryview): the expected MAC of the message.
+
+ Raises:
+ ValueError: if the MAC does not match. It means that the message
+ has been tampered with or that the MAC key is incorrect.
+ """
+
+ secret = get_random_bytes(16)
+
+ mac1 = BLAKE2s.new(digest_bits=160, key=secret, data=mac_tag)
+ mac2 = BLAKE2s.new(digest_bits=160, key=secret, data=self.digest())
+
+ if mac1.digest() != mac2.digest():
+ raise ValueError("MAC check failed")
+
+ def hexdigest(self):
+ """Return the **printable** MAC tag of the message authenticated so far.
+
+ :return: The MAC tag, computed over the data processed so far.
+ Hexadecimal encoded.
+ :rtype: string
+ """
+
+ return "".join(["%02x" % bord(x)
+ for x in tuple(self.digest())])
+
+ def hexverify(self, hex_mac_tag):
+ """Verify that a given **printable** MAC (computed by another party)
+ is valid.
+
+ Args:
+ hex_mac_tag (string): the expected MAC of the message,
+ as a hexadecimal string.
+
+ Raises:
+ ValueError: if the MAC does not match. It means that the message
+ has been tampered with or that the MAC key is incorrect.
+ """
+
+ self.verify(unhexlify(tobytes(hex_mac_tag)))
+
+
+def new(key, msg=b"", digestmod=None):
+ """Create a new MAC object.
+
+ Args:
+ key (bytes/bytearray/memoryview):
+ key for the MAC object.
+ It must be long enough to match the expected security level of the
+ MAC.
+ msg (bytes/bytearray/memoryview):
+ Optional. The very first chunk of the message to authenticate.
+ It is equivalent to an early call to :meth:`HMAC.update`.
+ digestmod (module):
+ The hash to use to implement the HMAC.
+ Default is :mod:`Crypto.Hash.MD5`.
+
+ Returns:
+ An :class:`HMAC` object
+ """
+
+ return HMAC(key, msg, digestmod)
diff --git a/lib/Crypto/Hash/HMAC.pyi b/lib/Crypto/Hash/HMAC.pyi
new file mode 100644
index 0000000..b577230
--- /dev/null
+++ b/lib/Crypto/Hash/HMAC.pyi
@@ -0,0 +1,25 @@
+from types import ModuleType
+from typing import Union, Dict
+
+Buffer = Union[bytes, bytearray, memoryview]
+
+digest_size: int
+
+class HMAC(object):
+ digest_size: int
+
+ def __init__(self,
+ key: Buffer,
+ msg: Buffer,
+ digestmod: ModuleType) -> None: ...
+ def update(self, msg: Buffer) -> HMAC: ...
+ def copy(self) -> HMAC: ...
+ def digest(self) -> bytes: ...
+ def hexdigest(self) -> str: ...
+ def verify(self, mac_tag: Buffer) -> None: ...
+ def hexverify(self, hex_mac_tag: str) -> None: ...
+
+
+def new(key: Buffer,
+ msg: Buffer = ...,
+ digestmod: ModuleType = ...) -> HMAC: ...
diff --git a/lib/Crypto/Hash/KMAC128.py b/lib/Crypto/Hash/KMAC128.py
new file mode 100644
index 0000000..05061fc
--- /dev/null
+++ b/lib/Crypto/Hash/KMAC128.py
@@ -0,0 +1,179 @@
+# ===================================================================
+#
+# Copyright (c) 2021, Legrandin <helderijs@gmail.com>
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+# ===================================================================
+
+from binascii import unhexlify
+
+from Crypto.Util.py3compat import bord, tobytes, is_bytes
+from Crypto.Random import get_random_bytes
+
+from . import cSHAKE128, SHA3_256
+from .cSHAKE128 import _bytepad, _encode_str, _right_encode
+
+
+class KMAC_Hash(object):
+ """A KMAC hash object.
+ Do not instantiate directly.
+ Use the :func:`new` function.
+ """
+
+ def __init__(self, data, key, mac_len, custom,
+ oid_variant, cshake, rate):
+
+ # See https://tools.ietf.org/html/rfc8702
+ self.oid = "2.16.840.1.101.3.4.2." + oid_variant
+ self.digest_size = mac_len
+
+ self._mac = None
+
+ partial_newX = _bytepad(_encode_str(tobytes(key)), rate)
+ self._cshake = cshake._new(partial_newX, custom, b"KMAC")
+
+ if data:
+ self._cshake.update(data)
+
+ def update(self, data):
+ """Authenticate the next chunk of message.
+
+ Args:
+ data (bytes/bytearray/memoryview): The next chunk of the message to
+ authenticate.
+ """
+
+ if self._mac:
+ raise TypeError("You can only call 'digest' or 'hexdigest' on this object")
+
+ self._cshake.update(data)
+ return self
+
+ def digest(self):
+ """Return the **binary** (non-printable) MAC tag of the message.
+
+ :return: The MAC tag. Binary form.
+ :rtype: byte string
+ """
+
+ if not self._mac:
+ self._cshake.update(_right_encode(self.digest_size * 8))
+ self._mac = self._cshake.read(self.digest_size)
+
+ return self._mac
+
+ def hexdigest(self):
+ """Return the **printable** MAC tag of the message.
+
+ :return: The MAC tag. Hexadecimal encoded.
+ :rtype: string
+ """
+
+ return "".join(["%02x" % bord(x) for x in tuple(self.digest())])
+
+ def verify(self, mac_tag):
+ """Verify that a given **binary** MAC (computed by another party)
+ is valid.
+
+ Args:
+ mac_tag (bytes/bytearray/memoryview): the expected MAC of the message.
+
+ Raises:
+ ValueError: if the MAC does not match. It means that the message
+ has been tampered with or that the MAC key is incorrect.
+ """
+
+ secret = get_random_bytes(16)
+
+ mac1 = SHA3_256.new(secret + mac_tag)
+ mac2 = SHA3_256.new(secret + self.digest())
+
+ if mac1.digest() != mac2.digest():
+ raise ValueError("MAC check failed")
+
+ def hexverify(self, hex_mac_tag):
+ """Verify that a given **printable** MAC (computed by another party)
+ is valid.
+
+ Args:
+ hex_mac_tag (string): the expected MAC of the message, as a hexadecimal string.
+
+ Raises:
+ ValueError: if the MAC does not match. It means that the message
+ has been tampered with or that the MAC key is incorrect.
+ """
+
+ self.verify(unhexlify(tobytes(hex_mac_tag)))
+
+ def new(self, **kwargs):
+ """Return a new instance of a KMAC hash object.
+ See :func:`new`.
+ """
+
+ if "mac_len" not in kwargs:
+ kwargs["mac_len"] = self.digest_size
+
+ return new(**kwargs)
+
+
+def new(**kwargs):
+ """Create a new KMAC128 object.
+
+ Args:
+ key (bytes/bytearray/memoryview):
+ The key to use to compute the MAC.
+ It must be at least 128 bits long (16 bytes).
+ data (bytes/bytearray/memoryview):
+ Optional. The very first chunk of the message to authenticate.
+ It is equivalent to an early call to :meth:`KMAC_Hash.update`.
+ mac_len (integer):
+ Optional. The size of the authentication tag, in bytes.
+ Default is 64. Minimum is 8.
+ custom (bytes/bytearray/memoryview):
+ Optional. A customization byte string (``S`` in SP 800-185).
+
+ Returns:
+ A :class:`KMAC_Hash` hash object
+ """
+
+ key = kwargs.pop("key", None)
+ if not is_bytes(key):
+ raise TypeError("You must pass a key to KMAC128")
+ if len(key) < 16:
+ raise ValueError("The key must be at least 128 bits long (16 bytes)")
+
+ data = kwargs.pop("data", None)
+
+ mac_len = kwargs.pop("mac_len", 64)
+ if mac_len < 8:
+ raise ValueError("'mac_len' must be 8 bytes or more")
+
+ custom = kwargs.pop("custom", b"")
+
+ if kwargs:
+ raise TypeError("Unknown parameters: " + str(kwargs))
+
+ return KMAC_Hash(data, key, mac_len, custom, "19", cSHAKE128, 168)
diff --git a/lib/Crypto/Hash/KMAC128.pyi b/lib/Crypto/Hash/KMAC128.pyi
new file mode 100644
index 0000000..8947dab
--- /dev/null
+++ b/lib/Crypto/Hash/KMAC128.pyi
@@ -0,0 +1,33 @@
+from typing import Union
+from types import ModuleType
+
+Buffer = Union[bytes, bytearray, memoryview]
+
+class KMAC_Hash(object):
+
+ def __init__(self,
+ data: Buffer,
+ key: Buffer,
+ mac_len: int,
+ custom: Buffer,
+ oid_variant: str,
+ cshake: ModuleType,
+ rate: int) -> None: ...
+
+ def update(self, data: Buffer) -> KMAC_Hash: ...
+
+ def digest(self) -> bytes: ...
+ def hexdigest(self) -> str: ...
+ def verify(self, mac_tag: Buffer) -> None: ...
+ def hexverify(self, hex_mac_tag: str) -> None: ...
+ def new(self,
+ data: Buffer = ...,
+ mac_len: int = ...,
+ key: Buffer = ...,
+ custom: Buffer = ...) -> KMAC_Hash: ...
+
+
+def new(key: Buffer,
+ data: Buffer = ...,
+ mac_len: int = ...,
+ custom: Buffer = ...) -> KMAC_Hash: ...
diff --git a/lib/Crypto/Hash/KMAC256.py b/lib/Crypto/Hash/KMAC256.py
new file mode 100644
index 0000000..2be8e2f
--- /dev/null
+++ b/lib/Crypto/Hash/KMAC256.py
@@ -0,0 +1,74 @@
+# ===================================================================
+#
+# Copyright (c) 2021, Legrandin <helderijs@gmail.com>
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+# ===================================================================
+
+from Crypto.Util.py3compat import is_bytes
+
+from .KMAC128 import KMAC_Hash
+from . import cSHAKE256
+
+
+def new(**kwargs):
+ """Create a new KMAC256 object.
+
+ Args:
+ key (bytes/bytearray/memoryview):
+ The key to use to compute the MAC.
+ It must be at least 256 bits long (32 bytes).
+ data (bytes/bytearray/memoryview):
+ Optional. The very first chunk of the message to authenticate.
+ It is equivalent to an early call to :meth:`KMAC_Hash.update`.
+ mac_len (integer):
+ Optional. The size of the authentication tag, in bytes.
+ Default is 64. Minimum is 8.
+ custom (bytes/bytearray/memoryview):
+ Optional. A customization byte string (``S`` in SP 800-185).
+
+ Returns:
+ A :class:`KMAC_Hash` hash object
+ """
+
+ key = kwargs.pop("key", None)
+ if not is_bytes(key):
+ raise TypeError("You must pass a key to KMAC256")
+ if len(key) < 32:
+ raise ValueError("The key must be at least 256 bits long (32 bytes)")
+
+ data = kwargs.pop("data", None)
+
+ mac_len = kwargs.pop("mac_len", 64)
+ if mac_len < 8:
+ raise ValueError("'mac_len' must be 8 bytes or more")
+
+ custom = kwargs.pop("custom", b"")
+
+ if kwargs:
+ raise TypeError("Unknown parameters: " + str(kwargs))
+
+ return KMAC_Hash(data, key, mac_len, custom, "20", cSHAKE256, 136)
diff --git a/lib/Crypto/Hash/KMAC256.pyi b/lib/Crypto/Hash/KMAC256.pyi
new file mode 100644
index 0000000..86cc500
--- /dev/null
+++ b/lib/Crypto/Hash/KMAC256.pyi
@@ -0,0 +1,10 @@
+from typing import Union
+
+from .KMAC128 import KMAC_Hash
+
+Buffer = Union[bytes, bytearray, memoryview]
+
+def new(key: Buffer,
+ data: Buffer = ...,
+ mac_len: int = ...,
+ custom: Buffer = ...) -> KMAC_Hash: ...
diff --git a/lib/Crypto/Hash/KangarooTwelve.py b/lib/Crypto/Hash/KangarooTwelve.py
new file mode 100644
index 0000000..f5358d4
--- /dev/null
+++ b/lib/Crypto/Hash/KangarooTwelve.py
@@ -0,0 +1,262 @@
+# ===================================================================
+#
+# Copyright (c) 2021, Legrandin <helderijs@gmail.com>
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+# ===================================================================
+
+from Crypto.Util._raw_api import (VoidPointer, SmartPointer,
+ create_string_buffer,
+ get_raw_buffer, c_size_t,
+ c_uint8_ptr, c_ubyte)
+
+from Crypto.Util.number import long_to_bytes
+from Crypto.Util.py3compat import bchr
+
+from .keccak import _raw_keccak_lib
+
+
+def _length_encode(x):
+ if x == 0:
+ return b'\x00'
+
+ S = long_to_bytes(x)
+ return S + bchr(len(S))
+
+
+# Possible states for a KangarooTwelve instance, which depend on the amount of data processed so far.
+SHORT_MSG = 1 # Still within the first 8192 bytes, but it is not certain we will exceed them.
+LONG_MSG_S0 = 2 # Still within the first 8192 bytes, and it is certain we will exceed them.
+LONG_MSG_SX = 3 # Beyond the first 8192 bytes.
+SQUEEZING = 4 # No more data to process.
+
+
+class K12_XOF(object):
+ """A KangarooTwelve hash object.
+ Do not instantiate directly.
+ Use the :func:`new` function.
+ """
+
+ def __init__(self, data, custom):
+
+ if custom == None:
+ custom = b''
+
+ self._custom = custom + _length_encode(len(custom))
+ self._state = SHORT_MSG
+ self._padding = None # Final padding is only decided in read()
+
+ # Internal hash that consumes FinalNode
+ self._hash1 = self._create_keccak()
+ self._length1 = 0
+
+ # Internal hash that produces CV_i (reset each time)
+ self._hash2 = None
+ self._length2 = 0
+
+ # Incremented by one for each 8192-byte block
+ self._ctr = 0
+
+ if data:
+ self.update(data)
+
+ def _create_keccak(self):
+ state = VoidPointer()
+ result = _raw_keccak_lib.keccak_init(state.address_of(),
+ c_size_t(32), # 32 bytes of capacity (256 bits)
+ c_ubyte(12)) # Reduced number of rounds
+ if result:
+ raise ValueError("Error %d while instantiating KangarooTwelve"
+ % result)
+ return SmartPointer(state.get(), _raw_keccak_lib.keccak_destroy)
+
+ def _update(self, data, hash_obj):
+ result = _raw_keccak_lib.keccak_absorb(hash_obj.get(),
+ c_uint8_ptr(data),
+ c_size_t(len(data)))
+ if result:
+ raise ValueError("Error %d while updating KangarooTwelve state"
+ % result)
+
+ def _squeeze(self, hash_obj, length, padding):
+ bfr = create_string_buffer(length)
+ result = _raw_keccak_lib.keccak_squeeze(hash_obj.get(),
+ bfr,
+ c_size_t(length),
+ c_ubyte(padding))
+ if result:
+ raise ValueError("Error %d while extracting from KangarooTwelve"
+ % result)
+
+ return get_raw_buffer(bfr)
+
+ def _reset(self, hash_obj):
+ result = _raw_keccak_lib.keccak_reset(hash_obj.get())
+ if result:
+ raise ValueError("Error %d while resetting KangarooTwelve state"
+ % result)
+
+ def update(self, data):
+ """Hash the next piece of data.
+
+ .. note::
+ For better performance, submit chunks with a length multiple of 8192 bytes.
+
+ Args:
+ data (byte string/byte array/memoryview): The next chunk of the
+ message to hash.
+ """
+
+ if self._state == SQUEEZING:
+ raise TypeError("You cannot call 'update' after the first 'read'")
+
+ if self._state == SHORT_MSG:
+ next_length = self._length1 + len(data)
+
+ if next_length + len(self._custom) <= 8192:
+ self._length1 = next_length
+ self._update(data, self._hash1)
+ return self
+
+ # Switch to tree hashing
+ self._state = LONG_MSG_S0
+
+ if self._state == LONG_MSG_S0:
+ data_mem = memoryview(data)
+ assert(self._length1 < 8192)
+ dtc = min(len(data), 8192 - self._length1)
+ self._update(data_mem[:dtc], self._hash1)
+ self._length1 += dtc
+
+ if self._length1 < 8192:
+ return self
+
+ # Finish hashing S_0 and start S_1
+ assert(self._length1 == 8192)
+
+ divider = b'\x03' + b'\x00' * 7
+ self._update(divider, self._hash1)
+ self._length1 += 8
+
+ self._hash2 = self._create_keccak()
+ self._length2 = 0
+ self._ctr = 1
+
+ self._state = LONG_MSG_SX
+ return self.update(data_mem[dtc:])
+
+ # LONG_MSG_SX
+ assert(self._state == LONG_MSG_SX)
+ index = 0
+ len_data = len(data)
+
+ # All iteractions could actually run in parallel
+ data_mem = memoryview(data)
+ while index < len_data:
+
+ new_index = min(index + 8192 - self._length2, len_data)
+ self._update(data_mem[index:new_index], self._hash2)
+ self._length2 += new_index - index
+ index = new_index
+
+ if self._length2 == 8192:
+ cv_i = self._squeeze(self._hash2, 32, 0x0B)
+ self._update(cv_i, self._hash1)
+ self._length1 += 32
+ self._reset(self._hash2)
+ self._length2 = 0
+ self._ctr += 1
+
+ return self
+
+ def read(self, length):
+ """
+ Produce more bytes of the digest.
+
+ .. note::
+ You cannot use :meth:`update` anymore after the first call to
+ :meth:`read`.
+
+ Args:
+ length (integer): the amount of bytes this method must return
+
+ :return: the next piece of XOF output (of the given length)
+ :rtype: byte string
+ """
+
+ custom_was_consumed = False
+
+ if self._state == SHORT_MSG:
+ self._update(self._custom, self._hash1)
+ self._padding = 0x07
+ self._state = SQUEEZING
+
+ if self._state == LONG_MSG_S0:
+ self.update(self._custom)
+ custom_was_consumed = True
+ assert(self._state == LONG_MSG_SX)
+
+ if self._state == LONG_MSG_SX:
+ if not custom_was_consumed:
+ self.update(self._custom)
+
+ # Is there still some leftover data in hash2?
+ if self._length2 > 0:
+ cv_i = self._squeeze(self._hash2, 32, 0x0B)
+ self._update(cv_i, self._hash1)
+ self._length1 += 32
+ self._reset(self._hash2)
+ self._length2 = 0
+ self._ctr += 1
+
+ trailer = _length_encode(self._ctr - 1) + b'\xFF\xFF'
+ self._update(trailer, self._hash1)
+
+ self._padding = 0x06
+ self._state = SQUEEZING
+
+ return self._squeeze(self._hash1, length, self._padding)
+
+ def new(self, data=None, custom=b''):
+ return type(self)(data, custom)
+
+
+def new(data=None, custom=None):
+ """Return a fresh instance of a KangarooTwelve object.
+
+ Args:
+ data (bytes/bytearray/memoryview):
+ Optional.
+ The very first chunk of the message to hash.
+ It is equivalent to an early call to :meth:`update`.
+ custom (bytes):
+ Optional.
+ A customization byte string.
+
+ :Return: A :class:`K12_XOF` object
+ """
+
+ return K12_XOF(data, custom)
diff --git a/lib/Crypto/Hash/KangarooTwelve.pyi b/lib/Crypto/Hash/KangarooTwelve.pyi
new file mode 100644
index 0000000..8b3fd74
--- /dev/null
+++ b/lib/Crypto/Hash/KangarooTwelve.pyi
@@ -0,0 +1,16 @@
+from typing import Union, Optional
+
+Buffer = Union[bytes, bytearray, memoryview]
+
+class K12_XOF(object):
+ def __init__(self,
+ data: Optional[Buffer] = ...,
+ custom: Optional[bytes] = ...) -> None: ...
+ def update(self, data: Buffer) -> K12_XOF: ...
+ def read(self, length: int) -> bytes: ...
+ def new(self,
+ data: Optional[Buffer] = ...,
+ custom: Optional[bytes] = ...) -> None: ...
+
+def new(data: Optional[Buffer] = ...,
+ custom: Optional[Buffer] = ...) -> K12_XOF: ...
diff --git a/lib/Crypto/Hash/MD2.py b/lib/Crypto/Hash/MD2.py
new file mode 100644
index 0000000..41decbb
--- /dev/null
+++ b/lib/Crypto/Hash/MD2.py
@@ -0,0 +1,166 @@
+# ===================================================================
+#
+# Copyright (c) 2014, Legrandin <helderijs@gmail.com>
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+# ===================================================================
+
+from Crypto.Util.py3compat import bord
+
+from Crypto.Util._raw_api import (load_pycryptodome_raw_lib,
+ VoidPointer, SmartPointer,
+ create_string_buffer,
+ get_raw_buffer, c_size_t,
+ c_uint8_ptr)
+
+_raw_md2_lib = load_pycryptodome_raw_lib(
+ "Crypto.Hash._MD2",
+ """
+ int md2_init(void **shaState);
+ int md2_destroy(void *shaState);
+ int md2_update(void *hs,
+ const uint8_t *buf,
+ size_t len);
+ int md2_digest(const void *shaState,
+ uint8_t digest[20]);
+ int md2_copy(const void *src, void *dst);
+ """)
+
+
+class MD2Hash(object):
+ """An MD2 hash object.
+ Do not instantiate directly. Use the :func:`new` function.
+
+ :ivar oid: ASN.1 Object ID
+ :vartype oid: string
+
+ :ivar block_size: the size in bytes of the internal message block,
+ input to the compression function
+ :vartype block_size: integer
+
+ :ivar digest_size: the size in bytes of the resulting hash
+ :vartype digest_size: integer
+ """
+
+ # The size of the resulting hash in bytes.
+ digest_size = 16
+ # The internal block size of the hash algorithm in bytes.
+ block_size = 16
+ # ASN.1 Object ID
+ oid = "1.2.840.113549.2.2"
+
+ def __init__(self, data=None):
+ state = VoidPointer()
+ result = _raw_md2_lib.md2_init(state.address_of())
+ if result:
+ raise ValueError("Error %d while instantiating MD2"
+ % result)
+ self._state = SmartPointer(state.get(),
+ _raw_md2_lib.md2_destroy)
+ if data:
+ self.update(data)
+
+ def update(self, data):
+ """Continue hashing of a message by consuming the next chunk of data.
+
+ Args:
+ data (byte string/byte array/memoryview): The next chunk of the message being hashed.
+ """
+
+ result = _raw_md2_lib.md2_update(self._state.get(),
+ c_uint8_ptr(data),
+ c_size_t(len(data)))
+ if result:
+ raise ValueError("Error %d while instantiating MD2"
+ % result)
+
+ def digest(self):
+ """Return the **binary** (non-printable) digest of the message that has been hashed so far.
+
+ :return: The hash digest, computed over the data processed so far.
+ Binary form.
+ :rtype: byte string
+ """
+
+ bfr = create_string_buffer(self.digest_size)
+ result = _raw_md2_lib.md2_digest(self._state.get(),
+ bfr)
+ if result:
+ raise ValueError("Error %d while instantiating MD2"
+ % result)
+
+ return get_raw_buffer(bfr)
+
+ def hexdigest(self):
+ """Return the **printable** digest of the message that has been hashed so far.
+
+ :return: The hash digest, computed over the data processed so far.
+ Hexadecimal encoded.
+ :rtype: string
+ """
+
+ return "".join(["%02x" % bord(x) for x in self.digest()])
+
+ def copy(self):
+ """Return a copy ("clone") of the hash object.
+
+ The copy will have the same internal state as the original hash
+ object.
+ This can be used to efficiently compute the digests of strings that
+ share a common initial substring.
+
+ :return: A hash object of the same type
+ """
+
+ clone = MD2Hash()
+ result = _raw_md2_lib.md2_copy(self._state.get(),
+ clone._state.get())
+ if result:
+ raise ValueError("Error %d while copying MD2" % result)
+ return clone
+
+ def new(self, data=None):
+ return MD2Hash(data)
+
+
+def new(data=None):
+ """Create a new hash object.
+
+ :parameter data:
+ Optional. The very first chunk of the message to hash.
+ It is equivalent to an early call to :meth:`MD2Hash.update`.
+ :type data: bytes/bytearray/memoryview
+
+ :Return: A :class:`MD2Hash` hash object
+ """
+
+ return MD2Hash().new(data)
+
+# The size of the resulting hash in bytes.
+digest_size = MD2Hash.digest_size
+
+# The internal block size of the hash algorithm in bytes.
+block_size = MD2Hash.block_size
diff --git a/lib/Crypto/Hash/MD2.pyi b/lib/Crypto/Hash/MD2.pyi
new file mode 100644
index 0000000..95a97a9
--- /dev/null
+++ b/lib/Crypto/Hash/MD2.pyi
@@ -0,0 +1,19 @@
+from typing import Union
+
+Buffer = Union[bytes, bytearray, memoryview]
+
+class MD4Hash(object):
+ digest_size: int
+ block_size: int
+ oid: str
+
+ def __init__(self, data: Buffer = ...) -> None: ...
+ def update(self, data: Buffer) -> None: ...
+ def digest(self) -> bytes: ...
+ def hexdigest(self) -> str: ...
+ def copy(self) -> MD4Hash: ...
+ def new(self, data: Buffer = ...) -> MD4Hash: ...
+
+def new(data: Buffer = ...) -> MD4Hash: ...
+digest_size: int
+block_size: int
diff --git a/lib/Crypto/Hash/MD4.py b/lib/Crypto/Hash/MD4.py
new file mode 100644
index 0000000..be12b19
--- /dev/null
+++ b/lib/Crypto/Hash/MD4.py
@@ -0,0 +1,185 @@
+# ===================================================================
+#
+# Copyright (c) 2014, Legrandin <helderijs@gmail.com>
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+# ===================================================================
+
+"""
+MD4 is specified in RFC1320_ and produces the 128 bit digest of a message.
+
+ >>> from Crypto.Hash import MD4
+ >>>
+ >>> h = MD4.new()
+ >>> h.update(b'Hello')
+ >>> print h.hexdigest()
+
+MD4 stand for Message Digest version 4, and it was invented by Rivest in 1990.
+This algorithm is insecure. Do not use it for new designs.
+
+.. _RFC1320: http://tools.ietf.org/html/rfc1320
+"""
+
+from Crypto.Util.py3compat import bord
+
+from Crypto.Util._raw_api import (load_pycryptodome_raw_lib,
+ VoidPointer, SmartPointer,
+ create_string_buffer,
+ get_raw_buffer, c_size_t,
+ c_uint8_ptr)
+
+_raw_md4_lib = load_pycryptodome_raw_lib(
+ "Crypto.Hash._MD4",
+ """
+ int md4_init(void **shaState);
+ int md4_destroy(void *shaState);
+ int md4_update(void *hs,
+ const uint8_t *buf,
+ size_t len);
+ int md4_digest(const void *shaState,
+ uint8_t digest[20]);
+ int md4_copy(const void *src, void *dst);
+ """)
+
+
+class MD4Hash(object):
+ """Class that implements an MD4 hash
+ """
+
+ #: The size of the resulting hash in bytes.
+ digest_size = 16
+ #: The internal block size of the hash algorithm in bytes.
+ block_size = 64
+ #: ASN.1 Object ID
+ oid = "1.2.840.113549.2.4"
+
+ def __init__(self, data=None):
+ state = VoidPointer()
+ result = _raw_md4_lib.md4_init(state.address_of())
+ if result:
+ raise ValueError("Error %d while instantiating MD4"
+ % result)
+ self._state = SmartPointer(state.get(),
+ _raw_md4_lib.md4_destroy)
+ if data:
+ self.update(data)
+
+ def update(self, data):
+ """Continue hashing of a message by consuming the next chunk of data.
+
+ Repeated calls are equivalent to a single call with the concatenation
+ of all the arguments. In other words:
+
+ >>> m.update(a); m.update(b)
+
+ is equivalent to:
+
+ >>> m.update(a+b)
+
+ :Parameters:
+ data : byte string/byte array/memoryview
+ The next chunk of the message being hashed.
+ """
+
+ result = _raw_md4_lib.md4_update(self._state.get(),
+ c_uint8_ptr(data),
+ c_size_t(len(data)))
+ if result:
+ raise ValueError("Error %d while instantiating MD4"
+ % result)
+
+ def digest(self):
+ """Return the **binary** (non-printable) digest of the message that
+ has been hashed so far.
+
+ This method does not change the state of the hash object.
+ You can continue updating the object after calling this function.
+
+ :Return: A byte string of `digest_size` bytes. It may contain non-ASCII
+ characters, including null bytes.
+ """
+
+ bfr = create_string_buffer(self.digest_size)
+ result = _raw_md4_lib.md4_digest(self._state.get(),
+ bfr)
+ if result:
+ raise ValueError("Error %d while instantiating MD4"
+ % result)
+
+ return get_raw_buffer(bfr)
+
+ def hexdigest(self):
+ """Return the **printable** digest of the message that has been
+ hashed so far.
+
+ This method does not change the state of the hash object.
+
+ :Return: A string of 2* `digest_size` characters. It contains only
+ hexadecimal ASCII digits.
+ """
+
+ return "".join(["%02x" % bord(x) for x in self.digest()])
+
+ def copy(self):
+ """Return a copy ("clone") of the hash object.
+
+ The copy will have the same internal state as the original hash
+ object.
+ This can be used to efficiently compute the digests of strings that
+ share a common initial substring.
+
+ :Return: A hash object of the same type
+ """
+
+ clone = MD4Hash()
+ result = _raw_md4_lib.md4_copy(self._state.get(),
+ clone._state.get())
+ if result:
+ raise ValueError("Error %d while copying MD4" % result)
+ return clone
+
+ def new(self, data=None):
+ return MD4Hash(data)
+
+
+def new(data=None):
+ """Return a fresh instance of the hash object.
+
+ :Parameters:
+ data : byte string/byte array/memoryview
+ The very first chunk of the message to hash.
+ It is equivalent to an early call to `MD4Hash.update()`.
+ Optional.
+
+ :Return: A `MD4Hash` object
+ """
+ return MD4Hash().new(data)
+
+#: The size of the resulting hash in bytes.
+digest_size = MD4Hash.digest_size
+
+#: The internal block size of the hash algorithm in bytes.
+block_size = MD4Hash.block_size
diff --git a/lib/Crypto/Hash/MD4.pyi b/lib/Crypto/Hash/MD4.pyi
new file mode 100644
index 0000000..a9a7295
--- /dev/null
+++ b/lib/Crypto/Hash/MD4.pyi
@@ -0,0 +1,19 @@
+from typing import Union, Optional
+
+Buffer = Union[bytes, bytearray, memoryview]
+
+class MD4Hash(object):
+ digest_size: int
+ block_size: int
+ oid: str
+
+ def __init__(self, data: Optional[Buffer] = ...) -> None: ...
+ def update(self, data: Buffer) -> None: ...
+ def digest(self) -> bytes: ...
+ def hexdigest(self) -> str: ...
+ def copy(self) -> MD4Hash: ...
+ def new(self, data: Optional[Buffer] = ...) -> MD4Hash: ...
+
+def new(data: Optional[Buffer] = ...) -> MD4Hash: ...
+digest_size: int
+block_size: int
diff --git a/lib/Crypto/Hash/MD5.py b/lib/Crypto/Hash/MD5.py
new file mode 100644
index 0000000..554b777
--- /dev/null
+++ b/lib/Crypto/Hash/MD5.py
@@ -0,0 +1,184 @@
+# -*- coding: utf-8 -*-
+#
+# ===================================================================
+# The contents of this file are dedicated to the public domain. To
+# the extent that dedication to the public domain is not available,
+# everyone is granted a worldwide, perpetual, royalty-free,
+# non-exclusive license to exercise all rights associated with the
+# contents of this file for any purpose whatsoever.
+# No rights are reserved.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+# ===================================================================
+
+from Crypto.Util.py3compat import *
+
+from Crypto.Util._raw_api import (load_pycryptodome_raw_lib,
+ VoidPointer, SmartPointer,
+ create_string_buffer,
+ get_raw_buffer, c_size_t,
+ c_uint8_ptr)
+
+_raw_md5_lib = load_pycryptodome_raw_lib("Crypto.Hash._MD5",
+ """
+ #define MD5_DIGEST_SIZE 16
+
+ int MD5_init(void **shaState);
+ int MD5_destroy(void *shaState);
+ int MD5_update(void *hs,
+ const uint8_t *buf,
+ size_t len);
+ int MD5_digest(const void *shaState,
+ uint8_t digest[MD5_DIGEST_SIZE]);
+ int MD5_copy(const void *src, void *dst);
+
+ int MD5_pbkdf2_hmac_assist(const void *inner,
+ const void *outer,
+ const uint8_t first_digest[MD5_DIGEST_SIZE],
+ uint8_t final_digest[MD5_DIGEST_SIZE],
+ size_t iterations);
+ """)
+
+class MD5Hash(object):
+ """A MD5 hash object.
+ Do not instantiate directly.
+ Use the :func:`new` function.
+
+ :ivar oid: ASN.1 Object ID
+ :vartype oid: string
+
+ :ivar block_size: the size in bytes of the internal message block,
+ input to the compression function
+ :vartype block_size: integer
+
+ :ivar digest_size: the size in bytes of the resulting hash
+ :vartype digest_size: integer
+ """
+
+ # The size of the resulting hash in bytes.
+ digest_size = 16
+ # The internal block size of the hash algorithm in bytes.
+ block_size = 64
+ # ASN.1 Object ID
+ oid = "1.2.840.113549.2.5"
+
+ def __init__(self, data=None):
+ state = VoidPointer()
+ result = _raw_md5_lib.MD5_init(state.address_of())
+ if result:
+ raise ValueError("Error %d while instantiating MD5"
+ % result)
+ self._state = SmartPointer(state.get(),
+ _raw_md5_lib.MD5_destroy)
+ if data:
+ self.update(data)
+
+ def update(self, data):
+ """Continue hashing of a message by consuming the next chunk of data.
+
+ Args:
+ data (byte string/byte array/memoryview): The next chunk of the message being hashed.
+ """
+
+ result = _raw_md5_lib.MD5_update(self._state.get(),
+ c_uint8_ptr(data),
+ c_size_t(len(data)))
+ if result:
+ raise ValueError("Error %d while instantiating MD5"
+ % result)
+
+ def digest(self):
+ """Return the **binary** (non-printable) digest of the message that has been hashed so far.
+
+ :return: The hash digest, computed over the data processed so far.
+ Binary form.
+ :rtype: byte string
+ """
+
+ bfr = create_string_buffer(self.digest_size)
+ result = _raw_md5_lib.MD5_digest(self._state.get(),
+ bfr)
+ if result:
+ raise ValueError("Error %d while instantiating MD5"
+ % result)
+
+ return get_raw_buffer(bfr)
+
+ def hexdigest(self):
+ """Return the **printable** digest of the message that has been hashed so far.
+
+ :return: The hash digest, computed over the data processed so far.
+ Hexadecimal encoded.
+ :rtype: string
+ """
+
+ return "".join(["%02x" % bord(x) for x in self.digest()])
+
+ def copy(self):
+ """Return a copy ("clone") of the hash object.
+
+ The copy will have the same internal state as the original hash
+ object.
+ This can be used to efficiently compute the digests of strings that
+ share a common initial substring.
+
+ :return: A hash object of the same type
+ """
+
+ clone = MD5Hash()
+ result = _raw_md5_lib.MD5_copy(self._state.get(),
+ clone._state.get())
+ if result:
+ raise ValueError("Error %d while copying MD5" % result)
+ return clone
+
+ def new(self, data=None):
+ """Create a fresh SHA-1 hash object."""
+
+ return MD5Hash(data)
+
+
+def new(data=None):
+ """Create a new hash object.
+
+ :parameter data:
+ Optional. The very first chunk of the message to hash.
+ It is equivalent to an early call to :meth:`MD5Hash.update`.
+ :type data: byte string/byte array/memoryview
+
+ :Return: A :class:`MD5Hash` hash object
+ """
+ return MD5Hash().new(data)
+
+# The size of the resulting hash in bytes.
+digest_size = 16
+
+# The internal block size of the hash algorithm in bytes.
+block_size = 64
+
+
+def _pbkdf2_hmac_assist(inner, outer, first_digest, iterations):
+ """Compute the expensive inner loop in PBKDF-HMAC."""
+
+ assert len(first_digest) == digest_size
+ assert iterations > 0
+
+ bfr = create_string_buffer(digest_size);
+ result = _raw_md5_lib.MD5_pbkdf2_hmac_assist(
+ inner._state.get(),
+ outer._state.get(),
+ first_digest,
+ bfr,
+ c_size_t(iterations))
+
+ if result:
+ raise ValueError("Error %d with PBKDF2-HMAC assis for MD5" % result)
+
+ return get_raw_buffer(bfr)
diff --git a/lib/Crypto/Hash/MD5.pyi b/lib/Crypto/Hash/MD5.pyi
new file mode 100644
index 0000000..d819556
--- /dev/null
+++ b/lib/Crypto/Hash/MD5.pyi
@@ -0,0 +1,19 @@
+from typing import Union
+
+Buffer = Union[bytes, bytearray, memoryview]
+
+class MD5Hash(object):
+ digest_size: int
+ block_size: int
+ oid: str
+
+ def __init__(self, data: Buffer = ...) -> None: ...
+ def update(self, data: Buffer) -> None: ...
+ def digest(self) -> bytes: ...
+ def hexdigest(self) -> str: ...
+ def copy(self) -> MD5Hash: ...
+ def new(self, data: Buffer = ...) -> MD5Hash: ...
+
+def new(data: Buffer = ...) -> MD5Hash: ...
+digest_size: int
+block_size: int
diff --git a/lib/Crypto/Hash/Poly1305.py b/lib/Crypto/Hash/Poly1305.py
new file mode 100644
index 0000000..eb5e0da
--- /dev/null
+++ b/lib/Crypto/Hash/Poly1305.py
@@ -0,0 +1,217 @@
+# -*- coding: utf-8 -*-
+#
+# Hash/Poly1305.py - Implements the Poly1305 MAC
+#
+# ===================================================================
+# The contents of this file are dedicated to the public domain. To
+# the extent that dedication to the public domain is not available,
+# everyone is granted a worldwide, perpetual, royalty-free,
+# non-exclusive license to exercise all rights associated with the
+# contents of this file for any purpose whatsoever.
+# No rights are reserved.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+# ===================================================================
+
+from binascii import unhexlify
+
+from Crypto.Util.py3compat import bord, tobytes, _copy_bytes
+
+from Crypto.Hash import BLAKE2s
+from Crypto.Random import get_random_bytes
+from Crypto.Util._raw_api import (load_pycryptodome_raw_lib,
+ VoidPointer, SmartPointer,
+ create_string_buffer,
+ get_raw_buffer, c_size_t,
+ c_uint8_ptr)
+
+
+_raw_poly1305 = load_pycryptodome_raw_lib("Crypto.Hash._poly1305",
+ """
+ int poly1305_init(void **state,
+ const uint8_t *r,
+ size_t r_len,
+ const uint8_t *s,
+ size_t s_len);
+ int poly1305_destroy(void *state);
+ int poly1305_update(void *state,
+ const uint8_t *in,
+ size_t len);
+ int poly1305_digest(const void *state,
+ uint8_t *digest,
+ size_t len);
+ """)
+
+
+class Poly1305_MAC(object):
+ """An Poly1305 MAC object.
+ Do not instantiate directly. Use the :func:`new` function.
+
+ :ivar digest_size: the size in bytes of the resulting MAC tag
+ :vartype digest_size: integer
+ """
+
+ digest_size = 16
+
+ def __init__(self, r, s, data):
+
+ if len(r) != 16:
+ raise ValueError("Parameter r is not 16 bytes long")
+ if len(s) != 16:
+ raise ValueError("Parameter s is not 16 bytes long")
+
+ self._mac_tag = None
+
+ state = VoidPointer()
+ result = _raw_poly1305.poly1305_init(state.address_of(),
+ c_uint8_ptr(r),
+ c_size_t(len(r)),
+ c_uint8_ptr(s),
+ c_size_t(len(s))
+ )
+ if result:
+ raise ValueError("Error %d while instantiating Poly1305" % result)
+ self._state = SmartPointer(state.get(),
+ _raw_poly1305.poly1305_destroy)
+ if data:
+ self.update(data)
+
+ def update(self, data):
+ """Authenticate the next chunk of message.
+
+ Args:
+ data (byte string/byte array/memoryview): The next chunk of data
+ """
+
+ if self._mac_tag:
+ raise TypeError("You can only call 'digest' or 'hexdigest' on this object")
+
+ result = _raw_poly1305.poly1305_update(self._state.get(),
+ c_uint8_ptr(data),
+ c_size_t(len(data)))
+ if result:
+ raise ValueError("Error %d while hashing Poly1305 data" % result)
+ return self
+
+ def copy(self):
+ raise NotImplementedError()
+
+ def digest(self):
+ """Return the **binary** (non-printable) MAC tag of the message
+ authenticated so far.
+
+ :return: The MAC tag digest, computed over the data processed so far.
+ Binary form.
+ :rtype: byte string
+ """
+
+ if self._mac_tag:
+ return self._mac_tag
+
+ bfr = create_string_buffer(16)
+ result = _raw_poly1305.poly1305_digest(self._state.get(),
+ bfr,
+ c_size_t(len(bfr)))
+ if result:
+ raise ValueError("Error %d while creating Poly1305 digest" % result)
+
+ self._mac_tag = get_raw_buffer(bfr)
+ return self._mac_tag
+
+ def hexdigest(self):
+ """Return the **printable** MAC tag of the message authenticated so far.
+
+ :return: The MAC tag, computed over the data processed so far.
+ Hexadecimal encoded.
+ :rtype: string
+ """
+
+ return "".join(["%02x" % bord(x)
+ for x in tuple(self.digest())])
+
+ def verify(self, mac_tag):
+ """Verify that a given **binary** MAC (computed by another party)
+ is valid.
+
+ Args:
+ mac_tag (byte string/byte string/memoryview): the expected MAC of the message.
+
+ Raises:
+ ValueError: if the MAC does not match. It means that the message
+ has been tampered with or that the MAC key is incorrect.
+ """
+
+ secret = get_random_bytes(16)
+
+ mac1 = BLAKE2s.new(digest_bits=160, key=secret, data=mac_tag)
+ mac2 = BLAKE2s.new(digest_bits=160, key=secret, data=self.digest())
+
+ if mac1.digest() != mac2.digest():
+ raise ValueError("MAC check failed")
+
+ def hexverify(self, hex_mac_tag):
+ """Verify that a given **printable** MAC (computed by another party)
+ is valid.
+
+ Args:
+ hex_mac_tag (string): the expected MAC of the message,
+ as a hexadecimal string.
+
+ Raises:
+ ValueError: if the MAC does not match. It means that the message
+ has been tampered with or that the MAC key is incorrect.
+ """
+
+ self.verify(unhexlify(tobytes(hex_mac_tag)))
+
+
+
+def new(**kwargs):
+ """Create a new Poly1305 MAC object.
+
+ Args:
+ key (bytes/bytearray/memoryview):
+ The 32-byte key for the Poly1305 object.
+ cipher (module from ``Crypto.Cipher``):
+ The cipher algorithm to use for deriving the Poly1305
+ key pair *(r, s)*.
+ It can only be ``Crypto.Cipher.AES`` or ``Crypto.Cipher.ChaCha20``.
+ nonce (bytes/bytearray/memoryview):
+ Optional. The non-repeatable value to use for the MAC of this message.
+ It must be 16 bytes long for ``AES`` and 8 or 12 bytes for ``ChaCha20``.
+ If not passed, a random nonce is created; you will find it in the
+ ``nonce`` attribute of the new object.
+ data (bytes/bytearray/memoryview):
+ Optional. The very first chunk of the message to authenticate.
+ It is equivalent to an early call to ``update()``.
+
+ Returns:
+ A :class:`Poly1305_MAC` object
+ """
+
+ cipher = kwargs.pop("cipher", None)
+ if not hasattr(cipher, '_derive_Poly1305_key_pair'):
+ raise ValueError("Parameter 'cipher' must be AES or ChaCha20")
+
+ cipher_key = kwargs.pop("key", None)
+ if cipher_key is None:
+ raise TypeError("You must pass a parameter 'key'")
+
+ nonce = kwargs.pop("nonce", None)
+ data = kwargs.pop("data", None)
+
+ if kwargs:
+ raise TypeError("Unknown parameters: " + str(kwargs))
+
+ r, s, nonce = cipher._derive_Poly1305_key_pair(cipher_key, nonce)
+
+ new_mac = Poly1305_MAC(r, s, data)
+ new_mac.nonce = _copy_bytes(None, None, nonce) # nonce may still be just a memoryview
+ return new_mac
diff --git a/lib/Crypto/Hash/Poly1305.pyi b/lib/Crypto/Hash/Poly1305.pyi
new file mode 100644
index 0000000..f97a14a
--- /dev/null
+++ b/lib/Crypto/Hash/Poly1305.pyi
@@ -0,0 +1,24 @@
+from types import ModuleType
+from typing import Union
+
+Buffer = Union[bytes, bytearray, memoryview]
+
+class Poly1305_MAC(object):
+ block_size: int
+ digest_size: int
+ oid: str
+
+ def __init__(self,
+ r : int,
+ s : int,
+ data : Buffer) -> None: ...
+ def update(self, data: Buffer) -> Poly1305_MAC: ...
+ def digest(self) -> bytes: ...
+ def hexdigest(self) -> str: ...
+ def verify(self, mac_tag: Buffer) -> None: ...
+ def hexverify(self, hex_mac_tag: str) -> None: ...
+
+def new(key: Buffer,
+ cipher: ModuleType,
+ nonce: Buffer = ...,
+ data: Buffer = ...) -> Poly1305_MAC: ...
diff --git a/lib/Crypto/Hash/RIPEMD.py b/lib/Crypto/Hash/RIPEMD.py
new file mode 100644
index 0000000..4e80235
--- /dev/null
+++ b/lib/Crypto/Hash/RIPEMD.py
@@ -0,0 +1,26 @@
+# -*- coding: utf-8 -*-
+#
+# ===================================================================
+# The contents of this file are dedicated to the public domain. To
+# the extent that dedication to the public domain is not available,
+# everyone is granted a worldwide, perpetual, royalty-free,
+# non-exclusive license to exercise all rights associated with the
+# contents of this file for any purpose whatsoever.
+# No rights are reserved.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+# ===================================================================
+
+# This file exists for backward compatibility with old code that refers to
+# Crypto.Hash.RIPEMD
+
+"""Deprecated alias for `Crypto.Hash.RIPEMD160`"""
+
+from Crypto.Hash.RIPEMD160 import new, block_size, digest_size
diff --git a/lib/Crypto/Hash/RIPEMD.pyi b/lib/Crypto/Hash/RIPEMD.pyi
new file mode 100644
index 0000000..e33eb2d
--- /dev/null
+++ b/lib/Crypto/Hash/RIPEMD.pyi
@@ -0,0 +1,3 @@
+# This file exists for backward compatibility with old code that refers to
+# Crypto.Hash.SHA
+
diff --git a/lib/Crypto/Hash/RIPEMD160.py b/lib/Crypto/Hash/RIPEMD160.py
new file mode 100644
index 0000000..820b57d
--- /dev/null
+++ b/lib/Crypto/Hash/RIPEMD160.py
@@ -0,0 +1,169 @@
+# ===================================================================
+#
+# Copyright (c) 2014, Legrandin <helderijs@gmail.com>
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+# ===================================================================
+
+from Crypto.Util.py3compat import bord
+
+from Crypto.Util._raw_api import (load_pycryptodome_raw_lib,
+ VoidPointer, SmartPointer,
+ create_string_buffer,
+ get_raw_buffer, c_size_t,
+ c_uint8_ptr)
+
+_raw_ripemd160_lib = load_pycryptodome_raw_lib(
+ "Crypto.Hash._RIPEMD160",
+ """
+ int ripemd160_init(void **shaState);
+ int ripemd160_destroy(void *shaState);
+ int ripemd160_update(void *hs,
+ const uint8_t *buf,
+ size_t len);
+ int ripemd160_digest(const void *shaState,
+ uint8_t digest[20]);
+ int ripemd160_copy(const void *src, void *dst);
+ """)
+
+
+class RIPEMD160Hash(object):
+ """A RIPEMD-160 hash object.
+ Do not instantiate directly.
+ Use the :func:`new` function.
+
+ :ivar oid: ASN.1 Object ID
+ :vartype oid: string
+
+ :ivar block_size: the size in bytes of the internal message block,
+ input to the compression function
+ :vartype block_size: integer
+
+ :ivar digest_size: the size in bytes of the resulting hash
+ :vartype digest_size: integer
+ """
+
+ # The size of the resulting hash in bytes.
+ digest_size = 20
+ # The internal block size of the hash algorithm in bytes.
+ block_size = 64
+ # ASN.1 Object ID
+ oid = "1.3.36.3.2.1"
+
+ def __init__(self, data=None):
+ state = VoidPointer()
+ result = _raw_ripemd160_lib.ripemd160_init(state.address_of())
+ if result:
+ raise ValueError("Error %d while instantiating RIPEMD160"
+ % result)
+ self._state = SmartPointer(state.get(),
+ _raw_ripemd160_lib.ripemd160_destroy)
+ if data:
+ self.update(data)
+
+ def update(self, data):
+ """Continue hashing of a message by consuming the next chunk of data.
+
+ Args:
+ data (byte string/byte array/memoryview): The next chunk of the message being hashed.
+ """
+
+ result = _raw_ripemd160_lib.ripemd160_update(self._state.get(),
+ c_uint8_ptr(data),
+ c_size_t(len(data)))
+ if result:
+ raise ValueError("Error %d while instantiating ripemd160"
+ % result)
+
+ def digest(self):
+ """Return the **binary** (non-printable) digest of the message that has been hashed so far.
+
+ :return: The hash digest, computed over the data processed so far.
+ Binary form.
+ :rtype: byte string
+ """
+
+ bfr = create_string_buffer(self.digest_size)
+ result = _raw_ripemd160_lib.ripemd160_digest(self._state.get(),
+ bfr)
+ if result:
+ raise ValueError("Error %d while instantiating ripemd160"
+ % result)
+
+ return get_raw_buffer(bfr)
+
+ def hexdigest(self):
+ """Return the **printable** digest of the message that has been hashed so far.
+
+ :return: The hash digest, computed over the data processed so far.
+ Hexadecimal encoded.
+ :rtype: string
+ """
+
+ return "".join(["%02x" % bord(x) for x in self.digest()])
+
+ def copy(self):
+ """Return a copy ("clone") of the hash object.
+
+ The copy will have the same internal state as the original hash
+ object.
+ This can be used to efficiently compute the digests of strings that
+ share a common initial substring.
+
+ :return: A hash object of the same type
+ """
+
+ clone = RIPEMD160Hash()
+ result = _raw_ripemd160_lib.ripemd160_copy(self._state.get(),
+ clone._state.get())
+ if result:
+ raise ValueError("Error %d while copying ripemd160" % result)
+ return clone
+
+ def new(self, data=None):
+ """Create a fresh RIPEMD-160 hash object."""
+
+ return RIPEMD160Hash(data)
+
+
+def new(data=None):
+ """Create a new hash object.
+
+ :parameter data:
+ Optional. The very first chunk of the message to hash.
+ It is equivalent to an early call to :meth:`RIPEMD160Hash.update`.
+ :type data: byte string/byte array/memoryview
+
+ :Return: A :class:`RIPEMD160Hash` hash object
+ """
+
+ return RIPEMD160Hash().new(data)
+
+# The size of the resulting hash in bytes.
+digest_size = RIPEMD160Hash.digest_size
+
+# The internal block size of the hash algorithm in bytes.
+block_size = RIPEMD160Hash.block_size
diff --git a/lib/Crypto/Hash/RIPEMD160.pyi b/lib/Crypto/Hash/RIPEMD160.pyi
new file mode 100644
index 0000000..b619473
--- /dev/null
+++ b/lib/Crypto/Hash/RIPEMD160.pyi
@@ -0,0 +1,19 @@
+from typing import Union
+
+Buffer = Union[bytes, bytearray, memoryview]
+
+class RIPEMD160Hash(object):
+ digest_size: int
+ block_size: int
+ oid: str
+
+ def __init__(self, data: Buffer = ...) -> None: ...
+ def update(self, data: Buffer) -> None: ...
+ def digest(self) -> bytes: ...
+ def hexdigest(self) -> str: ...
+ def copy(self) -> RIPEMD160Hash: ...
+ def new(self, data: Buffer = ...) -> RIPEMD160Hash: ...
+
+def new(data: Buffer = ...) -> RIPEMD160Hash: ...
+digest_size: int
+block_size: int
diff --git a/lib/Crypto/Hash/SHA.py b/lib/Crypto/Hash/SHA.py
new file mode 100644
index 0000000..0cc141c
--- /dev/null
+++ b/lib/Crypto/Hash/SHA.py
@@ -0,0 +1,24 @@
+# -*- coding: utf-8 -*-
+#
+# ===================================================================
+# The contents of this file are dedicated to the public domain. To
+# the extent that dedication to the public domain is not available,
+# everyone is granted a worldwide, perpetual, royalty-free,
+# non-exclusive license to exercise all rights associated with the
+# contents of this file for any purpose whatsoever.
+# No rights are reserved.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+# ===================================================================
+
+# This file exists for backward compatibility with old code that refers to
+# Crypto.Hash.SHA
+
+from Crypto.Hash.SHA1 import __doc__, new, block_size, digest_size
diff --git a/lib/Crypto/Hash/SHA.pyi b/lib/Crypto/Hash/SHA.pyi
new file mode 100644
index 0000000..4d7d57e
--- /dev/null
+++ b/lib/Crypto/Hash/SHA.pyi
@@ -0,0 +1,4 @@
+# This file exists for backward compatibility with old code that refers to
+# Crypto.Hash.SHA
+
+from Crypto.Hash.SHA1 import __doc__, new, block_size, digest_size
diff --git a/lib/Crypto/Hash/SHA1.py b/lib/Crypto/Hash/SHA1.py
new file mode 100644
index 0000000..f79d825
--- /dev/null
+++ b/lib/Crypto/Hash/SHA1.py
@@ -0,0 +1,185 @@
+# -*- coding: utf-8 -*-
+#
+# ===================================================================
+# The contents of this file are dedicated to the public domain. To
+# the extent that dedication to the public domain is not available,
+# everyone is granted a worldwide, perpetual, royalty-free,
+# non-exclusive license to exercise all rights associated with the
+# contents of this file for any purpose whatsoever.
+# No rights are reserved.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+# ===================================================================
+
+from Crypto.Util.py3compat import *
+
+from Crypto.Util._raw_api import (load_pycryptodome_raw_lib,
+ VoidPointer, SmartPointer,
+ create_string_buffer,
+ get_raw_buffer, c_size_t,
+ c_uint8_ptr)
+
+_raw_sha1_lib = load_pycryptodome_raw_lib("Crypto.Hash._SHA1",
+ """
+ #define SHA1_DIGEST_SIZE 20
+
+ int SHA1_init(void **shaState);
+ int SHA1_destroy(void *shaState);
+ int SHA1_update(void *hs,
+ const uint8_t *buf,
+ size_t len);
+ int SHA1_digest(const void *shaState,
+ uint8_t digest[SHA1_DIGEST_SIZE]);
+ int SHA1_copy(const void *src, void *dst);
+
+ int SHA1_pbkdf2_hmac_assist(const void *inner,
+ const void *outer,
+ const uint8_t first_digest[SHA1_DIGEST_SIZE],
+ uint8_t final_digest[SHA1_DIGEST_SIZE],
+ size_t iterations);
+ """)
+
+class SHA1Hash(object):
+ """A SHA-1 hash object.
+ Do not instantiate directly.
+ Use the :func:`new` function.
+
+ :ivar oid: ASN.1 Object ID
+ :vartype oid: string
+
+ :ivar block_size: the size in bytes of the internal message block,
+ input to the compression function
+ :vartype block_size: integer
+
+ :ivar digest_size: the size in bytes of the resulting hash
+ :vartype digest_size: integer
+ """
+
+ # The size of the resulting hash in bytes.
+ digest_size = 20
+ # The internal block size of the hash algorithm in bytes.
+ block_size = 64
+ # ASN.1 Object ID
+ oid = "1.3.14.3.2.26"
+
+ def __init__(self, data=None):
+ state = VoidPointer()
+ result = _raw_sha1_lib.SHA1_init(state.address_of())
+ if result:
+ raise ValueError("Error %d while instantiating SHA1"
+ % result)
+ self._state = SmartPointer(state.get(),
+ _raw_sha1_lib.SHA1_destroy)
+ if data:
+ self.update(data)
+
+ def update(self, data):
+ """Continue hashing of a message by consuming the next chunk of data.
+
+ Args:
+ data (byte string/byte array/memoryview): The next chunk of the message being hashed.
+ """
+
+ result = _raw_sha1_lib.SHA1_update(self._state.get(),
+ c_uint8_ptr(data),
+ c_size_t(len(data)))
+ if result:
+ raise ValueError("Error %d while instantiating SHA1"
+ % result)
+
+ def digest(self):
+ """Return the **binary** (non-printable) digest of the message that has been hashed so far.
+
+ :return: The hash digest, computed over the data processed so far.
+ Binary form.
+ :rtype: byte string
+ """
+
+ bfr = create_string_buffer(self.digest_size)
+ result = _raw_sha1_lib.SHA1_digest(self._state.get(),
+ bfr)
+ if result:
+ raise ValueError("Error %d while instantiating SHA1"
+ % result)
+
+ return get_raw_buffer(bfr)
+
+ def hexdigest(self):
+ """Return the **printable** digest of the message that has been hashed so far.
+
+ :return: The hash digest, computed over the data processed so far.
+ Hexadecimal encoded.
+ :rtype: string
+ """
+
+ return "".join(["%02x" % bord(x) for x in self.digest()])
+
+ def copy(self):
+ """Return a copy ("clone") of the hash object.
+
+ The copy will have the same internal state as the original hash
+ object.
+ This can be used to efficiently compute the digests of strings that
+ share a common initial substring.
+
+ :return: A hash object of the same type
+ """
+
+ clone = SHA1Hash()
+ result = _raw_sha1_lib.SHA1_copy(self._state.get(),
+ clone._state.get())
+ if result:
+ raise ValueError("Error %d while copying SHA1" % result)
+ return clone
+
+ def new(self, data=None):
+ """Create a fresh SHA-1 hash object."""
+
+ return SHA1Hash(data)
+
+
+def new(data=None):
+ """Create a new hash object.
+
+ :parameter data:
+ Optional. The very first chunk of the message to hash.
+ It is equivalent to an early call to :meth:`SHA1Hash.update`.
+ :type data: byte string/byte array/memoryview
+
+ :Return: A :class:`SHA1Hash` hash object
+ """
+ return SHA1Hash().new(data)
+
+
+# The size of the resulting hash in bytes.
+digest_size = SHA1Hash.digest_size
+
+# The internal block size of the hash algorithm in bytes.
+block_size = SHA1Hash.block_size
+
+
+def _pbkdf2_hmac_assist(inner, outer, first_digest, iterations):
+ """Compute the expensive inner loop in PBKDF-HMAC."""
+
+ assert len(first_digest) == digest_size
+ assert iterations > 0
+
+ bfr = create_string_buffer(digest_size);
+ result = _raw_sha1_lib.SHA1_pbkdf2_hmac_assist(
+ inner._state.get(),
+ outer._state.get(),
+ first_digest,
+ bfr,
+ c_size_t(iterations))
+
+ if result:
+ raise ValueError("Error %d with PBKDF2-HMAC assis for SHA1" % result)
+
+ return get_raw_buffer(bfr)
diff --git a/lib/Crypto/Hash/SHA1.pyi b/lib/Crypto/Hash/SHA1.pyi
new file mode 100644
index 0000000..d6c8e25
--- /dev/null
+++ b/lib/Crypto/Hash/SHA1.pyi
@@ -0,0 +1,19 @@
+from typing import Union, Optional
+
+Buffer = Union[bytes, bytearray, memoryview]
+
+class SHA1Hash(object):
+ digest_size: int
+ block_size: int
+ oid: str
+
+ def __init__(self, data: Optional[Buffer] = ...) -> None: ...
+ def update(self, data: Buffer) -> None: ...
+ def digest(self) -> bytes: ...
+ def hexdigest(self) -> str: ...
+ def copy(self) -> SHA1Hash: ...
+ def new(self, data: Optional[Buffer] = ...) -> SHA1Hash: ...
+
+def new(data: Optional[Buffer] = ...) -> SHA1Hash: ...
+digest_size: int
+block_size: int
diff --git a/lib/Crypto/Hash/SHA224.py b/lib/Crypto/Hash/SHA224.py
new file mode 100644
index 0000000..f788b06
--- /dev/null
+++ b/lib/Crypto/Hash/SHA224.py
@@ -0,0 +1,186 @@
+# -*- coding: utf-8 -*-
+#
+# ===================================================================
+# The contents of this file are dedicated to the public domain. To
+# the extent that dedication to the public domain is not available,
+# everyone is granted a worldwide, perpetual, royalty-free,
+# non-exclusive license to exercise all rights associated with the
+# contents of this file for any purpose whatsoever.
+# No rights are reserved.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+# ===================================================================
+
+from Crypto.Util.py3compat import bord
+
+from Crypto.Util._raw_api import (load_pycryptodome_raw_lib,
+ VoidPointer, SmartPointer,
+ create_string_buffer,
+ get_raw_buffer, c_size_t,
+ c_uint8_ptr)
+
+_raw_sha224_lib = load_pycryptodome_raw_lib("Crypto.Hash._SHA224",
+ """
+ int SHA224_init(void **shaState);
+ int SHA224_destroy(void *shaState);
+ int SHA224_update(void *hs,
+ const uint8_t *buf,
+ size_t len);
+ int SHA224_digest(const void *shaState,
+ uint8_t *digest,
+ size_t digest_size);
+ int SHA224_copy(const void *src, void *dst);
+
+ int SHA224_pbkdf2_hmac_assist(const void *inner,
+ const void *outer,
+ const uint8_t *first_digest,
+ uint8_t *final_digest,
+ size_t iterations,
+ size_t digest_size);
+ """)
+
+class SHA224Hash(object):
+ """A SHA-224 hash object.
+ Do not instantiate directly.
+ Use the :func:`new` function.
+
+ :ivar oid: ASN.1 Object ID
+ :vartype oid: string
+
+ :ivar block_size: the size in bytes of the internal message block,
+ input to the compression function
+ :vartype block_size: integer
+
+ :ivar digest_size: the size in bytes of the resulting hash
+ :vartype digest_size: integer
+ """
+
+ # The size of the resulting hash in bytes.
+ digest_size = 28
+ # The internal block size of the hash algorithm in bytes.
+ block_size = 64
+ # ASN.1 Object ID
+ oid = '2.16.840.1.101.3.4.2.4'
+
+ def __init__(self, data=None):
+ state = VoidPointer()
+ result = _raw_sha224_lib.SHA224_init(state.address_of())
+ if result:
+ raise ValueError("Error %d while instantiating SHA224"
+ % result)
+ self._state = SmartPointer(state.get(),
+ _raw_sha224_lib.SHA224_destroy)
+ if data:
+ self.update(data)
+
+ def update(self, data):
+ """Continue hashing of a message by consuming the next chunk of data.
+
+ Args:
+ data (byte string/byte array/memoryview): The next chunk of the message being hashed.
+ """
+
+ result = _raw_sha224_lib.SHA224_update(self._state.get(),
+ c_uint8_ptr(data),
+ c_size_t(len(data)))
+ if result:
+ raise ValueError("Error %d while hashing data with SHA224"
+ % result)
+
+ def digest(self):
+ """Return the **binary** (non-printable) digest of the message that has been hashed so far.
+
+ :return: The hash digest, computed over the data processed so far.
+ Binary form.
+ :rtype: byte string
+ """
+
+ bfr = create_string_buffer(self.digest_size)
+ result = _raw_sha224_lib.SHA224_digest(self._state.get(),
+ bfr,
+ c_size_t(self.digest_size))
+ if result:
+ raise ValueError("Error %d while making SHA224 digest"
+ % result)
+
+ return get_raw_buffer(bfr)
+
+ def hexdigest(self):
+ """Return the **printable** digest of the message that has been hashed so far.
+
+ :return: The hash digest, computed over the data processed so far.
+ Hexadecimal encoded.
+ :rtype: string
+ """
+
+ return "".join(["%02x" % bord(x) for x in self.digest()])
+
+ def copy(self):
+ """Return a copy ("clone") of the hash object.
+
+ The copy will have the same internal state as the original hash
+ object.
+ This can be used to efficiently compute the digests of strings that
+ share a common initial substring.
+
+ :return: A hash object of the same type
+ """
+
+ clone = SHA224Hash()
+ result = _raw_sha224_lib.SHA224_copy(self._state.get(),
+ clone._state.get())
+ if result:
+ raise ValueError("Error %d while copying SHA224" % result)
+ return clone
+
+ def new(self, data=None):
+ """Create a fresh SHA-224 hash object."""
+
+ return SHA224Hash(data)
+
+
+def new(data=None):
+ """Create a new hash object.
+
+ :parameter data:
+ Optional. The very first chunk of the message to hash.
+ It is equivalent to an early call to :meth:`SHA224Hash.update`.
+ :type data: byte string/byte array/memoryview
+
+ :Return: A :class:`SHA224Hash` hash object
+ """
+ return SHA224Hash().new(data)
+
+
+# The size of the resulting hash in bytes.
+digest_size = SHA224Hash.digest_size
+
+# The internal block size of the hash algorithm in bytes.
+block_size = SHA224Hash.block_size
+
+
+def _pbkdf2_hmac_assist(inner, outer, first_digest, iterations):
+ """Compute the expensive inner loop in PBKDF-HMAC."""
+
+ assert iterations > 0
+
+ bfr = create_string_buffer(len(first_digest));
+ result = _raw_sha224_lib.SHA224_pbkdf2_hmac_assist(
+ inner._state.get(),
+ outer._state.get(),
+ first_digest,
+ bfr,
+ c_size_t(iterations),
+ c_size_t(len(first_digest)))
+
+ if result:
+ raise ValueError("Error %d with PBKDF2-HMAC assist for SHA224" % result)
+
+ return get_raw_buffer(bfr)
diff --git a/lib/Crypto/Hash/SHA224.pyi b/lib/Crypto/Hash/SHA224.pyi
new file mode 100644
index 0000000..613a7f9
--- /dev/null
+++ b/lib/Crypto/Hash/SHA224.pyi
@@ -0,0 +1,19 @@
+from typing import Union, Optional
+
+Buffer = Union[bytes, bytearray, memoryview]
+
+class SHA224Hash(object):
+ digest_size: int
+ block_size: int
+ oid: str
+
+ def __init__(self, data: Optional[Buffer] = ...) -> None: ...
+ def update(self, data: Buffer) -> None: ...
+ def digest(self) -> bytes: ...
+ def hexdigest(self) -> str: ...
+ def copy(self) -> SHA224Hash: ...
+ def new(self, data: Optional[Buffer] = ...) -> SHA224Hash: ...
+
+def new(data: Optional[Buffer] = ...) -> SHA224Hash: ...
+digest_size: int
+block_size: int
diff --git a/lib/Crypto/Hash/SHA256.py b/lib/Crypto/Hash/SHA256.py
new file mode 100644
index 0000000..957aa37
--- /dev/null
+++ b/lib/Crypto/Hash/SHA256.py
@@ -0,0 +1,185 @@
+# -*- coding: utf-8 -*-
+#
+# ===================================================================
+# The contents of this file are dedicated to the public domain. To
+# the extent that dedication to the public domain is not available,
+# everyone is granted a worldwide, perpetual, royalty-free,
+# non-exclusive license to exercise all rights associated with the
+# contents of this file for any purpose whatsoever.
+# No rights are reserved.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+# ===================================================================
+
+from Crypto.Util.py3compat import bord
+
+from Crypto.Util._raw_api import (load_pycryptodome_raw_lib,
+ VoidPointer, SmartPointer,
+ create_string_buffer,
+ get_raw_buffer, c_size_t,
+ c_uint8_ptr)
+
+_raw_sha256_lib = load_pycryptodome_raw_lib("Crypto.Hash._SHA256",
+ """
+ int SHA256_init(void **shaState);
+ int SHA256_destroy(void *shaState);
+ int SHA256_update(void *hs,
+ const uint8_t *buf,
+ size_t len);
+ int SHA256_digest(const void *shaState,
+ uint8_t *digest,
+ size_t digest_size);
+ int SHA256_copy(const void *src, void *dst);
+
+ int SHA256_pbkdf2_hmac_assist(const void *inner,
+ const void *outer,
+ const uint8_t *first_digest,
+ uint8_t *final_digest,
+ size_t iterations,
+ size_t digest_size);
+ """)
+
+class SHA256Hash(object):
+ """A SHA-256 hash object.
+ Do not instantiate directly. Use the :func:`new` function.
+
+ :ivar oid: ASN.1 Object ID
+ :vartype oid: string
+
+ :ivar block_size: the size in bytes of the internal message block,
+ input to the compression function
+ :vartype block_size: integer
+
+ :ivar digest_size: the size in bytes of the resulting hash
+ :vartype digest_size: integer
+ """
+
+ # The size of the resulting hash in bytes.
+ digest_size = 32
+ # The internal block size of the hash algorithm in bytes.
+ block_size = 64
+ # ASN.1 Object ID
+ oid = "2.16.840.1.101.3.4.2.1"
+
+ def __init__(self, data=None):
+ state = VoidPointer()
+ result = _raw_sha256_lib.SHA256_init(state.address_of())
+ if result:
+ raise ValueError("Error %d while instantiating SHA256"
+ % result)
+ self._state = SmartPointer(state.get(),
+ _raw_sha256_lib.SHA256_destroy)
+ if data:
+ self.update(data)
+
+ def update(self, data):
+ """Continue hashing of a message by consuming the next chunk of data.
+
+ Args:
+ data (byte string/byte array/memoryview): The next chunk of the message being hashed.
+ """
+
+ result = _raw_sha256_lib.SHA256_update(self._state.get(),
+ c_uint8_ptr(data),
+ c_size_t(len(data)))
+ if result:
+ raise ValueError("Error %d while hashing data with SHA256"
+ % result)
+
+ def digest(self):
+ """Return the **binary** (non-printable) digest of the message that has been hashed so far.
+
+ :return: The hash digest, computed over the data processed so far.
+ Binary form.
+ :rtype: byte string
+ """
+
+ bfr = create_string_buffer(self.digest_size)
+ result = _raw_sha256_lib.SHA256_digest(self._state.get(),
+ bfr,
+ c_size_t(self.digest_size))
+ if result:
+ raise ValueError("Error %d while making SHA256 digest"
+ % result)
+
+ return get_raw_buffer(bfr)
+
+ def hexdigest(self):
+ """Return the **printable** digest of the message that has been hashed so far.
+
+ :return: The hash digest, computed over the data processed so far.
+ Hexadecimal encoded.
+ :rtype: string
+ """
+
+ return "".join(["%02x" % bord(x) for x in self.digest()])
+
+ def copy(self):
+ """Return a copy ("clone") of the hash object.
+
+ The copy will have the same internal state as the original hash
+ object.
+ This can be used to efficiently compute the digests of strings that
+ share a common initial substring.
+
+ :return: A hash object of the same type
+ """
+
+ clone = SHA256Hash()
+ result = _raw_sha256_lib.SHA256_copy(self._state.get(),
+ clone._state.get())
+ if result:
+ raise ValueError("Error %d while copying SHA256" % result)
+ return clone
+
+ def new(self, data=None):
+ """Create a fresh SHA-256 hash object."""
+
+ return SHA256Hash(data)
+
+def new(data=None):
+ """Create a new hash object.
+
+ :parameter data:
+ Optional. The very first chunk of the message to hash.
+ It is equivalent to an early call to :meth:`SHA256Hash.update`.
+ :type data: byte string/byte array/memoryview
+
+ :Return: A :class:`SHA256Hash` hash object
+ """
+
+ return SHA256Hash().new(data)
+
+
+# The size of the resulting hash in bytes.
+digest_size = SHA256Hash.digest_size
+
+# The internal block size of the hash algorithm in bytes.
+block_size = SHA256Hash.block_size
+
+
+def _pbkdf2_hmac_assist(inner, outer, first_digest, iterations):
+ """Compute the expensive inner loop in PBKDF-HMAC."""
+
+ assert iterations > 0
+
+ bfr = create_string_buffer(len(first_digest));
+ result = _raw_sha256_lib.SHA256_pbkdf2_hmac_assist(
+ inner._state.get(),
+ outer._state.get(),
+ first_digest,
+ bfr,
+ c_size_t(iterations),
+ c_size_t(len(first_digest)))
+
+ if result:
+ raise ValueError("Error %d with PBKDF2-HMAC assist for SHA256" % result)
+
+ return get_raw_buffer(bfr)
diff --git a/lib/Crypto/Hash/SHA256.pyi b/lib/Crypto/Hash/SHA256.pyi
new file mode 100644
index 0000000..cbf21bf
--- /dev/null
+++ b/lib/Crypto/Hash/SHA256.pyi
@@ -0,0 +1,18 @@
+from typing import Union, Optional
+
+
+class SHA256Hash(object):
+ digest_size: int
+ block_size: int
+ oid: str
+ def __init__(self, data: Optional[Union[bytes, bytearray, memoryview]]=None) -> None: ...
+ def update(self, data: Union[bytes, bytearray, memoryview]) -> None: ...
+ def digest(self) -> bytes: ...
+ def hexdigest(self) -> str: ...
+ def copy(self) -> SHA256Hash: ...
+ def new(self, data: Optional[Union[bytes, bytearray, memoryview]]=None) -> SHA256Hash: ...
+
+def new(data: Optional[Union[bytes, bytearray, memoryview]]=None) -> SHA256Hash: ...
+
+digest_size: int
+block_size: int
diff --git a/lib/Crypto/Hash/SHA384.py b/lib/Crypto/Hash/SHA384.py
new file mode 100644
index 0000000..a98fa9a
--- /dev/null
+++ b/lib/Crypto/Hash/SHA384.py
@@ -0,0 +1,186 @@
+# -*- coding: utf-8 -*-
+#
+# ===================================================================
+# The contents of this file are dedicated to the public domain. To
+# the extent that dedication to the public domain is not available,
+# everyone is granted a worldwide, perpetual, royalty-free,
+# non-exclusive license to exercise all rights associated with the
+# contents of this file for any purpose whatsoever.
+# No rights are reserved.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+# ===================================================================
+
+from Crypto.Util.py3compat import bord
+
+from Crypto.Util._raw_api import (load_pycryptodome_raw_lib,
+ VoidPointer, SmartPointer,
+ create_string_buffer,
+ get_raw_buffer, c_size_t,
+ c_uint8_ptr)
+
+_raw_sha384_lib = load_pycryptodome_raw_lib("Crypto.Hash._SHA384",
+ """
+ int SHA384_init(void **shaState);
+ int SHA384_destroy(void *shaState);
+ int SHA384_update(void *hs,
+ const uint8_t *buf,
+ size_t len);
+ int SHA384_digest(const void *shaState,
+ uint8_t *digest,
+ size_t digest_size);
+ int SHA384_copy(const void *src, void *dst);
+
+ int SHA384_pbkdf2_hmac_assist(const void *inner,
+ const void *outer,
+ const uint8_t *first_digest,
+ uint8_t *final_digest,
+ size_t iterations,
+ size_t digest_size);
+ """)
+
+class SHA384Hash(object):
+ """A SHA-384 hash object.
+ Do not instantiate directly. Use the :func:`new` function.
+
+ :ivar oid: ASN.1 Object ID
+ :vartype oid: string
+
+ :ivar block_size: the size in bytes of the internal message block,
+ input to the compression function
+ :vartype block_size: integer
+
+ :ivar digest_size: the size in bytes of the resulting hash
+ :vartype digest_size: integer
+ """
+
+ # The size of the resulting hash in bytes.
+ digest_size = 48
+ # The internal block size of the hash algorithm in bytes.
+ block_size = 128
+ # ASN.1 Object ID
+ oid = '2.16.840.1.101.3.4.2.2'
+
+ def __init__(self, data=None):
+ state = VoidPointer()
+ result = _raw_sha384_lib.SHA384_init(state.address_of())
+ if result:
+ raise ValueError("Error %d while instantiating SHA384"
+ % result)
+ self._state = SmartPointer(state.get(),
+ _raw_sha384_lib.SHA384_destroy)
+ if data:
+ self.update(data)
+
+ def update(self, data):
+ """Continue hashing of a message by consuming the next chunk of data.
+
+ Args:
+ data (byte string/byte array/memoryview): The next chunk of the message being hashed.
+ """
+
+ result = _raw_sha384_lib.SHA384_update(self._state.get(),
+ c_uint8_ptr(data),
+ c_size_t(len(data)))
+ if result:
+ raise ValueError("Error %d while hashing data with SHA384"
+ % result)
+
+ def digest(self):
+ """Return the **binary** (non-printable) digest of the message that has been hashed so far.
+
+ :return: The hash digest, computed over the data processed so far.
+ Binary form.
+ :rtype: byte string
+ """
+
+ bfr = create_string_buffer(self.digest_size)
+ result = _raw_sha384_lib.SHA384_digest(self._state.get(),
+ bfr,
+ c_size_t(self.digest_size))
+ if result:
+ raise ValueError("Error %d while making SHA384 digest"
+ % result)
+
+ return get_raw_buffer(bfr)
+
+ def hexdigest(self):
+ """Return the **printable** digest of the message that has been hashed so far.
+
+ :return: The hash digest, computed over the data processed so far.
+ Hexadecimal encoded.
+ :rtype: string
+ """
+
+ return "".join(["%02x" % bord(x) for x in self.digest()])
+
+ def copy(self):
+ """Return a copy ("clone") of the hash object.
+
+ The copy will have the same internal state as the original hash
+ object.
+ This can be used to efficiently compute the digests of strings that
+ share a common initial substring.
+
+ :return: A hash object of the same type
+ """
+
+ clone = SHA384Hash()
+ result = _raw_sha384_lib.SHA384_copy(self._state.get(),
+ clone._state.get())
+ if result:
+ raise ValueError("Error %d while copying SHA384" % result)
+ return clone
+
+ def new(self, data=None):
+ """Create a fresh SHA-384 hash object."""
+
+ return SHA384Hash(data)
+
+
+def new(data=None):
+ """Create a new hash object.
+
+ :parameter data:
+ Optional. The very first chunk of the message to hash.
+ It is equivalent to an early call to :meth:`SHA384Hash.update`.
+ :type data: byte string/byte array/memoryview
+
+ :Return: A :class:`SHA384Hash` hash object
+ """
+
+ return SHA384Hash().new(data)
+
+
+# The size of the resulting hash in bytes.
+digest_size = SHA384Hash.digest_size
+
+# The internal block size of the hash algorithm in bytes.
+block_size = SHA384Hash.block_size
+
+
+def _pbkdf2_hmac_assist(inner, outer, first_digest, iterations):
+ """Compute the expensive inner loop in PBKDF-HMAC."""
+
+ assert iterations > 0
+
+ bfr = create_string_buffer(len(first_digest));
+ result = _raw_sha384_lib.SHA384_pbkdf2_hmac_assist(
+ inner._state.get(),
+ outer._state.get(),
+ first_digest,
+ bfr,
+ c_size_t(iterations),
+ c_size_t(len(first_digest)))
+
+ if result:
+ raise ValueError("Error %d with PBKDF2-HMAC assist for SHA384" % result)
+
+ return get_raw_buffer(bfr)
diff --git a/lib/Crypto/Hash/SHA384.pyi b/lib/Crypto/Hash/SHA384.pyi
new file mode 100644
index 0000000..c2aab9e
--- /dev/null
+++ b/lib/Crypto/Hash/SHA384.pyi
@@ -0,0 +1,19 @@
+from typing import Union, Optional
+
+Buffer = Union[bytes, bytearray, memoryview]
+
+class SHA384Hash(object):
+ digest_size: int
+ block_size: int
+ oid: str
+
+ def __init__(self, data: Optional[Buffer] = ...) -> None: ...
+ def update(self, data: Buffer) -> None: ...
+ def digest(self) -> bytes: ...
+ def hexdigest(self) -> str: ...
+ def copy(self) -> SHA384Hash: ...
+ def new(self, data: Optional[Buffer] = ...) -> SHA384Hash: ...
+
+def new(data: Optional[Buffer] = ...) -> SHA384Hash: ...
+digest_size: int
+block_size: int
diff --git a/lib/Crypto/Hash/SHA3_224.py b/lib/Crypto/Hash/SHA3_224.py
new file mode 100644
index 0000000..54556d0
--- /dev/null
+++ b/lib/Crypto/Hash/SHA3_224.py
@@ -0,0 +1,174 @@
+# -*- coding: utf-8 -*-
+#
+# ===================================================================
+# The contents of this file are dedicated to the public domain. To
+# the extent that dedication to the public domain is not available,
+# everyone is granted a worldwide, perpetual, royalty-free,
+# non-exclusive license to exercise all rights associated with the
+# contents of this file for any purpose whatsoever.
+# No rights are reserved.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+# ===================================================================
+
+from Crypto.Util.py3compat import bord
+
+from Crypto.Util._raw_api import (load_pycryptodome_raw_lib,
+ VoidPointer, SmartPointer,
+ create_string_buffer,
+ get_raw_buffer, c_size_t,
+ c_uint8_ptr, c_ubyte)
+
+from Crypto.Hash.keccak import _raw_keccak_lib
+
+class SHA3_224_Hash(object):
+ """A SHA3-224 hash object.
+ Do not instantiate directly.
+ Use the :func:`new` function.
+
+ :ivar oid: ASN.1 Object ID
+ :vartype oid: string
+
+ :ivar digest_size: the size in bytes of the resulting hash
+ :vartype digest_size: integer
+ """
+
+ # The size of the resulting hash in bytes.
+ digest_size = 28
+
+ # ASN.1 Object ID
+ oid = "2.16.840.1.101.3.4.2.7"
+
+ # Input block size for HMAC
+ block_size = 144
+
+ def __init__(self, data, update_after_digest):
+ self._update_after_digest = update_after_digest
+ self._digest_done = False
+ self._padding = 0x06
+
+ state = VoidPointer()
+ result = _raw_keccak_lib.keccak_init(state.address_of(),
+ c_size_t(self.digest_size * 2),
+ c_ubyte(24))
+ if result:
+ raise ValueError("Error %d while instantiating SHA-3/224"
+ % result)
+ self._state = SmartPointer(state.get(),
+ _raw_keccak_lib.keccak_destroy)
+ if data:
+ self.update(data)
+
+ def update(self, data):
+ """Continue hashing of a message by consuming the next chunk of data.
+
+ Args:
+ data (byte string/byte array/memoryview): The next chunk of the message being hashed.
+ """
+
+ if self._digest_done and not self._update_after_digest:
+ raise TypeError("You can only call 'digest' or 'hexdigest' on this object")
+
+ result = _raw_keccak_lib.keccak_absorb(self._state.get(),
+ c_uint8_ptr(data),
+ c_size_t(len(data))
+ )
+ if result:
+ raise ValueError("Error %d while updating SHA-3/224"
+ % result)
+ return self
+
+ def digest(self):
+ """Return the **binary** (non-printable) digest of the message that has been hashed so far.
+
+ :return: The hash digest, computed over the data processed so far.
+ Binary form.
+ :rtype: byte string
+ """
+
+ self._digest_done = True
+
+ bfr = create_string_buffer(self.digest_size)
+ result = _raw_keccak_lib.keccak_digest(self._state.get(),
+ bfr,
+ c_size_t(self.digest_size),
+ c_ubyte(self._padding))
+ if result:
+ raise ValueError("Error %d while instantiating SHA-3/224"
+ % result)
+
+ self._digest_value = get_raw_buffer(bfr)
+ return self._digest_value
+
+ def hexdigest(self):
+ """Return the **printable** digest of the message that has been hashed so far.
+
+ :return: The hash digest, computed over the data processed so far.
+ Hexadecimal encoded.
+ :rtype: string
+ """
+
+ return "".join(["%02x" % bord(x) for x in self.digest()])
+
+ def copy(self):
+ """Return a copy ("clone") of the hash object.
+
+ The copy will have the same internal state as the original hash
+ object.
+ This can be used to efficiently compute the digests of strings that
+ share a common initial substring.
+
+ :return: A hash object of the same type
+ """
+
+ clone = self.new()
+ result = _raw_keccak_lib.keccak_copy(self._state.get(),
+ clone._state.get())
+ if result:
+ raise ValueError("Error %d while copying SHA3-224" % result)
+ return clone
+
+ def new(self, data=None):
+ """Create a fresh SHA3-224 hash object."""
+
+ return type(self)(data, self._update_after_digest)
+
+
+def new(*args, **kwargs):
+ """Create a new hash object.
+
+ Args:
+ data (byte string/byte array/memoryview):
+ The very first chunk of the message to hash.
+ It is equivalent to an early call to :meth:`update`.
+ update_after_digest (boolean):
+ Whether :meth:`digest` can be followed by another :meth:`update`
+ (default: ``False``).
+
+ :Return: A :class:`SHA3_224_Hash` hash object
+ """
+
+ data = kwargs.pop("data", None)
+ update_after_digest = kwargs.pop("update_after_digest", False)
+ if len(args) == 1:
+ if data:
+ raise ValueError("Initial data for hash specified twice")
+ data = args[0]
+
+ if kwargs:
+ raise TypeError("Unknown parameters: " + str(kwargs))
+
+ return SHA3_224_Hash(data, update_after_digest)
+
+# The size of the resulting hash in bytes.
+digest_size = SHA3_224_Hash.digest_size
+
+# Input block size for HMAC
+block_size = 144
diff --git a/lib/Crypto/Hash/SHA3_224.pyi b/lib/Crypto/Hash/SHA3_224.pyi
new file mode 100644
index 0000000..2180821
--- /dev/null
+++ b/lib/Crypto/Hash/SHA3_224.pyi
@@ -0,0 +1,19 @@
+from typing import Union, Optional
+
+Buffer = Union[bytes, bytearray, memoryview]
+
+class SHA3_224_Hash(object):
+ digest_size: int
+ block_size: int
+ oid: str
+ def __init__(self, data: Optional[Buffer], update_after_digest: bool) -> None: ...
+ def update(self, data: Buffer) -> SHA3_224_Hash: ...
+ def digest(self) -> bytes: ...
+ def hexdigest(self) -> str: ...
+ def copy(self) -> SHA3_224_Hash: ...
+ def new(self, data: Optional[Buffer]) -> SHA3_224_Hash: ...
+
+def new(__data: Buffer = ..., update_after_digest: bool = ...) -> SHA3_224_Hash: ...
+
+digest_size: int
+block_size: int
diff --git a/lib/Crypto/Hash/SHA3_256.py b/lib/Crypto/Hash/SHA3_256.py
new file mode 100644
index 0000000..b4f11ee
--- /dev/null
+++ b/lib/Crypto/Hash/SHA3_256.py
@@ -0,0 +1,174 @@
+# -*- coding: utf-8 -*-
+#
+# ===================================================================
+# The contents of this file are dedicated to the public domain. To
+# the extent that dedication to the public domain is not available,
+# everyone is granted a worldwide, perpetual, royalty-free,
+# non-exclusive license to exercise all rights associated with the
+# contents of this file for any purpose whatsoever.
+# No rights are reserved.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+# ===================================================================
+
+from Crypto.Util.py3compat import bord
+
+from Crypto.Util._raw_api import (load_pycryptodome_raw_lib,
+ VoidPointer, SmartPointer,
+ create_string_buffer,
+ get_raw_buffer, c_size_t,
+ c_uint8_ptr, c_ubyte)
+
+from Crypto.Hash.keccak import _raw_keccak_lib
+
+class SHA3_256_Hash(object):
+ """A SHA3-256 hash object.
+ Do not instantiate directly.
+ Use the :func:`new` function.
+
+ :ivar oid: ASN.1 Object ID
+ :vartype oid: string
+
+ :ivar digest_size: the size in bytes of the resulting hash
+ :vartype digest_size: integer
+ """
+
+ # The size of the resulting hash in bytes.
+ digest_size = 32
+
+ # ASN.1 Object ID
+ oid = "2.16.840.1.101.3.4.2.8"
+
+ # Input block size for HMAC
+ block_size = 136
+
+ def __init__(self, data, update_after_digest):
+ self._update_after_digest = update_after_digest
+ self._digest_done = False
+ self._padding = 0x06
+
+ state = VoidPointer()
+ result = _raw_keccak_lib.keccak_init(state.address_of(),
+ c_size_t(self.digest_size * 2),
+ c_ubyte(24))
+ if result:
+ raise ValueError("Error %d while instantiating SHA-3/256"
+ % result)
+ self._state = SmartPointer(state.get(),
+ _raw_keccak_lib.keccak_destroy)
+ if data:
+ self.update(data)
+
+ def update(self, data):
+ """Continue hashing of a message by consuming the next chunk of data.
+
+ Args:
+ data (byte string/byte array/memoryview): The next chunk of the message being hashed.
+ """
+
+ if self._digest_done and not self._update_after_digest:
+ raise TypeError("You can only call 'digest' or 'hexdigest' on this object")
+
+ result = _raw_keccak_lib.keccak_absorb(self._state.get(),
+ c_uint8_ptr(data),
+ c_size_t(len(data))
+ )
+ if result:
+ raise ValueError("Error %d while updating SHA-3/256"
+ % result)
+ return self
+
+ def digest(self):
+ """Return the **binary** (non-printable) digest of the message that has been hashed so far.
+
+ :return: The hash digest, computed over the data processed so far.
+ Binary form.
+ :rtype: byte string
+ """
+
+ self._digest_done = True
+
+ bfr = create_string_buffer(self.digest_size)
+ result = _raw_keccak_lib.keccak_digest(self._state.get(),
+ bfr,
+ c_size_t(self.digest_size),
+ c_ubyte(self._padding))
+ if result:
+ raise ValueError("Error %d while instantiating SHA-3/256"
+ % result)
+
+ self._digest_value = get_raw_buffer(bfr)
+ return self._digest_value
+
+ def hexdigest(self):
+ """Return the **printable** digest of the message that has been hashed so far.
+
+ :return: The hash digest, computed over the data processed so far.
+ Hexadecimal encoded.
+ :rtype: string
+ """
+
+ return "".join(["%02x" % bord(x) for x in self.digest()])
+
+ def copy(self):
+ """Return a copy ("clone") of the hash object.
+
+ The copy will have the same internal state as the original hash
+ object.
+ This can be used to efficiently compute the digests of strings that
+ share a common initial substring.
+
+ :return: A hash object of the same type
+ """
+
+ clone = self.new()
+ result = _raw_keccak_lib.keccak_copy(self._state.get(),
+ clone._state.get())
+ if result:
+ raise ValueError("Error %d while copying SHA3-256" % result)
+ return clone
+
+ def new(self, data=None):
+ """Create a fresh SHA3-256 hash object."""
+
+ return type(self)(data, self._update_after_digest)
+
+
+def new(*args, **kwargs):
+ """Create a new hash object.
+
+ Args:
+ data (byte string/byte array/memoryview):
+ The very first chunk of the message to hash.
+ It is equivalent to an early call to :meth:`update`.
+ update_after_digest (boolean):
+ Whether :meth:`digest` can be followed by another :meth:`update`
+ (default: ``False``).
+
+ :Return: A :class:`SHA3_256_Hash` hash object
+ """
+
+ data = kwargs.pop("data", None)
+ update_after_digest = kwargs.pop("update_after_digest", False)
+ if len(args) == 1:
+ if data:
+ raise ValueError("Initial data for hash specified twice")
+ data = args[0]
+
+ if kwargs:
+ raise TypeError("Unknown parameters: " + str(kwargs))
+
+ return SHA3_256_Hash(data, update_after_digest)
+
+# The size of the resulting hash in bytes.
+digest_size = SHA3_256_Hash.digest_size
+
+# Input block size for HMAC
+block_size = 136
diff --git a/lib/Crypto/Hash/SHA3_256.pyi b/lib/Crypto/Hash/SHA3_256.pyi
new file mode 100644
index 0000000..88436bd
--- /dev/null
+++ b/lib/Crypto/Hash/SHA3_256.pyi
@@ -0,0 +1,19 @@
+from typing import Union, Optional
+
+Buffer = Union[bytes, bytearray, memoryview]
+
+class SHA3_256_Hash(object):
+ digest_size: int
+ block_size: int
+ oid: str
+ def __init__(self, data: Optional[Buffer], update_after_digest: bool) -> None: ...
+ def update(self, data: Buffer) -> SHA3_256_Hash: ...
+ def digest(self) -> bytes: ...
+ def hexdigest(self) -> str: ...
+ def copy(self) -> SHA3_256_Hash: ...
+ def new(self, data: Optional[Buffer]) -> SHA3_256_Hash: ...
+
+def new(__data: Buffer = ..., update_after_digest: bool = ...) -> SHA3_256_Hash: ...
+
+digest_size: int
+block_size: int
diff --git a/lib/Crypto/Hash/SHA3_384.py b/lib/Crypto/Hash/SHA3_384.py
new file mode 100644
index 0000000..12f61ce
--- /dev/null
+++ b/lib/Crypto/Hash/SHA3_384.py
@@ -0,0 +1,179 @@
+# -*- coding: utf-8 -*-
+#
+# ===================================================================
+# The contents of this file are dedicated to the public domain. To
+# the extent that dedication to the public domain is not available,
+# everyone is granted a worldwide, perpetual, royalty-free,
+# non-exclusive license to exercise all rights associated with the
+# contents of this file for any purpose whatsoever.
+# No rights are reserved.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+# ===================================================================
+
+from Crypto.Util.py3compat import bord
+
+from Crypto.Util._raw_api import (load_pycryptodome_raw_lib,
+ VoidPointer, SmartPointer,
+ create_string_buffer,
+ get_raw_buffer, c_size_t,
+ c_uint8_ptr, c_ubyte)
+
+from Crypto.Hash.keccak import _raw_keccak_lib
+
+class SHA3_384_Hash(object):
+ """A SHA3-384 hash object.
+ Do not instantiate directly.
+ Use the :func:`new` function.
+
+ :ivar oid: ASN.1 Object ID
+ :vartype oid: string
+
+ :ivar digest_size: the size in bytes of the resulting hash
+ :vartype digest_size: integer
+ """
+
+ # The size of the resulting hash in bytes.
+ digest_size = 48
+
+ # ASN.1 Object ID
+ oid = "2.16.840.1.101.3.4.2.9"
+
+ # Input block size for HMAC
+ block_size = 104
+
+ def __init__(self, data, update_after_digest):
+ self._update_after_digest = update_after_digest
+ self._digest_done = False
+ self._padding = 0x06
+
+ state = VoidPointer()
+ result = _raw_keccak_lib.keccak_init(state.address_of(),
+ c_size_t(self.digest_size * 2),
+ c_ubyte(24))
+ if result:
+ raise ValueError("Error %d while instantiating SHA-3/384"
+ % result)
+ self._state = SmartPointer(state.get(),
+ _raw_keccak_lib.keccak_destroy)
+ if data:
+ self.update(data)
+
+ def update(self, data):
+ """Continue hashing of a message by consuming the next chunk of data.
+
+ Args:
+ data (byte string/byte array/memoryview): The next chunk of the message being hashed.
+ """
+
+ if self._digest_done and not self._update_after_digest:
+ raise TypeError("You can only call 'digest' or 'hexdigest' on this object")
+
+ result = _raw_keccak_lib.keccak_absorb(self._state.get(),
+ c_uint8_ptr(data),
+ c_size_t(len(data)))
+ if result:
+ raise ValueError("Error %d while updating SHA-3/384"
+ % result)
+ return self
+
+ def digest(self):
+ """Return the **binary** (non-printable) digest of the message that has been hashed so far.
+
+ :return: The hash digest, computed over the data processed so far.
+ Binary form.
+ :rtype: byte string
+ """
+
+ self._digest_done = True
+
+ bfr = create_string_buffer(self.digest_size)
+ result = _raw_keccak_lib.keccak_digest(self._state.get(),
+ bfr,
+ c_size_t(self.digest_size),
+ c_ubyte(self._padding))
+ if result:
+ raise ValueError("Error %d while instantiating SHA-3/384"
+ % result)
+
+ self._digest_value = get_raw_buffer(bfr)
+ return self._digest_value
+
+ def hexdigest(self):
+ """Return the **printable** digest of the message that has been hashed so far.
+
+ :return: The hash digest, computed over the data processed so far.
+ Hexadecimal encoded.
+ :rtype: string
+ """
+
+ return "".join(["%02x" % bord(x) for x in self.digest()])
+
+ def copy(self):
+ """Return a copy ("clone") of the hash object.
+
+ The copy will have the same internal state as the original hash
+ object.
+ This can be used to efficiently compute the digests of strings that
+ share a common initial substring.
+
+ :return: A hash object of the same type
+ """
+
+ clone = self.new()
+ result = _raw_keccak_lib.keccak_copy(self._state.get(),
+ clone._state.get())
+ if result:
+ raise ValueError("Error %d while copying SHA3-384" % result)
+ return clone
+
+ def new(self, data=None):
+ """Create a fresh SHA3-256 hash object."""
+
+ return type(self)(data, self._update_after_digest)
+
+
+ def new(self, data=None):
+ """Create a fresh SHA3-384 hash object."""
+
+ return type(self)(data, self._update_after_digest)
+
+
+def new(*args, **kwargs):
+ """Create a new hash object.
+
+ Args:
+ data (byte string/byte array/memoryview):
+ The very first chunk of the message to hash.
+ It is equivalent to an early call to :meth:`update`.
+ update_after_digest (boolean):
+ Whether :meth:`digest` can be followed by another :meth:`update`
+ (default: ``False``).
+
+ :Return: A :class:`SHA3_384_Hash` hash object
+ """
+
+ data = kwargs.pop("data", None)
+ update_after_digest = kwargs.pop("update_after_digest", False)
+ if len(args) == 1:
+ if data:
+ raise ValueError("Initial data for hash specified twice")
+ data = args[0]
+
+ if kwargs:
+ raise TypeError("Unknown parameters: " + str(kwargs))
+
+ return SHA3_384_Hash(data, update_after_digest)
+
+# The size of the resulting hash in bytes.
+digest_size = SHA3_384_Hash.digest_size
+
+# Input block size for HMAC
+block_size = 104
diff --git a/lib/Crypto/Hash/SHA3_384.pyi b/lib/Crypto/Hash/SHA3_384.pyi
new file mode 100644
index 0000000..98d00c6
--- /dev/null
+++ b/lib/Crypto/Hash/SHA3_384.pyi
@@ -0,0 +1,19 @@
+from typing import Union, Optional
+
+Buffer = Union[bytes, bytearray, memoryview]
+
+class SHA3_384_Hash(object):
+ digest_size: int
+ block_size: int
+ oid: str
+ def __init__(self, data: Optional[Buffer], update_after_digest: bool) -> None: ...
+ def update(self, data: Buffer) -> SHA3_384_Hash: ...
+ def digest(self) -> bytes: ...
+ def hexdigest(self) -> str: ...
+ def copy(self) -> SHA3_384_Hash: ...
+ def new(self, data: Optional[Buffer]) -> SHA3_384_Hash: ...
+
+def new(__data: Buffer = ..., update_after_digest: bool = ...) -> SHA3_384_Hash: ...
+
+digest_size: int
+block_size: int
diff --git a/lib/Crypto/Hash/SHA3_512.py b/lib/Crypto/Hash/SHA3_512.py
new file mode 100644
index 0000000..de8880c
--- /dev/null
+++ b/lib/Crypto/Hash/SHA3_512.py
@@ -0,0 +1,174 @@
+# -*- coding: utf-8 -*-
+#
+# ===================================================================
+# The contents of this file are dedicated to the public domain. To
+# the extent that dedication to the public domain is not available,
+# everyone is granted a worldwide, perpetual, royalty-free,
+# non-exclusive license to exercise all rights associated with the
+# contents of this file for any purpose whatsoever.
+# No rights are reserved.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+# ===================================================================
+
+from Crypto.Util.py3compat import bord
+
+from Crypto.Util._raw_api import (load_pycryptodome_raw_lib,
+ VoidPointer, SmartPointer,
+ create_string_buffer,
+ get_raw_buffer, c_size_t,
+ c_uint8_ptr, c_ubyte)
+
+from Crypto.Hash.keccak import _raw_keccak_lib
+
+class SHA3_512_Hash(object):
+ """A SHA3-512 hash object.
+ Do not instantiate directly.
+ Use the :func:`new` function.
+
+ :ivar oid: ASN.1 Object ID
+ :vartype oid: string
+
+ :ivar digest_size: the size in bytes of the resulting hash
+ :vartype digest_size: integer
+ """
+
+ # The size of the resulting hash in bytes.
+ digest_size = 64
+
+ # ASN.1 Object ID
+ oid = "2.16.840.1.101.3.4.2.10"
+
+ # Input block size for HMAC
+ block_size = 72
+
+ def __init__(self, data, update_after_digest):
+ self._update_after_digest = update_after_digest
+ self._digest_done = False
+ self._padding = 0x06
+
+ state = VoidPointer()
+ result = _raw_keccak_lib.keccak_init(state.address_of(),
+ c_size_t(self.digest_size * 2),
+ c_ubyte(24))
+ if result:
+ raise ValueError("Error %d while instantiating SHA-3/512"
+ % result)
+ self._state = SmartPointer(state.get(),
+ _raw_keccak_lib.keccak_destroy)
+ if data:
+ self.update(data)
+
+ def update(self, data):
+ """Continue hashing of a message by consuming the next chunk of data.
+
+ Args:
+ data (byte string/byte array/memoryview): The next chunk of the message being hashed.
+ """
+
+ if self._digest_done and not self._update_after_digest:
+ raise TypeError("You can only call 'digest' or 'hexdigest' on this object")
+
+ result = _raw_keccak_lib.keccak_absorb(self._state.get(),
+ c_uint8_ptr(data),
+ c_size_t(len(data)))
+ if result:
+ raise ValueError("Error %d while updating SHA-3/512"
+ % result)
+ return self
+
+ def digest(self):
+
+ """Return the **binary** (non-printable) digest of the message that has been hashed so far.
+
+ :return: The hash digest, computed over the data processed so far.
+ Binary form.
+ :rtype: byte string
+ """
+
+ self._digest_done = True
+
+ bfr = create_string_buffer(self.digest_size)
+ result = _raw_keccak_lib.keccak_digest(self._state.get(),
+ bfr,
+ c_size_t(self.digest_size),
+ c_ubyte(self._padding))
+ if result:
+ raise ValueError("Error %d while instantiating SHA-3/512"
+ % result)
+
+ self._digest_value = get_raw_buffer(bfr)
+ return self._digest_value
+
+ def hexdigest(self):
+ """Return the **printable** digest of the message that has been hashed so far.
+
+ :return: The hash digest, computed over the data processed so far.
+ Hexadecimal encoded.
+ :rtype: string
+ """
+
+ return "".join(["%02x" % bord(x) for x in self.digest()])
+
+ def copy(self):
+ """Return a copy ("clone") of the hash object.
+
+ The copy will have the same internal state as the original hash
+ object.
+ This can be used to efficiently compute the digests of strings that
+ share a common initial substring.
+
+ :return: A hash object of the same type
+ """
+
+ clone = self.new()
+ result = _raw_keccak_lib.keccak_copy(self._state.get(),
+ clone._state.get())
+ if result:
+ raise ValueError("Error %d while copying SHA3-512" % result)
+ return clone
+
+ def new(self, data=None):
+ """Create a fresh SHA3-521 hash object."""
+
+ return type(self)(data, self._update_after_digest)
+
+
+def new(*args, **kwargs):
+ """Create a new hash object.
+
+ Args:
+ data (byte string/byte array/memoryview):
+ The very first chunk of the message to hash.
+ It is equivalent to an early call to :meth:`update`.
+ update_after_digest (boolean):
+ Whether :meth:`digest` can be followed by another :meth:`update`
+ (default: ``False``).
+
+ :Return: A :class:`SHA3_512_Hash` hash object
+ """
+
+ data = kwargs.pop("data", None)
+ update_after_digest = kwargs.pop("update_after_digest", False)
+ if len(args) == 1:
+ if data:
+ raise ValueError("Initial data for hash specified twice")
+ data = args[0]
+
+ if kwargs:
+ raise TypeError("Unknown parameters: " + str(kwargs))
+
+ return SHA3_512_Hash(data, update_after_digest)
+
+# The size of the resulting hash in bytes.
+digest_size = SHA3_512_Hash.digest_size
+
+# Input block size for HMAC
+block_size = 72
diff --git a/lib/Crypto/Hash/SHA3_512.pyi b/lib/Crypto/Hash/SHA3_512.pyi
new file mode 100644
index 0000000..cdeec16
--- /dev/null
+++ b/lib/Crypto/Hash/SHA3_512.pyi
@@ -0,0 +1,19 @@
+from typing import Union, Optional
+
+Buffer = Union[bytes, bytearray, memoryview]
+
+class SHA3_512_Hash(object):
+ digest_size: int
+ block_size: int
+ oid: str
+ def __init__(self, data: Optional[Buffer], update_after_digest: bool) -> None: ...
+ def update(self, data: Buffer) -> SHA3_512_Hash: ...
+ def digest(self) -> bytes: ...
+ def hexdigest(self) -> str: ...
+ def copy(self) -> SHA3_512_Hash: ...
+ def new(self, data: Optional[Buffer]) -> SHA3_512_Hash: ...
+
+def new(__data: Buffer = ..., update_after_digest: bool = ...) -> SHA3_512_Hash: ...
+
+digest_size: int
+block_size: int
diff --git a/lib/Crypto/Hash/SHA512.py b/lib/Crypto/Hash/SHA512.py
new file mode 100644
index 0000000..403fe45
--- /dev/null
+++ b/lib/Crypto/Hash/SHA512.py
@@ -0,0 +1,204 @@
+# -*- coding: utf-8 -*-
+#
+# ===================================================================
+# The contents of this file are dedicated to the public domain. To
+# the extent that dedication to the public domain is not available,
+# everyone is granted a worldwide, perpetual, royalty-free,
+# non-exclusive license to exercise all rights associated with the
+# contents of this file for any purpose whatsoever.
+# No rights are reserved.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+# ===================================================================
+
+from Crypto.Util.py3compat import bord
+
+from Crypto.Util._raw_api import (load_pycryptodome_raw_lib,
+ VoidPointer, SmartPointer,
+ create_string_buffer,
+ get_raw_buffer, c_size_t,
+ c_uint8_ptr)
+
+_raw_sha512_lib = load_pycryptodome_raw_lib("Crypto.Hash._SHA512",
+ """
+ int SHA512_init(void **shaState,
+ size_t digest_size);
+ int SHA512_destroy(void *shaState);
+ int SHA512_update(void *hs,
+ const uint8_t *buf,
+ size_t len);
+ int SHA512_digest(const void *shaState,
+ uint8_t *digest,
+ size_t digest_size);
+ int SHA512_copy(const void *src, void *dst);
+
+ int SHA512_pbkdf2_hmac_assist(const void *inner,
+ const void *outer,
+ const uint8_t *first_digest,
+ uint8_t *final_digest,
+ size_t iterations,
+ size_t digest_size);
+ """)
+
+class SHA512Hash(object):
+ """A SHA-512 hash object (possibly in its truncated version SHA-512/224 or
+ SHA-512/256.
+ Do not instantiate directly. Use the :func:`new` function.
+
+ :ivar oid: ASN.1 Object ID
+ :vartype oid: string
+
+ :ivar block_size: the size in bytes of the internal message block,
+ input to the compression function
+ :vartype block_size: integer
+
+ :ivar digest_size: the size in bytes of the resulting hash
+ :vartype digest_size: integer
+ """
+
+ # The internal block size of the hash algorithm in bytes.
+ block_size = 128
+
+ def __init__(self, data, truncate):
+ self._truncate = truncate
+
+ if truncate is None:
+ self.oid = "2.16.840.1.101.3.4.2.3"
+ self.digest_size = 64
+ elif truncate == "224":
+ self.oid = "2.16.840.1.101.3.4.2.5"
+ self.digest_size = 28
+ elif truncate == "256":
+ self.oid = "2.16.840.1.101.3.4.2.6"
+ self.digest_size = 32
+ else:
+ raise ValueError("Incorrect truncation length. It must be '224' or '256'.")
+
+ state = VoidPointer()
+ result = _raw_sha512_lib.SHA512_init(state.address_of(),
+ c_size_t(self.digest_size))
+ if result:
+ raise ValueError("Error %d while instantiating SHA-512"
+ % result)
+ self._state = SmartPointer(state.get(),
+ _raw_sha512_lib.SHA512_destroy)
+ if data:
+ self.update(data)
+
+ def update(self, data):
+ """Continue hashing of a message by consuming the next chunk of data.
+
+ Args:
+ data (byte string/byte array/memoryview): The next chunk of the message being hashed.
+ """
+
+ result = _raw_sha512_lib.SHA512_update(self._state.get(),
+ c_uint8_ptr(data),
+ c_size_t(len(data)))
+ if result:
+ raise ValueError("Error %d while hashing data with SHA512"
+ % result)
+
+ def digest(self):
+ """Return the **binary** (non-printable) digest of the message that has been hashed so far.
+
+ :return: The hash digest, computed over the data processed so far.
+ Binary form.
+ :rtype: byte string
+ """
+
+ bfr = create_string_buffer(self.digest_size)
+ result = _raw_sha512_lib.SHA512_digest(self._state.get(),
+ bfr,
+ c_size_t(self.digest_size))
+ if result:
+ raise ValueError("Error %d while making SHA512 digest"
+ % result)
+
+ return get_raw_buffer(bfr)
+
+ def hexdigest(self):
+ """Return the **printable** digest of the message that has been hashed so far.
+
+ :return: The hash digest, computed over the data processed so far.
+ Hexadecimal encoded.
+ :rtype: string
+ """
+
+ return "".join(["%02x" % bord(x) for x in self.digest()])
+
+ def copy(self):
+ """Return a copy ("clone") of the hash object.
+
+ The copy will have the same internal state as the original hash
+ object.
+ This can be used to efficiently compute the digests of strings that
+ share a common initial substring.
+
+ :return: A hash object of the same type
+ """
+
+ clone = SHA512Hash(None, self._truncate)
+ result = _raw_sha512_lib.SHA512_copy(self._state.get(),
+ clone._state.get())
+ if result:
+ raise ValueError("Error %d while copying SHA512" % result)
+ return clone
+
+ def new(self, data=None):
+ """Create a fresh SHA-512 hash object."""
+
+ return SHA512Hash(data, self._truncate)
+
+
+def new(data=None, truncate=None):
+ """Create a new hash object.
+
+ Args:
+ data (bytes/bytearray/memoryview):
+ Optional. The very first chunk of the message to hash.
+ It is equivalent to an early call to :meth:`SHA512Hash.update`.
+ truncate (string):
+ Optional. The desired length of the digest. It can be either "224" or
+ "256". If not present, the digest is 512 bits long.
+ Passing this parameter is **not** equivalent to simply truncating
+ the output digest.
+
+ :Return: A :class:`SHA512Hash` hash object
+ """
+
+ return SHA512Hash(data, truncate)
+
+
+# The size of the full SHA-512 hash in bytes.
+digest_size = 64
+
+# The internal block size of the hash algorithm in bytes.
+block_size = 128
+
+
+def _pbkdf2_hmac_assist(inner, outer, first_digest, iterations):
+ """Compute the expensive inner loop in PBKDF-HMAC."""
+
+ assert iterations > 0
+
+ bfr = create_string_buffer(len(first_digest));
+ result = _raw_sha512_lib.SHA512_pbkdf2_hmac_assist(
+ inner._state.get(),
+ outer._state.get(),
+ first_digest,
+ bfr,
+ c_size_t(iterations),
+ c_size_t(len(first_digest)))
+
+ if result:
+ raise ValueError("Error %d with PBKDF2-HMAC assist for SHA512" % result)
+
+ return get_raw_buffer(bfr)
diff --git a/lib/Crypto/Hash/SHA512.pyi b/lib/Crypto/Hash/SHA512.pyi
new file mode 100644
index 0000000..f219ee9
--- /dev/null
+++ b/lib/Crypto/Hash/SHA512.pyi
@@ -0,0 +1,22 @@
+from typing import Union, Optional
+
+Buffer = Union[bytes, bytearray, memoryview]
+
+class SHA512Hash(object):
+ digest_size: int
+ block_size: int
+ oid: str
+
+ def __init__(self,
+ data: Optional[Buffer],
+ truncate: Optional[str]) -> None: ...
+ def update(self, data: Buffer) -> None: ...
+ def digest(self) -> bytes: ...
+ def hexdigest(self) -> str: ...
+ def copy(self) -> SHA512Hash: ...
+ def new(self, data: Optional[Buffer] = ...) -> SHA512Hash: ...
+
+def new(data: Optional[Buffer] = ...,
+ truncate: Optional[str] = ...) -> SHA512Hash: ...
+digest_size: int
+block_size: int
diff --git a/lib/Crypto/Hash/SHAKE128.py b/lib/Crypto/Hash/SHAKE128.py
new file mode 100644
index 0000000..894a41b
--- /dev/null
+++ b/lib/Crypto/Hash/SHAKE128.py
@@ -0,0 +1,129 @@
+# ===================================================================
+#
+# Copyright (c) 2015, Legrandin <helderijs@gmail.com>
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+# ===================================================================
+
+from Crypto.Util.py3compat import bord
+
+from Crypto.Util._raw_api import (load_pycryptodome_raw_lib,
+ VoidPointer, SmartPointer,
+ create_string_buffer,
+ get_raw_buffer, c_size_t,
+ c_uint8_ptr, c_ubyte)
+
+from Crypto.Hash.keccak import _raw_keccak_lib
+
+class SHAKE128_XOF(object):
+ """A SHAKE128 hash object.
+ Do not instantiate directly.
+ Use the :func:`new` function.
+
+ :ivar oid: ASN.1 Object ID
+ :vartype oid: string
+ """
+
+ # ASN.1 Object ID
+ oid = "2.16.840.1.101.3.4.2.11"
+
+ def __init__(self, data=None):
+ state = VoidPointer()
+ result = _raw_keccak_lib.keccak_init(state.address_of(),
+ c_size_t(32),
+ c_ubyte(24))
+ if result:
+ raise ValueError("Error %d while instantiating SHAKE128"
+ % result)
+ self._state = SmartPointer(state.get(),
+ _raw_keccak_lib.keccak_destroy)
+ self._is_squeezing = False
+ self._padding = 0x1F
+ if data:
+ self.update(data)
+
+ def update(self, data):
+ """Continue hashing of a message by consuming the next chunk of data.
+
+ Args:
+ data (byte string/byte array/memoryview): The next chunk of the message being hashed.
+ """
+
+ if self._is_squeezing:
+ raise TypeError("You cannot call 'update' after the first 'read'")
+
+ result = _raw_keccak_lib.keccak_absorb(self._state.get(),
+ c_uint8_ptr(data),
+ c_size_t(len(data)))
+ if result:
+ raise ValueError("Error %d while updating SHAKE128 state"
+ % result)
+ return self
+
+ def read(self, length):
+ """
+ Compute the next piece of XOF output.
+
+ .. note::
+ You cannot use :meth:`update` anymore after the first call to
+ :meth:`read`.
+
+ Args:
+ length (integer): the amount of bytes this method must return
+
+ :return: the next piece of XOF output (of the given length)
+ :rtype: byte string
+ """
+
+ self._is_squeezing = True
+ bfr = create_string_buffer(length)
+ result = _raw_keccak_lib.keccak_squeeze(self._state.get(),
+ bfr,
+ c_size_t(length),
+ c_ubyte(self._padding))
+ if result:
+ raise ValueError("Error %d while extracting from SHAKE128"
+ % result)
+
+ return get_raw_buffer(bfr)
+
+ def new(self, data=None):
+ return type(self)(data=data)
+
+
+def new(data=None):
+ """Return a fresh instance of a SHAKE128 object.
+
+ Args:
+ data (bytes/bytearray/memoryview):
+ The very first chunk of the message to hash.
+ It is equivalent to an early call to :meth:`update`.
+ Optional.
+
+ :Return: A :class:`SHAKE128_XOF` object
+ """
+
+ return SHAKE128_XOF(data=data)
diff --git a/lib/Crypto/Hash/SHAKE128.pyi b/lib/Crypto/Hash/SHAKE128.pyi
new file mode 100644
index 0000000..f618881
--- /dev/null
+++ b/lib/Crypto/Hash/SHAKE128.pyi
@@ -0,0 +1,13 @@
+from typing import Union, Optional
+
+Buffer = Union[bytes, bytearray, memoryview]
+
+class SHAKE128_XOF(object):
+ oid: str
+ def __init__(self,
+ data: Optional[Buffer] = ...) -> None: ...
+ def update(self, data: Buffer) -> SHAKE128_XOF: ...
+ def read(self, length: int) -> bytes: ...
+ def new(self, data: Optional[Buffer] = ...) -> SHAKE128_XOF: ...
+
+def new(data: Optional[Buffer] = ...) -> SHAKE128_XOF: ...
diff --git a/lib/Crypto/Hash/SHAKE256.py b/lib/Crypto/Hash/SHAKE256.py
new file mode 100644
index 0000000..f75b822
--- /dev/null
+++ b/lib/Crypto/Hash/SHAKE256.py
@@ -0,0 +1,130 @@
+# ===================================================================
+#
+# Copyright (c) 2015, Legrandin <helderijs@gmail.com>
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+# ===================================================================
+
+from Crypto.Util.py3compat import bord
+
+from Crypto.Util._raw_api import (load_pycryptodome_raw_lib,
+ VoidPointer, SmartPointer,
+ create_string_buffer,
+ get_raw_buffer, c_size_t,
+ c_uint8_ptr, c_ubyte)
+
+from Crypto.Hash.keccak import _raw_keccak_lib
+
+class SHAKE256_XOF(object):
+ """A SHAKE256 hash object.
+ Do not instantiate directly.
+ Use the :func:`new` function.
+
+ :ivar oid: ASN.1 Object ID
+ :vartype oid: string
+ """
+
+ # ASN.1 Object ID
+ oid = "2.16.840.1.101.3.4.2.12"
+
+ def __init__(self, data=None):
+ state = VoidPointer()
+ result = _raw_keccak_lib.keccak_init(state.address_of(),
+ c_size_t(64),
+ c_ubyte(24))
+ if result:
+ raise ValueError("Error %d while instantiating SHAKE256"
+ % result)
+ self._state = SmartPointer(state.get(),
+ _raw_keccak_lib.keccak_destroy)
+ self._is_squeezing = False
+ self._padding = 0x1F
+
+ if data:
+ self.update(data)
+
+ def update(self, data):
+ """Continue hashing of a message by consuming the next chunk of data.
+
+ Args:
+ data (byte string/byte array/memoryview): The next chunk of the message being hashed.
+ """
+
+ if self._is_squeezing:
+ raise TypeError("You cannot call 'update' after the first 'read'")
+
+ result = _raw_keccak_lib.keccak_absorb(self._state.get(),
+ c_uint8_ptr(data),
+ c_size_t(len(data)))
+ if result:
+ raise ValueError("Error %d while updating SHAKE256 state"
+ % result)
+ return self
+
+ def read(self, length):
+ """
+ Compute the next piece of XOF output.
+
+ .. note::
+ You cannot use :meth:`update` anymore after the first call to
+ :meth:`read`.
+
+ Args:
+ length (integer): the amount of bytes this method must return
+
+ :return: the next piece of XOF output (of the given length)
+ :rtype: byte string
+ """
+
+ self._is_squeezing = True
+ bfr = create_string_buffer(length)
+ result = _raw_keccak_lib.keccak_squeeze(self._state.get(),
+ bfr,
+ c_size_t(length),
+ c_ubyte(self._padding))
+ if result:
+ raise ValueError("Error %d while extracting from SHAKE256"
+ % result)
+
+ return get_raw_buffer(bfr)
+
+ def new(self, data=None):
+ return type(self)(data=data)
+
+
+def new(data=None):
+ """Return a fresh instance of a SHAKE256 object.
+
+ Args:
+ data (bytes/bytearray/memoryview):
+ The very first chunk of the message to hash.
+ It is equivalent to an early call to :meth:`update`.
+ Optional.
+
+ :Return: A :class:`SHAKE256_XOF` object
+ """
+
+ return SHAKE256_XOF(data=data)
diff --git a/lib/Crypto/Hash/SHAKE256.pyi b/lib/Crypto/Hash/SHAKE256.pyi
new file mode 100644
index 0000000..029347a
--- /dev/null
+++ b/lib/Crypto/Hash/SHAKE256.pyi
@@ -0,0 +1,13 @@
+from typing import Union, Optional
+
+Buffer = Union[bytes, bytearray, memoryview]
+
+class SHAKE256_XOF(object):
+ oid: str
+ def __init__(self,
+ data: Optional[Buffer] = ...) -> None: ...
+ def update(self, data: Buffer) -> SHAKE256_XOF: ...
+ def read(self, length: int) -> bytes: ...
+ def new(self, data: Optional[Buffer] = ...) -> SHAKE256_XOF: ...
+
+def new(data: Optional[Buffer] = ...) -> SHAKE256_XOF: ...
diff --git a/lib/Crypto/Hash/TupleHash128.py b/lib/Crypto/Hash/TupleHash128.py
new file mode 100644
index 0000000..8fd3283
--- /dev/null
+++ b/lib/Crypto/Hash/TupleHash128.py
@@ -0,0 +1,138 @@
+# ===================================================================
+#
+# Copyright (c) 2021, Legrandin <helderijs@gmail.com>
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+# ===================================================================
+
+from Crypto.Util.py3compat import bord, is_bytes, tobytes
+
+from . import cSHAKE128
+from .cSHAKE128 import _encode_str, _right_encode
+
+
+class TupleHash(object):
+ """A Tuple hash object.
+ Do not instantiate directly.
+ Use the :func:`new` function.
+ """
+
+ def __init__(self, custom, cshake, digest_size):
+
+ self.digest_size = digest_size
+
+ self._cshake = cshake._new(b'', custom, b'TupleHash')
+ self._digest = None
+
+ def update(self, data):
+ """Authenticate the next byte string in the tuple.
+
+ Args:
+ data (bytes/bytearray/memoryview): The next byte string.
+ """
+
+ if self._digest is not None:
+ raise TypeError("You cannot call 'update' after 'digest' or 'hexdigest'")
+
+ if not is_bytes(data):
+ raise TypeError("You can only call 'update' on bytes")
+
+ self._cshake.update(_encode_str(tobytes(data)))
+
+ return self
+
+ def digest(self):
+ """Return the **binary** (non-printable) digest of the tuple of byte strings.
+
+ :return: The hash digest. Binary form.
+ :rtype: byte string
+ """
+
+ if self._digest is None:
+ self._cshake.update(_right_encode(self.digest_size * 8))
+ self._digest = self._cshake.read(self.digest_size)
+
+ return self._digest
+
+ def hexdigest(self):
+ """Return the **printable** digest of the tuple of byte strings.
+
+ :return: The hash digest. Hexadecimal encoded.
+ :rtype: string
+ """
+
+ return "".join(["%02x" % bord(x) for x in tuple(self.digest())])
+
+ def new(self, **kwargs):
+ """Return a new instance of a TupleHash object.
+ See :func:`new`.
+ """
+
+ if "digest_bytes" not in kwargs and "digest_bits" not in kwargs:
+ kwargs["digest_bytes"] = self.digest_size
+
+ return new(**kwargs)
+
+
+def new(**kwargs):
+ """Create a new TupleHash128 object.
+
+ Args:
+ digest_bytes (integer):
+ Optional. The size of the digest, in bytes.
+ Default is 64. Minimum is 8.
+ digest_bits (integer):
+ Optional and alternative to ``digest_bytes``.
+ The size of the digest, in bits (and in steps of 8).
+ Default is 512. Minimum is 64.
+ custom (bytes):
+ Optional.
+ A customization bytestring (``S`` in SP 800-185).
+
+ :Return: A :class:`TupleHash` object
+ """
+
+ digest_bytes = kwargs.pop("digest_bytes", None)
+ digest_bits = kwargs.pop("digest_bits", None)
+ if None not in (digest_bytes, digest_bits):
+ raise TypeError("Only one digest parameter must be provided")
+ if (None, None) == (digest_bytes, digest_bits):
+ digest_bytes = 64
+ if digest_bytes is not None:
+ if digest_bytes < 8:
+ raise ValueError("'digest_bytes' must be at least 8")
+ else:
+ if digest_bits < 64 or digest_bits % 8:
+ raise ValueError("'digest_bytes' must be at least 64 "
+ "in steps of 8")
+ digest_bytes = digest_bits // 8
+
+ custom = kwargs.pop("custom", b'')
+
+ if kwargs:
+ raise TypeError("Unknown parameters: " + str(kwargs))
+
+ return TupleHash(custom, cSHAKE128, digest_bytes)
diff --git a/lib/Crypto/Hash/TupleHash128.pyi b/lib/Crypto/Hash/TupleHash128.pyi
new file mode 100644
index 0000000..3b1e81e
--- /dev/null
+++ b/lib/Crypto/Hash/TupleHash128.pyi
@@ -0,0 +1,22 @@
+from typing import Any, Union
+from types import ModuleType
+
+Buffer = Union[bytes, bytearray, memoryview]
+
+class TupleHash(object):
+ digest_size: int
+ def __init__(self,
+ custom: bytes,
+ cshake: ModuleType,
+ digest_size: int) -> None: ...
+ def update(self, data: Buffer) -> TupleHash: ...
+ def digest(self) -> bytes: ...
+ def hexdigest(self) -> str: ...
+ def new(self,
+ digest_bytes: int = ...,
+ digest_bits: int = ...,
+ custom: int = ...) -> TupleHash: ...
+
+def new(digest_bytes: int = ...,
+ digest_bits: int = ...,
+ custom: int = ...) -> TupleHash: ...
diff --git a/lib/Crypto/Hash/TupleHash256.py b/lib/Crypto/Hash/TupleHash256.py
new file mode 100644
index 0000000..9b4fba0
--- /dev/null
+++ b/lib/Crypto/Hash/TupleHash256.py
@@ -0,0 +1,73 @@
+# ===================================================================
+#
+# Copyright (c) 2021, Legrandin <helderijs@gmail.com>
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+# ===================================================================
+
+from . import cSHAKE256
+from .TupleHash128 import TupleHash
+
+
+def new(**kwargs):
+ """Create a new TupleHash256 object.
+
+ Args:
+ digest_bytes (integer):
+ Optional. The size of the digest, in bytes.
+ Default is 64. Minimum is 8.
+ digest_bits (integer):
+ Optional and alternative to ``digest_bytes``.
+ The size of the digest, in bits (and in steps of 8).
+ Default is 512. Minimum is 64.
+ custom (bytes):
+ Optional.
+ A customization bytestring (``S`` in SP 800-185).
+
+ :Return: A :class:`TupleHash` object
+ """
+
+ digest_bytes = kwargs.pop("digest_bytes", None)
+ digest_bits = kwargs.pop("digest_bits", None)
+ if None not in (digest_bytes, digest_bits):
+ raise TypeError("Only one digest parameter must be provided")
+ if (None, None) == (digest_bytes, digest_bits):
+ digest_bytes = 64
+ if digest_bytes is not None:
+ if digest_bytes < 8:
+ raise ValueError("'digest_bytes' must be at least 8")
+ else:
+ if digest_bits < 64 or digest_bits % 8:
+ raise ValueError("'digest_bytes' must be at least 64 "
+ "in steps of 8")
+ digest_bytes = digest_bits // 8
+
+ custom = kwargs.pop("custom", b'')
+
+ if kwargs:
+ raise TypeError("Unknown parameters: " + str(kwargs))
+
+ return TupleHash(custom, cSHAKE256, digest_bytes)
diff --git a/lib/Crypto/Hash/TupleHash256.pyi b/lib/Crypto/Hash/TupleHash256.pyi
new file mode 100644
index 0000000..82d943f
--- /dev/null
+++ b/lib/Crypto/Hash/TupleHash256.pyi
@@ -0,0 +1,5 @@
+from .TupleHash128 import TupleHash
+
+def new(digest_bytes: int = ...,
+ digest_bits: int = ...,
+ custom: int = ...) -> TupleHash: ...
diff --git a/lib/Crypto/Hash/_BLAKE2b.abi3.so b/lib/Crypto/Hash/_BLAKE2b.abi3.so
new file mode 100755
index 0000000..d7bc5bf
--- /dev/null
+++ b/lib/Crypto/Hash/_BLAKE2b.abi3.so
Binary files differ
diff --git a/lib/Crypto/Hash/_BLAKE2s.abi3.so b/lib/Crypto/Hash/_BLAKE2s.abi3.so
new file mode 100755
index 0000000..1e7ac6c
--- /dev/null
+++ b/lib/Crypto/Hash/_BLAKE2s.abi3.so
Binary files differ
diff --git a/lib/Crypto/Hash/_MD2.abi3.so b/lib/Crypto/Hash/_MD2.abi3.so
new file mode 100755
index 0000000..7671f60
--- /dev/null
+++ b/lib/Crypto/Hash/_MD2.abi3.so
Binary files differ
diff --git a/lib/Crypto/Hash/_MD4.abi3.so b/lib/Crypto/Hash/_MD4.abi3.so
new file mode 100755
index 0000000..ddd85d1
--- /dev/null
+++ b/lib/Crypto/Hash/_MD4.abi3.so
Binary files differ
diff --git a/lib/Crypto/Hash/_MD5.abi3.so b/lib/Crypto/Hash/_MD5.abi3.so
new file mode 100755
index 0000000..a8b6683
--- /dev/null
+++ b/lib/Crypto/Hash/_MD5.abi3.so
Binary files differ
diff --git a/lib/Crypto/Hash/_RIPEMD160.abi3.so b/lib/Crypto/Hash/_RIPEMD160.abi3.so
new file mode 100755
index 0000000..d37c515
--- /dev/null
+++ b/lib/Crypto/Hash/_RIPEMD160.abi3.so
Binary files differ
diff --git a/lib/Crypto/Hash/_SHA1.abi3.so b/lib/Crypto/Hash/_SHA1.abi3.so
new file mode 100755
index 0000000..7080984
--- /dev/null
+++ b/lib/Crypto/Hash/_SHA1.abi3.so
Binary files differ
diff --git a/lib/Crypto/Hash/_SHA224.abi3.so b/lib/Crypto/Hash/_SHA224.abi3.so
new file mode 100755
index 0000000..5ceeaa5
--- /dev/null
+++ b/lib/Crypto/Hash/_SHA224.abi3.so
Binary files differ
diff --git a/lib/Crypto/Hash/_SHA256.abi3.so b/lib/Crypto/Hash/_SHA256.abi3.so
new file mode 100755
index 0000000..0b4f41b
--- /dev/null
+++ b/lib/Crypto/Hash/_SHA256.abi3.so
Binary files differ
diff --git a/lib/Crypto/Hash/_SHA384.abi3.so b/lib/Crypto/Hash/_SHA384.abi3.so
new file mode 100755
index 0000000..8f87972
--- /dev/null
+++ b/lib/Crypto/Hash/_SHA384.abi3.so
Binary files differ
diff --git a/lib/Crypto/Hash/_SHA512.abi3.so b/lib/Crypto/Hash/_SHA512.abi3.so
new file mode 100755
index 0000000..804ee9e
--- /dev/null
+++ b/lib/Crypto/Hash/_SHA512.abi3.so
Binary files differ
diff --git a/lib/Crypto/Hash/__init__.py b/lib/Crypto/Hash/__init__.py
new file mode 100644
index 0000000..4bda084
--- /dev/null
+++ b/lib/Crypto/Hash/__init__.py
@@ -0,0 +1,24 @@
+# -*- coding: utf-8 -*-
+#
+# ===================================================================
+# The contents of this file are dedicated to the public domain. To
+# the extent that dedication to the public domain is not available,
+# everyone is granted a worldwide, perpetual, royalty-free,
+# non-exclusive license to exercise all rights associated with the
+# contents of this file for any purpose whatsoever.
+# No rights are reserved.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+# ===================================================================
+
+__all__ = ['HMAC', 'MD2', 'MD4', 'MD5', 'RIPEMD160', 'SHA1',
+ 'SHA224', 'SHA256', 'SHA384', 'SHA512', 'CMAC', 'Poly1305',
+ 'cSHAKE128', 'cSHAKE256', 'KMAC128', 'KMAC256',
+ 'TupleHash128', 'TupleHash256', 'KangarooTwelve']
diff --git a/lib/Crypto/Hash/__init__.pyi b/lib/Crypto/Hash/__init__.pyi
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/lib/Crypto/Hash/__init__.pyi
diff --git a/lib/Crypto/Hash/_ghash_clmul.abi3.so b/lib/Crypto/Hash/_ghash_clmul.abi3.so
new file mode 100755
index 0000000..77163c1
--- /dev/null
+++ b/lib/Crypto/Hash/_ghash_clmul.abi3.so
Binary files differ
diff --git a/lib/Crypto/Hash/_ghash_portable.abi3.so b/lib/Crypto/Hash/_ghash_portable.abi3.so
new file mode 100755
index 0000000..702cd16
--- /dev/null
+++ b/lib/Crypto/Hash/_ghash_portable.abi3.so
Binary files differ
diff --git a/lib/Crypto/Hash/_keccak.abi3.so b/lib/Crypto/Hash/_keccak.abi3.so
new file mode 100755
index 0000000..aaa33d7
--- /dev/null
+++ b/lib/Crypto/Hash/_keccak.abi3.so
Binary files differ
diff --git a/lib/Crypto/Hash/_poly1305.abi3.so b/lib/Crypto/Hash/_poly1305.abi3.so
new file mode 100755
index 0000000..a795027
--- /dev/null
+++ b/lib/Crypto/Hash/_poly1305.abi3.so
Binary files differ
diff --git a/lib/Crypto/Hash/cSHAKE128.py b/lib/Crypto/Hash/cSHAKE128.py
new file mode 100644
index 0000000..92a4e5c
--- /dev/null
+++ b/lib/Crypto/Hash/cSHAKE128.py
@@ -0,0 +1,187 @@
+# ===================================================================
+#
+# Copyright (c) 2021, Legrandin <helderijs@gmail.com>
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+# ===================================================================
+
+from Crypto.Util.py3compat import bchr
+
+from Crypto.Util._raw_api import (VoidPointer, SmartPointer,
+ create_string_buffer,
+ get_raw_buffer, c_size_t,
+ c_uint8_ptr, c_ubyte)
+
+from Crypto.Util.number import long_to_bytes
+
+from Crypto.Hash.keccak import _raw_keccak_lib
+
+
+def _left_encode(x):
+ """Left encode function as defined in NIST SP 800-185"""
+
+ assert (x < (1 << 2040) and x >= 0)
+
+ # Get number of bytes needed to represent this integer.
+ num = 1 if x == 0 else (x.bit_length() + 7) // 8
+
+ return bchr(num) + long_to_bytes(x)
+
+
+def _right_encode(x):
+ """Right encode function as defined in NIST SP 800-185"""
+
+ assert (x < (1 << 2040) and x >= 0)
+
+ # Get number of bytes needed to represent this integer.
+ num = 1 if x == 0 else (x.bit_length() + 7) // 8
+
+ return long_to_bytes(x) + bchr(num)
+
+
+def _encode_str(x):
+ """Encode string function as defined in NIST SP 800-185"""
+
+ bitlen = len(x) * 8
+ if bitlen >= (1 << 2040):
+ raise ValueError("String too large to encode in cSHAKE")
+
+ return _left_encode(bitlen) + x
+
+
+def _bytepad(x, length):
+ """Zero pad byte string as defined in NIST SP 800-185"""
+
+ to_pad = _left_encode(length) + x
+
+ # Note: this implementation works with byte aligned strings,
+ # hence no additional bit padding is needed at this point.
+ npad = (length - len(to_pad) % length) % length
+
+ return to_pad + b'\x00' * npad
+
+
+class cSHAKE_XOF(object):
+ """A cSHAKE hash object.
+ Do not instantiate directly.
+ Use the :func:`new` function.
+ """
+
+ def __init__(self, data, custom, capacity, function):
+ state = VoidPointer()
+
+ if custom or function:
+ prefix_unpad = _encode_str(function) + _encode_str(custom)
+ prefix = _bytepad(prefix_unpad, (1600 - capacity)//8)
+ self._padding = 0x04
+ else:
+ prefix = None
+ self._padding = 0x1F # for SHAKE
+
+ result = _raw_keccak_lib.keccak_init(state.address_of(),
+ c_size_t(capacity//8),
+ c_ubyte(24))
+ if result:
+ raise ValueError("Error %d while instantiating cSHAKE"
+ % result)
+ self._state = SmartPointer(state.get(),
+ _raw_keccak_lib.keccak_destroy)
+ self._is_squeezing = False
+
+ if prefix:
+ self.update(prefix)
+
+ if data:
+ self.update(data)
+
+ def update(self, data):
+ """Continue hashing of a message by consuming the next chunk of data.
+
+ Args:
+ data (byte string/byte array/memoryview): The next chunk of the message being hashed.
+ """
+
+ if self._is_squeezing:
+ raise TypeError("You cannot call 'update' after the first 'read'")
+
+ result = _raw_keccak_lib.keccak_absorb(self._state.get(),
+ c_uint8_ptr(data),
+ c_size_t(len(data)))
+ if result:
+ raise ValueError("Error %d while updating %s state"
+ % (result, self.name))
+ return self
+
+ def read(self, length):
+ """
+ Compute the next piece of XOF output.
+
+ .. note::
+ You cannot use :meth:`update` anymore after the first call to
+ :meth:`read`.
+
+ Args:
+ length (integer): the amount of bytes this method must return
+
+ :return: the next piece of XOF output (of the given length)
+ :rtype: byte string
+ """
+
+ self._is_squeezing = True
+ bfr = create_string_buffer(length)
+ result = _raw_keccak_lib.keccak_squeeze(self._state.get(),
+ bfr,
+ c_size_t(length),
+ c_ubyte(self._padding))
+ if result:
+ raise ValueError("Error %d while extracting from %s"
+ % (result, self.name))
+
+ return get_raw_buffer(bfr)
+
+
+def _new(data, custom, function):
+ # Use Keccak[256]
+ return cSHAKE_XOF(data, custom, 256, function)
+
+
+def new(data=None, custom=None):
+ """Return a fresh instance of a cSHAKE128 object.
+
+ Args:
+ data (bytes/bytearray/memoryview):
+ Optional.
+ The very first chunk of the message to hash.
+ It is equivalent to an early call to :meth:`update`.
+ custom (bytes):
+ Optional.
+ A customization bytestring (``S`` in SP 800-185).
+
+ :Return: A :class:`cSHAKE_XOF` object
+ """
+
+ # Use Keccak[256]
+ return cSHAKE_XOF(data, custom, 256, b'')
diff --git a/lib/Crypto/Hash/cSHAKE128.pyi b/lib/Crypto/Hash/cSHAKE128.pyi
new file mode 100644
index 0000000..1452fea
--- /dev/null
+++ b/lib/Crypto/Hash/cSHAKE128.pyi
@@ -0,0 +1,14 @@
+from typing import Union, Optional
+
+Buffer = Union[bytes, bytearray, memoryview]
+
+class cSHAKE_XOF(object):
+ def __init__(self,
+ data: Optional[Buffer] = ...,
+ function: Optional[bytes] = ...,
+ custom: Optional[bytes] = ...) -> None: ...
+ def update(self, data: Buffer) -> cSHAKE_XOF: ...
+ def read(self, length: int) -> bytes: ...
+
+def new(data: Optional[Buffer] = ...,
+ custom: Optional[Buffer] = ...) -> cSHAKE_XOF: ...
diff --git a/lib/Crypto/Hash/cSHAKE256.py b/lib/Crypto/Hash/cSHAKE256.py
new file mode 100644
index 0000000..b3b31d6
--- /dev/null
+++ b/lib/Crypto/Hash/cSHAKE256.py
@@ -0,0 +1,56 @@
+# ===================================================================
+#
+# Copyright (c) 2021, Legrandin <helderijs@gmail.com>
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+# ===================================================================
+
+from Crypto.Util._raw_api import c_size_t
+from Crypto.Hash.cSHAKE128 import cSHAKE_XOF
+
+
+def _new(data, custom, function):
+ # Use Keccak[512]
+ return cSHAKE_XOF(data, custom, 512, function)
+
+
+def new(data=None, custom=None):
+ """Return a fresh instance of a cSHAKE256 object.
+
+ Args:
+ data (bytes/bytearray/memoryview):
+ The very first chunk of the message to hash.
+ It is equivalent to an early call to :meth:`update`.
+ Optional.
+ custom (bytes):
+ Optional.
+ A customization bytestring (``S`` in SP 800-185).
+
+ :Return: A :class:`cSHAKE_XOF` object
+ """
+
+ # Use Keccak[512]
+ return cSHAKE_XOF(data, custom, 512, b'')
diff --git a/lib/Crypto/Hash/cSHAKE256.pyi b/lib/Crypto/Hash/cSHAKE256.pyi
new file mode 100644
index 0000000..205b816
--- /dev/null
+++ b/lib/Crypto/Hash/cSHAKE256.pyi
@@ -0,0 +1,8 @@
+from typing import Union, Optional
+
+from Crypto.Hash.cSHAKE128 import cSHAKE_XOF
+
+Buffer = Union[bytes, bytearray, memoryview]
+
+def new(data: Optional[Buffer] = ...,
+ custom: Optional[Buffer] = ...) -> cSHAKE_XOF: ...
diff --git a/lib/Crypto/Hash/keccak.py b/lib/Crypto/Hash/keccak.py
new file mode 100644
index 0000000..f3f8bb5
--- /dev/null
+++ b/lib/Crypto/Hash/keccak.py
@@ -0,0 +1,181 @@
+# ===================================================================
+#
+# Copyright (c) 2015, Legrandin <helderijs@gmail.com>
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+# ===================================================================
+
+from Crypto.Util.py3compat import bord
+
+from Crypto.Util._raw_api import (load_pycryptodome_raw_lib,
+ VoidPointer, SmartPointer,
+ create_string_buffer,
+ get_raw_buffer, c_size_t,
+ c_uint8_ptr, c_ubyte)
+
+_raw_keccak_lib = load_pycryptodome_raw_lib("Crypto.Hash._keccak",
+ """
+ int keccak_init(void **state,
+ size_t capacity_bytes,
+ uint8_t rounds);
+ int keccak_destroy(void *state);
+ int keccak_absorb(void *state,
+ const uint8_t *in,
+ size_t len);
+ int keccak_squeeze(const void *state,
+ uint8_t *out,
+ size_t len,
+ uint8_t padding);
+ int keccak_digest(void *state,
+ uint8_t *digest,
+ size_t len,
+ uint8_t padding);
+ int keccak_copy(const void *src, void *dst);
+ int keccak_reset(void *state);
+ """)
+
+class Keccak_Hash(object):
+ """A Keccak hash object.
+ Do not instantiate directly.
+ Use the :func:`new` function.
+
+ :ivar digest_size: the size in bytes of the resulting hash
+ :vartype digest_size: integer
+ """
+
+ def __init__(self, data, digest_bytes, update_after_digest):
+ # The size of the resulting hash in bytes.
+ self.digest_size = digest_bytes
+
+ self._update_after_digest = update_after_digest
+ self._digest_done = False
+ self._padding = 0x01
+
+ state = VoidPointer()
+ result = _raw_keccak_lib.keccak_init(state.address_of(),
+ c_size_t(self.digest_size * 2),
+ c_ubyte(24))
+ if result:
+ raise ValueError("Error %d while instantiating keccak" % result)
+ self._state = SmartPointer(state.get(),
+ _raw_keccak_lib.keccak_destroy)
+ if data:
+ self.update(data)
+
+ def update(self, data):
+ """Continue hashing of a message by consuming the next chunk of data.
+
+ Args:
+ data (byte string/byte array/memoryview): The next chunk of the message being hashed.
+ """
+
+ if self._digest_done and not self._update_after_digest:
+ raise TypeError("You can only call 'digest' or 'hexdigest' on this object")
+
+ result = _raw_keccak_lib.keccak_absorb(self._state.get(),
+ c_uint8_ptr(data),
+ c_size_t(len(data)))
+ if result:
+ raise ValueError("Error %d while updating keccak" % result)
+ return self
+
+ def digest(self):
+ """Return the **binary** (non-printable) digest of the message that has been hashed so far.
+
+ :return: The hash digest, computed over the data processed so far.
+ Binary form.
+ :rtype: byte string
+ """
+
+ self._digest_done = True
+ bfr = create_string_buffer(self.digest_size)
+ result = _raw_keccak_lib.keccak_digest(self._state.get(),
+ bfr,
+ c_size_t(self.digest_size),
+ c_ubyte(self._padding))
+ if result:
+ raise ValueError("Error %d while squeezing keccak" % result)
+
+ return get_raw_buffer(bfr)
+
+ def hexdigest(self):
+ """Return the **printable** digest of the message that has been hashed so far.
+
+ :return: The hash digest, computed over the data processed so far.
+ Hexadecimal encoded.
+ :rtype: string
+ """
+
+ return "".join(["%02x" % bord(x) for x in self.digest()])
+
+ def new(self, **kwargs):
+ """Create a fresh Keccak hash object."""
+
+ if "digest_bytes" not in kwargs and "digest_bits" not in kwargs:
+ kwargs["digest_bytes"] = self.digest_size
+
+ return new(**kwargs)
+
+
+def new(**kwargs):
+ """Create a new hash object.
+
+ Args:
+ data (bytes/bytearray/memoryview):
+ The very first chunk of the message to hash.
+ It is equivalent to an early call to :meth:`Keccak_Hash.update`.
+ digest_bytes (integer):
+ The size of the digest, in bytes (28, 32, 48, 64).
+ digest_bits (integer):
+ The size of the digest, in bits (224, 256, 384, 512).
+ update_after_digest (boolean):
+ Whether :meth:`Keccak.digest` can be followed by another
+ :meth:`Keccak.update` (default: ``False``).
+
+ :Return: A :class:`Keccak_Hash` hash object
+ """
+
+ data = kwargs.pop("data", None)
+ update_after_digest = kwargs.pop("update_after_digest", False)
+
+ digest_bytes = kwargs.pop("digest_bytes", None)
+ digest_bits = kwargs.pop("digest_bits", None)
+ if None not in (digest_bytes, digest_bits):
+ raise TypeError("Only one digest parameter must be provided")
+ if (None, None) == (digest_bytes, digest_bits):
+ raise TypeError("Digest size (bits, bytes) not provided")
+ if digest_bytes is not None:
+ if digest_bytes not in (28, 32, 48, 64):
+ raise ValueError("'digest_bytes' must be: 28, 32, 48 or 64")
+ else:
+ if digest_bits not in (224, 256, 384, 512):
+ raise ValueError("'digest_bytes' must be: 224, 256, 384 or 512")
+ digest_bytes = digest_bits // 8
+
+ if kwargs:
+ raise TypeError("Unknown parameters: " + str(kwargs))
+
+ return Keccak_Hash(data, digest_bytes, update_after_digest)
diff --git a/lib/Crypto/Hash/keccak.pyi b/lib/Crypto/Hash/keccak.pyi
new file mode 100644
index 0000000..844d256
--- /dev/null
+++ b/lib/Crypto/Hash/keccak.pyi
@@ -0,0 +1,23 @@
+from typing import Union, Any
+
+Buffer = Union[bytes, bytearray, memoryview]
+
+class Keccak_Hash(object):
+ digest_size: int
+ def __init__(self,
+ data: Buffer,
+ digest_bytes: int,
+ update_after_digest: bool) -> None: ...
+ def update(self, data: Buffer) -> Keccak_Hash: ...
+ def digest(self) -> bytes: ...
+ def hexdigest(self) -> str: ...
+ def new(self,
+ data: Buffer = ...,
+ digest_bytes: int = ...,
+ digest_bits: int = ...,
+ update_after_digest: bool = ...) -> Keccak_Hash: ...
+
+def new(data: Buffer = ...,
+ digest_bytes: int = ...,
+ digest_bits: int = ...,
+ update_after_digest: bool = ...) -> Keccak_Hash: ...