Index: setup.py =================================================================== --- setup.py (revision 88042) +++ setup.py (working copy) @@ -636,7 +636,7 @@ libs = ['crypt'] else: libs = [] - exts.append( Extension('crypt', ['cryptmodule.c'], libraries=libs) ) + exts.append( Extension('_crypt', ['_cryptmodule.c'], libraries=libs) ) # CSV files exts.append( Extension('_csv', ['_csv.c']) ) Index: Doc/library/crypt.rst =================================================================== --- Doc/library/crypt.rst (revision 88042) +++ Doc/library/crypt.rst (working copy) @@ -15,9 +15,9 @@ This module implements an interface to the :manpage:`crypt(3)` routine, which is a one-way hash function based upon a modified DES algorithm; see the Unix man -page for further details. Possible uses include allowing Python scripts to -accept typed passwords from the user, or attempting to crack Unix passwords with -a dictionary. +page for further details. Possible uses include storing hashed passwords +so you can check passwords without storing the actual password, or attempting +to crack Unix passwords with a dictionary. .. index:: single: crypt(3) @@ -26,22 +26,102 @@ extensions available on the current implementation will also be available on this module. +Hashing Methods +--------------- -.. function:: crypt(word, salt) +The :mod:`crypt` module defines the list of hashing methods (not all methods +are available on all platforms): +.. data:: METHOD_SHA512 + + A Modular Crypt Format method with 16 character salt and 86 character + hash. This is the strongest method. + +.. versionadded:: 3.3 + +.. data:: METHOD_SHA256 + + Another Modular Crypt Format method with 16 character salt and 43 + character hash. + +.. versionadded:: 3.3 + +.. data:: METHOD_MD5 + + Another Modular Crypt Format method with 8 character salt and 22 + character hash. + +.. versionadded:: 3.3 + +.. data:: METHOD_CRYPT + + The traditional method with a 2 character salt and 13 characters of + hash. This is the weakest method. + +.. versionadded:: 3.3 + +Module Functions +---------------- + +The :mod:`crypt` module defines the following functions: + +.. function:: crypt(word, salt=None) + *word* will usually be a user's password as typed at a prompt or in a graphical - interface. *salt* is usually a random two-character string which will be used - to perturb the DES algorithm in one of 4096 ways. The characters in *salt* must - be in the set ``[./a-zA-Z0-9]``. Returns the hashed password as a string, which - will be composed of characters from the same alphabet as the salt (the first two - characters represent the salt itself). + interface. The optional *salt* is either a string as returned from + :func:`mksalt`, one of the ``crypt.METHOD_*`` values (though not all + may be available on all platforms), or a full encrypted password + including salt, as returned by this function. If *salt* is not + provided, the strongest method will be used (as returned by + :func:`methods`. + Checking a password is usually done by passing the plain-text password + as *word* and the full results of a previous :func:`crypt` call, + which should be the same as the results of this call. + + *salt* (either a random 2 or 16 character string, possibly prefixed with + ``$digit$`` to indicate the method) which will be used to perturb the + encryption algorithm. The characters in *salt* must be in the set + ``[./a-zA-Z0-9]``, with the exception of Modular Crypt Format which + prefixes a ``$digit$``. + + Returns the hashed password as a string, which will be composed of + characters from the same alphabet as the salt. + .. index:: single: crypt(3) Since a few :manpage:`crypt(3)` extensions allow different values, with different sizes in the *salt*, it is recommended to use the full crypted password as salt when checking for a password. +.. versionchanged:: 3.3 + Before version 3.3, *salt* must be specified as a string and cannot + accept ``crypt.METHOD_*`` values (which don't exist anyway). + +.. function:: methods() + + Return a list of available password hashing algorithms, as + ``crypt.METHOD_*`` objects. This list is sorted from strongest to + weakest, and is guaranteed to have at least ``crypt.METHOD_CRYPT``. + +.. versionadded:: 3.3 + +.. function:: mksalt(method=None) + + Return a randomly generated salt of the specified method. If no + *method* is given, the strongest method available as returned by + :func:`methods` is used. + + The return value is a string either of 2 characters in length for + ``crypt.METHOD_CRYPT``, or 19 characters starting with ``$digit$`` and + 16 random characters from the set ``[./a-zA-Z0-9]``, suitable for + passing as the *salt* argument to :func:`crypt`. + +.. versionadded:: 3.3 + +Examples +-------- + A simple example illustrating typical use:: import crypt, getpass, pwd @@ -57,3 +137,11 @@ else: return 1 +To generate a hash of a password using the strongest available method and +check it against the original:: + + import crypt + + hashed = crypt.crypt(plaintext) + if hashed != crypt.crypt(plaintext, hashed): + raise "Hashed version doesn't validate against original" Index: Lib/test/test_crypt.py =================================================================== --- Lib/test/test_crypt.py (revision 88042) +++ Lib/test/test_crypt.py (working copy) @@ -10,6 +10,23 @@ if support.verbose: print('Test encryption: ', c) + def test_salt(self): + self.assertEqual(len(crypt.saltchars), 64) + for method in crypt.methods(): + salt = crypt.mksalt(method) + self.assertEqual(len(salt), + method.salt_chars + (3 if method.ident else 0)) + + def test_saltedcrypt(self): + for method in crypt.methods(): + pw = crypt.crypt('assword', method) + self.assertEqual(len(pw), method.total_size) + pw = crypt.crypt('assword', crypt.mksalt(method)) + self.assertEqual(len(pw), method.total_size) + + def test_methods(self): + self.assertTrue(len(crypt.methods()) > 1) + def test_main(): support.run_unittest(CryptTestCase) Index: Modules/_cryptmodule.c =================================================================== --- Modules/_cryptmodule.c (revision 88042) +++ Modules/_cryptmodule.c (working copy) @@ -45,7 +45,7 @@ static struct PyModuleDef cryptmodule = { PyModuleDef_HEAD_INIT, - "crypt", + "_crypt", NULL, -1, crypt_methods, @@ -56,7 +56,7 @@ }; PyMODINIT_FUNC -PyInit_crypt(void) +PyInit__crypt(void) { return PyModule_Create(&cryptmodule); } Index: Modules/Setup.dist =================================================================== --- Modules/Setup.dist (revision 88042) +++ Modules/Setup.dist (working copy) @@ -207,7 +207,7 @@ # # First, look at Setup.config; configure may have set this for you. -#crypt cryptmodule.c # -lcrypt # crypt(3); needs -lcrypt on some systems +#_crypt _cryptmodule.c # -lcrypt # crypt(3); needs -lcrypt on some systems # Some more UNIX dependent modules -- off by default, since these Index: Modules/cryptmodule.c =================================================================== --- Modules/cryptmodule.c (revision 88042) +++ Modules/cryptmodule.c (working copy) @@ -1,62 +0,0 @@ -/* cryptmodule.c - by Steve Majewski - */ - -#include "Python.h" - -#include - -#ifdef __VMS -#include -#endif - -/* Module crypt */ - - -static PyObject *crypt_crypt(PyObject *self, PyObject *args) -{ - char *word, *salt; -#ifndef __VMS - extern char * crypt(const char *, const char *); -#endif - - if (!PyArg_ParseTuple(args, "ss:crypt", &word, &salt)) { - return NULL; - } - /* On some platforms (AtheOS) crypt returns NULL for an invalid - salt. Return None in that case. XXX Maybe raise an exception? */ - return Py_BuildValue("s", crypt(word, salt)); - -} - -PyDoc_STRVAR(crypt_crypt__doc__, -"crypt(word, salt) -> string\n\ -word will usually be a user's password. salt is a 2-character string\n\ -which will be used to select one of 4096 variations of DES. The characters\n\ -in salt must be either \".\", \"/\", or an alphanumeric character. Returns\n\ -the hashed password as a string, which will be composed of characters from\n\ -the same alphabet as the salt."); - - -static PyMethodDef crypt_methods[] = { - {"crypt", crypt_crypt, METH_VARARGS, crypt_crypt__doc__}, - {NULL, NULL} /* sentinel */ -}; - - -static struct PyModuleDef cryptmodule = { - PyModuleDef_HEAD_INIT, - "crypt", - NULL, - -1, - crypt_methods, - NULL, - NULL, - NULL, - NULL -}; - -PyMODINIT_FUNC -PyInit_crypt(void) -{ - return PyModule_Create(&cryptmodule); -}