Generating Reasonable Passwords with Python

Thanks to a certain recent Open SSL bug there's been a lot of attention paid to passwords in the media. I've been using KeePassX to manage my passwords for the last few years so it's easy for me to find accounts that I should update. It's also a good opportunity to use passwords that are stronger than words such as 'banana'.

My problem is that I have always resisted the generation function in KeePassX because the resulting strings are very hard to remember and transcribe. This isn't an issue if you always use one machine but I tend to chop and change and don't always have my password database on the machine I'm using. I usually have a copy on my phone but successfully typing 'Gh46^f27EEGR1p{' is a hit and miss affair for me. So I prefer passwords that are long but easy to remember, not unlike the advice from XKCD.

Which leaves a problem. Given that I now have to change quite a lot of passwords how can I create suitably random passwords that aren't too difficult to remember or transcribe? Quite coincidentally I read an article titled Using Vim as a passowrd manager. The advice within it is quite sound and at the bottom there is a Python function to generate a password from word lists (in this case the system dictionary). This does a nice job with the caveat that as I understand it from a cryptographic standpoint the passwords it creates are not that strong. But useful enough for sites which aren't my bank or primary email. For those I'm using stupidly long values generated from KeePassX. When I tried the Python function on my machine there was one drawback, it doesn't work in Python 3. This is because the use of 'map' is discouraged in Python 3. But that's alright because I can replace it with one of my favourite Python constructs - the list comprehension. Here is an updated version of invert's function that works in Python 3. Use at your own risk.

def get_password():
    import random
    # Make a list of all of the words in our system dictionary
    f = open('/usr/share/dict/words')
    words = [x.strip() for x in f.readlines()]
    # Pick 2 random words from the list
    password = '-'.join(random.choice(words) for i in range(2)).capitalize()
    # Remove any apostrophes
    password = password.replace("'", "")
    # Add a random number to the end of our password
    password += str(random.randint(1, 9999))
    return password

It'll Come Out in the Wash

After reading this fine summary of the history of Python 3 by Nick Coghlan I was inspired to update as many of my half finished projects and miscellaneous scripts as possible. Then I looked up and I had lost several hours of my life. To save random internet strangers from the same pain as I experienced here is a catalogue of problems and how I solved them.

tl;dr - Python 3.3 on Ubuntu 12.04 LTS is possible but quite tricky. Unless you like system administration you are probably better off waiting until 14.04 LTS comes out in a few months.

I've settled on a default 'stack' of Python and related technologies that I use for most of the code that I write these days;

The good news is that all of the Python related components of these technologies have been ported to Python 3. The bad news is that Flask only runs on Python 3.3. Elementary OS is based on Ubuntu 12.04 LTS and that only has Python 3.2 in it's repositories.

So, first things first, how do we get Python 3.3 on Elementary OS? Or Ubuntu 12.04 which are interchangeable for the purposes of the rest of this post. Quite easily thanks to the 'deadsnakes PPA' as explained in this askubuntu post.

I also don't like to pollute my system Python or operating system packages with project specific Python modules so I always use virtualenv. Unfortunately this doesn't work with the Python 3.3 acquired from the deadsnakes PPA as that doesn't ship with easy_install.

So by this stage I have Python 3.3 running but no way to create or manage a virtual environment. I then remembered Nick's talk at PyCon AU where he mentioned that a new lightweight virtual environment would be included in the standard library. Sure enough the venv module (and pyvenv script) were present on my system so I decided it was a good idea to use them.

The bad part for me is that (and it says this in the documentation) venv does not include any means of installing other Python packages. How then do you get a package installer/manager when you can't install packages? The solution was in a c.l.p. post.

That link describes a process of creating a new virtual environment and then bootstrapping distribute. In case my link-fu fails me the simple shell based approach is;

$ pyvenv py3k ~/envs/py3k
$ cd py3k
$ source bin/activate
$ wget http://python-distribute.org/distribute_setup.py
$ python distribute_setup.py 
$ ./local/bin/easy_install pip

One final little wrinkle though. The c.l.p. post and other references I found indicate that your package scripts (such as 'pip' and 'python') will be installed in $ENV_HOME/bin. In my environment they are installed in $ENV_HOME/local/bin. It's probably something I have inadvertently done. But if you follow the instructions and can't find something in bin have a look in local/bin and you should find it.