diff options
| author | Eli Collins <elic@assurancetechnologies.com> | 2011-01-31 23:34:37 +0000 |
|---|---|---|
| committer | Eli Collins <elic@assurancetechnologies.com> | 2011-01-31 23:34:37 +0000 |
| commit | 0eafd59fcee89cf4769add43c057a11e62c49a3a (patch) | |
| tree | 19b476c6317f76ce39140900d1d671440aef34ae | |
| parent | 8903ccd8ecf8b26cd5e80466eecfdd8df140482b (diff) | |
| download | passlib-0eafd59fcee89cf4769add43c057a11e62c49a3a.tar.gz | |
updated docs
41 files changed, 547 insertions, 266 deletions
diff --git a/docs/conf.py b/docs/conf.py index 6bf186e..206a63a 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -38,7 +38,7 @@ import astdoc extensions = [ 'sphinx.ext.autodoc', 'sphinx.ext.todo', - + 'astdoc.ext.index_styles', #adds extra ids & classes to index html, for additional styling 'astdoc.ext.relbar_toc', #inserts toc into right hand nav bar (ala old style python docs) 'astdoc.ext.nested_sections', #handles ReST markup within function/class docstrings @@ -59,7 +59,7 @@ index_doc = 'index' # General information about the project. project = u'PassLib' -copyright = u'2008-2010, Assurance Technologies, LLC' +copyright = u'2008-2011, Assurance Technologies, LLC' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the @@ -109,7 +109,7 @@ pygments_style = 'sphinx' modindex_common_prefix = [ "passlib." ] # -- Options for all output --------------------------------------------------- -todo_include_todos = True +todo_include_todos = "todos" in os.environ.get("PASSLIB_DOCS","") keep_warnings = True # -- Options for HTML output --------------------------------------------------- diff --git a/docs/contents.rst b/docs/contents.rst index dd4ceb4..5398288 100644 --- a/docs/contents.rst +++ b/docs/contents.rst @@ -8,10 +8,11 @@ Table Of Contents install overview - lib/passlib - lib/passlib - lib/passlib.gen - lib/passlib.util + lib/passlib.hash + lib/passlib.unix + lib/passlib.utils + + crypt_handler_api history copyright diff --git a/docs/copyright.rst b/docs/copyright.rst index fc2c6ac..354574c 100644 --- a/docs/copyright.rst +++ b/docs/copyright.rst @@ -4,7 +4,7 @@ Copyrights & Licenses Copyright ========= -The PassLib library is (c) 2008-2010 `Assurance Technologies, LLC <http://www.assurancetechnologies.com>`_, +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. @@ -16,7 +16,7 @@ This library is released under the BSD license; we hope you find it useful. The PassLib Python Library - Copyright (c) 2008-2010 Assurance Technologies, LLC + 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 @@ -39,21 +39,14 @@ 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. -GPW ---- -The class :class:`passlib.gen.GpwGenerator` -is a python implementation of Tom Van Vleck's phonetic -password generation algorithm `GPW <http://www.multicians.org/thvv/gpw.html>`_. -It's released under informally worded BSD-compatible terms. - 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. -:mod:`passlib._bcrypt` is a python translation of this code, -which is used as a fallback backend for :class:`passlib.BCrypt` +:mod:`passlib.utils._slow_bcrypt` is a python translation of this code, +which is used as a fallback backend for :mod:`passlib.hash.bCrypt` when the external python library `py-bcrypt <http://www.mindrot.org/projects/py-bcrypt/>`_ is not installed. @@ -75,10 +68,10 @@ This is the license and copyright for jBCrypt:: MD5-Crypt --------- -The class :class:`passlib.Md5Crypt` is a pure-python -implementation of the md5-crypt password hashing algorithm. -It's derived from the FreeBSD md5-crypt implementation `<http://www.freebsd.org/cgi/cvsweb.cgi/~checkout~/src/lib/libcrypt/crypt.c?rev=1.2>`_, -which was released under the following license:: +The fallback pure-python implementation contained in :mod:`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 BEER-WARE LICENSE" (Revision 42): <phk@login.dknet.dk> wrote this file. As long as you retain this notice you @@ -92,9 +85,9 @@ is 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. -:mod:`passlib._unix_crypt` is a python translation of this code, -which is used as a fallback backend for :class:`passlib.UnixCrypt` -for platforms where stdlib's :mod:`crypt` is not available. +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:: diff --git a/docs/crypt_handler_api.rst b/docs/crypt_handler_api.rst index 500b0eb..e6192f5 100644 --- a/docs/crypt_handler_api.rst +++ b/docs/crypt_handler_api.rst @@ -1,3 +1,5 @@ +.. _crypt-handler-api: + ====================== api for crypt handlers ====================== @@ -163,4 +165,3 @@ the following attributes are usually exposed: The maximum number of rounds the scheme allows. Specifying values above this will generally result in a warning, and ``max_rounds`` will be used instead. - diff --git a/docs/history.rst b/docs/history.rst index 9a6e9bd..615574b 100644 --- a/docs/history.rst +++ b/docs/history.rst @@ -2,15 +2,18 @@ Release History =============== -??? -- version 1.1 +2011-01-10 -- version 1.0 * first public release - * documentation added + * bugfixes + * more documentation -2011-01-05 -- version 1.0 +2011-01-05 -- version 0.8 * various code cleanups preparing for public release + * ext-des-crypt, apr-md5-crypt, and other lesser known schemes added. + * documentation added 2009-03-10 -- version 0.7 - * added password generation helpers + * postgres and mysql hash schemes added. 2008-10-02 -- version 0.6 * CryptContext & CryptHandler framework diff --git a/docs/index.rst b/docs/index.rst index 821ee65..8ffcbc4 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -4,9 +4,9 @@ PassLib |release| documentation Introduction ============ -Welcome to the documentation for PassLib. - -PassLib is a +Passlib is a collection of routines for managing password hashes +as found in unix /etc/shadow files, as returned by stdlib `crypt()`, +as stored in mysql and postgres, and various other contexts. A quick sample of some of the more frequently used modules: diff --git a/docs/install.rst b/docs/install.rst index 1b29858..f6ba0c6 100644 --- a/docs/install.rst +++ b/docs/install.rst @@ -4,22 +4,26 @@ Installation Requirements ============ -PassLib tries to use pure-python implementations of things whereever possible, -and have as few dependancies as possible. The current set of requirements is: +PassLib currently has no external depedancies besides Python itself: * Python 2.5 or better is required. * PassLib has not been tested with Python 2.4 or earlier, and no guarantees are made about whether PassLib will work with them. - * Python 3.x has **not** been assessed for compatibility. - It probably won't work just yet. + * Python 3.x is **not** yet supported, work is ongoing (particularly, unicode issues). -The following libraries will be used if present, but they are not required: +The following libraries are not required, but will be used if found: * If installed, `py-bcrypt <http://www.mindrot.org/projects/py-bcrypt/>`_ will be used instead of PassLib's slower pure-python bcrypt implementation. - (see :class:`passlib.BCrypt`). + (see :mod:`passlib.hash.bcrypt`). + *This is strongly recommended, as the builtin implementation is VERY slow*. + + * stdlib ``crypt.crypt()`` will be used if present, and if the underlying + OS supports the specific scheme in question. OS support is autodetected + from the following schemes: des-crypt, md5-crypt, bcrypt, sha256-crypt, + and sha512-crypt. Installing ========== @@ -31,20 +35,20 @@ PassLib is pure python, there is nothing to compile or configure. Testing ======= PassLib contains a number of unittests (sadly, coverage is not yet complete). -all of which 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. +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, you may run the following commands:: #to run the basic passlib test suite: - nosetests passlib/tests + nosetests -v passlib.tests - #to run the extended passlib test suite, including some longer running tests: + #to test all passlib backends, including inactive ones: export PASSLIB_TESTS=all - nosetests passlib/tests + nosetests passlib.tests Documentation ============= -The latest copy of this documentation should always be available +The latest copy of this documentation should always be available at the `PassLib homepage <http://www.assurancetechnologies.com/software/passlib>`_. If you wish to generate your own copy of the documentation, diff --git a/docs/lib/passlib.hash/algorithms.rst b/docs/lib/_scratch1.rst index 4c00293..4c00293 100644 --- a/docs/lib/passlib.hash/algorithms.rst +++ b/docs/lib/_scratch1.rst diff --git a/docs/lib/passlib.hash/implementation.rst b/docs/lib/_scratch2.rst index d80ac56..d80ac56 100644 --- a/docs/lib/passlib.hash/implementation.rst +++ b/docs/lib/_scratch2.rst diff --git a/docs/lib/passlib.hash/contexts.rst b/docs/lib/passlib.base.rst index aa463f9..aa463f9 100644 --- a/docs/lib/passlib.hash/contexts.rst +++ b/docs/lib/passlib.base.rst diff --git a/docs/lib/passlib.gen.rst b/docs/lib/passlib.gen.rst deleted file mode 100644 index b8778e9..0000000 --- a/docs/lib/passlib.gen.rst +++ /dev/null @@ -1,14 +0,0 @@ -================================================ -:mod:`passlib.gen` -- Password Generation -================================================ - -.. module:: passlib.gen - :synopsis: password generation algorithms - -The following single function allows -easy password generation in a number of styles: - -.. autofunction:: generate_secret - -.. todo:: - document internal classes diff --git a/docs/lib/passlib.hash.apr_md5_crypt.rst b/docs/lib/passlib.hash.apr_md5_crypt.rst new file mode 100644 index 0000000..272c08d --- /dev/null +++ b/docs/lib/passlib.hash.apr_md5_crypt.rst @@ -0,0 +1,24 @@ +============================================================= +:mod:`passlib.hash.apr_md5_crypt` - Apache MD5-Crypt Variant +============================================================= + +.. module:: passlib.hash.apr_md5_crypt + :synopsis: Apache MD5 Crypt + +Stats: 96 bit checksum, 48 bit salt, :ref:`modular-crypt-format` compatible. + +This format is a variation of :mod:`~passlib.hash.md5_crypt`, +primarily used by the Apache webserver in ``htpasswd`` files. + +This format is identical to md5-crypt, except for two things: +it uses ``$apr1$`` as a prefix where md5-crypt uses ``$1$``, +and inserts ``$apr1$`` where md5-crypt inserts ``$1$`` into +it's internal hash calculation. Thus, this algorithm is just +as strong as md5-crypt, but the formats (and their contained checksums) +are in no way compatible with eachother. + +Implementation +============== +PassLib contains a builtin pure-python implementation of apr-md5-crypt, +based of the specification at `http://httpd.apache.org/docs/2.2/misc/password_encryptions.html`, +but code shared with :mod:`~passlib.hash.md5_crypt`. diff --git a/docs/lib/passlib.hash.bcrypt.rst b/docs/lib/passlib.hash.bcrypt.rst new file mode 100644 index 0000000..0b22e07 --- /dev/null +++ b/docs/lib/passlib.hash.bcrypt.rst @@ -0,0 +1,10 @@ +================================================================== +:mod:`passlib.hash.bcrypt` - BCrypt +================================================================== + +.. module:: passlib.hash.bcrypt + :synopsis: implementation of blowfish-based BCrypt scheme + +.. todo:: + + write documentation diff --git a/docs/lib/passlib.hash.des_crypt.rst b/docs/lib/passlib.hash.des_crypt.rst new file mode 100644 index 0000000..6b7e195 --- /dev/null +++ b/docs/lib/passlib.hash.des_crypt.rst @@ -0,0 +1,10 @@ +======================================================== +:mod:`passlib.hash.des_crypt` - Unix (DES) Crypt +======================================================== + +.. module:: passlib.hash.des_crypt + :synopsis: implementation of original unix-crypt / des-crypt scheme + +.. todo:: + + write documentation diff --git a/docs/lib/passlib.hash.ext_des_crypt.rst b/docs/lib/passlib.hash.ext_des_crypt.rst new file mode 100644 index 0000000..35da109 --- /dev/null +++ b/docs/lib/passlib.hash.ext_des_crypt.rst @@ -0,0 +1,10 @@ +================================================================== +:mod:`passlib.hash.ext_des_crypt` - BSDi Extended Unix (DES) Crypt +================================================================== + +.. module:: passlib.hash.ext_des_crypt + :synopsis: implementation of BSDi extended unix/des crypt scheme + +.. todo:: + + write documentation diff --git a/docs/lib/passlib.hash.md5_crypt.rst b/docs/lib/passlib.hash.md5_crypt.rst new file mode 100644 index 0000000..78fe3f3 --- /dev/null +++ b/docs/lib/passlib.hash.md5_crypt.rst @@ -0,0 +1,10 @@ +================================================================== +:mod:`passlib.hash.md5_crypt` - MD5-Crypt +================================================================== + +.. module:: passlib.hash.md5_crypt + :synopsis: implementation of MD5-Crypt scheme + +.. todo:: + + write documentation diff --git a/docs/lib/passlib.hash.myql_323.rst b/docs/lib/passlib.hash.myql_323.rst new file mode 100644 index 0000000..6bb1467 --- /dev/null +++ b/docs/lib/passlib.hash.myql_323.rst @@ -0,0 +1,10 @@ +================================================================== +:mod:`passlib.hash.mysql_323` - MySQL 3.2.3 "OLD_PASSWORD" Scheme +================================================================== + +.. module:: passlib.hash.mysql_323 + :synopsis: MySQL 3.2.3 "OLD_PASSWORD" scheme + +.. todo:: + + write documentation diff --git a/docs/lib/passlib.hash.mysql_41.rst b/docs/lib/passlib.hash.mysql_41.rst new file mode 100644 index 0000000..3665ebf --- /dev/null +++ b/docs/lib/passlib.hash.mysql_41.rst @@ -0,0 +1,10 @@ +================================================================== +:mod:`passlib.hash.mysql_41` - MySQL 4.1 "NEW_PASSWORD" Scheme +================================================================== + +.. module:: passlib.hash.mysql_41 + :synopsis: MySQL 4.1 "NEW_PASSWORD" scheme + +.. todo:: + + write documentation diff --git a/docs/lib/passlib.hash.postgres_md5.rst b/docs/lib/passlib.hash.postgres_md5.rst new file mode 100644 index 0000000..0b75dd5 --- /dev/null +++ b/docs/lib/passlib.hash.postgres_md5.rst @@ -0,0 +1,33 @@ +================================================================== +:mod:`passlib.hash.postgres_md5` - Postgres MD5 password hash +================================================================== + +.. module:: passlib.hash.mysql_md5 + :synopsis: Postgres MD5 password hash + +Stats: 512 bit checksum, username used as salt + +This implements the md5-based hash algorithm used by Postgres to store +passwords in the pg_shadow table. + +This algorithm shouldn't be used for any purpose besides Postgres interaction, +it's a weak unsalted algorithm which could be attacked with a rainbow table +built against common user names. + +.. warning:: + This algorithm is slightly different from most of the others, + in that both encrypt() and verify() require you pass in + the name of the user account via the required 'user' keyword, + since postgres uses this in place of a salt :( + +Usage Example:: + + >>> from passlib.hash import postgres_md5 as pm + >>> pm.encrypt("mypass", user="postgres") + 'md55fba2ea04fd36069d2574ea71c8efe9d' + >>> pm.verify("mypass", 'md55fba2ea04fd36069d2574ea71c8efe9d', user="postgres") + True + +.. todo:: + + find references diff --git a/docs/lib/passlib.hash.rst b/docs/lib/passlib.hash.rst index 621ed0d..313fa2e 100644 --- a/docs/lib/passlib.hash.rst +++ b/docs/lib/passlib.hash.rst @@ -1,44 +1,66 @@ -============================================= -:mod:`passlib` - Password Hashing -============================================= +============================================ +:mod:`passlib.hash` - Password Hash Schemes +============================================ -.. module:: passlib - :synopsis: password hashing (unix-crypt, md5-crypt, etc) +.. module:: passlib.hash + :synopsis: package containing handlers for all builtin password hash schemes Overview ======== -This module handles encrypting and verifying password hashes -(such as from unix shadow files). This module contains implementations of most -of the modern password hashing algorithms, -as well as a complex framework for implementing -new algorithms, managing hashes generated -within different contexts with different supported -algorithms, and other features. +This package contains handlers for all the password hash schemes built into +passlib. All modules within this package implement a single scheme, +and follow the :ref:`crypt-handler-api`. They can be imported +and used directly, eg:: -The algorithms currently supported by default in BPS: + >>> from passlib.hash import md5_crypt + >>> hash = md5_crypt.encrypt("password") - * Unix-Crypt - * MD5-Crypt - * BCrypt - * SHA-Crypt (256 & 512 bit modes) +As well, any third-party handlers registered with passlib via :func:`register_crypt_handler` +will be inserted into this package. - * PostgreSQL & MySQL password hashes +Note that many applications may find it easier to use a :class:`CryptContext` +instance, or retreive handlers via :func:`get_crypt_handler`, rather than +import directly from this module. -Sections +Contents ======== -The documentation for the pwhash module is broken into the following sections: - -* :doc:`Quick Start <passlib/quickstart>` -- frontend funcs for quickly creating / validating hashes -* :doc:`Crypt Contexts <passlib/contexts>` -- for using just the algorithms your application needs -* :doc:`Crypt Algorithms <passlib/algorithms>` -- details of the algorithms BPS implements -* :doc:`Implementing a Custom Crypt Algorithm <passlib/implementation>` -- Roll your own -* :doc:`Helper Functions <passlib/utils>` - -.. toctree:: - :hidden: - - passlib/quickstart - passlib/contexts - passlib/algorithms - passlib/implementation - passlib/utils +Passlib contains the following builtin password algorithms: + +Standard Unix Schemes +--------------------- +All these schemes are/were used by various unix flavors to store user passwords. +Because of this, all these schemes (except des-crypt and ext-des-crypt) follow +the :ref:`modular crypt format <modular-crypt-format>`. + +* :mod:`~passlib.hash.des_crypt` - Legacy DES-based unix crypt() algorithm. +* :mod:`~passlib.hash.ext_des_crypt` - Legacy BSDi extension of des-crypt which adds more salt and variable rounds. +* :mod:`~passlib.hash.md5_crypt` - MD5-based descendant of des-crypt. +* :mod:`~passlib.hash.bcrypt` - Blowfish-based replacement for md5-crypt, used mostly on BSD systems. +* :mod:`~passlib.hash.sha256_crypt` - SHA-256 based descendant of MD5 crypt, used mostly on Linux systems. +* :mod:`~passlib.hash.sha512_crypt` - SHA-512 based descendant of MD5 crypt, used mostly on Linux systems. + +Non-Standard Unix-Compatible Schemes +------------------------------------ +While few of these schemes were ever used by unix flavors to store user passwords, +these are compatible with the :ref:`modular crypt format <modular-crypt-format>`, and can be +used in contexts which support them in parallel with the others following +the same format. + +* :mod:`~passlib.hash.apr_md5_crypt` - Apache-specific variant of md5-crypt, used in htpasswd files + +.. todo:: + + These aren't fully implemented / tested yet: + + * :mod:`~passlib.hash.nthash` - modular-crypt-format encoding of legacy NTHASH algorithm + * :mod:`~passlib.hash.sun_md5_crypt` - MD5-based crypt descendant used by Solaris 10 (NOT related to md5-crypt above). + +Other Schemes +------------- +The following schemes are used in very specified contexts, +and have encoding schemes and other requirements +not seen outside those specific contexts: + +* :mod:`~passlib.hash.mysql_323` - Legacy scheme used by MySQL 3.2.3+ to store user passwords +* :mod:`~passlib.hash.mysql_41` - Current scheme used by MySQL 4.1+ to store user passwords +* :mod:`~passlib.hash.postgres_md5` - Current scheme used by PostgreSQL to store user passwords diff --git a/docs/lib/passlib.hash.sha256_crypt.rst b/docs/lib/passlib.hash.sha256_crypt.rst new file mode 100644 index 0000000..fbb23b0 --- /dev/null +++ b/docs/lib/passlib.hash.sha256_crypt.rst @@ -0,0 +1,10 @@ +================================================================== +:mod:`passlib.hash.sha256_crypt` - SHA-256 Crypt +================================================================== + +.. module:: passlib.hash.sha526_crypt + :synopsis: implementation of SHA-256 Crypt scheme + +.. todo:: + + write documentation diff --git a/docs/lib/passlib.hash.sha512_crypt.rst b/docs/lib/passlib.hash.sha512_crypt.rst new file mode 100644 index 0000000..325d963 --- /dev/null +++ b/docs/lib/passlib.hash.sha512_crypt.rst @@ -0,0 +1,16 @@ +================================================================== +:mod:`passlib.hash.sha512_crypt` - SHA-512 Crypt +================================================================== + +.. module:: passlib.hash.sha526_crypt + :synopsis: implementation of SHA-512 Crypt scheme + +Defined in the same specification, the SHA-512 Crypt scheme is almost identical to SHA-256 Crypt, +except for the following differences: + +* it uses the prefix ``$6$`` where the SHA-256 Crypt uses ``$5$``. +* it uses SHA-512 as it's internal hash function +* it's output hash is correspondingly larger. + +For details about this module, see :mod:`~passlib.hash.sha256_crypt`, +it is exactly the same except for the above differences. diff --git a/docs/lib/passlib.hash.sun_md5_crypt.rst b/docs/lib/passlib.hash.sun_md5_crypt.rst new file mode 100644 index 0000000..7db3bc9 --- /dev/null +++ b/docs/lib/passlib.hash.sun_md5_crypt.rst @@ -0,0 +1,10 @@ +============================================================= +:mod:`passlib.hash.sun_md5_crypt` - Sun MD5-Crypt +============================================================= + +.. module:: passlib.hash.sun_md5_crypt + :synopsis: Sun MD5 Crypt implementation + +.. todo:: + + write documentation diff --git a/docs/lib/passlib.hash/utils.rst b/docs/lib/passlib.hash/utils.rst deleted file mode 100644 index 752e381..0000000 --- a/docs/lib/passlib.hash/utils.rst +++ /dev/null @@ -1,19 +0,0 @@ -============================================= -:mod:`passlib` - Helper Functions -============================================= - -.. currentmodule:: passlib - -A couple of utility functions are available, -mainly useful when writing custom password hash algorithms. -The ``h64_*`` series of functions all provide -utilities for encoding & decoding strings -under the modified base64 system used by most -of the standard unix hash algorithms. - -.. autofunction:: h64_encode -.. autofunction:: h64_decode -.. autofunction:: h64_gen_salt - -.. autofunction:: is_crypt_context -.. autofunction:: is_crypt_handler diff --git a/docs/lib/passlib.unix.rst b/docs/lib/passlib.unix.rst new file mode 100644 index 0000000..9acf867 --- /dev/null +++ b/docs/lib/passlib.unix.rst @@ -0,0 +1,30 @@ +============================================ +:mod:`passlib.unix` - Password Hash Schemes +============================================ + +.. module:: passlib.unix + :synopsis: helpers for encrypting & verifying passwords on unix systems + +.. _modular-crypt-format: + +Modular Crypt Format +==================== +A vast majority of the schemes used on unix systems (and supported by this library) +follow the "Modular Crypt Format", introduced around the time :mod:`~passlib.hash.md5_crypt` was developed. +This scheme allows hashes generates by multiple schemes to co-exist within a database, +by requiring that all hash string begin with a unique prefix ``$identifier$``; +where ``identifier`` is a short alphanumeric string globally identifying +hashes generated by that algorithm. + +While not part of the specification, most modular crypt -compatible hashes +were designed to be used by unix systems to store user account passwords +in ``/etc/passwd`` or ``/etc/shadow``. Because of this, most of them +follow another defacto limitations: they avoid using the characters +``:``, ``\n``, and ``\x00`` anywhere in their encoded hash. +In fact, for the most part they avoid using any characters except +``a-zA-Z0-9./``, and ``$`` as an internal separator, though +this can be violated on some systems if the user intervenes. + +.. note:: + :mod:`passlib.hash.des_crypt` and :mod:`passlib.hash.ext_des_crypt` + do not follow this protocol, since they predate it by many years. diff --git a/docs/lib/passlib.utils.des.rst b/docs/lib/passlib.utils.des.rst new file mode 100644 index 0000000..f50cc95 --- /dev/null +++ b/docs/lib/passlib.utils.des.rst @@ -0,0 +1,17 @@ +============================================= +:mod:`passlib.utils.des` - DES routines +============================================= + +.. module:: passlib.utils.des + :synopsis: routines for performing DES encryption + +This module contains routines for encrypting blocks of data using the DES algorithm. + +They do not support multi-block operation or decryption, +since they are designed for use in password hash algorithms +such as :mod:`passlib.hash.des_crypt` and :mod:`passlib.hash.ext_des_crypt`. + +.. autofunction:: expand_des_key +.. autofunction:: des_encrypt_block +.. autofunction:: mdes_encrypt_int_block + diff --git a/docs/lib/passlib.utils.h64.rst b/docs/lib/passlib.utils.h64.rst new file mode 100644 index 0000000..b5f249b --- /dev/null +++ b/docs/lib/passlib.utils.h64.rst @@ -0,0 +1,42 @@ +================================================ +:mod:`passlib.utils.h64` - Hash-64 Codec helpers +================================================ + + +.. module:: passlib.utils.h64 + :synopsis: Hash-64 Codec helpers + +Many of the password hash algorithms in passlib +use a encoding scheme very similar to (but not compatible with) +the standard base64 encoding scheme. the main differences are that +it assigns the characters *completely* different numeric values compared +to base64, as well as using ``.`` instead of ``+`` in it's character set. + +This encoding system appears to have originated with des-crypt hash, +but is used by md5-crypt, sha-256-crypt, and others. +within passlib, this encoding is referred as ``hash64`` encoding, +and this module contains various utilities functions for encoding +and decoding strings in that format. + +.. note:: + It may *look* like bcrypt uses this scheme, + when in fact bcrypt uses the standard base64 encoding scheme, + but with ``+`` replaced with ``.``. + +.. data:: CHARS + + The character set used by the Hash-64 format. + Index in character set denotes 6-bit integer value. + +.. autofunction:: encode_3_offsets +.. autofunction:: encode_2_offsets +.. autofunction:: encode_1_offset + +.. autofunction:: decode_int12 +.. autofunction:: encode_int12 + +.. autofunction:: decode_int24 +.. autofunction:: encode_int24 + +.. autofunction:: decode_int64 +.. autofunction:: encode_int64 diff --git a/docs/lib/passlib.utils.md4.rst b/docs/lib/passlib.utils.md4.rst new file mode 100644 index 0000000..fb0f388 --- /dev/null +++ b/docs/lib/passlib.utils.md4.rst @@ -0,0 +1,20 @@ +==================================================== +:mod:`passlib.utils.md4` - MD4-Digest implementation +==================================================== + +.. module:: passlib.utils.md4 + :synopsis: implemented of MD4-Digest + +.. warning:: + + This digest is considered **VERY INSECURE**, + and not suitable for any new cryptographic activities. + Trivial-cost real-world attacks exist for all + password algorithms, stream ciphers, etc, that have + been based on MD4. + Do not use this hash or derived schemes unless you *really* have to. + +This module implements the MD4 hash algorithm in pure python, +based on the `rfc 1320 <http://www.faqs.org/rfcs/rfc1320.html>`_ specification of MD4. + +.. autoclass:: md4 diff --git a/docs/lib/passlib.utils.pbkdf2.rst b/docs/lib/passlib.utils.pbkdf2.rst new file mode 100644 index 0000000..7bd9ab9 --- /dev/null +++ b/docs/lib/passlib.utils.pbkdf2.rst @@ -0,0 +1,15 @@ +==================================================== +:mod:`passlib.utils.pbkdf2` - PBKDF2 algorithm support +==================================================== + +.. module:: passlib.utils.pbkdf2 + :synopsis: implementation of PBKDF2 algorithm + +This module provides a single function, :func:`pbkdf2`, +which provides the ability to generate an arbitrary +length key using the PBKDF2 key derivation algorithm, +as specified in `rfc 2898 <http://tools.ietf.org/html/rfc2898>`_. +This function can be helpful in creating password hashes +using schemes which have been based around the pbkdf2 algorithm. + +.. autofunction:: pbkdf2 diff --git a/docs/lib/passlib.utils.rst b/docs/lib/passlib.utils.rst new file mode 100644 index 0000000..00df703 --- /dev/null +++ b/docs/lib/passlib.utils.rst @@ -0,0 +1,79 @@ +============================================= +:mod:`passlib.utils` - Helper Functions +============================================= + +.. module:: passlib.utils + :synopsis: helper functions for implementing crypt handlers + +Overview +======== +This module contains a number of utility functions used by passlib +to implement the builtin handlers, and other code within passlib. +They may also be useful when implementing custom handlers for existing legacy formats. + +Decorators +========== +.. autofunction:: classproperty +.. autofunction:: abstractmethod +.. autofunction:: abstractclassmethod + +String Manipulation +=================== +.. autofunction:: splitcomma + +Bytes Manipulation +================== + +.. autofunction:: bytes_to_int +.. autofunction:: int_to_bytes +.. autofunction:: list_to_bytes +.. autofunction:: bytes_to_list + +.. autofunction:: xor_bytes + +Randomness +========== +.. data:: rng + + The random number generator used by passlib to generate + salt strings and other things which don't require a + cryptographically strong source of randomness. + +.. autofunction:: getrandbytes +.. autofunction:: getrandstr + +Object Tests +============ +.. autofunction:: is_crypt_handler + +.. todo:: + + .. autofunction:: is_crypt_context + +Crypt Handler Helpers +===================== +The following functions are used by passlib to do input validation +for many of the implemented password schemes: + +.. autofunction:: norm_rounds + +.. autofunction:: gen_salt(salt, charset=H64_CHARS) + +.. autofunction:: norm_salt(salt, min_chars, max_chars=None, charset=H64_CHARS, gen_charset=None, name=None) + +Submodules +========== +There are also a few sub modules which provide additional utility functions: + +.. toctree:: + + passlib.utils.des + passlib.utils.h64 + passlib.utils.md4 + passlib.utils.pbkdf2 + +.. todo:: + + document this module... + + passlib.utils.handlers diff --git a/docs/notes.txt b/docs/notes.txt index 72fcc89..fcbfa01 100644 --- a/docs/notes.txt +++ b/docs/notes.txt @@ -182,9 +182,6 @@ references for hashes & passwords http://chargen.matasano.com/chargen/2007/9/7/enough-with-the-rainbow-tables-what-you-need-to-know-about-s.html http://www.openwall.com/john/interviews/SF-20060222-p3 -http://search.cpan.org/~zefram/Authen-Passphrase-0.007/lib/Authen/Passphrase.pm - - =========== scrpyt http://www.tarsnap.com/scrypt.html diff --git a/docs/overview.rst b/docs/overview.rst index 71cb8cb..51559c1 100644 --- a/docs/overview.rst +++ b/docs/overview.rst @@ -2,20 +2,10 @@ Library Overview ================ -BPS started life in 2003 as an in-house collection of small functions -and tools which were frequently needed by the programmers at -`Assurance Technologies <http://www.assurancetechnologies.com>`_. -Over the years, it has accumlated a more small functions, -but it has also acquired some modules which provide major -new features that go above and beyond simple utility functions. -Since we have benefited greatly from open source software, -this library was released publically in 2009, in order -to fill a few niches for which there is a need (password hashing, -desktop interaction), as well as to simply give something -back to the community. +Passlib is a collection of routines for managing password hashes +as found in unix /etc/shadow files, as returned by stdlib `crypt()`, +as stored in mysql and postgres, and various other contexts. -.. module:: passlib - :synopsis: Root of all PassLib modules +.. todo:: -Organization -============ + more documentation! diff --git a/docs/lib/passlib.hash/quickstart.rst b/docs/quickstart.rst index c6128f8..c6128f8 100644 --- a/docs/lib/passlib.hash/quickstart.rst +++ b/docs/quickstart.rst diff --git a/passlib/base.py b/passlib/base.py index 09e5f89..53d48d4 100644 --- a/passlib/base.py +++ b/passlib/base.py @@ -21,10 +21,11 @@ import hashlib import logging; log = logging.getLogger(__name__) import time import os +from warnings import warn #site #libs import passlib.hash as _hmod -from passlib.utils import abstract_class_method, Undef, is_crypt_handler, splitcomma +from passlib.utils import abstractclassmethod, Undef, is_crypt_handler, splitcomma #pkg #local __all__ = [ @@ -85,8 +86,11 @@ def get_crypt_handler(name, default=Undef): "resolve crypt algorithm name" global _hmod - ###normalize name - ##name = name.replace("-","_").lower() + #normalize name + alt = name.replace("-","_").lower() + if alt != name: + warn("handler names be lower-case, and use underscores instead of hyphens: %r => %r" % (name, alt)) + name = alt #check if handler loaded handler = getattr(_hmod, name, None) diff --git a/passlib/hash/apr_md5_crypt.py b/passlib/hash/apr_md5_crypt.py index 3c1932c..5eb45fa 100644 --- a/passlib/hash/apr_md5_crypt.py +++ b/passlib/hash/apr_md5_crypt.py @@ -1,15 +1,4 @@ -"""passlib.hash.apr_md5_crypt - Apache variant of md5-crypt algorithm - -This format is primarily used by Apache in htpasswd files. - -.. note:: - This format would be identical to md5-crypt, - except for two things: it uses ``$apr1$`` as it's prefix - when encoded, and inserts that constant into the hash calculation - where md5-crypt would insert ``$1$``. - Thus, the formats aren't compatible, nor the checksums they contain. - Other than that, they have identical levels of security. -""" +"""passlib.hash.apr_md5_crypt - Apache variant of md5-crypt algorithm""" #========================================================= #imports #========================================================= diff --git a/passlib/hash/postgres_md5.py b/passlib/hash/postgres_md5.py index 0efefe6..faa27e5 100644 --- a/passlib/hash/postgres_md5.py +++ b/passlib/hash/postgres_md5.py @@ -1,26 +1,4 @@ -"""passlib.hash.postgres_md5 - MD5-based algorithm used by Postgres for pg_shadow table - -This implements the md5-based hash algorithm used by Postgres to store -passwords in the pg_shadow table. - -This algorithm shouldn't be used for any purpose besides Postgres interaction, -it's a weak unsalted algorithm which could be attacked with a rainbow table -built against common user names. - -.. warning:: - This algorithm is slightly different from most of the others, - in that both encrypt() and verify() require you pass in - the name of the user account via the required 'user' keyword, - since postgres uses this in place of a salt :( - -Usage Example:: - - >>> from passlib.hash import postgres_md5 as pm - >>> pm.encrypt("mypass", user="postgres") - 'md55fba2ea04fd36069d2574ea71c8efe9d' - >>> pm.verify("mypass", 'md55fba2ea04fd36069d2574ea71c8efe9d', user="postgres") - True -""" +"""passlib.hash.postgres_md5 - MD5-based algorithm used by Postgres for pg_shadow table""" #========================================================= #imports #========================================================= diff --git a/passlib/utils/__init__.py b/passlib/utils/__init__.py index 37b25b2..0a88df9 100644 --- a/passlib/utils/__init__.py +++ b/passlib/utils/__init__.py @@ -20,7 +20,7 @@ __all__ = [ #decorators "classproperty", "abstractmethod", - "abstract_class_property", + "abstractclassmethod", #byte manipulation "bytes_to_list", @@ -36,7 +36,7 @@ __all__ = [ #decorators #================================================================================= class classproperty(object): - """Decorator which acts like a combination of classmethod+property (limited to read-only)""" + """Function decorator which acts like a combination of classmethod+property (limited to read-only properties)""" def __init__(self, func): self.im_func = func @@ -57,9 +57,9 @@ def abstractmethod(func): update_wrapper(wrapper, func) return wrapper -def abstract_class_method(func): +def abstractclassmethod(func): """Class Method decorator which indicates this is a placeholder method which - should be overridden by subclass. + should be overridden by subclass, and must be a classmethod. If called directly, this method will raise an :exc:`NotImplementedError`. """ @@ -76,7 +76,7 @@ Undef = object() #singleton used as default kwd value in some functions #protocol helpers #========================================================== def is_crypt_handler(obj): - "check if obj follows CryptHandler api" + "check if object follows the :ref:`crypt handler api <crypt-handler-api>`" return all(hasattr(obj, name) for name in ( "name", "setting_kwds", "context_kwds", @@ -96,7 +96,7 @@ def is_crypt_handler(obj): #string helpers #================================================================================= def splitcomma(source): - "split comma separated list into elements, stripping whitespace" + "split comma separated string into list elements, stripping whitespace and empty elements" return [ elem.strip() for elem in source.split(",") @@ -143,14 +143,14 @@ def splitcomma(source): ## return out def bytes_to_int(value): - "decode bytes as single big-endian integer" + "decode string of bytes as single big-endian integer" out = 0 for v in value: out = (out<<8) | ord(v) return out def int_to_bytes(value, count): - "encode integer into single big-endian byte string" + "encodes integer into single big-endian byte string" assert value < (1<<(8*count)), "value too large for %d bytes: %d" % (count, value) return ''.join( chr((value>>s) & 0xff) @@ -161,8 +161,8 @@ def int_to_bytes(value, count): def list_to_bytes(value, bytes=None, order="big"): """Returns a multi-character string corresponding to a list of byte values. - This is similar to :func:`int_to_bytes`, except that this a list of integers - instead of a single encoded integer. + This is similar to :func:`int_to_bytes`, except that this returns a list + of integers, where each integer corresponds to a single byte of the input. :arg value: The list of integers to encode. @@ -183,7 +183,7 @@ def list_to_bytes(value, bytes=None, order="big"): Usage Example:: - >>> from passlib.util import list_to_bytes, bytes_to_list + >>> from passlib.utils import list_to_bytes, bytes_to_list >>> list_to_bytes([4, 210], 4) '\\x00\\x00\\x04\\xd2' @@ -253,7 +253,7 @@ def bytes_to_list(value, order="big"): _join = "".join def xor_bytes(left, right): - "bitwise-xor two byte-strings together" + "perform bitwise-xor of two byte-strings" return _join(chr(ord(l) ^ ord(r)) for l, r in zip(left, right)) #================================================================================= @@ -308,10 +308,10 @@ rng = random.Random(genseed()) #----------------------------------------------------------------------- def getrandbytes(rng, count): - """return string of *count* number of random bytes, using specified rng""" + """return byte-string containing *count* number of randomly generated bytes, using specified rng""" #NOTE: would be nice if this was present in stdlib Random class - ###just in case rng provides this (eg our SystemRandom subclass above)... + ###just in case rng provides this... ##meth = getattr(rng, "getrandbytes", None) ##if meth: ## return meth(count) @@ -324,23 +324,23 @@ def getrandbytes(rng, count): value //= 0xff return buf.getvalue() -def getrandstr(rng, alphabet, count): - """return string of *size* number of chars, whose elements are drawn from specified alphabet""" +def getrandstr(rng, charset, count): + """return character string containg *count* number of chars, whose elements are drawn from specified charset, using specified rng""" #check alphabet & count if count < 0: raise ValueError, "count must be >= 0" - letters = len(alphabet) + letters = len(charset) if letters == 0: raise ValueError, "alphabet must not be empty" if letters == 1: - return alphabet * count + return charset * count #get random value, and write out to buffer #XXX: break into chunks for large number of letters? value = rng.randrange(0, letters**count) buf = StringIO() for i in xrange(count): - buf.write(alphabet[value % letters]) + buf.write(charset[value % letters]) value //= letters assert value == 0 return buf.getvalue() @@ -374,6 +374,7 @@ def norm_rounds(rounds, default_rounds, min_rounds, max_rounds, name="this crypt return rounds def gen_salt(count, charset=h64.CHARS): + "generate salt string of *count* chars using specified *charset*" global rng return getrandstr(rng, charset, count) @@ -389,11 +390,12 @@ def norm_salt(salt, min_chars, max_chars=None, charset=h64.CHARS, gen_charset=No :arg salt: user-provided salt :arg min_chars: minimum number of chars in salt :arg max_chars: maximum number of chars in salt (if omitted, same as min_chars) - :param charset: character set that salt MUST be subset of + :param charset: character set that salt MUST be subset of (defaults to :) :param gen_charset: optional character set to restrict to when generating new salts (defaults to charset) :param name: optional name of handler, for inserting into error messages :raises ValueError: + * if salt contains chars that aren't in salt_charset. * if salt contains less than min_salt_chars characters. @@ -422,27 +424,5 @@ def norm_salt(salt, min_chars, max_chars=None, charset=h64.CHARS, gen_charset=No return salt #================================================================================= -#errors -#================================================================================= - -###NOTE: not all handlers will raise these errors, -### the only thing currently guaranteed is that they -### will -## -##class HandlerError(ValueError): -## "helper class for various errors used by some CryptHandlers" -## message = None -## -## def __init__(self, msg=None): -## ValueError.__init__(self, msg or self.message) -## -##class NoChecksumError(HandlerError): -## "helper raised by CryptHandler.verify() when config string passed in instead of hash" -## #helper for common message raised by handlers -## -## message = "hash lacks checksum (did you pass a config string into verify?)" - - -#================================================================================= #eof #================================================================================= diff --git a/passlib/utils/des.py b/passlib/utils/des.py index 0851bb3..aea62d7 100644 --- a/passlib/utils/des.py +++ b/passlib/utils/des.py @@ -1,17 +1,3 @@ -"""passlib.utils.des -- DES encryption routines - -This module contains routines for encrypting blocks of data using the DES algorithm. - -They do not support multi-block operation or decryption, -since they are designed for use in password hash algorithms -such as ``lmhash`` and ``des-crypt``. - -.. function:: expand_des_key -.. function:: des_encrypt_block -.. function:: mdes_encrypt_int_block - -""" - """ History ======= diff --git a/passlib/utils/h64.py b/passlib/utils/h64.py index 9e91cb7..4d472c2 100644 --- a/passlib/utils/h64.py +++ b/passlib/utils/h64.py @@ -1,22 +1,4 @@ -"""passlib.utils.h64 - hash64 encoding helpers - -many of the password hash algorithms in passlib -use a encoding scheme very similar to, but not compatible with, -the standard base64 encoding scheme. the main differences are that -it uses ``.`` instead of ``+``, and assigns the -characters *completely* different numeric values. - -this encoding system appears to have originated with des-crypt hash, -but is used by md5-crypt, sha-256-crypt, and others. -within passlib, this encoding is referred as ``hash64`` encoding, -and this module contains various utilities functions for encoding -and decoding strings in that format. - -.. note:: - It may *look* like bcrypt uses this scheme, - when in fact bcrypt uses the standard base64 encoding scheme, - but with ``+`` replaced with ``.``. -""" +"""passlib.utils.h64 - hash64 encoding helpers""" #================================================================================= #imports #================================================================================= @@ -34,10 +16,9 @@ __all__ = [ "encode_2_offsets", "encode_1_offset", - "decode_int12", + "decode_int12", "encode_int12" "decode_int24", "encode_int24", - "encode_int64", - + "decode_int64", "encode_int64", ] #================================================================================= @@ -93,17 +74,17 @@ def encode_1_offset(buffer, o1): # int <-> b64 string, used by des_crypt, ext_des_crypt #================================================================================= -##def decode_int(value): -## "decode hash-64 format used by crypt into integer" -## #FORMAT: little-endian, each char contributes 6 bits, -## # char value = index in H64_CHARS string -## try: -## out = 0 -## for c in reversed(value): -## out = (out<<6) + b64_decode_6bit(c) -## return out -## except KeyError: -## raise ValueError, "invalid character in string" +def decode_int(value): + "decode hash-64 format used by crypt into integer" + #FORMAT: little-endian, each char contributes 6 bits, + # char value = index in H64_CHARS string + try: + out = 0 + for c in reversed(value): + out = (out<<6) + b64_decode_6bit(c) + return out + except KeyError: + raise ValueError, "invalid character in string" def decode_int12(value): "decode 2 chars of hash-64 format used by crypt, returning 12-bit integer" @@ -112,8 +93,12 @@ def decode_int12(value): except KeyError: raise ValueError, "invalid character" +def encode_int12(value): + "encode 2 chars of hash-64 format from a 12-bit integer" + return encode_6bit(value & 0x3f) + encode_6bit((value>>6) & 0x3f) + def decode_int24(value): - "decode 4 chars of hash-64 format used by crypt, returning 24-bit integer" + "decode 4 chars of hash-64 format, returning 24-bit integer" try: return decode_6bit(value[0]) +\ (decode_6bit(value[1])<<6)+\ @@ -123,7 +108,7 @@ def decode_int24(value): raise ValueError, "invalid character" def encode_int24(value): - "decode 2 chars of hash-64 format used by crypt, returning 12-bit integer" + "encode 4 chars of hash-64 format from a 24-bit integer" return encode_6bit(value & 0x3f) + \ encode_6bit((value>>6) & 0x3f) + \ encode_6bit((value>>12) & 0x3f) + \ @@ -131,8 +116,12 @@ def encode_int24(value): _RR9_1 = range(9,-1,-1) +def decode_int64(value): + "decode 64-bit integer from 11 chars of hash-64 format" + return decode_int(value) + def encode_int64(value): - "encode 64-bit integer to hash-64 format used by crypt, returning 11 chars" + "encode 64-bit integer to hash-64 format, returning 11 chars" out = [None] * 10 + [ encode_6bit((value<<2)&0x3f) ] value >>= 4 for i in _RR9_1: diff --git a/passlib/utils/handlers.py b/passlib/utils/handlers.py index 6c09375..8e9fe1f 100644 --- a/passlib/utils/handlers.py +++ b/passlib/utils/handlers.py @@ -12,7 +12,7 @@ import time import os #site #libs -from passlib.utils import abstract_class_method, classproperty, h64, \ +from passlib.utils import abstractclassmethod, classproperty, h64, \ getrandstr, rng, Undef, is_crypt_handler #pkg #local @@ -67,7 +67,7 @@ class CryptHandler(object): #primary interface - primary methods implemented by each handler #========================================================= - @abstract_class_method + @abstractclassmethod def genhash(cls, secret, config, **context): """encrypt secret to hash @@ -355,7 +355,7 @@ class CryptHandler(object): ## #backend parsing routines - used by helpers below ## #========================================================= ## -## @abstract_class_method +## @abstractclassmethod ## def parse(cls, hash): ## """parse hash or config into dictionary. ## @@ -388,7 +388,7 @@ class CryptHandler(object): ## verify() method can work properly. ## """ ## -## @abstract_class_method +## @abstractclassmethod ## def render(cls, checksum=None, **settings): ## """render hash from checksum & settings (as returned by :meth:`parse`). ## diff --git a/passlib/utils/md4.py b/passlib/utils/md4.py index 550eff8..f01ecb0 100644 --- a/passlib/utils/md4.py +++ b/passlib/utils/md4.py @@ -24,7 +24,28 @@ def new(content=None): return md4(content) class md4(object): - "md4 hash algorithm" + """pep-247 compatible implementation of MD4 hash algorithm + + .. attribute:: digest_size + + size of md4 digest in bytes (16 bytes) + + .. method:: update + + update digest by appending additional content + + .. method:: copy + + create clone of digest object, including current state + + .. method:: digest + + return bytes representing md4 digest of current content + + .. method:: hexdigest + + return hexdecimal version of digest + """ #FIXME: make this follow hash object PEP better. #FIXME: this isn't threadsafe #XXX: should we monkeypatch ourselves into hashlib for general use? probably wouldn't be nice. |
