#!/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
'
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
'
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
'''
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)