|
1 # -*- coding: utf-8 -*- |
|
2 |
|
3 # Copyright (c) 2011 - 2022 Detlev Offenbach <detlev@die-offenbachs.de> |
|
4 # |
|
5 |
|
6 """ |
|
7 Module implementing a checker for password strength. |
|
8 """ |
|
9 |
|
10 import re |
|
11 |
|
12 |
|
13 class PasswordChecker: |
|
14 """ |
|
15 Class implementing a checker for password strength. |
|
16 """ |
|
17 Complexity_VeryWeak = 0 |
|
18 Complexity_Weak = 1 |
|
19 Complexity_Good = 2 |
|
20 Complexity_Strong = 3 |
|
21 Complexity_VeryStrong = 4 |
|
22 |
|
23 Status_Failed = 0 |
|
24 Status_Passed = 1 |
|
25 Status_Exceeded = 2 |
|
26 |
|
27 def __init__(self): |
|
28 """ |
|
29 Constructor |
|
30 """ |
|
31 self.score = { |
|
32 "count": 0, |
|
33 "adjusted": 0, |
|
34 "beforeRedundancy": 0 |
|
35 } |
|
36 |
|
37 # complexity index |
|
38 self.complexity = { |
|
39 "limits": [20, 50, 60, 80, 100], |
|
40 "value": self.Complexity_VeryWeak |
|
41 } |
|
42 |
|
43 # check categories follow |
|
44 |
|
45 # length of the password |
|
46 self.passwordLength = { |
|
47 "count": 0, |
|
48 "minimum": 6, |
|
49 "status": self.Status_Failed, |
|
50 "rating": 0, |
|
51 "factor": 0.5, # per character bonus |
|
52 "bonus": 10, # minimum reached? Get a bonus. |
|
53 "penalty": -20, # if we stay under minimum, we get punished |
|
54 } |
|
55 |
|
56 # recommended password length |
|
57 self.recommendedPasswordLength = { |
|
58 "count": 0, |
|
59 "minimum": 8, |
|
60 "status": self.Status_Failed, |
|
61 "rating": 0, |
|
62 "factor": 1.2, |
|
63 "bonus": 10, |
|
64 "penalty": -10, |
|
65 } |
|
66 |
|
67 # Basic requirements are: |
|
68 # 1) Password Length |
|
69 # 2) Uppercase letter use |
|
70 # 3) Lowercase letter use |
|
71 # 4) Numeric character use |
|
72 # 5) Symbol use |
|
73 self.basicRequirements = { |
|
74 "count": 0, |
|
75 "minimum": 3, # have to be matched to get the bonus |
|
76 "status": self.Status_Failed, |
|
77 "rating": 0, |
|
78 "factor": 1.0, |
|
79 "bonus": 10, |
|
80 "penalty": -10, |
|
81 } |
|
82 |
|
83 # how much redundancy is permitted, if the password is |
|
84 # long enough. we will skip the redudancy penalty if this |
|
85 # number is not exceeded (meaning redundancy < this number) |
|
86 self.redundancy = { |
|
87 "value": 1, # 1 means, not double characters, |
|
88 # default to start |
|
89 "permitted": 2.0, # 2 means, in average every character |
|
90 # can occur twice |
|
91 "status": self.Status_Failed, |
|
92 "rating": 0, |
|
93 } |
|
94 |
|
95 # number of uppercase letters, such as A-Z |
|
96 self.uppercaseLetters = { |
|
97 "count": 0, |
|
98 "minimum": 1, |
|
99 "status": self.Status_Failed, |
|
100 "rating": 0, |
|
101 "factor": 0.0, |
|
102 "bonus": 10, |
|
103 "penalty": -10, |
|
104 } |
|
105 |
|
106 # number of lowercase letters, such as a-z |
|
107 self.lowercaseLetters = { |
|
108 "count": 0, |
|
109 "minimum": 1, |
|
110 "status": self.Status_Failed, |
|
111 "rating": 0, |
|
112 "factor": 0.0, |
|
113 "bonus": 10, |
|
114 "penalty": -10, |
|
115 } |
|
116 |
|
117 # number of numeric characters |
|
118 self.numerics = { |
|
119 "count": 0, |
|
120 "minimum": 1, |
|
121 "status": self.Status_Failed, |
|
122 "rating": 0, |
|
123 "factor": 0.0, |
|
124 "bonus": 10, |
|
125 "penalty": -10, |
|
126 } |
|
127 |
|
128 # number of symbol characters |
|
129 self.symbols = { |
|
130 "count": 0, |
|
131 "minimum": 1, |
|
132 "status": self.Status_Failed, |
|
133 "rating": 0, |
|
134 "factor": 0.0, |
|
135 "bonus": 10, |
|
136 "penalty": -10, |
|
137 } |
|
138 |
|
139 # number of dedicated symbols in the middle |
|
140 self.middleSymbols = { |
|
141 "count": 0, |
|
142 "minimum": 1, |
|
143 "status": self.Status_Failed, |
|
144 "rating": 0, |
|
145 "factor": 0.0, |
|
146 "bonus": 10, |
|
147 "penalty": -10, |
|
148 } |
|
149 |
|
150 # number of dedicated numbers in the middle |
|
151 self.middleNumerics = { |
|
152 "count": 0, |
|
153 "minimum": 1, |
|
154 "status": self.Status_Failed, |
|
155 "rating": 0, |
|
156 "factor": 0.0, |
|
157 "bonus": 10, |
|
158 "penalty": -10, |
|
159 } |
|
160 |
|
161 # how many sequential characters should be checked |
|
162 # such as "abc" or "MNO" to be not part of the password |
|
163 self.sequentialLetters = { |
|
164 "data": "abcdefghijklmnopqrstuvwxyz", |
|
165 "length": 3, |
|
166 |
|
167 "count": 0, |
|
168 "status": self.Status_Failed, |
|
169 "rating": 0, |
|
170 "factor": -1.0, |
|
171 "bonus": 0, |
|
172 "penalty": -10, |
|
173 } |
|
174 |
|
175 # how many sequential characters should be checked |
|
176 # such as "123" to be not part of the password |
|
177 self.sequentialNumerics = { |
|
178 "data": "0123456789", |
|
179 "length": 3, |
|
180 |
|
181 "count": 0, |
|
182 "status": self.Status_Failed, |
|
183 "rating": 0, |
|
184 "factor": -1.0, |
|
185 "bonus": 0, |
|
186 "penalty": -10, |
|
187 } |
|
188 |
|
189 # keyboard patterns to check, typical sequences from your |
|
190 # keyboard |
|
191 self.keyboardPatterns = { |
|
192 # German and English keyboard text |
|
193 "data": [ |
|
194 "qwertzuiop", "asdfghjkl", "yxcvbnm", "!\"§$%&/()=", # de |
|
195 "1234567890", # de numbers |
|
196 "qaywsxedcrfvtgbzhnujmik,ol.pö-üä+#", # de up-down |
|
197 |
|
198 "qwertyuiop", "asdfghjkl", "zyxcvbnm", "!@#$%^&*()_", # en |
|
199 "1234567890", # en numbers |
|
200 "qazwsxedcrfvtgbyhnujmik,ol.p;/[']\\", # en up-down |
|
201 ], |
|
202 "length": 4, # how long is the pattern to check and blame for? |
|
203 |
|
204 "count": 0, # how many of these pattern can be found |
|
205 "status": self.Status_Failed, |
|
206 "rating": 0, |
|
207 "factor": -1.0, # each occurrence is punished with that factor |
|
208 "bonus": 0, |
|
209 "penalty": -10, |
|
210 } |
|
211 |
|
212 # check for repeated sequences, like in catcat |
|
213 self.repeatedSequences = { |
|
214 "length": 3, |
|
215 |
|
216 "count": 0, |
|
217 "status": self.Status_Failed, |
|
218 "rating": 0, |
|
219 "factor": 0.0, |
|
220 "bonus": 0, |
|
221 "penalty": -10, |
|
222 } |
|
223 |
|
224 # check for repeated mirrored sequences, like in tactac |
|
225 self.mirroredSequences = { |
|
226 "length": 3, |
|
227 |
|
228 "count": 0, |
|
229 "status": self.Status_Failed, |
|
230 "rating": 0, |
|
231 "factor": 0.0, |
|
232 "bonus": 0, |
|
233 "penalty": -10, |
|
234 } |
|
235 |
|
236 self.uppercaseRe = re.compile("[A-Z]") |
|
237 self.lowercaseRe = re.compile("[a-z]") |
|
238 self.numberRe = re.compile("[0-9]") |
|
239 self.symbolRe = re.compile("[^a-zA-Z0-9]") |
|
240 |
|
241 def __strReverse(self, string): |
|
242 """ |
|
243 Private method to reverse a string. |
|
244 |
|
245 @param string string to be reversed (string) |
|
246 @return reversed string (string) |
|
247 """ |
|
248 return "".join(reversed(string)) |
|
249 |
|
250 def __determineStatus(self, value): |
|
251 """ |
|
252 Private method to determine the status. |
|
253 |
|
254 @param value value to check (integer) |
|
255 @return status (Status_Failed, Status_Passed, Status_Exceeded) |
|
256 """ |
|
257 if value == 0: |
|
258 return self.Status_Passed |
|
259 elif value > 0: |
|
260 return self.Status_Exceeded |
|
261 else: |
|
262 return self.Status_Failed |
|
263 |
|
264 def __determineBinaryStatus(self, value): |
|
265 """ |
|
266 Private method to determine a binary status. |
|
267 |
|
268 @param value value to check (integer) |
|
269 @return status (Status_Failed, Status_Passed) |
|
270 """ |
|
271 if value == 0: |
|
272 return self.Status_Passed |
|
273 else: |
|
274 return self.Status_Failed |
|
275 |
|
276 def checkPassword(self, password): |
|
277 """ |
|
278 Public method to check a given password. |
|
279 |
|
280 @param password password to be checked (string) |
|
281 @return indication for the password strength (Complexity_VeryWeak, |
|
282 Complexity_Weak, Complexity_Good, Complexity_Strong, |
|
283 Complexity_VeryStrong) |
|
284 """ |
|
285 # how long is the password? |
|
286 self.passwordLength["count"] = len(password) |
|
287 self.recommendedPasswordLength["count"] = len(password) |
|
288 |
|
289 # Loop through password to check for Symbol, Numeric, Lowercase |
|
290 # and Uppercase pattern matches |
|
291 for index in range(len(password)): |
|
292 if self.uppercaseRe.match(password[index]): |
|
293 self.uppercaseLetters["count"] += 1 |
|
294 elif self.lowercaseRe.match(password[index]): |
|
295 self.lowercaseLetters["count"] += 1 |
|
296 elif self.numberRe.match(password[index]): |
|
297 if index > 0 and index < len(password) - 1: |
|
298 self.middleNumerics["count"] += 1 |
|
299 self.numerics["count"] += 1 |
|
300 elif self.symbolRe.match(password[index]): |
|
301 if index > 0 and index < len(password) - 1: |
|
302 self.middleSymbols["count"] += 1 |
|
303 self.symbols["count"] += 1 |
|
304 |
|
305 # check the variance of symbols or better the redundancy |
|
306 # makes only sense for at least two characters |
|
307 if len(password) > 1: |
|
308 uniqueCharacters = [] |
|
309 for index1 in range(len(password)): |
|
310 found = False |
|
311 for index2 in range(index1 + 1, len(password)): |
|
312 if password[index1] == password[index2]: |
|
313 found = True |
|
314 break |
|
315 if not found: |
|
316 uniqueCharacters.append(password[index1]) |
|
317 |
|
318 # calculate a redundancy number |
|
319 self.redundancy["value"] = len(password) / len(uniqueCharacters) |
|
320 |
|
321 # Check for sequential alpha string patterns (forward and reverse) |
|
322 # but only, if the string has already a length to check for, does |
|
323 # not make sense to check the password "ab" for the sequential data |
|
324 # "abc" |
|
325 lowercasedPassword = password.lower() |
|
326 |
|
327 if self.passwordLength["count"] >= self.sequentialLetters["length"]: |
|
328 for index in range(len(self.sequentialLetters["data"]) - |
|
329 self.sequentialLetters["length"] + 1): |
|
330 fwd = self.sequentialLetters["data"][ |
|
331 index:index + self.sequentialLetters["length"]] |
|
332 rev = self.__strReverse(fwd) |
|
333 if lowercasedPassword.find(fwd) != -1: |
|
334 self.sequentialLetters["count"] += 1 |
|
335 if lowercasedPassword.find(rev) != -1: |
|
336 self.sequentialLetters["count"] += 1 |
|
337 |
|
338 # Check for sequential numeric string patterns (forward and reverse) |
|
339 if self.passwordLength["count"] >= self.sequentialNumerics["length"]: |
|
340 for index in range(len(self.sequentialNumerics["data"]) - |
|
341 self.sequentialNumerics["length"] + 1): |
|
342 fwd = self.sequentialNumerics["data"][ |
|
343 index:index + self.sequentialNumerics["length"]] |
|
344 rev = self.__strReverse(fwd) |
|
345 if lowercasedPassword.find(fwd) != -1: |
|
346 self.sequentialNumerics["count"] += 1 |
|
347 if lowercasedPassword.find(rev) != -1: |
|
348 self.sequentialNumerics["count"] += 1 |
|
349 |
|
350 # Check common keyboard patterns |
|
351 patternsMatched = [] |
|
352 if self.passwordLength["count"] >= self.keyboardPatterns["length"]: |
|
353 for pattern in self.keyboardPatterns["data"]: |
|
354 for index in range( |
|
355 len(pattern) - self.keyboardPatterns["length"] + 1): |
|
356 fwd = pattern[index:index + |
|
357 self.keyboardPatterns["length"]] |
|
358 rev = self.__strReverse(fwd) |
|
359 if ( |
|
360 lowercasedPassword.find(fwd) != -1 and |
|
361 fwd not in patternsMatched |
|
362 ): |
|
363 self.keyboardPatterns["count"] += 1 |
|
364 patternsMatched.append(fwd) |
|
365 if ( |
|
366 lowercasedPassword.find(rev) != -1 and |
|
367 fwd not in patternsMatched |
|
368 ): |
|
369 self.keyboardPatterns["count"] += 1 |
|
370 patternsMatched.append(rev) |
|
371 |
|
372 # Try to find repeated sequences of characters. |
|
373 if self.passwordLength["count"] >= self.repeatedSequences["length"]: |
|
374 for index in range(len(lowercasedPassword) - |
|
375 self.repeatedSequences["length"] + 1): |
|
376 fwd = lowercasedPassword[ |
|
377 index:index + self.repeatedSequences["length"]] |
|
378 if lowercasedPassword.find( |
|
379 fwd, index + self.repeatedSequences["length"]) != -1: |
|
380 self.repeatedSequences["count"] += 1 |
|
381 |
|
382 # Try to find mirrored sequences of characters. |
|
383 if self.passwordLength["count"] >= self.mirroredSequences["length"]: |
|
384 for index in range(len(lowercasedPassword) - |
|
385 self.mirroredSequences["length"] + 1): |
|
386 fwd = lowercasedPassword[ |
|
387 index:index + self.mirroredSequences["length"]] |
|
388 rev = self.__strReverse(fwd) |
|
389 if lowercasedPassword.find( |
|
390 fwd, index + self.mirroredSequences["length"]) != -1: |
|
391 self.mirroredSequences["count"] += 1 |
|
392 |
|
393 # Initial score based on length |
|
394 self.score["count"] = ( |
|
395 self.passwordLength["count"] * self.passwordLength["factor"] |
|
396 ) |
|
397 |
|
398 # passwordLength |
|
399 # credit additional length or punish "under" length |
|
400 if self.passwordLength["count"] >= self.passwordLength["minimum"]: |
|
401 # credit additional characters over minimum |
|
402 self.passwordLength["rating"] = ( |
|
403 self.passwordLength["bonus"] + |
|
404 (self.passwordLength["count"] - |
|
405 self.passwordLength["minimum"]) * |
|
406 self.passwordLength["factor"] |
|
407 ) |
|
408 else: |
|
409 self.passwordLength["rating"] = self.passwordLength["penalty"] |
|
410 self.score["count"] += self.passwordLength["rating"] |
|
411 |
|
412 # recommendedPasswordLength |
|
413 # Credit reaching the recommended password length or put a |
|
414 # penalty on it |
|
415 if ( |
|
416 self.passwordLength["count"] >= |
|
417 self.recommendedPasswordLength["minimum"] |
|
418 ): |
|
419 self.recommendedPasswordLength["rating"] = ( |
|
420 self.recommendedPasswordLength["bonus"] + |
|
421 (self.passwordLength["count"] - |
|
422 self.recommendedPasswordLength["minimum"]) * |
|
423 self.recommendedPasswordLength["factor"] |
|
424 ) |
|
425 else: |
|
426 self.recommendedPasswordLength["rating"] = ( |
|
427 self.recommendedPasswordLength["penalty"] |
|
428 ) |
|
429 self.score["count"] += self.recommendedPasswordLength["rating"] |
|
430 |
|
431 # lowercaseLetters |
|
432 # Honor or punish the lowercase letter use |
|
433 if self.lowercaseLetters["count"] > 0: |
|
434 self.lowercaseLetters["rating"] = ( |
|
435 self.lowercaseLetters["bonus"] + |
|
436 self.lowercaseLetters["count"] * |
|
437 self.lowercaseLetters["factor"] |
|
438 ) |
|
439 else: |
|
440 self.lowercaseLetters["rating"] = self.lowercaseLetters["penalty"] |
|
441 self.score["count"] += self.lowercaseLetters["rating"] |
|
442 |
|
443 # uppercaseLetters |
|
444 # Honor or punish the lowercase letter use |
|
445 if self.uppercaseLetters["count"] > 0: |
|
446 self.uppercaseLetters["rating"] = ( |
|
447 self.uppercaseLetters["bonus"] + |
|
448 self.uppercaseLetters["count"] * |
|
449 self.uppercaseLetters["factor"] |
|
450 ) |
|
451 else: |
|
452 self.uppercaseLetters["rating"] = self.uppercaseLetters["penalty"] |
|
453 self.score["count"] += self.uppercaseLetters["rating"] |
|
454 |
|
455 # numerics |
|
456 # Honor or punish the numerics use |
|
457 if self.numerics["count"] > 0: |
|
458 self.numerics["rating"] = ( |
|
459 self.numerics["bonus"] + |
|
460 self.numerics["count"] * self.numerics["factor"] |
|
461 ) |
|
462 else: |
|
463 self.numerics["rating"] = self.numerics["penalty"] |
|
464 self.score["count"] += self.numerics["rating"] |
|
465 |
|
466 # symbols |
|
467 # Honor or punish the symbols use |
|
468 if self.symbols["count"] > 0: |
|
469 self.symbols["rating"] = ( |
|
470 self.symbols["bonus"] + |
|
471 self.symbols["count"] * self.symbols["factor"] |
|
472 ) |
|
473 else: |
|
474 self.symbols["rating"] = self.symbols["penalty"] |
|
475 self.score["count"] += self.symbols["rating"] |
|
476 |
|
477 # middleSymbols |
|
478 # Honor or punish the middle symbols use |
|
479 if self.middleSymbols["count"] > 0: |
|
480 self.middleSymbols["rating"] = ( |
|
481 self.middleSymbols["bonus"] + |
|
482 self.middleSymbols["count"] * self.middleSymbols["factor"] |
|
483 ) |
|
484 else: |
|
485 self.middleSymbols["rating"] = self.middleSymbols["penalty"] |
|
486 self.score["count"] += self.middleSymbols["rating"] |
|
487 |
|
488 # middleNumerics |
|
489 # Honor or punish the middle numerics use |
|
490 if self.middleNumerics["count"] > 0: |
|
491 self.middleNumerics["rating"] = ( |
|
492 self.middleNumerics["bonus"] + |
|
493 self.middleNumerics["count"] * self.middleNumerics["factor"] |
|
494 ) |
|
495 else: |
|
496 self.middleNumerics["rating"] = self.middleNumerics["penalty"] |
|
497 self.score["count"] += self.middleNumerics["rating"] |
|
498 |
|
499 # sequentialLetters |
|
500 # Honor or punish the sequential letter use |
|
501 if self.sequentialLetters["count"] == 0: |
|
502 self.sequentialLetters["rating"] = ( |
|
503 self.sequentialLetters["bonus"] + |
|
504 self.sequentialLetters["count"] * |
|
505 self.sequentialLetters["factor"] |
|
506 ) |
|
507 else: |
|
508 self.sequentialLetters["rating"] = ( |
|
509 self.sequentialLetters["penalty"] |
|
510 ) |
|
511 self.score["count"] += self.sequentialLetters["rating"] |
|
512 |
|
513 # sequentialNumerics |
|
514 # Honor or punish the sequential numerics use |
|
515 if self.sequentialNumerics["count"] == 0: |
|
516 self.sequentialNumerics["rating"] = ( |
|
517 self.sequentialNumerics["bonus"] + |
|
518 self.sequentialNumerics["count"] * |
|
519 self.sequentialNumerics["factor"] |
|
520 ) |
|
521 else: |
|
522 self.sequentialNumerics["rating"] = ( |
|
523 self.sequentialNumerics["penalty"] |
|
524 ) |
|
525 self.score["count"] += self.sequentialNumerics["rating"] |
|
526 |
|
527 # keyboardPatterns |
|
528 # Honor or punish the keyboard patterns use |
|
529 if self.keyboardPatterns["count"] == 0: |
|
530 self.keyboardPatterns["rating"] = ( |
|
531 self.keyboardPatterns["bonus"] + |
|
532 self.keyboardPatterns["count"] * |
|
533 self.keyboardPatterns["factor"] |
|
534 ) |
|
535 else: |
|
536 self.keyboardPatterns["rating"] = self.keyboardPatterns["penalty"] |
|
537 self.score["count"] += self.keyboardPatterns["rating"] |
|
538 |
|
539 # Count our basicRequirements and set the status |
|
540 self.basicRequirements["count"] = 0 |
|
541 |
|
542 # password length |
|
543 self.passwordLength["status"] = self.__determineStatus( |
|
544 self.passwordLength["count"] - self.passwordLength["minimum"]) |
|
545 if self.passwordLength["status"] != self.Status_Failed: |
|
546 # requirement met |
|
547 self.basicRequirements["count"] += 1 |
|
548 |
|
549 # uppercase letters |
|
550 self.uppercaseLetters["status"] = self.__determineStatus( |
|
551 self.uppercaseLetters["count"] - self.uppercaseLetters["minimum"]) |
|
552 if self.uppercaseLetters["status"] != self.Status_Failed: |
|
553 # requirement met |
|
554 self.basicRequirements["count"] += 1 |
|
555 |
|
556 # lowercase letters |
|
557 self.lowercaseLetters["status"] = self.__determineStatus( |
|
558 self.lowercaseLetters["count"] - self.lowercaseLetters["minimum"]) |
|
559 if self.lowercaseLetters["status"] != self.Status_Failed: |
|
560 # requirement met |
|
561 self.basicRequirements["count"] += 1 |
|
562 |
|
563 # numerics |
|
564 self.numerics["status"] = self.__determineStatus( |
|
565 self.numerics["count"] - self.numerics["minimum"]) |
|
566 if self.numerics["status"] != self.Status_Failed: |
|
567 # requirement met |
|
568 self.basicRequirements["count"] += 1 |
|
569 |
|
570 # symbols |
|
571 self.symbols["status"] = self.__determineStatus( |
|
572 self.symbols["count"] - self.symbols["minimum"]) |
|
573 if self.symbols["status"] != self.Status_Failed: |
|
574 # requirement met |
|
575 self.basicRequirements["count"] += 1 |
|
576 |
|
577 # judge the requirement status |
|
578 self.basicRequirements["status"] = self.__determineStatus( |
|
579 self.basicRequirements["count"] - |
|
580 self.basicRequirements["minimum"]) |
|
581 if self.basicRequirements["status"] != self.Status_Failed: |
|
582 self.basicRequirements["rating"] = ( |
|
583 self.basicRequirements["bonus"] + |
|
584 self.basicRequirements["factor"] * |
|
585 self.basicRequirements["count"] |
|
586 ) |
|
587 else: |
|
588 self.basicRequirements["rating"] = ( |
|
589 self.basicRequirements["penalty"] |
|
590 ) |
|
591 self.score["count"] += self.basicRequirements["rating"] |
|
592 |
|
593 # beyond basic requirements |
|
594 self.recommendedPasswordLength["status"] = self.__determineStatus( |
|
595 self.recommendedPasswordLength["count"] - |
|
596 self.recommendedPasswordLength["minimum"]) |
|
597 self.middleNumerics["status"] = self.__determineStatus( |
|
598 self.middleNumerics["count"] - |
|
599 self.middleNumerics["minimum"]) |
|
600 self.middleSymbols["status"] = self.__determineStatus( |
|
601 self.middleSymbols["count"] - |
|
602 self.middleSymbols["minimum"]) |
|
603 self.sequentialLetters["status"] = self.__determineBinaryStatus( |
|
604 self.sequentialLetters["count"]) |
|
605 self.sequentialNumerics["status"] = self.__determineBinaryStatus( |
|
606 self.sequentialNumerics["count"]) |
|
607 self.keyboardPatterns["status"] = self.__determineBinaryStatus( |
|
608 self.keyboardPatterns["count"]) |
|
609 self.repeatedSequences["status"] = self.__determineBinaryStatus( |
|
610 self.repeatedSequences["count"]) |
|
611 self.mirroredSequences["status"] = self.__determineBinaryStatus( |
|
612 self.mirroredSequences["count"]) |
|
613 |
|
614 # we apply them only, if the length is not awesome |
|
615 if self.recommendedPasswordLength["status"] != self.Status_Exceeded: |
|
616 # repeatedSequences |
|
617 # Honor or punish the use of repeated sequences |
|
618 if self.repeatedSequences["count"] == 0: |
|
619 self.repeatedSequences["rating"] = ( |
|
620 self.repeatedSequences["bonus"] |
|
621 ) |
|
622 else: |
|
623 self.repeatedSequences["rating"] = ( |
|
624 self.repeatedSequences["penalty"] + |
|
625 self.repeatedSequences["count"] * |
|
626 self.repeatedSequences["factor"] |
|
627 ) |
|
628 |
|
629 # mirroredSequences |
|
630 # Honor or punish the use of mirrored sequences |
|
631 if self.mirroredSequences["count"] == 0: |
|
632 self.mirroredSequences["rating"] = ( |
|
633 self.mirroredSequences["bonus"] |
|
634 ) |
|
635 else: |
|
636 self.mirroredSequences["rating"] = ( |
|
637 self.mirroredSequences["penalty"] + |
|
638 self.mirroredSequences["count"] * |
|
639 self.mirroredSequences["factor"] |
|
640 ) |
|
641 |
|
642 # save value before redundancy |
|
643 self.score["beforeRedundancy"] = self.score["count"] |
|
644 |
|
645 # apply the redundancy |
|
646 # is the password length requirement fulfilled? |
|
647 if ( |
|
648 self.recommendedPasswordLength[ |
|
649 "status"] != self.Status_Exceeded and |
|
650 self.score["count"] > 0 |
|
651 ): |
|
652 # full penalty, because password is not long enough, only for |
|
653 # a positive score |
|
654 self.score["count"] *= 1.0 / self.redundancy["value"] |
|
655 |
|
656 # level it out |
|
657 if self.score["count"] > 100: |
|
658 self.score["adjusted"] = 100 |
|
659 elif self.score["count"] < 0: |
|
660 self.score["adjusted"] = 0 |
|
661 else: |
|
662 self.score["adjusted"] = self.score["count"] |
|
663 |
|
664 # judge it |
|
665 for index in range(len(self.complexity["limits"])): |
|
666 if self.score["adjusted"] <= self.complexity["limits"][index]: |
|
667 self.complexity["value"] = index |
|
668 break |
|
669 |
|
670 return self.complexity["value"] |