8
0
Fork 0
mirror of https://gitlab2.federez.net/re2o/re2o synced 2024-12-26 08:53:46 +00:00

Custom password hasher for SSHA

This commit is contained in:
lhark 2016-07-06 22:44:30 +02:00
parent 17719418f2
commit cb04daf67f

View file

@ -2,29 +2,89 @@
# Module d'authentification # Module d'authentification
# David Sinquin, Gabriel Détraz, Goulven Kermarec # David Sinquin, Gabriel Détraz, Goulven Kermarec
import hashlib, binascii
import hashlib
import binascii
import os import os
from base64 import urlsafe_b64encode as encode from base64 import encodestring
from base64 import urlsafe_b64decode as decode from base64 import decodestring
from collections import OrderedDict
from django.contrib.auth import hashers
ALGO_NAME = "{SSHA}"
ALGO_LEN = len(ALGO_NAME + "$")
DIGEST_LEN = 20
def makeSecret(password): def makeSecret(password):
salt = os.urandom(4) salt = os.urandom(4)
h = hashlib.sha1(password.encode()) h = hashlib.sha1(password.encode())
h.update(salt) h.update(salt)
return "{SSHA}" + encode(h.digest() + salt).decode() return ALGO_NAME + "$" + encodestring(h.digest() + salt).decode()[:-1]
def hashNT(password): def hashNT(password):
hash = hashlib.new('md4', password.encode()).digest() hash = hashlib.new('md4', password.encode()).digest()
return binascii.hexlify(hash) return binascii.hexlify(hash)
def checkPassword(challenge_password, password): def checkPassword(challenge_password, password):
challenge_bytes = decode(challenge_password[6:]) challenge_bytes = decodestring(challenge_password[ALGO_LEN:].encode())
digest = challenge_bytes[:20] digest = challenge_bytes[:DIGEST_LEN]
salt = challenge_bytes[20:] salt = challenge_bytes[DIGEST_LEN:]
hr = hashlib.sha1(password.encode()) hr = hashlib.sha1(password.encode())
hr.update(salt) hr.update(salt)
valid_password = True valid_password = True
# La comparaison est volontairement en temps constant (pour éviter les timing-attacks) # La comparaison est volontairement en temps constant
# (pour éviter les timing-attacks)
for i, j in zip(digest, hr.digest()): for i, j in zip(digest, hr.digest()):
valid_password &= i == j valid_password &= i == j
return valid_password return valid_password
class SSHAPasswordHasher(hashers.BasePasswordHasher):
"""
SSHA password hashing to allow for LDAP auth compatibility
"""
algorithm = ALGO_NAME
def encode(self, password, salt, iterations=None):
"""
Hash and salt the given password using SSHA algorithm
salt is overridden
"""
assert password is not None
return makeSecret(password)
def verify(self, password, encoded):
"""
Check password against encoded using SSHA algorithm
"""
assert encoded.startswith(self.algorithm)
return checkPassword(encoded, password)
def safe_summary(self, encoded):
"""
Provides a safe summary ofthe password
"""
assert encoded.startswith(self.algorithm)
hash = encoded[ALGO_LEN:]
hash = binascii.hexlify(decodestring(hash.encode())).decode()
return OrderedDict([
('algorithm', self.algorithm),
('iterations', 0),
('salt', hashers.mask_hash(hash[2*DIGEST_LEN:], show=2)),
('hash', hashers.mask_hash(hash[:2*DIGEST_LEN])),
])
def harden_runtime(self, password, encoded):
"""
Method implemented to shut up BasePasswordHasher warning
As we are not using multiple iterations the method is pretty useless
"""
pass