# Password.py # Charles Van Loan # February 14, 2015 """ Assesses passwords for their strength. """ import math import string UPPER = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' LOWER = 'abcdefghijklmnopqrstuvwxyz' DIGITS = '1234567890' SPECIAL = string.punctuation def HowMany(s1,s2): """ Returns an int that is the number of characters in S1 that are also characters in S2. PreC: s1 and s2 are strings. """ N = 0 for c in s1: if s2.count(c)>0: # c is a character in s2 N = N+1 return N def HowManyTriplets(s1,s2): """ Returns an int that is the number of length-3 substrings in S1 that are length-3 substrings in S2 PreC: s1 and s2 are strings that have length 3 or greater. """ N = 0 m = len(s1) for k in range(m-2): t = s1[k:k+3] if s2.find(t)>=0: # t is a substring of s2 N = N + 1 return N def B1(s): """ Returns an int that is a score associated with password length. PreC: s is a valid password string.""" return 3*min(len(s),15) def B2(s): """ Returns an int that is a score associated with the mix of upper and lower case letters. PreC: s is a valid password string.""" L = HowMany(s,LOWER) U = HowMany(s,UPPER) N = L+U if N==0: return 0 else: return math.floor(40*(1-float(L)/float(N))*(1-float(U)/float(N))) def B3(s): """ Returns an int that is the score associated with the number of digits in s. PreC: s is a valid password string.""" return 4*HowMany(s,DIGITS) def B4(s): """ Returns an int that is the score associated with the number of special characters in s. PreC: s is a valid password string.""" return 6*HowMany(s,SPECIAL) def B5(s): """ Returns an int that is the score associated with the number of digits and special characters in s excepting the zeroth and last character. PreC: s is a valid password string.""" n = len(s) sMiddle = s[1:n-1] return (3*( HowMany(sMiddle,DIGITS) + HowMany(sMiddle,SPECIAL))) def B6(s): """ Returns an int that is the score associated with the number of different types of characters in s. PreC: s is a valid password string.""" if HowMany(s,LOWER)>0 and HowMany(s,UPPER)>0 and HowMany(s,DIGITS)>0 and HowMany(s,SPECIAL): return 10 else: return 0 def D1(s): """ Returns an int that is the deduction score associated with having a password that is just letters. PreC: s is a valid password string.""" if HowMany(s,UPPER+LOWER)==len(s): return len(s) else: return 0 def D2(s): """ Returns an int that is the deduction score associated with having a password that is just digits. PreC: s is a valid password string.""" if HowMany(s,DIGITS)==len(s): return len(s) else: return 0 def D3(s): """ Returns an int that is the deduction score associated with with repeat characters. PreC: s is a valid password string.""" N = len(s) M = 0 for c in s: if s.count(c)==1: # The character c appears exactly once in s M = M+1 return 3*(N-M) def D4(s): """ Returns an int that is the deduction score associated with look-a-like sequences. PreC: s is a valid password string.""" N = 0 for k in range(len(s)-1): # See if S[k] and s[k+1] are the same type of character if UPPER.find(s[k])>=0 and UPPER.find(s[k+1])>=0: N = N+1 elif LOWER.find(s[k])>=0 and LOWER.find(s[k+1])>=0: N = N+1 elif DIGITS.find(s[k])>=0 and DIGITS.find(s[k+1])>=0: N = N+1 elif SPECIAL.find(s[k])>=0 and SPECIAL.find(s[k+1])>=0: N = N+1 return 2*N def D5(s): """ Returns an int that is the deduction score associated with having row-1 triplets PreC: s is a valid password string.""" return 3*HowManyTriplets(s,'1234567890') def D6(s): """ Returns an int that is the deduction score associated with having row-2 triplets PreC: s is a valid password string.""" return 3*HowManyTriplets(s,'qwertyuiop') def D7(s): """ Returns an int that is the deduction score associated with having row-3 triplets PreC: s is a valid password string.""" return 3*HowManyTriplets(s,'asdfghjkl') def D8(s): """ Returns an int that is the deduction score associated with having row-4 triplets PreC: s is a valid password string.""" return 3*HowManyTriplets(s,'zxcvbnm') def PWS(s): """ Returns an int that is the password strength of s. PreC: s is a valid password string.""" TheBonuses = B1(s)+B2(s)+B3(s)+B4(s)+B5(s)+B6(s) TheDeductions = D1(s)+D2(s)+D3(s)+D4(s)+D5(s)+D6(s)+D7(s)+D8(s) return (TheBonuses-TheDeductions) if __name__ == '__main__': s = raw_input('Enter a string: ') nGoodChars = HowMany(s,UPPER)+HowMany(s,LOWER)+HowMany(s,DIGITS)+HowMany(s,SPECIAL) if len(s)>nGoodChars or len(s)==0: print '\n %s is an invalid password string' % s else: print '\n\n Password: %s' % s print ' Strength: %1d' %(PWS(s)) print '--------------------------------------------' print ' Bonuses: %2d %2d %2d %2d %2d %2d' % (B1(s),B2(s),B3(s),B4(s),B5(s),B6(s)) print 'Deductions: %2d %2d %2d %2d %2d %2d %2d %2d' % (D1(s),D2(s),D3(s),D4(s),D5(s),D6(s),D7(s),D8(s))