370 lines
11 KiB
Batchfile
370 lines
11 KiB
Batchfile
@rem = '--*-Perl-*--
|
|
@set "ErrorLevel="
|
|
@if "%OS%" == "Windows_NT" @goto WinNT
|
|
@perl -x -S "%0" %1 %2 %3 %4 %5 %6 %7 %8 %9
|
|
@set ErrorLevel=%ErrorLevel%
|
|
@goto endofperl
|
|
:WinNT
|
|
@perl -x -S %0 %*
|
|
@set ErrorLevel=%ErrorLevel%
|
|
@if NOT "%COMSPEC%" == "%SystemRoot%\system32\cmd.exe" @goto endofperl
|
|
@if %ErrorLevel% == 9009 @echo You do not have Perl in your PATH.
|
|
@goto endofperl
|
|
@rem ';
|
|
#!/usr/bin/perl -w
|
|
#line 30
|
|
use strict;
|
|
|
|
use lib 'lib';
|
|
|
|
use Term::ReadKey;
|
|
use Getopt::Long;
|
|
use Crypt::OpenPGP;
|
|
use Crypt::OpenPGP::KeyRing;
|
|
|
|
my %opts;
|
|
Getopt::Long::Configure('no_ignore_case');
|
|
GetOptions(\%opts, "sign|s", "encrypt|e", "detach-sign|b",
|
|
"decrypt", "verify", "list-keys",
|
|
"list-public-keys", "list-secret-keys",
|
|
"fingerprint", "keyring=s", "secret-keyring=s",
|
|
"recipient|r=s@", "armour|a", "clearsign",
|
|
"symmetric|c", "list-packets", "version|v",
|
|
"enarmour", "dearmour", "compat=s",
|
|
"keyserver=s", "local-user|u=s");
|
|
|
|
if ($opts{version}) {
|
|
print(version()), exit;
|
|
}
|
|
|
|
my %arg;
|
|
$arg{PubRing} = $opts{keyring} if $opts{keyring};
|
|
$arg{SecRing} = $opts{'secret-keyring'} if $opts{'secret-keyring'};
|
|
$arg{Compat} = $opts{compat} if $opts{compat};
|
|
|
|
## Default to GnuPG compatibility.
|
|
$arg{Compat} = 'GnuPG' unless keys %arg;
|
|
|
|
if (my $ks = $opts{keyserver}) {
|
|
$arg{AutoKeyRetrieve} = 1;
|
|
$arg{KeyServer} = $ks;
|
|
}
|
|
|
|
my $pgp = Crypt::OpenPGP->new( %arg ) or
|
|
die Crypt::OpenPGP->errstr;
|
|
|
|
my @args = ($pgp, \%opts, \@ARGV);
|
|
|
|
if ($opts{'list-keys'} || $opts{'list-public-keys'} || $opts{fingerprint}) {
|
|
do_list_keys(@args);
|
|
} elsif ($opts{'list-secret-keys'}) {
|
|
do_list_keys(@args, 1);
|
|
} elsif ($opts{encrypt}) {
|
|
do_encrypt(@args);
|
|
} elsif ($opts{symmetric}) {
|
|
do_encrypt(@args, 1);
|
|
} elsif ($opts{decrypt}) {
|
|
do_decrypt(@args);
|
|
} elsif ($opts{sign}) {
|
|
do_sign(@args);
|
|
} elsif ($opts{'detach-sign'}) {
|
|
do_sign(@args, 1);
|
|
} elsif ($opts{clearsign}) {
|
|
do_sign(@args, 0, 1);
|
|
} elsif ($opts{verify}) {
|
|
do_verify(@args);
|
|
} elsif ($opts{enarmour}) {
|
|
do_enarmour(@args);
|
|
} elsif ($opts{dearmour}) {
|
|
do_dearmour(@args);
|
|
} elsif ($opts{'list-packets'}) {
|
|
do_list_packets(@args);
|
|
} else {
|
|
do_wim(@args);
|
|
}
|
|
|
|
sub do_wim {
|
|
my($pgp, $opts, $args) = @_;
|
|
my $file = shift @$args or die "usage: $0 <file>";
|
|
my $res = $pgp->handle( Filename => $file ) or die $pgp->errstr;
|
|
if (defined $res->{Plaintext}) {
|
|
print $res->{Plaintext};
|
|
}
|
|
if (defined $res->{Validity}) {
|
|
my $v = $res->{Validity};
|
|
print STDERR $v ? qq(Good signature from "$v".\n) :
|
|
"Bad signature.\n";
|
|
}
|
|
}
|
|
|
|
sub do_list_keys {
|
|
my($pgp, $opts, $args, $secret) = @_;
|
|
my $fp = $opts->{fingerprint};
|
|
my $ring_file = $pgp->{cfg}->get( $secret ? 'SecRing' : 'PubRing' );
|
|
my $ring = Crypt::OpenPGP::KeyRing->new( Filename => $ring_file )
|
|
or die Crypt::OpenPGP::KeyRing->errstr;
|
|
$ring->read;
|
|
my @blocks = $ring->blocks;
|
|
|
|
print $ring_file, "\n", '-' x length($ring_file), "\n";
|
|
for my $kb (@blocks) {
|
|
my $cert = $kb->key;
|
|
printf "%s %4d%s/%s %s\n",
|
|
($cert->is_secret ? 'sec' : 'pub'),
|
|
$cert->key->size,
|
|
$cert->key->public_key->abbrev,
|
|
substr($cert->key_id_hex, -8, 8),
|
|
($kb->primary_uid || '');
|
|
if ($fp) {
|
|
my $f = $cert->fingerprint;
|
|
my $form = join ' ', ("%02X%02X " x (length($f) / 4)) x 2;
|
|
printf " Key fingerprint = $form\n", unpack 'C*', $f;
|
|
}
|
|
if (my $sub = $kb->subkey) {
|
|
printf "%s %4d%s/%s\n",
|
|
($sub->is_secret ? 'ssb' : 'sub'),
|
|
$sub->key->size,
|
|
$sub->key->public_key->abbrev,
|
|
substr($sub->key_id_hex, -8, 8),
|
|
}
|
|
print "\n";
|
|
}
|
|
}
|
|
|
|
sub do_encrypt {
|
|
my($pgp, $opts, $args, $symm) = @_;
|
|
my $recips = $opts->{recipient};
|
|
$recips && @$recips or die "usage: $0 --encrypt -r <recip> <file>"
|
|
unless $symm;
|
|
my $file = shift @$args or die "usage: $0 --encrypt -r <recip> <file>";
|
|
my $cb = sub {
|
|
my($keys) = @_;
|
|
return [] unless @$keys;
|
|
my $prompt = "
|
|
Message is being encrypted to:
|
|
";
|
|
my $i = 1;
|
|
for my $cert (@$keys) {
|
|
$prompt .= sprintf " [%d] %s (ID %s)\n",
|
|
$i++, $cert->uid,
|
|
substr($cert->key_id_hex, -8, 8);
|
|
}
|
|
$prompt .= "
|
|
If these are the intended recipients, press <enter>. Otherwise,
|
|
enter the indices of the recipients to which you wish to send
|
|
the message.
|
|
|
|
Enter numeric indices, separated by spaces: ";
|
|
my $n = prompt($prompt, join(' ', 1..$i-1));
|
|
my %seen;
|
|
my @keys = @{$keys}[ map { $seen{$_}++ ? () : ($_-1) } split /\s+/, $n ];
|
|
\@keys;
|
|
};
|
|
my %sign_args;
|
|
if ($opts->{sign}) {
|
|
my $cert = get_seckey($pgp, $opts) or die $pgp->errstr;
|
|
%sign_args = ( SignKeyID => $cert->key_id_hex,
|
|
SignPassphraseCallback => \&passphrase_cb );
|
|
}
|
|
my %enc_args;
|
|
if ($symm) {
|
|
my $pass = prompt("Enter passphrase: ", '', 1);
|
|
%enc_args = ( Passphrase => $pass );
|
|
} else {
|
|
%enc_args = ( Recipients => $recips,
|
|
RecipientsCallback => $cb );
|
|
}
|
|
my $ct = $pgp->encrypt(
|
|
%enc_args,
|
|
Filename => $file,
|
|
$opts->{armour} ? (Armour => $opts->{armour}) : (),
|
|
%sign_args,
|
|
) or die $pgp->errstr;
|
|
print $ct;
|
|
}
|
|
|
|
sub do_decrypt {
|
|
my($pgp, $opts, $args) = @_;
|
|
my $file = shift @$args or die "usage: $0 --decrypt <file>";
|
|
my($pt, $validity);
|
|
until ($pt) {
|
|
($pt, $validity) = $pgp->decrypt(
|
|
Filename => $file,
|
|
PassphraseCallback => \&passphrase_cb,
|
|
);
|
|
unless ($pt) {
|
|
if ($pgp->errstr =~ /Bad checksum/) {
|
|
print "Error: Bad passphrase.\n\n";
|
|
} else {
|
|
die $pgp->errstr;
|
|
}
|
|
}
|
|
}
|
|
print $pt;
|
|
warn "Signature verification failed: ", $pgp->errstr
|
|
unless defined $validity || $pgp->errstr ne 'No Signature';
|
|
if (defined $validity) {
|
|
print STDERR $validity ? qq(Good signature from "$validity".\n) :
|
|
"Bad signature.\n";
|
|
}
|
|
}
|
|
|
|
sub do_sign {
|
|
my($pgp, $opts, $args, $detach, $clear) = @_;
|
|
my $file = shift @$args or die "usage: $0 --sign <file>";
|
|
my $sig;
|
|
my $cert = get_seckey($pgp, $opts) or die $pgp->errstr;
|
|
until ($sig) {
|
|
$sig = $pgp->sign(
|
|
Filename => $file,
|
|
Detach => $detach,
|
|
Clearsign => $clear,
|
|
Key => $cert,
|
|
PassphraseCallback => \&passphrase_cb,
|
|
$opts->{armour} ? (Armour => $opts->{armour}) : (),
|
|
);
|
|
unless ($sig) {
|
|
if ($pgp->errstr =~ /Bad checksum/) {
|
|
print "Error: Bad passphrase.\n\n";
|
|
} else {
|
|
die $pgp->errstr;
|
|
}
|
|
}
|
|
}
|
|
print $sig;
|
|
}
|
|
|
|
sub do_verify {
|
|
my($pgp, $opts, $args) = @_;
|
|
my($sigfile, @files) = @$args;
|
|
my $valid = $pgp->verify( SigFile => $sigfile, Files => \@files );
|
|
die $pgp->errstr unless defined $valid;
|
|
print $valid ? qq(Good signature from "$valid".\n) : "Bad signature.\n";
|
|
}
|
|
|
|
sub do_enarmour {
|
|
my($pgp, $opts, $args) = @_;
|
|
my $file = shift @$args or return $pgp->error("No file");
|
|
require Crypt::OpenPGP::Armour;
|
|
print Crypt::OpenPGP::Armour->armour(
|
|
Data => $pgp->_read_files($file),
|
|
Object => 'MESSAGE',
|
|
);
|
|
}
|
|
|
|
sub do_dearmour {
|
|
my($pgp, $opts, $args) = @_;
|
|
my $file = shift @$args or return $pgp->error("No file");
|
|
require Crypt::OpenPGP::Armour;
|
|
my $data = Crypt::OpenPGP::Armour->unarmour($pgp->_read_files($file));
|
|
print $data->{Data};
|
|
}
|
|
|
|
sub do_list_packets {
|
|
my($pgp, $opts, $args) = @_;
|
|
my $file = shift @$args or die "usage: $0 --list-packets <file>";
|
|
require Crypt::OpenPGP::Message;
|
|
my $msg = Crypt::OpenPGP::Message->new( Filename => $file )
|
|
or die Crypt::OpenPGP::Message->errstr;
|
|
my @pieces = $msg->pieces;
|
|
for my $pkt (@pieces) {
|
|
if ($pkt->can('display')) {
|
|
print $pkt->display;
|
|
} else {
|
|
print ref($pkt), "\n";
|
|
}
|
|
}
|
|
}
|
|
|
|
sub version {
|
|
my $v = <<VERSION;
|
|
pgplet (Crypt::OpenPGP) $Crypt::OpenPGP::VERSION
|
|
|
|
Supported algorithms:
|
|
Public key: RSA, DSA, ElGamal
|
|
VERSION
|
|
require Crypt::OpenPGP::Cipher;
|
|
$v .= "Cipher: " .
|
|
join(', ', values %{ Crypt::OpenPGP::Cipher->supported }) . "\n";
|
|
require Crypt::OpenPGP::Digest;
|
|
$v .= "Hash: " .
|
|
join(', ', values %{ Crypt::OpenPGP::Digest->supported }) . "\n";
|
|
$v;
|
|
}
|
|
|
|
sub passphrase_cb {
|
|
my($cert) = @_;
|
|
my $prompt;
|
|
if ($cert) {
|
|
$prompt = sprintf qq(
|
|
You need a passphrase to unlock the secret key for
|
|
user "%s".
|
|
%d-bit %s key, ID %s
|
|
|
|
Enter passphrase: ), $cert->uid,
|
|
$cert->key->size,
|
|
$cert->key->alg,
|
|
substr($cert->key_id_hex, -8, 8);
|
|
} else {
|
|
$prompt = "Enter passphrase: ";
|
|
}
|
|
prompt($prompt, '', 1);
|
|
}
|
|
|
|
sub get_seckey {
|
|
my($pgp, $opts) = @_;
|
|
my $ring = Crypt::OpenPGP::KeyRing->new( Filename =>
|
|
$pgp->{cfg}->get('SecRing') ) or
|
|
return $pgp->error(Crypt::OpenPGP::KeyRing->errstr);
|
|
my $kb;
|
|
if (my $user = $opts->{'local-user'}) {
|
|
my($lr, @kb) = (length($user));
|
|
if (($lr == 8 || $lr == 16) && $user !~ /[^\da-fA-F]/) {
|
|
@kb = $ring->find_keyblock_by_keyid(pack 'H*', $user);
|
|
} else {
|
|
@kb = $ring->find_keyblock_by_uid($user);
|
|
}
|
|
if (@kb > 1) {
|
|
my $prompt = "
|
|
The following keys can be used to sign the message:
|
|
";
|
|
my $i = 1;
|
|
for my $kb (@kb) {
|
|
my $cert = $kb->signing_key or next;
|
|
$prompt .= sprintf " [%d] %s (ID %s)\n",
|
|
$i++, $kb->primary_uid,
|
|
substr($cert->key_id_hex, -8, 8);
|
|
}
|
|
$prompt .= "
|
|
Enter the index of the signing key you wish to use: ";
|
|
my $n;
|
|
$n = prompt($prompt, $i - 1) while $n < 1 || $n > @kb;
|
|
$kb = $kb[$n-1];
|
|
} else {
|
|
$kb = $kb[0];
|
|
}
|
|
} else {
|
|
$kb = $ring->find_keyblock_by_index(-1);
|
|
}
|
|
return $pgp->error("Can't find keyblock: " . $ring->errstr)
|
|
unless $kb;
|
|
my $cert = $kb->signing_key;
|
|
$cert->uid($kb->primary_uid);
|
|
$cert;
|
|
}
|
|
|
|
sub prompt {
|
|
my($prompt, $def, $noecho) = @_;
|
|
print STDERR $prompt . ($def ? "[$def] " : "");
|
|
if ($noecho) {
|
|
ReadMode('noecho');
|
|
}
|
|
chomp(my $ans = ReadLine(0));
|
|
ReadMode('restore');
|
|
print STDERR "\n";
|
|
defined $ans && $ans ne '' ? $ans : $def;
|
|
}
|
|
__END__
|
|
:endofperl
|
|
@set "ErrorLevel=" & @goto _undefined_label_ 2>NUL || @"%COMSPEC%" /d/c @exit %ErrorLevel%
|