def test_lookup_hash_alt_types(self): """lookup_hash() -- alternate types""" from passlib.crypto.digest import lookup_hash info = lookup_hash("sha256") self.assertIs(lookup_hash(info), info) self.assertIs(lookup_hash(info.const), info) self.assertRaises(TypeError, lookup_hash, 123)
def test_lookup_hash_ctor(self): """lookup_hash() -- constructor""" from passlib.crypto.digest import lookup_hash # invalid/unknown names should be rejected self.assertRaises(ValueError, lookup_hash, "new") self.assertRaises(ValueError, lookup_hash, "__name__") self.assertRaises(ValueError, lookup_hash, "sha4") # 1. should return hashlib builtin if found self.assertEqual(lookup_hash("md5"), (hashlib.md5, 16, 64)) # 2. should return wrapper around hashlib.new() if found try: hashlib.new("sha") has_sha = True except ValueError: has_sha = False if has_sha: record = lookup_hash("sha") const = record[0] self.assertEqual(record, (const, 20, 64)) self.assertEqual( hexlify(const(b"abc").digest()), b"0164b8a914cd2a5e74c4f7ff082c4d97f1edf880", ) else: self.assertRaises(ValueError, lookup_hash, "sha") # 3. should fall back to builtin md4 try: hashlib.new("md4") has_md4 = True except ValueError: has_md4 = False record = lookup_hash("md4") const = record[0] if not has_md4: from passlib.crypto._md4 import md4 self.assertIs(const, md4) self.assertEqual(record, (const, 16, 64)) self.assertEqual( hexlify(const(b"abc").digest()), b"a448017aaf21d8525fc10ae87aa6729d" ) # 4. unknown names should be rejected self.assertRaises(ValueError, lookup_hash, "xxx256") # should memoize records self.assertIs(lookup_hash("md5"), lookup_hash("md5"))
def test_lookup_hash_ctor(self): """lookup_hash() -- constructor""" from passlib.crypto.digest import lookup_hash # invalid/unknown names should be rejected self.assertRaises(ValueError, lookup_hash, "new") self.assertRaises(ValueError, lookup_hash, "__name__") self.assertRaises(ValueError, lookup_hash, "sha4") # 1. should return hashlib builtin if found self.assertEqual(lookup_hash("md5"), (hashlib.md5, 16, 64)) # 2. should return wrapper around hashlib.new() if found try: hashlib.new("sha") has_sha = True except ValueError: has_sha = False if has_sha: record = lookup_hash("sha") const = record[0] self.assertEqual(record, (const, 20, 64)) self.assertEqual(hexlify(const(b"abc").digest()), b"0164b8a914cd2a5e74c4f7ff082c4d97f1edf880") else: self.assertRaises(ValueError, lookup_hash, "sha") # 3. should fall back to builtin md4 try: hashlib.new("md4") has_md4 = True except ValueError: has_md4 = False record = lookup_hash("md4") const = record[0] if not has_md4: from passlib.crypto._md4 import md4 self.assertIs(const, md4) self.assertEqual(record, (const, 16, 64)) self.assertEqual(hexlify(const(b"abc").digest()), b"a448017aaf21d8525fc10ae87aa6729d") # 4. unknown names should be rejected self.assertRaises(ValueError, lookup_hash, "xxx256") # should memoize records self.assertIs(lookup_hash("md5"), lookup_hash("md5"))
def create_hex_hash(digest, module=__name__, django_name=None, required=True): """ create hex-encoded unsalted hasher for specified digest algorithm. .. versionchanged:: 1.7.3 If called with unknown/supported digest, won't throw error immediately, but instead return a dummy hasher that will throw error when called. set ``required=True`` to restore old behavior. """ info = lookup_hash(digest, required=required) name = "hex_" + info.name if not info.supported: info.digest_size = 0 hasher = type( name, (HexDigestHash, ), dict( name=name, __module__=module, # so ABCMeta won't clobber it # sometimes it's a function, sometimes not. so wrap it. _hash_func=staticmethod(info.const), checksum_size=info.digest_size * 2, __doc__= """This class implements a plain hexadecimal %s hash, and follows the :ref:`password-hash-api`. It supports no optional or contextual keywords. """ % (info.name, ))) if not info.supported: hasher.supported = False if django_name: hasher.django_name = django_name return hasher
def get_prf(name): """Lookup pseudo-random family (PRF) by name. :arg name: This must be the name of a recognized prf. Currently this only recognizes names with the format :samp:`hmac-{digest}`, where :samp:`{digest}` is the name of a hash function such as ``md5``, ``sha256``, etc. todo: restore text about callables. :raises ValueError: if the name is not known :raises TypeError: if the name is not a callable or string :returns: a tuple of :samp:`({prf_func}, {digest_size})`, where: * :samp:`{prf_func}` is a function implementing the specified PRF, and has the signature ``prf_func(secret, message) -> digest``. * :samp:`{digest_size}` is an integer indicating the number of bytes the function returns. Usage example:: >>> from passlib.utils.pbkdf2 import get_prf >>> hmac_sha256, dsize = get_prf("hmac-sha256") >>> hmac_sha256 <function hmac_sha256 at 0x1e37c80> >>> dsize 32 >>> digest = hmac_sha256('password', 'message') .. deprecated:: 1.7 This function is deprecated, and will be removed in Passlib 2.0. This only related replacement is :func:`passlib.crypto.digest.compile_hmac`. """ global _prf_cache if name in _prf_cache: return _prf_cache[name] if isinstance(name, native_string_types): if not name.startswith(_HMAC_PREFIXES): raise ValueError("unknown prf algorithm: %r" % (name, )) digest = lookup_hash(name[5:]).name def hmac(key, msg): return compile_hmac(digest, key)(msg) record = (hmac, hmac.digest_info.digest_size) elif callable(name): # assume it's a callable, use it directly digest_size = len(name(b'x', b'y')) record = (name, digest_size) else: raise ExpectedTypeError(name, "str or callable", "prf name") _prf_cache[name] = record return record
def get_prf(name): """Lookup pseudo-random family (PRF) by name. :arg name: This must be the name of a recognized prf. Currently this only recognizes names with the format :samp:`hmac-{digest}`, where :samp:`{digest}` is the name of a hash function such as ``md5``, ``sha256``, etc. todo: restore text about callables. :raises ValueError: if the name is not known :raises TypeError: if the name is not a callable or string :returns: a tuple of :samp:`({prf_func}, {digest_size})`, where: * :samp:`{prf_func}` is a function implementing the specified PRF, and has the signature ``prf_func(secret, message) -> digest``. * :samp:`{digest_size}` is an integer indicating the number of bytes the function returns. Usage example:: >>> from passlib.utils.pbkdf2 import get_prf >>> hmac_sha256, dsize = get_prf("hmac-sha256") >>> hmac_sha256 <function hmac_sha256 at 0x1e37c80> >>> dsize 32 >>> digest = hmac_sha256('password', 'message') .. deprecated:: 1.7 This function is deprecated, and will be removed in Passlib 2.0. This only related replacement is :func:`passlib.crypto.digest.compile_hmac`. """ global _prf_cache if name in _prf_cache: return _prf_cache[name] if isinstance(name, native_string_types): if not name.startswith(_HMAC_PREFIXES): raise ValueError("unknown prf algorithm: %r" % (name,)) digest = lookup_hash(name[5:]).name def hmac(key, msg): return compile_hmac(digest, key)(msg) record = (hmac, hmac.digest_info.digest_size) elif callable(name): # assume it's a callable, use it directly digest_size = len(name(b'x', b'y')) record = (name, digest_size) else: raise ExpectedTypeError(name, "str or callable", "prf name") _prf_cache[name] = record return record
def test_lookup_hash_metadata(self): """lookup_hash() -- metadata""" from passlib.crypto.digest import lookup_hash # quick test of metadata using known reference - sha256 info = lookup_hash("sha256") self.assertEqual(info.name, "sha256") self.assertEqual(info.iana_name, "sha-256") self.assertEqual(info.block_size, 64) self.assertEqual(info.digest_size, 32) self.assertIs(lookup_hash("SHA2-256"), info) # quick test of metadata using known reference - md5 info = lookup_hash("md5") self.assertEqual(info.name, "md5") self.assertEqual(info.iana_name, "md5") self.assertEqual(info.block_size, 64) self.assertEqual(info.digest_size, 16)
def test_lookup_hash_w_unknown_name(self): """lookup_hash() -- unknown hash name""" from passlib.crypto.digest import lookup_hash # unknown names should be rejected by default self.assertRaises(UnknownHashError, lookup_hash, "xxx256") # required=False should return stub record instead info = lookup_hash("xxx256", required=False) self.assertFalse(info.supported) self.assertRaisesRegex(UnknownHashError, "unknown hash: 'xxx256'", info.const) self.assertEqual(info.name, "xxx256") self.assertEqual(info.digest_size, None) self.assertEqual(info.block_size, None) # should cache stub records info2 = lookup_hash("xxx256", required=False) self.assertIs(info2, info)
def test_mock_fips_mode(self): """ lookup_hash() -- test set_mock_fips_mode() """ from passlib.crypto.digest import lookup_hash, _set_mock_fips_mode # check if md5 is available so we can test mock helper if not lookup_hash("md5", required=False).supported: raise self.skipTest("md5 not supported") # enable monkeypatch to mock up fips mode _set_mock_fips_mode() self.addCleanup(_set_mock_fips_mode, False) pat = "'md5' hash disabled for fips" self.assertRaisesRegex(UnknownHashError, pat, lookup_hash, "md5") info = lookup_hash("md5", required=False) self.assertRegex(info.error_text, pat) self.assertRaisesRegex(UnknownHashError, pat, info.const) # should use hardcoded fallback info self.assertEqual(info.digest_size, 16) self.assertEqual(info.block_size, 64)
def create_hex_hash(digest, module=__name__): # NOTE: could set digest_name=hash.name for cpython, but not for some other platforms. info = lookup_hash(digest) name = "hex_" + info.name return type( name, (HexDigestHash, ), dict( name=name, __module__=module, # so ABCMeta won't clobber it _hash_func=staticmethod( info.const ), # sometimes it's a function, sometimes not. so wrap it. checksum_size=info.digest_size * 2, __doc__= """This class implements a plain hexadecimal %s hash, and follows the :ref:`password-hash-api`. It supports no optional or contextual keywords. """ % (info.name, )))
def get_md4_const(self): """ get md4 constructor -- overridden by subclasses to use alternate backends. """ return lookup_hash("md4").const
# ============================================================================= # imports # ============================================================================= # core from binascii import hexlify import logging log = logging.getLogger(__name__) from warnings import warn # site # pkg from passlib.utils import to_unicode, right_pad_string from passlib.utils.compat import unicode from passlib.crypto.digest import lookup_hash md4 = lookup_hash("md4").const import passlib.utils.handlers as uh # local __all__ = [ "lmhash", "nthash", "bsd_nthash", "msdcc", "msdcc2", ] # ============================================================================= # lanman hash # =============================================================================
""" passlib.utils.md4 - DEPRECATED MODULE, WILL BE REMOVED IN 2.0 MD4 should now be looked up through ``passlib.crypto.digest.lookup_hash("md4").const``, which provides unified handling stdlib implementation (if present). """ #============================================================================= # issue deprecation warning for module #============================================================================= from warnings import warn warn("the module 'passlib.utils.md4' is deprecated as of Passlib 1.7, " "and will be removed in Passlib 2.0, please use " "'lookup_hash(\"md4\").const()' from 'passlib.crypto' instead", DeprecationWarning) #============================================================================= # backwards compat exports #============================================================================= __all__ = ["md4"] # this should use hashlib version if available, # and fall back to builtin version. from passlib.crypto.digest import lookup_hash md4 = lookup_hash("md4").const del lookup_hash #============================================================================= # eof #=============================================================================