diff options
author | Eli Collins <elic@assurancetechnologies.com> | 2012-05-01 12:42:43 -0400 |
---|---|---|
committer | Eli Collins <elic@assurancetechnologies.com> | 2012-05-01 12:42:43 -0400 |
commit | 7d4769b401b4fea8e21062e6e85503f20989aabd (patch) | |
tree | 23efda80cf128f79e475bc38054ce3c45ae219a8 | |
parent | 3fa87b2933ba2f596297297f97b8fc3e335ba581 (diff) | |
download | passlib-7d4769b401b4fea8e21062e6e85503f20989aabd.tar.gz |
loads of documentation updates
28 files changed, 282 insertions, 185 deletions
@@ -8,3 +8,4 @@ glob:*$py.class glob:MANIFEST glob:.tox glob:app.yaml +glob:.noseids @@ -15,23 +15,35 @@ Overview Welcome to Passlib 1.6. The main goal of this release was to clean up the codebase, reducing - the amount of internally-redundant code, and simplify the publically - exposed interface. This release also brings with it - a number of other improvements, including: 10 or so new hash algorithms, - additional security precautions for the existing ones, numerous - execution-time improvements, and reorganized documentation. - - .. note:: - - In order to simplify the publically exposed interface, a number - of the more cumbersome and less-used aspects (particularly the - semi-internal :class:`!CryptPolicy` class) have been deprecated. - This should not affect 99% of the code using Passlib. - - Just the same, *all deprecated interfaces are still supported, and will continue - to be supported for at least one more major release*. - To help with migration, all deprecated functions will issue an informative :exc:`DeprecationWarning` - when they are invoked, detailing their suggested replacement. + the amount of internally-redundant code, and to simplify the publically + exposed interface. It also brings with it a number of other improvements: + 10 or so new hash algorithms, additional security precautions + for the existing algorithms, a number of speed improvements, + and reorganized documentation. + +Deprecated APIs +............... + In order to improve the publically exposed interface, + some of the more cumbersome and less-used functions in Passlib + have been deprecated / renamed. This should not affect 99% of applications. + That said, all the deprecated interfaces are still present, and will continue + to be supported for at least one more major release. + To help with migration, all deprecated functions should issue an informative + :exc:`DeprecationWarning` when they are invoked, detailing their suggested replacement. + The following interfaces have changed: + + * The semi-internal :class:`!CryptPolicy` class has been deprecated + in it's entirety. All functionality has been rolled into the + parent :class:`!CryptContext` class (see :ref:`below <crypt-policy-deprecated>` for more). + + * The interface of the :mod:`passlib.apache` classes has been improved: + some confusing methods and options have been renamed, some new + constructors and other functions have been added. + + * The (undocumented) :mod:`!passlib.win32` module has been deprecated, + all of it's functionality is now offered through the + :doc:`lmhash <lib/passlib.hash.lmhash>` and :doc:`nthash <lib/passlib.hash.nthash>` + algorithms. New Hashes ---------- @@ -76,8 +88,7 @@ Existing Hashes This limit should be larger than any reasonable password size, and prevents various things including DOS abuses, and exploitation of OSes with a buggy :func:`!crypt` implementation. - See :exc:`!PasswordSizeError` for how to change - this limit. + See :exc:`~passlib.exc.PasswordSizeError` for how to change this limit. .. _consteq-issue: @@ -127,6 +138,8 @@ Existing Hashes This most likely only affects internal Passlib code. +.. _crypt-policy-deprecated: + CryptContext ------------ @@ -143,7 +156,7 @@ CryptContext * All new (and hopefully clearer) :ref:`tutorial <context-tutorial>` and :ref:`reference <context-reference>` documentation. - * The :class:`CryptPolicy` class and the :attr:`!CryptContext.policy` attribute have been deprecated. + * The :class:`CryptPolicy` class and the :attr:`!CryptContext.policy` attribute have been deprecated. This was a semi-internal class, which most applications were not involved with at all, but to be conservative about @@ -152,14 +165,14 @@ CryptContext All of the functionality of this class has been rolled into :class:`!CryptContext` itself, so there's one less class to remember. - Most of the methods exposed by :class:`!CryptPolicy` are now - :class:`!CryptContext` methods. + Many of the methods provided by :class:`!CryptPolicy` are now + :class:`!CryptContext` methods, most with the same name and call syntax. Information on migrating existing code can be found in the deprecation warnings issued by the class itself, and in the :class:`CryptPolicy` documentation. - * Two new class constructors have been added: :meth:`CryptContext.from_path` - and :meth:`CryptContext.from_string`, for loading CryptContext objects + * Two new class constructors have been added (:meth:`CryptContext.from_path` + and :meth:`CryptContext.from_string`) to aid in loading CryptContext objects directly from a configuration file. * The :ref:`deprecated <context-deprecated-option>` keyword @@ -194,6 +207,10 @@ Other Modules override Django's password hashing framework with a custom Passlib policy (an undocumented beta version of this was present in the 1.5 release). + * **new**: The :func:`passlib.utils.saslprep` function may be useful + for applications which need to normalize the unicode representation + of passwords before they are hashed. + Bugfixes -------- @@ -220,6 +237,8 @@ Internal Changes even features the addition of some simplistic fuzz testing. It will take a bit longer to run though. While not perfect, statement coverage is at about 95%. + Additionally, the hash test suite has been enhanced with many more + test vectors across the board, including 8-bit test vectors. * The internal framework used to construct the hash classes (:mod:`passlib.utils.handlers`) was rewritten drastically. The new version provides stricter input checking, @@ -409,8 +428,8 @@ Documentation * Added quickstart guide to documentation. * Various minor improvements. -Internals ---------- +Internal Changes +---------------- * Added more handler utility functions to reduce code duplication. * Expanded kdf helpers in :mod:`passlib.utils.pbkdf2`. @@ -421,9 +440,6 @@ Internals :exc:`~passlib.exc.MissingBackendError` if no backends are available. -Other ------ - * Builtin tests now use :mod:`!unittest2` if available. * Setup script no longer requires distribute or setuptools. * added (undocumented, experimental) Django app diff --git a/docs/conf.py b/docs/conf.py index d28206c..4151274 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -138,7 +138,7 @@ issue_tracker_url = "gc:passlib" # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. -html_theme = 'cloud' +html_theme = 'redcloud' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the diff --git a/docs/index.rst b/docs/index.rst index 433c251..5396e6b 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -27,8 +27,8 @@ using the :doc:`SHA256-Crypt </lib/passlib.hash.sha256_crypt>` algorithm:: >>> sha256_crypt.verify("joshua", hash) False -Contents -======== +Content Summary +=============== .. rst-class:: floater @@ -85,13 +85,12 @@ Application Helpers :mod:`passlib.ext.django` Django plugin which monkeypatches support for (almost) any hash in Passlib. -Support Modules ---------------- - :mod:`passlib.exc` - - custom warnings and exceptions used by Passlib - .. + Support Modules + --------------- + :mod:`passlib.exc` + + custom warnings and exceptions used by Passlib :mod:`passlib.registry` :mod:`passlib.utils` diff --git a/docs/install.rst b/docs/install.rst index 5007686..1fdfa31 100644 --- a/docs/install.rst +++ b/docs/install.rst @@ -54,34 +54,30 @@ To install from a source directory using :command:`setup.py`:: Testing ======= -Passlib contains a comprehensive set of unittests providing nearly complete coverage. +Passlib contains a comprehensive set of unittests (about 38% of the total code), +which provide nearly complete coverage, and verification of the hash +algorithms using multiple external sources (if detected at runtime). All unit tests are contained within the :mod:`passlib.tests` subpackage, and are designed to be run using the `Nose <http://somethingaboutorange.com/mrl/projects/nose>`_ unit testing library. -Once Passlib and Nose have been installed, the tests may be run from the source directory:: +Once Passlib and Nose have been installed, the main suite of tests may be run from the source directory:: - # to run the full passlib test suite... - PASSLIB_TEST_MODE="full" nosetests -v --tests passlib/tests + nosetests --tests passlib/tests -Tests may also be run via ``setup.py test`` or the included ``tox.ini`` file. +To run the full test suite, which includes internal cross-checks and mock-testing +of features not provided natively by the host OS:: -.. note:: + PASSLIB_TEST_MODE="full" nosetests --tests passlib/tests - Due to the critical nature of password hashing, Passlib's unittest framework - is rather extensive, covering the behavior of all the classes, 8-bit - test vectors for all supported hashes, and some primitive fuzz testing; - it occupies ~38% of the Passlib codebase. Because of this, the full test - suite make take some time to run. Setting ``PASSLIB_TEST_MODE`` to - ``"quick"`` or ``"default"`` will speed things up. +Tests may also be run via ``setup.py test`` or the included ``tox.ini`` file. .. rst-class:: html-toggle -Documentation -============= +Building the Documentation +========================== The latest copy of this documentation should always be available online at `<http://packages.python.org/passlib>`_. - If you wish to generate your own copy of the documentation, you will need to: diff --git a/docs/lib/passlib.context-tutorial.rst b/docs/lib/passlib.context-tutorial.rst index 338cbe8..953bc51 100644 --- a/docs/lib/passlib.context-tutorial.rst +++ b/docs/lib/passlib.context-tutorial.rst @@ -12,7 +12,7 @@ Overview ======== -The central class in the :mod:`passlib.context` module is the :class:`!CryptContext` class. +The :mod:`passlib.context` module contains one main class: :class:`!passlib.context.CryptContext`. This class is designed to take care of many of the more frequent coding patterns which occur in applications that need to handle multiple password hashes at once: @@ -51,38 +51,41 @@ Tutorial / Walkthrough Basic Usage ----------- At it's base, the :class:`!CryptContext` class is just a list of -:class:`!PasswordHash` objects, imported by name +:class:`~passlib.ifc.PasswordHash` objects, imported by name from the :mod:`passlib.hash` module. The following snippet creates -a new context object which supports three hash algorithms -- -:doc:`sha256_crypt <passlib.hash.sha256_crypt>`, +a new context object which supports three hash algorithms +(:doc:`sha256_crypt <passlib.hash.sha256_crypt>`, :doc:`md5_crypt <passlib.hash.md5_crypt>`, and -:doc:`des_crypt <passlib.hash.des_crypt>`:: +:doc:`des_crypt <passlib.hash.des_crypt>`):: >>> from passlib.context import CryptContext >>> myctx = CryptContext(schemes=["sha256_crypt", "md5_crypt", "des_crypt"]) This new object exposes a very similar set of methods to the :class:`!PasswordHash` -interface. Hashing and verifying passwords is equally straightforward:: +interface, and hashing and verifying passwords is equally as straightforward:: - >>> # loads first algorithm in the list (sha256_crypt), + >>> # this loads first algorithm in the schemes list (sha256_crypt), >>> # generates a new salt, and hashes the password: >>> hash1 = myctx.encrypt("joshua") >>> hash1 '$5$rounds=80000$HFEGd1wnFknpibRl$VZqjyYcTenv7CtOf986hxuE0pRaGXnuLXyfb7m9xL69' + >>> # when verifying a password, the algorithm is identified automatically: + >>> myctx.verify("gtnw", hash1) + False + >>> myctx.verify("joshua", hash1) + True + >>> # alternately, you can explicitly pick one of the configured algorithms, >>> # through this is rarely needed in practice: - >>> hash2 = myctx.encrypt("letmein", scheme="md5_crypt") + >>> hash2 = myctx.encrypt("dogsnamehere", scheme="md5_crypt") >>> hash2 '$1$e2nig/AC$stejMS1ek6W0/UogYKFao/' - >>> # when verifying a password, the algorithm is identified automatically: - >>> myctx.verify("socks", hash1) + >>> myctx.verify("letmein", hash2) False - >>> myctx.verify("joshua", hash1) + >>> myctx.verify("dogsnamehere", hash2) True - >>> myctx.verify("joshua", hash2) - False If not told otherwise, the context object will use the first algorithm listed in ``schemes`` when encrypting new hashes. This default can be changed by @@ -97,6 +100,10 @@ using the ``default`` keyword:: >>> myctx.identify(hash) 'des_crypt' +This concludes the basics of how to use a CryptContext object. +The rest of the sections detail the various features it offers, +which probably provide a better argument for *why* you'd want to use it. + .. seealso:: * the :meth:`CryptContext.encrypt`, :meth:`~CryptContext.verify`, and :meth:`~CryptContext.identify` methods. @@ -506,8 +513,11 @@ the following code in the correct function:: In the above code, the 'category' kwd can be omitted entirely, *OR* set to a string matching a user category specified in the policy file. In the latter case, any category-specific policy settings will be enforced. - For this example, assume it's ``None`` for most users, and ``"admin"`` for special users. - this namespace is entirely application chosen, it just has to match the policy file. + + For the purposes of this example (and the sample config file listed above), + it's assumed this value will be ``None`` for most users, and ``"admin"`` for special users. + This namespace is entirely up to the application, it just has to match the + category names used in the config file. See :ref:`user-categories` for more details. diff --git a/docs/lib/passlib.hash.bcrypt.rst b/docs/lib/passlib.hash.bcrypt.rst index f917dae..686665c 100644 --- a/docs/lib/passlib.hash.bcrypt.rst +++ b/docs/lib/passlib.hash.bcrypt.rst @@ -34,7 +34,7 @@ for new applications. This class can be used directly as follows:: :ref:`py-bcrypt or bcryptor <optional-libraries>` if this algorithm is going to be used. -.. seealso:: :ref:`password hash usage <password-hash-examples>` for more examples +.. seealso:: the generic :ref:`PasswordHash usage examples <password-hash-examples>` Interface ========= diff --git a/docs/lib/passlib.hash.bsdi_crypt.rst b/docs/lib/passlib.hash.bsdi_crypt.rst index c8e4b9b..3492dcc 100644 --- a/docs/lib/passlib.hash.bsdi_crypt.rst +++ b/docs/lib/passlib.hash.bsdi_crypt.rst @@ -31,7 +31,7 @@ It class can be used directly as follows:: >>> bsdi_crypt.verify("secret", hash) False -.. seealso:: :ref:`password hash usage <password-hash-examples>` for more examples +.. seealso:: the generic :ref:`PasswordHash usage examples <password-hash-examples>` Interface ========= diff --git a/docs/lib/passlib.hash.cisco_pix.rst b/docs/lib/passlib.hash.cisco_pix.rst index cb15639..a6327a7 100644 --- a/docs/lib/passlib.hash.cisco_pix.rst +++ b/docs/lib/passlib.hash.cisco_pix.rst @@ -42,7 +42,7 @@ PIX firewalls. This class can be used directly as follows:: >>> pix.verify("password", hash2) True -.. seealso:: :ref:`password hash usage <password-hash-examples>` for more examples +.. seealso:: the generic :ref:`PasswordHash usage examples <password-hash-examples>` Interface ========= diff --git a/docs/lib/passlib.hash.cisco_type7.rst b/docs/lib/passlib.hash.cisco_type7.rst index 1fdb9d3..e1efc80 100644 --- a/docs/lib/passlib.hash.cisco_type7.rst +++ b/docs/lib/passlib.hash.cisco_type7.rst @@ -14,7 +14,7 @@ .. currentmodule:: passlib.hash This class implements the "Type 7" password encoding used Cisco IOS. -This is not actually a true hash, but a reversible XOR Cipher encoding of the plaintext +This is not actually a true hash, but a reversible XOR Cipher encoding the plaintext password. Type 7 strings are (and were designed to be) plaintext equivalent; the goal was to protect from "over the shoulder" eavesdropping, and little else. They can be trivially decoded. @@ -38,7 +38,7 @@ This class can be used directly as follows:: >>> cisco_type7.decode(h) "password" -.. seealso:: :ref:`password hash usage <password-hash-examples>` for more examples +.. seealso:: the generic :ref:`PasswordHash usage examples <password-hash-examples>` .. note:: @@ -61,13 +61,16 @@ An example encoding (of ``"password"``) is ``044B0A151C36435C0D``. This has a salt/offset of 4 (``04`` in the example), and encodes password via ``4B0A151C36435C0D``. -The algorithm is a straightforward XOR Cipher (though note the description below -may not be entirely correct, see `Deviations`_ for details): +.. note:: + The following description may not be entirely correct with + respect to the official algorithm, see the `Deviations`_ section for details. + +The algorithm is a straightforward XOR Cipher: 1. The algorithm relies on the following ``ascii``-encoded 53-byte - secret key:: + constant:: - dsfd;kfoA,.iyewrkldJKDHSUBsgvca69834ncxv9873254k;fg87 + "dsfd;kfoA,.iyewrkldJKDHSUBsgvca69834ncxv9873254k;fg87" 2. A integer salt should be generated from the range 0 .. 15. The first two characters of the encoded string are the @@ -77,11 +80,11 @@ may not be entirely correct, see `Deviations`_ for details): For each byte in the password (starting with the 0th byte), the :samp:`{i}`'th byte of the password is encoded as follows: - * let ``j=(i + salt) % keylen`` - * XOR the :samp:`{i}`'th byte of the password with the :samp:`{j}`'th byte - of the secret key. - * encode the resulting byte as uppercase hexidecimal, - and append to the encoded string. + a. let ``j=(i + salt) % 53`` + b. XOR the :samp:`{i}`'th byte of the password with the :samp:`{j}`'th byte + of the magic constant. + c. encode the resulting byte as uppercase hexidecimal, + and append to the encoded string. Deviations ========== @@ -98,13 +101,13 @@ It may be updated as more information becomes available. different encoding is desired by an application, the password should be encoded before handing it to Passlib. -* Magic Key: +* Magic Constant: - Some implementations contain a truncated 26-byte key instead of the - 53-byte key listed above. However, it is likely those implementations have an - incomplete copy of the key, as they exhibit other issues as well after + Other implementations contain a truncated 26-byte constant instead of the + 53-byte constant listed above. However, it is likely those implementations + were merely incomplete, as they exhibit other issues as well after the 26th byte is reached (throwing an error, truncating the password, - outputing garbage), instead of wrapping around to the beginning of the key. + outputing garbage), and only worked for shorter passwords. * Salt Range: diff --git a/docs/lib/passlib.hash.des_crypt.rst b/docs/lib/passlib.hash.des_crypt.rst index 17113b3..f0ed330 100644 --- a/docs/lib/passlib.hash.des_crypt.rst +++ b/docs/lib/passlib.hash.des_crypt.rst @@ -26,7 +26,7 @@ It can used directly as follows:: >>> des_crypt.verify("letmein", hash) False -.. seealso:: :ref:`password hash usage <password-hash-examples>` for more examples +.. seealso:: the generic :ref:`PasswordHash usage examples <password-hash-examples>` Interface ========= diff --git a/docs/lib/passlib.hash.django_std.rst b/docs/lib/passlib.hash.django_std.rst index a908f00..1b76383 100644 --- a/docs/lib/passlib.hash.django_std.rst +++ b/docs/lib/passlib.hash.django_std.rst @@ -52,7 +52,7 @@ These classes can be used directly as follows:: >>> handler.verify("eville", h) False -.. seealso:: :ref:`password hash usage <password-hash-examples>` for more examples +.. seealso:: the generic :ref:`PasswordHash usage examples <password-hash-examples>` Interface --------- @@ -130,7 +130,7 @@ These classes can be used directly as follows:: >>> handler.verify("eville", h) False -.. seealso:: :ref:`password hash usage <password-hash-examples>` for more examples +.. seealso:: the generic :ref:`PasswordHash usage examples <password-hash-examples>` Interface --------- diff --git a/docs/lib/passlib.hash.fshp.rst b/docs/lib/passlib.hash.fshp.rst index 60e490d..7328ca4 100644 --- a/docs/lib/passlib.hash.fshp.rst +++ b/docs/lib/passlib.hash.fshp.rst @@ -39,7 +39,7 @@ It can be used directly as follows:: >>> fshp.verify("secret", hash) False -.. seealso:: :ref:`password hash usage <password-hash-examples>` for more examples +.. seealso:: the generic :ref:`PasswordHash usage examples <password-hash-examples>` Interface ========= diff --git a/docs/lib/passlib.hash.hex_digests.rst b/docs/lib/passlib.hash.hex_digests.rst index e1a7aa0..d9d9cb0 100644 --- a/docs/lib/passlib.hash.hex_digests.rst +++ b/docs/lib/passlib.hash.hex_digests.rst @@ -32,7 +32,7 @@ and can be used directly as follows:: >>> hex_sha1.verify("secret", h) #verify incorrect password False -.. seealso:: :ref:`password hash usage <password-hash-examples>` for more examples +.. seealso:: the generic :ref:`PasswordHash usage examples <password-hash-examples>` Interface ========= diff --git a/docs/lib/passlib.hash.lmhash.rst b/docs/lib/passlib.hash.lmhash.rst index fdd3234..eb3123b 100644 --- a/docs/lib/passlib.hash.lmhash.rst +++ b/docs/lib/passlib.hash.lmhash.rst @@ -36,7 +36,7 @@ This class can be used directly as follows:: >>> lmhash.verify("secret", h) False -.. seealso:: :ref:`password hash usage <password-hash-examples>` for more examples +.. seealso:: the generic :ref:`PasswordHash usage examples <password-hash-examples>` Interface ========= diff --git a/docs/lib/passlib.hash.nthash.rst b/docs/lib/passlib.hash.nthash.rst index ebee71a..da58f9c 100644 --- a/docs/lib/passlib.hash.nthash.rst +++ b/docs/lib/passlib.hash.nthash.rst @@ -33,7 +33,7 @@ This class can be used directly as follows:: >>> nthash.verify("secret", h) False -.. seealso:: :ref:`password hash usage <password-hash-examples>` for more examples +.. seealso:: the generic :ref:`PasswordHash usage examples <password-hash-examples>` Interface ========= diff --git a/docs/lib/passlib.hash.oracle10.rst b/docs/lib/passlib.hash.oracle10.rst index 7fd6ccb..540f086 100644 --- a/docs/lib/passlib.hash.oracle10.rst +++ b/docs/lib/passlib.hash.oracle10.rst @@ -31,7 +31,7 @@ a username for all encrypt/verify operations):: >>> oracle10.verify("letmein", hash, user="username") False -.. seealso:: :ref:`password hash usage <password-hash-examples>` for more examples +.. seealso:: the generic :ref:`PasswordHash usage examples <password-hash-examples>` .. warning:: diff --git a/docs/lib/passlib.hash.oracle11.rst b/docs/lib/passlib.hash.oracle11.rst index 84282d2..8eb8d80 100644 --- a/docs/lib/passlib.hash.oracle11.rst +++ b/docs/lib/passlib.hash.oracle11.rst @@ -21,7 +21,7 @@ This class can be can be used directly as follows:: >>> oracle11.verify("secret", hash) False -.. seealso:: :ref:`password hash usage <password-hash-examples>` for more examples +.. seealso:: the generic :ref:`PasswordHash usage examples <password-hash-examples>` .. warning:: diff --git a/docs/lib/passlib.hash.postgres_md5.rst b/docs/lib/passlib.hash.postgres_md5.rst index 2d038ef..b9e6200 100644 --- a/docs/lib/passlib.hash.postgres_md5.rst +++ b/docs/lib/passlib.hash.postgres_md5.rst @@ -35,7 +35,7 @@ That aside, this class can be used directly as follows:: >>> postgres_md5.verify("password", hash, user="username") False -.. seealso:: :ref:`password hash usage <password-hash-examples>` for more examples +.. seealso:: the generic :ref:`PasswordHash usage examples <password-hash-examples>` Interface ========= diff --git a/docs/lib/passlib.hash.scram.rst b/docs/lib/passlib.hash.scram.rst index 177ed3b..e9c0205 100644 --- a/docs/lib/passlib.hash.scram.rst +++ b/docs/lib/passlib.hash.scram.rst @@ -55,8 +55,8 @@ This class can be used like any other Passlib hash, as follows:: >>> scram.verify("secret", hash) False -See :ref:`password hash usage <password-hash-examples>` for more examples -for more examples of how to use the common hash interface. +See the generic :ref:`PasswordHash usage examples <password-hash-examples>` +for more details on how to use the common hash interface. Additionally, this class provides a number of useful methods for SCRAM-specific actions: * You can override the default list of digests, and/or the number of iterations:: diff --git a/docs/lib/passlib.utils.rst b/docs/lib/passlib.utils.rst index 02bf19c..9ecafda 100644 --- a/docs/lib/passlib.utils.rst +++ b/docs/lib/passlib.utils.rst @@ -19,36 +19,36 @@ They may also be useful when implementing custom handlers for existing legacy fo Constants ========= -.. data:: sys_bits +.. + .. data:: sys_bits - Native bit size of host architecture (either 32 or 64 bit). - used for various purposes internally. + Native bit size of host architecture (either 32 or 64 bit). + used for various purposes internally. .. data:: unix_crypt_schemes - List of the names of all the handlers in :mod:`passlib.hash` - which are supported by the native :func:`crypt()` function - of at least one OS. + List of the names of all the hashes in :mod:`passlib.hash` + which are natively supported by :func:`crypt` on at least one operating + system. For all hashes in this list, the expression - ``get_crypt_handler(name).has_backend("os_crypt")`` - will return ``True`` iff there is native OS support for that hash. - + :samp:`passlib.hash.{alg}.has_backend("os_crypt")` + will return ``True`` if the host OS natively supports the hash. This list is used by :data:`~passlib.hosts.host_context` and :data:`~passlib.apps.ldap_context` to determine which hashes are supported by the host. - See :ref:`mcf-identifiers` for a table of which OSes - are known to support which hashes. + .. seealso:: :ref:`mcf-identifiers` for a table of which OSes are known to support which hashes. .. PYPY JYTHON rounds_cost_values -Decorators -========== -.. autofunction:: classproperty +.. + Decorators + ========== + .. autofunction:: classproperty Unicode Helpers =============== @@ -77,7 +77,7 @@ Base64 Encoding Base64Engine Class ------------------ Passlib has to deal with a number of different Base64 encodings, -with varying endianness, as well as wildly different value <-> character +with varying endianness, as well as wildly different character <-> value mappings. This is all encapsulated in the :class:`Base64Engine` class, which provides common encoding actions for an arbitrary base64-style encoding scheme. There are also a couple of predefined instances which are commonly @@ -99,8 +99,8 @@ Common Character Maps This encoding system appears to have originated with :class:`~passlib.hash.des_crypt`, but is used by :class:`~passlib.hash.md5_crypt`, :class:`~passlib.hash.sha256_crypt`, - and others. Within Passlib, this encoding is referred as ``hash64`` encoding - to distinguish it from normal base64 and other encodings. + and others. Within Passlib, this encoding is referred as the "hash64" encoding, + to distinguish it from normal base64 and others. .. data:: BCRYPT_CHARS @@ -128,18 +128,17 @@ Predefined Instances the :class:`Base64Engine` class; the interface remains mostly unchanged. -Other ------ -.. autofunction:: ab64_encode -.. autofunction:: ab64_decode - .. + Other + ----- + .. autofunction:: ab64_encode + .. autofunction:: ab64_decode + .. data:: AB64_CHARS Variant of standard Base64 character map used by some custom Passlib hashes (see :func:`ab64_encode`). - .. Host OS ======= @@ -150,7 +149,7 @@ Randomness ========== .. data:: rng - The random number generator used by passlib to generate + The random number generator used by Passlib to generate salt strings and other things which don't require a cryptographically strong source of randomness. diff --git a/docs/modular_crypt_format.rst b/docs/modular_crypt_format.rst index d80bae4..22a285a 100644 --- a/docs/modular_crypt_format.rst +++ b/docs/modular_crypt_format.rst @@ -123,23 +123,22 @@ Identifiers & Platform Support ============================== The following table lists of all the major MCF hashes supported by Passlib, -and indicates which operating systems [#gae]_ offer native support. +and indicates which operating systems offer native support for them [#gae]_. -.. todo:: include MacOS X in this list - -==================================== ==================== =========== =========== =========== =========== ======= -Scheme Prefix Linux FreeBSD NetBSD OpenBSD Solaris -==================================== ==================== =========== =========== =========== =========== ======= -:class:`~passlib.hash.des_crypt` n/a y y y y y -:class:`~passlib.hash.bsdi_crypt` ``_`` y y y +==================================== ==================== =========== =========== =========== =========== ======= ======= +Scheme Prefix Linux FreeBSD NetBSD OpenBSD Solaris MacOSX +==================================== ==================== =========== =========== =========== =========== ======= ======= +:class:`~passlib.hash.des_crypt` none y y y y y y +:class:`~passlib.hash.bsdi_crypt` ``_`` y y y y :class:`~passlib.hash.md5_crypt` ``$1$`` y y y y y -:class:`~passlib.hash.sun_md5_crypt` ``$md5$``, ``$md5,`` y -:class:`~passlib.hash.bcrypt` ``$2$``, ``$2a$`` y y y y +:class:`~passlib.hash.sun_md5_crypt` ``$md5$``, ``$md5,`` y +:class:`~passlib.hash.bcrypt` ``$2$``, ``$2a$``, + ``$2x$``, ``$2y$`` y y y y :class:`~passlib.hash.bsd_nthash` ``$3$`` y :class:`~passlib.hash.sha256_crypt` ``$5$`` y y :class:`~passlib.hash.sha512_crypt` ``$6$`` y y :class:`~passlib.hash.sha1_crypt` ``$sha1$`` y -==================================== ==================== =========== =========== =========== =========== ======= +==================================== ==================== =========== =========== =========== =========== ======= ======= The following table lists the other MCF hashes supported by Passlib, most of which are only used by applications: @@ -163,6 +162,7 @@ Scheme Prefix Primary Use (if appears to provide hash support matching that of a typical Linux system. .. [#cta] :class:`!cta_pbkdf2_sha1` and :class:`!dlitz_pbkdf2_sha1` both use - the same identifier. They can be distinguished + the same identifier. While there are other internal differences, + they can be quickly distinguished by the fact that cta hashes will always end in ``=``, while dlitz hashes contain no ``=`` at all. diff --git a/passlib/apache.py b/passlib/apache.py index 58e0a84..ce4dbb9 100644 --- a/passlib/apache.py +++ b/passlib/apache.py @@ -415,7 +415,7 @@ class HtpasswdFile(_CommonFile): a new htpasswd file, applications can set ``new=True`` so that the existing file (if any) will not be loaded. - .. versionchanged:: 1.6 + .. versionadded:: 1.6 This feature was previously enabled by setting ``autoload=False``. That alias has been deprecated, and will be removed in Passlib 1.8 @@ -444,7 +444,7 @@ class HtpasswdFile(_CommonFile): Must be one of ``"apr_md5_crypt"``, ``"des_crypt"``, ``"ldap_sha1"``, ``"plaintext"``. It defaults to ``"apr_md5_crypt"``. - .. versionchanged:: 1.6 + .. versionadded:: 1.6 This keyword was previously named ``default``. That alias has been deprecated, and will be removed in Passlib 1.8. @@ -464,6 +464,23 @@ class HtpasswdFile(_CommonFile): will probably not be usuable by another application, and particularly not by Apache. + :param autoload: + Set to ``False`` to prevent the constructor from automatically + loaded the file from disk. + + .. deprecated:: 1.6 + This has been replaced by the *new* keyword. + Instead of setting ``autoload=False``, you should use + ``new=True``. Support for this keyword will be removed + in Passlib 1.8. + + :param default: + Change the default algorithm used to encrypt new passwords. + + .. deprecated:: 1.6 + This has been renamed to *default_scheme* for clarity. + Support for this alias will be removed in Passlib 1.8. + Loading & Saving ================ .. automethod:: load @@ -487,6 +504,19 @@ class HtpasswdFile(_CommonFile): ====================== .. automethod:: from_string + Attributes + ========== + .. attribute:: path + + Path to local file that will be used as the default + for all :meth:`load` and :meth:`save` operations. + May be written to, initialized by the *path* constructor keyword. + + .. attribute:: autosave + + Writeable flag indicating whether changes will be automatically + written to *path*. + Errors ====== :raises ValueError: @@ -697,7 +727,7 @@ class HtdigestFile(_CommonFile): a new htpasswd file, applications can set ``new=True`` so that the existing file (if any) will not be loaded. - .. versionchanged:: 1.6 + .. versionadded:: 1.6 This feature was previously enabled by setting ``autoload=False``. That alias has been deprecated, and will be removed in Passlib 1.8 @@ -720,6 +750,16 @@ class HtdigestFile(_CommonFile): This is also exposed as a readonly instance attribute. + :param autoload: + Set to ``False`` to prevent the constructor from automatically + loaded the file from disk. + + .. deprecated:: 1.6 + This has been replaced by the *new* keyword. + Instead of setting ``autoload=False``, you should use + ``new=True``. Support for this keyword will be removed + in Passlib 1.8. + Loading & Saving ================ .. automethod:: load @@ -745,6 +785,26 @@ class HtdigestFile(_CommonFile): ====================== .. automethod:: from_string + Attributes + ========== + .. attribute:: default_realm + + The default realm that will be used if one is not provided + to methods that require it. By default this is ``None``, + in which case an explicit realm must be provided for every + method call. Can be written to. + + .. attribute:: path + + Path to local file that will be used as the default + for all :meth:`load` and :meth:`save` operations. + May be written to, initialized by the *path* constructor keyword. + + .. attribute:: autosave + + Writeable flag indicating whether changes will be automatically + written to *path*. + Errors ====== :raises ValueError: diff --git a/passlib/handlers/cisco.py b/passlib/handlers/cisco.py index 956a060..3bc90a7 100644 --- a/passlib/handlers/cisco.py +++ b/passlib/handlers/cisco.py @@ -26,7 +26,7 @@ __all__ = [ class cisco_pix(uh.HasUserContext, uh.StaticHandler): """This class implements the password hash used by Cisco PIX firewalls, and follows the :ref:`password-hash-api`. - It has a single round, and relies on the username + It does a single round of hashing, and relies on the username as the salt. The :meth:`~passlib.ifc.PasswordHash.encrypt`, :meth:`~passlib.ifc.PasswordHash.genhash`, and :meth:`~passlib.ifc.PasswordHash.verify` methods diff --git a/passlib/handlers/oracle.py b/passlib/handlers/oracle.py index 7c028be..08850f0 100644 --- a/passlib/handlers/oracle.py +++ b/passlib/handlers/oracle.py @@ -54,7 +54,7 @@ ORACLE10_MAGIC = b("\x01\x23\x45\x67\x89\xAB\xCD\xEF") class oracle10(uh.HasUserContext, uh.StaticHandler): """This class implements the password hash used by Oracle up to version 10g, and follows the :ref:`password-hash-api`. - It has no salt and a single fixed round. + It does a single round of hashing, and relies on the username as the salt. The :meth:`~passlib.ifc.PasswordHash.encrypt`, :meth:`~passlib.ifc.PasswordHash.genhash`, and :meth:`~passlib.ifc.PasswordHash.verify` methods all require the following additional contextual keywords: diff --git a/passlib/handlers/postgres.py b/passlib/handlers/postgres.py index bb555ec..1324d83 100644 --- a/passlib/handlers/postgres.py +++ b/passlib/handlers/postgres.py @@ -24,7 +24,7 @@ __all__ = [ class postgres_md5(uh.HasUserContext, uh.StaticHandler): """This class implements the Postgres MD5 Password hash, and follows the :ref:`password-hash-api`. - It has no salt and a single fixed round. + It does a single round of hashing, and relies on the username as the salt. The :meth:`~passlib.ifc.PasswordHash.encrypt`, :meth:`~passlib.ifc.PasswordHash.genhash`, and :meth:`~passlib.ifc.PasswordHash.verify` methods all require the following additional contextual keywords: diff --git a/passlib/utils/__init__.py b/passlib/utils/__init__.py index a40b744..3e503a2 100644 --- a/passlib/utils/__init__.py +++ b/passlib/utils/__init__.py @@ -244,7 +244,7 @@ class memoized_property(object): #============================================================================= def consteq(left, right): - """check two strings/bytes for equality, taking constant time relative + """Check two strings/bytes for equality, taking constant time relative to the size of the righthand input. The purpose of this function is to aid in preventing timing attacks @@ -316,7 +316,7 @@ def splitcomma(source, sep=","): return [ elem.strip() for elem in source.split(sep) ] def saslprep(source, param="value"): - """normalizes unicode string using SASLPrep stringprep profile. + """Normalizes unicode string using SASLPrep stringprep profile. The SASLPrep profile is defined in :rfc:`4013`. It provides a uniform scheme for normalizing unicode usernames @@ -443,18 +443,16 @@ if stringprep is None: # pragma: no cover -- runtime detection # bytes helpers #============================================================================= def render_bytes(source, *args): - """helper for using formatting operator with bytes. - - this function is motivated by the fact that - :class:`bytes` instances do not support % or {} formatting under python 3. - this function is an attempt to provide a replacement - that will work uniformly under python 2 & 3. + """Peform ``%`` formating using bytes in a uniform manner across Python 2/3. + This function is motivated by the fact that + :class:`bytes` instances do not support ``%`` or ``{}`` formatting under Python 3. + This function is an attempt to provide a replacement: it converts everything to unicode (decode bytes instances as latin-1), performs the required formatting, then encodes the result to latin-1. - calling ``render_bytes(source, *args)`` should function roughly the same as - ``source % args`` under python 2. + Calling ``render_bytes(source, *args)`` should function roughly the same as + ``source % args`` under Python 2. """ if isinstance(source, bytes): source = source.decode("latin-1") @@ -481,10 +479,10 @@ else: return unhexlify(('%%0%dx' % (count<<1)) % value) add_doc(bytes_to_int, "decode byte string as single big-endian integer") -add_doc(int_to_bytes, "encode intger as single big-endian byte string") +add_doc(int_to_bytes, "encode integer as single big-endian byte string") def xor_bytes(left, right): - "perform bitwise-xor of two byte strings" + "Perform bitwise-xor of two byte strings (must be same size)" return int_to_bytes(bytes_to_int(left) ^ bytes_to_int(right), len(left)) def repeat_string(source, size): @@ -520,7 +518,7 @@ def is_ascii_codec(codec): return _ASCII_TEST_UNICODE.encode(codec) == _ASCII_TEST_BYTES def is_same_codec(left, right): - "check if two codec names are aliases for same codec" + "Check if two codec names are aliases for same codec" if left == right: return True if not (left and right): @@ -530,12 +528,12 @@ def is_same_codec(left, right): _B80 = b('\x80')[0] _U80 = u('\x80') def is_ascii_safe(source): - "check if source (bytes or unicode) contains only 7-bit ascii" + "Check if string (bytes or unicode) contains only 7-bit ascii" r = _B80 if isinstance(source, bytes) else _U80 return all(c < r for c in source) def to_bytes(source, encoding="utf-8", param="value", source_encoding=None): - """helper to normalize input to bytes. + """Helper to normalize input to bytes. :arg source: Source bytes/unicode to process. @@ -571,13 +569,13 @@ def to_bytes(source, encoding="utf-8", param="value", source_encoding=None): else: raise ExpectedStringError(source, param) -def to_unicode(source, source_encoding="utf-8", param="value"): - """helper to normalize input to unicode. +def to_unicode(source, encoding="utf-8", param="value"): + """Helper to normalize input to unicode. :arg source: source bytes/unicode to process. - :arg source_encoding: + :arg encoding: encoding to use when decoding bytes instances. :param param: @@ -587,13 +585,13 @@ def to_unicode(source, source_encoding="utf-8", param="value"): :returns: * returns unicode strings unchanged. - * returns bytes strings decoded using *source_encoding* + * returns bytes strings decoded using *encoding* """ - assert source_encoding + assert encoding if isinstance(source, unicode): return source elif isinstance(source, bytes): - return source.decode(source_encoding) + return source.decode(encoding) else: raise ExpectedStringError(source, param) @@ -615,10 +613,10 @@ else: raise ExpectedStringError(source, param) add_doc(to_native_str, - """take in unicode or bytes, return native string. + """Take in unicode or bytes, return native string. - python 2: encodes unicode using specified encoding, leaves bytes alone. - python 3: leaves unicode alone, decodes bytes using specified encoding. + Python 2: encodes unicode using specified encoding, leaves bytes alone. + Python 3: leaves unicode alone, decodes bytes using specified encoding. :raises TypeError: if source is not unicode or bytes. @@ -645,28 +643,42 @@ def to_hash_str(source, encoding="ascii"): # pragma: no cover -- deprecated & un #================================================================================= class Base64Engine(object): - """provides routines for encoding/decoding base64 data using + """Provides routines for encoding/decoding base64 data using arbitrary character mappings, selectable endianness, etc. :arg charmap: A string of 64 unique characters, - which will be used to encode successive 6-bit chunks. - A character's position within the string will correspond + which will be used to encode successive 6-bit chunks of data. + A character's position within the string should correspond to it's 6-bit value. :param big: Whether the encoding should be big-endian (default False). + .. note:: + This class does not currently handle base64's padding characters + in any way what so ever. + Raw Bytes <-> Encoded Bytes =========================== + The following methods convert between raw bytes, + and strings encoded using the engine's specific base64 variant: + .. automethod:: encode_bytes .. automethod:: decode_bytes .. automethod:: encode_transposed_bytes .. automethod:: decode_transposed_bytes - .. automethod:: check_repair_unused + + .. + .. automethod:: check_repair_unused + .. automethod:: repair_unused Integers <-> Encoded Bytes ========================== + The following methods allow encoding and decoding + unsigned integers to and from the engine's specific base64 variant. + Endianess is determined by the engine's ``big`` constructor keyword. + .. automethod:: encode_int6 .. automethod:: decode_int6 @@ -757,7 +769,7 @@ class Base64Engine(object): # encoding byte strings #============================================================= def encode_bytes(self, source): - """encode bytes to engine's specific base64 variant. + """encode bytes to base64 string. :arg source: byte string to encode. :returns: byte string containing encoded data. @@ -860,7 +872,7 @@ class Base64Engine(object): #============================================================= def decode_bytes(self, source): - """decode bytes from engine's specific base64 variant. + """decode bytes from base64 string. :arg source: byte string to decode. :returns: byte string containing decoded data. @@ -1060,7 +1072,7 @@ class Base64Engine(object): # integer decoding helpers - mainly used by des_crypt family #============================================================= def _decode_int(self, source, bits): - """decode hash64 string -> integer + """decode base64 string -> integer :arg source: base64 string to decode. :arg bits: number of bits in resulting integer. @@ -1114,7 +1126,7 @@ class Base64Engine(object): raise ValueError("invalid character") def decode_int12(self, source): - "decodes 2 char string -> 12-bit integer (little-endian order)" + "decodes 2 char string -> 12-bit integer" if not isinstance(source, bytes): raise TypeError("source must be bytes, not %s" % (type(source),)) if len(source) != 2: @@ -1129,7 +1141,7 @@ class Base64Engine(object): raise ValueError("invalid character") def decode_int24(self, source): - "decodes 4 char string -> 24-bit integer (little-endian order)" + "decodes 4 char string -> 24-bit integer" if not isinstance(source, bytes): raise TypeError("source must be bytes, not %s" % (type(source),)) if len(source) != 4: @@ -1349,7 +1361,7 @@ else: return None return result -add_doc(safe_crypt, """wrapper around stdlib's crypt. +add_doc(safe_crypt, """Wrapper around stdlib's crypt. This is a wrapper around stdlib's :func:`!crypt.crypt`, which attempts to provide uniform behavior across Python 2 and 3. diff --git a/passlib/utils/pbkdf2.py b/passlib/utils/pbkdf2.py index b2d3b23..916b295 100644 --- a/passlib/utils/pbkdf2.py +++ b/passlib/utils/pbkdf2.py @@ -59,18 +59,18 @@ _nhn_hash_names = [ _nhn_cache = {} def norm_hash_name(name, format="hashlib"): - """normalize hash function name + """Normalize hash function name :arg name: - un-normalized hash function name. + Original hash function name. - this name can be a Python :mod:`~hashlib` digest name, - a SCRAM mechanism name, IANA assigned hash name, etc; - case is ignored, underscores converted to hyphens. + This name can be a Python :mod:`~hashlib` digest name, + a SCRAM mechanism name, IANA assigned hash name, etc. + Case is ignored, and underscores are converted to hyphens. :param format: - naming convention to normalize hash names to. - possible values are: + Naming convention to normalize to. + Possible values are: * ``"hashlib"`` (the default) - normalizes name to be compatible with Python's :mod:`!hashlib`. @@ -80,7 +80,7 @@ def norm_hash_name(name, format="hashlib"): and then uses a heuristic to give a "best guess". :returns: - hash name, returned as native string. + Hash name, returned as native :class:`!str`. """ # check cache try: @@ -303,6 +303,7 @@ def pbkdf1(secret, salt, rounds, keylen=None, hash="sha1"): # 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) @@ -343,11 +344,11 @@ def pbkdf2(secret, salt, rounds, keylen=None, prf="hmac-sha1"): :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. + 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 + this defaults to ``"hmac-sha1"`` (the only prf explicitly listed in the PBKDF2 specification) :returns: |