A Better uuidgen

Tuesday, March 03, 2015 » security

Recently, I put together some examples to use in a RESTful workshop, and needed to generate a bunch of UUIDs.

On OS X I have a couple of options. First, there’s uuidgen:

1
2
$ uuidgen
E4B221B0-9466-4354-8A33-5B3EB5D3ABE3

Under OS X, this creates a DCE version 4 (random) UUID. However, under FreeBSD, uuidgen always generates a version 1 UUID. And finally, under Linux, uuidgen can create either version 1 or version 4 UUIDs, but defaults to a random UUID when a high-quality RNG is available.1

The second option I have on my MBP is the OSSP uuid tool. It’s also available on Linux and FreeBSD:

1
2
$ uuid
1d98d052-c107-11e4-a360-6796fc8cedc1

By default, uuid returns a DCE version 1 (time + MAC) UUID. I can override this to get a random UUID, which is usually what you want:

1
2
$ uuid -v 4
07158102-962d-4038-a3d6-fc6428b98313

Now I have a cross-platform way to generate a version 4 UUID from the command line. Next I need to get that value into my clipboard.

Trailing newlines with uuidgen and uuid

Both uuidgen and uuid output a trailing newline character. Now, that’s fine when just displaying the UUID in a terminal, but is decidedly less helpful when piping the UUID somewhere, for example to the clipboard:

1
$ uuidgen | pbcopy

Now when I paste the copied text somewhere else, like into a JSON document, the newline messes up my formatting (yes, I know, this is all very sad.)

1
2
3
4
{
    "uuid": "07158102-962d-4038-a3d6-fc6428b98313
"
}

A better uuidgen

Fortunately, the problems above are easily solved with a little bash magic. The systems where I run this have CPRNGs, so I’m comfortable with forcing version 4:

1
2
3
4
5
6
7
8
#!/usr/bin/env bash

if [ -t 1 ]
then
    uuid -v 4
else
    uuid -v 4 | tr -d '\n'
fi

This script first tests whether output is attached to a terminal. If so, a version 4 UUID is output with a trailing newline. Otherwise, if we are piping the output, we’ll strip off the trailing newline character.

Or, if you don’t want to depend on OSSP’s uuid tool:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
#!/usr/bin/env python

import sys
import uuid

result = str(uuid.uuid4())
if sys.stdout.isatty():
    print(result)
else:
    sys.stdout.write(result)

The Python uuid.uuid4() function will use your operating system’s native uuid_generate_random function call, if available, to generate the UUID. Failing that, it will use os.urandom. Only if a CPRNG is unavailable will uuid.uuid4() resort to using random.randrange(256) like some kind of primitive animal.2

Now I have a version of uuidgen that works consistently across platforms, and is pipe-able.

Hooray!

  • 1I suppose this makes some kind of sense, since a bad generator would be more likely to introduce collisions. You also don’t want to lull the user into a false sense of security (allowing them to assume their UUIDs are unpredicatable and opaque when they actually aren’t).
  • 2If you have to use such a platform, you have my sympathy.