Google Authenticator implementation in Perl

后端 未结 4 928
無奈伤痛
無奈伤痛 2021-02-06 19:02

I am looking for a simple Perl implementation that verifies a Google authenticator token that has been created using a server side secret. For instance,

The following Go

相关标签:
4条回答
  • 2021-02-06 19:21

    Would Auth::GoogleAuthenticator work for your purposes?

    Edit: sure it does; this validates the OTP as generated by the JS. When the counter isn't timely anymore it returns a empty string; i.e. false. And using the URL results in the app being synced to the 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
    
    0 讨论(0)
  • 2021-02-06 19:29

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

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

    use Digest::HMAC_SHA1 qw/ hmac_sha1_hex /;
    
    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 full TOTP 2 Factor Auth Perl script can be downloaded from Github.

    0 讨论(0)
  • 2021-02-06 19:31

    Ok it took a little while but I've got a Perl solution (hopefully this makes up for the slightly lazy question :) Thanks to Borodin for his help with this (Taking the SHA1 HMAC of 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);
    }
    
    0 讨论(0)
  • 2021-02-06 19:36

    Here's another solution, and you can verify 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";
    
    0 讨论(0)
提交回复
热议问题