1
+
import functools
1
2
import hashlib
2
3
3
4
from django.conf import settings
11
12
12
13
13
14
UNUSABLE_PASSWORD = '!' # This will never be a valid encoded hash
15
+
MAXIMUM_PASSWORD_LENGTH = 4096 # The maximum length a password can be to prevent DoS
14
16
HASHERS = None # lazily loaded from PASSWORD_HASHERS
15
17
PREFERRED_HASHER = None # defaults to first item in PASSWORD_HASHERS
16
18
17
19
20
+
def password_max_length(max_length):
21
+
def inner(fn):
22
+
@functools.wraps(fn)
23
+
def wrapper(self, password, *args, **kwargs):
24
+
if len(password) > max_length:
25
+
raise ValueError("Invalid password; Must be less than or equal"
26
+
" to %d bytes" % max_length)
27
+
return fn(self, password, *args, **kwargs)
28
+
return wrapper
29
+
return inner
30
+
31
+
18
32
def is_password_usable(encoded):
19
33
return (encoded is not None and encoded != UNUSABLE_PASSWORD)
20
34
@@ -202,6 +216,7 @@ class PBKDF2PasswordHasher(BasePasswordHasher):
202
216
iterations = 10000
203
217
digest = hashlib.sha256
204
218
219
+
@password_max_length(MAXIMUM_PASSWORD_LENGTH)
205
220
def encode(self, password, salt, iterations=None):
206
221
assert password
207
222
assert salt and '$' not in salt
@@ -211,6 +226,7 @@ def encode(self, password, salt, iterations=None):
211
226
hash = hash.encode('base64').strip()
212
227
return "%s$%d$%s$%s" % (self.algorithm, iterations, salt, hash)
213
228
229
+
@password_max_length(MAXIMUM_PASSWORD_LENGTH)
214
230
def verify(self, password, encoded):
215
231
algorithm, iterations, salt, hash = encoded.split('$', 3)
216
232
assert algorithm == self.algorithm
@@ -256,11 +272,13 @@ def salt(self):
256
272
bcrypt = self._load_library()
257
273
return bcrypt.gensalt(self.rounds)
258
274
275
+
@password_max_length(MAXIMUM_PASSWORD_LENGTH)
259
276
def encode(self, password, salt):
260
277
bcrypt = self._load_library()
261
278
data = bcrypt.hashpw(password, salt)
262
279
return "%s$%s" % (self.algorithm, data)
263
280
281
+
@password_max_length(MAXIMUM_PASSWORD_LENGTH)
264
282
def verify(self, password, encoded):
265
283
algorithm, data = encoded.split('$', 1)
266
284
assert algorithm == self.algorithm
@@ -285,12 +303,14 @@ class SHA1PasswordHasher(BasePasswordHasher):
285
303
"""
286
304
algorithm = "sha1"
287
305
306
+
@password_max_length(MAXIMUM_PASSWORD_LENGTH)
288
307
def encode(self, password, salt):
289
308
assert password
290
309
assert salt and '$' not in salt
291
310
hash = hashlib.sha1(salt + password).hexdigest()
292
311
return "%s$%s$%s" % (self.algorithm, salt, hash)
293
312
313
+
@password_max_length(MAXIMUM_PASSWORD_LENGTH)
294
314
def verify(self, password, encoded):
295
315
algorithm, salt, hash = encoded.split('$', 2)
296
316
assert algorithm == self.algorithm
@@ -313,12 +333,14 @@ class MD5PasswordHasher(BasePasswordHasher):
313
333
"""
314
334
algorithm = "md5"
315
335
336
+
@password_max_length(MAXIMUM_PASSWORD_LENGTH)
316
337
def encode(self, password, salt):
317
338
assert password
318
339
assert salt and '$' not in salt
319
340
hash = hashlib.md5(salt + password).hexdigest()
320
341
return "%s$%s$%s" % (self.algorithm, salt, hash)
321
342
343
+
@password_max_length(MAXIMUM_PASSWORD_LENGTH)
322
344
def verify(self, password, encoded):
323
345
algorithm, salt, hash = encoded.split('$', 2)
324
346
assert algorithm == self.algorithm
@@ -349,11 +371,13 @@ class UnsaltedSHA1PasswordHasher(BasePasswordHasher):
349
371
def salt(self):
350
372
return ''
351
373
374
+
@password_max_length(MAXIMUM_PASSWORD_LENGTH)
352
375
def encode(self, password, salt):
353
376
assert salt == ''
354
377
hash = hashlib.sha1(password).hexdigest()
355
378
return 'sha1$$%s' % hash
356
379
380
+
@password_max_length(MAXIMUM_PASSWORD_LENGTH)
357
381
def verify(self, password, encoded):
358
382
encoded_2 = self.encode(password, '')
359
383
return constant_time_compare(encoded, encoded_2)
@@ -383,10 +407,12 @@ class UnsaltedMD5PasswordHasher(BasePasswordHasher):
383
407
def salt(self):
384
408
return ''
385
409
410
+
@password_max_length(MAXIMUM_PASSWORD_LENGTH)
386
411
def encode(self, password, salt):
387
412
assert salt == ''
388
413
return hashlib.md5(password).hexdigest()
389
414
415
+
@password_max_length(MAXIMUM_PASSWORD_LENGTH)
390
416
def verify(self, password, encoded):
391
417
if len(encoded) == 37 and encoded.startswith('md5$$'):
392
418
encoded = encoded[5:]
@@ -412,13 +438,15 @@ class CryptPasswordHasher(BasePasswordHasher):
412
438
def salt(self):
413
439
return get_random_string(2)
414
440
441
+
@password_max_length(MAXIMUM_PASSWORD_LENGTH)
415
442
def encode(self, password, salt):
416
443
crypt = self._load_library()
417
444
assert len(salt) == 2
418
445
data = crypt.crypt(password, salt)
419
446
# we don't need to store the salt, but Django used to do this
420
447
return "%s$%s$%s" % (self.algorithm, '', data)
421
448
449
+
@password_max_length(MAXIMUM_PASSWORD_LENGTH)
422
450
def verify(self, password, encoded):
423
451
crypt = self._load_library()
424
452
algorithm, salt, data = encoded.split('$', 2)
@@ -433,4 +461,3 @@ def safe_summary(self, encoded):
433
461
(_('salt'), salt),
434
462
(_('hash'), mask_hash(data, show=3)),
435
463
])
436
-
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4