summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEli Collins <elic@assurancetechnologies.com>2012-09-20 12:56:26 -0400
committerEli Collins <elic@assurancetechnologies.com>2012-09-20 12:56:26 -0400
commit2d9c9a1368b947802a9200bc34f7316b4d22f394 (patch)
treec5d80cd0628c57df7548800d0a82e64ff3eae54b
parentbc3cecf3d800f24d5289f2dbf7ed33a09203262b (diff)
downloadpasslib-2d9c9a1368b947802a9200bc34f7316b4d22f394.tar.gz
various documentation updates
-rw-r--r--CHANGES8
-rw-r--r--MANIFEST.in3
-rw-r--r--admin/benchmarks.py37
-rw-r--r--docs/_static/masthead.pngbin6296 -> 8103 bytes
-rw-r--r--docs/_static/masthead.svg142
-rw-r--r--docs/conf.py26
-rw-r--r--docs/lib/passlib.registry.rst4
-rw-r--r--docs/lib/passlib.utils.handlers.rst9
-rw-r--r--docs/lib/passlib.utils.rst12
-rw-r--r--docs/modular_crypt_format.rst3
-rw-r--r--docs/password_hash_api.rst85
-rw-r--r--passlib/context.py2
-rw-r--r--passlib/utils/__init__.py11
-rw-r--r--setup.py9
14 files changed, 248 insertions, 103 deletions
diff --git a/CHANGES b/CHANGES
index cc52623..6f9f2be 100644
--- a/CHANGES
+++ b/CHANGES
@@ -28,18 +28,18 @@ Release History
* *bugfix*: FreeBSD 8.3 added native support for :class:`~passlib.hash.sha256_crypt` --
updated Passlib's unittests and documentation accordingly (:issue:`35`).
- * *bugfix:* Fixed bug which caused passlib.apache unittest to fail
+ * *bugfix:* Fixed bug which caused some :mod:`!passlib.apache` unittests to fail
if mtime resolution >= 1 second (:issue:`35`).
- * Various bugfixes for Python 3.3 compatibility.
+ * *bugfix:* Fixed minor bug in :mod:`!passlib.registry`, should now work correctly under Python 3.3.
* Various documentation updates and corrections.
+.. _whats-new:
+
**1.6** (2012-05-01)
====================
-.. _whats-new:
-
Overview
--------
diff --git a/MANIFEST.in b/MANIFEST.in
index 902f79d..447028c 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -1,3 +1,2 @@
recursive-include docs *
-include LICENSE README CHANGES passlib/*.cfg passlib/tests/*.cfg tox.ini setup.cfg
-prune *.komodoproject
+include LICENSE README CHANGES passlib/tests/*.cfg tox.ini setup.cfg
diff --git a/admin/benchmarks.py b/admin/benchmarks.py
index 4687f37..8edbcd5 100644
--- a/admin/benchmarks.py
+++ b/admin/benchmarks.py
@@ -15,6 +15,7 @@ sys.path.insert(0, os.curdir)
# imports
#=============================================================================
# core
+from binascii import hexlify
import logging; log = logging.getLogger(__name__)
import os
import warnings
@@ -64,13 +65,9 @@ class benchmark:
kwds = defaults.copy()
kwds.update(options)
if mode == "ctor":
- itr = obj()
- if not hasattr(itr, next_method_attr):
- itr = [itr]
- for func in itr:
- # TODO: per function name & options
- secs, precision = cls.measure(func, None, **kwds)
- yield name, secs, precision
+ func = obj()
+ secs, precision = cls.measure(func, None, **kwds)
+ yield name, secs, precision
else:
raise ValueError("invalid mode: %r" % (mode,))
@@ -224,7 +221,7 @@ def test_md5_crypt_builtin():
hash = md5_crypt.encrypt(SECRET)
md5_crypt.verify(SECRET, hash)
md5_crypt.verify(OTHER, hash)
- yield helper
+ return helper
@benchmark.constructor()
def test_ldap_salted_md5():
@@ -234,7 +231,7 @@ def test_ldap_salted_md5():
hash = handler.encrypt(SECRET, salt='....')
handler.verify(SECRET, hash)
handler.verify(OTHER, hash)
- yield helper
+ return helper
@benchmark.constructor()
def test_phpass():
@@ -245,20 +242,26 @@ def test_phpass():
hash = handler.encrypt(SECRET, **kwds)
handler.verify(SECRET, hash)
handler.verify(OTHER, hash)
- yield helper
+ return helper
#=============================================================================
# crypto utils
#=============================================================================
@benchmark.constructor()
-def test_pbkdf2():
-# from passlib.hash import pbkdf2_sha1
- from passlib.hash import pbkdf2_sha256 as hash
+def test_pbkdf2_sha1():
+ from passlib.utils.pbkdf2 import pbkdf2
+ def helper():
+ result = hexlify(pbkdf2("abracadabra", "open sesasme", 40960, 20, "hmac-sha1"))
+ assert result == 'ad317ed77bce584c90932b609e37e3736e6297bf', result
+ return helper
+
+@benchmark.constructor()
+def test_pbkdf2_sha256():
+ from passlib.utils.pbkdf2 import pbkdf2
def helper():
-# hash.encrypt("password", salt="salt", rounds=10000)
- result = pbkdf2_sha1.encrypt("password", salt="salt", rounds=10000)
- assert result == '$pbkdf2-sha256$10240$c2FsdA$FUGp71zmshcv1IwX1DV3ADWDyP66H/ANJZwmoGuF7FA'
- yield helper
+ result = hexlify(pbkdf2("abracadabra", "open sesasme", 10240, 32, "hmac-sha256"))
+ assert result == '21d1ac0d474aaec49feb4f2172a266223e43edcf1052643dd27d82ebd5fa10c6', result
+ return helper
#=============================================================================
# main
diff --git a/docs/_static/masthead.png b/docs/_static/masthead.png
index 45e9015..677fcb1 100644
--- a/docs/_static/masthead.png
+++ b/docs/_static/masthead.png
Binary files differ
diff --git a/docs/_static/masthead.svg b/docs/_static/masthead.svg
index 5c0b2ed..e02eca8 100644
--- a/docs/_static/masthead.svg
+++ b/docs/_static/masthead.svg
@@ -14,10 +14,10 @@
height="52"
id="svg2383"
sodipodi:version="0.32"
- inkscape:version="0.48.0 r9654"
+ inkscape:version="0.48.3.1 r9886"
sodipodi:docname="masthead.svg"
inkscape:output_extension="org.inkscape.output.svg.inkscape"
- inkscape:export-filename="/home/biscuit/dev/libs/passlib/trunk/docs/_static/masthead.png"
+ inkscape:export-filename="/home/biscuit/dev/libs/passlib/stable/docs/_static/masthead.png"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90"
version="1.0"
@@ -25,6 +25,18 @@
<defs
id="defs2385">
<linearGradient
+ inkscape:collect="always"
+ id="linearGradient3910">
+ <stop
+ style="stop-color:#000000;stop-opacity:1;"
+ offset="0"
+ id="stop3912" />
+ <stop
+ style="stop-color:#000000;stop-opacity:0;"
+ offset="1"
+ id="stop3914" />
+ </linearGradient>
+ <linearGradient
id="linearGradient4140">
<stop
style="stop-color:#ffe50a;stop-opacity:1;"
@@ -186,6 +198,28 @@
stdDeviation="5.9143996"
id="feGaussianBlur3885" />
</filter>
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3910"
+ id="radialGradient3916"
+ cx="25.454468"
+ cy="44.810959"
+ fx="25.454468"
+ fy="44.810959"
+ r="21.542249"
+ gradientTransform="matrix(2.0471236,-0.05164693,0.02652595,1.0514061,-25.117708,-21.747692)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3910"
+ id="radialGradient4270"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(-1.9278492,0.04803928,-0.03632715,-1.4578329,160.96275,83.618729)"
+ cx="67.387276"
+ cy="44.127342"
+ fx="67.387276"
+ fy="44.127342"
+ r="21.542249" />
</defs>
<sodipodi:namedview
id="base"
@@ -194,17 +228,17 @@
borderopacity="1.0"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
- inkscape:zoom="3.5"
- inkscape:cx="-15.023835"
- inkscape:cy="23.805612"
- inkscape:current-layer="layer8"
+ inkscape:zoom="2.4748737"
+ inkscape:cx="68.856099"
+ inkscape:cy="26.997913"
+ inkscape:current-layer="layer6"
showgrid="true"
inkscape:grid-bbox="true"
inkscape:document-units="px"
inkscape:window-width="1920"
- inkscape:window-height="1003"
+ inkscape:window-height="1021"
inkscape:window-x="0"
- inkscape:window-y="24"
+ inkscape:window-y="0"
borderlayer="true"
inkscape:window-maximized="1" />
<metadata
@@ -234,6 +268,98 @@
sodipodi:ry="8.485281"
d="m 55.356359,38.302536 a 30.910667,8.485281 0 1 1 -61.8213344,0 30.910667,8.485281 0 1 1 61.8213344,0 z"
transform="matrix(0.43660868,0,0,0.53092582,10.777999,23.937337)" />
+ <text
+ xml:space="preserve"
+ style="font-size:6.01588392000000027px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;opacity:0.34213125000000000;fill:url(#radialGradient4270);fill-opacity:1;stroke:none;display:inline;font-family:Sans"
+ x="-10.452676"
+ y="-45.764187"
+ id="text3028"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ x="-10.452676"
+ y="-45.764187"
+ style="fill:url(#radialGradient4270);fill-opacity:1"
+ id="tspan4268">0111010001101111011011110010000001101101011000010110111001111001</tspan><tspan
+ sodipodi:role="line"
+ x="-10.452676"
+ y="-38.244331"
+ style="fill:url(#radialGradient4270);fill-opacity:1"
+ id="tspan4316">0010000001110011011001010110001101110010011001010111010001110011</tspan><tspan
+ sodipodi:role="line"
+ x="-10.452676"
+ y="-30.724478"
+ style="fill:url(#radialGradient4270);fill-opacity:1"
+ id="tspan4318">0111010001101111011011110010000001101101011000010110111001111001</tspan><tspan
+ sodipodi:role="line"
+ x="-10.452676"
+ y="-23.204622"
+ style="fill:url(#radialGradient4270);fill-opacity:1"
+ id="tspan4320">0010000001110011011001010110001101110010011001010111010001110011</tspan><tspan
+ sodipodi:role="line"
+ x="-10.452676"
+ y="-15.684768"
+ style="fill:url(#radialGradient4270);fill-opacity:1"
+ id="tspan4322">0111010001101111011011110010000001101101011000010110111001111001</tspan><tspan
+ sodipodi:role="line"
+ x="-10.452676"
+ y="-8.1649122"
+ style="fill:url(#radialGradient4270);fill-opacity:1"
+ id="tspan4324">0010000001110011011001010110001101110010011001010111010001110011</tspan><tspan
+ sodipodi:role="line"
+ x="-10.452676"
+ y="-0.64505744"
+ style="fill:url(#radialGradient4270);fill-opacity:1"
+ id="tspan4326">0111010001101111011011110010000001101101011000010110111001111001</tspan><tspan
+ sodipodi:role="line"
+ x="-10.452676"
+ y="6.8747973"
+ style="fill:url(#radialGradient4270);fill-opacity:1"
+ id="tspan4328">0010000001110011011001010110001101110010011001010111010001110011</tspan><tspan
+ sodipodi:role="line"
+ x="-10.452676"
+ y="14.394652"
+ style="fill:url(#radialGradient4270);fill-opacity:1"
+ id="tspan4330">0111010001101111011011110010000001101101011000010110111001111001</tspan><tspan
+ sodipodi:role="line"
+ x="-10.452676"
+ y="21.914507"
+ style="fill:url(#radialGradient4270);fill-opacity:1"
+ id="tspan4332">0010000001110011011001010110001101110010011001010111010001110011</tspan><tspan
+ sodipodi:role="line"
+ x="-10.452676"
+ y="29.434362"
+ style="fill:url(#radialGradient4270);fill-opacity:1"
+ id="tspan4334">0111010001101111011011110010000001101101011000010110111001111001</tspan><tspan
+ sodipodi:role="line"
+ x="-10.452676"
+ y="36.954216"
+ style="fill:url(#radialGradient4270);fill-opacity:1"
+ id="tspan4336">0010000001110011011001010110001101110010011001010111010001110011</tspan><tspan
+ sodipodi:role="line"
+ x="-10.452676"
+ y="44.474072"
+ style="fill:url(#radialGradient4270);fill-opacity:1"
+ id="tspan4338">0111010001101111011011110010000001101101011000010110111001111001</tspan><tspan
+ sodipodi:role="line"
+ x="-10.452676"
+ y="51.993927"
+ style="fill:url(#radialGradient4270);fill-opacity:1"
+ id="tspan4340">0010000001110011011001010110001101110010011001010111010001110011</tspan><tspan
+ sodipodi:role="line"
+ x="-10.452676"
+ y="59.513783"
+ style="fill:url(#radialGradient4270);fill-opacity:1"
+ id="tspan4342">0111010001101111011011110010000001101101011000010110111001111001</tspan><tspan
+ sodipodi:role="line"
+ x="-10.452676"
+ y="67.033638"
+ style="fill:url(#radialGradient4270);fill-opacity:1"
+ id="tspan4344">0010000001110011011001010110001101110010011001010111010001110011</tspan><tspan
+ sodipodi:role="line"
+ x="-10.452676"
+ y="74.55349"
+ style="fill:url(#radialGradient4270);fill-opacity:1"
+ id="tspan4346" /></text>
</g>
<g
inkscape:groupmode="layer"
diff --git a/docs/conf.py b/docs/conf.py
index 27b26f4..b269161 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
"""
-Sphinx configuration file for Passlib documentation.
+Sphinx configuration file for the Passlib documentation.
This file is execfile()d with the current directory set to its containing dir.
Note that not all possible configuration values are present in this
@@ -12,21 +12,20 @@ commented out serve to show the default.
#=============================================================================
import sys, os
-# make sure passlib in sys.path
-doc_root = os.path.abspath(os.path.dirname(__file__))
-source_root = os.path.dirname(doc_root)
-sys.path.insert(0, source_root)
-
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
##sys.path.insert(0, os.path.abspath('.'))
+# make sure root of source dir in sys.path
+sys.path.insert(0, os.path.abspath(os.pardir))
+
#=============================================================================
# imports
#=============================================================================
-# load build option flags
+# build option flags:
+# "for-pypi" -- enable analytics tracker for pypi documentation
options = os.environ.get("PASSLIB_DOCS", "").split(",")
# building the docs requires the Cloud Sphinx theme & extensions (>= v1.4),
@@ -85,7 +84,8 @@ index_doc = 'index'
# General information about the project.
project = 'Passlib'
-copyright = '2008-2012, Assurance Technologies, LLC'
+author = "Assurance Technologies, LLC"
+copyright = "2008-2012, " + author
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
@@ -149,14 +149,14 @@ issue_tracker_url = "gc:passlib"
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
-html_theme = 'redcloud'
+html_theme = os.environ.get("SPHINX_THEME") or 'redcloud'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
html_theme_options = {}
if csp.is_cloud_theme(html_theme):
- html_theme_options.update(roottarget=index_doc)
+ html_theme_options.update(roottarget=index_doc, issueicon=None)
if 'for-pypi' in options:
html_theme_options.update(
googleanalytics_id = 'UA-22302196-2',
@@ -189,7 +189,7 @@ html_static_path = ['_static']
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
-html_last_updated_fmt = '%b %d, %Y'
+##html_last_updated_fmt = '%b %d, %Y'
# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
@@ -245,7 +245,7 @@ htmlhelp_basename = project + 'Doc'
# (source start file, target name, title, author, documentclass [howto/manual]).
latex_documents = [
(index_doc, project + '.tex', project + ' Documentation',
- 'Assurance Technologies, LLC', 'manual'),
+ author, 'manual'),
]
# The name of an image file (relative to this directory) to place at the top of
@@ -279,7 +279,7 @@ latex_documents = [
# (source start file, name, description, authors, manual section).
man_pages = [
(index_doc, project, project + ' Documentation',
- ['Assurance Technologies, LLC'], 1)
+ [author], 1)
]
#=============================================================================
diff --git a/docs/lib/passlib.registry.rst b/docs/lib/passlib.registry.rst
index 6db21c4..9aadca9 100644
--- a/docs/lib/passlib.registry.rst
+++ b/docs/lib/passlib.registry.rst
@@ -24,10 +24,10 @@ querying Passlib to detect what algorithms are available.
Interface
=========
-.. autofunction:: get_crypt_handler
+.. autofunction:: get_crypt_handler(name[, default])
.. autofunction:: list_crypt_handlers
.. autofunction:: register_crypt_handler_path
-.. autofunction:: register_crypt_handler
+.. autofunction:: register_crypt_handler(handler, force=False)
.. note::
diff --git a/docs/lib/passlib.utils.handlers.rst b/docs/lib/passlib.utils.handlers.rst
index 6b4799a..be948af 100644
--- a/docs/lib/passlib.utils.handlers.rst
+++ b/docs/lib/passlib.utils.handlers.rst
@@ -31,15 +31,16 @@ should look like (if the implementation even uses them).
That said, most of the handlers built into Passlib are based around the :class:`GenericHandler`
class, and it's associated mixin classes. While deriving from this class is not required,
-doing so will greatly reduce the amount of addition code that is needed for
+doing so will greatly reduce the amount of additional code that is needed for
all but the most convoluted password hash schemes.
Once a handler has been written, it may be used explicitly, passed into
-an application's custom :class:`CryptContext` directly, or registered
+a :class:`CryptContext` constructor, or registered
globally with Passlib via the :mod:`passlib.registry` module.
-See :ref:`testing-hash-handlers` for details about how to test
-custom handlers against Passlib's unittest suite.
+.. seealso::
+ :ref:`testing-hash-handlers` for details about how to test
+ custom handlers against Passlib's unittest suite.
The GenericHandler Class
========================
diff --git a/docs/lib/passlib.utils.rst b/docs/lib/passlib.utils.rst
index c3f2739..6427111 100644
--- a/docs/lib/passlib.utils.rst
+++ b/docs/lib/passlib.utils.rst
@@ -5,10 +5,6 @@
.. module:: passlib.utils
:synopsis: internal helpers for implementing password hashes
-This module contains a number of utility functions used by Passlib
-to implement the builtin hashes and other internals.
-They may also be useful when implementing custom handlers for existing legacy formats.
-
.. warning::
This module is primarily used as an internal support module.
@@ -16,6 +12,14 @@ They may also be useful when implementing custom handlers for existing legacy fo
between major releases of Passlib, as the internal code is cleaned up
and simplified.
+This module primarily contains utility functions used interally by Passlib.
+However, end-user applications may find some of the functions useful,
+in particular:
+
+ * :func:`consteq`
+ * :func:`saslprep`
+ * :func:`generate_password`
+
Constants
=========
diff --git a/docs/modular_crypt_format.rst b/docs/modular_crypt_format.rst
index a5566a5..a88297e 100644
--- a/docs/modular_crypt_format.rst
+++ b/docs/modular_crypt_format.rst
@@ -154,7 +154,8 @@ by the following operating systems and platforms:
===================== ==============================================================
**MacOS X** Darwin's native :func:`!crypt` provides limited functionality,
supporting only :class:`~passlib.hash.des_crypt` and
- :class:`~passlib.hash.bsdi_crypt`.
+ :class:`~passlib.hash.bsdi_crypt`. OS X uses a separate
+ system for it's own password hashes.
**Google App Engine** As of 2011-08-19, Google App Engine's :func:`!crypt`
implementation appears to match that of a typical Linux
diff --git a/docs/password_hash_api.rst b/docs/password_hash_api.rst
index 19a2b8d..d2ec5d5 100644
--- a/docs/password_hash_api.rst
+++ b/docs/password_hash_api.rst
@@ -150,9 +150,9 @@ and hash comparison.
Digest password using format-specific algorithm,
returning resulting hash string.
- For most hashes supported by Passlib, this string will include
- an algorithm identifier, a copy of the salt (if applicable),
- and any other configuration information required to verify the password later.
+ For most hashes supported by Passlib, the returned string will contain:
+ an algorithm identifier, a cost parameter, the salt string,
+ and finally the password digest itself.
:type secret: unicode or bytes
:arg secret: string containing the password to encode.
@@ -166,28 +166,27 @@ and hash comparison.
Examples of common keywords include ``rounds`` and ``salt_size``.
:returns:
- Resulting hash of password, using an algorithm-specific format.
-
+ Resulting password hash, encoded in an algorithm-specific format.
This will always be an instance of :class:`!str`
(i.e. :class:`unicode` under Python 3, ``ascii``-encoded :class:`bytes` under Python 2).
:raises ValueError:
- * If a keyword's value is invalid (e.g. if a ``salt`` string
+ * If a ``kwd``'s value is invalid (e.g. if a ``salt`` string
is too small, or a ``rounds`` value is out of range).
- * If the ``secret`` contains characters forbidden by the handler
+ * If ``secret`` contains characters forbidden by the hash algorithm
(e.g. :class:`!des_crypt` forbids NULL characters).
:raises TypeError:
- * if ``secret`` is not unicode or bytes.
- * if a keyword argument had an incorrect type.
- * if a required keyword was not provided.
+ * if ``secret`` is not :class:`!unicode` or :class:`bytes`.
+ * if a ``kwd`` argument has an incorrect type.
+ * if an algorithm-specific required ``kwd`` is not provided.
- *(Note that the name of this method is a misnomer, password hashes
- are typically based on irreversible cryptographic operations,
- see* :issue:`21` *).*
+ *(Note that the name of this method is a misnomer: nearly all
+ password hashes use an irreversible cryptographic digest,
+ rather than a reversible cipher. see* :issue:`21` *).*
.. versionchanged:: 1.6
Hashes now raise :exc:`TypeError` if a required keyword is missing,
@@ -237,23 +236,23 @@ and hash comparison.
``True`` if the secret matches, otherwise ``False``.
:raises TypeError:
- * if either *secret* or *hash* is not a unicode or bytes instance.
- * if the hash requires additional keywords which are not provided,
- or have the wrong type.
+ * if either ``secret`` or ``hash`` is not a unicode or bytes instance.
+ * if the hash requires additional ``kwds`` which are not provided,
+ * if a ``kwd`` argument has the wrong type.
:raises ValueError:
- * if *hash* does not match this algorithm's format.
- * if the secret contains forbidden characters (see
+ * if ``hash`` does not match this algorithm's format.
+ * if the ``secret`` contains forbidden characters (see
:meth:`~PasswordHash.encrypt`).
* if a configuration/salt string generated by :meth:`~PasswordHash.genconfig`
- is passed in as the value for *hash* (these strings look
+ is passed in as the value for ``hash`` (these strings look
similar to a full hash, but typically lack the digest portion
needed to verify a password).
.. versionchanged:: 1.6
- This function now raises :exc:`ValueError` if a ``None`` or config string is provided
- instead of a proper hash; previous releases were inconsistent
- in their handling of these cases.
+ This function now raises :exc:`ValueError` if ``None`` or a config string is provided
+ instead of a properly-formed hash; previous releases were inconsistent
+ in their handling of these two border cases.
.. _hash-unicode-behavior:
@@ -398,14 +397,16 @@ There is currently one additional support method, :meth:`~PasswordHash.identify`
.. note::
- Hashes which lack a reliable method of identification may incorrectly
- identify each-other's hashes (e.g. both :class:`~passlib.hash.lmhash`
- and :class:`~passlib.hash.nthash` hash consist 32 hexidecimal characters).
+ A small number of the hashes supported by Passlib lack a reliable
+ method of identification (e.g. :class:`~passlib.hash.lmhash`
+ and :class:`~passlib.hash.nthash` both consist of 32 hexidecimal characters,
+ with no distinguishing features). For such hashes, this method
+ may return false positives.
.. seealso::
If you are considering using this method to select from multiple
- algorithms in order to verify a password, you may be better served
+ algorithms (e.g. in order to verify a password), you will be better served
by the :ref:`CryptContext <context-overview>` class.
..
@@ -518,17 +519,19 @@ the hashes in passlib:
.. _relaxed-keyword:
``relaxed``
- By default, passing :meth:`~PasswordHash.encrypt` an invalid
- value will result in a :exc:`ValueError`. However, if ``relaxed=True``,
- Passlib will attempt to correct the error, and if successful,
+ By default, passing an invalid value to :meth:`~PasswordHash.encrypt`
+ will result in a :exc:`ValueError`. However, if ``relaxed=True``
+ then Passlib will attempt to correct the error and (if successful)
issue a :exc:`~passlib.exc.PasslibHashWarning` instead.
This warning may then be filtered if desired.
- Correctable errors include (but aren not limited to): ``rounds``
+ Correctable errors include (but are not limited to): ``rounds``
and ``salt_size`` values that are too low or too high, ``salt``
- strings that are too large, etc.
+ strings that are too large.
This option is supported by most of the hashes in Passlib.
+ .. versionadded:: 1.6
+
.. attribute:: PasswordHash.context_kwds
Tuple listing the keywords supported by :meth:`~PasswordHash.encrypt`,
@@ -585,7 +588,7 @@ and the following attributes should be defined:
.. attribute:: PasswordHash.min_salt_size
The minimum number of bytes/characters required for the salt.
- Must be an integer between 0 or :attr:`~PasswordHash.max_salt_size`.
+ Must be an integer between 0 and :attr:`~PasswordHash.max_salt_size`.
.. attribute:: PasswordHash.default_salt_size
@@ -619,8 +622,8 @@ and the following attributes should be defined:
Rounds Information
------------------
-For schemes which support a variable number of iterations to adjust their time-cost,
-``"rounds"`` should be listed in :attr:`~PasswordHash.setting_kwds`,
+For schemes which support a variable time-cost parameter,
+``"rounds"`` should be listed in their :attr:`~PasswordHash.setting_kwds`,
and the following attributes should be defined:
.. attribute:: PasswordHash.max_rounds
@@ -717,23 +720,25 @@ and the following attributes should be defined:
Choosing the right rounds value
===============================
-Passlib's default rounds settings attempt to be secure enough for
+For hash algorithms which support a variable time-cost,
+Passlib's default ``rounds`` choices attempt to be secure enough for
the average [#avgsys]_ system. But the "right" value for a given hash
is dependant on the server, it's cpu, it's expected load, and it's users.
Since larger values mean increased work for an attacker,
-**the right** ``rounds`` **value for a given server should be the largest
-possible value that doesn't cause intolerable delay for your users**.
+*the right* ``rounds`` *value for a given hash & server should be the largest
+possible value that doesn't cause intolerable delay for your users*.
+
For most public facing services, you can generally have signin
take upwards of 250ms - 400ms before users start getting annoyed.
For superuser accounts, it should take as much time as the admin can stand
(usually ~4x more delay than a regular account).
-Passlib's ``default_rounds`` values are retuned every major release (at a minimum)
+Passlib's ``default_rounds`` values are retuned periodically
by taking a rough estimate of what an "average" system is capable of,
-and setting all the ``default_rounds`` values to take ~300ms on such a system.
+and then setting all :samp:`{hash}.default_rounds` values to take ~300ms on such a system.
However, some older algorithms (e.g. :class:`~passlib.hash.bsdi_crypt`) are weak enough that
a tradeoff must be made, choosing "secure but intolerably slow" over "fast but unacceptably insecure".
-For this reason, it is strongly recommended to not use a value lower than Passlib's default.
+For this reason, it is strongly recommended to not use a value much lower than Passlib's default.
.. [#avgsys] For Passlib 1.6, all hashes were retuned to take ~250ms on a
system with a 3 ghz 64 bit CPU.
diff --git a/passlib/context.py b/passlib/context.py
index df338e2..89bfd5f 100644
--- a/passlib/context.py
+++ b/passlib/context.py
@@ -1767,7 +1767,7 @@ class CryptContext(object):
def load_path(self, path, update=False, section="passlib", encoding="utf-8"):
"""Load new configuration into CryptContext from a local file.
- This function is a wrapper for :meth:`load`, which
+ This function is a wrapper for :meth:`load` which
loads a configuration string from the local file *path*,
instead of an in-memory source. It's behavior and options
are otherwise identical to :meth:`!load` when provided with
diff --git a/passlib/utils/__init__.py b/passlib/utils/__init__.py
index aff0642..adabf0b 100644
--- a/passlib/utils/__init__.py
+++ b/passlib/utils/__init__.py
@@ -310,6 +310,7 @@ def consteq(left, right):
result = 1
# run constant-time string comparision
+ # TODO: use izip instead (but first verify it's faster than zip for this case)
if is_py3_bytes:
for l,r in zip(tmp, right):
result |= l ^ r
@@ -330,14 +331,16 @@ def splitcomma(source, sep=","):
return [ elem.strip() for elem in source.split(sep) ]
def saslprep(source, param="value"):
- """Normalizes unicode string using SASLPrep stringprep profile.
+ """Normalizes unicode strings using SASLPrep stringprep profile.
The SASLPrep profile is defined in :rfc:`4013`.
It provides a uniform scheme for normalizing unicode usernames
and passwords before performing byte-value sensitive operations
such as hashing. Among other things, it normalizes diacritic
representations, removes non-printing characters, and forbids
- invalid characters such as ``\\n``.
+ invalid characters such as ``\\n``. Properly internationalized
+ applications should run user passwords through this function
+ before hashing.
:arg source:
unicode string to normalize & validate
@@ -358,6 +361,8 @@ def saslprep(source, param="value"):
This function is not available under Jython,
as the Jython stdlib is missing the :mod:`!stringprep` module
(`Jython issue 1758320 <http://bugs.jython.org/issue1758320>`_).
+
+ .. versionadded:: 1.6
"""
# saslprep - http://tools.ietf.org/html/rfc4013
# stringprep - http://tools.ietf.org/html/rfc3454
@@ -414,7 +419,7 @@ def saslprep(source, param="value"):
in_table_c8 = stringprep.in_table_c8
in_table_c9 = stringprep.in_table_c9
for c in data:
- # check for this mapping stage should have removed
+ # check for chars mapping stage should have removed
assert not in_table_b1(c), "failed to strip B.1 in mapping stage"
assert not in_table_c12(c), "failed to replace C.1.2 in mapping stage"
diff --git a/setup.py b/setup.py
index 408a62a..d29e782 100644
--- a/setup.py
+++ b/setup.py
@@ -85,12 +85,12 @@ Passlib is a password hashing library for Python 2 & 3, which provides
cross-platform implementations of over 30 password hashing algorithms, as well
as a framework for managing existing password hashes. It's designed to be useful
for a wide range of tasks, from verifying a hash found in /etc/shadow, to
-providing full-strength password hashing for multi-user application.
+providing full-strength password hashing for multi-user applications.
-* See the `online documentation <http://packages.python.org/passlib>`_
+* See the `documentation <http://packages.python.org/passlib>`_
for details, installation instructions, and examples.
-* See the `Passlib homepage <http://passlib.googlecode.com>`_
+* See the `homepage <http://passlib.googlecode.com>`_
for the latest news, more information, and additional downloads.
* See the `changelog <http://packages.python.org/passlib/history.html>`_
@@ -158,7 +158,8 @@ setup(
url = "http://passlib.googlecode.com",
download_url =
- ("http://passlib.googlecode.com/files/passlib-" + VERSION + ".tar.gz")
+# ("http://passlib.googlecode.com/files/passlib-" + VERSION + ".tar.gz")
+ ("http://pypi.python.org/packages/source/p/passlib/passlib-" + VERSION + ".tar.gz")
if is_release else None,
description = SUMMARY,