File Coverage

blib/lib/Authen/HOTP.pm
Criterion Covered Total %
statement 31 31 100.0
branch 6 10 60.0
condition 3 5 60.0
subroutine 6 6 100.0
pod 0 1 0.0
total 46 53 86.7


line stmt bran cond sub pod time code
1             package Authen::HOTP;
2              
3 1     1   26115 use Digest::SHA1 qw(sha1);
  1         3217  
  1         150  
4 1     1   1512 use Digest::HMAC qw(hmac);
  1         789  
  1         105  
5 1     1   124135 use Math::BigInt;
  1         55270  
  1         7  
6 1     1   72378 use strict;
  1         3  
  1         41  
7 1     1   6 use warnings;
  1         2  
  1         13211  
8              
9             require Exporter;
10              
11             our @ISA = qw(Exporter);
12              
13             our %EXPORT_TAGS = ( 'all' => [ qw( hotp ) ] );
14              
15             our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
16              
17             our @EXPORT = qw( );
18              
19             our $VERSION = '0.02';
20              
21             sub hotp
22             {
23 11     11 0 3721 my ($secret, $c, $digits) = @_;
24              
25             # guess hex encoded
26 11 100       268 $secret = join("", map chr(hex), $secret =~ /(..)/g)
27             if $secret =~ /^[a-fA-F0-9]{32,}$/;
28              
29 11 50       98 $c = new Math::BigInt ($c)
30             unless ref $c eq "Math::BigInt";
31              
32 11   100     483 $digits ||= 6;
33              
34 11 50       27 die unless length $secret >= 16; # 128-bit minimum
35 11 50       25 die unless ref $c eq "Math::BigInt";
36 11 50 33     64 die unless $digits >= 6 and $digits <= 10;
37              
38 11         34 (my $hex = $c->as_hex) =~ s/^0x(.*)/"0"x(16 - length $1).$1/e;
  11         229  
39 11         123 my $bin = join '', map chr hex, $hex =~ /(..)/g; # pack 64-bit big endian
40 11         48 my $hash = hmac $bin, $secret, \&sha1;
41 11         204 my $offset = hex substr unpack("H*" => $hash), -1;
42 11         23 my $dt = unpack "N" => substr $hash, $offset, 4;
43 11         12 $dt &= 0x7fffffff; # 31-bit
44 11         18 $dt %= (10 ** $digits); # limit range
45              
46 11         84 sprintf "%0${digits}d", $dt;
47             }
48              
49             1;
50             __END__