def _load(): global _get_category # TODO: would like to add support for inheriting config from a preset # (or from existing hasher state) and letting PASSLIB_CONFIG # be an update, not a replacement. # TODO: wrap and import any custom hashers as passlib handlers, # so they could be used in the passlib config. # load config from settings _UNSET = object() config = getattr(settings, "PASSLIB_CONFIG", _UNSET) if config is _UNSET: # XXX: should probably deprecate this alias config = getattr(settings, "PASSLIB_CONTEXT", _UNSET) if config is _UNSET: config = "passlib-default" if config is None: warn( "setting PASSLIB_CONFIG=None is deprecated, " "and support will be removed in Passlib 1.8, " "use PASSLIB_CONFIG='disabled' instead.", DeprecationWarning) config = "disabled" elif not isinstance(config, (unicode, bytes, dict)): raise ExpectedTypeError(config, "str or dict", "PASSLIB_CONFIG") # load custom category func (if any) get_category = getattr(settings, "PASSLIB_GET_CATEGORY", None) if get_category and not callable(get_category): raise ExpectedTypeError(get_category, "callable", "PASSLIB_GET_CATEGORY") # check if we've been disabled if config == "disabled": if _patched: # pragma: no cover -- sanity check log.error("didn't expect monkeypatching would be applied!") _remove_patch() return # resolve any preset aliases if isinstance(config, str) and '\n' not in config: config = get_preset_config(config) # setup context _apply_patch() password_context.load(config) if get_category: # NOTE: _get_category is module global which is read by # monkeypatched functions constructed by _apply_patch() _get_category = get_category log.debug("passlib.ext.django loaded")
def pbkdf1(secret, salt, rounds, keylen=None, hash="sha1"): """pkcs#5 password-based key derivation v1.5 :arg secret: passphrase to use to generate key :arg salt: salt string to use when generating key :param rounds: number of rounds to use to generate key :arg keylen: number of bytes to generate (if ``None``, uses digest's native size) :param hash: hash function to use. must be name of a hash recognized by hashlib. :returns: raw bytes of generated key .. note:: This algorithm has been deprecated, new code should use PBKDF2. Among other limitations, ``keylen`` cannot be larger than the digest size of the specified hash. """ # validate secret & salt if not isinstance(secret, bytes): raise ExpectedTypeError(secret, "bytes", "secret") if not isinstance(salt, bytes): raise ExpectedTypeError(salt, "bytes", "salt") # validate rounds if not isinstance(rounds, int_types): raise ExpectedTypeError(rounds, "int", "rounds") if rounds < 1: raise ValueError("rounds must be at least 1") # resolve hash const, digest_size, block_size = get_hash_info(hash) # validate keylen if keylen is None: keylen = digest_size elif not isinstance(keylen, int_types): raise ExpectedTypeError(keylen, "int or None", "keylen") elif keylen < 0: raise ValueError("keylen must be at least 0") elif keylen > digest_size: raise ValueError("keylength too large for digest: %r > %r" % (keylen, digest_size)) # main pbkdf1 loop block = secret + salt for _ in irange(rounds): block = const(block).digest() return block[:keylen]
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. this can also be a callable with the signature ``prf(secret, message) -> digest``, in which case it will be returned unchanged. :raises ValueError: if the name is not known :raises TypeError: if the name is not a callable or string :returns: a tuple of :samp:`({func}, {digest_size})`. * :samp:`{func}` is a function implementing the specified prf, and has the signature ``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') this function will attempt to return the fastest implementation it can find; if M2Crypto is present, and supports the specified prf, :func:`M2Crypto.EVP.hmac` will be used behind the scenes. """ global _prf_cache if name in _prf_cache: return _prf_cache[name] if isinstance(name, str): if name.startswith("hmac-") or name.startswith("hmac_"): retval = _get_hmac_prf(name[5:]) else: raise ValueError("unknown prf algorithm: %r" % (name,)) elif callable(name): # assume it's a callable, use it directly digest_size = len(name(b('x'),b('y'))) retval = (name, digest_size) else: raise ExpectedTypeError(name, "str or callable", "prf name") _prf_cache[name] = retval return retval
def register_crypt_handler(handler, force=False, _attr=None): """register password hash handler. this method immediately registers a handler with the internal passlib registry, so that it will be returned by :func:`get_crypt_handler` when requested. :arg handler: the password hash handler to register :param force: force override of existing handler (defaults to False) :param _attr: [internal kwd] if specified, ensures ``handler.name`` matches this value, or raises :exc:`ValueError`. :raises TypeError: if the specified object does not appear to be a valid handler. :raises ValueError: if the specified object's name (or other required attributes) contain invalid values. :raises KeyError: if a (different) handler was already registered with the same name, and ``force=True`` was not specified. """ # validate handler if not is_crypt_handler(handler): raise ExpectedTypeError(handler, "password hash handler", "handler") if not handler: raise AssertionError("``bool(handler)`` must be True") # validate name name = handler.name _validate_handler_name(name) if _attr and _attr != name: raise ValueError( "handlers must be stored only under their own name (%r != %r)" % (_attr, name)) # check for existing handler other = _handlers.get(name) if other: if other is handler: log.debug("same %r handler already registered: %r", name, handler) return elif force: log.warning("overriding previously registered %r handler: %r", name, other) else: raise KeyError( "another %r handler has already been registered: %r" % (name, other)) # register handler _handlers[name] = handler log.debug("registered %r handler: %r", name, handler)
def pbkdf2(secret, salt, rounds, keylen=None, prf="hmac-sha1"): """pkcs#5 password-based key derivation v2.0 :arg secret: passphrase to use to generate key :arg salt: salt string to use when generating key :param rounds: number of rounds to use to generate key :arg keylen: number of bytes to generate. if set to ``None``, will use digest size of selected prf. :param prf: psuedo-random family to use for key strengthening. this can be any string or callable accepted by :func:`get_prf`. this defaults to ``"hmac-sha1"`` (the only prf explicitly listed in the PBKDF2 specification) :returns: raw bytes of generated key """ # validate secret & salt if not isinstance(secret, bytes): raise ExpectedTypeError(secret, "bytes", "secret") if not isinstance(salt, bytes): raise ExpectedTypeError(salt, "bytes", "salt") # validate rounds if not isinstance(rounds, int_types): raise ExpectedTypeError(rounds, "int", "rounds") if rounds < 1: raise ValueError("rounds must be at least 1") # validate keylen if keylen is not None: if not isinstance(keylen, int_types): raise ExpectedTypeError(keylen, "int or None", "keylen") elif keylen < 0: raise ValueError("keylen must be at least 0") # special case for m2crypto + hmac-sha1 if prf == "hmac-sha1" and _EVP: if keylen is None: keylen = 20 # NOTE: doing check here, because M2crypto won't take 'long' instances # (which this is when running under 32bit) if keylen > MAX_HMAC_SHA1_KEYLEN: raise ValueError("key length too long for digest") # NOTE: as of 2012-4-4, m2crypto has buffer overflow issue # which may cause segfaults if keylen > 32 (EVP_MAX_KEY_LENGTH). # therefore we're avoiding m2crypto for large keys until that's fixed. # see https://bugzilla.osafoundation.org/show_bug.cgi?id=13052 if keylen < 32: return _EVP.pbkdf2(secret, salt, rounds, keylen) # resolve prf prf_func, digest_size = get_prf(prf) if keylen is None: keylen = digest_size # figure out how many blocks we'll need block_count = (keylen+digest_size-1)//digest_size if block_count >= MAX_BLOCKS: raise ValueError("key length too long for digest") # build up result from blocks def gen(): for i in irange(block_count): digest = prf_func(secret, salt + pack(">L", i+1)) accum = bytes_to_int(digest) for _ in irange(rounds-1): digest = prf_func(secret, digest) accum ^= bytes_to_int(digest) yield int_to_bytes(accum, digest_size) return join_bytes(gen())[:keylen]
def pbkdf1(secret, salt, rounds, keylen=None, hash="sha1"): """pkcs#5 password-based key derivation v1.5 :arg secret: passphrase to use to generate key :arg salt: salt string to use when generating key :param rounds: number of rounds to use to generate key :arg keylen: number of bytes to generate (if ``None``, uses digest's native size) :param hash: hash function to use. must be name of a hash recognized by hashlib. :returns: raw bytes of generated key .. note:: This algorithm has been deprecated, new code should use PBKDF2. Among other limitations, ``keylen`` cannot be larger than the digest size of the specified hash. """ # validate secret & salt if not isinstance(secret, bytes): raise ExpectedTypeError(secret, "bytes", "secret") if not isinstance(salt, bytes): raise ExpectedTypeError(salt, "bytes", "salt") # validate rounds if not isinstance(rounds, int_types): raise ExpectedTypeError(rounds, "int", "rounds") if rounds < 1: raise ValueError("rounds must be at least 1") # resolve hash try: hash_const = getattr(hashlib, hash) except AttributeError: # check for ssl hash # NOTE: if hash unknown, new() will throw ValueError, which we'd just # reraise anyways; so instead of checking, we just let it get # thrown during first use, below # TODO: use builtin md4 class if hashlib doesn't have it. def hash_const(msg): return hashlib.new(hash, msg) # prime pbkdf1 loop, get block size block = hash_const(secret + salt).digest() # validate keylen if keylen is None: keylen = len(block) elif not isinstance(keylen, int_types): raise ExpectedTypeError(keylen, "int or None", "keylen") elif keylen < 0: raise ValueError("keylen must be at least 0") elif keylen > len(block): raise ValueError("keylength too large for digest: %r > %r" % (keylen, len(block))) # main pbkdf1 loop for _ in irange(rounds-1): block = hash_const(block).digest() return block[:keylen]
def utf8_truncate(source, index): """ helper to truncate UTF8 byte string to nearest character boundary ON OR AFTER <index>. returned prefix will always have length of at least <index>, and will stop on the first byte that's not a UTF8 continuation byte (128 - 191 inclusive). since utf8 should never take more than 4 bytes to encode known unicode values, we can stop after ``index+3`` is reached. :param bytes source: :param int index: :rtype: bytes """ # general approach: # # * UTF8 bytes will have high two bits (0xC0) as one of: # 00 -- ascii char # 01 -- ascii char # 10 -- continuation of multibyte char # 11 -- start of multibyte char. # thus we can cut on anything where high bits aren't "10" (0x80; continuation byte) # # * UTF8 characters SHOULD always be 1 to 4 bytes, though they may be unbounded. # so we just keep going until first non-continuation byte is encountered, or end of str. # this should work predictably even for malformed/non UTF8 inputs. if not isinstance(source, bytes): raise ExpectedTypeError(source, bytes, "source") # validate index end = len(source) if index < 0: index = max(0, index + end) if index >= end: return source # can stop search after 4 bytes, won't ever have longer utf8 sequence. end = min(index + 3, end) # loop until we find non-continuation byte while index < end: if byte_elem_value(source[index]) & 0xC0 != 0x80: # found single-char byte, or start-char byte. break # else: found continuation byte. index += 1 else: assert index == end # truncate at final index result = source[:index] def sanity_check(): # try to decode source try: text = source.decode("utf-8") except UnicodeDecodeError: # if source isn't valid utf8, byte level match is enough return True # validate that result was cut on character boundary assert text.startswith(result.decode("utf-8")) return True assert sanity_check() return result
def pbkdf2(secret, salt, rounds, keylen=None, prf="hmac-sha1"): """pkcs#5 password-based key derivation v2.0 :arg secret: passphrase to use to generate key :arg salt: salt string to use when generating key :param rounds: number of rounds to use to generate key :arg keylen: number of bytes to generate. if set to ``None``, will use digest size of selected prf. :param prf: psuedo-random family to use for key strengthening. this can be any string or callable accepted by :func:`get_prf`. this defaults to ``"hmac-sha1"`` (the only prf explicitly listed in the PBKDF2 specification) :returns: raw bytes of generated key """ # validate secret & salt if not isinstance(secret, bytes): raise ExpectedTypeError(secret, "bytes", "secret") if not isinstance(salt, bytes): raise ExpectedTypeError(salt, "bytes", "salt") # validate rounds if not isinstance(rounds, int_types): raise ExpectedTypeError(rounds, "int", "rounds") if rounds < 1: raise ValueError("rounds must be at least 1") # generated keyed prf helper keyed_prf, digest_size = get_keyed_prf(prf, secret) # validate keylen if keylen is None: keylen = digest_size elif not isinstance(keylen, int_types): raise ExpectedTypeError(keylen, "int or None", "keylen") elif keylen < 0: raise ValueError("keylen must be at least 0") # m2crypto's pbkdf2-hmac-sha1 is faster than ours, so use it if available. # NOTE: as of 2012-4-4, m2crypto has buffer overflow issue which frequently # causes segfaults if keylen > 32 (EVP_MAX_KEY_LENGTH). # therefore we're avoiding m2crypto for large keys until that's fixed. # (https://bugzilla.osafoundation.org/show_bug.cgi?id=13052) if prf == "hmac-sha1" and _EVP and keylen < 32: return _EVP.pbkdf2(secret, salt, rounds, keylen) # work out min block count s.t. keylen <= block_count * digest_size block_count = (keylen + digest_size - 1) // digest_size if block_count >= _MAX_BLOCKS: raise ValueError("keylen too long for digest") # build up result from blocks def gen(): for i in irange(block_count): digest = keyed_prf(salt + pack(">L", i + 1)) accum = bytes_to_int(digest) # speed-critical loop of pbkdf2 # NOTE: currently converting digests to integers since that XORs faster. for _ in irange(rounds - 1): digest = keyed_prf(digest) accum ^= bytes_to_int(digest) yield int_to_bytes(accum, digest_size) return join_bytes(gen())[:keylen]