#!/usr/bin/env python3 import argparse import math import os import string import sys class C: RED = "\033[91m" GREEN = "\033[92m" YELLOW = "\033[93m" CYAN = "\033[96m" WHITE = "\033[97m" GRAY = "\033[90m" BOLD = "\033[1m" DIM = "\033[2m" RESET = "\033[0m" BANNER = f""" {C.CYAN}{C.BOLD} ██████╗ █████╗ ███████╗███████╗███████╗ ██████╗ ██████╗ ██████╗ ███████╗ ██╔══██╗██╔══██╗██╔════╝██╔════╝██╔════╝██╔═══██╗██╔══██╗██╔════╝ ██╔════╝ ██████╔╝███████║███████╗███████╗█████╗ ██║ ██║██████╔╝██║ ███╗█████╗ ██╔═══╝ ██╔══██║╚════██║╚════██║██╔══╝ ██║ ██║██╔══██╗██║ ██║██╔══╝ ██║ ██║ ██║███████║███████║██║ ╚██████╔╝██║ ██║╚██████╔╝███████╗ ╚═╝ ╚═╝ ╚═╝╚══════╝╚══════╝╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═════╝ ╚══════╝{C.RESET} {C.GRAY} ────────────────────────────────────────────────────────────────────{C.RESET} {C.DIM} Secure Password Generator v1.0 {C.GRAY}by mainstarkov{C.RESET} {C.GRAY} ────────────────────────────────────────────────────────────────────{C.RESET} """ def generate_password(length: int, upper: bool, lower: bool, digits: bool, symbols: bool, exclude: str) -> str: charset = "" if upper: charset += string.ascii_uppercase if lower: charset += string.ascii_lowercase if digits: charset += string.digits if symbols: charset += "!@#$%^&*()-_=+[]{}|;:,.<>?" for ch in exclude: charset = charset.replace(ch, "") if not charset: print(f"{C.RED} ✗ Error: empty charset. Enable at least one character type.{C.RESET}") sys.exit(1) return "".join(charset[b % len(charset)] for b in os.urandom(length)) def analyze_strength(password: str) -> dict: length = len(password) has_lower = any(c in string.ascii_lowercase for c in password) has_upper = any(c in string.ascii_uppercase for c in password) has_digit = any(c in string.digits for c in password) has_symbol = any(c in "!@#$%^&*()-_=+[]{}|;:,.<>?" for c in password) charset_size = 0 if has_lower: charset_size += 26 if has_upper: charset_size += 26 if has_digit: charset_size += 10 if has_symbol: charset_size += 29 entropy = length * math.log2(charset_size) if charset_size > 0 else 0 combinations = charset_size ** length if charset_size > 0 else 0 if entropy >= 80: grade, color = "EXCELLENT", C.GREEN elif entropy >= 60: grade, color = "STRONG", C.GREEN elif entropy >= 40: grade, color = "MODERATE", C.YELLOW elif entropy >= 28: grade, color = "WEAK", C.RED else: grade, color = "VERY WEAK", C.RED bar_len = min(int(entropy / 4), 20) bar = f"{color}{'█' * bar_len}{C.GRAY}{'░' * (20 - bar_len)}{C.RESET}" return { "length": length, "entropy": round(entropy, 1), "charset_size": charset_size, "combinations": combinations, "grade": grade, "color": color, "bar": bar, "has_lower": has_lower, "has_upper": has_upper, "has_digit": has_digit, "has_symbol": has_symbol, } def format_combinations(n: int) -> str: if n > 10**30: exp = int(math.log10(n)) return f"~10^{exp}" if n > 1_000_000_000: return f"{n:.2e}" return f"{n:,}" def crack_time(combinations: int) -> str: guesses_per_sec = 10_000_000_000 seconds = combinations / guesses_per_sec if seconds < 1: return "instantly" elif seconds < 60: return f"{seconds:.0f} seconds" elif seconds < 3600: return f"{seconds / 60:.0f} minutes" elif seconds < 86400: return f"{seconds / 3600:.0f} hours" elif seconds < 31536000: return f"{seconds / 86400:.0f} days" elif seconds < 31536000 * 1000: return f"{seconds / 31536000:.0f} years" elif seconds < 31536000 * 1_000_000: return f"{seconds / 31536000:.0f} years" else: exp = int(math.log10(seconds / 31536000)) return f"~10^{exp} years" def display_password(password: str, analysis: dict): a = analysis print(f"\n {C.BOLD}{C.WHITE}Generated Password:{C.RESET}") print(f" ╔{'═' * (len(password) + 4)}╗") print(f" ║ {C.BOLD}{C.CYAN}{password}{C.RESET} ║") print(f" ╚{'═' * (len(password) + 4)}╝") print(f"\n {C.BOLD}Strength Analysis:{C.RESET}") print(f" {a['bar']} {a['color']}{C.BOLD}{a['grade']}{C.RESET}") print() print(f" {C.YELLOW}{'Length':<18}{C.RESET} {a['length']} characters") print(f" {C.YELLOW}{'Entropy':<18}{C.RESET} {a['entropy']} bits") print(f" {C.YELLOW}{'Charset size':<18}{C.RESET} {a['charset_size']} characters") print(f" {C.YELLOW}{'Combinations':<18}{C.RESET} {format_combinations(a['combinations'])}") print(f" {C.YELLOW}{'Crack time':<18}{C.RESET} {crack_time(a['combinations'])} (10B guesses/s)") print() print(f" {C.YELLOW}{'Character types':<18}{C.RESET} ", end="") types = [] if a["has_lower"]: types.append(f"{C.GREEN}abc{C.RESET}") if a["has_upper"]: types.append(f"{C.GREEN}ABC{C.RESET}") if a["has_digit"]: types.append(f"{C.GREEN}123{C.RESET}") if a["has_symbol"]: types.append(f"{C.GREEN}!@#{C.RESET}") print(" ".join(types)) print() def main(): parser = argparse.ArgumentParser(prog="passforge", description="Secure password generator") parser.add_argument("-l", "--length", type=int, default=16, help="password length (default: 16)") parser.add_argument("-n", "--count", type=int, default=1, help="number of passwords to generate") parser.add_argument("--no-upper", action="store_true", help="exclude uppercase letters") parser.add_argument("--no-lower", action="store_true", help="exclude lowercase letters") parser.add_argument("--no-digits", action="store_true", help="exclude digits") parser.add_argument("--no-symbols", action="store_true", help="exclude special characters") parser.add_argument("--exclude", default="", help="specific characters to exclude") parser.add_argument("--analyze", metavar="PWD", help="analyze strength of existing password") args = parser.parse_args() print(BANNER) if args.analyze: analysis = analyze_strength(args.analyze) display_password(args.analyze, analysis) return for i in range(args.count): pw = generate_password( length=args.length, upper=not args.no_upper, lower=not args.no_lower, digits=not args.no_digits, symbols=not args.no_symbols, exclude=args.exclude, ) analysis = analyze_strength(pw) display_password(pw, analysis) if i < args.count - 1: print(f" {C.GRAY}{'─' * 50}{C.RESET}") if __name__ == "__main__": main()