diff options
author | Todd Gamblin <tgamblin@llnl.gov> | 2018-07-22 23:40:28 -0700 |
---|---|---|
committer | Todd Gamblin <tgamblin@llnl.gov> | 2018-07-24 18:35:02 -0700 |
commit | 656e935e50945e7e6b52501164c1df9a8e3dbe12 (patch) | |
tree | 952cbe662e607d55c793ba6506d1d64e546d0af1 | |
parent | 674eb00e53e8d317a4b5c22d26aef2c0c7ec1753 (diff) | |
download | spack-656e935e50945e7e6b52501164c1df9a8e3dbe12.tar.gz spack-656e935e50945e7e6b52501164c1df9a8e3dbe12.tar.bz2 spack-656e935e50945e7e6b52501164c1df9a8e3dbe12.tar.xz spack-656e935e50945e7e6b52501164c1df9a8e3dbe12.zip |
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.
-rw-r--r-- | lib/spack/spack/test/url_fetch.py | 4 | ||||
-rw-r--r-- | lib/spack/spack/util/crypto.py | 88 |
2 files changed, 54 insertions, 38 deletions
diff --git a/lib/spack/spack/test/url_fetch.py b/lib/spack/spack/test/url_fetch.py index 2e6e1912bf..fb4165f4b2 100644 --- a/lib/spack/spack/test/url_fetch.py +++ b/lib/spack/spack/test/url_fetch.py @@ -52,7 +52,7 @@ def test_fetch( mock_archive.url 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: algo.update(f.read()) checksum = algo.hexdigest() @@ -145,7 +145,7 @@ def test_from_list_url(mock_packages, config): 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 checker = crypto.Checker(h) assert checker.hash_name == checksum_type diff --git a/lib/spack/spack/util/crypto.py b/lib/spack/spack/util/crypto.py index e364b62064..c2b536d50b 100644 --- a/lib/spack/spack/util/crypto.py +++ b/lib/spack/spack/util/crypto.py @@ -28,24 +28,28 @@ import hashlib import llnl.util.tty as tty -"""Set of acceptable hashes that Spack will use.""" -_hash_algorithms = [ - 'md5', - 'sha1', - 'sha224', - 'sha256', - 'sha384', - 'sha512'] +#: Set of hash algorithms that Spack can use, mapped to digest size in bytes +hashes = { + 'md5': 16, + 'sha1': 20, + 'sha224': 28, + 'sha256': 32, + 'sha384': 48, + 'sha512': 64 +} -_deprecated_hash_algorithms = ['md5'] +#: size of hash digests in bytes, mapped to algoritm names +_size_to_hash = dict((v, k) for k, v in hashes.items()) -hashes = dict() +#: List of deprecated hash functions. On some systems, these cannot be +#: used without special options to hashlib. +_deprecated_hash_algorithms = ['md5'] -"""Index for looking up hasher for a digest.""" -_size_to_hash = dict() +#: cache of hash functions generated +_hash_functions = {} class DeprecatedHash(object): @@ -65,22 +69,41 @@ class DeprecatedHash(object): return hashlib.new(self.hash_alg) -for h in _hash_algorithms: - try: - if h in _deprecated_hash_algorithms: - hash_gen = DeprecatedHash( - h, tty.debug, disable_security_check=False) - _size_to_hash[hash_gen(disable_alert=True).digest_size] = hash_gen +def hash_fun_for_algo(algo): + """Get a function that can perform the specified hash algorithm.""" + hash_gen = _hash_functions.get(algo) + if hash_gen is None: + if algo in _deprecated_hash_algorithms: + 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: - hash_gen = getattr(hashlib, h) - _size_to_hash[hash_gen().digest_size] = hash_gen - hashes[h] = hash_gen - except ValueError: - # 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) - hashes[h] = hash_gen - _size_to_hash[hash_gen(disable_alert=True).digest_size] = hash_gen + hash_gen = getattr(hashlib, algo) + _hash_functions[algo] = hash_gen + + return hash_gen + + +def hash_algo_for_digest(hexdigest): + """Gets name of the hash algorithm for a hex digest.""" + 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): @@ -123,15 +146,8 @@ class Checker(object): def __init__(self, hexdigest, **kwargs): self.block_size = kwargs.get('block_size', 2**20) self.hexdigest = hexdigest - self.sum = None - - 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] + self.sum = None + self.hash_fun = hash_fun_for_digest(hexdigest) @property def hash_name(self): |