I\'m interested in creating a very simple, high (cryptographic) quality random password generator. Is there a better way to do this?
import os, random, strin
You can't trust python's pseudo random number generator when generating a password. It is not necessarily cryptographically random. You are seeding the pseudo random number generator from os.urandom
which is a good start. But then you depend on python's generator after that.
A better choice would be the random.SystemRandom() class which takes random numbers from the same source as urandom
. According to the python documentation that should be good enough for cryptographic use. The SystemRandom
class gives you everything that the main random class does but you don't need to worry about the pseudorandomness.
Example code using random.SystemRandom (for Python 3):
import random, string
length = 13
chars = string.ascii_letters + string.digits + '!@#$%^&*()'
rnd = random.SystemRandom()
print(''.join(rnd.choice(chars) for i in range(length)))
Note: Your mileage may vary - the Python Documentation says that random.SystemRandom availability varies by operating system.
It is easy :)
def codegenerator():
alphabet = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
pw_length = 8
mypw = ""
for i in range(pw_length):
next_index = random.randrange(len(alphabet))
mypw = mypw + alphabet[next_index]
return mypw
and the do:
print codegenerator()
Thanks http://xkcd.com/936/
Here is another implementation (python 2; would require some minor rewrites to get it working in 3) that is much faster than OJW's, which seems to loop through the dictionary for each word, despite the comment/implication to the contrary. Timing of OJW's script on my machine, with an 80,000 IOP SSD:
real 0m3.264s
user 0m1.768s
sys 0m1.444s
The following script loads the whole dictionary into a list, then picks words based on a random selection of the index value, using OJW's regex for filtering.
This also generates 10 passphrase sets, allows passing command-line parameters to adjust the number of words, and adds number and symbol padding (also adjustable length).
Sample times for this script:
real 0m0.289s
user 0m0.176s
sys 0m0.108s
Usage: xkcdpass-mod.py 2 4 (for example; these are the default values).
It prints spaces in the output for easy reading, although I've almost never encountered an online service that allows using them, so I would just ignore them. This could definitely be cleaned up with argparse or getopt and allowing switches for including spaces or not, including/excluding symbols, capitals, etc., plus some additional refactoring, but I haven't gotten to that yet. So, without further ado:
#!/usr/bin/env python
#Copyright AMH, 2013; dedicated to public domain.
import os, re, sys, random
from sys import argv
def getargs():
if len(argv) == 3:
numwords = argv[1]
numpads = argv[2]
return(numwords, numpads)
elif len(argv) == 2:
numwords = argv[1]
numpads = 4
return (numwords, numpads)
else:
numwords = 2
numpads = 4
return (numwords, numpads)
def dicopen(dictionary="/usr/share/dict/american-english"):
f = open(dictionary, "r")
dic = f.readlines()
return dic
def genPassword(numwords, numpads):
r = random.SystemRandom()
pads = '0123456789!@#$%^&*()'
padding = []
words = dicopen()
wordlist = []
for i in range (0,int(numpads)):
padding.append(pads[r.randint(0,len(pads)-1)])
#initialize counter for only adding filtered words to passphrase
j = 0
while (j < int(numwords)):
inclusion_criteria = re.compile('^[a-z]{5,10}$')
#Select a random number, then pull the word at that index value, rather than looping through the dictionary for each word
current_word = words[r.randint(0,len(words)-1)].strip()
#Only append matching words
if inclusion_criteria.match(current_word):
wordlist.append(current_word)
j += 1
else:
#Ignore non-matching words
pass
return(" ".join(wordlist)+' '+''.join(padding))
if(__name__ == "__main__"):
for i in range (1,11):
print "item "+str(i)+"\n"+genPassword(getargs()[0], getargs()[1])
Sample output:
[✗]─[user@machine]─[~/bin]
└──╼ xkcdpass-mod.py
item 1
digress basketball )%^)
item 2
graves giant &118
item 3
impelled maniacs ^@%1
And going for the full "correct horse battery staple" (CHBS), no padding:
┌─[user@machine]─[~/bin]
└──╼ xkcdpass-mod.py 4 0
item 1
superseded warred nighthawk rotary
item 2
idealize chirruping gabbing vegan
item 3
wriggling contestant hiccoughs instanced
According to https://www.grc.com/haystack.htm, for all practical purposes, assuming 100 trillion guesses per second (i.e., 100 TH/s) the shorter version would take about 50-60 million centuries to crack; the full CHBS = 1.24 hundred trillion trillion centuries; adding padding to that, 15.51 trillion trillion trillion centuries.
Even enlisting the entire Bitcoin mining network (~2500 TH/s as of this writing), the short version would still likely take 250-300 million years to break, which is probably secure enough for most purposes.
This is more for fun than anything. Scores favorably in passwordmeter.com but impossible to remember.
#!/usr/bin/ruby
puts (33..126).map{|x| ('a'..'z').include?(x.chr.downcase) ?
(0..9).to_a.shuffle[0].to_s + x.chr :
x.chr}.uniq.shuffle[0..41].join[0..41]
Yes, no amatuer hacker is going to be cracking that password. Now, after this I recommend continuing on your random password generator project and make a UI or GUI interface either with Tkinter or Flask so others can use it. For example, I found this nice little project just by searching, 'password generator python UI'. https://passwordgenerator.pythonanywhere.com/
Maybe you'd like to make something similar to above? Its a good skill to know how to implement python into web development.
Good luck.
Peace out
import uuid
print('Your new password is: {0}').format(uuid.uuid4())