-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathpassword_analyzer.py
More file actions
217 lines (178 loc) · 8.45 KB
/
Copy pathpassword_analyzer.py
File metadata and controls
217 lines (178 loc) · 8.45 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
import re
import math
import string
class PasswordStrengthAnalyzer:
def __init__(self):
# Expanded common passwords
self.common_passwords = {
'password', '123456', '123456789', 'qwerty', 'abc123',
'password123', 'admin', 'letmein', 'welcome', 'monkey',
'dragon', 'master', 'hello', 'football', 'superman',
'admin123', 'root', 'toor', 'passw0rd', 'shadow', 'bitcoin',
'12345678', '111111', '123123', '12345', '1234567890',
'qwerty123', '1q2w3e4r', 'iloveyou', 'princess', 'sunshine'
}
self.char_sets = {
'lowercase': string.ascii_lowercase,
'uppercase': string.ascii_uppercase,
'digits': string.digits,
'special': string.punctuation
}
def calculate_entropy(self, password):
pool_size = 0
if re.search(r'[a-z]', password):
pool_size += len(self.char_sets['lowercase'])
if re.search(r'[A-Z]', password):
pool_size += len(self.char_sets['uppercase'])
if re.search(r'\d', password):
pool_size += len(self.char_sets['digits'])
if re.search(r'[!@#$%^&*(),.?":{}|<>]', password):
pool_size += len(self.char_sets['special'])
if pool_size == 0:
return 0
entropy = len(password) * math.log2(pool_size)
return round(entropy, 2)
def check_length(self, password):
length = len(password)
if length < 8:
return 0, "[FAIL] Too short - minimum 8 characters required"
elif length < 12:
return 1, "[WARN] Acceptable but vulnerable"
elif length < 16:
return 2, "[OK] Good length"
else:
return 3, "[PASS] Excellent length"
def check_character_variety(self, password):
has_lower = bool(re.search(r'[a-z]', password))
has_upper = bool(re.search(r'[A-Z]', password))
has_digit = bool(re.search(r'\d', password))
has_special = bool(re.search(r'[!@#$%^&*(),.?":{}|<>]', password))
variety_count = sum([has_lower, has_upper, has_digit, has_special])
if variety_count == 1:
return 0, "[FAIL] Very poor variety - easily predictable"
elif variety_count == 2:
return 1, "[WARN] Poor variety"
elif variety_count == 3:
return 2, "[OK] Good variety"
else:
return 3, "[PASS] Excellent variety - high complexity"
def check_patterns(self, password):
score = 3
warnings = []
if re.search(r'(.)\1{2,}', password):
score -= 1
warnings.append("Detected repeated characters")
sequences = ['abcdefghijklmnopqrstuvwxyz', '0123456789', 'qwertyuiop', 'asdfghjkl', 'zxcvbnm']
lower_pass = password.lower()
for seq in sequences:
for i in range(len(seq) - 2):
if seq[i:i+3] in lower_pass:
score -= 1
warnings.append("Detected sequential pattern")
break
if score < 2:
break
keyboard_patterns = ['qwerty', 'asdfgh', 'zxcvbn', '1qaz', '2wsx', 'qwertyuiop']
for pattern in keyboard_patterns:
if pattern in password.lower():
score -= 1
warnings.append("Detected keyboard pattern")
break
score = max(0, score)
if score == 3:
return score, "[PASS] No patterns detected"
elif score == 2:
return score, "[WARN] Weak patterns found"
elif score == 1:
return score, "[FAIL] Multiple patterns detected"
else:
return score, "[CRIT] Severe pattern vulnerabilities"
def check_common_password(self, password):
if password.lower() in self.common_passwords:
return 0, "[CRIT] Password found in breach database!"
return 3, "[PASS] Not found in common password lists"
def calculate_crack_time(self, entropy):
guesses_per_second = 1_000_000_000
total_combinations = 2 ** entropy if entropy > 0 else 0
if total_combinations <= 0 or entropy <= 0:
return "INSTANT (>_<)"
seconds = total_combinations / guesses_per_second
if seconds < 1:
return "< 1 second [VULNERABLE]"
elif seconds < 60:
return f"{seconds:.0f} seconds [INSECURE]"
elif seconds < 3600:
return f"{seconds/60:.1f} minutes [WEAK]"
elif seconds < 86400:
return f"{seconds/3600:.1f} hours [MODERATE]"
elif seconds < 31536000:
return f"{seconds/86400:.1f} days [STRONG]"
elif seconds < 31536000 * 100:
return f"{seconds/31536000:.1f} years [VERY STRONG]"
else:
return "> 100 years [IMPENETRABLE]"
def get_strength_rating(self, entropy, total_score):
max_possible_score = 12
percentage = (total_score / max_possible_score) * 100 if max_possible_score > 0 else 0
# Return level for theme switching
if entropy < 30 and total_score < 6:
return "⚠️ CRITICAL", "#ff0000", "critical"
elif entropy < 40 and total_score < 9:
return "⚠️ WEAK", "#ff6600", "weak"
elif entropy < 60 and total_score < 12:
return "🟡 MODERATE", "#ffff00", "moderate"
elif entropy < 80 or total_score < 14:
return "🟢 SECURE", "#00ff00", "secure"
else:
return "💪 IMPENETRABLE", "#00ffff", "impenetrable"
def generate_improvement_suggestions(self, password, results):
suggestions = []
if len(password) < 12:
suggestions.append(f"> Increase length to 12+ chars (current: {len(password)})")
missing_types = []
if not re.search(r'[A-Z]', password):
missing_types.append("UPPERCASE")
if not re.search(r'[a-z]', password):
missing_types.append("lowercase")
if not re.search(r'\d', password):
missing_types.append("numbers")
if not re.search(r'[!@#$%^&*(),.?":{}|<>]', password):
missing_types.append("special chars")
if missing_types:
suggestions.append(f"> Add missing: {', '.join(missing_types)}")
if results['patterns']['message'] != "[PASS] No patterns detected":
suggestions.append("> Avoid sequential/keyboard patterns")
if results['common']['score'] == 0:
suggestions.append("> DO NOT use common passwords!")
if results['entropy'] < 40:
suggestions.append("> Use random characters - avoid dictionary words")
if len(suggestions) < 3:
suggestions.append("> Use passphrase: 4+ random words with symbols")
suggestions.append("> Consider a password manager for unique passwords")
return suggestions
def analyze(self, password):
if not password or len(password.strip()) == 0:
return None
entropy = self.calculate_entropy(password)
length_score, length_msg = self.check_length(password)
variety_score, variety_msg = self.check_character_variety(password)
patterns_score, patterns_msg = self.check_patterns(password)
common_score, common_msg = self.check_common_password(password)
crack_time = self.calculate_crack_time(entropy)
results = {
'password': password,
'entropy': entropy,
'crack_time': crack_time,
'length': {'score': length_score, 'message': length_msg},
'variety': {'score': variety_score, 'message': variety_msg},
'patterns': {'score': patterns_score, 'message': patterns_msg},
'common': {'score': common_score, 'message': common_msg}
}
total_score = sum([length_score, variety_score, patterns_score, common_score])
strength_rating, strength_color, strength_level = self.get_strength_rating(entropy, total_score)
results['strength_rating'] = strength_rating
results['strength_color'] = strength_color
results['strength_level'] = strength_level
results['total_score'] = total_score
results['suggestions'] = self.generate_improvement_suggestions(password, results)
return results