Google Authenticator implementation in Perl

I'm looking for a simple Perl implementation that validates a google authentication token that was generated using the server secret. For example,

The following google url allows you to encode the server secret in base32 format (in the following case the secret e4ytonjeim4hcsrhja5fe5kqfu ) as a QR code that can be read from the google authenticator app (see image below):
https://chart.googleapis.com / chart? cht = qr & chs = 100x100 & chl = otpauth% 3A% 2F% 2Ftotp% 2Fmysite% 3A29% 3Fsecret% 3De4ytonjeim4hcsrhja5fe5kqfu% 26issuer% 3Dmysite

chart? cht = qr & chs = 200x200 & chl = otpauth% 3A% 2F% 2Ftotp% 2Fmysite% 3A29% 3Fsecret% 3De4ytonjeim4hcsrhja5fe5kqfu% 26issuer% 3Dmysite

As soon as the QR code is scanned into the authenticator app, it produces tokens like: 716340. How to check if the token is correct?

This question is the Perl equivalent of this question in Python: Implementation of Google Authenticator in Python

+3


source to share


4 answers


Ok, it took a little time, but I have a Perl solution (hopefully it compensates for the slightly lazy question :) Thanks Borodin for help with this ( Taking SHA1 HMAC hex strings in Perl )



#!/usr/bin/perl -w

use strict;
use warnings;

use Convert::Base32;
use Digest::HMAC_SHA1 qw/ hmac_sha1_hex /;

my $base_32_secret = "JBSWY3DPEHPK3PXP";
print "".totp_token($base_32_secret)."\n";

sub totp_token {
    my $secret = shift;

    my $key = unpack("H*", decode_base32($secret));
    my $lpad_time = sprintf("%016x", int(time()/30));
    my $hmac = hmac_sha1_hex_string($lpad_time, $key);

    my $offset = sprintf("%d", hex(substr($hmac, -1)));

    my $part1 = 0 + sprintf("%d", hex(substr($hmac, $offset*2, 8)));
    my $part2 = 0 + sprintf("%d", hex("7fffffff"));

    my $token = substr("".($part1 & $part2), -6);
    return $token;
}

sub  hmac_sha1_hex_string {
   my ($data, $key) = map pack('H*', $_), @_;
   hmac_sha1_hex($data, $key);
}

      

+5


source


Here's another solution and you can check it matches the tokens produced in this example



use Authen::OATH;
use Convert::Base32;
my $oath = Authen::OATH->new();
my $secret = "JBSWY3DPEHPK3PXP";
my $otp = $oath->totp(  decode_base32( $secret ) );
print $otp."\n";

      

+6


source


Will Auth :: GoogleAuthenticator work for your purposes?

Edit: required; this checks the OTP as JS generated. When the counter is not timely, it returns an empty string; those. false. And using a url causes the app to sync with JS:

use Data::Printer;
use Auth::GoogleAuthenticator;

my $auth = Auth::GoogleAuthenticator->new(secret_base32 => q/e4ytonjeim4hcsrhja5fe5kqfu/);
say $auth->registration_url;
p($auth->verify('252499'));

      

Output:

otpauth://totp/?secret=e4ytonjeim4hcsrhja5fe5kqfu
1

      

+3


source


For posterity, I took the script from @ Vijay's answer (thanks dude), simplified the algorithm a bit, added docs from the TOTP definition, and added some sample code.

The number generation code I have shortened to which is just a simplification of @ Vijay's answer:

my $paddedTime = sprintf("%016x", int(time() / $TIME_STEP));
my $data = pack('H*', $paddedTime);
my $key = decode_base32($secret);

# encrypt the data with the key and return the SHA1 of it in hex
my $hmac = hmac_sha1_hex($data, $key);

# take the 4 least significant bits (1 hex char) from the encrypted string as an offset
my $offset = hex(substr($hmac, -1));
# take the 4 bytes (8 hex chars) at the offset (* 2 for hex), and drop the high bit
my $encrypted = hex(substr($hmac, $offset * 2, 8)) & 0x7fffffff;

# the token is then the last 6 digits in the number
my $token = $encrypted % 1000000;
# make sure it is 0 prefixed
return sprintf("%06d", $token);

      

The complete TOTP 2 Factor Auth Perl script can be downloaded from Github.

+2


source







All Articles