login.py 2.46 KB
Newer Older
chirac's avatar
chirac committed
1 2 3 4
# -*- coding: utf-8 -*-
# Module d'authentification
# David Sinquin, Gabriel Détraz, Goulven Kermarec

lhark's avatar
lhark committed
5 6 7

import hashlib
import binascii
chirac's avatar
chirac committed
8
import os
lhark's avatar
lhark committed
9 10 11 12 13 14 15 16 17 18 19
from base64 import encodestring
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

chirac's avatar
chirac committed
20 21 22 23 24

def makeSecret(password):
    salt = os.urandom(4)
    h = hashlib.sha1(password.encode())
    h.update(salt)
lhark's avatar
lhark committed
25 26
    return ALGO_NAME + "$" + encodestring(h.digest() + salt).decode()[:-1]

chirac's avatar
chirac committed
27 28 29 30 31

def hashNT(password):
    hash = hashlib.new('md4', password.encode()).digest()
    return binascii.hexlify(hash)

lhark's avatar
lhark committed
32

chirac's avatar
chirac committed
33
def checkPassword(challenge_password, password):
lhark's avatar
lhark committed
34 35 36
    challenge_bytes = decodestring(challenge_password[ALGO_LEN:].encode())
    digest = challenge_bytes[:DIGEST_LEN]
    salt = challenge_bytes[DIGEST_LEN:]
chirac's avatar
chirac committed
37 38 39
    hr = hashlib.sha1(password.encode())
    hr.update(salt)
    valid_password = True
lhark's avatar
lhark committed
40 41
    # La comparaison est volontairement en temps constant
    # (pour éviter les timing-attacks)
chirac's avatar
chirac committed
42 43 44
    for i, j in zip(digest, hr.digest()):
        valid_password &= i == j
    return valid_password
lhark's avatar
lhark committed
45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90


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