Merge pull request #4835 from Masorubka1/test_hashlib.py

Update test_hashlib.py from Cpython v3.11.2 & refactor hashlib
This commit is contained in:
Jeong, YunWon
2023-04-23 23:28:38 +09:00
committed by GitHub
14 changed files with 685 additions and 83 deletions

10
Cargo.lock generated
View File

@@ -950,6 +950,15 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df"
[[package]]
name = "hmac"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
dependencies = [
"digest",
]
[[package]]
name = "iana-time-zone"
version = "0.1.53"
@@ -2137,6 +2146,7 @@ dependencies = [
"foreign-types-shared",
"gethostname",
"hex",
"hmac",
"itertools",
"libc",
"libsqlite3-sys",

316
Lib/hashlib.py vendored Normal file
View File

@@ -0,0 +1,316 @@
#. Copyright (C) 2005-2010 Gregory P. Smith (greg@krypto.org)
# Licensed to PSF under a Contributor Agreement.
#
__doc__ = """hashlib module - A common interface to many hash functions.
new(name, data=b'', **kwargs) - returns a new hash object implementing the
given hash function; initializing the hash
using the given binary data.
Named constructor functions are also available, these are faster
than using new(name):
md5(), sha1(), sha224(), sha256(), sha384(), sha512(), blake2b(), blake2s(),
sha3_224, sha3_256, sha3_384, sha3_512, shake_128, and shake_256.
More algorithms may be available on your platform but the above are guaranteed
to exist. See the algorithms_guaranteed and algorithms_available attributes
to find out what algorithm names can be passed to new().
NOTE: If you want the adler32 or crc32 hash functions they are available in
the zlib module.
Choose your hash function wisely. Some have known collision weaknesses.
sha384 and sha512 will be slow on 32 bit platforms.
Hash objects have these methods:
- update(data): Update the hash object with the bytes in data. Repeated calls
are equivalent to a single call with the concatenation of all
the arguments.
- digest(): Return the digest of the bytes passed to the update() method
so far as a bytes object.
- hexdigest(): Like digest() except the digest is returned as a string
of double length, containing only hexadecimal digits.
- copy(): Return a copy (clone) of the hash object. This can be used to
efficiently compute the digests of datas that share a common
initial substring.
For example, to obtain the digest of the byte string 'Nobody inspects the
spammish repetition':
>>> import hashlib
>>> m = hashlib.md5()
>>> m.update(b"Nobody inspects")
>>> m.update(b" the spammish repetition")
>>> m.digest()
b'\\xbbd\\x9c\\x83\\xdd\\x1e\\xa5\\xc9\\xd9\\xde\\xc9\\xa1\\x8d\\xf0\\xff\\xe9'
More condensed:
>>> hashlib.sha224(b"Nobody inspects the spammish repetition").hexdigest()
'a4337bc45a8fc544c03f52dc550cd6e1e87021bc896588bd79e901e2'
"""
# This tuple and __get_builtin_constructor() must be modified if a new
# always available algorithm is added.
__always_supported = ('md5', 'sha1', 'sha224', 'sha256', 'sha384', 'sha512',
'blake2b', 'blake2s',
'sha3_224', 'sha3_256', 'sha3_384', 'sha3_512',
'shake_128', 'shake_256')
algorithms_guaranteed = set(__always_supported)
algorithms_available = set(__always_supported)
__all__ = __always_supported + ('new', 'algorithms_guaranteed',
'algorithms_available', 'pbkdf2_hmac', 'file_digest')
__builtin_constructor_cache = {}
# Prefer our blake2 implementation
# OpenSSL 1.1.0 comes with a limited implementation of blake2b/s. The OpenSSL
# implementations neither support keyed blake2 (blake2 MAC) nor advanced
# features like salt, personalization, or tree hashing. OpenSSL hash-only
# variants are available as 'blake2b512' and 'blake2s256', though.
__block_openssl_constructor = {
'blake2b', 'blake2s',
}
def __get_builtin_constructor(name):
cache = __builtin_constructor_cache
constructor = cache.get(name)
if constructor is not None:
return constructor
try:
if name in {'SHA1', 'sha1'}:
import _sha1
cache['SHA1'] = cache['sha1'] = _sha1.sha1
elif name in {'MD5', 'md5'}:
import _md5
cache['MD5'] = cache['md5'] = _md5.md5
elif name in {'SHA256', 'sha256', 'SHA224', 'sha224'}:
import _sha256
cache['SHA224'] = cache['sha224'] = _sha256.sha224
cache['SHA256'] = cache['sha256'] = _sha256.sha256
elif name in {'SHA512', 'sha512', 'SHA384', 'sha384'}:
import _sha512
cache['SHA384'] = cache['sha384'] = _sha512.sha384
cache['SHA512'] = cache['sha512'] = _sha512.sha512
elif name in {'blake2b', 'blake2s'}:
import _blake2
cache['blake2b'] = _blake2.blake2b
cache['blake2s'] = _blake2.blake2s
elif name in {'sha3_224', 'sha3_256', 'sha3_384', 'sha3_512'}:
import _sha3
cache['sha3_224'] = _sha3.sha3_224
cache['sha3_256'] = _sha3.sha3_256
cache['sha3_384'] = _sha3.sha3_384
cache['sha3_512'] = _sha3.sha3_512
elif name in {'shake_128', 'shake_256'}:
import _sha3
cache['shake_128'] = _sha3.shake_128
cache['shake_256'] = _sha3.shake_256
except ImportError:
pass # no extension module, this hash is unsupported.'''
constructor = cache.get(name)
if constructor is not None:
return constructor
raise ValueError('unsupported hash type ' + name)
def __get_openssl_constructor(name):
if name in __block_openssl_constructor:
# Prefer our builtin blake2 implementation.
return __get_builtin_constructor(name)
try:
# MD5, SHA1, and SHA2 are in all supported OpenSSL versions
# SHA3/shake are available in OpenSSL 1.1.1+
f = getattr(_hashlib, 'openssl_' + name)
# Allow the C module to raise ValueError. The function will be
# defined but the hash not actually available. Don't fall back to
# builtin if the current security policy blocks a digest, bpo#40695.
f(usedforsecurity=False)
# Use the C function directly (very fast)
return f
except (AttributeError, ValueError):
return __get_builtin_constructor(name)
def __py_new(name, data=b'', **kwargs):
"""new(name, data=b'', **kwargs) - Return a new hashing object using the
named algorithm; optionally initialized with data (which must be
a bytes-like object).
"""
return __get_builtin_constructor(name)(data, **kwargs)
def __hash_new(name, data=b'', **kwargs):
"""new(name, data=b'') - Return a new hashing object using the named algorithm;
optionally initialized with data (which must be a bytes-like object).
"""
if name in __block_openssl_constructor:
# Prefer our builtin blake2 implementation.
return __get_builtin_constructor(name)(data, **kwargs)
try:
return _hashlib.new(name, data, **kwargs)
except ValueError:
# If the _hashlib module (OpenSSL) doesn't support the named
# hash, try using our builtin implementations.
# This allows for SHA224/256 and SHA384/512 support even though
# the OpenSSL library prior to 0.9.8 doesn't provide them.
return __get_builtin_constructor(name)(data)
try:
import _hashlib
new = __hash_new
__get_hash = __get_openssl_constructor
# TODO: RUSTPYTHON set in _hashlib instance PyFrozenSet algorithms_available
'''algorithms_available = algorithms_available.union(
_hashlib.openssl_md_meth_names)'''
except ImportError:
_hashlib = None
new = __py_new
__get_hash = __get_builtin_constructor
try:
# OpenSSL's PKCS5_PBKDF2_HMAC requires OpenSSL 1.0+ with HMAC and SHA
from _hashlib import pbkdf2_hmac
except ImportError:
from warnings import warn as _warn
_trans_5C = bytes((x ^ 0x5C) for x in range(256))
_trans_36 = bytes((x ^ 0x36) for x in range(256))
def pbkdf2_hmac(hash_name, password, salt, iterations, dklen=None):
"""Password based key derivation function 2 (PKCS #5 v2.0)
This Python implementations based on the hmac module about as fast
as OpenSSL's PKCS5_PBKDF2_HMAC for short passwords and much faster
for long passwords.
"""
_warn(
"Python implementation of pbkdf2_hmac() is deprecated.",
category=DeprecationWarning,
stacklevel=2
)
if not isinstance(hash_name, str):
raise TypeError(hash_name)
if not isinstance(password, (bytes, bytearray)):
password = bytes(memoryview(password))
if not isinstance(salt, (bytes, bytearray)):
salt = bytes(memoryview(salt))
# Fast inline HMAC implementation
inner = new(hash_name)
outer = new(hash_name)
blocksize = getattr(inner, 'block_size', 64)
if len(password) > blocksize:
password = new(hash_name, password).digest()
password = password + b'\x00' * (blocksize - len(password))
inner.update(password.translate(_trans_36))
outer.update(password.translate(_trans_5C))
def prf(msg, inner=inner, outer=outer):
# PBKDF2_HMAC uses the password as key. We can re-use the same
# digest objects and just update copies to skip initialization.
icpy = inner.copy()
ocpy = outer.copy()
icpy.update(msg)
ocpy.update(icpy.digest())
return ocpy.digest()
if iterations < 1:
raise ValueError(iterations)
if dklen is None:
dklen = outer.digest_size
if dklen < 1:
raise ValueError(dklen)
dkey = b''
loop = 1
from_bytes = int.from_bytes
while len(dkey) < dklen:
prev = prf(salt + loop.to_bytes(4))
# endianness doesn't matter here as long to / from use the same
rkey = from_bytes(prev)
for i in range(iterations - 1):
prev = prf(prev)
# rkey = rkey ^ prev
rkey ^= from_bytes(prev)
loop += 1
dkey += rkey.to_bytes(inner.digest_size)
return dkey[:dklen]
try:
# OpenSSL's scrypt requires OpenSSL 1.1+
from _hashlib import scrypt
except ImportError:
pass
def file_digest(fileobj, digest, /, *, _bufsize=2**18):
"""Hash the contents of a file-like object. Returns a digest object.
*fileobj* must be a file-like object opened for reading in binary mode.
It accepts file objects from open(), io.BytesIO(), and SocketIO objects.
The function may bypass Python's I/O and use the file descriptor *fileno*
directly.
*digest* must either be a hash algorithm name as a *str*, a hash
constructor, or a callable that returns a hash object.
"""
# On Linux we could use AF_ALG sockets and sendfile() to archive zero-copy
# hashing with hardware acceleration.
if isinstance(digest, str):
digestobj = new(digest)
else:
digestobj = digest()
if hasattr(fileobj, "getbuffer"):
# io.BytesIO object, use zero-copy buffer
digestobj.update(fileobj.getbuffer())
return digestobj
# Only binary files implement readinto().
if not (
hasattr(fileobj, "readinto")
and hasattr(fileobj, "readable")
and fileobj.readable()
):
raise ValueError(
f"'{fileobj!r}' is not a file-like object in binary reading mode."
)
# binary file, socket.SocketIO object
# Note: socket I/O uses different syscalls than file I/O.
buf = bytearray(_bufsize) # Reusable buffer to reduce allocations.
view = memoryview(buf)
while True:
size = fileobj.readinto(buf)
if size == 0:
break # EOF
digestobj.update(view[:size])
return digestobj
for __func_name in __always_supported:
# try them all, some may not work due to the OpenSSL
# version not supporting that algorithm.
try:
globals()[__func_name] = __get_hash(__func_name)
except ValueError:
import logging
logging.exception('code for hash %s was not found.', __func_name)
# Cleanup locals()
del __always_supported, __func_name, __get_hash
del __py_new, __hash_new, __get_openssl_constructor

View File

@@ -2889,9 +2889,7 @@ class TestIntFlag(unittest.TestCase):
self.assertEqual(Color.ALL.value, 7)
self.assertEqual(str(Color.BLUE), 'blue')
# TODO: RUSTPYTHON
@unittest.expectedFailure
@unittest.skipIf(sys.platform == "win32", "TODO: RUSTPYTHON, inconsistent test result on Windows due to threading")
@unittest.skip("TODO: RUSTPYTHON, inconsistent test result due to threading")
@threading_helper.reap_threads
def test_unique_composite(self):
# override __eq__ to be identity only

View File

@@ -10,6 +10,7 @@ import array
from binascii import unhexlify
import hashlib
import importlib
import io
import itertools
import os
import sys
@@ -20,6 +21,7 @@ import warnings
from test import support
from test.support import _4G, bigmemtest
from test.support.import_helper import import_fresh_module
from test.support import os_helper
from test.support import threading_helper
from test.support import warnings_helper
from http.client import HTTPException
@@ -102,8 +104,7 @@ class HashLibTestCase(unittest.TestCase):
'sha384', 'SHA384', 'sha512', 'SHA512',
'blake2b', 'blake2s',
'sha3_224', 'sha3_256', 'sha3_384', 'sha3_512',
'shake_128', 'shake_256'
)
'shake_128', 'shake_256')
shakes = {'shake_128', 'shake_256'}
@@ -214,15 +215,11 @@ class HashLibTestCase(unittest.TestCase):
else:
c.hexdigest()
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_algorithms_guaranteed(self):
self.assertEqual(hashlib.algorithms_guaranteed,
set(_algo for _algo in self.supported_hash_names
if _algo.islower()))
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_algorithms_available(self):
self.assertTrue(set(hashlib.algorithms_guaranteed).
issubset(hashlib.algorithms_available))
@@ -262,8 +259,6 @@ class HashLibTestCase(unittest.TestCase):
def test_new_upper_to_lower(self):
self.assertEqual(hashlib.new("SHA256").name, "sha256")
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_get_builtin_constructor(self):
get_builtin_constructor = getattr(hashlib,
'__get_builtin_constructor')
@@ -384,6 +379,39 @@ class HashLibTestCase(unittest.TestCase):
if not shake:
self.assertEqual(len(digest), m.digest_size)
if not shake and kwargs.get("key") is None:
# skip shake and blake2 extended parameter tests
self.check_file_digest(name, data, hexdigest)
def check_file_digest(self, name, data, hexdigest):
hexdigest = hexdigest.lower()
try:
hashlib.new(name)
except ValueError:
# skip, algorithm is blocked by security policy.
return
digests = [name]
digests.extend(self.constructors_to_test[name])
with open(os_helper.TESTFN, "wb") as f:
f.write(data)
try:
for digest in digests:
buf = io.BytesIO(data)
buf.seek(0)
'''self.assertEqual(
dir(hashlib), None
)'''
self.assertEqual(
hashlib.file_digest(buf, digest).hexdigest(), hexdigest
)
with open(os_helper.TESTFN, "rb") as f:
digestobj = hashlib.file_digest(f, digest)
self.assertEqual(digestobj.hexdigest(), hexdigest)
finally:
os.unlink(os_helper.TESTFN)
def check_no_unicode(self, algorithm_name):
# Unicode objects are not allowed as input.
constructors = self.constructors_to_test[algorithm_name]
@@ -475,6 +503,8 @@ class HashLibTestCase(unittest.TestCase):
self.check_sha3('shake_128', 256, 1344, b'\x1f')
self.check_sha3('shake_256', 512, 1088, b'\x1f')
# TODO: RUSTPYTHON implement all blake2 params
@unittest.expectedFailure
@requires_blake2
def test_blocksize_name_blake2(self):
self.check_blocksize_name('blake2b', 128, 64)
@@ -718,6 +748,8 @@ class HashLibTestCase(unittest.TestCase):
outer.update(keyed.digest())
return outer.hexdigest()
# TODO: RUSTPYTHON add to constructor const value
@unittest.expectedFailure
@requires_blake2
def test_blake2b(self):
self.check_blake2(hashlib.blake2b, 16, 16, 64, 64, (1<<64)-1)
@@ -739,6 +771,8 @@ class HashLibTestCase(unittest.TestCase):
"ba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d1"+
"7d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923")
# TODO: RUSTPYTHON implement all blake2 fields
@unittest.expectedFailure
@requires_blake2
def test_case_blake2b_all_parameters(self):
# This checks that all the parameters work in general, and also that
@@ -763,6 +797,8 @@ class HashLibTestCase(unittest.TestCase):
key = bytes.fromhex(key)
self.check('blake2b', msg, md, key=key)
# TODO: RUSTPYTHON add to constructor const value
@unittest.expectedFailure
@requires_blake2
def test_blake2s(self):
self.check_blake2(hashlib.blake2s, 8, 8, 32, 32, (1<<48)-1)
@@ -782,6 +818,8 @@ class HashLibTestCase(unittest.TestCase):
self.check('blake2s', b"abc",
"508c5e8c327c14e2e1a72ba34eeb452f37458b209ed63a294d999b4c86675982")
# TODO: RUSTPYTHON implement all blake2 fields
@unittest.expectedFailure
@requires_blake2
def test_case_blake2s_all_parameters(self):
# This checks that all the parameters work in general, and also that
@@ -898,6 +936,7 @@ class HashLibTestCase(unittest.TestCase):
)
@threading_helper.reap_threads
@threading_helper.requires_working_threading()
def test_threaded_hashing(self):
# Updating the same hash object from several threads at once
# using data chunk sizes containing the same byte sequences.
@@ -1136,12 +1175,37 @@ class KDFTests(unittest.TestCase):
hashlib.scrypt(b'password', salt=b'salt', n=2, r=8, p=1,
dklen=dklen)
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_normalized_name(self):
self.assertNotIn("blake2b512", hashlib.algorithms_available)
self.assertNotIn("sha3-512", hashlib.algorithms_available)
def test_file_digest(self):
data = b'a' * 65536
d1 = hashlib.sha256()
self.addCleanup(os.unlink, os_helper.TESTFN)
with open(os_helper.TESTFN, "wb") as f:
for _ in range(10):
d1.update(data)
f.write(data)
with open(os_helper.TESTFN, "rb") as f:
d2 = hashlib.file_digest(f, hashlib.sha256)
self.assertEqual(d1.hexdigest(), d2.hexdigest())
self.assertEqual(d1.name, d2.name)
self.assertIs(type(d1), type(d2))
with self.assertRaises(ValueError):
hashlib.file_digest(None, "sha256")
with self.assertRaises(ValueError):
with open(os_helper.TESTFN, "r") as f:
hashlib.file_digest(f, "sha256")
with self.assertRaises(ValueError):
with open(os_helper.TESTFN, "wb") as f:
hashlib.file_digest(f, "sha256")
if __name__ == "__main__":
unittest.main()

42
Lib/test/test_hmac.py vendored
View File

@@ -39,6 +39,8 @@ def ignore_warning(func):
class TestVectorsTestCase(unittest.TestCase):
# TODO: RUSTPYTHON
@unittest.expectedFailure
def assert_hmac_internals(
self, h, digest, hashname, digest_size, block_size
):
@@ -48,6 +50,8 @@ class TestVectorsTestCase(unittest.TestCase):
self.assertEqual(h.digest_size, digest_size)
self.assertEqual(h.block_size, block_size)
# TODO: RUSTPYTHON
@unittest.expectedFailure
def assert_hmac(
self, key, data, digest, hashfunc, hashname, digest_size, block_size
):
@@ -122,6 +126,8 @@ class TestVectorsTestCase(unittest.TestCase):
h, digest, hashname, digest_size, block_size
)
# TODO: RUSTPYTHON
@unittest.expectedFailure
@hashlib_helper.requires_hashdigest('md5', openssl=True)
def test_md5_vectors(self):
# Test the HMAC module against test vectors from the RFC.
@@ -164,6 +170,8 @@ class TestVectorsTestCase(unittest.TestCase):
b"and Larger Than One Block-Size Data"),
"6f630fad67cda0ee1fb1f562db3aa53e")
# TODO: RUSTPYTHON
@unittest.expectedFailure
@hashlib_helper.requires_hashdigest('sha1', openssl=True)
def test_sha_vectors(self):
def shatest(key, data, digest):
@@ -323,18 +331,26 @@ class TestVectorsTestCase(unittest.TestCase):
'134676fb6de0446065c97440fa8c6a58',
})
# TODO: RUSTPYTHON
@unittest.expectedFailure
@hashlib_helper.requires_hashdigest('sha224', openssl=True)
def test_sha224_rfc4231(self):
self._rfc4231_test_cases(hashlib.sha224, 'sha224', 28, 64)
# TODO: RUSTPYTHON
@unittest.expectedFailure
@hashlib_helper.requires_hashdigest('sha256', openssl=True)
def test_sha256_rfc4231(self):
self._rfc4231_test_cases(hashlib.sha256, 'sha256', 32, 64)
# TODO: RUSTPYTHON
@unittest.expectedFailure
@hashlib_helper.requires_hashdigest('sha384', openssl=True)
def test_sha384_rfc4231(self):
self._rfc4231_test_cases(hashlib.sha384, 'sha384', 48, 128)
# TODO: RUSTPYTHON
@unittest.expectedFailure
@hashlib_helper.requires_hashdigest('sha512', openssl=True)
def test_sha512_rfc4231(self):
self._rfc4231_test_cases(hashlib.sha512, 'sha512', 64, 128)
@@ -380,6 +396,8 @@ class ConstructorTestCase(unittest.TestCase):
"6c845b47f52b3b47f6590c502db7825aad757bf4fadc8fa972f7cd2e76a5bdeb"
)
# TODO: RUSTPYTHON
@unittest.expectedFailure
@hashlib_helper.requires_hashdigest('sha256')
def test_normal(self):
# Standard constructor call.
@@ -402,6 +420,8 @@ class ConstructorTestCase(unittest.TestCase):
with self.assertRaises(TypeError):
h = hmac.new("key", digestmod='sha256')
# TODO: RUSTPYTHON
@unittest.expectedFailure
@hashlib_helper.requires_hashdigest('sha256')
def test_withtext(self):
# Constructor call with text.
@@ -411,6 +431,8 @@ class ConstructorTestCase(unittest.TestCase):
self.fail("Constructor call with text argument raised exception.")
self.assertEqual(h.hexdigest(), self.expected)
# TODO: RUSTPYTHON
@unittest.expectedFailure
@hashlib_helper.requires_hashdigest('sha256')
def test_with_bytearray(self):
try:
@@ -420,6 +442,8 @@ class ConstructorTestCase(unittest.TestCase):
self.fail("Constructor call with bytearray arguments raised exception.")
self.assertEqual(h.hexdigest(), self.expected)
# TODO: RUSTPYTHON
@unittest.expectedFailure
@hashlib_helper.requires_hashdigest('sha256')
def test_with_memoryview_msg(self):
try:
@@ -428,6 +452,8 @@ class ConstructorTestCase(unittest.TestCase):
self.fail("Constructor call with memoryview msg raised exception.")
self.assertEqual(h.hexdigest(), self.expected)
# TODO: RUSTPYTHON
@unittest.expectedFailure
@hashlib_helper.requires_hashdigest('sha256')
def test_withmodule(self):
# Constructor call with text and digest module.
@@ -436,6 +462,8 @@ class ConstructorTestCase(unittest.TestCase):
except Exception:
self.fail("Constructor call with hashlib.sha256 raised exception.")
# TODO: RUSTPYTHON
@unittest.expectedFailure
@unittest.skipUnless(C_HMAC is not None, 'need _hashlib')
def test_internal_types(self):
# internal types like _hashlib.C_HMAC are not constructable
@@ -443,6 +471,8 @@ class ConstructorTestCase(unittest.TestCase):
with self.assertRaisesRegex(TypeError, "immutable type"):
C_HMAC.value = None
# TODO: RUSTPYTHON
@unittest.expectedFailure
@unittest.skipUnless(sha256_module is not None, 'need _sha256')
def test_with_sha256_module(self):
h = hmac.HMAC(b"key", b"hash this!", digestmod=sha256_module.sha256)
@@ -455,6 +485,8 @@ class ConstructorTestCase(unittest.TestCase):
class SanityTestCase(unittest.TestCase):
# TODO: RUSTPYTHON
@unittest.expectedFailure
@hashlib_helper.requires_hashdigest('sha256')
def test_exercise_all_methods(self):
# Exercising all methods once.
@@ -496,6 +528,8 @@ class CopyTestCase(unittest.TestCase):
"No real copy of the attribute 'outer'.")
self.assertIs(h1._hmac, None)
# TODO: RUSTPYTHON
@unittest.expectedFailure
@unittest.skipIf(_hashopenssl is None, "test requires _hashopenssl")
@hashlib_helper.requires_hashdigest('sha256')
def test_realcopy_hmac(self):
@@ -504,6 +538,8 @@ class CopyTestCase(unittest.TestCase):
h2 = h1.copy()
self.assertTrue(id(h1._hmac) != id(h2._hmac))
# TODO: RUSTPYTHON
@unittest.expectedFailure
@hashlib_helper.requires_hashdigest('sha256')
def test_equality(self):
# Testing if the copy has the same digests.
@@ -515,6 +551,8 @@ class CopyTestCase(unittest.TestCase):
self.assertEqual(h1.hexdigest(), h2.hexdigest(),
"Hexdigest of copy doesn't match original hexdigest.")
# TODO: RUSTPYTHON
@unittest.expectedFailure
@hashlib_helper.requires_hashdigest('sha256')
def test_equality_new(self):
# Testing if the copy has the same digests with hmac.new().
@@ -532,6 +570,8 @@ class CopyTestCase(unittest.TestCase):
class CompareDigestTestCase(unittest.TestCase):
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_hmac_compare_digest(self):
self._test_compare_digest(hmac.compare_digest)
if openssl_compare_digest is not None:
@@ -542,6 +582,8 @@ class CompareDigestTestCase(unittest.TestCase):
def test_operator_compare_digest(self):
self._test_compare_digest(operator_compare_digest)
# TODO: RUSTPYTHON
@unittest.expectedFailure
@unittest.skipIf(openssl_compare_digest is None, "test requires _hashlib")
def test_openssl_compare_digest(self):
self._test_compare_digest(openssl_compare_digest)

View File

@@ -55,6 +55,7 @@ sha-1 = "0.10.0"
sha2 = "0.10.2"
sha3 = "0.10.1"
blake2 = "0.10.4"
hmac = "0.12.1"
## unicode stuff
unicode_names2 = { workspace = true }

19
stdlib/src/blake2.rs Normal file
View File

@@ -0,0 +1,19 @@
// spell-checker:ignore usedforsecurity HASHXOF
pub(crate) use _blake2::make_module;
#[pymodule]
mod _blake2 {
use crate::hashlib::_hashlib::{local_blake2b, local_blake2s, BlakeHashArgs};
use crate::vm::{PyPayload, PyResult, VirtualMachine};
#[pyfunction]
fn blake2b(args: BlakeHashArgs, vm: &VirtualMachine) -> PyResult {
Ok(local_blake2b(args).into_pyobject(vm))
}
#[pyfunction]
fn blake2s(args: BlakeHashArgs, vm: &VirtualMachine) -> PyResult {
Ok(local_blake2s(args).into_pyobject(vm))
}
}

View File

@@ -1,13 +1,15 @@
// spell-checker:ignore usedforsecurity HASHXOF
pub(crate) use hashlib::make_module;
pub(crate) use _hashlib::make_module;
#[pymodule]
mod hashlib {
pub mod _hashlib {
use crate::common::lock::PyRwLock;
use crate::vm::{
builtins::{PyBytes, PyStrRef, PyTypeRef},
function::{ArgBytesLike, FuncArgs, OptionalArg},
convert::ToPyObject,
function::{ArgBytesLike, ArgStrOrBytesLike, FuncArgs, OptionalArg},
protocol::PyBuffer,
PyObjectRef, PyPayload, PyResult, VirtualMachine,
};
use blake2::{Blake2b512, Blake2s256};
@@ -19,24 +21,22 @@ mod hashlib {
use sha2::{Sha224, Sha256, Sha384, Sha512};
use sha3::{Sha3_224, Sha3_256, Sha3_384, Sha3_512, Shake128, Shake256};
#[derive(FromArgs, Traverse)]
#[derive(FromArgs, Debug)]
#[allow(unused)]
struct NewHashArgs {
#[pyarg(positional)]
name: PyStrRef,
#[pyarg(any, optional)]
data: OptionalArg<ArgBytesLike>,
#[pytraverse(skip)]
#[pyarg(named, default = "true")]
usedforsecurity: bool,
}
#[derive(FromArgs, Traverse)]
#[derive(FromArgs)]
#[allow(unused)]
struct BlakeHashArgs {
pub struct BlakeHashArgs {
#[pyarg(positional, optional)]
data: OptionalArg<ArgBytesLike>,
#[pytraverse(skip)]
pub data: OptionalArg<ArgBytesLike>,
#[pyarg(named, default = "true")]
usedforsecurity: bool,
}
@@ -50,12 +50,11 @@ mod hashlib {
}
}
#[derive(FromArgs, Traverse)]
#[derive(FromArgs, Debug)]
#[allow(unused)]
struct HashArgs {
pub struct HashArgs {
#[pyarg(any, optional)]
string: OptionalArg<ArgBytesLike>,
#[pytraverse(skip)]
pub string: OptionalArg<ArgBytesLike>,
#[pyarg(named, default = "true")]
usedforsecurity: bool,
}
@@ -84,11 +83,11 @@ mod hashlib {
}
#[pyattr]
#[pyclass(module = "hashlib", name = "HASH")]
#[pyclass(module = "_hashlib", name = "HASH")]
#[derive(PyPayload)]
struct PyHasher {
name: String,
ctx: PyRwLock<HashWrapper>,
pub struct PyHasher {
pub name: String,
pub ctx: PyRwLock<HashWrapper>,
}
impl std::fmt::Debug for PyHasher {
@@ -108,7 +107,7 @@ mod hashlib {
#[pyslot]
fn slot_new(_cls: PyTypeRef, _args: FuncArgs, vm: &VirtualMachine) -> PyResult {
Err(vm.new_type_error("cannot create 'hashlib.HASH' instances".into()))
Err(vm.new_type_error("cannot create '_hashlib.HASH' instances".into()))
}
#[pygetset]
@@ -148,9 +147,9 @@ mod hashlib {
}
#[pyattr]
#[pyclass(module = "hashlib", name = "HASHXOF")]
#[pyclass(module = "_hashlib", name = "HASHXOF")]
#[derive(PyPayload)]
struct PyHasherXof {
pub struct PyHasherXof {
name: String,
ctx: PyRwLock<HashXofWrapper>,
}
@@ -172,7 +171,7 @@ mod hashlib {
#[pyslot]
fn slot_new(_cls: PyTypeRef, _args: FuncArgs, vm: &VirtualMachine) -> PyResult {
Err(vm.new_type_error("cannot create 'hashlib.HASHXOF' instances".into()))
Err(vm.new_type_error("cannot create '_hashlib.HASHXOF' instances".into()))
}
#[pygetset]
@@ -214,108 +213,147 @@ mod hashlib {
#[pyfunction(name = "new")]
fn hashlib_new(args: NewHashArgs, vm: &VirtualMachine) -> PyResult<PyObjectRef> {
match args.name.as_str().to_lowercase().as_str() {
"md5" => Ok(md5(args.into()).into_pyobject(vm)),
"sha1" => Ok(sha1(args.into()).into_pyobject(vm)),
"sha224" => Ok(sha224(args.into()).into_pyobject(vm)),
"sha256" => Ok(sha256(args.into()).into_pyobject(vm)),
"sha384" => Ok(sha384(args.into()).into_pyobject(vm)),
"sha512" => Ok(sha512(args.into()).into_pyobject(vm)),
"sha3_224" => Ok(sha3_224(args.into()).into_pyobject(vm)),
"sha3_256" => Ok(sha3_256(args.into()).into_pyobject(vm)),
"sha3_384" => Ok(sha3_384(args.into()).into_pyobject(vm)),
"sha3_512" => Ok(sha3_512(args.into()).into_pyobject(vm)),
"shake_128" => Ok(shake_128(args.into()).into_pyobject(vm)),
"shake_256" => Ok(shake_256(args.into()).into_pyobject(vm)),
"blake2b" => Ok(blake2b(args.into()).into_pyobject(vm)),
"blake2s" => Ok(blake2s(args.into()).into_pyobject(vm)),
"md5" => Ok(local_md5(args.into()).into_pyobject(vm)),
"sha1" => Ok(local_sha1(args.into()).into_pyobject(vm)),
"sha224" => Ok(local_sha224(args.into()).into_pyobject(vm)),
"sha256" => Ok(local_sha256(args.into()).into_pyobject(vm)),
"sha384" => Ok(local_sha384(args.into()).into_pyobject(vm)),
"sha512" => Ok(local_sha512(args.into()).into_pyobject(vm)),
"sha3_224" => Ok(local_sha3_224(args.into()).into_pyobject(vm)),
"sha3_256" => Ok(local_sha3_256(args.into()).into_pyobject(vm)),
"sha3_384" => Ok(local_sha3_384(args.into()).into_pyobject(vm)),
"sha3_512" => Ok(local_sha3_512(args.into()).into_pyobject(vm)),
"shake_128" => Ok(local_shake_128(args.into()).into_pyobject(vm)),
"shake_256" => Ok(local_shake_256(args.into()).into_pyobject(vm)),
"blake2b" => Ok(local_blake2b(args.into()).into_pyobject(vm)),
"blake2s" => Ok(local_blake2s(args.into()).into_pyobject(vm)),
other => Err(vm.new_value_error(format!("Unknown hashing algorithm: {other}"))),
}
}
#[pyfunction]
fn md5(args: HashArgs) -> PyHasher {
#[pyfunction(name = "openssl_md5")]
pub fn local_md5(args: HashArgs) -> PyHasher {
PyHasher::new("md5", HashWrapper::new::<Md5>(args.string))
}
#[pyfunction]
fn sha1(args: HashArgs) -> PyHasher {
#[pyfunction(name = "openssl_sha1")]
pub fn local_sha1(args: HashArgs) -> PyHasher {
PyHasher::new("sha1", HashWrapper::new::<Sha1>(args.string))
}
#[pyfunction]
fn sha224(args: HashArgs) -> PyHasher {
#[pyfunction(name = "openssl_sha224")]
pub fn local_sha224(args: HashArgs) -> PyHasher {
PyHasher::new("sha224", HashWrapper::new::<Sha224>(args.string))
}
#[pyfunction]
fn sha256(args: HashArgs) -> PyHasher {
#[pyfunction(name = "openssl_sha256")]
pub fn local_sha256(args: HashArgs) -> PyHasher {
PyHasher::new("sha256", HashWrapper::new::<Sha256>(args.string))
}
#[pyfunction]
fn sha384(args: HashArgs) -> PyHasher {
#[pyfunction(name = "openssl_sha384")]
pub fn local_sha384(args: HashArgs) -> PyHasher {
PyHasher::new("sha384", HashWrapper::new::<Sha384>(args.string))
}
#[pyfunction]
fn sha512(args: HashArgs) -> PyHasher {
#[pyfunction(name = "openssl_sha512")]
pub fn local_sha512(args: HashArgs) -> PyHasher {
PyHasher::new("sha512", HashWrapper::new::<Sha512>(args.string))
}
#[pyfunction]
fn sha3_224(args: HashArgs) -> PyHasher {
#[pyfunction(name = "openssl_sha3_224")]
pub fn local_sha3_224(args: HashArgs) -> PyHasher {
PyHasher::new("sha3_224", HashWrapper::new::<Sha3_224>(args.string))
}
#[pyfunction]
fn sha3_256(args: HashArgs) -> PyHasher {
#[pyfunction(name = "openssl_sha3_256")]
pub fn local_sha3_256(args: HashArgs) -> PyHasher {
PyHasher::new("sha3_256", HashWrapper::new::<Sha3_256>(args.string))
}
#[pyfunction]
fn sha3_384(args: HashArgs) -> PyHasher {
#[pyfunction(name = "openssl_sha3_384")]
pub fn local_sha3_384(args: HashArgs) -> PyHasher {
PyHasher::new("sha3_384", HashWrapper::new::<Sha3_384>(args.string))
}
#[pyfunction]
fn sha3_512(args: HashArgs) -> PyHasher {
#[pyfunction(name = "openssl_sha3_512")]
pub fn local_sha3_512(args: HashArgs) -> PyHasher {
PyHasher::new("sha3_512", HashWrapper::new::<Sha3_512>(args.string))
}
#[pyfunction]
fn shake_128(args: HashArgs) -> PyHasherXof {
#[pyfunction(name = "openssl_shake_128")]
pub fn local_shake_128(args: HashArgs) -> PyHasherXof {
PyHasherXof::new("shake_128", HashXofWrapper::new_shake_128(args.string))
}
#[pyfunction]
fn shake_256(args: HashArgs) -> PyHasherXof {
#[pyfunction(name = "openssl_shake_256")]
pub fn local_shake_256(args: HashArgs) -> PyHasherXof {
PyHasherXof::new("shake_256", HashXofWrapper::new_shake_256(args.string))
}
#[pyfunction]
fn blake2b(args: BlakeHashArgs) -> PyHasher {
#[pyfunction(name = "openssl_blake2b")]
pub fn local_blake2b(args: BlakeHashArgs) -> PyHasher {
PyHasher::new("blake2b", HashWrapper::new::<Blake2b512>(args.data))
}
#[pyfunction]
fn blake2s(args: BlakeHashArgs) -> PyHasher {
#[pyfunction(name = "openssl_blake2s")]
pub fn local_blake2s(args: BlakeHashArgs) -> PyHasher {
PyHasher::new("blake2s", HashWrapper::new::<Blake2s256>(args.data))
}
trait ThreadSafeDynDigest: DynClone + DynDigest + Sync + Send {}
#[pyfunction]
fn compare_digest(
a: ArgStrOrBytesLike,
b: ArgStrOrBytesLike,
vm: &VirtualMachine,
) -> PyResult<PyObjectRef> {
fn is_str(arg: &ArgStrOrBytesLike) -> bool {
matches!(arg, ArgStrOrBytesLike::Str(_))
}
if is_str(&a) != is_str(&b) {
return Err(vm.new_type_error(format!(
"a bytes-like object is required, not '{}'",
b.as_object().class().name()
)));
}
let a_hash = a.borrow_bytes().to_vec();
let b_hash = b.borrow_bytes().to_vec();
Ok((a_hash == b_hash).to_pyobject(vm))
}
#[derive(FromArgs, Debug)]
#[allow(unused)]
pub struct NewHMACHashArgs {
#[pyarg(positional)]
name: PyBuffer,
#[pyarg(any, optional)]
data: OptionalArg<ArgBytesLike>,
#[pyarg(named, default = "true")]
digestmod: bool, // TODO: RUSTPYTHON support functions & name functions
}
#[pyfunction]
fn hmac_new(_args: NewHMACHashArgs, vm: &VirtualMachine) -> PyResult<PyObjectRef> {
Err(vm.new_type_error("cannot create 'hmac' instances".into())) // TODO: RUSTPYTHON support hmac
}
pub trait ThreadSafeDynDigest: DynClone + DynDigest + Sync + Send {}
impl<T> ThreadSafeDynDigest for T where T: DynClone + DynDigest + Sync + Send {}
clone_trait_object!(ThreadSafeDynDigest);
/// Generic wrapper patching around the hashing libraries.
#[derive(Clone)]
struct HashWrapper {
pub struct HashWrapper {
block_size: usize,
inner: Box<dyn ThreadSafeDynDigest>,
}
impl HashWrapper {
fn new<D>(data: OptionalArg<ArgBytesLike>) -> Self
pub fn new<D>(data: OptionalArg<ArgBytesLike>) -> Self
where
D: ThreadSafeDynDigest + BlockSizeUser + Default + 'static,
{
@@ -348,13 +386,13 @@ mod hashlib {
}
#[derive(Clone)]
enum HashXofWrapper {
pub enum HashXofWrapper {
Shake128(Shake128),
Shake256(Shake256),
}
impl HashXofWrapper {
fn new_shake_128(data: OptionalArg<ArgBytesLike>) -> Self {
pub fn new_shake_128(data: OptionalArg<ArgBytesLike>) -> Self {
let mut h = HashXofWrapper::Shake128(Shake128::default());
if let OptionalArg::Present(d) = data {
d.with_ref(|bytes| h.update(bytes));
@@ -362,7 +400,7 @@ mod hashlib {
h
}
fn new_shake_256(data: OptionalArg<ArgBytesLike>) -> Self {
pub fn new_shake_256(data: OptionalArg<ArgBytesLike>) -> Self {
let mut h = HashXofWrapper::Shake256(Shake256::default());
if let OptionalArg::Present(d) = data {
d.with_ref(|bytes| h.update(bytes));

View File

@@ -13,7 +13,14 @@ mod contextvars;
mod csv;
mod dis;
mod gc;
mod blake2;
mod hashlib;
mod md5;
mod sha1;
mod sha256;
mod sha3;
mod json;
#[cfg(not(any(target_os = "ios", target_os = "android", target_arch = "wasm32")))]
mod locale;
@@ -100,7 +107,13 @@ pub fn get_module_inits() -> impl Iterator<Item = (Cow<'static, str>, StdlibInit
"_csv" => csv::make_module,
"_dis" => dis::make_module,
"gc" => gc::make_module,
"hashlib" => hashlib::make_module,
"_hashlib" => hashlib::make_module,
"_sha1" => sha1::make_module,
"_sha3" => sha3::make_module,
"_sha256" => sha256::make_module,
// "_sha512" => sha512::make_module, // TODO: RUSPYTHON fix strange fail on vm: 'static type has not been initialized'
"_md5" => md5::make_module,
"_blake2" => blake2::make_module,
"_json" => json::make_module,
"math" => math::make_module,
"pyexpat" => pyexpat::make_module,

12
stdlib/src/md5.rs Normal file
View File

@@ -0,0 +1,12 @@
pub(crate) use _md5::make_module;
#[pymodule]
mod _md5 {
use crate::hashlib::_hashlib::{local_md5, HashArgs};
use crate::vm::{PyPayload, PyResult, VirtualMachine};
#[pyfunction]
fn md5(args: HashArgs, vm: &VirtualMachine) -> PyResult {
Ok(local_md5(args).into_pyobject(vm))
}
}

12
stdlib/src/sha1.rs Normal file
View File

@@ -0,0 +1,12 @@
pub(crate) use _sha1::make_module;
#[pymodule]
mod _sha1 {
use crate::hashlib::_hashlib::{local_sha1, HashArgs};
use crate::vm::{PyPayload, PyResult, VirtualMachine};
#[pyfunction]
fn sha1(args: HashArgs, vm: &VirtualMachine) -> PyResult {
Ok(local_sha1(args).into_pyobject(vm))
}
}

17
stdlib/src/sha256.rs Normal file
View File

@@ -0,0 +1,17 @@
pub(crate) use _sha256::make_module;
#[pymodule]
mod _sha256 {
use crate::hashlib::_hashlib::{local_sha224, local_sha256, HashArgs};
use crate::vm::{PyPayload, PyResult, VirtualMachine};
#[pyfunction]
fn sha224(args: HashArgs, vm: &VirtualMachine) -> PyResult {
Ok(local_sha224(args).into_pyobject(vm))
}
#[pyfunction]
fn sha256(args: HashArgs, vm: &VirtualMachine) -> PyResult {
Ok(local_sha256(args).into_pyobject(vm))
}
}

40
stdlib/src/sha3.rs Normal file
View File

@@ -0,0 +1,40 @@
pub(crate) use _sha3::make_module;
#[pymodule]
mod _sha3 {
use crate::hashlib::_hashlib::{
local_sha3_224, local_sha3_256, local_sha3_384, local_sha3_512, local_shake_128,
local_shake_256, HashArgs,
};
use crate::vm::{PyPayload, PyResult, VirtualMachine};
#[pyfunction]
fn sha3_224(args: HashArgs, vm: &VirtualMachine) -> PyResult {
Ok(local_sha3_224(args).into_pyobject(vm))
}
#[pyfunction]
fn sha3_256(args: HashArgs, vm: &VirtualMachine) -> PyResult {
Ok(local_sha3_256(args).into_pyobject(vm))
}
#[pyfunction]
fn sha3_384(args: HashArgs, vm: &VirtualMachine) -> PyResult {
Ok(local_sha3_384(args).into_pyobject(vm))
}
#[pyfunction]
fn sha3_512(args: HashArgs, vm: &VirtualMachine) -> PyResult {
Ok(local_sha3_512(args).into_pyobject(vm))
}
#[pyfunction]
fn shake_128(args: HashArgs, vm: &VirtualMachine) -> PyResult {
Ok(local_shake_128(args).into_pyobject(vm))
}
#[pyfunction]
fn shake_256(args: HashArgs, vm: &VirtualMachine) -> PyResult {
Ok(local_shake_256(args).into_pyobject(vm))
}
}

20
stdlib/src/sha512.rs Normal file
View File

@@ -0,0 +1,20 @@
// spell-checker:ignore usedforsecurity HASHXOF
pub(crate) use _sha512::make_module;
#[pymodule]
mod _sha512 {
use crate::hashlib::_hashlib::{HashArgs, HashWrapper, PyHasher};
use crate::vm::{PyObjectRef, PyPayload, PyResult, VirtualMachine};
use sha2::{Sha384, Sha512};
#[pyfunction(name = "sha384")]
fn sha384(args: HashArgs, vm: &VirtualMachine) -> PyResult<PyObjectRef> {
Ok(PyHasher::new("sha384", HashWrapper::new::<Sha384>(args.string)).into_pyobject(vm))
}
#[pyfunction(name = "sha512")]
fn sha512(args: HashArgs, vm: &VirtualMachine) -> PyResult<PyObjectRef> {
Ok(PyHasher::new("sha512", HashWrapper::new::<Sha512>(args.string)).into_pyobject(vm))
}
}