Skip to content

Commit 7861c0f

Browse files
authored
Create Password-Strength-Checker.py
1 parent f4a8651 commit 7861c0f

1 file changed

Lines changed: 304 additions & 0 deletions

File tree

Lines changed: 304 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,304 @@
1+
import sys
2+
import os
3+
import random
4+
import string
5+
import math
6+
import time
7+
import hashlib
8+
import threading
9+
import requests
10+
import tkinter as tk
11+
from tkinter import ttk, messagebox
12+
import sv_ttk
13+
from tkinter import filedialog
14+
15+
# =========================
16+
# Helpers
17+
# =========================
18+
def resource_path(file_name):
19+
base_path = getattr(sys, "_MEIPASS", os.path.dirname(os.path.abspath(__file__)))
20+
return os.path.join(base_path, file_name)
21+
22+
def set_status(msg):
23+
status_var.set(msg)
24+
root.update_idletasks()
25+
26+
# =========================
27+
# App Setup
28+
# =========================
29+
root = tk.Tk()
30+
root.title("Password Security Suite")
31+
root.geometry("720x680")
32+
sv_ttk.set_theme("light")
33+
34+
# =========================
35+
# Globals
36+
# =========================
37+
dark_mode_var = tk.BooleanVar(value=False)
38+
show_password_var = tk.BooleanVar(value=False)
39+
40+
password_var = tk.StringVar()
41+
entropy_var = tk.StringVar(value="Entropy: — bits")
42+
crack_time_var = tk.StringVar(value="Time to crack: —")
43+
44+
length_var = tk.IntVar(value=14)
45+
use_upper = tk.BooleanVar(value=True)
46+
use_lower = tk.BooleanVar(value=True)
47+
use_digits = tk.BooleanVar(value=True)
48+
use_symbols = tk.BooleanVar(value=True)
49+
50+
password_history = []
51+
52+
CLIPBOARD_CLEAR_SECONDS = 15
53+
GUESSES_PER_SECOND = 1e10 # realistic offline attack
54+
55+
# =========================
56+
# Theme Toggle
57+
# =========================
58+
def toggle_theme():
59+
bg = "#2E2E2E" if dark_mode_var.get() else "#FFFFFF"
60+
fg = "white" if dark_mode_var.get() else "black"
61+
root.configure(bg=bg)
62+
for w in ["TFrame", "TLabel", "TLabelframe", "TLabelframe.Label", "TCheckbutton"]:
63+
style.configure(w, background=bg, foreground=fg)
64+
password_entry.configure(background=bg, foreground=fg)
65+
66+
# =========================
67+
# Core Security Logic
68+
# =========================
69+
def calculate_entropy(pwd):
70+
pool = 0
71+
if any(c.islower() for c in pwd): pool += 26
72+
if any(c.isupper() for c in pwd): pool += 26
73+
if any(c.isdigit() for c in pwd): pool += 10
74+
if any(c in string.punctuation for c in pwd): pool += len(string.punctuation)
75+
return round(len(pwd) * math.log2(pool), 2) if pool else 0
76+
77+
def estimate_crack_time(entropy):
78+
guesses = 2 ** entropy
79+
seconds = guesses / GUESSES_PER_SECOND
80+
units = [("years", 31536000), ("days", 86400), ("hours", 3600), ("minutes", 60)]
81+
for name, div in units:
82+
if seconds >= div:
83+
return f"{seconds/div:.2f} {name}"
84+
return f"{seconds:.2f} seconds"
85+
86+
def update_strength_visuals(entropy):
87+
progress["value"] = min(entropy, 100)
88+
89+
if entropy < 40:
90+
color, label = "red", "Weak ❌"
91+
elif entropy < 60:
92+
color, label = "orange", "Medium ⚠️"
93+
elif entropy < 80:
94+
color, label = "green", "Strong ✅"
95+
else:
96+
color, label = "purple", "Very Strong 🔥"
97+
98+
style.configure("Entropy.Horizontal.TProgressbar", background=color)
99+
strength_label.config(text=label)
100+
101+
def evaluate_password(pwd):
102+
entropy = calculate_entropy(pwd)
103+
entropy_var.set(f"Entropy: {entropy} bits")
104+
crack_time_var.set(f"Time to crack: {estimate_crack_time(entropy)}")
105+
update_strength_visuals(entropy)
106+
107+
def generate_password():
108+
chars = ""
109+
if use_upper.get(): chars += string.ascii_uppercase
110+
if use_lower.get(): chars += string.ascii_lowercase
111+
if use_digits.get(): chars += string.digits
112+
if use_symbols.get(): chars += string.punctuation
113+
if not chars:
114+
messagebox.showwarning("Error", "Select at least one character type.")
115+
return
116+
117+
pwd = "".join(random.choice(chars) for _ in range(length_var.get()))
118+
password_var.set(pwd)
119+
evaluate_password(pwd)
120+
add_to_history(pwd)
121+
check_breach(pwd)
122+
set_status("Password generated")
123+
124+
# =========================
125+
# Password History
126+
# =========================
127+
def add_to_history(pwd):
128+
password_history.append(pwd)
129+
history_list.insert(tk.END, f"{len(password_history)}{pwd}")
130+
131+
# =========================
132+
# Clipboard Handling
133+
# =========================
134+
def copy_password():
135+
pwd = password_var.get()
136+
if not pwd: return
137+
root.clipboard_clear()
138+
root.clipboard_append(pwd)
139+
set_status("Copied to clipboard (auto-clear enabled)")
140+
threading.Thread(target=clear_clipboard_timer, daemon=True).start()
141+
142+
def clear_clipboard_timer():
143+
time.sleep(CLIPBOARD_CLEAR_SECONDS)
144+
root.clipboard_clear()
145+
set_status("Clipboard cleared")
146+
147+
# =========================
148+
# Export Password TXT
149+
# =========================
150+
def export_history_txt():
151+
if not password_history:
152+
messagebox.showinfo("Empty Vault", "No passwords to export.")
153+
return
154+
155+
file_path = filedialog.asksaveasfilename(
156+
defaultextension=".txt",
157+
filetypes=[("Text Files", "*.txt")],
158+
title="Export Password History"
159+
)
160+
161+
if not file_path:
162+
return # user cancelled
163+
164+
try:
165+
with open(file_path, "w", encoding="utf-8") as f:
166+
f.write("Password History Vault\n")
167+
f.write("=" * 30 + "\n\n")
168+
for i, pwd in enumerate(password_history, 1):
169+
f.write(f"{i}. {pwd}\n")
170+
171+
set_status("Password history exported")
172+
messagebox.showinfo("Export Successful", "Password history saved securely.")
173+
except Exception as e:
174+
messagebox.showerror("Export Failed", str(e))
175+
176+
# =========================
177+
# Password Visibility
178+
# =========================
179+
def toggle_password_visibility():
180+
password_entry.config(show="" if show_password_var.get() else "•")
181+
182+
# =========================
183+
# HIBP Breach Check (k-Anonymity)
184+
# =========================
185+
def check_breach(pwd):
186+
sha1 = hashlib.sha1(pwd.encode()).hexdigest().upper()
187+
prefix, suffix = sha1[:5], sha1[5:]
188+
189+
try:
190+
res = requests.get(
191+
f"https://api.pwnedpasswords.com/range/{prefix}",
192+
timeout=5
193+
)
194+
for line in res.text.splitlines():
195+
if suffix in line:
196+
messagebox.showwarning(
197+
"⚠ Breach Found",
198+
"This password has appeared in known data breaches!"
199+
)
200+
set_status("Breach detected")
201+
return
202+
set_status("Password not found in breaches")
203+
except Exception:
204+
set_status("Breach check failed (offline)")
205+
206+
# =========================
207+
# Styles
208+
# =========================
209+
style = ttk.Style()
210+
style.theme_use("clam")
211+
style.configure("Action.TButton", font=("Segoe UI", 11, "bold"), padding=8)
212+
style.configure("Entropy.Horizontal.TProgressbar", thickness=12)
213+
214+
# =========================
215+
# Status Bar
216+
# =========================
217+
status_var = tk.StringVar(value="Ready")
218+
ttk.Label(root, textvariable=status_var, anchor="w").pack(side=tk.BOTTOM, fill="x")
219+
220+
# =========================
221+
# UI Layout
222+
# =========================
223+
main = ttk.Frame(root, padding=20)
224+
main.pack(expand=True, fill="both")
225+
226+
ttk.Label(main, text="Password Security Suite",
227+
font=("Segoe UI", 22, "bold")).pack()
228+
229+
password_entry = ttk.Entry(
230+
main, textvariable=password_var,
231+
font=("Segoe UI", 14), justify="center", show="•"
232+
)
233+
password_entry.pack(fill="x", pady=8)
234+
235+
ttk.Checkbutton(main, text="Show Password",
236+
variable=show_password_var,
237+
command=toggle_password_visibility).pack()
238+
239+
ttk.Label(main, textvariable=entropy_var,
240+
font=("Segoe UI", 11, "bold")).pack()
241+
242+
ttk.Label(main, textvariable=crack_time_var,
243+
font=("Segoe UI", 10)).pack()
244+
245+
strength_label = ttk.Label(main, text="—",
246+
font=("Segoe UI", 12, "bold"))
247+
strength_label.pack(pady=4)
248+
249+
progress = ttk.Progressbar(
250+
main, style="Entropy.Horizontal.TProgressbar",
251+
maximum=100, length=420
252+
)
253+
progress.pack(pady=8)
254+
255+
# =========================
256+
# Controls
257+
# =========================
258+
controls = ttk.Frame(main)
259+
controls.pack(pady=8)
260+
261+
ttk.Button(controls, text="🔐 Generate",
262+
command=generate_password,
263+
style="Action.TButton").pack(side="left", padx=4)
264+
265+
ttk.Button(controls, text="📋 Copy",
266+
command=copy_password,
267+
style="Action.TButton").pack(side="left", padx=4)
268+
269+
ttk.Button(controls, text="📤 Export to TXT",
270+
command=export_history_txt,
271+
style="Action.TButton").pack(side="left", padx=4)
272+
273+
# =========================
274+
# History Vault
275+
# =========================
276+
vault = ttk.LabelFrame(main, text="Password History Vault", padding=10)
277+
vault.pack(fill="both", expand=True, pady=10)
278+
279+
history_list = tk.Listbox(vault, font=("Segoe UI", 10), height=6)
280+
history_list.pack(fill="both", expand=True)
281+
282+
# =========================
283+
# Options
284+
# =========================
285+
options = ttk.LabelFrame(main, text="Password Options", padding=10)
286+
options.pack(fill="x", pady=8)
287+
288+
ttk.Label(options, text="Length:").grid(row=0, column=0)
289+
ttk.Spinbox(options, from_=6, to=64,
290+
textvariable=length_var, width=6).grid(row=0, column=1)
291+
292+
ttk.Checkbutton(options, text="Uppercase", variable=use_upper).grid(row=1, column=0)
293+
ttk.Checkbutton(options, text="Lowercase", variable=use_lower).grid(row=1, column=1)
294+
ttk.Checkbutton(options, text="Numbers", variable=use_digits).grid(row=2, column=0)
295+
ttk.Checkbutton(options, text="Symbols", variable=use_symbols).grid(row=2, column=1)
296+
297+
ttk.Checkbutton(main, text="Dark Mode",
298+
variable=dark_mode_var,
299+
command=toggle_theme).pack(pady=6)
300+
301+
# =========================
302+
# Run App
303+
# =========================
304+
root.mainloop()

0 commit comments

Comments
 (0)