[Bps-public-commit] GnuPG-Interface branch, work-with-both-gnupgs, created. 0.52-49-g8e1eda2

Aaron Trevena ast at bestpractical.com
Mon May 11 13:47:49 EDT 2020


The branch, work-with-both-gnupgs has been created
        at  8e1eda2d14bc6d2050cbb9d39b94601de07c638b (commit)

- Log -----------------------------------------------------------------
commit 7413bf4c210ae29595fc043a440de4ad683f62bd
Author: Daniel Kahn Gillmor <dkg at fifthhorseman.net>
Date:   Tue Sep 13 07:26:00 2016 +0200

    fix spelling error ("settting" should be "setting")

diff --git a/README b/README
index fcb982a..f457577 100644
--- a/README
+++ b/README
@@ -6,7 +6,7 @@ SYNOPSIS
       use IO::Handle;
       use GnuPG::Interface;
   
-      # settting up the situation
+      # setting up the situation
       my $gnupg = GnuPG::Interface->new();
       $gnupg->options->hash_init( armor   => 1,
                                   homedir => '/home/foobar' );
diff --git a/lib/GnuPG/Interface.pm b/lib/GnuPG/Interface.pm
index f952f3e..83a4b1a 100644
--- a/lib/GnuPG/Interface.pm
+++ b/lib/GnuPG/Interface.pm
@@ -834,7 +834,7 @@ GnuPG::Interface - Perl interface to GnuPG
   use IO::Handle;
   use GnuPG::Interface;
   
-  # settting up the situation
+  # setting up the situation
   my $gnupg = GnuPG::Interface->new();
   $gnupg->options->hash_init( armor   => 1,
 			      homedir => '/home/foobar' );

commit 727791ba118861dcd7acb80997f958990e89a6c2
Author: Daniel Kahn Gillmor <dkg at fifthhorseman.net>
Date:   Tue Sep 13 10:38:12 2016 -0400

    Generalize the test suite
    
    The test suite currently assumes it knows something about the internal
    state of GnuPG's homedir.
    
    It's safer and less brittle to rely explicitly on the public interface
    that GnuPG has committed to, such as --import-keys and --list-keys,
    rather than assuming that certain files are in certain places in the
    GnuPG homedir.
    
    It's also better to create a fresh homedir and allow GnuPG to populate
    it during the test suite, cleaning it up at the end, rather than hope
    that GnuPG will leave a pre-existing homedir untouched.
    
    With this change, many more of the tests pass when /usr/bin/gpg is
    provided by GnuPG 2.1.

diff --git a/.gitignore b/.gitignore
index 3100061..44e438a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,8 +6,6 @@
 /pm_to_blib
 *.tar.gz
 /test/*/*.out
-/test/random_seed
 /test/temp
-/test/trustdb.gpg
 /MYMETA.*
 /MANIFEST.SKIP.bak
diff --git a/t/000_setup.t b/t/000_setup.t
new file mode 100644
index 0000000..7f7f7b0
--- /dev/null
+++ b/t/000_setup.t
@@ -0,0 +1,28 @@
+#!/usr/bin/perl -w
+
+use strict;
+use English qw( -no_match_vars );
+
+use lib './t';
+use MyTest;
+use MyTestSpecific;
+use Cwd;
+use File::Path qw (make_path);
+use File::Copy;
+
+TEST
+{
+    make_path('test/gnupghome', { mode => 0700 });
+    my $agentconf = IO::File->new( "> test/gnupghome/gpg-agent.conf" );
+    $agentconf->write("pinentry-program " . getcwd() . "/test/fake-pinentry.pl\n");
+    $agentconf->close();
+    copy('test/gpg.conf', 'test/gnupghome/gpg.conf');
+    reset_handles();
+
+    my $pid = $gnupg->import_keys(command_args => [ 'test/pubring.gpg', 'test/secring.gpg' ],
+                                  options => [ 'batch'],
+                                  handles => $handles);
+    waitpid $pid, 0;
+
+    return $CHILD_ERROR == 0;
+};
diff --git a/t/MyTestSpecific.pm b/t/MyTestSpecific.pm
index 053b749..1af98ae 100644
--- a/t/MyTestSpecific.pm
+++ b/t/MyTestSpecific.pm
@@ -40,7 +40,7 @@ use vars qw( @ISA           @EXPORT
 
 $gnupg = GnuPG::Interface->new( passphrase => 'test' );
 
-$gnupg->options->hash_init( homedir              => 'test',
+$gnupg->options->hash_init( homedir              => 'test/gnupghome',
                             armor                => 1,
                             meta_interactive     => 0,
                             meta_signing_key_id  => '0xF950DA9C',
diff --git a/t/zzz_cleanup.t b/t/zzz_cleanup.t
new file mode 100644
index 0000000..5c03a72
--- /dev/null
+++ b/t/zzz_cleanup.t
@@ -0,0 +1,17 @@
+#!/usr/bin/perl -w
+
+use strict;
+use English qw( -no_match_vars );
+
+use lib './t';
+use MyTest;
+use MyTestSpecific;
+use File::Path qw (remove_tree);
+
+# this is actually no test, just cleanup.
+TEST
+{
+    my $err = [];
+    remove_tree('test/gnupghome', {error => \$err});
+    return ! @$err;
+};
diff --git a/test/fake-pinentry.pl b/test/fake-pinentry.pl
new file mode 100755
index 0000000..12d3611
--- /dev/null
+++ b/test/fake-pinentry.pl
@@ -0,0 +1,28 @@
+#!/usr/bin/perl -w
+# Use this for your test suites when a perl interpreter is available.
+#
+# The encrypted keys in your test suite that you expect to work must
+# be locked with a passphrase of "test"
+#
+# Author: Daniel Kahn Gillmor <dkg at fifthhorseman.net>
+#
+# License: This trivial work is hereby explicitly placed into the
+# public domain.  Anyone may reuse it, modify it, redistribute it for
+# any purpose.
+
+use strict;
+use warnings;
+
+# turn off buffering
+$| = 1;
+
+print "OK This is only for test suites, and should never be used in production\n";
+while (<STDIN>) {
+  chomp;
+  next if (/^$/);
+  next if (/^#/);
+  print ("D test\n") if (/^getpin/i);
+  print "OK\n";
+  exit if (/^bye/i);
+}
+1;
diff --git a/test/options b/test/gpg.conf
similarity index 100%
rename from test/options
rename to test/gpg.conf
diff --git a/test/secret-keys/1.0.test b/test/secret-keys/1.0.test
index 5999484..129d472 100644
--- a/test/secret-keys/1.0.test
+++ b/test/secret-keys/1.0.test
@@ -1,5 +1,5 @@
-test/secring.gpg
-----------------
+test/gnupghome/secring.gpg
+--------------------------
 sec   1024D/F950DA9C 2000-02-06
 uid                  GnuPG test key (for testing purposes only)
 uid                  Foo Bar (1)

commit 2b6d6d92611f4adc28fafb1ea3a798cbdcedbf86
Author: Daniel Kahn Gillmor <dkg at fifthhorseman.net>
Date:   Tue Sep 13 11:35:31 2016 -0400

    subkey validity of an key when we have established no trust anchors
    
    This apparently isn't tested by deep comparisons, though, so it was
    never caught.

diff --git a/t/get_public_keys.t b/t/get_public_keys.t
index 53db021..73e320b 100644
--- a/t/get_public_keys.t
+++ b/t/get_public_keys.t
@@ -175,7 +175,7 @@ TEST
     ];
 
     my $subkey = GnuPG::SubKey->new
-      ( validity                 => 'u',
+      ( validity                 => '-',
         length                   => 768,
         algo_num                 => 16,
         hex_id                   => 'ADB99D9C2E854A6B',
diff --git a/t/get_secret_keys.t b/t/get_secret_keys.t
index 3a1d99f..7bba083 100644
--- a/t/get_secret_keys.t
+++ b/t/get_secret_keys.t
@@ -48,7 +48,7 @@ TEST
 
 
     my $subkey = GnuPG::SubKey->new
-      ( validity                 => 'u',
+      ( validity                 => '-',
         length                   => 768,
         algo_num                 => 16,
         hex_id                   => 'ADB99D9C2E854A6B',

commit 9f579ba1105849d2d41aa09c41123312afb6269b
Author: Daniel Kahn Gillmor <dkg at fifthhorseman.net>
Date:   Tue Sep 13 11:39:04 2016 -0400

    ensure that test covers all signatures
    
    The earlier test wasn't reporting on one of the known self-sigs for
    the test key for some reason.
    
    This change ensures that all known signatures are present.

diff --git a/t/get_public_keys.t b/t/get_public_keys.t
index 73e320b..9e96f7d 100644
--- a/t/get_public_keys.t
+++ b/t/get_public_keys.t
@@ -83,7 +83,17 @@ TEST
                             date_string => '2000-02-06',
                             hex_id => '53AE596EF950DA9C',
                             sig_class => 0x13,
-                            validity => '!'));
+                            validity => '!'),
+      GnuPG::Signature->new(
+                            date => 1177086329,
+                            algo_num => 17,
+                            is_exportable => 1,
+                            user_id_string => 'GnuPG test key (for testing purposes only)',
+                            date_string => '2007-04-20',
+                            hex_id => '53AE596EF950DA9C',
+                            sig_class => 0x13,
+                            validity => '!'),
+                          );
 
     my $uid1 = GnuPG::UserId->new( as_string =>  'Foo Bar (1)',
                                    validity => '-');

commit 9ffa3b0a900c494042d8b757f3f22a6f45ca034e
Author: Daniel Kahn Gillmor <dkg at fifthhorseman.net>
Date:   Tue Sep 13 14:31:38 2016 -0400

    add $gpg_is_modern to test suite
    
    MyTestSpecific.pm now produces a new variable indicating whether it
    the version of GnuPG we run against is from the "Modern" line of GnuPG
    development (2.1 or later).  This will be useful when comparing output
    that we can't expect from earlier versions.

diff --git a/t/MyTestSpecific.pm b/t/MyTestSpecific.pm
index 1af98ae..a309698 100644
--- a/t/MyTestSpecific.pm
+++ b/t/MyTestSpecific.pm
@@ -29,17 +29,20 @@ use GnuPG::Handles;
 use vars qw( @ISA           @EXPORT
              $stdin         $stdout           $stderr
              $gpg_program   $handles          $gnupg
-             %texts
+             %texts         $gpg_is_modern
            );
 
 @ISA    = qw( Exporter );
 @EXPORT = qw( stdin                  stdout          stderr
               gnupg_program handles  reset_handles
-              texts                  file_match
+              texts                  file_match      gpg_is_modern
             );
 
 $gnupg = GnuPG::Interface->new( passphrase => 'test' );
 
+my @version = split('\.', $gnupg->version());
+$gpg_is_modern = ($version[0] > 2 || ($version[0] == 2 && $version[1] >= 1));
+
 $gnupg->options->hash_init( homedir              => 'test/gnupghome',
                             armor                => 1,
                             meta_interactive     => 0,

commit eed2615f6fd0c34ff070ee9f25137e934314dedd
Author: Daniel Kahn Gillmor <dkg at fifthhorseman.net>
Date:   Tue Sep 13 14:12:40 2016 -0400

    Modern GnuPG (2.1) reports more detail about secret keys
    
    the GnuPG "modern" suite (version 2.1 or later) reports more detail
    about secret keys than previous versions did.  In particular, it
    reports stored ownertrust, public key data, and designated revokers
    for secret keys.  Older versions only reported those attributes for
    public keys.
    
    This patch adjusts the test suite to ensure that our handmade key
    matches the produced key when /usr/bin/gpg is supplied by the modern
    suite.

diff --git a/t/get_secret_keys.t b/t/get_secret_keys.t
index 7bba083..c798cce 100644
--- a/t/get_secret_keys.t
+++ b/t/get_secret_keys.t
@@ -23,16 +23,34 @@ TEST
     return 0 unless @returned_keys == 1;
 
     $given_key = shift @returned_keys;
-
-    $handmade_key = GnuPG::PrimaryKey->new
-      ( length                 => 1024,
+    my $pubkey_data = [
+     Math::BigInt->from_hex('0x'.
+      '88FCAAA5BCDCD52084D46143F44ED1715A339794641158DE03AA2092AFD3174E3DCA2CB7DF2DDC6FEDF7C3620F5A8BDAD06713E6153F8748DD76CB97305F30CBA8F8801DB47FAC11EED725F55672CB9BDAD629178A677CBB089B3E8AE0D9A9AD7741697A35F2868C62D25670994A92D810480173DC24263EEA0F103A43C0B64B'),
+     Math::BigInt->from_hex('0x'.
+      '8F2A3842C70FF17660CBB78C78FC93F534AB9A17'),
+     Math::BigInt->from_hex('0x'.
+      '83E348C2AA65F56DE84E8FDCE6DA7B0991B1C75EC8CA446FA85869A43350907BFF36BE512385E8E7E095578BB2138C04E318495873218286DE2B8C86F36EA670135434967AC798EBA28581F709F0C6B696EB512D3E561E381A06E4B5239BCC655015F9A926C74E4B859B26EAD604F208A556511A76A40EDCD9C38E6BD82CCCB4'),
+     Math::BigInt->from_hex('0x'.
+      '80DE04C85E30C9D62C13F90CFF927A84A5A59D0900B3533D4D6193FEF8C5DAEF9FF8A7D5F76B244FBC17644F50D524E0B19CD3A4B5FC2D78DAECA3FE58FA1C1A64E6C7B96C4EE618173543163A72EF954DFD593E84342699096E9CA76578AC1DE3D893BCCD0BF470CEF625FAF816A0F503EF75C18C6173E35C8675AF919E5704')
+    ];
+
+
+    my $args = {
+        length                 => 1024,
         algo_num               => 17,
         hex_id                 => '53AE596EF950DA9C',
         creation_date          => 949813093,
         creation_date_string   => '2000-02-06',
-        owner_trust            => '', # secret keys do not report ownertrust?
+        owner_trust            => '-',
         usage_flags            => 'scaESCA',
-      );
+        pubkey_data            => $pubkey_data,
+    };
+    if (!$gpg_is_modern) {
+      # older versions don't report ownertrust or pubkey_data for secret keys:
+      delete $args->{pubkey_data};
+      $args->{owner_trust} = '';
+    }
+    $handmade_key = GnuPG::PrimaryKey->new($args);
 
     $handmade_key->fingerprint
       ( GnuPG::Fingerprint->new( as_hex_string =>
@@ -42,20 +60,42 @@ TEST
 
     $handmade_key->push_user_ids(
       GnuPG::UserId->new( as_string => 'GnuPG test key (for testing purposes only)',
-                          validity => ''), # secret keys do not report uid validity?
+                          validity => $args->{owner_trust}),
       GnuPG::UserId->new( as_string => 'Foo Bar (1)',
-                          validity => '')); # secret keys do not report uid validity?
-
-
-    my $subkey = GnuPG::SubKey->new
-      ( validity                 => '-',
+                          validity => $args->{owner_trust}));
+
+    my $revoker = GnuPG::Revoker->new
+      ( algo_num       => 17,
+        class          => 0x80,
+        fingerprint    => GnuPG::Fingerprint->new( as_hex_string =>
+                                                   '4F863BBBA8166F0A340F600356FFD10A260C4FA3'),
+        );
+
+    my $subkey_pub_data = [
+     Math::BigInt->from_hex('0x'.
+      '8831982DADC4C5D05CBB01D9EAF612131DDC9C24CEA7246557679423FB0BA42F74D10D8E7F5564F6A4FB8837F8DC4A46571C19B122E6DF4B443D15197A6A22688863D0685FADB6E402316DAA9B560D1F915475364580A67E6DF0A727778A5CF3'),
+     Math::BigInt->from_hex('0x'.
+      '6'),
+     Math::BigInt->from_hex('0x'.
+      '2F3850FF130C6AC9AA0962720E86539626FAA9B67B33A74DFC0DE843FF3E90E43E2F379EE0182D914FA539CCCF5C83A20DB3A7C45E365B8A2A092E799A3DFF4AD8274EB977BAAF5B1AFB2ACB8D6F92454F01682F555565E73E56793C46EF7C3E')
+    ];
+
+    my $sub_args = {
+        validity                 => '-',
         length                   => 768,
         algo_num                 => 16,
         hex_id                   => 'ADB99D9C2E854A6B',
         creation_date            => 949813119,
         creation_date_string     => '2000-02-06',
         usage_flags              => 'e',
-      );
+        pubkey_data              => $subkey_pub_data,
+      };
+
+    if (!$gpg_is_modern) {
+      # older versions do not report pubkey data for secret keys
+      delete $sub_args->{pubkey_data};
+    }
+    my $subkey = GnuPG::SubKey->new($sub_args);
 
     $subkey->fingerprint
       ( GnuPG::Fingerprint->new( as_hex_string =>
@@ -64,6 +104,8 @@ TEST
       );
 
     $handmade_key->push_subkeys( $subkey );
+    # older versions do not report designated revokers for secret keys
+    $handmade_key->push_revokers( $revoker ) if ($gpg_is_modern);
 
     $handmade_key->compare( $given_key );
 };

commit cb03617f079bd9912fc8249f128f9a9af75e8070
Author: Daniel Kahn Gillmor <dkg at fifthhorseman.net>
Date:   Tue Sep 13 14:46:13 2016 -0400

    test suite: match plaintext output across versions of GnuPG
    
    The human-readable version of --list-keys is *not* expected to be
    static over time or as the user's environment changes (e.g. LANG or
    LC_MESSAGES), so expecting it to be machine-parseable is probably a
    mistake.
    
    That said, some users might want to pull textual information about
    specific keys to display directly to the user, so it's not a terrible
    idea to have it in the test suite.
    
    Modern GnuPG (2.1 or later) changes the default structure of the
    human-readable output in a few significant ways:
    
     * it writes the path to the keyring as an absolute path, even if
       $GNUPGHOME is set to a non-absolute path.
    
     * it shows the calculated user id validity by default (see
       show-uid-validity in gpg's --list-options).  (note that this is a
       translated string, so that "unknown" (in the default C locale)
       becomes "inconnue" when LANG or LC_MESSAGES is set to fr_CH.UTF-8,
       for example.
    
     * it writes the key algorithm names differently (e.g. rsa2048 instead
       of 2048R)
    
     * it does not display the key ID at all by default
    
     * it displays the full fingerprint in compact form by default
    
    This changeset fixes the test suite so that it can do a rough
    verification of the human-readable text output by list_secret_keys in
    the C locale in modern versions of GnuPG, while leaving it working for
    older GnuPG suites.

diff --git a/t/list_secret_keys.t b/t/list_secret_keys.t
index 1fe9b7e..51e3651 100644
--- a/t/list_secret_keys.t
+++ b/t/list_secret_keys.t
@@ -16,13 +16,22 @@ TEST
 {
     reset_handles();
 
+    $ENV{LC_MESSAGES} = 'C';
     my $pid = $gnupg->list_secret_keys( handles => $handles );
     close $stdin;
 
     $outfile = 'test/secret-keys/1.out';
     my $out = IO::File->new( "> $outfile" )
       or die "cannot open $outfile for writing: $ERRNO";
-    $out->print( <$stdout> );
+    while (<$stdout>) {
+      if ($gpg_is_modern && /^\/.*\/test\/gnupghome\/pubring.kbx$/) {
+        $out->print("test/gnupghome/pubring.kbx\n");
+      } elsif ($gpg_is_modern && /^--*$/) {
+        $out->print("--------------------------\n");
+      } else {
+        $out->print( $_ );
+      }
+    }
     close $stdout;
     $out->close();
     waitpid $pid, 0;
@@ -33,7 +42,9 @@ TEST
 
 TEST
 {
-    my @files_to_test = ( 'test/secret-keys/1.0.test' );
+    my $suffix = '0';
+    $suffix = 'modern' if ($gpg_is_modern);
+    my @files_to_test = ( 'test/secret-keys/1.'.$suffix.'.test' );
 
     return file_match( $outfile, @files_to_test );
 };
diff --git a/test/secret-keys/1.modern.test b/test/secret-keys/1.modern.test
new file mode 100644
index 0000000..3e46407
--- /dev/null
+++ b/test/secret-keys/1.modern.test
@@ -0,0 +1,8 @@
+test/gnupghome/pubring.kbx
+--------------------------
+sec   dsa1024 2000-02-06 [SCA]
+      93AFC4B1B0288A104996B44253AE596EF950DA9C
+uid           [ unknown] GnuPG test key (for testing purposes only)
+uid           [ unknown] Foo Bar (1)
+ssb   elg768 2000-02-06 [E]
+

commit 1ad31ca89e92c94c3f47a8d436d75859b09a9371
Author: Daniel Kahn Gillmor <dkg at fifthhorseman.net>
Date:   Tue Sep 13 15:22:27 2016 -0400

    fix test_default_key_passphrase when passphrase comes from agent
    
    In the modern GnuPG suite, where the passphrase is always managed by
    the agent, gpg itself doesn't emit the GOOD_PASSPHRASE status.
    Instead, if signing is successful it emits plain old SIG_CREATED.
    
    There are probably even better ways to test whether a given key is
    unlocked in this case, but this is a straightforward baseline fix that
    should get this part of the test suite to pass with all available
    versions of GnuPG.

diff --git a/lib/GnuPG/Interface.pm b/lib/GnuPG/Interface.pm
index 83a4b1a..1f1e6d5 100644
--- a/lib/GnuPG/Interface.pm
+++ b/lib/GnuPG/Interface.pm
@@ -808,7 +808,7 @@ sub test_default_key_passphrase() {
 
     # all we realy want to check is the status fh
     while (<$status>) {
-        if (/^\[GNUPG:\]\s*GOOD_PASSPHRASE/) {
+        if (/^\[GNUPG:\]\s*(GOOD_PASSPHRASE|SIG_CREATED)/) {
             waitpid $pid, 0;
             return 1;
         }

commit 18068dde145bc0ebff8a8fdc7ec384dc1369fd25
Author: Daniel Kahn Gillmor <dkg at fifthhorseman.net>
Date:   Tue Oct 11 19:52:13 2016 -0400

    clean up trailing whitespace

diff --git a/lib/GnuPG/Fingerprint.pm b/lib/GnuPG/Fingerprint.pm
index fcb1028..81c38a7 100644
--- a/lib/GnuPG/Fingerprint.pm
+++ b/lib/GnuPG/Fingerprint.pm
@@ -20,7 +20,7 @@ with qw(GnuPG::HashInit);
 
 has as_hex_string => (
     isa => 'Any',
-    is  => 'rw',        
+    is  => 'rw',
 );
 
 sub compare {
diff --git a/lib/GnuPG/Handles.pm b/lib/GnuPG/Handles.pm
index b30ca57..3eee0e3 100644
--- a/lib/GnuPG/Handles.pm
+++ b/lib/GnuPG/Handles.pm
@@ -73,7 +73,7 @@ GnuPG::Handles - GnuPG handles bundle
     = ( IO::Handle->new(), IO::Handle->new(), IO::Handle->new(),
         IO::Handle->new(), IO::Handle->new(), IO::Handle->new(),
       );
- 
+
   my $handles = GnuPG::Handles->new
     ( stdin      => $stdin,
       stdout     => $stdout,
diff --git a/lib/GnuPG/Interface.pm b/lib/GnuPG/Interface.pm
index 1f1e6d5..19e8070 100644
--- a/lib/GnuPG/Interface.pm
+++ b/lib/GnuPG/Interface.pm
@@ -833,7 +833,7 @@ GnuPG::Interface - Perl interface to GnuPG
   # A simple example
   use IO::Handle;
   use GnuPG::Interface;
-  
+
   # setting up the situation
   my $gnupg = GnuPG::Interface->new();
   $gnupg->options->hash_init( armor   => 1,
@@ -852,7 +852,7 @@ GnuPG::Interface - Perl interface to GnuPG
   # Now we'll go about encrypting with the options already set
   my @plaintext = ( 'foobar' );
   my $pid = $gnupg->encrypt( handles => $handles );
-  
+
   # Now we write to the input of GnuPG
   print $input @plaintext;
   close $input;
@@ -1144,7 +1144,7 @@ The following setup can be done before any of the following examples:
 
   my $handles = GnuPG::Handles->new( stdin    => $input,
                                      stdout   => $output );
-   
+
   # this sets up the communication
   # Note that the recipients were specified earlier
   # in the 'options' data member of the $gnupg object.
@@ -1220,7 +1220,7 @@ The following setup can be done before any of the following examples:
   # a file written to disk
   # Make sure you "use IO::File" if you use this module!
   my $cipher_file = IO::File->new( 'encrypted.gpg' );
-   
+
   # this sets up the communication
   my $pid = $gnupg->decrypt( handles => $handles );
 
@@ -1252,7 +1252,7 @@ The following setup can be done before any of the following examples:
   # This time we'll just let GnuPG print to our own output
   # and read from our input, because no input is needed!
   my $handles = GnuPG::Handles->new();
-  
+
   my @ids = ( 'ftobin', '0xABCD1234' );
 
   # this time we need to specify something for
@@ -1260,7 +1260,7 @@ The following setup can be done before any of the following examples:
   # search ids as arguments
   my $pid = $gnupg->list_public_keys( handles      => $handles,
                                       command_args => [ @ids ] );
-  
+
    waitpid $pid, 0;
 
 =head2 Creating GnuPG::PublicKey Objects
@@ -1280,7 +1280,7 @@ The following setup can be done before any of the following examples:
       command_args => [ qw( test/key.1.asc ) ],
       handles      => $handles,
     );
-    
+
     my @out = <$handles->stdout()>;
     waitpid $pid, 0;
 
@@ -1357,7 +1357,7 @@ under the same terms as Perl itself.
 
 =head1 AUTHOR
 
-GnuPg::Interface is currently maintained by Jesse Vincent <jesse at cpan.org>.  
+GnuPg::Interface is currently maintained by Jesse Vincent <jesse at cpan.org>.
 
 Frank J. Tobin, ftobin at cpan.org was the original author of the package.
 

commit 2b630509770bbea5a0aa20417793ca6b01cc8bef
Author: Daniel Kahn Gillmor <dkg at fifthhorseman.net>
Date:   Tue Oct 11 19:52:58 2016 -0400

    fix capitalization of GnuPG

diff --git a/README b/README
index f457577..aa7c984 100644
--- a/README
+++ b/README
@@ -427,7 +427,7 @@ LICENSE
     under the same terms as Perl itself.
 
 AUTHOR
-    GnuPg::Interface is currently maintained by Jesse Vincent
+    GnuPG::Interface is currently maintained by Jesse Vincent
     <jesse at cpan.org>.
 
     Frank J. Tobin, ftobin at cpan.org was the original author of the package.
diff --git a/lib/GnuPG/Interface.pm b/lib/GnuPG/Interface.pm
index 19e8070..cf7138f 100644
--- a/lib/GnuPG/Interface.pm
+++ b/lib/GnuPG/Interface.pm
@@ -1357,7 +1357,7 @@ under the same terms as Perl itself.
 
 =head1 AUTHOR
 
-GnuPg::Interface is currently maintained by Jesse Vincent <jesse at cpan.org>.
+GnuPG::Interface is currently maintained by Jesse Vincent <jesse at cpan.org>.
 
 Frank J. Tobin, ftobin at cpan.org was the original author of the package.
 

commit 49d5310d532a96cbb9e3cd45b07812b6d026f13c
Author: Daniel Kahn Gillmor <dkg at fifthhorseman.net>
Date:   Tue Oct 11 19:57:10 2016 -0400

    ommand_args should be command_args

diff --git a/t/list_public_keys.t b/t/list_public_keys.t
index 7e563c1..a36a78b 100644
--- a/t/list_public_keys.t
+++ b/t/list_public_keys.t
@@ -38,7 +38,7 @@ TEST
     reset_handles();
 
     my $pid = $gnupg->list_public_keys( handles     => $handles,
-                                        ommand_args => '0xF950DA9C'
+                                        command_args => '0xF950DA9C'
                                       );
     close $stdin;
 

commit 5241873b59a293ac451d3088d94593bdf462c99f
Author: Daniel Kahn Gillmor <dkg at fifthhorseman.net>
Date:   Tue Oct 11 20:05:16 2016 -0400

    use fingerprints as inputs during tests to demonstrate explicit usage

diff --git a/README b/README
index aa7c984..ed94ede 100644
--- a/README
+++ b/README
@@ -228,7 +228,7 @@ EXAMPLES
 
       $gnupg->options->hash_init( armor    => 1,
                                   recipients => [ 'ftobin at uiuc.edu',
-                                                  '0xABCD1234' ],
+                                                  '0xABCD1234ABCD1234ABCD1234ABCD1234ABCD1234' ],
                                   meta_interactive => 0 ,
                                 );
 
@@ -347,7 +347,7 @@ EXAMPLES
       # and read from our input, because no input is needed!
       my $handles = GnuPG::Handles->new();
   
-      my @ids = ( 'ftobin', '0xABCD1234' );
+      my @ids = ( 'ftobin', '0xABCD1234ABCD1234ABCD1234ABCD1234ABCD1234' );
 
       # this time we need to specify something for
       # command_args because --list-public-keys takes
@@ -358,7 +358,7 @@ EXAMPLES
        waitpid $pid, 0;
 
   Creating GnuPG::PublicKey Objects
-      my @ids = [ 'ftobin', '0xABCD1234' ];
+      my @ids = [ 'ftobin', '0xABCD1234ABCD1234ABCD1234ABCD1234ABCD1234' ];
 
       my @keys = $gnupg->get_public_keys( @ids );
 
diff --git a/lib/GnuPG/Interface.pm b/lib/GnuPG/Interface.pm
index cf7138f..6eaef7d 100644
--- a/lib/GnuPG/Interface.pm
+++ b/lib/GnuPG/Interface.pm
@@ -1130,7 +1130,7 @@ The following setup can be done before any of the following examples:
 
   $gnupg->options->hash_init( armor    => 1,
                               recipients => [ 'ftobin at uiuc.edu',
-                                              '0xABCD1234' ],
+                                              '0xABCD1234ABCD1234ABCD1234ABCD1234ABCD1234' ],
                               meta_interactive => 0 ,
                             );
 
@@ -1253,7 +1253,7 @@ The following setup can be done before any of the following examples:
   # and read from our input, because no input is needed!
   my $handles = GnuPG::Handles->new();
 
-  my @ids = ( 'ftobin', '0xABCD1234' );
+  my @ids = ( 'ftobin', '0xABCD1234ABCD1234ABCD1234ABCD1234ABCD1234' );
 
   # this time we need to specify something for
   # command_args because --list-public-keys takes
@@ -1265,7 +1265,7 @@ The following setup can be done before any of the following examples:
 
 =head2 Creating GnuPG::PublicKey Objects
 
-  my @ids = [ 'ftobin', '0xABCD1234' ];
+  my @ids = [ 'ftobin', '0xABCD1234ABCD1234ABCD1234ABCD1234ABCD1234' ];
 
   my @keys = $gnupg->get_public_keys( @ids );
 
diff --git a/lib/GnuPG/Options.pm b/lib/GnuPG/Options.pm
index 86261a0..7788662 100644
--- a/lib/GnuPG/Options.pm
+++ b/lib/GnuPG/Options.pm
@@ -198,7 +198,7 @@ GnuPG::Options - GnuPG options embodiment
 
   # assuming $gnupg is a GnuPG::Interface object
   $gnupg->options->armor( 1 );
-  $gnupg->options->push_recipients( 'ftobin', '0xABCD1234' );
+  $gnupg->options->push_recipients( 'ftobin', '0xABCD1234ABCD1234ABCD1234ABCD1234ABCD1234' );
 
 =head1 DESCRIPTION
 
diff --git a/t/MyTestSpecific.pm b/t/MyTestSpecific.pm
index a309698..c8764cc 100644
--- a/t/MyTestSpecific.pm
+++ b/t/MyTestSpecific.pm
@@ -46,7 +46,7 @@ $gpg_is_modern = ($version[0] > 2 || ($version[0] == 2 && $version[1] >= 1));
 $gnupg->options->hash_init( homedir              => 'test/gnupghome',
                             armor                => 1,
                             meta_interactive     => 0,
-                            meta_signing_key_id  => '0xF950DA9C',
+                            meta_signing_key_id  => '0x93AFC4B1B0288A104996B44253AE596EF950DA9C',
                             always_trust         => 1,
                           );
 
diff --git a/t/encrypt.t b/t/encrypt.t
index 3183ac4..e6bdc08 100644
--- a/t/encrypt.t
+++ b/t/encrypt.t
@@ -27,7 +27,7 @@ TEST
 
     $gnupg->options->clear_recipients();
     $gnupg->options->clear_meta_recipients_keys();
-    $gnupg->options->push_recipients( '0x2E854A6B' );
+    $gnupg->options->push_recipients( '0x7466B7E98C4CCB64C2CE738BADB99D9C2E854A6B' );
 
     my $pid = $gnupg->encrypt( handles => $handles );
 
@@ -43,7 +43,7 @@ TEST
 {
     reset_handles();
 
-    my @keys = $gnupg->get_public_keys( '0xF950DA9C' );
+    my @keys = $gnupg->get_public_keys( '0x93AFC4B1B0288A104996B44253AE596EF950DA9C' );
     $gnupg->options->clear_recipients();
     $gnupg->options->clear_meta_recipients_keys();
     $gnupg->options->push_meta_recipients_keys( @keys );
@@ -64,7 +64,7 @@ TEST
 
     $gnupg->options->clear_recipients();
     $gnupg->options->clear_meta_recipients_keys();
-    $gnupg->options->push_recipients( '0x2E854A6B' );
+    $gnupg->options->push_recipients( '0x7466B7E98C4CCB64C2CE738BADB99D9C2E854A6B' );
 
     $handles->stdin( $texts{plain}->fh() );
     $handles->options( 'stdin' )->{direct} = 1;
diff --git a/t/export_keys.t b/t/export_keys.t
index cf5c82b..5add064 100644
--- a/t/export_keys.t
+++ b/t/export_keys.t
@@ -15,7 +15,7 @@ TEST
     reset_handles();
 
     my $pid = $gnupg->export_keys( handles      => $handles,
-                                   command_args => '0xF950DA9C' );
+                                   command_args => '0x93AFC4B1B0288A104996B44253AE596EF950DA9C' );
     close $stdin;
     waitpid $pid, 0;
 
@@ -31,7 +31,7 @@ TEST
     $handles->options( 'stdout' )->{direct} = 1;
 
     my $pid = $gnupg->export_keys( handles            => $handles,
-                                   command_args => '0xF950DA9C' );
+                                   command_args => '0x93AFC4B1B0288A104996B44253AE596EF950DA9C' );
     waitpid $pid, 0;
     return $CHILD_ERROR == 0;
 };
diff --git a/t/get_public_keys.t b/t/get_public_keys.t
index 9e96f7d..7893625 100644
--- a/t/get_public_keys.t
+++ b/t/get_public_keys.t
@@ -19,7 +19,7 @@ TEST
 {
     reset_handles();
 
-    my @returned_keys = $gnupg->get_public_keys_with_sigs( '0xF950DA9C' );
+    my @returned_keys = $gnupg->get_public_keys_with_sigs( '0x93AFC4B1B0288A104996B44253AE596EF950DA9C' );
 
     return 0 unless @returned_keys == 1;
 
diff --git a/t/get_secret_keys.t b/t/get_secret_keys.t
index c798cce..a7f1348 100644
--- a/t/get_secret_keys.t
+++ b/t/get_secret_keys.t
@@ -18,7 +18,7 @@ TEST
 {
     reset_handles();
 
-    my @returned_keys = $gnupg->get_secret_keys( '0xF950DA9C' );
+    my @returned_keys = $gnupg->get_secret_keys( '0x93AFC4B1B0288A104996B44253AE596EF950DA9C' );
 
     return 0 unless @returned_keys == 1;
 
diff --git a/t/list_public_keys.t b/t/list_public_keys.t
index a36a78b..622b092 100644
--- a/t/list_public_keys.t
+++ b/t/list_public_keys.t
@@ -38,7 +38,7 @@ TEST
     reset_handles();
 
     my $pid = $gnupg->list_public_keys( handles     => $handles,
-                                        command_args => '0xF950DA9C'
+                                        command_args => '0x93AFC4B1B0288A104996B44253AE596EF950DA9C'
                                       );
     close $stdin;
 
@@ -64,7 +64,7 @@ TEST
     $handles->options( 'stdout' )->{direct} = 1;
 
     my $pid = $gnupg->list_public_keys( handles      => $handles,
-                                        command_args => '0xF950DA9C',
+                                        command_args => '0x93AFC4B1B0288A104996B44253AE596EF950DA9C',
                                       );
 
     waitpid $pid, 0;
diff --git a/t/list_secret_keys.t b/t/list_secret_keys.t
index 51e3651..7040c38 100644
--- a/t/list_secret_keys.t
+++ b/t/list_secret_keys.t
@@ -55,7 +55,7 @@ TEST
     reset_handles();
 
     my $pid = $gnupg->list_secret_keys( handles      => $handles,
-                                        command_args => '0xF950DA9C' );
+                                        command_args => '0x93AFC4B1B0288A104996B44253AE596EF950DA9C' );
     close $stdin;
 
     $outfile = 'test/secret-keys/2.out';
@@ -80,7 +80,7 @@ TEST
     $handles->options( 'stdout' )->{direct} = 1;
 
     my $pid = $gnupg->list_secret_keys( handles      => $handles,
-                                        command_args => '0xF950DA9C' );
+                                        command_args => '0x93AFC4B1B0288A104996B44253AE596EF950DA9C' );
 
     waitpid $pid, 0;
 
diff --git a/t/list_sigs.t b/t/list_sigs.t
index 16cfa6a..1301fb2 100644
--- a/t/list_sigs.t
+++ b/t/list_sigs.t
@@ -36,7 +36,7 @@ TEST
     reset_handles();
 
     my $pid = $gnupg->list_sigs( handles      => $handles,
-                                 command_args => '0xF950DA9C',
+                                 command_args => '0x93AFC4B1B0288A104996B44253AE596EF950DA9C',
                                );
     close $stdin;
 
@@ -60,7 +60,7 @@ TEST
     $handles->options( 'stdout' )->{direct} = 1;
 
     my $pid = $gnupg->list_sigs( handles      => $handles,
-                                 command_args => '0xF950DA9C',
+                                 command_args => '0x93AFC4B1B0288A104996B44253AE596EF950DA9C',
                                );
 
     waitpid $pid, 0;
diff --git a/t/sign_and_encrypt.t b/t/sign_and_encrypt.t
index 5dc1c08..df0fc75 100644
--- a/t/sign_and_encrypt.t
+++ b/t/sign_and_encrypt.t
@@ -14,7 +14,7 @@ TEST
 {
     reset_handles();
 
-    $gnupg->options->push_recipients( '0x2E854A6B' );
+    $gnupg->options->push_recipients( '0x7466B7E98C4CCB64C2CE738BADB99D9C2E854A6B' );
     my $pid = $gnupg->sign_and_encrypt( handles => $handles );
 
     print $stdin @{ $texts{plain}->data() };

commit 8d68798f8b5b6caf3654975ce2599bafd95b3058
Author: Daniel Kahn Gillmor <dkg at fifthhorseman.net>
Date:   Tue Oct 11 20:17:49 2016 -0400

    move key files to generic names

diff --git a/t/000_setup.t b/t/000_setup.t
index 7f7f7b0..a8e3042 100644
--- a/t/000_setup.t
+++ b/t/000_setup.t
@@ -19,7 +19,7 @@ TEST
     copy('test/gpg.conf', 'test/gnupghome/gpg.conf');
     reset_handles();
 
-    my $pid = $gnupg->import_keys(command_args => [ 'test/pubring.gpg', 'test/secring.gpg' ],
+    my $pid = $gnupg->import_keys(command_args => [ 'test/public_keys.pgp', 'test/secret_keys.pgp' ],
                                   options => [ 'batch'],
                                   handles => $handles);
     waitpid $pid, 0;
diff --git a/test/pubring.gpg b/test/public_keys.pgp
similarity index 100%
rename from test/pubring.gpg
rename to test/public_keys.pgp
diff --git a/test/secring.gpg b/test/secret_keys.pgp
similarity index 100%
rename from test/secring.gpg
rename to test/secret_keys.pgp

commit 205397b25d17cbc9bbd8f64b514b680b7e4a13be
Author: Daniel Kahn Gillmor <dkg at fifthhorseman.net>
Date:   Tue Oct 11 20:25:48 2016 -0400

    fix spelling: s/convience/convenience/

diff --git a/README b/README
index ed94ede..a05ef9b 100644
--- a/README
+++ b/README
@@ -143,7 +143,7 @@ OBJECT METHODS
         does not come into play. If the passphrase data member handle of the
         handles object is not defined, but the the passphrase data member
         handle of GnuPG::Interface object is, GnuPG::Interface will handle
-        passing this information into GnuPG for the user as a convience.
+        passing this information into GnuPG for the user as a convenience.
         Note that this will result in GnuPG::Interface storing the
         passphrase in memory, instead of having it simply 'pass-through' to
         GnuPG via a handle.
@@ -271,7 +271,7 @@ EXAMPLES
                                        );
 
       # indicate our pasphrase through the
-      # convience method
+      # convenience method
       $gnupg->passphrase( $passphrase );
 
       # this sets up the communication
diff --git a/lib/GnuPG/Interface.pm b/lib/GnuPG/Interface.pm
index 6eaef7d..29205f0 100644
--- a/lib/GnuPG/Interface.pm
+++ b/lib/GnuPG/Interface.pm
@@ -1008,7 +1008,7 @@ and so this information is not generated and does not come into play.
 If the B<passphrase> data member handle of the B<handles> object
 is not defined, but the the B<passphrase> data member handle of GnuPG::Interface
 object is, GnuPG::Interface will handle passing this information into GnuPG
-for the user as a convience.  Note that this will result in
+for the user as a convenience.  Note that this will result in
 GnuPG::Interface storing the passphrase in memory, instead of having
 it simply 'pass-through' to GnuPG via a handle.
 
@@ -1175,7 +1175,7 @@ The following setup can be done before any of the following examples:
 				   );
 
   # indicate our pasphrase through the
-  # convience method
+  # convenience method
   $gnupg->passphrase( $passphrase );
 
   # this sets up the communication

commit 8835cc5151e5a7b6a80a4f6655c3a8c75ae3c4ed
Author: Daniel Kahn Gillmor <dkg at fifthhorseman.net>
Date:   Tue Oct 11 20:59:43 2016 -0400

    added new secret key with different passphrase
    
    Adding a new secret key with a different passphrase should allow us to
    differentiate between passing the passphrase explicitly and relying on
    the agent + pinentry.

diff --git a/t/000_setup.t b/t/000_setup.t
index a8e3042..b183241 100644
--- a/t/000_setup.t
+++ b/t/000_setup.t
@@ -19,7 +19,7 @@ TEST
     copy('test/gpg.conf', 'test/gnupghome/gpg.conf');
     reset_handles();
 
-    my $pid = $gnupg->import_keys(command_args => [ 'test/public_keys.pgp', 'test/secret_keys.pgp' ],
+    my $pid = $gnupg->import_keys(command_args => [ 'test/public_keys.pgp', 'test/secret_keys.pgp', 'test/new_secret.pgp' ],
                                   options => [ 'batch'],
                                   handles => $handles);
     waitpid $pid, 0;
diff --git a/test/new_secret.pgp b/test/new_secret.pgp
new file mode 100644
index 0000000..5feb72c
--- /dev/null
+++ b/test/new_secret.pgp
@@ -0,0 +1,58 @@
+-----BEGIN PGP PRIVATE KEY BLOCK-----
+
+lQPGBFf9iNIBCACZGF36JFTAggUJK85gweUquqh0kvVQICUtyiHXFXBBPzCK+RWL
+oc5yeOfILHH7FfOztwPH1oJ7SWQtOgpuoiMHPtF7ne+MYevMf9jTYb/xCT0yZID5
+/ieoHwUQQPiowxGewOww23RLQ1Cf46nqGBUD+fsWwT2Eq6ojLp/H72h+2lQ1ZCWd
+Q/9MSQQgDo5tWptokFGmLBKCS59pYMBaLbKSj7lFa/ekPm9zhcdmmLrLHCS9rIUP
+VKlWAg02MVmMB4fYm9nbtuwYHWvbDFYzpVr2WNlRZlPy0Y46ahxFbFwhtmOJAgT1
+tgaQtDXo3kXRXngYZstDfe61Hqmc44j1vJ4VABEBAAH+BwMCnvb4v9vnhhzmdZdJ
+EzK3ikXYQp3PcOMDlRE5qtBmXhOJXH2tdEmXjegjWGA501eeoks0VnpBba2m4B36
+Z37fjpOEi4QOuTn6emVwijJZgmmTAC7JHNzAW+IsiRvk/2907UZCwa/1UQpC0bik
+pHTZx+yKp33vGbkbCkKgHFQoHcS9D1by0WOkaLSlcE9CUCKb5LCe2Q1KDwZGrg60
+4WUvg9eM2eatixAyOJEoRONlXDcQnUhSnG5+TUPNhVVWIaM/tPAgYmBG5oCSJ/N0
+ls8cXoOVup/itBHo2Bfn+nyh0OAWdgdVmB0rPYUCLJV0FiQx5tB59OHmA3Naokj5
+rvumyklCg314NnkEXrbPq7kKbX0X8UPoXdzAmalb4++OhgzEwd3NkWxvFSxKkQAt
+XAU5i9XNHJXLwATAMlEaXMBmfcpjyIx4WpBUSmYMTjh0Nu5ee+kGvMY9fUxOKbet
+IS9agFSMwVNRsX91+pKtBCQc7Je5tIrLhC8Hbvotn0GA8iFgu6LBqkrUO9Rh30Xs
+vzz3oXm7WgHbL30m9h+rJ2dmPZOwmW/0zRUec/7alizx0T4sLx7T0qUPUxeEjkeU
+JWtqfrcXEc3xIR9r5S2xqsUSKx6h1UhHMeMtQaDBgeH/Syq7a2gnkNoY84xxojGj
+lGkis5PF3xFpYqvjY0thyPFNxQguRlqktN8gNB+V1dShbCpNI9bDzv4pzvogEiM0
+EM/xvJSCkARCe6nqOugWV8j5f3+9tuyREqcidHq+PR+USoNYdUWQO14kPY6e62wO
+lC5B4G7TDQtigCfOyEOiPXYC/qnC8sPVR2u5bCYm2YJT7L+rYRLSN+628qz7BwH3
+9XtpnRtBFWpjI5qjn4uMM42e3k5UVB/r4GyrLXhEuO8D81TVzRQhjiqLweguk73h
+VDjEd0yachHbtCxHbnVQRzo6SW50ZXJmYWNlIFRlc3Qga2V5IDx0ZXN0QGV4YW1w
+bGUub3JnPokBNwQTAQgAIQUCV/2I0gIbAwULCQgHAgYVCAkKCwIEFgIDAQIeAQIX
+gAAKCRAbkTzptnR93EZkB/9groVsVMBJtGP1GSFMg2Q9loyijXT2P6hCbUTS4YMz
+O4jQPB8UQ39XIhyWo7hVGsXeA777+7VTto7q0CG9Ph7FTGKK8W2AnzTUKNdXAC6h
+qIc+ymvlm71GxhkKFR0vDbFg6CLJ/MX/x1Bd0TKh4RZtgOqX6A7Pzw/AI7f2YJcJ
+BKPT+/q/F/Wp1r+mxZ5pxUvYm643GVzdnbtuoqgBLng/3n1zjIz+oIz6RGBjzHni
+3TUTKe//ewn1lIdTxPdUZA9G4vTE5dCnM4MHTxQSXA+aUexuONswQhiANtfVCW8c
+sf9MQpkQ/Vqv9hfeYwH4pJ8IPK1No9F0a0fvnq2JaX4gnQPGBFf9iNIBCADEQ6HK
+s5tWN2Ph/3A6D0A2nSc6m1Mh/AXhdptka0aPhhVgspCmQ1lJP/Kdf6AnlCi6u1G7
+QXvGX8OtbKNosLi91nIqvNwckUOvXrLcAk/epkmidopOuHUZhE+1UaLKs7UssBOe
+TQTtADdl2786E3qbtaNrjDTvbNesU1DEZjNoBWfKYHZYv2wCF170Lwzp7NJhAueO
+bTwfUO8EusST6d1NYB0zFxbBi60/hJHCfcAuaSn00jFQ+kj8m7jXCgcyB+1+25d2
+gpPbs19S4pi9f7eQflhglm0wB13C6yl+YgwVZQxU/fU70jgSYhkXNPx5bEN3WGkg
+4hnP53hrsI4p3se1ABEBAAH+BwMCAppvwSTp9Y/mu317D14a9k6m/zC2LrzPx6dl
+P3GtDJUCs1CVH/wXsUxLY4hAgS188xPhNLuIWuXwQ7qX7E8kanxgPqeK7NTAPKxH
+CEqJPevFRBtftHq3zqZZF9CHXulDO3KkWxIHANMclq+zcUotrc4GXIxeYjewXv9p
+tzKEjlt27Q00VvwRM7JVxBlC3xJvKXf6zyRoUt2/Clq+CFkb2s+dAzCI52o7tlB9
+El84sTIlJr0+b6+GcwrKonS8HcGUECfYmSiIiNmxlkJ/4OabDlDYlzvmCYv2pMjc
+Bif70Dowb8TBD/iTFLPY2lkhqBFi3Bcqc51MVecaQk3rRbVyOqhvGaRE084/LmkN
+gkE6vQKRSbzRmYwyKC/QUKOW5qbl5Jf3lrjVeM5tEnvJeRCfZEokKjIZul4nX4dK
+zxH+l+sCUA+RnEeGB2y1yhnPkP4dYHEb8iMLINqXQd18FpBFSs9yv9tFWJhdblUK
+SiS8DXmuoZI2Mk8yMZ0j0bi8mu9eh52dqYgBGD7TgjP5vpYU/zbtpNgMP0Zvne1X
+gig6NKK1+3VAZaiOvYUUHZERJGp/eggTtF66cD/0EHJjoZ/0pAciEvWYUyXWVBdj
+eVWBZE/RVOwrTMBVtrxQsPJ3sfeGlLt21IZYKathTZ/dn5PSlU+i4f9VyC/hHd8S
+xouQU3nB//ihbrR65YH5E53e8+jPaRtFvLbcqmY8YftV0y/5BZwduZoxcOtxD3A0
+J/2GVpUhs3WngCksdUAEbrEXzKKSOC7b4KDw2sTIT5xHra4CBK5L5N85ny8tG7A6
+wmTt+6PHo51gx/W/0jiMB3rEiGoTZ86uWLaGv5SgqLP49euCIEXNKK9srFK3o7QE
+04upH9zOXR8ytvPOLy/K5zT6YH2eyNs19sWfjAfP/bxhnrDYajsZ2WKZiQEfBBgB
+CAAJBQJX/YjSAhsMAAoJEBuRPOm2dH3c+6kH+wWoEqTlPdPLZcTN8I5a6HHD0Ul8
+7xt3OtiRFoMD2M+zgLvImaj8AULap4w/0G+J+7PCUER8JhcePSzLbizfpTczbDP2
+E1LhEM8IBE6GT8yL8VB9AL1xW+hXIi5sWW/f900deOhoh7ikrP7KxT0c8zQjaaqV
+n6bio93CvZ3yBqMO20apwWDyiSoBpXVjLrW00BdL8i9Rsf6v5UwIIy9o7pfjK5zo
+mAZM2dKzlp9z4q5P6yE4aXI0bHz+XvG7hdpkHmjG5A+EQCnN2qoDNIA4QiRhH8TQ
+aTaj4AlCiCAV2hEelPYve5QKccAsfC//qr+FMF+0bhZa05X2afxLYtku0Ms=
+=ftgB
+-----END PGP PRIVATE KEY BLOCK-----
diff --git a/test/secret-keys/1.0.test b/test/secret-keys/1.0.test
index 129d472..f8239a9 100644
--- a/test/secret-keys/1.0.test
+++ b/test/secret-keys/1.0.test
@@ -5,3 +5,7 @@ uid                  GnuPG test key (for testing purposes only)
 uid                  Foo Bar (1)
 ssb    768g/2E854A6B 2000-02-06
 
+sec   2048R/B6747DDC 2016-10-12
+uid                  GnuPG::Interface Test key <test at example.org>
+ssb   2048R/AE441D0F 2016-10-12
+
diff --git a/test/secret-keys/1.modern.test b/test/secret-keys/1.modern.test
index 3e46407..42b27a1 100644
--- a/test/secret-keys/1.modern.test
+++ b/test/secret-keys/1.modern.test
@@ -6,3 +6,8 @@ uid           [ unknown] GnuPG test key (for testing purposes only)
 uid           [ unknown] Foo Bar (1)
 ssb   elg768 2000-02-06 [E]
 
+sec   rsa2048 2016-10-12 [SC]
+      278F850AA702911F1318F0A61B913CE9B6747DDC
+uid           [ unknown] GnuPG::Interface Test key <test at example.org>
+ssb   rsa2048 2016-10-12 [E]
+

commit 31ba007b4de7d2979ce8573fc35cca28f1a13fba
Author: Daniel Kahn Gillmor <dkg at fifthhorseman.net>
Date:   Tue Oct 11 21:29:22 2016 -0400

    Test use of gpg without explicit passphrase (agent+pinentry)
    
    The modern GnuPG suite encourages the use of gpg-agent to control
    access to secret key material.  In this use case, we avoid setting an
    explicit passphrase in code, and rely on either a correctly-configured
    and primed gpg-agent or a dedicated pinentry program to supply the
    passphrase.
    
    This additional test verifies that the passphrase can be handled by
    the agent.  Note that the passphrase for this additional test key is
    *not* the default passphrase, so this test should fail in the event
    that gpg can't use the agent and the pinentry for this task.
    
    Unfortunately, this all assumes that we're using GnuPG "Modern".  I've
    noted concerns about writing forward- and backward-compatible bindings
    for GnuPG here:
    https://lists.gnupg.org/pipermail/gnupg-devel/2016-October/031800.html

diff --git a/README b/README
index a05ef9b..be06ef3 100644
--- a/README
+++ b/README
@@ -5,7 +5,7 @@ SYNOPSIS
       # A simple example
       use IO::Handle;
       use GnuPG::Interface;
-  
+
       # setting up the situation
       my $gnupg = GnuPG::Interface->new();
       $gnupg->options->hash_init( armor   => 1,
@@ -24,7 +24,7 @@ SYNOPSIS
       # Now we'll go about encrypting with the options already set
       my @plaintext = ( 'foobar' );
       my $pid = $gnupg->encrypt( handles => $handles );
-  
+
       # Now we write to the input of GnuPG
       print $input @plaintext;
       close $input;
@@ -140,13 +140,26 @@ OBJECT METHODS
         standard error, standard output, or standard error. If the status or
         logger handle is not defined, this channel of communication is never
         established with GnuPG, and so this information is not generated and
-        does not come into play. If the passphrase data member handle of the
-        handles object is not defined, but the the passphrase data member
-        handle of GnuPG::Interface object is, GnuPG::Interface will handle
-        passing this information into GnuPG for the user as a convenience.
-        Note that this will result in GnuPG::Interface storing the
-        passphrase in memory, instead of having it simply 'pass-through' to
-        GnuPG via a handle.
+        does not come into play.
+
+        If the passphrase data member handle of the handles object is not
+        defined, but the the passphrase data member handle of
+        GnuPG::Interface object is, GnuPG::Interface will handle passing
+        this information into GnuPG for the user as a convenience. Note that
+        this will result in GnuPG::Interface storing the passphrase in
+        memory, instead of having it simply 'pass-through' to GnuPG via a
+        handle.
+
+        If neither the passphrase data member of the GnuPG::Interface nor
+        the passphrase data member of the handles object is defined, then
+        GnuPG::Interface assumes that access and control over the secret key
+        will be handled by the running gpg-agent process. This represents
+        the simplest mode of operation with the GnuPG "modern" suite
+        (version 2.1 and later). It is also the preferred mode for tools
+        intended to be user-facing, since the user will be prompted directly
+        by gpg-agent for use of the secret key material. Note that for
+        programmatic use, this mode requires the gpg-agent and pinentry to
+        already be correctly configured.
 
   Other Methods
     get_public_keys( @search_strings )
@@ -241,7 +254,7 @@ EXAMPLES
 
       my $handles = GnuPG::Handles->new( stdin    => $input,
                                          stdout   => $output );
-   
+
       # this sets up the communication
       # Note that the recipients were specified earlier
       # in the 'options' data member of the $gnupg object.
@@ -315,7 +328,7 @@ EXAMPLES
       # a file written to disk
       # Make sure you "use IO::File" if you use this module!
       my $cipher_file = IO::File->new( 'encrypted.gpg' );
-   
+
       # this sets up the communication
       my $pid = $gnupg->decrypt( handles => $handles );
 
@@ -346,7 +359,7 @@ EXAMPLES
       # This time we'll just let GnuPG print to our own output
       # and read from our input, because no input is needed!
       my $handles = GnuPG::Handles->new();
-  
+
       my @ids = ( 'ftobin', '0xABCD1234ABCD1234ABCD1234ABCD1234ABCD1234' );
 
       # this time we need to specify something for
@@ -354,7 +367,7 @@ EXAMPLES
       # search ids as arguments
       my $pid = $gnupg->list_public_keys( handles      => $handles,
                                           command_args => [ @ids ] );
-  
+
        waitpid $pid, 0;
 
   Creating GnuPG::PublicKey Objects
@@ -372,7 +385,7 @@ EXAMPLES
           command_args => [ qw( test/key.1.asc ) ],
           handles      => $handles,
         );
-    
+
         my @out = <$handles->stdout()>;
         waitpid $pid, 0;
 
diff --git a/lib/GnuPG/Interface.pm b/lib/GnuPG/Interface.pm
index 29205f0..5d8b0ec 100644
--- a/lib/GnuPG/Interface.pm
+++ b/lib/GnuPG/Interface.pm
@@ -106,6 +106,14 @@ sub fork_attach_exec( $% ) {
     my ( $self, %args ) = @_;
 
     my $handles = $args{handles} or croak 'no GnuPG::Handles passed';
+    my $use_loopback_pinentry = 0;
+
+    # WARNING: this assumes that we're using the "modern" GnuPG suite
+    # -- version 2.1.x or later.  It's not clear to me how we can
+    # safely and efficiently avoid this assumption (see
+    # https://lists.gnupg.org/pipermail/gnupg-devel/2016-October/031800.html)
+    $use_loopback_pinentry = 1
+      if ($handles->passphrase());
 
     # deprecation support
     $args{commands} ||= $args{gnupg_commands};
@@ -293,8 +301,12 @@ sub fork_attach_exec( $% ) {
             $self->options->$option($fileno);
         }
 
+        my @args = $self->options->get_args();
+        push @args, '--pinentry-mode', 'loopback'
+          if $use_loopback_pinentry;
+
         my @command = (
-            $self->call(), $self->options->get_args(),
+            $self->call(), @args,
             @commands,     @command_args
         );
 
@@ -1005,6 +1017,7 @@ and standard error will be tied to the running program's standard error,
 standard output, or standard error.  If the B<status> or B<logger> handle
 is not defined, this channel of communication is never established with GnuPG,
 and so this information is not generated and does not come into play.
+
 If the B<passphrase> data member handle of the B<handles> object
 is not defined, but the the B<passphrase> data member handle of GnuPG::Interface
 object is, GnuPG::Interface will handle passing this information into GnuPG
@@ -1012,6 +1025,17 @@ for the user as a convenience.  Note that this will result in
 GnuPG::Interface storing the passphrase in memory, instead of having
 it simply 'pass-through' to GnuPG via a handle.
 
+If neither the B<passphrase> data member of the GnuPG::Interface nor
+the B<passphrase> data member of the B<handles> object is defined,
+then GnuPG::Interface assumes that access and control over the secret
+key will be handled by the running gpg-agent process.  This represents
+the simplest mode of operation with the GnuPG "modern" suite (version
+2.1 and later).  It is also the preferred mode for tools intended to
+be user-facing, since the user will be prompted directly by gpg-agent
+for use of the secret key material.  Note that for programmatic use,
+this mode requires the gpg-agent and pinentry to already be correctly
+configured.
+
 =back
 
 =head2 Other Methods
diff --git a/t/MyTestSpecific.pm b/t/MyTestSpecific.pm
index c8764cc..e513c25 100644
--- a/t/MyTestSpecific.pm
+++ b/t/MyTestSpecific.pm
@@ -55,9 +55,15 @@ struct( Text => { fn => "\$", fh => "\$", data => "\$" } );
 $texts{plain} = Text->new();
 $texts{plain}->fn( 'test/plain.1.txt' );
 
+$texts{alt_plain} = Text->new();
+$texts{alt_plain}->fn( 'test/plain.2.txt' );
+
 $texts{encrypted} = Text->new();
 $texts{encrypted}->fn( 'test/encrypted.1.gpg' );
 
+$texts{alt_encrypted} = Text->new();
+$texts{alt_encrypted}->fn( 'test/encrypted.2.gpg' );
+
 $texts{signed} = Text->new();
 $texts{signed}->fn( 'test/signed.1.asc' );
 
@@ -68,7 +74,7 @@ $texts{temp} = Text->new();
 $texts{temp}->fn( 'test/temp' );
 
 
-foreach my $name ( qw( plain encrypted signed key ) )
+foreach my $name ( qw( plain alt_plain encrypted alt_encrypted signed key ) )
 {
     my $entry = $texts{$name};
     my $filename = $entry->fn();
@@ -90,7 +96,7 @@ sub reset_handles
         stderr  => $stderr
       );
 
-    foreach my $name ( qw( plain encrypted signed key ) )
+    foreach my $name ( qw( plain alt_plain encrypted alt_encrypted signed key ) )
     {
         my $entry = $texts{$name};
         my $filename = $entry->fn();
diff --git a/t/decrypt.t b/t/decrypt.t
index b2639ed..ee41448 100644
--- a/t/decrypt.t
+++ b/t/decrypt.t
@@ -58,3 +58,30 @@ TEST
 {
     return compare( $texts{plain}->fn(), $texts{temp}->fn() ) == 0;
 };
+
+
+# test without default_passphrase (that is, by using the agent)
+TEST
+{
+    reset_handles();
+
+    $handles->stdin( $texts{alt_encrypted}->fh() );
+    $handles->options( 'stdin' )->{direct} = 1;
+
+    $handles->stdout( $texts{temp}->fh() );
+    $handles->options( 'stdout' )->{direct} = 1;
+
+    $gnupg->clear_passphrase();
+
+    my $pid = $gnupg->decrypt( handles => $handles );
+
+    waitpid $pid, 0;
+
+    return $CHILD_ERROR == 0;
+};
+
+
+TEST
+{
+    return compare( $texts{alt_plain}->fn(), $texts{temp}->fn() ) == 0;
+};
diff --git a/test/encrypted.2.gpg b/test/encrypted.2.gpg
new file mode 100644
index 0000000..105cbb3
--- /dev/null
+++ b/test/encrypted.2.gpg
@@ -0,0 +1,12 @@
+-----BEGIN PGP MESSAGE-----
+
+hQEMAw3NS2KuRB0PAQgAuCMQO6blPRIJZib+kDa51gac+BYPl8caXYTLqIHtiz2/
+YRVqePJON4lNAqT6qUksIzQHtejFO6tb1SLqgX9Ti+fKAMLrQw9VGOYaJFoRrTJs
++X33S4GHVVikRTu0dydAsekbfPSc2nRmTFUlSEV3psgAmg9xy8KA6cZroK9Xfcuh
+xW7KLE0hLP+2NZ7zNmJMdu6LDGzvlQsnm1UeElXK8XdMGf8kA3R+GgeeOnR/oEQc
+Uep77k/fLc+UV4fp9Dk1OBeg3Ko/irSaefk4mU7F4HmS8jIERHRvXBTiur1Zx8Nx
+9U3fcQuc+P9+JC89iS4PJPF1Hr0MlezAghZYJrhOrtJIAe5Uaft5KMGRfy0VQnAs
+MHqGnGtzzVWK6GK83ibgG4tTfPEHHIgNFsJf3rM4cWklUmCS9TeeDJJZfhnRA6+/
+X82e6OI7QNbO
+=DlGE
+-----END PGP MESSAGE-----
diff --git a/test/fake-pinentry.pl b/test/fake-pinentry.pl
index 12d3611..40b8b08 100755
--- a/test/fake-pinentry.pl
+++ b/test/fake-pinentry.pl
@@ -21,7 +21,7 @@ while (<STDIN>) {
   chomp;
   next if (/^$/);
   next if (/^#/);
-  print ("D test\n") if (/^getpin/i);
+  print ("D supercalifragilisticexpialidocious\n") if (/^getpin/i);
   print "OK\n";
   exit if (/^bye/i);
 }
diff --git a/test/plain.2.txt b/test/plain.2.txt
new file mode 100644
index 0000000..da5a1d5
--- /dev/null
+++ b/test/plain.2.txt
@@ -0,0 +1 @@
+test message

commit e83ff6ff53f79ba3e7ad3370d8d40f45773f9b3b
Author: Daniel Kahn Gillmor <dkg at fifthhorseman.net>
Date:   Thu May 25 16:07:45 2017 -0400

    Kill any GnuPG agent before and after the test suite.
    
    This helps to ensure that the test suite daemon is started fresh at
    every test suite run.  And it also avoids leaving a daemon running
    after the test suite, assuming the test suite manages to reach the
    end.
    
    This is considered a reasonable practice by upstream.

diff --git a/t/000_setup.t b/t/000_setup.t
index b183241..4dc4329 100644
--- a/t/000_setup.t
+++ b/t/000_setup.t
@@ -17,6 +17,9 @@ TEST
     $agentconf->write("pinentry-program " . getcwd() . "/test/fake-pinentry.pl\n");
     $agentconf->close();
     copy('test/gpg.conf', 'test/gnupghome/gpg.conf');
+    # reset the state of any long-lived gpg-agent, ignoring errors:
+    system('gpgconf', '--homedir=test/gnupghome', '--quiet', '--kill', 'gpg-agent');
+
     reset_handles();
 
     my $pid = $gnupg->import_keys(command_args => [ 'test/public_keys.pgp', 'test/secret_keys.pgp', 'test/new_secret.pgp' ],
diff --git a/t/zzz_cleanup.t b/t/zzz_cleanup.t
index 5c03a72..eea3a48 100644
--- a/t/zzz_cleanup.t
+++ b/t/zzz_cleanup.t
@@ -12,6 +12,8 @@ use File::Path qw (remove_tree);
 TEST
 {
     my $err = [];
+    # kill off any long-lived gpg-agent, ignoring errors:
+    system('gpgconf', '--homedir=test/gnupghome', '--quiet', '--kill', 'gpg-agent');
     remove_tree('test/gnupghome', {error => \$err});
     return ! @$err;
 };

commit d72465a97c0e7ee5758ae52a8032dcf828cbdc67
Author: Daniel Kahn Gillmor <dkg at fifthhorseman.net>
Date:   Fri May 26 09:51:40 2017 -0400

    Use a short temporary homedir during the test suite
    
    This avoids problems with the length of the path to the homedir as
    compared to the size limits of sockaddr_un.sun_path, particularly on
    systems where /run/user/$(id -u) is not present or available (such as
    many minimalist build environments).

diff --git a/t/000_setup.t b/t/000_setup.t
index 4dc4329..82d7005 100644
--- a/t/000_setup.t
+++ b/t/000_setup.t
@@ -12,13 +12,14 @@ use File::Copy;
 
 TEST
 {
-    make_path('test/gnupghome', { mode => 0700 });
-    my $agentconf = IO::File->new( "> test/gnupghome/gpg-agent.conf" );
+    my $homedir = $gnupg->options->homedir();
+    make_path($homedir, { mode => 0700 });
+    my $agentconf = IO::File->new( "> " . $homedir . "/gpg-agent.conf" );
     $agentconf->write("pinentry-program " . getcwd() . "/test/fake-pinentry.pl\n");
     $agentconf->close();
-    copy('test/gpg.conf', 'test/gnupghome/gpg.conf');
+    copy('test/gpg.conf', $homedir . '/gpg.conf');
     # reset the state of any long-lived gpg-agent, ignoring errors:
-    system('gpgconf', '--homedir=test/gnupghome', '--quiet', '--kill', 'gpg-agent');
+    system('gpgconf', '--homedir', $homedir, '--quiet', '--kill', 'gpg-agent');
 
     reset_handles();
 
diff --git a/t/MyTestSpecific.pm b/t/MyTestSpecific.pm
index e513c25..809d55c 100644
--- a/t/MyTestSpecific.pm
+++ b/t/MyTestSpecific.pm
@@ -22,6 +22,7 @@ use IO::Seekable;
 use File::Compare;
 use Exporter;
 use Class::Struct;
+use File::Temp qw (tempdir);
 
 use GnuPG::Interface;
 use GnuPG::Handles;
@@ -40,10 +41,25 @@ use vars qw( @ISA           @EXPORT
 
 $gnupg = GnuPG::Interface->new( passphrase => 'test' );
 
+
+my $homedir;
+if (-f "test/gnupghome") {
+  my $record = IO::File->new( "< test/gnupghome" );
+  $homedir = <$record>;
+  $record->close();
+} else {
+  $homedir = tempdir( DIR => '/tmp');
+  my $record = IO::File->new( "> test/gnupghome" );
+  $record->write($homedir);
+  $record->close();
+}
+
 my @version = split('\.', $gnupg->version());
 $gpg_is_modern = ($version[0] > 2 || ($version[0] == 2 && $version[1] >= 1));
 
-$gnupg->options->hash_init( homedir              => 'test/gnupghome',
+
+
+$gnupg->options->hash_init( homedir              => $homedir,
                             armor                => 1,
                             meta_interactive     => 0,
                             meta_signing_key_id  => '0x93AFC4B1B0288A104996B44253AE596EF950DA9C',
diff --git a/t/list_secret_keys.t b/t/list_secret_keys.t
index 7040c38..d1e3f30 100644
--- a/t/list_secret_keys.t
+++ b/t/list_secret_keys.t
@@ -23,8 +23,9 @@ TEST
     $outfile = 'test/secret-keys/1.out';
     my $out = IO::File->new( "> $outfile" )
       or die "cannot open $outfile for writing: $ERRNO";
+    my $modern_pubring_line = $gnupg->options->homedir() . "/pubring.kbx\n";
     while (<$stdout>) {
-      if ($gpg_is_modern && /^\/.*\/test\/gnupghome\/pubring.kbx$/) {
+      if ($gpg_is_modern && ($_ eq $modern_pubring_line)) {
         $out->print("test/gnupghome/pubring.kbx\n");
       } elsif ($gpg_is_modern && /^--*$/) {
         $out->print("--------------------------\n");
diff --git a/t/zzz_cleanup.t b/t/zzz_cleanup.t
index eea3a48..c3ec16f 100644
--- a/t/zzz_cleanup.t
+++ b/t/zzz_cleanup.t
@@ -11,9 +11,11 @@ use File::Path qw (remove_tree);
 # this is actually no test, just cleanup.
 TEST
 {
+    my $homedir = $gnupg->options->homedir();
     my $err = [];
     # kill off any long-lived gpg-agent, ignoring errors:
-    system('gpgconf', '--homedir=test/gnupghome', '--quiet', '--kill', 'gpg-agent');
-    remove_tree('test/gnupghome', {error => \$err});
+    system('gpgconf', '--homedir', $homedir, '--quiet', '--kill', 'gpg-agent');
+    remove_tree($homedir, {error => \$err});
+    unlink('test/gnupghome');
     return ! @$err;
 };

commit 42cc6f251cb923a2c55ac884df36eb78b5b772ca
Author: Daniel Kahn Gillmor <dkg at fifthhorseman.net>
Date:   Fri May 26 18:15:24 2017 -0400

    Make things work with gpg1 (assuming plain 'gpg' is modern)
    
     * avoid sending --pinentry-mode=loopback if gpg is invoked as gpg1
     * fix up t/list_secret_keys to account for the varied output
     * t/decrypt.t still fails two agent-only tests, but presumably folks
       who use gpg1 are not expecting to use the agent.

diff --git a/lib/GnuPG/Interface.pm b/lib/GnuPG/Interface.pm
index 5d8b0ec..f80ead5 100644
--- a/lib/GnuPG/Interface.pm
+++ b/lib/GnuPG/Interface.pm
@@ -112,8 +112,11 @@ sub fork_attach_exec( $% ) {
     # -- version 2.1.x or later.  It's not clear to me how we can
     # safely and efficiently avoid this assumption (see
     # https://lists.gnupg.org/pipermail/gnupg-devel/2016-October/031800.html)
+    #
+    # as a (brittle and incomplete) cleanup, we will avoid trying to
+    # send pinentry-loopback if the program is invoked as "gpg1"
     $use_loopback_pinentry = 1
-      if ($handles->passphrase());
+      if ($handles->passphrase() && ! ($self->call =~ m/gpg1$/));
 
     # deprecation support
     $args{commands} ||= $args{gnupg_commands};
diff --git a/t/list_secret_keys.t b/t/list_secret_keys.t
index d1e3f30..8e3c911 100644
--- a/t/list_secret_keys.t
+++ b/t/list_secret_keys.t
@@ -23,11 +23,12 @@ TEST
     $outfile = 'test/secret-keys/1.out';
     my $out = IO::File->new( "> $outfile" )
       or die "cannot open $outfile for writing: $ERRNO";
-    my $modern_pubring_line = $gnupg->options->homedir() . "/pubring.kbx\n";
+    my $seckey_file = $gpg_is_modern ? 'pubring.kbx' : 'secring.gpg';
+    my $pubring_line = $gnupg->options->homedir() . '/' . $seckey_file . "\n";
     while (<$stdout>) {
-      if ($gpg_is_modern && ($_ eq $modern_pubring_line)) {
-        $out->print("test/gnupghome/pubring.kbx\n");
-      } elsif ($gpg_is_modern && /^--*$/) {
+      if ($_ eq $pubring_line) {
+        $out->print('test/gnupghome/'.$seckey_file."\n");
+      } elsif (/^--*$/) {
         $out->print("--------------------------\n");
       } else {
         $out->print( $_ );

commit 4efd9cda9b39de44d682444f86cdf2db21626d65
Author: Brian C. Duggan <brian at bestpractical.com>
Date:   Tue Mar 13 15:46:01 2018 -0400

    Get gpg version once at instantiation
    
    Previously, GnuPG::Interface::version shelled out to `gpg --version` on
    each call. This change makes GnuPG::Interface shell out to get the
    version only when a GnuPG::interface object is created. It replaces the
    `version` method with a Moo attribute reader method.

diff --git a/lib/GnuPG/Interface.pm b/lib/GnuPG/Interface.pm
index f80ead5..34ee4db 100644
--- a/lib/GnuPG/Interface.pm
+++ b/lib/GnuPG/Interface.pm
@@ -36,6 +36,13 @@ has $_ => (
     clearer => 'clear_' . $_,
 ) for qw(call passphrase);
 
+has version => (
+    isa      => 'Str',
+    is       => 'ro',
+    reader   => 'version',
+    writer   => '_set_version',
+);
+
 has options => (
     isa        => 'GnuPG::Options',
     is         => 'rw',
@@ -52,6 +59,7 @@ sub BUILD {
 
     $self->hash_init( call => 'gpg' );
     $self->hash_init(%$args);
+    $self->_set_version($self->_version());
 }
 
 struct(
@@ -777,7 +785,7 @@ sub search_keys( $% ) {
     );
 }
 
-sub version {
+sub _version {
     my ( $self ) = @_;
 
     my $out = IO::Handle->new;

commit 6f88de454c0e6167a3cc174a4c4118fee8826af1
Author: Brian C. Duggan <brian at bestpractical.com>
Date:   Tue Mar 13 22:37:08 2018 -0400

    Add convenient GnuPG branch-checking methods
    
    The only thing a test or application usually needs to know about the
    GnuPG version is its "branch":
    
    classic <= 1.4
    stable == 2.0
    modern >= 2.1
    
    This change adds methods to quickly determine which branch
    GnuPG::Interface is running.

diff --git a/lib/GnuPG/Interface.pm b/lib/GnuPG/Interface.pm
index 34ee4db..12376ce 100644
--- a/lib/GnuPG/Interface.pm
+++ b/lib/GnuPG/Interface.pm
@@ -43,6 +43,13 @@ has version => (
     writer   => '_set_version',
 );
 
+has branch => (
+    isa      => 'Str',
+    is       => 'ro',
+    reader   => 'branch',
+    writer   => '_set_branch',
+);
+
 has options => (
     isa        => 'GnuPG::Options',
     is         => 'rw',
@@ -60,6 +67,12 @@ sub BUILD {
     $self->hash_init( call => 'gpg' );
     $self->hash_init(%$args);
     $self->_set_version($self->_version());
+
+    my @version = split('\.', $self->version());
+
+    $self->_set_branch('classic') if $version[0] == 1;
+    $self->_set_branch('stable') if $version[0] == 2 && $version[1] == 0;
+    $self->_set_branch('modern') if $version [0] > 2 || $version[0] == 2 && $version[2] >= 1;
 }
 
 struct(
@@ -116,15 +129,14 @@ sub fork_attach_exec( $% ) {
     my $handles = $args{handles} or croak 'no GnuPG::Handles passed';
     my $use_loopback_pinentry = 0;
 
-    # WARNING: this assumes that we're using the "modern" GnuPG suite
-    # -- version 2.1.x or later.  It's not clear to me how we can
-    # safely and efficiently avoid this assumption (see
-    # https://lists.gnupg.org/pipermail/gnupg-devel/2016-October/031800.html)
+    # Don't use loopback pintentry for non-modern GPG
     #
-    # as a (brittle and incomplete) cleanup, we will avoid trying to
-    # send pinentry-loopback if the program is invoked as "gpg1"
+    # Check that $version is populated before running is_modern(). If
+    # we are invoked as part of BUILD to populate $version, then any
+    # methods that depend on $version will fail. We don't care about
+    # loopback when we're called just to check gpg version.
     $use_loopback_pinentry = 1
-      if ($handles->passphrase() && ! ($self->call =~ m/gpg1$/));
+      if ($handles->passphrase() && $self->version && $self->is_modern );
 
     # deprecation support
     $args{commands} ||= $args{gnupg_commands};
@@ -796,6 +808,21 @@ sub _version {
     return $1;
 }
 
+sub is_classic {
+    my ( $self ) = @_;
+    return $self->branch eq 'classic';
+}
+
+sub is_stable {
+    my ( $self ) = @_;
+    return $self->branch eq 'stable';
+}
+
+sub is_modern {
+    my ( $self ) = @_;
+    return $self->branch eq 'modern';
+}
+
 sub test_default_key_passphrase() {
     my ($self) = @_;
 

commit 0d63b7bfe5c221004840bb9fda8556446e3c7acf
Author: Brian C. Duggan <brian at bestpractical.com>
Date:   Tue Mar 13 22:46:48 2018 -0400

    Use GnuPG::Interface branch-checking methods in tests
    
    Replace gpg version and branch checking functions defined and used in
    the tests with GnuPG::Interface class methods.

diff --git a/t/MyTestSpecific.pm b/t/MyTestSpecific.pm
index 809d55c..1599949 100644
--- a/t/MyTestSpecific.pm
+++ b/t/MyTestSpecific.pm
@@ -30,13 +30,13 @@ use GnuPG::Handles;
 use vars qw( @ISA           @EXPORT
              $stdin         $stdout           $stderr
              $gpg_program   $handles          $gnupg
-             %texts         $gpg_is_modern
+             %texts
            );
 
 @ISA    = qw( Exporter );
 @EXPORT = qw( stdin                  stdout          stderr
               gnupg_program handles  reset_handles
-              texts                  file_match      gpg_is_modern
+              texts                  file_match
             );
 
 $gnupg = GnuPG::Interface->new( passphrase => 'test' );
@@ -54,11 +54,6 @@ if (-f "test/gnupghome") {
   $record->close();
 }
 
-my @version = split('\.', $gnupg->version());
-$gpg_is_modern = ($version[0] > 2 || ($version[0] == 2 && $version[1] >= 1));
-
-
-
 $gnupg->options->hash_init( homedir              => $homedir,
                             armor                => 1,
                             meta_interactive     => 0,
diff --git a/t/get_secret_keys.t b/t/get_secret_keys.t
index a7f1348..b370e57 100644
--- a/t/get_secret_keys.t
+++ b/t/get_secret_keys.t
@@ -45,7 +45,7 @@ TEST
         usage_flags            => 'scaESCA',
         pubkey_data            => $pubkey_data,
     };
-    if (!$gpg_is_modern) {
+    if (!$gnupg->is_modern) {
       # older versions don't report ownertrust or pubkey_data for secret keys:
       delete $args->{pubkey_data};
       $args->{owner_trust} = '';
@@ -91,7 +91,7 @@ TEST
         pubkey_data              => $subkey_pub_data,
       };
 
-    if (!$gpg_is_modern) {
+    if (!$gnupg->is_modern) {
       # older versions do not report pubkey data for secret keys
       delete $sub_args->{pubkey_data};
     }
@@ -105,7 +105,7 @@ TEST
 
     $handmade_key->push_subkeys( $subkey );
     # older versions do not report designated revokers for secret keys
-    $handmade_key->push_revokers( $revoker ) if ($gpg_is_modern);
+    $handmade_key->push_revokers( $revoker ) if ($gnupg->is_modern);
 
     $handmade_key->compare( $given_key );
 };
diff --git a/t/list_secret_keys.t b/t/list_secret_keys.t
index 8e3c911..ed6430f 100644
--- a/t/list_secret_keys.t
+++ b/t/list_secret_keys.t
@@ -23,7 +23,7 @@ TEST
     $outfile = 'test/secret-keys/1.out';
     my $out = IO::File->new( "> $outfile" )
       or die "cannot open $outfile for writing: $ERRNO";
-    my $seckey_file = $gpg_is_modern ? 'pubring.kbx' : 'secring.gpg';
+    my $seckey_file = $gnupg->is_modern ? 'pubring.kbx' : 'secring.gpg';
     my $pubring_line = $gnupg->options->homedir() . '/' . $seckey_file . "\n";
     while (<$stdout>) {
       if ($_ eq $pubring_line) {
@@ -44,9 +44,7 @@ TEST
 
 TEST
 {
-    my $suffix = '0';
-    $suffix = 'modern' if ($gpg_is_modern);
-    my @files_to_test = ( 'test/secret-keys/1.'.$suffix.'.test' );
+    my @files_to_test = ( 'test/secret-keys/1.'.$gnupg->branch.'.test' );
 
     return file_match( $outfile, @files_to_test );
 };

commit d33d4e078ce0dfb0ccc241fb34fedea338259ca5
Author: Brian C. Duggan <brian at bestpractical.com>
Date:   Wed Mar 14 16:00:10 2018 -0400

    Skip loopback tests on not-modern GPG
    
    Don't run tests that require loopback pinentry on less-than-modern GPG
    (<= 2.1).

diff --git a/t/decrypt.t b/t/decrypt.t
index ee41448..70aeaf6 100644
--- a/t/decrypt.t
+++ b/t/decrypt.t
@@ -63,6 +63,8 @@ TEST
 # test without default_passphrase (that is, by using the agent)
 TEST
 {
+    return 1 unless $gnupg->is_modern();
+
     reset_handles();
 
     $handles->stdin( $texts{alt_encrypted}->fh() );
@@ -83,5 +85,7 @@ TEST
 
 TEST
 {
+    return 1 unless $gnupg->is_modern();
+
     return compare( $texts{alt_plain}->fn(), $texts{temp}->fn() ) == 0;
 };

commit af773f7f7ff87b9a1881d75616208d2c3f1a79dc
Author: Brian C. Duggan <brian at bestpractical.com>
Date:   Wed Mar 14 16:06:47 2018 -0400

    Restore test resource file name construction with branch name
    
    There are resource files under test/ and some of them have alternate
    versions for modern GPG. Previously, I had updated the list_secret_keys
    test to construct the filename with the system GPG's branch name
    (classic/stable/modern). But the only versions of the file are named
    with a '0' and 'modern'. This change restores the original functionality
    using the new GPG version convenience methods in GnuPG::Interface.

diff --git a/t/list_secret_keys.t b/t/list_secret_keys.t
index ed6430f..0c141d6 100644
--- a/t/list_secret_keys.t
+++ b/t/list_secret_keys.t
@@ -44,7 +44,8 @@ TEST
 
 TEST
 {
-    my @files_to_test = ( 'test/secret-keys/1.'.$gnupg->branch.'.test' );
+    my $branch = $gnupg->is_modern ? 'modern' : '0';
+    my @files_to_test = ( 'test/secret-keys/1.'.$branch.'.test' );
 
     return file_match( $outfile, @files_to_test );
 };

commit 2115aaec07ffdbc17469118d6bf60a53868fb2c9
Author: Brian C. Duggan <brian at bestpractical.com>
Date:   Thu Mar 15 11:54:28 2018 -0400

    Disable modern gpg setup and teardown steps under classic
    
    The setup and teardown tests create and destroy a temporary gpg home
    directory for the subsequent tests to use.
    
    Prior this change, setup configured gpg-agent in that temporary homdir
    to use a loopback pinentry program. Loopback pinentry is a feature of
    modern gpg.
    
    Also prior to this change, the setup and teardown tests attempted to
    kill gpg-agents based on the temporary homedir with gpgconf.
    
    These tests don't start gpg-agent with classic gpg, so there's no need
    to try to kill gpg-agent. Further, gpgconf cannot kill gpg-agent based
    on the home directory.
    
    Loopback pinentry is also a modern gpg feature.
    
    This change disables using gpgconf to kill gpg-agent under classic
    gpg. It also skips configuring gpg-agent to use the loopback pinentry
    program.

diff --git a/t/000_setup.t b/t/000_setup.t
index 82d7005..2677245 100644
--- a/t/000_setup.t
+++ b/t/000_setup.t
@@ -15,11 +15,14 @@ TEST
     my $homedir = $gnupg->options->homedir();
     make_path($homedir, { mode => 0700 });
     my $agentconf = IO::File->new( "> " . $homedir . "/gpg-agent.conf" );
-    $agentconf->write("pinentry-program " . getcwd() . "/test/fake-pinentry.pl\n");
+    # Classic gpg can't use loopback pinentry programs like fake-pinentry.pl.
+    $agentconf->write("pinentry-program " . getcwd() . "/test/fake-pinentry.pl\n") if $gnupg->is_modern;
     $agentconf->close();
     copy('test/gpg.conf', $homedir . '/gpg.conf');
+    # In classic gpg, gpgconf cannot kill gpg-agent. But these tests
+    # will not start an agent when using classic gpg. For modern gpg,
     # reset the state of any long-lived gpg-agent, ignoring errors:
-    system('gpgconf', '--homedir', $homedir, '--quiet', '--kill', 'gpg-agent');
+    system('gpgconf', '--homedir', $homedir, '--quiet', '--kill', 'gpg-agent') if $gnupg->is_modern;
 
     reset_handles();
 
diff --git a/t/zzz_cleanup.t b/t/zzz_cleanup.t
index c3ec16f..8ed60e6 100644
--- a/t/zzz_cleanup.t
+++ b/t/zzz_cleanup.t
@@ -13,8 +13,10 @@ TEST
 {
     my $homedir = $gnupg->options->homedir();
     my $err = [];
+    # In classic gpg, gpgconf cannot kill gpg-agent. But these tests
+    # will not start an agent when using classic gpg. For modern gpg,
     # kill off any long-lived gpg-agent, ignoring errors:
-    system('gpgconf', '--homedir', $homedir, '--quiet', '--kill', 'gpg-agent');
+    system('gpgconf', '--homedir', $homedir, '--quiet', '--kill', 'gpg-agent') if $gnupg->is_modern();
     remove_tree($homedir, {error => \$err});
     unlink('test/gnupghome');
     return ! @$err;

commit 85acd916bbd6dfae0b630ef9072ff393aac75b50
Author: Brian C. Duggan <brian at bestpractical.com>
Date:   Fri Mar 16 03:33:04 2018 -0400

    Switch to version comparison from branch comparison
    
    Remove the is_modern and friends branch checking functions. Replace with
    cmp_version for comparing version numbers.

diff --git a/lib/GnuPG/Interface.pm b/lib/GnuPG/Interface.pm
index 12376ce..afd68c8 100644
--- a/lib/GnuPG/Interface.pm
+++ b/lib/GnuPG/Interface.pm
@@ -36,6 +36,20 @@ has $_ => (
     clearer => 'clear_' . $_,
 ) for qw(call passphrase);
 
+# NB: GnuPG versions
+#
+# There are three primary "branches" of GnuPG: classic, stable, and
+# modern. They each behave slightly differently. Each branch
+# corresponds to contiguous versions of GnuPG.
+#
+# When using features specific to branches, check that the system's
+# version of gpg corresponds to the branch.
+#
+# classic: < 2.0
+# stable:  >= 2.0 and < 2.1
+# modern:  >= 2.1
+#
+# You can find examples of version comparison in the tests.
 has version => (
     isa      => 'Str',
     is       => 'ro',
@@ -43,13 +57,6 @@ has version => (
     writer   => '_set_version',
 );
 
-has branch => (
-    isa      => 'Str',
-    is       => 'ro',
-    reader   => 'branch',
-    writer   => '_set_branch',
-);
-
 has options => (
     isa        => 'GnuPG::Options',
     is         => 'rw',
@@ -67,12 +74,6 @@ sub BUILD {
     $self->hash_init( call => 'gpg' );
     $self->hash_init(%$args);
     $self->_set_version($self->_version());
-
-    my @version = split('\.', $self->version());
-
-    $self->_set_branch('classic') if $version[0] == 1;
-    $self->_set_branch('stable') if $version[0] == 2 && $version[1] == 0;
-    $self->_set_branch('modern') if $version [0] > 2 || $version[0] == 2 && $version[2] >= 1;
 }
 
 struct(
@@ -131,12 +132,12 @@ sub fork_attach_exec( $% ) {
 
     # Don't use loopback pintentry for non-modern GPG
     #
-    # Check that $version is populated before running is_modern(). If
+    # Check that $version is populated before running cmp_version. If
     # we are invoked as part of BUILD to populate $version, then any
     # methods that depend on $version will fail. We don't care about
     # loopback when we're called just to check gpg version.
     $use_loopback_pinentry = 1
-      if ($handles->passphrase() && $self->version && $self->is_modern );
+      if ($handles->passphrase() && $self->version && $self->cmp_version($self->version, '2.1') > 0 );
 
     # deprecation support
     $args{commands} ||= $args{gnupg_commands};
@@ -808,19 +809,17 @@ sub _version {
     return $1;
 }
 
-sub is_classic {
-    my ( $self ) = @_;
-    return $self->branch eq 'classic';
-}
-
-sub is_stable {
-    my ( $self ) = @_;
-    return $self->branch eq 'stable';
-}
-
-sub is_modern {
-    my ( $self ) = @_;
-    return $self->branch eq 'modern';
+sub cmp_version($$) {
+    my ( $self, $a, $b ) = (@_);
+    my @a = split '\.', $a;
+    my @b = split '\.', $b;
+    @a > @b
+        ? push @b, (0) x (@a- at b)
+        : push @a, (0) x (@b- at a);
+    for ( my $i = 0; $i < @a; $i++ ) {
+        return $a[$i] <=> $b[$i] if $a[$i] <=> $b[$i];
+    }
+    return 0;
 }
 
 sub test_default_key_passphrase() {
diff --git a/t/000_setup.t b/t/000_setup.t
index 2677245..e133ec5 100644
--- a/t/000_setup.t
+++ b/t/000_setup.t
@@ -16,13 +16,13 @@ TEST
     make_path($homedir, { mode => 0700 });
     my $agentconf = IO::File->new( "> " . $homedir . "/gpg-agent.conf" );
     # Classic gpg can't use loopback pinentry programs like fake-pinentry.pl.
-    $agentconf->write("pinentry-program " . getcwd() . "/test/fake-pinentry.pl\n") if $gnupg->is_modern;
+    $agentconf->write("pinentry-program " . getcwd() . "/test/fake-pinentry.pl\n") if $gnupg->cmp_version($gnupg->version, '2.1') >= 0;
     $agentconf->close();
     copy('test/gpg.conf', $homedir . '/gpg.conf');
     # In classic gpg, gpgconf cannot kill gpg-agent. But these tests
     # will not start an agent when using classic gpg. For modern gpg,
     # reset the state of any long-lived gpg-agent, ignoring errors:
-    system('gpgconf', '--homedir', $homedir, '--quiet', '--kill', 'gpg-agent') if $gnupg->is_modern;
+    system('gpgconf', '--homedir', $homedir, '--quiet', '--kill', 'gpg-agent') if $gnupg->cmp_version($gnupg->version, '2.1') >= 0;
 
     reset_handles();
 
diff --git a/t/decrypt.t b/t/decrypt.t
index 70aeaf6..b72b782 100644
--- a/t/decrypt.t
+++ b/t/decrypt.t
@@ -63,7 +63,7 @@ TEST
 # test without default_passphrase (that is, by using the agent)
 TEST
 {
-    return 1 unless $gnupg->is_modern();
+    return 1 unless $gnupg->cmp_version($gnupg->version, '2.1') >= 0;
 
     reset_handles();
 
@@ -85,7 +85,7 @@ TEST
 
 TEST
 {
-    return 1 unless $gnupg->is_modern();
+    return 1 unless $gnupg->cmp_version($gnupg->version, '2.1') >= 0;
 
     return compare( $texts{alt_plain}->fn(), $texts{temp}->fn() ) == 0;
 };
diff --git a/t/get_secret_keys.t b/t/get_secret_keys.t
index b370e57..5b4f97e 100644
--- a/t/get_secret_keys.t
+++ b/t/get_secret_keys.t
@@ -45,7 +45,7 @@ TEST
         usage_flags            => 'scaESCA',
         pubkey_data            => $pubkey_data,
     };
-    if (!$gnupg->is_modern) {
+    if ($gnupg->cmp_version($gnupg->version, '2.1') < 0) {
       # older versions don't report ownertrust or pubkey_data for secret keys:
       delete $args->{pubkey_data};
       $args->{owner_trust} = '';
@@ -91,7 +91,7 @@ TEST
         pubkey_data              => $subkey_pub_data,
       };
 
-    if (!$gnupg->is_modern) {
+    if ($gnupg->cmp_version($gnupg->version, '2.1') < 0) {
       # older versions do not report pubkey data for secret keys
       delete $sub_args->{pubkey_data};
     }
@@ -105,7 +105,7 @@ TEST
 
     $handmade_key->push_subkeys( $subkey );
     # older versions do not report designated revokers for secret keys
-    $handmade_key->push_revokers( $revoker ) if ($gnupg->is_modern);
+    $handmade_key->push_revokers( $revoker ) if ($gnupg->cmp_version($gnupg->version, '2.1') >= 0);
 
     $handmade_key->compare( $given_key );
 };
diff --git a/t/list_secret_keys.t b/t/list_secret_keys.t
index 0c141d6..52f698f 100644
--- a/t/list_secret_keys.t
+++ b/t/list_secret_keys.t
@@ -23,7 +23,7 @@ TEST
     $outfile = 'test/secret-keys/1.out';
     my $out = IO::File->new( "> $outfile" )
       or die "cannot open $outfile for writing: $ERRNO";
-    my $seckey_file = $gnupg->is_modern ? 'pubring.kbx' : 'secring.gpg';
+    my $seckey_file = $gnupg->cmp_version($gnupg->version, '2.1') >= 0 ? 'pubring.kbx' : 'secring.gpg';
     my $pubring_line = $gnupg->options->homedir() . '/' . $seckey_file . "\n";
     while (<$stdout>) {
       if ($_ eq $pubring_line) {
@@ -44,7 +44,8 @@ TEST
 
 TEST
 {
-    my $branch = $gnupg->is_modern ? 'modern' : '0';
+    my $branch = $gnupg->cmp_version($gnupg->version, '2.1') >= 0 ? 'modern' : '0';
+    print $branch."\n";
     my @files_to_test = ( 'test/secret-keys/1.'.$branch.'.test' );
 
     return file_match( $outfile, @files_to_test );
diff --git a/t/zzz_cleanup.t b/t/zzz_cleanup.t
index 8ed60e6..6eba3f4 100644
--- a/t/zzz_cleanup.t
+++ b/t/zzz_cleanup.t
@@ -16,7 +16,7 @@ TEST
     # In classic gpg, gpgconf cannot kill gpg-agent. But these tests
     # will not start an agent when using classic gpg. For modern gpg,
     # kill off any long-lived gpg-agent, ignoring errors:
-    system('gpgconf', '--homedir', $homedir, '--quiet', '--kill', 'gpg-agent') if $gnupg->is_modern();
+    system('gpgconf', '--homedir', $homedir, '--quiet', '--kill', 'gpg-agent') if $gnupg->cmp_version($gnupg->version, '2.1') >=0;
     remove_tree($homedir, {error => \$err});
     unlink('test/gnupghome');
     return ! @$err;

commit 9c5f9d4b99c031840606322e07ebc0775af5918f
Author: Brian C. Duggan <brian at bestpractical.com>
Date:   Tue Mar 27 01:00:26 2018 -0400

    Update pubkey_data documentation
    
    The pubkey_data member on key objects is populated when listing secret
    keys in GnuPG >= 2.1.0.

diff --git a/lib/GnuPG/Key.pm b/lib/GnuPG/Key.pm
index 8f98f85..c450821 100644
--- a/lib/GnuPG/Key.pm
+++ b/lib/GnuPG/Key.pm
@@ -207,7 +207,8 @@ instantiated, and should always be undef.
 =item pubkey_data
 
 A list of Math::BigInt objects that correspond to the public key
-material for the given key (this member is empty on secret keys).
+material for the given key. This member is empty on secret keys in
+GnuPG < 2.1.0. It is populated on secret keys In GnuPG >= 2.1.0.
 
 For DSA keys, the values are: prime (p), group order (q), group generator (g), y
 

commit d39f8114e44dbc667f8121c1fe1a00d03b8e497c
Author: Brian C. Duggan <brian at bestpractical.com>
Date:   Tue Mar 27 19:05:19 2018 -0400

    Use GNUPGHOME for gpgconf instead of --homedir
    
    GnuPG >= 2.1.0 starts gpg-agent for the gpg homedir if one is not
    already running. gpgconf >= 2.1.0 can kill a gpg-agent for the homedir
    with the --kill flag. But gpgconf >= 2.1.0 and <= 2.1.12 do not support
    specifying homedir by the '--homedir' flag. That feature was introduced
    in 2.1.13.
    
    However, gpgconf >= 2.1.0 and <= 2.1.12 do respect the GNUPGHOME
    environment variable. This change skips gpg-agent cleanup in the tests
    for GnuPG < 2.1.0. It also updates the gpgconf call to use GNUPGHOME
    instead of --homedir in the gpg-agent cleanup.

diff --git a/t/zzz_cleanup.t b/t/zzz_cleanup.t
index 6eba3f4..6029b5d 100644
--- a/t/zzz_cleanup.t
+++ b/t/zzz_cleanup.t
@@ -11,12 +11,15 @@ use File::Path qw (remove_tree);
 # this is actually no test, just cleanup.
 TEST
 {
+    return 1 unless $gnupg->cmp_version($gnupg->version, '2.1') >= 0;
     my $homedir = $gnupg->options->homedir();
     my $err = [];
-    # In classic gpg, gpgconf cannot kill gpg-agent. But these tests
-    # will not start an agent when using classic gpg. For modern gpg,
-    # kill off any long-lived gpg-agent, ignoring errors:
-    system('gpgconf', '--homedir', $homedir, '--quiet', '--kill', 'gpg-agent') if $gnupg->cmp_version($gnupg->version, '2.1') >=0;
+    # kill off any long-lived gpg-agent, ignoring errors.
+    # gpgconf versions < 2.1.11 do not support '--homedir', but still
+    # respect the GNUPGHOME environment variable
+    $ENV{'GNUPGHOME'} = $homedir;
+    system('gpgconf', '--quiet', '--kill', 'gpg-agent');
+    delete $ENV{'GNUPGHOME'};
     remove_tree($homedir, {error => \$err});
     unlink('test/gnupghome');
     return ! @$err;

commit 43a708122e272aef0f059dc79f6098261ae66e81
Author: Brian C. Duggan <brian at bestpractical.com>
Date:   Tue Mar 27 19:59:40 2018 -0400

    Run all test cleanup except gpg-agent cleanup for <= 2.1.0
    
    Run the test cleanup for all GnuPG versions. Don't run gpg-agent part of
    the cleanup for GnuPG <= 2.1.0.

diff --git a/t/zzz_cleanup.t b/t/zzz_cleanup.t
index 6029b5d..e7583af 100644
--- a/t/zzz_cleanup.t
+++ b/t/zzz_cleanup.t
@@ -11,15 +11,16 @@ use File::Path qw (remove_tree);
 # this is actually no test, just cleanup.
 TEST
 {
-    return 1 unless $gnupg->cmp_version($gnupg->version, '2.1') >= 0;
     my $homedir = $gnupg->options->homedir();
     my $err = [];
     # kill off any long-lived gpg-agent, ignoring errors.
     # gpgconf versions < 2.1.11 do not support '--homedir', but still
     # respect the GNUPGHOME environment variable
-    $ENV{'GNUPGHOME'} = $homedir;
-    system('gpgconf', '--quiet', '--kill', 'gpg-agent');
-    delete $ENV{'GNUPGHOME'};
+    if ($gnupg->cmp_version($gnupg->version, '2.1') >= 0) {
+        $ENV{'GNUPGHOME'} = $homedir;
+        system('gpgconf', '--quiet', '--kill', 'gpg-agent');
+        delete $ENV{'GNUPGHOME'};
+    }
     remove_tree($homedir, {error => \$err});
     unlink('test/gnupghome');
     return ! @$err;

commit b6762c9f39a977bfe1e1cf99d29708494d71f7ab
Author: Brian C. Duggan <brian at bestpractical.com>
Date:   Tue Mar 27 20:18:48 2018 -0400

    Use GNUPGHOME instead of --homedir when calling gpgconf in test setup
    
    The test setup also used --homedir when it ran gpgconf. This change
    updates it to use GNUPGHOME.

diff --git a/t/000_setup.t b/t/000_setup.t
index e133ec5..e1ffcf1 100644
--- a/t/000_setup.t
+++ b/t/000_setup.t
@@ -22,8 +22,11 @@ TEST
     # In classic gpg, gpgconf cannot kill gpg-agent. But these tests
     # will not start an agent when using classic gpg. For modern gpg,
     # reset the state of any long-lived gpg-agent, ignoring errors:
-    system('gpgconf', '--homedir', $homedir, '--quiet', '--kill', 'gpg-agent') if $gnupg->cmp_version($gnupg->version, '2.1') >= 0;
-
+    if ($gnupg->cmp_version($gnupg->version, '2.1') >= 0) {
+	$ENV{'GNUPGHOME'} = $homedir;
+	system('gpgconf', '--quiet', '--kill', 'gpg-agent');
+	delete $ENV{'GNUPGHOME'};
+    }
     reset_handles();
 
     my $pid = $gnupg->import_keys(command_args => [ 'test/public_keys.pgp', 'test/secret_keys.pgp', 'test/new_secret.pgp' ],

commit 44f5b98ad39972ccddbbabd8894f815babf817fc
Author: Brian C. Duggan <brian at bestpractical.com>
Date:   Wed Mar 28 13:44:38 2018 -0400

    Update secret key lists and test for early versions of gpg 2.1.x
    
    At least as of gpg 2.1.11, gpg --list-secret-keys did not include the
    key fingerprint by default. This change adds a new secret key list to
    compare against in the test. It also migrates the key list versioning to
    a numbering scheme instead of using GnuPG branch names, like 'modern'.

diff --git a/t/list_secret_keys.t b/t/list_secret_keys.t
index 52f698f..13a7ae2 100644
--- a/t/list_secret_keys.t
+++ b/t/list_secret_keys.t
@@ -44,9 +44,19 @@ TEST
 
 TEST
 {
-    my $branch = $gnupg->cmp_version($gnupg->version, '2.1') >= 0 ? 'modern' : '0';
-    print $branch."\n";
-    my @files_to_test = ( 'test/secret-keys/1.'.$branch.'.test' );
+    my $keylist;
+    if ($gnupg->cmp_version($gnupg->version, '2.1') < 0) {
+	$keylist = '0';
+    }
+    else {
+	if ($gnupg->cmp_version($gnupg->version, '2.1.11') <= 0) {
+	    $keylist = '1';
+	}
+	else {
+	    $keylist = '2';
+	}
+    }
+    my @files_to_test = ( 'test/secret-keys/1.'.$keylist.'.test' );
 
     return file_match( $outfile, @files_to_test );
 };
diff --git a/test/secret-keys/1.1.test b/test/secret-keys/1.1.test
new file mode 100644
index 0000000..2fa6ceb
--- /dev/null
+++ b/test/secret-keys/1.1.test
@@ -0,0 +1,11 @@
+test/gnupghome/pubring.kbx
+--------------------------
+sec   dsa1024/F950DA9C 2000-02-06 [SCA]
+uid         [ unknown] GnuPG test key (for testing purposes only)
+uid         [ unknown] Foo Bar (1)
+ssb   elg768/2E854A6B 2000-02-06 [E]
+
+sec   rsa2048/B6747DDC 2016-10-12 [SC]
+uid         [ unknown] GnuPG::Interface Test key <test at example.org>
+ssb   rsa2048/AE441D0F 2016-10-12 [E]
+
diff --git a/test/secret-keys/1.modern.test b/test/secret-keys/1.2.test
similarity index 100%
rename from test/secret-keys/1.modern.test
rename to test/secret-keys/1.2.test

commit a387dcaa15ff34b84afdafa55347f0c045f89afc
Author: Brian C. Duggan <brian at bestpractical.com>
Date:   Wed Mar 28 14:22:01 2018 -0400

    Pass --homdir in encrypt_symmetrically for GnuPG >= 2.1.0
    
    GnuPG::Interface::ecnrypt_symmetrically cleared the --homedir option
    since version 2.0.x fails symmetric encryption when it is passed. This
    change does not clear the --homdir option for GnuPG >= 2.1.0.

diff --git a/lib/GnuPG/Interface.pm b/lib/GnuPG/Interface.pm
index afd68c8..7316af3 100644
--- a/lib/GnuPG/Interface.pm
+++ b/lib/GnuPG/Interface.pm
@@ -695,15 +695,17 @@ sub encrypt( $% ) {
 
 sub encrypt_symmetrically( $% ) {
     my ( $self, %args ) = @_;
-    # Strip the homedir and put it back after encrypting; gpg 2.0.x
-    # fails symmetric encryption when one is passed.
+    # Strip the homedir and put it back after encrypting; gpg > 2.0.0
+    # and < 2.1.0 fail symmetric encryption when one is passed.
     my $homedir = $self->options->homedir;
-    $self->options->clear_homedir;
+    $self->options->clear_homedir
+        unless $self->cmp_version($self->version, '2.1') >= 0;
     my $pid = $self->wrap_call(
         %args,
         commands => ['--symmetric']
     );
-    $self->options->homedir($homedir);
+    $self->options->homedir($homedir)
+        unless $self->cmp_version($self->version, '2.1') >= 0;
     return $pid;
 }
 

commit f00ea4432c07a95fd5aac062bb56f7224e126584
Author: Brian C. Duggan <brian at bestpractical.com>
Date:   Wed Mar 28 15:29:57 2018 -0400

    Set allow-loopback-pinentry for gpg-agent in test homedirs
    
    Early versions of GnuPG 2.1.x did not allow loopback pinentry by
    default. Some tests test this functionality for GnuPG 2.1.x. This
    change explicitly enables loopback pinentry for all 2.1.x versions.

diff --git a/t/000_setup.t b/t/000_setup.t
index e1ffcf1..b336427 100644
--- a/t/000_setup.t
+++ b/t/000_setup.t
@@ -16,7 +16,10 @@ TEST
     make_path($homedir, { mode => 0700 });
     my $agentconf = IO::File->new( "> " . $homedir . "/gpg-agent.conf" );
     # Classic gpg can't use loopback pinentry programs like fake-pinentry.pl.
-    $agentconf->write("pinentry-program " . getcwd() . "/test/fake-pinentry.pl\n") if $gnupg->cmp_version($gnupg->version, '2.1') >= 0;
+    $agentconf->write(
+	"allow-loopback-pinentry\n".
+	"pinentry-program " . getcwd() . "/test/fake-pinentry.pl\n"
+    ) if $gnupg->cmp_version($gnupg->version, '2.1') >= 0;
     $agentconf->close();
     copy('test/gpg.conf', $homedir . '/gpg.conf');
     # In classic gpg, gpgconf cannot kill gpg-agent. But these tests

commit 91ba04df1325594f41ee65e3354c8ebcad6dae05
Author: Brian C. Duggan <brian at bestpractical.com>
Date:   Thu May 3 16:22:23 2018 -0400

    Always print newline for empty passphrases
    
    GnuPG 2.1+ needs empty passphrases to be terminated with a newline. This
    change substitutes a newline character for empty passphrases.

diff --git a/lib/GnuPG/Interface.pm b/lib/GnuPG/Interface.pm
index 7316af3..fa2415a 100644
--- a/lib/GnuPG/Interface.pm
+++ b/lib/GnuPG/Interface.pm
@@ -99,17 +99,18 @@ sub wrap_call( $% ) {
     $handles->stdout('>&STDOUT') unless $handles->stdout();
     $handles->stderr('>&STDERR') unless $handles->stderr();
 
-    # so call me sexist; English just doen't cope well
-    my $needs_passphrase_handled_for_him
+    $self->passphrase("\n") unless $self->passphrase();
+
+    my $needs_passphrase_handled
         = ( $self->passphrase() and not $handles->passphrase() ) ? 1 : 0;
 
-    if ($needs_passphrase_handled_for_him) {
+    if ($needs_passphrase_handled) {
         $handles->passphrase( IO::Handle->new() );
     }
 
     my $pid = $self->fork_attach_exec(%args);
 
-    if ($needs_passphrase_handled_for_him) {
+    if ($needs_passphrase_handled) {
         my $passphrase_handle = $handles->passphrase();
         print $passphrase_handle $self->passphrase();
         close $passphrase_handle;

commit 7f5c845cd4ba52a7a04d25f634b0766495a70bba
Author: Brian C. Duggan <brian at bestpractical.com>
Date:   Thu May 3 17:28:17 2018 -0400

    Use --no-options for GnuPG::Interface::version()
    
    Without passing --no-options, any invocation of gpg will use the default
    homedir and its config files. While --version should never make any
    changes to the homedir, this can still cause issues when switching
    between gpg versions. For example, if the user has gpg 2.1.x-specific
    config lines in gpg.conf, then running GnuPG::Interface::version() with
    gpg 1.4.x will throw warnings.

diff --git a/lib/GnuPG/Interface.pm b/lib/GnuPG/Interface.pm
index fa2415a..d433392 100644
--- a/lib/GnuPG/Interface.pm
+++ b/lib/GnuPG/Interface.pm
@@ -806,7 +806,7 @@ sub _version {
 
     my $out = IO::Handle->new;
     my $handles = GnuPG::Handles->new( stdout => $out );
-    $self->wrap_call( commands => [ '--version' ], handles => $handles );
+    $self->wrap_call( commands => [ '--no-options', '--version' ], handles => $handles );
     my $line = $out->getline;
     $line =~ /(\d+\.\d+\.\d+)/;
     return $1;

commit 0528f629372610afb5989d2f6119a43023220eac
Author: Michael Schout <mschout at gkg.net>
Date:   Mon Jan 8 18:26:54 2018 -0600

    add missing waitpid() in version()

diff --git a/lib/GnuPG/Interface.pm b/lib/GnuPG/Interface.pm
index d433392..5e3a55e 100644
--- a/lib/GnuPG/Interface.pm
+++ b/lib/GnuPG/Interface.pm
@@ -806,9 +806,12 @@ sub _version {
 
     my $out = IO::Handle->new;
     my $handles = GnuPG::Handles->new( stdout => $out );
-    $self->wrap_call( commands => [ '--no-options', '--version' ], handles => $handles );
+    my $pid = $self->wrap_call( commands => [ '--no-options', '--version' ], handles => $handles );
     my $line = $out->getline;
     $line =~ /(\d+\.\d+\.\d+)/;
+
+    waitpid $pid, 0;
+
     return $1;
 }
 

commit 2a573192319e4acc9364d1ad89e2fcb97203a0df
Author: Aaron Trevena <aaron at aarontrevena.co.uk>
Date:   Thu Apr 23 15:58:21 2020 +0100

    Update testing of public keys for GPG 2.2
    
    GnuPG 2.2 returns different validity and empty user_id_string for expired sigs

diff --git a/t/MyTestSpecific.pm b/t/MyTestSpecific.pm
index 1599949..32d2070 100644
--- a/t/MyTestSpecific.pm
+++ b/t/MyTestSpecific.pm
@@ -144,4 +144,27 @@ sub file_match
 
 
 
+# blank user_id_string and different validity for expired sig in GPG 2.2.x vs 1.x, 2.1
+sub get_expired_test_sig_params {
+    my $gnupg = shift;
+    my $version = $gnupg->version;
+
+    my %sig_params = (
+        date_string => '2000-03-16',
+        hex_id => '56FFD10A260C4FA3',
+        sig_class => 0x10,
+        algo_num => 17,
+        is_exportable => 1,
+    );
+    if ($gnupg->cmp_version($gnupg->version, '2.2') > 0) {
+        $sig_params{user_id_string} = '';
+        $sig_params{validity} = '?';
+    }
+    else {
+        $sig_params{user_id_string} = 'Frank J. Tobin <ftobin at neverending.org>',
+        $sig_params{validity} = '!';
+    }
+    return %sig_params
+}
+
 1;
diff --git a/t/get_public_keys.t b/t/get_public_keys.t
index 7893625..aa1be93 100644
--- a/t/get_public_keys.t
+++ b/t/get_public_keys.t
@@ -54,6 +54,7 @@ TEST
       );
 
 
+    # Note, blank user_id_string and different validity for expired sig in GPG 2.2.x
     my $uid0 = GnuPG::UserId->new( as_string =>  'GnuPG test key (for testing purposes only)',
                                    validity => '-');
     $uid0->push_signatures(
@@ -67,14 +68,9 @@ TEST
                             sig_class => 0x13,
                             validity => '!'),
       GnuPG::Signature->new(
+                            get_expired_test_sig_params($gnupg),
                             date => 953180097,
-                            algo_num => 17,
-                            is_exportable => 1,
-                            user_id_string => 'Frank J. Tobin <ftobin at neverending.org>',
-                            date_string => '2000-03-16',
-                            hex_id => '56FFD10A260C4FA3',
-                            sig_class => 0x10,
-                            validity => '!'),
+      ),
       GnuPG::Signature->new(
                             date => 949813093,
                             algo_num => 17,
@@ -95,6 +91,7 @@ TEST
                             validity => '!'),
                           );
 
+    # Note, blank user_id_string and different validity for expired sig in GPG 2.2.x
     my $uid1 = GnuPG::UserId->new( as_string =>  'Foo Bar (1)',
                                    validity => '-');
     $uid1->push_signatures(
@@ -108,14 +105,9 @@ TEST
                             sig_class => 0x13,
                             validity => '!'),
       GnuPG::Signature->new(
+                            get_expired_test_sig_params($gnupg),
                             date => 953180103,
-                            algo_num => 17,
-                            is_exportable => 1,
-                            user_id_string => 'Frank J. Tobin <ftobin at neverending.org>',
-                            date_string => '2000-03-16',
-                            hex_id => '56FFD10A260C4FA3',
-                            sig_class => 0x10,
-                            validity => '!'),
+      ),
       GnuPG::Signature->new(
                             date => 953179891,
                             algo_num => 17,
@@ -126,8 +118,6 @@ TEST
                             sig_class => 0x13,
                             validity => '!'));
 
-
-
     $handmade_key->push_user_ids($uid0, $uid1);
 
     my $subkey_signature = GnuPG::Signature->new

commit 26cf1b506e16cf416fb1015baa097c4be588e784
Author: Aaron Trevena <aaron at aarontrevena.co.uk>
Date:   Fri Apr 24 10:48:09 2020 +0100

    Updated options to add ignore_mdc_error and logging
    
    Newer GnuPG will force failure for some old ciphertext unless ignore_mdc_error is set, now defaults to true
    Added debug_level and logger_file options to aid troubleshooting problems

diff --git a/lib/GnuPG/Options.pm b/lib/GnuPG/Options.pm
index 7788662..fc86168 100644
--- a/lib/GnuPG/Options.pm
+++ b/lib/GnuPG/Options.pm
@@ -35,6 +35,7 @@ use constant BOOLEANS => qw(
     meta_pgp_5_compatible
     meta_pgp_2_compatible
     meta_interactive
+    ignore_mdc_error
 );
 
 use constant SCALARS => qw(
@@ -49,6 +50,8 @@ use constant SCALARS => qw(
     options
     meta_signing_key
     meta_signing_key_id
+    debug_level
+    logger_file
 );
 
 use constant LISTS => qw(
@@ -93,6 +96,9 @@ for my $list (LISTS) {
 
 sub BUILD {
     my ( $self, $args ) = @_;
+    # Newer GnuPG will force failure for old ciphertext unless set
+    $args->{ignore_mdc_error} //= 1;
+
     $self->hash_init( meta_interactive => 1 );
     $self->hash_init(%$args);
 }
@@ -157,6 +163,11 @@ sub get_option_args {
     push @args, map { ( '--recipient',  $_ ) } $self->recipients();
     push @args, map { ( '--encrypt-to', $_ ) } $self->encrypt_to();
 
+    push @args, '--debug-level', $self->debug_level() if ($self->debug_level);
+    push @args, '--logger-file', $self->logger_file() if ($self->logger_file());
+
+    push @args, '--ignore-mdc-error' if ($self->ignore_mdc_error());
+
     return @args;
 }
 

commit 67d18c86d1ca3b25b4b12570dc6deaeaf7fece17
Author: Aaron Trevena <aaron at aarontrevena.co.uk>
Date:   Fri Apr 24 16:46:56 2020 +0100

    Updated tests to skip using gpg-agent unless ENV var set
    
    setup and decryption tests will skip gnupg-agent setup and testing
    unless TEST_USE_GPG_AGENT is true and gnupg is version 2.2 or higher

diff --git a/t/000_setup.t b/t/000_setup.t
index b336427..752eda0 100644
--- a/t/000_setup.t
+++ b/t/000_setup.t
@@ -10,24 +10,38 @@ use Cwd;
 use File::Path qw (make_path);
 use File::Copy;
 
+# $gnupg->options->debug_level(4);
+# $gnupg->options->logger_file("/tmp/gnupg-$$-setup-".time().".log");
+
 TEST
 {
     my $homedir = $gnupg->options->homedir();
     make_path($homedir, { mode => 0700 });
-    my $agentconf = IO::File->new( "> " . $homedir . "/gpg-agent.conf" );
-    # Classic gpg can't use loopback pinentry programs like fake-pinentry.pl.
-    $agentconf->write(
-	"allow-loopback-pinentry\n".
-	"pinentry-program " . getcwd() . "/test/fake-pinentry.pl\n"
-    ) if $gnupg->cmp_version($gnupg->version, '2.1') >= 0;
-    $agentconf->close();
-    copy('test/gpg.conf', $homedir . '/gpg.conf');
-    # In classic gpg, gpgconf cannot kill gpg-agent. But these tests
-    # will not start an agent when using classic gpg. For modern gpg,
-    # reset the state of any long-lived gpg-agent, ignoring errors:
-    if ($gnupg->cmp_version($gnupg->version, '2.1') >= 0) {
+
+    if ($gnupg->cmp_version($gnupg->version, '2.2') >= 0 and $ENV{TEST_USE_GPG_AGENT}) {
+        my $agentconf = IO::File->new( "> " . $homedir . "/gpg-agent.conf" );
+        # Classic gpg can't use loopback pinentry programs like fake-pinentry.pl.
+        $agentconf->write(
+            "allow-preset-passphrase\n".
+                "allow-loopback-pinentry\n".
+                "pinentry-program " . getcwd() . "/test/fake-pinentry.pl\n"
+            );
+        $agentconf->close();
+        copy('test/gpg.conf', $homedir . '/gpg.conf');
+
+        # In classic gpg, gpgconf cannot kill gpg-agent. But these tests
+        # will not start an agent when using classic gpg. For modern gpg,
+        # reset the state of any long-lived gpg-agent, ignoring errors:
 	$ENV{'GNUPGHOME'} = $homedir;
-	system('gpgconf', '--quiet', '--kill', 'gpg-agent');
+	my $error = system('gpgconf', '--quiet', '--kill', 'gpg-agent', ' > /tmp/gpgconf.log  2> /tmp/gpgconf.error_log');
+        if ($error) {
+            warn "gpgconf returned error : $error";
+        }
+        $error = system('gpg-connect-agent', 'reloadagent', '/bye');
+        if ($error) {
+            warn "gpg-connect-agent returned error : $error";
+        }
+
 	delete $ENV{'GNUPGHOME'};
     }
     reset_handles();
diff --git a/t/decrypt.t b/t/decrypt.t
index b72b782..0c7a596 100644
--- a/t/decrypt.t
+++ b/t/decrypt.t
@@ -60,10 +60,10 @@ TEST
 };
 
 
-# test without default_passphrase (that is, by using the agent)
+# test without default_passphrase (that is, by using the agent, if ENV flag set)
 TEST
 {
-    return 1 unless $gnupg->cmp_version($gnupg->version, '2.1') >= 0;
+    return 1 unless ($gnupg->cmp_version($gnupg->version, '2.2') >= 0 and $ENV{TEST_USE_GPG_AGENT});
 
     reset_handles();
 
@@ -85,7 +85,6 @@ TEST
 
 TEST
 {
-    return 1 unless $gnupg->cmp_version($gnupg->version, '2.1') >= 0;
-
+    return 1 unless ($gnupg->cmp_version($gnupg->version, '2.2') >= 0 and $ENV{TEST_USE_GPG_AGENT});
     return compare( $texts{alt_plain}->fn(), $texts{temp}->fn() ) == 0;
 };

commit 00bf6f175bba3bb38a581955d8f9c9ac21d8d3bc
Author: Aaron Trevena <aaron at aarontrevena.co.uk>
Date:   Fri Apr 24 19:17:49 2020 +0100

    Added local_id field to GnuPG::Key comparson

diff --git a/lib/GnuPG/Key.pm b/lib/GnuPG/Key.pm
index c450821..243cd5b 100644
--- a/lib/GnuPG/Key.pm
+++ b/lib/GnuPG/Key.pm
@@ -88,11 +88,12 @@ sub compare {
     hex_data
     expiration_date
     expiration_date_string
+    local_id
   );
   foreach $field (@can_be_undef) {
-    return 0 unless (defined $self->$field) == (defined $other->$field);
-    if (defined $self->$field) {
-      return 0 unless $self->$field eq $other->$field;
+    return 0 unless ((defined $self->$field && ( $self->$field ne '') ) == (defined $other->$field && ( $other->$field ne '')));
+    if (defined $self->$field && ( $self->$field ne '')  ) {
+      return 0 unless ($self->$field eq $other->$field);
     }
   }
   my @objs = qw(

commit f5a8fc2e332a792ea74b64c7e9755261ed9c58e4
Author: Aaron Trevena <aaron at aarontrevena.co.uk>
Date:   Fri Apr 24 19:37:24 2020 +0100

    Update changelog for release

diff --git a/Changes b/Changes
index b42fd07..a26cf80 100644
--- a/Changes
+++ b/Changes
@@ -1,5 +1,13 @@
 Revision history for GnuPG-Interface
 
+0.53
+ - Limit support to GnuPG 2.2+ and 1.4
+ - Additional information from keys when using GnuPG 2.2 or higher
+ - Add support for use of agent/pinentry
+ - Updated options to add ignore_mdc_error and logging
+ - Improvements to tests
+ - Update pubkey_data documentation
+
 0.52 - 2016-02-16
  - Skip "grp" records, generated by GPG 2.1; this suppresses "unknown
    record type" warnings

commit 493862623848ae0393f737e89b2ad032daf681ce
Author: Aaron Trevena <aaron at aarontrevena.co.uk>
Date:   Fri Apr 24 21:28:23 2020 +0100

    Restrict to version 1.4 or 2.2+
    
    Throw error unless supported version, updated documentation

diff --git a/lib/GnuPG/Interface.pm b/lib/GnuPG/Interface.pm
index 5e3a55e..889a12f 100644
--- a/lib/GnuPG/Interface.pm
+++ b/lib/GnuPG/Interface.pm
@@ -38,16 +38,14 @@ has $_ => (
 
 # NB: GnuPG versions
 #
-# There are three primary "branches" of GnuPG: classic, stable, and
-# modern. They each behave slightly differently. Each branch
-# corresponds to contiguous versions of GnuPG.
+# There are now two supported versions of GnuPG: legacy 1.4 and stable 2.2
+# They are detected and each behave slightly differently.
 #
 # When using features specific to branches, check that the system's
 # version of gpg corresponds to the branch.
 #
-# classic: < 2.0
-# stable:  >= 2.0 and < 2.1
-# modern:  >= 2.1
+# legacy: 1.4
+# stable: >= 2.2
 #
 # You can find examples of version comparison in the tests.
 has version => (
@@ -131,14 +129,14 @@ sub fork_attach_exec( $% ) {
     my $handles = $args{handles} or croak 'no GnuPG::Handles passed';
     my $use_loopback_pinentry = 0;
 
-    # Don't use loopback pintentry for non-modern GPG
+    # Don't use loopback pintentry for legacy (1.4) GPG
     #
     # Check that $version is populated before running cmp_version. If
     # we are invoked as part of BUILD to populate $version, then any
     # methods that depend on $version will fail. We don't care about
     # loopback when we're called just to check gpg version.
     $use_loopback_pinentry = 1
-      if ($handles->passphrase() && $self->version && $self->cmp_version($self->version, '2.1') > 0 );
+      if ($handles->passphrase() && $self->version && $self->cmp_version($self->version, '2.2') > 0 );
 
     # deprecation support
     $args{commands} ||= $args{gnupg_commands};
@@ -696,17 +694,16 @@ sub encrypt( $% ) {
 
 sub encrypt_symmetrically( $% ) {
     my ( $self, %args ) = @_;
-    # Strip the homedir and put it back after encrypting; gpg > 2.0.0
-    # and < 2.1.0 fail symmetric encryption when one is passed.
+    # Strip the homedir and put it back after encrypting;
     my $homedir = $self->options->homedir;
     $self->options->clear_homedir
-        unless $self->cmp_version($self->version, '2.1') >= 0;
+        unless $self->cmp_version($self->version, '2.2') >= 0;
     my $pid = $self->wrap_call(
         %args,
         commands => ['--symmetric']
     );
     $self->options->homedir($homedir)
-        unless $self->cmp_version($self->version, '2.1') >= 0;
+        unless $self->cmp_version($self->version, '2.2') >= 0;
     return $pid;
 }
 
@@ -810,9 +807,14 @@ sub _version {
     my $line = $out->getline;
     $line =~ /(\d+\.\d+\.\d+)/;
 
+    my $version = $1;
+    unless ($self->cmp_version($version, '2.2') >= 0 or
+        ($self->cmp_version($version, '1.4') >= 0 and $self->cmp_version($version, '1.5') < 0 )) {
+        croak "GnuPG Version 1.4 or 2.2+ required";
+    }
     waitpid $pid, 0;
 
-    return $1;
+    return $version;
 }
 
 sub cmp_version($$) {
@@ -1072,8 +1074,8 @@ If neither the B<passphrase> data member of the GnuPG::Interface nor
 the B<passphrase> data member of the B<handles> object is defined,
 then GnuPG::Interface assumes that access and control over the secret
 key will be handled by the running gpg-agent process.  This represents
-the simplest mode of operation with the GnuPG "modern" suite (version
-2.1 and later).  It is also the preferred mode for tools intended to
+the simplest mode of operation with the GnuPG "stable" suite (version
+2.2 and later).  It is also the preferred mode for tools intended to
 be user-facing, since the user will be prompted directly by gpg-agent
 for use of the secret key material.  Note that for programmatic use,
 this mode requires the gpg-agent and pinentry to already be correctly
diff --git a/lib/GnuPG/Key.pm b/lib/GnuPG/Key.pm
index 243cd5b..e8d743b 100644
--- a/lib/GnuPG/Key.pm
+++ b/lib/GnuPG/Key.pm
@@ -209,7 +209,7 @@ instantiated, and should always be undef.
 
 A list of Math::BigInt objects that correspond to the public key
 material for the given key. This member is empty on secret keys in
-GnuPG < 2.1.0. It is populated on secret keys In GnuPG >= 2.1.0.
+GnuPG 1.4. It is populated on secret keys In GnuPG >= 2.2.0.
 
 For DSA keys, the values are: prime (p), group order (q), group generator (g), y
 
diff --git a/lib/GnuPG/PrimaryKey.pm b/lib/GnuPG/PrimaryKey.pm
index 3776588..e26cdc7 100644
--- a/lib/GnuPG/PrimaryKey.pm
+++ b/lib/GnuPG/PrimaryKey.pm
@@ -48,8 +48,6 @@ has $_ => (
 sub compare {
   my ($self, $other, $deep) = @_;
 
-  # not comparing local_id because it is meaningless in modern
-  # versions of GnuPG.
   my @comparison_fields = qw (
      owner_trust
   );

commit 75f84d26d4bc22c23a4f18286de0c7153d615b05
Author: Aaron Trevena <aaron at aarontrevena.co.uk>
Date:   Tue May 5 14:42:36 2020 +0100

    Fix check for passing passphrase

diff --git a/lib/GnuPG/Interface.pm b/lib/GnuPG/Interface.pm
index 889a12f..b9be5bb 100644
--- a/lib/GnuPG/Interface.pm
+++ b/lib/GnuPG/Interface.pm
@@ -100,7 +100,7 @@ sub wrap_call( $% ) {
     $self->passphrase("\n") unless $self->passphrase();
 
     my $needs_passphrase_handled
-        = ( $self->passphrase() and not $handles->passphrase() ) ? 1 : 0;
+        = ( $self->passphrase() =~ m/\S/ and not $handles->passphrase() ) ? 1 : 0;
 
     if ($needs_passphrase_handled) {
         $handles->passphrase( IO::Handle->new() );

commit c7230d7b88a30c9ad66b25bfd2292d0aa900badd
Author: Aaron Trevena <aaron at aarontrevena.co.uk>
Date:   Tue May 5 15:11:41 2020 +0100

    added debug logging options to synopsis in documentation

diff --git a/lib/GnuPG/Interface.pm b/lib/GnuPG/Interface.pm
index b9be5bb..4c39970 100644
--- a/lib/GnuPG/Interface.pm
+++ b/lib/GnuPG/Interface.pm
@@ -1203,6 +1203,11 @@ The following setup can be done before any of the following examples:
                               meta_interactive => 0 ,
                             );
 
+   $gnupg->options->debug_level(4);
+
+   $gnupg->options->logger_file("/tmp/gnupg-$$-decrypt-".time().".log");
+
+
 =head2 Encrypting
 
   # We'll let the standard error of GnuPG pass through

commit 9d3394e42873820bda83d7e5cf46979d2763563d
Author: Aaron Trevena <aaron at aarontrevena.co.uk>
Date:   Tue May 5 15:12:01 2020 +0100

    Updated test setup for gnupg-agent

diff --git a/t/000_setup.t b/t/000_setup.t
index 752eda0..1659b61 100644
--- a/t/000_setup.t
+++ b/t/000_setup.t
@@ -10,14 +10,13 @@ use Cwd;
 use File::Path qw (make_path);
 use File::Copy;
 
-# $gnupg->options->debug_level(4);
-# $gnupg->options->logger_file("/tmp/gnupg-$$-setup-".time().".log");
-
 TEST
 {
     my $homedir = $gnupg->options->homedir();
     make_path($homedir, { mode => 0700 });
 
+    copy('test/gpg.conf', $homedir . '/gpg.conf');
+
     if ($gnupg->cmp_version($gnupg->version, '2.2') >= 0 and $ENV{TEST_USE_GPG_AGENT}) {
         my $agentconf = IO::File->new( "> " . $homedir . "/gpg-agent.conf" );
         # Classic gpg can't use loopback pinentry programs like fake-pinentry.pl.
@@ -27,22 +26,22 @@ TEST
                 "pinentry-program " . getcwd() . "/test/fake-pinentry.pl\n"
             );
         $agentconf->close();
-        copy('test/gpg.conf', $homedir . '/gpg.conf');
 
-        # In classic gpg, gpgconf cannot kill gpg-agent. But these tests
-        # will not start an agent when using classic gpg. For modern gpg,
-        # reset the state of any long-lived gpg-agent, ignoring errors:
-	$ENV{'GNUPGHOME'} = $homedir;
-	my $error = system('gpgconf', '--quiet', '--kill', 'gpg-agent', ' > /tmp/gpgconf.log  2> /tmp/gpgconf.error_log');
+        my $error = system("gpg-connect-agent", "--homedir", "$homedir", '/bye');
         if ($error) {
-            warn "gpgconf returned error : $error";
+            warn "gpg-connect-agent returned error : $error";
         }
-        $error = system('gpg-connect-agent', 'reloadagent', '/bye');
+
+        $error = system('gpg-connect-agent', "--homedir", "$homedir", 'reloadagent', '/bye');
         if ($error) {
             warn "gpg-connect-agent returned error : $error";
         }
 
-	delete $ENV{'GNUPGHOME'};
+        $error = system("gpg-agent", '--homedir', "$homedir");
+        if ($error) {
+            warn "gpg-agent returned error : $error";
+        }
+
     }
     reset_handles();
 
diff --git a/t/MyTestSpecific.pm b/t/MyTestSpecific.pm
index 32d2070..c335d62 100644
--- a/t/MyTestSpecific.pm
+++ b/t/MyTestSpecific.pm
@@ -39,9 +39,6 @@ use vars qw( @ISA           @EXPORT
               texts                  file_match
             );
 
-$gnupg = GnuPG::Interface->new( passphrase => 'test' );
-
-
 my $homedir;
 if (-f "test/gnupghome") {
   my $record = IO::File->new( "< test/gnupghome" );
@@ -54,6 +51,9 @@ if (-f "test/gnupghome") {
   $record->close();
 }
 
+$ENV{'GNUPGHOME'} = $homedir;
+
+$gnupg = GnuPG::Interface->new( passphrase => 'test' );
 $gnupg->options->hash_init( homedir              => $homedir,
                             armor                => 1,
                             meta_interactive     => 0,
diff --git a/t/zzz_cleanup.t b/t/zzz_cleanup.t
index e7583af..f671b28 100644
--- a/t/zzz_cleanup.t
+++ b/t/zzz_cleanup.t
@@ -18,10 +18,11 @@ TEST
     # respect the GNUPGHOME environment variable
     if ($gnupg->cmp_version($gnupg->version, '2.1') >= 0) {
         $ENV{'GNUPGHOME'} = $homedir;
-        system('gpgconf', '--quiet', '--kill', 'gpg-agent');
+        system('gpgconf', '--homedir', $homedir, '--quiet', '--kill', 'gpg-agent');
         delete $ENV{'GNUPGHOME'};
     }
     remove_tree($homedir, {error => \$err});
     unlink('test/gnupghome');
+    unlink($homedir);
     return ! @$err;
 };

commit c9db2be1b1ec399e3652f97a0dce7897c22eedbf
Author: Aaron Trevena <aaron at aarontrevena.co.uk>
Date:   Tue May 5 15:12:39 2020 +0100

    fix passphrase clearing in decyption test

diff --git a/t/decrypt.t b/t/decrypt.t
index 0c7a596..ce8a884 100644
--- a/t/decrypt.t
+++ b/t/decrypt.t
@@ -73,6 +73,7 @@ TEST
     $handles->stdout( $texts{temp}->fh() );
     $handles->options( 'stdout' )->{direct} = 1;
 
+    $handles->clear_passphrase();
     $gnupg->clear_passphrase();
 
     my $pid = $gnupg->decrypt( handles => $handles );

commit 4a8af8183663033326fd2b592b0d493dfa7030ed
Author: Aaron Trevena <aaron at aarontrevena.co.uk>
Date:   Tue May 5 16:55:03 2020 +0100

    workaround for some gpg2.2.x expired sig behaviour in keys test

diff --git a/t/get_public_keys.t b/t/get_public_keys.t
index aa1be93..300c81c 100644
--- a/t/get_public_keys.t
+++ b/t/get_public_keys.t
@@ -218,5 +218,40 @@ TEST
 
 TEST
 {
+    # Some versions of GnuPG 2.2.x give same user_id and validity for expired sig as 1.4
+    # this forces them to be consistent and still test them with 2.2 codepath
+    no warnings qw(redefine once);
+    local *GnuPG::Signature::compare = sub {
+        my ($self, $other) = @_;
+        if ($gnupg->cmp_version($gnupg->version, '2.2') > 0) {
+            if ( defined $self->user_id_string and
+                     $self->user_id_string eq 'Frank J. Tobin <ftobin at neverending.org>') {
+                $self->user_id_string('');
+                $self->validity('?');
+            }
+        }
+
+        my @compared_fields = qw(
+                                    validity
+                                    algo_num
+                                    hex_id
+                                    date
+                                    date_string
+                                    sig_class
+                                    is_exportable
+                            );
+
+        foreach my $field ( @compared_fields ) {
+            return 0 unless $self->$field eq $other->$field;
+        }
+        # check for expiration if present?
+        return 0 unless (defined $self->expiration_date) == (defined $other->expiration_date);
+        if (defined $self->expiration_date) {
+            return 0 unless (($self->expiration_date == $other->expiration_date) ||
+                                 ($self->expiration_date_string eq $other->expiration_date_string));
+        }
+        return 1;
+    };
+
     $handmade_key->compare( $given_key, 1 );
 };

commit 633a9f625ffe1d941f04f402353d792fb06ee7c0
Author: Aaron Trevena <aaron at aarontrevena.co.uk>
Date:   Wed May 6 19:12:48 2020 +0100

    remove env flag for testing with agent if gnugpg 2.2

diff --git a/t/000_setup.t b/t/000_setup.t
index 1659b61..8e3235a 100644
--- a/t/000_setup.t
+++ b/t/000_setup.t
@@ -17,7 +17,7 @@ TEST
 
     copy('test/gpg.conf', $homedir . '/gpg.conf');
 
-    if ($gnupg->cmp_version($gnupg->version, '2.2') >= 0 and $ENV{TEST_USE_GPG_AGENT}) {
+    if ($gnupg->cmp_version($gnupg->version, '2.2') >= 0) {
         my $agentconf = IO::File->new( "> " . $homedir . "/gpg-agent.conf" );
         # Classic gpg can't use loopback pinentry programs like fake-pinentry.pl.
         $agentconf->write(
diff --git a/t/decrypt.t b/t/decrypt.t
index ce8a884..5bb35da 100644
--- a/t/decrypt.t
+++ b/t/decrypt.t
@@ -63,7 +63,7 @@ TEST
 # test without default_passphrase (that is, by using the agent, if ENV flag set)
 TEST
 {
-    return 1 unless ($gnupg->cmp_version($gnupg->version, '2.2') >= 0 and $ENV{TEST_USE_GPG_AGENT});
+    return 1 unless ($gnupg->cmp_version($gnupg->version, '2.2') >= 0);
 
     reset_handles();
 
@@ -86,6 +86,6 @@ TEST
 
 TEST
 {
-    return 1 unless ($gnupg->cmp_version($gnupg->version, '2.2') >= 0 and $ENV{TEST_USE_GPG_AGENT});
+    return 1 unless ($gnupg->cmp_version($gnupg->version, '2.2') >= 0);
     return compare( $texts{alt_plain}->fn(), $texts{temp}->fn() ) == 0;
 };

commit 8e1eda2d14bc6d2050cbb9d39b94601de07c638b
Author: Aaron Trevena <aaron at aarontrevena.co.uk>
Date:   Wed May 6 19:17:28 2020 +0100

    Added --keyring and --no-default-keyring options to GnuPG::Options

diff --git a/lib/GnuPG/Options.pm b/lib/GnuPG/Options.pm
index fc86168..9b94653 100644
--- a/lib/GnuPG/Options.pm
+++ b/lib/GnuPG/Options.pm
@@ -36,6 +36,8 @@ use constant BOOLEANS => qw(
     meta_pgp_2_compatible
     meta_interactive
     ignore_mdc_error
+    keyring
+    no_default_keyring
 );
 
 use constant SCALARS => qw(
@@ -167,6 +169,8 @@ sub get_option_args {
     push @args, '--logger-file', $self->logger_file() if ($self->logger_file());
 
     push @args, '--ignore-mdc-error' if ($self->ignore_mdc_error());
+    push @args, '--keyring' if ( $self->keyring() );
+    push @args, '--no-default-keyring' if ( $self->no_default_keyring() );
 
     return @args;
 }

-----------------------------------------------------------------------


More information about the Bps-public-commit mailing list