Advertisement
Zadyk

PaswordGen

Jan 16th, 2025
41
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 16.29 KB | Cybersecurity | 0 0
  1. import os
  2. import secrets
  3. import string
  4. import bcrypt
  5. import time
  6. from datetime import datetime
  7. import itertools
  8. import geocoder
  9. import requests
  10. import shutil
  11. import sys
  12. import tarfile
  13. from tqdm import tqdm
  14.  
  15. ########################################################################
  16. # 1. GET APPROXIMATE LOCATION (OPTIONAL)
  17. ########################################################################
  18. def get_real_time_location():
  19.     """
  20.    Retrieves approximate (latitude, longitude) from public IP.
  21.    May be disabled if you don't want to reveal your position.
  22.    """
  23.     g = geocoder.ip('me')
  24.     if g.ok:
  25.         return g.latlng
  26.     else:
  27.         raise ValueError("Unable to retrieve location (optional).")
  28.  
  29. ########################################################################
  30. # 2. CHOOSE A BRIGHT STAR
  31. ########################################################################
  32. def get_star_position():
  33.     """
  34.    Returns randomly chosen real star data (RA, Dec, Magnitude)
  35.    from a fixed list of bright stars.
  36.    """
  37.     bright_stars = [
  38.         {"name": "Sirius",         "RA": "06h45m08.9s", "Dec": "-16°42'58\"", "Mag": -1.46},
  39.         {"name": "Canopus",        "RA": "06h23m57.1s", "Dec": "-52°41'44\"", "Mag": -0.74},
  40.         {"name": "Alpha Centauri", "RA": "14h39m36.5s", "Dec": "-60°50'02\"", "Mag": -0.27},
  41.         {"name": "Arcturus",       "RA": "14h15m39.7s", "Dec": "+19°10'57\"", "Mag": -0.04},
  42.         {"name": "Vega",           "RA": "18h36m56.3s", "Dec": "+38°47'01\"", "Mag": 0.03},
  43.         {"name": "Capella",        "RA": "05h16m41.4s", "Dec": "+45°59'53\"", "Mag": 0.08},
  44.         {"name": "Rigel",          "RA": "05h14m32.3s", "Dec": "-08°12'06\"", "Mag": 0.13},
  45.         {"name": "Procyon",        "RA": "07h39m18.1s", "Dec": "+05°13'29\"", "Mag": 0.34},
  46.         {"name": "Betelgeuse",     "RA": "05h55m10.3s", "Dec": "+07°24'25\"", "Mag": 0.42},
  47.         {"name": "Achernar",       "RA": "01h37m42.8s", "Dec": "-57°14'12\"", "Mag": 0.45},
  48.         {"name": "Hadar",          "RA": "14h05m23.2s", "Dec": "-60°22'22\"", "Mag": 0.61},
  49.         {"name": "Altair",         "RA": "19h50m46.0s", "Dec": "+08°52'06\"", "Mag": 0.77},
  50.         {"name": "Aldebaran",      "RA": "04h35m55.2s", "Dec": "+16°30'33\"", "Mag": 0.85},
  51.         {"name": "Spica",          "RA": "13h25m11.6s", "Dec": "-11°09'41\"", "Mag": 0.96},
  52.         {"name": "Antares",        "RA": "16h29m24.5s", "Dec": "-26°25'55\"", "Mag": 1.06},
  53.     ]
  54.     return secrets.choice(bright_stars)
  55.  
  56. ########################################################################
  57. # 3. GENERATE A SECURE PASSWORD
  58. ########################################################################
  59. def generate_secure_password(length=12):
  60.     """
  61.    Generates a secure password using:
  62.      - Python's 'secrets' for cryptographic randomness,
  63.      - A wide character set (letters, digits, punctuation).
  64.    """
  65.     chars = string.ascii_letters + string.digits + string.punctuation
  66.     return ''.join(secrets.choice(chars) for _ in range(length))
  67.  
  68. ########################################################################
  69. # 4. HASH A PASSWORD WITH BCRYPT
  70. ########################################################################
  71. def hash_password(password):
  72.     """
  73.    Returns a bcrypt hash of the password (with internal salt/pepper).
  74.    """
  75.     salt = bcrypt.gensalt()
  76.     hashed = bcrypt.hashpw(password.encode('utf-8'), salt)
  77.     return hashed
  78.  
  79. ########################################################################
  80. # 5. ESTIMATE THEORETICAL BRUTE FORCE TIME
  81. ########################################################################
  82. def estimate_brute_force_time(password_length, character_set_size, attempts_per_second):
  83.     total_combinations = character_set_size ** password_length
  84.     estimated_time_seconds = total_combinations / attempts_per_second
  85.     return estimated_time_seconds
  86.  
  87. ########################################################################
  88. # 6. FORMAT TIME (MILLENNIA, YEARS, MONTHS...)
  89. ########################################################################
  90. def format_time_extended(seconds):
  91.     """
  92.    Formats time in millennia, years, months, days, hours, minutes,
  93.    seconds, and milliseconds.
  94.    1 millenium = 1000 years, 1 year = 365 days, 1 month ~ 30 days.
  95.    """
  96.     import math
  97.  
  98.     millenniums, seconds = divmod(seconds, 1000 * 365 * 24 * 3600)
  99.     years, seconds = divmod(seconds, 365 * 24 * 3600)
  100.     months, seconds = divmod(seconds, 30 * 24 * 3600)
  101.     days, seconds = divmod(seconds, 24 * 3600)
  102.     hours, seconds = divmod(seconds, 3600)
  103.     minutes, seconds = divmod(seconds, 60)
  104.     milliseconds = int((seconds % 1) * 1000)
  105.     seconds = int(seconds)
  106.  
  107.     parts = []
  108.     if millenniums > 0:
  109.         parts.append(f"{int(millenniums)} millennia")
  110.     if years > 0:
  111.         parts.append(f"{int(years)} years")
  112.     if months > 0:
  113.         parts.append(f"{int(months)} months")
  114.     if days > 0:
  115.         parts.append(f"{int(days)} days")
  116.     if hours > 0:
  117.         parts.append(f"{int(hours)} hours")
  118.     if minutes > 0:
  119.         parts.append(f"{int(minutes)} minutes")
  120.     if seconds > 0:
  121.         parts.append(f"{int(seconds)} seconds")
  122.     if milliseconds > 0:
  123.         parts.append(f"{milliseconds} ms")
  124.  
  125.     if not parts:
  126.         return "0 seconds"
  127.     return ", ".join(parts)
  128.  
  129. ########################################################################
  130. # 7. DECOMPRESS rockyou.txt.tar.gz
  131. ########################################################################
  132. def decompress_tar_gz(tar_gz_path, extract_folder="Dictionary"):
  133.     """
  134.    Decompresses a .tar.gz file into 'extract_folder'.
  135.    If it contains 'rockyou.txt', it will be extracted in clear text.
  136.    """
  137.     if not os.path.exists(tar_gz_path):
  138.         print(f"[ERROR] {tar_gz_path} does not exist.")
  139.         return
  140.  
  141.     print(f"[INFO] Decompressing {tar_gz_path} ...")
  142.     with tarfile.open(tar_gz_path, "r:gz") as tar:
  143.         tar.extractall(path=extract_folder)
  144.     print("[INFO] Decompression complete.")
  145.  
  146. ########################################################################
  147. # 8. DOWNLOAD AND UPDATE PUBLIC DICTIONARIES
  148. ########################################################################
  149. def download_public_dictionary(url, dictionary_folder="Dictionary"):
  150.     """
  151.    Download a dictionary from a URL and save it into 'dictionary_folder'.
  152.    If the file is .txt, we save it in text mode, ensuring proper UTF-8 encoding.
  153.    If it's .tar.gz, we save it as binary for later decompression.
  154.    """
  155.     if not os.path.exists(dictionary_folder):
  156.         os.makedirs(dictionary_folder)
  157.  
  158.     file_name = url.split('/')[-1]
  159.     local_filename = os.path.join(dictionary_folder, file_name)
  160.  
  161.     if os.path.exists(local_filename):
  162.         print(f"[INFO] {local_filename} already exists. Skipping download.")
  163.         return local_filename
  164.  
  165.     print(f"[INFO] Downloading {url} ...")
  166.     # Decide whether this is a known compressed format or a raw .txt
  167.     if file_name.endswith(".txt"):
  168.         # We do a text-based download
  169.         response = requests.get(url)
  170.         response.raise_for_status()
  171.         # Write in text mode with UTF-8
  172.         with open(local_filename, 'w', encoding='utf-8') as f:
  173.             f.write(response.text)
  174.         print(f"[INFO] Saved dictionary (text) to {local_filename}")
  175.     else:
  176.         # We do a binary download (tar.gz, zip, etc.)
  177.         with requests.get(url, stream=True) as r:
  178.             r.raise_for_status()
  179.             with open(local_filename, 'wb') as f:
  180.                 shutil.copyfileobj(r.raw, f)
  181.         print(f"[INFO] Saved dictionary (binary) to {local_filename}")
  182.  
  183.     return local_filename
  184.  
  185.  
  186. def update_public_dictionaries(dictionary_urls, dictionary_folder="Dictionary"):
  187.     """
  188.    Ensures that required dictionaries are downloaded and, if needed,
  189.    decompress them (e.g., rockyou.txt.tar.gz -> rockyou.txt).
  190.    """
  191.     for url in dictionary_urls:
  192.         local_path = download_public_dictionary(url, dictionary_folder=dictionary_folder)
  193.  
  194.         # If it ends with 'rockyou.txt.tar.gz', decompress if we haven't rockyou.txt yet
  195.         if local_path.endswith("rockyou.txt.tar.gz"):
  196.             rockyou_txt_path = os.path.join(dictionary_folder, "rockyou.txt")
  197.             if not os.path.exists(rockyou_txt_path):
  198.                 decompress_tar_gz(local_path, extract_folder=dictionary_folder)
  199.             else:
  200.                 print("[INFO] 'rockyou.txt' already decompressed. Skipping.")
  201.  
  202. ########################################################################
  203. # 9. DICTIONARY ATTACK
  204. ########################################################################
  205. def dictionary_attack(password, dictionaries_folder="Dictionary"):
  206.     """
  207.    Tries a dictionary attack on 'password' in clear.
  208.    Only searches .txt files in the 'dictionaries_folder'.
  209.    """
  210.     if not os.path.exists(dictionaries_folder):
  211.         print("[WARN] No dictionary folder found. Skipping dictionary attack.")
  212.         return False
  213.  
  214.     files = os.listdir(dictionaries_folder)
  215.     txt_files = [f for f in files if f.lower().endswith(".txt")]
  216.  
  217.     if not txt_files:
  218.         print("[WARN] No .txt dictionaries found in the folder. Skipping dictionary attack.")
  219.         return False
  220.  
  221.     for txt_file in txt_files:
  222.         file_path = os.path.join(dictionaries_folder, txt_file)
  223.         print(f"[INFO] Trying dictionary: {file_path}")
  224.         try:
  225.             with open(file_path, 'r', encoding="utf-8", errors="ignore") as f:
  226.                 for word in f:
  227.                     if word.strip() == password:
  228.                         print("[SUCCESS] Dictionary attack found the password!")
  229.                         return True
  230.         except Exception as e:
  231.             print(f"[ERROR] Could not read {file_path}: {e}")
  232.  
  233.     return False
  234.  
  235. ########################################################################
  236. # 10. BRUTE FORCE ATTACK (EXHAUSTIVE) with tqdm
  237. ########################################################################
  238. def brute_force_attack(password, character_set=string.ascii_letters + string.digits + string.punctuation):
  239.     """
  240.    May take infinite time if the password is long.
  241.    Displays a single progress bar via tqdm.
  242.    """
  243.     print("\n[INFO] Starting BRUTE FORCE attack (exhaustive). This can be extremely long.")
  244.     start_time = time.time()
  245.     attempts = 0
  246.  
  247.     total_combinations = 0
  248.     for length in range(1, len(password) + 1):
  249.         total_combinations += len(character_set) ** length
  250.  
  251.     print(f"[INFO] Total combinations to try: {total_combinations}")
  252.  
  253.     with tqdm(total=total_combinations, desc="Brute forcing password") as pbar:
  254.         for length in range(1, len(password) + 1):
  255.             for combo in itertools.product(character_set, repeat=length):
  256.                 attempts += 1
  257.                 candidate = ''.join(combo)
  258.                 pbar.update(1)
  259.  
  260.                 if candidate == password:
  261.                     pbar.close()
  262.                     elapsed = time.time() - start_time
  263.                     print(f"\n[SUCCESS] Bruteforce found password '{candidate}'")
  264.                     print(f"Attempts: {attempts}, Time: {elapsed:.2f} s")
  265.                     return True
  266.  
  267.     print(f"\n[FAIL] Bruteforce did NOT find the password up to length={len(password)}.")
  268.     return False
  269.  
  270. ########################################################################
  271. # 11. EXPLAIN HOW PASSWORDS ARE GENERATED
  272. ########################################################################
  273. def explain_password_generation():
  274.     explanation = (
  275.         "\n==== HOW WE GENERATE YOUR PASSWORDS ====\n"
  276.         "1) We use Python's 'secrets' module for cryptographic randomness.\n"
  277.         "2) The characters are chosen from:\n"
  278.         "   - Uppercase letters (A-Z)\n"
  279.         "   - Lowercase letters (a-z)\n"
  280.         "   - Digits (0-9)\n"
  281.         "   - Punctuation (!@#$%^&* etc.)\n"
  282.         "3) Default length is 12, but you can choose more.\n"
  283.         "4) A longer password yields an astronomically large search space.\n"
  284.         "5) We can add 'salt' or extra data (like star info, name) for even more entropy.\n"
  285.         "=========================================\n"
  286.     )
  287.     print(explanation)
  288.  
  289. ########################################################################
  290. # 12. TEST A USER'S PASSWORD
  291. ########################################################################
  292. def test_user_password():
  293.     """
  294.    1) Prompt the user to enter a password
  295.    2) Perform dictionary attack
  296.    3) Perform brute force attack
  297.    """
  298.     user_pw = input("Enter the password you want to test: ")
  299.     print("\n[INFO] Dictionary attack...")
  300.     found_dict = dictionary_attack(user_pw)
  301.     if found_dict:
  302.         print("[WARN] Your password was found in the dictionary (Weak).")
  303.     else:
  304.         print("[INFO] Your password was NOT found in the dictionary.")
  305.  
  306.     brute_force_attack(user_pw)
  307.  
  308. ########################################################################
  309. # 13. GENERATE PASSWORD FLOW
  310. ########################################################################
  311. def generate_password_flow():
  312.     """
  313.    1) Ask for user's first name
  314.    2) Retrieve location (optional)
  315.    3) Pick a bright star
  316.    4) Generate a password (12 chars)
  317.    5) Show theoretical brute force time + bcrypt hash
  318.    6) Perform dictionary + brute force attacks
  319.    """
  320.     user_name = input("Enter your first name: ")
  321.  
  322.     # Optional location
  323.     try:
  324.         lat, lon = get_real_time_location()
  325.         print(f"[INFO] Approx. location: lat={lat}, lon={lon}")
  326.     except Exception:
  327.         print("[INFO] Unable to retrieve location (optional).")
  328.  
  329.     # Pick a star
  330.     star_data = get_star_position()
  331.     print(f"[INFO] Selected star: {star_data['name']} | RA={star_data['RA']} | "
  332.           f"Dec={star_data['Dec']} | Mag={star_data['Mag']}")
  333.  
  334.     # Generate password
  335.     generated_password = generate_secure_password(length=12)
  336.     print(f"[INFO] Generated password (plain) for {user_name}: {generated_password}")
  337.  
  338.     # Theoretical brute force time
  339.     char_set_size = len(string.ascii_letters + string.digits + string.punctuation)
  340.     pw_length = len(generated_password)
  341.     attempts_per_sec = 1e6  # example: 1 million attempts/s
  342.     estimated_sec = estimate_brute_force_time(pw_length, char_set_size, attempts_per_sec)
  343.     print(f"[INFO] Estimated brute force time at 1e6 attempts/s: {format_time_extended(estimated_sec)}")
  344.  
  345.     # Hash
  346.     hashed_pw = hash_password(generated_password)
  347.     print(f"[INFO] Bcrypt hash of this password: {hashed_pw.decode('utf-8', errors='ignore')}")
  348.  
  349.     # Dictionary attack
  350.     print("\n[INFO] Attempting dictionary attack on your new password...")
  351.     found_dict = dictionary_attack(generated_password)
  352.     if found_dict:
  353.         print("[WARN] This password was found in a dictionary (Weak).")
  354.     else:
  355.         print("[INFO] This password was NOT found in the tested dictionaries.")
  356.  
  357.     # Brute force
  358.     brute_force_attack(generated_password)
  359.  
  360. ########################################################################
  361. # 14. MAIN MENU
  362. ########################################################################
  363. def main():
  364.     # Update or download dictionaries in advance
  365.     dictionary_urls = [
  366.         "https://raw.githubusercontent.com/danielmiessler/SecLists/master/Passwords/Leaked-Databases/rockyou.txt.tar.gz",
  367.         "https://raw.githubusercontent.com/danielmiessler/SecLists/master/Passwords/Common-Credentials/10k-most-common.txt"
  368.     ]
  369.     update_public_dictionaries(dictionary_urls)
  370.  
  371.     # Main loop
  372.     while True:
  373.         print("\n========== MAIN MENU ==========")
  374.         print("1) Generate a password (and test it)")
  375.         print("2) Test your own password")
  376.         print("3) How are our passwords generated?")
  377.         print("4) Quit")
  378.         choice = input("Choice [1-4]: ")
  379.  
  380.         if choice == "1":
  381.             generate_password_flow()
  382.         elif choice == "2":
  383.             test_user_password()
  384.         elif choice == "3":
  385.             explain_password_generation()
  386.         elif choice == "4":
  387.             print("Goodbye!")
  388.             break
  389.         else:
  390.             print("Invalid choice. Please try again.")
  391.  
  392. ########################################################################
  393. # 15. ENTRY POINT
  394. ########################################################################
  395. if __name__ == "__main__":
  396.     main()
  397.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement