|
|
8db7d0 |
diff --git a/Doc/library/crypt.rst b/Doc/library/crypt.rst
|
|
|
8db7d0 |
index 91464ef..6ee64d6 100644
|
|
|
8db7d0 |
--- a/Doc/library/crypt.rst
|
|
|
8db7d0 |
+++ b/Doc/library/crypt.rst
|
|
|
8db7d0 |
@@ -16,9 +16,9 @@
|
|
|
8db7d0 |
|
|
|
8db7d0 |
This module implements an interface to the :manpage:`crypt(3)` routine, which is
|
|
|
8db7d0 |
a one-way hash function based upon a modified DES algorithm; see the Unix man
|
|
|
8db7d0 |
-page for further details. Possible uses include allowing Python scripts to
|
|
|
8db7d0 |
-accept typed passwords from the user, or attempting to crack Unix passwords with
|
|
|
8db7d0 |
-a dictionary.
|
|
|
8db7d0 |
+page for further details. Possible uses include storing hashed passwords
|
|
|
8db7d0 |
+so you can check passwords without storing the actual password, or attempting
|
|
|
8db7d0 |
+to crack Unix passwords with a dictionary.
|
|
|
8db7d0 |
|
|
|
8db7d0 |
.. index:: single: crypt(3)
|
|
|
8db7d0 |
|
|
|
8db7d0 |
@@ -27,15 +27,81 @@ the :manpage:`crypt(3)` routine in the running system. Therefore, any
|
|
|
8db7d0 |
extensions available on the current implementation will also be available on
|
|
|
8db7d0 |
this module.
|
|
|
8db7d0 |
|
|
|
8db7d0 |
+Hashing Methods
|
|
|
8db7d0 |
+---------------
|
|
|
8db7d0 |
|
|
|
8db7d0 |
-.. function:: crypt(word, salt)
|
|
|
8db7d0 |
+The :mod:`crypt` module defines the list of hashing methods (not all methods
|
|
|
8db7d0 |
+are available on all platforms):
|
|
|
8db7d0 |
+
|
|
|
8db7d0 |
+.. data:: METHOD_SHA512
|
|
|
8db7d0 |
+
|
|
|
8db7d0 |
+ A Modular Crypt Format method with 16 character salt and 86 character
|
|
|
8db7d0 |
+ hash. This is the strongest method.
|
|
|
8db7d0 |
+
|
|
|
8db7d0 |
+.. versionadded:: 3.3
|
|
|
8db7d0 |
+
|
|
|
8db7d0 |
+.. data:: METHOD_SHA256
|
|
|
8db7d0 |
+
|
|
|
8db7d0 |
+ Another Modular Crypt Format method with 16 character salt and 43
|
|
|
8db7d0 |
+ character hash.
|
|
|
8db7d0 |
+
|
|
|
8db7d0 |
+.. versionadded:: 3.3
|
|
|
8db7d0 |
+
|
|
|
8db7d0 |
+.. data:: METHOD_MD5
|
|
|
8db7d0 |
+
|
|
|
8db7d0 |
+ Another Modular Crypt Format method with 8 character salt and 22
|
|
|
8db7d0 |
+ character hash.
|
|
|
8db7d0 |
+
|
|
|
8db7d0 |
+.. versionadded:: 3.3
|
|
|
8db7d0 |
+
|
|
|
8db7d0 |
+.. data:: METHOD_CRYPT
|
|
|
8db7d0 |
+
|
|
|
8db7d0 |
+ The traditional method with a 2 character salt and 13 characters of
|
|
|
8db7d0 |
+ hash. This is the weakest method.
|
|
|
8db7d0 |
+
|
|
|
8db7d0 |
+.. versionadded:: 3.3
|
|
|
8db7d0 |
+
|
|
|
8db7d0 |
+
|
|
|
8db7d0 |
+Module Attributes
|
|
|
8db7d0 |
+-----------------
|
|
|
8db7d0 |
+
|
|
|
8db7d0 |
+
|
|
|
8db7d0 |
+.. attribute:: methods
|
|
|
8db7d0 |
+
|
|
|
8db7d0 |
+ A list of available password hashing algorithms, as
|
|
|
8db7d0 |
+ ``crypt.METHOD_*`` objects. This list is sorted from strongest to
|
|
|
8db7d0 |
+ weakest, and is guaranteed to have at least ``crypt.METHOD_CRYPT``.
|
|
|
8db7d0 |
+
|
|
|
8db7d0 |
+.. versionadded:: 3.3
|
|
|
8db7d0 |
+
|
|
|
8db7d0 |
+
|
|
|
8db7d0 |
+Module Functions
|
|
|
8db7d0 |
+----------------
|
|
|
8db7d0 |
+
|
|
|
8db7d0 |
+The :mod:`crypt` module defines the following functions:
|
|
|
8db7d0 |
+
|
|
|
8db7d0 |
+.. function:: crypt(word, salt=None)
|
|
|
8db7d0 |
|
|
|
8db7d0 |
*word* will usually be a user's password as typed at a prompt or in a graphical
|
|
|
8db7d0 |
- interface. *salt* is usually a random two-character string which will be used
|
|
|
8db7d0 |
- to perturb the DES algorithm in one of 4096 ways. The characters in *salt* must
|
|
|
8db7d0 |
- be in the set ``[./a-zA-Z0-9]``. Returns the hashed password as a string, which
|
|
|
8db7d0 |
- will be composed of characters from the same alphabet as the salt (the first two
|
|
|
8db7d0 |
- characters represent the salt itself).
|
|
|
8db7d0 |
+ interface. The optional *salt* is either a string as returned from
|
|
|
8db7d0 |
+ :func:`mksalt`, one of the ``crypt.METHOD_*`` values (though not all
|
|
|
8db7d0 |
+ may be available on all platforms), or a full encrypted password
|
|
|
8db7d0 |
+ including salt, as returned by this function. If *salt* is not
|
|
|
8db7d0 |
+ provided, the strongest method will be used (as returned by
|
|
|
8db7d0 |
+ :func:`methods`.
|
|
|
8db7d0 |
+
|
|
|
8db7d0 |
+ Checking a password is usually done by passing the plain-text password
|
|
|
8db7d0 |
+ as *word* and the full results of a previous :func:`crypt` call,
|
|
|
8db7d0 |
+ which should be the same as the results of this call.
|
|
|
8db7d0 |
+
|
|
|
8db7d0 |
+ *salt* (either a random 2 or 16 character string, possibly prefixed with
|
|
|
8db7d0 |
+ ``$digit$`` to indicate the method) which will be used to perturb the
|
|
|
8db7d0 |
+ encryption algorithm. The characters in *salt* must be in the set
|
|
|
8db7d0 |
+ ``[./a-zA-Z0-9]``, with the exception of Modular Crypt Format which
|
|
|
8db7d0 |
+ prefixes a ``$digit$``.
|
|
|
8db7d0 |
+
|
|
|
8db7d0 |
+ Returns the hashed password as a string, which will be composed of
|
|
|
8db7d0 |
+ characters from the same alphabet as the salt.
|
|
|
8db7d0 |
|
|
|
8db7d0 |
.. index:: single: crypt(3)
|
|
|
8db7d0 |
|
|
|
8db7d0 |
@@ -43,6 +109,27 @@ this module.
|
|
|
8db7d0 |
different sizes in the *salt*, it is recommended to use the full crypted
|
|
|
8db7d0 |
password as salt when checking for a password.
|
|
|
8db7d0 |
|
|
|
8db7d0 |
+.. versionchanged:: 3.3
|
|
|
8db7d0 |
+ Before version 3.3, *salt* must be specified as a string and cannot
|
|
|
8db7d0 |
+ accept ``crypt.METHOD_*`` values (which don't exist anyway).
|
|
|
8db7d0 |
+
|
|
|
8db7d0 |
+
|
|
|
8db7d0 |
+.. function:: mksalt(method=None)
|
|
|
8db7d0 |
+
|
|
|
8db7d0 |
+ Return a randomly generated salt of the specified method. If no
|
|
|
8db7d0 |
+ *method* is given, the strongest method available as returned by
|
|
|
8db7d0 |
+ :func:`methods` is used.
|
|
|
8db7d0 |
+
|
|
|
8db7d0 |
+ The return value is a string either of 2 characters in length for
|
|
|
8db7d0 |
+ ``crypt.METHOD_CRYPT``, or 19 characters starting with ``$digit$`` and
|
|
|
8db7d0 |
+ 16 random characters from the set ``[./a-zA-Z0-9]``, suitable for
|
|
|
8db7d0 |
+ passing as the *salt* argument to :func:`crypt`.
|
|
|
8db7d0 |
+
|
|
|
8db7d0 |
+.. versionadded:: 3.3
|
|
|
8db7d0 |
+
|
|
|
8db7d0 |
+Examples
|
|
|
8db7d0 |
+--------
|
|
|
8db7d0 |
+
|
|
|
8db7d0 |
A simple example illustrating typical use::
|
|
|
8db7d0 |
|
|
|
8db7d0 |
import crypt, getpass, pwd
|
|
|
8db7d0 |
@@ -59,3 +146,11 @@ A simple example illustrating typical use::
|
|
|
8db7d0 |
else:
|
|
|
8db7d0 |
return 1
|
|
|
8db7d0 |
|
|
|
8db7d0 |
+To generate a hash of a password using the strongest available method and
|
|
|
8db7d0 |
+check it against the original::
|
|
|
8db7d0 |
+
|
|
|
8db7d0 |
+ import crypt
|
|
|
8db7d0 |
+
|
|
|
8db7d0 |
+ hashed = crypt.crypt(plaintext)
|
|
|
8db7d0 |
+ if hashed != crypt.crypt(plaintext, hashed):
|
|
|
8db7d0 |
+ raise "Hashed version doesn't validate against original"
|
|
|
8db7d0 |
diff --git a/Lib/crypt.py b/Lib/crypt.py
|
|
|
8db7d0 |
new file mode 100644
|
|
|
8db7d0 |
index 0000000..bf0a416
|
|
|
8db7d0 |
--- /dev/null
|
|
|
8db7d0 |
+++ b/Lib/crypt.py
|
|
|
8db7d0 |
@@ -0,0 +1,71 @@
|
|
|
8db7d0 |
+"""Wrapper to the POSIX crypt library call and associated functionality.
|
|
|
8db7d0 |
+
|
|
|
8db7d0 |
+Note that the ``methods`` and ``METHOD_*`` attributes are non-standard
|
|
|
8db7d0 |
+extensions to Python 2.7, backported from 3.3"""
|
|
|
8db7d0 |
+
|
|
|
8db7d0 |
+import _crypt
|
|
|
8db7d0 |
+import string as _string
|
|
|
8db7d0 |
+from random import SystemRandom as _SystemRandom
|
|
|
8db7d0 |
+from collections import namedtuple as _namedtuple
|
|
|
8db7d0 |
+
|
|
|
8db7d0 |
+
|
|
|
8db7d0 |
+_saltchars = _string.ascii_letters + _string.digits + './'
|
|
|
8db7d0 |
+_sr = _SystemRandom()
|
|
|
8db7d0 |
+
|
|
|
8db7d0 |
+
|
|
|
8db7d0 |
+class _Method(_namedtuple('_Method', 'name ident salt_chars total_size')):
|
|
|
8db7d0 |
+
|
|
|
8db7d0 |
+ """Class representing a salt method per the Modular Crypt Format or the
|
|
|
8db7d0 |
+ legacy 2-character crypt method."""
|
|
|
8db7d0 |
+
|
|
|
8db7d0 |
+ def __repr__(self):
|
|
|
8db7d0 |
+ return '<crypt.METHOD_%s>' % self.name
|
|
|
8db7d0 |
+
|
|
|
8db7d0 |
+
|
|
|
8db7d0 |
+def mksalt(method=None):
|
|
|
8db7d0 |
+ """Generate a salt for the specified method.
|
|
|
8db7d0 |
+
|
|
|
8db7d0 |
+ If not specified, the strongest available method will be used.
|
|
|
8db7d0 |
+
|
|
|
8db7d0 |
+ This is a non-standard extension to Python 2.7, backported from 3.3
|
|
|
8db7d0 |
+ """
|
|
|
8db7d0 |
+ if method is None:
|
|
|
8db7d0 |
+ method = methods[0]
|
|
|
8db7d0 |
+ s = '$%s$' % method.ident if method.ident else ''
|
|
|
8db7d0 |
+ s += ''.join(_sr.sample(_saltchars, method.salt_chars))
|
|
|
8db7d0 |
+ return s
|
|
|
8db7d0 |
+
|
|
|
8db7d0 |
+
|
|
|
8db7d0 |
+def crypt(word, salt=None):
|
|
|
8db7d0 |
+ """Return a string representing the one-way hash of a password, with a salt
|
|
|
8db7d0 |
+ prepended.
|
|
|
8db7d0 |
+
|
|
|
8db7d0 |
+ If ``salt`` is not specified or is ``None``, the strongest
|
|
|
8db7d0 |
+ available method will be selected and a salt generated. Otherwise,
|
|
|
8db7d0 |
+ ``salt`` may be one of the ``crypt.METHOD_*`` values, or a string as
|
|
|
8db7d0 |
+ returned by ``crypt.mksalt()``.
|
|
|
8db7d0 |
+
|
|
|
8db7d0 |
+ Note that these are non-standard extensions to Python 2.7's crypt.crypt()
|
|
|
8db7d0 |
+ entrypoint, backported from 3.3: the standard Python 2.7 crypt.crypt()
|
|
|
8db7d0 |
+ entrypoint requires two strings as the parameters, and does not support
|
|
|
8db7d0 |
+ keyword arguments.
|
|
|
8db7d0 |
+ """
|
|
|
8db7d0 |
+ if salt is None or isinstance(salt, _Method):
|
|
|
8db7d0 |
+ salt = mksalt(salt)
|
|
|
8db7d0 |
+ return _crypt.crypt(word, salt)
|
|
|
8db7d0 |
+
|
|
|
8db7d0 |
+
|
|
|
8db7d0 |
+# available salting/crypto methods
|
|
|
8db7d0 |
+METHOD_CRYPT = _Method('CRYPT', None, 2, 13)
|
|
|
8db7d0 |
+METHOD_MD5 = _Method('MD5', '1', 8, 34)
|
|
|
8db7d0 |
+METHOD_SHA256 = _Method('SHA256', '5', 16, 63)
|
|
|
8db7d0 |
+METHOD_SHA512 = _Method('SHA512', '6', 16, 106)
|
|
|
8db7d0 |
+
|
|
|
8db7d0 |
+methods = []
|
|
|
8db7d0 |
+for _method in (METHOD_SHA512, METHOD_SHA256, METHOD_MD5):
|
|
|
8db7d0 |
+ _result = crypt('', _method)
|
|
|
8db7d0 |
+ if _result and len(_result) == _method.total_size:
|
|
|
8db7d0 |
+ methods.append(_method)
|
|
|
8db7d0 |
+methods.append(METHOD_CRYPT)
|
|
|
8db7d0 |
+del _result, _method
|
|
|
8db7d0 |
+
|
|
|
8db7d0 |
diff --git a/Lib/test/test_crypt.py b/Lib/test/test_crypt.py
|
|
|
8db7d0 |
index 7cd9c71..b061a55 100644
|
|
|
8db7d0 |
--- a/Lib/test/test_crypt.py
|
|
|
8db7d0 |
+++ b/Lib/test/test_crypt.py
|
|
|
8db7d0 |
@@ -16,6 +16,25 @@ class CryptTestCase(unittest.TestCase):
|
|
|
8db7d0 |
self.assertEqual(cr2, cr)
|
|
|
8db7d0 |
|
|
|
8db7d0 |
|
|
|
8db7d0 |
+ def test_salt(self):
|
|
|
8db7d0 |
+ self.assertEqual(len(crypt._saltchars), 64)
|
|
|
8db7d0 |
+ for method in crypt.methods:
|
|
|
8db7d0 |
+ salt = crypt.mksalt(method)
|
|
|
8db7d0 |
+ self.assertEqual(len(salt),
|
|
|
8db7d0 |
+ method.salt_chars + (3 if method.ident else 0))
|
|
|
8db7d0 |
+
|
|
|
8db7d0 |
+ def test_saltedcrypt(self):
|
|
|
8db7d0 |
+ for method in crypt.methods:
|
|
|
8db7d0 |
+ pw = crypt.crypt('assword', method)
|
|
|
8db7d0 |
+ self.assertEqual(len(pw), method.total_size)
|
|
|
8db7d0 |
+ pw = crypt.crypt('assword', crypt.mksalt(method))
|
|
|
8db7d0 |
+ self.assertEqual(len(pw), method.total_size)
|
|
|
8db7d0 |
+
|
|
|
8db7d0 |
+ def test_methods(self):
|
|
|
8db7d0 |
+ # Gurantee that METHOD_CRYPT is the last method in crypt.methods.
|
|
|
8db7d0 |
+ self.assertTrue(len(crypt.methods) >= 1)
|
|
|
8db7d0 |
+ self.assertEqual(crypt.METHOD_CRYPT, crypt.methods[-1])
|
|
|
8db7d0 |
+
|
|
|
8db7d0 |
def test_main():
|
|
|
8db7d0 |
test_support.run_unittest(CryptTestCase)
|
|
|
8db7d0 |
|
|
|
8db7d0 |
diff --git a/Modules/Setup.dist b/Modules/Setup.dist
|
|
|
8db7d0 |
index 2712f06..3ea4f0c 100644
|
|
|
8db7d0 |
--- a/Modules/Setup.dist
|
|
|
8db7d0 |
+++ b/Modules/Setup.dist
|
|
|
8db7d0 |
@@ -225,7 +225,7 @@ _ssl _ssl.c \
|
|
|
8db7d0 |
#
|
|
|
8db7d0 |
# First, look at Setup.config; configure may have set this for you.
|
|
|
8db7d0 |
|
|
|
8db7d0 |
-crypt cryptmodule.c # -lcrypt # crypt(3); needs -lcrypt on some systems
|
|
|
8db7d0 |
+_crypt _cryptmodule.c -lcrypt # crypt(3); needs -lcrypt on some systems
|
|
|
8db7d0 |
|
|
|
8db7d0 |
|
|
|
8db7d0 |
# Some more UNIX dependent modules -- off by default, since these
|
|
|
8db7d0 |
diff --git a/Modules/cryptmodule.c b/Modules/cryptmodule.c
|
|
|
8db7d0 |
index 76de54f..7c69ca6 100644
|
|
|
8db7d0 |
--- a/Modules/cryptmodule.c
|
|
|
8db7d0 |
+++ b/Modules/cryptmodule.c
|
|
|
8db7d0 |
@@ -43,7 +43,7 @@ static PyMethodDef crypt_methods[] = {
|
|
|
8db7d0 |
};
|
|
|
8db7d0 |
|
|
|
8db7d0 |
PyMODINIT_FUNC
|
|
|
8db7d0 |
-initcrypt(void)
|
|
|
8db7d0 |
+init_crypt(void)
|
|
|
8db7d0 |
{
|
|
|
8db7d0 |
- Py_InitModule("crypt", crypt_methods);
|
|
|
8db7d0 |
+ Py_InitModule("_crypt", crypt_methods);
|
|
|
8db7d0 |
}
|
|
|
8db7d0 |
diff --git a/setup.py b/setup.py
|
|
|
8db7d0 |
index b787487..c60ac35 100644
|
|
|
8db7d0 |
--- a/setup.py
|
|
|
8db7d0 |
+++ b/setup.py
|
|
|
8db7d0 |
@@ -798,7 +798,7 @@ class PyBuildExt(build_ext):
|
|
|
8db7d0 |
libs = ['crypt']
|
|
|
8db7d0 |
else:
|
|
|
8db7d0 |
libs = []
|
|
|
8db7d0 |
- exts.append( Extension('crypt', ['cryptmodule.c'], libraries=libs) )
|
|
|
8db7d0 |
+ exts.append( Extension('_crypt', ['_cryptmodule.c'], libraries=libs) )
|
|
|
8db7d0 |
|
|
|
8db7d0 |
# CSV files
|
|
|
8db7d0 |
exts.append( Extension('_csv', ['_csv.c']) )
|