Monday, September 23, 2013

Generating Random Passcodes / PSK

This blog is basically a response to a recent diary post at the SANS Internet Storm Center. Rob VandenBrink's post on How do you spell "PSK"? offers up a nice block of code to generate pre-shared keys in python. This got me thinking of the various ways I've used to generate random passwords or pre-shared keys. There are a lot of good tools that do this for you. I like apg as it has a nice "pronounceable" mode to generate strings that could be spoken easier.

The first one is a simple binary pre-shared key generator. It will output HEX strings based on the byte size of the data. Since pre-shared keys are often used in crypto, this is the only example I'm giving that uses /dev/random instead of /dev/urandom.

  • dd if=/dev/urandom count=1 bs=24 2>/dev/null | xxd -p
To change the size of the key, alter the block size (bs) parameter for the dd command.

This next one is a simple perl example that I've used from time to time. This one takes two parameters for the minimum and maximum lengths that you want to generate the strings with. This matches Rob VandenBrink's example in that it only generates alpha-numeric strings.
  • #!/usr/bin/perl -w
  • use strict;
  • use Getopt::Long;
  • my $min;
  • my $max;
  • GetOptions('min=s' => \$min, 'max=s' => \$max );
  • if(!$max){ $max = shift(@ARGV); }
  • if(!$min){ $min = shift(@ARGV); }
  • if(!$max){ usage(); }
  • if(!$min){ $min = 1; }
  • if($min =~ m/[^0-9]/ || $max =~ m/[^0-9]/){ usage(); }
  • if($min > $max){ usage(); }
  • my $len = $min + int( rand($min - $max) );
  • my $exp_pass = join('', map { ("A".."Z", "a".."z", 0..9 )[rand 62] } 1..$len);
  • print "$exp_pass\n";
  • sub usage {
    • print "Usage: [-max] INTEGER [[-min] INTEGER]\n";
    • exit(1);
  • }

The last script is a bash command that I use the most often. It's probably the worst example code wise, but it's very simple and seems to generate the most random strings. Basically it reads a 1k block from /dev/urandom and removes all the unwanted characters. Similar to the previous example, it will take up to two parameters for min/max length, but it will run best without any parameters. This command will generate strings with symbols in them, so you'll have to watch where you use them. It still amazes me how often I run across documentation that has no mention on how to escape symbols or special characters in their authentication parameters.
  • #!/bin/bash
  • exec 2>/dev/null
  • MIN=0
  • test -n "$1" -a $1 -gt 0 && MIN=$1
  • MAX=100
  • test -n "$2" -a $2 -gt $MIN && MAX=$2
  • PW=""
  • until [ ${#PW} -gt $MIN -a ${#PW} -le $MAX ]; do
    • PW=`dd if=/dev/urandom bs=1k count=1 2>/dev/null | sed -e 's/[^-a-zA-Z0-9<>.,;:=+]*//g' | head -n 1`
  • done
  • echo "$PW"

I hope these examples will help other people. They've been useful for me. When I need a new password generated, I will usually run one of these multiple times and pick one out of the list.