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=4af4a46fa12221e8e7ea7d525619a2dbc89f7786

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

    Dpkg::OpenPGP::Backend: Refactor GnuPG functions from Dpkg::OpenPGP
    
    Create a generic Dpkg::OpenPGP::Backend as the base class for all
    backend implementations. Move the GnuPG specific backend into a new
    Dpkg::OpenPGP::Backend::GnuPG.
    
    This will make adding other implementations easier.
---
 scripts/Dpkg/OpenPGP.pm                            | 227 ++-------------------
 scripts/Dpkg/OpenPGP/Backend.pm                    | 109 ++++++++++
 .../Dpkg/{OpenPGP.pm => OpenPGP/Backend/GnuPG.pm}  |  61 ++----
 scripts/Makefile.am                                |   2 +
 4 files changed, 141 insertions(+), 258 deletions(-)

diff --git a/scripts/Dpkg/OpenPGP.pm b/scripts/Dpkg/OpenPGP.pm
index 005d6d7eb..06286d61d 100644
--- a/scripts/Dpkg/OpenPGP.pm
+++ b/scripts/Dpkg/OpenPGP.pm
@@ -18,61 +18,43 @@ package Dpkg::OpenPGP;
 use strict;
 use warnings;
 
-use POSIX qw(:sys_wait_h);
-use File::Temp;
-use MIME::Base64;
-
 use Dpkg::Gettext;
 use Dpkg::ErrorHandling;
 use Dpkg::IPC;
-use Dpkg::File;
 use Dpkg::Path qw(find_command);
-use Dpkg::OpenPGP::ErrorCodes;
+use Dpkg::OpenPGP::Backend::GnuPG;
 
 our $VERSION = '0.01';
 
-sub _detect_cmd {
-    my ($cmd, $default) = @_;
-
-    if (! defined $cmd || $cmd eq 'auto') {
-        return find_command($default);
-    } else {
-        return find_command($cmd);
-    }
-}
-
 sub new {
     my ($this, %opts) = @_;
     my $class = ref($this) || $this;
 
-    my $self = {
-        cmdv => _detect_cmd($opts{cmdv}, 'gpgv'),
-        cmd => _detect_cmd($opts{cmd}, 'gpg'),
-    };
+    my $self = {};
     bless $self, $class;
 
-    return $self;
-}
+    my %backend_opts = (
+        cmdv => $opts{cmdv} // 'auto',
+        cmd => $opts{cmd} // 'auto',
+    );
 
-sub _gpg_has_keystore {
-    my $self = shift;
+    $self->{backend} = Dpkg::OpenPGP::Backend::GnuPG->new(%backend_opts);
 
-    return 1 if ($ENV{GNUPGHOME} && -e $ENV{GNUPGHOME}) ||
-                ($ENV{HOME} && -e "$ENV{HOME}/.gnupg");
-    return 0;
+    return $self;
 }
 
 sub can_use_secrets {
     my ($self, $key) = @_;
 
-    return 0 unless $self->{cmd};
+    return 0 unless $self->{backend}->has_backend_cmd();
+
     if ($key->type eq 'keyfile') {
         return 1 if -f $key->handle;
     } elsif ($key->type eq 'keystore') {
         return 1 if -e $key->handle;
     } else {
         # For IDs we need a keystore.
-        return $self->_gpg_has_keystore();
+        return $self->{backend}->has_keystore();
     }
     return 0;
 }
@@ -80,212 +62,37 @@ sub can_use_secrets {
 sub get_trusted_keyrings {
     my $self = shift;
 
-    my @keyrings;
-    if (length $ENV{HOME} and -r "$ENV{HOME}/.gnupg/trustedkeys.gpg") {
-        push @keyrings, "$ENV{HOME}/.gnupg/trustedkeys.gpg";
-    }
-    return @keyrings;
-}
-
-# _pgp_* functions are strictly for applying or removing ASCII armor.
-# See <https://datatracker.ietf.org/doc/html/rfc4880#section-6> for more
-# details.
-#
-# Note that these _pgp_* functions are only necessary while relying on
-# gpgv, and gpgv itself does not verify multiple signatures correctly
-# (see https://bugs.debian.org/1010955).
-
-sub _pgp_dearmor_data {
-    my ($type, $data) = @_;
-
-    # Note that we ignore an incorrect or absent checksum, following the
-    # guidance of
-    # <https://datatracker.ietf.org/doc/draft-ietf-openpgp-crypto-refresh/>.
-    my $armor_regex = qr{
-        -----BEGIN\ PGP\ \Q$type\E-----[\r\t ]*\n
-        (?:[^:]+:\ [^\n]*[\r\t ]*\n)*
-        [\r\t ]*\n
-        ([a-zA-Z0-9/+\n]+={0,2})[\r\t ]*\n
-        (?:=[a-zA-Z0-9/+]{4}[\r\t ]*\n)?
-        -----END\ PGP\ \Q$type\E-----
-    }xm;
-
-    if ($data =~ m/$armor_regex/) {
-        return decode_base64($1);
-    }
-    return;
-}
-
-sub _pgp_armor_checksum {
-    my ($data) = @_;
-
-    # From the upcoming revision to RFC 4880
-    # <https://datatracker.ietf.org/doc/draft-ietf-openpgp-crypto-refresh/>.
-    #
-    # The resulting three-octet-wide value then gets base64-encoded into
-    # four base64 ASCII characters.
-
-    my $CRC24_INIT = 0xB704CE;
-    my $CRC24_GENERATOR = 0x864CFB;
-
-    my @bytes = unpack 'C*', $data;
-    my $crc = $CRC24_INIT;
-    for my $b (@bytes) {
-        $crc ^= ($b << 16);
-        for (1 .. 8) {
-            $crc <<= 1;
-            if ($crc & 0x1000000) {
-                # Clear bit 25 to avoid overflow.
-                $crc &= 0xffffff;
-                $crc ^= $CRC24_GENERATOR;
-            }
-        }
-    }
-    my $sum = pack 'CCC', ($crc >> 16) & 0xff, ($crc >> 8) & 0xff, $crc & 0xff;
-    return encode_base64($sum, q{});
-}
-
-sub _pgp_armor_data {
-    my ($type, $data) = @_;
-
-    my $out = encode_base64($data, q{}) =~ s/(.{1,64})/$1\n/gr;
-    chomp $out;
-    my $crc = _pgp_armor_checksum($data);
-    my $armor = <<~"ARMOR";
-        -----BEGIN PGP $type-----
-
-        $out
-        =$crc
-        -----END PGP $type-----
-        ARMOR
-    return $armor;
+    return $self->{backend}->get_trusted_keyrings();
 }
 
 sub armor {
     my ($self, $type, $in, $out) = @_;
 
-    my $raw_data = file_slurp($in);
-    my $data = _pgp_dearmor_data($type, $raw_data) // $raw_data;
-    my $armor = _pgp_armor_data($type, $data);
-    return OPENPGP_BAD_DATA unless defined $armor;
-    file_dump($out, $armor);
-
-    return OPENPGP_OK;
+    return $self->{backend}->armor($type, $in, $out);
 }
 
 sub dearmor {
     my ($self, $type, $in, $out) = @_;
 
-    my $armor = file_slurp($in);
-    my $data = _pgp_dearmor_data($type, $armor);
-    return OPENPGP_BAD_DATA unless defined $data;
-    file_dump($out, $data);
-
-    return OPENPGP_OK;
-}
-
-sub _gpg_exec
-{
-    my ($self, @exec) = @_;
-
-    my ($stdout, $stderr);
-    spawn(exec => \@exec, wait_child => 1, nocheck => 1, timeout => 10,
-          to_string => \$stdout, error_to_string => \$stderr);
-    if (WIFEXITED($?)) {
-        my $status = WEXITSTATUS($?);
-        print { *STDERR } "$stdout$stderr" if $status;
-        return $status;
-    } else {
-        subprocerr("@exec");
-    }
-}
-
-sub _gpg_options_weak_digests {
-    my @gpg_weak_digests = map {
-        (qw(--weak-digest), $_)
-    } qw(SHA1 RIPEMD160);
-
-    return @gpg_weak_digests;
-}
-
-sub _gpg_verify {
-    my ($self, $signeddata, $sig, $data, @certs) = @_;
-
-    return OPENPGP_MISSING_CMD if ! $self->{cmdv} || ! $self->{cmd};
-
-    my $gpg_home = File::Temp->newdir('dpkg-gpg-verify.XXXXXXXX', TMPDIR => 1);
-
-    my @exec;
-    if ($self->{cmdv}) {
-        push @exec, $self->{cmdv};
-    } else {
-        push @exec, $self->{cmd};
-        push @exec, qw(--no-options --no-default-keyring --batch --quiet);
-    }
-    push @exec, _gpg_options_weak_digests();
-    push @exec, '--homedir', $gpg_home;
-    foreach my $cert (@certs) {
-        my $certring = File::Temp->new(UNLINK => 1, SUFFIX => '.pgp');
-        my $rc = $self->dearmor('PUBLIC KEY BLOCK', $cert, $certring);
-        $certring = $cert if $rc;
-        push @exec, '--keyring', $certring;
-    }
-    push @exec, '--output', $data if defined $data;
-    if (! $self->{cmdv}) {
-        push @exec, '--verify';
-    }
-    push @exec, $sig if defined $sig;
-    push @exec, $signeddata;
-
-    my $status = $self->_gpg_exec(@exec);
-    return OPENPGP_NO_SIG if $status;
-    return OPENPGP_OK;
+    return $self->{backend}->dearmor($type, $in, $out);
 }
 
 sub inline_verify {
     my ($self, $inlinesigned, $data, @certs) = @_;
 
-    return $self->_gpg_verify($inlinesigned, undef, $data, @certs);
+    return $self->{backend}->inline_verify($inlinesigned, $data, @certs);
 }
 
 sub verify {
     my ($self, $data, $sig, @certs) = @_;
 
-    return $self->_gpg_verify($data, $sig, undef, @certs);
-}
-
-sub _gpg_inline_sign {
-    my ($self, $data, $inlinesigned, $key) = @_;
-
-    return OPENPGP_MISSING_CMD if ! $self->{cmd};
-
-    my @exec = ($self->{cmd});
-    push @exec, _gpg_options_weak_digests();
-    push @exec, qw(--utf8-strings --textmode --armor);
-    if ($key->type eq 'keyfile') {
-        # Promote the keyfile keyhandle to a keystore, this way we share the
-        # same gpg-agent and can get any password cached.
-        my $gpg_home = File::Temp->newdir('dpkg-sign.XXXXXXXX', TMPDIR => 1);
-
-        push @exec, '--homedir', $gpg_home;
-        $self->_gpg_exec(@exec, qw(--quiet --no-tty --batch --import), 
$key->handle);
-        $key->set('keystore', $gpg_home);
-    } elsif ($key->type eq 'keystore') {
-        push @exec, '--homedir', $key->handle;
-    } else {
-        push @exec, '--local-user', $key->handle;
-    }
-    push @exec, '--output', $inlinesigned;
-
-    my $rc = $self->_gpg_exec(@exec, '--clearsign', $data);
-    return OPENPGP_KEY_CANNOT_SIGN if $rc;
-    return OPENPGP_OK;
+    return $self->{backend}->verify($data, $sig, @certs);
 }
 
 sub inline_sign {
     my ($self, $data, $inlinesigned, $key) = @_;
 
-    return $self->_gpg_inline_sign($data, $inlinesigned, $key);
+    return $self->{backend}->inline_sign($data, $inlinesigned, $key);
 }
 
 1;
diff --git a/scripts/Dpkg/OpenPGP/Backend.pm b/scripts/Dpkg/OpenPGP/Backend.pm
new file mode 100644
index 000000000..39d1cf475
--- /dev/null
+++ b/scripts/Dpkg/OpenPGP/Backend.pm
@@ -0,0 +1,109 @@
+# Copyright © 2017, 2022 Guillem Jover <[email protected]>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+package Dpkg::OpenPGP::Backend;
+
+use strict;
+use warnings;
+
+our $VERSION = '0.01';
+
+use List::Util qw(first);
+
+use Dpkg::Path qw(find_command);
+use Dpkg::OpenPGP::ErrorCodes;
+
+sub DEFAULT_CMDV {
+    return [];
+}
+
+sub DEFAULT_CMD {
+    return [];
+}
+
+sub _detect_cmd {
+    my ($cmd, $default) = @_;
+
+    if (! defined $cmd || $cmd eq 'auto') {
+        return first { find_command($_) } @{$default};
+    } else {
+        return find_command($cmd);
+    }
+}
+
+sub new {
+    my ($this, %opts) = @_;
+    my $class = ref($this) || $this;
+
+    my $self = {
+        strict_verify => $opts{strict_verify} // 1,
+    };
+    bless $self, $class;
+
+    $self->{cmdv} = _detect_cmd($opts{cmdv}, $self->DEFAULT_CMDV());
+    $self->{cmd} = _detect_cmd($opts{cmd}, $self->DEFAULT_CMD());
+
+    return $self;
+}
+
+sub has_backend_cmd {
+    my $self = shift;
+
+    return defined $self->{cmd};
+}
+
+sub has_keystore {
+    my $self = shift;
+
+    return 0;
+}
+
+sub get_trusted_keyrings {
+    my $self = shift;
+
+    return ();
+}
+
+sub armor {
+    my ($self, $type, $in, $out) = @_;
+
+    return OPENPGP_UNSUPPORTED_SUBCMD;
+}
+
+sub dearmor {
+    my ($self, $type, $in, $out) = @_;
+
+    return OPENPGP_UNSUPPORTED_SUBCMD;
+}
+
+sub inline_verify {
+    my ($self, $inlinesigned, $data, @certs) = @_;
+
+    return OPENPGP_UNSUPPORTED_SUBCMD;
+}
+
+sub verify {
+    my ($self, $data, $sig, @certs) = @_;
+
+    return OPENPGP_UNSUPPORTED_SUBCMD;
+}
+
+sub inline_sign {
+    my ($self, $data, $inlinesigned, $key) = @_;
+
+    return OPENPGP_UNSUPPORTED_SUBCMD;
+}
+
+1;
diff --git a/scripts/Dpkg/OpenPGP.pm b/scripts/Dpkg/OpenPGP/Backend/GnuPG.pm
similarity index 85%
copy from scripts/Dpkg/OpenPGP.pm
copy to scripts/Dpkg/OpenPGP/Backend/GnuPG.pm
index 005d6d7eb..e60826575 100644
--- a/scripts/Dpkg/OpenPGP.pm
+++ b/scripts/Dpkg/OpenPGP/Backend/GnuPG.pm
@@ -1,4 +1,4 @@
-# Copyright © 2017 Guillem Jover <[email protected]>
+# Copyright © 207, 2022 Guillem Jover <[email protected]>
 #
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -13,48 +13,34 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
-package Dpkg::OpenPGP;
+package Dpkg::OpenPGP::Backend::GnuPG;
 
 use strict;
 use warnings;
 
+our $VERSION = '0.01';
+
 use POSIX qw(:sys_wait_h);
 use File::Temp;
 use MIME::Base64;
 
-use Dpkg::Gettext;
 use Dpkg::ErrorHandling;
 use Dpkg::IPC;
 use Dpkg::File;
 use Dpkg::Path qw(find_command);
 use Dpkg::OpenPGP::ErrorCodes;
 
-our $VERSION = '0.01';
-
-sub _detect_cmd {
-    my ($cmd, $default) = @_;
+use parent qw(Dpkg::OpenPGP::Backend);
 
-    if (! defined $cmd || $cmd eq 'auto') {
-        return find_command($default);
-    } else {
-        return find_command($cmd);
-    }
+sub DEFAULT_CMDV {
+    return [ qw(gpgv) ];
 }
 
-sub new {
-    my ($this, %opts) = @_;
-    my $class = ref($this) || $this;
-
-    my $self = {
-        cmdv => _detect_cmd($opts{cmdv}, 'gpgv'),
-        cmd => _detect_cmd($opts{cmd}, 'gpg'),
-    };
-    bless $self, $class;
-
-    return $self;
+sub DEFAULT_CMD {
+    return [ qw(gpg) ];
 }
 
-sub _gpg_has_keystore {
+sub has_keystore {
     my $self = shift;
 
     return 1 if ($ENV{GNUPGHOME} && -e $ENV{GNUPGHOME}) ||
@@ -62,21 +48,6 @@ sub _gpg_has_keystore {
     return 0;
 }
 
-sub can_use_secrets {
-    my ($self, $key) = @_;
-
-    return 0 unless $self->{cmd};
-    if ($key->type eq 'keyfile') {
-        return 1 if -f $key->handle;
-    } elsif ($key->type eq 'keystore') {
-        return 1 if -e $key->handle;
-    } else {
-        # For IDs we need a keystore.
-        return $self->_gpg_has_keystore();
-    }
-    return 0;
-}
-
 sub get_trusted_keyrings {
     my $self = shift;
 
@@ -237,8 +208,8 @@ sub _gpg_verify {
     push @exec, $sig if defined $sig;
     push @exec, $signeddata;
 
-    my $status = $self->_gpg_exec(@exec);
-    return OPENPGP_NO_SIG if $status;
+    my $rc = $self->_gpg_exec(@exec);
+    return OPENPGP_NO_SIG if $rc;
     return OPENPGP_OK;
 }
 
@@ -254,7 +225,7 @@ sub verify {
     return $self->_gpg_verify($data, $sig, undef, @certs);
 }
 
-sub _gpg_inline_sign {
+sub inline_sign {
     my ($self, $data, $inlinesigned, $key) = @_;
 
     return OPENPGP_MISSING_CMD if ! $self->{cmd};
@@ -282,10 +253,4 @@ sub _gpg_inline_sign {
     return OPENPGP_OK;
 }
 
-sub inline_sign {
-    my ($self, $data, $inlinesigned, $key) = @_;
-
-    return $self->_gpg_inline_sign($data, $inlinesigned, $key);
-}
-
 1;
diff --git a/scripts/Makefile.am b/scripts/Makefile.am
index a6364f677..4375e23f7 100644
--- a/scripts/Makefile.am
+++ b/scripts/Makefile.am
@@ -106,6 +106,8 @@ nobase_dist_perllib_DATA = \
        Dpkg/IPC.pm \
        Dpkg/Lock.pm \
        Dpkg/OpenPGP.pm \
+       Dpkg/OpenPGP/Backend.pm \
+       Dpkg/OpenPGP/Backend/GnuPG.pm \
        Dpkg/OpenPGP/ErrorCodes.pm \
        Dpkg/OpenPGP/KeyHandle.pm \
        Dpkg/Package.pm \

-- 
Dpkg.Org's dpkg

Reply via email to