blob: dc62dba8a326a8bb9d355342836e21d5643bfcfd [file] [log] [blame]
Sean Reifscheidere2dfefb2011-02-22 10:55:44 +00001'''Wrapper to the POSIX crypt library call and associated functionality.
2'''
3
4import _crypt
5
6saltchars = 'abcdefghijklmnopqrstuvwxyz'
7saltchars += saltchars.upper()
8saltchars += '0123456789./'
9
10
11class _MethodClass:
12 '''Class representing a salt method per the Modular Crypt Format or the
13 legacy 2-character crypt method.'''
14 def __init__(self, name, ident, salt_chars, total_size):
15 self.name = name
16 self.ident = ident
17 self.salt_chars = salt_chars
18 self.total_size = total_size
19
20 def __repr__(self):
21 return '<crypt.METHOD_%s>' % self.name
22
23
24# available salting/crypto methods
25METHOD_CRYPT = _MethodClass('CRYPT', None, 2, 13)
26METHOD_MD5 = _MethodClass('MD5', '1', 8, 34)
27METHOD_SHA256 = _MethodClass('SHA256', '5', 16, 63)
28METHOD_SHA512 = _MethodClass('SHA512', '6', 16, 106)
29
30
31def methods():
32 '''Return a list of methods that are available in the platform ``crypt()``
33 library, sorted from strongest to weakest. This is guaranteed to always
34 return at least ``[METHOD_CRYPT]``'''
35 method_list = [ METHOD_SHA512, METHOD_SHA256, METHOD_MD5 ]
36 ret = [ method for method in method_list
37 if len(crypt('', method)) == method.total_size ]
38 ret.append(METHOD_CRYPT)
39 return ret
40
41
42def mksalt(method = None):
43 '''Generate a salt for the specified method. If not specified, the
44 strongest available method will be used.'''
45 import random
46
47 if method == None: method = methods()[0]
48 s = '$%s$' % method.ident if method.ident else ''
49 s += ''.join([ random.choice(saltchars) for x in range(method.salt_chars) ])
50 return(s)
51
52
53def crypt(word, salt = None):
54 '''Return a string representing the one-way hash of a password, preturbed
55 by a salt. If ``salt`` is not specified or is ``None``, the strongest
56 available method will be selected and a salt generated. Otherwise,
57 ``salt`` may be one of the ``crypt.METHOD_*`` values, or a string as
58 returned by ``crypt.mksalt()``.'''
59 if salt == None: salt = mksalt()
60 elif isinstance(salt, _MethodClass): salt = mksalt(salt)
61 return(_crypt.crypt(word, salt))