235 |
235 |
return '%s:%s' % (_hash, salt.decode('hex'))
|
236 |
236 |
else:
|
237 |
237 |
return _hash
|
|
238 |
|
|
239 |
|
|
240 |
class PloneSHA1PasswordHasher(hashers.SHA1PasswordHasher):
|
|
241 |
# from https://www.fourdigits.nl/blog/converting-plone-data-to-django/
|
|
242 |
"""
|
|
243 |
The SHA1 password hashing algorithm used by Plone.
|
|
244 |
|
|
245 |
Plone uses `password + salt`, Django has `salt + password`.
|
|
246 |
"""
|
|
247 |
|
|
248 |
algorithm = "plonesha1"
|
|
249 |
_prefix = '{SSHA}'
|
|
250 |
|
|
251 |
def encode(self, password, salt):
|
|
252 |
"""Encode a plain text password into a plonesha1 style hash."""
|
|
253 |
assert password is not None
|
|
254 |
assert salt
|
|
255 |
password = force_bytes(password)
|
|
256 |
salt = force_bytes(salt)
|
|
257 |
|
|
258 |
hashed = base64.b64encode(hashlib.sha1(password + salt).digest() + salt)
|
|
259 |
return "%s$%s%s" % (self.algorithm, self._prefix, hashed)
|
|
260 |
|
|
261 |
def verify(self, password, encoded):
|
|
262 |
"""Verify the given password against the encoded string."""
|
|
263 |
algorithm, data = encoded.split('$', 1)
|
|
264 |
assert algorithm == self.algorithm
|
|
265 |
|
|
266 |
# throw away the prefix
|
|
267 |
if data.startswith(self._prefix):
|
|
268 |
data = data[len(self._prefix):]
|
|
269 |
|
|
270 |
# extract salt from encoded data
|
|
271 |
intermediate = base64.b64decode(data)
|
|
272 |
salt = intermediate[20:].strip()
|
|
273 |
|
|
274 |
password_encoded = self.encode(password, salt)
|
|
275 |
return constant_time_compare(password_encoded, encoded)
|
|
276 |
|
|
277 |
def safe_summary(self, encoded):
|
|
278 |
algorithm, hash = encoded.split('$', 1)
|
|
279 |
assert algorithm == self.algorithm
|
|
280 |
return OrderedDict([
|
|
281 |
(_('algorithm'), algorithm),
|
|
282 |
(_('hash'), hashers.mask_hash(hash)),
|
|
283 |
])
|