aboutsummaryrefslogtreecommitdiff
path: root/SBO-Lib/lib/SBO/Lib/Cryptography.pm
diff options
context:
space:
mode:
Diffstat (limited to 'SBO-Lib/lib/SBO/Lib/Cryptography.pm')
-rw-r--r--SBO-Lib/lib/SBO/Lib/Cryptography.pm159
1 files changed, 159 insertions, 0 deletions
diff --git a/SBO-Lib/lib/SBO/Lib/Cryptography.pm b/SBO-Lib/lib/SBO/Lib/Cryptography.pm
new file mode 100644
index 0000000..4fca277
--- /dev/null
+++ b/SBO-Lib/lib/SBO/Lib/Cryptography.pm
@@ -0,0 +1,159 @@
+package SBO::Lib::Cryptography;
+
+use 5.016;
+use strict;
+use warnings;
+
+our $VERSION = '2.7.2';
+
+use Cwd;
+use File::Temp "tempdir";
+use IPC::Open3;
+
+use constant {
+ BAD_SIGNATURE => 'bad signature',
+ EXPIRED_KEY => 'expired key',
+ VALID_SIGNATURE => 'good signature',
+};
+
+use Exporter 'import';
+
+our @EXPORT_OK = qw{
+ has_valid_gpg_signature
+ import_gpg_key
+ verify_gpg_signed_file
+
+ BAD_SIGNATURE
+ EXPIRED_KEY
+ VALID_SIGNATURE
+};
+
+our %EXPORT_TAGS = (
+ all => \@EXPORT_OK,
+);
+
+=pod
+
+=encoding UTF-8
+
+=head2
+
+ has_valid_gpg_signature(@output, $key_id);
+
+C<has_valid_gpg_siganture()> validates whether the captured gpg status output
+contains a good signature for the given GPG key.
+
+=cut
+
+sub has_valid_gpg_signature {
+ my $output = shift;
+ my $key_id = shift;
+
+ # VALIDSIG contains the hex key ID, GOODSIG is required for certainty. More
+ # information can be found in 'DETAILS' in the gnupg2 documentation folder.
+ my $is_good_sig = 0;
+ my $is_valid_sig = 0;
+
+ my $line;
+ foreach $line (@$output) {
+ if ($line =~ /^\[GNUPG\:] VALIDSIG $key_id/) {
+ $is_valid_sig = 1;
+ } elsif ($line =~ /^\[GNUPG\:] GOODSIG /) {
+ $is_good_sig = 1;
+ }
+
+ if ($is_good_sig && $is_valid_sig) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+=head2 import_gpg_key
+
+ import_gpg_key($key);
+
+C<import_gpg_key()> will import the key into the systems keychain. An error will
+be reported if the configured key fingerprint does not match the imported one.
+
+=cut
+
+sub import_gpg_key {
+ script_error('import_gpg_key requires two arguments.') unless @_ == 2;
+
+ my $key = shift;
+ my $key_id = shift;
+
+ my $key_source;
+ if ($key =~ m!^http://! || $key =~ m!^https://!) {
+ open3(undef, $key_source, ">&STDERR", "wget", $key, "-O", "-") || die("could not download key from $key");
+ } else {
+ open($key_source, "<", $key) || die("could not read '$key': $!");
+ }
+
+ my $old = $ENV{'GNUPGHOME'};
+
+ $ENV{'GNUPGHOME'} = tempdir(CLEANUP => 1);
+
+ my $gpg_cmd;
+ open3($gpg_cmd, undef, undef, "gpg", "--batch", "--yes", "--import", "-") || die("could not import key: $!\n");
+
+ while (my $line = <$key_source>) {
+ print($gpg_cmd $line);
+ }
+
+ close($gpg_cmd);
+ close($key_source);
+
+ sleep(1);
+
+ if (system(">/dev/null gpg --list-keys $key_id")) {
+ die("GPG key '$key_id' not found. Confirm the correct key is configured or is being imported.\n");
+ }
+
+ my $gpg_export;
+ open($gpg_export, "-|", "gpg", "--export", $key_id) || die("could not export key: $!\n");
+
+ $ENV{'GNUPGHOME'} = $old;
+
+ my $gpg_import;
+ open3($gpg_import, ">&STDOUT", ">&STDOUT", "gpg", "--batch", "--yes", "--import", "-") || die("could not import key: $!\n");
+
+ while (my $line = <$gpg_export>) {
+ print($gpg_import $line);
+ }
+
+ close($gpg_export);
+ close($gpg_import);
+
+ print("key imported\n");
+}
+
+=head2 verify_gpg_signed_file
+
+ verify_gpg_signed_file($file_path, $key_id);
+
+C<verify_gpg_signed_file()> verifies the C<file_path> is signed by C<key_id>.
+
+=cut
+
+sub verify_gpg_signed_file {
+ script_error('verify_gpg_signed_file requires two arguments.') unless @_ == 2;
+
+ my $file_path = shift;
+ my $key_id = shift;
+
+ my @output;
+ open3(undef, my $std_out, undef, "gpg", "--status-fd=1", "--verify", $file_path) or die("dead");
+ while (my $line = <$std_out>) {
+ push(@output, $line);
+ }
+ close($std_out);
+
+ if (! has_valid_gpg_signature(\@output, $key_id)) {
+ return BAD_SIGNATURE;
+ }
+
+ return VALID_SIGNATURE;
+}