Pass-OTP-1.801/0000755000225700022570000000000014744441512012417 5ustar00jbaierjbaierPass-OTP-1.801/lib/0000755000225700022570000000000014744441512013165 5ustar00jbaierjbaierPass-OTP-1.801/lib/Pass/0000755000225700022570000000000014744441512014073 5ustar00jbaierjbaierPass-OTP-1.801/lib/Pass/OTP.pm0000644000225700022570000000734514744441317015107 0ustar00jbaierjbaierpackage Pass::OTP;
=encoding utf8
=head1 NAME
Pass::OTP - Perl implementation of HOTP / TOTP algorithms
=head1 SYNOPSIS
use Pass::OTP qw(otp);
use Pass::OTP::URI qw(parse);
my $uri = "otpauth://totp/ACME:john.doe@email.com?secret=HXDMVJECJJWSRB3HWIZR4IFUGFTMXBOZ&issuer=ACME&digits=6";
my $otp_code = otp(parse($uri));
=cut
use utf8;
use strict;
use warnings;
use MIME::Base32 qw(decode_base32);
use Digest::HMAC;
use Digest::SHA;
use Math::BigInt;
require Exporter;
our @ISA = qw(Exporter);
our @EXPORT_OK = qw(otp hotp totp);
our $VERSION = '1.801';
=head1 DESCRIPTION
The C module provides implementation of HOTP and TOTP algorithms according to the RFC 4226 and RFC 6238.
=head1 FUNCTIONS
=over 4
=item hotp(%options)
Computes HMAC-based One-time Password (RFC 4226).
HOTP(K,C) = Truncate(HMAC-SHA-1(K,C))
Step 1: Generate an HMAC-SHA-1 value
Let HS = HMAC-SHA-1(K,C)
Step 2: Generate a 4-byte string (Dynamic Truncation)
Let Sbits = DT(HS)
Step 3: Compute an HOTP value
Let Snum = StToNum(Sbits) # Convert S to a number in 0..2^{31}-1
Return D = Snum mod 10^Digit # D us a number in the range 0..10^{Digit}-1
=cut
sub hotp {
my %options = (
algorithm => 'sha1',
counter => 0,
digits => 6,
@_,
);
my $C = Math::BigInt->new($options{counter});
my ($hex) = $C->as_hex =~ /^0x(.*)/;
$hex = "0" x (16 - length($hex)) . $hex;
my ($algorithm) = $options{algorithm} =~ /sha(\d+)/i;
my $digest = Digest::SHA->new($algorithm);
my $hmac = Digest::HMAC->new(
$options{base32} ? decode_base32($options{secret} =~ s/ //gr) : pack('H*', $options{secret}),
$digest,
$algorithm < 384? 64 : 128,
);
$hmac->add(pack 'H*', $hex);
my $hash = $hmac->digest;
my $offset = hex(substr(unpack('H*', $hash), -1));
my $bin_code = unpack('N', substr($hash, $offset, 4));
$bin_code &= 0x7fffffff;
$bin_code = Math::BigInt->new($bin_code);
if (defined $options{chars}) {
my $otp = "";
foreach (1 .. $options{digits}) {
$otp .= substr($options{chars}, $bin_code->copy->bmod(length($options{chars})), 1);
$bin_code = $bin_code->btdiv(length($options{chars}));
}
return $otp;
}
else {
my $otp = $bin_code->bmod(10**$options{digits});
return "0" x ($options{digits} - length($otp)) . $otp;
}
}
=item totp(%options)
Computes Time-based One-time Password (RFC 6238).
TOTP = HOTP(K,T)
T = (Current Unix time - T0) / X
=cut
sub totp {
my %options = (
'start-time' => 0,
now => time,
period => 30,
@_,
);
$options{counter} = Math::BigInt->new(int(($options{now} - $options{'start-time'}) / $options{period}));
return hotp(%options);
}
=item otp(%options)
Convenience wrapper which calls totp/hotp according to options.
=cut
sub otp {
my %options = (
type => 'hotp',
@_,
);
return totp(
%options,
digits => 5,
chars => "23456789BCDFGHJKMNPQRTVWXY",
) if defined $options{issuer} and $options{issuer} =~ /^Steam/i;
return hotp(%options) if $options{type} eq 'hotp';
return totp(%options) if $options{type} eq 'totp';
}
=back
=head1 SEE ALSO
L
L
RFC 4226
RFC 6238
L
=head1 COPYRIGHT AND LICENSE
Copyright (C) 2020 Jan Baier
This program is free software; you can redistribute it and/or modify it
under the terms of either: the GNU General Public License as published
by the Free Software Foundation; or the Artistic License.
See L for more information.
=cut
1;
Pass-OTP-1.801/lib/Pass/OTP/0000755000225700022570000000000014744441512014535 5ustar00jbaierjbaierPass-OTP-1.801/lib/Pass/OTP/URI.pm0000644000225700022570000000237314570621571015541 0ustar00jbaierjbaierpackage Pass::OTP::URI;
=encoding utf8
=head1 NAME
Pass::OTP::URI - Parse otpauth:// URI
=head1 SYNOPSIS
use Pass::OTP::URI qw(parse);
my $uri = "otpauth://totp/ACME:john.doe@email.com?secret=HXDMVJECJJWSRB3HWIZR4IFUGFTMXBOZ&issuer=ACME&digits=6";
my %options = parse($uri);
=cut
use utf8;
use strict;
use warnings;
require Exporter;
our @ISA = qw(Exporter);
our @EXPORT_OK = qw(parse);
=head1 FUNCTIONS
=over 4
=item parse($uri)
=cut
sub parse {
my ($uri) = @_;
my %options = (
base32 => 1,
);
($options{type}, $options{label}, my $params) = $uri =~ m#^otpauth://([th]otp)/((?:[^:?]+(?::|%3A))?[^:?]+)\?(.*)#;
foreach my $param (split(/&/, $params)) {
my ($option, $value) = split(/=/, $param);
$options{$option} = $value;
}
return (%options);
}
=back
=head1 SEE ALSO
L
L
=head1 COPYRIGHT AND LICENSE
Copyright (C) 2024 Jan Baier
This program is free software; you can redistribute it and/or modify it
under the terms of either: the GNU General Public License as published
by the Free Software Foundation; or the Artistic License.
See L for more information.
=cut
1;
Pass-OTP-1.801/bin/0000755000225700022570000000000014744441512013167 5ustar00jbaierjbaierPass-OTP-1.801/bin/oathtool0000755000225700022570000000337014744440475014760 0ustar00jbaierjbaier#!/usr/bin/perl
=head1 NAME
oathtool - alternative Perl implementation of oathtool(1), one-time password tool
=head1 SYNOPSIS
oathtool [options] KEY | URI
Options:
-h, --help
-v, --version
-a, --algorithm
--hotp
--totp[=STRING]
-b, --base32
-c, --counter=INT
-s, --time-step-size, --period=SECONDS
-S, --start-time=UNIX_TIME
-N, --now=UNIX_TIME
-d, --digits=INT
URI: otpauth://....
=cut
use utf8;
use strict;
use warnings;
use open qw(:std :utf8);
use Getopt::Long;
use Pod::Usage;
use Pass::OTP qw(otp);
use Pass::OTP::URI qw(parse);
our $VERSION = '1.2';
pod2usage(2) if @ARGV == 0;
my %options;
$options{secret} = pop if $ARGV[-1] !~ /^-/;
Getopt::Long::Configure(qw(auto_version no_ignore_case));
GetOptions(\%options,
'help|h|?' => sub { pod2usage(-verbose => 2) },
'algorithm|a=s',
'hotp',
'totp:s',
'base32|b',
'counter|c=i',
'period|time-step-size|s=s',
'start-time|S=s',
'now|N=s',
'digits|d=i',
) || pod2usage(2);
pod2usage(2) if @ARGV == 0 and not defined $options{secret};
$options{type} = 'totp' if defined $options{totp};
$options{algorithm} = $options{totp} if defined $options{totp} and $options{totp} ne '';
%options = parse($options{secret}) if $options{secret} =~ m#^otpauth://#;
my $code = otp(%options);
print "$code\n";
exit 0;
__END__
=head1 SEE ALSO
L
L
=head1 COPYRIGHT AND LICENSE
Copyright (C) 2020 Jan Baier
This program is free software; you can redistribute it and/or modify it
under the terms of either: the GNU General Public License as published
by the Free Software Foundation; or the Artistic License.
See L for more information.
=cut
Pass-OTP-1.801/bin/otptool0000755000225700022570000000163514500304046014610 0ustar00jbaierjbaier#!/usr/bin/perl
=head1 NAME
otptool - one-time password tool
=head1 SYNOPSIS
otptool otpauth://...
=cut
use utf8;
use strict;
use warnings;
use open qw(:std :utf8);
use Getopt::Long;
use Pod::Usage;
use Pass::OTP qw(otp);
use Pass::OTP::URI qw(parse);
our $VERSION = $Pass::OTP::VERSION;
Getopt::Long::Configure(qw(auto_version));
GetOptions(
'help|h|?' => sub { pod2usage(-verbose => 2) },
) || pod2usage(2);
pod2usage(2) if @ARGV == 0;
pod2usage(2) if $ARGV[0] !~ m#^otpauth://#;
printf("%s\n", otp(parse($ARGV[0])));
exit 0;
__END__
=head1 SEE ALSO
L
L
=head1 COPYRIGHT AND LICENSE
Copyright (C) 2020 Jan Baier
This program is free software; you can redistribute it and/or modify it
under the terms of either: the GNU General Public License as published
by the Free Software Foundation; or the Artistic License.
See L for more information.
=cut
Pass-OTP-1.801/README.pod0000644000225700022570000000313014570621535014057 0ustar00jbaierjbaier=encoding utf8
=head1 NAME
Pass::OTP - Perl implementation of HOTP / TOTP algorithms
=head1 SYNOPSIS
use Pass::OTP qw(otp);
use Pass::OTP::URI qw(parse);
my $uri = "otpauth://totp/ACME:john.doe@email.com?secret=HXDMVJECJJWSRB3HWIZR4IFUGFTMXBOZ&issuer=ACME&digits=6";
my $otp_code = otp(parse($uri));
=head1 DESCRIPTION
The C module provides implementation of HOTP and TOTP algorithms according to the RFC 4226 and RFC 6238.
=head1 FUNCTIONS
=over 4
=item hotp(%options)
Computes HMAC-based One-time Password (RFC 4226).
HOTP(K,C) = Truncate(HMAC-SHA-1(K,C))
Step 1: Generate an HMAC-SHA-1 value
Let HS = HMAC-SHA-1(K,C)
Step 2: Generate a 4-byte string (Dynamic Truncation)
Let Sbits = DT(HS)
Step 3: Compute an HOTP value
Let Snum = StToNum(Sbits) # Convert S to a number in 0..2^{31}-1
Return D = Snum mod 10^Digit # D us a number in the range 0..10^{Digit}-1
=item totp(%options)
Computes Time-based One-time Password (RFC 6238).
TOTP = HOTP(K,T)
T = (Current Unix time - T0) / X
=item otp(%options)
Convenience wrapper which calls totp/hotp according to options.
=back
=head1 SEE ALSO
L
L
RFC 4226
RFC 6238
L
=head1 COPYRIGHT AND LICENSE
Copyright (C) 2024 Jan Baier
This program is free software; you can redistribute it and/or modify it
under the terms of either: the GNU General Public License as published
by the Free Software Foundation; or the Artistic License.
See L for more information.
Pass-OTP-1.801/README0000644000225700022570000000323614570621527013306 0ustar00jbaierjbaierNAME
Pass::OTP - Perl implementation of HOTP / TOTP algorithms
SYNOPSIS
use Pass::OTP qw(otp);
use Pass::OTP::URI qw(parse);
my $uri = "otpauth://totp/ACME:john.doe@email.com?secret=HXDMVJECJJWSRB3HWIZR4IFUGFTMXBOZ&issuer=ACME&digits=6";
my $otp_code = otp(parse($uri));
DESCRIPTION
The "Pass::OTP" module provides implementation of HOTP and TOTP
algorithms according to the RFC 4226 and RFC 6238.
FUNCTIONS
hotp(%options)
Computes HMAC-based One-time Password (RFC 4226).
HOTP(K,C) = Truncate(HMAC-SHA-1(K,C))
Step 1: Generate an HMAC-SHA-1 value
Let HS = HMAC-SHA-1(K,C)
Step 2: Generate a 4-byte string (Dynamic Truncation)
Let Sbits = DT(HS)
Step 3: Compute an HOTP value
Let Snum = StToNum(Sbits) # Convert S to a number in 0..2^{31}-1
Return D = Snum mod 10^Digit # D us a number in the range 0..10^{Digit}-1
totp(%options)
Computes Time-based One-time Password (RFC 6238).
TOTP = HOTP(K,T)
T = (Current Unix time - T0) / X
otp(%options)
Convenience wrapper which calls totp/hotp according to options.
SEE ALSO
Digest::HMAC
oathtool(1)
RFC 4226 RFC 6238
COPYRIGHT AND LICENSE
Copyright (C) 2024 Jan Baier
This program is free software; you can redistribute it and/or modify it
under the terms of either: the GNU General Public License as published
by the Free Software Foundation; or the Artistic License.
See for more information.
Pass-OTP-1.801/Changes0000644000225700022570000000136414744441262013720 0ustar00jbaierjbaier1.801 2025/01/23
- fix rt#158598
1.800 2025/01/02
- switch to MIME::Base32
1.702 2024/03/11
- fix type in Makefile.PL
1.701 2024/03/11
- fix kwalitee issues
- improve versioning
1.700 2024/03/02
- fix algorithm case matching
1.600 2023/09/13
- fix SHA384 and SHA512 blocksize (#1)
1.500 2020/07/02
- fix min supported versions
1.400 2020/07/01
- fix min supported versions
1.300 2020/06/27
- added otptool which knows only otpauth:// links
1.200 2020/06/27
- added support for custom character classes
- added compatibility with Steam Guard
1.100 2020/06/27
- added support for parsing otpauth://... links
- added compatibility with Google Authenticator
1.000 2020/06/27
- added support for HOTP
- added support for TOTP
Pass-OTP-1.801/t/0000755000225700022570000000000014744441512012662 5ustar00jbaierjbaierPass-OTP-1.801/t/oathtool.t0000644000225700022570000000620514500307504014673 0ustar00jbaierjbaieruse Test::More;
use utf8;
use strict;
use warnings;
use open qw(:std :utf8);
my $oathtool = '/usr/bin/oathtool';
sub t {
my ($cmd, %args) = @_;
my $ret = qx($cmd);
chomp($ret);
my $code = Pass::OTP::otp(%args);
return is($code, $ret, $cmd);
}
if (not -x $oathtool or system("$oathtool -w1 00")) {
plan skip_all => 'oathtool not installed';
}
require_ok 'Pass::OTP';
t(
'oathtool 00',
secret => "00",
);
TODO: {
local $TODO = "Parameter --window not implemented";
t(
"$oathtool -w 10 3132333435363738393031323334353637383930",
secret => "3132333435363738393031323334353637383930",
window => 10,
);
t(
"$oathtool --base32 -w 3 GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ",
secret => "GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ",
base32 => 1,
window => 3,
);
}
t(
"$oathtool --base32 --totp 'gr6d 5br7 25s6 vnck v4vl hlao re'",
secret => "gr6d 5br7 25s6 vnck v4vl hlao re",
base32 => 1,
type => 'totp',
);
t(
"$oathtool -c 5 3132333435363738393031323334353637383930",
secret => "3132333435363738393031323334353637383930",
counter => 5,
);
t(
"$oathtool -b --totp --now '2008-04-23 17:42:17 UTC' IFAUCQIK",
secret => "IFAUCQIK",
base32 => 1,
type => 'totp',
now => `date -d'2008-04-23 17:42:17 UTC' +%s`,
);
t(
"$oathtool --totp --now '2008-04-23 17:42:17 UTC' 00",
secret => "00",
type => 'totp',
now => `date -d'2008-04-23 17:42:17 UTC' +%s`,
);
t(
"$oathtool --totp 00",
secret => "00",
type => 'totp',
);
t(
"$oathtool --totp --digits=8 --now '2009-02-13 23:31:30 UTC' 3132333435363738393031323334353637383930313233343536373839303132",
secret => "3132333435363738393031323334353637383930313233343536373839303132",
type => 'totp',
digits => 8,
now => `date -d'2009-02-13 23:31:30 UTC' +%s`
);
t(
"$oathtool --totp=sha256 --digits=8 --now '2009-02-13 23:31:30 UTC' 3132333435363738393031323334353637383930313233343536373839303132",
secret => "3132333435363738393031323334353637383930313233343536373839303132",
type => 'totp',
algorithm => 'sha256',
digits => 8,
now => `date -d'2009-02-13 23:31:30 UTC' +%s`,
);
t(
"$oathtool --totp=sha512 --digits=8 --now '2009-02-13 23:31:30 UTC' 3132333435363738393031323334353637383930313233343536373839303132",
secret => "3132333435363738393031323334353637383930313233343536373839303132",
type => 'totp',
algorithm => 'sha512',
digits => 8,
now => `date -d'2009-02-13 23:31:30 UTC' +%s`,
);
TODO: {
local $TODO = "Parameter --window not implemented";
t(
"$oathtool --totp 00 -w5",
secret => "00",
type => 'totp',
window => 5,
);
}
TODO: {
local $TODO = "Parameter --verbose not implemented";
t(
"$oathtool --totp -v -N '2033-05-18 03:33:20 UTC' -d8 3132333435363738393031323334353637383930",
secret => "3132333435363738393031323334353637383930",
type => 'totp',
verbose => 1,
now => `date -d'2033-05-18 03:33:20 UTC' +%s`,
digits => 8,
);
}
done_testing(14);
Pass-OTP-1.801/t/totp-test-vectors.t0000644000225700022570000000422514570621253016477 0ustar00jbaierjbaieruse Test::More;
use utf8;
use strict;
use warnings;
require_ok 'Pass::OTP';
# Based on the test vectors from RFC6238
my %seeds = (
sha1 => '3132333435363738393031323334353637383930',
sha256 => '3132333435363738393031323334353637383930'.
'313233343536373839303132',
sha512 => '3132333435363738393031323334353637383930'.
'3132333435363738393031323334353637383930'.
'3132333435363738393031323334353637383930'.
'31323334',
);
sub is_totp {
my %opts = (@_);
is(
Pass::OTP::otp(
secret => $seeds{lc $opts{algorithm}},
algorithm => $opts{algorithm},
now => $opts{now},
digits => 8,
type => 'totp'),
$opts{totp},
"Test vector with time (sec) $opts{now} on mode $opts{algorithm}"
);
}
is_totp(now => 59, algorithm => 'sha1', totp => '94287082');
is_totp(now => 59, algorithm => 'sha256', totp => '46119246');
is_totp(now => 59, algorithm => 'sha512', totp => '90693936');
is_totp(now => 1111111109, algorithm => 'sha1', totp => '07081804');
is_totp(now => 1111111109, algorithm => 'sha256', totp => '68084774');
is_totp(now => 1111111109, algorithm => 'sha512', totp => '25091201');
is_totp(now => 1111111111, algorithm => 'sha1', totp => '14050471');
is_totp(now => 1111111111, algorithm => 'sha256', totp => '67062674');
is_totp(now => 1111111111, algorithm => 'sha512', totp => '99943326');
is_totp(now => 1234567890, algorithm => 'sha1', totp => '89005924');
is_totp(now => 1234567890, algorithm => 'sha256', totp => '91819424');
is_totp(now => 1234567890, algorithm => 'sha512', totp => '93441116');
is_totp(now => 2000000000, algorithm => 'sha1', totp => '69279037');
is_totp(now => 2000000000, algorithm => 'sha256', totp => '90698825');
is_totp(now => 2000000000, algorithm => 'sha512', totp => '38618901');
is_totp(now => 20000000000, algorithm => 'sha1', totp => '65353130');
is_totp(now => 20000000000, algorithm => 'sha256', totp => '77737706');
is_totp(now => 20000000000, algorithm => 'sha512', totp => '47863826');
is_totp(now => 59, algorithm => 'SHA512', totp => '90693936');
done_testing(20);
Pass-OTP-1.801/t/simple.t0000644000225700022570000000130514500304046014325 0ustar00jbaierjbaieruse Test::More;
use utf8;
use strict;
use warnings;
require_ok 'Pass::OTP';
require_ok 'Pass::OTP::URI';
is(
Pass::OTP::otp(
secret => "00",
),
'328482',
'oathtool 00',
);
is(
Pass::OTP::otp(
secret => "00",
counter => 100,
),
'032003',
'oathtool -c 100 00',
);
is(
Pass::OTP::otp(
Pass::OTP::URI::parse('otpauth://hotp/Test?secret=abcdefgh')
),
'058591',
'otptool otpauth://hotp/Test?secret=abcdefgh',
);
is(
Pass::OTP::otp(
Pass::OTP::URI::parse('otpauth://totp/Test?secret=abcdefgh&issuer=Steam&now=1')
),
'KMP7M',
'otptool otpauth://totp/Test?secret=abcdefgh&issuer=Steam',
);
done_testing(6);
Pass-OTP-1.801/MANIFEST0000644000225700022570000000031614573571160013553 0ustar00jbaierjbaier.perltidyrc
bin/oathtool
bin/otptool
Changes
lib/Pass/OTP.pm
lib/Pass/OTP/URI.pm
Makefile.PL
MANIFEST This list of files
META.json
META.yml
README
README.pod
t/oathtool.t
t/simple.t
t/totp-test-vectors.t
Pass-OTP-1.801/Makefile.PL0000644000225700022570000000243614744441352014400 0ustar00jbaierjbaieruse strict;
use warnings;
use ExtUtils::MakeMaker;
WriteMakefile(
NAME => 'Pass::OTP',
AUTHOR => q{Jan Baier },
ABSTRACT_FROM => 'lib/Pass/OTP.pm',
VERSION_FROM => 'lib/Pass/OTP.pm',
LICENSE => 'perl_5',
EXE_FILES => ['bin/oathtool', 'bin/otptool'],
MIN_PERL_VERSION => '5.014',
TEST_REQUIRES => {
'Test::More' => '0',
},
PREREQ_PM => {
'MIME::Base32' => '0',
'Digest::HMAC' => '0',
'Digest::SHA' => '0',
'Math::BigInt' => '1.999806',
},
META_MERGE => {
'meta-spec' => { version => 2 },
resources => {
repository => {
type => 'git',
url => 'git://github.com/baierjan/Pass-OTP-perl.git',
web => 'https://github.com/baierjan/Pass-OTP-perl',
},
},
provides => {
'Pass::OTP' => {
file => 'lib/Pass/OTP.pm', version => '1.801',
},
'Pass::OTP::URI' => {
file => 'lib/Pass/OTP/URI.pm', version => '1.801',
},
}
},
dist => {
COMPRESS => 'gzip -9f', SUFFIX => 'gz',
TARFLAGS => '--format=ustar -c -v -f',
},
clean => { FILES => 'Pass-OTP-*' },
);
Pass-OTP-1.801/.perltidyrc0000644000225700022570000000051114500304046014564 0ustar00jbaierjbaier-l=120 # 120 characters per line
-fbl # don't change blank lines
-fnl # don't remove new lines
-nsfs # no spaces before semicolons
-baao # space after operators
-bbao # space before operators
-pt=2 # no spaces around ()
-bt=2 # no spaces around []
-sbt=2 # no spaces around {}
-sct # stack closing tokens )}
Pass-OTP-1.801/META.yml0000644000225700022570000000156214744441512013674 0ustar00jbaierjbaier---
abstract: 'Perl implementation of HOTP / TOTP algorithms'
author:
- 'Jan Baier '
build_requires:
ExtUtils::MakeMaker: '0'
Test::More: '0'
configure_requires:
ExtUtils::MakeMaker: '0'
dynamic_config: 1
generated_by: 'ExtUtils::MakeMaker version 7.70, CPAN::Meta::Converter version 2.150010'
license: perl
meta-spec:
url: http://module-build.sourceforge.net/META-spec-v1.4.html
version: '1.4'
name: Pass-OTP
no_index:
directory:
- t
- inc
provides:
Pass::OTP:
file: lib/Pass/OTP.pm
version: '1.801'
Pass::OTP::URI:
file: lib/Pass/OTP/URI.pm
version: '1.801'
requires:
Digest::HMAC: '0'
Digest::SHA: '0'
MIME::Base32: '0'
Math::BigInt: '1.999806'
perl: '5.014'
resources:
repository: git://github.com/baierjan/Pass-OTP-perl.git
version: '1.801'
x_serialization_backend: 'CPAN::Meta::YAML version 0.018'
Pass-OTP-1.801/META.json0000644000225700022570000000311214744441512014035 0ustar00jbaierjbaier{
"abstract" : "Perl implementation of HOTP / TOTP algorithms",
"author" : [
"Jan Baier "
],
"dynamic_config" : 1,
"generated_by" : "ExtUtils::MakeMaker version 7.70, CPAN::Meta::Converter version 2.150010",
"license" : [
"perl_5"
],
"meta-spec" : {
"url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec",
"version" : 2
},
"name" : "Pass-OTP",
"no_index" : {
"directory" : [
"t",
"inc"
]
},
"prereqs" : {
"build" : {
"requires" : {
"ExtUtils::MakeMaker" : "0"
}
},
"configure" : {
"requires" : {
"ExtUtils::MakeMaker" : "0"
}
},
"runtime" : {
"requires" : {
"Digest::HMAC" : "0",
"Digest::SHA" : "0",
"MIME::Base32" : "0",
"Math::BigInt" : "1.999806",
"perl" : "5.014"
}
},
"test" : {
"requires" : {
"Test::More" : "0"
}
}
},
"provides" : {
"Pass::OTP" : {
"file" : "lib/Pass/OTP.pm",
"version" : "1.801"
},
"Pass::OTP::URI" : {
"file" : "lib/Pass/OTP/URI.pm",
"version" : "1.801"
}
},
"release_status" : "stable",
"resources" : {
"repository" : {
"type" : "git",
"url" : "git://github.com/baierjan/Pass-OTP-perl.git",
"web" : "https://github.com/baierjan/Pass-OTP-perl"
}
},
"version" : "1.801",
"x_serialization_backend" : "JSON::PP version 4.16"
}