diff options
-rw-r--r-- | LICENSE | 69 | ||||
-rw-r--r-- | README | 27 | ||||
-rw-r--r-- | docs/contents.rst | 1 | ||||
-rw-r--r-- | docs/copyright.rst | 91 | ||||
-rw-r--r-- | docs/history.rst | 37 | ||||
-rw-r--r-- | docs/index.rst | 25 | ||||
-rw-r--r-- | docs/install.rst | 64 | ||||
-rw-r--r-- | docs/lib/passlib.context-interface.rst | 9 | ||||
-rw-r--r-- | docs/lib/passlib.context-options.rst | 266 | ||||
-rw-r--r-- | docs/lib/passlib.context.rst | 26 | ||||
-rw-r--r-- | passlib/context.py | 61 | ||||
-rw-r--r-- | passlib/registry.py | 7 | ||||
-rw-r--r-- | setup.cfg | 2 | ||||
-rw-r--r-- | setup.py | 8 |
14 files changed, 456 insertions, 237 deletions
@@ -0,0 +1,69 @@ +License for PassLib +=================== +Copyright (c) 2008-2011 by Assurance Technologies, LLC. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Licenses for incorporated software +================================== +PassLib contains some code derived from the following sources: + +jBcrypt +------- +Copyright (c) 2006 Damien Miller <djm@mindrot.org> + +Permission to use, copy, modify, and distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +MD5-Crypt +--------- +"THE BEER-WARE LICENSE" (Revision 42): +<phk@login.dknet.dk> wrote this file. As long as you retain this notice you +can do whatever you want with this stuff. If we meet some day, and you think +this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp + +UnixCrypt.java +-------------- +UnixCrypt.java 0.9 96/11/25 +Copyright (c) 1996 Aki Yoshida. All rights reserved. +Permission to use, copy, modify and distribute this software +for non-commercial or commercial purposes and without fee is +hereby granted provided that this copyright notice appears in +all copies. + +modified April 2001 +by Iris Van den Broeke, Daniel Deville + +modified Aug 2005 +by Greg Wilkins (gregw) @@ -1,13 +1,28 @@ +.. -*- restructuredtext -*- + ========================== The PassLib Python Library ========================== +PassLib is a password hash library, which provides cross-platform +implementations of over 20 password hashing algorithms; as well as a framework for managing +and migrating existing password hashes. It's designed to be useful +for any task from quickly verifying a hash found in /etc/shadow, +to providing full-strength password hashing for multi-user application. + +The latest documentation can be found online `XXX`. + +Requirements +----------- +* Python 2.5 - 2.7 +* PyBCrypt (optional; if bcrypt support is needed) +* M2Crypto (optional) + Installation ------------ -* For detailed installation instructions, see "docs/install.rst" +To install from source using ``setup.py``:: + + python setup.py build + sudo python setup.py install -Copyright & License -------------------- -* (c) 2008-2011 - Assurance Technologies LLC -* released under BSD license -* For more license & copyright information, see "docs/copyright.rst" +For more detailed installation & testing instructions, see "docs/install.rst", diff --git a/docs/contents.rst b/docs/contents.rst index 4912478..9331a9d 100644 --- a/docs/contents.rst +++ b/docs/contents.rst @@ -9,7 +9,6 @@ Table Of Contents overview lib/passlib.context - lib/passlib.context-interface lib/passlib.context-options lib/passlib.apps lib/passlib.apache diff --git a/docs/copyright.rst b/docs/copyright.rst index fbb22b4..23c160f 100644 --- a/docs/copyright.rst +++ b/docs/copyright.rst @@ -2,48 +2,45 @@ Copyrights & Licenses ===================== -Copyright -========= -The PassLib library is (c) 2008-2011 `Assurance Technologies, LLC <http://www.assurancetechnologies.com>`_, -excepting any code noted below as taken from :ref:`third party sources <third-party-software>`. -Such portions are copyright their respective owners. - -License -======= -This library is released under the BSD license; we hope you find it useful. - -:: - - The PassLib Python Library - - Copyright (c) 2008-2011 Assurance Technologies, LLC - - Permission to use, copy, modify, and distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - - THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -.. _third-party-software: - -Third Party Software -==================== -PassLib contains some code taken from various third-party sources, which have their -own licenses (all of which, it should be noted, are BSD-compatible). -The following is a list of these sources, their owners, licenses, and the parts -of PassLib derived from them. +License for PassLib +=================== +PassLib is available under the BSD license, and is (c) `Assurance Technologies <http://www.assurancetechnologies.com>`_:: + + Copyright (c) 2008-2011 by Assurance Technologies, LLC. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Licenses for incorporated software +================================== +PassLib contains some code derived from the following sources: jBcrypt ------- `jBCrypt <http://www.mindrot.org/projects/jBCrypt/>`_ is a pure-java implementation of OpenBSD's BCrypt algorithm, written by Damien Miller, -and released under a BSD license. +and released under a BSD-like license. :mod:`passlib.utils._slow_bcrypt` is a python translation of this code, which is used as a fallback backend for :class:`passlib.hash.bcrypt` @@ -68,10 +65,9 @@ This is the license and copyright for jBCrypt:: MD5-Crypt --------- -The fallback pure-python implementation contained in :class:`passlib.hash.md5_crypt` -was derived from the -`FreeBSD md5-crypt <http://www.freebsd.org/cgi/cvsweb.cgi/~checkout~/src/lib/libcrypt/crypt.c?rev=1.2>`_, -implementation which was released under the following license:: +The pure-python fallback used by :class:`passlib.hash.md5_crypt` was derived from the original +`FreeBSD md5-crypt implementation <http://www.freebsd.org/cgi/cvsweb.cgi/~checkout~/src/lib/libcrypt/crypt.c?rev=1.2>`_, +which is available under the following license:: "THE BEER-WARE LICENSE" (Revision 42): <phk@login.dknet.dk> wrote this file. As long as you retain this notice you @@ -80,16 +76,11 @@ implementation which was released under the following license:: UnixCrypt.java -------------- -`UnixCrypt.java <http://www.dynamic.net.au/christos/crypt/UnixCrypt2.txt>`_ -is a pure-java implementation of the historic unix-crypt password hash algorithm. +The DES utility functions in :mod:`passlib.utils.des` are derived +from `UnixCrypt.java <http://www.dynamic.net.au/christos/crypt/UnixCrypt2.txt>`_, +a pure-java implementation of the historic unix-crypt password hash algorithm. Originally written by Aki Yoshida, and modified by others, -it was released under a BSD-like license. - -The DES utility functions in :mod:`passlib.utils.des` are a descendant of -this code, after being translated into python. (These are used for des-crypt, -ext-des-crypt, and nthash support). - -This is the license and copyright for UnixCrypt.java:: +it is available under a BSD-like license:: UnixCrypt.java 0.9 96/11/25 Copyright (c) 1996 Aki Yoshida. All rights reserved. diff --git a/docs/history.rst b/docs/history.rst index 520666e..6cfc820 100644 --- a/docs/history.rst +++ b/docs/history.rst @@ -2,25 +2,36 @@ Release History =============== -2011-01-10 -- version 1.0 +**1.3** (To Be Released) + * first public release - * bugfixes - * more documentation + * documentation completed + * 99% unittest coverage + * some refactoring and lots of bugfixes + * added support for a number of addtional password schemes: + bigcrypt, crypt16, sun md5 crypt, nthash, lmhash, oracle10 & 11, + phpass, sha1, generic hex digests, ldap digests. + +**1.2** (2011-01-06) + + * many bugfixes + * global registry added + * transitional release for applications using BPS library. + * first truly functional release since splitting from BPS library (see below). -2011-01-05 -- version 0.8 - * various code cleanups preparing for public release - * bsdi-crypt, apr-md5-crypt, and other lesser known schemes added. - * documentation added +.. note:: -2009-03-10 -- version 0.7 - * postgres and mysql hash schemes added. + For all previous versions, PassLib did not exist independantly, + but as a subpackage of *BPS*, an private & unreleased toolkit library. + +**1.0** (2009-12-11) -2008-10-02 -- version 0.6 * CryptContext & CryptHandler framework + * added support for: des-crypt, bcrypt (via pybcrypt), postgres, mysql * added unit tests - * added Unix-Crypt support and pure-python fallback -2008-05-10 -- version 0.5 +**0.5** (2008-05-10) + * initial production version * consolidated from code scattered across multiple applications - * MD5-Crypt, SHA-256-Crypt, SHA-512-Crypt support + * MD5-Crypt, SHA256-Crypt, SHA512-Crypt support diff --git a/docs/index.rst b/docs/index.rst index 849df09..46cfc34 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -5,7 +5,7 @@ PassLib |release| documentation Introduction ============ PassLib is a library for encrypting, verifying, and managing password hashes. -It supports over 20 current and historical password hash schemes. +It supports over 20 different password hash schemes. It can be used for a variety of purposes: * cross-platform replacement for stdlib's :func:`!crypt`. @@ -21,8 +21,25 @@ It can be used for a variety of purposes: Quick Links =========== -* See the :doc:`Library Overview <overview>` for more details about passlib. +.. raw:: html -* See the :doc:`Installation Instructions <install>` to get PassLib installed on your system. + <table class="contentstable" align="center"> + <tr> + <td width="50%" valign="top"> + <p class="biglink"> + <a class="biglink" href="overview.html">Library Overview</a><br> + <span class="linkdescr">describes how PassLib is laid out</span> + </p> -* See the :mod:`passlib.hash <passlib.hash>` module for a complete list of supported hash algorithms. + <p class="biglink"> + <a class="biglink" href="install.html">Installation</a><br> + <span class="linkdescr">requirements and installation instructions</span> + </p> + + <p class="biglink"> + <a class="biglink" href="lib/passlib.hash.html"><i>passlib.hash</i> module</a><br> + <span class="linkdescr">complete list of supported password hash algorithms</span> + </p> + </td> + </tr> + </table> diff --git a/docs/install.rst b/docs/install.rst index 64d8f30..8837e6f 100644 --- a/docs/install.rst +++ b/docs/install.rst @@ -4,56 +4,58 @@ Installation Requirements ============ -PassLib currently has no external depedancies besides Python itself: +* Python 2.5 - 2.7 is required. - * Python 2.5 or better is required. + .. note:: - * PassLib has not been tested with Python 2.4 or earlier, - and no guarantees are made about whether PassLib will work with them. + PassLib has not been tested with Python 2.4 or earlier, + and no guarantees are made about whether PassLib will work with those versions. - * Python 3.x is **not** yet supported, work is ongoing. + .. note:: -The following libraries are not required, but will be used if found: + Python 3.x is **not** yet supported, work is ongoing. - stdlib's :mod:`!crypt` module +* `py-bcrypt <http://www.mindrot.org/projects/py-bcrypt/>`_ (optional) - :func:`!crypt()` will be used if present, and if the host - OS supports the specific scheme in question. OS support is autodetected - for the following schemes: des-crypt, md5-crypt, bcrypt, sha256-crypt, - and sha512-crypt. + If installed, pybcrypt will be used to support the BCrypt hash algorithm. + This is required if you want to handle BCrypt hashes, + and stdlib :mod:`!crypt` does not support BCrypt + (which is pretty much all non-BSD systems). - `py-bcrypt <http://www.mindrot.org/projects/py-bcrypt/>`_ +* `M2Crypto <http://chandlerproject.org/bin/view/Projects/MeTooCrypto>`_ (optional) - If installed, pybcrypt will be used to support the BCrypt hash algorithm. - This is required if you want to handle BCrypt hashes, - and stdlib :mod:`!crypt` does not support BCrypt - (which is pretty much all non-BSD systems). + If installed, M2Crypto will be used to accelerate some + internal support functions, but it is not required. - `M2Crypto <http://chandlerproject.org/bin/view/Projects/MeTooCrypto>`_ - - If installed, M2Crypto will be used to accelerate some - internal support functions, but it is not required. - -PassLib should be useable on all operating systems. +PassLib is pure-python, and should be useable on all platforms. Installing ========== -PassLib can be installed with easy_install / pip, linked/copied into sys.path directly -from it's source directory, or installed using :samp:`{$SOURCE}/setup.py install`, -where :samp:`{$SOURCE}` is the path to the PassLib source directory. -PassLib is pure python, there is nothing to compile or configure. +To install from source directory using ``setup.py`` (requires Setuptools or Distribute):: + + python setup.py build + sudo python setup.py install + +To install using easy_install:: + + easy_install passlib + +To install using pip:: + + pip install passlib Testing ======= PassLib contains a comprehensive set of unittests providing nearly complete coverage. All unit tests are contained within the :mod:`passlib.tests` package, 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:: - # to run the basic tests from the source directory... +Once PassLib and Nose have been installed, the tests may be run from the source directory:: + + # to run the platform-relevant tests... nosetests -v passlib/tests - # to run ALL tests from the source directory... + # to run all tests... PASSLIB_TESTS="all" nosetests -v passlib/tests Documentation @@ -68,7 +70,7 @@ you will need to: * install `astdoc <http://www.assurancetechnologies.com/software/astdoc>`_ (a bundle of custom sphinx themes & extensions used by Assurance Technologies). * download the PassLib source -* run :samp:`python {$SOURCE}/docs/make.py clean html` (where :samp:`{$SOURCE}` is the path to the PassLib source directory). +* run :samp:`python docs/make.py clean html`. -Once Sphinx completes it's run, point a web browser to the file at :samp:`{$SOURCE}/docs/_build/html/index.html` +Once Sphinx completes it's run, point a web browser to the file at :samp:`docs/_build/html/index.html` to access the PassLib documentation in html format. diff --git a/docs/lib/passlib.context-interface.rst b/docs/lib/passlib.context-interface.rst deleted file mode 100644 index e7689b2..0000000 --- a/docs/lib/passlib.context-interface.rst +++ /dev/null @@ -1,9 +0,0 @@ -=============================================== -:mod:`passlib.context` - CryptContext interface -=============================================== - -.. currentmodule:: passlib.context - -.. autoclass:: CryptContext(schemes=None, policy=<default policy>, \*\*kwds) - -.. autoclass:: CryptPolicy(\*\*kwds) diff --git a/docs/lib/passlib.context-options.rst b/docs/lib/passlib.context-options.rst index 24ddc72..1cf6000 100644 --- a/docs/lib/passlib.context-options.rst +++ b/docs/lib/passlib.context-options.rst @@ -1,124 +1,185 @@ +.. _cryptcontext-options: + ============================================= :mod:`passlib.context` - CryptContext options ============================================= .. currentmodule:: passlib.context -Context Configuration Policy -============================ -.. warning:: +The :class:`CryptContext` accepts a number of keyword options. +These are divides into the "context options", which affect +the context instance directly, and the "hash options", +which affect the context treats a particular type of hash: - This section's writing and design are still very much in flux. +Context Options +=============== +The following keyword options are accepted by both the :class:`CryptContext` +and :class:`CryptPolicy` constructors, and directly affect the behavior +of the :class:`!CryptContext` instance itself: -Each CryptContext instance is extremely configuration through a wide range -of options. All of these options can be specified via the CryptContext -constructor, or by loading the configuration of a section of an ini file -(allowing an application's password policy to be specified externally). +``schemes`` + List of handler names and/or instances which the CryptContext should recognize. + This is usually required. -All configuration options are stored in a CryptPolicy object, -which can be created in the following ways: + For use in INI files, this may also be specified as a single comma-separated string + of handler names. -* passing in options as keywords to it's constructor -* loading options from a section of a :mod:`ConfigParser` ini file. -* compositing together existing CryptPolicy objects (this allows for default policies, application policies, and run-time policies) + Any names specified must be registered globally with PassLib. -Hash Configuration Options -========================== -Options for configuring a specific hash take the form of the name of -``{name}.{option}`` (eg ``sha512_crypt.default_rounds``); where ``{name}`` is usually the name of a password hash, -and ``{option}`` is one of the options specified below. -There are a few reserved hash names: -Any options of the form ``all.{option}`` will be inherited by all hashes -if they do not have a ``{hash}.{option}`` value overriding the default. -Any options of the form ``context.{option}`` will be treated as options for the context object itself, -and not for a specified hash. Any options of the form ``{option}`` are taken to implicitly -belong to the context, and are treated as if they started with the prefix ``context.``. -The remaining options - + Example: ``schemes=["sha256_crypt", "md5_crypt", "des_crypt"]``. -``context.schemes`` - comma separated list of the schemes this context should recognize, specified by name. - when a context is identifying hashes, it will check each scheme in this list - in order. if this value is being specified programmatically, - it may also be a python list containing a mixture of names - and password hash handler objects. +``deprecated`` -``context.deprecated`` - comma separated list of the schemes which this context should recognize, - generated hashes only if explicitly requested, and for which ``context.hash_needs_update()`` should return ``False``. - if not specified, none are considered deprecated. - this must be a subset of the names listed in context.schemes + List of handler names which should be considered deprecated by the CryptContext. + This should be a subset of the names of the handlers listed in schemes. + This is optional, if not specified, no handlers will be considered deprecated. -``context.default`` - the default scheme context should use for generating new hashes. - if not specified, the first entry in ``context.schemes`` is used. + For use in INI files, this may also be specified as a single comma-separated string + of handler names. -``context.min_verify_time`` - if specified, all ``context.verify()`` calls will take at least this many seconds. - if set to an amount larger than the time used by the strongest hash in the system, - this prevents an attacker from guessing the strength of particular hashes remotely. - (specified in fractional seconds). + This is primarily used by :meth:`CryptContext.hash_needs_update` and :meth:`CryptPolicy.handler_is_deprecated`. + If the application does not use these methods, this option can be ignored. -``{hash}.min_rounds``, ``{hash}.max_rounds`` + Example: ``deprecated=["des_crypt"]``. - place limits on the number of rounds allowed for a specific hash. +``default`` - * these are configurable per-context limits, hard limits set by algorithm are always applied - * if min > max, max will be increased to equal min. - * ``context.genconfig()`` or ``config.encrypt()`` - requests outside of these bounds will be clipped. - * ``context.hash_needs_update()`` - existing hashes w/ rounds outside of range are not compliant - * for hashes which do not have a rounds parameter, these values are ignored. + Specifies the name of the default handler to use when encrypting a new password. + If no default is specified, the first handler listed in ``schemes`` will be used. -``{hash}.default_rounds`` + Example: ``default="sha256_crypt"``. - sets the default number of rounds to use when generating new hashes. +``min_verify_time`` - * if this value is out side of per-policy min/max, it will be clipped just like user provided value. - * ``context.genconfig()`` or ``config.encrypt()`` - if rounds are not provided explicitly, this value will be used. - * for hashes which do not have a rounds parameter, this value is ignored. - * if not specified, max_rounds is used if available, then min_rounds, then the algorithm default. + If specified, all :meth:`CryptContext.verify` calls will take at least this many seconds. + If set to an amount larger than the time used by the strongest hash in the system, + this prevents an attacker from guessing the strength of particular hashes through timing measurements. -``{hash}.vary_rounds`` + Specified in integer or fractional seconds. - [only applies if ``{hash}.default_rounds`` is specified and > 0] + Example: ``min_verify_time=0.1``. - if specified, every time a new hash is created using {hash}/default_rounds for it's rounds value, - the actual value used is generated at random, using default_rounds as a hint. +.. note:: - * integer value - a value will be chosen using the formula ``randint(default_rounds-vary_rounds, default_rounds+vary_rounds)``. - * integer value between 0 and 100 with ``%`` suffix - same as above, with integer value equal to ``vary_rounds*default_rounds/100``. - * note that if algorithms indicate they use a logarthmic rounds parameter, the percent syntax equation uses ``log(vary_rounds*(2**default_rounds)/100,2)``, - to permit a default value to be applicable to all schemes. XXX: this might be a bad / overly complex idea. + For symmetry with the format of the hash option keywords (below), + all of the above context option keywords may also be specified + using the format :samp:`context__{option}` (note double underscores), + or :samp:`context.{option}` within INI files. -``{hash}.{setting}`` - any keys which match the name of a configuration parameter accepted by the hash - will be used directly as default values. +.. note:: - * for security purposes, ``salt`` is *forbidden* from being used in this way. - * if ``rounds`` is specified directly, it will override the entire min/max/default_rounds framework. + To override context options for a particular :ref:`user category <user-categories>`, + use the format :samp:`{category}__context__{option}`, + or :samp:`{category}.context.{option}` within an INI file. -``{hash}.{other}`` - any keys which do not fall under the above categories will be ignored +Hash Options +============ +The following keyword options are accepted by both the :class:`CryptContext` +and :class:`CryptPolicy` constructors, and affect how a :class:`!CryptContext` instance +treats hashes belonging to a particular hash scheme, as identified by the hash's handler name. -User Categories -=============== -One frequent need is for certain categories of users (eg the root account) -to have more strigent password requirements than default users. -PassLib allows this by recognizing options of the format ``{category}.{name}.{option}``, -and allowing many of it's entry methods to accept an optional ``category`` parameter. +All hash option keywords should be specified using the format :samp:`{hash}__{option}` +(note double underscores); where :samp:`{hash}` is the name of the hash's handler, +and :samp:`{option}` is the name of the specific options being set. +Within INI files, this may be specified using the alternate format :samp:`{hash}.{option}`. + +:samp:`{hash}__default_rounds` + + Sets the default number of rounds to use when generating new hashes (via :meth:`CryptContext.encrypt`). + + If not set, this will use max rounds hash option (see below), + or fall back to the algorithm-specified default. + For hashes which do not support a rounds parameter, this option is ignored. + +:samp:`{hash}__vary_rounds` + + if specified along with :samp:`{hash}__default_rounds`, + this will cause each new hash created by :meth:`CryptContext.encrypt` + to have a rounds value random chosen from the range :samp:`{default_rounds} +/- {vary_rounds}`. + + this may be specified as an integer value, or as a string containing an integer + with a percent suffix (eg: ``"10%"``). if specified as a percent, + the amount varied will be calculated as a percentage of the :samp:`{default_rounds}` value. + + The default passlib policy sets this to ``"10%"``. + + .. note:: + + If this is specified as a percentage, and the hash algorithm + uses a logarithmic rounds parameter, the amount varied + will be calculated based on the effective number of linear rounds, + not the actual rounds value. + This allows ``vary_rounds`` to be given a default value for all hashes + within a context, and behave sanely for both linear and logarithmic rounds parameters. + +:samp:`{hash}__min_rounds`, :samp:`{hash}__max_rounds` + + Place limits on the number of rounds allowed for a specific hash. + ``min_rounds`` defaults to 0, ``max_rounds`` defaults to unlimited. + + When encrypting new passwords with the specified hash (via :meth:`CryptContext.encrypt`), + the number of rounds will be clipped to these boundaries. + When checking for out-of-date hashes (via :meth:`CryptContext.hash_needs_update`), + it will flag any whose rounds are outside the range specified as needing to be re-encrypted. + For hashes which do not support a rounds parameter, these options are ignored. -When one is specified, any ``{category}.{name}.{option}`` keywords in the configuration -will override any ``{name}.{option}`` keywords. + .. note:: -In order to simplify behavior and implementation, categories cannot override the ``context/schemes`` keyword, -though they may override the other context keys. + These are configurable per-context limits, + they will be clipped by any hard limits set in the hash algorithm itself. -Default Policies -================ -PassLib defines a library-default policy, updated perodically, providing (hopefully) sensible defaults for the various contexts. +:samp:`{hash}__{setting}` + + Any other option values, which match the name of a parameter listed + in the hash algorithm's ``handler.setting_kwds`` attribute, + will be passed directly to that hash whenever :meth:`CryptContext.encrypt` is called. + + For security purposes, ``salt`` is *forbidden* from being used in this way. + + If ``rounds`` is specified directly, it will override the entire min/max/default_rounds framework. + +.. note:: + + Default options which will be applied to all hashes within the context + can be specified using the special hash name ``all``. For example, ``all__vary_rounds="10%"`` + would set the ``vary_rounds`` option to ``"10%"`` for all hashes, unless + it was overridden for a specific hash, such as by specifying ``sha256_crypt__vary_rounds="5%"``. + This feature is generally only useful for the ``vary_rounds`` hash option. + +.. _user-categories: + +User Categories +=============== +CryptContext offers an optional feature of "user categories": + +User categories take the form of a string (eg: ``admin`` or ``guest``), +passed to the CryptContext when one of it's methods is called. +These may be set by an application to indicate the hash belongs +to a user account which should be treated according to a slightly +different set of configuration options from normal user accounts; +this may involve requiring a stronger hash scheme, a larger +number of rounds for that scheme, or just a longer verify time. + +If an application wishes to use this feature, it all that is needed +is to prefix the name of any hash or context options with the name +of the category string it wants to use, and add an additional separator to the keyword: +:samp:`{category}__{hash}__{option}`` or ``{category}__context__{option}``. + +.. note:: + + For implementation & predictability purposes, + the context option ``schemes`` cannot be overridden per-category, + though all other options are allowed. In most cases, + the need to use a different hash for a particular category + can instead be acheived by overridden the ``default`` context option. + +Default Policy +============== +PassLib defines a library-default policy, providing (hopefully) sensible defaults for new contexts. When a new CryptContext is created, a policy is generated from it's constructor arguments, which is then composited over the library-default policy. You may optionally override the default policy used by overriding the ``policy`` keyword -of CryptContext. This keyword accepts a single CryptPolicy object or string (which will be treated as an ini file to load); -it also accepts a list of CryptPolicys and/or strings, which will be composited together along with any constructor options. +of CryptContext. This default policy object may be imported as :data:`passlib.context.default_policy`, +or viewed in the source code under ``$SOURCE/passlib/default.cfg``. Sample Policy File ================== @@ -138,7 +199,30 @@ A sample policy file:: sha512_crypt.min_rounds = 40000 bcrypt.min_rounds = 10 - #create a "root" category, which uses bcrypt by default, and has stronger hashes - root.context.fallback = bcrypt - root.sha512_crypt.min_rounds = 100000 - root.bcrypt.min_rounds = 13 + #create a "admin" category, which uses bcrypt by default, and has stronger hashes + admin.context.fallback = bcrypt + admin.sha512_crypt.min_rounds = 100000 + admin.bcrypt.min_rounds = 13 + +And the equivalent as a set of python keyword options:: + + dict( + #configure what schemes the context supports (note the "context." prefix is implied for these keys) + schemes = ["md5_crypt", "sha512_crypt", "bcrypt" ], + deprecated = ["md5_crypt"], + default = "sha512_crypt", + min_verify_time = 0.1, + + #set some common options for all schemes + all__vary_rounds = "10%", + + #setup some hash-specific defaults + sha512_crypt__min_rounds = 40000, + bcrypt__min_rounds = 10, + + #create a "admin" category, which uses bcrypt by default, and has stronger hashes + admin__context__fallback = bcrypt + admin__sha512_crypt__min_rounds = 100000 + admin__bcrypt__min_rounds = 13 + ) + diff --git a/docs/lib/passlib.context.rst b/docs/lib/passlib.context.rst index b3af128..13cbde8 100644 --- a/docs/lib/passlib.context.rst +++ b/docs/lib/passlib.context.rst @@ -13,7 +13,7 @@ Similarly, over time, applications may need to deprecate password schemes in favor of newer ones, or raise the number of rounds required by existing hashes. -This module provides the :class:`!CryptContext` class, which is designed +This module provides the :class:`CryptContext` class, which is designed to handle (as much as possible) of these tasks for an application. Essentially, a :class:`!CryptContext` instance contains a list of hash handlers that it should recognize, along with information @@ -21,17 +21,9 @@ about which ones are deprecated, which is the default, and what configuration constraints an application has placed on a particular hash. -Since this class contains so many methods and options, -the documentation for this module is broken up into three -sections: - -* Usage examples (below) -* Next, documentation of the complete :doc:`CryptContext interface <passlib.context-interface>`. -* Finally, a comprehensive list of :doc:`CryptContext options <passlib.context-options>`. - Usage ===== -To start off with a simple example:: +To start off with a simple example of how to create and use a CryptContext:: >>> from passlib.context import CryptContext @@ -67,8 +59,8 @@ To start off with a simple example:: >>> myctx.identify(hash1, resolve=True) <class 'passlib.handlers.md5_crypt.md5_crypt'> -All of the configuration options for a :class:`!CryptContext` instance -are stored in a :class:`CryptPolicy` instance accessible through +If introspection of a :class:`!CryptContext` instance +is needed, all configuration options are stored in a :class:`CryptPolicy` instance accessible through their ``policy`` attribute:: >>> from passlib.context import CryptContext @@ -81,3 +73,13 @@ their ``policy`` attribute:: >>> #get the default handler class : >>> myctx.policy.get_handler() <class 'passlib.handlers.md5_crypt.md5_crypt'> + +Interface +========= +This details the constructors and methods provided by :class:`!CryptContext` +and :class:`!CryptPolicy`. A list of all the keyword options accepted by these classes is listed separately +in :doc:`passlib.context-options`. + +.. autoclass:: CryptContext(schemes=None, policy=<default policy>, \*\*kwds) + +.. autoclass:: CryptPolicy(\*\*kwds) diff --git a/passlib/context.py b/passlib/context.py index 6c19fb0..37987be 100644 --- a/passlib/context.py +++ b/passlib/context.py @@ -91,17 +91,16 @@ def parse_policy_items(source): class CryptPolicy(object): """stores configuration options for a CryptContext object. - .. note:: - Instances of CryptPolicy should be treated as immutable. - - Policy objects can be constructed by the following methods: + .. method:: __init__(**options) - .. method:: (constructor) + The CryptPolicy class constructor accepts a dictionary + of keywords, which can include all the options + listed in the :ref:`cryptcontext-options`. - You can specify options directly to the constructor. - This accepts dot-seperated keywords such as found in the config file format, - but for programmatic convience, it also accepts keys with ``.`` replaced with ``__``, - allowing options to be specified programmatically in python. + Constructors + ============ + In addition to passing in keywords directly, + CryptPolicy objects can be constructed by the following methods: .. automethod:: from_path .. automethod:: from_string @@ -109,6 +108,8 @@ class CryptPolicy(object): .. automethod:: from_sources .. automethod:: replace + Introspection + ============= .. automethod:: has_schemes .. automethod:: schemes .. automethod:: iter_handlers @@ -117,10 +118,15 @@ class CryptPolicy(object): .. automethod:: handler_is_deprecated .. automethod:: get_min_verify_time + Exporting + ========= .. automethod:: iter_config .. automethod:: to_dict .. automethod:: to_file .. automethod:: to_string + + .. note:: + Instances of CryptPolicy should be treated as immutable. """ #========================================================= @@ -599,22 +605,47 @@ default_policy = CryptPolicy.from_string(resource_string("passlib", "default.cfg class CryptContext(object): """Helper for encrypting passwords using different algorithms. - :param policy: optionally override the default policy CryptContext starts with before options are added. - :param kwds: ``schemes`` and all other keywords are passed to the CryptPolicy constructor. + :param policy: + optionally override the default policy CryptContext starts with before options are added. + + If not specified, the new instance will inherit a set of default options (such as rounds, etc) + from the passlib default policy (importable as :data:`passlib.context.default_policy`). + + If explicitly set to ``None``, the new instance will not inherit from the default policy, + and will contain only the configuration specified by any additional keywords. + + Alternately, a custom CryptPolicy instance can be passed in, + which allows loading the policy from a configuration file, + combining multiple policies together, and other features. + :param kwds: + + ``schemes`` and all other keywords are passed to the CryptPolicy constructor, + or to :meth:`CryptPolicy.replace`, if a policy has also been specified. + + .. automethod:: replace + + Configuration + ============= .. attribute:: policy This exposes the :class:`CryptPolicy` instance which contains the configuration used by this context object. - .. automethod:: hash_needs_update + This attribute may be written to (replacing it with another CryptPolicy instance), + in order to reconfigure a CryptContext while an application is running. + However, this should only be done for context instances created by the application, + and NOT for context instances provided by PassLib. + + Main Interface + ============== .. automethod:: identify .. automethod:: encrypt .. automethod:: verify - .. automethod:: genconfig - .. automethod:: genhash - .. automethod:: replace + Migration Helpers + ================= + .. automethod:: hash_needs_update """ #=================================================================== #instance attrs diff --git a/passlib/registry.py b/passlib/registry.py index cb380bd..6544f0c 100644 --- a/passlib/registry.py +++ b/passlib/registry.py @@ -108,6 +108,9 @@ _handler_locations = { #: master regexp for detecting valid handler names _name_re = re.compile("^[a-z][_a-z0-9]{2,}$") +#: names which aren't allowed for various reasons (mainly keyword conflicts in CryptContext) +_forbidden_names = frozenset(["policy", "context", "all", "default", "none"]) + #========================================================== #registry frontend functions #========================================================== @@ -175,6 +178,10 @@ def register_crypt_handler(handler, force=False, name=None): raise ValueError, "name must be lower-case: %r" % (name,) if not _name_re.match(name): raise ValueError, "invalid characters in name (must be 3+ characters, begin with a-z, and contain only underscore, a-z, 0-9): %r" % (name,) + if '__' in name: + raise ValueError, "name may not contain double-underscores: %r" % (name,) + if name in _forbidden_names: + raise ValueError, "that name is not allowed: %r" % (name,) #check for existing handler other = _handlers.get(name) @@ -1,3 +1,3 @@ [egg_info] -tag_build = a1 +tag_build = b1 @@ -29,11 +29,11 @@ setup( description = "password hash library", long_description = """\ -PassLib provides cross-platform implementations of most of the major -password hashing algorithms; as well as a framework for managing +PassLib is a password hash library, which provides cross-platform +implementations of over 20 password hashing algorithms; as well as a framework for managing and migrating existing password hashes. It's designed to be useful -for anything from quickly verify a hash found in /etc/shadow, -to integrating full-strength password hashing for multi-user application. +for any task from quickly verifying a hash found in /etc/shadow, +to providing full-strength password hashing for multi-user application. """, keywords = "password secret hash security crypt md5-crypt sha256-crypt sha512-crypt bcrypt htpasswd htdigest", |