/
ylts
/
PasswordGenerator
Обзор
Документация
Войти
/
ylts
/
PasswordGenerator
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
TestPassword/TestPassword.py
449 строк
19 KB
Sergey
Add all logic
26 июн 2026, 15:46
26 июн 2026, 15:46
9b5b513
Код
Авторство
О чём код?
import math import re from typing import Dict, Union, List, Optional import json class PasswordCrackerEstimator: def __init__(self, speed: int = 1_000_000): self.speed = speed self.character_sets = { 'digits_only': { 'chars': 10, 'label': 'Только цифры', 'description': '0-9' }, 'lowercase_latin': { 'chars': 26, 'label': 'Только строчные латиница', 'description': 'a-z' }, 'uppercase_latin': { 'chars': 26, 'label': 'Только заглавные латиница', 'description': 'A-Z' }, 'mixed_case_latin': { 'chars': 52, 'label': 'Смешанный регистр (латиница)', 'description': 'A-Z, a-z' }, 'latin_with_numbers': { 'chars': 62, 'label': 'Латиница + цифры', 'description': 'A-Z, a-z, 0-9' }, 'cyrillic_lowercase': { 'chars': 33, 'label': 'Только строчные кириллица', 'description': 'а-я (без ё)' }, 'cyrillic_uppercase': { 'chars': 33, 'label': 'Только заглавные кириллица', 'description': 'А-Я (без Ё)' }, 'cyrillic_mixed': { 'chars': 66, 'label': 'Кириллица (все регистры)', 'description': 'А-Я, а-я (без ё)' }, 'cyrillic_with_numbers': { 'chars': 76, 'label': 'Кириллица + цифры', 'description': 'А-Я, а-я, 0-9' }, 'latin_cyrillic': { 'chars': 118, 'label': 'Латиница + кириллица', 'description': 'A-Z, a-z, А-Я, а-я' }, 'latin_cyrillic_numbers': { 'chars': 128, 'label': 'Латиница + кириллица + цифры', 'description': 'A-Z, a-z, А-Я, а-я, 0-9' }, 'with_special': { 'chars': 94, 'label': 'Полный набор ASCII (со спецсимволами)', 'description': 'A-Z, a-z, 0-9, !@#$%^&*...' }, 'unicode_full': { 'chars': 0, 'label': 'Unicode (все буквы)', 'description': 'Все буквенные символы Unicode' } } self._cache = {} def _get_unicode_letter_count(self) -> int: if not hasattr(self, '_unicode_letter_cache'): count = 0 for code_point in range(0x0041, 0xFFFF): try: char = chr(code_point) if char.isalpha() and not char.isascii(): count += 1 except: continue self._unicode_letter_cache = count return self._unicode_letter_cache def _detect_complexity(self, password: str) -> Dict[str, Union[str, int]]: if not password: return { 'type': 'empty', 'label': 'Пустой пароль', 'char_count': 0 } has_latin_lower = bool(re.search(r'[a-z]', password)) has_latin_upper = bool(re.search(r'[A-Z]', password)) has_cyrillic = bool(re.search(r'[А-Яа-яЁё]', password)) has_digit = bool(re.search(r'[0-9]', password)) has_special_ascii = bool(re.search(r'[^A-Za-z0-9А-Яа-яЁё]', password)) other_letters = [] for char in password: if char.isalpha() and not char.isascii() and not char in 'АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдеёжзийклмнопрстуфхцчшщъыьэюя': other_letters.append(char) has_other_unicode = bool(other_letters) unique_chars = set(password) char_count = len(unique_chars) char_count_calculated = 0 if has_latin_lower: char_count_calculated += 26 if has_latin_upper: char_count_calculated += 26 if has_cyrillic: char_count_calculated += 66 if has_digit: char_count_calculated += 10 if has_special_ascii: char_count_calculated += 32 if has_other_unicode: unicode_letters = set() for char in password: if char.isalpha() and not char.isascii() and not char in 'АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдеёжзийклмнопрстуфхцчшщъыьэюя': unicode_letters.add(char) char_count_calculated += len(unicode_letters) if has_special_ascii: complexity_type = 'with_special' label = 'Полный набор ASCII (со спецсимволами)' elif has_other_unicode: complexity_type = 'unicode_full' label = f'Unicode ({len(unicode_letters)} буквенных символов)' elif has_cyrillic and (has_latin_lower or has_latin_upper): if has_digit: complexity_type = 'latin_cyrillic_numbers' label = 'Латиница + кириллица + цифры' else: complexity_type = 'latin_cyrillic' label = 'Латиница + кириллица' elif has_cyrillic and has_digit: complexity_type = 'cyrillic_with_numbers' label = 'Кириллица + цифры' elif has_cyrillic and has_latin_lower and has_latin_upper: complexity_type = 'cyrillic_mixed' label = 'Кириллица (все регистры)' elif has_latin_lower and has_latin_upper and has_digit: complexity_type = 'latin_with_numbers' label = 'Латиница + цифры' elif has_latin_lower and has_latin_upper: complexity_type = 'mixed_case_latin' label = 'Смешанный регистр (латиница)' elif has_digit and not has_latin_lower and not has_latin_upper and not has_cyrillic: complexity_type = 'digits_only' label = 'Только цифры' elif has_latin_lower and not has_latin_upper and not has_digit and not has_cyrillic: complexity_type = 'lowercase_latin' label = 'Только строчные латиница' elif has_latin_upper and not has_latin_lower and not has_digit and not has_cyrillic: complexity_type = 'uppercase_latin' label = 'Только заглавные латиница' elif has_cyrillic and not has_latin_lower and not has_latin_upper and not has_digit: has_cyrillic_lower = bool(re.search(r'[а-яё]', password)) has_cyrillic_upper = bool(re.search(r'[А-ЯЁ]', password)) if has_cyrillic_lower and has_cyrillic_upper: complexity_type = 'cyrillic_mixed' label = 'Кириллица (все регистры)' elif has_cyrillic_lower: complexity_type = 'cyrillic_lowercase' label = 'Только строчные кириллица' else: complexity_type = 'cyrillic_uppercase' label = 'Только заглавные кириллица' else: complexity_type = 'custom' label = f'Уникальные символы ({char_count})' char_count_calculated = char_count if complexity_type == 'unicode_full': char_count_calculated = len(unicode_letters) if has_other_unicode else self._get_unicode_letter_count() elif complexity_type == 'with_special': char_count_calculated = 94 elif complexity_type in self.character_sets: if self.character_sets[complexity_type]['chars'] > 0: char_count_calculated = self.character_sets[complexity_type]['chars'] return { 'type': complexity_type, 'label': label, 'char_count': char_count_calculated, 'has_cyrillic': has_cyrillic, 'has_latin': has_latin_lower or has_latin_upper, 'has_digit': has_digit, 'has_special': has_special_ascii, 'has_unicode': has_other_unicode } def _format_time(self, seconds: float) -> str: if seconds < 1: return "Мгновенно" if seconds < 60: return f"{int(seconds)} секунд" minutes = seconds / 60 if minutes < 60: return f"{int(minutes)} минут" hours = minutes / 60 if hours < 24: return f"{int(hours)} часов" days = hours / 24 if days < 30: return f"{int(days)} дней" months = days / 30 if months < 12: return f"{int(months)} месяцев" years = months / 12 if years < 1000: return f"{int(years)} лет" if years < 1_000_000: return f"{int(years/1000)} тысяч лет" if years < 1_000_000_000: return f"{int(years/1_000_000)} миллионов лет" return f"{int(years/1_000_000_000)} миллиардов лет" def _get_entropy(self, password: str) -> float: complexity_info = self._detect_complexity(password) char_count = complexity_info['char_count'] length = len(password) if char_count == 0 or length == 0: return 0 return length * math.log2(char_count) def estimate(self, password: str, use_cache: bool = True) -> Dict[str, Union[str, int, float]]: if use_cache and password in self._cache: return self._cache[password].copy() if not password: result = { 'password': '', 'length': 0, 'complexity': 'Пустой пароль', 'complexity_type': 'empty', 'char_set_size': 0, 'total_combinations': 0, 'crack_time_seconds': 0, 'crack_time_human': 'Мгновенно', 'strength': 'Критически слабый', 'entropy_bits': 0, 'speed': self.speed, 'has_cyrillic': False, 'has_latin': False, 'has_digit': False, 'has_special': False, 'has_unicode': False } if use_cache: self._cache[password] = result return result.copy() complexity_info = self._detect_complexity(password) length = len(password) char_count = complexity_info['char_count'] total_combinations = char_count ** length crack_time_seconds = total_combinations / self.speed entropy = self._get_entropy(password) if crack_time_seconds < 1: strength = "Критически слабый" elif crack_time_seconds < 60: strength = "Очень слабый" elif crack_time_seconds < 3600: strength = "Слабый" elif crack_time_seconds < 86400: strength = "Средний" elif crack_time_seconds < 2592000: strength = "Выше среднего" elif crack_time_seconds < 31536000: strength = "Сильный" else: strength = "Очень сильный" result = { 'password': password, 'length': length, 'complexity': complexity_info['label'], 'complexity_type': complexity_info['type'], 'char_set_size': char_count, 'total_combinations': total_combinations, 'crack_time_seconds': crack_time_seconds, 'crack_time_human': self._format_time(crack_time_seconds), 'strength': strength, 'entropy_bits': round(entropy, 2), 'speed': self.speed, 'has_cyrillic': complexity_info.get('has_cyrillic', False), 'has_latin': complexity_info.get('has_latin', False), 'has_digit': complexity_info.get('has_digit', False), 'has_special': complexity_info.get('has_special', False), 'has_unicode': complexity_info.get('has_unicode', False) } if use_cache: self._cache[password] = result return result.copy() def estimate_batch(self, passwords: List[str], use_cache: bool = True) -> List[Dict]: return [self.estimate(pwd, use_cache) for pwd in passwords] def get_strength_rating(self, password: str) -> str: return self.estimate(password)['strength'] def get_entropy(self, password: str) -> float: return self.estimate(password)['entropy_bits'] def is_strong(self, password: str, threshold: str = "Средний") -> bool: strength_levels = [ "Критически слабый", "Очень слабый", "Слабый", "Средний", "Выше среднего", "Сильный", "Очень сильный" ] current_strength = self.get_strength_rating(password) threshold_index = strength_levels.index(threshold) current_index = strength_levels.index(current_strength) return current_index >= threshold_index def suggest_improvements(self, password: str) -> List[str]: suggestions = [] length = len(password) if length < 8: suggestions.append(f"Увеличьте длину пароля (сейчас {length}, рекомендуется минимум 8 символов)") elif length < 12: suggestions.append(f"Хорошо бы увеличить длину до 12+ символов (сейчас {length})") has_latin_lower = bool(re.search(r'[a-z]', password)) has_latin_upper = bool(re.search(r'[A-Z]', password)) has_cyrillic = bool(re.search(r'[А-Яа-яЁё]', password)) has_digit = bool(re.search(r'[0-9]', password)) has_special = bool(re.search(r'[^A-Za-z0-9А-Яа-яЁё]', password)) if not has_latin_lower and not has_cyrillic: suggestions.append("Добавьте строчные буквы (a-z или а-я)") if not has_latin_upper and not has_cyrillic: suggestions.append("Добавьте заглавные буквы (A-Z или А-Я)") if not has_digit: suggestions.append("Добавьте цифры (0-9)") if not has_special: suggestions.append("Добавьте спецсимволы (!@#$%^&* и т.д.)") if has_latin_lower or has_latin_upper: if not has_cyrillic: suggestions.append("Попробуйте использовать кириллицу вместе с латиницей для увеличения сложности") elif has_cyrillic: if not has_latin_lower and not has_latin_upper: suggestions.append("Попробуйте использовать латиницу вместе с кириллицей для увеличения сложности") common_words = ['password', 'admin', '123456', 'qwerty', 'letmein', 'welcome', 'пароль', 'админ', 'привет', '123456789'] if any(word in password.lower() for word in common_words): suggestions.append("Избегайте простых слов и шаблонов") return suggestions def compare_passwords(self, passwords: List[str]) -> Dict: results = [self.estimate(pwd) for pwd in passwords] sorted_results = sorted(results, key=lambda x: x['crack_time_seconds']) return { 'best': sorted_results[-1]['password'], 'worst': sorted_results[0]['password'], 'ranking': [ { 'password': r['password'], 'strength': r['strength'], 'time': r['crack_time_human'], 'entropy': r['entropy_bits'] } for r in reversed(sorted_results) ] } def clear_cache(self): self._cache.clear() def get_cache_stats(self) -> Dict: return { 'cached_passwords': len(self._cache), 'cache_keys': list(self._cache.keys()) } def to_json(self, password: str, indent: int = 2) -> str: result = self.estimate(password) return json.dumps(result, ensure_ascii=False, indent=indent) def get_complexity_details(self, password: str) -> Dict: has_latin_lower = bool(re.search(r'[a-z]', password)) has_latin_upper = bool(re.search(r'[A-Z]', password)) has_cyrillic_lower = bool(re.search(r'[а-яё]', password)) has_cyrillic_upper = bool(re.search(r'[А-ЯЁ]', password)) has_digit = bool(re.search(r'[0-9]', password)) has_special = bool(re.search(r'[^A-Za-z0-9А-Яа-яЁё]', password)) counts = { 'latin_lower': sum(1 for c in password if c.isascii() and c.islower()), 'latin_upper': sum(1 for c in password if c.isascii() and c.isupper()), 'cyrillic_lower': sum(1 for c in password if c in 'абвгдеёжзийклмнопрстуфхцчшщъыьэюя'), 'cyrillic_upper': sum(1 for c in password if c in 'АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ'), 'digits': sum(1 for c in password if c.isdigit()), 'special': sum(1 for c in password if not c.isalnum()), 'other_unicode': sum(1 for c in password if c.isalpha() and not c.isascii() and not c in 'АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдеёжзийклмнопрстуфхцчшщъыьэюя') } return { 'has_latin_lower': has_latin_lower, 'has_latin_upper': has_latin_upper, 'has_cyrillic_lower': has_cyrillic_lower, 'has_cyrillic_upper': has_cyrillic_upper, 'has_digit': has_digit, 'has_special': has_special, 'counts': counts, 'unique_characters': len(set(password)) }