This is an automated email from the git hooks/post-receive script.

guillem pushed a commit to branch main
in repository dpkg.

View the commit online:
https://git.dpkg.org/cgit/dpkg/dpkg.git/commit/?id=6afb4b99eefeebd3de2868b590ce42def0906446

commit 6afb4b99eefeebd3de2868b590ce42def0906446
Author: Guillem Jover <[email protected]>
AuthorDate: Fri Jul 29 23:25:39 2022 +0200

    Dpkg::OpenPGP: Add multi-backend loading support
    
    This adds the generic infrastructure to be able to load multiple
    OpenPGP backends, depending on the requested CLI, API, or specific
    needed features when in auto mode.
---
 scripts/Dpkg/Control/HashCore.pm |   2 +-
 scripts/Dpkg/OpenPGP.pm          |  63 +++++++++++++++++++-
 scripts/Dpkg/Source/Package.pm   |   2 +-
 scripts/dpkg-buildpackage.pl     |   3 +
 scripts/t/Dpkg_OpenPGP.t         | 121 ++++++++++++++++++++++-----------------
 5 files changed, 136 insertions(+), 55 deletions(-)

diff --git a/scripts/Dpkg/Control/HashCore.pm b/scripts/Dpkg/Control/HashCore.pm
index b45bc512e..e31ef299e 100644
--- a/scripts/Dpkg/Control/HashCore.pm
+++ b/scripts/Dpkg/Control/HashCore.pm
@@ -274,7 +274,7 @@ sub parse {
                    $self->parse_error($desc, g_('unfinished OpenPGP 
signature'));
                 }
                # This does not mean the signature is correct, that needs to
-               # be verified by gnupg.
+               # be verified by an OpenPGP backend.
                $$self->{is_pgp_signed} = 1;
            }
            last; # Finished parsing one block
diff --git a/scripts/Dpkg/OpenPGP.pm b/scripts/Dpkg/OpenPGP.pm
index 06286d61d..f45c1ff7d 100644
--- a/scripts/Dpkg/OpenPGP.pm
+++ b/scripts/Dpkg/OpenPGP.pm
@@ -18,14 +18,22 @@ package Dpkg::OpenPGP;
 use strict;
 use warnings;
 
+use List::Util qw(none);
+
 use Dpkg::Gettext;
 use Dpkg::ErrorHandling;
 use Dpkg::IPC;
 use Dpkg::Path qw(find_command);
-use Dpkg::OpenPGP::Backend::GnuPG;
 
 our $VERSION = '0.01';
 
+my @BACKENDS = qw(
+    gpg
+);
+my %BACKEND = (
+    gpg => 'GnuPG',
+);
+
 sub new {
     my ($this, %opts) = @_;
     my $class = ref($this) || $this;
@@ -33,16 +41,67 @@ sub new {
     my $self = {};
     bless $self, $class;
 
+    my $backend = $opts{backend} // 'auto';
     my %backend_opts = (
         cmdv => $opts{cmdv} // 'auto',
         cmd => $opts{cmd} // 'auto',
     );
 
-    $self->{backend} = Dpkg::OpenPGP::Backend::GnuPG->new(%backend_opts);
+    if ($backend eq 'auto') {
+        # Defaults for stateless full API auto-detection.
+        $opts{needs}{api} //= 'full';
+        $opts{needs}{keystore} //= 0;
+
+        if (none { $opts{needs}{api} eq $_ } qw(full verify)) {
+            error(g_('unknown OpenPGP api requested %s'), $opts{needs}{api});
+        }
+
+        $self->{backend} = $self->_auto_backend($opts{needs}, %backend_opts);
+    } elsif (exists $BACKEND{$backend}) {
+        $self->{backend} = $self->_load_backend($BACKEND{$backend}, 
%backend_opts);
+        if (! $self->{backend}) {
+            error(g_('cannot load OpenPGP backend %s'), $backend);
+        }
+    } else {
+        error(g_('unknown OpenPGP backend %s'), $backend);
+    }
 
     return $self;
 }
 
+sub _load_backend {
+    my ($self, $backend, %opts) = @_;
+
+    my $module = "Dpkg::OpenPGP::Backend::$backend";
+    eval qq{
+        pop \@INC if \$INC[-1] eq '.';
+        require $module;
+    };
+    return if $@;
+
+    return $module->new(%opts);
+}
+
+sub _auto_backend {
+    my ($self, $needs, %opts) = @_;
+
+    foreach my $backend (@BACKENDS) {
+        my $module = $self->_load_backend($BACKEND{$backend}, %opts);
+
+        if ($needs->{api} eq 'verify') {
+            next if ! $module->has_verify_cmd();
+        } else {
+            next if ! $module->has_backend_cmd();
+        }
+        next if $needs->{keystore} && ! $module->has_keystore();
+
+        return $module;
+    }
+
+    # Otherwise load a dummy backend.
+    return Dpkg::OpenPGP::Backend->new();
+}
+
 sub can_use_secrets {
     my ($self, $key) = @_;
 
diff --git a/scripts/Dpkg/Source/Package.pm b/scripts/Dpkg/Source/Package.pm
index 6d0e86392..1956ad06e 100644
--- a/scripts/Dpkg/Source/Package.pm
+++ b/scripts/Dpkg/Source/Package.pm
@@ -212,7 +212,7 @@ sub new {
         fields => Dpkg::Control->new(type => CTRL_PKG_SRC),
         format => Dpkg::Source::Format->new(),
         options => {},
-        checksums => Dpkg::Checksums->new(),
+        checksums => Dpkg::Checksums->new(needs => { api => 'verify' }),
         openpgp => Dpkg::OpenPGP->new(),
     };
     bless $self, $class;
diff --git a/scripts/dpkg-buildpackage.pl b/scripts/dpkg-buildpackage.pl
index 2c204b8ef..440edf0dd 100755
--- a/scripts/dpkg-buildpackage.pl
+++ b/scripts/dpkg-buildpackage.pl
@@ -545,6 +545,9 @@ signkey_validate();
 
 my $openpgp = Dpkg::OpenPGP->new(
     cmd => $signcommand // 'auto',
+    needs => {
+        keystore => $signkey->needs_keystore(),
+    },
 );
 
 if (not $openpgp->can_use_secrets($signkey)) {
diff --git a/scripts/t/Dpkg_OpenPGP.t b/scripts/t/Dpkg_OpenPGP.t
index 4dea39f6f..849014afc 100644
--- a/scripts/t/Dpkg_OpenPGP.t
+++ b/scripts/t/Dpkg_OpenPGP.t
@@ -22,20 +22,30 @@ use Test::Dpkg qw(:paths :needs);
 use File::Compare;
 
 use Dpkg::ErrorHandling;
+use Dpkg::Path qw(find_command);
 use Dpkg::OpenPGP::KeyHandle;
 
-test_needs_command('gpg');
+my @backend_cmds = qw(
+    gpg
+);
+my %backend_cmd = (
+    auto => 'auto',
+    gpg => 'gpg',
+);
+my @cmds = grep { find_command($_) } @backend_cmds;
+if (@cmds == 0) {
+    plan skip_all => "requires at least one backend command: @backend_cmds";
+}
 
-plan tests => 17;
+unshift @cmds, 'auto';
+
+plan tests => 2 + 15 * scalar @cmds;
 
 use_ok('Dpkg::OpenPGP');
 use_ok('Dpkg::OpenPGP::ErrorCodes');
 
 report_options(quiet_warnings => 1);
 
-my $datadir = test_get_data_path();
-my $tempdir = test_get_temp_path();
-
 sub test_diff
 {
     my ($exp_file, $gen_file, $desc) = @_;
@@ -47,51 +57,60 @@ sub test_diff
     ok($res == 0, "$desc ($exp_file vs $gen_file)");
 }
 
-my $openpgp = Dpkg::OpenPGP->new();
-
-ok($openpgp->dearmor('PUBLIC KEY BLOCK', "$datadir/dpkg-test-pub.asc", 
"$tempdir/dpkg-test-pub.pgp") == OPENPGP_OK(),
-    'dearmoring OpenPGP ASCII Armored certificate');
-ok($openpgp->armor('PUBLIC KEY BLOCK', "$tempdir/dpkg-test-pub.pgp", 
"$tempdir/dpkg-test-pub.asc") == OPENPGP_OK(),
-    'armoring OpenPGP binary certificate');
-test_diff("$datadir/dpkg-test-pub.asc", "$tempdir/dpkg-test-pub.asc",
-    'OpenPGP certificate dearmor/armor round-trip correctly');
-
-ok($openpgp->armor('SIGNATURE', "$datadir/sign-file.sig", 
"$tempdir/sign-file.asc") == OPENPGP_OK(),
-    'armoring OpenPGP binary signature succeeded');
-ok(compare("$datadir/sign-file.sig", "$tempdir/sign-file.asc") != 0,
-    'armoring OpenPGP ASCII Armor changed the file');
-ok($openpgp->armor('SIGNATURE', "$datadir/sign-file.asc", 
"$tempdir/sign-file-rearmor.asc") == OPENPGP_OK(),
-    'armoring OpenPGP armored signature succeeded');
-test_diff("$datadir/sign-file.asc", "$tempdir/sign-file-rearmor.asc",
-    'rearmoring OpenPGP ASCII Armor changed the file');
-
-ok($openpgp->dearmor('SIGNATURE', "$tempdir/sign-file.asc", 
"$tempdir/sign-file.sig") == OPENPGP_OK(),
-    'dearmoring OpenPGP armored signature succeeded');
-test_diff("$datadir/sign-file.sig", "$tempdir/sign-file.sig",
-    'dearmored OpenPGP ASCII Armor signature matches');
-
-my $cert = "$datadir/dpkg-test-pub.asc";
-
-ok($openpgp->inline_verify("$datadir/sign-file-inline.asc", undef, $cert) == 
OPENPGP_OK(),
-    'verify OpenPGP ASCII Armor inline signature');
-ok($openpgp->inline_verify("$datadir/sign-file-inline.sig", undef, $cert) == 
OPENPGP_OK(),
-    'verify OpenPGP binary inline signature');
-
-ok($openpgp->verify("$datadir/sign-file", "$datadir/sign-file.asc", $cert) == 
OPENPGP_OK(),
-    'verify OpenPGP ASCII Armor detached signature');
-ok($openpgp->verify("$datadir/sign-file", "$datadir/sign-file.sig", $cert) == 
OPENPGP_OK(),
-    'verify OpenPGP binary detached signature');
-
-my $key = Dpkg::OpenPGP::KeyHandle->new(
-    type => 'keyfile',
-    handle => "$datadir/dpkg-test-sec.asc",
-);
-
-ok($openpgp->inline_sign("$datadir/sign-file", 
"$tempdir/sign-file-inline.asc", $key) == OPENPGP_OK(),
-    'inline OpenPGP sign');
-ok($openpgp->inline_verify("$tempdir/sign-file-inline.asc", undef, $cert) == 
OPENPGP_OK(),
-    'verify generated inline OpenPGP signature');
-
-# TODO: Add actual test cases.
+foreach my $cmd (@cmds) {
+    my $datadir = test_get_data_path();
+    my $tempdir = test_get_temp_path();
+
+    my $backend = $backend_cmd{$cmd};
+    my $openpgp = Dpkg::OpenPGP->new(
+        backend => $backend,
+        cmd => $cmd,
+    );
+
+    ok($openpgp->dearmor('PUBLIC KEY BLOCK', "$datadir/dpkg-test-pub.asc", 
"$tempdir/dpkg-test-pub.pgp") == OPENPGP_OK(),
+        "($backend:$cmd) dearmoring OpenPGP ASCII Armored certificate");
+    ok($openpgp->armor('PUBLIC KEY BLOCK', "$tempdir/dpkg-test-pub.pgp", 
"$tempdir/dpkg-test-pub.asc") == OPENPGP_OK(),
+        "($backend:$cmd) armoring OpenPGP binary certificate");
+    test_diff("$datadir/dpkg-test-pub.asc", "$tempdir/dpkg-test-pub.asc",
+        "($backend:$cmd) OpenPGP certificate dearmor/armor round-trip 
correctly");
+
+    ok($openpgp->armor('SIGNATURE', "$datadir/sign-file.sig", 
"$tempdir/sign-file.asc") == OPENPGP_OK(),
+        "($backend:$cmd) armoring OpenPGP binary signature succeeded");
+    ok(compare("$datadir/sign-file.sig", "$tempdir/sign-file.asc") != 0,
+        "($backend:$cmd) armoring OpenPGP ASCII Armor changed the file");
+    ok($openpgp->armor('SIGNATURE', "$datadir/sign-file.asc", 
"$tempdir/sign-file-rearmor.asc") == OPENPGP_OK(),
+        "($backend:$cmd) armoring OpenPGP armored signature succeeded");
+    test_diff("$datadir/sign-file.asc", "$tempdir/sign-file-rearmor.asc",
+        "($backend:$cmd) rearmoring OpenPGP ASCII Armor changed the file");
+
+    ok($openpgp->dearmor('SIGNATURE', "$tempdir/sign-file.asc", 
"$tempdir/sign-file.sig") == OPENPGP_OK(),
+        "($backend:$cmd) dearmoring OpenPGP armored signature succeeded");
+    test_diff("$datadir/sign-file.sig", "$tempdir/sign-file.sig",
+        "($backend:$cmd) dearmored OpenPGP ASCII Armor signature matches");
+
+    my $cert = "$datadir/dpkg-test-pub.asc";
+
+    ok($openpgp->inline_verify("$datadir/sign-file-inline.asc", undef, $cert) 
== OPENPGP_OK(),
+        "($backend:$cmd) verify OpenPGP ASCII Armor inline signature");
+    ok($openpgp->inline_verify("$datadir/sign-file-inline.sig", undef, $cert) 
== OPENPGP_OK(),
+        "($backend:$cmd) verify OpenPGP binary inline signature");
+
+    ok($openpgp->verify("$datadir/sign-file", "$datadir/sign-file.asc", $cert) 
== OPENPGP_OK(),
+        "($backend:$cmd) verify OpenPGP ASCII Armor detached signature");
+    ok($openpgp->verify("$datadir/sign-file", "$datadir/sign-file.sig", $cert) 
== OPENPGP_OK(),
+        "($backend:$cmd) verify OpenPGP binary detached signature");
+
+    my $key = Dpkg::OpenPGP::KeyHandle->new(
+        type => 'keyfile',
+        handle => "$datadir/dpkg-test-sec.asc",
+    );
+
+    ok($openpgp->inline_sign("$datadir/sign-file", 
"$tempdir/sign-file-inline.asc", $key) == OPENPGP_OK(),
+        "($backend:$cmd) inline OpenPGP sign");
+    ok($openpgp->inline_verify("$tempdir/sign-file-inline.asc", undef, $cert) 
== OPENPGP_OK(),
+        "($backend:$cmd) verify generated inline OpenPGP signature");
+
+    # TODO: Add more test cases.
+}
 
 1;

-- 
Dpkg.Org's dpkg

Reply via email to