summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEli Collins <elic@assurancetechnologies.com>2012-01-10 00:15:48 -0500
committerEli Collins <elic@assurancetechnologies.com>2012-01-10 00:15:48 -0500
commitb65d2f35fb0d95c1cca55c484c8adc311042a935 (patch)
treeaa0ee3c721b21343df5cd377e130309d13cb3e10
parent66b1c670d57deff8fc1c0799df5e34febc58a0d0 (diff)
downloadpasslib-b65d2f35fb0d95c1cca55c484c8adc311042a935.tar.gz
added example hashes to scram documentation, other doc tweaks
-rw-r--r--CHANGES25
-rw-r--r--docs/lib/passlib.hash.scram.rst73
-rw-r--r--docs/modular_crypt_format.rst10
-rw-r--r--passlib/handlers/scram.py19
4 files changed, 73 insertions, 54 deletions
diff --git a/CHANGES b/CHANGES
index 6f389f6..86f7e06 100644
--- a/CHANGES
+++ b/CHANGES
@@ -6,12 +6,21 @@ Release History
**1.6** (NOT YET RELEASED)
+ Hashes
+
+ * Passlib now offers the :doc:`scram <lib/passlib.hash.scram>` hash,
+ specially designed for storing digest information for verifying
+ a user against the SCRAM protocol (:rfc:`5802`). It can also
+ be used to verify users in the same way as any other password
+ hash in Passlib, though it offers no particular advantages
+ outside of this special case.
+
CryptContext
.. currentmodule:: passlib.context
- * :class:`~CryptContext` now supports a :ref:`passprep` option,
- which runs all passwords through :rfc:`SASLPrep <4013>`
+ * :class:`~CryptContext` now supports a :ref:`passprep <passprep>` option,
+ which runs all passwords through SASLPrep (:rfc:`4013`)
in order to normalize their unicode representation before hashing
[issue 24].
@@ -43,7 +52,9 @@ Release History
Other
* Passlib is now source-compatible with Python 2.5+ and Python 3,
- and no longer requires the use of :cmd:`2to3` to run under Python 3.
+ and no longer requires the use of :command:`2to3` to run under Python 3.
+
+ .. currentmodule:: passlib.hash
.. _consteq-issue:
@@ -59,14 +70,14 @@ Release History
as it requires the attacker to both know the salt,
and be able to generate digests beginning with a specific prefix.
However, while this task should be computationally difficult
- against modern hashes (such as :class:`!sha512_crypt`), this
+ against modern hashes (such as :class:`sha512_crypt`), this
change should pre-emptively protect Passlib in case someone
constructs a such an attack in the future. Furthermore, some of
the legacy hashes supported by Passlib (such as
- :class:`!mysql323`) are already weak enough to be vulnerable.
+ :class:`mysql323`) are already weak enough to be vulnerable.
- * Builtin implementations of :class:`~md5_crypt`,
- :class:`~sha256_crypt`, and :class:`~sha512_crypt` sped up by
+ * Builtin implementations of :class:`md5_crypt`,
+ :class:`sha256_crypt`, and :class:`sha512_crypt` sped up by
about 25% due via additional pre-computation step.
* Restored builtin pure-python BCrypt implementation
diff --git a/docs/lib/passlib.hash.scram.rst b/docs/lib/passlib.hash.scram.rst
index 0a22c93..fff87bf 100644
--- a/docs/lib/passlib.hash.scram.rst
+++ b/docs/lib/passlib.hash.scram.rst
@@ -34,16 +34,16 @@ This class can be used like any other Passlib hash, as follows::
>>> #generate new salt, encrypt password against default list of algorithms
>>> h = scram.encrypt("password")
- >>> h
- '$scram$40000$xxxxxx$sha-1=..............................,\
- sha-256=........................................,\
- sha-512=.........................................'
+ >>> h # (output split over multiple lines for readability)
+ '$scram$6400$.Z/znnNOKWUsBaCU$sha-1=cRseQyJpnuPGn3e6d6u6JdJWk.0,sha-256=5G\
+ cjEbRaUIIci1r6NAMdI9OPZbxl9S5CFR6la9CHXYc,sha-512=.DHbIm82ajXbFR196Y.9Ttbs\
+ gzvGjbMeuWCtKve8TPjRMNoZK9EGyHQ6y0lW9OtWdHZrDZbBUhB9ou./VI2mlw'
>>> #same, but with explict number of rounds
- >>> scram.encrypt("password", rounds=10000)
- '$scram$40000$xxxxxx$sha-1=..............................,\
- sha-256=........................................,\
- sha-512=.........................................'
+ >>> scram.encrypt("password", rounds=8000)
+ '$scram$8000$Y0zp/R/DeO89h/De$sha-1=eE8dq1f1P1hZm21lfzsr3CMbiEA,sha-256=Nf\
+ kaDFMzn/yHr/HTv7KEFZqaONo6psRu5LBBFLEbZ.o,sha-512=XnGG11X.J2VGSG1qTbkR3FVr\
+ 9j5JwsnV5Fd094uuC.GtVDE087m8e7rGoiVEgXnduL48B2fPsUD9grBjURjkiA'
>>> #check if hash is recognized
>>> scram.identify(h)
@@ -63,69 +63,76 @@ for SCRAM-specific actions::
>>> from passlib.hash import scram
>>> # generate new salt, encrypt password against default list of algorithms
- >>> h = scram.encrypt("password")
- >>> h
- '$scram$40000$xxxxxx$sha-1=..............................,\
- sha-256=........................................,\
- sha-512=.........................................'
+ >>> scram.encrypt("password")
+ '$scram$6400$.Z/znnNOKWUsBaCU$sha-1=cRseQyJpnuPGn3e6d6u6JdJWk.0,sha-256=5G\
+ cjEbRaUIIci1r6NAMdI9OPZbxl9S5CFR6la9CHXYc,sha-512=.DHbIm82ajXbFR196Y.9Ttbs\
+ gzvGjbMeuWCtKve8TPjRMNoZK9EGyHQ6y0lW9OtWdHZrDZbBUhB9ou./VI2mlw'
>>> # generate new salt, encrypt password against specific list of algorithms
>>> # and choose explicit number of rounds
>>> h = scram.encrypt("password", rounds=1000, algs="sha-1,sha-256,md5")
>>> h
- '$scram$40000$xxxxxx$sha-1=..............................,\
- sha-256=........................................,\
- md5=.........................................'
+ '$scram$1000$RsgZo7T2/l8rBUBI$md5=iKsH555d3ctn795Za4S7bQ,sha-1=dRcE2AUjALLF\
+ tX5DstdLCXZ9Afw,sha-256=WYE/LF7OntriUUdFXIrYE19OY2yL0N5qsQmdPNFn7JE'
>>> # given a scram hash, retrieve the information SCRAM needs
>>> # to authenticate using a specific mechanism -
>>> # returns salt, rounds, digest
- >>> scram.extact_digest_info("sha-1")
- (b"xxxxxx", 400000, b".................")
+ >>> scram.extact_digest_info(h, "sha-1")
+ ('F\xc8\x19\xa3\xb4\xf6\xfe_+\x05@H',
+ 1000,
+ 'u\x17\x04\xd8\x05#\x00\xb2\xc5\xb5~C\xb2\xd7K\tv}\x01\xfc')
>>> # given a scram hash, return list of digest algs present
>>> scram.extract_digest_algs(h)
- ["sha-1","sha-256","md5"]
+ ["md5", "sha-1", "sha-256"]
- >>> # and a standalone helper that can calculated the SaltedPassword
- >>> # portion of the SCRAM protocol.
- >>> scram.derive_digest("password", b'xxxxx', 40000, 'sha-1')
- b'..............................'
+ >>> # and a standalone helper that can calculate the SaltedPassword
+ >>> # portion of the SCRAM protocol, taking care of SASLPrep as well.
+ >>> scram.derive_digest("password", b'\x01\x02\x03', 1000, "sha-1")
+ b'k\x086vg\xb3\xfciz\xb4\xb4\xe2JRZ\xaet\xe4`\xe7'
Interface
=========
.. autoclass:: scram(algs=None, salt=None, rounds=None, strict=False)
+.. rst-class:: html-toggle
+
Format & Algorithm
==================
-An example scram hash (of the string ``password``) is:
+An example scram hash (of the string ``password``) is::
- ``$6$rounds=40000$JvTuqzqw9bQ8iBl6$SxklIkW4gz00LvuOsKRCfNEllLciOqY/FSAwODHon45YTJEozmy.QAWiyVpuiq7XMTUMWbIWWEuQytdHkigcN/``.
+ $scram$6400$.Z/znnNOKWUsBaCU$sha-1=cRseQyJpnuPGn3e6d6u6JdJWk.0,sha-256=5G
+ cjEbRaUIIci1r6NAMdI9OPZbxl9S5CFR6la9CHXYc,sha-512=.DHbIm82ajXbFR196Y.9Ttb
+ sgzvGjbMeuWCtKve8TPjRMNoZK9EGyHQ6y0lW9OtWdHZrDZbBUhB9ou./VI2mlw'
-An scram hash string has the format :samp:`$scram${rounds}${salt}${alg}={digest}{,...}`, where:
+An scram hash string has the format :samp:`$scram${rounds}${salt}${alg1}={digest1},{alg2}={digest2},{...}`, where:
* ``$scram$`` is the prefix used to identify Passlib scram hashes,
following the :ref:`modular-crypt-format`
-* :samp:`{rounds}` is the decimal number of rounds to use (40000 in the example).
+* :samp:`{rounds}` is the decimal number of rounds to use (6400 in the example).
-* :samp:`{salt}` is a base64 encoded salt string (``JvTuqzqw9bQ8iBl6`` in the example).
+* :samp:`{salt}` is a base64 encoded salt string (``.Z/znnNOKWUsBaCU`` in the example).
* :samp:`{alg}` is a lowercase IANA hash function name, which should
match the digest in the SCRAM mechanism name.
* :samp:`{digest}` is a base64 digest for the specific algorithm.
+ Digests for ``sha-1``, ``sha-256``, and ``sha-512`` are present in the example.
-* There will be one or more `{alg}={digest}` pairs, separated by a comma;
+* There will be one or more :samp:`{alg}={digest}` pairs, separated by a comma;
per the SCRAM specification, the algorithm ``sha-1`` should always be present.
There is also an alternate format :samp:`$scram${rounds}${salt}${alg}{,...}`
which is used to represent a configuration string that doesn't contain
any digests.
-The algorithm used to calculate each digest is
-``pbkdf2(salsprep(password).encode("utf-8"), salt, rounds, -1, alg)``,
-as laid out in the SCRAM specification. All digests
+The algorithm used to calculate each digest is::
+
+ pbkdf2(salsprep(password).encode("utf-8"), salt, rounds, -1, alg)
+
+...as laid out in the SCRAM specification. All digests
verify against the same password, or the hash should be considered malformed.
.. note::
@@ -133,4 +140,4 @@ verify against the same password, or the hash should be considered malformed.
This format is similar in spirit to the LDAP storage format for SCRAM hashes,
defined in :rfc:`5803`, except that it encodes everything into a single
string, and does not have any storage requirements (outside of the ability
- to store 1024+ character ascii strings).
+ to store 512+ character ascii strings).
diff --git a/docs/modular_crypt_format.rst b/docs/modular_crypt_format.rst
index 5db4905..bc96ffe 100644
--- a/docs/modular_crypt_format.rst
+++ b/docs/modular_crypt_format.rst
@@ -149,20 +149,20 @@ Scheme Prefix Primary Use (if
=========================================== =================== ===========================
:class:`~passlib.hash.apr_md5_crypt` ``$apr1$`` Apache htdigest files
:class:`~passlib.hash.phpass` ``$P$``, ``$H$`` PHPass-based applications
-:class:`~passlib.hash.pbkdf2_sha1` ``$pbkdf2$``
-:class:`~passlib.hash.pbkdf2_sha256` ``$pbkdf2-sha256$``
-:class:`~passlib.hash.pbkdf2_sha512` ``$pbkdf2-sha512$``
+:class:`~passlib.hash.pbkdf2_sha1` ``$pbkdf2$`` Passlib-specific
+:class:`~passlib.hash.pbkdf2_sha256` ``$pbkdf2-sha256$`` Passlib-specific
+:class:`~passlib.hash.pbkdf2_sha512` ``$pbkdf2-sha512$`` Passlib-specific
+:class:`~passlib.hash.scram` ``$scram$`` Passlib-specific
:class:`~passlib.hash.cta_pbkdf2_sha1` ``$p5k2$`` [#cta]_
:class:`~passlib.hash.dlitz_pbkdf2_sha1` ``$p5k2$`` [#cta]_
=========================================== =================== ===========================
.. rubric:: Footnotes
-.. [#gae] As of 2011-08-19, Google App Engine's :mod:`crypt` implementation
+.. [#gae] As of 2011-08-19, Google App Engine's :mod:`crypt` implementation
appears to provide hash support matching that of a typical Linux system.
.. [#cta] :class:`!cta_pbkdf2_sha1` and :class:`!dlitz_pbkdf2_sha1` both use
the same identifier. They can be distinguished
by the fact that cta hashes will always end in ``=``, while dlitz
hashes contain no ``=`` at all.
-
diff --git a/passlib/handlers/scram.py b/passlib/handlers/scram.py
index f7b2d47..176f57c 100644
--- a/passlib/handlers/scram.py
+++ b/passlib/handlers/scram.py
@@ -163,14 +163,15 @@ class scram(uh.HasRounds, uh.HasRawSalt, uh.HasRawChecksum, uh.GenericHandler):
Specify list of digest algorithms to use.
By default each scram hash will contain digests for SHA-1,
- SHA-256, and SHA-512. This may either be a list such as
- ``["sha-1", "sha-256"]``, or a comma-separated string such as
- ``"sha-1,sha-256"``. Names are case insensitive, and may
- use hashlib or IANA compatible hash names.
+ SHA-256, and SHA-512. This can be overridden by specify either be a
+ list such as ``["sha-1", "sha-256"]``, or a comma-separated string
+ such as ``"sha-1, sha-256"``. Names are case insensitive, and may
+ use :mod:`!hashlib` or `IANA <http://www.iana.org/assignments/hash-function-text-names>`_
+ hash names.
- This class also provides the following additional class methods
- for manipulating Passlib scram hashes in ways useful for pluging
- into a SCRAM protocol stack:
+ In addition to the standard :ref:`password-hash-api` methods,
+ this class also provides the following methods for manipulating Passlib
+ scram hashes in ways useful for pluging into a SCRAM protocol stack:
.. automethod:: extract_digest_info
.. automethod:: extract_digest_algs
@@ -291,10 +292,10 @@ class scram(uh.HasRounds, uh.HasRawSalt, uh.HasRawChecksum, uh.GenericHandler):
:arg password: password as unicode or utf-8 encoded bytes.
:arg salt: raw salt as bytes.
:arg rounds: number of iterations.
- :arg alg: SCRAM-compatible name of digest (e.g. ``"SHA-1"``).
+ :arg alg: name of digest to use (e.g. ``"SHA-1"``).
:returns:
- raw bytes of SaltedPassword
+ raw bytes of ``SaltedPassword``
"""
if isinstance(password, bytes):
password = password.decode("utf-8")