#!/usr/bin/python # # # # PassGen.py # Password generator # # Copyright 2004 # from random import choice import sys import os import string def mashWords(): '''Get words and mash them together with other words''' wDivider = (' ', '_', '-','.') noOfWords = choice((1,2,3)) if noOfWords == 1: resultPasswd = chooseMethod(getWord()) elif noOfWords == 2: randWord = chooseMethod(getWord()) randWord2 = chooseMethod(getWord()) resultPasswd = '%s%s%s' % (randWord, choice(wDivider), randWord2) else: randWord = chooseMethod(getWord()) randWord2 = chooseMethod(getWord()) randWord3 = chooseMethod(getWord()) resultPasswd = '%s%s%s%s%s' % (randWord, choice(wDivider), randWord2, choice(wDivider), randWord3) return(resultPasswd) def chooseMethod(sheepWord): '''Use different ways to modify random characters of the word''' countDigits = range(21) #countDigits = (0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20) #tortureMethods = ('changeCase','insertDigits','replaceChars') #Inserting digits makes the passwords hard to memorize. Must fix. tortureMethods = ('changeCase','replaceChars') finalWord = sheepWord a = choice(countDigits) while a >= 0: currentMethod = choice(tortureMethods) if currentMethod == 'changeCase': finalWord = changeCase(finalWord) elif currentMethod == 'insertDigits': finalWord = insertDigits(finalWord) else: finalWord = replaceChars(finalWord) a = a-1 return(finalWord) def replaceChars(rawWord): '''Substitutes various characters with similar characters''' # List of possible replacements for characters someChanges = ( ('a','@'), ('A','@'), ('a','6'), ('A','6'), ('@','a'), ('@','A'), ('6','a'), ('6','A'), ('B','8'), ('8','B'), ('c','('), ('C','('), ('(','c'), ('(','C'), ('e','3'), ('E','3'), ('3','e'), ('3','E'), ('h','#'), ('H','#'), ('#','h'), ('#','H'), ('i','1'), ('I','1'), ('i','!'), ('I','!'), ('1','i'), ('1','I'), ('!','i'), ('!','I'), ('l','1'), ('L','1'), ('l','!'), ('L','!'), ('L','7'), ('1','l'), ('1','L'), ('!','l'), ('!','L'), ('7','L'), ('o','0'), ('O','0'), ('0','o'), ('0','O'), ('s','$'), ('S','$'), ('s','5'), ('S','5'), ('$','s'), ('$','S'), ('$','5'), ('$','5'), ('t','+'), ('T','+'), ('+','t'), ('+','T'), ('x','%'), ('X','%'), ('%','x'), ('%','X'), ('z','2'), ('Z','2'), ('2','z'), ('2','Z') ) randChange = choice(someChanges) cookedWord = string.replace(rawWord,randChange[0],randChange[1],1) return(cookedWord) def changeCase(sheepWord): '''Swap the case for a random letter in a word''' charList = [] a = 0 for c in sheepWord: charList.append(a) a = a+1 itemNo = choice(charList) changedWord = '%s%s%s' % (sheepWord[:itemNo], string.swapcase(sheepWord[itemNo]), sheepWord[itemNo+1:]) return(changedWord) def insertDigits(sheepWord): '''Insert random digits before or after the password''' digitsToInsert = ('0','1','2','3','4','5','6','7','8','9') noOfDigits = (0,1,2,0) digitPosition = ('b','a') chosenDigits = '' a = choice(noOfDigits) while a > 0: chosenDigits = '%s%s' %(chosenDigits, choice(digitsToInsert)) a = a-1 pos = choice(digitPosition) if pos == 'a': changedWord = '%s%s' % (sheepWord, chosenDigits) else: changedWord = '%s%s' % (chosenDigits, sheepWord) return(changedWord) def checkPasswd(freshPasswd): '''Check to see if the password has at least one lowercase, one uppercase one digit and one meta character''' # Using regex like this was the best way I could come up with though # I'm sure it could be done in a better way import re mPat = re.compile(r'[!@#$%^&*()+=]') nPat = re.compile(r'[0-9]') lPat = re.compile(r'[a-z]') uPat = re.compile(r'[A-Z]') mMatch = mPat.search(freshPasswd) if mMatch: nMatch = nPat.search(freshPasswd) if nMatch: lMatch = lPat.search(freshPasswd) if lMatch: uMatch = uPat.search(freshPasswd) if uMatch: result = 'good' else: result = 'bad' else: result = 'bad' else: result = 'bad' else: result = 'bad' return(result) def getWord(): '''Choose a random word from the dictionary that is at least 6 characters and no more than 10''' global DICTWORDS goodWord = choice(DICTWORDS) # A check to keep the word from containing problematic characters, being # too short or too long. searchResult = string.find(goodWord,'\'') + string.find(goodWord,'\"') if (searchResult != -2) | (len(goodWord) < 6) | (len(goodWord) > 10): goodWord = getWord() goodWord = string.strip(goodWord) return(goodWord) def cgiPage(): '''Gets input from a html form and outputs the results''' import cgi cgiLocation = '/python/passgen.py' print 'Content-type: text/html\n\n' print '' form = cgi.FieldStorage() if form.has_key('generate'): pCount = int(form['count'].value) noOfPasswds = pCount if noOfPasswds < 21: passList = '

' while noOfPasswds > 0: newPasswd = mashWords() # Generate new passwords until a good one is found while (checkPasswd(newPasswd) == 'bad'): newPasswd = mashWords() passList = '%s\n%s
' % (passList, newPasswd) noOfPasswds = noOfPasswds - 1 passList = '%s

' % passList else: passList = '

Nice try sonny

' print '''

Number of passwords to generate (upto 20):

\n
\n(Please note that unless run on the same system or over SSL, these passwords are being transmitted in plain text and could be intercepted)\n
\n''' % (cgiLocation, pCount) print passList else: print '''

Number of passwords to generate (upto 20):

''' % cgiLocation print '' sys.exit(0) return() ########################################### ## Start ## ########################################### # Dictionary file dictFile = '/usr/share/dict/words' d = open(dictFile) DICTWORDS = d.readlines() d.close() # Testing #DICTWORDS = ['Sa\'jjad','Test','Sufyan','Zaidi','Za\"idi','abc','1234','1234567890'] # Decide on the method if os.environ.has_key("GATEWAY_INTERFACE"): cgiPage() elif len(sys.argv) > 1: noOfPasswords = int(sys.argv[1]) else: noOfPasswords = 1 while noOfPasswords > 0: newPasswd = mashWords() # Generate new passwords until a good one is found while (checkPasswd(newPasswd) == 'bad'): newPasswd = mashWords() print newPasswd noOfPasswords = noOfPasswords - 1 sys.exit(0)