[Tutor] Clunky Password maker

Martin A. Brown martin at linux-ip.net
Wed May 25 20:34:34 CEST 2011


Hello,

 : Is there a less clunky way to do this?

Yes.  There are probably many ways to do this, and this is just 
something I cooked up at a moment's notice in reply to your 
question, and probably could be significantly improved upon.

 : def new_pass():

Your function takes no arguments.  Maybe this is something to 
reconsider.  Keep reading.

 :     series = ['`', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', \
 :               '~', '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', \
 :               'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\\', \
 :               'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '{', '}', '|', \
 :               'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', "'", \
 :               'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', '"', \
 :               'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/', \
 :               'Z', 'X', 'C', 'V', 'B', 'N', 'M', '<', '>', '?']

So, if I were staring at my keyboard and starting to type in the 
characters (not a QWERTZ or AZERTY keyboard, it seems), I would ask 
myself the question--is there any module that has the complete set 
of characters that are available to me already, so I don't have to 
type them all in?

Indeed, there is?  I found the module called 'string'.  In that 
module, the following are available to you:

    ascii_letters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
    ascii_lowercase = 'abcdefghijklmnopqrstuvwxyz'
    ascii_uppercase = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
    digits = '0123456789'
    hexdigits = '0123456789abcdefABCDEF'
    letters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
    lowercase = 'abcdefghijklmnopqrstuvwxyz'
    octdigits = '01234567'
    printable = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTU...
    punctuation = '!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~'
    uppercase = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
    whitespace = '\t\n\x0b\x0c\r '

OK, so because I believe in strong (or, at least, reasonably strong) 
passwords, I'll concatenate a few of these to make the set of 
characters that I would like to produce in a password.  To me, the 
'printable' set seems a bit, well, difficult for the person typing a 
password, so I'll stick with the following:

  validchars = string.ascii_letters + string.digits + string.punctuation
  series = list( validchars )

 :     passwd = []
 :     p = input("Enter the length you want your password to be: ")
 :               # length of password

Maybe you could make the function 'new_pass' take as an argument the 
number of characters.  That way, your function would not need to 
read input from anyplace else (the console).  It could generate 
passwords of any length regardless of whether it was called from a 
CGI or command-line or even used from a bulk password generation 
system.

 :     for i in range(p):
 :         r = random.randint(0, 94)

Uh-oh!  You had already counted and hard-coded the length of your 
variable 'series'!  What happens if I want to add a few characters 
at the end?  Like maybe the €, the £ or the ¥.  I am currently 
obsessed with (con)currency.

If you were to let Python count the elements in your variable 
'series', then you would not have to have counted all 94 elements 
yourself.  So, think about why this might be better (more 
maintainable):

 :     passwd = []
 :     for i in range(p):
 :         r = random.randint(0, len(series))
 :         passwd.append(series[r]) # Append a random char from series[] to passwd

Now, in the (admittedly, revised loop, above), you generate a single 
random.randint() for each of the elements that you wish to extract.  
Chances are that somebody has already implemented a feature to do 
this sort of thing.  Doesn't it seem like a pretty rational sort of 
thing to have a compute do?  Select a bunch of elements randomly 
from an input set?

So, let's try this one of two different ways.  Using option A, you 
can mix up your data and repeatedly pull out the same result 
for the N items:

  random.shuffle(series)
  ''.join(series[0:p])

In option B, you would get a different result each time:

  ''.join(random.sample(validchars,p))

So, I would rewrite your function to be (substituting 'charcount' 
for 'p'):

  #! /usr/bin/env python
  
  import string
  import random
  
  def new_pass(charcount):
      validchars = string.ascii_letters + string.digits + string.punctuation
      series = list( validchars )
      random.shuffle(series)
      print ''.join(series[0:charcount])
  
  if __name__ == '__main__':
      new_pass(int(input("Enter the length you want your password to be: ")))

Two others have already suggested that you look at the string and 
random modules.  Here's just one example of how you could use them 
together.  Now, let's see what you do with MD5 and SHA.  (You are 
looking at 'hashlib', correct?)

Good luck,

-Martin

-- 
Martin A. Brown
http://linux-ip.net/


More information about the Tutor mailing list