Wednesday, July 11, 2018

[idrdhpwa] Key stretching in Perl

Below is a demonstration of how to use Perl's crypt function to generate a SHA-256 password hash stretched to 654321 iterations.

(Previously similar, in Python.)  Perl may be better than Python for this because setting user passwords often happens very early during or after installation of a system, when Python (nor the mkpasswd utility in the whois package) might not yet be installed, but Debian even in its most minimal installation configuration always installs Perl.

Unlike Python, Perl does not have a built-in "make random salt" function, so we make it ourselves out of bytes read out of /dev/urandom.  Maybe we should have an option to manually specify the salt.

We permit retyping the password as many times as you want, in contrast to 2 times in typical password-setting procedures.  More than twice decreases the chance of typing it wrong accidentally twice, a fluke that is unsettlingly likely for a long password.  The password is echoed as you type; if this is a danger, then use a separate utility that doesn't echo and pipe it in (The Unix Way).  Possible dangers include shoulder-surfing and the visible password persisting in memory, then swap, of terminal buffers, e.g., gnome-terminal or screen.  Incidentally, which terminals wipe and overwrite memory when clearing the screen (^L) or closing a window?

Typing (much) more than twice also allows learning it.

Maybe we should have had a separate program (The Unix Way) for typing the password multiple times, verifying they are identical.

At the top in the comments are instructions on attempting to avoid the password hash from hitting disk.  (/run/shm can get swapped to disk, though, so it's not attempting too hard.)  These instructions are little bit silly because the hash eventually must hit disk in /etc/shadow.

#! perl -wl
# mkdir /run/shm/me
# chmod go-rwx /run/shm/me
# perl crypt-password.pl > /run/shm/me/passwd
# sudo usermod -p $(cat /run/shm/me/passwd) username
# rm -fr /run/shm/me
print STDERR "Keep repeatedly typing the password, or 'done':";
$plain=<STDIN>;
chomp$plain;
for(;;){
    # Note: password will be echoed as you type.
    $verify=<STDIN>;
    chomp$verify;
    last if $verify eq "done";
    die unless $verify eq$plain;
}
$_=`dd if=/dev/urandom count=1`;
@random=unpack("C*",$_);
die unless @random>=16;
#for(@random){    print "$_\n"; }
@chars=("a".."z","A".."Z","0".."9",".","/");
die unless @chars==64;
for(0..15){
    $salt.=$chars[$random[$_] & 63];
    # we assume lower bits are just as random as upper, unlike LFSR PRNG.
}
#print$salt;
$type=5; #or 6
$rounds=654321;
$full='$'.$type."\$rounds=$rounds\$".$salt;
$hash=crypt $plain,$full;
print$hash;

No comments :