Passlib 1.7

1.7.2 (2019-11-22)

This release rolls up assorted bug & compatibility fixes since 1.7.1.

New Features

  • argon2: Now supports Argon2 “ID” and “D” hashes (assuming new enough backend library). Now defaults to “ID” hashes instead of “I” hashes, but this can be overridden via type keyword. (issue 101)
  • scrypt: Now uses python 3.6 stdlib’s hashlib.scrypt() as backend, if present (issue 86).

Bugfixes

  • Python 3.8 compatibility fixes
  • passlib.apache.HtpasswdFile: Now generates bcrypt hashes using the "$2y$" prefix, which should work properly with Apache 2.4’s htpasswd tool. Previous releases used the functionally equivalent "$2b$" prefix, which htpasswd was unable to read (issue 95).
  • passlib.totp: The TOTP.to_uri() method now prepends the issuer to URI label, (per the KeyURI spec). This should fix some compatibility issues with older TOTP clients (issue 92)
  • Fixed error in argon2.parsehash() (issue 97)
  • unittests: crypt() unittests now account for linux systems running libxcrypt (such as recent Fedora releases)

Deprecations

Warning

Due to lack of pip and venv support, Passlib is no longer fully tested on Python 2.6 & 3.3. There are no known issues, and bugfixes against these versions will still be accepted for the Passlib 1.7.x series. However, Passlib 1.8 will drop support for Python 2.6 & 3.3; and Passlib 2.0 will drop support for Python 2.x entirely.

  • Support for Python 2.6 & 3.3 is deprecated; and will be dropped in Passlib 1.8.
  • bcrypt: py-bcrypt and bcryptor backends are deprecated, and support will be removed in Passlib 1.8. Please switch to the bcrypt backend.

Other Changes

  • setup.py: now honors $SOURCE_DATE_EPOCH to help with reproducible builds
  • argon2: Now throws helpful error if “argon2” package is actually an incompatible or supported version of argon2_cffi (issue 99).
  • documentation: Various updates & corrections. building the documentation now requires Sphinx 1.6 or newer.

1.7.1 (2017-1-30)

This release rolls up assorted bug & compatibility fixes since 1.7.0.

Bugfixes

  • cisco_asa and cisco_pix: Fixed a number of issues which under certain conditions caused prior releases to generate hashes that were unverifiable on Cisco systems.
  • PasswordHash.hash() will now warn if passed any settings keywords. This usage was deprecated in 1.7.0, but warning wasn’t properly enabled. See Customizing the Configuration for the preferred way to pass settings.
  • setup.py: Don’t append timestamp when run from an sdist. This should fix some downstream build issues.
  • passlib.tests.test_totp: Test suite now traps additional errors that datetime.utcfromtimestamp() may throw under python 3, which should fix some test failures on architectures with rarer ILP sizes. It also works around Python 3.6 bug 29100.

Deprecations

  • CryptContext: The harden_verify flag has been turned into a NOOP and deprecated. It will be removed in passlib 1.8 along with the already-deprecated min_verify_time (issue 83).

Other Changes

  • passlib.tests.utils: General truncation policy details were hammered out, and additional hasher tests were added to enforce them.
  • documentation: Various updates & corrections.

1.7.0 (2016-11-22)

Overview

Welcome to Passlib 1.7!

This release includes a number of new features, cleans up some long-standing design issues, and contains a number of internal improvements; all part of the roadmap towards a leaner and simpler Passlib 2.0.

Highlights include:

  • The misnamed PasswordHash.encrypt() method has been renamed to PasswordHash.hash() (and the old alias deprecated). This is part of a much larger project to clean up passlib’s password hashing API, see the PasswordHash Tutorial for a walkthrough.
  • Large speedup of the internal PBKDF2 routines.
  • Updated documentation

Requirements

  • Passlib now requires Python 2.6, 2.7, or >= 3.3. Support for Python versions 2.5 and 3.0 through 3.2 have been dropped. Support for PyPy 1.x has also been dropped.
  • The passlib.ext.django extension now requires Django 1.8 or better. Django 1.7 and earlier are no longer supported.

New Features

New Hashes

New Modules

  • New passlib.totp module provides full support for TOTP tokens on both client and server side. This module contains both low-level primitives, and high-level helpers for persisting and tracking client state.
  • New passlib.pwd module added to aid in password generation. Features support for alphanumeric passwords, or generation of phrases using the EFF’s password generation wordlist.

CryptContext Features

  • The CryptContext object now has helper methods for dealing with hashes representing disabled accounts (issue 45).
  • All hashers which truncate passwords (e.g. bcrypt and des_crypt) can now be configured to raise a PasswordTruncateError when a overly-large password is provided. This configurable via (for example) bcrypt.using(truncate_error=True).hash(secret), or globally as an option to CryptContext (issue 59).

Cryptographic Backends

  • The pbkdf2_hmac() function and all PBKDF2-based hashes have been sped up by ~20% compared to Passlib 1.6. For an even greater speedup, it will now take advantage of the external fastpbk2 library, or stdlib’s hashlib.pbkdf2_hmac() (when available).

Other Changes

Other changes of note in Passlib 1.7:

  • New workflows have been for configuring the hashers through PasswordHash.using(), and testing hashes through PasswordHash.needs_update(). See the PasswordHash Tutorial for a walkthrough.

  • bcrypt and bcrypt_sha256 now default to the “2b” format.

  • Added support for Django’s Argon2 wrapper (django_argon2)

  • passlib.apache.HtpasswdFile has been updated to support all of Apache 2.4’s hash schemes, as well as all host OS crypt formats; allowing for much more secure hashes in htpasswd files.

    You can now specify if the default hash should be compatible with apache 2.2 or 2.4, and host-specific or portable. See the default_schemes keyword for details.

  • Large parts of the documentation have been rewritten, to separate tutorial & api reference content, and provide more detail on various features.

  • Official documentation is now at https://passlib.readthedocs.io

Internal Changes

  • The majority of CryptContext’s internal rounds handling & migration code has been moved to the password hashes themselves, taking advantage of the new PasswordHash.using() and PasswordHash.needs_update() methods.

    This allows much more flexibility when configuring a hasher directly, as well making it easier for CryptContext to support hash-specific parameters.

  • The shared PasswordHash unittests now check all hash handlers for basic thread-safety (motivated by the pybcrypt 0.2 concurrency bug).

  • consteq() is now wraps stdlib’s hmac.compare_digest() when available (python 2.7.11, python 3.3 and up).

Bugfixes

  • bcrypt: Passlib will now detect and work around a fatal concurrency bug in py-bcrypt 0.2 and earlier (a PasslibSecurityWarning will also be issued). Nevertheless, users are strongly encouraged to upgrade to py-bcrypt 0.3 or another bcrypt library if you are using the bcrypt hash.
  • CryptContext instances now pass contextual keywords (such as “user”) to the hashes that support them, but ignore them for hashes that don’t (issue 63).
  • The passlib.apache htpasswd helpers now preserve blank lines and comments, rather than throwing a parse error (issue 73).
  • passlib.ext.django and unittests: compatibility fixes for Django 1.9 / 1.10, and some internal refactoring (issue 68).
  • The django_disabled hash now appends a 40-char alphanumeric string, to match Django’s behavior.

Deprecations

As part of a long-range plan to restructure and simplify both the API and the internals of Passlib, a number of methods have been deprecated & replaced. The eventually goal is a large cleanup and overhaul as part of Passlib 2.0. There will be at least one more 1.x version before Passlib 2.0, to provide a final transitional release (see the Passlib Roadmap).

Password Hash API Deprecations

As part of this cleanup, the PasswordHash API (used by all hashes in passlib), has had a number of changes:

See also

PasswordHash Tutorial, which walks through using the new hasher interface.

  • [major] The PasswordHash.encrypt() method has been renamed to PasswordHash.hash(), to clarify that it’s performing one-way hashing rather than reversiable encryption. A compatibility alias will remain in place until Passlib 2.0. This should fix the longstanding issue 21.

  • [major] Passing explicit configuration options to the PasswordHash.encrypt() method (now called PasswordHash.hash()) is deprecated. To provide settings such as rounds and salt_size, callers should use the new PasswordHash.using() method, which generates a new hasher with a customized configuration. For example, instead of:

    >>>  sha256_crypt.encrypt("secret", rounds=12345)
    

    … applications should now use:

    >>>  sha256_crypt.using(rounds=12345).hash("secret")
    

    Support for the old syntax will be removed in Passlib 2.0.

    Note

    This doesn’t apply to contextual options such as cisco_pix’s user keyword, which should still be passed into the hash() method.

  • [minor] The little-used PasswordHash.genhash() and PasswordHash.genconfig() methods have been deprecated. Compatibility aliases will remain in place until Passlib 2.0, at which point they will be removed entirely.

Crypt Context API Deprecations

Applications which use passlib’s CryptContext should not be greatly affected by this release; only one major deprecation was made:

  • [major] To match the PasswordHash API changes above, the CryptContext.encrypt() method was renamed to CryptContext.hash(). A compatibility alias will remain until Passlib 2.0.

A fewer internal options and infrequently used features have been deprecated:

  • [minor] CryptContext.hash(), verify(), verify_and_update(), and needs_update(): The scheme keyword is now deprecated; support will be removed in Passlib 2.0.

  • [minor] CryptContext.hash(): Passing settings keywords to hash() such as rounds and salt is deprecated. Code should now get ahold of the default hasher, and invoke it explicitly:

    >>>  # for example, calls that did this:
    >>>  context.hash(secret, rounds=1234)
    
    >>>  # should use this instead:
    >>>  context.handler().using(rounds=1234).hash(secret)
    
  • [minor] The vary_rounds option has been deprecated, and will be removed in Passlib 2.0. It provided very little security benefit, and was judged not worth the additional code complexity it requires.

  • [minor] The special wildcard all scheme name has been deprecated, and will be removed in Passlib 2.0. The only legitimate use was to support vary_rounds, which itself will be removed in 2.0.

Other Deprecations

A few other assorted deprecations have been made:

  • The passlib.utils.generate_secret() function has been deprecated in favor of the new passlib.pwd module, and the old function will be removed in Passlib 2.0.

  • Most of passlib’s internal cryptography helpers have been moved from passlib.utils to passlib.crypto, and the APIs refactored. This allowed unification of various hash management routines, some speed ups to the HMAC and PBKDF2 primitives, and opens up the architecture to support more optional backend libraries.

    Compatibility wrappers will be kept in place at the old location until Passlib 2.0.

  • Some deprecations and internal changes have been made to the passlib.utils.handlers module, which provides the common framework Passlib uses to implement hashers.

Caution

More backwards-incompatible relocations are planned for the internal passlib.utils module in the Passlib 1.8 / 1.9 releases.

Backwards Incompatibilities

Changes in existing behavior:

  • [minor] M2Crypto no longer used to accelerate pbkdf2-hmac-sha1; applications relying on this to speed up pbkdf2_sha1 should install fastpbkdf2.

Scheduled removal of features:

  • [minor] passlib.context: The min_verify_time keyword that was deprecated in release 1.6, is now completely ignored. Support will be removed entirely in release 1.8.
  • [trivial] passlib.hash: The internal PasswordHash.parse_rounds() method, deprecated in 1.6, has been removed.

Minor incompatibilities:

  • [minor] passlib.hash: The little-used method genconfig() will now always return a valid hash, rather than a truncated configuration string or None.
  • [minor] passlib.hash: The little-used method genhash() no longer accepts None as a config argument.
  • [trivial] passlib.utils.pbkdf2.pbkdf2() no longer supports custom PRF callables. this was an unused feature, and prevented some useful optimizations.