In this article I'll explain (with code) how to create and validate a Google Authenticator two-step authentication token, using Perl. Both on the command-line and on a simple website.

Some background

Google Authenticator is a two-step authentication process created by Google. It's software based, unlike those hardware tokens your bank may use, and when paired with a service it provides a six digit number which must be used alongside one's credentials to log in to a service which requires use of it.

Google provides software versions of the Authenticator for iOS, Blackberry, Android, and a number of other platforms, including "native" Windows executables, or even HTML5 web applications.

Google generates an 80-bit secret key for each user, saves it, and then displays it (encoded in base32) to the user in the form of a QR code. The user can then use their mobile application to input it, and use the generated six-digits to authenticate from then onwards.

The application "simply" creates a HMAC-SHA1 using the secret key, using the "number of 30-seconds periods elapsed since the Epoch" as the message. A part of this is extracted and converted to the six-digit code.

 How do I create a Google Authenticator secret?

At its most basic, the "secret" comprises of 10 bytes from /dev/urandom. That's it. For more security, one would want to get more than just 10 bytes. Say, 50:

my $len_secret_bytes = 50;
open my $RNG, '<', '/dev/urandom'
  or die "Cannot open /dev/urandom for reading: $!";
sysread $RNG, my $secret_bytes, $len_secret_bytes
  or die "Cannot read $len_secret_bytes from /dev/urandom: $!";
close $RNG
  or die "Cannot close /dev/urandom: $!";

Since you will want to have users input the secret data in the Authenticator application, or have them snap a picture of the QR code to input it in the application, you'll have to convert those $secret_bytes to Base32:

use Convert::Base32;
my $secret_base32 = encode_base32( $secret_bytes );

You will want to save that $secret_base32 somewhere like in a database (next to the "password_bcrypt" for the user), or in a flat file (in ~/.google_authenticator) that is user-specific, since you and your users will want to have it handy: without it, you won't be able to verify their six-digit code, and they won't be able to either.

 Displaying the QR code

Now you have the $secret_base32. You can just display that to the users, and they'll be able to manually enter it on the Google Authenticator app.

Or, you can have that displayed as a nice QR code they can simply take a picture of, and the app will add the account for them. To do that, you will just need two things: the $secret_base32 code you have already generated for the user, and a $label you want your users to associate with that secret, for example [email protected] on Your Website.

use URI::Escape;
my $url   = sprintf("otpauth://totp/%s?secret=%s", $label, $secret_base32);
my $qr_url = sprintf("https://www.google.com/chart?chs=200x200" .
    "&chld=M|0&cht=qr&chl=%s", uri_escape($url));

You can then embed that QR code URL inside an <img> tag and your users will be able to easily add the Google Authenticator secret you have generated for them to their app.

 How to authenticate users

You have generated a Google Authenticator code for a user, they have added it to their app via a QR code, and now they'd like to... use it!

Once you have a user's $secret_base32 you can check whether the $given_token they give you is the correct one, in which case you can continue on checking their password, or not:

use Authen::OATH;
use Convert::Base32;
my $correct_token = make_token_6(
    Authen::OATH->new->totp(
        decode_base32( $secret_base32 )
    )
);
$given_token = make_token_6($given_token);

if ($given_token eq $correct_token) {
    print "Yup!"
    # Check password, etc.
} else {
    print "Nope!"
}

sub make_token_6 {
    my $token = shift;
    while (length $token < 6) {
        $token = "0$token";
    }
    return $token;
}

The above method will only authenticate users against the current value shown on their Google Authenticator. If their device's clock (or your server's clock) drifts too much, they may have problems authenticating against your server.

If you're comfortable enough with a 30-seconds drifting either way, you can look into the following optional parameter for ->totp:

my $otp = $oath->totp( $secret [, $manual_time ] );

You will then be able to compute:

my @possible_tokens =
    map { $oath->totp($secret, $_) }
        time-30, time, time+30;

And check either of @possible_tokens against the $given_token.

 Conclusions

If you have read through this article, you should now be able to use Perl and the CPAN to:

  • Create a secure Google Authenticator "secret", and store it per-user
  • Display the "secret" so your users can easily import it on their app
  • Authenticate your users against the known secret

Here's to hoping this article will help make websites written in Perl just that bit more secure!