./PaxHeaders/Pass-OTP-1.60000644000000000000000000000013214500312764012066 xustar0030 mtime=1694602740.571394104 30 atime=1694602740.358057821 30 ctime=1694602740.571394104 Pass-OTP-1.6/0000755000225700022570000000000014500312764013510 5ustar00jbaierjbaier00000000000000Pass-OTP-1.6/PaxHeaders/bin0000644000000000000000000000013214500312764012501 xustar0030 mtime=1694602740.358057821 30 atime=1694602740.358057821 30 ctime=1694602740.358057821 Pass-OTP-1.6/bin/0000755000225700022570000000000014500312764014260 5ustar00jbaierjbaier00000000000000Pass-OTP-1.6/bin/PaxHeaders/otptool0000644000000000000000000000013214500304046014173 xustar0030 mtime=1694599206.939021322 30 atime=1694602687.933995484 30 ctime=1694602740.358057821 Pass-OTP-1.6/bin/otptool0000755000225700022570000000163514500304046015705 0ustar00jbaierjbaier00000000000000#!/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.6/bin/PaxHeaders/oathtool0000644000000000000000000000013214500304046014324 xustar0030 mtime=1694599206.939021322 30 atime=1694602687.933995484 30 ctime=1694602740.358057821 Pass-OTP-1.6/bin/oathtool0000755000225700022570000000335114500304046016033 0ustar00jbaierjbaier00000000000000#!/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)); 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.6/PaxHeaders/.perltidyrc0000644000000000000000000000013214500304046014162 xustar0030 mtime=1694599206.939021322 30 atime=1694602687.933995484 30 ctime=1694602740.358057821 Pass-OTP-1.6/.perltidyrc0000644000225700022570000000051114500304046015661 0ustar00jbaierjbaier00000000000000-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.6/PaxHeaders/Changes0000644000000000000000000000013114500307740013276 xustar0029 mtime=1694601184.83342574 30 atime=1694602687.933995484 30 ctime=1694602740.358057821 Pass-OTP-1.6/Changes0000644000225700022570000000100414500307740014774 0ustar00jbaierjbaier000000000000001.6 2023/09/13 - fix SHA384 and SHA512 blocksize (#1) 1.5 2020/07/02 - fix min supported versions 1.4 2020/07/01 - fix min supported versions 1.3 2020/06/27 - added otptool which knows only otpauth:// links 1.2 2020/06/27 - added support for custom character classes - added compatibility with Steam Guard 1.1 2020/06/27 - added support for parsing otpauth://... links - added compatibility with Google Authenticator 1.0 2020/06/27 - added support for HOTP - added support for TOTP Pass-OTP-1.6/PaxHeaders/MANIFEST0000644000000000000000000000013214500312764013137 xustar0030 mtime=1694602740.618061414 30 atime=1694602740.618061414 30 ctime=1694602740.618061414 Pass-OTP-1.6/MANIFEST0000644000225700022570000000054314500312764014643 0ustar00jbaierjbaier00000000000000.perltidyrc bin/oathtool bin/otptool Changes lib/Pass/OTP.pm lib/Pass/OTP/URI.pm Makefile.PL MANIFEST This list of files README README.pod t/oathtool.t t/simple.t t/totp-test-vectors.t META.yml Module YAML meta-data (added by MakeMaker) META.json Module JSON meta-data (added by MakeMaker) Pass-OTP-1.6/PaxHeaders/README.pod0000644000000000000000000000013214500304046013441 xustar0030 mtime=1694599206.939021322 30 atime=1694602687.933995484 30 ctime=1694602740.358057821 Pass-OTP-1.6/README.pod0000644000225700022570000000313014500304046015140 0ustar00jbaierjbaier00000000000000=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) 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. Pass-OTP-1.6/PaxHeaders/lib0000644000000000000000000000013214500312764012477 xustar0030 mtime=1694602740.358057821 30 atime=1694602740.358057821 30 ctime=1694602740.358057821 Pass-OTP-1.6/lib/0000755000225700022570000000000014500312764014256 5ustar00jbaierjbaier00000000000000Pass-OTP-1.6/lib/PaxHeaders/Pass0000644000000000000000000000013214500312764013405 xustar0030 mtime=1694602740.358057821 30 atime=1694602740.358057821 30 ctime=1694602740.358057821 Pass-OTP-1.6/lib/Pass/0000755000225700022570000000000014500312764015164 5ustar00jbaierjbaier00000000000000Pass-OTP-1.6/lib/Pass/PaxHeaders/OTP.pm0000644000000000000000000000013214500310545014455 xustar0030 mtime=1694601573.427560286 30 atime=1694602687.933995484 30 ctime=1694602740.358057821 Pass-OTP-1.6/lib/Pass/OTP.pm0000644000225700022570000000734514500310545016170 0ustar00jbaierjbaier00000000000000package 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 Convert::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.6'; =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+)/; 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.6/lib/Pass/PaxHeaders/OTP0000644000000000000000000000013214500312764014047 xustar0030 mtime=1694602740.358057821 30 atime=1694602740.358057821 30 ctime=1694602740.358057821 Pass-OTP-1.6/lib/Pass/OTP/0000755000225700022570000000000014500312764015626 5ustar00jbaierjbaier00000000000000Pass-OTP-1.6/lib/Pass/OTP/PaxHeaders/URI.pm0000644000000000000000000000013114500304046015112 xustar0029 mtime=1694599206.94235469 30 atime=1694602687.933995484 30 ctime=1694602740.358057821 Pass-OTP-1.6/lib/Pass/OTP/URI.pm0000644000225700022570000000237314500304046016622 0ustar00jbaierjbaier00000000000000package 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) 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.6/PaxHeaders/Makefile.PL0000644000000000000000000000013214500304046013752 xustar0030 mtime=1694599206.939021322 30 atime=1694602687.933995484 30 ctime=1694602740.358057821 Pass-OTP-1.6/Makefile.PL0000644000225700022570000000125514500304046015457 0ustar00jbaierjbaier00000000000000use 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 => { 'Convert::Base32' => '0', 'Digest::HMAC' => '0', 'Digest::SHA' => '0', 'Math::BigInt' => '1.999806', }, dist => { COMPRESS => 'gzip -9f', SUFFIX => 'gz', }, clean => { FILES => 'Pass-OTP-*' }, ); Pass-OTP-1.6/PaxHeaders/t0000644000000000000000000000013214500312764012174 xustar0030 mtime=1694602740.358057821 30 atime=1694602740.358057821 30 ctime=1694602740.358057821 Pass-OTP-1.6/t/0000755000225700022570000000000014500312764013753 5ustar00jbaierjbaier00000000000000Pass-OTP-1.6/t/PaxHeaders/simple.t0000644000000000000000000000013114500304046013720 xustar0029 mtime=1694599206.94235469 30 atime=1694602687.933995484 30 ctime=1694602740.358057821 Pass-OTP-1.6/t/simple.t0000644000225700022570000000130514500304046015422 0ustar00jbaierjbaier00000000000000use 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.6/t/PaxHeaders/totp-test-vectors.t0000644000000000000000000000013114500307504016057 xustar0029 mtime=1694601028.35176072 30 atime=1694602687.933995484 30 ctime=1694602740.358057821 Pass-OTP-1.6/t/totp-test-vectors.t0000644000225700022570000000412214500307504017561 0ustar00jbaierjbaier00000000000000use 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{$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'); done_testing(19); Pass-OTP-1.6/t/PaxHeaders/oathtool.t0000644000000000000000000000013114500307504014262 xustar0029 mtime=1694601028.35176072 30 atime=1694602687.933995484 30 ctime=1694602740.358057821 Pass-OTP-1.6/t/oathtool.t0000644000225700022570000000620514500307504015770 0ustar00jbaierjbaier00000000000000use 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.6/PaxHeaders/README0000644000000000000000000000013214500304046012660 xustar0030 mtime=1694599206.939021322 30 atime=1694602687.933995484 30 ctime=1694602740.358057821 Pass-OTP-1.6/README0000644000225700022570000000323614500304046014366 0ustar00jbaierjbaier00000000000000NAME 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) 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 for more information. Pass-OTP-1.6/PaxHeaders/META.yml0000644000000000000000000000013114500312764013256 xustar0030 mtime=1694602740.474726102 29 atime=1694602740.36805796 30 ctime=1694602740.474726102 Pass-OTP-1.6/META.yml0000644000225700022570000000124314500312764014761 0ustar00jbaierjbaier00000000000000--- 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 requires: Convert::Base32: '0' Digest::HMAC: '0' Digest::SHA: '0' Math::BigInt: '1.999806' perl: '5.014' version: '1.6' x_serialization_backend: 'CPAN::Meta::YAML version 0.018' Pass-OTP-1.6/PaxHeaders/META.json0000644000000000000000000000013214500312764013427 xustar0030 mtime=1694602740.568060723 30 atime=1694602740.481392861 30 ctime=1694602740.571394104 Pass-OTP-1.6/META.json0000644000225700022570000000223314500312764015131 0ustar00jbaierjbaier00000000000000{ "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" : { "Convert::Base32" : "0", "Digest::HMAC" : "0", "Digest::SHA" : "0", "Math::BigInt" : "1.999806", "perl" : "5.014" } }, "test" : { "requires" : { "Test::More" : "0" } } }, "release_status" : "stable", "version" : "1.6", "x_serialization_backend" : "JSON::PP version 4.16" }