core: make spack.util.crypto
initialization less expensive.
- This hard-codes the hash lengths rather than computing them on import. - Also cleans up the code in `spack.util.crypto` to make it easier to understand.
This commit is contained in:
parent
674eb00e53
commit
656e935e50
@ -52,7 +52,7 @@ def test_fetch(
|
|||||||
mock_archive.url
|
mock_archive.url
|
||||||
mock_archive.path
|
mock_archive.path
|
||||||
|
|
||||||
algo = crypto.hashes[checksum_type]()
|
algo = crypto.hash_fun_for_algo(checksum_type)()
|
||||||
with open(mock_archive.archive_file, 'rb') as f:
|
with open(mock_archive.archive_file, 'rb') as f:
|
||||||
algo.update(f.read())
|
algo.update(f.read())
|
||||||
checksum = algo.hexdigest()
|
checksum = algo.hexdigest()
|
||||||
@ -145,7 +145,7 @@ def test_from_list_url(mock_packages, config):
|
|||||||
|
|
||||||
|
|
||||||
def test_hash_detection(checksum_type):
|
def test_hash_detection(checksum_type):
|
||||||
algo = crypto.hashes[checksum_type]()
|
algo = crypto.hash_fun_for_algo(checksum_type)()
|
||||||
h = 'f' * (algo.digest_size * 2) # hex -> bytes
|
h = 'f' * (algo.digest_size * 2) # hex -> bytes
|
||||||
checker = crypto.Checker(h)
|
checker = crypto.Checker(h)
|
||||||
assert checker.hash_name == checksum_type
|
assert checker.hash_name == checksum_type
|
||||||
|
@ -28,24 +28,28 @@
|
|||||||
import llnl.util.tty as tty
|
import llnl.util.tty as tty
|
||||||
|
|
||||||
|
|
||||||
"""Set of acceptable hashes that Spack will use."""
|
#: Set of hash algorithms that Spack can use, mapped to digest size in bytes
|
||||||
_hash_algorithms = [
|
hashes = {
|
||||||
'md5',
|
'md5': 16,
|
||||||
'sha1',
|
'sha1': 20,
|
||||||
'sha224',
|
'sha224': 28,
|
||||||
'sha256',
|
'sha256': 32,
|
||||||
'sha384',
|
'sha384': 48,
|
||||||
'sha512']
|
'sha512': 64
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#: size of hash digests in bytes, mapped to algoritm names
|
||||||
|
_size_to_hash = dict((v, k) for k, v in hashes.items())
|
||||||
|
|
||||||
|
|
||||||
|
#: List of deprecated hash functions. On some systems, these cannot be
|
||||||
|
#: used without special options to hashlib.
|
||||||
_deprecated_hash_algorithms = ['md5']
|
_deprecated_hash_algorithms = ['md5']
|
||||||
|
|
||||||
|
|
||||||
hashes = dict()
|
#: cache of hash functions generated
|
||||||
|
_hash_functions = {}
|
||||||
|
|
||||||
"""Index for looking up hasher for a digest."""
|
|
||||||
_size_to_hash = dict()
|
|
||||||
|
|
||||||
|
|
||||||
class DeprecatedHash(object):
|
class DeprecatedHash(object):
|
||||||
@ -65,22 +69,41 @@ def __call__(self, disable_alert=False):
|
|||||||
return hashlib.new(self.hash_alg)
|
return hashlib.new(self.hash_alg)
|
||||||
|
|
||||||
|
|
||||||
for h in _hash_algorithms:
|
def hash_fun_for_algo(algo):
|
||||||
try:
|
"""Get a function that can perform the specified hash algorithm."""
|
||||||
if h in _deprecated_hash_algorithms:
|
hash_gen = _hash_functions.get(algo)
|
||||||
hash_gen = DeprecatedHash(
|
if hash_gen is None:
|
||||||
h, tty.debug, disable_security_check=False)
|
if algo in _deprecated_hash_algorithms:
|
||||||
_size_to_hash[hash_gen(disable_alert=True).digest_size] = hash_gen
|
try:
|
||||||
|
hash_gen = DeprecatedHash(
|
||||||
|
algo, tty.debug, disable_security_check=False)
|
||||||
|
|
||||||
|
# call once to get a ValueError if usedforsecurity is needed
|
||||||
|
hash_gen(disable_alert=True)
|
||||||
|
except ValueError:
|
||||||
|
# Some systems may support the 'usedforsecurity' option
|
||||||
|
# so try with that (but display a warning when it is used)
|
||||||
|
hash_gen = DeprecatedHash(
|
||||||
|
algo, tty.warn, disable_security_check=True)
|
||||||
else:
|
else:
|
||||||
hash_gen = getattr(hashlib, h)
|
hash_gen = getattr(hashlib, algo)
|
||||||
_size_to_hash[hash_gen().digest_size] = hash_gen
|
_hash_functions[algo] = hash_gen
|
||||||
hashes[h] = hash_gen
|
|
||||||
except ValueError:
|
return hash_gen
|
||||||
# Some systems may support the 'usedforsecurity' option so try with
|
|
||||||
# that (but display a warning when it is used)
|
|
||||||
hash_gen = DeprecatedHash(h, tty.warn, disable_security_check=True)
|
def hash_algo_for_digest(hexdigest):
|
||||||
hashes[h] = hash_gen
|
"""Gets name of the hash algorithm for a hex digest."""
|
||||||
_size_to_hash[hash_gen(disable_alert=True).digest_size] = hash_gen
|
bytes = len(hexdigest) / 2
|
||||||
|
if bytes not in _size_to_hash:
|
||||||
|
raise ValueError(
|
||||||
|
'Spack knows no hash algorithm for this digest: %s' % hexdigest)
|
||||||
|
return _size_to_hash[bytes]
|
||||||
|
|
||||||
|
|
||||||
|
def hash_fun_for_digest(hexdigest):
|
||||||
|
"""Gets a hash function corresponding to a hex digest."""
|
||||||
|
return hash_fun_for_algo(hash_algo_for_digest(hexdigest))
|
||||||
|
|
||||||
|
|
||||||
def checksum(hashlib_algo, filename, **kwargs):
|
def checksum(hashlib_algo, filename, **kwargs):
|
||||||
@ -123,15 +146,8 @@ class Checker(object):
|
|||||||
def __init__(self, hexdigest, **kwargs):
|
def __init__(self, hexdigest, **kwargs):
|
||||||
self.block_size = kwargs.get('block_size', 2**20)
|
self.block_size = kwargs.get('block_size', 2**20)
|
||||||
self.hexdigest = hexdigest
|
self.hexdigest = hexdigest
|
||||||
self.sum = None
|
self.sum = None
|
||||||
|
self.hash_fun = hash_fun_for_digest(hexdigest)
|
||||||
bytes = len(hexdigest) / 2
|
|
||||||
if bytes not in _size_to_hash:
|
|
||||||
raise ValueError(
|
|
||||||
'Spack knows no hash algorithm for this digest: %s'
|
|
||||||
% hexdigest)
|
|
||||||
|
|
||||||
self.hash_fun = _size_to_hash[bytes]
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def hash_name(self):
|
def hash_name(self):
|
||||||
|
Loading…
Reference in New Issue
Block a user