lemonldap-ng/lemonldap-ng-common/lib/Lemonldap/NG/Common/TOTP.pm

56 lines
1.4 KiB
Perl
Raw Normal View History

package Lemonldap::NG::Common::TOTP;
# This module is inspired by Auth::GoogleAuth written by Gryphon Shafer
# <gryphon@cpan.org>
use strict;
use Mouse;
use Convert::Base32 qw(decode_base32 encode_base32);
use Crypt::URandom;
2018-02-19 22:47:10 +01:00
use Digest::HMAC_SHA1 'hmac_sha1_hex';
2019-02-12 18:21:38 +01:00
our $VERSION = '2.1.0';
2018-08-18 21:21:20 +02:00
# Verify that TOTP $code matches with $secret
sub verifyCode {
2018-02-21 22:07:12 +01:00
my ( $self, $interval, $range, $digits, $secret, $code ) = @_;
my $s = eval { decode_base32($secret) };
if ($@) {
$self->logger->error('Bad characters in TOTP secret');
return -1;
}
for ( -$range .. $range ) {
2018-02-21 22:07:12 +01:00
if ( $code eq $self->_code( $s, $_, $interval, $digits ) ) {
$self->userLogger->info("Codes match at range $_");
return 1;
}
}
return 0;
}
# Internal subroutine that calculates TOTP code using $secret and $interval
sub _code {
2018-02-21 22:07:12 +01:00
my ( $self, $secret, $r, $interval, $digits ) = @_;
my $hmac = hmac_sha1_hex(
pack( 'H*',
2018-03-18 22:38:12 +01:00
sprintf( '%016x', int( ( time - $r * $interval ) / $interval ) ) ),
$secret,
);
return sprintf(
2018-02-21 22:07:12 +01:00
'%0' . $digits . 'd',
(
hex( substr( $hmac, hex( substr( $hmac, -1 ) ) * 2, 8 ) ) &
0x7fffffff
2018-08-18 21:21:20 +02:00
) % 10**$digits
);
}
# Simply generate new base32 secret
sub newSecret {
my ($self) = @_;
return encode_base32( Crypt::URandom::urandom(20) );
}
1;