[Rt-commit] rt branch, 4.4/provide-more-public-key-details, created. rt-4.4.4-158-ga6a6c7eedd

Dianne Skoll dianne at bestpractical.com
Thu Nov 12 17:07:59 EST 2020


The branch, 4.4/provide-more-public-key-details has been created
        at  a6a6c7eedd7207c2f26ffa458e1e2fdd81118708 (commit)

- Log -----------------------------------------------------------------
commit a468f47072a661761b5dfb166aa901468890bc3c
Author: Dianne Skoll <dianne at bestpractical.com>
Date:   Thu Nov 12 10:08:23 2020 -0500

    Add AgorithmName to info returned by ParseKeysInfo

diff --git a/lib/RT/Crypt/GnuPG.pm b/lib/RT/Crypt/GnuPG.pm
index 06569ad9db..07ceff8fce 100644
--- a/lib/RT/Crypt/GnuPG.pm
+++ b/lib/RT/Crypt/GnuPG.pm
@@ -1711,6 +1711,7 @@ sub ParseKeysInfo {
                 Empty Empty Capabilities Other
             ) } = split /:/, $line, 12;
 
+            $info{AlgorithmName} = $self->PubkeyAlgorithmToName($info{Algorithm}) if defined($info{Algorithm});
             # workaround gnupg's wierd behaviour, --list-keys command report calculated trust levels
             # for any model except 'always', so you can change models and see changes, but not for 'always'
             # we try to handle it in a simple way - we set ultimate trust for any key with trust
@@ -1738,6 +1739,7 @@ sub ParseKeysInfo {
                 Created Expire Empty OwnerTrustChar
                 Empty Empty Capabilities Other
             ) } = split /:/, $line, 12;
+            $info{AlgorithmName} = $self->PubkeyAlgorithmToName($info{Algorithm}) if defined($info{Algorithm});
             @info{qw(OwnerTrust OwnerTrustTerse OwnerTrustLevel)} = 
                 _ConvertTrustChar( $info{'OwnerTrustChar'} );
             $info{ $_ } = $self->ParseDate( $info{ $_ } )
@@ -1957,6 +1959,23 @@ sub _make_gpg_handles {
     return ($handles, \%handle_map);
 }
 
+# Given a PGP public-key algorithm number, return the algorithm name.
+# See https://tools.ietf.org/html/rfc4880#section-9
+sub PubkeyAlgorithmToName
+{
+    my ($self, $alg) = @_;
+    return 'RSA'                         if ($alg == 1);
+    return 'RSA Encrypt-Only'            if ($alg == 2);
+    return 'RSA Sign-Only'               if ($alg == 3);
+    return 'Elgamal Encrypt-Ony'         if ($alg == 16);
+    return 'DSA'                         if ($alg == 17);
+    return 'Reserved for EC'             if ($alg == 18);
+    return 'Reserved for ECDSA'          if ($alg == 19);
+    return 'Reserved (formerly Elgamal)' if ($alg == 20);
+    return 'Reserved (DH)'               if ($alg == 21);
+    return undef;
+}
+
 RT::Base->_ImportOverlays();
 
 1;

commit a6a6c7eedd7207c2f26ffa458e1e2fdd81118708
Author: Dianne Skoll <dianne at bestpractical.com>
Date:   Thu Nov 12 17:06:48 2020 -0500

    For GnuPG, add a tooltip with additional info about the signature, and add ability to download public key.

diff --git a/lib/RT/Crypt/GnuPG.pm b/lib/RT/Crypt/GnuPG.pm
index 07ceff8fce..4ae1261121 100644
--- a/lib/RT/Crypt/GnuPG.pm
+++ b/lib/RT/Crypt/GnuPG.pm
@@ -1489,6 +1489,15 @@ sub ParseStatus {
 
             foreach my $line ( @status[ $i .. $#status ] ) {
                 next unless $line =~ /^VALIDSIG\s+(.*)/;
+                # Fingerprint = key fingerprint in hex
+                # CreationDate = key creation date (YYYY-MM-DD)
+                # Timestamp = signature creation time (seconds from UNIX epoch)
+                # ExpireTimestamp = signature expiration time (since epoch) or 0 for "never expires"
+                # Version = signature version straight from the packet
+                # PubkeyAlgo = Public key algorithm (https://tools.ietf.org/html/rfc4880#section-9.1)
+                # HashAlgo = Hash algorithm (https://tools.ietf.org/html/rfc4880#section-9.4)
+                # Class = Signature type (https://tools.ietf.org/html/rfc4880#section-5.2.1)
+                # PKFingerprint = Primary Key Fingerprint
                 @res{ qw(
                     Fingerprint
                     CreationDate
@@ -1502,6 +1511,8 @@ sub ParseStatus {
                     PKFingerprint
                     Other
                 ) } = split /\s+/, $1, 10;
+                $res{HashAlgoName} = $self->HashAlgorithmToName($res{HashAlgo}) if defined($res{HashAlgo});
+                $res{PubkeyAlgoName} = $self->PubkeyAlgorithmToName($res{PubkeyAlgo}) if defined($res{PubkeyAlgo});
                 last;
             }
             push @res, \%res;
@@ -1959,8 +1970,24 @@ sub _make_gpg_handles {
     return ($handles, \%handle_map);
 }
 
+# Gigne a PGP hash algorithm number, return the algorithm name.
+# See https://tools.ietf.org/html/rfc4880#section-9.4
+sub HashAlgorithmToName
+{
+    my ($self, $alg) = @_;
+    return 'MD5'           if ($alg == 1);
+    return 'SHA-1'         if ($alg == 2);
+    return 'RIPE-MD/160'   if ($alg == 3);
+    return 'Reserved'      if ($alg >= 4 && $alg <= 7);
+    return 'SHA256'        if ($alg == 8);
+    return 'SHA384'        if ($alg == 9);
+    return 'SHA512'        if ($alg == 10);
+    return 'SHA224'        if ($alg == 11);
+    return undef;
+}
+
 # Given a PGP public-key algorithm number, return the algorithm name.
-# See https://tools.ietf.org/html/rfc4880#section-9
+# See https://tools.ietf.org/html/rfc4880#section-9.1
 sub PubkeyAlgorithmToName
 {
     my ($self, $alg) = @_;
@@ -1976,6 +2003,25 @@ sub PubkeyAlgorithmToName
     return undef;
 }
 
+# Given a public-key fingerprint, return the public key (in PEM format)
+# if we have it, undef if we do not.
+sub GetPubkey
+{
+    my ($self, $fingerprint) = @_;
+
+    # Sanity-check the fingerprint, which must be a string of
+    # hex digits
+    if ($fingerprint =~ /[^0-9a-fA-F]/) {
+        return undef;
+    }
+
+    my @pubkey;
+    my %res = $self->CallGnuPG(
+        Command     => 'export_keys',
+        CommandArgs => $fingerprint,
+        Output      => \@pubkey);
+    return join('', @pubkey);
+}
 RT::Base->_ImportOverlays();
 
 1;
diff --git a/share/html/Crypt/GetGPGPubkey.html b/share/html/Crypt/GetGPGPubkey.html
new file mode 100644
index 0000000000..eb10caa0e4
--- /dev/null
+++ b/share/html/Crypt/GetGPGPubkey.html
@@ -0,0 +1,77 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2020 Best Practical Solutions, LLC
+%#                                          <sales at bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work 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, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
+%#
+%#
+%# CONTRIBUTION SUBMISSION POLICY:
+%#
+%# (The following paragraph is not intended to limit the rights granted
+%# to you to modify and distribute this software under the terms of
+%# the GNU General Public License and is only of importance to you if
+%# you choose to contribute your changes and enhancements to the
+%# community by submitting them to Best Practical Solutions, LLC.)
+%#
+%# By intentionally submitting any modifications, corrections or
+%# derivatives to this work, or any other work intended for use with
+%# Request Tracker, to Best Practical Solutions, LLC, you confirm that
+%# you are the copyright holder for those contributions and you grant
+%# Best Practical Solutions,  LLC a nonexclusive, worldwide, irrevocable,
+%# royalty-free, perpetual, license to use, copy, create derivative
+%# works based on those contributions, and sublicense and distribute
+%# those contributions and any derivatives thereof.
+%#
+%# END BPS TAGGED BLOCK }}}
+<& /Elements/Header, 
+   Title => $title
+ &>
+<& /Elements/Tabs &>
+
+<& /Elements/ListActions, actions => \@results &>
+
+<%ARGS>
+$Fingerprint => undef
+$title => loc('Download GnuPG Public Key')
+</%ARGS>
+
+<%INIT>
+my @results;
+if (!$Fingerprint) {
+    push(@results, loc('Fingerprint must be supplied to download a public key.'));
+} else {
+    my $key = RT::Crypt::GnuPG->GetPubkey($Fingerprint);
+    if (!$key) {
+        push(@results, loc('Could not find GnuPG public key with fingerprint [_1].'));
+    } else {
+        $r->content_type('application/pgp-keys');
+        $r->header_out('Content-Disposition' => "attachment; filename=\"$Fingerprint.pub\"");
+        $m->out($key);
+        $m->flush_buffer;
+        $m->abort();
+    }
+}
+
+</%INIT>
diff --git a/share/html/Elements/CryptStatus b/share/html/Elements/CryptStatus
index e6c0e7ba8f..f40f281767 100644
--- a/share/html/Elements/CryptStatus
+++ b/share/html/Elements/CryptStatus
@@ -71,6 +71,44 @@ foreach ( $Message->SplitHeaders ) {
 
 return unless @runs or $needs_unsigned_warning;
 
+# Returns string representing a date in UNIX timestamp format
+sub DisplayDate {
+    my ($ts) = @_;
+    my $date = RT::Date->new($session{'CurrentUser'});
+    $date->Unix($ts);
+    return $date->AsString();
+}
+
+# Generate a little tooltip with additional info about a signature
+sub VerifyTooltip {
+    my ($line) = @_;
+    my $tooltip = '';
+    $tooltip .= "\n" . loc('Fingerprint:') . ' ' . $line->{Fingerprint} if $line->{Fingerprint};
+    $tooltip .= "\n" . loc('Signature Created:') . ' ' . DisplayDate($line->{Timestamp}) if $line->{Timestamp};
+    $tooltip .= "\n" . loc('Key Expires:') . ' ';
+    if ($line->{ExpireTimestamp}) {
+        $tooltip .= DisplayDate($line->{Timestamp});
+    } else {
+        $tooltip .= loc('Never');
+    }
+    $tooltip .= "\n" . loc('Public Key Algorithm:') . ' ' . $line->{PubkeyAlgoName} if $line->{PubkeyAlgoName};
+    $tooltip .= "\n" . loc('Hash Algorithm:') . ' ' . $line->{HashAlgoName} if $line->{HashAlgoName};
+    $tooltip =~ s/^\s+//;
+    return $tooltip;
+}
+
+# Generate a download link for the public key
+sub KeyDownloadLink {
+    my ($protocol, $line) = @_;
+    my $txt = '';
+    if ($protocol eq 'GnuPG') {
+        if ($line->{Fingerprint} && $line->{Fingerprint} !~ /[^0-9A-F]/i) {
+            $txt = '<a href="' . RT->Config->Get('WebPath') . '/Crypt/GetGPGPubkey.html?Fingerprint=' . $line->{Fingerprint} . '">' . loc(' (Download Public Key)') . '</a>';
+        }
+    }
+    return $txt;
+}
+
 my $reverify_cb = sub {
     my $top = shift;
 
@@ -170,7 +208,7 @@ foreach my $run ( @runs ) {
             push @messages, {
                 Tag     => $protocol,
                 Classes => ['verify', lc $line->{Status}, 'trust-'.($line->{Trust} || 'UNKNOWN')],
-                Value   => $m->interp->apply_escapes( loc( $line->{'Message'} ), 'h'),
+                Value   => '<span title="' . $m->interp->apply_escapes(VerifyTooltip($line)) . '">' . $m->interp->apply_escapes( loc( $line->{'Message'} ), 'h') . '</span>' . KeyDownloadLink($protocol, $line),
             };
         }
         else {

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


More information about the rt-commit mailing list