Загрузка данных
import random
import string
import math
from pathlib import Path
import tkinter as tk
from tkinter import ttk, messagebox
# ================== ЛОГИКА РАБОТЫ С ПАРОЛЯМИ (без GUI) ==================
def generate_password(length: int,
use_letters: bool = True,
use_digits: bool = True,
use_symbols: bool = True) -> str:
"""
Генерация случайного пароля заданной длины.
:param length: длина пароля в символах
:param use_letters: использовать ли буквы (латинские, строчные и заглавные)
:param use_digits: использовать ли цифры
:param use_symbols: использовать ли спецсимволы
:return: сгенерированный пароль в виде строки
"""
# Формируем "алфавит" символов, из которых можно собирать пароль
alphabet = ""
if use_letters:
# string.ascii_letters содержит 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
alphabet += string.ascii_letters
if use_digits:
# string.digits содержит '0123456789'
alphabet += string.digits
if use_symbols:
# Набор спецсимволов задаём вручную
alphabet += "!@#$%^&*()-_=+[]{};:,.?/"
# Если пользователь отключил все типы символов — это ошибка
if not alphabet:
raise ValueError("Нужно выбрать хотя бы один тип символов")
# Собираем пароль из случайных символов выбранного алфавита
return "".join(random.choice(alphabet) for _ in range(length))
def load_common_passwords(path: str = "common_passwords.txt") -> set[str]:
"""
Загружает список распространённых паролей из файла.
Предполагается, что в файле по одному паролю в строке.
Используем множество (set), чтобы быстро проверять вхождение.
:param path: путь к файлу со списком паролей
:return: множество строк-паролей
"""
p = Path(path)
# Если файла нет, просто возвращаем пустое множество,
# чтобы программа не падала, а работала без этой проверки
if not p.exists():
return set()
# Читаем файл целиком, разбиваем по строкам, обрезаем пробелы
return {
line.strip()
for line in p.read_text(encoding="utf-8").splitlines()
if line.strip() # пропускаем пустые строки
}
def check_strength(password: str, common_passwords: set[str]) -> dict:
"""
Проверяет пароль по нескольким критериям:
- длина
- наличие строчных/заглавных букв, цифр, спецсимволов
- присутствие в списке популярных паролей
Возвращает словарь с подробной информацией и итоговым уровнем.
"""
length = len(password)
# Проверяем, есть ли хотя бы один символ каждого типа
has_lower = any(c.islower() for c in password)
has_upper = any(c.isupper() for c in password)
has_digit = any(c.isdigit() for c in password)
# not c.isalnum() — символ не является буквой или цифрой, значит спецсимвол
has_symbol = any(not c.isalnum() for c in password)
# Проверяем, входит ли пароль в список распространённых
in_common = password in common_passwords
# Подсчитываем "очки" надёжности
score = 0
if length >= 8:
score += 1
if length >= 12:
score += 1
if has_lower and has_upper:
score += 1
if has_digit:
score += 1
if has_symbol:
score += 1
# Если пароль часто встречается, независимо от остальных критериев — считаем его слабым
if in_common:
score = 0
# Переводим числовой балл в текстовый уровень
if score <= 1:
level = "слабый"
elif score <= 3:
level = "средний"
else:
level = "сильный"
# Возвращаем подробный отчёт в виде словаря
return {
"length": length,
"has_lower": has_lower,
"has_upper": has_upper,
"has_digit": has_digit,
"has_symbol": has_symbol,
"in_common": in_common,
"score": score,
"level": level,
}
# Небольшой встроенный словарь слов для генерации парольных фраз
WORDS = [
"cat", "dog", "tree", "cloud", "river", "stone", "mouse", "green",
"red", "blue", "star", "music", "black", "white", "coffee", "book",
"sun", "moon", "road", "car"
]
def generate_passphrase(num_words: int = 4, add_number: bool = True) -> str:
"""
Генерирует запоминающуюся парольную фразу из нескольких случайных слов.
:param num_words: сколько слов использовать в фразе
:param add_number: добавлять ли в конце случайное число
:return: фраза вида 'cat-dog-tree42'
"""
# Выбираем нужное количество случайных слов из списка WORDS
words = [random.choice(WORDS) for _ in range(num_words)]
# Соединяем слова через дефис
phrase = "-".join(words)
# По желанию добавляем в конец случайное число от 0 до 99
if add_number:
phrase += str(random.randint(0, 99))
return phrase
def estimate_bruteforce_time(password: str,
attempts_per_second: float = 1e9) -> dict:
"""
Оценивает время перебора пароля методом грубой силы (brute force).
Предполагаем, что атакующий знает, какие типы символов используются,
и перебирает все возможные комбинации заданного алфавита.
:param password: пароль, который оцениваем
:param attempts_per_second: предполагаемая скорость перебора (попыток в секунду)
:return: словарь с размером алфавита, числом комбинаций и временем в секундах/человекочитаемом виде
"""
length = len(password)
alphabet_size = 0
# Определяем, какие типы символов присутствуют в пароле,
# и по этому оцениваем размер алфавита
if any(c.islower() for c in password):
alphabet_size += 26 # строчные латинские буквы
if any(c.isupper() for c in password):
alphabet_size += 26 # заглавные латинские буквы
if any(c.isdigit() for c in password):
alphabet_size += 10 # цифры 0-9
if any(not c.isalnum() for c in password):
# Грубо считаем около 32 возможных спецсимволов
alphabet_size += 32
# Если пароль пустой или алфавит не определён — время взлома считать бессмысленно
if alphabet_size == 0 or length == 0:
return {
"seconds": 0,
"formatted": "0 секунд",
"alphabet_size": 0,
"combinations": 0,
}
# Общее число возможных комбинаций: A^L (A — размер алфавита, L — длина пароля)
combinations = alphabet_size ** length
# Время (в секундах) = количество комбинаций / скорость перебора
seconds = combinations / attempts_per_second
# Внутренняя функция для красивого форматирования времени
def format_time(sec: float) -> str:
# Меньше минуты — показываем в секундах
if sec < 60:
return f"{sec:.2f} секунд"
minutes = sec / 60
# Меньше часа — в минутах
if minutes < 60:
return f"{minutes:.2f} минут"
hours = minutes / 60
# Меньше суток — в часах
if hours < 24:
return f"{hours:.2f} часов"
days = hours / 24
# Меньше года — в днях
if days < 365:
return f"{days:.2f} дней"
years = days / 365
# Больше года — в годах в экспоненциальном виде
return f"{years:.2e} лет"
# Возвращаем подробную информацию
return {
"alphabet_size": alphabet_size,
"combinations": combinations,
"seconds": seconds,
"formatted": format_time(seconds),
}
# ================== TKINTER GUI ==================
class PasswordApp(tk.Tk):
"""
Класс графического приложения на Tkinter.
Наследуемся от tk.Tk, чтобы описать окно и все его виджеты.
"""
def __init__(self):
# Инициализируем базовый класс окна
super().__init__()
# Задаём заголовок окна и размер
self.title("Генератор и проверка паролей")
self.geometry("700x450")
# Один раз загружаем список популярных паролей
self.common_passwords = load_common_passwords()
# Создаём виджет Notebook — это набор вкладок (как в браузере)
notebook = ttk.Notebook(self)
# Размещаем notebook так, чтобы он занимал всё окно
notebook.pack(fill="both", expand=True, padx=10, pady=10)
# Создаём отдельный фрейм (страницу) под каждую задачу
self.tab_gen = ttk.Frame(notebook)
self.tab_check = ttk.Frame(notebook)
self.tab_phrase = ttk.Frame(notebook)
self.tab_bruteforce = ttk.Frame(notebook)
# Добавляем страницы во вкладки с подписями
notebook.add(self.tab_gen, text="Генерация пароля")
notebook.add(self.tab_check, text="Проверка пароля")
notebook.add(self.tab_phrase, text="Парольная фраза")
notebook.add(self.tab_bruteforce, text="Время взлома")
# Инициализируем содержимое каждой вкладки
self.create_gen_tab()
self.create_check_tab()
self.create_phrase_tab()
self.create_bruteforce_tab()
# ---------- Вкладка 1: генерация пароля ----------
def create_gen_tab(self):
"""Создание элементов интерфейса для вкладки генерации пароля."""
frame = self.tab_gen
# Переменные Tkinter, к которым будут "привязаны" виджеты
self.gen_length_var = tk.IntVar(value=12) # длина пароля
self.gen_letters_var = tk.BooleanVar(value=True) # использовать буквы
self.gen_digits_var = tk.BooleanVar(value=True) # использовать цифры
self.gen_symbols_var = tk.BooleanVar(value=True) # использовать спецсимволы
self.gen_result_var = tk.StringVar() # результат (сам пароль)
# Подпись и поле ввода длины пароля
ttk.Label(frame, text="Длина пароля:").grid(row=0, column=0, sticky="w", padx=5, pady=5)
ttk.Spinbox(frame, from_=4, to=64, textvariable=self.gen_length_var, width=5).grid(
row=0, column=1, sticky="w", padx=5, pady=5
)
# Чекбоксы для выбора типов символов
ttk.Checkbutton(frame, text="Буквы (a-z, A-Z)", variable=self.gen_letters_var).grid(
row=1, column=0, columnspan=2, sticky="w", padx=5, pady=2
)
ttk.Checkbutton(frame, text="Цифры (0-9)", variable=self.gen_digits_var).grid(
row=2, column=0, columnspan=2, sticky="w", padx=5, pady=2
)
ttk.Checkbutton(frame, text="Спецсимволы (!@#$...)", variable=self.gen_symbols_var).grid(
row=3, column=0, columnspan=2, sticky="w", padx=5, pady=2
)
# Кнопка генерации
ttk.Button(frame, text="Сгенерировать пароль", command=self.on_generate_password).grid(
row=4, column=0, columnspan=2, pady=10
)
# Поле для вывода результата
ttk.Label(frame, text="Результат:").grid(row=5, column=0, sticky="w", padx=5, pady=5)
ttk.Entry(frame, textvariable=self.gen_result_var, width=40).grid(
row=5, column=1, sticky="w", padx=5, pady=5
)
def on_generate_password(self):
"""Обработчик нажатия кнопки 'Сгенерировать пароль'."""
try:
# Берём длину и флаги из GUI-переменных
length = int(self.gen_length_var.get())
pwd = generate_password(
length,
self.gen_letters_var.get(),
self.gen_digits_var.get(),
self.gen_symbols_var.get(),
)
# Кладём результат в StringVar, к которому привязан Entry
self.gen_result_var.set(pwd)
except ValueError as e:
# Показываем всплывающее окно с сообщением об ошибке
messagebox.showerror("Ошибка", str(e))
# ---------- Вкладка 2: проверка пароля ----------
def create_check_tab(self):
"""Создание элементов интерфейса для вкладки проверки пароля."""
frame = self.tab_check
self.check_input_var = tk.StringVar() # сюда пользователь вводит пароль для проверки
ttk.Label(frame, text="Пароль для проверки:").grid(
row=0, column=0, sticky="w", padx=5, pady=5
)
ttk.Entry(frame, textvariable=self.check_input_var, width=40, show="*").grid(
row=0, column=1, sticky="w", padx=5, pady=5
)
ttk.Button(frame, text="Проверить надёжность", command=self.on_check_password).grid(
row=1, column=0, columnspan=2, pady=10
)
# Многострочный текстовый виджет для вывода отчёта
self.check_result_text = tk.Text(frame, height=10, width=60, state="disabled")
self.check_result_text.grid(row=2, column=0, columnspan=2, padx=5, pady=5)
def on_check_password(self):
"""Обработчик нажатия кнопки 'Проверить надёжность'."""
pwd = self.check_input_var.get()
if not pwd:
messagebox.showwarning("Внимание", "Введите пароль для проверки.")
return
info = check_strength(pwd, self.common_passwords)
# Разрешаем запись в Text
self.check_result_text.config(state="normal")
# Очищаем предыдущее содержимое
self.check_result_text.delete("1.0", "end")
# Печатаем подробную информацию по паролю
self.check_result_text.insert("end", f"Длина: {info['length']}\n")
self.check_result_text.insert(
"end",
f"Строчные буквы: {info['has_lower']}\n"
f"Заглавные буквы: {info['has_upper']}\n"
f"Цифры: {info['has_digit']}\n"
f"Спецсимволы: {info['has_symbol']}\n",
)
self.check_result_text.insert("end", f"В списке популярных: {info['in_common']}\n")
self.check_result_text.insert("end", f"Уровень надёжности: {info['level']}\n")
# Запрещаем редактирование пользователем
self.check_result_text.config(state="disabled")
# ---------- Вкладка 3: генерация парольной фразы ----------
def create_phrase_tab(self):
"""Создание элементов интерфейса для вкладки парольной фразы."""
frame = self.tab_phrase
self.phrase_num_words_var = tk.IntVar(value=4) # сколько слов во фразе
self.phrase_add_number_var = tk.BooleanVar(value=True) # добавлять ли число
self.phrase_result_var = tk.StringVar() # результат
ttk.Label(frame, text="Количество слов:").grid(
row=0, column=0, sticky="w", padx=5, pady=5
)
ttk.Spinbox(frame, from_=2, to=10, textvariable=self.phrase_num_words_var, width=5).grid(
row=0, column=1, sticky="w", padx=5, pady=5
)
ttk.Checkbutton(frame, text="Добавить число в конец", variable=self.phrase_add_number_var).grid(
row=1, column=0, columnspan=2, sticky="w", padx=5, pady=5
)
ttk.Button(frame, text="Сгенерировать фразу", command=self.on_generate_phrase).grid(
row=2, column=0, columnspan=2, pady=10
)
ttk.Label(frame, text="Результат:").grid(row=3, column=0, sticky="w", padx=5, pady=5)
ttk.Entry(frame, textvariable=self.phrase_result_var, width=50).grid(
row=3, column=1, sticky="w", padx=5, pady=5
)
def on_generate_phrase(self):
"""Обработчик нажатия кнопки 'Сгенерировать фразу'."""
try:
n = int(self.phrase_num_words_var.get())
except ValueError:
messagebox.showerror("Ошибка", "Некорректное число слов.")
return
phrase = generate_passphrase(n, self.phrase_add_number_var.get())
self.phrase_result_var.set(phrase)
# ---------- Вкладка 4: оценка времени взлома ----------
def create_bruteforce_tab(self):
"""Создание элементов интерфейса для вкладки оценки времени взлома."""
frame = self.tab_bruteforce
self.brute_password_var = tk.StringVar() # пароль для оценки
self.brute_speed_var = tk.StringVar(value="1000000000") # скорость перебора (1e9 по умолчанию)
ttk.Label(frame, text="Пароль:").grid(row=0, column=0, sticky="w", padx=5, pady=5)
ttk.Entry(frame, textvariable=self.brute_password_var, width=40, show="*").grid(
row=0, column=1, sticky="w", padx=5, pady=5
)
ttk.Label(frame, text="Скорость перебора (попыток/сек):").grid(
row=1, column=0, sticky="w", padx=5, pady=5
)
ttk.Entry(frame, textvariable=self.brute_speed_var, width=20).grid(
row=1, column=1, sticky="w", padx=5, pady=5
)
ttk.Button(frame, text="Оценить время взлома", command=self.on_bruteforce_estimate).grid(
row=2, column=0, columnspan=2, pady=10
)
self.brute_result_text = tk.Text(frame, height=10, width=60, state="disabled")
self.brute_result_text.grid(row=3, column=0, columnspan=2, padx=5, pady=5)
def on_bruteforce_estimate(self):
"""Обработчик нажатия кнопки 'Оценить время взлома'."""
pwd = self.brute_password_var.get()
if not pwd:
messagebox.showwarning("Внимание", "Введите пароль.")
return
speed_str = self.brute_speed_var.get().strip()
# Если пользователь что-то ввёл — пытаемся преобразовать в число
if speed_str:
try:
attempts_per_second = float(speed_str)
except ValueError:
messagebox.showerror("Ошибка", "Некорректная скорость перебора.")
return
else:
# Если поле пустое, используем значение по умолчанию
attempts_per_second = 1e9
est = estimate_bruteforce_time(pwd, attempts_per_second)
self.brute_result_text.config(state="normal")
self.brute_result_text.delete("1.0", "end")
self.brute_result_text.insert("end", f"Размер алфавита: {est['alphabet_size']}\n")
self.brute_result_text.insert("end", f"Количество вариантов: {est['combinations']}\n")
self.brute_result_text.insert("end", f"Оценка времени перебора: {est['formatted']}\n")
self.brute_result_text.config(state="disabled")
# ================== ТОЧКА ВХОДА ==================
def main():
"""
Главная точка входа в программу.
Здесь мы создаём экземпляр нашего графического приложения
и запускаем главный цикл обработки событий Tkinter.
"""
app = PasswordApp()
app.mainloop() # запускаем цикл обработки событий (ожидание кликов, ввода и т.д.)
# Запускаем main() только если файл исполнен как скрипт,
# а не импортирован как модуль
if __name__ == "__main__":
main()