File-KDBX
view release on metacpan or search on metacpan
lib/File/KDBX/Key/YubiKey.pm view on Meta::CPAN
package File::KDBX::Key::YubiKey;
# ABSTRACT: A Yubico challenge-response key
use warnings;
use strict;
use File::KDBX::Constants qw(:yubikey);
use File::KDBX::Error;
use File::KDBX::Util qw(:class :io pad_pkcs7);
use IPC::Cmd 0.84 qw(run_forked);
use Ref::Util qw(is_arrayref);
use Symbol qw(gensym);
use namespace::clean;
extends 'File::KDBX::Key::ChallengeResponse';
our $VERSION = '0.906'; # VERSION
# It can take some time for the USB device to be ready again, so we can retry a few times.
our $RETRY_COUNT = 5;
our $RETRY_INTERVAL = 0.1;
my @CONFIG_VALID = (0, CONFIG1_VALID, CONFIG2_VALID);
my @CONFIG_TOUCH = (0, CONFIG1_TOUCH, CONFIG2_TOUCH);
sub challenge {
my $self = shift;
my $challenge = shift;
my %args = @_;
my $device = $args{device} // $self->device;
my $slot = $args{slot} // $self->slot;
my $timeout = $args{timeout} // $self->timeout;
local $self->{device} = $device;
local $self->{slot} = $slot;
local $self->{timeout} = $timeout;
my $hooks = $challenge ne 'test';
if ($hooks and my $hook = $self->{pre_challenge}) {
$hook->($self, $challenge);
}
my @cmd = ($self->_program('ykchalresp'), "-n$device", "-$slot", qw{-H -i-}, $timeout == 0 ? '-N' : ());
my $r;
my $try = 0;
TRY:
{
$r = $self->_run_ykpers(\@cmd, {
(0 < $timeout ? (timeout => $timeout) : ()),
child_stdin => pad_pkcs7($challenge, 64),
terminate_on_parent_sudden_death => 1,
});
if (my $t = $r->{timeout}) {
throw 'Timed out while waiting for challenge response',
command => \@cmd,
challenge => $challenge,
timeout => $t,
result => $r;
}
my $exit_code = $r->{exit_code};
if ($exit_code != 0) {
my $err = $r->{stderr};
chomp $err;
my $yk_errno = _yk_errno($err);
if ($yk_errno == YK_EUSBERR && $err =~ /resource busy/i && ++$try <= $RETRY_COUNT) {
sleep $RETRY_INTERVAL;
goto TRY;
}
( run in 0.638 second using v1.01-cache-2.11-cpan-39bf76dae61 )