# For random number generation
# https://cryptography.io/en/latest/random-numbers/
import os
# Library for hashBackend (openssl)
# https://cryptography.io/en/latest/hazmat/backends/
from cryptography.hazmat.backends import openssl
# Lirary for Hash functions like SHA-2 family, SHA-3 family, Blake2, and MD5
# https://cryptography.io/en/latest/hazmat/primitives/cryptographic-hashes/
from cryptography.hazmat.primitives import hashes

# Returns a random lowercase character using os.urandom
def get_rand_lowercase():
    # rand_int is geneated the same was as length but with the range 97 - 122
    # this range specifies the ascii values for lowercase letter in decimal
    rand_int = int.from_bytes(os.urandom(1), byteorder="big")
    rand_int = rand_int % (122 - 97 + 1) + 97
    # convert rand_int to a char to get random character
    return chr(rand_int)

# Returns a string with a length in range(min_length, max_length)
# length is randomly generated but within specified range
def get_random_string(min_length, max_length):
    # empty string that will hold random characters and will eventually be returned
    return_str = ""
    # randomly generated byte which is then converted to an int
    length = int.from_bytes(os.urandom(1), byteorder="big")
    # modify length to be within the range(min_length, max_length)
    length = length % (max_length - min_length + 1) + min_length

    for i in range(length):
        # get a random lowercase character
        rand_char = get_rand_lowercase()
        # append the random character to the return string
        return_str = return_str + rand_char

    return return_str


def gen_password_file(min_length, max_length, num_of_accounts):    
    # Display arguments to the console
    print("Min length:", min_length)
    print("Max length:", max_length)
    print("Generating", num_of_accounts, "account(s)")
    # Create/Open the password files
    password_file0 = open("accounts0.txt", "w") # Plaintext
    password_file1 = open("accounts1.txt", "w") # Hashed
    password_file2 = open("accounts2.txt", "w") # Salted Hash

    for i in range(num_of_accounts):
        # Create username "userN" where N = range(0, num_accounts) 
        username = "user" + str(i)
        # Create random password
        password = get_random_string(min_length, max_length)
        # Get salt
        salt = get_random_string(1, 1)
        # Append salt to password to get salted password
        salted_password = password + salt
        # Display details to the console
        print("Username:\t", username)
        print("Password:\t", password)
        print("Salt:\t\t", salt)
        # Encode passwords in ascii for hashing purposes
        byte_password = password.encode("ascii")
        byte_salted_password = salted_password.encode("ascii")
        # Hash the password w/out salt
        digest0 = hashes.Hash(hashes.SHA256(), backend=openssl.backend)
        digest0.update(byte_password)
        hash_password = digest0.finalize()
        hash_password = hash_password.hex()
        # Hash the password with salt
        digest0 = hashes.Hash(hashes.SHA256(), backend=openssl.backend)
        digest0.update(byte_salted_password)
        salted_hash_password = digest0.finalize()
        salted_hash_password = salted_hash_password.hex()
        # Display hashes to the console
        print("Hashword:\t", hash_password)
        print("Salted:\t\t", salted_hash_password)
        # Writing plaintext passwords
        password_file0.write(username + "," + password + "\n")
        # Writing hashed passwords
        password_file1.write(username + "," + hash_password + "\n")
        # Writing salted hash passwords
        password_file2.write(username + "," + salted_hash_password + "," + salt + "\n")

def generate_accounts():
    acceptable_input = False
    while not acceptable_input:
        # Get required values for account generation
        min_length = int(input("Enter min password length: "))
        max_length = int(input("Enter max password length: "))
        num_of_accounts = int(input("Enter # of accounts to generate: "))

        # Validate input is acceptable
        if min_length <= 0 or min_length > max_length:
            print("Min password length must be greater than 0 and less than max password length")
        if max_length <= 0 or max_length > 64:
            print("Max password length must be greater than 0 and less than 64")
        if num_of_accounts < 0 or num_of_accounts > 1000:
            print("Number of accounts to be generated must be greater than 0 and less than or equal to 1000")
        else:
            acceptable_input = True
            gen_password_file(min_length, max_length, num_of_accounts)

if __name__ == "__main__":
    generate_accounts()