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