My password generator

January 30th, 2010

How do you create a really good password that you don't need to remember but that you might occasionally need to write on paper or type into a keyboard? These days modern operating system provide really good sources of randomness, and one method that is often used is to read some randomness from the operating system PRNG located at /dev/random and run the data through the base64 encoding to get letters, numbers, + (plus) and / (slash). However, those passwords are not that conveinent and sometimes when I write them down people mistake my zeroes for capital o and things like that.

What I wanted was a password generator that could output a configurable length password using only easily distinguishable letters and numbers, so I wrote one. As usual I place this code in the public domain, feel free to use it any way you want.

Features:

  1. The entropy of the password is as good as the underlying operating system. If you use a recent Linux or OSX version, the data returned from /dev/random is quite good.
  2. The code is simple and it is easy to verify that the program actually uses the entropy that it reads.
  3. The resulting passwords are easy to type on keyboards and write on paper without confusing the reader with similar characters such as 1 (one) and l (lower case l).
  4. The length of the password is configurable.
#!/usr/bin/python
 
import sys
 
# alphanumeric chars minus l, I, O, 0, 1
alphabet = "abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789"
# Some expeimentation told me that 2 ** 5.8 = 55.7
BITS_PER_CHAR = 5.8
 
# The default password length has the capacity of a bit more
# than 64 bits of entropy.
DEFAULT_LEN = 12
 
def main(args):
	count = DEFAULT_LEN
	if len(args) > 1:
		if args[1] == '-h':
			usage()
			return
		elif args[1] == '-c' and len(args) > 2:
			count = int(sys.argv[2])
		else:
			usage()
			return
	print(create_password(count))
 
def string_to_bignum(s):
	num = 0
	for c in s:
		num = ord(c) + (num << 8);
	return num
 
def create_password(length):
	random = open("/dev/random", "r")
	needed_bytes = (int)(length * BITS_PER_CHAR) / 8 + 1
	n = string_to_bignum(random.read(needed_bytes))
	random.close()
	s = ""
	for i in xrange(length):
		s = s + alphabet[n % len(alphabet)]
		n = n / len(alphabet)
	return s
 
def usage():
	print """mkpasswd [-h] [-c COUNT]
Create a random password using the operating system's entropy pool
using a 57 character alphabet of letters and numbers. The characters
in the alphabet excludes characters and letters easily confusable such
as I and 1. 
 
Each password character holds about 5.8 bits of entropy, so the
standard 12 character password can hold a theroretical maximum of
69 bits of entropy.
 
The actual entropy present in any generated password is a function
of the entropy gathering algortihm present in the kernel of your
operating system.
 
  -h        display this help text
  -c COUNT  create a password with COUNT characters."""  
 
if __name__ == '__main__':
	main(sys.argv)

My DNSSEC validator

November 8th, 2009

As readers of this blog might have noticed I started to experiment with DNSSEC a few months ago. DNSSEC is basically a way of adding cryptographic keys and signatures to your DNS data that gives resolvers the ability to cryptographically verify the correctness of your DNS records using a series of cryptographic operations.

DNSSEC protects the DNS system against a certain group of security problems such as the kaminskybug, where an attacker tricks a DNS server to return the wrong data to end users. If an attack against the DNS system is successful that means serious trouble, since we depend on it to work reliably in a vast number of online activities. An attacker that controls the DNS system can trick people to for example supply their account information to their online bank and use that to steal money. Whenever there is the potential for large scale fraud you can pretty much be sure that someone will try to break it, and that is why DNSSEC is important.

So, we need DNSSEC. What's stopping us from using it? A few things, but the most important obstacle in my opinion is that it is a complex set of standards and that it is difficult to understand. There are some presentations and HOWTO documents online that attempts to explain and help people get started, but the learning curve is steep. One thing that I ran into when experimenting with my own zones was that somehow I managed to corrupt the signatures of one zone and I couldn't easily pinpoint what the problem was.

When confronted with this I got the idea to build an online service that tries to answer a simple question. What data was used and what cryptographic operations was performed to actually verify one specific DNS record? The answer to that question can be thought of as a chain of operations and records where one link connects to the other from all the way from the record being verified down to the DLV root key.

I decided to write the service in Python and it was one of the most fun programming projects that I have worked on in years. In a way it was basic research but with a clear application and an end result that I think could be a useful contribution. I even wrote my own RSA signature verification functionality, with a lots of help from Python's excellent large integer support.

The service can be found at http://dnssec.resare.com Feel free to give it a spin. There are no doubt bugs and errors that will be fixed and other modifications that will be made, but the basic functionality is in place.

Thanks to Alex for the beautiful HTML design,  to the python dns library dnspython that I use extensively and the airspeed templating library.

DIGEST-MD5 implemented in python

October 8th, 2009

I set up an XMPP server a while ago, and yesterday I noticed that the Psi client could not connect to my server. A quick look in the logs indicated that something went wrong during authentication. Unfortunately the authentication method used, something called DIGEST-MD5, isn't easily debuggable by hand, so I dug up the specification of the method in RFC2831 and wrote a small utility in python that generates a response to a DIGEST-MD5 challenge. It is available from here: digest-md5.py.

I have tested it with the XMPP and SMTP protocols and should run on python 2 variants from 2.3 onwards. Feel free to use it any way you want. Comments and suggestions for improvements are welcome, as always.