diff -r 84fcff2829a6 roundup/cgi/form_parser.py --- a/roundup/cgi/form_parser.py Wed Mar 04 20:52:35 2015 +0300 +++ b/roundup/cgi/form_parser.py Wed Apr 01 13:35:08 2015 +0300 @@ -393,7 +393,8 @@ raise FormError, self._('Password and confirmation text ' 'do not match') try: - value = password.Password(value, config=self.db.config) + value = password.Password(value, scheme = proptype.scheme, + config=self.db.config) except hyperdb.HyperdbValueError, msg: raise FormError, msg diff -r 84fcff2829a6 roundup/hyperdb.py --- a/roundup/hyperdb.py Wed Mar 04 20:52:35 2015 +0300 +++ b/roundup/hyperdb.py Wed Apr 01 13:35:08 2015 +0300 @@ -72,11 +72,15 @@ class Password(_Type): """An object designating a Password property.""" + def __init__(self, scheme=None, required=False, default_value = None): + super(Password, self).__init__(required, default_value) + self.scheme = scheme + def from_raw(self, value, translator=translation, **kw): if not value: return None try: - return password.Password(encrypted=value, strict=True) + return password.Password(encrypted=value, scheme=self.scheme, strict=True) except password.PasswordValueError, message: raise HyperdbValueError, \ translator.gettext('property %s: %s')%(kw['propname'], message) diff -r 84fcff2829a6 roundup/password.py --- a/roundup/password.py Wed Mar 04 20:52:35 2015 +0300 +++ b/roundup/password.py Wed Apr 01 13:35:08 2015 +0300 @@ -20,6 +20,7 @@ __docformat__ = 'restructuredtext' import re, string, random +import os from base64 import b64encode, b64decode from roundup.anypy.hashlib_ import md5, sha1, shamodule try: @@ -80,6 +81,16 @@ out += block return out[:keylen] +def ssha(password, salt): + ''' Make ssha digest from password and salt. + Based on code of Roberto Aguilar + https://gist.github.com/rca/7217540 + ''' + shaval = sha1(password) + shaval.update( salt ) + ssha_digest = b64encode( '{}{}'.format(shaval.digest(), salt) ).strip() + return ssha_digest + def pbkdf2(password, salt, rounds, keylen): """pkcs#5 password-based key derivation v2.0 @@ -148,6 +159,16 @@ raise PasswordValueError, "invalid PBKDF2 hash (rounds too low)" raw_digest = pbkdf2(plaintext, raw_salt, rounds, 20) return "%d$%s$%s" % (rounds, salt, h64encode(raw_digest)) + elif scheme == 'SSHA': + if other: + raw_other = b64decode(other) + salt = raw_other[20:] + else: + #new password + # variable salt length + salt_len = random.randrange(36, 52) + salt = os.urandom(salt_len) + s = ssha(plaintext, salt) elif scheme == 'SHA': s = sha1(plaintext).hexdigest() elif scheme == 'MD5': @@ -240,7 +261,7 @@ #TODO: code to migrate from old password schemes. deprecated_schemes = ["SHA", "MD5", "crypt", "plaintext"] - known_schemes = ["PBKDF2"] + deprecated_schemes + known_schemes = ["PBKDF2", "SSHA"] + deprecated_schemes def __init__(self, plaintext=None, scheme=None, encrypted=None, strict=False, config=None): """Call setPassword if plaintext is not None.""" @@ -318,6 +339,13 @@ assert 'sekrit' == p assert 'not sekrit' != p + # SSHA + p = Password('sekrit', 'SSHA') + assert p == 'sekrit' + assert p != 'not sekrit' + assert 'sekrit' == p + assert 'not sekrit' != p + # PBKDF2 - low level function from binascii import unhexlify k = pbkdf2("password", "ATHENA.MIT.EDUraeburn", 1200, 32)