]> Dogcows Code - chaz/p5-File-KDBX/commitdiff
Fix YubiKey unit test portability issues
authorCharles McGarvey <ccm@cpan.org>
Wed, 13 Apr 2022 22:43:52 +0000 (16:43 -0600)
committerCharles McGarvey <ccm@cpan.org>
Sun, 1 May 2022 00:29:00 +0000 (18:29 -0600)
lib/File/KDBX/Key/YubiKey.pm
t/files/bin/ykchalresp
t/files/bin/ykinfo
t/yubikey.t

index 7a7e23893313d2b4ff184e3df98509f57e8925e7..e86b6e786a5246b3b2d21189f4e7a112312bb16a 100644 (file)
@@ -8,6 +8,7 @@ use File::KDBX::Constants qw(:yubikey);
 use File::KDBX::Error;
 use File::KDBX::Util qw(pad_pkcs7);
 use IPC::Open3;
+use Ref::Util qw(is_arrayref);
 use Scope::Guard;
 use Symbol qw(gensym);
 use namespace::clean;
@@ -38,7 +39,7 @@ sub challenge {
         $hook->($self, $challenge);
     }
 
-    my @cmd = ($self->ykchalresp, "-n$device", "-$slot", qw{-H -i-}, $timeout == 0 ? '-N' : ());
+    my @cmd = ($self->_program('ykchalresp'), "-n$device", "-$slot", qw{-H -i-}, $timeout == 0 ? '-N' : ());
     my ($pid, $child_in, $child_out, $child_err) = _run_ykpers(@cmd);
     push @cleanup, Scope::Guard->new(sub { kill $pid if defined $pid });
 
@@ -310,7 +311,7 @@ sub _get_yubikey_info {
     my $self = shift;
     my $device = shift;
 
-    my @cmd = ($self->ykinfo, "-n$device", qw{-a});
+    my @cmd = ($self->_program('ykinfo'), "-n$device", qw{-a});
 
     my $try = 0;
     TRY:
@@ -361,12 +362,22 @@ sub _set_yubikey_info {
     @$self{keys %info} = values %info;
 }
 
+sub _program {
+    my $self = shift;
+    my $name = shift;
+    my @cmd = $self->$name // $name;
+    my $name_uc = uc($name);
+    my $flags = $ENV{"${name_uc}_FLAGS"};
+    push @cmd, split(/\h+/, $flags) if $flags;
+    return @cmd;
+}
+
 sub _run_ykpers {
     my ($child_err, $child_in, $child_out) = (gensym);
     my $pid = eval { open3($child_in, $child_out, $child_err, @_) };
     if (my $err = $@) {
         throw "Failed to run $_[0] - Make sure you have the YubiKey Personalization Tool (CLI) package installed.\n",
-            error   => $err;
+            error => $err;
     }
     return ($pid, $child_in, $child_out, $child_err);
 }
@@ -436,9 +447,11 @@ See L<https://keepassxc.org/docs/#faq-yubikey-howto> for more information.
 
 =for :list
 * C<YKCHALRESP> - Path to the L<ykchalresp(1)> program
+* C<YKCHALRESP_FLAGS> - Extra arguments to the B<ykchalresp> program
 * C<YKINFO> - Path to the L<ykinfo(1)> program
+* C<YKINFO_FLAGS> - Extra arguments to the B<ykinfo> program
 
-C<YubiKey> searches for these programs in the same way perl typically searches for executables (using the
+B<YubiKey> searches for these programs in the same way perl typically searches for executables (using the
 C<PATH> environment variable on many platforms). If the programs aren't installed normally, or if you want to
 override the default programs, these environment variables can be used.
 
index 7cac1f5fd409a930eadaff2ddc2332e8f2da6f39..c94a3d56add7770cfa655fe0ec70092e71089c6e 100755 (executable)
@@ -1,76 +1,55 @@
-#!/bin/sh
+#!/usr/bin/env perl
 
 # This is a fake ykchalresp program that provides canned responses, for testing.
 
-device=
-slot=
-blocking=1
-hmac=
-in=
+use warnings;
+use strict;
 
-while getopts 12HNn:i: arg
-do
-    case "$arg" in
-        n)
-            device="$OPTARG"
-            ;;
-        1)
-            slot=1
-            ;;
-        2)
-            slot=2
-            ;;
-        H)
-            hmac=1
-            ;;
-        N)
-            blocking=0
-            ;;
-        i)
-            in="$OPTARG"
-            ;;
-    esac
-done
+use Getopt::Std;
 
-if [ -z "$hmac" ]
-then
-    echo 'HMAC-SHA1 not requested' >&2
-    exit 3
-fi
+my %opts;
+getopts('12HNn:i:', \%opts);
 
-if [ "$in" != '-' ]
-then
-    echo "Unexpected input file: $in" >&2
-    exit 3
-fi
+my ($device, $hmac, $nonblocking, $in) = @opts{qw(n H N i)};
 
-read challenge
+if (!$hmac) {
+    print STDERR "HMAC-SHA1 not requested\n";
+    exit 3;
+}
+elsif (!defined($in) || $in ne '-') {
+    $in //= '(none)';
+    print STDERR "Unexpected input file: $in\n";
+    exit 3;
+}
+
+my $challenge = <STDIN>;
+
+my $mock = $ENV{YKCHALRESP_MOCK} || '';
+if ($mock eq 'block') {
+    if ($nonblocking) {
+        print STDERR "Yubikey core error: operation would block\n";
+        exit 1;
+    }
+    sleep 2;
+    succeed();
+}
+elsif ($mock eq 'error') {
+    my $resp = $ENV{YKCHALRESP_ERROR} || 'not yet implemented';
+    print STDERR "Yubikey core error: $resp\n";
+    exit 1;
+}
+elsif ($mock eq 'usberror') {
+    print STDERR "USB error: something happened\n";
+    exit 1;
+}
+else {  # OK
+    succeed();
+}
 
-succeed() {
-    echo "${YKCHALRESP_RESPONSE:-f000000000000000000000000000000000000000}"
-    exit 0
+sub succeed {
+    my $resp = $ENV{YKCHALRESP_RESPONSE} || 'f000000000000000000000000000000000000000';
+    print "$resp\n";
+    exit 0;
 }
 
-case "$YKCHALRESP_MOCK" in
-    block)
-        if [ "$blocking" -eq 0 ]
-        then
-            echo "Yubikey core error: operation would block" >&2
-            exit 1
-        fi
-        sleep 2
-        succeed
-        ;;
-    error)
-        echo "Yubikey core error: ${YKCHALRESP_ERROR:-not yet implemented}" >&2
-        exit 1
-        ;;
-    usberror)
-        echo "USB error: something happened" >&2
-        exit 1
-        ;;
-    *)  # OK
-        succeed
-        ;;
-esac
-exit 2
+exit 2;
index 8a93cc3b208d2c7cd555b67a0afbdb5141af7fe6..a8cc0218e0d273ff314e2dc7f47475e0e166e394 100755 (executable)
@@ -1,43 +1,37 @@
-#!/bin/sh
+#!/usr/bin/env perl
 
 # This is a fake ykinfo program that provides canned responses, for testing.
 
-device=
-all=
+use warnings;
+use strict;
 
-while getopts an: arg
-do
-    case "$arg" in
-        n)
-            device="$OPTARG"
-            ;;
-        a)
-            all=1
-            ;;
-    esac
-done
+use Getopt::Std;
 
-case "$device" in
-    0)
-        printf 'serial: 123
+our ($opt_a, $opt_n);
+getopts('an:');
+
+my $device = $opt_n // -1;
+
+if ($device == 0) {
+    print q{serial: 123
 version: 2.0.0
 touch_level: 0
 vendor_id: 1050
 product_id: 113
-'
-        exit 0
-        ;;
-    1)
-        printf 'serial: 456
+};
+    exit 0;
+}
+elsif ($device == 1) {
+    print q{serial: 456
 version: 3.0.1
 touch_level: 10
 vendor_id: 1050
 product_id: 401
-'
-        exit 0
-        ;;
-    *)
-        echo "Yubikey core error: no yubikey present" >&2
-        exit 1
-esac
+};
+    exit 0;
+}
+else {
+    print STDERR "Yubikey core error: no yubikey present\n";
+    exit 1;
+}
 
index 1ec1ed41223e5d4020ab2fb5208a02304e56dddc..61ca28ca1d25a9e662533db2eeaa8bffc46fa4b8 100644 (file)
@@ -6,12 +6,12 @@ use strict;
 use lib 't/lib';
 use TestCommon;
 
+use Config;
+use File::KDBX::Key::YubiKey;
 use Test::More;
 
-BEGIN { use_ok 'File::KDBX::Key::YubiKey' }
-
-local $ENV{YKCHALRESP} = testfile(qw{bin ykchalresp});
-local $ENV{YKINFO}     = testfile(qw{bin ykinfo});
+@ENV{qw(YKCHALRESP YKCHALRESP_FLAGS)}   = ($Config{perlpath}, testfile(qw{bin ykchalresp}));
+@ENV{qw(YKINFO YKINFO_FLAGS)}           = ($Config{perlpath}, testfile(qw{bin ykinfo}));
 
 {
     my ($pre, $post);
@@ -20,8 +20,7 @@ local $ENV{YKINFO}     = testfile(qw{bin ykinfo});
         post_challenge  => sub { ++$post },
     );
     my $resp;
-    is exception { $resp = $key->challenge('foo') }, undef,
-        'Do not throw during non-blocking response';
+    is exception { $resp = $key->challenge('foo') }, undef, 'Do not throw during non-blocking response';
     is $resp, "\xf0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 'Get a non-blocking challenge response';
     is length($resp), 20, 'Response is the proper length';
     is $pre,  1, 'The pre-challenge callback is called';
@@ -77,6 +76,7 @@ local $ENV{YKINFO}     = testfile(qw{bin ykinfo});
 
 {
     local $ENV{YKCHALRESP} = testfile(qw{bin nonexistent});
+    local $ENV{YKCHALRESP_FLAGS} = undef;
     my $key = File::KDBX::Key::YubiKey->new;
     like exception { $key->challenge('foo') }, qr/failed to run|failed to receive challenge response/i,
         'Throw if the program failed to run';
This page took 0.02869 seconds and 4 git commands to generate.