Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import os
- import secrets
- import string
- import bcrypt
- import time
- from datetime import datetime
- import itertools
- import geocoder
- import requests
- import shutil
- import sys
- import tarfile
- from tqdm import tqdm
- ########################################################################
- # 1. GET APPROXIMATE LOCATION (OPTIONAL)
- ########################################################################
- def get_real_time_location():
- """
- Retrieves approximate (latitude, longitude) from public IP.
- May be disabled if you don't want to reveal your position.
- """
- g = geocoder.ip('me')
- if g.ok:
- return g.latlng
- else:
- raise ValueError("Unable to retrieve location (optional).")
- ########################################################################
- # 2. CHOOSE A BRIGHT STAR
- ########################################################################
- def get_star_position():
- """
- Returns randomly chosen real star data (RA, Dec, Magnitude)
- from a fixed list of bright stars.
- """
- bright_stars = [
- {"name": "Sirius", "RA": "06h45m08.9s", "Dec": "-16°42'58\"", "Mag": -1.46},
- {"name": "Canopus", "RA": "06h23m57.1s", "Dec": "-52°41'44\"", "Mag": -0.74},
- {"name": "Alpha Centauri", "RA": "14h39m36.5s", "Dec": "-60°50'02\"", "Mag": -0.27},
- {"name": "Arcturus", "RA": "14h15m39.7s", "Dec": "+19°10'57\"", "Mag": -0.04},
- {"name": "Vega", "RA": "18h36m56.3s", "Dec": "+38°47'01\"", "Mag": 0.03},
- {"name": "Capella", "RA": "05h16m41.4s", "Dec": "+45°59'53\"", "Mag": 0.08},
- {"name": "Rigel", "RA": "05h14m32.3s", "Dec": "-08°12'06\"", "Mag": 0.13},
- {"name": "Procyon", "RA": "07h39m18.1s", "Dec": "+05°13'29\"", "Mag": 0.34},
- {"name": "Betelgeuse", "RA": "05h55m10.3s", "Dec": "+07°24'25\"", "Mag": 0.42},
- {"name": "Achernar", "RA": "01h37m42.8s", "Dec": "-57°14'12\"", "Mag": 0.45},
- {"name": "Hadar", "RA": "14h05m23.2s", "Dec": "-60°22'22\"", "Mag": 0.61},
- {"name": "Altair", "RA": "19h50m46.0s", "Dec": "+08°52'06\"", "Mag": 0.77},
- {"name": "Aldebaran", "RA": "04h35m55.2s", "Dec": "+16°30'33\"", "Mag": 0.85},
- {"name": "Spica", "RA": "13h25m11.6s", "Dec": "-11°09'41\"", "Mag": 0.96},
- {"name": "Antares", "RA": "16h29m24.5s", "Dec": "-26°25'55\"", "Mag": 1.06},
- ]
- return secrets.choice(bright_stars)
- ########################################################################
- # 3. GENERATE A SECURE PASSWORD
- ########################################################################
- def generate_secure_password(length=12):
- """
- Generates a secure password using:
- - Python's 'secrets' for cryptographic randomness,
- - A wide character set (letters, digits, punctuation).
- """
- chars = string.ascii_letters + string.digits + string.punctuation
- return ''.join(secrets.choice(chars) for _ in range(length))
- ########################################################################
- # 4. HASH A PASSWORD WITH BCRYPT
- ########################################################################
- def hash_password(password):
- """
- Returns a bcrypt hash of the password (with internal salt/pepper).
- """
- salt = bcrypt.gensalt()
- hashed = bcrypt.hashpw(password.encode('utf-8'), salt)
- return hashed
- ########################################################################
- # 5. ESTIMATE THEORETICAL BRUTE FORCE TIME
- ########################################################################
- def estimate_brute_force_time(password_length, character_set_size, attempts_per_second):
- total_combinations = character_set_size ** password_length
- estimated_time_seconds = total_combinations / attempts_per_second
- return estimated_time_seconds
- ########################################################################
- # 6. FORMAT TIME (MILLENNIA, YEARS, MONTHS...)
- ########################################################################
- def format_time_extended(seconds):
- """
- Formats time in millennia, years, months, days, hours, minutes,
- seconds, and milliseconds.
- 1 millenium = 1000 years, 1 year = 365 days, 1 month ~ 30 days.
- """
- import math
- millenniums, seconds = divmod(seconds, 1000 * 365 * 24 * 3600)
- years, seconds = divmod(seconds, 365 * 24 * 3600)
- months, seconds = divmod(seconds, 30 * 24 * 3600)
- days, seconds = divmod(seconds, 24 * 3600)
- hours, seconds = divmod(seconds, 3600)
- minutes, seconds = divmod(seconds, 60)
- milliseconds = int((seconds % 1) * 1000)
- seconds = int(seconds)
- parts = []
- if millenniums > 0:
- parts.append(f"{int(millenniums)} millennia")
- if years > 0:
- parts.append(f"{int(years)} years")
- if months > 0:
- parts.append(f"{int(months)} months")
- if days > 0:
- parts.append(f"{int(days)} days")
- if hours > 0:
- parts.append(f"{int(hours)} hours")
- if minutes > 0:
- parts.append(f"{int(minutes)} minutes")
- if seconds > 0:
- parts.append(f"{int(seconds)} seconds")
- if milliseconds > 0:
- parts.append(f"{milliseconds} ms")
- if not parts:
- return "0 seconds"
- return ", ".join(parts)
- ########################################################################
- # 7. DECOMPRESS rockyou.txt.tar.gz
- ########################################################################
- def decompress_tar_gz(tar_gz_path, extract_folder="Dictionary"):
- """
- Decompresses a .tar.gz file into 'extract_folder'.
- If it contains 'rockyou.txt', it will be extracted in clear text.
- """
- if not os.path.exists(tar_gz_path):
- print(f"[ERROR] {tar_gz_path} does not exist.")
- return
- print(f"[INFO] Decompressing {tar_gz_path} ...")
- with tarfile.open(tar_gz_path, "r:gz") as tar:
- tar.extractall(path=extract_folder)
- print("[INFO] Decompression complete.")
- ########################################################################
- # 8. DOWNLOAD AND UPDATE PUBLIC DICTIONARIES
- ########################################################################
- def download_public_dictionary(url, dictionary_folder="Dictionary"):
- """
- Download a dictionary from a URL and save it into 'dictionary_folder'.
- If the file is .txt, we save it in text mode, ensuring proper UTF-8 encoding.
- If it's .tar.gz, we save it as binary for later decompression.
- """
- if not os.path.exists(dictionary_folder):
- os.makedirs(dictionary_folder)
- file_name = url.split('/')[-1]
- local_filename = os.path.join(dictionary_folder, file_name)
- if os.path.exists(local_filename):
- print(f"[INFO] {local_filename} already exists. Skipping download.")
- return local_filename
- print(f"[INFO] Downloading {url} ...")
- # Decide whether this is a known compressed format or a raw .txt
- if file_name.endswith(".txt"):
- # We do a text-based download
- response = requests.get(url)
- response.raise_for_status()
- # Write in text mode with UTF-8
- with open(local_filename, 'w', encoding='utf-8') as f:
- f.write(response.text)
- print(f"[INFO] Saved dictionary (text) to {local_filename}")
- else:
- # We do a binary download (tar.gz, zip, etc.)
- with requests.get(url, stream=True) as r:
- r.raise_for_status()
- with open(local_filename, 'wb') as f:
- shutil.copyfileobj(r.raw, f)
- print(f"[INFO] Saved dictionary (binary) to {local_filename}")
- return local_filename
- def update_public_dictionaries(dictionary_urls, dictionary_folder="Dictionary"):
- """
- Ensures that required dictionaries are downloaded and, if needed,
- decompress them (e.g., rockyou.txt.tar.gz -> rockyou.txt).
- """
- for url in dictionary_urls:
- local_path = download_public_dictionary(url, dictionary_folder=dictionary_folder)
- # If it ends with 'rockyou.txt.tar.gz', decompress if we haven't rockyou.txt yet
- if local_path.endswith("rockyou.txt.tar.gz"):
- rockyou_txt_path = os.path.join(dictionary_folder, "rockyou.txt")
- if not os.path.exists(rockyou_txt_path):
- decompress_tar_gz(local_path, extract_folder=dictionary_folder)
- else:
- print("[INFO] 'rockyou.txt' already decompressed. Skipping.")
- ########################################################################
- # 9. DICTIONARY ATTACK
- ########################################################################
- def dictionary_attack(password, dictionaries_folder="Dictionary"):
- """
- Tries a dictionary attack on 'password' in clear.
- Only searches .txt files in the 'dictionaries_folder'.
- """
- if not os.path.exists(dictionaries_folder):
- print("[WARN] No dictionary folder found. Skipping dictionary attack.")
- return False
- files = os.listdir(dictionaries_folder)
- txt_files = [f for f in files if f.lower().endswith(".txt")]
- if not txt_files:
- print("[WARN] No .txt dictionaries found in the folder. Skipping dictionary attack.")
- return False
- for txt_file in txt_files:
- file_path = os.path.join(dictionaries_folder, txt_file)
- print(f"[INFO] Trying dictionary: {file_path}")
- try:
- with open(file_path, 'r', encoding="utf-8", errors="ignore") as f:
- for word in f:
- if word.strip() == password:
- print("[SUCCESS] Dictionary attack found the password!")
- return True
- except Exception as e:
- print(f"[ERROR] Could not read {file_path}: {e}")
- return False
- ########################################################################
- # 10. BRUTE FORCE ATTACK (EXHAUSTIVE) with tqdm
- ########################################################################
- def brute_force_attack(password, character_set=string.ascii_letters + string.digits + string.punctuation):
- """
- May take infinite time if the password is long.
- Displays a single progress bar via tqdm.
- """
- print("\n[INFO] Starting BRUTE FORCE attack (exhaustive). This can be extremely long.")
- start_time = time.time()
- attempts = 0
- total_combinations = 0
- for length in range(1, len(password) + 1):
- total_combinations += len(character_set) ** length
- print(f"[INFO] Total combinations to try: {total_combinations}")
- with tqdm(total=total_combinations, desc="Brute forcing password") as pbar:
- for length in range(1, len(password) + 1):
- for combo in itertools.product(character_set, repeat=length):
- attempts += 1
- candidate = ''.join(combo)
- pbar.update(1)
- if candidate == password:
- pbar.close()
- elapsed = time.time() - start_time
- print(f"\n[SUCCESS] Bruteforce found password '{candidate}'")
- print(f"Attempts: {attempts}, Time: {elapsed:.2f} s")
- return True
- print(f"\n[FAIL] Bruteforce did NOT find the password up to length={len(password)}.")
- return False
- ########################################################################
- # 11. EXPLAIN HOW PASSWORDS ARE GENERATED
- ########################################################################
- def explain_password_generation():
- explanation = (
- "\n==== HOW WE GENERATE YOUR PASSWORDS ====\n"
- "1) We use Python's 'secrets' module for cryptographic randomness.\n"
- "2) The characters are chosen from:\n"
- " - Uppercase letters (A-Z)\n"
- " - Lowercase letters (a-z)\n"
- " - Digits (0-9)\n"
- " - Punctuation (!@#$%^&* etc.)\n"
- "3) Default length is 12, but you can choose more.\n"
- "4) A longer password yields an astronomically large search space.\n"
- "5) We can add 'salt' or extra data (like star info, name) for even more entropy.\n"
- "=========================================\n"
- )
- print(explanation)
- ########################################################################
- # 12. TEST A USER'S PASSWORD
- ########################################################################
- def test_user_password():
- """
- 1) Prompt the user to enter a password
- 2) Perform dictionary attack
- 3) Perform brute force attack
- """
- user_pw = input("Enter the password you want to test: ")
- print("\n[INFO] Dictionary attack...")
- found_dict = dictionary_attack(user_pw)
- if found_dict:
- print("[WARN] Your password was found in the dictionary (Weak).")
- else:
- print("[INFO] Your password was NOT found in the dictionary.")
- brute_force_attack(user_pw)
- ########################################################################
- # 13. GENERATE PASSWORD FLOW
- ########################################################################
- def generate_password_flow():
- """
- 1) Ask for user's first name
- 2) Retrieve location (optional)
- 3) Pick a bright star
- 4) Generate a password (12 chars)
- 5) Show theoretical brute force time + bcrypt hash
- 6) Perform dictionary + brute force attacks
- """
- user_name = input("Enter your first name: ")
- # Optional location
- try:
- lat, lon = get_real_time_location()
- print(f"[INFO] Approx. location: lat={lat}, lon={lon}")
- except Exception:
- print("[INFO] Unable to retrieve location (optional).")
- # Pick a star
- star_data = get_star_position()
- print(f"[INFO] Selected star: {star_data['name']} | RA={star_data['RA']} | "
- f"Dec={star_data['Dec']} | Mag={star_data['Mag']}")
- # Generate password
- generated_password = generate_secure_password(length=12)
- print(f"[INFO] Generated password (plain) for {user_name}: {generated_password}")
- # Theoretical brute force time
- char_set_size = len(string.ascii_letters + string.digits + string.punctuation)
- pw_length = len(generated_password)
- attempts_per_sec = 1e6 # example: 1 million attempts/s
- estimated_sec = estimate_brute_force_time(pw_length, char_set_size, attempts_per_sec)
- print(f"[INFO] Estimated brute force time at 1e6 attempts/s: {format_time_extended(estimated_sec)}")
- # Hash
- hashed_pw = hash_password(generated_password)
- print(f"[INFO] Bcrypt hash of this password: {hashed_pw.decode('utf-8', errors='ignore')}")
- # Dictionary attack
- print("\n[INFO] Attempting dictionary attack on your new password...")
- found_dict = dictionary_attack(generated_password)
- if found_dict:
- print("[WARN] This password was found in a dictionary (Weak).")
- else:
- print("[INFO] This password was NOT found in the tested dictionaries.")
- # Brute force
- brute_force_attack(generated_password)
- ########################################################################
- # 14. MAIN MENU
- ########################################################################
- def main():
- # Update or download dictionaries in advance
- dictionary_urls = [
- "https://raw.githubusercontent.com/danielmiessler/SecLists/master/Passwords/Leaked-Databases/rockyou.txt.tar.gz",
- "https://raw.githubusercontent.com/danielmiessler/SecLists/master/Passwords/Common-Credentials/10k-most-common.txt"
- ]
- update_public_dictionaries(dictionary_urls)
- # Main loop
- while True:
- print("\n========== MAIN MENU ==========")
- print("1) Generate a password (and test it)")
- print("2) Test your own password")
- print("3) How are our passwords generated?")
- print("4) Quit")
- choice = input("Choice [1-4]: ")
- if choice == "1":
- generate_password_flow()
- elif choice == "2":
- test_user_password()
- elif choice == "3":
- explain_password_generation()
- elif choice == "4":
- print("Goodbye!")
- break
- else:
- print("Invalid choice. Please try again.")
- ########################################################################
- # 15. ENTRY POINT
- ########################################################################
- if __name__ == "__main__":
- main()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement