Mercurial > p > roundup > code
comparison roundup/password.py @ 4486:693c75d56ebe
Add new config-option 'password_pbkdf2_default_rounds'...
...in 'main' section to configure the default parameter for new
password generation. Set this to a higher value on faster systems
which want more security. Thanks to Eli Collins for implementing this
(see issue2550688).
This now passes a config object (default None in which case we fall back
to hard-coded parameters) into the password generation routine. This way
we can add further parameters for password generation in the future.
Also added a small regression test for this new feature.
| author | Ralf Schlatterbeck <schlatterbeck@users.sourceforge.net> |
|---|---|
| date | Fri, 15 Apr 2011 08:09:59 +0000 |
| parents | 95aace124a8e |
| children | 357c6079c73b |
comparison
equal
deleted
inserted
replaced
| 4485:95aace124a8e | 4486:693c75d56ebe |
|---|---|
| 133 except ValueError: | 133 except ValueError: |
| 134 raise PasswordValueError, "invalid PBKDF2 hash (invalid rounds)" | 134 raise PasswordValueError, "invalid PBKDF2 hash (invalid rounds)" |
| 135 raw_salt = h64decode(salt) | 135 raw_salt = h64decode(salt) |
| 136 return rounds, salt, raw_salt, digest | 136 return rounds, salt, raw_salt, digest |
| 137 | 137 |
| 138 def encodePassword(plaintext, scheme, other=None): | 138 def encodePassword(plaintext, scheme, other=None, config=None): |
| 139 """Encrypt the plaintext password. | 139 """Encrypt the plaintext password. |
| 140 """ | 140 """ |
| 141 if plaintext is None: | 141 if plaintext is None: |
| 142 plaintext = "" | 142 plaintext = "" |
| 143 if scheme == "PBKDF2": | 143 if scheme == "PBKDF2": |
| 144 if other: | 144 if other: |
| 145 rounds, salt, raw_salt, digest = pbkdf2_unpack(other) | 145 rounds, salt, raw_salt, digest = pbkdf2_unpack(other) |
| 146 else: | 146 else: |
| 147 raw_salt = getrandbytes(20) | 147 raw_salt = getrandbytes(20) |
| 148 salt = h64encode(raw_salt) | 148 salt = h64encode(raw_salt) |
| 149 #FIXME: find way to access config, so default rounds | 149 if config: |
| 150 # can be altered for faster/slower hosts via config.ini | 150 rounds = config.PASSWORD_PBKDF2_DEFAULT_ROUNDS |
| 151 rounds = 10000 | 151 else: |
| 152 rounds = 10000 | |
| 152 if rounds < 1000: | 153 if rounds < 1000: |
| 153 raise PasswordValueError, "invalid PBKDF2 hash (rounds too low)" | 154 raise PasswordValueError, "invalid PBKDF2 hash (rounds too low)" |
| 154 raw_digest = pbkdf2(plaintext, raw_salt, rounds, 20) | 155 raw_digest = pbkdf2(plaintext, raw_salt, rounds, 20) |
| 155 return "%d$%s$%s" % (rounds, salt, h64encode(raw_digest)) | 156 return "%d$%s$%s" % (rounds, salt, h64encode(raw_digest)) |
| 156 elif scheme == 'SHA': | 157 elif scheme == 'SHA': |
| 241 #TODO: code to migrate from old password schemes. | 242 #TODO: code to migrate from old password schemes. |
| 242 | 243 |
| 243 deprecated_schemes = ["SHA", "MD5", "crypt", "plaintext"] | 244 deprecated_schemes = ["SHA", "MD5", "crypt", "plaintext"] |
| 244 known_schemes = ["PBKDF2"] + deprecated_schemes | 245 known_schemes = ["PBKDF2"] + deprecated_schemes |
| 245 | 246 |
| 246 def __init__(self, plaintext=None, scheme=None, encrypted=None, strict=False): | 247 def __init__(self, plaintext=None, scheme=None, encrypted=None, strict=False, config=None): |
| 247 """Call setPassword if plaintext is not None.""" | 248 """Call setPassword if plaintext is not None.""" |
| 248 if scheme is None: | 249 if scheme is None: |
| 249 scheme = self.default_scheme | 250 scheme = self.default_scheme |
| 250 if plaintext is not None: | 251 if plaintext is not None: |
| 251 self.setPassword (plaintext, scheme) | 252 self.setPassword (plaintext, scheme, config=config) |
| 252 elif encrypted is not None: | 253 elif encrypted is not None: |
| 253 self.unpack(encrypted, scheme, strict=strict) | 254 self.unpack(encrypted, scheme, strict=strict, config=config) |
| 254 else: | 255 else: |
| 255 self.scheme = self.default_scheme | 256 self.scheme = self.default_scheme |
| 256 self.password = None | 257 self.password = None |
| 257 self.plaintext = None | 258 self.plaintext = None |
| 258 | 259 |
| 265 rounds, salt, raw_salt, digest = pbkdf2_unpack(self.password) | 266 rounds, salt, raw_salt, digest = pbkdf2_unpack(self.password) |
| 266 if rounds < 1000: | 267 if rounds < 1000: |
| 267 return True | 268 return True |
| 268 return False | 269 return False |
| 269 | 270 |
| 270 def unpack(self, encrypted, scheme=None, strict=False): | 271 def unpack(self, encrypted, scheme=None, strict=False, config=None): |
| 271 """Set the password info from the scheme:<encryted info> string | 272 """Set the password info from the scheme:<encryted info> string |
| 272 (the inverse of __str__) | 273 (the inverse of __str__) |
| 273 """ | 274 """ |
| 274 m = self.pwre.match(encrypted) | 275 m = self.pwre.match(encrypted) |
| 275 if m: | 276 if m: |
| 276 self.scheme = m.group(1) | 277 self.scheme = m.group(1) |
| 277 self.password = m.group(2) | 278 self.password = m.group(2) |
| 278 self.plaintext = None | 279 self.plaintext = None |
| 279 else: | 280 else: |
| 280 # currently plaintext - encrypt | 281 # currently plaintext - encrypt |
| 281 self.setPassword(encrypted, scheme) | 282 self.setPassword(encrypted, scheme, config=config) |
| 282 if strict and self.scheme not in self.known_schemes: | 283 if strict and self.scheme not in self.known_schemes: |
| 283 raise PasswordValueError, "unknown encryption scheme: %r" % (self.scheme,) | 284 raise PasswordValueError, "unknown encryption scheme: %r" % (self.scheme,) |
| 284 | 285 |
| 285 def setPassword(self, plaintext, scheme=None): | 286 def setPassword(self, plaintext, scheme=None, config=None): |
| 286 """Sets encrypts plaintext.""" | 287 """Sets encrypts plaintext.""" |
| 287 if scheme is None: | 288 if scheme is None: |
| 288 scheme = self.default_scheme | 289 scheme = self.default_scheme |
| 289 self.scheme = scheme | 290 self.scheme = scheme |
| 290 self.password = encodePassword(plaintext, scheme) | 291 self.password = encodePassword(plaintext, scheme, config=config) |
| 291 self.plaintext = plaintext | 292 self.plaintext = plaintext |
| 292 | 293 |
| 293 def __str__(self): | 294 def __str__(self): |
| 294 """Stringify the encrypted password for database storage.""" | 295 """Stringify the encrypted password for database storage.""" |
| 295 if self.password is None: | 296 if self.password is None: |
