diff options
| author | Eli Collins <elic@assurancetechnologies.com> | 2012-01-10 00:15:48 -0500 |
|---|---|---|
| committer | Eli Collins <elic@assurancetechnologies.com> | 2012-01-10 00:15:48 -0500 |
| commit | b65d2f35fb0d95c1cca55c484c8adc311042a935 (patch) | |
| tree | aa0ee3c721b21343df5cd377e130309d13cb3e10 | |
| parent | 66b1c670d57deff8fc1c0799df5e34febc58a0d0 (diff) | |
| download | passlib-b65d2f35fb0d95c1cca55c484c8adc311042a935.tar.gz | |
added example hashes to scram documentation, other doc tweaks
| -rw-r--r-- | CHANGES | 25 | ||||
| -rw-r--r-- | docs/lib/passlib.hash.scram.rst | 73 | ||||
| -rw-r--r-- | docs/modular_crypt_format.rst | 10 | ||||
| -rw-r--r-- | passlib/handlers/scram.py | 19 |
4 files changed, 73 insertions, 54 deletions
@@ -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") |
