summaryrefslogtreecommitdiffstats
path: root/Lib
diff options
context:
space:
mode:
Diffstat (limited to 'Lib')
-rw-r--r--Lib/crypt.py84
-rw-r--r--Lib/test/test_crypt.py13
2 files changed, 46 insertions, 51 deletions
diff --git a/Lib/crypt.py b/Lib/crypt.py
index dc62dba..e027d2c 100644
--- a/Lib/crypt.py
+++ b/Lib/crypt.py
@@ -1,61 +1,57 @@
-'''Wrapper to the POSIX crypt library call and associated functionality.
-'''
+"""Wrapper to the POSIX crypt library call and associated functionality."""
import _crypt
+import string
+from random import choice
+from collections import namedtuple
-saltchars = 'abcdefghijklmnopqrstuvwxyz'
-saltchars += saltchars.upper()
-saltchars += '0123456789./'
+_saltchars = string.ascii_letters + string.digits + './'
-class _MethodClass:
- '''Class representing a salt method per the Modular Crypt Format or the
- legacy 2-character crypt method.'''
- def __init__(self, name, ident, salt_chars, total_size):
- self.name = name
- self.ident = ident
- self.salt_chars = salt_chars
- self.total_size = total_size
- def __repr__(self):
- return '<crypt.METHOD_%s>' % self.name
+class _Method(namedtuple('_Method', 'name ident salt_chars total_size')):
+ """Class representing a salt method per the Modular Crypt Format or the
+ legacy 2-character crypt method."""
-# available salting/crypto methods
-METHOD_CRYPT = _MethodClass('CRYPT', None, 2, 13)
-METHOD_MD5 = _MethodClass('MD5', '1', 8, 34)
-METHOD_SHA256 = _MethodClass('SHA256', '5', 16, 63)
-METHOD_SHA512 = _MethodClass('SHA512', '6', 16, 106)
+ def __repr__(self):
+ return '<crypt.METHOD_{}>'.format(self.name)
-def methods():
- '''Return a list of methods that are available in the platform ``crypt()``
- library, sorted from strongest to weakest. This is guaranteed to always
- return at least ``[METHOD_CRYPT]``'''
- method_list = [ METHOD_SHA512, METHOD_SHA256, METHOD_MD5 ]
- ret = [ method for method in method_list
- if len(crypt('', method)) == method.total_size ]
- ret.append(METHOD_CRYPT)
- return ret
+def mksalt(method=None):
+ """Generate a salt for the specified method.
-def mksalt(method = None):
- '''Generate a salt for the specified method. If not specified, the
- strongest available method will be used.'''
- import random
+ If not specified, the strongest available method will be used.
- if method == None: method = methods()[0]
- s = '$%s$' % method.ident if method.ident else ''
- s += ''.join([ random.choice(saltchars) for x in range(method.salt_chars) ])
- return(s)
+ """
+ if method is None:
+ method = methods[0]
+ s = '${}$'.format(method.ident) if method.ident else ''
+ s += ''.join(choice(_saltchars) for _ in range(method.salt_chars))
+ return s
-def crypt(word, salt = None):
- '''Return a string representing the one-way hash of a password, preturbed
- by a salt. If ``salt`` is not specified or is ``None``, the strongest
+def crypt(word, salt=None):
+ """Return a string representing the one-way hash of a password, with a salt
+ prepended.
+
+ If ``salt`` is not specified or is ``None``, the strongest
available method will be selected and a salt generated. Otherwise,
``salt`` may be one of the ``crypt.METHOD_*`` values, or a string as
- returned by ``crypt.mksalt()``.'''
- if salt == None: salt = mksalt()
- elif isinstance(salt, _MethodClass): salt = mksalt(salt)
- return(_crypt.crypt(word, salt))
+ returned by ``crypt.mksalt()``.
+
+ """
+ if salt is None or isinstance(salt, _Method):
+ salt = mksalt(salt)
+ return _crypt.crypt(word, salt)
+
+
+# available salting/crypto methods
+METHOD_CRYPT = _Method('CRYPT', None, 2, 13)
+METHOD_MD5 = _Method('MD5', '1', 8, 34)
+METHOD_SHA256 = _Method('SHA256', '5', 16, 63)
+METHOD_SHA512 = _Method('SHA512', '6', 16, 106)
+
+methods = [METHOD_SHA512, METHOD_SHA256, METHOD_MD5, METHOD_CRYPT]
+methods[:-1] = [m for m in methods[:-1] if len(crypt('', m)) == m.total_size]
diff --git a/Lib/test/test_crypt.py b/Lib/test/test_crypt.py
index 306296f..dc107d8 100644
--- a/Lib/test/test_crypt.py
+++ b/Lib/test/test_crypt.py
@@ -11,24 +11,23 @@ class CryptTestCase(unittest.TestCase):
print('Test encryption: ', c)
def test_salt(self):
- self.assertEqual(len(crypt.saltchars), 64)
- for method in crypt.methods():
+ 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():
+ 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):
- # Gurantee that METHOD_CRYPT is the last method in crypt.methods().
- methods = crypt.methods()
- self.assertTrue(len(methods) >= 1)
- self.assertEqual(crypt.METHOD_CRYPT, methods[-1])
+ # Gurantee that METHOD_CRYPT is the last method in crypt.methods.
+ self.assertTrue(len(crypt.methods) >= 1)
+ self.assertEqual(crypt.METHOD_CRYPT, crypt.methods[-1])
def test_main():
support.run_unittest(CryptTestCase)