TOTP in Perl

TOTP (or Time-based One-time Password Algorithm) is an algorithm used to generate a one-time password from a previously shared key.  Now if that confused you, maybe you’re more familiar with Google Authenticator.  If you’re keen to generate an OTP in Perl, or integrate it in your web application, then the code below can be used to either generate the OTP, or to check if the OTP is valid.

#!/usr/bin/perl

# Phil's Google Authenticator code in Perl

use strict;
use Digest::SHA qw(hmac_sha1);
use Convert::Base32;

my $otp = &generateOTP('abcd efgh ijkl mnop');
print "Your OTP is $otp\n";

sub generateOTP
{
my ($key,$interval) = @_;

# Turn the key into a standard string, no spaces, all upper case
$key = uc($key);
$key =~ s/\ //g;

# decode the key from base32
my $key_decoded = decode_base32($key);

# Read the time, and produce the 30 second slice
my $time = int(time / 30) + $interval;

# Pack the time to binary
$time = chr(0) . chr(0) . chr(0) . chr(0) . pack('N*',$time);

# hash the time with the key
my $hmac = hmac_sha1 ($time,$key_decoded);

# get the offset
my $offset = ord(substr($hmac,-1)) & 0x0F;

# use the offset to get part of the hash
my $hashpart = substr($hmac,$offset,4);

# get the first number
my @val = unpack("N",$hashpart);
my $value = $val[0];

# grab the first 32 bits
$value = $value & 0x7FFFFFFF;
$value = $value % 1000000;

return $value;
}

Tags:

  1. Nice, or HOTP:

    #!/usr/bin/perl

    use strict;
    use Digest::SHA qw(hmac_sha1);
    use Convert::Base32;

    my $otp = &generateOTP(‘abcd efgh ijkl mnop’, 7 );
    print “Your OTP is $otp\n”;

    sub generateOTP
    {
    my ($key,$counter) = @_;

    # Turn the key into a standard string, no spaces, all upper case
    $key = uc($key);
    $key =~ s/\ //g;

    # decode the key from base32
    my $key_decoded = decode_base32($key);

    # Pack the counter to binary
    $counter = chr(0) . chr(0) . chr(0) . chr(0) . pack(‘N*’,$counter);

    # hash the counter with the key
    my $hmac = hmac_sha1 ($counter,$key_decoded);

    # get the offset
    my $offset = ord(substr($hmac,-1)) & 0x0F;

    # use the offset to get part of the hash
    my $hashpart = substr($hmac,$offset,4);

    # get the first number
    my @val = unpack(“N”,$hashpart);
    my $value = $val[0];

    # grab the first 32 bits
    $value = $value & 0x7FFFFFFF;
    $value = $value % 1000000;

    return $value;
    }

    Granted you would have to handle the counter and some sort of “look-ahead”

    Now I’d prefer TOTP but in some occasions a OTP generator might not have a timesource (such as a yubikey in OATH mode)

    Tho both examples suffer from not printing the zeros the the case of the OTP would have ended up beeing something like 004211

    Nice stuff, thanks Phil

  2. Almost forgot, one could also argue that the interval should actually be what you divided the time with (unless 0 🙂 and perhaps default 30 ) and not something you add. Google authenticator divide by 30 but it all comes down to an agreement of the validator and generator in terms of the timeframe for a valid OTP

Leave a Reply

Your email address will not be published. Required fields are marked *