r4nd0m pa$$w0rd

Let’s take it easy this week. What about we generate random passwords? That should be fun, right?

dice

Well, we know it shouldn’t be too hard since there’s /dev/urandom readily available to generate strong random bits. We only need to extract them, and somehow convert them to typable keyboard—meaning typable without resorting to that alt-number or shift-ctrl-u nonsense.

The /dev/urandom pseudo-device just spews out random bits as long as you read it, so we must find a way of reading just a certain number of bytes, a number that corresponds to the desired length of the random password. This seems simple enough until you realize you have to filter untypable characters out, and that we must have a rejection-type approach. That is, we read bytes until the wanted number of characters have made their way through. OK, so let’s see how we can filter the characters.

tr, one of the commands I love to hate, allows to filter to delete any unwanted characters. But instead of specifying which character to reject, we will define the set of acceptable characters then complement it:

... | tr --delete --complement 0-9a-z

will filter whatever comes through stdin and will let pass only the letters from a to z and digits 0 to 9. We can use a better set later on. Now to have, say, 8 characters, we can use head, which we normally use for line-based operations, but which also has a character-oriented mode.

... | tr --delete --complement 0-9a-z | head --bytes 8

will output the 8 first bytes of the stream. Or, more exactly, will read the stream until 8 bytes have been output. We just need to pipe in the random stream:

tr --delete --complement 0-9a-z < /dev/urandom| head --bytes 8

and we’re done. Typical output looks like p7081txs.

*
* *

OK, that’s not much of a script, it’s merely a command-line piped command. Let’s wrap that in something more usable. Let’s write a script that takes as optional arguments both length and charset for the password. If no arguments are provided, default charset and length will be used. If one argument is provided, it’s either an integer interpreted as a length (and implied default charset) or a charset name. If two arguments are provided, they must be an integer for the length of the password and a charset name.

#!/usr/bin/env bash

usage()
 {
     echo usage: 
     echo
     echo $0 [length] [style]
     echo 
     echo where
     echo $'\t'length \(optional, defaults to 8\) is length of password
     echo $'\t'style \(optional, defaults to full\) is one of simple, hackerish, full
 }

 
# if argument 1 is provided, check if
# is a number, if not, assume default
# length and take it as a style

case $# in
    0)
        # defaults everything
        len=8
        style=hackerish
        ;;
    1)
        if [ $1 == "-h" ]
        then
            usage
            exit 0
        elif [[ $1 =~ [0-9]+ ]] # is $1 number-like?
        then
            len=$1
            style=hackerish
        else
            len=8
            style=$1
        fi
        ;;
    2)
        if [[ $1 =~ [0-9]+ ]] # is $1 number-like?
        then
            len=$1
            style=$2
        else
            echo first argument must be integer.
            exit 1
        fi
        ;;
esac


case "$style" in

    simple)
        s=0-9a-z
        ;;

    hackerish)
        s=0-9$!a-zA-Z
        ;;

    full)
       
        s='~!@#$%^&*()\+_\-0-9a-zA-Z'
        ;;

    *)
        echo unkown style \"$style\"
        exit 0
        ;;
esac


# spew random password from /dev/urandom
tr --delete --complement $s </dev/urandom | head --bytes $len
echo

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: