File Coverage

blib/lib/Authen/OATH.pm
Criterion Covered Total %
statement 13 15 86.6
branch n/a
condition n/a
subroutine 5 5 100.0
pod n/a
total 18 20 90.0


line stmt bran cond sub pod time code
1             #!/usr/bin/perl
2             ################################################################################
3             # $Id: OATH.pm 4 2010-05-25 16:18:21Z v89326 $
4             # $URL: file:///S:/svn/Authen-OATH/trunk/lib/Authen/OATH.pm $
5             ################################################################################
6             #
7             # Title: Authen::OATH
8             # Author: Kurt Kincaid
9             # VERSION: 1.0.0
10             #
11             ################################################################################
12             package Authen::OATH;
13              
14 1     1   22038 use warnings;
  1         2  
  1         29  
15 1     1   4 use strict;
  1         2  
  1         30  
16 1     1   775 use Digest::HMAC;
  1         614  
  1         54  
17 1     1   1536 use Math::BigInt;
  1         25399  
  1         6  
18 1     1   17136 use Moose;
  0            
  0            
19              
20             has 'digits' => (
21             'is' => 'rw',
22             'isa' => 'Int',
23             'default' => 6
24             );
25              
26             has 'digest' => (
27             'is' => 'rw',
28             'isa' => 'Str',
29             'default' => 'Digest::SHA1'
30             );
31              
32             has 'timestep' => (
33             'is' => 'rw',
34             'isa' => 'Int',
35             'default' => 30
36             );
37              
38             =head1 NAME
39              
40             Authen::OATH - OATH One Time Passwords
41              
42             =head1 VERSION
43              
44             Version 1.0.0
45              
46             =cut
47              
48             our $VERSION = "1.0.0";
49              
50             =head1 SYNOPSIS
51              
52             Implementation of the HOTP and TOTP One Time Password algorithms
53             as defined by OATH (http://www.openauthentication.org)
54              
55             All necessary parameters are set by default, though these can be
56             overridden. Both totp() and htop() have passed all of the test
57             vectors defined in the RFC documents for TOTP and HOTP.
58              
59             totp() and hotp() both default to returning 6 digits and using SHA1.
60             As such, both can be called by passing only the secret key and a
61             valid OTP will be returned.
62              
63             use Authen::OATH;
64              
65             my $oath = Authen::OATH->new();
66             my $totp = $oath->totp( "MySecretPassword" );
67             my $hotp = $oath->hotp( "MyOtherSecretPassword" );
68            
69             Parameters may be overridden when creating the new object:
70              
71             my $oath = Authen::OATH->new( 'digits' => 8 );
72            
73             The three parameters are "digits", "digest", and "timestep."
74             Timestep only applies to the totp() function.
75              
76             While strictly speaking this is outside the specifications of
77             HOTP and TOTP, you can specify digests other than SHA1. For example:
78              
79             my $oath = Authen::OATH->new( "digits" => 10,
80             "digest" => "Digest::MD6"
81             );
82              
83             =head1 SUBROUTINES/METHODS
84              
85             =head2 totp
86              
87             my $otp = $oath->totp( $secret [, $manual_time ] );
88            
89             Manual time is an optional parameter. If it is not passed, the current
90             time is used. This is useful for testing purposes.
91              
92             =cut
93              
94             sub totp {
95             my ( $self, $secret, $manual_time ) = @_;
96             $secret = join( "", map chr( hex() ), $secret =~ /(..)/g )
97             if $secret =~ /^[a-fA-F0-9]{32,}$/;
98             my $mod = $self->{ 'digest' };
99             if ( eval "require $mod" ) {
100             $mod->import();
101             }
102             my $time = $manual_time || time();
103             my $T = Math::BigInt->new( int( $time / $self->{ 'timestep' } ) );
104             die "Must request at least 6 digits" if $self->{ 'digits' } < 6;
105             ( my $hex = $T->as_hex ) =~ s/^0x(.*)/"0"x(16 - length $1) . $1/e;
106             my $bin_code = join( "", map chr hex, $hex =~ /(..)/g );
107             my $otp = _process( $self, $secret, $bin_code );
108             return $otp;
109             }
110              
111             =head2 hotp
112              
113             my $opt = $oath->hotp( $secret, $counter );
114            
115             Both parameters are required.
116              
117             =cut
118              
119             sub hotp {
120             my ( $self, $secret, $c ) = @_;
121             $secret = join( "", map chr( hex() ), $secret =~ /(..)/g )
122             if $secret =~ /^[a-fA-F0-9]{32,}$/;
123             my $mod = $self->{ 'digest' };
124             if ( eval "require $mod" ) {
125             $mod->import();
126             }
127             $c = Math::BigInt->new( $c );
128             die "Must request at least 6 digits" if $self->{ 'digits' } < 6;
129             ( my $hex = $c->as_hex ) =~ s/^0x(.*)/"0"x(16 - length $1) . $1/e;
130             my $bin_code = join( "", map chr hex, $hex =~ /(..)/g );
131             my $otp = _process( $self, $secret, $bin_code );
132             return $otp;
133             }
134              
135             =head2 _process
136              
137             This is an internal routine and is never called directly.
138              
139             =cut
140              
141             sub _process {
142             my ( $self, $secret, $bin_code ) = @_;
143             my $hmac = Digest::HMAC->new( $secret, $self->{ 'digest' } );
144             $hmac->add( $bin_code );
145             my $hash = $hmac->digest();
146             my $offset = hex substr unpack( "H*" => $hash ), -1;
147             my $dt = unpack "N" => substr $hash, $offset, 4;
148             $dt &= 0x7fffffff;
149             $dt = Math::BigInt->new( $dt );
150             my $modulus = 10 ** $self->{ 'digits' };
151              
152             if ( $self->{ 'digits' } < 10 ) {
153             return sprintf( "%0$self->{ 'digits' }d", $dt->bmod( $modulus ) );
154             }
155             else {
156             return $dt->bmod( $modulus );
157             }
158              
159             }
160              
161             =head1 AUTHOR
162              
163             Kurt Kincaid, C<< <kurt.kincaid at gmail.com> >>
164              
165             =head1 BUGS
166              
167             Please report any bugs or feature requests to C<bug-authen-totp at rt.cpan.org>, or through
168             the web interface at L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Authen-OATH>. I will be notified, and then you'll
169             automatically be notified of progress on your bug as I make changes.
170              
171              
172             =head1 SUPPORT
173              
174             You can find documentation for this module with the perldoc command.
175              
176             perldoc Authen::OATH
177              
178              
179             You can also look for information at:
180              
181             =over 4
182              
183             =item * RT: CPAN's request tracker
184              
185             L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=Authen-OATH>
186              
187             =item * AnnoCPAN: Annotated CPAN documentation
188              
189             L<http://annocpan.org/dist/Authen-OATH>
190              
191             =item * CPAN Ratings
192              
193             L<http://cpanratings.perl.org/d/Authen-OATH>
194              
195             =item * Search CPAN
196              
197             L<http://search.cpan.org/dist/Authen-OATH/>
198              
199             =back
200              
201              
202             =head1 ACKNOWLEDGEMENTS
203              
204              
205             =head1 LICENSE AND COPYRIGHT
206              
207             Copyright 2010 Kurt Kincaid.
208              
209             This program is free software; you can redistribute it and/or modify it
210             under the terms of either: the GNU General Public License as published
211             by the Free Software Foundation; or the Artistic License.
212              
213             See http://dev.perl.org/licenses/ for more information.
214              
215              
216             =cut
217              
218             1; # End of Authen::OATH
219              
220             ################################################################################
221             # EOF