|I l@ve RuBoard|
7.4 Generating Non-Totally Random Passwords
Credit: Luther Blissett
You need to create new passwords randomly梖or example, to assign them automatically to new user accounts梐nd want the passwords to be somewhat feasible to remember for typical users, so they won't be written down.
We can use a pastiche approach for this, mimicking letter n-grams in actual English words. A grander way to look at the same approach is to call it a Markov Chain simulation of English:
import random, string class password: # Any substantial file of English words will do just as well data = open("/usr/share/dict/words").read().lower( ) def renew(self, n, maxmem=3): self.chars =  for i in range(n): # Randomly "rotate" self.data randspot = random.randrange(len(self.data)) self.data = self.data[randspot:] + self.data[:randspot] where = -1 # Get the n-gram locate = ''.join(self.chars[-maxmem:]) while where<0 and locate: # Locate the n-gram in the data where = self.data.find(locate) # Back off to a shorter n-gram if necessary locate = locate[1:] c = self.data[where+len(locate)+1] if not c.islower( ): c = random.choice(string.lowercase) self.chars.append(c) def _ _str_ _(self): return ''.join(self.chars) if _ _name_ _ == '_ _main_ _': "Usage: pastiche [passwords [length [memory]]]" import sys if len(sys.argv)>1: dopass = int(sys.argv) else: dopass = 8 if len(sys.argv)>2: length = int(sys.argv) else: length = 10 if len(sys.argv)>3: memory = int(sys.argv) else: memory = 3 onepass = password( ) for i in range(dopass): onepass.renew(length, memory) print onepass
This recipe is useful when creating new user accounts and assigning each user a different, random password, using passwords that a typical user will find feasible to remember, so that the passwords will not be written down. See Recipe 7.3 if you prefer totally-random passwords.
The recipe's idea is based on the good old pastiche concept. Each letter (always lowercase) in the password is chosen pseudo-randomly from data that is a collection of words in a natural language familiar to the users. This recipe uses /usr/share/dict/words as supplied with Linux systems (on my machine, a file of over 45,000 words), but any large document in plain text will do just as well. The trick that makes the passwords sort of memorable, and not fully random, is that each letter is chosen based on the last few letters already picked for the password as it stands so far, so that letter transitions will tend to be repetitive. There is a break when the normal choice procedure would have chosen a nonalphabetic character, in which case a random letter is chosen instead.
[situ@tioni cooker]$ python pastiche.py yjackjaceh ackjavagef aldsstordb dingtonous stictlyoke cvaiwandga lidmanneck olexnarinl [situ@tioni cooker]$ python pastiche.py ptiontingt punchankin cypresneyf sennemedwa iningrated fancejacev sroofcased nryjackman [situ@tioni cooker]$
As you can see, some of these are definitely wordlike, others less so, but for a typical human being, none are more problematic to remember than a sequence of even fewer totally random, uncorrelated letters. No doubt some theoretician will complain (justifiably, in a way) that these aren't as random as all that. Well, tough. My point is that they had better not be if some poor fellow is going to have to remember them! You can compensate for this by making them a bit longer. If said theoretician shows us how to compute the entropy per character of this method of password generation (versus the obvious 4.7 bits/character of passwords made up of totally random lowercase letters, for example), now that would be a useful contribution indeed. Meanwhile, I'll keep generating passwords this way, rather than in a totally random way, whenever I'm asked to do so. If nothing else, it's the closest thing to a useful application for the pastiche concept that I've found.
7.4.4 See Also
Recipe 7.3; documentation of the standard library module random in the Library Reference.
|I l@ve RuBoard|