Sean Reifscheider | e2dfefb | 2011-02-22 10:55:44 +0000 | [diff] [blame] | 1 | '''Wrapper to the POSIX crypt library call and associated functionality. |
| 2 | ''' |
| 3 | |
| 4 | import _crypt |
| 5 | |
| 6 | saltchars = 'abcdefghijklmnopqrstuvwxyz' |
| 7 | saltchars += saltchars.upper() |
| 8 | saltchars += '0123456789./' |
| 9 | |
| 10 | |
| 11 | class _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 |
| 25 | METHOD_CRYPT = _MethodClass('CRYPT', None, 2, 13) |
| 26 | METHOD_MD5 = _MethodClass('MD5', '1', 8, 34) |
| 27 | METHOD_SHA256 = _MethodClass('SHA256', '5', 16, 63) |
| 28 | METHOD_SHA512 = _MethodClass('SHA512', '6', 16, 106) |
| 29 | |
| 30 | |
| 31 | def 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 | |
| 42 | def 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 | |
| 53 | def 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)) |