[Cryptography] Throwing dice for "random" numbers

Jonathan Thornburg jthorn4242 at gmail.com
Wed Aug 15 00:32:51 EDT 2018


On Tue, Aug 14, 2018 at 11:33:51PM -0400, Arnold Reinhold via cryptography wrote:
> For random characters from the set {a-z, 0-9}, one can make a six
> by six table with the 36 possible characters and use pairs fo dice
> rolls to select each character. For just letters, one can just
> discard pairs of rolls that produce digits from that table. To
> include upper and lower case letters and some special characters
> (the ten above the digits on standard keyboards), one can use a
> third die and press the shift key if the roll is odd. In the last
> example there are 72 possible characters, giving 6.17 bits of entropy
> per character, compared to 6.57 bits per character selected randomly
> from the 95 printing ASCII characters.

If one has (and is willing to trust) a computer, random alphanumeric
passwords can be generated by
  % dd if=/dev/random bs=50 count=1|alphanumeric.encode 
where the following script should be somewhere in your search path:

#!/usr/bin/perl -w
use strict;
use Getopt::Long;
my $false = 0;
my $true  = 1;

########################################

my $help_msg = <<'EOF';
Usage:
   dd if=/dev/arandom bs=100 count=1| alphanumeric.encode
or
   dd if=/dev/arandom bs=100 count=1| alphanumeric.encode  --lower-case

By default, this program encodes standard input binary-data into mixed-case
alphanumeric characgers ([a-zA-Z0-9]).

If the --lower-case option is specified, then this program encodes into
lower-case alphanumeric characters ([a-z0-9]).
EOF

#
# At present we do the encoding "by hand".
# FIXME: It would probably be better to not reinvent the wheel here,
#        and instead use an existing Perl module such as Math::Int2Base
#        or Math::Base::Convert.
#
# Mixed-case:
#   Since 2*26+10 = 62, and 62**4 = 14776336 = less than 256**3 = 16777216,
#   we can encode 3 chars (24 bits) of binary input into 4 base62 output
#   characters.  If we get an "unencodable" input 3-tuple we just discard
#   it and try again with the next input 3-tuple.
#
# Lower-case
#   Since 26+10 = 36, and 36**3 = 46656 = less than 256**2 = 65536,
#   we can encode 2 chars (16 bits) of binary input into 3 base36 output
#   characters.  If we get an "unencodable" input 2-tuple we just discard
#   it and try again with the next input 2-tuple.
#

########################################

my $debug           = 0;
my $help_flag       = $false;
my $lower_case_flag = $false;
GetOptions(
  'debug=i'    => \$debug,
  'help'       => \$help_flag,
  'lower-case' => \$lower_case_flag,
	  ) || die $help_msg;				# *** ERROR EXIT ***
if ($help_flag)
	{ print $help_msg; exit; }			# *** --help EXIT ***

########################################

my $N_in     = $lower_case_flag ? 2 : 3;
my $in_base  = 256;
my $in_limit = $in_base ** $N_in;
my $N_out        = $lower_case_flag ? 3 : 4;
my @out_alphabet = $lower_case_flag ? (0..9, 'a'..'z')
				    : (0..9, 'a'..'z', 'A'..'Z');
my $out_base     = scalar(@out_alphabet);
my $out_limit    = $out_base ** $N_out;

if ($debug > 0)
	{
	print "in_base=${in_base} N_in=${N_in} ==> in_limit = ${in_limit}\n";
	print "out_base=${out_base} N_out=${N_out} ==> out_limit = ${out_limit}\n";
	}

binmode(STDIN);

my $count_on_line = 0;
my $buffer;
	while (my $N_read = read(STDIN, $buffer, $N_in))
	{
	if ($N_read != $N_in)
		{ last; }				# *** LOOP EXIT ***
	my @in_chars  = split(//, $buffer);
	my @in_digits = map {ord($_)} @in_chars;

	# integer in [0, $in_limit)
	my $N = $lower_case_flag
		? $in_digits[0] + $in_base*$in_digits[1]
		: $in_digits[0] + $in_base
				  *($in_digits[1] + $in_base*$in_digits[2]);
	if ($debug >= 6)
		{ print "in_digits=(",join(',', at in_digits),") ==> N=${N}\n"; }

	if ($N >= $out_limit)
		{
		if ($debug >= 6)
			{ print "   N > out_limit ==> try again\n"; }
		next;					# *** LOOP CONTROL ***
		}
	
	my @out_digits = ();
	
		for (my $i = 0 ; $i < $N_out ; ++$i)
		{
		my $d  = $N % $out_base;
		$N = int($N / $out_base);
		push @out_digits, $d;
		}
	if ($debug >= 6)
		{ print "   out_digits=(",join(',', at out_digits),")\n"; }

	my @out_chars = map {$out_alphabet[$_]} @out_digits;

	$count_on_line += $N_out;
	if ($debug >= 6)
		{ print '   :',join('', at out_chars),":\n"; }
	   else { print join('', at out_chars); }
	if ($count_on_line >= 60)
		{
		print "\n";
		$count_on_line = 0;
		}
	}


More information about the cryptography mailing list