#!/usr/bin/python # # PyKey (http://pykey.sajjadzaidi.com/) # Password generator # # Written by A. Sajjad Zaidi (http://www.sajjadzaidi.com/) # # Copyright 2006 # from random import choice import sys import os ####################################### # Basic Control Functions # ####################################### def cgiProcess(): '''Gets input from a html form and processes the options''' global DICTOPEN import cgi cgiLocation = '/pykey.py' form = cgi.FieldStorage() if form.has_key('generate'): pCount = int(form['count'].value) noOfPasswds = pCount pLevel = form['level'].value tooEasyLevel = '' phraseLevel = '' easyLevel = '' midLevel = '' hardLevel = '' hardestLevel = '' pStrength = form['strength'].value longStrength = '' shortStrength = '' if pStrength == 'long': longStrength = 'checked' elif pStrength == 'short': shortStrength = 'checked' else: longStrength = 'checked' pStrength = 'long' if noOfPasswds < 21: if DICTOPEN == 'no' and pLevel == 'medium': pLevel = 'hard' if DICTOPEN == 'no' and pLevel == 'veasy': pLevel = 'easy' if pLevel == 'hardest': passList = '

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

' % passList hardestLevel = 'checked ' elif pLevel == 'hard': passList = '

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

' % passList hardLevel = 'checked ' elif pLevel == 'medium': passList = '

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

' % passList midLevel = 'checked ' elif pLevel == 'easy': passList = '

' while noOfPasswds > 0: newPasswd = makeEasy(pStrength) passList = '%s\n%s
' % (passList, newPasswd) noOfPasswds = noOfPasswds - 1 passList = '%s

' % passList easyLevel = 'checked ' elif pLevel == 'phrase': passList = '

' while noOfPasswds > 0: newPasswd = makePhrase(pStrength) passList = '%s\n%s
' % (passList, newPasswd) noOfPasswds = noOfPasswds - 1 passList = '%s

' % passList phraseLevel = 'checked ' else: passList = '

' while noOfPasswds > 0: newPasswd = makeEasiest(pStrength) passList = '%s\n%s
' % (passList, newPasswd) noOfPasswds = noOfPasswds - 1 passList = '%s

' % passList tooEasyLevel = 'checked ' else: passList = '

Nice try sonny

' midLevel = 'checked ' outputText = '''

Number of passwords to generate (upto 20): 

Security level:
  Hack Me (non-altered words from the dictionary)
  Pass Phrase (phrase of dictionary words)
  Easy (randomly generated, hopefully pronounceable words)
  Medium (mutated dictionary words)
  Hard (mutated generated words)
  Insane (random sequence of letters, numerals and other characters)

Strength:
  Long
  Short

\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%s''' % (cgiLocation, pCount, tooEasyLevel, phraseLevel, easyLevel, midLevel, hardLevel, hardestLevel, longStrength, shortStrength, passList) else: outputText = '''

Number of passwords to generate (upto 20): 

Security level:
  Hack Me (non-altered words from the dictionary)
  Pass Phrase (phrase of dictionary words)
  Easy (randomly generated, hopefully pronounceable words)
  Medium (mutated dictionary words)
  Hard (mutated generated words)
  Insane (random sequence of letters, numerals and other characters)

Strength:
  Long
  Short

''' % cgiLocation printWebPage(outputText) sys.exit(0) return() def cliProcess(pLevel, pStrength, noOfPasswords): global DICTOPEN if DICTOPEN == 'no' and pLevel == 'medium': pLevel = 'hard' if DICTOPEN == 'no' and pLevel == 'veasy': pLevel = 'easy' if pLevel == 'hardest': while noOfPasswords > 0: newPasswd = makeHardest(pStrength) # Generate new passwords until a good one is found while (checkPasswd(newPasswd) == 'bad'): newPasswd = makeHardest(pStrength) print newPasswd noOfPasswords = noOfPasswords - 1 elif pLevel == 'hard': while noOfPasswords > 0: newPasswd = makeHard(pStrength) # Generate new passwords until a good one is found while (checkPasswd(newPasswd) == 'bad'): newPasswd = makeHard(pStrength) print newPasswd noOfPasswords = noOfPasswords - 1 elif pLevel == 'medium': while noOfPasswords > 0: newPasswd = makeMedium(pStrength) # Generate new passwords until a good one is found while (checkPasswd(newPasswd) == 'bad'): newPasswd = makeMedium(pStrength) print newPasswd noOfPasswords = noOfPasswords - 1 elif pLevel == 'easy': while noOfPasswords > 0: newPasswd = makeEasy(pStrength) print newPasswd noOfPasswords = noOfPasswords - 1 elif pLevel == 'phrase': while noOfPasswords > 0: newPasswd = makePhrase(pStrength) print newPasswd noOfPasswords = noOfPasswords - 1 else: while noOfPasswords > 0: newPasswd = makeEasiest(pStrength) print newPasswd noOfPasswords = noOfPasswords - 1 return() ####################################### # Difficulty level functions # ####################################### def makeHardest(pStrength): '''Pick a random length and random characters to generate a password''' characters = ('a','b','c','d','e','f','g','h','i','j','k','l','m', 'n','o','p','q','r','s','t','u','v','w','x','y','z', 'A','B','C','D','E','F','G','H','I','J','K','L','M', 'N','O','P','Q','R','S','T','U','V','W','X','Y','Z', '0','1','2','3','4','5','6','7','8','9', '!','@','#','$','%','^','&','*','=','?','+','-','_') if pStrength == 'long': pLength = choice(range(8,14)) else: pLength = choice(range(4,6)) randomPass = '' a = 0 while a < pLength: randomPass = '%s%s' %(randomPass, choice(characters)) a = a + 1 return(randomPass) def makeHard(pStrength): '''Generate new words, modify them and then mash together''' if pStrength == 'long': lenRange = range(4,7) else: lenRange = range(2,3) wDivider = (' ', '_', '-','.') noOfWords = choice((1,2,3)) if noOfWords == 1: resultPasswd = mutateWord(makeWord(choice(lenRange))) elif noOfWords == 2: randWord = mutateWord(makeWord(choice(lenRange))) randWord2 = mutateWord(makeWord(choice(lenRange))) resultPasswd = '%s%s%s' % (randWord, choice(wDivider), randWord2) else: randWord = mutateWord(makeWord(choice(lenRange))) randWord2 = mutateWord(makeWord(choice(lenRange))) randWord3 = mutateWord(makeWord(choice(lenRange))) resultPasswd = '%s%s%s%s%s' % (randWord, choice(wDivider), randWord2, choice(wDivider), randWord3) return(resultPasswd) def makeMedium(pStrength): '''Get dictionary words, modify them and then mash together''' wDivider = (' ', '_', '-','.') noOfWords = choice((1,2,3)) if noOfWords == 1: resultPasswd = mutateWord(getWord()) elif noOfWords == 2: randWord = mutateWord(getWord()) randWord2 = mutateWord(getWord()) resultPasswd = '%s%s%s' % (randWord, choice(wDivider), randWord2) else: randWord = mutateWord(getWord()) randWord2 = mutateWord(getWord()) randWord3 = mutateWord(getWord()) resultPasswd = '%s%s%s%s%s' % (randWord, choice(wDivider), randWord2, choice(wDivider), randWord3) return(resultPasswd) def makeEasy(pStrength): '''Create new words, then mash them together''' if pStrength == 'long': lenRange = range(4,7) else: lenRange = range(2,3) wDivider = (' ', '_', '-','.') noOfWords = choice((1,2,3)) if noOfWords == 1: resultPasswd = makeWord(choice(lenRange)) elif noOfWords == 2: randWord = makeWord(choice(lenRange)) randWord2 = makeWord(choice(lenRange)) resultPasswd = '%s%s%s' % (randWord, choice(wDivider), randWord2) else: randWord = makeWord(choice(lenRange)) randWord2 = makeWord(choice(lenRange)) randWord3 = makeWord(choice(lenRange)) resultPasswd = '%s%s%s%s%s' % (randWord, choice(wDivider), randWord2, choice(wDivider), randWord3) return(resultPasswd) def makePhrase(pStrength): '''Create a phrase''' noOfWords = choice(range(2,7)) separators = ('','in','of','is a','by','','for','an','a','','and','was') pPhrase = getWord() for n in range(noOfWords): pPhrase = '%s %s %s' % (pPhrase, choice(separators), getWord()) resultPasswd = pPhrase return(resultPasswd) def makeEasiest(pStrength): '''Get words and just join them together with other words without modifying them''' wDivider = (' ', '_', '.') noOfWords = choice((1,2)) if noOfWords == 1: resultPasswd = getWord() else: randWord = getWord() randWord2 = getWord() resultPasswd = '%s%s%s' % (randWord, choice(wDivider), randWord2) return(resultPasswd) ####################################### # Other useful functions # ####################################### def parseCLI(): """If command line arguments are given, parse them and return the results. This seems to work, but all the required options must be given""" from getopt import getopt import re allCmdArgs = sys.argv cmdUsage = 'Usage: %s [OPTIONS]\n\ Command line blog entries.\n\ -h, --help display this help and exit\n\ -l, --level=[veasy|phrase|easy|medium|hard|hardest] Set the difficulty level of the passwords\n\ -s, --strength=[long|short] Set the range of password length\n\ -n, --number=NUMBER Choose the number of passwords to generate' % sys.argv[0] allCmdArgs.pop(0) shortCmdOpts = "hl:s:n:" longCmdOpts = ['help','level=','strength=','number='] try: pLevel = 'medium' pStrength = 'long' pCount = 1 opts, args = getopt(allCmdArgs,shortCmdOpts,longCmdOpts) for opt, val in opts: if opt == "-h" or opt == "--help": sys.exit(1) if opt == "-l" or opt == "--level": pLevel = val if opt == "-s" or opt == "--strength": pStrength = val if opt == "-n" or opt == "--number": pCount = int(val) except: print cmdUsage sys.exit(1) return(pLevel, pStrength, pCount) def printWebPage(outputData): '''Outputs the results''' pageHeader = ''' PyKey | Password Generator

PyKey Password Generator

''' pageFooter = '''
''' print 'Content-type: text/html\n\n' print pageHeader print outputData print pageFooter return() def getWord(): '''Choose a random word from the dictionary that is at least 6 characters and no more than 10''' global DICTWORDS # # Dictionary file # dictFile = '/usr/share/dict/words' # # # Read it into memory. I know, waste of resources, but I thought # # it was better than opening and closing the file for every single # # word # try: # d = open(dictFile) # DICTWORDS = d.readlines() # d.close() # except: # print 'Error opening dictionary file. Exiting.' # sys.exit(1) goodWord = choice(DICTWORDS) # A check to keep the word from containing problematic characters, being # too short or too long. searchResult = goodWord.find('\'') + goodWord.find('\"') if (searchResult != -2) | (len(goodWord) < 6) | (len(goodWord) > 10): goodWord = getWord() goodWord = goodWord.strip() return(goodWord) def makeWord(pLength): '''Try to generate a pronounceable word''' conSounds = ('b','c','d','f','g','h','j','k','l','m', 'n','p','q','r','s','t','v','w','x','y','z', 'bl','br','cl','cr','ch','dr','fl','fr','gh', 'gl','gr','gw','kl','kr','ph','pl','pr','sc', 'sh','sl','sn','sp','st','th','tr','wh','wr', 'B','C','D','F','G','H','J','K','L','M', 'N','P','Q','R','S','T','V','W','X','Y','Z', 'Bl','Br','Cl','Cr','Ch','Dr','Fl','Fr','Gh', 'Gl','Gr','Gw','Kl','Kr','Ph','Pl','Pr','Sc', 'Sh','Sl','Sn','Sp','St','Th','Tr','Wh','Wr') vowelies = ('a','e','i','o','u','y') resultPasswd = '' a = 0 while a < pLength: resultPasswd = '%s%s%s' %(resultPasswd, choice(conSounds),choice(vowelies)) a = a + 1 return(resultPasswd) def mutateWord(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'), ('tion','shun'), ('tion','shon'), ('f','ph'), ('ph','f'), ('th','d'), ('d','th'), ('sh','sc'), ('sion','zon'), ('ee','ie'), ('dg','j') ) randChange = choice(someChanges) cookedWord = rawWord.replace(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], sheepWord[itemNo].swapcase(), sheepWord[itemNo+1:]) return(changedWord) def insertDigits(sheepWord): '''Insert random digits before or after the password''' digitsToInsert = (range(0,10)) 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) nMatch = nPat.search(freshPasswd) lMatch = lPat.search(freshPasswd) uMatch = uPat.search(freshPasswd) if mMatch and nMatch and lMatch and uMatch: result = 'good' else: result = 'bad' return(result) ########################################### ## Start ## ########################################### # Dictionary file dictFile = '/usr/share/dict/words' # Read it into memory. I know, waste of resources, but I thought # it was better than opening and closing the file for every single # word try: d = open(dictFile) DICTWORDS = d.readlines() d.close() DICTOPEN = 'yes' except: DICTOPEN = 'no' pLevel = 'medium' # Decide on the method if os.environ.has_key("GATEWAY_INTERFACE"): cgiProcess() elif len(sys.argv) > 1: #noOfPasswords = int(sys.argv[1]) pLevel, pStrength, noOfPasswords = parseCLI() cliProcess(pLevel, pStrength, noOfPasswords) else: cliProcess('medium', 'short', 1) sys.exit(0)