Python 100 project #2: Password generator

I made a password generator which takes one strings, and alters the character to the leet character.

It also check if the password is complicated enough to meet some of the criteria(eg. length, uppercase, lowercase, symbol), as well as it checks if the generated password has been seen in the past data breach.

 

Output example

shogokobayashi 100p $ python3 password_generator.py iamsleepy
Password generated: i@MSL3EpY
Password is complex enough: True
Generated password is not found in the previous pwned database.

 

 

import hashlib
import random
import re
import sys

import requests

def generate_password(source_str):
    """
    Generate password candidate from the given strings by altering some characters with leet

    Input: A String

    Return: A Password candidate string
    """

    if not any(character.isupper() for character in source_str):
        temp_str = ""
        for char in source_str:
            if random.choice([True, False]):
                char = char.upper()
            temp_str += char
        source_str = temp_str

    leet_dict = {
        "a": ["@", "4", "^", "a"],
        "b": ["8", "b"],
        "c": ["[","<","(", "c"],
        "d": [")", "d"],
        "e": ["3","&", "e"],
        "f": ["f"],
        "g": ["6", "[", "(", "g"],
        "h": ["#", "h"],
        "i": ["1", "!", "i"],
        "j": ["j"],
        "k": ["k"],
        "l": ["1", "l"],
        "m": ["m"],
        "n": ["n"],
        "o": ["0", "o"],
        "p": ["p"],
        "q": ["q"],
        "r": ["2", "r"],
        "s": ["5", "$", "s"],
        "t": ["+", "7", "t"],
        "u": ["u"],
        "v": ["v"],
        "w": ["w"],
        "x": ["x"],
        "y": ["y"],
        "z": ["z"],

    }

    candidate = ""
    for c in source_str:
        if leet_dict.get(c):
             c = random.choice(leet_dict[c])
        candidate += c

    return candidate


def is_password_complicated(password):
    """
    Reference from stackoverflow: https://stackoverflow.com/questions/16709638/checking-the-strength-of-a-password-how-to-check-conditions

    Verify the strength of 'password'
    Returns a dict indicating the wrong criteria
    A password is considered strong if:
        8 characters length or more
        1 digit or more
        1 symbol or more
        1 uppercase letter or more
        1 lowercase letter or more

    Return: Boolean
    """

    # calculating the length
    has_length = len(password) >= 8

    # searching for digits
    has_digit = re.search(r"\d", password) is not None

    # searching for uppercase
    has_uppercase = re.search(r"[A-Z]", password) is not None

    # searching for lowercase
    has_lowercase = re.search(r"[a-z]", password) is not None

    # searching for symbols
    has_symbol = re.search(r"\W", password) is not None

    # overall result
    return  (has_length and has_digit and has_uppercase and has_lowercase and has_symbol)


def is_pwned(candidate):
    """
    Check "Have I been pwned" to see if the generated password was seen in the previous data breach
    :param candidate: generated password
    :return: Boolean
    """

    m = hashlib.sha1(candidate.encode('utf-8'))

    url = "https://api.pwnedpasswords.com/pwnedpassword/" + m.hexdigest()

    response = requests.get(url)

    if response.status_code == 200:
        return True
    elif response.status_code == 404:
        return False
    else:
        raise Exception


if __name__ == '__main__':

    if len(sys.argv) != 2:
        print(f"Usage: {sys.argv[0]} source_string")
        sys.exit(1)
    elif len(sys.argv[1]) < 8:
        print("Provided string is too short. Please provide at least 8 characters strings for optimum result.")
        sys.exit(1)



    generated_password = generate_password(sys.argv[1])
    print(f"Password generated: {generated_password}")

    complexity_result = is_password_complicated(generated_password)
    print(f"Password is complex enough: {complexity_result}")

    pwned = is_pwned(generated_password)
    if pwned:
        print("Generated password is found in the pwned database, you may want to run this script again to generate another password..." )
    else:
        print("Generated password is not found in the previous pwned database.")