# ---------------------------
#Fichier : security.py
#Date : 14.10.2020
#But :
#Remarque :
#------------------------------
import random, string, base64, hmac, re


SCRYPT_N = 65536
SCRYPT_SALT_LEN = 16
API_ID_LEN = 24

def implode(pw_hash, salt):
    """
    Implodes the given hash and its arguments into a string

    :param pw_hash: password hash
    :param salt: hash salt
    :returns: hash string with arguments in order (salt,hash)
    """
    return '$' + salt + '$' + pw_hash

def explode(hashstr):
    """
    Explodes a hash string whose values are separated by '$' into an array

    :param hashstr: hash string to explode
    :returns: elements of the given hash string as an array
    """

    return hashstr.split('$')

def pw_complexity(password):
    """
    Checks the password complexity before using it

    :param password: password to verify complexity
    :returns: true if the password is complex enoughs, false otherwise
    """
    # Dans le cas d'un mot de passe inférieur à 8 caractères
    if (len(password) < 8):
        return False
    # Dans le cas d'un mot de passe sans chiffre
    if (re.search(r"\d", password) is None):
        return False
    # Dans le cas d'un mot de passe sans majuscule
    if (re.search(r"[A-Z]", password) is None):
        return False
    # Dans le cas d'un mot de passe sans minuscule
    if (re.search(r"[a-z]", password) is None):
        return False

    return True


def hash_pw(password):
    """
    Hashes the given password using scrypt and a random salt

    :param password: password to hash
    :returns: hashed password with arguments
    """

    salt = gen_rand_string()
    pw_hash = base64.b64encode(
        hmac.new(
            key=salt.encode('utf8'),
            msg=password.encode('utf8'),
            digestmod='SHA256'
        ).digest()
    ).decode('utf8')

    return implode(pw_hash, salt)

def check_pw(password, pw_hash):
    """
    Checks whether the specified password and parameters match the given hash

    :param password: password to check
    :param pw_hash: hash string to compare with
    :returns: True if the password is correct, else False
    """

    hashvars = explode(pw_hash)
    mac = base64.b64decode(pw_hash)

    return hmac.compare_digest(hashvars[2].encode('utf8'), base64.b64encode(hmac.new(
        key=hashvars[1].encode('utf8'),
        msg=password.encode('utf8'),
        digestmod='SHA256'
    ).digest()))

def gen_rand_string(prefix=None):
    """
    Generates a random string of 24 characters (alphanumeric case-sensitive)

    :param prefix: prefix to append to the random string
    :returns: random string of 24 alphanumeric characters (case-sensitive)
    """
    return (prefix + '_' if prefix != None else '') + ''.join(
        random.SystemRandom().choice(
            string.ascii_letters + string.digits
        ) for _ in range(API_ID_LEN)
    )