Here is an interesting problem: how to generate sufficiently random but semi-pronounceable, and easy to remember passwords. I mean, putting together a random password generator is easy – just pick bunch of random character from a pool of printable symbols and you are done. The problem with this method is that more often than not you end up with something like Bz9hFtT[mp1 which while reasonably strong, is virtually impossible to remember. I don’t know about you, but my brain is just not very good at holding on to random sequences of characters and/or numbers.
To me best passwords are ones that are not dictionary words, but retain word-like qualities. You can sort of sound them out in your head. How do you accomplish that though? The simplest way is to alternate vowels and consonants. The algorithm goes something like this:
- Pick a random character
- If the last character was a consonant, next pick a vowel
- If the last character was a vowel, pick a consonant/number/sybmol
- Consonants should common, numbers/symbols should be rare
You could improve this a bit by allowing strings of 2-3 consonants to happen every once in a while. If you really wanted to be fancy you could attach weight to each character based on the statistical frequency in which it tends to appear in natural language. But the above works surprisingly well – especially if you choose to break up your passwords into 4 or 6 character syllables. Here are some sample passwords I generated using the above method:
Pretty decent for something this simple, eh? I thin that RoFu-RYKy and WiRU-xOZi are my favorites from this list.
Here is a sample python code I used to accomplish this magic:
# ct: dd66d04b-ce35-420a-9b64-63817bd43fa9 def next_char(last_char, use_symbols=False): """ Return a randomly generated character """ vowels = ['a', 'e', 'i', 'o', 'u', 'y', 'A', 'E', 'I', 'O', 'U', 'Y'] consonants = [i for i in string.letters if i not in vowels] # Using a reduced set of symbols for clarity symbols = ["@", "#", "$", "%", "&"] if not last_char: return random.choice(string.letters) if last_char in consonants or last_char in string.digits or last_char in symbols: return random.choice(vowels) if last_char in vowels: pct = random.randint(0, 100) if pct < 60: return random.choice(consonants) elif pct < 90: return random.choice(string.digits) elif use_symbols: return random.choice(symbols) else: return random.choice(consonants) def gen_password(length = 8): """ Generate random password given length """ passwd = "" last = "" for i in range(length): last = next_char(last) passwd += last; if i%4==3 and i!=length-1: passwd += "-" return passwd
In case you were wondering, I have a working demo here. The code could probably be improved, but feel free to use it if you want to. See if you can retain the GUID number up top. I decided to follow Jeff Atwood's advice and start tagging the code snippets with a unique ID's. This way if they for some strange reason become part of your code base you will know where they came from and you can always google that ID to check for updates. This is probably not that important in a trivial piece of code like this one, but I'm doing it to get in the habit.