sbotools2

Maintenance fork of the original sbotools version 2
git clone git://git.server.ky/slackcoder/sbotools2
Log | Files | Refs | README

Cryptography.pm (4557B)


      1 package SBO::Lib::Cryptography;
      2 
      3 use 5.016;
      4 use strict;
      5 use warnings;
      6 
      7 our $VERSION = '2.9.0';
      8 
      9 use Cwd;
     10 use File::Temp "tempdir";
     11 use IPC::Open3;
     12 
     13 use Exporter 'import';
     14 
     15 # Minimal definitions of some GPG raw output messages.
     16 use constant {
     17   BADSIG => 'bad signature',
     18   EXPSIG => 'signature expired',
     19   EXPKEYSIG => 'signed by expired key',
     20   ERRSIG => 'signature verification not possible',
     21   GOODSIG => 'good signature',
     22   REVKEYSIG => 'good signature by revoked public key',
     23   NO_PUBKEY => 'public key unavailable',
     24   VALIDSIG => 'valid signature',
     25   # An unknown message type.
     26   UNKNOWN => 'unknown',
     27 };
     28 
     29 our @EXPORT_OK = qw{
     30   import_gpg_key
     31   parse_gpg_output
     32   verify_gpg_signed_file
     33 
     34   BADSIG
     35   EXPSIG
     36   EXPKEYSIG
     37   ERRSIG
     38   GOODSIG
     39   REVKEYSIG
     40   NO_PUBKEY
     41   VALIDSIG
     42   UNKNOWN
     43 };
     44 
     45 our %EXPORT_TAGS = (
     46   all => \@EXPORT_OK,
     47 );
     48 
     49 =pod
     50 
     51 =encoding UTF-8
     52 
     53 =cut
     54 
     55 # Messages on Signature status which should only turn up once.
     56 our %is_signature_status = (
     57   &BADSIG => 1,
     58   &ERRSIG => 1,
     59   &EXPSIG => 1,
     60   &EXPKEYSIG => 1,
     61   &GOODSIG => 1,
     62   &REVKEYSIG => 1,
     63 );
     64 
     65 =head2
     66 
     67   parse_gpg_output(@output, $key_id);
     68 
     69 =cut
     70 
     71 sub parse_gpg_output {
     72   my $output = shift;
     73   my $key_id = shift;
     74 
     75 
     76   my $line;
     77   my $status = '';
     78 
     79   foreach $line (@$output) {
     80     my $msg = parse_gpg_line($line, $key_id);
     81 
     82     if (exists($is_signature_status{$msg})) {
     83       # Only one signature expected.
     84       if ($status ne '') {
     85         return UNKNOWN;
     86       }
     87 
     88       $status = $msg;
     89     }
     90 
     91     if ($msg eq NO_PUBKEY) {
     92       return NO_PUBKEY;
     93     } elsif ($msg eq VALIDSIG) {
     94       # VALIDSIG contains the full hex key ID to be certain it is signed by the
     95       # right key. information can be found in 'DETAILS' in the gnupg2
     96       # documentation folder.
     97       return $status;
     98     }
     99   }
    100 
    101   return UNKNOWN;
    102 }
    103 
    104 sub parse_gpg_line {
    105   my $line = shift;
    106   my $key_id = shift;
    107 
    108   if ($line =~ /^\[GNUPG\:] BADSIG/) {
    109     return BADSIG;
    110   } elsif ($line =~ /^\[GNUPG\:] EXPSIG/) {
    111     return EXPSIG;
    112   } elsif ($line =~ /^\[GNUPG\:] EXPKEYSIG/) {
    113     return EXPKEYSIG;
    114   } elsif ($line =~ /^\[GNUPG\:] ERRSIG/) {
    115     return ERRSIG;
    116   } elsif ($line =~ /^\[GNUPG\:] GOODSIG/) {
    117     return GOODSIG;
    118   } elsif ($line =~ /^\[GNUPG\:] REVKEYSIG/) {
    119     return REVKEYSIG;
    120   } elsif ($line =~ /^\[GNUPG\:] NO_PUBKEY/) {
    121     return NO_PUBKEY;
    122   } elsif ($line =~ /^\[GNUPG\:] VALIDSIG $key_id/) {
    123     return VALIDSIG;
    124   } else {
    125     return UNKNOWN;
    126   }
    127 }
    128 
    129 =head2 import_gpg_key
    130 
    131   import_gpg_key($key);
    132 
    133 C<import_gpg_key()> will import the key into the systems keychain.  An error will
    134 be reported if the configured key fingerprint does not match the imported one.
    135 
    136 =cut
    137 
    138 sub import_gpg_key {
    139   script_error('import_gpg_key requires two arguments.') unless @_ == 2;
    140 
    141   my $key = shift;
    142   my $key_id = shift;
    143 
    144   my $key_source;
    145   if ($key =~ m!^http://! || $key =~ m!^https://!) {
    146     open3(undef, $key_source, ">&STDERR", "wget", $key, "-O", "-") || die("could not download key from $key");
    147   } else {
    148     open($key_source, "<", $key) || die("could not read '$key': $!");
    149   }
    150 
    151   my $old = $ENV{'GNUPGHOME'};
    152 
    153   $ENV{'GNUPGHOME'} = tempdir(CLEANUP => 1);
    154 
    155   my $gpg_cmd;
    156   open3($gpg_cmd, undef, undef, "gpg", "--batch", "--yes", "--import", "-") || die("could not import key: $!\n");
    157 
    158   while (my $line = <$key_source>) {
    159     print($gpg_cmd $line);
    160   }
    161 
    162   close($gpg_cmd);
    163   close($key_source);
    164 
    165   sleep(1);
    166 
    167   if (system(">/dev/null gpg --list-keys $key_id")) {
    168     die("GPG key '$key_id' not found.  Confirm the correct key is configured or is being imported.\n");
    169   }
    170 
    171   my $gpg_export;
    172   open($gpg_export, "-|", "gpg", "--export", $key_id) || die("could not export key: $!\n");
    173 
    174   $ENV{'GNUPGHOME'} = $old;
    175 
    176   my $gpg_import;
    177   open3($gpg_import, ">&STDOUT", ">&STDOUT", "gpg", "--batch", "--yes", "--import", "-") || die("could not import key: $!\n");
    178 
    179   while (my $line = <$gpg_export>) {
    180     print($gpg_import $line);
    181   }
    182 
    183   close($gpg_export);
    184   close($gpg_import);
    185 
    186   sleep(1);
    187 
    188   print("key imported\n");
    189 }
    190 
    191 =head2 verify_gpg_signed_file
    192 
    193   verify_gpg_signed_file($file_path, $key_id);
    194 
    195 C<verify_gpg_signed_file()> verifies the C<file_path> is signed by C<key_id>. 
    196 
    197 =cut
    198 
    199 sub verify_gpg_signed_file {
    200   script_error('verify_gpg_signed_file requires two arguments.') unless @_ == 2;
    201   
    202   my $file_path = shift;
    203   my $key_id = shift;
    204 
    205   my @output;
    206   open3(undef, my $std_out, undef, "gpg", "--status-fd=1", "--verify", $file_path) or die("dead");
    207   while (my $line = <$std_out>) {
    208     push(@output, $line);
    209   }
    210   close($std_out);
    211 
    212   return parse_gpg_output(\@output, $key_id);
    213 }