pax_global_header00006660000000000000000000000064150057102120014503gustar00rootroot0000000000000052 comment=799c0dce0276aa006d28144f6dab634c54d4fb1e signing-party-2.12/000077500000000000000000000000001500571021200142225ustar00rootroot00000000000000signing-party-2.12/Makefile000066400000000000000000000014751500571021200156710ustar00rootroot00000000000000-include /usr/share/dpkg/pkg-info.mk DIRS=caff gpg-key2ps gpg-mailkeys gpgsigs gpglist gpgparticipants keyanalyze keylookup \ sig2dot springgraph gpgwrap gpgdir keyart gpg-key2latex all: sed -i "s/@@VERSION@@/$(DEB_VERSION_UPSTREAM)/g" \ caff/caff \ caff/pgp-clean \ caff/pgp-fixkey \ gpg-key2latex/gpg-key2latex \ gpg-key2ps/gpg-key2ps \ gpg-mailkeys/gpg-mailkeys \ gpgsigs/gpgsigs \ keylookup/keylookup for dir in $(DIRS) ; do if [ -f $$dir/Makefile ] ; then $(MAKE) -C $$dir || exit 1 ; fi ; done install: for dir in $(DIRS) ; do if [ -f $$dir/Makefile ] ; then $(MAKE) -C $$dir install || exit 1 ; fi ; done clean: for dir in $(DIRS) ; do if [ -f $$dir/Makefile ] ; then $(MAKE) -C $$dir clean || exit 1 ; fi ; done signing-party-2.12/README000066400000000000000000000022701500571021200151030ustar00rootroot00000000000000This is a collection of several projects relating to OpenPGP. * caff: CA - Fire and Forget signs and mails a key * pgp-clean: removes all non-self signatures from key * pgp-fixkey: removes broken packets from keys * gpg-mailkeys: simply mail out a signed key to its owner * gpg-key2ps: generate PostScript file with fingerprint paper slips * gpgdir: recursive directory encryption tool * gpglist: show who signed which of your UIDs * gpgsigs: annotates list of GnuPG keys with already done signatures * gpgparticipants: create list of party participants for the organiser * gpgwrap: a passphrase wrapper * keyanalyze: minimum signing distance (MSD) analysis on keyrings * keylookup: ncurses wrapper around gpg --search * sig2dot: converts a list of GnuPG signatures to a .dot file * springgraph: creates a graph from a .dot file * keyart: creates a random ASCII art of a PGP key file * gpg-key2latex: generate LaTeX file with fingerprint paper slips For more information on each of these tools, please see their respective manpages. Please note that each individual project has its own license, consult the licensing information in the subdirectories. -- Peter Palfrader, Tue, 29 Jun 2004 13:48:09 +0200 signing-party-2.12/caff/000077500000000000000000000000001500571021200151215ustar00rootroot00000000000000signing-party-2.12/caff/Makefile000066400000000000000000000001541500571021200165610ustar00rootroot00000000000000MAN = caff.1 pgp-clean.1 pgp-fixkey.1 all: $(MAN) %.1: % pod2man $< > $@ install: clean: rm -f $(MAN) signing-party-2.12/caff/README000066400000000000000000000030461500571021200160040ustar00rootroot00000000000000caff -- CA - fire and forget ============================== caff is a script that helps you in keysigning. It takes a list of keyids on the command line, fetches them from a keyserver and calls GnuPG so that you can sign it. It then mails each key to all its email addresses - only including the one UID that we send to in each mail, pruned from all but self sigs and sigs done by you. The mailed key is encrypted with itself as a means to verify that key belongs to the recipient. Since we do not upload the new signatures, or import them into our main keyring, the signature only gets public if: - the email address is valid, and - the person reading the email can decrypt the mail (if it was sent encrypted). Therefore we achieve the same level of security as common Challenge Response systems like CABot, without all the extra hassle of those systems. FEATURES -------- * Easy to setup. * Attaches only the very UID that we send to in the mail. * Prunes the key from all signatures that are not self sigs and not done by you, thereby greatly reducing the size of mails. * Sends the mail encrypted if possible, will warn before sending unencrypted mail (sign only keys) * Creates proper PGP MIME messages. * Uses separate GNUPGHOME for all its operations. DEPENDENCIES ------------ gnupg (>= 1.3.92), perl, libgnupg-interface-perl, libtext-template-perl, libmime-perl, libmailtools-perl (>= 1.62), libnet-idn-encode-perl INSTALLATION ------------ After creating a ~/.caffrc from the template, caff almost works out of the box. -- Peter signing-party-2.12/caff/README.gpg-agent000066400000000000000000000010101500571021200176410ustar00rootroot00000000000000Running caff with gpg-agent --------------------------- gpg-agent is part of GnuPG 2 (aka 1.9), but runs also with gpg 1.x binaries. 0. Debian users apt-get install gnupg-agent and one of the pinentry-* packages. 1. Put "use-agent" in ~/.caff/gnupghome/gpg.conf. 2. Optionally edit ~/.gnupg/gpg-agent.conf, e.g.: default-cache-ttl 600 pinentry-program /usr/bin/pinentry-gtk-2 2. Start the agent: $ eval `gpg-agent --daemon` 3. Run caff. -- Christoph Berg Mon, 27 Jun 2005 01:31:27 +0200 signing-party-2.12/caff/README.many-keys000066400000000000000000000075031500571021200177220ustar00rootroot00000000000000Using caff to sign lots of keys ------------------------------- If you have loads of keys to sign (sometimes, there are keysigning parties with more than 100 participants), keysigning can be awkward, even with caff. It gets worse if you have multiple local keys and want to sign with all. Some hints to get the signing done faster: * Use fingerprints instead of key ids. caff and gpg allow you to specify the full fingerprint. This will save you from having to check the fingerprint yourself. If you have a text file with all fingerprints, use that and then run $ xargs caff --no-download --key-file keyring.asc ,. $ caff -u , * Use gpg-agent. See README.gpg-agent. * Use gpg-sign-args. $CONFIG{'gpg-sign-args'} = "save"; This automatically saves the key after signing in gpg. The advantage is that you do not have to type "save" for each key. The disadvantage is that you cannot choose which UIDs to sign by answering "no" at the "Really sign?" prompt any more; you will have *not* to send out some mails. (And you have unwanted signatures lingering around in ~/.caff/gnupghome/pubring.gpg.) -- Guilhem Moulin Thu, 03 Apr 2014 12:43:13 +0200 signing-party-2.12/caff/README.v3-keys000066400000000000000000000013341500571021200173020ustar00rootroot00000000000000v3 keys are evil ---------------- The good thing about v4 keys is that the last 16 chars of the fingerprint are the same as the 8 byte keyid, and likewise the last 8 chars are the 4 byte keyid. For v3 keys that is not true. This, and some issues with HKP key servers make the handling of v3 a PITA. To sign v3 keys with caff, do the following: $ caff [note that caff does not accept fingerprints for v3 keys, use keyids] The key will be imported from the keyserver, but caff thinks it failed. Now run caff again with -R: $ caff -R Since the key is already there, caff will proceed. Of course, this could be automated... patches welcome :) -- Christoph Berg Sat, 2 Jul 2005 21:34:48 +0200 signing-party-2.12/caff/THANKS000066400000000000000000000006241500571021200160360ustar00rootroot00000000000000Caff is not the work of a sole author. It wouldn't be what it is without the help of several people. I'd like to say thank you to everybody who helped to make caff better. - Nick Mathewson For suggesting and implementing --no-download and --no-sign. - And of course everyone who sent comments, suggestions or bug-reports. -- Peter Palfrader Thu, 15 Jul 2004 03:08:14 +0200 signing-party-2.12/caff/TODO000066400000000000000000000004211500571021200156060ustar00rootroot00000000000000Legend: - Not done * Top priority . Partially done o Done D Deferred X Abandoned o need to import own keys (including public) into our gnupghome (else it complains about not finding the public key for the used secret key) signing-party-2.12/caff/caff000077500000000000000000002315511500571021200157550ustar00rootroot00000000000000#!/usr/bin/perl # caff -- CA - Fire and Forget # # Copyright (c) 2004, 2005, 2006 Peter Palfrader # Copyright (c) 2005, 2006 Christoph Berg # Copyright (c) 2014-2016 Guilhem Moulin # # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # 3. The name of the author may not be used to endorse or promote products # derived from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. =pod =head1 NAME caff -- CA - Fire and Forget =head1 SYNOPSIS =over =item B [-eERS] [-m I] [-u I] I [I ..] =item B [-eERS] [-m I] [-u I] [I ..] =back =head1 DESCRIPTION CA Fire and Forget is a script that helps you in keysigning. It takes a list of keyids on the command line, fetches them from a keyserver and calls GnuPG so that you can sign it. It then mails each key to all its email addresses - only including the one UID that we send to in each mail, pruned from all but self sigs and sigs done by you. The mailed key is encrypted with itself as a means to verify that key belongs to the recipient. The list of keys to sign can also be provided through caff's standard input, as gpgparticipants(1) formatted content. Only keys for which both the "Fingerprint OK" and "ID OK" boxes are ticked (i.e., marked with an "x") are considered for signing. Furthermore, the input header must include at least one checksum line, and all checksum boxes must be marked as verified (with an "x"). =head1 OPTIONS =over =item B<-e>, B<--export-old> Export old signatures. Default is to ask the user for each old signature. =item B<-E>, B<--no-export-old> Do not export old signatures. Default is to ask the user for each old signature. =item B<-m>, B<--mail> I Whether to send mail after signing. Default is to ask, for each uid, with a default value of yes. =item B<-R>, B<--no-download> Do not retrieve the key to be signed from a keyserver. =item B<-S>, B<--no-sign> Do not sign the keys. =item B<-u> I, B<--local-user> I Select the key that is used for signing, in case you have more than one key. To sign with multiple keys at once, separate multiple keyids by comma. This option requires the key(s) to be defined through the keyid variable in the configuration file. =item B<--key-file> I Import keys from file. Can be supplied more than once. =item B<--keys-from-gnupg> Try to import keys from your standard GnuPG keyrings. =item B<--debug> Enable debug messages. =back =head1 ENVIRONMENT =over =item I The default home directory. =item I The gpg binary. Default: C<"gpg">. =item I The default working directory for gpg. Default: C<$HOME/.gnupg>. =back =head1 FILES =over =item $HOME/.caffrc - configuration file =item $HOME/.caff/keys/yyyy-mm-dd/ - processed keys =item $HOME/.caff/gnupghome/ - caff's working directory for gpg =item $HOME/.caff/gnupghome/gpg.conf - gpg configuration (see B below) useful options include use-agent, keyserver, keyserver-options, default-cert-level, etc. =back =head1 CONFIGURATION FILE OPTIONS The configuration file is a perl script that sets values in the hash B<%CONFIG>. The file is generated when it does not exist. Example: $CONFIG{'owner'} = q{Peter Palfrader}; $CONFIG{'email'} = q{peter@palfrader.org}; $CONFIG{'keyid'} = [ qw{DE7AAF6E94C09C7F 62AF4031C82E0039} ]; =head2 Required basic settings =over =item B [string] Your name. B. =item B [string] Your email address, used in From: lines. B. =item B [list of keyids] A list of your keys. This is used to determine which signatures to keep in the pruning step. If you select a key using B<-u> it has to be in this list. B. =back =head2 General settings =over =item B [string] Base directory for the files caff stores. Default: B<$HOME/.caff/>. =item B [hash] How to color output messages. See the C documentation for the list of supported colors; colored output can be disabled by setting this option to an empty hash B<{}>. Default: { error => 'bold bright_red' , warn => 'bright_red' , notice => 'bold' , info => '' , success => 'green' # used in combination with 'notice' and 'info' , fail => 'yellow' # used in combination with 'notice' and 'info' } =back =head2 GnuPG settings =over =item B [string] Path to the GnuPG binary. Default: The value of the I environment variable if set, otherwise C. =item B [string] Path to your secret keyring (GnuPG < 2.1), or to the GnuPGHOME of the agent managing the secret key material (GnuPG >= 2.1). Default: B<$HOME/.gnupg/secring.gpg>. If the value is not a directory with GnuPG >= 2.1, the parent directory (i.e., B<$HOME/.gnupg> by default) is considered instead. =item B [keyid, or list of keyids] Additional keyids to encrypt messages to. Default: none. =item B [string] The prefix to the "sign" command used to make the signature from gpg's shell. Can be set to a mix of "l" (local), "nr" (non-revocable) or "t" (trust) to make a signature of the given type. See gpg(1) for details. Default: "" (i.e., make a regular, exportable, signature). =item B [string] Additional commands to pass to gpg after the "sign" command. Default: none. =back =head2 Key import settings =over =item B [boolean] If true, then skip the step of fetching keys from the keyserver. Default: B<0>. =item B [list of files] A list of files containing keys to be imported. =back =head2 Signing settings =over =item B [boolean] If true, then skip the signing step. Default: B<0>. =item B [boolean] If true, then pause before continuing to the signing step. This is useful for offline signing. Default: B<0>. =item B [seconds] Don't export UIDs by default, on which your latest signature is older than this age. Default: B<24*60*60> (i.e. one day). =item B [keyid, or list of keyids] Select the key that is used for signing, in case you have more than one key. With multiple keyids, sign with each key in turn. =item B [auto|ask|no] Whether to locally sign the UIDs in the user's GnuPGHOME, in addition to caff's signatures in its own GnuPGHOME. Such signatures are not exportable. This can be useful when the recipient forgets to upload the signatures caff sent (or if they are non-exportable as well), as it gives a way to keep track of which UIDs were verified. However, note that local signatures will not be deleted once the recipient does the upload and the signer refreshes her keyring. If the value is not I and if B contains "l", each (local) signature is merely exported from caff's own GnuPGHOME to the user's. Otherwise, if the value is I, each UID signed in caff's own GnuPGHOME gets automatically locally signed in the user's, using the same certification level; this requires a working gpg-agent(1). If I, the user is prompted for which UIDs to locally sign. Default: B. =item B [boolean] If true, then before signing a key gpg will display the photos attached to it, if any. (The photo viewer can be specified with a "photo-viewer" option in caff's GnuPGHOME.) Default: B<0>. =back =head2 Mail settings =over =item B [yes|ask-yes|ask-no|no] Whether to send mails. This is a quad-option, with which you can set the behaviour: yes always sends, no never sends; ask-yes and ask-no asks, for each uid, with according defaults for the question. Default: B. In any case, the messages are also written to $CONFIG{'caffhome'}/keys/ =item B [yes|ask-yes|ask-no|no] The value of this option is considered instead of that of B for recipient keys without encryption capability. Default to the value of B. =item B [string] Sets the value of the "Subject:" header field. C<%k> will be expanded to the long key ID of the signed key. Default: C. =item B [string] Email template which is used as the body text for the email sent out instead of the default text if specified. The following perl variables can be used in the template: =over =item B<{owner}> [string] Your name as specified in the L|/item_owner__5bstring_5d> setting. =item B<{key}> [string] The keyid of the key you signed. =item B<{@uids}> [array] The UIDs for which signatures are included in the mail. =back Note that you should probably customize the template if you intend to send non-exportable signatures (i.e., if B contains "l"), as uploading such signatures doesn't make sense, and they require the import option "import-local-sigs" which isn't set by default. =item B [string] Add a Reply-To: header to messages sent. Default: none. =item B [string] Address to send blind carbon copies to when sending mail. Default: none. =item B [array] Parameters to pass to Mail::Mailer. Default: none. Setting this option is strongly discouraged: fix your local MTA instead. This could for example be $CONFIG{'mailer-send'} = [ 'smtp', Server => 'mail.server', Auth => ['user', 'pass'] ]; to use the perl SMTP client, or $CONFIG{'mailer-send'} = [ 'sendmail', '-f', $CONFIG{'email'}, '-it' ]; to pass arguments to the sendmail program. To specify a sendmail binary you can set the C<< PERL_MAILERS >> environment variable as follows: $ENV{'PERL_MAILERS'} = 'sendmail:/path/to/sendmail_compatible_mta'; For more information see Mail::Mailer(3pm). =back =head1 NOTES As noted above caff uses its own GnuPGHOME and GnuPG configuration file. In fact it only needs its own keyring for the signing work, but it would be unsafe to reuse the same GnuPG configuration file because the user could have set an option in $HOME/.gnupg/gpg.conf which would break caff. Therefore the GnuPG options that are intended to be used with caff, such as C or C, need to be placed in $HOME/.caff/gnupghome/gpg.conf instead. If this file does not exist, the GnuPG options found in $HOME/.gnupg/gpg.conf that are known to be safe (and useful) for caff, are passed to gpg(1) as command-line options. =head1 AUTHORS =over =item Peter Palfrader =item Christoph Berg =item Guilhem Moulin =back =head1 SEE ALSO gpg(1), pgp-clean(1), /usr/share/doc/signing-party/caff/ =cut use strict; use warnings; use IO::Handle; use File::Copy qw{copy}; use File::Temp; use Text::Template; use MIME::Entity; use Encode (); use I18N::Langinfo qw{langinfo}; use Net::IDN::Encode qw{email_to_ascii domain_to_ascii}; use Fcntl; use IO::Select; use Getopt::Long; use GnuPG::Interface; use POSIX qw{strftime setlocale}; use Term::ANSIColor qw{colored}; my %CONFIG; my $VERSION = '@@VERSION@@'; my $LOCALE = Encode::find_encoding(langinfo(I18N::Langinfo::CODESET())); my $USER_AGENT = "caff $VERSION"; # Global variables my @KEYIDS; my @LOCAL_USER; my $PARAMS; my $KEYSBASE; my $GNUPGHOME; ## # Display an error message on STDERR and then exit. # # @param $exitcode exit code status to use to end the program # @param $line error message to display on STDERR # sub mycolored($@) { my $msg = shift; my $color = join (' ', grep defined, map { defined $_ ? $CONFIG{colors}->{$_} : undef } @_) if defined $CONFIG{colors}; $msg = colored($msg, $color) if defined $color and $color !~ /^\s*$/; return $msg; } sub myerror($$) { my ($exitcode, $line) = @_; print STDERR mycolored("[ERROR] $line", 'error'), "\n"; exit $exitcode; } sub mywarn($) { my ($line) = @_; print STDERR mycolored("[WARN] $line", 'warn'), "\n"; } sub notice($;$) { my ($line,$color) = @_; $color = $color ? 'success' : 'fail' if defined $color; print STDERR mycolored("[NOTICE] $line", 'notice', $color), "\n"; } sub info($;$) { my ($line,$color) = @_; $color = $color ? 'success' : 'fail' if defined $color; print STDERR mycolored("[INFO] $line", 'info', $color), "\n"; } sub debug($) { my ($line) = @_; print STDERR "[DEBUG] $line\n" if $PARAMS->{debug}; } sub trace($) { my ($line) = @_; #print STDERR "[trace] $line\n"; } sub trace2($) { my ($line) = @_; #print STDERR "[trace2] $line\n"; } sub mysystem(@) { system { $_[0] } @_; myerror($?, "$_[0] exited with value ".($? >> 8)) if $?; } # Return -1 if the GnuPG version is < $_[0], 0 if == $_[0], 1 if > $_[0]. my $GNUPG_VERSION; sub GnuPG_version($) { unless (defined $GNUPG_VERSION) { # cache the version $GNUPG_VERSION = `$CONFIG{gpg} --no-options --with-colons --list-config version` or exit 1; chomp $GNUPG_VERSION; $GNUPG_VERSION =~ s/^cfg:version:// or die; debug "gpg (GnuPG) $GNUPG_VERSION"; } my @v1 = split /\./, $GNUPG_VERSION; my @v2 = split /\./, shift; while (@v1 or @v2) { my $v1 = shift @v1 // 0; my $v2 = shift @v2 // 0; my $r = $v1 <=> $v2; return $r unless $r == 0; } return 0; } sub gpgconf(@) { my $pid = open my $fh, '-|', 'gpgconf', @_; my %conf; while (<$fh>) { my ($k, $v) = split /:/, $_; chomp ($conf{$k} = $v); $conf{$k} =~ s/%(\p{AHex}{2})/ chr(hex($1)) /ge; # unescape the %-encoded chars } waitpid $pid, 0; myerror($?, "gpgconf exited with value ".($? >> 8)) if $? > 0; close $fh; return \%conf; } # See RFC 5322 section 3.4.1; only the pattern for the local part, which # doesn't go beyond the ASCII range, is validated. The domain part is # NOT checked against RFC 5322, as it must be encoded to ASCII first; # for now any string in the full-range unicode that does not contain # U+0040 (commercial at), U+FE6B (small commercial at) and U+FF20 # (fullwidth commercial at) is accepted. my $RE_word = qr/[\x21\x23-\x27\x2A\x2B\x2D\x2F-\x39\x3D\x3F\x41-\x5A\x5E-\x7E]+ # atom: any ASCII CHAR except specials, SPACE and CTLs |\x22(?:[\x00-\x21\x23-\x5B\x5D-\x7E]|\x5C\p{ASCII})*\x22 # quoted string /x; my $RE_address_spec = qr/(?$RE_word(?:\.$RE_word)*)[\@\N{U+FE6B}\N{U+FF20}](?[^\@\N{U+FE6B}\N{U+FF20}]+)/o; # A domain label is a non-empty ASCII string of length at most 63 # characters (RFC 1035 2.3.4). Valid characters are alphanumeric and # hyphen '-', but an hyphen may not appear at the start or end of a # label (RFC 952, RFC 1123 2.1). my $RE_label = qr/[0-9a-z](?:[0-9a-z\x2D]{0,61}[0-9a-z])?/aai; # Take a 'mailbox' (RFC 5322 section 3.4) and return its ASCII-encoded # 'addr-spec'; or undef if it violates one of RFC 5322/5892/1035/5321. # We're not using Email::Valid because it's not unicode-friendly. # NOTE: This subroutine should only be used to extract e-mail addresses # from UIDs. The phrase is NOT checked against RFC 5322 (any string # containing only characters in the full-unicode printable range are # accepted), but we don't care as long as it's not used in email # headers. sub email_valid($) { local $_ = shift // return; return unless /\A$RE_address_spec\z/ao or # addr-spec /\A(?:\p{Print}*\p{Space})?<$RE_address_spec> # [phrase] "<" addr-spec ">" (?:\p{Space}+\((?:[^()\\]|\\.)*\))?\z/aox; my ($l,$d) = @+{qw/l d/}; if ($d =~ /\P{ASCII}/) { # encode the IDN to ASCII using Punycode for RFC 5321 validation eval { $d = domain_to_ascii($d) }; return if $@; # violates RFC 5892 } my $address = "$l\@$d"; return unless length $d > 0 and length $d <= 255 # violates RFC 1035 2.3.4 "size limits" and length $l <= 64 # violates RFC 5321 4.5.3.1.1 and length $address <= 254 # violates RFC 5321 4.5.3.1.3 and $d =~ /\A$RE_label(?:\.$RE_label)+\z/o; # ignore non-FQDN return $address; } open NULL, '+<', '/dev/null'; my $NULL = fileno NULL; sub generate_config() { notice("Error: \$LOGNAME is not set", 0) unless defined $ENV{'LOGNAME'}; my $gecos = defined $ENV{'LOGNAME'} ? (getpwnam($ENV{LOGNAME}))[6] : undef; my $email; my @keys; # BSD does not have hostname -f, so we try without -f first my $hostname = `hostname`; $hostname = `hostname -f` unless $hostname =~ /\./; chomp $hostname; my ($Cgecos,$Cemail,$Ckeys) = ('','',''); if (defined $gecos) { $gecos =~ s/,.*//; $CONFIG{'gpg'} = $ENV{GNUPGBIN} // 'gpg'; my $gpg = mkGnuPG( extra_args => ['--with-colons'] ); my $handles = mkGnuPG_fds ( stdout => undef ); my $pid = $gpg->list_public_keys(handles => $handles, command_args => [ $gecos ]); my %output = readwrite_gpg($handles); waitpid $pid, 0; $handles->{stdout}->close; if ($output{stdout} eq '') { mywarn "No data from $CONFIG{gpg} for list-key"; # There should be at least 'tru:' everywhere. }; @keys = ($output{stdout} =~ /^pub:[^eir:]*:(?:[^:]*:){2}([0-9A-F]{16}):/mg); unless (scalar @keys) { notice("Error: No keys were found using \"$CONFIG{gpg} --list-public-keys '$gecos'\"", 0); @keys = qw{0123456789ABCDEF 89ABCDEF76543210}; $Ckeys = '#'; } my @emails = ($output{stdout} =~ /^uid:[^eir:]*:(?:[^:]*:){7}([^:]+)(?::.*)?$/mg); if (@emails) { s/\\x(\p{AHex}{2})/ chr(hex($1)) /ge foreach @emails; @emails = grep defined, map {email_valid(Encode::decode_utf8($_))} @emails; $email = shift @emails; # take the first valid address } unless (defined $email) { notice("Error: No email address was found using \"$CONFIG{gpg} --list-public-keys '$gecos'\"", 0); $email = $ENV{'LOGNAME'}.'@'.$hostname; $Cemail = '#'; } } else { $gecos = 'Unknown Caff User'; $email = $ENV{'LOGNAME'}.'@'.$hostname; @keys = qw{0123456789ABCDEF 89ABCDEF76543210}; ($Cgecos,$Cemail,$Ckeys) = ('#','#','#'); }; my $template = < # # If you have a v4 key, it will simply be the last 16 digits of # your fingerprint. # # Example: # \$CONFIG{'keyid'} = [ qw{FEDCBA9876543210} ]; # or, if you have more than one key: # \$CONFIG{'keyid'} = [ qw{0123456789ABCDEF 89ABCDEF76543210} ]; $Ckeys\$CONFIG{'keyid'} = [ qw{@keys} ]; # Select this/these keys to sign with #\$CONFIG{'local-user'} = [ qw{@keys} ]; # Additionally encrypt messages for these keyids #\$CONFIG{'also-encrypt-to'} = [ qw{@keys} ]; # Mail template to use for the encrypted part #\$CONFIG{'mail-template'} = << 'EOM'; EOT $template .= "#$_" foreach ; $template .= "#EOM\n"; return $template; } my @GNUPGOPTS; sub load_config() { my $config = $ENV{'HOME'} . '/.caffrc'; unless (-f $config) { notice "No configfile $config present, I will use this template:"; my $template = generate_config(); print $template, "\n"; notice "Please edit $config and run caff again."; open F, '>', $config or myerror(1, "$config: $!"); print F $template; close F; exit(1); } unless (scalar eval `cat $config`) { myerror(1, "Couldn't parse $config: $@") if $@; }; myerror(1, "$0: $_ is not defined in $config") for grep {!defined $CONFIG{$_}} qw/owner email keyid/; myerror(1, "$0: keyid is not an array ref in $config") unless ref $CONFIG{'keyid'} eq 'ARRAY'; myerror(1, "$0: key $_ is not specified as a long (16 digit) keyid or fingerprint in $config") for grep !/^((?:0x)?\p{AHex}{16}|\p{AHex}{40}|(?:\p{AHex}{4} ){5}(?: \p{AHex}{4}){5})$/, @{$CONFIG{'keyid'}}; $CONFIG{'caffhome'} //= $ENV{'HOME'}.'/.caff'; $KEYSBASE = $CONFIG{'caffhome'}.'/keys'; $GNUPGHOME = $CONFIG{'caffhome'}.'/gnupghome'; foreach ($CONFIG{'caffhome'}, $KEYSBASE, $GNUPGHOME) { next if -d $_; debug("Creating $_"); mkdir $_, 0700 or myerror(1, "Cannot mkdir $_: $!"); } @{$CONFIG{'keyid'}} = map { s/^0x//; uc (substr y/ //dr, -16) } @{$CONFIG{'keyid'}}; # must be a list of long keyids $CONFIG{'export-sig-age'} //= 24*60*60; $CONFIG{'gpg'} //= $ENV{GNUPGBIN} // 'gpg'; $CONFIG{'secret-keyring'} //= ($ENV{'GNUPGHOME'} // "$ENV{'HOME'}/.gnupg") . '/secring.gpg'; $CONFIG{'no-download'} //= 0; $CONFIG{'no-sign'} //= 0; $CONFIG{'key-files'} //= []; $CONFIG{'mailer-send'} //= []; myerror(1, "$0: mailer-send is not an array ref in $config") unless ref $CONFIG{'mailer-send'} eq 'ARRAY'; $CONFIG{'mail-subject'} //= "Your signed PGP key 0x%k"; $CONFIG{'mail-template'} //= do { local $/; }; $CONFIG{'also-encrypt-to'} = [ $CONFIG{'also-encrypt-to'} ] if defined $CONFIG{'also-encrypt-to'} and !ref $CONFIG{'also-encrypt-to'}; if (defined $LOCALE) { $CONFIG{$_} = $LOCALE->decode($CONFIG{$_}) for qw/owner mail-template mail-subject/; $CONFIG{$_} = email_to_ascii($LOCALE->decode($CONFIG{$_})) for grep {defined $CONFIG{$_}} qw/email bcc reply-to/; } $CONFIG{'gpg-sign-type'} //= ''; myerror(1, "$0: $CONFIG{'gpg-sign-type'} is an invalid signature type") unless $CONFIG{'gpg-sign-type'} =~ /^(?:l|nr|t)*$/; $CONFIG{'also-lsign-in-gnupghome'} //= 'no'; $CONFIG{'also-lsign-in-gnupghome'} = 'no' if $CONFIG{'no-sign'}; myerror(1, "$0: invalid value for 'also-lsign-in-gnupghome': $CONFIG{'also-lsign-in-gnupghome'}") unless grep { $_ eq $CONFIG{'also-lsign-in-gnupghome'} } qw/auto ask no/; $CONFIG{'show-photos'} //= 0; $CONFIG{colors} //= { error => 'bold bright_red', warn => 'bright_red', notice => 'bold', info => '', success => 'green', fail => 'yellow' }; # Import some options from ~/.gnupghome/gpg.conf. We don't symlink # the whole file because the user could have set an option in # ~/.gnupg/gpg.conf which would break caff. my $gpgconf = ($ENV{'GNUPGHOME'} // "$ENV{'HOME'}/.gnupg") . '/gpg.conf'; if ( ! -f "$GNUPGHOME/gpg.conf" and -f $gpgconf and open my $fh, '<', $gpgconf) { # the list of options to import from ~/.gnupg/gpg.conf; only # safe (and useful) options for caff should be listed here my @gnupgopts_n = qw/ disable-ccid ask-cert-level no-ask-cert-level use-agent no-use-agent no-random-seed-file no-greeting expert no-expert gnupg no-pgp2 pgp6 no-pgp6 pgp7 no-pgp7 pgp8 no-pgp8 rfc2440 rfc4880 openpgp default-comment no-comments emit-version no-emit-version no-version allow-weak-digest-algos ask-cert-expire no-ask-cert-expire utf8-strings no-utf8-strings only-sign-text-ids /; my @gnupgopts_i = qw/ default-cert-level limit-card-insert-tries /; my @gnupgopts_s = qw/ photo-viewer exec-path pcsc-driver reader-port display-charset charset keyserver keyserver-options gpg-agent-info personal-cipher-preferences personal-digest-preferences comment cert-notation set-notation cert-policy-url set-policy-url cipher-algo cert-digest-algo pinentry-mode weak-digest default-cert-expire disable-cipher-algo disable-pubkey-algo agent-program dirmngr-program /; push @GNUPGOPTS, '--no-options'; notice('Importing GnuPG options from '.($ENV{'GNUPGHOME'} // '~/.gnupg').'/gpg.conf:'); while (<$fh>) { s/(?:\r\n|\r|\n)\z// or next; if (/\A\s*([0-9a-zA-Z\-]+)\z/ and grep { $_ eq $1 } @gnupgopts_n) { push @GNUPGOPTS, "--$1"; } elsif (/\A\s*([0-9a-zA-Z\-]+)\s+(\d+)\z/ and grep { $_ eq $1 } @gnupgopts_i) { push @GNUPGOPTS, "--$1=$2"; } elsif (/\A\s*([0-9a-zA-Z\-]+)\s+(\P{Control}+)\z/ and grep { $_ eq $1 } @gnupgopts_s) { push @GNUPGOPTS, "--$1=$2"; } else { next; } notice(" $_"); } close $fh; } # deprecated options, will be removed in a future release mywarn("Deprecated option \$CONFIG{'$_'} = '$CONFIG{$_}'") foreach grep {defined $CONFIG{$_}} qw{gpg-sign gpg-delsig keyserver}; } # Create a new GnuPG::Interface object with common options sub mkGnuPG(%) { my %h = @_; my $gpg = GnuPG::Interface::->new(); $gpg->call( $CONFIG{'gpg'} ); $h{meta_interactive} //= 0; $h{always_trust} //= 1; $h{extra_args} //= []; unshift @{$h{extra_args}}, '--no-auto-check-trustdb'; unshift @{$h{extra_args}}, '--fixed-list-mode' if GnuPG_version('2.0.0') < 0; unshift @{$h{extra_args}}, '--no-autostart' if GnuPG_version('2.1.0') >= 0; # never autostart unshift @{$h{extra_args}}, @GNUPGOPTS if @GNUPGOPTS and defined $h{homedir}; $gpg->options->hash_init(%h); debug(join (' ', $gpg->call(), $gpg->options->get_args(), "...")); return $gpg; } # Create a GnuPG::Handles object. This function takes a hash where keys # are handle names, and values are either IO::Handle objects, in which # case the existing handle is used, or undefined, in which case a new # IO::Handle is created. sub mkGnuPG_fds(%) { my %fd = @_; my @direct; foreach (keys %fd) { push @direct, $_ if defined $fd{$_} and $fd{$_} !~ /^[<>]&/; $fd{$_} //= IO::Handle::->new(); } # Redirect the STDIN and STDOUT to /dev/null unless explicitly # redirected. Also redirect logger to /dev/null in non-debug mode, # but NEVER redirect STDERR! $fd{stdin} = "<&=$NULL" unless exists $fd{stdin}; $fd{stdout} = ">&=$NULL" unless exists $fd{stdout}; $fd{logger} = ">&=$NULL" unless exists $fd{logger} or $PARAMS->{debug}; my $handles = GnuPG::Handles::->new(%fd); $handles->options($_)->{direct} = 1 foreach @direct; debug(join (', ', map {"$_: " . ($handles->options($_)->{direct} ? $fd{$_}->fileno : $fd{$_})} keys %fd)); return $handles; } sub done_gpg($;$) { my ($pid, $handles) = @_; waitpid $pid, 0; mywarn("$CONFIG{gpg} exited with value ".($? >> 8)) if $? > 0; return unless defined $handles; foreach (GnuPG::Handles::HANDLES) { next unless defined $handles->{$_} and $handles->{$_} !~ /^[<>]&/; $handles->{$_}->close if $handles->{$_}->opened; } } # Send some data on GnuPG handles, and retrieve output from all handles # at once using select(2) syscalls. Stop when some output matches a # given regex, or when there nothing more to read or write. A newline # '\n' character is automatically appended to the text to be send to the # 'command' handle; the prefix "[GNUPG:] " to the 'status' handle is # added as well. sub readwrite_gpg($%) { my $handles = shift; my %opts = @_; # ignore direct and dup handles my @infhs = grep {defined $opts{$_} and !$handles->options($_)->{direct} and $handles->{$_} !~ /^[<>]&/} qw/stdin passphrase command/; my @outfhs = grep {defined $handles->{$_} and !$handles->options($_)->{direct} and $handles->{$_} !~ /^[<>]&/} qw/stdout stderr status logger/; my %fh = map { $handles->{$_} => $_ } (@infhs, @outfhs); my %offset = map {$_ => 0} @infhs; my %output = map {$_ => ''} @outfhs; if (defined $opts{command}) { # automatically send the command chomp $opts{command}; $opts{command} .= "\n"; } $opts{status} = qr/^\[GNUPG:\] $opts{status}$/m if defined $opts{status}; $handles->{$_}->blocking(0) foreach (@infhs, @outfhs); my $sin = IO::Select::->new(map {$handles->{$_}} @infhs); my $sout = IO::Select::->new(map {$handles->{$_}} @outfhs); trace("entering readwrite_gpg."); trace("doing stuff until one of: ". join(', ', map {"$_ =~ $opts{$_}"} grep {defined $opts{$_}} @outfhs)) if grep {defined $opts{$_}} @outfhs; my $readwrote_stuff_this_time = 0; my $do_not_wait_on_select = 0; while ($sin->count() + $sout->count() > 0) { if (!$sin->count() and grep {defined $opts{$_} and $output{$_} =~ $opts{$_}} @outfhs) { if ($readwrote_stuff_this_time) { trace("read/write some more."); $do_not_wait_on_select = 1; } else { trace("that's it in our while loop."); last; } }; trace("select waiting for ".($sin->count()+$sout->count())." fds."); my ($readyr, $readyw, undef) = IO::Select::select($sout, $sin, undef, $do_not_wait_on_select ? 0 : 1); trace("ready: write: ". join (',', map {$fh{$_}} @{$readyw // []}). "; read: ". join (',', map {$fh{$_}} @{$readyr // []})); $readwrote_stuff_this_time = 0; for my $fd (@{$readyw // []}) { $readwrote_stuff_this_time = 1; my $fh = $fh{$fd}; if ($offset{$fh} != length $opts{$fh}) { trace ("writing to '$fh'". ($offset{$fh} ? "" : ": ".(split /\n/, $opts{$fh}, 2)[0])); my $written = $fd->syswrite($opts{$fh}, length($opts{$fh}) - $offset{$fh}, $offset{$fh}); $offset{$fh} += $written; } if ($offset{$fh} == length $opts{$fh}) { trace "done writing to '$fh'."; $sin->remove($fd); $fd->close && trace "closed '$fh'." if $opts{autoclose}; } } for my $fd (@{$readyr // []}) { $readwrote_stuff_this_time = 1; my $fh = $fh{$fd}; if ($fd->eof) { trace "done reading from '$fh'."; $sout->remove($fd); next; } trace "reading from '$fh'."; $output{$fh} .= do { local $/; <$fd> }; trace2 "$fh is now:\n$output{$fh}\n================"; } } trace("readwrite_gpg done."); return %output; } sub ask($$;$$) { my ($question, $default, $forceyes, $forceno) = @_; my $fd = fileno(TTY) // die; my $termios = POSIX::Termios::->new(); $termios->getattr($fd) // die "getattr: $!"; my $echo = POSIX::ECHO | POSIX::ECHOK | POSIX::ICANON; my $c_iflag = $termios->getiflag(); my $c_lflag = $termios->getlflag(); unless ($c_iflag & POSIX::ICRNL and ($c_lflag & $echo) == $echo) { debug("Sanitizing TTY"); $termios->setiflag( $c_iflag | POSIX::ICRNL ); $termios->setlflag( $c_lflag | $echo ); $termios->setattr($fd, POSIX::TCSANOW) // warn "setattr: $!"; } my $answer; my $yn = $default ? '[Y/n]' : '[y/N]'; while (1) { print $question,' ',$yn, ' '; if ($forceyes && $forceno) { print "$default (from config/command line)\n"; return $default; }; if ($forceyes) { print "YES (from config/command line)\n"; return 1; }; if ($forceno) { print "NO (from config/command line)\n"; return 0; }; $answer = ; chomp $answer; last if ((length $answer == 0) || ($answer =~ m/^[yYnN]$/) ); print "What about $yn is so hard to understand?\nAnswer with either 'n' or 'y' or just press enter for the default.\n"; sleep 1; }; my $result = $default; $result = 1 if $answer =~ /y/i; $result = 0 if $answer =~ /n/i; return $result; } my $KEYEDIT_PROMPT = qr/GET_LINE keyedit\.prompt/; my $KEYEDIT_DELUID_PROMPT = qr/GET_BOOL keyedit\.remove\.uid\.okay/; my $KEYEDIT_DELSIG_PROMPT = qr/GET_BOOL keyedit\.delsig\.(?:unknown|invalid|valid)/; # we won't delete selfsigs my $KEYEDIT_KEYEDIT_OR_DELSIG_PROMPT = qr/$KEYEDIT_PROMPT|$KEYEDIT_DELSIG_PROMPT/; my $KEYEDIT_DELSUBKEY_PROMPT = qr/GET_BOOL keyedit\.remove\.subkey\.okay/; my $KEYEDIT_SIGNUID_CLASS_PROMPT = qr/GET_LINE sign_uid\.class/; my $KEYEDIT_SIGNUID_PROMPT = qr/GET_BOOL sign_uid\.okay/; sub version($) { my ($fd) = @_; print $fd "$0 $VERSION - (c) 2004, 2005, 2006 Peter Palfrader et al.\n"; } sub usage($$) { my ($fd, $exitcode) = @_; version($fd); print $fd "Usage: $0 [-eERS] [-m ] [-u ] [ ...]\n"; print $fd "Consult the manual page for more information.\n"; exit $exitcode; } ###### # export keys @$keyids from $gnupghome. In list context, fork and # return the pid and the file descriptor of its standard output; # otherwise, wait until the export is done and return the ASCII key. # # /!\ Failure to export a key will not be detected, unless *all* keys # couldn't be exported. Therefore for safe export/import, you need # to inspect '$asciikey' or the status FD on the import side. ###### sub export_keys($$@) { my ($gnupghome, $keyids, @export_options) = @_; myerror(1, "Nothing to export") unless defined $keyids and @$keyids; my @extra_args = ('--export-options', join (',', @export_options)) if @export_options; # don't armor when piping since it's faster my $gpg = mkGnuPG( homedir => $gnupghome, armor => (wantarray ? 0 : 1), extra_args => \@extra_args ); my $handles = mkGnuPG_fds( stdout => undef ); my $pid = $gpg->export_keys( handles => $handles, command_args => $keyids ); if (wantarray) { return ($pid, $handles->{stdout}); } else { my $asciikey = do { local $/; readline $handles->{stdout} }; done_gpg($pid, $handles); return $asciikey; } } ###### # Create an email to $address. If $can_encrypt is true then the mail # will be PGP/MIME encrypted to $longkeyid. # # $longkeyid, $uid, and @attached will be used in the email and the template. ###### # create_mail($address, $can_encrypt, $longkeyid, $uid, @attached); sub create_mail($$$@) { my ($address, $can_encrypt, $key_id, @keys) = @_; my $template = Text::Template::->new(TYPE => 'STRING', SOURCE => $CONFIG{'mail-template'}); myerror(1, "Cannot create template: $Text::Template::ERROR") unless defined $template; my $message = $template->fill_in(HASH => { key => $key_id, uids => [ map {$_->{'text'}} @keys ], owner => $CONFIG{'owner'}}); myerror(1, "Cannot fill in template: $Text::Template::ERROR") unless defined $message; my $message_entity = MIME::Entity->build( Type => "text/plain", Charset => "utf-8", Disposition => 'inline', Data => Encode::encode_utf8($message)); my @key_entities; for my $key (@keys) { $message_entity->attach( Type => "application/pgp-keys", Disposition => 'attachment', Encoding => "7bit", Description => "PGP Key 0x$key_id, uid ".Encode::encode_utf8($key->{text})." ($key->{serial}), signed by 0x$CONFIG{keyid}[0]", Data => $key->{key}, Filename => "0x$key_id.$key->{serial}.signed-by-0x$CONFIG{keyid}[0].asc"); }; if ($can_encrypt) { my $gpg = mkGnuPG( homedir => $GNUPGHOME, armor => 1, textmode => 1 ); $gpg->options->push_recipients($key_id); $gpg->options->push_recipients(@{$CONFIG{'also-encrypt-to'}}) if defined $CONFIG{'also-encrypt-to'}; my $handles = mkGnuPG_fds( stdin => undef, stdout => undef, status => undef ); my $pid = $gpg->encrypt(handles => $handles); my %output = readwrite_gpg($handles, stdin => $message_entity->stringify(), autoclose => 1); done_gpg($pid, $handles); my ($message, $status) = @output{qw/stdout status/}; if ($message eq '') { if ($status =~ /^\[GNUPG:\] INV_RECP ([0-9]+) ([0-9A-F]+)$/m and defined $CONFIG{'also-encrypt-to'}) { my $reason = $1; my $keyid = $2; if (grep { $_ eq $keyid } @{$CONFIG{'also-encrypt-to'}}) { mywarn "Could not encrypt to $keyid, specified in CONFIG{'also-encrypt-to'}"; mywarn "Try to update the key using gpg --homedir=$GNUPGHOME --import "; return; }; }; mywarn "No data from gpg for encrypting mail; status output was:\n$status"; return; }; $message_entity = MIME::Entity->build( Type => 'multipart/encrypted; protocol="application/pgp-encrypted"', Encoding => '7bit' ); $message_entity->attach( Type => "application/pgp-encrypted", Filename => "signedkey.msg", Disposition => 'attachment', Encoding => "7bit", Data => "Version: 1\n" ); $message_entity->attach( Type => "application/octet-stream", Filename => 'msg.asc', Disposition => 'inline', Encoding => "7bit", Data => $message ); }; my $from = Encode::encode('MIME-Q', $CONFIG{owner})." <$CONFIG{email}>"; $message_entity->head->add("From", $from); $message_entity->head->add("Date", strfCtime("%a, %e %b %Y %H:%M:%S %z", localtime)); $message_entity->head->add("Subject", Encode::encode('MIME-Q', $CONFIG{'mail-subject'} =~ s/%k/$key_id/gr)); $message_entity->head->add("To", $address); $message_entity->head->add("Sender", $from); $message_entity->head->add("Reply-To", $CONFIG{'reply-to'}) if defined $CONFIG{'reply-to'}; $message_entity->head->add("Bcc", $CONFIG{'bcc'}) if defined $CONFIG{'bcc'}; $message_entity->head->add("User-Agent", $USER_AGENT); return $message_entity; } ###### # send a mail message (MIME::Entity) ###### my $warned_about_broken_mailer_send = 0; sub send_message($) { my ($message_entity) = @_; if ((scalar @{$CONFIG{'mailer-send'}} > 0) && !$warned_about_broken_mailer_send) { mywarn("You have set arguments to pass to Mail::Mailer. Better fix your MTA. (Also, Mail::Mailer's error reporting is non existent, so it won't tell you when it doesn't work.)"); $warned_about_broken_mailer_send = 1; }; $message_entity->send(@{$CONFIG{'mailer-send'}}); } ###### # clean up a UID so that it can be used on the FS. ###### sub sanitize_uid($) { my ($uid) = @_; my $good_uid = $uid; $good_uid =~ tr#/:\\#_#; trace2("[sanitize_uid] changed UID from $uid to $good_uid.\n") if $good_uid ne $uid; return $good_uid; } # Delete all non self-sigs that are not made by one of the @$keyids, and # return the date of the most recent signature and a hash reference # {$signer => $level} of the keys in @$keyids that have an exportable # signature on that $uid. If $keep_lsigs_only, our exportable # signatures are removed as well. sub delete_signatures($$$$;$) { my ($handles, $longkeyid, $uid, $keyids, $keep_lsigs_only) = @_; readwrite_gpg($handles, command => "uid 0", status => $KEYEDIT_PROMPT); # unmark all uids from delsig readwrite_gpg($handles, command => "uid $uid", status => $KEYEDIT_PROMPT); # mark $uid for delsig my $last_signed_on = 0; my %xsigners; my %output = readwrite_gpg($handles, command => "delsig", status => $KEYEDIT_DELSIG_PROMPT); while($output{status} =~ /$KEYEDIT_DELSIG_PROMPT/m) { # sig:?::17:EA2199412477CAF8:1058095214:::::13x my @sigline = grep /^sig:/, (split /\n/, $output{stdout}); my $answer = "no"; if (!@sigline) { debug("[sigremoval] no sig line here, only got:\n".$output{stdout}); } else { # only if we found a sig here - we never remove revocation packets for instance my $sig = pop @sigline; $sig =~ /^sig:(?:[^:]*:){3}([0-9A-F]{16}):(\d+):(?:[^:]*:){4}(1[0-3]|30)([lx])(?::.*)?$/ or mywarn("I hit a bug, please report: Couldn't parse sigline $sig"); debug("[sigremoval] doing sigline $sig"); if ($1 eq $longkeyid) { debug("[sigremoval] selfsig ($1)"); $answer = "no"; } elsif (grep { $1 eq $_ } @$keyids and $3 != 30) { debug("[sigremoval] signed by us ($1)"); $answer = ($keep_lsigs_only and $4 eq 'x') ? "yes" : "no"; $last_signed_on = $2 if $last_signed_on < $2; $xsigners{$1} = $3-10 if $4 eq 'x'; } else { debug("[sigremoval] not interested in that sig ($1)"); $answer = "yes"; }; mywarn("I hit a bug, please report: Found the following ".($#sigline+2)." siglines in that part of the dialog:\n".$output{stdout}) if @sigline; } %output = readwrite_gpg($handles, command => $answer, status => $KEYEDIT_KEYEDIT_OR_DELSIG_PROMPT); } return ($last_signed_on, \%xsigners); } ## # Check the local user keys. # # This function checks if the keyids defined through the --local-user # command line option or set in ~/.caffrc are valid and known to be one of the # keyids listed in ~/.caffrc. # # @return an array containing the local user keys\n # (undef) if no valid key has been found # sub get_local_user_keys() { # No user-defined key id has been specified by the user, no need for # further checks return @{$CONFIG{'keyid'}} unless $CONFIG{'local-user'}; # Parse the list of keys my @key_list = ref $CONFIG{'local-user'} ? @{$CONFIG{'local-user'}} : split /\s*,\s*/, $CONFIG{'local-user'}; my @local_user; # Check every key defined by the user... for my $user_key (@key_list) { unless ($user_key =~ m/^((?:0x)?\p{AHex}{8}|(?:0x)?\p{AHex}{16}|\p{AHex}{40}|(?:\p{AHex}{4} ){5}(?: \p{AHex}{4}){5})$/) { mywarn "Local-user $user_key is not a valid keyid"; next; } $user_key =~ s/^0x//; $user_key =~ y/ //d; if (my @matching_keyids = grep {$user_key =~ /\Q$_\E$/i or /\Q$user_key\E$/i} @{$CONFIG{'keyid'}}) { push @local_user, @matching_keyids; # @{$CONFIG{'keyid'}} is always a list of long keyids } else { mywarn "Local-user $user_key is not defined as one of your keyid in ~/.caffrc (it will not be used)"; } } # If no local-user key are valid, there is no need to go further myerror(1, "None of the local-user keys seem to be known as a keyid listed in ~/.caffrc") unless @local_user; return @local_user; } ## # Import keys from a gnupghome to another. # # @param keyids keyids of the OpenPGP keys to import # @param src_gnupghome gnupghome directory where to export the key from # @param dst_gnupghome gnupghome directory where to import the key into # @param die_on_error whether to die if some of the keyids couldn't be imported # @param import_options a list of import-options, see gpg(1) # # @ return a hash reference mapping each key ID to the list of matching # imported key fingerprint. # sub import_keys_from_gnupghome($$$$@) { my ($keyids, $src_gpghome, $dst_gpghome, $die_on_error, @import_options) = @_; my $src = $src_gpghome // "your normal GnuPGHOME"; my $dst = $dst_gpghome // "your normal GnuPGHOME"; my @extra_args; push @import_options, 'import-local-sigs' if $CONFIG{'gpg-sign-type'} =~ /l/ and !grep /import-local-sigs$/, @import_options; push @import_options, 'keep-ownertrust' unless defined $dst_gpghome or GnuPG_version('2.1.0') >= 0; # don't modify our own trustdb push @extra_args, '--min-cert-level=1' if grep { $_ eq 'import-clean' } @import_options; push @extra_args, '--import-options', join (',', @import_options) if @import_options; # export the (non-armored) keys to $pipe debug("Exporting key(s) ".(join ',', @$keyids)." from $src to $dst"); my @export_options = ('export-local-sigs') if grep {$_ eq 'import-local-sigs'} @import_options; my ($ePid, $pipe) = export_keys($src_gpghome, $keyids, @export_options); my $gpg = mkGnuPG( homedir => $dst_gpghome, quiet => 1, extra_args => \@extra_args ); my $handles = mkGnuPG_fds( stdin => $pipe, status => undef ); # import keys from $pipe my $iPid = $gpg->import_keys( handles => $handles ); my $status = import_loop($handles->{status}, defined $src_gpghome ? 0 : 1, $keyids); done_gpg($iPid, $handles); # import done waitpid $ePid => 0; # export done my @failed = grep { !@{$status->{$_}} } keys %$status; if (@failed) { my $msg = "Couldn't import key(s) ".(join ',', @failed)." from $src"; $die_on_error ? myerror(1, $msg) : info($msg, 0); } return $status; } ## # Import loop. # # @param fh the status file handle from GnuPG::Interface # @param verbose whether to list the status of each key as it # is being imported. # @param keyids an array of keyids to be imported # @param ignore_unexpected whether not to print a warning upon receiving # an unexpected key # # @ return a hash reference mapping each key ID to the list of matching # imported key fingerprint. # sub import_loop($$$;$) { my ($fh, $verbose, $keyids, $ignore_unexpected) = @_; # [GNUPG:] IMPORT_OK 0 5B00C96D5D54AEE1206BAF84DE7AAF6E94C09C7F # [GNUPG:] NODATA 1 # [GNUPG:] NODATA 1 # [GNUPG:] IMPORT_OK 0 25FC1614B8F87B52FF2F99B962AF4031C82E0039 my %status = map { $_ => [] } @$keyids; while (<$fh>) { # inspect the $status FD as data gets out if (/^\[GNUPG:\] IMPORT_OK (\d+) ([0-9A-F]{40})$/) { my ($r, $fpr) = ($1, $2); my @matching_keyids = grep { $fpr =~ /\Q$_\E$/ } @$keyids; unless (@matching_keyids) { unless ($ignore_unexpected) { mywarn("Imported unexpected key $fpr. Are you trying to work on a subkey?"); } elsif ($verbose) { info( "Key $fpr ". ($r == 0 ? 'not changed' : 'imported'), ($r == 0 ? undef : 1) ); } next; } debug( "Imported $fpr for ".join(',', @matching_keyids)); info( "Key " .join(',', @matching_keyids).' '. ($r == 0 ? 'not changed' : 'imported'), ($r == 0 ? undef : 1) ) if $verbose; push @{$status{$_} //= []}, $fpr foreach @matching_keyids; } elsif (/^\[GNUPG:\] IMPORT_OK \d+ ([0-9A-F]{32})$/) { mywarn("Imported v3 key $1. Version 3 keys are obsolete, should not be used, and are not and will not be properly supported."); } elsif (!/^\[GNUPG:\]\ (?:NODATA\ \d | IMPORT_RES\ .+ | IMPORTED\ .+ | KEYEXPIRED\ \d+ | SIGEXPIRED\ (?:\ deprecated-use-keyexpired-instead)? | KEY_CONSIDERED\ [0-9A-F]{40}\ \d+ | FAILURE\ recv-keys\ \d+ )$/x) { mywarn("Got unknown reply from gpg: ".$_); } } return \%status; } ## # Import keys to be signed into caff gnupghome directory. # # This function imports the keys the user wants to sign into the caff gnupghome # directory. We looks for the keys in the the user gnupghome directory first, # and in the key files specified by the user if not all of the keys have been # found. # sub import_keys_to_sign($) { my $keyids = shift; return unless $CONFIG{'keys-from-gnupg'} or @{$CONFIG{'key-files'}} or !$CONFIG{'no-download'}; # map each keyid to a list of matching fingerprints; there is a # collision if a keyid is mapped to multiple fingerprints, but we'll # detect that later in the code my $status = { map { $_ => [] } @$keyids }; if ($CONFIG{'keys-from-gnupg'}) { notice("Importing keys from your normal GnuPGHOME (".($ENV{'GNUPGHOME'} // "~/.gnupg").")"); merge_import_status( $status, import_keys_from_gnupghome($keyids, undef, $GNUPGHOME, 0) ); } foreach my $keyfile (@{$CONFIG{'key-files'}}) { notice("Importing key file $keyfile"); my $gpg = mkGnuPG( homedir => $GNUPGHOME, quiet => 1 ); $gpg->options->push_extra_args(qw/--import-options import-local-sigs/) if $CONFIG{'gpg-sign-type'} =~ /l/; my $handles = mkGnuPG_fds( status => undef ); my $pid = $gpg->import_keys( handles => $handles, command_args => $keyfile ); merge_import_status( $status, import_loop($handles->{status}, 1, $keyids, 1) ); done_gpg($pid, $handles); } # Receive keys from keyserver unless ($CONFIG{'no-download'}) { notice("Fetching keys from a keyserver (this may take a while)..."); my @args = (extra_args => ['--keyserver='.$CONFIG{'keyserver'}]) if defined $CONFIG{'keyserver'}; my $gpg = mkGnuPG( homedir => $GNUPGHOME, @args ); # logger: requesting key ... from hkp # stdout: gpgkeys: key ... not found on keyserver my $handles = mkGnuPG_fds( status => undef ); my $pid = $gpg->recv_keys(handles => $handles, command_args => $keyids); my $s = import_loop($handles->{status}, 1, $keyids); merge_import_status($status, $s); done_gpg($pid, $handles); my @failed = grep { !@{$s->{$_}} } keys %$s; info("Couldn't import key(s) ".(join ',', @failed)." from the keyserver", 0) if @failed; } my @failed = grep { !@{$status->{$_}} } keys %$status; if (@failed) { exit 1 unless ask ("Some keys could not be imported - continue anyway?", 0); mywarn("Assuming ". join(' ', @failed).' '.($#failed > 0 ? 'are' : 'is a')." fine keyid".($#failed > 0 ? 's' : '')); } } sub merge_import_status($$) { foreach my $keyid (keys %{$_[1]}) { push @{$_[0]->{$keyid} //= []}, @{$_[1]->{$keyid}}; } } ## # A non-localized version of POSIX::strftime. # sub strfCtime($@) { my $lc_time = setlocale(POSIX::LC_TIME); setlocale(POSIX::LC_TIME, 'C'); my $str = strftime(@_); setlocale(POSIX::LC_TIME, $lc_time); return $str; } ################### # argument handling ################### Getopt::Long::config('bundling'); if (!GetOptions ( '-h' => \$PARAMS->{'help'}, '--help' => \$PARAMS->{'help'}, '--version' => \$PARAMS->{'version'}, '-V' => \$PARAMS->{'version'}, '-u=s' => \$PARAMS->{'local-user'}, '--local-user=s' => \$PARAMS->{'local-user'}, '-e' => \$PARAMS->{'export-old'}, '--export-old' => \$PARAMS->{'export-old'}, '-E' => \$PARAMS->{'no-export-old'}, '--no-export-old' => \$PARAMS->{'no-export-old'}, '-m:s' => \$PARAMS->{'mail'}, '--mail:s' => \$PARAMS->{'mail'}, '-M' => \$PARAMS->{'no-mail'}, '--no-mail' => \$PARAMS->{'no-mail'}, '-R' => \$PARAMS->{'no-download'}, '--no-download' => \$PARAMS->{'no-download'}, '-S' => \$PARAMS->{'no-sign'}, '--no-sign' => \$PARAMS->{'no-sign'}, '--key-file=s@' => \$PARAMS->{'key-files'}, '--keys-from-gnupg' => \$PARAMS->{'keys-from-gnupg'}, '--debug' => \$PARAMS->{'debug'}, )) { usage(\*STDERR, 1); } if ($PARAMS->{'help'}) { usage(\*STDOUT, 0); } if ($PARAMS->{'version'}) { version(\*STDOUT); exit(0); } load_config(); my $NOW = time; my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($NOW); my $DATE_STRING = sprintf("%04d-%02d-%02d", $year+1900, $mon+1, $mday); if (-t \*STDIN) { # we're already talking to a TTY usage(\*STDERR, 1) unless @ARGV; *TTY = *STDIN; } else { my @checksums; my $goodblock; my $got_input; # detect xargs, /dev/null, ... while () { unless ($got_input) { notice("Reading gpgparticipants formatted input on STDIN"); $got_input = 1; } if (/^(\S+)\s+Checksum:\s+[_ 0-9A-F]+(?:\s+\[(.)\])?$/i) { # ensure the checksum is (claimed to be) verified my ($md, $r) = ($1, $2); while (!defined $r) { $_ = ; if (/^\s+[_ 0-9A-F]+\s+\[(.)\]$/i) { $r = $1; } elsif (!/^(:?\s+[_ 0-9A-F]+)?$/i) { myerror(1, "Unexpected input line: $_"); } } myerror(1, "$md checksum wasn't marked as verified!") unless lc $r eq 'x'; notice "Found $md checksum (marked as verified, assumed good)"; push @checksums, uc $md; } elsif (/^(?:-+|_+)$/) { $goodblock = 0; } elsif (/^(#*)\d*\s+\[(.)\] Fingerprint(?:\(s\)|s)? OK\s+\[(.)\] ID OK\s*$/) { $goodblock = (!$1 and lc $2 eq 'x' and lc $3 eq 'x') ? 1 : 0; } elsif (/^ {5,}Key fingerprint = ([A-F0-9]{32}|(?:[A-F0-9]{2} ){8}(?: [A-F0-9]{2}){8})$/) { mywarn("Ignoring v3 fingerprint ".($1 =~ y/ //dr).". v3 keys are obsolete."); } elsif (/^ {5,}(?:Key fingerprint = )?([A-F0-9]{40}|(?:[A-F0-9]{4} ){5}(?: [A-F0-9]{4}){5})$/) { my $fpr = ($1 =~ y/ //dr); if ($goodblock) { info("Adding fingerprint $fpr", 1); push @KEYIDS, $fpr; } else { info("Ignoring fingerprint $fpr", 0); } } } if ($got_input) { if (!@checksums) { mywarn "No checksum found!"; } elsif (!grep { my $x = $_; grep { $x eq $_ } qw/SHA256 SHA384 SHA512 SHA224/ } @checksums) { mywarn "No checksum of the SHA-2 family found!"; } } close STDIN; open TTY, '<', '/dev/tty' or myerror(1,"No TTY.") } my $TERMIOS = do { my $t = POSIX::Termios::->new(); $t->getattr(fileno(TTY)) // die "getattr: $!"; $t; }; for my $hashkey (qw{local-user no-download no-sign no-mail mail keys-from-gnupg}) { $CONFIG{$hashkey} = $PARAMS->{$hashkey} if defined $PARAMS->{$hashkey}; } # If old 'no-mail' parameter, or if the 'mail' parameter is set to 'no' if ( defined $CONFIG{'no-mail'} || (defined $CONFIG{'mail'} && $CONFIG{'mail'} eq 'no') ) { $CONFIG{'mail'} = 'no'; } elsif ( !defined $CONFIG{'mail'} ) { $CONFIG{'mail'} = 'ask-yes'; } $CONFIG{'mail-cant-encrypt'} //= $CONFIG{'mail'}; push @{$CONFIG{'key-files'}}, @{$PARAMS->{'key-files'}} if defined $PARAMS->{'key-files'}; for my $keyid (map { split /\n/ } @ARGV) { # caff "`cat txt`" is a single argument if ($keyid =~ /^(\p{AHex}{32}|(?:\p{AHex}{2} ){8}(?: \p{AHex}{2}){8})$/) { mywarn("Ignoring v3 fingerprint ".($keyid =~ y/ //dr).". v3 keys are obsolete."); next; } elsif ($keyid !~ /^((?:0x)?\p{AHex}{8}|(?:0x)?\p{AHex}{16}|\p{AHex}{40}|(?:\p{AHex}{4} ){5}(?: \p{AHex}{4}){5})$/) { print STDERR "$keyid is not a keyid.\n"; usage(\*STDERR, 1); } $keyid =~ s/^0x//; $keyid =~ y/ //d; # gpg --fingerprint includes spaces push @KEYIDS, uc($keyid); } if (GnuPG_version('2.1.0') >= 0) { my %sockets; unless ($CONFIG{'no-sign'}) { # Ensure we have a working agent for our secret key material my $secdir = $CONFIG{'secret-keyring'}; $secdir =~ s#/[^/]+$## unless -d $secdir; mysystem('gpg-connect-agent', '--homedir', $secdir, '/bye'); $sockets{'agent-socket'} = GnuPG_version('2.1.13') < 0 ? # gpgconf < 2.1.13 doesn't understand --homedir; but on # these versions the gpg-agent socket path is always # $GNUPGHOME/S.gpg-agent. "$secdir/S.gpg-agent" : gpgconf('--homedir', $secdir, '--list-dirs')->{'agent-socket'}; } unless ($CONFIG{'no-download'}) { # Ensure we have a working agent for the downloads my $homedir = $ENV{'GNUPGHOME'} // "$ENV{'HOME'}/.gnupg"; mysystem('gpg-connect-agent', '--homedir', $homedir, '--dirmngr', '/bye'); $sockets{'dirmngr-socket'} = GnuPG_version('2.1.13') < 0 ? # gpgconf < 2.1.13 doesn't understand --homedir; but on # these versions the gpg-agent socket path is always # $GNUPGHOME/S.dirmngr. "$homedir/S.dirmngr" : gpgconf('--homedir', $homedir, '--list-dirs')->{'dirmngr-socket'}; } foreach my $k (keys %sockets) { my $socket = $sockets{$k}; my $l = GnuPG_version('2.1.13') < 0 ? $socket =~ s#.*/#$GNUPGHOME/#r : gpgconf('--homedir', $GNUPGHOME, '--list-dirs')->{$k}; if (-l $l) { unlink $l } elsif (-S $l) { # don't run agents in caff's homedir myerror(1, "$l: socket exists; runaway gpg-agent?"); } elsif (! -S $socket) { myerror(1, "Missing socket $socket"); } debug "Creating symlink $l to $socket"; symlink $socket, $l or myerror(1, "Cannot symlink: $!"); } } elsif ($CONFIG{'also-lsign-in-gnupghome'} eq 'auto' and $CONFIG{'gpg-sign-type'} !~ /l/) { # Ensure there is a working gpg-agent if $CONFIG{'also-lsign-in-gnupghome'} is 'auto' system qw/gpg-agent -q/; unless ($? == 0) { mywarn("No gpg-agent running: set \$CONFIG{'also-lsign-in-gnupghome'} = 'ask'"); $CONFIG{'also-lsign-in-gnupghome'} = 'ask'; } } ################################## # import own keys and keys to sign ################################## import_keys_from_gnupghome($CONFIG{'keyid'}, undef, $GNUPGHOME, 1); import_keys_from_gnupghome($CONFIG{'also-encrypt-to'}, undef, $GNUPGHOME, 0) if defined $CONFIG{'also-encrypt-to'}; @LOCAL_USER = get_local_user_keys() unless $CONFIG{'no-sign'}; import_keys_to_sign(\@KEYIDS); if ($CONFIG{'ask-sign'} && ! $CONFIG{'no-sign'}) { $CONFIG{'no-sign'} = ! ask("Continue with signing?", 1); } my %KEYS; for my $keyid (@KEYIDS) { # get key listing (and ensure there is no collision) #################################################### my $gpg = mkGnuPG( homedir => $GNUPGHOME, extra_args => ['--with-fingerprint', '--with-colons'] ); my $handles = mkGnuPG_fds( stdout => undef ); # process the keys one by one so we can detect collisions my $pid = $gpg->list_public_keys( handles => $handles, command_args => [$keyid] ); my @matching_keys; while (readline $handles->{stdout}) { if (/^pub:([^:]+):(?:[^:]*:){2}([0-9A-F]{16}):(?:[^:]*:){6}([^:]+)/) { push @matching_keys, { validity => $1, longkeyid => $2, capability => $3, uids => [], subkeys => [] }; } elsif (/^fpr:(?:[^:]*:){8}([0-9A-F]{40}|[0-9A-F]{32})(?::.*)?$/) { $matching_keys[$#matching_keys]->{fpr} //= $1; } elsif (/^sub:[^:]+:(?:[^:]*:){2}([0-9A-F]{16}):/) { push @{$matching_keys[$#matching_keys]->{subkeys}}, $1; } elsif (/^(uid|uat):([^:]+):(?:[^:]*:){5}([0-9A-F]{40}):[^:]*:([^:]+)/) { my $uid = { type => $1 , validity => $2 , hash => $3 , text => $1 eq 'uid' ? $4 : '[attribute]' }; $uid->{text} =~ s/\\x(\p{AHex}{2})/ chr(hex($1)) /ge; # --with-colons always outputs UTF-8 $uid->{text} = Encode::decode_utf8($uid->{text}); $uid->{address} = email_valid $uid->{text} if $uid->{type} eq 'uid'; push @{$matching_keys[$#matching_keys]->{uids}}, $uid; } elsif (!/^(?:rvk|tru):/) { chomp; mywarn("Got unknown reply from gpg: ".$_); } } done_gpg($pid, $handles); unless (@matching_keys) { mywarn("No public keys found with list-key $keyid (note that caff uses its own keyring in $GNUPGHOME)"); next; } my $key; foreach (@matching_keys) { my $reason = $_->{fpr} =~ /^\p{AHex}{32}$/ ? 'obsolete v3' : $_->{validity} =~ /e/ ? 'expired' : $_->{validity} =~ /i/ ? 'invalid' : $_->{validity} =~ /r/ ? 'revoked' : $_->{capability} =~ /D/ ? 'disabled' : do { $key = $_; last }; mywarn("Ignoring $reason key $_->{fpr}"); } mywarn( "More than one key matched $keyid (assuming $key->{fpr}). " . "Try to specify the long keyid or full fingerprint to avoid collisions.") if $#matching_keys > 0 and defined $key; if (defined $key) { $KEYS{$keyid} = $key; } else { my $msg = "public key found with list-key $keyid"; mywarn( @matching_keys ? "No valid $msg" : "No $msg (note that caff uses its own keyring in $GNUPGHOME)" ); } } unless (keys %KEYS) { notice("No keys to sign found", 0); exit 0; } for my $keyid (@KEYIDS) { next unless exists $KEYS{$keyid}; my ($longkeyid, $fpr) = @{$KEYS{$keyid}}{qw/longkeyid fpr/}; ########### # sign keys ########### unless ($CONFIG{'no-sign'}) { notice("Sign the following keys according to your policy, then exit gpg with 'save' after signing each key"); foreach my $local_user (@LOCAL_USER) { my @command = ($CONFIG{'gpg'}); push @command, "--homedir=$GNUPGHOME"; push @command, @GNUPGOPTS if @GNUPGOPTS; push @command, '--secret-keyring', $CONFIG{'secret-keyring'} if GnuPG_version('2.1.0') < 0; push @command, qw/--no-auto-check-trustdb --trust-model=always/; push @command, '--local-user', $local_user; push @command, '--edit-key', $fpr; push @command, 'showphoto' if $CONFIG{'show-photos'}; push @command, $CONFIG{'gpg-sign-type'}.'sign'; push @command, split ' ', $CONFIG{'gpg-sign-args'} || ""; print join(' ', @command),"\n"; mysystem(@command); }; }; ################## # export and prune ################## # export the key ################ my $keydir = File::Temp->newdir( "caff-$keyid-XXXXX", TMPDIR => 1 ); # we can't use only one import here because the cleaning is done as the # keys come and our keys might not be imported yet import_keys_from_gnupghome($CONFIG{'keyid'}, $GNUPGHOME, $keydir, 1, 'import-minimal', 'import-local-sigs'); import_keys_from_gnupghome([$fpr], $GNUPGHOME, $keydir, 1, 'import-clean', 'import-local-sigs'); # the first UID. we won't delete that one when pruning for UATs because a key has to have at least one UID my @uids = @{$KEYS{$keyid}->{uids}}; my $first_uid = (grep {$_->{type} eq 'uid'} @uids)[0]; for (my $uid_number = 1; $uid_number <= $#uids+1; $uid_number++) { debug("Doing key $keyid, uid $uid_number"); my $uid = $uids[$uid_number-1]; # /!\ this serial is valid in caff's GnuPGHOME only, and can't # be relied upon if the keyring is modified in the meantime. $uid->{serial} = $uid_number; next if $uid->{validity} =~ /[eir]/; # skip expired / invalid / revokey UIDs # copy pubring to temporary gpghome ################################### my $uiddir = File::Temp->newdir( "caff-$keyid-$uid_number-XXXXX", TMPDIR => 1 ); foreach (qw/pubring.gpg pubring.kbx/) { copy($keydir.'/'.$_, $uiddir.'/'.$_) if -e $keydir.'/'.$_; } # prune it ########## my $gpg = mkGnuPG( homedir => $uiddir, extra_args => ['--with-colons'] ); my $handles = mkGnuPG_fds( command => undef, stdout => undef, status => undef ); my $pid = $gpg->wrap_call( commands => [ '--edit-key' ], command_args => [ $fpr ], handles => $handles ); debug("Starting edit session"); my %output = readwrite_gpg($handles, status => $KEYEDIT_PROMPT); # delete other uids ################### my $delete_some = 0; for (my $i = 1; $i <= $#uids+1; $i++) { # it's quicker with gpg2: 'uid *' then 'uid $i' next if $i == $uid_number; next if $uid->{type} ne 'uid' and $uids[$i-1]->{hash} eq $first_uid->{hash}; # keep the first UID debug("Marking UID $i ($uids[$i-1]->{hash}) for deletion"); readwrite_gpg($handles, command => "uid $i", status => $KEYEDIT_PROMPT); $delete_some++; } if ($delete_some) { debug("Need to delete $delete_some uids"); readwrite_gpg($handles, command => "deluid", status => $KEYEDIT_DELUID_PROMPT); readwrite_gpg($handles, command => "yes", status => $KEYEDIT_PROMPT); }; # delete all subkeys #################### if (@{$KEYS{$keyid}->{subkeys}}) { for (my $i = 1; $i <= $#{$KEYS{$keyid}->{subkeys}} + 1; $i++) { debug("Marking subkey $i ($KEYS{$keyid}->{subkeys}->[$i-1]) for deletion"); readwrite_gpg($handles, command => "key $i", status => $KEYEDIT_PROMPT); }; readwrite_gpg($handles, command => "delkey", status => $KEYEDIT_DELSUBKEY_PROMPT); readwrite_gpg($handles, command => "yes", status => $KEYEDIT_PROMPT); }; # delete signatures ################### # this shouldn't delete anything as $longkeyid is already clean, but maybe we didn't sign that uid with all keys in @{$CONFIG{'keyid'}} my ($last_signed_on, $xsigners) = delete_signatures($handles, $longkeyid, $uid->{hash}, $CONFIG{'keyid'}); delete_signatures($handles, $longkeyid, $first_uid->{hash}, []) if $uid->{type} ne 'uid'; # delete all sigs on the first UID if $uid is an attribute readwrite_gpg($handles, command => "save"); done_gpg($pid, $handles); debug("Done editing"); my $asciikey = export_keys($uiddir, [$fpr], 'export-local-sigs'); undef $uiddir; # delete dir unless ($asciikey) { mywarn "No data from gpg for export $fpr"; next; }; if ($last_signed_on) { # it's a bit inefficient to store the $asciikey in memory, # but it has been pruned so it's shouldn't be too big $uid->{key} = $asciikey; $uid->{xsigners} = $xsigners; $uid->{last_signed_on} = $last_signed_on; }; }; unless ($CONFIG{'also-lsign-in-gnupghome'} eq 'no') { # remove all exportable sigs, and import into our GnuPGHOME ########################################################### my $gpg = mkGnuPG( homedir => $keydir, extra_args => ['--with-colons'] ); my $handles = mkGnuPG_fds( command => undef, stdout => undef, status => undef ); my $pid = $gpg->wrap_call( commands => [ '--edit-key' ], command_args => [ $fpr ], handles => $handles ); debug("Starting edit session on $keyid"); my %output = readwrite_gpg($handles, status => $KEYEDIT_PROMPT); delete_signatures($handles, $longkeyid, $uids[$_]->{hash}, $CONFIG{'keyid'}, 1) foreach (0 .. $#uids); readwrite_gpg($handles, command => "save"); done_gpg($pid, $handles); debug("Done editing"); # import the pruned keys with our own local sigs only; this is # required even if there are no lsigs, to ensure we've got all # UIDs in our own GnuPGHOME import_keys_from_gnupghome( [$fpr], $keydir, undef, 1, 'import-local-sigs' ); } undef $keydir; # delete dir if ($CONFIG{'also-lsign-in-gnupghome'} eq 'ask') { # manually lsign the key ######################## foreach my $local_user (@LOCAL_USER) { my @command = ($CONFIG{'gpg'}); push @command, '--secret-keyring', $CONFIG{'secret-keyring'} if GnuPG_version('2.1.0') < 0; push @command, qw/--no-auto-check-trustdb --trust-model=always/; push @command, '--local-user', $local_user; push @command, '--edit-key', $fpr; push @command, 'showphoto' if $CONFIG{'show-photos'}; push @command, 'lsign'; push @command, split ' ', $CONFIG{'gpg-sign-args'} || ""; print join(' ', @command),"\n"; mysystem(@command); } } elsif ($CONFIG{'also-lsign-in-gnupghome'} eq 'auto') { # auto lsign the uids we for which we have an exportable sig ############################################################ my @uids = grep {exists $_->{xsigners}} @{$KEYS{$keyid}->{uids}}; my @signers = map {keys %{$_->{xsigners}}} @uids; # which of @LOCAL_USER has signed at least one UID in this key? @signers = grep { my $u = $_; grep { $u eq $_ } @signers } @LOCAL_USER; @signers = keys %{{ map { $_ => 1 } @signers }}; # remove duplicates to avoid double signing foreach my $u (@signers) { my @signeduids; # uids signed by $u foreach my $uid (@uids) { # we use UIDs hashes to distinguish and select UIDs; it's the only reliable way to identify them across keyrings push @signeduids, $uid if grep { $u eq $_ } (keys %{$uid->{xsigners}}) and !grep { $uid->{hash} eq $_->{hash} } @signeduids; } my $gpg = mkGnuPG( extra_args => ['--local-user' => $u, '--ask-cert-level', '--with-colons', '--no-batch'] ); $gpg->options->push_extra_args('--secret-keyring', $CONFIG{'secret-keyring'}) if GnuPG_version('2.1.0') < 0; $gpg->options->push_extra_args('--use-agent') if GnuPG_version('2.0.0') < 0; # we know there is a working agent my $handles = mkGnuPG_fds( command => undef, stdout => undef, status => undef ); my $pid = $gpg->wrap_call( commands => [ '--edit-key' ], command_args => [ $fpr ], handles => $handles ); debug("Starting edit session on $keyid, signer $u"); readwrite_gpg($handles, status => $KEYEDIT_PROMPT); foreach my $level (0..3) { my @signeduids_with_level = grep {$_->{xsigners}->{$u} eq $level} @signeduids; next unless @signeduids_with_level; notice("Key $longkeyid UID(s) #".(join ',', sort (map {$_->{serial}} @signeduids_with_level)).": lsign'ing with $u, cert level $level", 1); readwrite_gpg($handles, command => "uid 0", status => $KEYEDIT_PROMPT); # unselect UIDs readwrite_gpg($handles, command => "uid $_->{hash}", status => $KEYEDIT_PROMPT) for @signeduids_with_level; my %output = readwrite_gpg($handles, command => "lsign", status => qr/$KEYEDIT_SIGNUID_CLASS_PROMPT|$KEYEDIT_PROMPT/); next if $output{status} =~ /^\[GNUPG:\] $KEYEDIT_PROMPT/m; # already signed readwrite_gpg($handles, command => $level, status => $KEYEDIT_SIGNUID_PROMPT); readwrite_gpg($handles, command => "yes", status => $KEYEDIT_PROMPT); } readwrite_gpg($handles, command => "save"); done_gpg($pid, $handles); debug("Done editing"); } } } ############# # send emails ############# for my $keyid (@KEYIDS) { next unless exists $KEYS{$keyid}; my $longkeyid = $KEYS{$keyid}->{longkeyid}; my $can_encrypt = $KEYS{$keyid}->{capability} =~ /E/; my @uids = @{$KEYS{$keyid}->{uids}}; unless (grep {$_->{last_signed_on}} @uids) { info("Key 0x$longkeyid has no signed uids, skipping", 0); next; } my @attached; for my $uid (@uids) { my $text = defined $LOCALE ? $LOCALE->encode($uid->{text}) : $uid->{text}; trace("UID: $text\n"); if ($uid->{validity} =~ /[eir]/) { my $reason = $uid->{validity} =~ /e/ ? 'expired' : $uid->{validity} =~ /i/ ? 'invalid' : $uid->{validity} =~ /r/ ? 'revoked' : die; info("Key 0x$longkeyid ".(uc $uid->{type})." $uid->{serial} $text is $reason, skipping", 0); next; } unless ($uid->{last_signed_on}) { info("Key 0x$longkeyid ".(uc $uid->{type})." $uid->{serial} $text is not signed by me, skipping", 0); next; } if ($NOW - $uid->{last_signed_on} > $CONFIG{'export-sig-age'} and !ask("Signature on $text is old. Export?", 0, $PARAMS->{'export-old'}, $PARAMS->{'no-export-old'})) { next; } # save the armored key my $keydir = "$KEYSBASE/$DATE_STRING"; -d $keydir || mkdir $keydir, 0700 or myerror(1, "Cannot mkdir $keydir: $!"); my $keyfile = "$keydir/$longkeyid.key.$uid->{serial}.".sanitize_uid($text).".asc"; open my $KEY, '>', $keyfile or myerror(1, "Cannot open $keyfile: $!"); debug "Writing armored key 0x$longkeyid to $keyfile"; print $KEY $uid->{key}; close $KEY; if ($uid->{type} eq 'uat') { if (ask("UID $text is an attribute UID, attach it to every email?", 1)) { push @attached, $uid; $uid->{export} = 1; } } elsif (!defined $uid->{address}) { if (ask("UID $text is no email address, attach it to every email?", 1)) { push @attached, $uid; $uid->{export} = 1; } } else { $uid->{export} = 1; } info("Key 0x$longkeyid ".(uc $uid->{type})." $uid->{serial} $text done", 1); } @uids = grep {$_->{last_signed_on}} @uids; # ignore UIDs we didn't sign delete $_->{key} foreach grep {!$_->{export}} @uids; # delete non-exported keys if (!grep {defined $_->{address}} @uids) { mywarn "No signed RFC 5322 UID on $longkeyid; won't send other signed UID and attributes!" if @attached; } elsif (grep {$_->{export}} @uids) { notice("Key 0x$longkeyid has no encryption capabilities, mail(s) will be sent/stored unencrypted", 0) unless $can_encrypt; my $sendmail = $can_encrypt ? $CONFIG{'mail'} : $CONFIG{'mail-cant-encrypt'}; for my $uid (@uids) { next unless defined $uid->{address}; next unless $uid->{export} or @attached; my @keys = @attached; unshift @keys, $uid if exists $uid->{key}; my $mail = create_mail($uid->{address}, $can_encrypt, $longkeyid, @keys); if (defined $mail) { my @sentuids = map {$_->{text}} @attached; unshift @sentuids, $uid->{text} if $uid->{export}; do { $_ = $LOCALE->encode($_) foreach @sentuids; } if defined $LOCALE; my $text = join(', ', map {"'$_'"} @sentuids); my $should_send_mail = ask("Mail ".($can_encrypt ? '' : '*unencrypted* ')."signature for $text to '$uid->{address}'?", $sendmail ne 'ask-no', $sendmail eq 'yes', $sendmail eq 'no'); send_message($mail) if $should_send_mail; my $keydir = "$KEYSBASE/$DATE_STRING"; my $mailfile = "$keydir/$longkeyid.mail.".($should_send_mail ? '' : 'unsent.').$uid->{'serial'}.".".sanitize_uid($text); open my $MAILFILE, '>', $mailfile or myerror(1, "Cannot open $mailfile: $!"); debug "Writing message to $mailfile"; $mail->print($MAILFILE); close $MAILFILE; } else { mywarn "Generating mail failed"; } } } info("Key 0x$longkeyid done", 1); } END { if (defined $TERMIOS and defined (my $fd = fileno(TTY))) { $TERMIOS->setattr($fd, POSIX::TCSANOW) // warn "setattr: $!"; } } ########################### # the default mail template ########################### __DATA__ Hi, please find attached the user id{(scalar @uids >= 2 ? 's' : '')} {foreach $uid (@uids) { $OUT .= "\t".$uid."\n"; };}of your key {$key} signed by me. If you have multiple user ids, I sent the signature for each user id separately to that user id's associated email address. You can import the signatures by running each through `gpg --import`. Note that I did not upload your key to any keyservers. If you want this new signature to be available to others, please upload it yourself. With GnuPG this can be done using gpg --keyserver hkps://keyserver.ubuntu.com --send-key {$key} If you have any questions, don't hesitate to ask. Regards, -- {$owner} signing-party-2.12/caff/caffrc.sample000066400000000000000000000043511500571021200175530ustar00rootroot00000000000000# vim:ft=perl: $CONFIG{'owner'} = 'John Doe'; $CONFIG{'email'} = 'user@example.com'; # you can get your long keyid from # gpg --with-colons --list-key # # if you have a v4 key, it will simply be the last 16 digits of # your fingerprint. # This is the list of keys whose signatures you want to mail around. Usually # you would list all your keys here. To specify which keys to sign with, set # local-user. See the manpage for further details. $CONFIG{'keyid'} = [ qw{DE7AAF6E94C09C7F 62AF4031C82E0039} ]; $CONFIG{'also-encrypt-to'} = [ qw{DE7AAF6E94C09C7F} ]; $CONFIG{'caffhome'} = $ENV{'HOME'}.'/.caff'; # The options below need not be changed for normal operation. # Paths to GnuPG binaries: # $CONFIG{'gpg'} = 'gpg'; # $CONFIG{'gpg-sign'} = $CONFIG{'gpg'}; # $CONFIG{'gpg-delsig'} = '/home/weasel/tmp/gpg/gnupg-1.3.92/g10/gpg'; # defaults to ~/.gnupg/secring.gpg # $CONFIG{'secret-keyring'} = '/tmp/gpg/secring.gpg'; # Don't export UIDs by default, on which your latest signature is older than this age. # $CONFIG{'export-sig-age'} = 24*60*60; # Keyserver to download keys from. Default: hkps://keyserver.ubuntu.com # $CONFIG{'keyserver'} = 'pgp.surfnet.nl'; # Boolean options, all default to false (0). # # Skip fetching the keys from the keyserver. # $CONFIG{'no-download'} = 1; # Skip signing the keys. # $CONFIG{'no-sign'} = 1; # Ask to continue before starting the signing (for offline signing). # $CONFIG{'ask-sign'} = 1; # $CONFIG{'mail-template'} = <<'EOM' # Hi, # # please find attached the user id{(scalar @uids >= 2 ? 's' : '')} # {foreach $uid (@uids) { # $OUT .= "\t".$uid."\n"; # };}of your key {$key} signed by me. # # Note that I did not upload your key to any keyservers. # If you have multiple user ids, I sent the signature for each user id # separately to that user id's associated email address. You can import # the signatures by running each through `gpg --import`. # # If you want this new signature to be available to others, please upload # it yourself. With GnuPG this can be done using # gpg --keyserver hkps://keyserver.ubuntu.com --send-key {$key} # # If you have any questions, don't hesitate to ask. # # Regards, # {$owner} # EOM signing-party-2.12/caff/pgp-clean000077500000000000000000000335431500571021200167250ustar00rootroot00000000000000#!/usr/bin/perl -w # pgp-clean -- remove all non-self signatures from key # # Copyright (c) 2004, 2005 Peter Palfrader # Copyright (c) 2006 Christoph Berg # # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # 3. The name of the author may not be used to endorse or promote products # derived from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. =pod =head1 NAME pgp-clean -- remove all non-self signatures from key =head1 SYNOPSIS =over =item B [B<-s>] I [I ...] =back =head1 DESCRIPTION B takes a list of keyids on the command line and outputs an ascii-armored keyring on stdout for each key with all signatures except self-signatures stripped. Its use is to reduce the size of keys sent out after signing (e.g. with B). =head1 OPTIONS =over =item B<-s> B<--export-subkeys> Do not remove subkeys. (Pruned by default.) =item I Use this key. =back =head1 ENVIRONMENT =over =item I The default home directory. =item I The gpg binary. Default: C<"gpg">. =item I The default working directory for gpg. Default: C<$HOME/.gnupg>. =back =head1 FILES =over =item $HOME/.gnupg/pubring.gpg - default GnuPG keyring =back =head1 SEE ALSO caff(1), gpg(1). =head1 AUTHOR Peter Palfrader This manpage was written in POD by Christoph Berg . =cut use strict; use IO::Handle; use English '-no_match_vars'; use File::Path; use File::Temp qw{tempdir}; use Fcntl; use IO::Select; use Getopt::Long; use GnuPG::Interface; my $VERSION = '@@VERSION@@'; ########### # functions ########### sub notice($) { my ($line) = @_; print STDERR "[NOTICE] $line\n"; }; sub info($) { my ($line) = @_; print STDERR "[INFO] $line\n"; }; sub debug($) { my ($line) = @_; #print STDERR "[DEBUG] $line\n"; }; sub trace($) { my ($line) = @_; #print STDERR "[trace] $line\n"; }; sub trace2($) { my ($line) = @_; #print STDERR "[trace2] $line\n"; }; sub make_gpg_fds() { my %fds = ( stdin => IO::Handle->new(), stdout => IO::Handle->new(), stderr => IO::Handle->new(), status => IO::Handle->new() ); my $handles = GnuPG::Handles->new( %fds ); return ($fds{'stdin'}, $fds{'stdout'}, $fds{'stderr'}, $fds{'status'}, $handles); }; sub readwrite_gpg($$$$$%) { my ($in, $inputfd, $stdoutfd, $stderrfd, $statusfd, %options) = @_; trace("Entering readwrite_gpg."); my ($first_line, $dummy) = split /\n/, $in; debug("readwrite_gpg sends ".(defined $first_line ? $first_line : "")); local $INPUT_RECORD_SEPARATOR = undef; my $sout = IO::Select->new(); my $sin = IO::Select->new(); my $offset = 0; trace("input is $inputfd; output is $stdoutfd; err is $stderrfd; status is ".(defined $statusfd ? $statusfd : 'undef')."."); $inputfd->blocking(0); $stdoutfd->blocking(0); $statusfd->blocking(0) if defined $statusfd; $stderrfd->blocking(0); $sout->add($stdoutfd); $sout->add($stderrfd); $sout->add($statusfd) if defined $statusfd; $sin->add($inputfd); my ($stdout, $stderr, $status) = ("", "", ""); my $exitwhenstatusmatches = $options{'exitwhenstatusmatches'}; trace("doing stuff until we find $exitwhenstatusmatches") if defined $exitwhenstatusmatches; my $readwrote_stuff_this_time = 0; my $do_not_wait_on_select = 0; my ($readyr, $readyw, $written); while ($sout->count() > 0 || (defined($sin) && ($sin->count() > 0))) { if (defined $exitwhenstatusmatches) { if ($status =~ /$exitwhenstatusmatches/m) { trace("readwrite_gpg found match on $exitwhenstatusmatches"); if ($readwrote_stuff_this_time) { trace("read/write some more\n"); $do_not_wait_on_select = 1; } else { trace("that's it in our while loop.\n"); last; } }; }; $readwrote_stuff_this_time = 0; trace("select waiting for ".($sout->count())." fds."); ($readyr, $readyw, undef) = IO::Select::select($sout, $sin, undef, $do_not_wait_on_select ? 0 : 1); trace("ready: write: ".(defined $readyw ? scalar @$readyw : 0 )."; read: ".(defined $readyr ? scalar @$readyr : 0)); for my $wfd (@$readyw) { $readwrote_stuff_this_time = 1; if (length($in) != $offset) { trace("writing to $wfd."); $written = $wfd->syswrite($in, length($in) - $offset, $offset); $offset += $written; }; if ($offset == length($in)) { trace("writing to $wfd done."); unless ($options{'nocloseinput'}) { close $wfd; trace("$wfd closed."); }; $sin->remove($wfd); $sin = undef; } } next unless defined $readyr and @$readyr; # Wait some more. for my $rfd (@$readyr) { $readwrote_stuff_this_time = 1; if ($rfd->eof) { trace("reading from $rfd done."); $sout->remove($rfd); close($rfd); next; } trace("reading from $rfd."); if ($rfd == $stdoutfd) { $stdout .= <$rfd>; trace2("stdout is now $stdout\n================"); next; } if (defined $statusfd && $rfd == $statusfd) { $status .= <$rfd>; trace2("status is now $status\n================"); next; } if ($rfd == $stderrfd) { $stderr .= <$rfd>; trace2("stderr is now $stderr\n================"); next; } } } trace("readwrite_gpg done."); return ($stdout, $stderr, $status); }; sub export_key($$) { my ($gnupghome, $keyid) = @_; my $gpg = GnuPG::Interface->new(); $gpg->call( $ENV{GNUPGBIN} ) if defined $ENV{GNUPGBIN}; my %confighash = ( armor => 1 ); $confighash{'homedir'}=$gnupghome if (defined $gnupghome); $gpg->options->hash_init( %confighash ); $gpg->options->meta_interactive( 0 ); my ($inputfd, $stdoutfd, $stderrfd, $statusfd, $handles) = make_gpg_fds(); my $pid = $gpg->export_keys(handles => $handles, command_args => [ $keyid ]); my ($stdout, $stderr, $status) = readwrite_gpg('', $inputfd, $stdoutfd, $stderrfd, $statusfd); waitpid $pid, 0; return $stdout; }; ################## # global variables ################## my $KEYEDIT_PROMPT = '^\[GNUPG:\] GET_LINE keyedit.prompt'; my $KEYEDIT_DELUID_PROMPT = '^\[GNUPG:\] GET_BOOL keyedit.remove.uid.okay'; my $KEYEDIT_DELSIG_PROMPT = '^\[GNUPG:\] GET_BOOL keyedit.delsig'; my $KEYEDIT_KEYEDIT_OR_DELSIG_PROMPT = '^\[GNUPG:\] (GET_BOOL keyedit.delsig|GET_LINE keyedit.prompt)'; my $KEYEDIT_DELSUBKEY_PROMPT = '^\[GNUPG:\] GET_BOOL keyedit.remove.subkey'; my $params; ################### # argument handling ################### sub version($) { my ($fd) = @_; print $fd "pgp-clean $VERSION - (c) 2004, 2005, 2006 Peter Palfrader et al.\n"; }; sub usage($$) { my ($fd, $exitcode) = @_; version($fd); print $fd "Usage: $PROGRAM_NAME [-s] [ ...]\n"; print $fd "-s --export-subkeys do not remove subkeys\n"; exit $exitcode; }; Getopt::Long::config('bundling'); if (!GetOptions ( '-h' => \$params->{'help'}, '--help' => \$params->{'help'}, '-V' => \$params->{'version'}, '--version' => \$params->{'version'}, '-s' => \$params->{'export-subkeys'}, '--export-subkeys' => \$params->{'export-subkeys'}, )) { usage(\*STDERR, 1); }; if ($params->{'help'}) { usage(\*STDOUT, 0); }; if ($params->{'version'}) { version(\*STDOUT); exit(0); }; usage(\*STDERR, 1) unless scalar @ARGV >= 1; my @KEYIDS; for my $keyid (@ARGV) { $keyid =~ s/^0x//i; unless ($keyid =~ /^[A-Za-z0-9]{8}([A-Za-z0-9]{8})?$/) { print STDERR "$keyid is not a keyid.\n"; usage(\*STDERR, 1); }; push @KEYIDS, uc($keyid); }; ################## # export and prune ################## KEYS: for my $keyid (@KEYIDS) { # get key listing ################# my $gpg = GnuPG::Interface->new(); $gpg->call( $ENV{GNUPGBIN} ) if defined $ENV{GNUPGBIN}; $gpg->options->meta_interactive( 0 ); my ($inputfd, $stdoutfd, $stderrfd, $statusfd, $handles) = make_gpg_fds(); $gpg->options->hash_init( 'extra_args' => [ '--with-colons', '--fixed-list-mode' ] ); my $pid = $gpg->list_public_keys(handles => $handles, command_args => [ $keyid ]); my ($stdout, $stderr, $status) = readwrite_gpg('', $inputfd, $stdoutfd, $stderrfd, $statusfd); waitpid $pid, 0; if ($stdout eq '') { warn ("No data from gpg for list-key $keyid\n"); next; }; my $keyinfo = $stdout; my @publine = grep /^pub/, (split /\n/, $stdout); my ($dummy1, $dummy2, $dummy3, $dummy4, $longkeyid, $dummy6, $dummy7, $dummy8, $dummy9, $dummy10, $dummy11, $flags) = split /:/, pop @publine; my $can_encrypt = $flags =~ /E/; unless (defined $longkeyid) { warn ("Didn't find public keyid in edit dialog of key $keyid.\n"); next; }; # export the key ################ my $asciikey = export_key(undef, $keyid); if ($asciikey eq '') { warn ("No data from gpg for export $keyid\n"); next; }; my @UIDS; my $uid_number = 0; my $this_uid_text = ''; $uid_number++; debug("Doing key $keyid, uid $uid_number"); # import into temporary gpghome ############################### my $tempdir = tempdir( "caff-$keyid-XXXXX", DIR => '/tmp/', CLEANUP => 1); $gpg = GnuPG::Interface->new(); $gpg->call( $ENV{GNUPGBIN} ) if defined $ENV{GNUPGBIN}; $gpg->options->hash_init( 'homedir' => $tempdir ); $gpg->options->meta_interactive( 0 ); ($inputfd, $stdoutfd, $stderrfd, $statusfd, $handles) = make_gpg_fds(); $pid = $gpg->import_keys(handles => $handles); ($stdout, $stderr, $status) = readwrite_gpg($asciikey, $inputfd, $stdoutfd, $stderrfd, $statusfd); waitpid $pid, 0; if ($status !~ /^\[GNUPG:\] IMPORT_OK/m) { warn ("Could not import $keyid into temporary gnupg.\n"); next; }; # prune it ########## $gpg = GnuPG::Interface->new(); $gpg->call( $ENV{GNUPGBIN} ) if defined $ENV{GNUPGBIN}; $gpg->options->hash_init( 'homedir' => $tempdir, 'extra_args' => [ '--with-colons', '--fixed-list-mode', '--command-fd=0', '--no-tty' ] ); ($inputfd, $stdoutfd, $stderrfd, $statusfd, $handles) = make_gpg_fds(); $pid = $gpg->wrap_call( commands => [ '--edit-key' ], command_args => [ $keyid ], handles => $handles ); debug("Starting edit session"); ($stdout, $stderr, $status) = readwrite_gpg('', $inputfd, $stdoutfd, $stderrfd, $statusfd, exitwhenstatusmatches => $KEYEDIT_PROMPT, nocloseinput => 1); # mark all uids ################### my $number_of_subkeys = 0; my $i = 1; my $have_one = 0; my $is_uat = 0; my $delete_some = 0; debug("Parsing stdout output."); for my $line (split /\n/, $stdout) { debug("Checking line $line"); my ($type, $dummy2, $dummy3, $dummy4, $dummy5, $dummy6, $dummy7, $dummy8, $dummy9, $uidtext) = split /:/, $line; if ($type eq 'sub') { $number_of_subkeys++; }; next unless ($type eq 'uid' || $type eq 'uat'); debug("line is interesting."); debug("mark uid."); readwrite_gpg("$i\n", $inputfd, $stdoutfd, $stderrfd, $statusfd, exitwhenstatusmatches => $KEYEDIT_PROMPT, nocloseinput => 1); $i++; }; debug("Parsing stdout output done."); # delete subkeys ################ if (!$params->{'export-subkeys'} and $number_of_subkeys > 0) { for (my $i=1; $i<=$number_of_subkeys; $i++) { readwrite_gpg("key $i\n", $inputfd, $stdoutfd, $stderrfd, $statusfd, exitwhenstatusmatches => $KEYEDIT_PROMPT, nocloseinput => 1); }; readwrite_gpg("delkey\n", $inputfd, $stdoutfd, $stderrfd, $statusfd, exitwhenstatusmatches => $KEYEDIT_DELSUBKEY_PROMPT, nocloseinput => 1); readwrite_gpg("yes\n", $inputfd, $stdoutfd, $stderrfd, $statusfd, exitwhenstatusmatches => $KEYEDIT_PROMPT, nocloseinput => 1); }; # delete signatures ################### my $signed_by_me = 0; ($stdout, $stderr, $status) = readwrite_gpg("delsig\n", $inputfd, $stdoutfd, $stderrfd, $statusfd, exitwhenstatusmatches => $KEYEDIT_DELSIG_PROMPT, nocloseinput => 1); while($status =~ /$KEYEDIT_DELSIG_PROMPT/m) { # sig:?::17:EA2199412477CAF8:1058095214:::::13x: my @sigline = grep /^sig/, (split /\n/, $stdout); $stdout =~ s/\n/\\n/g; notice("[sigremoval] why are there ".(scalar @sigline)." siglines in that part of the dialog!? got: $stdout") if scalar @sigline >= 2; # XXX my $line = pop @sigline; my $answer = "no"; if (defined $line) { # only if we found a sig here - we never remove revocation packets for instance debug("[sigremoval] doing line $line."); my ($dummy1, $dummy2, $dummy3, $dummy4, $signer, $created, $dummy7, $dummy8, $dummy9) = split /:/, $line; if ($signer eq $longkeyid) { debug("[sigremoval] selfsig ($signer)."); $answer = "no"; } else { debug("[sigremoval] not interested in that sig ($signer)."); $answer = "yes"; }; } else { debug("[sigremoval] no sig line here, only got: ".$stdout); }; ($stdout, $stderr, $status) = readwrite_gpg($answer."\n", $inputfd, $stdoutfd, $stderrfd, $statusfd, exitwhenstatusmatches => $KEYEDIT_KEYEDIT_OR_DELSIG_PROMPT, nocloseinput => 1); }; readwrite_gpg("save\n", $inputfd, $stdoutfd, $stderrfd, $statusfd); waitpid $pid, 0; $asciikey = export_key($tempdir, $longkeyid); if ($asciikey eq '') { warn ("No data from gpg for export $longkeyid\n"); next; }; print $asciikey; } signing-party-2.12/caff/pgp-fixkey000077500000000000000000000271111500571021200171340ustar00rootroot00000000000000#!/usr/bin/perl -w # pgp-fixkey -- remove broken packets from keys # # Copyright (c) 2004, 2005 Peter Palfrader # # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # 3. The name of the author may not be used to endorse or promote products # derived from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. =pod =head1 NAME pgp-fixkey -- remove broken packets from keys =head1 SYNOPSIS =over =item B [I [I ...]] =back =head1 DESCRIPTION B removes broken packets from keys in the GnuPG keyring that make GnuPG spew ugly warnings. It optionally takes a list of keyids on the command line and only cleans those keys. =head1 OPTIONS =over =item I Use this key. =back =head1 ENVIRONMENT =over =item I The default home directory. =item I The gpg binary. Default: C<"gpg">. =item I The default working directory for gpg. Default: C<$HOME/.gnupg>. =back =head1 FILES =over =item $HOME/.gnupg/pubring.gpg - default GnuPG keyring =back =head1 SEE ALSO caff(1), gpg(1). =head1 AUTHOR Peter Palfrader =cut use strict; use IO::Handle; use English '-no_match_vars'; use File::Path; use Fcntl; use IO::Select; use GnuPG::Interface; my $VERSION = '@@VERSION@@'; sub notice($) { my ($line) = @_; print STDERR "[NOTICE] $line\n"; }; sub info($) { my ($line) = @_; print STDERR "[INFO] $line\n"; }; sub debug($) { my ($line) = @_; #print STDERR "[DEBUG] $line\n"; }; sub trace($) { my ($line) = @_; #print STDERR "[trace] $line\n"; }; sub trace2($) { my ($line) = @_; #print STDERR "[trace2] $line\n"; }; sub make_gpg_fds() { my %fds = ( stdin => IO::Handle->new(), stdout => IO::Handle->new(), stderr => IO::Handle->new(), status => IO::Handle->new() ); my $handles = GnuPG::Handles->new( %fds ); return ($fds{'stdin'}, $fds{'stdout'}, $fds{'stderr'}, $fds{'status'}, $handles); }; sub readwrite_gpg($$$$$%) { my ($in, $inputfd, $stdoutfd, $stderrfd, $statusfd, %options) = @_; trace("Entering readwrite_gpg."); my ($first_line, $dummy) = split /\n/, $in; debug("readwrite_gpg sends ".(defined $first_line ? $first_line : "")); local $INPUT_RECORD_SEPARATOR = undef; my $sout = IO::Select->new(); my $sin = IO::Select->new(); my $offset = 0; trace("input is $inputfd; output is $stdoutfd; err is $stderrfd; status is ".(defined $statusfd ? $statusfd : 'undef')."."); $inputfd->blocking(0); $stdoutfd->blocking(0); $statusfd->blocking(0) if defined $statusfd; $stderrfd->blocking(0); $sout->add($stdoutfd); $sout->add($stderrfd); $sout->add($statusfd) if defined $statusfd; $sin->add($inputfd); my ($stdout, $stderr, $status) = ("", "", ""); my $exitwhenstatusmatches = $options{'exitwhenstatusmatches'}; trace("doing stuff until we find $exitwhenstatusmatches") if defined $exitwhenstatusmatches; my $readwrote_stuff_this_time = 0; my $do_not_wait_on_select = 0; my ($readyr, $readyw, $written); while ($sout->count() > 0 || (defined($sin) && ($sin->count() > 0))) { if (defined $exitwhenstatusmatches) { if ($status =~ /$exitwhenstatusmatches/m) { trace("readwrite_gpg found match on $exitwhenstatusmatches"); if ($readwrote_stuff_this_time) { trace("read/write some more\n"); $do_not_wait_on_select = 1; } else { trace("that's it in our while loop.\n"); last; } }; }; $readwrote_stuff_this_time = 0; trace("select waiting for ".($sout->count())." fds."); ($readyr, $readyw, undef) = IO::Select::select($sout, $sin, undef, $do_not_wait_on_select ? 0 : 1); trace("ready: write: ".(defined $readyw ? scalar @$readyw : 0 )."; read: ".(defined $readyr ? scalar @$readyr : 0)); for my $wfd (@$readyw) { $readwrote_stuff_this_time = 1; if (length($in) != $offset) { trace("writing to $wfd."); $written = $wfd->syswrite($in, length($in) - $offset, $offset); $offset += $written; }; if ($offset == length($in)) { trace("writing to $wfd done."); unless ($options{'nocloseinput'}) { close $wfd; trace("$wfd closed."); }; $sin->remove($wfd); $sin = undef; } } next unless defined $readyr and @$readyr; # Wait some more. for my $rfd (@$readyr) { $readwrote_stuff_this_time = 1; if ($rfd->eof) { trace("reading from $rfd done."); $sout->remove($rfd); close($rfd); next; } trace("reading from $rfd."); if ($rfd == $stdoutfd) { $stdout .= <$rfd>; trace2("stdout is now $stdout\n================"); next; } if (defined $statusfd && $rfd == $statusfd) { $status .= <$rfd>; trace2("status is now $status\n================"); next; } if ($rfd == $stderrfd) { $stderr .= <$rfd>; trace2("stderr is now $stderr\n================"); next; } } } trace("readwrite_gpg done."); return ($stdout, $stderr, $status); }; my $KEYEDIT_PROMPT = '^\[GNUPG:\] GET_LINE keyedit.prompt'; my $KEYEDIT_DELUID_PROMPT = '^\[GNUPG:\] GET_BOOL keyedit.remove.uid.okay'; my $KEYEDIT_DELSIG_PROMPT = '^\[GNUPG:\] GET_BOOL keyedit.delsig'; my $KEYEDIT_KEYEDIT_OR_DELSIG_PROMPT = '^\[GNUPG:\] (GET_BOOL keyedit.delsig|GET_LINE keyedit.prompt)'; my $KEYEDIT_DELSUBKEY_PROMPT = '^\[GNUPG:\] GET_BOOL keyedit.remove.subkey'; sub usage() { print STDERR "pgp-fixkey $VERSION - (c) 2004, 2005 Peter Palfrader\n"; print STDERR "Usage: $PROGRAM_NAME [ [ ...]]\n"; exit 1; }; my @KEYIDS; for my $keyid (@ARGV) { $keyid =~ s/^0x//i; unless ($keyid =~ /^[A-Za-z0-9]{8}([A-Za-z0-9]{8})?$/) { print STDERR "$keyid is not a keyid.\n"; usage(); }; push @KEYIDS, uc($keyid); }; # find a list of all interesting keys. if (scalar @KEYIDS == 0) { my $gpg = GnuPG::Interface->new(); $gpg->call( $ENV{GNUPGBIN} ) if defined $ENV{GNUPGBIN}; $gpg->options->meta_interactive( 0 ); my ($inputfd, $stdoutfd, $stderrfd, $statusfd, $handles) = make_gpg_fds(); $gpg->options->hash_init( 'extra_args' => [ '--with-colons', '--fast-list-mode', '--fixed-list-mode' ] ); my $pid = $gpg->list_sigs(handles => $handles); my ($stdout, $stderr, $status) = readwrite_gpg('', $inputfd, $stdoutfd, $stderrfd, $statusfd); waitpid $pid, 0; if ($stdout eq '') { die ("No data from gpg for list-sigs\n"); }; my $thiskey = undef; for my $line (split /\n/, $stdout) { debug("Checking line $line"); my ($type, undef, undef, undef, $keyid, $created, undef, undef, undef, undef) = split /:/, $line; if ($type eq 'pub') { $thiskey = $keyid; debug("Found key $thiskey"); } elsif (!defined $thiskey) { next; } elsif ($type eq 'sig' || $type eq 'rev') { if (($keyid eq '0' x 16) || ($created eq "") || ($created == 0)) { push @KEYIDS, $thiskey; info("Key $thiskey needs cleaning."); $thiskey = undef; }; }; }; } KEYS: for my $keyid (@KEYIDS) { # get key listing ################# my $gpg = GnuPG::Interface->new(); $gpg->call( $ENV{GNUPGBIN} ) if defined $ENV{GNUPGBIN}; $gpg->options->meta_interactive( 0 ); my ($inputfd, $stdoutfd, $stderrfd, $statusfd, $handles) = make_gpg_fds(); $gpg->options->hash_init( 'extra_args' => [ '--with-colons', '--fixed-list-mode' ] ); my $pid = $gpg->list_public_keys(handles => $handles, command_args => [ $keyid ]); my ($stdout, $stderr, $status) = readwrite_gpg('', $inputfd, $stdoutfd, $stderrfd, $statusfd); waitpid $pid, 0; if ($stdout eq '') { warn ("No data from gpg for list-key $keyid\n"); next; }; my $keyinfo = $stdout; my @publine = grep /^pub/, (split /\n/, $stdout); if ($#publine < 0) { warn ("No public key found for $keyid.\n"); next; } my (undef, undef, undef, undef, $longkeyid, undef, undef, undef, undef, undef, undef, $flags) = split /:/, pop @publine; my $can_encrypt = $flags =~ /E/; unless (defined $longkeyid) { warn ("Didn't find public keyid in edit dialog of key $keyid.\n"); next; }; my @UIDS; my $uid_number = 0; my $this_uid_text = ''; $uid_number++; debug("Doing key $keyid, uid $uid_number"); # prune it ########## $gpg = GnuPG::Interface->new(); $gpg->call( $ENV{GNUPGBIN} ) if defined $ENV{GNUPGBIN}; $gpg->options->hash_init( 'extra_args' => [ '--with-colons', '--fixed-list-mode', '--command-fd=0', '--no-tty' ] ); ($inputfd, $stdoutfd, $stderrfd, $statusfd, $handles) = make_gpg_fds(); $pid = $gpg->wrap_call( commands => [ '--edit-key' ], command_args => [ $keyid ], handles => $handles ); debug("Starting edit session"); ($stdout, $stderr, $status) = readwrite_gpg('', $inputfd, $stdoutfd, $stderrfd, $statusfd, exitwhenstatusmatches => $KEYEDIT_PROMPT, nocloseinput => 1); # mark all uids ################### my $number_of_subkeys = 0; my $i = 1; my $have_one = 0; my $is_uat = 0; my $delete_some = 0; debug("Parsing stdout output."); for my $line (split /\n/, $stdout) { debug("Checking line $line"); my ($type, undef, undef, undef, undef, undef, undef, undef, undef, $uidtext) = split /:/, $line; if ($type eq 'sub') { $number_of_subkeys++; }; next unless ($type eq 'uid' || $type eq 'uat'); debug("line is interesting."); debug("mark uid."); readwrite_gpg("$i\n", $inputfd, $stdoutfd, $stderrfd, $statusfd, exitwhenstatusmatches => $KEYEDIT_PROMPT, nocloseinput => 1); $i++; }; debug("Parsing stdout output done."); # delete signatures ################### ($stdout, $stderr, $status) = readwrite_gpg("delsig\n", $inputfd, $stdoutfd, $stderrfd, $statusfd, exitwhenstatusmatches => $KEYEDIT_DELSIG_PROMPT, nocloseinput => 1); while($status =~ /$KEYEDIT_DELSIG_PROMPT/m) { # sig:?::17:EA2199412477CAF8:1058095214:::::13x: my @sigline = grep /^sig/, (split /\n/, $stdout); $stdout =~ s/\n/\\n/g; notice("[sigremoval] why are there ".(scalar @sigline)." siglines in that part of the dialog!? got: $stdout") if scalar @sigline >= 2; # XXX my $line = pop @sigline; my $answer = "no"; if (defined $line) { # only if we found a sig here - we never remove revocation packets for instance debug("[sigremoval] doing line $line."); my (undef, undef, undef, undef, $signer, $created, undef, undef, undef) = split /:/, $line; if (($signer eq '0' x 16) || ($created == 0)) { debug("[sigremoval] not interested in that sig ($signer, created: $created)."); $answer = "yes"; }; } else { debug("[sigremoval] no sig line here, only got: ".$stdout); }; ($stdout, $stderr, $status) = readwrite_gpg($answer."\n", $inputfd, $stdoutfd, $stderrfd, $statusfd, exitwhenstatusmatches => $KEYEDIT_KEYEDIT_OR_DELSIG_PROMPT, nocloseinput => 1); }; readwrite_gpg("save\n", $inputfd, $stdoutfd, $stderrfd, $statusfd); waitpid $pid, 0; } signing-party-2.12/debian/000077500000000000000000000000001500571021200154445ustar00rootroot00000000000000signing-party-2.12/debian/NEWS000066400000000000000000000021731500571021200161460ustar00rootroot00000000000000signing-party (1.0-1) unstable; urgency=low There are several new tools: * gpgparticipants creates a list of party participants for the organiser. * sig2dot and springgraph were imported from their formerly separate packages. -- Christoph Berg Wed, 12 Mar 2008 22:58:00 +0100 signing-party (0.4.2-1) unstable; urgency=low * New: gpglist -- show who signed which of your UIDs -- Christoph Berg Mon, 8 Aug 2005 00:03:55 +0200 signing-party (0.4.0-1) unstable; urgency=low * Signing-party has now been merged with the (never released) pgp-tools package. It now includes two new tools. The package will be extended further in the near future. * New: caff -- CA - fire and forget: caff takes a list of keyids on the command line, fetches them from a keyserver and calls GnuPG so that you can sign it. It then mails each key to all its email addresses - only including the one UID that we send to in each mail. * New: gpgsigs -- annotates a list of GPG keys with already done signatures. -- Thijs Kinkhorst Fri, 3 Jun 2005 11:18:36 +0200 signing-party-2.12/debian/changelog000066400000000000000000001333771500571021200173340ustar00rootroot00000000000000signing-party (2.12-1) unstable; urgency=medium [ Guilhem Moulin ] * d/rules: Replace DEB_BUILD_MAINT_OPTIONS:=hardening=+bindnow,+pie with hardening=+all. * d/rules: Use execute_after_dh_* from Debhelper compatibility level 13 when relevant. * Update Standards-Version to 4.7.2 (no changes necessary). * caff: Relax regex to parse comments at the end of UIDs. * keyart: Regression fix when --fingerprint is used (closes: #979122). [ Uwe Kleine-König ] * caff: Parse UIDs with a comment after the email address. * Replace pool.sks-keyservers.net with keyserver.ubuntu.com (closes: #1002880). * gpgsigs: Properly handle prefilled checksums (closes: #900444). -- Guilhem Moulin Sun, 04 May 2025 17:51:50 +0200 signing-party (2.11-1) unstable; urgency=medium [ Guilhem Moulin ] * d/control, d/install: Apply wrap-and-sort(1). * d/control: Remove useless Build-Depends: python. * d/control, d/rules: Replace dh_python2 with dh_python3. This has AFAICT no impact on the binary packages which have been ported to python3 in 2.8-1. (Closes: #938480.) * Set Rules-Requires-Root: no. * Trim trailing whitespace. * Move source package lintian overrides to debian/source. * Rename debian/NEWS.Debian to debian/NEWS. * Update standards version to 4.5.0, no changes needed. * Bump debhelper from 9 to 13. Remove now useless Build-Depends: autotools-dev. * Set debhelper-compat version in Build-Depends. * Use the dh sequencer in debian/rules. [ Max Harmathy ] * New script gpgparticipants-filter. -- Guilhem Moulin Wed, 08 Jul 2020 18:43:59 +0200 signing-party (2.10-2) unstable; urgency=high * gpg-key2ps: Security fix for CVE-2019-11627: unsafe shell call enabling shell injection via a User ID. Use Perl's (core) module Encode.pm instead of shelling out to `iconv`. (Closes: #928256.) -- Guilhem Moulin Sun, 05 May 2019 15:13:54 +0200 signing-party (2.9-1) unstable; urgency=medium * gpglist: + When --signer's argument is a long keyid or fingerprint, don't require the key to be present in the keyring. This enable filtering on unknown signing keys. + Don't choke on direct-key signatures. (Closes: #921331.) * gpgparticipants: + Improve quoting and replace `echo` with `printf`. + Avoid including subkey fingerprints when gpg.conf contains 'fingerprint'. -- Guilhem Moulin Fri, 01 Mar 2019 15:39:40 +0100 signing-party (2.8-1) unstable; urgency=low [ Guilhem Moulin ] * keyart, gpgparticipants-prefill: port to python3. * keyart: + Don't print ASCII art for subkeys, only the master key. + Pass --no-auto-check-trustdb flag to gpg(1). + Fix crash with non-ASCII UIDs. Patch from Grégoire Detrez. * Fix a couple of spelling errors. Thanks to Edward Betts for the report and patch. (Closes: #882729) * caff: + Add the "only-sign-text-ids" to the list of gpg(1) options imported from ~/.gnupg/gpg.conf. + Ensure the terminal is "sane enough" when asking questions ('echo', 'echok', 'icanon', 'icrnl' settings are all set), and restore original settings when exit()'ing the program. (Closes: #872529) * caff, gpglist, gpgsigs: in `gpg --with-colons` output, allow signature class to be followed with an optional revocation reason. gpg(1) does that since 2.2.9. (Closes: #905097.) * caff, gpg-key2latex, gpg-key2ps, gpglist, gpgsigs, keylookup: Remove references to https://pgp-tools.alioth.debian.org/ . * caff, gpg-key2latex, gpg-key2ps, gpg-mailkeys, gpglist, gpgparticipants, gpgsigs, keylookup: Remove SVN keywords ($Id$, $Rev$, etc.) * debian/control: + Migrate Vcs-Browser and Vcs-Git to https://salsa.debian.org/signing-party-team/signing-party . + Bump Standards-Version to 4.3.0 (no changes necessary). * sig2dot: Don't use the diamond operator. [ Edward Betts] * debian/control: add neomutt as an alternative to mutt -- Guilhem Moulin Mon, 28 Jan 2019 03:05:33 +0100 signing-party (2.7-2) unstable; urgency=low * debian/control: + Migrate Vcs-Browser and Vcs-Git from Alioth to Salsa. (Closes: #898200) + Bump Standards-Version to 4.1.4 (no changes necessary). -- Guilhem Moulin Wed, 09 May 2018 13:05:37 +0200 signing-party (2.7-1) unstable; urgency=low * gpg-key2ps: + Add support for ECDH, ECDSA and EDDSA key types. + Align key type & size listing to the GnuPG 2.1.x output (e.g., "rsa4096/DEADBEEF" instead of "4096R/DEADBEEF"). * gpgsigs: + Set UAT (jpeg photos) density to 90dpi so XeLaTeX doesn't complain that the image is too large. * debian/control: Bump Standards-Version to 4.1.3 + Replace "extra" priority to "optional" * debian/rules: Remove deprecated dh_autotools-dev_updateconfig and dh_autotools-dev_restoreconfig. -- Guilhem Moulin Mon, 29 Jan 2018 11:46:19 +0100 signing-party (2.6-1) unstable; urgency=low * gpgsigs: + Skip undefined UIDs. + Properly handle (skip) unknown attributes. + Allow digest hexadecimal characters to replace multiple '_' in the fill-in forms. (This is not what gpgparticipants(1) produces, but some KSP organizers use other scripts.) * gpglist: New option '--signer' to limit listed signers to the matching keys. * Makefile, debian/rules: Avoid parsing dpkg-parsechangelog(1) output. * debian/control: Bump Standards-Version to 4.0.0 (no changes necessary). -- Guilhem Moulin Fri, 07 Jul 2017 17:05:30 +0200 signing-party (2.5-1) unstable; urgency=low * caff: + Show how to set $ENV{'PERL_MAILERS'} to specify a sendmail binary (or use a sendmail-compatible MTA such as msmtp(1)). Credits to Ryan Kavanagh's blog post. + Fix regression (introduced in 2.4-1) skipping --recv-key when 'keys-from-gnupg' isn't set. (Closes: #837406) + List all UIDs contained in an email when asking whether to send it. * gpg-key2latex: Add an option '--qrcode-data' to specify the data to encode in a QR code (default: "OPENPGP4FPR:%f"). * gpg-key2ps: + Fix revoked UID stroke slant with "-r strike". Thanks to Grégoire Détrez for the patch. + Ensure subkeys are hiden unless '--show-subkeys' is set. (As of GnuPG 2.1.15-3, `gpg --with-colons --list-keys` lists subkeys by defaults.) Thanks to Grégoire Détrez for the patch. -- Guilhem Moulin Thu, 06 Oct 2016 14:59:44 +0200 signing-party (2.4-1) unstable; urgency=medium * caff, gpg-key2latex, gpgsigs: Ignore "KEY_CONSIDERED" status output emitted by gpg 2.1.13 and later. * caff, gpgsigs: Allow input produced by gpgparticipants(1) using gpg 2.1.13. With this version, key IDs are not displayed by default and the "Key fingerprint = " prefix is omitted. * caff: + Fix GnuPG version number comparison. + With GnuPG 2.1.13 or later, use gpgconf(1) to determine the socket paths. (It is not used on earlier gpg since earlier gpgconf do not support --homedir.) This fixes compatibility with GnuPG 2.1.13. (Closes: #834984) + When ~/.caff/gnupghome/gpg.conf does not exist, instead of creating a temporary file (as it's done since signing-party 2.3), parse ~/.gnup/gpg.conf and pass the GnuPG options that are known to be safe (and useful) for caff to gpg(1) using command line options. This soves the problem of lingering configuration files in case caff is killed. + Use full fingerprints internally to avoid collisions. (However $CONFIG{'keyid'} and $CONFIG{'local-users'} are kept to 64-bits key IDs as per RFC 4880 full fingerprints are not available in key signatures, and thus not exposed by `gpg --with-colons --list-sigs`.) + Automatically import the $CONFIG{'also-encrypt-to'} from the normal GnuPGHOME when possible. * d/source.lintian-overrides: Add 'debian-watch-file-is-missing' as we're upstream. * d/control: Remove Franck Joncourt from the Uploaders list per request of the MIA team. (Closes: #831321) -- Guilhem Moulin Mon, 22 Aug 2016 00:19:48 +0200 signing-party (2.3-1) unstable; urgency=low * debian.control: + Replace "Suggests: fonts-droid" by "Suggests: fonts-noto-mono, fonts-noto-cjk". (Closes: #804689) + Replace "http://" with "https://" in the Homepage and Vcs-Browser fields. + Bump Standards-Version to 3.9.8 (no changes necessary). * debian.rules: + Add 'hardening=+pie' to DEB_BUILD_MAINT_OPTIONS to compile ELF executables as PIEs. * caff: + Replace a hash slice with a reverse map for compatibility with Perl older than 5.20. Patch from Gregor Jasny. (Closes: #813469) + Deprecate $CONFIG{'keyserver'}. Users of GnuPG <2.1 should put the option in caff's GnuPG configuration file (~/.caff/gnupghome/gpg.conf by default) instead. GnuPG 2.1 delegates network access to another process (dirmngr), hence for 2.1 the keyserver should be set in ~/.gnupg/dirmngr.conf instead. + When caff's own GnuPG configuration file (~/.caff/gnupghome/gpg.conf) does not exist, automatically generate it with the GnuPG options found in ~/.gnup/gpg.conf that are known to be safe (and useful) for caff. This includes "keyserver", "keyserver-options", "ask-cert-level" and "cert-digest-algo" (among many others). Hence in the absence of its own GnuPG configuration file caff now uses the certification options from the user's GnuPG configuration file. * gpgsigs, gpg-key2latex: + Use "Noto Mono" as default font when compiling with XeLaTeX or LuaLaTeX; and "Noto Sans Mono CJK" as CJK font when compiling with XeLaTeX. * gpg-key2latex: + Don't show capabilities of the entire key when --show-subkeys is set. (Instead, the capabilities of the master key and each subkey are shown independently in uppercase.) + Enclose (sub)key capabilities in square brackets, to match GnuPG 2.1.11+'s output. + For ECDH, ECDSA, EDDSA (sub)keys, show the curve name instead of the public key algorithm and length. This matches GnuPG 2.1.x's output. + The master key's fingerprint was incorrectly set to the last unusable (eg, expired or revoked) subkey fingerprint, if any. (Closes: #815721) * keyart: + Print the public key algorithm and length as shown by GnuPG 2.1 (e.g., "rsa4096" instead of "4096R"); for ECDH, ECDSA and EDDSA keys, show the curve name instead. * keyanalyze: + Remove autogenerated files from the repository, so that they do not clutter it, and do not generated useless diffs. Patch from Guillem Jover. + Switch to use system libmd; this removes the embedded SHA-1 code. Patch from Guillem Jover. (Closes: #816851) -- Guilhem Moulin Tue, 26 Apr 2016 00:04:47 +0200 signing-party (2.2-1) unstable; urgency=low * caff: + "gpgparticipants"-formated input: accept key blocks not starting with a number such as [x] Fingerprint(s) OK [x] ID OK This makes caff able to process the Debconf 15 KSP file. (Closes: #797714) * gpglist: + Don't prune revoked UIDs with a subsequent selfsig. (Closes: #796664) + Add an option '--show-revoked' to show revoked UIDs. Thanks Tomasz Buchert for the patch. + Mention in the manpage that the path to the gpg binary is taken from the GNUPGBIN environment variable, if defined. -- Guilhem Moulin Tue, 15 Dec 2015 03:10:43 +0100 signing-party (2.1-1) unstable; urgency=low * caff: + Only consider non-expired/invalid/revoked keys and UIDs when generating the caffrc. + Proper RFC 5322 validation of email addresses. Currently gpg(1) only accepts a subset of RFC 5322-valid addresses (unless --allow-freeform-uid is set). caff is now able to extract the email address from any UID of the form "addr-spec" or "[phrase] " with a RFC 5322-valid addr-spec. + Prefix the signature by "-- \n" in the email template. + Automatically mkdir ~/.caff if it doesn't exit. Regression introduced in r776. (Closes: #786933) -- Guilhem Moulin Sat, 08 Aug 2015 16:34:16 +0200 signing-party (2.0-1) unstable; urgency=low * caff: + Fix broken compatibility with GnuPG 2.1 (2.1.3 and later only; earlier 2.1.x versions remain unsuported), due to --secret-keyring being ignored by the most recent gpg(1). On those we automaticalyl symlink the S.gpg-agent (unless 'no-sign') and S.dirmngr (unless 'no-download') sockets to avoid spawning new agents in caff's GNUPGHOME. This require an extra call to gpg(1) at the beginning to determine the version. (Closes: #771857) + Default $CONFIG{'local-user'} to $CONFIG{'keyid'} rather than importing the public part of *all* keys found in the secret keyring. (When not pruning the good keys with -u, gpg(1) croaks with exit status 2 when there are secret keys without public part.) + Print {error,warnings,notice,info} lines on STDERR. + Add a --debug flag to enable debug messages. + Deprecate $CONFIG{'gpg-sign'} and $CONFIG{'gpg-delsig'}. + Never redirect STDERR. Send the logger output to /dev/null instead (unless in debug mode). (Closes: #775702) + Send attachements and non RFC 2822 UIDs to *all* signed addresses, not only those for which the UID is exported. This is useful when the signee has some already signed RFC 2822 UIDs and a freshly added attribute, for instance. + Use Term::ANSIColor to produce fancy colored output. Can be configured by setting $CONFIG{colors} to a suitable hash; in particular setting $CONFIG{colors} = {} reverts to the old uncolored output. + Prune keys with import-{clean,minimal} not export-{clean,minimal}. + Fix $CONFIG{'also-lsign-in-gnupghome'}: local signatures are directly imported from caff's GNUPGHOME to our own; in auto-lsign'ing mode, lsign UID for which we have an exportable signature (preserving the signer and cert level). + Pass the 'keyserver-options' specified in ~/.gnupg/gpg.conf to $CONFIG{keyserver} when it is left unset. (Closes: #780836) * gpgsigs: + Add a legend with the different signature types. + Mark local signatures as 'L' (formerly they were marked as 'S'), and expiring -- but not expired -- signatures as 'x'. * caff, pgp-clean, pgp-fixkey, gpg-key2latex, gpg-key2ps, gpg-mailkeys, gpgdir, gpgparticipants, gpgsigs, keyart, keylookup: + Add the possibility to choose the gpg binary via the "GNUPGBIN" environment variable. (Default: "gpg".) -- Guilhem Moulin Tue, 14 Apr 2015 20:02:36 +0200 signing-party (1.1.12-1) unstable; urgency=low * debian.control: + Remove useless Build-Conflicts autoconf2.13 and automake1.4. + Suggest texlive-latex-extra due to gpg-key2latex using adjustbox.sty. (Closes: #769956) + Add dh-python to Build-Depends. * caff: + Don't consider KEYEXPIRED and SIGEXPIRED as unknown reply from gpg when downloading a key containing a revoked subkey or UID from a keyserver. (Closes: #769892) + Import the public part of *all* keys found in the secret keyring, not only those in @{$CONFIG{keyid}}. Otherwise gpg(1) croaks with exit status 2 when there are secret keys without public part. + Add a configuration option 'mail-subject' to set the "Subject:" header field. (Closes: #771436) * keyart: + Replace shebang by /usr/bin/python. * gpg-key2latex: + Add a fixed 4 module wide quiet zone around QR codes as specified in the standard. (Closes: #772854) + Generate QR codes with (M)edium error correction level. * gpgsigs: + Add --no-auto-check-trustdb to the gpg options (ownertrust values are irrelevant here). * gpglist: + Add --no-auto-check-trustdb to the gpg options (ownertrust values are irrelevant here). -- Guilhem Moulin Fri, 02 Jan 2015 19:45:29 +0100 signing-party (1.1.11-1) unstable; urgency=medium [ Thijs Kinkhorst ] * Remove self from from uploaders. [ Guilhem Moulin ] * caff: + Fix RCF 2822 violation: Never localize the "Date" header, regarless of the LC_ALL, LC_TIME and LANG in use. Regression introduced in r698. (Closes: #767371) + Regression fix: allow 8-digits keyid to be passed for annotation. (Closes: #768342) * gpg-key2latex: new script to generate a LaTeX file with fingerprint paper slips. (Closes: #316131, #412512) -- Guilhem Moulin Fri, 14 Nov 2014 01:16:49 +0100 signing-party (1.1.10-1) unstable; urgency=low * debian.compat: + Upgrade packaging to debhelper level 9. * debian.control: + Upgrade Standards-Version to 3.9.6. * caff: + Add a "Date" email header. Patch from Clint Adams. (Closes: #760316) + Fix edge-case RFC 2822 violation in "From" and "Sender" email headers. + Use gpg's --textmode to normalize the line separators to the standard CRLF. + Unescape escaped characters in gpg --with-colons's output (eg, ':'). + Fix encoding on non-UTF8 charsets. + Improve key ID validation. -- Guilhem Moulin Sat, 11 Oct 2014 22:48:53 +0200 signing-party (1.1.9-1) unstable; urgency=low [ Guilhem Moulin ] * debian.copyright: + Upgragrade to the new, machine-readable, format. * caff: + Fix regression introduced in r518 when removing the 'defined' in 'defined @array'. (Closes: #756459) * gpg-key2ps: + Replace ',' with '.' in paperconf's output, which is localized while Postscript understands only '.' as decimal mark. (Closes: #758991) * gpgsigs: + Use Perl's "Encode" core module for charset conversion. Also, change the default 'from' charset to be the locale in use, as it was the case for 'to'. (Use -f/-t to select alternative from/to charsets.) -- Guilhem Moulin Mon, 25 Aug 2014 21:50:04 +0200 signing-party (1.1.8-1) unstable; urgency=low [ Guilhem Moulin ] * caff: + Improve the documentation of gpgparticipants annotations. + When clean-exporting a key (aka pruning), don't ignore signatures just because their certification level in under 'min-cert-level' (which defaults to 2). (Closes: #751252) * gpgwrap: + Import the default Debian CFLAGS defined by dpkg-buildflags, which makes the Buildd Log Scanner stop emitting 'W-dpkg-buildflags-missing CFLAGS'. * keyanalyze: + Import the default Debian CFLAGS defined by dpkg-buildflags, which makes the Buildd Log Scanner stop emitting 'W-dpkg-buildflags-missing CFLAGS'. * sig2dot: + Apply -d on OpenPGP signatures and revocation certificates only. (LP: #393242) * springgraph: + Avoid 'illegal division by zero' errors when dealing with isolated nodes and/or degenerated cylinders. (LP: #1267981) + Add an option -f to set the (TrueType) font, style and size to use on labels. This allows proper displaying of non-ASCII labels (provided the chosen font covers all the glyphs). * debian.control, debian.rules: + Update the config files before building the package. Patch from Breno Leitao. (Closes: #748977) [ Aaron Toponce ] * keyart: new script to dislay an ASCII art representation of OpenPGP key files. -- Guilhem Moulin Sat, 21 Jun 2014 17:32:13 +0200 signing-party (1.1.7-1) unstable; urgency=low [ Guilhem Moulin ] * caff: + Give an example of 'mailer-send' with a custom envelope sender address. + Document a possible workflow in README.many-keys, when working with an annotated gpgparticipants(1) list. + Document a possible workflow for offline signing. * gpgparticipants: + Escape hyphen-minuses (-) in the documentation, as groff may interpret them as hyphens (U+2010). + Use gpg's --display-charset to force the output to be UTF-8 encoded, as the C.UTF-8 locale isn't installed on all platforms. Patch from Stefan Huber. (Closes: #747296). + Prefix gpg with LANGUAGE=en to force the output to be in English. (Other tools in signing-party parse this output.) + Prefix date(1) with LC_ALL=C to prevent its output to be localized. * gpgwrap: + Import the default Debian CPPFLAGS defined by dpkg-buildflags, which makes lintian stop emitting the 'hardening-no-fortify-functions' tag. * debian.control: + Update the Vcs-Svn and Vcs-Browser to canonical URIs. [ Peter Palfrader ] * gpgparticipants-prefill: + add --max-length and --prefix options. -- Guilhem Moulin Sun, 11 May 2014 14:49:43 +0200 signing-party (1.1.6-1) unstable; urgency=low [ Guilhem Moulin ] * Avoid unnecessary forks and calls to the command shell. * caff: + Properly MIME-Q encode e-mail headers. (Closes: #659971) + Convert internationalized addresses to ASCII in e-mail envelopes and headers. (Closes: #637222) + Adapt patch from Julius Seemayer to reopen STDIN as a TTY. (Closes: #723024) + Allow local and owner keyids to be specified as fingerprints. (Closes: #635359) + Don't redirect gpg's STDERR when importing the keys. (Closes: #612178) + Create the mail files in ~/.caff/keys when mail=no. (Closes: #652686) + Add an option 'mail-cant-encrypt' for special treatment of keys without encryption capability. (Closes: #622790) + Adapt patch from oli and add an option 'gpg-sign-type' for custom signature types. (Closes: #679951) + Add an option 'also-lsign-in-gnupghome' to add non-exportable signatures on the user's GnuPGHOME. (Closes: #680136) + Accept gpgparticipants formatted content on STDIN. (Closes: #622560) + Prefer the keyserver defined in ~/.gnupg/gpg.conf if found. (Closes: #720413) + Abort upon gpg failure. + Create temporary directories in $TMPDIR. (Closes: #735384) + Significant performance boost when importing and pruning. + Adapt patch from Gaudenz Steinlin and add an option 'show-photos' to view photos before signing. (Closes: #594252) + Ignore expired / invalid / revoked UIDs when pruning and exporting. * gpgsigs: + Allow long keyids and key fingerprints (preferred). (Closes: #736963) + Wrap long UIDs (when used with --latex) and digest lines. (Closes: #738718) + Make --latex produce a {pdf,lua,Xe}LaTeX-compatible output. (Closes: #618781) + Allow any checksum algorithm supported by gpg. + Don't identify two UATs that have the same size (use the packet hash to distinguish them). + Mark expired or revoked signatures as such. + Display the correct size of UATs. + Don't use a helper --photo-viewer to store the UATs; instead use --attribute-file and split the output. (Closes: #693906) + Add a dependency to GnuPG::Interface to separate the status output from STDOUT. * gpg-mailkeys: + Apply patch from Stefan Huber to check for empty FROM. (Closes: #727000) + Adapt patch from Stefan Huber to make sendmail's path configurable. (Closes: #727001) * gpgparticipants: + Adapt patch from Tanguy Ortolo to output to STDOUT. (Closes: #694465) + Adapt patch from Tanguy Ortolo to output to accept any digest algorithm supported by gpg (default: SHA256,RIPEMD160). (Closes: #659990) * gpglist: + Handle time-limited signatures (don't list them). + Don't mark a signature as revoked if another signature was created after the revocation certificate (cf. RFC 4880 section 5.2.1, signature type 0x30), or if the signature was non-revokable. + Don't identify two UATs that have the same size (use the packet hash to distinguish them). * pgpring: + Apply patch from Fabrizio Tarizzo to display the correct key length for DSA and Elgamal keys. (Closes: #602284) + Apply patch from Fabrizio Tarizzo to show creation date on signatures and, when present, expiration date on keys and signatures. Also, add options -E and -e to respectively exclude expired keys and signatures from the listing. (Closes: #603257) + Upgrade aclocal.m4 to autoconf 2.69. + Don't checkout unsafe symlink depcomp (automatically created by 'automake --add-missing') and other files automatically generated by automake or autoconf. * gpgwrap: + Link with "read-only relocation" flag set. * debian.control: + Add libnet-idn-encode-perl as Depends for gpgsigs. + Add texlive-xetex and fonts-droid as Suggests for gpgsigs. + Bump Standards-Version up to 3.9.5 (no changes). + Add autotools-dev as Build-Depends for keyanalyze. + Add ${python:Depends} as Depends and python as Build-Depends for gpgparticipants-prefill. + Add autoconf and automake1.11 as Build-Depends for keyanalyze. * Don't checkout empty directories, as git-svn(1) doesn't like them. [ Thijs Kinkhorst ] * Remove Peter Palfrader and Christoph Berg from uploaders, as per their request. -- Guilhem Moulin Sat, 22 Mar 2014 20:09:26 +0100 signing-party (1.1.5-1) unstable; urgency=medium [ Thijs Kinkhorst ] * caff: correct man page for keys-from-gnupg (closes: #652683) * caff: fix infinite loop after signing a key with newer gpg. Thanks Bernd Zeimetz for the patch. (closes: #722206, #735536) * Drop keyanalyze transitional package, present since 2009. [ Stefan Huber ] * gpgparticipants: Use more modern hashes than md5. [ Peter Palfrader ] * Add gpgparticipants-prefill by Stefan Huber. * Fix comment about keyids in caffrc.sample. -- Thijs Kinkhorst Thu, 30 Jan 2014 14:28:08 +0100 signing-party (1.1.4-1) unstable; urgency=low [ Thijs Kinkhorst ] * caff: + Correct path of ~/.caffrc in informational messages (Closes: #582603). + Be more verbose on unexpected key ID (Closes: #645792). * gpg-key2ps: + Apply patch from Uwe Kleine-König to deal with latin1 characters (Closes: #596377). * Debian package: + Import dpkg build flags, plus small cleanups. [ Franck Joncourt ] * Debian package: + Switch to dpkg-source 3.0 (quilt) format. - Removed README.source which was needed only for documentation purpose. - Removed quilt framework in d.rules. - Removed BD on quilt. - Added source/format file. + Depend on "default-mta | mail-transport-agent" rather than the old "exim4 | mail-transport-agent". + Bumped Standards-Version up to 3.9.0 (no changes). * gpg-mailkeys: + Correct path of ~/.gpg-mailkeysrc and ~/.signature in manpage. + Add new environment variable SENDMAIL_ARGS to allow user to pass arguments to sendmail (closes: #599409). * caff: + Refactor import of own key and import for keys to sign from keyrings. + Also automatically import keys to sign from the user's normal gpg keyrings. + Use --no-auto-check-trustdb when importing keys from files or the user's normal gpg keyrings (closes: #539643). [ Peter Palfrader ] * caff: + manpage: Refer to all of /usr/share/doc/signing-party/caff/ and not just to /usr/share/doc/signing-party/caff/caffrc.sample (closes: #568052). + Fix horrible &function calls used because of broken prototypes. + Even if all keys to sign were found in the user's normal gpg keyrings we still need to import them (again) from any keyrings passed with --key-files - the keys there might be newer, containing new subkeys (for encryption), uids (for signing) or revocations. + Make importing of keys to be signed from the normal gpg optional (--keys-from-gnupg). + refactor copying of command line options into global config variable. + Create the mail files in ~/.caff/keys even if mail is not sent (closes: #590666). -- Thijs Kinkhorst Wed, 02 Nov 2011 18:17:25 +0100 signing-party (1.1.3-1) unstable; urgency=low [ Franck Joncourt ] * Debian package: + Updated my email address in d.control. + Added myself as contributor in d.copyright * keylookup: + Fixed typo noticed by lintian in manpage keylookup.1. * caff: + Set the Sender header with the email address which is used for the From header. This overrides the default value which was set by the MIME::Entity Perl module based on the local hostname. (Closes: #556782) [ Thijs Kinkhorst ] * Bumped Standards-Version up to 3.8.4 (no changes). -- Thijs Kinkhorst Wed, 03 Feb 2010 22:03:41 +0100 signing-party (1.1.2-1) unstable; urgency=low * gpgsigs: + Added patch from Roland Rosenfeld to support RIPEMD160 checksum. (Closes: #533747). + Updated man page to mention support for SHA256 and RIPEMD160 checksum. + Made removal of nonexistent photos quiet by the use of the force option. + Updated generated tex file in latex mode so that it uses the grffile package. This allows pdflatex to process our tex file assuming the photos are previously converted to PDF. (Closes: #542478) + Added texlive-latex-recommended as a suggested package in debian/control. It contains the grffile latex package. * caff: Updated check for the local-user keyids. + Moved the current check to a new function get_local_user_keys(). + Warned the user if a local-user keyid is not listed as a keyid in ~/.caffrc. (Closes: #540165). * gpgdir: New upstream release. * gpg-mailkeys: + The charset for the text of the message is deduced from the charset used by ~/.gpg-mailkeysrc and ~/.signature. The text message is encoded in quoted printable and thus it requires a new dependency on qprint in debian/control. (Closes: #545186) + Mentionned both the .gpg-mailkeysrc and .signature files in the manpage. * debian.control: added ${misc:Depends} as Depends for the keyanalyze package. * Bumped Standards-Version up to 3.8.3 (no changes). -- Franck Joncourt Thu, 24 Sep 2009 19:29:07 +0200 signing-party (1.1.1-1) unstable; urgency=low [ Christoph Berg ] * gpg-key2ps: assume gpg output to be in utf-8, instead of depending on the current locale. [ Franck Joncourt ] * caff: + Take the CONFIG{'mail'} variable from .caffrc into count. (Closes: #520387) + Update caff's manpage to mention a keyid specified with -u requires to be listed in the configuration file through the keyid variable. Add a note in caffrc.sample about using the -u option to select which keyid from the CONFIG{'keyid'} as to be used for signing. (Closes: #482693) * keyanalyze/top50.pl: Fix the error message when a line from stdin does not match. * keyanalyze/analyze.sh: + Make the entries in the msd-sorted.txt file sorted. + Used msd.txt file as input for top50.pl. The current pattern in top50.pl does not match data from the msd-sorted.txt file. + Allowed the top1000 report to report the first 1000 ranks by passing *-n 1000* to top50.pl. Set to 50 by default. [ Thijs Kinkhorst ] * Make pool.sks-keyservers.net the default keyserver (closes: #527941). * Checked for policy 3.8.1, no changes necessary. -- Thijs Kinkhorst Fri, 12 Jun 2009 08:50:33 +0200 signing-party (1.1-2) unstable; urgency=low * Fix build error when only building the binary package by fixing the build-arch target (Closes: #516804). -- Thijs Kinkhorst Mon, 23 Feb 2009 21:37:20 +0100 signing-party (1.1-1) unstable; urgency=low [ Franck Joncourt ] * Imported gpgdir. (Closes: #498167) * Imported gpgwrap. (Closes: #454074) Added Quilt framework/README.source to handle gpgwrap. * Refreshed debian/control and README files with the new descriptions. * Added new dependencies required by gpgdir: libterm-readkey-perl, libclass-methodmaker-perl * Added wipe as suggested package (useful with gpgdir). [ Thijs Kinkhorst ] * Checked for policy 3.8.0, no changes. * caff: Fix pod syntax problems (Closes: #485653). * gpg-key2ps: no longer waste paper on the subkey information by default. It's not relevant to the keysigning process, but added option to enable if you want it. * Remove obsolete transitional packages sig2dot, springgraph. * Upgrade packaging to debhelper level 7. [ Christoph Berg ] * Import keyanalyze into signing-party. Thanks to Matthew Wilcox for the permission. Provide a transitional package. + Add patch to flatten output structure for small keyrings. (Closes: #309101) + Update config.{sub,guess}. (Closes: #365148) + Add patch by Stephan Beyer to improve process_keys' input parsing, remedying the need of weird grep and sed commands. (Closes: #370571) + Add -h option to keyanalyze, and update manpage. (Closes: #370570) * Put examples in .../examples/$prog, not the other way round. * Use dh_lintian. -- Thijs Kinkhorst Sun, 22 Feb 2009 17:58:09 +0100 signing-party (1.0-2) unstable; urgency=low * gpg-mailkeys: Fix parameter escaping of printf, thanks Olivier Tetard (Closes: #478151). * springgraph: clarify help text (Closes: 474351). * Switch dependency libmime-perl to libmime-tools-perl (renamed). * Put springgraph and sig2dot in section graphics. -- Thijs Kinkhorst Tue, 20 May 2008 12:23:52 +0200 signing-party (1.0-1) unstable; urgency=low * The 1.0 release. [ Christoph Berg ] * gpglist: Do not barf on revokers (rvk). Thanks to Faidon Liambotis for spotting. * sig2dot, springgraph: new scripts joining the family, no longer provided as separate packages. We shamelessly bump our version number to something greater, and build transitional packages. * gpg-key2ps: also handle revoked subkeys (Closes: #467001). * gpgsigs: correctly handle keys with several photo ids via a helper script in /usr/share/signing-party/ (Closes: #453840). * Start moving the installation machinery from debian/* to Makefiles. [ Thijs Kinkhorst ] * keylookup: cope with 16 or 40 nibble keyids, thanks Philippe Teuwen (Closes: #466716). * gpg-key2ps: also accept revoked-style long option, thanks Luca Capello (Closes: #466993). * gpgparticipants: new script to create a participant list useful for party organisers using the Zimmermann-Sassaman key-signing protocol. Thanks Philippe Teuwen (Closes: #467338). * caff: always update the user's key from their GnuPG home, to adequately cope with changed keys (Closes: #462897). -- Christoph Berg Sun, 16 Mar 2008 20:58:50 +0100 signing-party (0.4.13-1) unstable; urgency=low [ Christoph Berg ] * gpgsigs: Implement support for LaTeX output and photo ids. (Closes: #412433, #430607) * gpg-key2ps: Mention http://www.debian.org/events/materials/business-cards/ as an alternative. (Closes: #439510) [ Thijs Kinkhorst ] * Move Homepage to control field. -- Christoph Berg Sun, 18 Nov 2007 21:02:49 +0100 signing-party (0.4.12-1) unstable; urgency=low [ Christoph Berg ] * caff: + Allow spaces in keyids/fingerprints for easier cut-and-paste. * Replaces: keylookup. [ Peter Palfrader ] * caff: + Fix a warning message when encrypting fails. + Try to handle expired keys specified in also-encrypt-to better. [ Thijs Kinkhorst ] * caff: + Add filename to one of the MIME parts as its absence breaks certain virus scanners. + Make -m (mail options) a yes/no/ask-yes/ask-no option, based on a suggestion by Gerfried Fuchs (Closes: #383423). * gpg-mailkeys: + Resolve bashisms in gpg-mailkeys. * Cleanup package dependencies. -- Thijs Kinkhorst Thu, 23 Aug 2007 12:09:43 +0200 signing-party (0.4.11-1) unstable; urgency=low The "Debconf 7" release. * gpgsigs: Apply patch by Joachim Breitner to also show incoming signatures and support sha256 checksum (Closes: #428538). -- Christoph Berg Sun, 17 Jun 2007 12:32:21 +0200 signing-party (0.4.10-1) unstable; urgency=low * caff: + Fix syntax error in example config variables (Closes: #413020). + Fix perl warnings when calling pgp-fixkey with unknown keyid or with empty signature create date. * gpg-key2ps: + Add '-1' option to only display one column of slips, for extra wide keys (Closes: #399474). * keylookup: + Fix perl warnings caused by empty lines from gpg output. * Drop transitional and now obsolete keylookup package. * Remove no longer needed dependency on mailx. -- Thijs Kinkhorst Tue, 22 May 2007 11:41:32 +0200 signing-party (0.4.9-1) unstable; urgency=low * caff: + Fix a bug with checking if we have exactly one or more keys that failed downloading. + Mention in manpage that keyserver-options is a useful setting in .caff/gnupghome/gpg.conf (Closes: #392811). + q-p-encode From: header (Closes: #366745). -- Christoph Berg Sun, 29 Oct 2006 21:02:56 +0100 signing-party (0.4.8-1) unstable; urgency=low * gpglist: do not die with with-fingerprint (Closes: #382019). * gpg-key2ps: add --list-key to gpg call (works around #382794). * caff: when set, use $ENV{'GNUPGHOME'} to find secring.gpg. Suggested by Gerfried Fuchs. -- Christoph Berg Sun, 8 Oct 2006 01:29:59 +0200 signing-party (0.4.7-1) unstable; urgency=low * Update my maintainer address. * gpg-mailkeys: use right content-type for attached key, thanks Wesley Landaker (Closes: #370566). -- Thijs Kinkhorst Tue, 4 Jul 2006 15:33:53 +0200 signing-party (0.4.6-2) unstable; urgency=low * gpgsigs: recognize rvk (revoker), found in ksp-dc6.txt. * Bump Standards-Version to 3.7.2, no changes. -- Christoph Berg Fri, 12 May 2006 13:40:37 -0500 signing-party (0.4.6-1) unstable; urgency=low * caff: + Try hostname without -f first to be compatible with BSD (Closes: #356830). + Make local-user a config option, thanks to Michael C. Toren for the patch (Closes: #361316). + Make local-user accept a list of keyids (Closes: #333832). + Make also-encrypt-to a list, add a commented stanza to the default config file (Closes: #325163). + Include mail-template in default config file, some minor syntax fixes. + Bump copyright, use URL in User-Agent header. * pgp-clean: + Import caff's getopt handling. + Add option to allow exporting subkeys (Closes: #359698). * Bump Standards-Version, no change. -- Christoph Berg Wed, 3 May 2006 23:43:44 +0200 signing-party (0.4.5-1) unstable; urgency=low * Upgrade debhelper compatibility to the recommended level 5. * Update FSF addresses. * caff: tweak documentation. * caff: note that mailed keys are encrypted (suggested by Sune Vuorela). * caff: You can now specify additional arguments to pass to the send method of Mail::Mailer. This allows you to send mails via SMTP and use authentication for instance. Thanks to Martin von Gagern. * gpg-key2ps, keylookup: make them less dependent on specific installation paths and thus better portable outside of Debian (Closes: #354142). -- Christoph Berg Sun, 12 Mar 2006 13:34:20 +0100 signing-party (0.4.4-2) unstable; urgency=low * Fix path in caff(1) (Closes: #327556, thanks Axel Beckert). -- Christoph Berg Sun, 11 Sep 2005 23:55:27 +0200 signing-party (0.4.4-1) unstable; urgency=low * caff: + When building a ~/.caffrc when there is none we would fail if we cannot find a key or an email address for the current user. Change this to produce something sensible in that case as well (closes: #325156). -- Christoph Berg Sun, 11 Sep 2005 02:01:50 +0200 signing-party (0.4.3-1) unstable; urgency=low * gpg-key2ps: + handle revoked uids in perl to get linecount right (Closes: #320785). * Debian package: + gpgsigs: install example files. + Install caff's pgp-fixkey. + Make the version of the empty keylookup package grow too. + Install keylookup's old Debian changelog as changelog.keylookup.gz. + Make some lintian overrides. + Slightly change keylookup's long description. + Update signing-party's short description. -- Peter Palfrader Tue, 23 Aug 2005 01:51:16 +0200 signing-party (0.4.2-1) unstable; urgency=low * New upstream release. * debian/control: move libpaper-utils to Recommends, remove redundant perl dependency. * Do not ship useless README file in the binary package. It's more or less the same as the long description * caff: + create configfile on first use (Closes: #316611). + note use of ~/.caff/gnupghome/gpg.conf in manpage (Closes: #321235). + allow adding a Reply-To: header (Closes: #321007, thanks to Joost van Baal for the patch). + parse IMPORT_OK correctly (Closes: #321496). + be more verbose when user tries to sign obsolete v3 crap. + install README.*. * gpglist: added new script, thanks to Uli Martens. * gpg-key2ps: + use Getopt::Long, general code cleanup. + gpg-key2ps "my name" works now. + Convert from shell script to perl. + Fix too few slips on a page (Closes: #320785). * Add keylookup: ncurses wrapper around gpg --search + keylookup now calls gpg to search for keys instead of connecting to the keyserver itself. Therefore there is no need to parse ~/.gnupg/gpg.conf anymore (Closes: #164750). + Recommend dialog | whiptail -- Peter Palfrader Wed, 17 Aug 2005 23:06:15 +0200 signing-party (0.4.1-1) unstable; urgency=low * New upstream release. Lots of fixes and enhancements, including: gpg-key2ps: + handles revoked subkeys, thanks Christof Douma (Closes: #311990). gpg-mailkeys: + add -i switch to sendmail to prevent lines with a dot causing unexpected behaviour, thanks Stephen Gran (Closes: #319762). caff: + now handles attribute UIDs (Closes: #316278). + now detects all invalid keyids (Closes: #317754). + allows to BCC the signer, thanks Joost van Baal (Closes: #316176). + new option to pause before continuing to the signing step, thanks Christof Douma (Closes: #316129). + create unique attachment filenames, thanks Robin H. Johnson (Closes: #318469). + let caff import keys from file, thanks Jon Åslund (Closes: #318744). + Improve diagnosis if caff can't read answers from STDIN, like when people use xargs (Closes: #319519). * Depend on libtext-template-perl as caff needs it. * Update to Standards-Version 3.6.2, no changes necessary. * Add gpglist to distribution. -- Peter Palfrader Sun, 7 Aug 2005 21:32:23 +0200 signing-party (0.4.0-1) unstable; urgency=low * Merging signing-party with pgp-tools, now contains caff and gpgsigs as well. * Update gpg-mailkeys man page to list environment vars introduced in 0.3.0 (Closes: #310418). -- Thijs Kinkhorst Wed, 8 Jun 2005 19:49:11 +0200 signing-party (0.3.0-1) unstable; urgency=low * New maintainer, Simon is moving to co-maintenance. * New upstream release: + Fix missing section number from manpages. (fixes Lintian warnings) + Add more documentation (README, and in gpg-mailkeys). + In gpg-key2ps: - Add handling for 'tru' and 'uat' packets. (Closes: #222664) - Change env.var PAPERCONF to PAPERSIZE due to changed behaviour since libpaper-1.1.13. (Closes: #172698) - Truncate too long UIDs so they don't mess up the keys next to them. (Closes: #171846) + In gpg-mailkeys: - Send the key as an attachment. (Closes: #176245) - Make gpg-mailkeys messages more customizable. (Closes: #200070) - By default, let sendmail determine the From-line (more reliable). * Update to Standards-Version 3.6.1 (no changes necessary). * Depend on libpaper-utils >= 1.1.13. * Clarify copyright / maintainer documentation. -- Thijs Kinkhorst Sat, 5 Mar 2005 14:39:24 +0100 keyanalyze (0.0.200204-1) unstable; urgency=low * Initial release (Closes: #238140) * pgpring: - Fixed key ID generation for ElGamal primary keys - Enabled -W -Wall -g -O2 CFLAGS - Fixed type-punning problems - Fixed implicit declaration of exit() - Wrote manpage * process_keys: - Deleted two unused variables - Wrote manpage * keyanalyze: - Rewrote list handling to solve off-by-one problems - Used an array of structs instead of a lot of arrays - Made it accept -i and -o arguments to override the input and output names. - Wrote manpage -- Matthew Wilcox Sat, 19 Jun 2004 13:07:48 -0400 signing-party (0.2.3-2) unstable; urgency=low * Added handling for 'tru' packets -- Simon Richter Mon, 8 Dec 2003 21:54:50 +0100 signing-party (0.2.3-1) unstable; urgency=low * New upstream release (Closes: #180455) -- Simon Richter Sat, 15 Feb 2003 16:09:27 +0100 signing-party (0.2.2.1-1) unstable; urgency=low * Upstream bugfix release (some changes from 0.2 to 0.2.1 dropped out of 0.2.2 again) -- Simon Richter Fri, 1 Nov 2002 12:15:15 +0100 signing-party (0.2.2-1) unstable; urgency=low * Fixed manpage bug (Closes: #137924, #137937) * Upstream changelog now contains data (Closes: #158854) * Finally wrote a real description -- Simon Richter Fri, 4 Oct 2002 22:41:50 +0200 signing-party (0.2.1-1) unstable; urgency=low * New release -- Simon Richter Thu, 29 Aug 2002 17:12:29 +0200 signing-party (0.2-1) unstable; urgency=low * New release * Can now print multiple keys * Supports alternate paper sizes (Closes: #134975) * New script: gpg-mailkeys -- Simon Richter Thu, 21 Feb 2002 22:24:09 +0100 signing-party (0.1-1) unstable; urgency=low * Initial Release (Closes: #117977). -- Simon Richter Fri, 2 Nov 2001 19:33:42 +0100 signing-party-2.12/debian/changelog.keylookup000066400000000000000000000020551500571021200213410ustar00rootroot00000000000000signing-party (0.4.2-1) unstable; urgency=low * Merged with signing-party in 0.4.2-1. This package is now empty and can safely be removed. -- Peter Palfrader Tue, 16 Aug 2005 16:52:27 +0200 keylookup (2.2-2) unstable; urgency=low * Fixed --help output, closes: #169298. -- Ola Lundqvist Thu, 17 Apr 2003 23:33:28 +0200 keylookup (2.2-1) unstable; urgency=low * New upstream version, closes: #161392. -- Ola Lundqvist Tue, 24 Sep 2002 11:30:41 +0200 keylookup (2.1-1) unstable; urgency=low * New upstream version with english documentation, closes: #120861. * Changed section from non-US/main to utils. -- Ola Lundqvist Thu, 29 Aug 2002 07:09:35 +0200 keylookup (2.0-2) unstable; urgency=low * Fixed description, closes: #122456, #124838. -- Ola Lundqvist Sat, 19 Jan 2002 12:06:22 +0100 keylookup (2.0-1) unstable; urgency=low * Initial release, closes: 117892. -- Ola Lundqvist Thu, 1 Nov 2001 11:19:16 +0100 signing-party-2.12/debian/changelog.sig2dot000066400000000000000000000112601500571021200206700ustar00rootroot00000000000000sig2dot (0.37) unstable; urgency=low * Add patch by Matteo Corti to make the graph title configurable. * Fix targets in debian/rules. * Bump Standards-Version. -- Christoph Berg Wed, 31 May 2006 16:33:41 +0200 sig2dot (0.36) unstable; urgency=low * Patch by Thomas Huriaux: sig2dot must not include external signatures if using a subset of your keyring (Closes: #302693). * Patch by Eero Häkkinen: recognize trust signatures with a level >= 10 (Closes: #348783). -- Christoph Berg Sat, 21 Jan 2006 10:08:40 +0100 sig2dot (0.35) unstable; urgency=low * Use strict and warnings. * New -h/-q/-v options. * Remove comment fields from UIDs (Closes: #318751). * Support localized strings for revoked keys (Closes: #318740). * Bump Standards-Version. -- Christoph Berg Fri, 23 Sep 2005 13:27:33 +0200 sig2dot (0.34-4) unstable; urgency=high * Fix breakage with gnupg 1.4 (keys with trust signatures) noted by Matthew Wilcox (Closes: #308271). -- Christoph Berg Wed, 18 May 2005 23:48:47 +0200 sig2dot (0.34-3) unstable; urgency=low * New maintainer (Closes: #297446). * Fixed to work with gpg 1.4 output, fixed some perl -w warnings. * Updated pointer to dot syntax documentation. * Fixed unescaped hyphens in sig2dot.1 and removed some whitespace. * Removed redundant stuff in debian/rules and fixed non-functional dh_perl call. * Recommends: gnupg. -- Christoph Berg Sun, 6 Mar 2005 20:18:41 +0100 sig2dot (0.34-2) unstable; urgency=low * Orphan as in bug #297446 -- Kevin M. Rosenberg Sat, 5 Mar 2005 02:43:15 -0700 sig2dot (0.34-1) unstable; urgency=low * Incorporate patch from Thomas Huriaux -- Kevin M. Rosenberg Fri, 28 Jan 2005 20:00:15 -0700 sig2dot (0.33-1) unstable; urgency=low * Fix option -d processing (closes:285706) -- root Thu, 16 Dec 2004 05:44:21 -0700 sig2dot (0.32-1) unstable; urgency=low * Fix typo (closes:207025) -- Kevin M. Rosenberg Sun, 24 Aug 2003 13:27:57 -0600 sig2dot (0.31-1) unstable; urgency=low * Avoid potential division by 0 -- Kevin M. Rosenberg Wed, 20 Aug 2003 10:47:04 -0600 sig2dot (0.30k-2) unstable; urgency=low * Add -u parameter to man page. -- Kevin M. Rosenberg Thu, 31 Jul 2003 07:58:06 -0600 sig2dot (0.30k-1) unstable; urgency=low * Add localization patch by Marcus Frings (closes:202503) -- Kevin M. Rosenberg Thu, 31 Jul 2003 00:55:01 -0600 sig2dot (0.30j-1) unstable; urgency=low * Revert patch (closes: 202484) -- Kevin M. Rosenberg Tue, 22 Jul 2003 21:27:22 -0600 sig2dot (0.30i-1) unstable; urgency=low * Incorporate patch for renderdate option setting (closes: 201339) -- Kevin M. Rosenberg Tue, 15 Jul 2003 10:24:28 -0600 sig2dot (0.30h-1) unstable; urgency=low * Incorporate patch for non-7bit names (closes:200329) -- Kevin M. Rosenberg Mon, 7 Jul 2003 10:25:19 -0600 sig2dot (0.30g-1) unstable; urgency=low * Remove double quotes around names (closes:193123) -- Kevin M. Rosenberg Fri, 16 May 2003 23:23:56 -0600 sig2dot (0.30f-1) unstable; urgency=low * Add -a option (closes:189417) -- Kevin M. Rosenberg Sun, 11 May 2003 22:27:36 -0600 sig2dot (0.30e-1) unstable; urgency=low * Add N to regex (closes:188277) -- Kevin M. Rosenberg Tue, 8 Apr 2003 19:34:50 -0600 sig2dot (0.30d-1) unstable; urgency=low * Improve comment (closes: 186608) -- Kevin M. Rosenberg Fri, 28 Mar 2003 07:52:26 -0700 sig2dot (0.30c-1) unstable; urgency=low * Add another flag to regex (closes: 186138) -- Kevin M. Rosenberg Mon, 24 Mar 2003 18:42:05 -0700 sig2dot (0.30b-1) unstable; urgency=low * Fix comment lines (closes: 183818) -- Kevin M. Rosenberg Fri, 7 Mar 2003 19:42:39 -0700 sig2dot (0.30a-1) unstable; urgency=low * Fix manpage and copyright (closes: 183613) Thanks again for Marco Bodrato * Update sig2dot comments with v0.30 information -- Kevin M. Rosenberg Thu, 6 Mar 2003 07:01:09 -0700 sig2dot (0.30-1) unstable; urgency=low * Make regex more robust. Closes:#183496. Thanks Marco Bodrato. -- Kevin M. Rosenberg Wed, 5 Mar 2003 11:11:09 -0700 sig2dot (0.29-2) unstable; urgency=low * Change architecture to all -- Kevin M. Rosenberg Fri, 21 Feb 2003 12:21:08 -0700 sig2dot (0.29-1) unstable; urgency=low * Initial Release. -- Kevin M. Rosenberg Tue, 18 Feb 2003 14:11:08 -0700 signing-party-2.12/debian/changelog.springgraph000066400000000000000000000046721500571021200216520ustar00rootroot00000000000000springgraph (0.82-6) unstable; urgency=low * Add missing targets in debian/rules (Closes: #395791). * Build package in binary-indep. * Use dh_perl. -- Christoph Berg Sat, 28 Oct 2006 13:32:54 +0200 springgraph (0.82-5) unstable; urgency=low * Fix typo in manpage (Closes: #310340, thanks to A Costa for the patch). * Add syntax example to manpage. * Use ${perl:Depends}. * Bump Standards-Version. -- Christoph Berg Mon, 5 Sep 2005 14:01:12 +0200 springgraph (0.82-4) unstable; urgency=low * New maintainer (Closes: #297447). * Removed redundant parts of debian/rules. * Updated pointer to dot language documentation (Closes: #295700). -- Christoph Berg Sun, 6 Mar 2005 16:37:47 +0100 springgraph (0.82-3) unstable; urgency=low * Orphan as in bug #297447 -- Kevin M. Rosenberg Sat, 5 Mar 2005 02:44:42 -0700 springgraph (0.82-2) unstable; urgency=low * Add more documentation (closes:267495) -- Kevin M. Rosenberg Mon, 23 Aug 2004 14:45:18 -0600 springgraph (0.82-1) unstable; urgency=low * Accept patch from Tobias Gruetzma for more layout options (closes:234117) -- Kevin M. Rosenberg Tue, 24 Feb 2004 08:53:45 -0700 springgraph (0.81-1) unstable; urgency=low * Caption stderr progress notes (closes:206147) -- Kevin M. Rosenberg Wed, 20 Aug 2003 10:37:21 -0600 springgraph (0.80.1-1) unstable; urgency=low * Allow use of libgd-gd2-noxpm-perl (closes:188856) -- Kevin M. Rosenberg Mon, 14 Apr 2003 08:15:30 -0600 springgraph (0.80-1) unstable; urgency=low * Apply speed-up patch from Marco Bodrato (closes: 185532) -- Kevin M. Rosenberg Wed, 19 Mar 2003 21:06:15 -0700 springgraph (0.79-4) unstable; urgency=low * Use libgd-gd2-perl rather than libgd-gd1-perl (closes:183919) -- Kevin M. Rosenberg Sat, 8 Mar 2003 05:20:43 -0700 springgraph (0.79-3) unstable; urgency=low * Change architecture to all -- Kevin M. Rosenberg Fri, 21 Feb 2003 12:19:36 -0700 springgraph (0.79-2) unstable; urgency=low * Added perl dependency * Move to extra priority. * Use dh_perl and ${perl:depend} -- Kevin M. Rosenberg Tue, 18 Feb 2003 19:21:04 -0700 springgraph (0.79-1) unstable; urgency=low * Initial Release. -- Kevin M. Rosenberg Tue, 18 Feb 2003 14:11:08 -0700 signing-party-2.12/debian/control000066400000000000000000000047271500571021200170610ustar00rootroot00000000000000Source: signing-party Section: misc Priority: optional Maintainer: Guilhem Moulin Uploaders: Simon Richter Build-Depends: autoconf, automake, debhelper-compat (= 13), dh-python, libmd-dev, python3 Standards-Version: 4.7.2 Vcs-Git: https://salsa.debian.org/signing-party-team/signing-party.git Vcs-Browser: https://salsa.debian.org/signing-party-team/signing-party Rules-Requires-Root: no Package: signing-party Architecture: any Depends: gnupg, libclass-methodmaker-perl, libgnupg-interface-perl, libmailtools-perl, libmime-tools-perl, libnet-idn-encode-perl, libterm-readkey-perl, libtext-template-perl, qprint, ${misc:Depends}, ${perl:Depends}, ${python3:Depends}, ${shlibs:Depends} Recommends: default-mta | mail-transport-agent, dialog | whiptail, libgd-gd2-noxpm-perl | libgd-gd2-perl, libpaper-utils Suggests: fonts-noto-cjk, fonts-noto-mono, imagemagick | graphicsmagick-imagemagick-compat, mutt | neomutt, qrencode, texlive-font-utils, texlive-latex-extra, texlive-latex-recommended, texlive-xetex, wipe Provides: keyanalyze, sig2dot, springgraph Description: Various OpenPGP related tools signing-party is a collection for all kinds of PGP/GnuPG related things, including tools for signing keys, keyring analysis, and party preparation. . * caff: CA - Fire and Forget signs and mails a key * pgp-clean: removes all non-self signatures from key * pgp-fixkey: removes broken packets from keys * gpg-mailkeys: simply mail out a signed key to its owner * gpg-key2ps: generate PostScript file with fingerprint paper slips * gpgdir: recursive directory encryption tool * gpglist: show who signed which of your UIDs * gpgsigs: annotates list of GnuPG keys with already done signatures * gpgparticipants: create list of party participants for the organiser * gpgwrap: a passphrase wrapper * keyanalyze: minimum signing distance (MSD) analysis on keyrings * keylookup: ncurses wrapper around gpg --search * sig2dot: converts a list of GnuPG signatures to a .dot file * springgraph: creates a graph from a .dot file * keyart: creates a random ASCII art of a PGP key file * gpg-key2latex: generate LaTeX file with fingerprint paper slips signing-party-2.12/debian/copyright000066400000000000000000000204271500571021200174040ustar00rootroot00000000000000Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Source: native package Files: * Copyright: © 2000, 2002, 2004-2006 Peter Palfrader © 2004-2008 Christoph Berg © 2001-2005 Simon Richter © 2005-2008 Thijs Kinkhorst © 2008-2011 Franck Joncourt License: BSD-3-clause Files: caff/* Copyright: © 2004-2006 Peter Palfrader © 2005-2006 Christoph Berg © 2014-2016 Guilhem Moulin License: BSD-3-clause Files: gpgdir/* Copyright: © 2002-2008 Michael B. Rash (mbr@cipherdyne.org) License: GPL-2+ Comment: Downloaded from http://www.cipherdyne.com/gpgdir/ . Files: gpg-key2ps/* Copyright: © 2001-2005 Simon Richter © 2005-2008 Thijs Kinkhorst © 2005-2008 Christoph Berg License: GPL-2+ Files: gpglist/* Copyright: © 2004 Uli Martens © 2005 Peter Palfrader License: BSD-3-clause Files: gpg-mailkeys/* Copyright: © 2001-2005 Simon Richter © 2005 Thijs Kinkhorst License: GPL-2+ Files: gpgparticipants/* Copyright: © 2008 Philippe Teuwen License: GPL-2+ Files: gpgparticipants/gpgparticipants-prefill* Copyright: © 2013 Stefan Huber © 2014 Peter Palfrader License: MIT Files: gpgparticipants/gpgparticipants-filter* Copyright: © 2019 Max Harmathy License: MIT Files: gpgsigs/* Copyright: © 2004 Uli Martens © 2004-2005 Peter Palfrader © 2004-2007 Christoph Berg © 2014-2015 Guilhem Moulin License: BSD-3-clause Files: gpgwrap/* Copyright: © 2004-2006 Karsten Scheibler License: GPL-2+ Comment: Downloaded from http://unusedino.de/gpgwrap/ . Files: keyanalyze/* Copyright: © 2001 M. Drew Streib © 1997-2000 Thomas Roessler © 2001 Hal J. Burch © 2001 Matt Kraai © 2001 Steve Langasek © 2003-2004 Matthew Wilcox © 1996-2000 Michael R. Elkins License: GPL-2 Comment: Downloaded from http://dtype.org/keyanalyze/code.php . Files: keylookup/* Copyright: © 2000, 2002 Christian Kurz © 2000, 2002, 2005 Peter Palfrader License: GPL-2+ Files: sig2dot/* Copyright: © 2002 © 2003, 2005 Kevin M. Rosenberg © 2005-2006 Christoph Berg License: GPL-2+ Comment: Downloaded from http://www.chaosreigns.com/code/sig2dot/ . Files: springgraph/* Copyright: © 2002 © 2003-2005 Kevin M. Rosenberg License: GPL-2+ Comment: Downloaded from http://www.chaosreigns.com/code/springgraph/ . Files: keyart/* Copyright: © 2014 Aaron Toponce © 2016 Guilhem Moulin License: BSD-2-clause Files: gpg-key2latex/* Copyright: © 2014-2016 Guilhem Moulin License: GPL-3+ License: GPL-3+ This package 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 3 of the License, or (at your option) any later version. . The full text of version 3 of the GPL is distributed in /usr/share/common-licenses/GPL-3 on Debian systems. License: GPL-2+ This package 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. . The full text of version 2 of the GPL is distributed in /usr/share/common-licenses/GPL-2 on Debian systems. License: GPL-2 This package 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, version 2 of the License. . The full text of version 2 of the GPL is distributed in /usr/share/common-licenses/GPL-2 on Debian systems. License: MIT Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: . The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. . THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. License: BSD-2-clause All rights reserved. . Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: . 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. . THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. License: BSD-3-clause All rights reserved. . Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the University nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. . THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. signing-party-2.12/debian/docs000066400000000000000000000001211500571021200163110ustar00rootroot00000000000000debian/changelog.keylookup debian/changelog.sig2dot debian/changelog.springgraph signing-party-2.12/debian/gbp.conf000066400000000000000000000003101500571021200170550ustar00rootroot00000000000000[DEFAULT] upstream-branch = master debian-branch = master upstream-tag = v%(version)s debian-tag = debian/%(version)s pristine-tar = False [import-orig] filter = [ 'debian/**' ] merge-mode = replace signing-party-2.12/debian/install000066400000000000000000000014271500571021200170410ustar00rootroot00000000000000caff/caff usr/bin caff/pgp-clean usr/bin caff/pgp-fixkey usr/bin gpg-key2latex/gpg-key2latex usr/bin gpg-key2ps/gpg-key2ps usr/bin gpg-mailkeys/gpg-mailkeys usr/bin gpgdir/gpgdir usr/bin gpglist/gpglist usr/bin gpgparticipants/gpgparticipants usr/bin gpgparticipants/gpgparticipants-filter usr/bin gpgparticipants/gpgparticipants-prefill usr/bin gpgwrap/bin/gpgwrap usr/bin keyanalyze/analyze.sh usr/share/doc/signing-party/examples/keyanalyze keyanalyze/scripts/* usr/share/doc/signing-party/examples/keyanalyze/scripts keyanalyze/willy/* usr/share/doc/signing-party/examples/keyanalyze/willy keyart/doc/druken-bishop.txt keyart/doc/party-worksheet keyart/README usr/share/doc/signing-party/keyart keyart/doc/examples/* usr/share/doc/signing-party/examples/keyart keyart/keyart usr/bin signing-party-2.12/debian/manpages000066400000000000000000000007451500571021200171700ustar00rootroot00000000000000caff/caff.1 caff/pgp-clean.1 caff/pgp-fixkey.1 gpg-key2latex/gpg-key2latex.1 gpg-key2ps/gpg-key2ps.1 gpg-mailkeys/gpg-mailkeys.1 gpgdir/gpgdir.1 gpglist/gpglist.1 gpgparticipants/gpgparticipants-filter.1 gpgparticipants/gpgparticipants-prefill.1 gpgparticipants/gpgparticipants.1 gpgsigs/gpgsigs.1 gpgwrap/doc/gpgwrap.1 keyanalyze/keyanalyze.1 keyanalyze/pgpring/pgpring.1 keyanalyze/process_keys.1 keyart/doc/keyart.1 keylookup/keylookup.1 sig2dot/sig2dot.1 springgraph/springgraph.1 signing-party-2.12/debian/patches/000077500000000000000000000000001500571021200170735ustar00rootroot00000000000000signing-party-2.12/debian/patches/gpgwrap_makefile.diff000066400000000000000000000020351500571021200232310ustar00rootroot00000000000000From: Franck Joncourt Subject: [PATCH] fixes/gpgwrap_makefile This allows the main Makefile not to fail when trying to install gpgwrap. It just adds the install target in the gpgwrap Makefile. That could be avoided by handling differently the gpgwrap project in the main Makefile, but then it is going to be the mess. The new target does not install anything since so far the man page is currently installed from debian/rules, and thus everything to install gpgwrap is added to debian.rules for consistency purpose. Signed-off-by: Franck Joncourt --- gpgwrap/Makefile | 4 +++- 1 files changed, 3 insertions(+), 1 deletions(-) diff --git a/gpgwrap/Makefile b/gpgwrap/Makefile index 30064df..3dc65b9 100644 --- a/gpgwrap/Makefile +++ b/gpgwrap/Makefile @@ -1,9 +1,11 @@ MAKE=make -.PHONY: all clean +.PHONY: all clean install all: cd src && ${MAKE} all DIET="${DIET}" +install: + clean: cd src && ${MAKE} clean -- tg: (9286c56..) fixes/gpgwrap_makefile (depends on: master) signing-party-2.12/debian/patches/series000066400000000000000000000000261500571021200203060ustar00rootroot00000000000000gpgwrap_makefile.diff signing-party-2.12/debian/rules000077500000000000000000000006371500571021200165320ustar00rootroot00000000000000#!/usr/bin/make -f export DEB_BUILD_MAINT_OPTIONS := hardening=+all NAME = signing-party datarootdir = $(CURDIR)/debian/$(NAME)/usr/share docdir = $(datarootdir)/doc/$(NAME) %: dh $@ --with python3 execute_after_dh_installdocs: install -Dt $(docdir)/caff caff/README caff/README.* caff/caffrc.sample install -T gpgdir/ChangeLog $(docdir)/changelog.gpgdir install -T gpgwrap/NEWS $(docdir)/changelog.gpgwrap signing-party-2.12/debian/signing-party.lintian-overrides000066400000000000000000000000421500571021200236130ustar00rootroot00000000000000wrong-name-for-upstream-changelog signing-party-2.12/debian/source/000077500000000000000000000000001500571021200167445ustar00rootroot00000000000000signing-party-2.12/debian/source/format000066400000000000000000000000141500571021200201520ustar00rootroot000000000000003.0 (quilt) signing-party-2.12/debian/source/lintian-overrides000066400000000000000000000000561500571021200223260ustar00rootroot00000000000000# we're upstream debian-watch-file-is-missing signing-party-2.12/gpg-key2latex/000077500000000000000000000000001500571021200167055ustar00rootroot00000000000000signing-party-2.12/gpg-key2latex/Makefile000066400000000000000000000001341500571021200203430ustar00rootroot00000000000000MAN = gpg-key2latex.1 all: $(MAN) %.1: % pod2man $< > $@ install: clean: rm -f $(MAN) signing-party-2.12/gpg-key2latex/gpg-key2latex000077500000000000000000000455021500571021200213240ustar00rootroot00000000000000#!/usr/bin/perl # gpg-key2latex -- Generate a LaTeX file for fingerprint slips. # # Copyright (c) 2014-2016 Guilhem Moulin # # 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 3 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 . use warnings; use strict; our $VERSION = '@@VERSION@@'; use Encode (); use File::Temp (); use I18N::Langinfo 'langinfo'; use IO::Handle (); use IO::Select (); use POSIX qw/dup2 strftime/; use Pod::Usage 'pod2usage'; use Getopt::Long qw/:config posix_default no_ignore_case gnu_compat bundling auto_help auto_version/; use GnuPG::Interface (); my %options; GetOptions(\%options, qw/paper-size|p=s show-subkeys|s show-photo show-qrcode qrcode-data=s attr-height=i/) or pod2usage(2); pod2usage(2) unless @ARGV; my $LOCALE = Encode::find_encoding(langinfo(I18N::Langinfo::CODESET())); chomp ($options{'paper-size'} = `paperconf` || 'a4') unless defined $options{'paper-size'}; $options{'paper-size'} =~ y/[A-Z]/[a-z]/; my (@KEYIDS, @KEYS); # Get the list of all matching keys. { my $gpg = GnuPG::Interface::->new(); $gpg->call( $ENV{GNUPGBIN} ) if defined $ENV{GNUPGBIN}; $gpg->options->hash_init( 'extra_args' => [ qw/--fingerprint --fixed-list-mode --no-auto-check-trustdb --with-colons/ ] , 'meta_interactive' => 0 ); my $stdout = IO::Handle::->new(); my $handles = GnuPG::Handles::->new( stdout => $stdout ); my $pid = $gpg->list_public_keys( handles => $handles, command_args => \@ARGV ); while (<$stdout>) { push @KEYIDS, $1 if /^fpr:(?:[^:]*:){8}([0-9A-F]{40})(?::.*)?$/; } waitpid $pid, 0; close $stdout; } # Read each key independently foreach my $keyid (@KEYIDS) { my $photos = $options{'show-photo'} ? File::Temp::->new(TMPDIR => 1) : '/dev/null'; my $gpg = GnuPG::Interface::->new(); $gpg->call( $ENV{GNUPGBIN} ) if defined $ENV{GNUPGBIN}; # we need --attribute-{fd,file} and --status-{fd,file} to get the # correct attribute size $gpg->options->hash_init( 'extra_args' => [ '--attribute-file', $photos, qw/--fingerprint --fingerprint --fixed-list-mode --no-auto-check-trustdb --with-colons/ ] , 'meta_interactive' => 0 ); my $stdout = IO::Handle::->new(); my $status = IO::Handle::->new(); my $handles = GnuPG::Handles::->new( stdout => $stdout, status => $status ); my $pid = $gpg->list_public_keys( handles => $handles, command_args => [ $keyid ] ); $_->blocking(0) for ($stdout, $status); my $output = IO::Select::->new(); $output->add($stdout, $status); my (%key, $sub); # current context my ($oldstdout, $oldstatus) = ('', ''); while ($output->count() > 0) { foreach my $fd (@{(IO::Select::select($output))[0]}) { # reader if ($fd->eof) { $output->remove($fd); close $fd; next; } if ($fd == $stdout) { while (<$fd>) { if ($oldstdout) { # prepend unfinished output $_ = $oldstdout . $_; $oldstdout = ''; } if (!/\n\z/) { # there is more coming $oldstdout = $_; next; } chomp; if (/^pub:([^:]*):([^:]*):([^:]*):([0-9A-F]{16}):(\d+):(\d*):(?:[^:]*:){4}([^:]*)(:.*)?$/) { my $keyid = $4; %key = (); undef $sub; if ($1 =~ /[eir]/ or $7 =~ /D/) { warn "Ignoring unusable key $keyid.\n"; } else { $key{length} = $2; $key{algo} = $3; $key{creation} = $5; $key{expiration} = $6 if $6 ne ''; $key{caps} = $7; $key{curve} = $1 if defined $8 and $8 =~ /^(?::[^:]*){4}:([^:]+)/; } next; } next unless %key; if (/^uid:([^:]+):(?:[^:]*:){6}[^:]*:([^:]+)/) { undef $sub; next if $1 =~ /[er]/; my $text = $2; $text =~ s/\\x(\p{AHex}{2})/ chr(hex($1)) /ge; # --with-colons always outputs UTF-8 push @{$key{uids}}, { type => 'uid', text => Encode::decode_utf8($text) }; next; } if (/^sub:([^:]+):([^:]*):([^:]*):([0-9A-F]{16}):(\d+):(\d*):(?:[^:]*:){4}([^:]*)(:.*)?$/) { $sub = {}; $sub->{validity} = $1; $sub->{length} = $2; $sub->{algo} = $3; $sub->{creation} = $5; $sub->{expiration} = $6 if $6 ne ''; $sub->{caps} = $7; $sub->{curve} = $1 if defined $8 and $8 =~ /^(?::[^:]*){4}:([^:]+)/; next; } if (/^fpr:(?:[^:]*:){8}([0-9A-F]{40})(?::.*)?$/) { if (defined $sub) { # subkey fingerprint $sub->{fpr} = $1; push @{$key{sub}}, $sub # ignore unusable subkeys unless $sub->{validity} =~ /[eir]/ or $sub->{caps} =~ /D/; } else { # key fingerprint $key{fpr} = $1; } next; } if (!/^(?:rvk|tru|uat):/) { # revoke/revoker/trust/uat warn "Unknown value: '$_'\n"; } } } elsif ($fd == $status) { while (<$fd>) { if ($oldstatus) { # prepend unfinished output $_ = $oldstatus . $_; $oldstatus = ''; } if (!/\n\z/) { # there is more coming $oldstatus = $_; next; } chomp; # see /usr/share/doc/gnupg/DETAILS.gz if (/^\[GNUPG:\] ATTRIBUTE [0-9A-F]{24}([0-9A-F]{16}) (\d+) 1 1 1 \d+ \d+ (\d+)$/) { push @{$key{uats}}, {size => $2, revoked => $3 & 0x02}; next; } if (!/^\[GNUPG:\] (?:KEYEXPIRED \d+|SIGEXPIRED(?: deprecated-use-keyexpired-instead)?|KEY_CONSIDERED [0-9A-F]{40} \d+)$/) { warn "Unknown value: '$_'"; } } } } } warn "Parsing gpg's output went wrong.\n" if $oldstdout or $oldstatus; waitpid $pid, 0; close $_ for ($stdout, $status); if ($options{'show-photo'}) { open my $fd, '<:raw', $photos or die "Couldn't open: $!"; # get photo sizes and split $photos foreach (@{$key{uats}}) { my $chunk; my $got = read $fd, $chunk, $_->{size} or die "Couldn't read: $!"; warn "Read $_->{size} bytes but got $got bytes.\n" if $got != $_->{size}; # take the first non-revoked attribute unless ($_->{revoked}) { $key{photo} = substr($key{fpr},-16).'.jpg'; open my $fd2, '>:raw', $key{photo} or die "Couldn't open: $!"; print $fd2 (substr $chunk, 16); close $fd2; last; } } close $fd; } if ($options{'show-qrcode'} or defined $options{'qrcode-data'}) { $key{qrcode} = substr($key{fpr},-16).'-qrcode.pdf'; pipe my ($rfd, $wfd) or die "Can't pipe: $!"; if (my $pid = fork) { close $wfd or die "Can't close: $!"; dup2 (fileno $rfd, 0); close $rfd or die "Can't close: $!"; system qw/epstopdf -f -o/, $key{qrcode}; die "system epstopdf failed: $?.\n" if $?; waitpid $pid, 0; } else { close $rfd or die "Can't close: $!"; dup2 (fileno $wfd, 1); close $wfd or die "Can't close: $!"; my $uri = $options{'qrcode-data'} // 'OPENPGP4FPR:%f'; $uri =~ s/%([fk%])/ $1 eq 'f' ? $key{fpr} : $1 eq 'k' ? substr($key{fpr},-16) : $1 eq '%' ? '%' : die /ge; exec qw/qrencode -i -lM -d300 -tEPS -o -/, $uri or die "Can't exec: $!"; } } push @KEYS, \%key if %key; } die "No usable key found.\n" unless @KEYS; print "\\documentclass[landscape,$options{'paper-size'}paper]{article}\n"; print << 'EOF' \usepackage{fancyvrb} \usepackage[export]{adjustbox} \usepackage{graphicx,calc} \usepackage{ifluatex,ifxetex} \usepackage[margin=.5cm,centering]{geometry} \ifnum 0\ifxetex 1\fi\ifluatex 1\fi=0 \usepackage[utf8x]{inputenc} \else \usepackage[log-declarations=false]{xparse} \usepackage{fontspec} \setmonofont{Noto Mono} \ifxetex \usepackage[quiet]{xeCJK} \CJKfontspec{Noto Sans Mono CJK TC} \CJKfontspec{Noto Sans Mono CJK SC} \CJKfontspec{Noto Sans Mono CJK JP} \CJKfontspec{Noto Sans Mono CJK KR} \fi \fi EOF ; sub pubkey_string ($$) { my ($type,$key) = @_; my $str = $type.' '; # See 'pubkey_string' in gnupg's source code (g10/keyid.c) $str .= $key->{algo} == 1 ? 'rsa'.$key->{length} : # RSA $key->{algo} == 2 ? 'rsa'.$key->{length} : # RSA encrypt only (legacy) $key->{algo} == 3 ? 'rsa'.$key->{length} : # RSA sign only $key->{algo} == 16 ? 'elg'.$key->{length} : # Elgamal encrypt only $key->{algo} == 17 ? 'dsa'.$key->{length} : # DSA $key->{algo} == 20 ? 'xxx'.$key->{length} : # Elgamal encrypt+sign (legacy) $key->{algo} == 18 ? $key->{curve} // ('ecdh'.$key->{length}) : # ECDH $key->{algo} == 19 ? $key->{curve} // ('ecdsa'.$key->{length}) : # ECDSA $key->{algo} == 22 ? $key->{curve} // ('eddsa'.$key->{length}) : # EDDSA "unknown_$key->{algo}"; $str .= '/'.substr($key->{fpr},-8).' '; $str .= strftime '%Y-%m-%d', localtime($key->{creation}); $str .= ' [expires: '.strftime('%Y-%m-%d', localtime($key->{expiration})).']' if defined $key->{expiration}; $str .= ' ' x (72 + 1 - length($str) - length($key->{caps}) - 2) . '['.$key->{caps}.']'; $str . sprintf "\n Key fingerprint = %s %s %s %s %s %s %s %s %s %s\n", map { substr($key->{fpr}, ($_ * 4), 4) } (0..9); } unless (defined $options{'attr-height'}) { $options{'attr-height'} = 0; $options{'attr-height'} >= $_ or $options{'attr-height'} = $_ for map {$#{$_->{uids}}+1} @KEYS; $options{'attr-height'} = 5 if $options{'attr-height'} > 5; } foreach my $n (0 .. $#KEYS) { my $key = $KEYS[$n]; if ($options{'show-subkeys'}) { $key->{caps} =~ y/[A-Z]//d; # ignore usable capabilities of the entire key $_->{caps} =~ y/[a-z]/[A-Z]/ foreach ($key, @{$key->{sub}}); } else { $key->{caps} =~ y/[a-z]//d; # ignore subkey capabilities } print "\\begin{SaveVerbatim}{PubKey$n}\n" .pubkey_string('pub',$key) ."\\end{SaveVerbatim}\n"; print "\\begin{SaveVerbatim}{UID$n}\n"; my $tag = "uid "; my $max = 72 - length $tag; my $x = 2.2 * ($options{'attr-height'} > 5 ? 5 : $options{'attr-height'}); $max -= $x if defined $key->{photo}; $max -= $x if defined $key->{photo} and defined $key->{qrcode} and $#{$key->{uids}} < 1.5*$options{'attr-height'}; foreach my $uid (grep {$_->{type} eq 'uid'} @{$key->{uids}}) { my $text = $LOCALE->encode($uid->{text}); for (my $i = 0; $i < length $text; $i+=$max) { print STDOUT ($i ? ' ' x length $tag : $tag ), substr ($text, $i, $max), "\n"; } } print "\\end{SaveVerbatim}\n"; if ($options{'show-subkeys'}) { print "\\begin{SaveVerbatim}{SubKey$n}\n"; print pubkey_string('sub',$_) foreach @{$key->{sub}}; print "\\end{SaveVerbatim}\n"; } print "\\expandafter\\newsavebox\\csname Key$n\\endcsname\n\n"; } print << "EOF" \\def\\COLUMNS{2} \\def\\MAXKEY{$#KEYS} EOF ; print << 'EOF' \newlength\MaxWidth \newlength\Width \pagestyle{empty} \begin{document} \setlength\parindent{0pt} \setlength\MaxWidth{ \textwidth/\COLUMNS - 1ex*\COLUMNS - 1pt*(\COLUMNS-1) } \settowidth\Width{\texttt{xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx}} % " Key fingerprint = 7420 DF86 BCE1 5A45 8DCE 9976 3927 8DA8 109E 6244" EOF ; foreach my $n (0 .. $#KEYS) { my $key = $KEYS[$n]; my $w; if (defined $key->{photo} and defined $key->{qrcode}) { $w = $#{$key->{uids}} < 1.5*$options{'attr-height'} ? ($options{'attr-height'}+5)."\\baselineskip+1pt" # horizontal : '5\baselineskip' # vertical } elsif (defined $key->{photo}) { $w = '5\baselineskip' } elsif (defined $key->{qrcode}) { $w = "$options{'attr-height'}\\baselineskip" } print "\\expandafter\\savebox\\csname Key$n\\endcsname{%\n" ." \\begin{adjustbox}{minipage=\\Width,valign=t,max width=\\MaxWidth,margin=1ex}%\n" ." \\BUseVerbatim{PubKey$n}\\\\[.3\\baselineskip]%\n" ." \\parbox[b]{\\Width".(defined $w ? "-($w)" : '' )."}{\\BUseVerbatim{UID$n}}%\n"; if (defined $key->{photo} or defined $key->{qrcode}) { print " \\parbox[b]{$w}{\\raggedleft\\tt%\n"; print " \\includegraphics[height=$options{'attr-height'}\\baselineskip,max width=5\\baselineskip,raise=-.3\\baselineskip]{$key->{photo}}%\n" if defined $key->{photo}; print " ".($#{$key->{uids}} < 1.5*$options{'attr-height'} ? "~" : "\\\\[1ex]")."%\n" if defined $key->{photo} and defined $key->{qrcode}; print " \\includegraphics[padding=-1ex,height=$options{'attr-height'}\\baselineskip,max width=5\\baselineskip,raise=-.3\\baselineskip]{$key->{qrcode}}%\n" if defined $key->{qrcode}; print " }%\n"; } print " \\\\[.3\\baselineskip]%\n" ." \\BUseVerbatim{SubKey$n}%\n" if $options{'show-subkeys'}; print " \\end{adjustbox}%\n}\n"; } print << 'EOF' \makeatletter \newtoks\toks@table \newtoks\toks@row \newtoks\toks@cell \newcounter{n@column} \newcounter{n@key} \def\free@space{\textheight} \newlength\Row@Height \newlength\Cell@Height \toks@table = {\setcounter{n@key}{0}} \toks@cell = {\expandafter\usebox\csname Key\then@key\endcsname} \loop { \setcounter{n@column}{0} \setlength\Row@Height{0pt} \toks@row = {} % generate a single row \loop \ifnum \value{n@column} < \COLUMNS \settototalheight\Cell@Height{\the\toks@cell} \ifnum \Cell@Height>\Row@Height \global\Row@Height=\Cell@Height \fi \ifnum \value{n@column} > 0 \toks@row = \expandafter{\the\toks@row & } \fi \global\toks@row = \expandafter{\the\toks@row% \the\toks@cell% \stepcounter{n@key}% \ifnum \value{n@key}>\MAXKEY \setcounter{n@key}{0} \fi% } \stepcounter{n@key} \ifnum \value{n@key}>\MAXKEY \setcounter{n@key}{0} \fi \stepcounter{n@column} \repeat } \edef\free@space{\number \numexpr \free@space - \Row@Height \relax} \ifnum \free@space > 0 \toks@table = \expandafter{\the\toks@table\the\toks@row \\ \hline} \repeat \begin{tabular}{@{}|*\COLUMNS{@{}l@{}|@{}}}\hline% \the\toks@table \end{tabular} \makeatother \end{document} EOF ; __END__ =encoding utf8 =head1 NAME gpg-key2latex - Generate a LaTeX file for fingerprint slips. =head1 SYNOPSIS =over =item B [B<-p> I] [B<-s>] [B<--show-photo>] [B<--show-qrcode>] I [I...] =back =head1 DESCRIPTION gpg-key2latex generates a LaTeX file with an OpenPGP key fingerprint and User IDs, repeated as often as it fits on a single page. The LaTeX data is written to STDOUT. Note: In most cases the generated file can be compiled to PDF using pdflatex(1), but xelatex(1) is required if some UID contains CJK characters. =head1 OPTIONS =over =item B<-p> I, B<--paper-size=>I Select the output paper size. The default is the output of paperconf(1), or I if libpaper-utils isn't installed. =item B<-s>, B<--show-subkeys> Show subkey information. =item B<--show-photo> Show the first valid user attribute, if any. Note: This writes JPG files to the current directory. =item B<--show-qrcode> Show a QR code of the string specified by B<--qrcode-data>. Note: This writes PDF files to the current directory. Requires qrencode(1) and epstopdf(1). =item B<--qrcode-data=>I The data to encode in a QR code (implies B<--show-qrcode>). The string "%f" is expanded to the OpenPGP key fingerprint (40 hexadecimal digits, without spaces); "%k" is expanded to the long (16 hexadecimal digits) key ID; "%%" is replaced by a single "%". The default data to encode, if B<--qrcode-data> is not specified, is the string "OPENPGP4FPR:%f". =item B<--attr-height> The height, in number of lines, of the photo and QR code. The default is the number of User ID, with a maximum of 5. =item B<-?>, B<--help> Print a brief help and exit. =item B<--version> Print the version and exit. =back =head1 ENVIRONMENT =over =item I The default home directory. =item I The gpg binary. Default: C<"gpg">. =item I The default working directory for gpg. Default: C<$HOME/.gnupg>. =back =head1 SEE ALSO gpg(1), gpg-key2ps(1) =head1 BUGS AND FEEDBACK Bugs or feature requests for B should be filed with the Debian project's bug tracker at L. =head1 AUTHOR Guilhem Moulin Eguilhem@debian.orgE =head1 COPYRIGHT AND LICENSE Copyright (c) 2014 Guilhem Moulin. B is free software, distributed under the GNU Public License, version 3 or later. signing-party-2.12/gpg-key2ps/000077500000000000000000000000001500571021200162125ustar00rootroot00000000000000signing-party-2.12/gpg-key2ps/Makefile000066400000000000000000000001431500571021200176500ustar00rootroot00000000000000all: gpg-key2ps.1 gpg-key2ps.1: gpg-key2ps pod2man $< > $@ install: clean: rm -f gpg-key2ps.1 signing-party-2.12/gpg-key2ps/README000066400000000000000000000027051500571021200170760ustar00rootroot00000000000000gpg-key2ps ---------- Usage: gpg-key2ps [-p papersize] [-r revoked-style] [-1] [-s] keyid-or-name revoked-style is one of: grey - Print text in grey hide - Don't show revoked uids note - Add "(revoked)" show - List revoked uids normally strike - Strike through lines Output is PostScript which can be sent to e.g. the lpr command. Specifying the paper size only works when libpaper is installed. CREDITS ------- This script comes from the original Signing-Party package which has been originally created and maintained by Simon Richter and Thijs Kinkhorst . COPYRIGHT & LICENSE ------------------- Copyright (C) 2001-2008 Simon Richter and Thijs Kinkhorst 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 with the Debian GNU/Linux distribution in file /usr/share/common-licenses/GPL; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA signing-party-2.12/gpg-key2ps/gpg-key2ps000077500000000000000000000223331500571021200201330ustar00rootroot00000000000000#!/usr/bin/perl -w # # gpg-key2ps: convert a PGP/GnuPG key into paper slips. # Copyright (C) 2001-2005 Simon Richter # Copyright (C) 2005-2008 Thijs Kinkhorst # Copyright (C) 2005-2008 Christoph Berg # Licenced under the GNU General Public License, # version 2 or later. # use strict; use Encode (); use Getopt::Long; my $version = '@@VERSION@@'; my $showsubkeys = 0; my $revokestyle = "hide"; my $columns = 2; my $creationdate = scalar(localtime); sub version($) { my $fd = shift; print $fd "gpg-key2ps $version - (c) 2001-2008 Simon Richter, Thijs Kinkhorst, Christoph Berg\n"; } sub usage($$) { my ($fd, $exitcode) = @_; version ($fd); print $fd < \$opts->{help}, '--help' => \$opts->{help}, '-v' => \$opts->{version}, '--version' => \$opts->{version}, '-s' => \$opts->{showsubkeys}, '--show-subkeys' => \$opts->{showsubkeys}, '-p=s' => \$opts->{papersize}, '--paper-size=s' => \$opts->{papersize}, '-r=s' => \$opts->{revokestyle}, '--revoked-style=s' => \$opts->{revokestyle}, '-1' => \$opts->{1}, )) { usage(\*STDERR, 1); } if ($opts->{help}) { usage (\*STDOUT, 0); } if ($opts->{version}) { version (\*STDOUT); exit 0; } if ( $opts->{papersize} ) { $ENV{'PAPERSIZE'} = $opts->{papersize}; } if ( $opts->{showsubkeys} ) { $showsubkeys = 1; } if ( $opts->{revokestyle} ) { $revokestyle = $opts->{revokestyle}; } if ( $revokestyle !~ /^(grey|hide|note|show|strike)$/ ) { print STDERR "Unknown revoked-style \"$revokestyle\".\n"; usage (\*STDERR, 1); } if ( $opts->{1} ) { $columns = 1; } usage(\*STDERR, 1) unless scalar @ARGV >= 1; # determine the paper size through the paperconf tool my $w; my $h; if ( `which paperconf` && $? == 0 ) { $w=`paperconf -w`; $h=`paperconf -h`; chomp for ($w,$h); y/,/./ for ($w,$h); } else { # Default to A4. print STDERR "Warning: libpaper-utils is not installed, defaulting to A4.\n"; $w=596; $h=842; } # open a gpg process we'll be reading from below # --list-key due to #382794 open GPG, '-|', $ENV{GNUPGBIN} // 'gpg', qw/--list-key --with-fingerprint --with-colons/, @ARGV; sub start_postscript { # start the PostScript output print <> exch get show } def /pub { condhline 50 y moveto (pub) show 70 y moveto showAlgorithm show (/) show show 150 y moveto show 200 y moveto show newline needhline } def /fpr { 70 y moveto (Key fingerprint = ) show show newline } def /uid { 50 y moveto (uid) show 200 y moveto show newline } def /sbk { 50 y moveto (sub) show 70 y moveto showAlgorithm show (/) show show 150 y moveto show newline } def EOF # output the desired display for revoked uids if ( $revokestyle eq "grey" ) { print <) { # we don't use these if ( /^(tru|uat):/ ) { next; } # every primary uid causes an extra line because of the separator if ( /^pub:/ ) { start_postscript() unless $started; $started = 1; $subkey = 0; $numlines++; } # primary uid s/^pub:[^:]*:([^:]*):([0-9]*):.{8,8}(.{8,8}):([^:]*):[^:]*:[^:]*:[^:]*:([^:]*):[^:]*:[^:]*:.*/ ($5) ($4) ($3) ($1) $2 pub/; # fingerprint, format it nicely with spaces if ( /^fpr:[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:([^:]*):.*/ ) { next if $subkey; my $fpr = $1; # v4 key $fpr =~ s/(\w{4})(\w{4})(\w{4})(\w{4})(\w{4})(\w{4})(\w{4})(\w{4})(\w{4})(\w{4})/$1 $2 $3 $4 $5 $6 $7 $8 $9 $10/; # v3 key $fpr =~ s/(\w{2})(\w{2})(\w{2})(\w{2})(\w{2})(\w{2})(\w{2})(\w{2})(\w{2})(\w{2})(\w{2})(\w{2})(\w{2})(\w{2})(\w{2})(\w{2})/$1 $2 $3 $4 $5 $6 $7 $8 $9 $10 $11 $12 $13 $14 $15 $16/g; $_ = " ($fpr) fpr\n"; } # user ids s/\\x(\p{AHex}{2})/ chr(hex($1)) /ge; $_ = Encode::encode("latin1", Encode::decode_utf8($_)); s/^uid:[^:r]*:[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:([^:]*):.*/ ($1) uid/; # revoked user id if (s/^uid:r[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:([^:]*):.*/ ($1) revuid/) { next if $revokestyle eq "hide"; } # subkey $subkey = 1 if /^sub:/; if (s/^sub:[^r:]*:([^:]*):([0-9]*):.{8,8}(.{8,8}):([^:]*):[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:.*/ ($4) ($3) ($1) $2 sbk/) { next if ! $showsubkeys; } if (s/^sub:r[^:]*:([^:]*):([0-9]*):.{8,8}(.{8,8}):([^:]*):[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:.*/ ($4) ($3) ($1) $2 revsbk/) { next if (!$showsubkeys) || $revokestyle eq "hide"; } $numlines++; # print this line print; } close(GPG); unless ($started) { print STDERR "No public key found.\n"; exit 1; } # output the remaining postscript print < - generates a PS file from a GnuPG keyring =head1 SYNOPSIS B [B<-r> I] [B<-p> I] [B<-1>] [B<-s>] I [ I<...> ] =head1 DESCRIPTION gpg-key2ps generates a PostScript file with your OpenPGP key fingerprint (repeated as often as it fits) useful for keysigning parties. The only argument is the same as you would pass to GPG's list-keys command, either a key-id or a (partial) name. The PS data is written to stdout. =head1 OPTIONS =over =item B<-p> B<--paper-size> I Select the output paper size. Default is to look into /etc/papersize or A4 if libpaper isn't installed. =item B<-s> B<--show-subkeys> Enable subkey information to be printed on the slips. Subkey information is normally not relevant to the key signing process. =item B<-r> B<--revoked-style> I Select how to mark revoked UIDs and subkeys. Five styles are available: B don't show at all (default), B show normally, B display in 50% grey, B add "[revoked]", and B strike through. =item B<-1> Print only one column of paper slips instead of the default two. Useful for keys with long UIDs that otherwise won't fit. =item I Keyids to print. Multiple can be separated by spaces. =item B<-h> B<--help> Print usage and exit. =item B<-v> B<--version> Print version and exit. =back =head1 ENVIRONMENT =over =item I The default home directory. =item I The gpg binary. Default: C<"gpg">. =item I The default working directory for gpg. Default: C<$HOME/.gnupg>. =back =head1 SEE ALSO =over =item gpg(1) GNU Privacy Guard. =item gpg-key2latex(1) Like B, but produces LaTeX output instead. =item https://www.debian.org/events/materials/business-cards/ B prints plain fingerprint slips. If you are looking for something more stylish, look at these latex templates for business cards that also include fingerprints. =back =head1 AUTHORS AND COPYRIGHT =over =item (c) 2001-2005 Simon Richter =item (c) 2005-2008 Thijs Kinkhorst =item (c) 2005-2008 Christoph Berg =back signing-party-2.12/gpg-mailkeys/000077500000000000000000000000001500571021200166135ustar00rootroot00000000000000signing-party-2.12/gpg-mailkeys/README000066400000000000000000000027001500571021200174720ustar00rootroot00000000000000gpg-mailkeys ------------ Given one or more key-ids, gpg-mailkeys mails these keys to their owners. You use this after you've signed them. By default, the mails contain a standard text and your name and address as the From (as determined by the sendmail command). You can modify how this script behaves by putting a .gpg-mailkeysrc file in your homedir. An example of this file is provided with this document. CREDITS ------- This script comes from the original Signing-Party package which has been originally created and maintained by Simon Richter and Thijs Kinkhorst . COPYRIGHT & LICENSE ------------------- Copyright (C) 2001-2005 Simon Richter and Thijs Kinkhorst 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 with the Debian GNU/Linux distribution in file /usr/share/common-licenses/GPL; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA signing-party-2.12/gpg-mailkeys/example.gpg-mailkeysrc000066400000000000000000000010601500571021200231030ustar00rootroot00000000000000# ~/.gpg-mailkeysrc # Sender name NAME="Your name here" # Sender address (don't use "<...>"!) EMAIL="your.address@example.org" # Subject SUBJECT="Your signed key" # Text to be included in the message body TEXT=" Hi, here you are: your signed key. See you! " # You can add here whatever arguments you want to pass to sendmail. # For example, if you use ssmtp, you may want to set your credentials this way # SENDMAIL_ARGS="-au myusername -aps3cr3t" # to authenticate to the smtp server with username myusername and password # s3cr3t. SENDMAIL_ARGS="" signing-party-2.12/gpg-mailkeys/gpg-mailkeys000077500000000000000000000054341500571021200211400ustar00rootroot00000000000000#! /bin/sh # # gpg-mailkeys: mail out just signed keys to their owners # set -e VERSION='@@VERSION@@' # Define the charset used in the text message of the mail LOCAL_CHARSET="" ## # Get the local charset. # # The local charset is deduced from the charset used by both ~/.gpg-mailkeysrc # and ~/.signature. If none of these files exist, the local charset is assumed # to be us-ascii. get_local_charset () { local charset="us-ascii" local file_list="$HOME/.signature $HOME/.gpg-mailkeysrc" for filename in $file_list; do if [ -e $filename ]; then charset=`file --mime-encoding $filename | cut -d ' ' -f 2` break fi done; LOCAL_CHARSET=$charset } if [ -z "$*" ]; then printf "Send people their newly signed GPG key by mail.\n" printf "Usage: $0 keyid ...\n" exit 1 fi if [ -e ~/.gpg-mailkeysrc ] ; then . ~/.gpg-mailkeysrc fi if [ -n "$EMAIL" ]; then FROM="$EMAIL" fi if [ -z "$FROM" ]; then echo "Error: No EMAIL set in ~/.gpg-mailkeysrc and no FROM set in environment." exit 1 fi if [ -z "$SUBJECT" ]; then SUBJECT="Your signed GPG key" fi if [ -z "$NAME" ]; then NAME=`getent passwd $USER | cut -d: -f5 | cut -d, -f1` fi if [ -z "$TEXT" ]; then TEXT="Hi, Here is your signed GPG key. Enjoy, $NAME" fi get_local_charset FAILKEYS= while [ -n "$1" ]; do printf "[$1] " TEMPFILE=`mktemp -t gpg2mail.XXXXXX` ADDR=`${GNUPGBIN:-gpg} --with-colons --fixed-list-mode --list-key $1 | sed -e 's/^uid:[^rei:]*:[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:\([^:<]*<[^:>]*>\):.*/@@uid@@ \1/' -e '/^@@uid@@ /!d' -e 's/([^)]*)//g' -e 's/ */ /g' -e 's/^@@uid@@ //' | head -1` if [ -z "$ADDR" ]; then printf "(no usable user ids)\n" FAILKEYS="$FAILKEYS:$1" shift 1 continue fi NANOTIME=`date +%s-%N` BOUNDARY="ksp-$$-boundary-$NANOTIME" printf "$ADDR:" printf >$TEMPFILE "From: $NAME <$FROM>\n" cat << EOM >> $TEMPFILE To: $ADDR Subject: $SUBJECT User-Agent: gpg-mailkeys/$VERSION MIME-Version: 1.0 Content-Type: multipart/mixed; micalg=pgp-sha1; boundary="$BOUNDARY" Content-Disposition: inline --$BOUNDARY Content-Type: text/plain; charset=$LOCAL_CHARSET Content-Disposition: inline Content-Transfer-Encoding: quoted-printable `echo "$TEXT" | qprint -e` EOM if [ -f ~/.signature ]; then printf -- "--=20\n" >> $TEMPFILE qprint -e ~/.signature >> $TEMPFILE fi cat << EOM >> $TEMPFILE --$BOUNDARY Content-Type: application/pgp-keys Content-Disposition: attachment; filename="$1.asc" `${GNUPGBIN:-gpg} --armor --export $1` --$BOUNDARY-- EOM printf " sending" ${SENDMAIL:-/usr/sbin/sendmail} $SENDMAIL_ARGS -ti <$TEMPFILE rm $TEMPFILE printf " done.\n" shift 1 done if [ -n "$FAILKEYS" ]; then printf "\nNote: The following keys could not be sent:\n" printf "$FAILKEYS\n" | tr ':' '\n' | sed -e '/^ *$/d' -e 's/^/ /' fi signing-party-2.12/gpg-mailkeys/gpg-mailkeys.1000066400000000000000000000032151500571021200212670ustar00rootroot00000000000000.\" .TH GPG-MAILKEYS 1 "Nov 23, 2010" .SH NAME .B gpg\-mailkeys \- sends emails containing keys to their owners .SH SYNOPSIS .B gpg-mailkeys .RI id ... .SH DESCRIPTION .B gpg-mailkeys sends the owners of the keys listed on the command line a mail each, containing their public key. After signing, you can use this tool to send everyone the new signatures they just got. If you're interested in encrypting the signature before it's sent, in order to verify that the associated address actually belongs to the key owner, you should check out .B caff .SH OPTIONS None .SH ENVIRONMENT .TP 13 .I NAME Set the name to use when sending messages .TP 13 .I EMAIL Set the email address to use when sending messages .TP 13 .I SUBJECT Set the subject of the messages you send .TP 13 .I TEXT Set the body text of the messages .TP 13 .I SENDMAIL Set the path to sendmail. Default: /usr/sbin/sendmail .TP 13 .I SENDMAIL_ARGS Set arguments to pass to $SENDMAIL. This can be useful to set credentials in order to authenticate to an smtp server when using ssmtp for example. .TP 13 .I HOME Set the default home directory. .TP 13 .I GNUPGBIN Set the gpg binary. Default: "gpg". .TP 13 .I GNUPGHOME Set the default working directory for gpg. Default: "~/.gnupg". .SH FILES The behavior of .B gpg-mailkeys can be updated through some files available in the user home directory. Thus, .B ~/.gpg-mailkeysrc allows the user to overwrite the default value for the above environment variables, and any text in .B ~/.signature will be appended to the end of the body text of the message. .SH SEE ALSO .BR gpg (1), caff (1) .SH AUTHOR This manual page was written by Simon Richter . signing-party-2.12/gpgdir/000077500000000000000000000000001500571021200154765ustar00rootroot00000000000000signing-party-2.12/gpgdir/CREDITS000066400000000000000000000042111500571021200165140ustar00rootroot00000000000000Per Ronny Westin - Found PLAINTEXT vs. DECRYPTION_OKAY return code bug for GnuPG 1.2.6. - Reported directory decryption bug in gpgdir-1.6. The result was the addition of the gpgdir test suite. Kai Raven - Bugfix in man page for file compression/decompression wording. Craig Needs - Suggested --gnupg-dir option, testing help. Chris P - Found bug where gpgdir would not decrypt files that contained spaces. Ian Scott - Reported "protocol error: expected SHM_GET_XXX got GOOD_PASSPHRASE" bug in GnuPG module. Mate Wierdl - Contributed patch (originally for the psad project) for building the RPM on x86_64 platforms. pyllyukko - Added the gpgdir.SlackBuild script (adapted from the psad project). Anthony Chivetta - Submitted patch to fix a bug where files named "0.gpg" could not be decrypted. - Submitted patch to implement the --overwrite-encrypted command line argument to allow previously encrypted files to be overwritten. This is useful for updating an encrypted directory with new versions of the previously encrypted files. Fermin Manzanedo - Suggested the --Symmetric option so that files can be encrypted/ decrypted via a symmetric cipher (GnuPG supports CAST5 by default). Franck Joncourt - Performed analysis of locale settings for fwknop installer and suggested using the LC_ALL environmental variable instead of the LANG variable (which is superseded by LC_* vars). - Suggested moving perl modules to the deps/ directory. This is to support the integration of the Cipherdyne projects with Debian. - Added Short description to the gpgdir man page. This fixes the following lintian warning: http://lintian.debian.org/tags/manpage-has-bad-whatis-entry.html - Suggested the appropriate bugfix to interface non-interactively with the wipe program (-f instead of -I in later versions). Lars Wilke - Reported a bug in missing the proper handling of files with spaces when using the --Wipe secure deletion mode. - Suggested the ability to encrypt/decrypt hidden files, and --Force now supports this. signing-party-2.12/gpgdir/ChangeLog000066400000000000000000000330671500571021200172610ustar00rootroot00000000000000gpgdir-1.9.5 (09/05/2009): - Added support for the decryption of PGP encrypted files (to round out the support of GnuPG). gpgdir-1.9.4 (02/12/2009): - Fixed a bug in missing the proper handling of files with spaces when using the --Wipe secure deletion mode (reported by Lars Wilke). - The --Force option now supports the ability to encrypt/decrypt hidden files (suggested by Lars Wilke). gpgdir-1.9.3 (11/05/2008): - Bugfix for using -f instead of -I for non-interactive file erasure (Franck Joncourt). - Simplified test suite code by creating a set of default arguments for the gpgdir command line as each test is executed. gpgdir-1.9.2 (08/31/2008): - Added new modes '--sign ' and '--verify ' to allow all files in the specified directory to be signed or verified instead of encrypted or decrypted. All GnuPG signatures are created as ".asc", and the original file is not removed in --sign mode. In --verify mode, if any file does not match the expected .asc signature, then a warning like the following will be generated: [+] Verifying: /home/mbr/src/gpgdir/test/data-dir/multi-line-ascii.asc [GNUPG:] BADSIG 9EDEEEEBA742EEEF Some User - Bugfix to not die() when files that are encrypted with a different GnuPG key are encountered in a directory that is being decrypted. A warning message (see below) is now generated and the file is skipped: [+] Decrypting: /home/mbr/tmp/gpgdir/a.gpg [GNUPG:] BAD_PASSPHRASE CF16F0FCFFF3FF4F [-] Skipping file encrypted with different GnuPG key: a.gpg - Updated to use the status output from GnuPG::Interface to detect a bad passphrase and whether a file is encrypted with the expected GnuPG key. - Moved the GnuPG::Interface, Class::MethodMaker, and Term::ReadKey modules to the deps/ directory, and updated the installer and RPM spec file to account for the path change. This change was suggested by Franck Joncourt for the other cipherdyne.org projects. - Updated the test suite to generate files in the output/ directory according to test number and append the result of each test within each file. This makes it easy to tell which tests have failed with a simple 'grep fail output/*test'. - Added the gpgdir-nodeps.spec file to allow an RPM to be built that does not contain any perl modules dependencies. - Updated gpgdir to import perl modules via 'require' statements instead of 'use' statements so that the path to the modules directory can be changed via the --Lib-dir command line argument. Also updated to use the 'auto' heuristic (first implemented in the fwknop project) to detect perl module directories that should be used in the --Lib-dir directory to import perl modules from. gpgdir-1.9.1 (06/07/2008): - Updated to Class::MethodMaker 2.11 from CPAN. This helps with systems running perl-5.10.0 and greater (such as Fedora 9). - Updated to always set the LC_ALL environmental variable to the "C" locale. This can be set to other locales with a new argument --locale, or the default locale can be used by using --no-locale argument. gpgdir-1.9 (05/31/2008): - Changed --Obfuscate-filenames format to not include the gpgdir PID. This allows directories to be encrypted/decrypted under -O multiple times without creating new filenames (which would pollute encrypted directories under rsync to other systems). The new -O encrypted filename format is just "gpgdir_.gpg". - Added PID locking against directories so that multiple gpgdir processes cannot operate against the same top-level directory simultaneously. This is useful for users that typically operate with multiple shells and might launch gpgdir from any of them. gpgdir-1.8 (04/04/2008): - Updated the test suite to validate the gpgdir --Obfuscate-filenames mode to ensure that files are encrypted as "gpgdir__.gpg". - Minor bug fix to remove the .gpgdir_map_file in --Obfuscate-filenames mode after a successful decryption cycle. - Updated to version 0.36 of CPAN GnuPG::Interface module. gpgdir-1.7 (02/18/2008): - Bugfix to ensure that encrypted directories can actually be decrypted. This bug was reported by Per Ronny Westin. - Updated to use the ".asc" extension for encrypted files in --Plain-ascii mode. - Added gpgdir test suite. All future gpgdir releases (and including this 1.7 release) require that all gpgdir tests pass on the systems where gpgdir is developed. gpgdir-1.6 (02/17/2008): - Bugfix to not include previously encrypted files (i.e. those with a .gpg extension) in the encryption/decryption file list. This bug was introduced in gpgdir-1.5 when a change was made to ignore ascii-armored files. - Added added LC_ALL=C locale setting for the install.pl script (this should help to ensure gpgdir is properly installed on most systems). Two new command line arguments --LC_ALL and --no-LC_ALL also allow the locale setting to be changed or not used at all. - Added --Exclude-mod-regex option to the install.pl script so that it is possible to force the exclusion of perl modules that gpgdir would normally install. This is useful for ensuring that gpgdir references perl modules that are already installed in the system perl library tree instead of using those that are installed in /usr/lib/gpgdir. - Updated to display command line usage warnings without automatically displaying the entire usage() page (which is quite long). gpgdir-1.5 (08/31/2007): - Added the --Symmetric option so that files can be encrypted/decrypted via a symmetric encryption algorithm (GnuPG commonly uses CAST5 for this). - Added the --Plain-ascii option so that GnuPG is invoked with the -a option so that encrypted files are ascii armored instead of encrypted in binary form. - Bugfix to ensure not to delete zero-size files if a bad password is given (gpgdir now just throws a warning and exits in this case). - Minor code enhancements to provide a consistent hash_init() invocation with the same options hash. - Updated to exclude .asc files from the encryption/decryption process. gpgdir-1.4 (07/20/2007): - (Anthony Chivetta) Submitted patch to implement the --overwrite-encrypted command line argument to allow previously encrypted files to be overwritten. This is useful for updating an encrypted directory with new versions of the previously encrypted files. Also added the --overwrite-decrypted command line argument to perform the same function for previously decrypted files. - (Anthony Chivetta) Submitted patch to fix a bug where a filename of "0.gpg" could not be decrypted because "0" does not evaluate to a true value. gpgdir-1.3 (06/09/2007): - Added --Obfuscate mode so that the files within a directory can be altered into unrecognizable names (which are stored within the file .gpgdir_map_file within each sub-directory, and this file is itself encrypted). The obfuscated file names are reversed when a directory is decrypted. - Added the --Agent-info command line argument so that the value of the GPG_AGENT_INFO environment variable can be specified on the gpgdir command line. gpgdir-1.2 (05/28/2007): - Added support for installing gpgdir on Windows under Cygwin (via the install.pl script). Installing gpgdir on FreeBSD systems also works. - Added support for installing gpgdir within a user home directory without the need for root access (this requires installing gpgdir with the install.pl script). - Added --agent to have gpgdir acquire gpg key password from a running gpg-agent instance. - Added --no-password so gpgdir can use a gpg key with no associated password (this is not common). The user is not prompted for a password in this case. gpgdir-1.1 (05/21/2007): - Added the ability to securely delete the original versions of files with the 'wipe' program (after they have been successfully encrypted). Also added --wipe-path to specify a path to the wipe binary (the default is /usr/bin/wipe), --wipe-interactive to force the wipe program to prompt the user before a file is deleted, and --wipe-cmdline to allow the user to build a set of command line arguments that are passed to the wipe program. - Added --Force to have gpgdir skip over the error condition where a file cannot be deleted (because of a permissions issue for example). - Added --Trial-run to allow the user to see what actions gpgdir would take to encrypt or decrypt files, but no files are actually modified. - Added --Interactive to have gpgdir prompt the user before every file is encrypted, decrypted. - Added the gpgdir.SlackBuild script (contributed by pyllyukko originally for the psad project) for building gpgdir on Slackware systems. gpgdir-1.0.3 (09/17/2006): - Minor bugfix to correct 1.0.1 version number (which should have been set to 1.0.2) in the gpgdir RPM spec file. gpgdir-1.0.2 (09/17/2006): - Minor bugfix to correct 1.0 version number (which should have been set to 1.0.1). The result is the 1.0.2 release. gpgdir-1.0.1 (09/16/2006): - Added --quiet option to have gpgdir print as little as possible to the screen when encrypting or decrypting a directory. - Added x86_64 RPM (original patch from Mate Wierdl adapted for gpgdir). gpgdir-1.0 (09/13/2006): - Added --Key-id command line argument so that use_key can be overridden from the command line - Made the argument to use_key not have to strictly be a keyID since GnuPG allows a unique string match on keys in the key ring - Added --Default-key to allow the user to have gpgdir use the default key that is defined by GnuPG within the ~/.gnupg/options file. - Updated the .gpgdirrc file to include the line "default_key" to allow the user to have gpgdir prefer to use the GnuPG default key. - Added the ChangeLog.svn file to show exactly which files have been changed from release to release, and what the corresponding Subversion log messages are. - Minor documentation updates. gpgdir-0.9.9 (09/07/2006): - Added RPM .spec file to build gpgdir as an RPM. - Added the --Skip-mod-install command line argument to install.pl to allow all perl module installs to be skipped. - Added the --force-mod-regex command line argument to install.pl to allow a regex match on perl module names to force matching modules to be installed. - Updated to TermReadKey-2.30 from 2.21. gpgdir-0.9.8 (07/03/2006): - Updated to use GnuPG::Interface instead of GnuPG module. This should fix the incompatibility issues seen between the GnuPG module and some GnuPG installations. - Added perl module installation code from fwknop (see http://www.cipherdyne.org/fwknop/). This allows gpgdir to preferentially use any perl modules that are already be installed on the system. gpgdir-0.9.4 (10/12/2005): - Updated test mode to encrypt and decrypt a testing file within the directory to be encrypted or decrypted. This file is located at /gpgdir_test, and is removed after the test is completed. - Bugfix for "protocol error: expected SHM_GET_XXX got GOOD_PASSPHRASE" error in GnuPG module. gpgdir-0.9.3 (02/20/2005): - Added --Include and --Include-from options to allow inclusion regular expressions to be specified. - Bugfix for not decrypting filesnames that contain spaces. gpgdir-0.9.2 (01/05/2005): - Added preservation of file mtime and atime values (may be disabled with the --no-preserve-times option). - Added testing encryption and decryption of dummy file (may be disabled with --skip-test) by default for both encrypt and decrypt modes. - Added --test-mode to run encrypt -> decrypt test and exit. - Removed unnecessary compression options. - Updated get_homedir() to reference HOME environmental variable if the /etc/passwd file does not exist (OS X being a good example). - Added --verbose mode. - Updated output to generate errors on a per-file basis instead of dumping them at the end of an encrypt/decrypt operation. gpgdir-0.9.1 (11/11/2004): - Updated GnuPG.pm perl module to handle return code of PLAINTEXT which seems to be returned by GunPG now (as of version 1.2.6) instead of DECRYPTION_OKAY upon a successful decryption. gpgdir-0.9 (09/12/2004): - Added --gnupg-dir option to allow a user to specify a different user's .gnupg directory for encryption keys. - Switched to "[+]" (and related) message prefixes. gpgdir-0.8 (05/29/2004): - Added --Exclude and --Exclude-from options to allow files to be excluded based on regex matches. - Reworked error messages so they contain the filename associated with each error. gpgdir-0.4 (04/23/2004): - Added --pw-file option so that a decryption password can be read out of a file. - Better directory validation (filesystem -e and -d checks). - Added INSTALL file. - Updated man page and README file. gpgdir-0.3 (09/27/2003): - Bundled perl modules GnuPG and TermReadKey with gpgdir. - Modified install.pl and gpgdir to install and use GnuPG and TermReadKey modules from the /usr/lib/gpgdir directory. - Added check_commands() subroutine from psad. signing-party-2.12/gpgdir/ChangeLog.svn000066400000000000000000000045771500571021200200720ustar00rootroot00000000000000------------------------------------------------------------------------ r349 | mbr | 2009-09-05 14:52:04 -0400 (Sat, 05 Sep 2009) | 1 line Changed paths: A /gpgdir/branches/gpgdir-1.9.5 (from /gpgdir/trunk:348) recreated gpgdir-1.9.5 branch ------------------------------------------------------------------------ r347 | mbr | 2009-09-05 14:50:48 -0400 (Sat, 05 Sep 2009) | 10 lines Changed paths: A /gpgdir/trunk/packaging/gpgdir-nobuildreqs.spec M /gpgdir/trunk/packaging/gpgdir.spec For all RPM's built on the local system (Ubuntu for now), updated to reference the "-nobuildreqs.spec" file so that the "BuildRequires: perl-ExtUtils-MakeMaker" directive is not used. Using this results in the following error on an Ubuntu system where no software is installed/upgrade with RPM: rpm: To install rpm packages on Debian systems, use alien. See README.Debian. error: cannot open Packages index using db3 - No such file or directory (2) error: cannot open Packages database in /var/lib/rpm ------------------------------------------------------------------------ r344 | mbr | 2009-09-05 14:28:24 -0400 (Sat, 05 Sep 2009) | 1 line Changed paths: M /gpgdir/trunk/packaging/gpgdir-nodeps.spec M /gpgdir/trunk/packaging/gpgdir.spec updated to include 1.9.5 release ------------------------------------------------------------------------ r342 | mbr | 2009-09-05 14:24:38 -0400 (Sat, 05 Sep 2009) | 1 line Changed paths: M /gpgdir/trunk/ChangeLog updated 1.9.5 release date ------------------------------------------------------------------------ r341 | mbr | 2009-08-25 22:43:51 -0400 (Tue, 25 Aug 2009) | 1 line Changed paths: M /gpgdir/trunk/gpgdir M /gpgdir/trunk/test/gpgdir_test.pl minor copyright update ------------------------------------------------------------------------ r340 | mbr | 2009-08-23 14:32:29 -0400 (Sun, 23 Aug 2009) | 1 line Changed paths: M /gpgdir/trunk/VERSION M /gpgdir/trunk/gpgdir M /gpgdir/trunk/test/gpgdir_test.pl bumped version to 1.9.5 ------------------------------------------------------------------------ r339 | mbr | 2009-08-23 14:31:48 -0400 (Sun, 23 Aug 2009) | 3 lines Changed paths: M /gpgdir/trunk/ChangeLog M /gpgdir/trunk/gpgdir M /gpgdir/trunk/test/gpgdir_test.pl - Added support for the decryption of PGP encrypted files (to round out the support of GnuPG). ------------------------------------------------------------------------ signing-party-2.12/gpgdir/INSTALL000066400000000000000000000011161500571021200165260ustar00rootroot00000000000000Installation notes: QUICK AND EASY INSTALLATION INSTRUCTIONS: Just run the gpgdir installation script "install.pl" from the gpgdir sources directory: # ./install.pl If you are not installing as root, the install.pl script will install gpgdir along with required perl modules within your home directory. If you are installing as root, required perl modules will be installed in /usr/lib/gpgdir so as to not pollute the system perl library tree. The required perl modules are GnuPG::Interface, Class::MethodMaker, and Term::ReadKey, and these modules are placed in the deps/ directory. signing-party-2.12/gpgdir/LICENSE000066400000000000000000000430771500571021200165160ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 19yy 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, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) 19yy name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. signing-party-2.12/gpgdir/README000066400000000000000000000025201500571021200163550ustar00rootroot00000000000000File: gpgdir Author: Michael Rash Download: http://www.cipherdyne.org/gpgdir License: GNU General Public License Version: 0.9.8 gpgdir is a perl script that uses the CPAN GnuPG::Interface perl module to encrypt and decrypt directories using a gpg key specified in ~/.gpgdirrc. Gpgdir recursively descends through a directory in order to make sure it encrypts or decrypts every file in a directory and all of its subdirectories. By default the mtime and atime values of all files will be preserved upon encryption and decryption (this can be disabled with the --no-preserve-times option). Note that in --encrypt mode, gpgdir will delete the original files that it successfully encrypts (unless the --no-delete option is given). However, upon startup gpgdir first asks for the decryption password to be sure that a dummy file can successfully be encrypted and decrypted. The initial test can be disabled with the --skip-test option so that a directory can easily be encrypted without having to also specify a password (this is consistent with gpg behavior). Also, note that gpgdir is careful not encrypt hidden files and directories. After all, you probably don't want your ~/.gnupg directory or ~/.bashrc file to be encrypted. Installation: Just run the install.pl script (as root) that comes with the gpgdir sources. signing-party-2.12/gpgdir/VERSION000066400000000000000000000000061500571021200165420ustar00rootroot000000000000001.9.5 signing-party-2.12/gpgdir/bump_version.pl000077500000000000000000000026721500571021200205550ustar00rootroot00000000000000#!/usr/bin/perl -w # ############################################################################# # # File: bump_version.pl # # Purpose: Minor script to enforce consistency in gpgdir version tags. # ############################################################################# # # $Id: bump_version.pl 1055 2008-05-21 02:57:17Z mbr $ # use strict; my @files = qw( gpgdir test/gpgdir_test.pl ); my $new_version = $ARGV[0] or die "[*] $0 "; open F, '< VERSION' or die "[*] Could not open VERSION file: $!"; my $old_version = ; close F; chomp $old_version; print "[+] Updating software versions...\n"; for my $file (@files) { if ($file =~ /\.c/) { ###* Version: 1.8.4-pre2 my $search_re = qr/^\*\s+Version:\s+$old_version/; my $replace_str = '* Version: ' . $new_version; system qq{perl -p -i -e 's|$search_re|} . qq{$replace_str|' $file}; } else { ### Version: 1.8.4 my $search_re = qr/#\s+Version:\s+$old_version/; my $replace_str = '# Version: ' . $new_version; system qq{perl -p -i -e 's|$search_re|$replace_str|' $file}; ### my $version = '1.8.4'; $search_re = qr/^my\s+\x24version\s+=\s+'$old_version';/; $replace_str = q|my \x24version = '| . $new_version . q|';|; system qq{perl -p -i -e "s|$search_re|$replace_str|" $file}; } } system qq{perl -p -i -e 's|$old_version|$new_version|' VERSION}; exit 0; signing-party-2.12/gpgdir/gpgdir000077500000000000000000001430141500571021200167030ustar00rootroot00000000000000#!/usr/bin/perl -w # ########################################################################### # # File: gpgdir # # URL: http://www.cipherdyne.org/gpgdir/ # # Purpose: To encrypt/decrypt whole directories # # Author: Michael Rash (mbr@cipherdyne.com) # # Version: 1.9.5 # # Copyright (C) 2002-2009 Michael Rash (mbr@cipherdyne.org) # # License: GNU General Public License version 2 (GPLv2) # # 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, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 # USA # ########################################################################### # # $Id: gpgdir 341 2009-08-26 02:43:51Z mbr $ # use File::Find; use File::Copy; use IO::File; use IO::Handle; use Getopt::Long; use Cwd; use strict; ### set the current gpgdir version and file revision numbers my $version = '1.9.5'; my $revision_svn = '$Revision: 341 $'; my $rev_num = '1'; ($rev_num) = $revision_svn =~ m|\$Rev.*:\s+(\S+)|; ### establish some defaults my $encrypt_user = ''; my $gpg_homedir = ''; my $dir = ''; my $pw = ''; my $encrypt_dir = ''; my $decrypt_dir = ''; my $sign_dir = ''; my $verify_dir = ''; my $homedir = ''; my $exclude_pat = ''; my $exclude_file = ''; my $include_pat = ''; my $include_file = ''; my $lib_dir = '/usr/lib/gpgdir'; my $pid_file = ''; my $total_encrypted = 0; my $total_decrypted = 0; my $norecurse = 0; my $printver = 0; my $no_delete = 0; my $no_fs_times = 0; my $test_and_exit = 0; my $trial_run = 0; my $skip_test_mode = 0; my $verbose = 0; my $quiet = 0; my $use_gpg_agent = 0; ### use gpg-agent for passwords my $gpg_agent_info = ''; my $force_mode = 0; my $help = 0; my $wipe_mode = 0; my $encrypt_mode = 0; my $signing_mode = 0; my $verify_mode = 0; my $use_default_key = 0; my $pw_file = ''; my $wipe_cmd = '/usr/bin/wipe'; my $wipe_cmdline = ''; my $wipe_interactive = 0; my $interactive_mode = 0; my $ascii_armor_mode = 0; my @exclude_patterns = (); my @include_patterns = (); my %files = (); my %options = (); my %obfuscate_ctrs = (); my %obfuscated_dirs = (); my $total_mapped_files = 0; my $have_obfuscated_file = 0; my $cmdline_no_password = 0; my $obfuscate_mode = 0; my $obfuscate_map_filename = '.gpgdir_map_file'; my $overwrite_encrypted = 0; my $overwrite_decrypted = 0; my $symmetric_mode = 0; my $DEL_SOURCE_FILE = 1; my $NO_DEL_SOURCE_FILE = 0; my $locale = 'C'; ### default LC_ALL env variable my $no_locale = 0; ### for user answers my $ACCEPT_YES_DEFAULT = 1; my $ACCEPT_NO_DEFAULT = 2; ### turn off buffering $| = 1; unless ($< == $>) { die "[*] Real and effective uid must be the same. Make sure\n", " gpgdir has not been installed as a SUID binary.\n", "Exiting."; } my @args_cp = @ARGV; ### make Getopts case sensitive Getopt::Long::Configure('no_ignore_case'); die "[*] Use --help for usage information.\n" unless(GetOptions ( 'encrypt-dir=s' => \$encrypt_dir, # Encrypt files in this directory. 'decrypt-dir=s' => \$decrypt_dir, # Decrypt files in this directory. 'sign-dir=s' => \$sign_dir, # Sign files in this directory. 'verify-dir=s' => \$verify_dir, # Verify files in this directory. 'gnupg-dir=s' => \$gpg_homedir, # Path to /path/to/.gnupg directory. 'pw-file=s' => \$pw_file, # Read password out of this file. 'agent' => \$use_gpg_agent, # Use gpg-agent for passwords. 'Agent-info=s' => \$gpg_agent_info, # Specify GnuPG agent connection # information. 'Wipe' => \$wipe_mode, # Securely delete unencrypted files. 'wipe-path=s' => \$wipe_cmd, # Path to wipe command. 'wipe-interactive' => \$wipe_interactive, # Disable "wipe -I" 'wipe-cmdline=s' => \$wipe_cmdline, # Specify wipe command line. 'Obfuscate-filenames' => \$obfuscate_mode, # substitute real filenames # with manufactured ones. 'obfuscate-map-file=s' => \$obfuscate_map_filename, # path to mapping file. 'Force' => \$force_mode, # Continue if files can't be deleted. 'overwrite-encrypted' => \$overwrite_encrypted, # Overwrite encrypted files # even if they exist. 'overwrite-decrypted' => \$overwrite_decrypted, # Overwrite decrypted files # even if they exist. 'Exclude=s' => \$exclude_pat, # Exclude a pattern from encrypt/decrypt # cycle. 'Exclude-from=s' => \$exclude_file, # Exclude patterns in from # encrypt decrypt cycle. 'Include=s' => \$include_pat, # Specify a pattern used to restrict # encrypt/decrypt operation to. 'Include-from=s' => \$include_file, # Specify a file of include patterns to # restrict all encrypt/decrypt # operations to. 'test-mode' => \$test_and_exit, # Run encrypt -> decrypt test only and # exit. 'Trial-run' => \$trial_run, # Don't modify any files; just show what # would have happened. 'quiet' => \$quiet, # Print as little as possible to # stdout. 'Interactive' => \$interactive_mode, # Query the user before encrypting/ # decrypting/deleting any files. 'Key-id=s' => \$encrypt_user, # Specify encrypt/decrypt key 'Default-key' => \$use_default_key, # Assume that default-key is set within # ~/.gnupg/options. 'Symmetric' => \$symmetric_mode, # encrypt using symmetric cipher. # (this option is not required to # also decrypt, GnuPG handles # that automatically). 'Plain-ascii' => \$ascii_armor_mode, # Ascii armor mode (creates non-binary # encrypted files). 'skip-test' => \$skip_test_mode, # Skip encrypt -> decrypt test. 'no-recurse' => \$norecurse, # Don't encrypt/decrypt files in # subdirectories. 'no-delete' => \$no_delete, # Don't delete files once they have # been encrypted. 'no-password' => \$cmdline_no_password, # Do not query for a password (only # useful for when the gpg literally # has no password). 'user-homedir=s' => \$homedir, # Path to home directory. 'no-preserve-times' => \$no_fs_times, # Don't preserve mtimes or atimes. 'LC_ALL=s' => \$locale, 'locale=s' => \$locale, # synonym 'no-LC_ALL' => \$no_locale, 'no-locale' => \$no_locale, # synonym 'Lib-dir=s' => \$lib_dir, # Path to perl module path 'verbose' => \$verbose, # Verbose mode. 'Version' => \$printver, # Print version 'help' => \$help # Print help )); &usage_and_exit() if $help; ### set LC_ALL env variable $ENV{'LC_ALL'} = $locale unless $no_locale; print "[+] gpgdir v$version (file revision: $rev_num)\n", " by Michael Rash \n" and exit 0 if $printver; if ($symmetric_mode and ($use_gpg_agent or $gpg_agent_info)) { die "[*] gpg-agent incompatible with --Symmetric mode"; } die "[*] Cannot --sign-dir and --verify-dir" if $sign_dir and $verify_dir; if ($sign_dir) { $encrypt_dir = $sign_dir; $signing_mode = 1; } elsif ($verify_dir) { $decrypt_dir = $verify_dir; $verify_mode = 1; } if ($encrypt_dir and $overwrite_decrypted) { die "[*] The -e and --overwrite-decrypted options are incompatible."; } if ($decrypt_dir and $overwrite_encrypted) { die "[*] The -d and --overwrite-encrypted options are incompatible."; } ### import perl modules (GnuPG::Interface, etc.) &import_perl_modules(); if ($wipe_mode) { unless (-e $wipe_cmd) { die "[*] Can't find wipe command at: $wipe_cmd,\n", " use --wipe-path to specify path."; } unless (-e $wipe_cmd) { die "[*] Can't execute $wipe_cmd"; } } my $initial_dir = cwd or die "[*] Could not get CWD: $!"; if ($gpg_homedir) { ### it was specified on the command line if ($gpg_homedir !~ m|^/|) { $gpg_homedir = $initial_dir . '/' . $gpg_homedir; } } ### build up GnuPG options hash if ($verbose) { %options = ('homedir' => $gpg_homedir); } else { %options = ( 'batch' => 1, 'homedir' => $gpg_homedir ); } $options{'armor'} = 1 if $ascii_armor_mode or $signing_mode; ### get the path to the user's home directory $homedir = &get_homedir() unless $homedir; unless ($symmetric_mode) { unless ($gpg_homedir) { $gpg_homedir = "${homedir}/.gnupg" if -d "${homedir}/.gnupg"; } unless (-d $gpg_homedir) { die "[*] GnuPG directory: $gpg_homedir does not exist. Please\n", " create it by executing: \"gpg --gen-key\". Exiting.\n"; } ### get the key identifier from ~/.gnupg $encrypt_user = &get_key() unless $encrypt_user or $use_default_key; } if ($decrypt_dir and $encrypt_dir) { die "[*] Cannot encrypt and decrypt the same directory, see --help\n"; } unless ($decrypt_dir or $encrypt_dir or $test_and_exit) { die "[*] Please specify -e , -d , or --test-mode, see --help\n"; } if ($obfuscate_mode) { if ($sign_dir) { die "[*] -O mode incompatible with --sign-dir"; } elsif ($verify_dir) { die "[*] -O mode incompatible with --verify-dir"; } } ### exclude file pattern push @exclude_patterns, $exclude_pat if $exclude_pat; if ($exclude_file) { open P, '<', $exclude_file or die "[*] Could not open file: $exclude_file"; my @lines =

; close P; for my $line (@lines) { next unless $line =~ /\S/; chomp $line; push @exclude_patterns, qr{$line}; } } ### include file pattern push @include_patterns, $include_pat if $include_pat; if ($include_file) { open P, '<', $include_file or die "[*] Could not open file: $include_file"; my @lines =

; close P; for my $line (@lines) { next unless $line =~ /\S/; chomp $line; push @include_patterns, qr{$line}; } } if ($encrypt_dir) { $dir = $encrypt_dir; $encrypt_mode = 1; } elsif ($decrypt_dir) { $dir = $decrypt_dir; $encrypt_mode = 0; } if ($dir) { die "[*] Directory does not exist: $dir" unless -e $dir; die "[*] Not a directory: $dir" unless -d $dir; } ### don't need to test encrypt/decrypt ability if we are running ### in --Trial-run mode. $skip_test_mode = 1 if $trial_run or $signing_mode or $verify_mode; if ($dir eq '.') { $dir = $initial_dir; } elsif ($dir !~ m|^/|) { $dir = $initial_dir . '/' . $dir; } $dir =~ s|/$||; ### remove any trailing slash ### make sure another gpgdir process is not trying to operate ### on the same directory $pid_file = "$dir/.gpgdir.pid"; &unique_pid(); &write_pid(); if ($symmetric_mode or $signing_mode) { &get_password(); } else { &get_password() unless (($encrypt_mode and $skip_test_mode) or $verify_mode); } ### run a test to make sure gpgdir and encrypt and decrypt a file unless ($skip_test_mode) { my $rv = &test_mode(); exit $rv if $test_and_exit; } if ($signing_mode) { print "[+] Signing files in directory: $dir\n" unless $quiet; } elsif ($encrypt_mode) { print "[+] Encrypting files in directory: $dir\n" unless $quiet; } elsif ($verify_mode) { print "[+] Verifying signatures in directory: $dir\n" unless $quiet; } else { print "[+] Decrypting files in directory: $dir\n" unless $quiet; } ### build a hash of file paths to work against &get_files($dir); ### perform the gpg operation (encrypt/decrypt) &gpg_operation(); &obfuscated_mapping_files() if $obfuscate_mode; unless ($obfuscate_mode) { if ($have_obfuscated_file) { print "[-] Obfuscated filenames detected, try decrypting with -O\n" unless $quiet; } } if ($signing_mode) { print "[+] Total number of files signed: " . "$total_encrypted\n" unless $quiet; } elsif ($encrypt_mode) { print "[+] Total number of files encrypted: " . "$total_encrypted\n" unless $quiet; } elsif ($verify_mode) { print "[+] Total number of files verified: " . "$total_decrypted\n" unless $quiet; } else { print "[+] Total number of files decrypted: " . "$total_decrypted\n" unless $quiet; } if (-e $pid_file) { unlink $pid_file or die "[*] Could not remove pid file $pid_file: $!"; } exit 0; #==================== end main ===================== sub encrypt_or_sign_file() { my ($in_file, $out_file, $del_flag) = @_; my $gpg = GnuPG::Interface->new(); $gpg->call( $ENV{GNUPGBIN} ) if defined $ENV{GNUPGBIN}; $gpg->options->hash_init(%options); die "[*] Could not create new gpg object with ", "homedir: $gpg_homedir" unless $gpg; unless ($symmetric_mode or $use_default_key) { $gpg->options->default_key($encrypt_user); $gpg->options->push_recipients($encrypt_user); } my ($input_fh, $output_fh, $error_fh, $pw_fh, $status_fh) = (IO::File->new($in_file), IO::File->new("> $out_file"), IO::Handle->new(), IO::Handle->new(), IO::Handle->new()); my $handles = GnuPG::Handles->new( stdin => $input_fh, stdout => $output_fh, stderr => $error_fh, passphrase => $pw_fh, status => $status_fh ); $handles->options('stdin')->{'direct'} = 1; $handles->options('stdout')->{'direct'} = 1; my $pid; if ($use_gpg_agent or $gpg_agent_info) { ### set environment explicitly if --Agent was specified if ($gpg_agent_info) { $ENV{'GPG_AGENT_INFO'} = $gpg_agent_info; } $pid = $gpg->encrypt('handles' => $handles, 'command_args' => [ qw( --use-agent ) ]); } else { if ($symmetric_mode) { $pid = $gpg->encrypt_symmetrically('handles' => $handles); } elsif ($signing_mode) { $pid = $gpg->detach_sign('handles' => $handles); } else { $pid = $gpg->encrypt('handles' => $handles); } } print $pw_fh $pw; close $pw_fh; my @errors = <$error_fh>; close $error_fh; my @status = <$status_fh>; close $status_fh; close $input_fh; close $output_fh; waitpid $pid, 0; if ($verbose) { print for @errors; } else { for (@errors) { print if /bad\s+pass/; } } if (-s $out_file == 0) { &delete_file($out_file); &delete_file($in_file) if $del_flag == $DEL_SOURCE_FILE; if ($use_gpg_agent) { die "[*] Created zero-size file: $out_file\n", " Maybe gpg-agent does not yet have the password for that key?\n", " Try with --verbose"; } else { die "[*] Created zero-size file: $out_file\n", " Bad password? Try with --verbose"; } } return 1; } sub decrypt_or_verify_file() { my ($in_file, $out_file, $del_flag) = @_; my $pid; my $bad_passphrase = 0; my $bad_signature = 0; my $file_encrypted_with_expected_key = 0; my $input_fh = ''; my $output_fh = ''; my $error_fh = ''; my $pw_fh = ''; my $status_fh = ''; my $handles = ''; my $gpg = GnuPG::Interface->new(); $gpg->call( $ENV{GNUPGBIN} ) if defined $ENV{GNUPGBIN}; $gpg->options->hash_init(%options); die "[*] Could not create new gpg object with ", "homedir: $gpg_homedir" unless $gpg; unless ($verify_mode or $symmetric_mode or $use_default_key) { $gpg->options->default_key($encrypt_user); $gpg->options->push_recipients($encrypt_user); } if ($verify_mode) { ($input_fh, $output_fh, $error_fh, $status_fh) = (IO::Handle->new(), IO::Handle->new(), IO::Handle->new(), IO::Handle->new()); $handles = GnuPG::Handles->new( stdin => $input_fh, stdout => $output_fh, stderr => $error_fh, status => $status_fh ); } else { ($input_fh, $output_fh, $error_fh, $pw_fh, $status_fh) = (IO::File->new($in_file), IO::File->new("> $out_file"), IO::Handle->new(), IO::Handle->new(), IO::Handle->new()); $handles = GnuPG::Handles->new( stdin => $input_fh, stdout => $output_fh, stderr => $error_fh, passphrase => $pw_fh, status => $status_fh ); $handles->options('stdin')->{'direct'} = 1; $handles->options('stdout')->{'direct'} = 1; } if ($use_gpg_agent) { $pid = $gpg->decrypt('handles' => $handles, 'command_args' => [ qw( --use-agent ) ]); } else { if ($verify_mode) { $pid = $gpg->wrap_call( 'commands' => [ qw( --verify ) ], 'command_args' => [ ( $in_file ) ], 'handles' => $handles ); } else { $pid = $gpg->decrypt('handles' => $handles); } } unless ($verify_mode) { print $pw_fh $pw; close $pw_fh; } my @errors = <$error_fh>; close $error_fh; my @status = <$status_fh>; close $status_fh; close $input_fh; close $output_fh; waitpid $pid, 0; for (@status) { if ($verify_mode) { ### [GNUPG:] BADSIG 9EEEEE6BEE428EEE Some User $bad_signature = 1 if /BADSIG/; } else { ### [GNUPG:] BAD_PASSPHRASE C326F95CE133EA4E $bad_passphrase = 1 if /BAD_?PASS/; if (/NEED_PASSPHRASE\s\S+\s+\S+$encrypt_user\s/) { ### [GNUPG:] NEED_PASSPHRASE CDE4D7DDFD66DCB9 95D85DDDDD42D39D 16 0 $file_encrypted_with_expected_key = 1; } elsif ((length($encrypt_user) == 8) and /USERID_HINT\s+.*$encrypt_user/) { $file_encrypted_with_expected_key = 1; } } } if ($verbose) { print " GnuPG errors:\n"; print for @errors; print " GnuPG status:\n"; print for @status; } else { for (@status) { if (/BAD_?PASS/) { print unless $quiet; } elsif (/BADSIG/) { print unless $quiet; } } } if ($bad_passphrase) { if (-s $out_file == 0) { &delete_file($out_file); &delete_file($in_file) if $del_flag == $DEL_SOURCE_FILE; if ($file_encrypted_with_expected_key) { die "[*] Bad passphrase, try gpgdir with -v"; } else { print "[-] Skipping file encrypted with different ", "GnuPG key: $in_file\n" unless $quiet; } } else { die "[*] Bad passphrase, but created non-zero sized output file, should not\n", " happen. Try with --verbose"; } } elsif (-s $out_file == 0) { &delete_file($out_file); &delete_file($in_file) if $del_flag == $DEL_SOURCE_FILE; if ($use_gpg_agent) { die "[*] Created zero-size file: $out_file\n", " Maybe gpg-agent does not yet have the password for that key?\n", " Try with --verbose"; } else { die "[*] Created zero-size file: $out_file\n", " Bad password? Try with --verbose"; } } if ($bad_signature) { return 0; } return 1; } sub delete_file() { my $file = shift; return if $no_delete; return unless -e $file; if ($wipe_mode) { my @cmd = ($wipe_cmd); if ($wipe_cmdline) { push @cmd, $wipe_cmdline; } else { push @cmd, $wipe_interactive ? '-i' : qw/-f -s/; } push @cmd, $file; if ($verbose) { print " Executing: ", join(' ', @cmd), "\n"; } ### wipe the file system @cmd; } else { unlink $file; } if (-e $file) { my $msg = "[-] Could not delete file: $file\n"; if ($force_mode) { print $msg unless $quiet; } else { die $msg unless $quiet; } } return; } sub gpg_operation() { ### sort by oldest to youngest mtime FILE: for my $file (sort {$files{$a}{'mtime'} <=> $files{$b}{'mtime'}} keys %files) { ### see if we have an exclusion pattern that implies ### we should skip this file if (@exclude_patterns and &exclude_file($file)) { print "[+] Skipping excluded file: $file\n" if $verbose and not $quiet; next FILE; } ### see if we have an inclusion pattern that implies ### we should process this file if (@include_patterns and not &include_file($file)) { print "[+] Skipping non-included file: $file\n" if $verbose and not $quiet; next FILE; } ### dir is always a full path my ($dir, $filename) = ($file =~ m|(.*)/(.*)|); unless (chdir($dir)) { print "[-] Could not chdir $dir, skipping.\n" unless $quiet; next FILE; } my $mtime = $files{$file}{'mtime'}; my $atime = $files{$file}{'atime'}; if ($encrypt_mode) { my $encrypt_filename = "$filename.gpg"; if ($obfuscate_mode) { unless (defined $obfuscate_ctrs{$dir}) { ### create a new gpgdir mapping file for obfuscated file ### names, but preserve any previously encrypted file ### name mappings &handle_old_obfuscated_map_file(); ### make obfuscated file names start at 1 for each ### directory $obfuscate_ctrs{$dir} = 1; } $encrypt_filename = 'gpgdir_' . $obfuscate_ctrs{$dir} . '.gpg'; } if ($ascii_armor_mode or $signing_mode) { $encrypt_filename = "$filename.asc"; } if (-e $encrypt_filename and not $overwrite_encrypted) { my $str = 'Encrypted'; $str = 'Signed' if $signing_mode; print "[-] $str file $dir/$encrypt_filename already ", "exists, skipping.\n" unless $quiet; next FILE; } if ($interactive_mode) { my $str = 'Encrypt'; $str = 'Sign' if $signing_mode; next FILE unless (&query_yes_no( " $str: $file ([y]/n)? ", $ACCEPT_YES_DEFAULT)); } my $str = 'Encrypting'; $str = 'Signing' if $signing_mode; print "[+] $str: $file\n" unless $quiet; unless ($trial_run) { my $rv = &encrypt_or_sign_file($filename, $encrypt_filename, $NO_DEL_SOURCE_FILE); if (-e $encrypt_filename and -s $encrypt_filename != 0) { ### set the atime and mtime to be the same as the ### original file. unless ($no_fs_times) { if (defined $mtime and $mtime and defined $atime and $atime) { utime $atime, $mtime, $encrypt_filename; } } unless ($signing_mode) { ### only delete the original file if ### the encrypted one exists if ($wipe_mode and not $quiet) { print " Securely deleting file: $file\n"; } &delete_file($filename); if ($obfuscate_mode) { ### record the original file name mapping &append_obfuscated_mapping($filename, $encrypt_filename); $obfuscate_ctrs{$dir}++; } } $total_encrypted++; } else { my $str = 'encrypt'; $str = 'sign' if $signing_mode; print "[-] Could not $str file: $file\n" unless $quiet; next FILE; } } } else { ### allow filenames with spaces my $decrypt_filename = ''; if ($filename =~ /^(.+)\.gpg$/) { $decrypt_filename = $1; } elsif ($filename =~ /^(.+)\.asc$/) { $decrypt_filename = $1; } elsif ($filename =~ /^(.+)\.pgp$/) { $decrypt_filename = $1; } if ($obfuscate_mode) { &import_obfuscated_file_map($dir) unless defined $obfuscated_dirs{$dir}; if (defined $obfuscated_dirs{$dir}{$filename}) { $decrypt_filename = $obfuscated_dirs{$dir}{$filename}; } else { ### print "[-] Obfuscated file map does not exist for ", "$filename in\n $obfuscate_map_filename, ", "skipping.\n" unless $quiet; next FILE; } } else { if (not $force_mode and ($file =~ /gpgdir_\d+_\d+\.gpg/ or $file =~ /gpgdir_\d+\.gpg/)) { ### be careful not to decrypt obfuscated file unless we ### are running in -O mode. This ensures that the ### original file names will be acquired from the ### /some/dir/.gpgdir_map_file $have_obfuscated_file = 1; next FILE; } } ### length() allows files named "0" next FILE unless length($decrypt_filename) > 0; if ($verify_mode) { unless (-e $decrypt_filename) { print "[-] Original file $decrypt_filename ", "does not exist, skipping.\n"; next FILE; } } else { ### don't decrypt a file on top of a normal file of ### the same name if (-e $decrypt_filename and not $overwrite_decrypted) { print "[-] Decrypted file $dir/$decrypt_filename ", "already exists. Skipping.\n" unless $quiet; next FILE; } } if ($interactive_mode) { my $str = 'Decrypt'; $str = 'Verify' if $verify_mode; next FILE unless (&query_yes_no( " $str: $file ([y]/n)? ", $ACCEPT_YES_DEFAULT)); } unless ($trial_run) { my $str = 'Decrypting'; $str = 'Verifying' if $verify_mode; print "[+] $str: $dir/$filename\n" unless $quiet; my $rv = &decrypt_or_verify_file($filename, $decrypt_filename, $NO_DEL_SOURCE_FILE); if ($verify_mode) { $total_decrypted++ if $rv; } else { if (-e $decrypt_filename and -s $decrypt_filename != 0) { ### set the atime and mtime to be the same as the ### original file. unless ($no_fs_times) { if (defined $mtime and $mtime and defined $atime and $atime) { utime $atime, $mtime, $decrypt_filename; } } if ($wipe_mode and not $quiet) { print " Securely deleting file: $file\n"; } ### only delete the original encrypted ### file if the decrypted one exists &delete_file($filename); $total_decrypted++; } else { print "[-] Could not decrypt file: $file\n" unless $quiet; next FILE; } } } } } print "\n" unless $quiet; chdir $initial_dir or die "[*] Could not chdir: $initial_dir\n"; return; } sub get_files() { my $dir = shift; print "[+] Building file list...\n" unless $quiet; if ($norecurse) { opendir D, $dir or die "[*] Could not open $dir: $!"; my @files = readdir D; closedir D; for my $file (@files) { next if $file eq '.'; next if $file eq '..'; &check_file_criteria("$dir/$file"); } } else { ### get all files in all subdirectories find(\&find_files, $dir); } return; } sub exclude_file() { my $file = shift; for my $pat (@exclude_patterns) { if ($file =~ m|$pat|) { print "[+] Skipping $file (matches exclude pattern: $pat)\n" if $verbose and not $quiet; return 1; } } return 0; } sub include_file() { my $file = shift; for my $pat (@include_patterns) { if ($file =~ m|$pat|) { print "[+] Including $file (matches include pattern: $pat)\n" if $verbose and not $quiet; return 1; } } return 0; } sub obfuscated_mapping_files() { my $dirs_href = {}; if ($encrypt_mode) { $dirs_href = \%obfuscate_ctrs; } else { $dirs_href = \%obfuscated_dirs; } DIR: for my $dir (keys %$dirs_href) { unless (chdir($dir)) { print "[-] Could not chdir $dir, skipping.\n" unless $quiet; next DIR; } if ($encrypt_mode) { next DIR unless -e $obfuscate_map_filename; ### encrypt the map file now that we have encrypted ### the directory print "[+] Encrypting mapping file: ", "$dir/$obfuscate_map_filename\n" unless $quiet; unless ($trial_run) { &encrypt_or_sign_file($obfuscate_map_filename, "$obfuscate_map_filename.gpg", $NO_DEL_SOURCE_FILE); unlink $obfuscate_map_filename; } } else { next DIR unless -e "$obfuscate_map_filename.gpg"; ### delete the map file since we have decrypted ### the directory print "[+] Decrypting mapping file: ", "$dir/$obfuscate_map_filename.gpg\n" unless $quiet; unless ($trial_run) { &decrypt_or_verify_file("$obfuscate_map_filename.gpg", $obfuscate_map_filename, $NO_DEL_SOURCE_FILE); unlink "$obfuscate_map_filename.gpg"; if ($total_mapped_files == $total_decrypted) { ### we are confident that we decrypted all of them, ### so delete the mapping file. unlink $obfuscate_map_filename; } } } } return; } sub handle_old_obfuscated_map_file() { return unless -e "$obfuscate_map_filename.gpg"; &decrypt_or_verify_file("$obfuscate_map_filename.gpg", $obfuscate_map_filename, $NO_DEL_SOURCE_FILE); unlink "$obfuscate_map_filename.gpg"; my @existing_obfuscated_files = (); open F, '<', $obfuscate_map_filename or die "[*] Could not open ", "$obfuscate_map_filename: $!"; while () { if (/^\s*.*\s+(gpgdir_\d+_\d+\.gpg)/) { if (-e $1) { push @existing_obfuscated_files, $_; } } elsif (/^\s*.*\s+(gpgdir_\d+\.gpg)/) { if (-e $1) { push @existing_obfuscated_files, $_; } } } close F; if (@existing_obfuscated_files) { ### there are some obfuscated files from a previous gpgdir ### execution open G, '>', $obfuscate_map_filename or die "[*] Could not open ", "$obfuscate_map_filename: $!"; print G for @existing_obfuscated_files; close G; } return; } sub append_obfuscated_mapping() { my ($filename, $encrypt_filename) = @_; open G, '>>', $obfuscate_map_filename or die "[*] Could not open ", "$obfuscate_map_filename: $!"; print G "$filename $encrypt_filename\n"; close G; return; } sub import_obfuscated_file_map() { my $dir = shift; $obfuscated_dirs{$dir} = {}; return unless -e "$obfuscate_map_filename.gpg"; &decrypt_or_verify_file("$obfuscate_map_filename.gpg", $obfuscate_map_filename, $NO_DEL_SOURCE_FILE); open G, '<', $obfuscate_map_filename or die "[*] Could not open ", "$obfuscate_map_filename: $!"; while () { if (/^\s*(.*)\s+(gpgdir_\d+_\d+\.gpg)/) { $obfuscated_dirs{$dir}{$2} = $1; $total_mapped_files++; } elsif (/^\s*(.*)\s+(gpgdir_\d+\.gpg)/) { $obfuscated_dirs{$dir}{$2} = $1; $total_mapped_files++; } } close G; return; } sub get_homedir() { my $uid = $<; my $homedir = ''; if (-e '/etc/passwd') { open P, '<', '/etc/passwd' or die "[*] Could not open /etc/passwd. Exiting.\n"; my @lines =

; close P; for my $line (@lines) { ### mbr:x:222:222:Michael Rash:/home/mbr:/bin/bash chomp $line; if ($line =~ /^(?:.*:){2}$uid:(?:.*:){2}(\S+):/) { $homedir = $1; last; } } } else { $homedir = $ENV{'HOME'} if defined $ENV{'HOME'}; } die "[*] Could not determine home directory. Use the -u option." unless $homedir; return $homedir; } sub get_key() { if (-e "${homedir}/.gpgdirrc") { open F, '<', "$homedir/.gpgdirrc" or die "[*] Could not open ", "${homedir}/.gpgdirrc. Exiting.\n"; my @lines = ; close F; my $key = ''; for my $line (@lines) { chomp $line; if ($line =~ /^\s*default_key/) { ### prefer to use the default GnuPG key $use_default_key = 1; return ''; } elsif ($line =~ /^\s*use_key\s+(.*)$/) { ### GnuPG accepts strings to match the key, so we don't ### have to strictly require a key ID... just a string ### that matches the key return $1; } } die "[*] Please edit ${homedir}/.gpgdirrc to include your gpg key identifier\n", " (e.g. \"D4696445\"; see the output of \"gpg --list-keys\"), or use the\n", " default GnuPG key defined in ~/.gnupg/options"; } print "[+] Creating gpgdir rc file: $homedir/.gpgdirrc\n"; open F, '>', "$homedir/.gpgdirrc" or die "[*] Could not open " . "${homedir}/.gpgdirrc. Exiting.\n"; print F <<_CONFIGRC_; # Config file for gpgdir. # # Set the key to use to encrypt files with "use_key ", e.g. # "use_key D4696445". See "gpg --list-keys" for a list of keys on your # GnuPG key ring. Alternatively, if you want gpgdir to always use the # default key that is defined by the "default-key" variable in # ~/.gnupg/options, then uncomment the "default_key" line below. # Uncomment to use the GnuPG default key defined in ~/.gnupg/options: #default_key # If you want to use a specific GnuPG key, Uncomment the next line and # replace "KEYID" with your real key id: #use_key KEYID _CONFIGRC_ close F; die "[*] Please edit $homedir/.gpgdirrc to include your gpg key identifier,\n", " or use the default GnuPG key defined in ~/.gnupg/options. Exiting.\n"; } sub find_files() { my $file = $File::Find::name; &check_file_criteria($file); return; } sub check_file_criteria() { my $file = shift; ### skip all links, zero size files, all hidden ### files (includes the .gnupg directory), etc. return if -d $file; unless ($force_mode) { if ($file =~ m|/\.|) { print "[-] Skipping file: $file\n" if $verbose and not $quiet; return; } } if (-e $file and not -l $file and -s $file != 0 and $file !~ m|\.gpgdir\.pid| and $file !~ m|\.gnupg|) { if ($encrypt_mode or $signing_mode) { if ($file =~ m|\.gpg| or $file =~ m|\.asc| or $file =~ m|\.pgp|) { print "[-] Skipping encrypted/signed file: $file\n" unless $quiet; return; } } elsif ($verify_mode) { unless ($file =~ m|\.asc|) { ### only pick up the signature files return; } } else { unless ($file =~ m|\.gpg| or $file =~ m|\.asc| or $file =~ m|\.pgp|) { print "[-] Skipping unencrypted file: $file\n" unless $quiet; return; } } my ($atime, $mtime) = (stat($file))[8,9]; $files{$file}{'atime'} = $atime; $files{$file}{'mtime'} = $mtime; } else { print "[-] Skipping file: $file\n" if $verbose and not $quiet; } return; } sub get_password() { ### this is only useful if the gpg key literally has no password ### (usually this is not the case, but gpgdir will support it if ### so). return if $cmdline_no_password; ### if we are using gpg-agent for passwords, then return return if $use_gpg_agent; if ($pw_file) { open PW, '<', $pw_file or die "[*] Could not open $pw_file: $!"; $pw = ; close PW; chomp $pw; } else { print "[+] Executing: gpgdir @args_cp\n" unless $quiet; if ($symmetric_mode) { print " [Symmetric mode]\n" unless $quiet; } else { if ($use_default_key) { print " Using default GnuPG key.\n" unless $quiet; } else { print " Using GnuPG key: $encrypt_user\n" unless $quiet; } } if ($test_and_exit) { print " *** test_mode() ***\n" unless $quiet; } if ($signing_mode) { print " Enter signing password.\n" unless $quiet; } elsif ($encrypt_mode) { print ' Enter password (for initial ' . "encrypt/decrypt test)\n" unless $quiet; } my $msg = 'Password: '; ### get the password without echoing the chars back to the screen ReadMode('noecho'); while (not $pw) { print $msg; $pw = ReadLine(0); chomp $pw; } ReadMode('normal'); if ($quiet) { print "\n"; } else { print "\n\n"; } } return; } sub test_mode() { chdir $dir or die "[*] Could not chdir($dir): $!"; my $test_file = "gpgdir_test.$$"; print "[+] test_mode(): Encrypt/Decrypt test of $test_file\n" if (($test_and_exit or $verbose) and not $quiet); if (-e $test_file) { &delete_file($test_file) or die "[*] test_mode(): Could not remove $test_file: $!"; } if (-e "$test_file.gpg") { &delete_file("$test_file.gpg") or die "[*] test_mode(): Could not remove $test_file.gpg: $!"; } open G, '>', $test_file or die "[*] test_mode(): Could not create $test_file: $!"; print G "gpgdir test\n"; close G; if (-e $test_file) { print "[+] test_mode(): Created $test_file\n" if (($test_and_exit or $verbose) and not $quiet); } else { die "[*] test_mode(): Could not create $test_file\n"; } &encrypt_or_sign_file($test_file, "${test_file}.gpg", $DEL_SOURCE_FILE); if (-e "$test_file.gpg" and (-s $test_file != 0)) { print "[+] test_mode(): Successful encrypt of $test_file\n" if (($test_and_exit or $verbose) and not $quiet); &delete_file($test_file) if -e $test_file; } else { die "[*] test_mode(): not encrypt $test_file (try adding -v).\n"; } &decrypt_or_verify_file("${test_file}.gpg", $test_file, $DEL_SOURCE_FILE); if (-e $test_file and (-s $test_file != 0)) { print "[+] test_mode(): Successful decrypt of $test_file\n" if (($test_and_exit or $verbose) and not $quiet); } else { die "[*] test_mode(): Could not decrypt $test_file.gpg ", "(try adding -v).\n"; } open F, '<', $test_file or die "[*] test_mode(): Could not open $test_file: $!"; my $line = ; close F; if (defined $line and $line =~ /\S/) { chomp $line; if ($line eq 'gpgdir test') { print "[+] test_mode(): Decrypted content matches original.\n", "[+] test_mode(): Success!\n\n" if (($test_and_exit or $verbose) and not $quiet); } else { die "[*] test_mode(): Decrypted content does not match ", "original (try adding -v)."; } } else { die "[*] test_mode(): Fail (try adding -v).\n"; } &delete_file($test_file) if -e $test_file; &delete_file("$test_file.gpg") if -e "$test_file.gpg"; chdir $initial_dir or die "[*] Could not chdir($initial_dir)"; return 0; ### exit status } sub query_yes_no() { my ($msg, $style) = @_; my $ans = ''; while ($ans ne 'y' and $ans ne 'n') { print $msg; $ans = lc(); if ($style == $ACCEPT_YES_DEFAULT) { return 1 if $ans eq "\n"; } elsif ($style == $ACCEPT_NO_DEFAULT) { return 0 if $ans eq "\n"; } chomp $ans; } return 1 if $ans eq 'y'; return 0; } sub unique_pid() { return unless -e $pid_file; open P, '<', $pid_file or die "[*] Could not open $pid_file: $!"; my $pid =

; chomp $pid; close P; if (kill 0, $pid) { die "[*] Another gpgdir process (pid: $pid) is already ", "running against\n $dir"; } return; } sub write_pid() { open P, '>', $pid_file or die "[*] Could not open $pid_file: $!"; print P $$, "\n"; close P; return; } sub import_perl_modules() { my $mod_paths_ar = &get_mod_paths(); if ($#$mod_paths_ar > -1) { ### /usr/lib/gpgdir/ exists push @$mod_paths_ar, @INC; splice @INC, 0, $#$mod_paths_ar+1, @$mod_paths_ar; } if ($verbose) { print "[+] import_perl_modules(): The \@INC array:\n"; print "$_\n" for @INC; } require GnuPG::Interface; require Term::ReadKey; Term::ReadKey->import(qw/ReadMode ReadLine/); return; } sub get_mod_paths() { my @paths = (); unless (-d $lib_dir) { my $dir_tmp = $lib_dir; $dir_tmp =~ s|lib/|lib64/|; if (-d $dir_tmp) { $lib_dir = $dir_tmp; } else { return []; } } opendir D, $lib_dir or die "[*] Could not open $lib_dir: $!"; my @dirs = readdir D; closedir D; push @paths, $lib_dir; for my $dir (@dirs) { ### get directories like "/usr/lib/gpgdir/x86_64-linux" next unless -d "$lib_dir/$dir"; push @paths, "$lib_dir/$dir" if $dir =~ m|linux| or $dir =~ m|thread| or (-d "$lib_dir/$dir/auto"); } return \@paths; } sub usage_and_exit() { print <<_HELP_; gpgdir; Recursive direction encryption and decryption with GnuPG [+] Version: $version (file revision: $rev_num) By Michael Rash (mbr\@cipherdyne.org) URL: http://www.cipherdyne.org/gpgdir/ Usage: gpgdir -e|-d [options] Options: -e, --encrypt - Recursively encrypt all files in and all subdirectories. -d, --decrypt - Recursively decrypt all files in and all subdirectories. --sign - Recursively sign all files in and all subdirectories. --verify - Recursively verify all GnuPG signatures in . -K, --Key-id - Specify GnuPG key ID, or key-matching string. This overrides the use_key value in ~/.gpgdirrc -D, --Default-key - Use the key that GnuPG defines as the default (i.e. the key that is specified by the default-key option in ~/.gnupg/options). -a, --agent - Acquire password information from a running instance of gpg-agent. -A, --Agent-info - Specify the value for the GPG_AGENT_INFO environment variable as returned by 'gpg-agent --daemon'. -g, --gnupg-dir

- Specify a path to a .gnupg directory for gpg keys (the default is ~/.gnupg if this option is not used). -S, --Symmetric - Use symmetric encryption instead of the default asymmetric encryption. -p, --pw-file - Read password in from . --skip-test - Skip encrypt -> decrypt test. -t, --test-mode - Run encrypt -> decrypt test and exit. -T, --Trial-run - Show what filesystem actions would take place without actually doing them. -P, --Plain-ascii - Ascii armor mode (creates non-binary encrypted files). --Interactive - Query the user before encrypting, decrypting, or deleting any files. --Exclude - Skip all filenames that match . --Exclude-from - Skip all filenames that match any pattern contained within . --Include - Include only those filenames that match . --Include-from - Include only those filenames that match a pattern contained within . -O, --Obfuscate-filenames - Substitute all real filenames in a directory with manufactured ones (the original filenames are preserved in a mapping file and restored when the directory is decrypted). --obfuscate-map_file - Specify path to obfuscated mapping file (in -O mode). -F, --Force - Continue to run even if files cannot be deleted (because of permissions problems for example). --overwrite-encrypted - Overwrite encrypted files even if a previous .gpg file already exists. --overwrite-decrypted - Overwrite decrypted files even if the previous unencrypted file already exists. -q, --quiet - Print as little to the screen as possible -W, --Wipe - Use the 'wipe' command to securely delete unencrypted copies of files after they have been encrypted. --wipe-path - Specify path to the wipe command. --wipe-interactive - Force interactive mode with the wipe command. --wipe-cmdline - Manually specify command line arguments to the wipe command. --no-recurse - Don't recursively encrypt/decrypt subdirectories. --no-delete - Don't delete original unencrypted files. --no-preserve-times - Don't preserve original mtime and atime values on encrypted/decrypted files. --no-password - Assume the gpg key has no password at all (this is not common). -u, --user-homedir - Path to home directory. -l, --locale - Manually define a locale setting. --Lib-dir - Path to the perl modules directory (not usually necessary). --no-locale - Don't set the locale to anything (the default is the "C" locale). --verbose - Run in verbose mode. -V, --Version - print version. -h, --help - print help. _HELP_ exit 0; } signing-party-2.12/gpgdir/gpgdir.1000066400000000000000000000242341500571021200170410ustar00rootroot00000000000000.\" Process this file with .\" groff -man -Tascii foo.1 .\" .TH GPGDIR 1 "May, 2007" Linux .SH NAME .B gpgdir \- recursive directory encryption with GnuPG .SH SYNOPSIS .B gpgdir \-e|\-d [options] .SH DESCRIPTION .B gpgdir is a perl script that uses the CPAN GnuPG::Interface perl module to recursively encrypt and decrypt directories using gpg. .B gpgdir recursively descends through a directory in order to make sure it encrypts or decrypts every file in a directory and all of its subdirectories. By default the mtime and atime values of all files will be preserved upon encryption and decryption (this can be disabled with the .B \-\-no-preserve-times option). Note that in .B \-\-encrypt mode, gpgdir will delete the original files that it successfully encrypts (unless the .B \-\-no-delete option is given). However, upon startup gpgdir first asks for a the decryption password to be sure that a dummy file can successfully be encrypted and decrypted. The initial test can be disabled with the .B \-\-skip-test option so that a directory can easily be encrypted without having to also specify a password (this is consistent with .B gpg behavior). Also, note that gpgdir is careful not encrypt hidden files and directories. After all, you probably don't want your ~/.gnupg directory or ~/.bashrc file to be encrypted. The key .B gpgdir uses to encrypt/decrypt a directory is specified in ~/.gpgdirrc. Finally, .B gpgdir can use the .B wipe program with the .B \-\-Wipe command line option to securely delete the original unencrypted files after they have been successfully encrypted. This elevates the security stance of gpgdir since it is more difficult to recover the unencrypted data associated with files from the filesystem after they are encrypted (unlink() does not erase data blocks even though a file is removed). .SH OPTIONS .TP .BR \-e ", " \-\^\-encrypt\ \ Recursively encrypt all files in the directory specified on the command line. All original files will be deleted (a password check is performed first to make sure that the correct password to unlock the private GnuPG key is known to the user). .TP .BR \-d ", " \-\^\-decrypt\ \ Recursively decrypt all files in the directory specified on the command line. The encrypted .gpg version of each file will be deleted. .TP .BR \-\^\-sign\ \ Recursively sign all files in the directory specified on the command line. For each file, a detached .asc signature will be created. .TP .BR \-\^\-verify\ \ Recursively verify all .asc signatures for files in the directory specified on the command line. .TP .BR \-g ", " \-\^\-gnupg-dir\ \ Specify which .gnupg directory will be used to find GnuPG keys. The default is ~/.gnupg if this option is not used. This option allows gpgdir to be run as one user but use the keys of another user (assuming permissions are setup correctly, etc.). .TP .BR \-p ", " \-\^\-pw-file\ \ Read decryption password from .B pw-file instead of typing it on the command line. .TP .BR \-t ", " \-\^\-test-mode Run an encryption and decryption test against a dummy file and exit. This test is always run by default in both .B \-\-encrypt and .B \-\-decrypt mode. .TP .BR \-S ", " \-\^\-Symmetric Instruct .B gpgdir to encrypt to decrypt files using a symmetric cipher supported by GnuPG (CAST5 is commonly used). This results in a significant speed up for the encryption/decryption process. .TP .BR \-T ", " \-\^\-Trial-run Show what encrypt/decrypt actions would take place without actually doing them. The filesystem is not changed in any way in this mode. .TP .BR \-I ", " \-\^\-Interactive Prompt the user before actually encrypting or decrypting each file. This is useful to have fine-grained control over .B gpgdir operations as it recurses through a directory structure. .TP .BR \-F ", " \-\^\-Force Tell .B gpgdir to ignore non-fatal error conditions, such as the inability to encrypt or decrypt individual files because of permissions errors. .TP .BR \-\^\-Exclude\ \ Instruct gpgdir to skip all files that match .B pattern as a regex match against each filename. This is similar to the .B \-\-exclude option in the standard GNU tar command. .TP .BR \-\^\-Exclude-from\ \ Instruct gpgdir to exclude all files matched by patterns listed in .B file. This is similar to the .B \-\-exclude-from the GNU tar command. .TP .BR \-\^\-Include\ \ Instruct gpgdir to only include files that match .B pattern as a regex match against each filename. .TP .BR \-\^\-Include-from\ \ Instruct gpgdir to only include files matched by patterns listed in .B file. .TP .BR \-W ", " \-\^\-Wipe Use the .B wipe program to securely delete files after they have been successfully encrypted. .TP .BR \-O ", " \-\^\-Obfuscate-filename Tell .B gpgdir to obfuscate the file names of files that it encrypts (in \-e mode). The names of each file are stored within the file .gpgdir_map_file for every sub-directory, and this file is itself encrypted. In decryption mode (\-d), the \-O argument reverses the process so that the original files are restored. .TP .BR \-\^\-overwrite-encrypted Overwrite encrypted files even if a previous .gpg file already exists. .TP .BR \-\^\-overwrite-decrypted Overwrite decrypted files even if the previous unencrypted file already exists. .TP .BR \-K ", " \-\^\-Key-id\ \ Manually specify a GnuPG key ID from the command line. Because GnuPG supports matching keys with a string, .B id does not strictly have to be a key ID; it can be a string that uniquely matches a key in the GnuPG key ring. .TP .BR \-D ", " \-\^\-Default-key Use the key that GnuPG defines as the default, i.e. the key that is specified by the .B default-key variable in ~/.gnupg/options. If the default-key variable is not defined within ~/.gnupg/options, then GnuPG tries to use the first suitable key on its key ring (the initial encrypt/decrypt test makes sure that the user knows the corresponding password for the key). .TP .BR \-a ", " " \-\^\-agent Instruct .B gpgdir to acquire gpg key password from a running .B gpg-agent instance. .TP .BR \-A ", " \-\^\-Agent-info\ \ Specify the value of the GPG_AGENT_INFO environment variable as returned by the .B gpg-agent \-\-daemon command. If the .B gpgdir \-\-agent command line argument is used instead of .B \-\-Agent-info, then gpgdir assumes that the GPG_AGENT_INFO environment variable has already been set in the current shell. .TP .BR \-s ", " " \-\^\-skip-test Skip encryption and decryption test. This will allow .B gpgdir to be used to encrypt a directory without specifying a password (which normally gets used in encryption mode to test to make sure decryption against a dummy file works properly). .TP .BR \-q ", " \-\^\-quiet Print as little as possible to the screen when encrypting or decrypting a directory. .TP .BR \-\^\-no-recurse Instruct gpgdir to not recurse through any subdirectories of the directory that is being encrypted or decrypted. .TP .BR \-\^\-no-password Instruct gpgdir to not ask the user for a password. This is only useful when a gpg key literally has no associated password (this is not common). .TP .BR \-\^\-no-delete Instruct gpgdir to not delete original files at encrypt time. .TP .BR \-\^\-no-preserve times Instruct gpgdir to not preserve original file mtime and atime values upon encryption or decryption. .TP .BR \-l ", " " \-\^\-locale\ \ Provide a locale setting other than the default "C" locale. .TP .BR \-\^\-no-locale Do not set the locale at all so that the default system locale will apply. .TP .BR \-v ", " \-\^\-verbose Run in verbose mode. .TP .BR \-V ", " \-\^\-Version Print version number and exit. .TP .BR \-h ", " \-\^\-help Print usage information and exit. .SH FILES .B ~/.gpgdirrc .RS Contains the key id of the user gpg key that will be used to encrypt or decrypt the files within a directory. .RE .PP .SH ENVIRONMENT .TP 13 .I HOME Set the default home directory. .TP 13 .I GNUPGBIN Set the gpg binary. Default: "gpg". .TP 13 .I GNUPGHOME Set the default working directory for gpg. Default: "~/.gnupg". .SH EXAMPLES The following examples illustrate the command line arguments that could be supplied to gpgdir in a few situations: .PP To encrypt a directory: .PP .B $ gpgdir \-e /some/dir .PP To encrypt a directory, and use the wipe command to securely delete the original unencrypted files: .PP .B $ gpgdir \-W \-e /some/dir .PP To encrypt a directory with the default GnuPG key defined in ~/.gnupg/options: .PP .B $ gpgdir \-e /some/dir \-\-Default-key .PP To decrypt a directory with a key specified in ~/.gpgdirrc: .PP .B $ gpgdir \-d /some/dir .PP To encrypt a directory but skip all filenames that contain the string "host": .PP .B $ gpgdir \-e /some/dir \-\-Exclude host .PP To encrypt a directory but only encrypt those files that contain the string "passwd": .PP .B $ gpgdir \-e /some/dir \-\-Include passwd .PP To acquire the GnuPG key password from a running gpg-agent daemon in order to decrypt a directory (this requires that gpg-agent has the password): .PP .B $ gpgdir \-A /tmp/gpg-H4DBhc/S.gpg-agent:7046:1 \-d /some/dir .PP To encrypt a directory but skip the encryption/decryption test (so you will not be prompted for a decryption password): .PP .B $ gpgdir \-e /some/dir \-s .PP To encrypt a directory and no subdirectories: .PP .B $ gpgdir \-e /some/dir \-\-no-recurse .PP To encrypt root's home directory, but use the GnuPG keys associated with the user "bob": .PP .B # gpgdir \-e /root \-g /home/bob/.gnupg .PP .SH DEPENDENCIES .B gpgdir requires that gpg, the Gnu Privacy Guard (http://www.gnupg.org) is installed. .B gpgdir also requires the GnuPG::Interface perl module from CPAN, but it is bundled with .B gpgdir and is installed in /usr/lib/gpgdir at install-time so it does not pollute the system perl library tree. .SH "SEE ALSO" .BR gpg (1) .SH AUTHOR Michael Rash .SH CONTRIBUTORS Many people who are active in the open source community have contributed to gpgdir; see the .B CREDITS file in the gpgdir sources. .SH BUGS Send bug reports to mbr@cipherdyne.org. Suggestions and/or comments are always welcome as well. .SH DISTRIBUTION .B gpgdir is distributed under the GNU General Public License (GPL), and the latest version may be downloaded from .B http://www.cipherdyne.org signing-party-2.12/gpgdir/install.pl000077500000000000000000000335541500571021200175160ustar00rootroot00000000000000#!/usr/bin/perl -w # #################################################################### # # File: install.pl # # Purpose: To install gpgdir on a Linux system. # # Author: Michael Rash (mbr@cipherdyne.org) # # Copyright (C) 2002-2008 Michael Rash (mbr@cipherdyne.org) # # License (GNU Public License): # # 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, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 # USA # #################################################################### # # $Id: install.pl 311 2008-08-31 23:11:12Z mbr $ # use Cwd; use File::Copy; use Getopt::Long; use strict; #======================= config ======================= my $install_dir = '/usr/bin'; my $libdir = '/usr/lib/gpgdir'; my $manpage = 'gpgdir.1'; ### only used it $ENV{'HOME'} is not set for some reason my $config_homedir = ''; ### system binaries my $gzipCmd = '/usr/bin/gzip'; my $perlCmd = '/usr/bin/perl'; my $makeCmd = '/usr/bin/make'; #===================== end config ===================== my $print_help = 0; my $uninstall = 0; my $force_mod_re = ''; my $exclude_mod_re = ''; my $skip_module_install = 0; my $cmdline_force_install = 0; my $locale = 'C'; ### default LC_ALL env variable my $no_locale = 0; my $deps_dir = 'deps'; my %cmds = ( 'gzip' => $gzipCmd, 'perl' => $perlCmd, 'make' => $makeCmd ); ### map perl modules to versions my %required_perl_modules = ( 'Class::MethodMaker' => { 'force-install' => 0, 'mod-dir' => 'Class-MethodMaker' }, 'GnuPG::Interface' => { 'force-install' => 0, 'mod-dir' => 'GnuPG-Interface' }, 'Term::ReadKey' => { 'force-install' => 0, 'mod-dir' => 'TermReadKey' } ); ### make Getopts case sensitive Getopt::Long::Configure('no_ignore_case'); &usage(1) unless (GetOptions( 'force-mod-install' => \$cmdline_force_install, ### force install of all modules 'Force-mod-regex=s' => \$force_mod_re, ### force specific mod install with regex 'Exclude-mod-regex=s' => \$exclude_mod_re, ### exclude a particular perl module 'Skip-mod-install' => \$skip_module_install, 'home-dir=s' => \$config_homedir, ### force a specific home dir 'LC_ALL=s' => \$locale, 'locale=s' => \$locale, 'no-LC_ALL' => \$no_locale, 'no-locale' => \$no_locale, ### synonym 'uninstall' => \$uninstall, # Uninstall gpgdir. 'help' => \$print_help # Display help. )); &usage(0) if $print_help; ### set LC_ALL env variable $ENV{'LC_ALL'} = $locale unless $no_locale; $force_mod_re = qr|$force_mod_re| if $force_mod_re; $exclude_mod_re = qr|$exclude_mod_re| if $exclude_mod_re; ### check to see if we are installing in a Cygwin environment my $non_root_user = 0; if (&is_cygwin()) { print "[+] It looks like you are installing gpgdir in a Cygwin environment.\n"; $non_root_user = 1; } else { unless ($< == 0 && $> == 0) { print "[+] It looks like you are installing gpgdir as a non-root user, so gpgdir\n", " will be installed in your local home directory.\n\n"; $non_root_user = 1; } } if ($non_root_user) { ### we are installing as a normal user instead of root, so see ### if it is ok to install within the user's home directory my $homedir = ''; if ($config_homedir) { $homedir = $config_homedir; } else { $homedir = $ENV{'HOME'} or die '[*] Could not get home ', "directory, set the $config_homedir var."; } print " gpgdir will be installed at $homedir/bin/gpgdir, and a few\n", " perl modules needed by gpgdir will be installed in $homedir/lib/gpgdir/.\n\n", mkdir "$homedir/lib" unless -d "$homedir/lib"; $libdir = "$homedir/lib/gpgdir"; $install_dir = "$homedir/bin"; } ### make sure we can find the system binaries ### in the expected locations. &check_commands(); my $src_dir = getcwd() or die "[*] Could not get current working directory."; ### create directories, make sure executables exist, etc. &setup(); print "[+] Installing gpgdir in $install_dir\n"; &install_gpgdir(); ### install perl modules unless ($skip_module_install) { for my $module (keys %required_perl_modules) { &install_perl_module($module); } } chdir $src_dir or die "[*] Could not chdir $src_dir: $!"; print "[+] Installing man page.\n"; &install_manpage(); print "\n It is highly recommended to run the test suite in the test/\n", " directory to ensure proper gpgdir operation.\n", "\n[+] gpgdir has been installed!\n"; exit 0; #===================== end main ======================= sub install_gpgdir() { die "[*] gpgdir does not exist. Download gpgdir from " . "http://www.cipherdyne.org/gpgdir" unless -e 'gpgdir'; copy 'gpgdir', "${install_dir}/gpgdir" or die "[*] Could not copy " . "gpgdir to $install_dir: $!"; if ($non_root_user) { open F, '<', "$install_dir/gpgdir" or die "[*] Could not open ", "${install_dir}/gpgdir: $!"; my @lines = ; close F; open P, '>', "$install_dir/gpgdir.tmp" or die "[*] Could not open ", "${install_dir}/gpgdir.tmp: $!"; for my $line (@lines) { ### change the lib dir to new homedir path if ($line =~ m|^\s*use\s+lib\s+\'/usr/lib/gpgdir\';|) { print P "use lib '", $libdir, "';\n"; } else { print P $line; } } close P; move "${install_dir}/gpgdir.tmp", "${install_dir}/gpgdir" or die "[*] Could not move ${install_dir}/gpgdir.tmp -> ", "${install_dir}/gpgdir: $!"; chmod 0700, "${install_dir}/gpgdir" or die "[*] Could not set " . "permissions on gpgdir to 0755"; } else { chmod 0755, "${install_dir}/gpgdir" or die "[*] Could not set " . "permissions on gpgdir to 0755"; chown 0, 0, "${install_dir}/gpgdir" or die "[*] Could not chown 0,0,${install_dir}/gpgdir: $!"; } return; } sub install_perl_module() { my $mod_name = shift; chdir $src_dir or die "[*] Could not chdir $src_dir: $!"; chdir $deps_dir or die "[*] Could not chdir($deps_dir): $!"; die '[*] Missing force-install key in required_perl_modules hash.' unless defined $required_perl_modules{$mod_name}{'force-install'}; die '[*] Missing mod-dir key in required_perl_modules hash.' unless defined $required_perl_modules{$mod_name}{'mod-dir'}; if ($exclude_mod_re and $exclude_mod_re =~ /$mod_name/) { print "[+] Excluding installation of $mod_name module.\n"; return; } my $version = '(NA)'; my $mod_dir = $required_perl_modules{$mod_name}{'mod-dir'}; if (-e "$mod_dir/VERSION") { open F, '<', "$mod_dir/VERSION" or die "[*] Could not open $mod_dir/VERSION: $!"; $version = ; close F; chomp $version; } else { print "[-] Warning: VERSION file does not exist in $mod_dir\n"; } my $install_module = 0; if ($required_perl_modules{$mod_name}{'force-install'} or $cmdline_force_install) { ### install regardless of whether the module may already be ### installed $install_module = 1; } elsif ($force_mod_re and $force_mod_re =~ /$mod_name/) { print "[+] Forcing installation of $mod_name module.\n"; $install_module = 1; } else { if (has_perl_module($mod_name)) { print "[+] Module $mod_name is already installed in the ", "system perl tree, skipping.\n"; } else { ### install the module in the /usr/lib/gpgdir directory because ### it is not already installed. $install_module = 1; } } if ($install_module) { unless (-d $libdir) { print "[+] Creating $libdir\n"; mkdir $libdir, 0755 or die "[*] Could not mkdir $libdir: $!"; } print "[+] Installing the $mod_name $version perl " . "module in $libdir/\n"; my $mod_dir = $required_perl_modules{$mod_name}{'mod-dir'}; chdir $mod_dir or die "[*] Could not chdir to ", "$mod_dir: $!"; unless (-e 'Makefile.PL') { die "[*] Your $mod_name source directory appears to be incomplete!\n", " Download the latest sources from ", "http://www.cipherdyne.org/\n"; } system "$cmds{'make'} clean" if -e 'Makefile'; system "$cmds{'perl'} Makefile.PL PREFIX=$libdir LIB=$libdir"; system $cmds{'make'}; # system "$cmds{'make'} test"; system "$cmds{'make'} install"; chdir $src_dir or die "[*] Could not chdir $src_dir: $!"; print "\n\n"; } chdir $src_dir or die "[*] Could not chdir $src_dir: $!"; return; } sub has_perl_module() { my $module = shift; # 5.8.0 has a bug with require Foo::Bar alone in an eval, so an # extra statement is a workaround. my $file = "$module.pm"; $file =~ s{::}{/}g; eval { require $file }; return $@ ? 0 : 1; } sub install_manpage() { if ($non_root_user) { print "[+] Because this is a non-root install, the man page will not be installed\n", " but you can download it here: http://www.cipherdyne.org/gpgdir\n\n"; return; } die "[*] man page: $manpage does not exist. Download gpgdir " . "from http://www.cipherdyne.org/gpgdir" unless -e $manpage; ### default location to put the gpgdir man page, but check with ### /etc/man.config my $mpath = '/usr/share/man/man1'; if (-e '/etc/man.config') { ### prefer to install $manpage in /usr/local/man/man1 if ### this directory is configured in /etc/man.config open M, '<', '/etc/man.config' or die "[*] Could not open /etc/man.config: $!"; my @lines = ; close M; ### prefer the path "/usr/share/man" my $found = 0; for my $line (@lines) { chomp $line; if ($line =~ m|^MANPATH\s+/usr/share/man|) { $found = 1; last; } } ### try to find "/usr/local/man" if we didn't find /usr/share/man unless ($found) { for my $line (@lines) { chomp $line; if ($line =~ m|^MANPATH\s+/usr/local/man|) { $mpath = '/usr/local/man/man1'; $found = 1; last; } } } ### if we still have not found one of the above man paths, ### just select the first one out of /etc/man.config unless ($found) { for my $line (@lines) { chomp $line; if ($line =~ m|^MANPATH\s+(\S+)|) { $mpath = $1; last; } } } } mkdir $mpath, 0755 unless -d $mpath; my $mfile = "${mpath}/${manpage}"; print "[+] Installing $manpage man page as: $mfile\n"; copy $manpage, $mfile or die "[*] Could not copy $manpage to " . "$mfile: $!"; chmod 0644, $mfile or die "[*] Could not set permissions on ". "$mfile to 0644"; chown 0, 0, $mfile or die "[*] Could not chown 0,0,$mfile: $!"; print "[+] Compressing man page: $mfile\n"; ### remove the old one so gzip doesn't prompt us unlink "${mfile}.gz" if -e "${mfile}.gz"; system "$cmds{'gzip'} $mfile"; return; } ### check paths to commands and attempt to correct if any are wrong. sub check_commands() { my @path = qw( /bin /sbin /usr/bin /usr/sbin /usr/local/bin /usr/local/sbin ); CMD: for my $cmd (keys %cmds) { unless (-x $cmds{$cmd}) { my $found = 0; PATH: for my $dir (@path) { if (-x "${dir}/${cmd}") { $cmds{$cmd} = "${dir}/${cmd}"; $found = 1; last PATH; } } unless ($found) { die "[*] Could not find $cmd anywhere!!! ", "Please edit the config section to include the path to ", "$cmd.\n"; } } unless (-x $cmds{$cmd}) { die "[*] $cmd is located at ", "$cmds{$cmd} but is not executable by uid: $<\n"; } } return; } sub is_cygwin() { my $rv = 0; ### get OS output from uname open UNAME, '-|', qw/uname -o/ or return $rv; while () { $rv = 1 if /Cygwin/; } close UNAME; return $rv; } sub setup() { unless (-d $libdir) { mkdir $libdir, 0755 or die "[*] Could not create $libdir: $!" } return; } sub usage() { my $exit_status = shift; print <<_HELP_; Usage: install.pl [options] -u, --uninstall - Uninstall gpgdir. -f, --force-mod-install - Force all perl modules to be installed even if some already exist in the system /usr/lib/perl5 tree. -F, --Force-mod-regex - Specify a regex to match a module name and force the installation of such modules. -E, --Exclude-mod-regex - Exclude a perl module that matches this regular expression. -S, --Skip-mod-install - Do not install any perl modules. -L, --LANG - Specify LANG env variable (actually the LC_ALL variable). -h --help - Prints this help message. _HELP_ exit $exit_status; } signing-party-2.12/gpgdir/packaging/000077500000000000000000000000001500571021200174225ustar00rootroot00000000000000signing-party-2.12/gpgdir/packaging/cd_rpmbuilder000077500000000000000000000160751500571021200221740ustar00rootroot00000000000000#!/usr/bin/perl -w # ############################################################################# # # File: cd_rpmbuilder "CipherDyne Rpm Builder" # # Purpose: Provides a consistent way to build RPMs of CipherDyne open source # projects (psad, fwsnort, fwsknop, and gpgdir). # # Author: Michael Rash # # Copyright (C) 2006-2008 Michael Rash (mbr@cipherdyne.org) # # License (GNU Public License - GPLv2): # # 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, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 # USA # ############################################################################# # # $Id: cd_rpmbuilder 1864 2008-08-22 03:16:19Z mbr $ # use File::Find; use File::Copy; use Getopt::Long 'GetOptions'; use strict; #============================ config ============================= my $rpm_root_dir = '/usr/src/redhat'; my $build_url_base = 'http://www.cipherdyne.org'; ### commands my $rpmbuildCmd = '/usr/bin/rpmbuild'; my $wgetCmd = '/usr/bin/wget'; #========================== end config =========================== my $version = '0.9'; my $project = ''; my $build_version = ''; my $print_version = 0; my $nodeps = 0; my $verbose = 0; my $help = 0; my @rpm_paths = (); my $RM = 1; my $PRINT = 2; my %projects = ( 'psad' => '', 'fwknop' => '', 'fwsnort' => '', 'gpgdir' => '' ); Getopt::Long::Configure('no_ignore_case'); &usage() unless (GetOptions( 'project=s' => \$project, 'build-version=s' => \$build_version, 'rpm-build-dir=s' => \$rpm_root_dir, 'no-deps' => \$nodeps, 'verbose' => \$verbose, 'Version' => \$print_version, 'help' => \$help )); &usage() if $help; if ($print_version) { print "[+] cd_rpmbuilder by Michael Rash \n"; exit 0; } if ($project) { unless (defined $projects{$project}) { print "[*] Unrecognized project: $project; must be one of:\n"; print $_, "\n" for keys %projects; exit 1; } } else { die "[*] Must specify a project with -p \n"; } die "[*] $wgetCmd is not a valid path to wget, update the config section." unless -x $wgetCmd; die "[*] $rpmbuildCmd is not a valid path to rpmbuild, update the config " . "section." unless -x $rpmbuildCmd; chdir "$rpm_root_dir/SPECS" or die "[*] Could not chdir $rpm_root_dir/SPECS"; unless ($build_version) { ### we need to get the latest version from cipherdyne.org &get_latest_version(); } my $spec_file = "$project-$build_version.spec"; my $tar_file = "$project-$build_version.tar.gz"; if ($nodeps) { $spec_file = "$project-nodeps-$build_version.spec"; $tar_file = "$project-nodeps-$build_version.tar.gz"; } ### remove old RPMS &find_rpms($RM); ### get the remote spec file &download_file($spec_file); &md5_check($spec_file); ### get the remote source tarball and md5 sum file &download_file($tar_file); &md5_check($tar_file); if ($nodeps) { move $tar_file, "../SOURCES/$project-$build_version.tar.gz" or die $!; } else { move $tar_file, '../SOURCES' or die $!; } ### build the rpm &build_rpm(); ### print the paths to the new RPMS &find_rpms($PRINT); exit 0; #======================= end main ======================== sub find_rpms() { my $action = shift; @rpm_paths = (); find(\&get_rpms, "$rpm_root_dir/SRPMS"); find(\&get_rpms, "$rpm_root_dir/RPMS"); if ($action == $PRINT) { if (@rpm_paths) { print "[+] The following RPMS were successfully built:\n\n"; } else { print "[-] No RPMS were successfully built; try running ", "with --verbose\n"; } } for my $rpm_file (@rpm_paths) { if ($action == $RM) { unlink $rpm_file or die "[*] Could not unlink $rpm_file: $!"; } elsif ($action == $PRINT) { if ($rpm_file =~ /\.src\.rpm/) { print " $rpm_file (source RPM)\n"; } else { print " $rpm_file\n"; } } } print "\n" if $action == $PRINT; return; } sub get_rpms() { my $file = $File::Find::name; if ($file =~ /$project-$build_version-.*\.rpm$/) { push @rpm_paths, $file; } return; } sub download_file() { my $file = shift; unlink $file if -e $file; print "[+] Downloading file:\n", " $build_url_base/$project/download/$file\n"; my $cmd = "$wgetCmd $build_url_base/$project/download/$file"; unless ($verbose) { $cmd .= ' > /dev/null 2>&1'; } system $cmd; die "[*] Could not download $file, try running with -v" unless -e $file; return; } sub md5_check() { my $file = shift; &download_file("$file.md5"); ### check MD5 sum open MD5, '-|', qw/md5sum -c/, "$file.md5" or die $!; my $sum_line = ; close MD5; unless ($sum_line =~ m/$file:\s+OK/) { die "[*] MD5 sum check failed for $file, ", "exiting."; } print "[+] Valid md5 sum check for $file\n"; unlink "$file.md5"; return; } sub build_rpm() { print "[+] Building RPM, this may take a little while (try -v if you want\n", " to see all of the steps)...\n\n"; my $cmd = "$rpmbuildCmd -ba $spec_file"; unless ($verbose) { $cmd .= ' > /dev/null 2>&1'; } system $cmd; return; } sub get_latest_version() { unlink "$project-latest" if -e "$project-latest"; print "[+] Getting latest version file:\n", " $build_url_base/$project/$project-latest\n"; my $cmd = "$wgetCmd $build_url_base/$project/$project-latest"; unless ($verbose) { $cmd .= ' > /dev/null 2>&1'; } system $cmd; open F, '<', "$project-latest" or die "[*] Could not open $project-latest: $!"; my $line = ; close F; chomp $line; $build_version = $line; die "[*] Could not get build version" unless $build_version; unlink "$project-latest" if -e "$project-latest"; return; } sub usage() { print <<_HELP_; cd_rpmbuilder; the CipherDyne RPM builder [+] Version: $version [+] By Michael Rash (mbr\@cipherdyne.org, http://www.cipherdyne.org) Usage: cd_rpmbuilder -p [options] Options: -p, --project - This can be one of "psad", "fwknop", "gpgdir", or "fwsnort". -b, --build-version - Build a specific project version. -r, --rpm-build-dir - Change the RPM build directory from the default of $rpm_root_dir. -n, --no-deps - Build the specified project without any dependencies (such as perl modules). -v, --verbose - Run in verbose mode. -V, --Version - Print version and exit. -h, --help - Display usage information. _HELP_ exit 0; } signing-party-2.12/gpgdir/packaging/gpgdir-nobuildreqs.spec000066400000000000000000000155201500571021200241020ustar00rootroot00000000000000%define name gpgdir %define version 1.9.5 %define release 1 %define gpgdirlibdir %_libdir/%name ### get the first @INC directory that includes the string "linux". ### This may be 'i386-linux', or 'i686-linux-thread-multi', etc. %define gpgdirmoddir `perl -e '$path='i386-linux'; for (@INC) { if($_ =~ m|.*/(.*linux.*)|) {$path = $1; last; }} print $path'` Summary: Gpgdir recursively encrypts/decrypts directories with GnuPG. Name: %name Version: %version Release: %release License: GPL Group: Applications/Cryptography Url: http://www.cipherdyne.org/gpgdir/ Source: %name-%version.tar.gz BuildRoot: %_tmppath/%{name}-buildroot #Prereq: rpm-helper %description gpgdir is a perl script that uses the CPAN GnuPG::Interface perl module to encrypt and decrypt directories using a gpg key specified in ~/.gpgdirrc. gpgdir recursively descends through a directory in order to make sure it encrypts or decrypts every file in a directory and all of its subdirectories. By default the mtime and atime values of all files will be preserved upon encryption and decryption (this can be disabled with the --no-preserve-times option). Note that in --encrypt mode, gpgdir will delete the original files that it successfully encrypts (unless the --no-delete option is given). However, upon startup gpgdir first asks for a the decryption pass- word to be sure that a dummy file can successfully be encrypted and decrypted. The initial test can be disabled with the --skip-test option so that a directory can eas- ily be encrypted without having to also specify a password (this is consistent with gpg behavior). Also, note that gpgdir is careful not encrypt hidden files and direc- tories. After all, you probably don't want your ~/.gnupg directory or ~/.bashrc file to be encrypted. %prep [ "$RPM_BUILD_ROOT" != "/" ] && rm -rf $RPM_BUILD_ROOT %setup -q for i in $(grep -r "use lib" . | cut -d: -f1); do awk '/use lib/ { sub("/usr/lib/gpgdir", "%_libdir/%name") } { print }' $i > $i.tmp mv $i.tmp $i done cd deps cd Class-MethodMaker && perl Makefile.PL PREFIX=%gpgdirlibdir LIB=%gpgdirlibdir cd .. cd GnuPG-Interface && perl Makefile.PL PREFIX=%gpgdirlibdir LIB=%gpgdirlibdir cd .. cd TermReadKey && perl Makefile.PL PREFIX=%gpgdirlibdir LIB=%gpgdirlibdir cd ../.. %build ### build perl modules used by gpgdir cd deps make OPTS="$RPM_OPT_FLAGS" -C Class-MethodMaker make OPTS="$RPM_OPT_FLAGS" -C GnuPG-Interface make OPTS="$RPM_OPT_FLAGS" -C TermReadKey cd .. %install ### gpgdir module dirs cd deps mkdir -p $RPM_BUILD_ROOT%gpgdirlibdir/%gpgdirmoddir/auto/Term/ReadKey mkdir -p $RPM_BUILD_ROOT%gpgdirlibdir/%gpgdirmoddir/auto/Class/MethodMaker/array mkdir -p $RPM_BUILD_ROOT%gpgdirlibdir/%gpgdirmoddir/auto/Class/MethodMaker/Engine mkdir -p $RPM_BUILD_ROOT%gpgdirlibdir/%gpgdirmoddir/auto/Class/MethodMaker/hash mkdir -p $RPM_BUILD_ROOT%gpgdirlibdir/%gpgdirmoddir/auto/Class/MethodMaker/scalar mkdir -p $RPM_BUILD_ROOT%gpgdirlibdir/%gpgdirmoddir/Class/MethodMaker mkdir -p $RPM_BUILD_ROOT%gpgdirlibdir/%gpgdirmoddir/Term mkdir -p $RPM_BUILD_ROOT%gpgdirlibdir/auto/GnuPG/Interface mkdir -p $RPM_BUILD_ROOT%gpgdirlibdir/GnuPG mkdir -p $RPM_BUILD_ROOT%_bindir mkdir -p $RPM_BUILD_ROOT%{_mandir}/man1 mkdir -p $RPM_BUILD_ROOT%_sbindir cd .. install -m 755 gpgdir $RPM_BUILD_ROOT%_bindir/ install -m 644 gpgdir.1 $RPM_BUILD_ROOT%{_mandir}/man1/ ### install perl modules used by gpgdir cd deps install -m 444 Class-MethodMaker/blib/lib/auto/Class/MethodMaker/array/*.* $RPM_BUILD_ROOT%gpgdirlibdir/%gpgdirmoddir/auto/Class/MethodMaker/array/ install -m 444 Class-MethodMaker/blib/lib/auto/Class/MethodMaker/scalar/*.* $RPM_BUILD_ROOT%gpgdirlibdir/%gpgdirmoddir/auto/Class/MethodMaker/scalar/ install -m 444 Class-MethodMaker/blib/lib/auto/Class/MethodMaker/hash/*.* $RPM_BUILD_ROOT%gpgdirlibdir/%gpgdirmoddir/auto/Class/MethodMaker/hash/ install -m 444 Class-MethodMaker/blib/lib/auto/Class/MethodMaker/Engine/*.* $RPM_BUILD_ROOT%gpgdirlibdir/%gpgdirmoddir/auto/Class/MethodMaker/Engine/ install -m 444 Class-MethodMaker/blib/arch/auto/Class/MethodMaker/MethodMaker.bs $RPM_BUILD_ROOT%gpgdirlibdir/%gpgdirmoddir/auto/Class/MethodMaker/MethodMaker.bs install -m 444 Class-MethodMaker/blib/arch/auto/Class/MethodMaker/MethodMaker.so $RPM_BUILD_ROOT%gpgdirlibdir/%gpgdirmoddir/auto/Class/MethodMaker/MethodMaker.so install -m 444 Class-MethodMaker/blib/lib/Class/MethodMaker.pm $RPM_BUILD_ROOT%gpgdirlibdir/%gpgdirmoddir/Class/MethodMaker.pm install -m 444 Class-MethodMaker/blib/lib/Class/MethodMaker/*.pm $RPM_BUILD_ROOT%gpgdirlibdir/%gpgdirmoddir/Class/MethodMaker install -m 444 GnuPG-Interface/blib/lib/auto/GnuPG/Interface/*.* $RPM_BUILD_ROOT%gpgdirlibdir/auto/GnuPG/Interface/ install -m 444 GnuPG-Interface/blib/lib/GnuPG/*.pm $RPM_BUILD_ROOT%gpgdirlibdir/GnuPG/ install -m 444 TermReadKey/blib/lib/Term/ReadKey.pm $RPM_BUILD_ROOT%gpgdirlibdir/%gpgdirmoddir/Term/ReadKey.pm install -m 444 TermReadKey/blib/lib/auto/Term/ReadKey/autosplit.ix $RPM_BUILD_ROOT%gpgdirlibdir/%gpgdirmoddir/auto/Term/ReadKey/autosplit.ix install -m 444 TermReadKey/blib/arch/auto/Term/ReadKey/ReadKey.bs $RPM_BUILD_ROOT%gpgdirlibdir/%gpgdirmoddir/auto/Term/ReadKey/ReadKey.bs install -m 444 TermReadKey/blib/arch/auto/Term/ReadKey/ReadKey.so $RPM_BUILD_ROOT%gpgdirlibdir/%gpgdirmoddir/auto/Term/ReadKey/ReadKey.so cd .. %clean [ "$RPM_BUILD_ROOT" != "/" ] && rm -rf $RPM_BUILD_ROOT %pre %post %preun %files %defattr(-,root,root) %_bindir/* %{_mandir}/man1/* %_libdir/%name %changelog * Sat Sep 05 2009 Michael Rash - gpgdir-1.9.5 release * Thu Feb 12 2009 Michael Rash - gpgdir-1.9.4 release * Wed Nov 11 2008 Michael Rash - gpgdir-1.9.3 release * Sun Aug 31 2008 Michael Rash - Updated to use the deps/ directory for all perl module sources. - gpgdir-1.9.2 release * Sat Jun 07 2008 Michael Rash - gpgdir-1.9.1 release * Sat May 31 2008 Michael Rash - gpgdir-1.9 release * Mon Feb 18 2008 Michael Rash - gpgdir-1.8 release * Mon Feb 18 2008 Michael Rash - gpgdir-1.7 release * Sun Feb 17 2008 Michael Rash - gpgdir-1.6 release * Fri Aug 31 2007 Michael Rash - gpgdir-1.5 release * Sat Jul 20 2007 Michael Rash - gpgdir-1.4 release * Sat Jun 09 2007 Michael Rash - gpgdir-1.3 release * Mon May 28 2007 Michael Rash - gpgdir-1.2 release * Mon May 21 2007 Michael Rash - gpgdir-1.1 release * Sun Sep 17 2006 Michael Rash - gpgdir-1.0.3 release (1.0.2 was skipped accidentally). * Sat Sep 16 2006 Michael Rash - Added x86_64 RPM. - Removed iptables as a prerequisite. - gpgdir-1.0.1 release * Wed Sep 13 2006 Michael Rash - gpgdir-1.0 release * Thu Sep 09 2006 Michael Rash - Initial RPM release of gpgdir-0.9.9 signing-party-2.12/gpgdir/packaging/gpgdir-nodeps.spec000066400000000000000000000065001500571021200230410ustar00rootroot00000000000000%define name gpgdir %define version 1.9.5 %define release 1 %define gpgdirlibdir %_libdir/%name Summary: Gpgdir recursively encrypts/decrypts directories with GnuPG. Name: %name Version: %version Release: %release License: GPL Group: Applications/Cryptography Url: http://www.cipherdyne.org/gpgdir/ Source: %name-%version.tar.gz BuildRoot: %_tmppath/%{name}-buildroot #Prereq: rpm-helper %description gpgdir is a perl script that uses the CPAN GnuPG::Interface perl module to encrypt and decrypt directories using a gpg key specified in ~/.gpgdirrc. gpgdir recursively descends through a directory in order to make sure it encrypts or decrypts every file in a directory and all of its subdirectories. By default the mtime and atime values of all files will be preserved upon encryption and decryption (this can be disabled with the --no-preserve-times option). Note that in --encrypt mode, gpgdir will delete the original files that it successfully encrypts (unless the --no-delete option is given). However, upon startup gpgdir first asks for a the decryption pass- word to be sure that a dummy file can successfully be encrypted and decrypted. The initial test can be disabled with the --skip-test option so that a directory can eas- ily be encrypted without having to also specify a password (this is consistent with gpg behavior). Also, note that gpgdir is careful not encrypt hidden files and direc- tories. After all, you probably don't want your ~/.gnupg directory or ~/.bashrc file to be encrypted. %prep [ "$RPM_BUILD_ROOT" != "/" ] && rm -rf $RPM_BUILD_ROOT %setup -q %build %install install -m 755 gpgdir $RPM_BUILD_ROOT%_bindir/ install -m 644 gpgdir.1 $RPM_BUILD_ROOT%{_mandir}/man1/ %clean [ "$RPM_BUILD_ROOT" != "/" ] && rm -rf $RPM_BUILD_ROOT %pre %post %preun %files %defattr(-,root,root) %_bindir/* %{_mandir}/man1/* %_libdir/%name %changelog * Sat Sep 05 2009 Michael Rash - gpgdir-1.9.5 release * Thu Feb 12 2009 Michael Rash - gpgdir-1.9.4 release * Wed Nov 11 2008 Michael Rash - gpgdir-1.9.3 release * Sun Aug 31 2008 Michael Rash - This spec file omits installing any perl module dependencies. - gpgdir-1.9.2 release * Sat Jun 07 2008 Michael Rash - gpgdir-1.9.1 release * Sat May 31 2008 Michael Rash - gpgdir-1.9 release * Mon Feb 18 2008 Michael Rash - gpgdir-1.8 release * Mon Feb 18 2008 Michael Rash - gpgdir-1.7 release * Sun Feb 17 2008 Michael Rash - gpgdir-1.6 release * Fri Aug 31 2007 Michael Rash - gpgdir-1.5 release * Sat Jul 20 2007 Michael Rash - gpgdir-1.4 release * Sat Jun 09 2007 Michael Rash - gpgdir-1.3 release * Mon May 28 2007 Michael Rash - gpgdir-1.2 release * Mon May 21 2007 Michael Rash - gpgdir-1.1 release * Sun Sep 17 2006 Michael Rash - gpgdir-1.0.3 release (1.0.2 was skipped accidentally). * Sat Sep 16 2006 Michael Rash - Added x86_64 RPM. - Removed iptables as a prerequisite. - gpgdir-1.0.1 release * Wed Sep 13 2006 Michael Rash - gpgdir-1.0 release * Thu Sep 09 2006 Michael Rash - Initial RPM release of gpgdir-0.9.9 signing-party-2.12/gpgdir/packaging/gpgdir.SlackBuild000077500000000000000000000025421500571021200226430ustar00rootroot00000000000000#!/bin/bash ################################################################################ # gpgdir.SlackBuild -- pyllyukko@maimed.org -- 26.1.2007 (originally for psad) # ################################################################################ declare -r RPM_BUILDER="http://www.cipherdyne.org/scripts/cd_rpmbuilder.tar.gz" declare -r RPM_ROOT_DIR="/usr/src/rpm" declare -r ARCH="i386" declare -ri BUILD=1 GPGDIR_VERSION=`wget --no-verbose --output-document=- http://www.cipherdyne.org/gpgdir/gpgdir-latest` || { echo "error!" 1>&2 exit 1 } ################################################################################ wget --no-verbose --output-document=- "${RPM_BUILDER}" | tar xz --to-stdout | perl -- - -p gpgdir -r "${RPM_ROOT_DIR}" [ $[ ${PIPESTATUS[0]} | ${PIPESTATUS[1]} | ${PIPESTATUS[2]} ] -ne 0 ] && { echo "error!" 1>&2 exit 1 } [ ! -f "${RPM_ROOT_DIR}/RPMS/${ARCH}/gpgdir-${GPGDIR_VERSION}-${BUILD}.${ARCH}.rpm" ] && { echo "error: file \`gpgdir-${GPGDIR_VERSION}-${BUILD}.${ARCH}.rpm' doesn't exist!" 1>&2 exit 1 } pushd "${RPM_ROOT_DIR}/RPMS/${ARCH}" || exit 1 rpm2tgz "gpgdir-${GPGDIR_VERSION}-${BUILD}.${ARCH}.rpm" || exit 1 mv -v "gpgdir-${GPGDIR_VERSION}-${BUILD}.${ARCH}.tgz" "gpgdir-${GPGDIR_VERSION}-${ARCH}-${BUILD}.tgz" || exit 1 ls -l "${RPM_ROOT_DIR}/RPMS/${ARCH}/gpgdir-${GPGDIR_VERSION}-${ARCH}-${BUILD}.tgz" exit ${?} signing-party-2.12/gpgdir/packaging/gpgdir.spec000066400000000000000000000155671500571021200215700ustar00rootroot00000000000000%define name gpgdir %define version 1.9.5 %define release 1 %define gpgdirlibdir %_libdir/%name ### get the first @INC directory that includes the string "linux". ### This may be 'i386-linux', or 'i686-linux-thread-multi', etc. %define gpgdirmoddir `perl -e '$path='i386-linux'; for (@INC) { if($_ =~ m|.*/(.*linux.*)|) {$path = $1; last; }} print $path'` Summary: Gpgdir recursively encrypts/decrypts directories with GnuPG. Name: %name Version: %version Release: %release License: GPL Group: Applications/Cryptography Url: http://www.cipherdyne.org/gpgdir/ Source: %name-%version.tar.gz BuildRoot: %_tmppath/%{name}-buildroot BuildRequires: perl-ExtUtils-MakeMaker #Prereq: rpm-helper %description gpgdir is a perl script that uses the CPAN GnuPG::Interface perl module to encrypt and decrypt directories using a gpg key specified in ~/.gpgdirrc. gpgdir recursively descends through a directory in order to make sure it encrypts or decrypts every file in a directory and all of its subdirectories. By default the mtime and atime values of all files will be preserved upon encryption and decryption (this can be disabled with the --no-preserve-times option). Note that in --encrypt mode, gpgdir will delete the original files that it successfully encrypts (unless the --no-delete option is given). However, upon startup gpgdir first asks for a the decryption pass- word to be sure that a dummy file can successfully be encrypted and decrypted. The initial test can be disabled with the --skip-test option so that a directory can eas- ily be encrypted without having to also specify a password (this is consistent with gpg behavior). Also, note that gpgdir is careful not encrypt hidden files and direc- tories. After all, you probably don't want your ~/.gnupg directory or ~/.bashrc file to be encrypted. %prep [ "$RPM_BUILD_ROOT" != "/" ] && rm -rf $RPM_BUILD_ROOT %setup -q for i in $(grep -r "use lib" . | cut -d: -f1); do awk '/use lib/ { sub("/usr/lib/gpgdir", "%_libdir/%name") } { print }' $i > $i.tmp mv $i.tmp $i done cd deps cd Class-MethodMaker && perl Makefile.PL PREFIX=%gpgdirlibdir LIB=%gpgdirlibdir cd .. cd GnuPG-Interface && perl Makefile.PL PREFIX=%gpgdirlibdir LIB=%gpgdirlibdir cd .. cd TermReadKey && perl Makefile.PL PREFIX=%gpgdirlibdir LIB=%gpgdirlibdir cd ../.. %build ### build perl modules used by gpgdir cd deps make OPTS="$RPM_OPT_FLAGS" -C Class-MethodMaker make OPTS="$RPM_OPT_FLAGS" -C GnuPG-Interface make OPTS="$RPM_OPT_FLAGS" -C TermReadKey cd .. %install ### gpgdir module dirs cd deps mkdir -p $RPM_BUILD_ROOT%gpgdirlibdir/%gpgdirmoddir/auto/Term/ReadKey mkdir -p $RPM_BUILD_ROOT%gpgdirlibdir/%gpgdirmoddir/auto/Class/MethodMaker/array mkdir -p $RPM_BUILD_ROOT%gpgdirlibdir/%gpgdirmoddir/auto/Class/MethodMaker/Engine mkdir -p $RPM_BUILD_ROOT%gpgdirlibdir/%gpgdirmoddir/auto/Class/MethodMaker/hash mkdir -p $RPM_BUILD_ROOT%gpgdirlibdir/%gpgdirmoddir/auto/Class/MethodMaker/scalar mkdir -p $RPM_BUILD_ROOT%gpgdirlibdir/%gpgdirmoddir/Class/MethodMaker mkdir -p $RPM_BUILD_ROOT%gpgdirlibdir/%gpgdirmoddir/Term mkdir -p $RPM_BUILD_ROOT%gpgdirlibdir/auto/GnuPG/Interface mkdir -p $RPM_BUILD_ROOT%gpgdirlibdir/GnuPG mkdir -p $RPM_BUILD_ROOT%_bindir mkdir -p $RPM_BUILD_ROOT%{_mandir}/man1 mkdir -p $RPM_BUILD_ROOT%_sbindir cd .. install -m 755 gpgdir $RPM_BUILD_ROOT%_bindir/ install -m 644 gpgdir.1 $RPM_BUILD_ROOT%{_mandir}/man1/ ### install perl modules used by gpgdir cd deps install -m 444 Class-MethodMaker/blib/lib/auto/Class/MethodMaker/array/*.* $RPM_BUILD_ROOT%gpgdirlibdir/%gpgdirmoddir/auto/Class/MethodMaker/array/ install -m 444 Class-MethodMaker/blib/lib/auto/Class/MethodMaker/scalar/*.* $RPM_BUILD_ROOT%gpgdirlibdir/%gpgdirmoddir/auto/Class/MethodMaker/scalar/ install -m 444 Class-MethodMaker/blib/lib/auto/Class/MethodMaker/hash/*.* $RPM_BUILD_ROOT%gpgdirlibdir/%gpgdirmoddir/auto/Class/MethodMaker/hash/ install -m 444 Class-MethodMaker/blib/lib/auto/Class/MethodMaker/Engine/*.* $RPM_BUILD_ROOT%gpgdirlibdir/%gpgdirmoddir/auto/Class/MethodMaker/Engine/ install -m 444 Class-MethodMaker/blib/arch/auto/Class/MethodMaker/MethodMaker.bs $RPM_BUILD_ROOT%gpgdirlibdir/%gpgdirmoddir/auto/Class/MethodMaker/MethodMaker.bs install -m 444 Class-MethodMaker/blib/arch/auto/Class/MethodMaker/MethodMaker.so $RPM_BUILD_ROOT%gpgdirlibdir/%gpgdirmoddir/auto/Class/MethodMaker/MethodMaker.so install -m 444 Class-MethodMaker/blib/lib/Class/MethodMaker.pm $RPM_BUILD_ROOT%gpgdirlibdir/%gpgdirmoddir/Class/MethodMaker.pm install -m 444 Class-MethodMaker/blib/lib/Class/MethodMaker/*.pm $RPM_BUILD_ROOT%gpgdirlibdir/%gpgdirmoddir/Class/MethodMaker install -m 444 GnuPG-Interface/blib/lib/auto/GnuPG/Interface/*.* $RPM_BUILD_ROOT%gpgdirlibdir/auto/GnuPG/Interface/ install -m 444 GnuPG-Interface/blib/lib/GnuPG/*.pm $RPM_BUILD_ROOT%gpgdirlibdir/GnuPG/ install -m 444 TermReadKey/blib/lib/Term/ReadKey.pm $RPM_BUILD_ROOT%gpgdirlibdir/%gpgdirmoddir/Term/ReadKey.pm install -m 444 TermReadKey/blib/lib/auto/Term/ReadKey/autosplit.ix $RPM_BUILD_ROOT%gpgdirlibdir/%gpgdirmoddir/auto/Term/ReadKey/autosplit.ix install -m 444 TermReadKey/blib/arch/auto/Term/ReadKey/ReadKey.bs $RPM_BUILD_ROOT%gpgdirlibdir/%gpgdirmoddir/auto/Term/ReadKey/ReadKey.bs install -m 444 TermReadKey/blib/arch/auto/Term/ReadKey/ReadKey.so $RPM_BUILD_ROOT%gpgdirlibdir/%gpgdirmoddir/auto/Term/ReadKey/ReadKey.so cd .. %clean [ "$RPM_BUILD_ROOT" != "/" ] && rm -rf $RPM_BUILD_ROOT %pre %post %preun %files %defattr(-,root,root) %_bindir/* %{_mandir}/man1/* %_libdir/%name %changelog * Sat Sep 05 2009 Michael Rash - gpgdir-1.9.5 release * Thu Feb 12 2009 Michael Rash - gpgdir-1.9.4 release * Wed Nov 11 2008 Michael Rash - gpgdir-1.9.3 release * Sun Aug 31 2008 Michael Rash - Updated to use the deps/ directory for all perl module sources. - gpgdir-1.9.2 release * Sat Jun 07 2008 Michael Rash - gpgdir-1.9.1 release * Sat May 31 2008 Michael Rash - gpgdir-1.9 release * Mon Feb 18 2008 Michael Rash - gpgdir-1.8 release * Mon Feb 18 2008 Michael Rash - gpgdir-1.7 release * Sun Feb 17 2008 Michael Rash - gpgdir-1.6 release * Fri Aug 31 2007 Michael Rash - gpgdir-1.5 release * Sat Jul 20 2007 Michael Rash - gpgdir-1.4 release * Sat Jun 09 2007 Michael Rash - gpgdir-1.3 release * Mon May 28 2007 Michael Rash - gpgdir-1.2 release * Mon May 21 2007 Michael Rash - gpgdir-1.1 release * Sun Sep 17 2006 Michael Rash - gpgdir-1.0.3 release (1.0.2 was skipped accidentally). * Sat Sep 16 2006 Michael Rash - Added x86_64 RPM. - Removed iptables as a prerequisite. - gpgdir-1.0.1 release * Wed Sep 13 2006 Michael Rash - gpgdir-1.0 release * Thu Sep 09 2006 Michael Rash - Initial RPM release of gpgdir-0.9.9 signing-party-2.12/gpgdir/test/000077500000000000000000000000001500571021200164555ustar00rootroot00000000000000signing-party-2.12/gpgdir/test/conf/000077500000000000000000000000001500571021200174025ustar00rootroot00000000000000signing-party-2.12/gpgdir/test/conf/broken.pw000066400000000000000000000000161500571021200212270ustar00rootroot00000000000000boguspassword signing-party-2.12/gpgdir/test/conf/test-gpg/000077500000000000000000000000001500571021200211345ustar00rootroot00000000000000signing-party-2.12/gpgdir/test/conf/test-gpg/pubring.gpg000066400000000000000000000022531500571021200233030ustar00rootroot00000000000000G~N/!lUjgQ/qR')yX +M&ma%[$Ds*்+9[LJ=|%s>#SjpxrIײ)BF#Ƴ;)1<${̟[{S?"1v)%9h!Sy>#c;E?rĢKtdiVu.'؅(כTllPG}ej֔\;ѧw f:Of47+N=VPEQ&_>8Mdy{&FQ8nBVҿωw 힠3No{_Kcu?m]?gpgdir (gpgdir test key; DO NOT USE for production deployments)` G   7]}@٪Uh"+e?pS2αk)t^ G+zKg~;6IFŔ} JBQ>-XX&2rX^2xfk:d!ǚbۃ{ٴA uvyeVV1BYj- .èVqȴd8dq׊-%x'ǔ KBoR`;C1[]f.1nS%]xLL>%iUQ6 Jjg?cXzIi_<q2uWbV9zїlv7w36'맩I*O" ~jC?C~YG\ydgin:?SQ^P-a"~hAl VD|.Cw7^бB=8޲in\dF;}RƪYݞӌ:#%b&G0:v!dS|)6s8=)UM 'yJ1UT97IzήK"AK5Vox!u]]Qk#SjpxrIײ)BF#Ƴ;)1<${̟[{S?"1v)%9h!Sy>#c;E?rĢKtdiVu.'؅(כTllPG}ej֔\;ѧw f:Of47+N=VPEQ&_>8Mdy{&FQ8nBVҿωw 힠3No{_Kcu?m]-`D5)05Ý ~~ߒl`-%`0W`R";.Tzδ?gpgdir (gpgdir test key; DO NOT USE for production deployments)` G   7]}@٪Uh"+e?pS2αk)t^cG+zKg~;6IFŔ} JBQ>-XX&2rX^2xfk:d!ǚbۃ{ٴA uvyeVV1BYj- .èVqȴd8dq׊-%x'ǔ KBoR`;C1[]f.1nS%]xLL>%iUQ6 Jjg?cXzIi_<q2uWbV9zїlv7w36'맩I*O" ~jC?C~YG\ydgin:?SQ^P-a"~hAl VD|.Cw7^бB=8޲in\dF;}RƪYݞӌ:#%b&G0:v!dS|)6s8=)UM 'yJ1UT97IzήK"AK5Vox!u]]QkV2qi{signing-party-2.12/gpgdir/test/conf/test-gpg/trustdb.gpg000066400000000000000000000024001500571021200233160ustar00rootroot00000000000000gpgG  oL0;.6 7]} SV&p $ ؅signing-party-2.12/gpgdir/test/conf/test.pw000066400000000000000000000000131500571021200207230ustar00rootroot00000000000000gpgdirtest signing-party-2.12/gpgdir/test/data-dir/000077500000000000000000000000001500571021200201425ustar00rootroot00000000000000signing-party-2.12/gpgdir/test/data-dir/.hidden000066400000000000000000000000001500571021200213640ustar00rootroot00000000000000signing-party-2.12/gpgdir/test/data-dir/.hidden-dir/000077500000000000000000000000001500571021200222275ustar00rootroot00000000000000signing-party-2.12/gpgdir/test/data-dir/.hidden-dir/git-svn_doesnt_like_empty_dirs000066400000000000000000000000001500571021200303460ustar00rootroot00000000000000signing-party-2.12/gpgdir/test/data-dir/dir2/000077500000000000000000000000001500571021200210025ustar00rootroot00000000000000signing-party-2.12/gpgdir/test/data-dir/dir2/.hidden000066400000000000000000000000001500571021200222240ustar00rootroot00000000000000signing-party-2.12/gpgdir/test/data-dir/dir2/dir4/000077500000000000000000000000001500571021200216445ustar00rootroot00000000000000signing-party-2.12/gpgdir/test/data-dir/dir2/dir4/.hidden000066400000000000000000000000001500571021200230660ustar00rootroot00000000000000signing-party-2.12/gpgdir/test/data-dir/dir2/dir4/somefile000066400000000000000000000003211500571021200233660ustar00rootroot00000000000000This is a file that contains multiple lines of ascii text, but there is no file extension on this one. gppdir should encrypt this file under the test suite. This file is in the top-level data-dir directory. signing-party-2.12/gpgdir/test/data-dir/dir2/dir4/somefile.txt000066400000000000000000000003651500571021200242140ustar00rootroot00000000000000This is a file that contains multiple lines of ascii text, and this file has a .txt extension (which gpgdir should handle without issues). gppdir should encrypt this file under the test suite. This file is in the top-level data-dir directory. signing-party-2.12/gpgdir/test/data-dir/dir2/new-ascii000066400000000000000000000003211500571021200226000ustar00rootroot00000000000000This is a file that contains multiple lines of ascii text, but there is no file extension on this one. gppdir should encrypt this file under the test suite. This file is in the top-level data-dir directory. signing-party-2.12/gpgdir/test/data-dir/dir2/new-ascii.txt000066400000000000000000000003651500571021200234260ustar00rootroot00000000000000This is a file that contains multiple lines of ascii text, and this file has a .txt extension (which gpgdir should handle without issues). gppdir should encrypt this file under the test suite. This file is in the top-level data-dir directory. signing-party-2.12/gpgdir/test/data-dir/dir3/000077500000000000000000000000001500571021200210035ustar00rootroot00000000000000signing-party-2.12/gpgdir/test/data-dir/dir3/.hidden000066400000000000000000000000001500571021200222250ustar00rootroot00000000000000signing-party-2.12/gpgdir/test/data-dir/dir3/dir4/000077500000000000000000000000001500571021200216455ustar00rootroot00000000000000signing-party-2.12/gpgdir/test/data-dir/dir3/dir4/.hidden000066400000000000000000000000001500571021200230670ustar00rootroot00000000000000signing-party-2.12/gpgdir/test/data-dir/dir3/dir4/gpgdir-copy000077500000000000000000001201071500571021200240200ustar00rootroot00000000000000#!/usr/bin/perl -w # ########################################################################### # # File: gpgdir # # URL: http://www.cipherdyne.org/gpgdir/ # # Purpose: To encrypt/decrypt whole directories # # Author: Michael Rash (mbr@cipherdyne.com) # # Version: 1.7 # # Copyright (C) 2002-2007 Michael Rash (mbr@cipherdyne.org) # # License (GNU General Public License): # # 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, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 # USA # ########################################################################### # # $Id: gpgdir 246 2008-02-18 14:29:16Z mbr $ # use lib '/usr/lib/gpgdir'; use File::Find; use File::Copy; use Term::ReadKey; use GnuPG::Interface; use IO::File; use IO::Handle; use Getopt::Long; use Cwd; use strict; ### set the current gpgdir version and file revision numbers my $version = '1.7'; my $revision_svn = '$Revision: 246 $'; my $rev_num = '1'; ($rev_num) = $revision_svn =~ m|\$Rev.*:\s+(\S+)|; ### establish some defaults my $encrypt_user = ''; my $gpg_homedir = ''; my $dir = ''; my $pw = ''; my $encrypt_dir = ''; my $decrypt_dir = ''; my $homedir = ''; my $exclude_pat = ''; my $exclude_file = ''; my $include_pat = ''; my $include_file = ''; my $total_encrypted = 0; my $total_decrypted = 0; my $norecurse = 0; my $printver = 0; my $no_delete = 0; my $no_fs_times = 0; my $test_and_exit = 0; my $trial_run = 0; my $skip_test_mode = 0; my $verbose = 0; my $quiet = 0; my $use_gpg_agent = 0; ### use gpg-agent for passwords my $gpg_agent_info = ''; my $force_mode = 0; my $help = 0; my $wipe_mode = 0; my $encrypt_mode = 0; my $use_default_key = 0; my $pw_file = ''; my $wipe_cmd = '/usr/bin/wipe'; my $wipe_cmdline = ''; my $wipe_interactive = 0; my $interactive_mode = 0; my $ascii_armor_mode = 0; my @exclude_patterns = (); my @include_patterns = (); my %files = (); my %options = (); my %obfuscate_ctrs = (); my %obfuscated_dirs = (); my $have_obfuscated_file = 0; my $cmdline_no_password = 0; my $obfuscate_mode = 0; my $obfuscate_map_filename = '.gpgdir_map_file'; my $overwrite_encrypted = 0; my $overwrite_decrypted = 0; my $symmetric_mode = 0; my $DEL_SOURCE_FILE = 1; my $NO_DEL_SOURCE_FILE = 0; ### for user answers my $ACCEPT_YES_DEFAULT = 1; my $ACCEPT_NO_DEFAULT = 2; unless ($< == $>) { die "[*] Real and effective uid must be the same. Make sure\n", " gpgdir has not been installed as a SUID binary.\n", "Exiting."; } my @args_cp = @ARGV; ### make Getopts case sensitive Getopt::Long::Configure('no_ignore_case'); die "[-] Use --help for usage information.\n" unless(GetOptions ( 'encrypt=s' => \$encrypt_dir, # Encrypt files in this directory. 'decrypt=s' => \$decrypt_dir, # Decrypt files in this directory. 'gnupg-dir=s' => \$gpg_homedir, # Path to /path/to/.gnupg directory. 'pw-file=s' => \$pw_file, # Read password out of this file. 'agent' => \$use_gpg_agent, # Use gpg-agent for passwords. 'Agent-info=s' => \$gpg_agent_info, # Specify GnuPG agent connection # information. 'Wipe' => \$wipe_mode, # Securely delete unencrypted files. 'wipe-path=s' => \$wipe_cmd, # Path to wipe command. 'wipe-interactive' => \$wipe_interactive, # Disable "wipe -I" 'wipe-cmdline=s' => \$wipe_cmdline, # Specify wipe command line. 'Obfuscate-filenames' => \$obfuscate_mode, # substitute real filenames # with manufactured ones. 'obfuscate-map-file=s' => \$obfuscate_map_filename, # path to mapping file. 'Force' => \$force_mode, # Continue if files can't be deleted. 'overwrite-encrypted' => \$overwrite_encrypted, # Overwrite encrypted files # even if they exist. 'overwrite-decrypted' => \$overwrite_decrypted, # Overwrite decrypted files # even if they exist. 'Exclude=s' => \$exclude_pat, # Exclude a pattern from encrypt/decrypt # cycle. 'Exclude-from=s' => \$exclude_file, # Exclude patterns in from # encrypt decrypt cycle. 'Include=s' => \$include_pat, # Specify a pattern used to restrict # encrypt/decrypt operation to. 'Include-from=s' => \$include_file, # Specify a file of include patterns to # restrict all encrypt/decrypt # operations to. 'test-mode' => \$test_and_exit, # Run encrypt -> decrypt test only and # exit. 'Trial-run' => \$trial_run, # Don't modify any files; just show what # would have happened. 'quiet' => \$quiet, # Print as little as possible to # stdout. 'Interactive' => \$interactive_mode, # Query the user before encrypting/ # decrypting/deleting any files. 'Key-id=s' => \$encrypt_user, # Specify encrypt/decrypt key 'Default-key' => \$use_default_key, # Assume that default-key is set within # ~/.gnupg/options. 'Symmetric' => \$symmetric_mode, # encrypt using symmetric cipher. # (this option is not required to # also decrypt, GnuPG handles # that automatically). 'Plain-ascii' => \$ascii_armor_mode, # Ascii armor mode (creates non-binary # encrypted files). 'skip-test' => \$skip_test_mode, # Skip encrypt -> decrypt test. 'no-recurse' => \$norecurse, # Don't encrypt/decrypt files in # subdirectories. 'no-delete' => \$no_delete, # Don't delete files once they have # been encrypted. 'no-password' => \$cmdline_no_password, # Do not query for a password (only # useful for when the gpg literally # has no password). 'user-homedir=s' => \$homedir, # Path to home directory. 'no-preserve-times' => \$no_fs_times, # Don't preserve mtimes or atimes. 'verbose' => \$verbose, # Verbose mode. 'Version' => \$printver, # Print version 'help' => \$help # Print help )); &usage_and_exit() if $help; print "[+] gpgdir v$version (file revision: $rev_num)\n", " by Michael Rash \n" and exit 0 if $printver; if ($symmetric_mode and ($use_gpg_agent or $gpg_agent_info)) { die "[*] gpg-agent incompatible with --Symmetric mode"; } if ($encrypt_dir and $overwrite_decrypted) { die "[*] The -e and --overwrite-decrypted options are incompatible."; } if ($decrypt_dir and $overwrite_encrypted) { die "[*] The -d and --overwrite-encrypted options are incompatible."; } if ($wipe_mode) { unless (-e $wipe_cmd) { die "[*] Can't find wipe command at: $wipe_cmd,\n", " use --wipe-path to specify path."; } unless (-e $wipe_cmd) { die "[*] Can't execute $wipe_cmd"; } } ### build up GnuPG options hash if ($verbose) { %options = ('homedir' => $gpg_homedir); } else { %options = ( 'batch' => 1, 'homedir' => $gpg_homedir ); } $options{'armor'} = 1 if $ascii_armor_mode; ### get the path to the user's home directory $homedir = &get_homedir() unless $homedir; unless ($symmetric_mode) { if ($gpg_homedir) { ### specified on the command line with --gnupg-dir unless ($gpg_homedir =~ /\.gnupg$/) { die "[*] Must specify the path to a user .gnupg directory ", "e.g. /home/username/.gnupg\n"; } } else { if (-d "${homedir}/.gnupg") { $gpg_homedir = "${homedir}/.gnupg"; } } unless (-d $gpg_homedir) { die "[*] GnuPG directory: ${homedir}/.gnupg does not exist. Please\n", " create it by executing: \"gpg --gen-key\". Exiting.\n"; } ### get the key identifier from ~/.gnupg $encrypt_user = &get_key() unless $encrypt_user or $use_default_key; } if ($decrypt_dir and $encrypt_dir) { die "[*] You cannot encrypt and decrypt the same directory.\n"; &usage_and_exit(); } unless ($decrypt_dir or $encrypt_dir or $test_and_exit) { print "[*] Please specify -e , -d , or --test-mode\n"; &usage_and_exit(); } ### exclude file pattern push @exclude_patterns, $exclude_pat if $exclude_pat; if ($exclude_file) { open P, '<', $exclude_file or die "[*] Could not open file: $exclude_file"; my @lines =

; close P; for my $line (@lines) { next unless $line =~ /\S/; chomp $line; push @exclude_patterns, qr{$line}; } } ### include file pattern push @include_patterns, $include_pat if $include_pat; if ($include_file) { open P, '<', $include_file or die "[*] Could not open file: $include_file"; my @lines =

; close P; for my $line (@lines) { next unless $line =~ /\S/; chomp $line; push @include_patterns, qr{$line}; } } if ($encrypt_dir) { $dir = $encrypt_dir; $encrypt_mode = 1; } elsif ($decrypt_dir) { $dir = $decrypt_dir; $encrypt_mode = 0; } if ($dir) { die "[*] Directory does not exist: $dir" unless -e $dir; die "[*] Not a directory: $dir" unless -d $dir; } ### don't need to test encrypt/decrypt ability if we are running ### in --Trial-run mode. $skip_test_mode = 1 if $trial_run; my $initial_dir = cwd or die "[*] Could not get CWD: $!"; if ($symmetric_mode) { &get_password(); } else { &get_password() unless $encrypt_mode and $skip_test_mode; } if ($dir eq '.') { $dir = $initial_dir; } elsif ($dir !~ m|^/|) { $dir = $initial_dir . '/' . $dir; } $dir =~ s|/$||; ### remove any trailing slash ### run a test to make sure gpgdir and encrypt and decrypt a file unless ($skip_test_mode) { my $rv = &test_mode(); exit $rv if $test_and_exit; } if ($encrypt_mode) { print "[+] Encrypting directory: $dir\n" unless $quiet; } else { print "[+] Decrypting directory: $dir\n" unless $quiet; } ### build a hash of file paths to work against &get_files($dir); ### perform the gpg operation (encrypt/decrypt) &gpg_operation(); &obfuscated_mapping_files() if $obfuscate_mode; unless ($obfuscate_mode) { if ($have_obfuscated_file) { print "[-] Obfuscated filenames detected, try decrypting with -O.\n" unless $quiet; } } if ($encrypt_mode) { print "[+] Total number of files encrypted: " . "$total_encrypted\n" unless $quiet; } else { print "[+] Total number of files decrypted: " . "$total_decrypted\n" unless $quiet; } exit 0; #==================== end main ===================== sub encrypt_file() { my ($in_file, $out_file, $del_flag) = @_; my $gpg = GnuPG::Interface->new(); $gpg->options->hash_init(%options); die "[*] Could not create new gpg object with ", "homedir: $gpg_homedir" unless $gpg; unless ($symmetric_mode or $use_default_key) { $gpg->options->default_key($encrypt_user); $gpg->options->push_recipients($encrypt_user); } my ($input_fh, $output_fh, $error_fh, $pw_fh, $status_fh) = (IO::File->new($in_file), IO::File->new("> $out_file"), IO::Handle->new(), IO::Handle->new(), IO::Handle->new()); my $handles = GnuPG::Handles->new( stdin => $input_fh, stdout => $output_fh, stderr => $error_fh, passphrase => $pw_fh, status => $status_fh ); $handles->options('stdin')->{'direct'} = 1; $handles->options('stdout')->{'direct'} = 1; my $pid; if ($use_gpg_agent or $gpg_agent_info) { ### set environment explicitly if --Agent was specified if ($gpg_agent_info) { $ENV{'GPG_AGENT_INFO'} = $gpg_agent_info; } $pid = $gpg->encrypt('handles' => $handles, 'command_args' => [ qw( --use-agent ) ]); } else { if ($symmetric_mode) { $pid = $gpg->encrypt_symmetrically('handles' => $handles); } else { $pid = $gpg->encrypt('handles' => $handles); } } print $pw_fh $pw; close $pw_fh; my @errors = <$error_fh>; if ($verbose) { print for @errors; } else { for (@errors) { print if /bad\s+pass/; } } close $input_fh; close $output_fh; close $error_fh; close $status_fh; waitpid $pid, 0; if (-s $out_file == 0) { &delete_file($out_file); &delete_file($in_file) if $del_flag == $DEL_SOURCE_FILE; if ($use_gpg_agent) { die "[*] Created zero-size file: $out_file\n", " Maybe gpg-agent does not yet have the password for that key?\n", " Try re-running with -v."; } else { die "[*] Created zero-size file: $out_file\n", " Bad password? Try re-running with -v."; } } return; } sub decrypt_file() { my ($in_file, $out_file, $del_flag) = @_; my $gpg = GnuPG::Interface->new(); $gpg->options->hash_init(%options); die "[*] Could not create new gpg object with ", "homedir: $gpg_homedir" unless $gpg; unless ($symmetric_mode or $use_default_key) { $gpg->options->default_key($encrypt_user); $gpg->options->push_recipients($encrypt_user); } my ($input_fh, $output_fh, $error_fh, $pw_fh, $status_fh) = (IO::File->new($in_file), IO::File->new("> $out_file"), IO::Handle->new(), IO::Handle->new(), IO::Handle->new()); my $handles = GnuPG::Handles->new( stdin => $input_fh, stdout => $output_fh, stderr => $error_fh, passphrase => $pw_fh, status => $status_fh ); $handles->options('stdin')->{'direct'} = 1; $handles->options('stdout')->{'direct'} = 1; my $pid; if ($use_gpg_agent) { $pid = $gpg->decrypt('handles' => $handles, 'command_args' => [ qw( --use-agent ) ]); } else { $pid = $gpg->decrypt('handles' => $handles); } print $pw_fh $pw; close $pw_fh; my @errors = <$error_fh>; if ($verbose) { print for @errors; } else { for (@errors) { print if /bad\s+pass/; } } close $input_fh; close $output_fh; close $error_fh; close $status_fh; waitpid $pid, 0; if (-s $out_file == 0) { &delete_file($out_file); &delete_file($in_file) if $del_flag == $DEL_SOURCE_FILE; if ($use_gpg_agent) { die "[*] Created zero-size file: $out_file\n", " Maybe gpg-agent does not yet have the password for that key?\n", " Try re-running with -v."; } else { die "[*] Created zero-size file: $out_file\n", " Bad password? Try re-running with -v."; } } return; } sub delete_file() { my $file = shift; return if $no_delete; return unless -e $file; if ($wipe_mode) { my $cmd = $wipe_cmd; if ($wipe_cmdline) { $cmd .= " $wipe_cmdline "; } else { if ($wipe_interactive) { $cmd .= ' -i '; } else { $cmd .= ' -I -s '; } } $cmd .= $file; if ($verbose) { print " Executing: $cmd\n"; } ### wipe the file system $cmd; } else { unlink $file; } if (-e $file) { my $msg = "[-] Could not delete file: $file\n"; if ($force_mode) { print $msg unless $quiet; } else { die $msg unless $quiet; } } return; } sub gpg_operation() { ### sort by oldest to youngest mtime FILE: for my $file (sort {$files{$a}{'mtime'} <=> $files{$b}{'mtime'}} keys %files) { ### see if we have an exclusion pattern that implies ### we should skip this file if (@exclude_patterns and &exclude_file($file)) { print "[+] Skipping excluded file: $file\n" if $verbose and not $quiet; next FILE; } ### see if we have an inclusion pattern that implies ### we should process this file if (@include_patterns and not &include_file($file)) { print "[+] Skipping non-included file: $file\n" if $verbose and not $quiet; next FILE; } ### dir is always a full path my ($dir, $filename) = ($file =~ m|(.*)/(.*)|); unless (chdir($dir)) { print "[-] Could not chdir $dir, skipping.\n" unless $quiet; next FILE; } my $mtime = $files{$file}{'mtime'}; my $atime = $files{$file}{'atime'}; if ($encrypt_mode) { my $encrypt_filename = "$filename.gpg"; if ($obfuscate_mode) { unless (defined $obfuscate_ctrs{$dir}) { ### create a new gpgdir mapping file for obfuscated file ### names, but preserve any previously encrypted file ### name mappings &handle_old_obfuscated_map_file(); ### make obfuscated file names start at 1 for each ### directory $obfuscate_ctrs{$dir} = 1; } $encrypt_filename = 'gpgdir_' . $$ . '_' . $obfuscate_ctrs{$dir} . '.gpg'; } if ($ascii_armor_mode) { $encrypt_filename = "$filename.asc"; } if (-e $encrypt_filename and not $overwrite_encrypted) { print "[-] Encrypted file $dir/$encrypt_filename already ", "exists, skipping.\n" unless $quiet; next FILE; } if ($interactive_mode) { next FILE unless (&query_yes_no( " Encrypt: $file ([y]/n)? ", $ACCEPT_YES_DEFAULT)); } print "[+] Encrypting: $file\n" unless $quiet; unless ($trial_run) { &encrypt_file($filename, $encrypt_filename, $NO_DEL_SOURCE_FILE); if (-e $encrypt_filename && -s $encrypt_filename != 0) { ### set the atime and mtime to be the same as the ### original file. unless ($no_fs_times) { if (defined $mtime and $mtime and defined $atime and $atime) { utime $atime, $mtime, $encrypt_filename; } } ### only delete the original file if ### the encrypted one exists if ($wipe_mode and not $quiet) { print " Securely deleting file: $file\n"; } &delete_file($filename); if ($obfuscate_mode) { ### record the original file name mapping &append_obfuscated_mapping($filename, $encrypt_filename); $obfuscate_ctrs{$dir}++; } $total_encrypted++; } else { print "[-] Could not encrypt file: $file\n" unless $quiet; next FILE; } } } else { ### allow filenames with spaces my $decrypt_filename = ''; if ($filename =~ /^(.+)\.gpg$/) { $decrypt_filename = $1; } elsif ($filename =~ /^(.+)\.asc$/) { $decrypt_filename = $1; } if ($obfuscate_mode) { &import_obfuscated_file_map($dir) unless defined $obfuscated_dirs{$dir}; if (defined $obfuscated_dirs{$dir}{$filename}) { $decrypt_filename = $obfuscated_dirs{$dir}{$filename}; } else { ### print "[-] Obfuscated file map does not exist for $filename in\n", " $obfuscate_map_filename, skipping.\n"; next FILE; } } else { if (not $force_mode and $file =~ /gpgdir_\d+_\d+.gpg/) { ### be careful not to decrypt obfuscated file unless we ### are running in -O mode. This ensures that the ### original file names will be acquired from the ### /some/dir/.gpgdir_map_file $have_obfuscated_file = 1; next FILE; } } ### length() allows files named "0" next FILE unless length($decrypt_filename) > 0; ### don't decrypt a file on top of a normal file of ### the same name if (-e $decrypt_filename and not $overwrite_decrypted) { print "[-] Decrypted file $dir/$decrypt_filename ", "already exists. Skipping.\n" unless $quiet; next FILE; } if ($interactive_mode) { next FILE unless (&query_yes_no( " Decrypt: $file ([y]/n)? ", $ACCEPT_YES_DEFAULT)); } unless ($trial_run) { print "[+] Decrypting: $dir/$filename\n" unless $quiet; &decrypt_file($filename, $decrypt_filename, $NO_DEL_SOURCE_FILE); if (-e $decrypt_filename && -s $decrypt_filename != 0) { ### set the atime and mtime to be the same as the ### original file. unless ($no_fs_times) { if (defined $mtime and $mtime and defined $atime and $atime) { utime $atime, $mtime, $decrypt_filename; } } if ($wipe_mode and not $quiet) { print " Securely deleting file: $file\n"; } ### only delete the original encrypted ### file if the decrypted one exists &delete_file($filename); $total_decrypted++; } else { print "[-] Could not decrypt file: $file\n" unless $quiet; next FILE; } } } } print "\n" unless $quiet; chdir $initial_dir or die "[*] Could not chdir: $initial_dir\n"; return; } sub get_files() { my $dir = shift; print "[+] Building file list...\n" unless $quiet; if ($norecurse) { opendir D, $dir or die "[*] Could not open $dir: $!"; my @files = readdir D; closedir D; for my $file (@files) { next if $file eq '.'; next if $file eq '..'; &check_file_criteria("$dir/$file"); } } else { ### get all files in all subdirectories find(\&find_files, $dir); } return; } sub exclude_file() { my $file = shift; for my $pat (@exclude_patterns) { if ($file =~ m|$pat|) { print "[+] Skipping $file (matches exclude pattern: $pat)\n" if $verbose and not $quiet; return 1; } } return 0; } sub include_file() { my $file = shift; for my $pat (@include_patterns) { if ($file =~ m|$pat|) { print "[+] Including $file (matches include pattern: $pat)\n" if $verbose and not $quiet; return 1; } } return 0; } sub obfuscated_mapping_files() { my $dirs_href; if ($encrypt_mode) { $dirs_href = \%obfuscate_ctrs; } else { $dirs_href = \%obfuscated_dirs; } DIR: for my $dir (keys %$dirs_href) { unless (chdir($dir)) { print "[-] Could not chdir $dir, skipping.\n" unless $quiet; next DIR; } if ($encrypt_mode) { next DIR unless -e $obfuscate_map_filename; ### encrypt the map file now that we have encrypted ### the directory print "[+] Encrypting mapping file: ", "$dir/$obfuscate_map_filename\n" unless $quiet; unless ($trial_run) { &encrypt_file($obfuscate_map_filename, "$obfuscate_map_filename.gpg", $NO_DEL_SOURCE_FILE); unlink $obfuscate_map_filename; } } else { next DIR unless -e "$obfuscate_map_filename.gpg"; ### delete the map file since we have decrypted ### the directory print "[+] Decrypting mapping file: ", "$dir/$obfuscate_map_filename.gpg\n" unless $quiet; unless ($trial_run) { &decrypt_file("$obfuscate_map_filename.gpg", $obfuscate_map_filename, $NO_DEL_SOURCE_FILE); unlink "$obfuscate_map_filename.gpg"; } } } return; } sub handle_old_obfuscated_map_file() { return unless -e "$obfuscate_map_filename.gpg"; &decrypt_file("$obfuscate_map_filename.gpg", $obfuscate_map_filename, $NO_DEL_SOURCE_FILE); unlink "$obfuscate_map_filename.gpg"; my @existing_obfuscated_files = (); open F, '<', $obfuscate_map_filename or die "[*] Could not open ", "$obfuscate_map_filename: $!"; while () { if (/^\s*.*\s+(gpgdir_\d+_\d+.gpg)/) { if (-e $1) { push @existing_obfuscated_files, $_; } } } close F; if (@existing_obfuscated_files) { ### there are some obfuscated files from a previous gpgdir ### execution open G, '>', $obfuscate_map_filename or die "[*] Could not open ", "$obfuscate_map_filename: $!"; print G for @existing_obfuscated_files; close G; } return; } sub append_obfuscated_mapping() { my ($filename, $encrypt_filename) = @_; open G, '>>', $obfuscate_map_filename or die "[*] Could not open ", "$obfuscate_map_filename: $!"; print G "$filename $encrypt_filename\n"; close G; return; } sub import_obfuscated_file_map() { my $dir = shift; $obfuscated_dirs{$dir} = {}; return unless -e "$obfuscate_map_filename.gpg"; &decrypt_file("$obfuscate_map_filename.gpg", $obfuscate_map_filename, $NO_DEL_SOURCE_FILE); open G, '<', $obfuscate_map_filename or die "[*] Could not open ", "$obfuscate_map_filename: $!"; while () { if (/^\s*(.*)\s+(gpgdir_\d+_\d+.gpg)/) { $obfuscated_dirs{$dir}{$2} = $1; } } close G; return; } sub get_homedir() { my $uid = $<; my $homedir = ''; if (-e '/etc/passwd') { open P, '<', '/etc/passwd' or die "[*] Could not open /etc/passwd. Exiting.\n"; my @lines =

; close P; for my $line (@lines) { ### mbr:x:222:222:Michael Rash:/home/mbr:/bin/bash chomp $line; if ($line =~ /^(?:.*:){2}$uid:(?:.*:){2}(\S+):/) { $homedir = $1; last; } } } else { $homedir = $ENV{'HOME'} if defined $ENV{'HOME'}; } die "[*] Could not determine home directory. Use the -u option." unless $homedir; return $homedir; } sub get_key() { if (-e "${homedir}/.gpgdirrc") { open F, '<', "$homedir/.gpgdirrc" or die "[*] Could not open ", "${homedir}/.gpgdirrc. Exiting.\n"; my @lines = ; close F; my $key = ''; for my $line (@lines) { chomp $line; if ($line =~ /^\s*default_key/) { ### prefer to use the default GnuPG key $use_default_key = 1; return ''; } elsif ($line =~ /^\s*use_key\s+(.*)$/) { ### GnuPG accepts strings to match the key, so we don't ### have to strictly require a key ID... just a string ### that matches the key return $1; } } die "[*] Please edit ${homedir}/.gpgdirrc to include your gpg key identifier\n", " (e.g. \"D4696445\"; see the output of \"gpg --list-keys\"), or use the\n", " default GnuPG key defined in ~/.gnupg/options"; } print "[+] Creating gpgdir rc file: $homedir/.gpgdirrc\n"; open F, '>', "$homedir/.gpgdirrc" or die "[*] Could not open " . "${homedir}/.gpgdirrc. Exiting.\n"; print F <<_CONFIGRC_; # Config file for gpgdir. # # Set the key to use to encrypt files with "use_key ", e.g. # "use_key D4696445". See "gpg --list-keys" for a list of keys on your # GnuPG key ring. Alternatively, if you want gpgdir to always use the # default key that is defined by the "default-key" variable in # ~/.gnupg/options, then uncomment the "default_key" line below. # Uncomment to use the GnuPG default key defined in ~/.gnupg/options: #default_key # If you want to use a specific GnuPG key, Uncomment the next line and # replace "KEYID" with your real key id: #use_key KEYID _CONFIGRC_ close F; print "[*] Please edit $homedir/.gpgdirrc to include your gpg key identifier,\n", " or use the default GnuPG key defined in ~/.gnupg/options. Exiting.\n"; exit 0; } sub find_files() { my $file = $File::Find::name; &check_file_criteria($file); return; } sub check_file_criteria() { my $file = shift; ### skip all links, zero size files, all hidden ### files (includes .gnupg files), etc. return if -d $file; if (-e $file and not -l $file and -s $file != 0 and $file !~ m|/\.|) { if ($encrypt_mode) { if ($file =~ m|\.gpg| or $file =~ m|\.asc|) { print "[-] Skipping encrypted file: $file\n" unless $quiet; return; } } else { unless ($file =~ m|\.gpg| or $file =~ m|\.asc|) { print "[-] Skipping unencrypted file: $file\n" unless $quiet; return; } } my ($atime, $mtime) = (stat($file))[8,9]; $files{$file}{'atime'} = $atime; $files{$file}{'mtime'} = $mtime; } else { print "[-] Skipping file: $file\n" if $verbose and not $quiet; } return; } sub get_password() { ### this is only useful if the gpg key literally has no password ### (usually this is not the case, but gpgdir will support it if ### so). return if $cmdline_no_password; ### if we are using gpg-agent for passwords, then return return if $use_gpg_agent; if ($pw_file) { open PW, '<', $pw_file or die "[*] Could not open $pw_file: $!"; $pw = ; close PW; chomp $pw; } else { print "[+] Executing: gpgdir @args_cp\n" unless $quiet; if ($symmetric_mode) { print " [Symmetric mode]\n" unless $quiet; } else { if ($use_default_key) { print " Using default GnuPG key.\n" unless $quiet; } else { print " Using GnuPG key: $encrypt_user\n" unless $quiet; } } if ($test_and_exit) { print " *** test_mode() ***\n" unless $quiet; } if ($encrypt_mode) { print ' Enter password (for initial ' . "encrypt/decrypt test)\n" unless $quiet; } my $msg = 'Password: '; ### get the password without echoing the chars back to the screen ReadMode 'noecho'; while (! $pw) { print $msg; $pw = ReadLine 0; chomp $pw; } ReadMode 'normal'; if ($quiet) { print "\n"; } else { print "\n\n"; } } return; } sub test_mode() { chdir $dir or die "[*] Could not chdir($dir): $!"; my $test_file = "gpgdir_test.$$"; print "[+] test_mode(): Encrypt/Decrypt test of $test_file\n" if (($test_and_exit or $verbose) and not $quiet); if (-e $test_file) { &delete_file($test_file) or die "[*] test_mode(): Could not remove $test_file: $!"; } if (-e "$test_file.gpg") { &delete_file("$test_file.gpg") or die "[*] test_mode(): Could not remove $test_file.gpg: $!"; } open G, '>', $test_file or die "[*] test_mode(): Could not create $test_file: $!"; print G "gpgdir test\n"; close G; if (-e $test_file) { print "[+] test_mode(): Created $test_file\n" if (($test_and_exit or $verbose) and not $quiet); } else { die "[*] test_mode(): Could not create $test_file\n"; } &encrypt_file($test_file, "${test_file}.gpg", $DEL_SOURCE_FILE); if (-e "$test_file.gpg" and (-s $test_file != 0)) { print "[+] test_mode(): Successful encrypt of $test_file\n" if (($test_and_exit or $verbose) and not $quiet); &delete_file($test_file) if -e $test_file; } else { die "[*] test_mode(): not encrypt $test_file (try adding -v).\n"; } &decrypt_file("${test_file}.gpg", $test_file, $DEL_SOURCE_FILE); if (-e $test_file and (-s $test_file != 0)) { print "[+] test_mode(): Successful decrypt of $test_file\n" if (($test_and_exit or $verbose) and not $quiet); } else { die "[*] test_mode(): Could not decrypt $test_file.gpg ", "(try adding -v).\n"; } open F, '<', $test_file or die "[*] test_mode(): Could not open $test_file: $!"; my $line = ; close F; if (defined $line and $line =~ /\S/) { chomp $line; if ($line eq 'gpgdir test') { print "[+] test_mode(): Decrypted content matches original.\n", "[+] test_mode(): Success!\n\n" if (($test_and_exit or $verbose) and not $quiet); } else { die "[*] test_mode(): Decrypted content does not match ", "original (try adding -v)."; } } else { die "[*] test_mode(): Fail (try adding -v).\n"; } &delete_file($test_file) if -e $test_file; &delete_file("$test_file.gpg") if -e "$test_file.gpg"; chdir $initial_dir or die "[*] Could not chdir($initial_dir)"; return 1; } sub query_yes_no() { my ($msg, $style) = @_; my $ans = ''; while ($ans ne 'y' and $ans ne 'n') { print $msg; $ans = lc(); if ($style == $ACCEPT_YES_DEFAULT) { return 1 if $ans eq "\n"; } elsif ($style == $ACCEPT_NO_DEFAULT) { return 0 if $ans eq "\n"; } chomp $ans; } return 1 if $ans eq 'y'; return 0; } sub usage_and_exit() { print <<_HELP_; gpgdir; Recursive direction encryption and decryption with GnuPG [+] Version: $version (file revision: $rev_num) By Michael Rash (mbr\@cipherdyne.org) URL: http://www.cipherdyne.org/gpgdir/ Usage: gpgdir -e|-d [options] Options: -e, --encrypt - Encrypt and all of its subdirectories. -d, --decrypt - Decrypt and all of its subdirectories. -a, --agent - Acquire password information from a running instance of gpg-agent. -A, --Agent-info - Specify the value for the GPG_AGENT_INFO environment variable as returned by 'gpg-agent --daemon'. -g, --gnupg-dir

- Specify a path to a .gnupg directory for gpg keys (the default is ~/.gnupg if this option is not used). -p, --pw-file - Read password in from . -s, --skip-test - Skip encrypt -> decrypt test. -t, --test-mode - Run encrypt -> decrypt test and exit. -T, --Trial-run - Show what filesystem actions would take place without actually doing them. -P, --Plain-ascii - Ascii armor mode (creates non-binary encrypted files). --Interactive - Query the user before encrypting, decrypting, or deleting any files. --Exclude - Skip all filenames that match . --Exclude-from - Skip all filenames that match any pattern contained within . --Include - Include only those filenames that match . --Include-from - Include only those filenames that match a pattern contained within . -K, --Key-id - Specify GnuPG key ID, or key-matching string. This overrides the use_key value in ~/.gpgdirrc -D, --Default-key - Use the key that GnuPG defines as the default (i.e. the key that is specified by the default-key option in ~/.gnupg/options). -O, --Obfuscate-filenames - Substitute all real filenames in a directory with manufactured ones (the original filenames are preserved in a mapping file and restored when the directory is decrypted). --obfuscate-map_file - Specify path to obfuscated mapping file (in -O mode). -F, --Force - Continue to run even if files cannot be deleted (because of permissions problems for example). --overwrite-encrypted - Overwrite encrypted files even if a previous .gpg file already exists. --overwrite-decrypted - Overwrite decrypted files even if the previous unencrypted file already exists. -q, --quiet - Print as little to the screen as possible -W, --Wipe - Use the 'wipe' command to securely delete unencrypted copies of files after they have been encrypted. --wipe-path - Specify path to the wipe command. --wipe-interactive - Force interactive mode with the wipe command. --wipe-cmdline - Manually specify command line arguments to the wipe command. --no-recurse - Don't recursively encrypt/decrypt subdirectories. --no-delete - Don't delete original unencrypted files. --no-preserve-times - Don't preserve original mtime and atime values on encrypted/decrypted files. --no-password - Assume the gpg key has no password at all (this is not common). -u, --user-homedir - Path to home directory. -v, --verbose - Run in verbose mode. -V, --Version - print version. -h, --help - print help. _HELP_ exit 0; } signing-party-2.12/gpgdir/test/data-dir/dir3/dir4/gpgdir-copy.pl000077500000000000000000001201071500571021200244320ustar00rootroot00000000000000#!/usr/bin/perl -w # ########################################################################### # # File: gpgdir # # URL: http://www.cipherdyne.org/gpgdir/ # # Purpose: To encrypt/decrypt whole directories # # Author: Michael Rash (mbr@cipherdyne.com) # # Version: 1.7 # # Copyright (C) 2002-2007 Michael Rash (mbr@cipherdyne.org) # # License (GNU General Public License): # # 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, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 # USA # ########################################################################### # # $Id: gpgdir 246 2008-02-18 14:29:16Z mbr $ # use lib '/usr/lib/gpgdir'; use File::Find; use File::Copy; use Term::ReadKey; use GnuPG::Interface; use IO::File; use IO::Handle; use Getopt::Long; use Cwd; use strict; ### set the current gpgdir version and file revision numbers my $version = '1.7'; my $revision_svn = '$Revision: 246 $'; my $rev_num = '1'; ($rev_num) = $revision_svn =~ m|\$Rev.*:\s+(\S+)|; ### establish some defaults my $encrypt_user = ''; my $gpg_homedir = ''; my $dir = ''; my $pw = ''; my $encrypt_dir = ''; my $decrypt_dir = ''; my $homedir = ''; my $exclude_pat = ''; my $exclude_file = ''; my $include_pat = ''; my $include_file = ''; my $total_encrypted = 0; my $total_decrypted = 0; my $norecurse = 0; my $printver = 0; my $no_delete = 0; my $no_fs_times = 0; my $test_and_exit = 0; my $trial_run = 0; my $skip_test_mode = 0; my $verbose = 0; my $quiet = 0; my $use_gpg_agent = 0; ### use gpg-agent for passwords my $gpg_agent_info = ''; my $force_mode = 0; my $help = 0; my $wipe_mode = 0; my $encrypt_mode = 0; my $use_default_key = 0; my $pw_file = ''; my $wipe_cmd = '/usr/bin/wipe'; my $wipe_cmdline = ''; my $wipe_interactive = 0; my $interactive_mode = 0; my $ascii_armor_mode = 0; my @exclude_patterns = (); my @include_patterns = (); my %files = (); my %options = (); my %obfuscate_ctrs = (); my %obfuscated_dirs = (); my $have_obfuscated_file = 0; my $cmdline_no_password = 0; my $obfuscate_mode = 0; my $obfuscate_map_filename = '.gpgdir_map_file'; my $overwrite_encrypted = 0; my $overwrite_decrypted = 0; my $symmetric_mode = 0; my $DEL_SOURCE_FILE = 1; my $NO_DEL_SOURCE_FILE = 0; ### for user answers my $ACCEPT_YES_DEFAULT = 1; my $ACCEPT_NO_DEFAULT = 2; unless ($< == $>) { die "[*] Real and effective uid must be the same. Make sure\n", " gpgdir has not been installed as a SUID binary.\n", "Exiting."; } my @args_cp = @ARGV; ### make Getopts case sensitive Getopt::Long::Configure('no_ignore_case'); die "[-] Use --help for usage information.\n" unless(GetOptions ( 'encrypt=s' => \$encrypt_dir, # Encrypt files in this directory. 'decrypt=s' => \$decrypt_dir, # Decrypt files in this directory. 'gnupg-dir=s' => \$gpg_homedir, # Path to /path/to/.gnupg directory. 'pw-file=s' => \$pw_file, # Read password out of this file. 'agent' => \$use_gpg_agent, # Use gpg-agent for passwords. 'Agent-info=s' => \$gpg_agent_info, # Specify GnuPG agent connection # information. 'Wipe' => \$wipe_mode, # Securely delete unencrypted files. 'wipe-path=s' => \$wipe_cmd, # Path to wipe command. 'wipe-interactive' => \$wipe_interactive, # Disable "wipe -I" 'wipe-cmdline=s' => \$wipe_cmdline, # Specify wipe command line. 'Obfuscate-filenames' => \$obfuscate_mode, # substitute real filenames # with manufactured ones. 'obfuscate-map-file=s' => \$obfuscate_map_filename, # path to mapping file. 'Force' => \$force_mode, # Continue if files can't be deleted. 'overwrite-encrypted' => \$overwrite_encrypted, # Overwrite encrypted files # even if they exist. 'overwrite-decrypted' => \$overwrite_decrypted, # Overwrite decrypted files # even if they exist. 'Exclude=s' => \$exclude_pat, # Exclude a pattern from encrypt/decrypt # cycle. 'Exclude-from=s' => \$exclude_file, # Exclude patterns in from # encrypt decrypt cycle. 'Include=s' => \$include_pat, # Specify a pattern used to restrict # encrypt/decrypt operation to. 'Include-from=s' => \$include_file, # Specify a file of include patterns to # restrict all encrypt/decrypt # operations to. 'test-mode' => \$test_and_exit, # Run encrypt -> decrypt test only and # exit. 'Trial-run' => \$trial_run, # Don't modify any files; just show what # would have happened. 'quiet' => \$quiet, # Print as little as possible to # stdout. 'Interactive' => \$interactive_mode, # Query the user before encrypting/ # decrypting/deleting any files. 'Key-id=s' => \$encrypt_user, # Specify encrypt/decrypt key 'Default-key' => \$use_default_key, # Assume that default-key is set within # ~/.gnupg/options. 'Symmetric' => \$symmetric_mode, # encrypt using symmetric cipher. # (this option is not required to # also decrypt, GnuPG handles # that automatically). 'Plain-ascii' => \$ascii_armor_mode, # Ascii armor mode (creates non-binary # encrypted files). 'skip-test' => \$skip_test_mode, # Skip encrypt -> decrypt test. 'no-recurse' => \$norecurse, # Don't encrypt/decrypt files in # subdirectories. 'no-delete' => \$no_delete, # Don't delete files once they have # been encrypted. 'no-password' => \$cmdline_no_password, # Do not query for a password (only # useful for when the gpg literally # has no password). 'user-homedir=s' => \$homedir, # Path to home directory. 'no-preserve-times' => \$no_fs_times, # Don't preserve mtimes or atimes. 'verbose' => \$verbose, # Verbose mode. 'Version' => \$printver, # Print version 'help' => \$help # Print help )); &usage_and_exit() if $help; print "[+] gpgdir v$version (file revision: $rev_num)\n", " by Michael Rash \n" and exit 0 if $printver; if ($symmetric_mode and ($use_gpg_agent or $gpg_agent_info)) { die "[*] gpg-agent incompatible with --Symmetric mode"; } if ($encrypt_dir and $overwrite_decrypted) { die "[*] The -e and --overwrite-decrypted options are incompatible."; } if ($decrypt_dir and $overwrite_encrypted) { die "[*] The -d and --overwrite-encrypted options are incompatible."; } if ($wipe_mode) { unless (-e $wipe_cmd) { die "[*] Can't find wipe command at: $wipe_cmd,\n", " use --wipe-path to specify path."; } unless (-e $wipe_cmd) { die "[*] Can't execute $wipe_cmd"; } } ### build up GnuPG options hash if ($verbose) { %options = ('homedir' => $gpg_homedir); } else { %options = ( 'batch' => 1, 'homedir' => $gpg_homedir ); } $options{'armor'} = 1 if $ascii_armor_mode; ### get the path to the user's home directory $homedir = &get_homedir() unless $homedir; unless ($symmetric_mode) { if ($gpg_homedir) { ### specified on the command line with --gnupg-dir unless ($gpg_homedir =~ /\.gnupg$/) { die "[*] Must specify the path to a user .gnupg directory ", "e.g. /home/username/.gnupg\n"; } } else { if (-d "${homedir}/.gnupg") { $gpg_homedir = "${homedir}/.gnupg"; } } unless (-d $gpg_homedir) { die "[*] GnuPG directory: ${homedir}/.gnupg does not exist. Please\n", " create it by executing: \"gpg --gen-key\". Exiting.\n"; } ### get the key identifier from ~/.gnupg $encrypt_user = &get_key() unless $encrypt_user or $use_default_key; } if ($decrypt_dir and $encrypt_dir) { die "[*] You cannot encrypt and decrypt the same directory.\n"; &usage_and_exit(); } unless ($decrypt_dir or $encrypt_dir or $test_and_exit) { print "[*] Please specify -e , -d , or --test-mode\n"; &usage_and_exit(); } ### exclude file pattern push @exclude_patterns, $exclude_pat if $exclude_pat; if ($exclude_file) { open P, '<', $exclude_file or die "[*] Could not open file: $exclude_file"; my @lines =

; close P; for my $line (@lines) { next unless $line =~ /\S/; chomp $line; push @exclude_patterns, qr{$line}; } } ### include file pattern push @include_patterns, $include_pat if $include_pat; if ($include_file) { open P, '<', $include_file or die "[*] Could not open file: $include_file"; my @lines =

; close P; for my $line (@lines) { next unless $line =~ /\S/; chomp $line; push @include_patterns, qr{$line}; } } if ($encrypt_dir) { $dir = $encrypt_dir; $encrypt_mode = 1; } elsif ($decrypt_dir) { $dir = $decrypt_dir; $encrypt_mode = 0; } if ($dir) { die "[*] Directory does not exist: $dir" unless -e $dir; die "[*] Not a directory: $dir" unless -d $dir; } ### don't need to test encrypt/decrypt ability if we are running ### in --Trial-run mode. $skip_test_mode = 1 if $trial_run; my $initial_dir = cwd or die "[*] Could not get CWD: $!"; if ($symmetric_mode) { &get_password(); } else { &get_password() unless $encrypt_mode and $skip_test_mode; } if ($dir eq '.') { $dir = $initial_dir; } elsif ($dir !~ m|^/|) { $dir = $initial_dir . '/' . $dir; } $dir =~ s|/$||; ### remove any trailing slash ### run a test to make sure gpgdir and encrypt and decrypt a file unless ($skip_test_mode) { my $rv = &test_mode(); exit $rv if $test_and_exit; } if ($encrypt_mode) { print "[+] Encrypting directory: $dir\n" unless $quiet; } else { print "[+] Decrypting directory: $dir\n" unless $quiet; } ### build a hash of file paths to work against &get_files($dir); ### perform the gpg operation (encrypt/decrypt) &gpg_operation(); &obfuscated_mapping_files() if $obfuscate_mode; unless ($obfuscate_mode) { if ($have_obfuscated_file) { print "[-] Obfuscated filenames detected, try decrypting with -O.\n" unless $quiet; } } if ($encrypt_mode) { print "[+] Total number of files encrypted: " . "$total_encrypted\n" unless $quiet; } else { print "[+] Total number of files decrypted: " . "$total_decrypted\n" unless $quiet; } exit 0; #==================== end main ===================== sub encrypt_file() { my ($in_file, $out_file, $del_flag) = @_; my $gpg = GnuPG::Interface->new(); $gpg->options->hash_init(%options); die "[*] Could not create new gpg object with ", "homedir: $gpg_homedir" unless $gpg; unless ($symmetric_mode or $use_default_key) { $gpg->options->default_key($encrypt_user); $gpg->options->push_recipients($encrypt_user); } my ($input_fh, $output_fh, $error_fh, $pw_fh, $status_fh) = (IO::File->new($in_file), IO::File->new("> $out_file"), IO::Handle->new(), IO::Handle->new(), IO::Handle->new()); my $handles = GnuPG::Handles->new( stdin => $input_fh, stdout => $output_fh, stderr => $error_fh, passphrase => $pw_fh, status => $status_fh ); $handles->options('stdin')->{'direct'} = 1; $handles->options('stdout')->{'direct'} = 1; my $pid; if ($use_gpg_agent or $gpg_agent_info) { ### set environment explicitly if --Agent was specified if ($gpg_agent_info) { $ENV{'GPG_AGENT_INFO'} = $gpg_agent_info; } $pid = $gpg->encrypt('handles' => $handles, 'command_args' => [ qw( --use-agent ) ]); } else { if ($symmetric_mode) { $pid = $gpg->encrypt_symmetrically('handles' => $handles); } else { $pid = $gpg->encrypt('handles' => $handles); } } print $pw_fh $pw; close $pw_fh; my @errors = <$error_fh>; if ($verbose) { print for @errors; } else { for (@errors) { print if /bad\s+pass/; } } close $input_fh; close $output_fh; close $error_fh; close $status_fh; waitpid $pid, 0; if (-s $out_file == 0) { &delete_file($out_file); &delete_file($in_file) if $del_flag == $DEL_SOURCE_FILE; if ($use_gpg_agent) { die "[*] Created zero-size file: $out_file\n", " Maybe gpg-agent does not yet have the password for that key?\n", " Try re-running with -v."; } else { die "[*] Created zero-size file: $out_file\n", " Bad password? Try re-running with -v."; } } return; } sub decrypt_file() { my ($in_file, $out_file, $del_flag) = @_; my $gpg = GnuPG::Interface->new(); $gpg->options->hash_init(%options); die "[*] Could not create new gpg object with ", "homedir: $gpg_homedir" unless $gpg; unless ($symmetric_mode or $use_default_key) { $gpg->options->default_key($encrypt_user); $gpg->options->push_recipients($encrypt_user); } my ($input_fh, $output_fh, $error_fh, $pw_fh, $status_fh) = (IO::File->new($in_file), IO::File->new("> $out_file"), IO::Handle->new(), IO::Handle->new(), IO::Handle->new()); my $handles = GnuPG::Handles->new( stdin => $input_fh, stdout => $output_fh, stderr => $error_fh, passphrase => $pw_fh, status => $status_fh ); $handles->options('stdin')->{'direct'} = 1; $handles->options('stdout')->{'direct'} = 1; my $pid; if ($use_gpg_agent) { $pid = $gpg->decrypt('handles' => $handles, 'command_args' => [ qw( --use-agent ) ]); } else { $pid = $gpg->decrypt('handles' => $handles); } print $pw_fh $pw; close $pw_fh; my @errors = <$error_fh>; if ($verbose) { print for @errors; } else { for (@errors) { print if /bad\s+pass/; } } close $input_fh; close $output_fh; close $error_fh; close $status_fh; waitpid $pid, 0; if (-s $out_file == 0) { &delete_file($out_file); &delete_file($in_file) if $del_flag == $DEL_SOURCE_FILE; if ($use_gpg_agent) { die "[*] Created zero-size file: $out_file\n", " Maybe gpg-agent does not yet have the password for that key?\n", " Try re-running with -v."; } else { die "[*] Created zero-size file: $out_file\n", " Bad password? Try re-running with -v."; } } return; } sub delete_file() { my $file = shift; return if $no_delete; return unless -e $file; if ($wipe_mode) { my $cmd = $wipe_cmd; if ($wipe_cmdline) { $cmd .= " $wipe_cmdline "; } else { if ($wipe_interactive) { $cmd .= ' -i '; } else { $cmd .= ' -I -s '; } } $cmd .= $file; if ($verbose) { print " Executing: $cmd\n"; } ### wipe the file system $cmd; } else { unlink $file; } if (-e $file) { my $msg = "[-] Could not delete file: $file\n"; if ($force_mode) { print $msg unless $quiet; } else { die $msg unless $quiet; } } return; } sub gpg_operation() { ### sort by oldest to youngest mtime FILE: for my $file (sort {$files{$a}{'mtime'} <=> $files{$b}{'mtime'}} keys %files) { ### see if we have an exclusion pattern that implies ### we should skip this file if (@exclude_patterns and &exclude_file($file)) { print "[+] Skipping excluded file: $file\n" if $verbose and not $quiet; next FILE; } ### see if we have an inclusion pattern that implies ### we should process this file if (@include_patterns and not &include_file($file)) { print "[+] Skipping non-included file: $file\n" if $verbose and not $quiet; next FILE; } ### dir is always a full path my ($dir, $filename) = ($file =~ m|(.*)/(.*)|); unless (chdir($dir)) { print "[-] Could not chdir $dir, skipping.\n" unless $quiet; next FILE; } my $mtime = $files{$file}{'mtime'}; my $atime = $files{$file}{'atime'}; if ($encrypt_mode) { my $encrypt_filename = "$filename.gpg"; if ($obfuscate_mode) { unless (defined $obfuscate_ctrs{$dir}) { ### create a new gpgdir mapping file for obfuscated file ### names, but preserve any previously encrypted file ### name mappings &handle_old_obfuscated_map_file(); ### make obfuscated file names start at 1 for each ### directory $obfuscate_ctrs{$dir} = 1; } $encrypt_filename = 'gpgdir_' . $$ . '_' . $obfuscate_ctrs{$dir} . '.gpg'; } if ($ascii_armor_mode) { $encrypt_filename = "$filename.asc"; } if (-e $encrypt_filename and not $overwrite_encrypted) { print "[-] Encrypted file $dir/$encrypt_filename already ", "exists, skipping.\n" unless $quiet; next FILE; } if ($interactive_mode) { next FILE unless (&query_yes_no( " Encrypt: $file ([y]/n)? ", $ACCEPT_YES_DEFAULT)); } print "[+] Encrypting: $file\n" unless $quiet; unless ($trial_run) { &encrypt_file($filename, $encrypt_filename, $NO_DEL_SOURCE_FILE); if (-e $encrypt_filename && -s $encrypt_filename != 0) { ### set the atime and mtime to be the same as the ### original file. unless ($no_fs_times) { if (defined $mtime and $mtime and defined $atime and $atime) { utime $atime, $mtime, $encrypt_filename; } } ### only delete the original file if ### the encrypted one exists if ($wipe_mode and not $quiet) { print " Securely deleting file: $file\n"; } &delete_file($filename); if ($obfuscate_mode) { ### record the original file name mapping &append_obfuscated_mapping($filename, $encrypt_filename); $obfuscate_ctrs{$dir}++; } $total_encrypted++; } else { print "[-] Could not encrypt file: $file\n" unless $quiet; next FILE; } } } else { ### allow filenames with spaces my $decrypt_filename = ''; if ($filename =~ /^(.+)\.gpg$/) { $decrypt_filename = $1; } elsif ($filename =~ /^(.+)\.asc$/) { $decrypt_filename = $1; } if ($obfuscate_mode) { &import_obfuscated_file_map($dir) unless defined $obfuscated_dirs{$dir}; if (defined $obfuscated_dirs{$dir}{$filename}) { $decrypt_filename = $obfuscated_dirs{$dir}{$filename}; } else { ### print "[-] Obfuscated file map does not exist for $filename in\n", " $obfuscate_map_filename, skipping.\n"; next FILE; } } else { if (not $force_mode and $file =~ /gpgdir_\d+_\d+.gpg/) { ### be careful not to decrypt obfuscated file unless we ### are running in -O mode. This ensures that the ### original file names will be acquired from the ### /some/dir/.gpgdir_map_file $have_obfuscated_file = 1; next FILE; } } ### length() allows files named "0" next FILE unless length($decrypt_filename) > 0; ### don't decrypt a file on top of a normal file of ### the same name if (-e $decrypt_filename and not $overwrite_decrypted) { print "[-] Decrypted file $dir/$decrypt_filename ", "already exists. Skipping.\n" unless $quiet; next FILE; } if ($interactive_mode) { next FILE unless (&query_yes_no( " Decrypt: $file ([y]/n)? ", $ACCEPT_YES_DEFAULT)); } unless ($trial_run) { print "[+] Decrypting: $dir/$filename\n" unless $quiet; &decrypt_file($filename, $decrypt_filename, $NO_DEL_SOURCE_FILE); if (-e $decrypt_filename && -s $decrypt_filename != 0) { ### set the atime and mtime to be the same as the ### original file. unless ($no_fs_times) { if (defined $mtime and $mtime and defined $atime and $atime) { utime $atime, $mtime, $decrypt_filename; } } if ($wipe_mode and not $quiet) { print " Securely deleting file: $file\n"; } ### only delete the original encrypted ### file if the decrypted one exists &delete_file($filename); $total_decrypted++; } else { print "[-] Could not decrypt file: $file\n" unless $quiet; next FILE; } } } } print "\n" unless $quiet; chdir $initial_dir or die "[*] Could not chdir: $initial_dir\n"; return; } sub get_files() { my $dir = shift; print "[+] Building file list...\n" unless $quiet; if ($norecurse) { opendir D, $dir or die "[*] Could not open $dir: $!"; my @files = readdir D; closedir D; for my $file (@files) { next if $file eq '.'; next if $file eq '..'; &check_file_criteria("$dir/$file"); } } else { ### get all files in all subdirectories find(\&find_files, $dir); } return; } sub exclude_file() { my $file = shift; for my $pat (@exclude_patterns) { if ($file =~ m|$pat|) { print "[+] Skipping $file (matches exclude pattern: $pat)\n" if $verbose and not $quiet; return 1; } } return 0; } sub include_file() { my $file = shift; for my $pat (@include_patterns) { if ($file =~ m|$pat|) { print "[+] Including $file (matches include pattern: $pat)\n" if $verbose and not $quiet; return 1; } } return 0; } sub obfuscated_mapping_files() { my $dirs_href; if ($encrypt_mode) { $dirs_href = \%obfuscate_ctrs; } else { $dirs_href = \%obfuscated_dirs; } DIR: for my $dir (keys %$dirs_href) { unless (chdir($dir)) { print "[-] Could not chdir $dir, skipping.\n" unless $quiet; next DIR; } if ($encrypt_mode) { next DIR unless -e $obfuscate_map_filename; ### encrypt the map file now that we have encrypted ### the directory print "[+] Encrypting mapping file: ", "$dir/$obfuscate_map_filename\n" unless $quiet; unless ($trial_run) { &encrypt_file($obfuscate_map_filename, "$obfuscate_map_filename.gpg", $NO_DEL_SOURCE_FILE); unlink $obfuscate_map_filename; } } else { next DIR unless -e "$obfuscate_map_filename.gpg"; ### delete the map file since we have decrypted ### the directory print "[+] Decrypting mapping file: ", "$dir/$obfuscate_map_filename.gpg\n" unless $quiet; unless ($trial_run) { &decrypt_file("$obfuscate_map_filename.gpg", $obfuscate_map_filename, $NO_DEL_SOURCE_FILE); unlink "$obfuscate_map_filename.gpg"; } } } return; } sub handle_old_obfuscated_map_file() { return unless -e "$obfuscate_map_filename.gpg"; &decrypt_file("$obfuscate_map_filename.gpg", $obfuscate_map_filename, $NO_DEL_SOURCE_FILE); unlink "$obfuscate_map_filename.gpg"; my @existing_obfuscated_files = (); open F, '<', $obfuscate_map_filename or die "[*] Could not open ", "$obfuscate_map_filename: $!"; while () { if (/^\s*.*\s+(gpgdir_\d+_\d+.gpg)/) { if (-e $1) { push @existing_obfuscated_files, $_; } } } close F; if (@existing_obfuscated_files) { ### there are some obfuscated files from a previous gpgdir ### execution open G, '>', $obfuscate_map_filename or die "[*] Could not open ", "$obfuscate_map_filename: $!"; print G for @existing_obfuscated_files; close G; } return; } sub append_obfuscated_mapping() { my ($filename, $encrypt_filename) = @_; open G, '>>', $obfuscate_map_filename or die "[*] Could not open ", "$obfuscate_map_filename: $!"; print G "$filename $encrypt_filename\n"; close G; return; } sub import_obfuscated_file_map() { my $dir = shift; $obfuscated_dirs{$dir} = {}; return unless -e "$obfuscate_map_filename.gpg"; &decrypt_file("$obfuscate_map_filename.gpg", $obfuscate_map_filename, $NO_DEL_SOURCE_FILE); open G, '<', $obfuscate_map_filename or die "[*] Could not open ", "$obfuscate_map_filename: $!"; while () { if (/^\s*(.*)\s+(gpgdir_\d+_\d+.gpg)/) { $obfuscated_dirs{$dir}{$2} = $1; } } close G; return; } sub get_homedir() { my $uid = $<; my $homedir = ''; if (-e '/etc/passwd') { open P, '<', '/etc/passwd' or die "[*] Could not open /etc/passwd. Exiting.\n"; my @lines =

; close P; for my $line (@lines) { ### mbr:x:222:222:Michael Rash:/home/mbr:/bin/bash chomp $line; if ($line =~ /^(?:.*:){2}$uid:(?:.*:){2}(\S+):/) { $homedir = $1; last; } } } else { $homedir = $ENV{'HOME'} if defined $ENV{'HOME'}; } die "[*] Could not determine home directory. Use the -u option." unless $homedir; return $homedir; } sub get_key() { if (-e "${homedir}/.gpgdirrc") { open F, '<', "$homedir/.gpgdirrc" or die "[*] Could not open ", "${homedir}/.gpgdirrc. Exiting.\n"; my @lines = ; close F; my $key = ''; for my $line (@lines) { chomp $line; if ($line =~ /^\s*default_key/) { ### prefer to use the default GnuPG key $use_default_key = 1; return ''; } elsif ($line =~ /^\s*use_key\s+(.*)$/) { ### GnuPG accepts strings to match the key, so we don't ### have to strictly require a key ID... just a string ### that matches the key return $1; } } die "[*] Please edit ${homedir}/.gpgdirrc to include your gpg key identifier\n", " (e.g. \"D4696445\"; see the output of \"gpg --list-keys\"), or use the\n", " default GnuPG key defined in ~/.gnupg/options"; } print "[+] Creating gpgdir rc file: $homedir/.gpgdirrc\n"; open F, '>', "$homedir/.gpgdirrc" or die "[*] Could not open " . "${homedir}/.gpgdirrc. Exiting.\n"; print F <<_CONFIGRC_; # Config file for gpgdir. # # Set the key to use to encrypt files with "use_key ", e.g. # "use_key D4696445". See "gpg --list-keys" for a list of keys on your # GnuPG key ring. Alternatively, if you want gpgdir to always use the # default key that is defined by the "default-key" variable in # ~/.gnupg/options, then uncomment the "default_key" line below. # Uncomment to use the GnuPG default key defined in ~/.gnupg/options: #default_key # If you want to use a specific GnuPG key, Uncomment the next line and # replace "KEYID" with your real key id: #use_key KEYID _CONFIGRC_ close F; print "[*] Please edit $homedir/.gpgdirrc to include your gpg key identifier,\n", " or use the default GnuPG key defined in ~/.gnupg/options. Exiting.\n"; exit 0; } sub find_files() { my $file = $File::Find::name; &check_file_criteria($file); return; } sub check_file_criteria() { my $file = shift; ### skip all links, zero size files, all hidden ### files (includes .gnupg files), etc. return if -d $file; if (-e $file and not -l $file and -s $file != 0 and $file !~ m|/\.|) { if ($encrypt_mode) { if ($file =~ m|\.gpg| or $file =~ m|\.asc|) { print "[-] Skipping encrypted file: $file\n" unless $quiet; return; } } else { unless ($file =~ m|\.gpg| or $file =~ m|\.asc|) { print "[-] Skipping unencrypted file: $file\n" unless $quiet; return; } } my ($atime, $mtime) = (stat($file))[8,9]; $files{$file}{'atime'} = $atime; $files{$file}{'mtime'} = $mtime; } else { print "[-] Skipping file: $file\n" if $verbose and not $quiet; } return; } sub get_password() { ### this is only useful if the gpg key literally has no password ### (usually this is not the case, but gpgdir will support it if ### so). return if $cmdline_no_password; ### if we are using gpg-agent for passwords, then return return if $use_gpg_agent; if ($pw_file) { open PW, '<', $pw_file or die "[*] Could not open $pw_file: $!"; $pw = ; close PW; chomp $pw; } else { print "[+] Executing: gpgdir @args_cp\n" unless $quiet; if ($symmetric_mode) { print " [Symmetric mode]\n" unless $quiet; } else { if ($use_default_key) { print " Using default GnuPG key.\n" unless $quiet; } else { print " Using GnuPG key: $encrypt_user\n" unless $quiet; } } if ($test_and_exit) { print " *** test_mode() ***\n" unless $quiet; } if ($encrypt_mode) { print ' Enter password (for initial ' . "encrypt/decrypt test)\n" unless $quiet; } my $msg = 'Password: '; ### get the password without echoing the chars back to the screen ReadMode 'noecho'; while (! $pw) { print $msg; $pw = ReadLine 0; chomp $pw; } ReadMode 'normal'; if ($quiet) { print "\n"; } else { print "\n\n"; } } return; } sub test_mode() { chdir $dir or die "[*] Could not chdir($dir): $!"; my $test_file = "gpgdir_test.$$"; print "[+] test_mode(): Encrypt/Decrypt test of $test_file\n" if (($test_and_exit or $verbose) and not $quiet); if (-e $test_file) { &delete_file($test_file) or die "[*] test_mode(): Could not remove $test_file: $!"; } if (-e "$test_file.gpg") { &delete_file("$test_file.gpg") or die "[*] test_mode(): Could not remove $test_file.gpg: $!"; } open G, '>', $test_file or die "[*] test_mode(): Could not create $test_file: $!"; print G "gpgdir test\n"; close G; if (-e $test_file) { print "[+] test_mode(): Created $test_file\n" if (($test_and_exit or $verbose) and not $quiet); } else { die "[*] test_mode(): Could not create $test_file\n"; } &encrypt_file($test_file, "${test_file}.gpg", $DEL_SOURCE_FILE); if (-e "$test_file.gpg" and (-s $test_file != 0)) { print "[+] test_mode(): Successful encrypt of $test_file\n" if (($test_and_exit or $verbose) and not $quiet); &delete_file($test_file) if -e $test_file; } else { die "[*] test_mode(): not encrypt $test_file (try adding -v).\n"; } &decrypt_file("${test_file}.gpg", $test_file, $DEL_SOURCE_FILE); if (-e $test_file and (-s $test_file != 0)) { print "[+] test_mode(): Successful decrypt of $test_file\n" if (($test_and_exit or $verbose) and not $quiet); } else { die "[*] test_mode(): Could not decrypt $test_file.gpg ", "(try adding -v).\n"; } open F, '<', $test_file or die "[*] test_mode(): Could not open $test_file: $!"; my $line = ; close F; if (defined $line and $line =~ /\S/) { chomp $line; if ($line eq 'gpgdir test') { print "[+] test_mode(): Decrypted content matches original.\n", "[+] test_mode(): Success!\n\n" if (($test_and_exit or $verbose) and not $quiet); } else { die "[*] test_mode(): Decrypted content does not match ", "original (try adding -v)."; } } else { die "[*] test_mode(): Fail (try adding -v).\n"; } &delete_file($test_file) if -e $test_file; &delete_file("$test_file.gpg") if -e "$test_file.gpg"; chdir $initial_dir or die "[*] Could not chdir($initial_dir)"; return 1; } sub query_yes_no() { my ($msg, $style) = @_; my $ans = ''; while ($ans ne 'y' and $ans ne 'n') { print $msg; $ans = lc(); if ($style == $ACCEPT_YES_DEFAULT) { return 1 if $ans eq "\n"; } elsif ($style == $ACCEPT_NO_DEFAULT) { return 0 if $ans eq "\n"; } chomp $ans; } return 1 if $ans eq 'y'; return 0; } sub usage_and_exit() { print <<_HELP_; gpgdir; Recursive direction encryption and decryption with GnuPG [+] Version: $version (file revision: $rev_num) By Michael Rash (mbr\@cipherdyne.org) URL: http://www.cipherdyne.org/gpgdir/ Usage: gpgdir -e|-d [options] Options: -e, --encrypt - Encrypt and all of its subdirectories. -d, --decrypt - Decrypt and all of its subdirectories. -a, --agent - Acquire password information from a running instance of gpg-agent. -A, --Agent-info - Specify the value for the GPG_AGENT_INFO environment variable as returned by 'gpg-agent --daemon'. -g, --gnupg-dir

- Specify a path to a .gnupg directory for gpg keys (the default is ~/.gnupg if this option is not used). -p, --pw-file - Read password in from . -s, --skip-test - Skip encrypt -> decrypt test. -t, --test-mode - Run encrypt -> decrypt test and exit. -T, --Trial-run - Show what filesystem actions would take place without actually doing them. -P, --Plain-ascii - Ascii armor mode (creates non-binary encrypted files). --Interactive - Query the user before encrypting, decrypting, or deleting any files. --Exclude - Skip all filenames that match . --Exclude-from - Skip all filenames that match any pattern contained within . --Include - Include only those filenames that match . --Include-from - Include only those filenames that match a pattern contained within . -K, --Key-id - Specify GnuPG key ID, or key-matching string. This overrides the use_key value in ~/.gpgdirrc -D, --Default-key - Use the key that GnuPG defines as the default (i.e. the key that is specified by the default-key option in ~/.gnupg/options). -O, --Obfuscate-filenames - Substitute all real filenames in a directory with manufactured ones (the original filenames are preserved in a mapping file and restored when the directory is decrypted). --obfuscate-map_file - Specify path to obfuscated mapping file (in -O mode). -F, --Force - Continue to run even if files cannot be deleted (because of permissions problems for example). --overwrite-encrypted - Overwrite encrypted files even if a previous .gpg file already exists. --overwrite-decrypted - Overwrite decrypted files even if the previous unencrypted file already exists. -q, --quiet - Print as little to the screen as possible -W, --Wipe - Use the 'wipe' command to securely delete unencrypted copies of files after they have been encrypted. --wipe-path - Specify path to the wipe command. --wipe-interactive - Force interactive mode with the wipe command. --wipe-cmdline - Manually specify command line arguments to the wipe command. --no-recurse - Don't recursively encrypt/decrypt subdirectories. --no-delete - Don't delete original unencrypted files. --no-preserve-times - Don't preserve original mtime and atime values on encrypted/decrypted files. --no-password - Assume the gpg key has no password at all (this is not common). -u, --user-homedir - Path to home directory. -v, --verbose - Run in verbose mode. -V, --Version - print version. -h, --help - print help. _HELP_ exit 0; } signing-party-2.12/gpgdir/test/data-dir/dir3/dir4/random-binary-data000066400000000000000000000001271500571021200252410ustar00rootroot00000000000000Uп1'uUfInsq,ASIJ fn:f1n5CNotqWbydiIzPhaC)!ճ5signing-party-2.12/gpgdir/test/data-dir/dir3/dir4/random-binary-data.bin000066400000000000000000000001271500571021200260100ustar00rootroot00000000000000Uп1'uUfInsq,ASIJ fn:f1n5CNotqWbydiIzPhaC)!ճ5signing-party-2.12/gpgdir/test/data-dir/dir3/file1000066400000000000000000000000131500571021200217200ustar00rootroot00000000000000some lines signing-party-2.12/gpgdir/test/data-dir/dir3/file2000066400000000000000000000000131500571021200217210ustar00rootroot00000000000000more lines signing-party-2.12/gpgdir/test/data-dir/files_with_spaces/000077500000000000000000000000001500571021200236355ustar00rootroot00000000000000signing-party-2.12/gpgdir/test/data-dir/files_with_spaces/file1 space1000066400000000000000000000000261500571021200257130ustar00rootroot00000000000000one space in filename signing-party-2.12/gpgdir/test/data-dir/files_with_spaces/file2 space2000066400000000000000000000000271500571021200257560ustar00rootroot00000000000000two spaces in filename signing-party-2.12/gpgdir/test/data-dir/files_with_spaces/file3 -dash000066400000000000000000000000171500571021200255350ustar00rootroot00000000000000space and dash signing-party-2.12/gpgdir/test/data-dir/multi-line-ascii000066400000000000000000000003211500571021200232260ustar00rootroot00000000000000This is a file that contains multiple lines of ascii text, but there is no file extension on this one. gppdir should encrypt this file under the test suite. This file is in the top-level data-dir directory. signing-party-2.12/gpgdir/test/data-dir/multi-line-ascii.txt000066400000000000000000000003651500571021200240540ustar00rootroot00000000000000This is a file that contains multiple lines of ascii text, and this file has a .txt extension (which gpgdir should handle without issues). gppdir should encrypt this file under the test suite. This file is in the top-level data-dir directory. signing-party-2.12/gpgdir/test/data-dir/random-binary-data000066400000000000000000000001271500571021200235360ustar00rootroot00000000000000Uп1'uUfInsq,ASIJ fn:f1n5CNotqWbydiIzPhaC)!ճ5signing-party-2.12/gpgdir/test/data-dir/random-binary-data.bin000066400000000000000000000001271500571021200243050ustar00rootroot00000000000000Uп1'uUfInsq,ASIJ fn:f1n5CNotqWbydiIzPhaC)!ճ5signing-party-2.12/gpgdir/test/gpgdir_test.pl000077500000000000000000000414451500571021200213400ustar00rootroot00000000000000#!/usr/bin/perl -w # ############################################################################# # # File: gpgdir_test.pl # # Purpose: This program provides a testing infrastructure for the gpgdir # Single Packet Authorization client and server. # # Author: Michael Rash (mbr@cipherdyne.org) # # Version: 1.9.5 # # Copyright (C) 2008-2009 Michael Rash (mbr@cipherdyne.org) # # License (GNU Public License): # # 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, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 # USA # ############################################################################# # # $Id: gpgdir_test.pl 341 2009-08-26 02:43:51Z mbr $ # use Digest::MD5 'md5_base64'; use File::Find; use File::Copy; use Getopt::Long; use strict; #=================== config defaults ============== my $gpgdirCmd = '../gpgdir'; my $conf_dir = 'conf'; my $output_dir = 'output'; my $logfile = 'test.log'; my $tarfile = 'gpgdir_test.tar.gz'; my $data_dir = 'data-dir'; my $gpg_dir = "$conf_dir/test-gpg"; my $pw_file = "$conf_dir/test.pw"; my $broken_pw_file = "$conf_dir/broken.pw"; my $key_id = '375D7DB9'; #==================== end config ================== my $help = 0; my $test_num = 0; my $PRINT_LEN = 68; my $APPEND = 1; my $NO_APPEND = 0; my $failed_tests = 0; my $prepare_results = 0; my $successful_tests = 0; my $current_test_file = "$output_dir/$test_num.test"; my $previous_test_file = ''; my @data_dir_files = (); my %md5sums = (); my $default_args = "--gnupg-dir $gpg_dir " . "--Key-id $key_id --pw-file $pw_file"; die "[*] Use --help" unless GetOptions( 'Prepare-results' => \$prepare_results, 'help' => \$help ); exit &prepare_results() if $prepare_results; &setup(); &collect_md5sums(); &logr("\n[+] ==> Running gpgdir test suite <==\n\n"); ### execute the tests &test_driver('(Setup) gpgdir program compilation', \&perl_compilation); &test_driver('(Setup) Command line argument processing', \&getopt_test); &test_driver('(Test mode) gpgdir basic test mode', \&test_mode); ### encrypt/decrypt &test_driver('(Encrypt dir) gpgdir directory encryption', \&encrypt); &test_driver('(Encrypt dir) Files recursively encrypted', \&recursively_encrypted); &test_driver('(Encrypt dir) Exclude hidden files/dirs', \&skipped_hidden_files_dirs); &test_driver('(Decrypt dir) gpgdir directory decryption', \&decrypt); &test_driver('(Decrypt dir) Files recursively decrypted', \&recursively_decrypted); &test_driver('(MD5 digest) match across encrypt/decrypt cycle', \&md5sum_validation); ### ascii encrypt/decrypt &test_driver('(Ascii-armor dir) gpgdir directory encryption', \&ascii_encrypt); &test_driver('(Ascii-armor dir) Files recursively encrypted', \&ascii_recursively_encrypted); &test_driver('(Ascii-armor dir) Exclude hidden files/dirs', \&skipped_hidden_files_dirs); &test_driver('(Decrypt dir) gpgdir directory decryption', \&decrypt); &test_driver('(Decrypt dir) Files recursively decrypted', \&ascii_recursively_decrypted); &test_driver('(MD5 digest) match across encrypt/decrypt cycle', \&md5sum_validation); ### obfuscate filenames encrypt/decrypt cycle &test_driver('(Obfuscate filenames) gpgdir directory encryption', \&obf_encrypt); &test_driver('(Obfuscate filenames) Files recursively encrypted', \&obf_recursively_encrypted); &test_driver('(Obfuscate filenames) Exclude hidden files/dirs', \&obf_skipped_hidden_files_dirs); &test_driver('(Decrypt dir) gpgdir directory decryption', \&obf_decrypt); &test_driver('(Decrypt dir) Files recursively decrypted', \&obf_recursively_decrypted); ### same as ascii_recursively_decrypted() &test_driver('(MD5 digest) match across encrypt/decrypt cycle', \&md5sum_validation); ### sign/verify cycle &test_driver('(Sign/verify dir) gpgdir directory signing', \&sign); &test_driver('(Sign/verify dir) Files recursively signed', \&recursively_signed); &test_driver('(Sign/verify dir) Exclude hidden files/dirs', \&skipped_hidden_files_dirs); &test_driver('(Sign/verify dir) Broken signature detection', \&broken_sig_detection); &test_driver('(Sign/verify dir) gpgdir directory verification', \&verify); &test_driver('(Sign/verify dir) Files recursively verified', \&recursively_verified); ### bad password detection &test_driver('(Bad passphrase) detect broken passphrase', \&broken_passphrase); &logr("\n"); if ($successful_tests) { &logr("[+] ==> Passed $successful_tests/$test_num tests " . "against gpgdir. <==\n"); } if ($failed_tests) { &logr("[+] ==> Failed $failed_tests/$test_num tests " . "against gpgdir. <==\n"); } &logr("[+] This console output has been stored in: $logfile\n\n"); exit 0; #======================== end main ========================= sub test_driver() { my ($msg, $func_ref) = @_; my $test_status = 'pass'; &dots_print($msg); if (&{$func_ref}) { &pass(); } else { $test_status = 'fail'; $failed_tests++; } open C, ">> $current_test_file" or die "[*] Could not open $current_test_file: $!"; print C "\nTEST: $msg, STATUS: $test_status\n"; close C; $previous_test_file = $current_test_file; $test_num++; $current_test_file = "$output_dir/$test_num.test"; return; } sub broken_passphrase() { if (not &run_cmd("$gpgdirCmd --gnupg-dir $gpg_dir " . " --pw-file $broken_pw_file --Key-id $key_id -e $data_dir", $NO_APPEND)) { my $found_bad_pass = 0; open F, "< $current_test_file" or die $!; while () { if (/BAD_?PASS/) { $found_bad_pass = 1; } } close F; if ($found_bad_pass) { return 1; } } return &print_errors("[-] Accepted broken passphrase"); } sub encrypt() { if (&run_cmd("$gpgdirCmd $default_args -e $data_dir", $NO_APPEND)) { return 1; } return &print_errors("[-] Directory encryption"); } sub ascii_encrypt() { if (&run_cmd("$gpgdirCmd $default_args --Plain-ascii -e $data_dir", $NO_APPEND)) { return 1; } return &print_errors("[-] Directory encryption"); } sub obf_encrypt() { if (&run_cmd("$gpgdirCmd $default_args -O -e $data_dir", $NO_APPEND)) { return 1; } return &print_errors("[-] Directory encryption"); } sub sign() { if (&run_cmd("$gpgdirCmd $default_args --sign $data_dir", $NO_APPEND)) { return 1; } return &print_errors("[-] Directory signing"); } sub decrypt() { if (&run_cmd("$gpgdirCmd $default_args -d $data_dir", $NO_APPEND)) { return 1; } return &print_errors("[-] Directory decryption"); } sub obf_decrypt() { if (&run_cmd("$gpgdirCmd $default_args -O -d $data_dir", $NO_APPEND)) { return 1; } return &print_errors("[-] Directory decryption"); } sub verify() { if (&run_cmd("$gpgdirCmd $default_args --verify $data_dir", $NO_APPEND)) { return 1; } return &print_errors("[-] Directory verification"); } sub recursively_encrypted() { @data_dir_files = (); find(\&find_files, $data_dir); for my $file (@data_dir_files) { if (-f $file and not ($file =~ m|^\.| or $file =~ m|/\.|)) { unless ($file =~ m|\.gpg$|) { return &print_errors("[-] File $file not encrypted"); } } } return 1; } sub recursively_signed() { @data_dir_files = (); find(\&find_files, $data_dir); for my $file (@data_dir_files) { if (-f $file and not ($file =~ m|^\.| or $file =~ m|/\.|)) { if ($file !~ m|\.asc$|) { unless (-e "$file.asc") { return &print_errors("[-] File $file not signed"); } } } } return 1; } sub recursively_decrypted() { @data_dir_files = (); find(\&find_files, $data_dir); for my $file (@data_dir_files) { if (-f $file and not ($file =~ m|^\.| or $file =~ m|/\.|)) { if ($file =~ m|\.gpg$| or $file =~ m|\.pgp$|) { return &print_errors("[-] File $file not encrypted"); } } } return 1; } sub broken_sig_detection() { move "$data_dir/multi-line-ascii", "$data_dir/multi-line-ascii.orig" or die $!; open F, "> $data_dir/multi-line-ascii" or die $!; print F "bogus data\n"; close F; &run_cmd("$gpgdirCmd $default_args --verify $data_dir", $NO_APPEND); my $found_bad_sig = 0; open F, "< $current_test_file" or die $!; while () { if (/BADSIG/) { $found_bad_sig = 1; } } close F; if ($found_bad_sig) { unlink "$data_dir/multi-line-ascii"; move "$data_dir/multi-line-ascii.orig", "$data_dir/multi-line-ascii" or die $!; return 1; } return &print_errors("[-] Could not find bad signature"); } sub recursively_verified() { ### search for signature verification errors here my $found_bad_sig = 0; open F, "< $previous_test_file" or die $!; while () { if (/BADSIG/) { $found_bad_sig = 1; } } close F; if ($found_bad_sig) { return &print_errors("[-] Bad signature generated"); } ### now remove signature files @data_dir_files = (); find(\&find_files, $data_dir); for my $file (@data_dir_files) { if (-f $file and not ($file =~ m|^\.| or $file =~ m|/\.|)) { if ($file =~ m|\.asc$|) { unlink $file; } } } return 1; } sub ascii_recursively_encrypted() { @data_dir_files = (); find(\&find_files, $data_dir); for my $file (@data_dir_files) { if (-f $file and not ($file =~ m|^\.| or $file =~ m|/\.|)) { unless ($file =~ m|\.asc$|) { return &print_errors("[-] File $file not encrypted"); } } } return 1; } sub obf_recursively_encrypted() { @data_dir_files = (); find(\&find_files, $data_dir); for my $file (@data_dir_files) { if (-f $file and not ($file =~ m|^\.| or $file =~ m|/\.|)) { ### gpgdir_1.gpg unless ($file =~ m|gpgdir_\d+\.gpg$|) { return &print_errors("[-] File $file not " . "encrypted and obfuscated"); } } } return 1; } sub ascii_recursively_decrypted() { @data_dir_files = (); find(\&find_files, $data_dir); for my $file (@data_dir_files) { if (-f $file and not ($file =~ m|^\.| or $file =~ m|/\.|)) { if ($file =~ m|\.asc$|) { return &print_errors("[-] File $file not encrypted"); } } } return 1; } sub obf_recursively_decrypted() { @data_dir_files = (); find(\&find_files, $data_dir); for my $file (@data_dir_files) { if (-f $file and not ($file =~ m|^\.| or $file =~ m|/\.|)) { if ($file =~ m|\.asc$|) { return &print_errors("[-] File $file not encrypted"); } } } return 1; } sub skipped_hidden_files_dirs() { @data_dir_files = (); find(\&find_files, $data_dir); for my $file (@data_dir_files) { if ($file =~ m|^\.| or $file =~ m|/\.|) { ### check for any .gpg or .asc extensions except ### for the gpgdir_map_file if ($file =~ m|\.gpg$| or $file =~ m|\.asc$| or $file =~ m|\.pgp$|) { return &print_errors("[-] Encrypted hidden file"); } } } return 1; } sub obf_skipped_hidden_files_dirs() { @data_dir_files = (); find(\&find_files, $data_dir); for my $file (@data_dir_files) { if ($file =~ m|^\.| or $file =~ m|/\.|) { ### check for any .gpg or .asc extensions except ### for the gpgdir_map_file if ($file !~ m|gpgdir_map_file| and ($file =~ m|\.gpg$| or $file =~ m|\.asc$| or $file =~ m|\.pgp$|)) { return &print_errors("[-] Encrypted hidden file"); } } } return 1; } sub find_files() { my $file = $File::Find::name; push @data_dir_files, $file; return; } sub collect_md5sums() { @data_dir_files = (); find(\&find_files, $data_dir); for my $file (@data_dir_files) { if (-f $file) { $md5sums{$file} = md5_base64($file); } } return 1; } sub md5sum_validation() { @data_dir_files = (); find(\&find_files, $data_dir); for my $file (@data_dir_files) { if (-f $file) { if (not defined $md5sums{$file} or $md5sums{$file} ne md5_base64($file)) { return &print_errors("[-] MD5 sum mis-match for $file"); } } } return 1; } sub test_mode() { if (&run_cmd("$gpgdirCmd $default_args --test", $NO_APPEND)) { my $found = 0; open F, "< $current_test_file" or die "[*] Could not open $current_test_file: $!"; while () { if (/Decrypted\s+content\s+matches\s+original/i) { $found = 1; last; } } close F; return 1 if $found; } return &print_errors("[-] Encrypt/decrypt basic --test mode"); } sub perl_compilation() { unless (&run_cmd("perl -c $gpgdirCmd", $NO_APPEND)) { return &print_errors("[-] $gpgdirCmd does not compile"); } return 1; } sub getopt_test() { if (&run_cmd("$gpgdirCmd --no-such-argument", $NO_APPEND)) { return &print_errors("[-] $gpgdirCmd " . "allowed --no-such-argument on the command line"); } return 1; } sub dots_print() { my $msg = shift; &logr($msg); my $dots = ''; for (my $i=length($msg); $i < $PRINT_LEN; $i++) { $dots .= '.'; } &logr($dots); return; } sub print_errors() { my $msg = shift; &logr("fail ($test_num)\n$msg\n"); if (-e $current_test_file) { &logr(" STDOUT and STDERR available in: " . "$current_test_file file.\n"); open F, ">> $current_test_file" or die "[*] Could not open $current_test_file: $!"; print F "MSG: $msg\n"; close F; } return 0; } sub run_cmd() { my ($cmd, $append) = @_; if ($append == $APPEND) { open F, ">> $current_test_file" or die "[*] Could not open $current_test_file: $!"; print F "CMD: $cmd\n"; close F; } else { open F, "> $current_test_file" or die "[*] Could not open $current_test_file: $!"; print F "CMD: $cmd\n"; close F; } my $rv = ((system "$cmd >> $current_test_file 2>&1") >> 8); if ($rv == 0) { return 1; } return 0; } sub prepare_results() { my $rv = 0; die "[*] $output_dir does not exist" unless -d $output_dir; die "[*] $logfile does not exist, has gpgdir_test.pl been executed?" unless -e $logfile; if (-e $tarfile) { unlink $tarfile or die "[*] Could not unlink $tarfile: $!"; } ### create tarball system "tar cvfz $tarfile $logfile $output_dir"; print "[+] Test results file: $tarfile\n"; if (-e $tarfile) { $rv = 1; } return $rv; } sub setup() { $|++; ### turn off buffering die "[*] $conf_dir directory does not exist." unless -d $conf_dir; unless (-d $output_dir) { mkdir $output_dir or die "[*] Could not mkdir $output_dir: $!"; } die "[*] Password file $pw_file does not exist" unless -f $pw_file; die "[*] Broken password file $broken_pw_file does not exist" unless -f $broken_pw_file; die "[*] $data_dir/multi-line-ascii file does not exist" unless -f "$data_dir/multi-line-ascii"; for my $file (glob("$output_dir/cmd*")) { unlink $file or die "[*] Could not unlink($file)"; } for my $file (glob("$output_dir/*.test")) { unlink $file or die "[*] Could not unlink($file)"; } for my $file (glob("$output_dir/*.warn")) { unlink $file or die "[*] Could not unlink($file)"; } for my $file (glob("$output_dir/*.die")) { unlink $file or die "[*] Could not unlink($file)"; } die "[*] $gpgdirCmd does not exist" unless -e $gpgdirCmd; die "[*] $gpgdirCmd not executable" unless -x $gpgdirCmd; if (-e $logfile) { unlink $logfile or die $!; } return; } sub pass() { &logr("pass ($test_num)\n"); $successful_tests++; return; } sub logr() { my $msg = shift; print STDOUT $msg; open F, ">> $logfile" or die $!; print F $msg; close F; return; } signing-party-2.12/gpgdir/test/output/000077500000000000000000000000001500571021200200155ustar00rootroot00000000000000signing-party-2.12/gpgdir/test/output/README000066400000000000000000000012641500571021200207000ustar00rootroot00000000000000This directory is used by the gpgdir test suite to store test output (both stdout and stderr) from various tests against gpgdir. The gpgdir test suite creates files in this directory with a ".N" extension according to each test number. The files in this directory are useful for debugging purposes, and if there is a problem running gpgdir on a particular system then the information in this directory along with the output of the test suite may provide a clues as to why. If gpgdir appears to not be working properly and you want additional help to diagnose the problem, you can tar up the output/ directory and send it to Michael Rash at the following email address: mbr@cipherdyne.org. signing-party-2.12/gpglist/000077500000000000000000000000001500571021200156735ustar00rootroot00000000000000signing-party-2.12/gpglist/Makefile000066400000000000000000000001261500571021200173320ustar00rootroot00000000000000MAN = gpglist.1 all: $(MAN) %.1: % pod2man $< > $@ install: clean: rm -f $(MAN) signing-party-2.12/gpglist/gpglist000077500000000000000000000140741500571021200173000ustar00rootroot00000000000000#!/usr/bin/perl # small script to show in an intuitive way who signed which of your user ids # # Copyright (c) 2004 Uli Martens # Copyright (c) 2005 Peter Palfrader # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # 3. The name of the author may not be used to endorse or promote products # derived from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. =pod =head1 NAME gpglist -- show who signed which of your UIDs =head1 SYNOPSIS =over =item B [B<--signer=>I] [B<--show-revoked>] I =back =head1 DESCRIPTION B takes a keyid and creates a listing showing who signed I's user IDs. $ gpglist 6D8ABE71 +----- 1 Christoph Berg | +-- 2 Christoph Berg 1 2 x 7929AB90F7AC3AF0 Martin Helas x x 29BE5D2268FD549F Martin Michlmayr x 7DDB2B8DB4B462C5 Martin Wanke By default only non-revoked identities are listed, but it can be overridden it with B<--show-revoked>. One or more B<--signer> option can be used to limit signers to the matching keys. See the GnuPG manual for the different ways to specify a key or user ID. The path to the gpg binary can be specified with the I environment variable (default: C). =head1 AUTHORS =over =item Uli Martens =item Peter Palfrader =back =head1 SEE ALSO gpgsigs(1), gpg(1), caff(1). =cut use strict; use warnings; use English '-no_match_vars'; use Getopt::Long; my $now = time; my $show_revoked; my @signers; sub usage() { die "Usage: $PROGRAM_NAME [--signer=] [--show-revoked] \n"; } sub gpg(@) { my @cmd = ($ENV{GNUPGBIN} // 'gpg', qw/--no-auto-check-trustdb --fixed-list-mode --with-colons/, @_); my $pid = open my $fh, '-|', @cmd or die "gpg failed: $!"; return ($fh, $pid); } GetOptions("show-revoked" => \$show_revoked, 'signer=s@' => \@signers) or usage(); usage() unless @ARGV and $#ARGV == 0; my $key = shift @ARGV; my @signing_keys; if (@signers) { foreach (@signers) { if (/^(?:0x)?(\p{AHex}{16})$/) { push @signing_keys, uc $1; } elsif (/^(?:\p{AHex}{40}|(?:\p{AHex}{4} ){5}(?: \p{AHex}{4}){5})$/) { tr/ //d; push @signing_keys, uc (substr y/ //dr, -16); } else { my ($fh, $pid) = gpg(qw/--list-keys --/, $_); while (<$fh>) { push @signing_keys, $1 if /^pub:(?:[^:]*:){3}([0-9A-F]{16}):/; } waitpid $pid, 0; die "gpg exited with value ".($? >> 8)."\n" if $? > 0; close $fh; } } } my ($SIGS) = gpg(qw/--list-options show-sig-subpackets --list-sigs/, $key); my ($uid, $id) = ('', ''); my (%uids, @uids); my %sigs; my %revs; my %ids; my $longkey; while (<$SIGS>) { if (/^uid:(?:[^:]*:){6}([0-9A-F]{40}):[^:]*:([^:]+)/) { $uid = $1; # use the hash to have proper distinction between UATs push @uids, $uid; # preserve the order $uids{$uid} = $2; } elsif (/^sig:(?:[^:]*:){3}([0-9A-F]{16}):(\d+):(\d*):(?:[^:]*:){2}([^:]+):(1[0-3fF])[lx](?::.*)?$/) { $id = $1; next if $3 ne '' and $3 < $now; # expired next if lc $5 eq '1f'; # direct-key signature $ids{$id} = $4; # keep only the most recent sig (a more recent sig might appear anywhere in the list) $sigs{$id}->{$uid} = $2 unless defined $sigs{$id}->{$uid} and ($sigs{$id}->{$uid} < 0 or # non revocable sig $sigs{$id}->{$uid} > $2); } elsif (/^spk:7:1:1:%00$/) { # non-revocable signature $sigs{$id}->{$uid} = -1; } elsif (/^rev:(?:[^:]*:){3}([0-9A-F]{16}):(\d+):(?:[^:]*:){4}30x(?:,\p{AHex}{2})?(?::.*)?$/) { $revs{$1}->{$uid} = $2; } elsif (/^uat:(?:[^:]*:){6}([0-9A-F]{40}):/) { $uid = $1; push @uids, $uid; # preserve the order $uids{$uid} = "Photo ID"; # XXX [jpeg image of size ...] } elsif (/^pub:(?:[^:]*:){3}([0-9A-F]{16}):/) { $longkey = $1; } elsif (/^sub:/) { last; } elsif (!/^(?:fpr|tru|rvk|spk):/) { print STDERR "hi, i'm a bug. please report me to my owner\n"; die "input: $_, key: $key"; } } close $SIGS; sub is_revoked($;$) { my $uid = shift; my $keyid = shift // $longkey; defined $revs{$keyid}->{$uid} and defined $sigs{$keyid}->{$uid} and $sigs{$keyid}->{$uid} > 0 and # < 0 means non-revocable $revs{$keyid}->{$uid} > $sigs{$keyid}->{$uid} ? 1 : 0; } @uids = grep { !is_revoked($_) } @uids unless $show_revoked; for ( my $a=0; $a <= $#uids; $a++ ) { printf "| " x $a . "+--" . "---" x ($#uids-$a) . (is_revoked($uids[$a]) ? 'R' : ' ') . "%2i $uids{$uids[$a]}\n", $a+1; } for ( my $a=0; $a <= $#uids; $a++ ) { printf "%-2i ", $a+1; } print "\n"; for my $id (sort {$ids{$a} cmp $ids{$b}} keys %ids) { next unless !@signers or grep {$id eq $_} @signing_keys; foreach my $uid (@uids) { my $x = is_revoked($uid,$id) ? 'R' : defined $sigs{$id}->{$uid} ? 'x' : ' '; print $x.' '; } print "$id $ids{$id}\n"; } signing-party-2.12/gpgparticipants/000077500000000000000000000000001500571021200174215ustar00rootroot00000000000000signing-party-2.12/gpgparticipants/gpgparticipants000077500000000000000000000101511500571021200225440ustar00rootroot00000000000000#!/bin/sh # Prepare a printable list of keysigning participants. # Useful for the party organiser. # # License: GPLv2 or later # Copyright Philippe Teuwen 2008 usage() { cat <<- EOF Usage: $0 [-a HASHES|--algorithm HASHES] input output datestring organizer title Use a single hyphen-minus (-) in place of input to read from STDIN (resp. of output to write to STDOUT). Example: echo 9AD7E3DB 54C12701 |\\ $0 - ksp-file.txt "20080222 1100" "My Name " "my party 08" EOF exit ${1:-0} } # Handle options and arguments ############################## # Use getopt to validate and normalize options and arguments, # then reinject them as main arguments OPTS=$(getopt -o a:h -l algorithm:,help -n $0 -- "$@") || usage 1 eval set -- "$OPTS" # Default options algos="SHA256,RIPEMD160" # Parse options while [ "$1" != -- ]; do case "$1" in -a|--algorithm) algos="$2"; shift 2;; -h|--help) usage 0; shift;; *) usage 1;; esac done # Get rid of the '--' left before the arguments shift # Five arguments should remain [ $# -eq 5 ] || usage 1 algos="$(printf "%s" "$algos" | tr '[:lower:]' '[:upper:]')" halgos="$algos" algos="$(printf "%s" "$algos" | tr ',' ' ')" for algo in $algos; do # Ensure this is a valid algo ${GNUPGBIN:-gpg} --print-md "$algo" /dev/null >/dev/null || exit $? done [ "$1" = "-" ] || { exec < "$1"; } output="$2" date="$3" org="$4" title="$(printf "%s" "$5" | tr a-z A-Z | sed 's/\(.\)/\1 /g')" [ "$output" = - ] && output=/path/to/ksp-file.txt || { exec > "$output"; } # Date of event LC_ALL=C date --date="$date" +"%A, %B %e, %Y; %H:%M" # Organiser contact printf "%80s\n\n\n" "$org" # Title printf "%*s\n\n" $(((72+$(printf "%s" "$title"|wc -c))/2)) "$title" # Header cat < 0: yield s[0:n] s = s[n:] out = " ".join([ " ".join(inpacks(octp, 4)) for octp in inpacks(s, 8)]) return out def range_hex(length, fixed_prefix=''): """Give all hex-strings from 00...0 until ff...f of given length.""" if length == 0: yield "" elif len(fixed_prefix) > 0: prefix = fixed_prefix[0] for postfix in range_hex(length-1, fixed_prefix[1:]): yield prefix + postfix elif length == 1: for c in hexdigits: yield c elif length > 1: for prefix in range_hex(length-1): for postfix in range_hex(1): yield prefix + postfix def usage(): """Print --help text""" print("""Usage: {0} {0} --help {0} -h Takes a file produced by gpgparticipants as and trys to fill in some digits into the SHA256 field such that the resulting list actually has a SHA256 checksum that starts with those digits. Whenever a match is found a file with the digits filled in is written to `.DIGITS`. OPTIONS: --fastforward If a match is found of given length and --fastforward is given then the program immediately jumps to the next length. --min-length NUM Start search with given length. --max-length NUM Stop search with given length. """.format(sys.argv[0])) if __name__ == "__main__": fastforward = False minlength = 1 maxlength = 32 prefix = '' optlist, args = getopt.getopt(sys.argv[1:], 'h', ['fastforward', 'min-length=', 'max-length=', 'prefix=', 'help']) for o, a in optlist: if o in ("-h", "--help"): usage() exit(0) elif o in ("--fastforward"): fastforward = True elif o in ("--min-length"): minlength = int(a) elif o in ("--max-length"): maxlength = int(a)+1 elif o in ("--prefix"): prefix = a.lower() if len(args) < 2: print("You need to give two filenames.", file=sys.stderr) exit(1) if not all(c in hexdigits for c in prefix): print("Invalid prefix.", file=sys.stderr) exit(1) emptyfile = open(args[0]).read() idx = emptyfile.find("SHA256 Checksum:") idx = emptyfile.find("_", idx) for l in range(minlength, maxlength): print("Looking at length", l) for h in range_hex(l, prefix): H = insertspaces(h.upper()) filledfile = emptyfile[:idx] + H + emptyfile[idx+len(H):] actual = hashlib.sha256(filledfile.encode('utf8')).hexdigest() if actual[:len(h)] == h: print ("Found: ", H) open(args[1] + "." + h, "w").write(filledfile) if fastforward: break signing-party-2.12/gpgparticipants/gpgparticipants-prefill.1000066400000000000000000000023201500571021200243320ustar00rootroot00000000000000.TH GPGPARTICIPANTS-PREFILL 1 "October 17, 2013" .SH NAME gpgparticipants-prefill \- insert checksum-digits in a gpgparticicpants' form .SH SYNOPSIS .B gpgparticipants-prefill [\fIOPTIONS\fP] \fIEMPTYLIST\fP \fIFILLEDLIST\fP .SH DESCRIPTION .B gpgparticipants-prefill takes a file produced by \fBgpgparticipants\fP (\fIEMPTYLIST\fP) and tries to fill in some digits into the SHA256 field such that the resulting list actually has a SHA256 checksum that starts with those digits. In other words, it tries to produce a file that hashes to a checksum that is partially written down in the file. Whenever a match is found, a file with the digits filled in is written to \fIFILLEDLIST\fP\fB.\fP\fIDIGITS\fP. .SH OPTIONS .IP "\fB\-h\fP, \fB\-\-help\fP" 8 Print the usage text. .IP "\fB\-\-fastforward\fP" 8 If a match is found of some length, immediately jump to the next length. .IP "\fB\-\-min-length\fP \fINUM\fP" 8 Start search with given length. .IP "\fB\-\-max-length\fP \fINUM\fP" 8 Stop search after the given length. .IP "\fB\-\-prefix\fP \fIPREFIX\fP" 8 Only consider hex strings starting with \fIPREFIX\fP. .SH SEE ALSO .BR gpgparticipants (1) .SH AUTHOR This manual page was written by Stefan Huber . signing-party-2.12/gpgparticipants/gpgparticipants.1000066400000000000000000000030041500571021200226770ustar00rootroot00000000000000.TH GPGPARTICIPANTS 1 "March 05, 2008" .SH NAME gpgparticipants \- generate paper list for keysigning party .SH SYNOPSIS .B gpgparticipants .RB [\fIOPTIONS\fR] {input|-} {output|-} datestring organizer title .SH DESCRIPTION .B gpgparticipants is targeted at organisers of a keysigning party that uses the Zimmermann-Sassaman key-signing protocol. It creates a file with all the keys from a list of participators in your keysigning party. You give it a list with key ID's and some general variables, and it will generate a file you can publish. You publish this list; participants will download and print this file to take with them to the party. .SH OPTIONS .TP .BI -a \fIHASHES\fR,\ \fB\-\-algorithm=\fIHASHES\fR For each algorithm in \fIHASHES\fR, a comma-separated list of digest algorithms supported by \fBgpg\fR(1), add a placeholder for the digest value (to be filled by the participants) to the output file. Default: SHA256,RIPEMD160. .SH ENVIRONMENT .TP 13 .I HOME Set the default home directory. .TP 13 .I GNUPGBIN Set the gpg binary. Default: "gpg". .TP 13 .I GNUPGHOME Set the default working directory for gpg. Default: "~/.gnupg". .SH EXAMPLES To create a KSP file with all keys present in the keyring: gpg \-\-with-colons \-\-fingerprint | sed \-n "/^pub:/ {n; /^fpr:/p}" | cut \-sd: \-f10 | \\ gpgparticipants \- ksp-file.txt "20080222 1100" "My Name " "my party 08" .SH SEE ALSO .BR gpg (1), gpgsigs (1) .SH AUTHOR This manual page was written by Thijs Kinkhorst . signing-party-2.12/gpgsigs/000077500000000000000000000000001500571021200156655ustar00rootroot00000000000000signing-party-2.12/gpgsigs/Makefile000066400000000000000000000004501500571021200173240ustar00rootroot00000000000000all: gpgsigs.1 gpgsigs.1: gpgsigs pod2man $< > $@ install: install -D gpgsigs $(DESTDIR)/usr/bin/gpgsigs install -d $(DESTDIR)/usr/share/doc/signing-party/examples/gpgsigs install -m644 gpgsigs-lt2k5*.txt \ $(DESTDIR)/usr/share/doc/signing-party/examples/gpgsigs clean: rm -f gpgsigs.1 signing-party-2.12/gpgsigs/gpgsigs000077500000000000000000000453371500571021200172720ustar00rootroot00000000000000#!/usr/bin/perl # See the pod documentation at the end of this file for author, # copyright, and licence information. # # Changelog: # 0.1 # 0.2 2005-05-14 cb: # * use the user's normal keyring to find signatures # * support for multiple user keys # * better charset conversion # * pod documentation # see the Debian changelog for further changes. my $VERSION = '@@VERSION@@'; use strict; use warnings; use Encode (); use I18N::Langinfo 'langinfo'; use English '-no_match_vars'; use IPC::Open3; use Getopt::Long; use File::Temp; use IO::Handle; use IO::Select; use GnuPG::Interface; sub version($) { my ($fd) = @_; print $fd < (c) 2004, 2005 Peter Palfrader (c) 2004, 2005, 2006, 2007 Christoph Berg (c) 2014, 2015 Guilhem Moulin EOF } sub usage($$) { my ($fd, $error) = @_; version($fd); print $fd <] [] keyid is a long or short keyid (e.g. DE7AAF6E94C09C7F or 94C09C7F) or a key fingerprint separate multiple keyids with ',' -r call gpg --recv-keys before proceeding -f convert from charset -t convert UIDs to charset in output --refresh regenerate UID lists on keys --latex generate LaTeX output including photo IDs EOF exit $error; } my ($fromcharset, $charset, $recv_keys, $refresh, $latex); Getopt::Long::config('bundling'); GetOptions( '-f=s' => \$fromcharset, '-t=s' => \$charset, r => \$recv_keys, refresh => \$refresh, latex => \$latex, help => sub { usage(*STDOUT, 0); }, version => sub { version(*STDOUT); exit 0;}, ) or usage(*STDERR, 1); # charset conversion $fromcharset //= langinfo(I18N::Langinfo::CODESET()); $charset //= langinfo(I18N::Langinfo::CODESET()); my $locale = Encode::find_encoding($charset); # parse options my @mykeys = split /,/, uc(shift @ARGV); my $keytxt = (shift @ARGV) || usage(*STDERR, 1); my $outfile = (shift @ARGV) || '-'; map { y/ //d if /^(?:[0-9A-F]{4} ){5}(?: [0-9A-F]{4}){5}$/; # remove spaces in fprs /^[0-9A-F]{40}$/ ? s/.{24}// : s/^0x//i; } @mykeys; if (!@mykeys || scalar @ARGV) { usage(*STDERR, 1); } foreach my $falsekey (grep { $_ !~ /^([0-9A-F]{16}|[0-9A-F]{8})$/ } @mykeys) { print STDERR "Invalid keyid $falsekey given\n"; usage(*STDERR, 1); } -r $keytxt or die ("$keytxt does not exist\n"); # get list of keys in file (from fingerprints if available) my (@keys, @shortkeys); open (TXT, '<', $keytxt) or die ("Cannot open $keytxt\n"); while () { if ( m/^pub {2,}[^ \/]+\/(?:0x)?([0-9A-F]{8}|[0-9A-F]{16}) [0-9]{4}-[0-9]{2}-[0-9]{2}/ ) { push @shortkeys, $1; } elsif ( m/^ {5,}(?:Key fingerprint = )?((?:[0-9A-F]{4} ){5}(?: [0-9A-F]{4}){5}|[0-9A-F]{40})$/ ) { push @keys, substr ($1 =~ y/ //dr, -16); } } close TXT; @keys = @shortkeys unless @keys; # get all known signatures if ($recv_keys) { print STDERR "Requesting keys from keyserver\n"; system $ENV{GNUPGBIN} // 'gpg', '--recv-keys', @keys; } my $now = time; print STDERR "Running --list-sigs, this may take a while "; my $photos = $latex ? File::Temp::->new(TMPDIR => 1) : '/dev/null'; my $gpg = GnuPG::Interface::->new(); $gpg->call( $ENV{GNUPGBIN} ) if defined $ENV{GNUPGBIN}; # we need --attribute-{fd,file} and --status-{fd,file} to get the # correct attribute size $gpg->options->hash_init( 'extra_args' => [ '--attribute-file', $photos, qw/ --list-options show-sig-subpackets --no-auto-check-trustdb --fixed-list-mode --with-colons/ ] , 'meta_interactive' => 0 ); my $stdout = IO::Handle::->new(); my $status = IO::Handle::->new(); my $handles = GnuPG::Handles::->new( stdout => $stdout, status => $status ); my $pid = $gpg->list_sigs( handles => $handles, command_args => [ @mykeys, @keys ] ); $_->blocking(0) for ($stdout, $status); my $output = IO::Select::->new(); $output->add($stdout, $status); my (%keys, %uids, @photos, %sigs, %revs); my ($key, $uid, $sig); # current context my ($oldstdout, $oldstatus) = ('', ''); while ($output->count() > 0) { foreach my $fd (@{(IO::Select::select($output))[0]}) { # reader if ($fd->eof) { $output->remove($fd); close $fd; next; } if ($fd == $stdout) { while (<$fd>) { if ($oldstdout) { # prepend unfinished output $_ = $oldstdout . $_; $oldstdout = ''; } if (!/\n\z/) { # there is more coming $oldstdout = $_; next; } chomp; undef $sig unless /^spk:/; if (/^pub:([^:]+):(?:[^:]*:){2}([0-9A-F]{16}):(?:[^:]*:){6}([^:]+)/) { undef $uid; $key = $2; if ($1 =~ /[ir]/ or $3 =~ /D/ ) { warn "Ignoring unusable key $key.\n"; undef $key; last; } $keys{$key} = []; print STDERR '.'; @mykeys = map { substr($key,-8) eq $_ ? $key : $_ } @mykeys; next; } next unless $key; # nothing to do on revoked keys if (/^(uid|uat):([^:]+):(?:[^:]*:){5}([0-9A-F]{40}):[^:]*:([^:]+)/) { undef $uid; next if $2 =~ /[er]/; $uid = $3; # use the hash to have proper distinction between UATs if ($1 eq 'uid') { my $text = $4; $text =~ s/\\x(\p{AHex}{2})/ chr(hex($1)) /ge; # --with-colons always outputs UTF-8 $uids{$key}->{$uid} = { type => 'uid', text => $locale->encode(Encode::decode_utf8($text)) }; } else { # we can't rely on $4 for the size: get it from # the status fd instead $uids{$key}->{$uid} = { type => 'uat' }; } push @{$keys{$key}}, $uid; # preserve order next; } next unless defined $uid; # nothing to do on revoked uids and direct-key signatures if (/^sig:(?:[^:]*:){3}([0-9A-F]{16}):(\d+):(\d*):(?:[^:]*:){3}(1[0-3][lx])(?::.*)?$/) { if (!grep { $1 =~ /$_$/ or $key =~ /$_$/ } @mykeys) { $sig = []; # $key is not ours, and the signer isn't us: don't waste resources } else { $sigs{$key}->{$uid}->{$1} //= []; $sig = $sigs{$key}->{$uid}->{$1}; push @$sig, { created => $2, expiring => $3, class => $4, revocable => 1 }; } next; } if (/^spk:7:1:1:%0([01])$/ and @$sig) { # mark the last sig as revocable (1) or not (0) $sig->[$#$sig]->{revocable} = $1; next; } if (/^rev:(?:[^:]*:){3}([0-9A-F]{16}):(\d+):(?:[^:]*:){4}30x(?:,\p{AHex}{2})?(?::.*)?$/) { $revs{$key}->{$uid}->{$1} = $2 # keep only the most recent revocation cert unless defined $revs{$key}->{$uid}->{$1} and $revs{$key}->{$uid}->{$1} > $2; next; } if (/^sub:/) { undef $uid; next; } if (!/^(?:rvk|tru|fpr|spk):/) { # revoke/revoker/trust/fpr warn "Unknown value: '$_', key: ".($key // 'none')."\n"; } } } elsif ($fd == $status) { while (<$fd>) { if ($oldstatus) { # prepend unfinished output $_ = $oldstatus . $_; $oldstatus = ''; } if (!/\n\z/) { # there is more coming $oldstatus = $_; next; } chomp; # see /usr/share/doc/gnupg/DETAILS.gz if (/^\[GNUPG:\] ATTRIBUTE [0-9A-F]{24}([0-9A-F]{16}) (\d+) (\d+) (\d+) (\d+) \d+ \d+ (\d+)$/) { my $ignore = ($3 == 1 and $4 == 1 and $5 == 1) ? 0 : 1; push @photos, {key => $1, size => $2, revoked => $6 & 0x02, ignore => $ignore}; next; } if (!/^\[GNUPG:\] (?:KEYEXPIRED \d+|SIGEXPIRED(?: deprecated-use-keyexpired-instead)?|KEY_CONSIDERED [0-9A-F]{40} \d+)$/) { warn "Unknown value: '$_'"; } } } } } warn "Parsing gpg's output went wrong.\n" if $oldstdout or $oldstatus; waitpid $pid, 0; die if $?; close $_ for ($stdout, $status); my $photosfd; if ($latex) { open $photosfd, '<:raw', $photos or die "Couldn't open: $!"; } # get photo sizes and split $photos if $latex foreach my $photo (@photos) { my $chunk; if ($latex) { my $got = read $photosfd, $chunk, $photo->{size} or die "Couldn't read: $!"; warn "Read $photo->{size} bytes but got $got bytes.\n" if $got != $photo->{size}; } next if $photo->{revoked} or $photo->{ignore}; # ignore revoked attributes my $key = $photo->{key}; my @uats = grep { $uids{$key}->{$_}->{type} eq 'uat' } @{$keys{$key}}; my @found = grep { !defined $uids{$key}->{$_}->{text} } @uats; my $size = $photo->{size} - 16; # remove the header part unless (@found) { warn "No more UAT was found on $key, but there is an image of size $size that belongs to that key.\n"; next; } my $uat = $uids{$key}->{shift @found}; $uat->{text} = "[jpeg image of size $size]"; if ($latex) { $uat->{file} = "${key}_".($#uats - $#found).".jpg"; my $pid = open my $pic, '|-', 'convert', 'jpg:-', '-density', 90, $uat->{file} or die "Can't run convert(1)\n"; print $pic (substr $chunk, 16); close $pic; waitpid $pid, 0; warn "convert(1) exited with value ".($? >> 8)."\n" if $? > 0; } } close $photosfd if $latex; # collapse sigs following RFC 4880 while (my ($key, $uids) = each %sigs) { while (my ($uid, $signers) = each %$uids) { while (my ($signer, $sigs) = each %$signers) { my $lastrev = $revs{$key}->{$uid}->{$signer}; my $class; my @sigs; # remove expired signatures @sigs = grep {!$_->{expiring} or $now < $_->{expiring}} @$sigs; $class = 'X' if !$class and !@sigs; # eXpired # remove revoked signatures (but keep signatures issued after the last revocation cert) @sigs = grep {!$_->{revocable} or $lastrev < $_->{created}} @sigs if $lastrev; $class = 'R' if !$class and !@sigs; # Revoked unless ($class) { # only non-expired, non-revoked sigs are left in @sigs. @sigs = grep { $_->{class} =~ /x$/ } @sigs; # grep for exportable sigs if (@sigs) { # we take the one with the best level @sigs = sort { $a->{class} cmp $b->{class} } @sigs; my $s = pop @sigs; if ($s->{expiring}) { $class = 'x'; } else { $class = $s->{class} =~ s/1([0-3])./$1/r; $class = 'S' if $class eq '0'; } } else { $class = 'L'; } } undef $sigs; $signers->{$signer} = $class; } } } sub getChecksum ($$) { my ($algo, $infile) = @_; my $pid = open MD, '-|', $ENV{GNUPGBIN} // 'gpg', qw/--with-colons --print-md/, $algo, $infile or warn "Can't get gpg $algo digest\n"; my $digest = ; waitpid $pid, 0; warn "gpg(1) exited with value ".($? >> 8)."\n" if $? > 0; close MD; return $1 if $digest and $digest =~ /:([0-9A-F]+):[^:]*$/; } # write out result sub print_tag { my ($key, $uid) = @_; my $r = '('; $r .= $sigs{$key}->{$uid}->{$_} // ' ' for @mykeys; $r .= ')'; return $r; } $key = undef; $uid = undef; my $line = 0; my $keys = 0; print STDERR "\nAnnotating $keytxt, writing into $outfile\n"; open (TXT, '<', $keytxt) or die ("Cannot open $keytxt\n"); $outfile eq '-' ? *WRITE = *STDOUT : open (WRITE, '>', $outfile) or die ("Cannot open $outfile for writing\n"); if ($latex) { print WRITE <<'EOF'; \documentclass{article} \usepackage[margin=2cm]{geometry} \usepackage{alltt} \usepackage{graphicx} \usepackage{ifluatex,ifxetex} \ifnum 0\ifxetex 1\fi\ifluatex 1\fi=0 \usepackage[utf8x]{inputenc} \else \usepackage[log-declarations=false]{xparse} \usepackage{fontspec} \setmonofont{Noto Mono} \ifxetex \usepackage[quiet]{xeCJK} \CJKfontspec{Noto Sans Mono CJK TC} \CJKfontspec{Noto Sans Mono CJK SC} \CJKfontspec{Noto Sans Mono CJK JP} \CJKfontspec{Noto Sans Mono CJK KR} \fi \fi \begin{document} \begin{alltt} EOF } while () { $line++; if (/^(\S+) Checksum:/) { my $algo = uc $1; my $md = getChecksum($algo, $keytxt); if ($md) { my $r = $_; while ( /^(?:.*_)?$/ ) { $line++; $_ = ; $r .= $_; } $r =~ /^(?:\S+) Checksum:\s*([0-9a-fA-F\s]*)/; my $prefill = $1; if ($prefill) { # Replace the prefilled value by underscores for the following replacement my $emptyprefill = $prefill =~ s/[0-9a-fA-F]/_/rg; $r =~ s/\Q$prefill\E/$emptyprefill/; } my $n = $r =~ y/_//; my $k = $n / length($md); if (int($k) * length($md) != $n) { warn "Skipping $algo digest value (invalid fill-in length)\n"; } else { my $w = '_' x $k; $r =~ s/\Q$w\E/%c/g; $r = sprintf($r, unpack ("C*", $md)); if ($prefill and $r !~ /^(?:\S+) Checksum:\s*\Q$prefill\E/i) { warn "Wrong prefill for $algo digest value\n"; } } print WRITE $r; next; } } if ( m/^[0-9]+\s+\[ \] Fingerprint OK/ ){ if ($latex) { if ($keys > 0) { print WRITE "\\end{samepage}\n"; } print WRITE "\\begin{samepage}\n"; ++$keys; } print WRITE; next; } if ( m/^pub {2,}[^ \/]+\/(?:0x)?([0-9A-F]{8}|[0-9A-F]{16}) [0-9]{4}-[0-9]{2}-[0-9]{2}/ ) { $key = $1; print WRITE; next; } if ( m/^ {5,}(?:Key fingerprint = )?((?:[0-9A-F]{4} ){5}(?: [0-9A-F]{4}){5}|[0-9A-F]{40})$/ ) { # derive the keyid from the fingerprint if available $key = substr $1 =~ y/ //dr, -16; print WRITE; my $inc = ""; foreach my $mykey (@mykeys) { foreach my $myuid (@{$keys{$mykey}}) { $inc .= $sigs{$mykey}->{$myuid}->{$key} // ' '; } } print WRITE "[$inc] incoming signatures\n" if $inc =~ /\S/; if ($refresh or $latex) { foreach my $uid (@{$keys{$key}}) { next unless defined $uids{$key}->{$uid}->{text}; my $tag = print_tag($key, $uid); if (!$latex) { print WRITE $tag .' '. $uids{$key}->{$uid}->{text},"\n"; } else { for (my $i = 0; $i < length $uids{$key}->{$uid}->{text}; $i+=78) { print WRITE ($i ? ' ' x length $tag : $tag ), ' ', substr ($uids{$key}->{$uid}->{text}, $i, 78), "\n"; } } if ($latex and $uids{$key}->{$uid}->{type} eq 'uat') { print WRITE "\\begin{flushright}\n"; print WRITE "\\includegraphics[height=3cm]{$uids{$key}->{$uid}->{file}}\n"; print WRITE "\\end{flushright}\n"; } } } next; } if ( m/^uid +(.*)$/ ) { next if $refresh or $latex; my $uid = $locale->encode( Encode::decode($fromcharset, $1) ); unless (defined $key) { warn "key is undefined - input text is possibly malformed near line $line\n"; next; }; my @h = grep { $uids{$key}->{$_}->{text} eq $uid } @{$keys{$key}}; if (@h) { # if there are multiple matches we can't distinguish them print WRITE print_tag($key, pop @h)." $uid\n"; } else { warn "uid '$uid' not found on key $key\n"; print WRITE "(" . (' ' x @mykeys) . ") $uid\n"; } next; } if ( /^(?:-+|_+)$/ and $latex ) { $_ = "\n\\hrule\n"; } print WRITE; } close TXT; if ($latex and $keys > 0) { print WRITE "\\end{samepage}\n"; } print WRITE "Legend:\n"; my $num_myuids = 0; foreach my $i (0 .. $#mykeys) { print WRITE ' (' . ' 'x$i . 'S' . ' 'x(@mykeys-$i-1) . ") signed with $mykeys[$i] $uids{$mykeys[$i]}->{$keys{$mykeys[$i]}->[0]}->{text}\n"; $num_myuids += @{$keys{$mykeys[$i]}}; } my $i = 0; foreach my $mykey (@mykeys) { foreach my $myuid (@{$keys{$mykey}}) { print WRITE " [" . ' 'x$i . 'S' . ' 'x($num_myuids-$i-1) . "] has signed $mykey $uids{$mykey}->{$myuid}->{text}\n"; $i++; } } print WRITE <<'EOF'; Signature types: R Revoked signature X Expired signature L Local (non-exportable) signature x Exportable signature with an expiration date in the future S Non-expiring, exportable signature with certification level 0 1 Non-expiring, exportable signature with certification level 1 2 Non-expiring, exportable signature with certification level 2 3 Non-expiring, exportable signature with certification level 3 EOF if ($latex) { print WRITE <<'EOF'; \end{alltt} \end{document} EOF } close WRITE; __END__ =head1 NAME B - annotate list of GnuPG keys with already done signatures =head1 SYNOPSIS B [I] II<[>B<,>IB<,>I<...>I<]>>I<]> F [F] =head1 DESCRIPTION B was written to assist the user in signing keys during a keysigning party. It takes as input a file containing keys in C format and prepends every line with a tag indicating if the user has already signed that uid. When the file contains C lines and placeholders (C<__ __>), the checksum is inserted. ALGO can be set to the following algorithms: MD5 SHA1 SHA256 or RIPEMD160. =head1 OPTIONS =over =item B<-r> Call I before creating the output. =item B<-f> I Convert F from I. The default is ISO-8859-1. =item B<-t> I Convert UIDs to I. The default is derived from LC_ALL, LC_CTYPE, and LANG, and if all these are unset, the default is ISO-8859-1. =item B<--refresh> Refresh the UID lists per key from gpg. Useful when UIDs were added or revoked since the input text was generated. =item B<--latex> Generate LaTeX output, including photo IDs. Implies B<--refresh>. B This writes jpg files to the current directory. =item I Use this keyid (8 or 16 bytes, or full fingerprint) for annotation. Multiple keyids can be separated by a comma (B<,>). =item F Read input from F. =item F Write output to F. Default is stdout. =back =head1 ENVIRONMENT =over =item I The default home directory. =item I The gpg binary. Default: C<"gpg">. =item I The default working directory for gpg. Default: C<$HOME/.gnupg>. =back =head1 EXAMPLES The following key signing parties are using B: http://www.palfrader.org/ksp-lt2k4.html http://www.palfrader.org/ksp-lt2k5.html =head1 BUGS B is known to change its output format quite often. This version has been tested with gpg 1.4.18 and gpg 2.0.26. YMMV. =head1 SEE ALSO gpg(1), caff(1). =head1 AUTHORS AND COPYRIGHT (c) 2004 Uli Martens (c) 2004, 2005 Peter Palfrader (c) 2004, 2005, 2006, 2007 Christoph Berg (c) 2014, 2015 Guilhem Moulin =head1 LICENSE All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. signing-party-2.12/gpgsigs/gpgsigs-lt2k5-annotated.txt000066400000000000000000000077071500571021200230160ustar00rootroot00000000000000Saturday, June 25th, 2005; 14:00 Room R 2.05 Peter Palfrader ######## ######## ### ######## ######## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ######## ## ## ###### ## ## ## ## ## ######### ## ## ## ## ## ## ## ## ## ## ######## ## ## ## ## ## ## L I N U X T A G K E Y S I G N I N G P A R T Y List of Participants (v 0.0) Here's what you have to do with this file: (1) Print this file to paper. (2) Compute this file's MD5 checksum and optionally also its SHA1 checksum. gpg --print-md md5 ksp-lt2k5.txt (or use md5sum) gpg --print-md sha1 ksp-lt2k5.txt (or use sha1sum) (3) fill in the hash values on the printout. (4) Bring the printout, a pen, and proof of identity to the keysigningparty (and be on time!). MD5 Checksum: 37 90 98 40 22 7D 68 90 1E B1 1C 1B FF 7C 0A 49 [ ] SHA1 Checksum: 4A01 4EC9 1043 8C39 7F5F 4CA8 FC51 AC99 16F8 2FE9 [ ] 001 [ ] Fingerprint OK [ ] ID OK pub 1024D/CD15A883 2002-09-28 Key fingerprint = 02DF 08F5 FD35 6BF2 7F5F 7B83 8921 B5DC CD15 A883 (S ) uid Alexander Schmehl (privat) (S ) uid Alexander Schmehl (private) ( ) uid Alexander Schmehl (knOEpix) ( ) uid Alexander Schmehl (Skolelinux) (S ) uid Alexander Schmehl (university) (S ) uid Alexander Schmehl (university) (S ) uid Alexander Schmehl (unused, but read) 002 [ ] Fingerprint OK [ ] ID OK pub 1024D/00D8CD16 2002-09-28 Key fingerprint = 46CD D292 0692 D5A2 8F81 2E48 0717 74E0 00D8 CD16 (SS) uid Alexander Schmehl (university) (SS) uid Alexander Schmehl (privat) (SS) uid Alexander Schmehl (university) 003 [ ] Fingerprint OK [ ] ID OK pub 1024R/6D8ABE71 1998-07-25 Key fingerprint = 09 9D 09 8F 89 52 24 12 FE C2 31 9D FE F8 5C 03 (SS) uid Christoph Berg (SS) uid Christoph Berg 004 [ ] Fingerprint OK [ ] ID OK pub 1024D/58510B5A 2004-04-17 Key fingerprint = D224 C8B0 7E63 A694 6DA3 2E07 C5AF 774A 5851 0B5A (SS) uid Christoph Berg 005 [ ] Fingerprint OK [ ] ID OK pub 1024D/514B3E7C 2003-07-19 Key fingerprint = 34F8 7997 8BC1 03F0 9C43 F3D7 B375 3E4D 514B 3E7C (SS) uid Florian Ernst ( ) uid Florian Ernst (SS) uid Florian Ernst (SS) uid Florian Ernst 006 [ ] Fingerprint OK [ ] ID OK pub 1024D/7E7B8AC9 2002-05-11 Key fingerprint = DF7D EB2F DB28 FD2B A9FB FA6D 715E D6A0 7E7B 8AC9 (SS) uid Joerg Jaspert (SS) uid Joerg Jaspert (SS) uid Joerg Jaspert (SS) uid Joerg Jaspert 007 [ ] Fingerprint OK [ ] ID OK pub 1024D/94C09C7F 1999-11-10 Key fingerprint = 5B00 C96D 5D54 AEE1 206B AF84 DE7A AF6E 94C0 9C7F ( ) uid Peter Palfrader (SS) uid Weasel (SS) uid Peter Palfrader (SS) uid Peter Palfrader (SS) uid Peter Palfrader 008 [ ] Fingerprint OK [ ] ID OK pub 4096R/C82E0039 2003-03-24 Key fingerprint = 25FC 1614 B8F8 7B52 FF2F 99B9 62AF 4031 C82E 0039 ( ) uid Peter Palfrader ( ) uid Peter Palfrader Legend: (S ) signed with 6D8ABE71 ( S) signed with 58510B5A signing-party-2.12/gpgsigs/gpgsigs-lt2k5.txt000066400000000000000000000074031500571021200210340ustar00rootroot00000000000000Saturday, June 25th, 2005; 14:00 Room R 2.05 Peter Palfrader ######## ######## ### ######## ######## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ######## ## ## ###### ## ## ## ## ## ######### ## ## ## ## ## ## ## ## ## ## ######## ## ## ## ## ## ## L I N U X T A G K E Y S I G N I N G P A R T Y List of Participants (v 0.0) Here's what you have to do with this file: (1) Print this file to paper. (2) Compute this file's MD5 checksum and optionally also its SHA1 checksum. gpg --print-md md5 ksp-lt2k5.txt (or use md5sum) gpg --print-md sha1 ksp-lt2k5.txt (or use sha1sum) (3) fill in the hash values on the printout. (4) Bring the printout, a pen, and proof of identity to the keysigningparty (and be on time!). MD5 Checksum: __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ [ ] SHA1 Checksum: ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ [ ] 001 [ ] Fingerprint OK [ ] ID OK pub 1024D/CD15A883 2002-09-28 Key fingerprint = 02DF 08F5 FD35 6BF2 7F5F 7B83 8921 B5DC CD15 A883 uid Alexander Schmehl (privat) uid Alexander Schmehl (private) uid Alexander Schmehl (knOEpix) uid Alexander Schmehl (Skolelinux) uid Alexander Schmehl (university) uid Alexander Schmehl (university) uid Alexander Schmehl (unused, but read) 002 [ ] Fingerprint OK [ ] ID OK pub 1024D/00D8CD16 2002-09-28 Key fingerprint = 46CD D292 0692 D5A2 8F81 2E48 0717 74E0 00D8 CD16 uid Alexander Schmehl (university) uid Alexander Schmehl (privat) uid Alexander Schmehl (university) 003 [ ] Fingerprint OK [ ] ID OK pub 1024R/6D8ABE71 1998-07-25 Key fingerprint = 09 9D 09 8F 89 52 24 12 FE C2 31 9D FE F8 5C 03 uid Christoph Berg uid Christoph Berg 004 [ ] Fingerprint OK [ ] ID OK pub 1024D/58510B5A 2004-04-17 Key fingerprint = D224 C8B0 7E63 A694 6DA3 2E07 C5AF 774A 5851 0B5A uid Christoph Berg 005 [ ] Fingerprint OK [ ] ID OK pub 1024D/514B3E7C 2003-07-19 Key fingerprint = 34F8 7997 8BC1 03F0 9C43 F3D7 B375 3E4D 514B 3E7C uid Florian Ernst uid Florian Ernst uid Florian Ernst uid Florian Ernst 006 [ ] Fingerprint OK [ ] ID OK pub 1024D/7E7B8AC9 2002-05-11 Key fingerprint = DF7D EB2F DB28 FD2B A9FB FA6D 715E D6A0 7E7B 8AC9 uid Joerg Jaspert uid Joerg Jaspert uid Joerg Jaspert uid Joerg Jaspert 007 [ ] Fingerprint OK [ ] ID OK pub 1024D/94C09C7F 1999-11-10 Key fingerprint = 5B00 C96D 5D54 AEE1 206B AF84 DE7A AF6E 94C0 9C7F uid Peter Palfrader uid Weasel uid Peter Palfrader uid Peter Palfrader uid Peter Palfrader 008 [ ] Fingerprint OK [ ] ID OK pub 4096R/C82E0039 2003-03-24 Key fingerprint = 25FC 1614 B8F8 7B52 FF2F 99B9 62AF 4031 C82E 0039 uid Peter Palfrader uid Peter Palfrader signing-party-2.12/gpgwrap/000077500000000000000000000000001500571021200156715ustar00rootroot00000000000000signing-party-2.12/gpgwrap/LICENSE000066400000000000000000000354531500571021200167100ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS signing-party-2.12/gpgwrap/Makefile000066400000000000000000000001521500571021200173270ustar00rootroot00000000000000MAKE=make .PHONY: all clean all: cd src && ${MAKE} all DIET="${DIET}" clean: cd src && ${MAKE} clean signing-party-2.12/gpgwrap/NEWS000066400000000000000000000011351500571021200163700ustar00rootroot00000000000000gpgwrap 0.04-20060904 * new option: -P * in previous versions the --passphrase-fd option was appended after --homedir or --options , now also --homedir= and --options= should be handled correctly gpgwrap 0.03-20050321 * new options: -o, -c * do not always ignore the exit code of child processes as in version 0.02 (see manpage for more) * format of verbose output changed gpgwrap 0.02-20041014 * new options: -V, -F, -v, -i, -a * the passphrase may also be given via stdin, via environment variable or via prompt gpgwrap 0.01-20040601 * first official release signing-party-2.12/gpgwrap/README000066400000000000000000000016731500571021200165600ustar00rootroot00000000000000Author: Karsten Scheibler Homepage: http://unusedino.de/gpgwrap/ eMail: gpgwrap@unusedino.de ======================= [1] GENERAL INFORMATION ======================= See doc/gpgwrap.1 for more ============================ [2] SHORT BUILD INSTRUCTIONS ============================ To build gpgwrap a simple 'make' should be enough. You may build gpgwrap with dietlibc, just set and export the environment variable DIET to the location of your 'diet' binary and run 'make'. Dietlibc is a libc that is optimized for small size, look at http://www.fefe.de/dietlibc/ for more. =================================== [3] SHORT INSTALLATION INSTRUCTIONS =================================== Copy the files from bin/ and doc/ to your favorite directories, for example: chown root bin/* doc/*.1 chgrp root bin/* doc/*.1 (cd bin && tar cf - *) | (cd /usr/bin && tar xvf -) (cd doc && tar cf - *.1) | (cd /usr/man/man1 && tar xvf -) signing-party-2.12/gpgwrap/doc/000077500000000000000000000000001500571021200164365ustar00rootroot00000000000000signing-party-2.12/gpgwrap/doc/gpgwrap.1000066400000000000000000000227061500571021200201760ustar00rootroot00000000000000.ds Q" "" .de Vb .ft CW .nf .ne \\$1 .. .de Ve .ft R .fi .. .TH gpgwrap 1 "gpgwrap 0.04" .SH NAME gpgwrap \- a small wrapper for gpg .SH SYNOPSIS .B gpgwrap \-V .B gpgwrap \-P [\-v] [\-i] [\-a] [\-p \fI\fR] .B gpgwrap \-F [\-v] [\-i] [\-a] [\-c] [\-p \fI\fR] [\-o \fI\fR] [\-\-] \fI\fR [\fI\fR ... ] .B gpgwrap [\-v] [\-i] [\-a] [\-p \fI\fR] [\-o \fI\fR] [\-\-] \fBgpg\fR [gpg options] .SH DESCRIPTION .PP The GNU Privacy Guard (\fBgpg\fR) supplies the option \-\-passphrase\-fd. This instructs \fBgpg\fR to read the passphrase from the given file descriptor. Usually this file descriptor is opened before \fBgpg\fR is executed via \fBexecvp(3)\fR. Exactly that is what \fBgpgwrap\fR is doing. The passphrase may be passed to \fBgpgwrap\fR in 4 ways: .RS .IP * 2 as file path, whereat the passphrase is stored as plain text in the file .IP * 2 it is piped from another program to the stdin of \fBgpgwrap\fR .IP * 2 through the \fBGPGWRAP_PASSPHRASE\fR environment variable .IP * 2 \fBgpgwrap\fR prompts for it .RE With no precautions the first point undermines the secure infrastructure \fBgpg\fR provides. But in pure batch oriented environments this may be what you want. Otherwise if you are willing to enter passphrases once and don't want them to be stored as plain text in a file \fBgpg\-agent\fR is what you are looking for. Another security objection could be the use of the environment variable \fBGPGWRAP_PASSPHRASE\fR which contains the passphrase and may be read by other processes of the same user. .SH OPTIONS .IP "\-V, \-\-version" 8 Print out version and exit. .IP "\-P, \-\-print" 8 Get the passphrase and print it mangled to stdout. .IP "\-F, \-\-file" 8 Read \fBgpg\fR commands from the given files. If \fI\fR is \- it is read from stdin. Exactly one command per line is expected. The given line is handled in the following way: .RS .IP * 2 In the first place the passphrase is mangled. This means that unusual characters are replaced by their backslash escaped octal numbers. .IP * 2 Secondly the mangled passphrase is stored in the environment variable \fBGPGWRAP_PASSPHRASE\fR. .IP * 2 \*(Q"exec gpgwrap \-\- \*(Q" is prepended to each line, before the result is passed as argument to \*(Q"sh \-c\*(Q". .RE .IP "\-h, \-\-help" 8 Print out usage information. .IP "\-v, \-\-verbose" 8 Increase verbosity level. .IP "\-i, \-\-interactive" 8 Always prompt for passphrase (ignores \-p and the environment variable). .IP "\-a, \-\-ask\-twice" 8 Ask twice if prompting for a passphrase. .IP "\-c, \-\-check\-exit\-code" 8 While reading gpg commands from a file, \fBgpgwrap\fR ignores per default the exit code of its child processes. This option enables the check of the exit code. If a child terminates abnormal or with an exit code not equal 0 \fBgpgwrap\fR stops immediately and does return with this exit code. See also section \fBBUGS\fR. .IP "\-p \fI\fR, \-\-passphrase\-file \fI\fR" 8 Read passphrase from \fI\fR. If \fI\fR is \- it is read from stdin. The passphrase is expected to be in plain text. If this option is not given the passphrase will be taken either from the environment variable \fBGPGWRAP_PASSPHRASE\fR or it will be prompted on the controlling tty if the environment variable is not set. .IP "\-o \fI\fR, \-\-option\-name \fI\fR" 8 Specify the name of the \*(Q"\-\-passphrase\-fd\*(Q" option understood by the program to be executed. This is useful if you want to use \fBgpgwrap\fR in combination with other programs than \fBgpg\fR. .SH LIMITATIONS The given passphrase is subject to several limitations depending on the way it was passed to \fBgpgwrap\fR: .RS .IP * 2 There is a size limitation: the passphrase should be not larger than some kilobytes (examine the source code for the exact limit). .IP * 2 \fBgpgwrap\fR allows you to use all characters in a passphrase even \\000, but this does not mean that \fBgpg\fR will accept it. \fBgpg\fR may reject your passphrase or may only read a part of it, if it contains characters like \\012 (in C also known as \\n). .IP * 2 If you set the environment variable \fBGPGWRAP_PASSPHRASE\fR you should take special care with the backslash character, because \fBgpgwrap\fR uses backslash to escape octal numbers, (see option \-F). Therefore write backslash itself as octal number: \\134. .RE .SH EXAMPLES .IP "1." 8 .Vb \&\fBgpgwrap\fR \-p /path/to/a/secret/file \\ \&\fBgpg\fR \-c \-z 0 \-\-batch \-\-no\-tty \\ \& \-\-cipher\-algo blowfish < infile > outfile .Ve Read passphrase from /path/to/a/secret/file and execute \fBgpg\fR to do symmetric encryption of infile and write it to outfile. .IP "2." 8 .Vb \&\fBgpgwrap\fR \-i \-a \\ \&\fBgpg\fR \-c \-z 0 \-\-batch \-\-no\-tty \\ \& \-\-cipher\-algo blowfish < infile > outfile .Ve Same as above except that \fBgpgwrap\fR prompts twice for the passphrase. .IP "3." 8 .Vb \&\fBgpgwrap\fR \-F \-i \- < \*(Q"$HOME/outfile1\*(Q" \&\fBgpg\fR \-\-decrypt \-\-batch \-\-no\-tty < \*(Q"$HOME/infile2\*(Q" > \*(Q"$HOME/outfile2\*(Q" \&\fBgpg\fR \-\-decrypt \-\-batch \-\-no\-tty < \*(Q"$HOME/infile3\*(Q" > \*(Q"$HOME/outfile3\*(Q" \&\fBgpg\fR \-\-decrypt \-\-batch \-\-no\-tty < \*(Q"$HOME/infile4\*(Q" > \*(Q"$HOME/outfile4\*(Q" \&EOL .Ve \fBgpgwrap\fR prompts for the passphrase and executes four instances of \fBgpg\fR to decrypt the given files. .IP "4." 8 .Vb \&\fBGPGWRAP_PASSPHRASE\fR=\*(Q"mysecretpassphrase\*(Q" \&export \fBGPGWRAP_PASSPHRASE\fR \&\fBgpgwrap\fR \-F \-c \-v /tmp/cmdfile1 \- /tmp/cmdfile2 < \*(Q"$HOME/outfile1\*(Q" \&\fBgpg\fR \-\-decrypt \-\-batch \-\-no\-tty < \*(Q"$HOME/infile2\*(Q" > \*(Q"$HOME/outfile2\*(Q" \&\fBgpg\fR \-\-decrypt \-\-batch \-\-no\-tty < \*(Q"$HOME/infile3\*(Q" > \*(Q"$HOME/outfile3\*(Q" \&\fBgpg\fR \-\-decrypt \-\-batch \-\-no\-tty < \*(Q"$HOME/infile4\*(Q" > \*(Q"$HOME/outfile4\*(Q" \&EOL .Ve Same as above except that \fBgpgwrap\fR gets the passphrase via the environment variable, reads commands additionally from other files and checks the exit code of every \fBgpg\fR instance. This means if one \fBgpg\fR command has a non zero exit code, no further commands are executed. Furthermore \fBgpgwrap\fR produces verbose output. .IP "5." 8 .Vb \&\fBGPGWRAP_PASSPHRASE\fR=\*(Q"$(\fBgpgwrap\fR \-P \-i \-a)\*(Q" \&export \fBGPGWRAP_PASSPHRASE\fR \& \&\fBfind\fR . \-maxdepth 1 \-type f | \&while read FILE; do \& FILE2=\*(Q"$FILE.bz2.gpg\*(Q" \& \fBbzip2\fR \-c \*(Q"$FILE\*(Q" | \& \fBgpgwrap\fR \fBgpg\fR \-c \-z 0 \-\-batch \-\-no\-tty \\ \& \-\-cipher\-algo blowfish > \*(Q"$FILE2\*(Q" && \& \fBtouch\fR \-r \*(Q"$FILE\*(Q" \*(Q"$FILE2\*(Q" && \& \fBrm\fR \-f \*(Q"$FILE\*(Q" \&done .Ve Read in passphrase, compress all files in the current directory, encrypt them and keep date from original file. .IP "6." 8 .Vb \&\fBfind\fR . \-maxdepth 1 \-type f \-name '*.bz2.gpg' | \&\fBawk\fR '{ \& printf(\*(Q"gpg \-\-decrypt \-\-batch \-\-no\-tty \-\-quiet \*(Q"); \& printf(\*(Q"\-\-no\-secmem\-warning < %s\\n\*(Q", $0); \& }' | \&\fBgpgwrap\fR \-F \-i \-c \- | \&\fBbzip2\fR \-d \-c \- | \&\fBgrep\fR \-i 'data' .Ve Decrypt all *.bz2.gpg files in the current directory, decompress them and print out all occurrences of data. If you pipe the result to \fBless\fR you get into trouble because \fBgpgwrap\fR and \fBless\fR try to read from the TTY at the same time. In such a case it is better to use the environment variable to give the passphrase (the example above shows how to do this). .IP "7." 8 .Vb \&\fBGPGWRAP_PASSPHRASE\fR=\*(Q"$(\fBgpgwrap\fR \-P \-i \-a)\*(Q" \&export \fBGPGWRAP_PASSPHRASE\fR \& \&\fBgpgwrap\fR \-P | \&\fBssh\fR \-C \-x \-P \-l user host \*(Q" \& \fBGPGWRAP_PASSPHRASE\fR=\\\*(Q"\\$(\fBcat\fR)\\\*(Q" \& ... \& \*(Q" .Ve Prompt for a passphrase twice and write it to the \fBGPGWRAP_PASSPHRASE\fR environment variable. .IP "8." 8 .Vb \&\fBecho\fR \-n \*(Q"Passphrase: \*(Q" \&\fBstty\fR \-echo \&read \fBGPGWRAP_PASSPHRASE\fR \&\fBecho\fR \&\fBstty\fR echo \&export \fBGPGWRAP_PASSPHRASE\fR .Ve Another way to prompt manually for the passphrase. It was needed in combination with older versions of \fBgpgwrap\fR, because they did not upport \-P. Be aware that with this method no automatic conversion to backslash escaped octal numbers takes place. .IP "9." 8 .Vb \&\fBecho\fR \*(Q"mysecretpassphrase\*(Q" | \&\fBgpg\fR \-\-batch \-\-no\-tty \-\-passphrase\-fd 0 \\ \& \-\-output outfile \-\-decrypt infile .Ve Cheap method to give passphrase to \fBgpg\fR without \fBgpgwrap\fR. Note that you can't use stdin to pass a file to \fBgpg\fR, because stdin is already used for the passphrase. .IP "10." 8 .Vb \&\fBgpg\fR \-\-batch \-\-no\-tty \\ \& \-\-passphrase\-fd 3 3< /path/to/a/secret/file \\ \& < infile > outfile .Ve This is a more advanced method to give the passphrase, it is equivalent to Option \-p of \fBgpgwrap\fR. This example should at least work with the bash. .IP "11." 8 .Vb \&\fBgpg\fR \-\-batch \-\-no\-tty \-\-passphrase\-fd 3 \\ \& 3< <(echo \*(Q"mysecretpassphrase\*(Q") \\ \& < infile > outfile .Ve Like above, but the passphrase is given directly. This example should at least work with the bash. .SH BUGS In version 0.02 of \fBgpgwrap\fR the exit code of \fBgpg\fR was only returned if \fBgpgwrap\fR read the passphrase from a file. Since version 0.03, only \-F omits exit code checking by default, but it can be enabled with \-c. .SH "SEE ALSO" \fBgpg\fR, \fBgpg\-agent\fR .SH AUTHOR Karsten Scheibler signing-party-2.12/gpgwrap/src/000077500000000000000000000000001500571021200164605ustar00rootroot00000000000000signing-party-2.12/gpgwrap/src/Makefile000066400000000000000000000007431500571021200201240ustar00rootroot00000000000000CC=${DIET} gcc -s CFLAGS:=$(shell dpkg-buildflags --get CFLAGS) -fomit-frame-pointer CPPFLAGS:=$(shell dpkg-buildflags --get CPPFLAGS) LDFLAGS:=$(shell dpkg-buildflags --get LDFLAGS) STRIP=strip -R .note -R .comment RM=rm -f TARGET=../bin/gpgwrap .PHONY: all clean all: ${TARGET} ${TARGET}: gpgwrap.c [ -d $(dir ${TARGET}) ] || mkdir $(dir ${TARGET}) ${CC} ${CPPFLAGS} ${CFLAGS} ${LDFLAGS} -o ${TARGET} gpgwrap.c ${STRIP} ${TARGET} 2>/dev/null || true clean: ${RM} ${TARGET} signing-party-2.12/gpgwrap/src/gpgwrap.c000066400000000000000000000562431500571021200203050ustar00rootroot00000000000000/**************************************************************************** **************************************************************************** * * gpgwrap.c * **************************************************************************** ****************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include "version.h" #define PROGRAM_NAME "gpgwrap" #define VERSION_STRING PROGRAM_NAME " " VERSION "-" VERSION_DATE #define EXEC_ARGV_SIZE 1024 #define PASSPHRASE_BUFFER_SIZE 0x10000 #define LIST_BUFFER_SIZE 0x10000 #define CMDLINE_MAX_FILES 1024 #define GPGWRAP_MODE_DEFAULT 0 #define GPGWRAP_MODE_VERSION 1 #define GPGWRAP_MODE_FILE 2 #define GPGWRAP_MODE_PRINT 3 static char program_name[] = PROGRAM_NAME; static char environ_name[] = "GPGWRAP_PASSPHRASE"; static int mode = GPGWRAP_MODE_DEFAULT; static int verbose = 0; static int interactive = 0; static int ask_twice = 0; static int check_exit_code = 0; static char *calling_path = NULL; static char *environ_var = NULL; static char *passphrase_file = NULL; static char *option_name = "--passphrase-fd"; static char *files[CMDLINE_MAX_FILES]; static int nfiles = 0; static char **gpg_cmd = NULL; /**************************************************************************** * do_perror ****************************************************************************/ static void do_perror( void) { perror(program_name); exit(1); } /**************************************************************************** * do_error ****************************************************************************/ #define do_error(args...) \ do \ { \ fprintf(stderr, "%s: ", program_name); \ fprintf(stderr, args); \ fprintf(stderr, "\n"); \ exit(1); \ } \ while (0) /**************************************************************************** * do_warning ****************************************************************************/ #define do_warning(args...) \ do \ { \ fprintf(stderr, "%s: ", program_name); \ fprintf(stderr, args); \ fprintf(stderr, "\n"); \ } \ while (0) /**************************************************************************** * do_error_oom ****************************************************************************/ static void do_error_oom( void) { do_error("could not allocate memory"); } /**************************************************************************** * do_error_too_long ****************************************************************************/ static void do_error_too_long( void) { do_error("passphrase too long"); } /**************************************************************************** * do_verbose ****************************************************************************/ #define do_verbose(level, args...) \ do \ { \ if (verbose < level) break; \ fprintf(stderr, "%s[%d]: ", program_name, getpid()); \ fprintf(stderr, args); \ fprintf(stderr, "\n"); \ } \ while (0) /**************************************************************************** * do_verbose_start ****************************************************************************/ #define do_verbose_start(level, args...) \ do \ { \ if (verbose < level) break; \ fprintf(stderr, "%s[%d] ", program_name, getpid()); \ fprintf(stderr, args); \ } \ while (0) /**************************************************************************** * do_verbose_append ****************************************************************************/ #define do_verbose_append(level, args...) \ do \ { \ if (verbose < level) break; \ fprintf(stderr, args); \ } \ while (0) /**************************************************************************** * do_snprintf ****************************************************************************/ #define do_snprintf(string, max, args...) do_snprintf2(snprintf(string, max, args), max) /**************************************************************************** * do_snprintf2 ****************************************************************************/ static int do_snprintf2( int len, int max) { if ((len == -1) || (len >= max)) do_error("do_snprintf() size exceeded"); return (len); } /**************************************************************************** * mangle_passphrase ****************************************************************************/ static int mangle_passphrase( char *buffer, int size, char *mbuffer, int msize) { char c; int i, j, c1; /* * look for "unusual" characters and convert them to * backslash escaped octal numbers */ for (i = j = 0, msize--; i < size; i++) { c = buffer[i]; if (j >= msize) goto error; if ((c < '+') || ((c > ';') && (c < 'A')) || ((c > 'Z') && (c != '_') && (c < 'a')) || ((c > 'z') && (c != '~'))) { c1 = (unsigned char) c; if (j >= msize - 4) goto error; mbuffer[j++] = '\\'; mbuffer[j++] = '0' + (c1 >> 6); mbuffer[j++] = '0' + ((c1 >> 3) & 7); mbuffer[j++] = '0' + (c1 & 7); } else mbuffer[j++] = c; } mbuffer[j] = '\0'; return (j); error: do_error("could not mangle passphrase"); } /**************************************************************************** * unmangle_passphrase ****************************************************************************/ static int unmangle_passphrase( char *buffer, int size) { char c; int i, j, c1, c2, c3; /* replace backslash escaped octal numbers */ for (i = j = 0; j < size; i++) { c = buffer[j++]; if (c == '\\') { if (j > size - 3) goto error; c1 = buffer[j++]; c2 = buffer[j++]; c3 = buffer[j++]; if ((c1 < '0') || (c1 > '3') || (c2 < '0') || (c2 > '7') || (c3 < '0') || (c3 > '7')) goto error; c1 -= '0'; c2 -= '0'; c3 -= '0'; c = (char) (((c1 << 6) | (c2 << 3) | c3) & 0xff); } buffer[i] = c; } return (i); error: do_error("could not unmangle passphrase"); } /**************************************************************************** * read_passphrase ****************************************************************************/ static int read_passphrase( char *buffer, int size) { int fd, len, i; do_verbose(2, "reading passphrase from file '%s'", passphrase_file); if (strcmp(passphrase_file, "-") == 0) fd = STDIN_FILENO; else fd = open(passphrase_file, O_RDONLY); if (fd == -1) do_perror(); for (len = 0; (i = read(fd, buffer, size)) > 0; len += i) { buffer += i; size -= i; if (size == 0) do_error_too_long(); } if (i == -1) do_perror(); if (close(fd) == -1) do_perror(); return (len); } /**************************************************************************** * prompt_passphrase ****************************************************************************/ static int prompt_passphrase( char *buffer, int size) { int len, len2; int fd; struct termios t, tt; char tty[] = "/dev/tty"; char pp[] = "Passphrase: "; char pp2[] = "\nPassphrase (again): "; char *buffer2; /* * don't touch stdin, just open the controlling tty and ask for the * passphrase */ do_verbose(2, "opening '%s' to prompt for passphrase", tty); fd = open(tty, O_RDWR); if (fd == -1) do_perror(); write(fd, pp, strlen(pp)); tcgetattr(fd, &t); tt = t; tt.c_lflag &= ~ECHO; tcsetattr(fd, TCSAFLUSH, &tt); len = read(fd, buffer, size); if (len == -1) do_perror(); if ((ask_twice) && (len < size)) { buffer2 = (char *) alloca(sizeof (char) * size); if (buffer2 == NULL) do_error_oom(); write(fd, pp2, strlen(pp2)); len2 = read(fd, buffer2, size); if (len2 == -1) do_perror(); write(fd, "\n", 1); tcsetattr(fd, TCSAFLUSH, &t); if ((len != len2) || (memcmp(buffer, buffer2, len) != 0)) do_error("passphrases are not the same"); } else { write(fd, "\n", 1); tcsetattr(fd, TCSAFLUSH, &t); /* * if the above read() returns with len == size, we don't * know if there are more bytes, so we assume passphrase is * too long */ if (len >= size) do_error_too_long(); } if (close(fd) == -1) do_perror(); /* ignore trailing \012 */ return (len - 1); } /**************************************************************************** * environ_or_prompt ****************************************************************************/ static int environ_or_prompt( char *buffer, int size) { int len, len2; char *env; env = getenv(environ_name); if ((env != NULL) && (! interactive)) { do_verbose(2, "got passphrase from environment variable: %s=%s", environ_name, env); /* * first unmangle the content of the environment * variable inplace, then clear the memory */ len2 = strlen(env); len = unmangle_passphrase(env, len2); if (len > size) do_error_too_long(); memcpy(buffer, env, len); memset(env, 0, len2); } else len = prompt_passphrase(buffer, size); return (len); } /**************************************************************************** * do_wait ****************************************************************************/ static void do_wait( void) { int status, value = 1; do_verbose(2, "waiting for child"); wait(&status); if (! check_exit_code) return; do_verbose(2, "checking child exit code"); if (! WIFEXITED(status)) goto out; value = WEXITSTATUS(status); if (value == 0) return; do_verbose(2, "child process terminated abnormal, exiting"); out: exit(value); } /**************************************************************************** * do_fork ****************************************************************************/ static int do_fork( char *buffer, int size) { int fds[2], i; /* * parent will write passphrase to the opened pipe, child will * pass the fd to gpg */ if (pipe(fds) == -1) do_perror(); do_verbose(2, "forking"); switch (fork()) { case -1: do_perror(); case 0: /* child */ if (close(fds[1]) == -1) do_perror(); return (fds[0]); default: break; } /* parent */ signal(SIGPIPE, SIG_IGN); if (close(fds[0]) == -1) do_perror(); while (size > 0) { i = write(fds[1], buffer, size); if ((i == -1) && (errno == EPIPE)) break; if (i == -1) do_perror(); buffer += i; size -= i; } if (size > 0) do_warning("only partial passphrase written"); if (close(fds[1]) == -1) do_perror(); do_wait(); return (-1); } /**************************************************************************** * get_passphrase_fd ****************************************************************************/ static int get_passphrase_fd( void) { int fd, len; char buffer[PASSPHRASE_BUFFER_SIZE]; if ((passphrase_file == NULL) || (interactive)) { len = environ_or_prompt(buffer, sizeof (buffer)); fd = do_fork(buffer, len); } else if (strcmp(passphrase_file, "-") == 0) { len = read_passphrase(buffer, sizeof (buffer)); fd = do_fork(buffer, len); } else { do_verbose(2, "opening file '%s' to pass fd", passphrase_file); fd = open(passphrase_file, O_RDONLY); if (fd == -1) do_perror(); } return (fd); } /**************************************************************************** * get_passphrase ****************************************************************************/ static int get_passphrase( char *buffer, int size) { int len; if ((passphrase_file == NULL) || (interactive)) len = environ_or_prompt(buffer, size); else len = read_passphrase(buffer, size); return (len); } /**************************************************************************** * do_putenv ****************************************************************************/ static void do_putenv( char *buffer, int len) { int size, len2; char *old_var; /* * putenv() only stores the given pointer in **environ, so we have * to use malloc here */ size = strlen(environ_name) + (4 * len) + 2; old_var = environ_var; environ_var = (char *) malloc(sizeof (char) * size); if (environ_var == NULL) do_error_oom(); len2 = do_snprintf(environ_var, size, "%s=", environ_name); if ((buffer != NULL) && (len > 0)) mangle_passphrase(buffer, len, &environ_var[len2], size - len2); do_verbose(2, "setting environment variable: %s", environ_var); if (putenv(environ_var) == -1) do_perror(); if (old_var != NULL) free(old_var); } /**************************************************************************** * do_exec ****************************************************************************/ static void do_exec( char **argv, int clear) { if (clear) do_putenv(NULL, 0); if (verbose > 0) { int i; do_verbose_start(1, "executing:"); for (i = 0; argv[i] != NULL; i++) do_verbose_append(1, " %s", argv[i]); do_verbose_append(1, "\n"); } execvp(argv[0], argv); /* only reached if execvp fails */ do_perror(); } /**************************************************************************** * exec_gpg ****************************************************************************/ static void exec_gpg( void) { int fd; int i, j, k; char fd_num[32]; char *argv[EXEC_ARGV_SIZE]; char homedir_eq[] = "--homedir="; char options_eq[] = "--options="; /* * get fd to read passphrase from, parent will return with fd == -1 * after fork */ fd = get_passphrase_fd(); if (fd == -1) return; /* create argv for execvp */ do_snprintf(fd_num, sizeof (fd_num), "%d", fd); for (i = 0, j = 0, k = 1; gpg_cmd[i] != NULL; i++, k--) { /* * check if there is enough space to store option_name * and fd_num */ if (i >= (EXEC_ARGV_SIZE - 4)) do_error("too many gpg arguments specified"); if (strcmp(gpg_cmd[i], option_name) == 0) do_error("gpg command already has a '%s' option", option_name); if (k == 0) { if ((strncmp(gpg_cmd[i], homedir_eq, sizeof (homedir_eq) - 1) == 0) || (strncmp(gpg_cmd[i], options_eq, sizeof (options_eq) - 1) == 0)) k = 1; else if ((strcmp(gpg_cmd[i], "--homedir") == 0) || (strcmp(gpg_cmd[i], "--options") == 0)) k = 2; else { argv[j++] = option_name; argv[j++] = fd_num; } } argv[j++] = gpg_cmd[i]; } if (k >= 0) { argv[j++] = option_name; argv[j++] = fd_num; } argv[j] = NULL; do_exec(argv, 1); } /**************************************************************************** * exec_line ****************************************************************************/ static void exec_line( char *line) { char shell_cmd[LIST_BUFFER_SIZE]; char verbose_string[128] = ""; char *argv[] = { "sh", "-c", NULL, NULL }; int fds[2], i; /* fork a child and disallow it to read stdin from parent */ if (pipe(fds) == -1) do_perror(); do_verbose(1, "forking"); switch (fork()) { case -1: do_perror(); case 0: break; default: /* parent */ if (close(fds[0]) == -1) do_perror(); if (close(fds[1]) == -1) do_perror(); do_wait(); return; } /* child */ if (close(fds[1]) == -1) do_perror(); if (fds[0] != STDIN_FILENO) dup2(fds[0], STDIN_FILENO); /* create argv for execvp */ for (i = 0; i < verbose; i++) { if (strlen(verbose_string) >= sizeof (verbose_string) - 4) break; strcat(verbose_string, " -v"); } do_snprintf(shell_cmd, sizeof (shell_cmd), "exec %s%s -o %s -- %s", calling_path, verbose_string, option_name, line); argv[2] = shell_cmd; do_exec(argv, 0); } /**************************************************************************** * exec_list ****************************************************************************/ static void exec_list( char *path, char *buffer, int len) { int fd; char lbuffer[LIST_BUFFER_SIZE]; int inuse, start, free, nread, llen; char *line, *next_line; /* open file */ do_verbose(1, "reading gpg commands from file: '%s'", path); if (strcmp(path, "-") == 0) fd = STDIN_FILENO; else fd = open(path, O_RDONLY); if (fd == -1) do_perror(); /* export passphrase to environment */ do_putenv(buffer, len); /* read gpg commands */ for (inuse = 0, free = LIST_BUFFER_SIZE; (nread = read(fd, &lbuffer[inuse], free)) > 0; ) { inuse += nread; for (line = lbuffer; (next_line = memchr(line, '\n', inuse)) != NULL; ) { *next_line = '\0'; llen = (int) (next_line - line) + 1; if (llen != strlen(line) + 1) do_error("line contains \\0 character"); exec_line(line); inuse -= llen; line = next_line + 1; } start = (int) (line - lbuffer); if ((start == 0) && (inuse == LIST_BUFFER_SIZE)) do_error("line too long"); if ((start > 0) && (inuse > 0)) memmove(lbuffer, &lbuffer[start], inuse); free = LIST_BUFFER_SIZE - inuse; } /* check for error while read() */ if (nread == -1) do_perror(); if (close(fd) == -1) do_perror(); /* check if there are bytes left */ if (inuse > 0) do_error("last line incomplete"); } /**************************************************************************** * cmdline_fill_space ****************************************************************************/ static void cmdline_fill_space( char *s) { while (*s != '\0') *s++ = ' '; } /**************************************************************************** * cmdline_usage ****************************************************************************/ static void cmdline_usage( void) { char space1[] = VERSION_STRING; char space2[] = PROGRAM_NAME; cmdline_fill_space(space1); cmdline_fill_space(space2); printf(VERSION_STRING " | written by Karsten Scheibler\n" "%s | http://unusedino.de/gpgwrap/\n" "%s | gpgwrap@unusedino.de\n\n" "Usage: %s -V\n" "or: %s -P [-v] [-i] [-a] [-p ]\n" "or: %s -F [-v] [-i] [-a] [-c] [-p ] [-o ]\n" " %s [--] [ ... ]\n" "or: %s [-v] [-i] [-a] [-p ] [-o ]\n" " %s [--] gpg [gpg options]\n\n" " -V print out version\n" " -P get the passphrase and print it mangled to stdout\n" " -F read gpg commands from file\n" " -v be more verbose\n" " -i be interactive, always prompt for passphrase\n" " -a ask twice if prompting for passphrase\n" " -c check exit code of child processes\n" " -p read passphrase from \n" " -o specify name of \"--passphrase-fd\" option\n" " -h this help\n", space1, space1, program_name, program_name, program_name, space2, program_name, space2); exit(0); } /**************************************************************************** * cmdline_check_arg ****************************************************************************/ static char * cmdline_check_arg( char *msg, char *file) { if (file == NULL) do_error("%s expects a file name", msg); return (file); } /**************************************************************************** * cmdline_check_stdin ****************************************************************************/ static char * cmdline_check_stdin( char *msg, char *file) { static int stdin_count = 0; cmdline_check_arg(msg, file); if (strcmp(file, "-") == 0) stdin_count++; if (stdin_count > 1) do_error("%s used stdin although already used before", msg); return (file); } /**************************************************************************** * cmdline_parse ****************************************************************************/ static void cmdline_parse( int argc, char **argv) { char *arg; int args; int ignore = 0; calling_path = argv[0]; for (args = 0, argv++; (arg = *argv++) != NULL; args++) { if ((arg[0] != '-') || (ignore)) { if (mode == GPGWRAP_MODE_FILE) goto get_file; gpg_cmd = argv - 1; break; } else if ((strcmp(arg, "-") == 0) && (mode == GPGWRAP_MODE_FILE)) { get_file: if (nfiles >= CMDLINE_MAX_FILES) do_error("too many files specified"); files[nfiles++] = cmdline_check_stdin("-F/--file", arg); } else if (strcmp(arg, "--") == 0) { ignore = 1; } else if ((strcmp(arg, "-h") == 0) || (strcmp(arg, "--help") == 0)) { cmdline_usage(); } else if (((strcmp(arg, "-V") == 0) || (strcmp(arg, "--version") == 0)) && (args == 0)) { mode = GPGWRAP_MODE_VERSION; } else if (((strcmp(arg, "-F") == 0) || (strcmp(arg, "--file") == 0)) && (args == 0)) { mode = GPGWRAP_MODE_FILE; } else if (((strcmp(arg, "-P") == 0) || (strcmp(arg, "--print") == 0)) && (args == 0)) { mode = GPGWRAP_MODE_PRINT; } else if (mode == GPGWRAP_MODE_VERSION) { goto bad_option; } else if ((strcmp(arg, "-v") == 0) || (strcmp(arg, "--verbose") == 0)) { verbose++; } else if ((strcmp(arg, "-i") == 0) || (strcmp(arg, "--interactive") == 0)) { interactive = 1; } else if ((strcmp(arg, "-a") == 0) || (strcmp(arg, "--ask-twice") == 0)) { ask_twice = 1; } else if ((strcmp(arg, "-p") == 0) || (strcmp(arg, "--passphrase-file") == 0)) { if (passphrase_file != NULL) do_error("-p/--passphrase-file specified more than once"); passphrase_file = cmdline_check_stdin("-p/--passphrase-file", *argv++); } else if (mode == GPGWRAP_MODE_PRINT) { goto bad_option; } else if ((strcmp(arg, "-o") == 0) || (strcmp(arg, "--option-name") == 0)) { option_name = cmdline_check_arg("-o/--option-name", *argv++); } else if (mode != GPGWRAP_MODE_FILE) { goto bad_option; } else if ((strcmp(arg, "-c") == 0) || (strcmp(arg, "--check-exit-code") == 0)) { check_exit_code = 1; } else { bad_option: do_error("unrecognized option '%s'", arg); } } if ((mode == GPGWRAP_MODE_DEFAULT) && (nfiles == 0) && (gpg_cmd == NULL)) do_error("no gpg command specified"); if ((mode == GPGWRAP_MODE_FILE) && (nfiles == 0)) do_error("no files to process"); if ((mode == GPGWRAP_MODE_PRINT) && (nfiles > 0)) do_error("no additional arguments allowed"); if (mode != GPGWRAP_MODE_FILE) check_exit_code = 1; } /**************************************************************************** * main ****************************************************************************/ int main( int argc, char **argv) { /* * we need setlinebuf(), because otherwise do_verbose() output of * parent and child processes may get mixed in some cases */ setlinebuf(stderr); /* parse cmdline */ cmdline_parse(argc, argv); /* do it */ if (mode == GPGWRAP_MODE_VERSION) { printf(VERSION_STRING "\n"); } else if (mode == GPGWRAP_MODE_FILE) { int i, len; char buffer[PASSPHRASE_BUFFER_SIZE]; len = get_passphrase(buffer, sizeof (buffer)); for (i = 0; i < nfiles; i++) exec_list(files[i], buffer, len); } else if (mode == GPGWRAP_MODE_PRINT) { char buffer[PASSPHRASE_BUFFER_SIZE]; char mbuffer[PASSPHRASE_BUFFER_SIZE]; int len; len = get_passphrase(buffer, sizeof (buffer)); mangle_passphrase(buffer, len, mbuffer, sizeof (mbuffer)); printf("%s\n", mbuffer); } else exec_gpg(); /* done */ return (0); } /******************************************************** Karsten Scheibler */ signing-party-2.12/gpgwrap/src/version.h000066400000000000000000000010341500571021200203140ustar00rootroot00000000000000/**************************************************************************** **************************************************************************** * * version.h * **************************************************************************** ****************************************************************************/ #ifndef VERSION_H #define VERSION_H #define VERSION "0.04" #define VERSION_DATE "20060904" #endif /* !VERSION_H */ /******************************************************** Karsten Scheibler */ signing-party-2.12/keyanalyze/000077500000000000000000000000001500571021200163765ustar00rootroot00000000000000signing-party-2.12/keyanalyze/Changelog000066400000000000000000000007171500571021200202150ustar00rootroot00000000000000200203 * Significant improvements to scripts to create HTML output. 200112 * Applied Steve Langasek's patch to include farthest hop statistic and a fix for a one-off bug causing the last hop count not to be displayed. * Cleaned up some packaging, so that a few more of the simple command lines are automated via analyze.sh 200111 * Applied Matt Kraai's patch to use Tarjan's algorighm and increse efficiency somewhat in finding set connectivity. signing-party-2.12/keyanalyze/Makefile000066400000000000000000000013071500571021200200370ustar00rootroot00000000000000LDLIBS=-lpthread CFLAGS:=$(shell dpkg-buildflags --get CFLAGS) all: keyanalyze process_keys pgpring/pgpring keyanalyze: keyanalyze.o process_keys: process_keys.o pgpring/configure: pgpring/configure.ac cd pgpring && autoreconf -f -i pgpring/config.status: pgpring/configure cd pgpring && CFLAGS="${CFLAGS}" ./configure pgpring/pgpring: pgpring/config.status $(MAKE) -C pgpring pgpring install: install pgpring/pgpring $(DESTDIR)/usr/bin install keyanalyze $(DESTDIR)/usr/bin install process_keys $(DESTDIR)/usr/bin clean: [ ! -f pgpring/Makefile ] || $(MAKE) -C pgpring distclean -rm -f *.o core *~ keyanalyze process_keys -rm -f test.pre preprocess.keys keyanalyze.out all.keys -rm -rf output signing-party-2.12/keyanalyze/README000066400000000000000000000012421500571021200172550ustar00rootroot00000000000000OpenPGP key analysis keyanalyze takes a PGP/GnuPG public key ring and analyses the relationships between the keys in it. It produces output suitable for placing on a web site showing which keys have signed which other keys. Its aim is to stimulate awareness of keysigning and help people notice gaps in the Web of Trust that could easily be filled. More documentation later on I hope. For now: make ./analyze.sh path/to/pubring.pgp Output is stored in ./output/ - Be prepared. There is a lot of it. I'd suggest having at least 1GB free if you're processing from the 'full' keyring. (1 million keys or more) Modify the analyze.sh file to meet your needs if necessary. signing-party-2.12/keyanalyze/allkeys.sh000077500000000000000000000005041500571021200204000ustar00rootroot00000000000000#!/bin/bash -- # usage ./analyze.sh path/to/pubring.pgp set -e make # comment these next lines out if you are working with an existing # preprocess.keys file pgpring/pgpring -S -k $1 \ | grep "\(pub\|sig\|rev\|uid\)" \ | sed -e "s/^\([a-z]*\).*:\([0-9A-F]\{16\}\):.*/\1 \2/g" \ -e "s/^uid:.*/uid/" > all.keys signing-party-2.12/keyanalyze/analyze.sh000077500000000000000000000014601500571021200204010ustar00rootroot00000000000000#!/bin/bash -- # usage ./analyze.sh path/to/pubring.pgp set -e make # comment these next lines out if you are working with an existing # preprocess.keys file pgpring -S -k "$1" | process_keys $2 > preprocess.keys # the actual processing of the main report keyanalyze # html beautification and reports and such # comment this out if you don't want all the stuff in the report # at http://dtype.org/keyanalyze/ cat output/msd.txt | sort -k 3 | nl -s ' ' > output/msd-sorted.txt cat output/msd.txt | scripts/top50.pl > output/top50table.html cat scripts/report_top.php output/top50table.html \ scripts/report_bottom.php > output/report.php cat output/msd.txt | scripts/top50.pl -n 1000 > output/top1000table.html cat scripts/1000_top.php output/top1000table.html \ scripts/1000_bottom.php > output/report_1000.php signing-party-2.12/keyanalyze/keyanalyze.1000066400000000000000000000030331500571021200206330ustar00rootroot00000000000000.\" keyanalyze, a program for analysing webs of trust .\" manpage Copyright (C) 2004 Matthew Wilcox .\" .\" 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. .\" .TH keyanalyze 1 .SH NAME keyanalyze \- Web of Trust analysis .SH SYNTAX \fBkeyanalyze\fP [ \fB\-h1\fP ] [ \fB\-i\fP \fIinfile\fP ] [ \fB\-o\fP \fIoutdir\fP ] .SH DESCRIPTION \fIkeyanalyze\fP analyses the web of trust within a group of keys. It takes preprocessed keys as input (see .BR process_keys (1)) and produces an output directory full of statistics about the keys. Usually called like $ pgpring \-S \-k ./keyring.gpg | process_keys > preprocess.keys $ keyanalyze .SH OPTIONS .TP .BI \-i " infile" Read from \fIinfile\fP instead of \fBpreprocess.keys\fP. .TP .BI \-o " outdir" Put the results in \fIoutdir\fP instead of \fBoutput/\fP. The directory will be created if it does not already exist. .TP .BI \-h Print help. .TP .BI \-1 Per default, \fBkeyanalyze\fP writes the output into subdirectories named after the first two characters of the key ID. This options disables this; useful for small keyrings. .SH AUTHORS M. Drew Streib , .br Thomas Roessler , .br Hal J. Burch , .br Matt Kraai , .br Steve Langasek , .br Matthew Wilcox signing-party-2.12/keyanalyze/keyanalyze.c000066400000000000000000000312341500571021200207210ustar00rootroot00000000000000/* keyanalyze.c * Does some analysis of pre-monged pgp keyrings for some interesting data. * Some code (c)2001 M. Drew Streib * Some code (c)2001 Thomas Roessler * Some code (c)2001 Hal J. Burch * Some code (c)2001 Matt Kraai * Some Code (c)2001 Steve Langasek * * You are licenced to use this code under the terms of the GNU General * Public License (GPL) version 2. */ /* some configurables */ static char *infile = "preprocess.keys"; static char *outdir = "output/"; static int outsubdirs = 1; /* create output/12/12345678 or output/12345678 */ #define MAXKEYS 160000 /* MUST be > `grep p preprocess.keys | wc` */ #define MINSETSIZE 10 /* minimum set size we care about for strong sets */ #define MAXHOPS 30 /* max hop count we care about for report */ /* includes */ #include #include #include #include #include #include #include #include /* globals */ struct sig { int id; struct sig *next; }; typedef struct sig sig; struct threadparam { int threadnum; }; typedef struct threadparam threadparam; struct keydata { unsigned int id1; unsigned int id2; sig *to; sig *from; }; struct keydata keys[MAXKEYS]; FILE *fpin,*fpout,*fpstat,*fpsets,*fpmsd; int numkeys = 0; int numsigs = 0; int component[MAXKEYS]; int max_component; int max_size; int reachable[MAXKEYS]; int num_reachable; float meantotal; pthread_mutex_t mean_l; /* declarations */ void AddKey (unsigned char *newid); void AddSig (int src, int dst); void CloseFiles(); int CountSigs(sig *current); unsigned int ConvertFromHex (const unsigned char *c); int GetKeyById(const unsigned char* searchid); void MeanCrawler(int *distset, int id, int len); float MeanDistance(int id, int *hops, int *hophigh, sig **farthest); /* ################################################################# */ /* helper functions, in alpha order */ void AddKey (unsigned char *newid) { struct keydata *key = &keys[numkeys++]; /* assume no dupes for now */ key->id1 = ConvertFromHex(newid); key->id2 = ConvertFromHex(newid+8); } void AddKeyToList(sig **pptr, int id) { while (*pptr) pptr = &(*pptr)->next; *pptr = (sig *) calloc (1,sizeof(sig)); (*pptr)->id = id; } void AddSig (int src, int dst) { /* if GetKeyById returned -1, then we exit here */ if ((src == -1) || (dst == -1)) return; AddKeyToList(&keys[dst].to, src); AddKeyToList(&keys[src].from, dst); numsigs++; } void CloseFiles() { fclose(fpin); fclose(fpout); } int CountSigs(sig *current) { int ret = 0; while (current->next) { current = current->next; ret++; } return ret; } unsigned int ConvertFromHex (const unsigned char *c) { unsigned char buf1[5]; unsigned char buf2[5]; unsigned int ret; buf1[4] = 0; buf2[4] = 0; memcpy (buf1,c,4); memcpy (buf2,c+4,4); ret = strtol(buf1,NULL,16)*65536 + strtol(buf2,NULL,16); return ret; } void DeleteKeyList(sig **pptr) { sig *current = *pptr; while (*pptr) { current = (*pptr)->next; free (*pptr); *pptr = current; } } /* recursive function to mark connected keys in the connected set */ int DFSMarkConnected (int *markset, int id) { sig *psig; int num = 1; /* mark this node, call this function for all subnodes that aren't * marked already */ markset[id] = 1; for (psig = keys[id].from; psig; psig = psig->next) { if (!markset[psig->id]) num += DFSMarkConnected (markset, psig->id); } return num; } int GetKeyById(const unsigned char* searchid) { int i; unsigned int s1,s2; s1 = ConvertFromHex(searchid); s2 = ConvertFromHex(searchid+8); for (i = 0; i < numkeys; i++) { struct keydata *key = &keys[i]; if ((s1 == key->id1) && (s2 == key->id2)) { return i; } } return (-1); } /* new _much_ faster BFS version of MeanCrawler() contributed by * Hal J. Burch */ void MeanCrawler(int *distset, int id, int len) { sig *psig; int queue[MAXKEYS]; int qhead, qtail; memset(queue,0,sizeof(int)*MAXKEYS); queue[0] = id; distset[id] = 0; qhead = 0; qtail = 1; while (qtail > qhead) { id = queue[qhead++]; len = distset[id]; psig = keys[id].to; while (psig) { if ((len+1) < distset[psig->id]) { distset[psig->id] = len+1; queue[qtail++] = psig->id; } psig = psig->next; } } } float MeanDistance(int id, int *hops, int *hophigh, sig **farthest) { int dist[MAXKEYS]; int i; int totaldist = 0; /* init to a large value here, so shortest distance will always be * less */ memset(dist,100,sizeof(int)*MAXKEYS); MeanCrawler(dist,id,0); for (i=0;i *hophigh) { *hophigh = dist[i]; DeleteKeyList(farthest); } if (dist[i] == *hophigh) { AddKeyToList(farthest, i); } } } if (*hophigh > MAXHOPS) *hophigh = MAXHOPS; return ((float)totaldist / max_size); } FILE *OpenFileById(unsigned int id) { char buf[255]; char idchr[9]; sprintf(idchr,"%08X",id); /* first the directory */ buf[0] = '\0'; strcat(buf, outdir); if (outsubdirs) { strncat(buf,idchr,2); mkdir(buf,(mode_t)493); strcat(buf,"/"); } strcat(buf,idchr); return fopen(buf,"w"); } /* ################################################################# */ /* program block functions, not predeclared */ int OpenFiles() { char buf[255]; fpin = fopen(infile, "r"); if (!fpin) return 1; /* create output dir if necessary. this will just fail if it exists */ mkdir(outdir, (mode_t)493); /* status file */ buf[0] = '\0'; strcat(buf, outdir); strcat(buf,"status.txt"); fpstat = fopen(buf,"w"); if (!fpstat) return 1; /* msd output file */ buf[0] = '\0'; strcat(buf, outdir); strcat(buf,"msd.txt"); fpmsd = fopen(buf,"w"); if (!fpmsd) return 1; /* othersets output file */ buf[0] = '\0'; strcat(buf, outdir); strcat(buf,"othersets.txt"); fpsets = fopen(buf,"w"); if (!fpsets) return 1; /* other output file */ buf[0] = '\0'; strcat(buf, outdir); strcat(buf,"other.txt"); fpout = fopen(buf,"w"); if (!fpout) return 1; return 0; } void ParseArgs(int argc, char **argv) { int outdirlen; while (1) { int option = getopt(argc, argv, "hi:o:1"); if (option == -1) break; switch (option) { case 'h': printf ("Usage: %s [-h1] [-i infile] [-o outdir]\n", argv[0]); exit (0); break; case 'i': infile = optarg; break; case 'o': outdir = optarg; outdirlen = strlen(outdir); if (outdir[outdirlen - 1] != '/') { outdir = malloc(outdirlen + 2); memcpy(outdir, optarg, outdirlen); outdir[outdirlen] = '/'; outdir[outdirlen + 1] = '\0'; } break; case '1': outsubdirs = 0; break; } } if (optind < argc) { /* Assume it's infile */ infile = argv[optind]; } } int PrintKeyList(FILE *f, sig *s) { int i = 0; while (s) { struct keydata *key = &keys[s->id]; fprintf(f, " %08X %08X\n", key->id1, key->id2); s = s->next; i++; } return i; } void ReadInput() { unsigned char buf[20]; int currentkey = -1; fprintf(fpstat,"Importing pass 1 (keys)...\n"); while (fread(buf,1,18,fpin) == 18) { if (buf[17] != '\n') continue; if (buf[0] == 'p') { AddKey(buf+1); } } fprintf(fpstat,"done.\n"); fprintf(fpstat,"%d keys imported\n",numkeys); rewind(fpin); fprintf(fpstat,"Importing pass 2 (sigs)...\n"); while (fread(buf,1,18,fpin) == 18) { if (buf[17] != '\n') continue; if (buf[0] == 'p') { currentkey = GetKeyById(buf+1); if (currentkey == -1) { fprintf(fpstat,"Error finding key in pass 2.\n"); exit(EXIT_FAILURE); } } if (buf[0] == 's') { AddSig(GetKeyById(buf+1),currentkey); if ((numsigs%1000) == 0) { fprintf(fpstat,"%d sigs imported...\n",numsigs); fflush(fpstat); } } } fprintf(fpstat,"done.\n"); fprintf(fpstat,"%d sigs imported\n",numsigs); } /* This is intended for later use. As it takes a lot of time for the * signature imports, this will save time for future runs of the program * with the same data set. */ void SaveState() { /* not yet implemented. need to figure out how to best handle the * linked lists of sigs first */ } int dfsnum[MAXKEYS]; int lownum[MAXKEYS]; int removed[MAXKEYS]; int stack[MAXKEYS]; int stackindex; int lastdfsnum; void DFSVisit(int id) { sig *psig; dfsnum[id] = lownum[id] = ++lastdfsnum; stack[stackindex++] = id; for (psig = keys[id].to; psig; psig = psig->next) { int neighbor = psig->id; if (removed[neighbor]) continue; if (!dfsnum[neighbor]) { DFSVisit (neighbor); if (lownum[neighbor] < lownum[id]) lownum[id] = lownum[neighbor]; } else if (dfsnum[neighbor] < lownum[id]) lownum[id] = dfsnum[neighbor]; } if (lownum[id] == dfsnum[id]) { int i, size = 0; do { struct keydata *key; i = stack[--stackindex]; key = &keys[i]; component[i] = id; removed[i] = 1; size++; fprintf(fpsets, "%08X %08X\n", key->id1, key->id2); } while (i != id); fprintf(fpsets, "*** %d keys in this strongly connected set\n\n", size); if (max_size < size) { max_size = size; max_component = id; } } } void TestConnectivity() { int i; for (i = 0; i < numkeys; i++) if (!dfsnum[i]) DFSVisit (i); num_reachable = DFSMarkConnected (reachable, max_component); fprintf(fpstat,"reachable set is size %d\n", num_reachable); fprintf(fpstat,"strongly connected set is size %d\n", max_size); } /* ################################################################# */ /* report functions, sort of top level */ void IndivReport(FILE *fp,int key) { int totalsigsto, totalsigsfrom; /* head of report */ fprintf(fp,"KeyID %08X %08X\n\n", keys[key].id1, keys[key].id2); fprintf(fp,"This individual key report was generated as part of the monthly keyanalyze\n"); fprintf(fp,"report at http://dtype.org/keyanalyze/.\n\n"); fprintf(fp,"Note: Key signature counts and lists are from a pruned list that only\n"); fprintf(fp,"includes keys with signatures other than their own.\n\n"); fprintf(fp,"Signatures to this key:\n"); totalsigsto = PrintKeyList(fp, keys[key].to); fprintf(fp,"Total: %d signatures to this id from this set\n\n",totalsigsto); fprintf(fp,"Signatures from this key:\n"); totalsigsfrom = PrintKeyList(fp, keys[key].from); fprintf(fp,"Total: %d signatures from this id to this set\n\n",totalsigsfrom); } /* ################################################################# */ /* thread routine */ void *thread_slave(void *arg) { int i,j; float threadmean; sig *distant_sigs = NULL; FILE *fpindiv; int hops[MAXHOPS]; /* array for hop histogram */ int hophigh; /* highest number of hops for this key */ threadparam data = *(threadparam *)arg; for (i=0;iid1, key->id2, threadmean); fflush(fpmsd); pthread_mutex_unlock(&mean_l); /* individual report */ fpindiv = OpenFileById(key->id2); IndivReport(fpindiv,i); fprintf(fpindiv, "This key is %sin the strong set.\n", component[i] == max_component ? "" : "not "); fprintf(fpindiv,"Mean distance to this key from strong set: %8.4f\n\n",threadmean); fprintf(fpindiv,"Breakout by hop count (only from strong set):\n"); for (j=0;j<=hophigh;j++) { fprintf(fpindiv,"%2d hops: %5d\n",j,hops[j]); } if (distant_sigs) { fprintf(fpindiv,"\nFarthest keys (%d hops):\n", j-1); PrintKeyList(fpindiv, distant_sigs); DeleteKeyList(&distant_sigs); } fclose(fpindiv); } } return NULL; } /* ################################################################# */ /* main() */ int main(int argc, char **argv) { pthread_t *slave0,*slave1; threadparam arg0,arg1; void *retval; ParseArgs(argc, argv); if (OpenFiles()) { fprintf(stderr, "Error opening files.\n"); exit(EXIT_FAILURE); } ReadInput(); TestConnectivity(); pthread_mutex_init(&mean_l,NULL); slave0 = (pthread_t *) calloc(1, sizeof(pthread_t)); slave1 = (pthread_t *) calloc(1, sizeof(pthread_t)); arg0.threadnum = 0; arg1.threadnum = 1; if (pthread_create(slave0,NULL,thread_slave,&arg0)) { fprintf(stderr,"Cannot create thread 0."); } if (pthread_create(slave1,NULL,thread_slave,&arg1)) { fprintf(stderr,"Cannot create thread 1."); } pthread_join(*slave0, &retval); pthread_join(*slave1, &retval); fprintf(fpout,"Average mean is %9.4f\n",meantotal/num_reachable); /* ReportMostSignatures(); */ CloseFiles(); return 0; } signing-party-2.12/keyanalyze/pgpring/000077500000000000000000000000001500571021200200445ustar00rootroot00000000000000signing-party-2.12/keyanalyze/pgpring/Makefile.am000066400000000000000000000001361500571021200221000ustar00rootroot00000000000000bin_PROGRAMS = pgpring pgpring_SOURCES = pgppubring.c pgplib.c lib.c extlib.c \ pgppacket.c signing-party-2.12/keyanalyze/pgpring/configure.ac000066400000000000000000000005751500571021200223410ustar00rootroot00000000000000AC_INIT([pgpring], [0.0]) AC_CONFIG_SRCDIR([pgppubring.c]) AM_INIT_AUTOMAKE([-Wall foreign]) AC_CANONICAL_HOST AC_PROG_CC AC_ISC_POSIX AC_PROG_CPP AC_PROG_MAKE_SET AC_PROG_INSTALL AC_C_INLINE AC_C_CONST AC_SEARCH_LIBS([SHA1Update], [md]) AC_DEFINE([HAVE_PGP], [1], [Do you want PGP support (--enable-pgp)?]) AC_CONFIG_FILES([Makefile]) AC_CONFIG_HEADERS([config.h]) AC_OUTPUT signing-party-2.12/keyanalyze/pgpring/extlib.c000066400000000000000000000021531500571021200215000ustar00rootroot00000000000000/* * Copyright (C) 1999-2000 Thomas Roessler * * 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, write to the Free * Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111, USA. */ /* * Some simple dummies, so we can reuse the routines from * lib.c in external programs. */ #include #define WHERE #define _EXTLIB_C #include "lib.h" void (*mutt_error) (const char *, ...) = mutt_nocurses_error; void mutt_exit (int code) { exit (code); } signing-party-2.12/keyanalyze/pgpring/lib.c000066400000000000000000000271501500571021200207630ustar00rootroot00000000000000/* * Copyright (C) 1996-2000 Michael R. Elkins * Copyright (C) 1999-2000 Thomas Roessler * * 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, write to the Free * Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111, USA. */ /* * This file used to contain some more functions, namely those * which are now in muttlib.c. They have been removed, so we have * some of our "standard" functions in external programs, too. */ #include #include #include #include #include #include #include #include #include #include "lib.h" void mutt_nocurses_error (const char *fmt, ...) { va_list ap; va_start (ap, fmt); vfprintf (stderr, fmt, ap); va_end (ap); fputc ('\n', stderr); } void *safe_calloc (size_t nmemb, size_t size) { void *p; if (!nmemb || !size) return NULL; if (!(p = calloc (nmemb, size))) { mutt_error _("Out of memory!"); sleep (1); mutt_exit (1); } return p; } void *safe_malloc (size_t siz) { void *p; if (siz == 0) return 0; if ((p = (void *) malloc (siz)) == 0) /* __MEM_CHECKED__ */ { mutt_error _("Out of memory!"); sleep (1); mutt_exit (1); } return (p); } void safe_realloc (void *p2, size_t siz) { void **p = (void **)p2; void *r; if (siz == 0) { if (*p) { free (*p); /* __MEM_CHECKED__ */ *p = NULL; } return; } if (*p) r = (void *) realloc (*p, siz); /* __MEM_CHECKED__ */ else { /* realloc(NULL, nbytes) doesn't seem to work under SunOS 4.1.x --- __MEM_CHECKED__ */ r = (void *) malloc (siz); /* __MEM_CHECKED__ */ } if (!r) { mutt_error _("Out of memory!"); sleep (1); mutt_exit (1); } *p = r; } void safe_free (void *p2) { void **p = (void **)p2; if (*p) { free (*p); /* __MEM_CHECKED__ */ *p = 0; } } int safe_fclose (FILE **f) { int r = 0; if (*f) r = fclose (*f); *f = NULL; return r; } char *safe_strdup (const char *s) { char *p; size_t l; if (!s || !*s) return 0; l = strlen (s) + 1; p = (char *)safe_malloc (l); memcpy (p, s, l); return (p); } void mutt_str_replace (char **p, const char *s) { safe_free (p); *p = safe_strdup (s); } void mutt_str_adjust (char **p) { if (!p || !*p) return; safe_realloc ((void **) p, strlen (*p) + 1); } /* convert all characters in the string to lowercase */ char *mutt_strlower (char *s) { char *p = s; while (*p) { *p = tolower (*p); p++; } return (s); } void mutt_unlink (const char *s) { FILE *f; struct stat sb; char buf[2048]; if (stat (s, &sb) == 0) { if ((f = fopen (s, "r+"))) { unlink (s); memset (buf, 0, sizeof (buf)); while (sb.st_size > 0) { fwrite (buf, 1, sizeof (buf), f); sb.st_size -= sizeof (buf); } fclose (f); } } } int mutt_copy_bytes (FILE *in, FILE *out, size_t size) { char buf[2048]; size_t chunk; while (size > 0) { chunk = (size > sizeof (buf)) ? sizeof (buf) : size; if ((chunk = fread (buf, 1, chunk, in)) < 1) break; if (fwrite (buf, 1, chunk, out) != chunk) { /* dprint (1, (debugfile, "mutt_copy_bytes(): fwrite() returned short byte count\n")); */ return (-1); } size -= chunk; } return 0; } int mutt_copy_stream (FILE *fin, FILE *fout) { size_t l; char buf[LONG_STRING]; while ((l = fread (buf, 1, sizeof (buf), fin)) > 0) { if (fwrite (buf, 1, l, fout) != l) return (-1); } return 0; } static int compare_stat (struct stat *osb, struct stat *nsb) { if (osb->st_dev != nsb->st_dev || osb->st_ino != nsb->st_ino || osb->st_rdev != nsb->st_rdev) { return -1; } return 0; } int safe_symlink(const char *oldpath, const char *newpath) { struct stat osb, nsb; if(!oldpath || !newpath) return -1; if(unlink(newpath) == -1 && errno != ENOENT) return -1; if (oldpath[0] == '/') { if (symlink (oldpath, newpath) == -1) return -1; } else { char abs_oldpath[_POSIX_PATH_MAX]; if ((getcwd (abs_oldpath, sizeof abs_oldpath) == NULL) || (strlen (abs_oldpath) + 1 + strlen (oldpath) + 1 > sizeof abs_oldpath)) return -1; strcat (abs_oldpath, "/"); /* __STRCAT_CHECKED__ */ strcat (abs_oldpath, oldpath); /* __STRCAT_CHECKED__ */ if (symlink (abs_oldpath, newpath) == -1) return -1; } if(stat(oldpath, &osb) == -1 || stat(newpath, &nsb) == -1 || compare_stat(&osb, &nsb) == -1) { unlink(newpath); return -1; } return 0; } /* * This function is supposed to do nfs-safe renaming of files. * * Warning: We don't check whether src and target are equal. */ int safe_rename (const char *src, const char *target) { struct stat ssb, tsb; if (!src || !target) return -1; if (link (src, target) != 0) { /* * Coda does not allow cross-directory links, but tells * us it's a cross-filesystem linking attempt. * * However, the Coda rename call is allegedly safe to use. * * With other file systems, rename should just fail when * the files reside on different file systems, so it's safe * to try it here. * */ if (errno == EXDEV) return rename (src, target); return -1; } /* * Stat both links and check if they are equal. */ if (stat (src, &ssb) == -1) { return -1; } if (stat (target, &tsb) == -1) { return -1; } /* * pretend that the link failed because the target file * did already exist. */ if (compare_stat (&ssb, &tsb) == -1) { errno = EEXIST; return -1; } /* * Unlink the original link. Should we really ignore the return * value here? XXX */ unlink (src); return 0; } int safe_open (const char *path, int flags) { struct stat osb, nsb; int fd; if ((fd = open (path, flags, 0600)) < 0) return fd; /* make sure the file is not symlink */ if (lstat (path, &osb) < 0 || fstat (fd, &nsb) < 0 || compare_stat(&osb, &nsb) == -1) { /* dprint (1, (debugfile, "safe_open(): %s is a symlink!\n", path)); */ close (fd); return (-1); } return (fd); } /* when opening files for writing, make sure the file doesn't already exist * to avoid race conditions. */ FILE *safe_fopen (const char *path, const char *mode) { if (mode[0] == 'w') { int fd; int flags = O_CREAT | O_EXCL; #ifdef O_NOFOLLOW flags |= O_NOFOLLOW; #endif if (mode[1] == '+') flags |= O_RDWR; else flags |= O_WRONLY; if ((fd = safe_open (path, flags)) < 0) return (NULL); return (fdopen (fd, mode)); } else return (fopen (path, mode)); } static char safe_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+@{}._-:%/"; void mutt_sanitize_filename (char *f, short slash) { if (!f) return; for (; *f; f++) { if ((slash && *f == '/') || !strchr (safe_chars, *f)) *f = '_'; } } /* these characters must be escaped in regular expressions */ static char rx_special_chars[] = "^.[$()|*+?{\\"; int mutt_rx_sanitize_string (char *dest, size_t destlen, const char *src) { while (*src && --destlen > 2) { if (strchr (rx_special_chars, *src)) { *dest++ = '\\'; destlen--; } *dest++ = *src++; } *dest = '\0'; if (*src) return -1; else return 0; } /* Read a line from ``fp'' into the dynamically allocated ``s'', * increasing ``s'' if necessary. The ending "\n" or "\r\n" is removed. * If a line ends with "\", this char and the linefeed is removed, * and the next line is read too. */ char *mutt_read_line (char *s, size_t *size, FILE *fp, int *line) { size_t offset = 0; char *ch; if (!s) { s = safe_malloc (STRING); *size = STRING; } FOREVER { if (fgets (s + offset, *size - offset, fp) == NULL) { safe_free (&s); return NULL; } if ((ch = strchr (s + offset, '\n')) != NULL) { (*line)++; *ch = 0; if (ch > s && *(ch - 1) == '\r') *--ch = 0; if (ch == s || *(ch - 1) != '\\') return s; offset = ch - s - 1; } else { int c; c = getc (fp); /* This is kind of a hack. We want to know if the char at the current point in the input stream is EOF. feof() will only tell us if we've already hit EOF, not if the next character is EOF. So, we need to read in the next character and manually check if it is EOF. */ if (c == EOF) { /* The last line of fp isn't \n terminated */ (*line)++; return s; } else { ungetc (c, fp); /* undo our damage */ /* There wasn't room for the line -- increase ``s'' */ offset = *size - 1; /* overwrite the terminating 0 */ *size += STRING; safe_realloc (&s, *size); } } } } char * mutt_substrcpy (char *dest, const char *beg, const char *end, size_t destlen) { size_t len; len = end - beg; if (len > destlen - 1) len = destlen - 1; memcpy (dest, beg, len); dest[len] = 0; return dest; } char *mutt_substrdup (const char *begin, const char *end) { size_t len; char *p; if (end) len = end - begin; else len = strlen (begin); p = safe_malloc (len + 1); memcpy (p, begin, len); p[len] = 0; return p; } /* prepare a file name to survive the shell's quoting rules. * From the Unix programming FAQ by way of Liviu. */ size_t mutt_quote_filename (char *d, size_t l, const char *f) { size_t i, j = 0; if(!f) { *d = '\0'; return 0; } /* leave some space for the trailing characters. */ l -= 6; d[j++] = '\''; for(i = 0; j < l && f[i]; i++) { if(f[i] == '\'' || f[i] == '`') { d[j++] = '\''; d[j++] = '\\'; d[j++] = f[i]; d[j++] = '\''; } else d[j++] = f[i]; } d[j++] = '\''; d[j] = '\0'; return j; } /* NULL-pointer aware string comparison functions */ int mutt_strcmp(const char *a, const char *b) { return strcmp(NONULL(a), NONULL(b)); } int mutt_strcasecmp(const char *a, const char *b) { return strcasecmp(NONULL(a), NONULL(b)); } int mutt_strncmp(const char *a, const char *b, size_t l) { return strncmp(NONULL(a), NONULL(b), l); } int mutt_strncasecmp(const char *a, const char *b, size_t l) { return strncasecmp(NONULL(a), NONULL(b), l); } size_t mutt_strlen(const char *a) { return a ? strlen (a) : 0; } const char *mutt_stristr (const char *haystack, const char *needle) { const char *p, *q; if (!haystack) return NULL; if (!needle) return (haystack); while (*(p = haystack)) { for (q = needle; *p && *q && tolower (*p) == tolower (*q); p++, q++) ; if (!*q) return (haystack); haystack++; } return NULL; } char *mutt_skip_whitespace (char *p) { SKIPWS (p); return p; } void mutt_remove_trailing_ws (char *s) { char *p; for (p = s + mutt_strlen (s) - 1 ; p >= s && ISSPACE (*p) ; p--) *p = 0; } signing-party-2.12/keyanalyze/pgpring/lib.h000066400000000000000000000071541500571021200207720ustar00rootroot00000000000000/* * Copyright (C) 1996-2000 Michael R. Elkins * Copyright (C) 1999-2000 Thomas Roessler * * 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, write to the Free * Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111, USA. */ /* mutt functions which are generally useful. */ #ifndef _LIB_H # define _LIB_H # include "config.h" # include # include # ifdef HAVE_UNISTD_H # include /* needed for SEEK_SET */ # endif # include # include # include # include # include # include # ifndef _POSIX_PATH_MAX # include # endif # ifdef ENABLE_NLS # include # define _(a) (gettext (a)) # ifdef gettext_noop # define N_(a) gettext_noop (a) # else # define N_(a) (a) # endif # else # define _(a) (a) # define N_(a) a # endif # define TRUE 1 # define FALSE 0 # define HUGE_STRING 5120 # define LONG_STRING 1024 # define STRING 256 # define SHORT_STRING 128 # define FREE(x) safe_free(x) # define NONULL(x) x?x:"" # define ISSPACE(c) isspace((unsigned char)c) # define strfcpy(A,B,C) strncpy(A,B,C), *(A+(C)-1)=0 #define FOREVER while (1) /* this macro must check for *c == 0 since isspace(0) has unreliable behavior on some systems */ # define SKIPWS(c) while (*(c) && isspace ((unsigned char) *(c))) c++; /* * These functions aren't defined in lib.c, but * they are used there. * * A non-mutt "implementation" (ahem) can be found in extlib.c. */ # ifndef _EXTLIB_C extern void (*mutt_error) (const char *, ...); # endif void mutt_exit (int); /* The actual library functions. */ FILE *safe_fopen (const char *, const char *); char *mutt_read_line (char *, size_t *, FILE *, int *); char *mutt_skip_whitespace (char *); char *mutt_strlower (char *); char *mutt_substrcpy (char *, const char *, const char *, size_t); char *mutt_substrdup (const char *, const char *); char *safe_strdup (const char *); const char *mutt_stristr (const char *, const char *); int mutt_copy_stream (FILE *, FILE *); int mutt_copy_bytes (FILE *, FILE *, size_t); int mutt_rx_sanitize_string (char *, size_t, const char *); int mutt_strcasecmp (const char *, const char *); int mutt_strcmp (const char *, const char *); int mutt_strncasecmp (const char *, const char *, size_t); int mutt_strncmp (const char *, const char *, size_t); int safe_open (const char *, int); int safe_symlink (const char *, const char *); int safe_rename (const char *, const char *); int safe_fclose (FILE **); size_t mutt_quote_filename (char *, size_t, const char *); size_t mutt_strlen (const char *); void *safe_calloc (size_t, size_t); void *safe_malloc (size_t); void mutt_nocurses_error (const char *, ...); void mutt_remove_trailing_ws (char *); void mutt_sanitize_filename (char *, short); void mutt_str_replace (char **p, const char *s); void mutt_str_adjust (char **p); void mutt_unlink (const char *); void safe_free (void *); void safe_realloc (void *, size_t); #endif signing-party-2.12/keyanalyze/pgpring/pgplib.c000066400000000000000000000100671500571021200214710ustar00rootroot00000000000000/* * Copyright (C) 1997-2000 Thomas Roessler * * 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, write to the Free * Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111, USA. */ /* Generally useful, pgp-related functions. */ #include #include #include #include #include #include "lib.h" #include "pgplib.h" const char *pgp_pkalgbytype (unsigned char type) { switch (type) { case 1: return "RSA"; case 2: return "RSA"; case 3: return "RSA"; case 16: return "ElG"; case 17: return "DSA"; case 20: return "ElG"; default: return "unk"; } } /* unused */ #if 0 static const char *hashalgbytype (unsigned char type) { switch (type) { case 1: return "MD5"; case 2: return "SHA1"; case 3: return "RIPE-MD/160"; case 4: return "HAVAL"; default: return "unknown"; } } #endif short pgp_canencrypt (unsigned char type) { switch (type) { case 1: case 2: case 16: case 20: return 1; default: return 0; } } short pgp_cansign (unsigned char type) { switch (type) { case 1: case 3: case 17: case 20: return 1; default: return 0; } } /* return values: * 1 = sign only * 2 = encrypt only * 3 = both */ short pgp_get_abilities (unsigned char type) { return (pgp_canencrypt (type) << 1) | pgp_cansign (type); } void pgp_free_sig (pgp_sig_t **sigp) { pgp_sig_t *sp, *q; if (!sigp || !*sigp) return; for (sp = *sigp; sp; sp = q) { q = sp->next; safe_free (&sp); } *sigp = NULL; } void pgp_free_uid (pgp_uid_t ** upp) { pgp_uid_t *up, *q; if (!upp || !*upp) return; for (up = *upp; up; up = q) { q = up->next; pgp_free_sig (&up->sigs); safe_free (&up->addr); safe_free (&up); } *upp = NULL; } pgp_uid_t *pgp_copy_uids (pgp_uid_t *up, pgp_key_t *parent) { pgp_uid_t *l = NULL; pgp_uid_t **lp = &l; for (; up; up = up->next) { *lp = safe_calloc (1, sizeof (pgp_uid_t)); (*lp)->trust = up->trust; (*lp)->flags = up->flags; (*lp)->addr = safe_strdup (up->addr); (*lp)->parent = parent; lp = &(*lp)->next; } return l; } static void _pgp_free_key (pgp_key_t ** kpp) { pgp_key_t *kp; if (!kpp || !*kpp) return; kp = *kpp; pgp_free_uid (&kp->address); safe_free (&kp->keyid); safe_free (kpp); } pgp_key_t *pgp_remove_key (pgp_key_t ** klist, pgp_key_t * key) { pgp_key_t **last; pgp_key_t *p, *q, *r; if (!klist || !*klist || !key) return NULL; if (key->parent && key->parent != key) key = key->parent; last = klist; for (p = *klist; p && p != key; p = p->next) last = &p->next; if (!p) return NULL; for (q = p->next, r = p; q && q->parent == p; q = q->next) r = q; if (r) r->next = NULL; *last = q; return q; } void pgp_free_key (pgp_key_t ** kpp) { pgp_key_t *p, *q, *r; if (!kpp || !*kpp) return; if ((*kpp)->parent && (*kpp)->parent != *kpp) *kpp = (*kpp)->parent; /* Order is important here: * * - First free all children. * - If we are an orphan (i.e., our parent was not in the key list), * free our parent. * - free ourselves. */ for (p = *kpp; p; p = q) { for (q = p->next; q && q->parent == p; q = r) { r = q->next; _pgp_free_key (&q); } if (p->parent) _pgp_free_key (&p->parent); _pgp_free_key (&p); } *kpp = NULL; } signing-party-2.12/keyanalyze/pgpring/pgplib.h000066400000000000000000000055211500571021200214750ustar00rootroot00000000000000/* * Copyright (C) 1996,1997 Michael R. Elkins * Copyright (C) 1999-2000 Thomas Roessler * * 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, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ #ifdef HAVE_PGP #define PGPENCRYPT (1 << 0) #define PGPSIGN (1 << 1) #define PGPKEY (1 << 2) #define PGPGOODSIGN (1 << 3) #define KEYFLAG_CANSIGN (1 << 0) #define KEYFLAG_CANENCRYPT (1 << 1) #define KEYFLAG_SECRET (1 << 7) #define KEYFLAG_EXPIRED (1 << 8) #define KEYFLAG_REVOKED (1 << 9) #define KEYFLAG_DISABLED (1 << 10) #define KEYFLAG_SUBKEY (1 << 11) #define KEYFLAG_CRITICAL (1 << 12) #define KEYFLAG_PREFER_ENCRYPTION (1 << 13) #define KEYFLAG_PREFER_SIGNING (1 << 14) #define KEYFLAG_CANTUSE (KEYFLAG_DISABLED|KEYFLAG_REVOKED|KEYFLAG_EXPIRED) #define KEYFLAG_RESTRICTIONS (KEYFLAG_CANTUSE|KEYFLAG_CRITICAL) #define KEYFLAG_ABILITIES (KEYFLAG_CANSIGN|KEYFLAG_CANENCRYPT|KEYFLAG_PREFER_ENCRYPTION|KEYFLAG_PREFER_SIGNING) #define SIGFLAG_EXPIRED (1 << 8) typedef struct pgp_signature { struct pgp_signature *next; unsigned char sigtype; unsigned long sid1; unsigned long sid2; int flags; time_t gen_time; time_t exp_time; } pgp_sig_t; typedef struct pgp_keyinfo { char *keyid; struct pgp_uid *address; int flags; short keylen; time_t gen_time; time_t exp_time; int numalg; const char *algorithm; struct pgp_keyinfo *parent; struct pgp_signature *sigs; struct pgp_keyinfo *next; } pgp_key_t; typedef struct pgp_uid { char *addr; short trust; int flags; struct pgp_keyinfo *parent; struct pgp_uid *next; struct pgp_signature *sigs; } pgp_uid_t; enum pgp_version { PGP_V2, PGP_V3, PGP_GPG, PGP_UNKNOWN }; enum pgp_ring { PGP_PUBRING, PGP_SECRING }; typedef enum pgp_ring pgp_ring_t; /* prototypes */ const char *pgp_pkalgbytype (unsigned char); pgp_key_t *pgp_remove_key (pgp_key_t **, pgp_key_t *); pgp_uid_t *pgp_copy_uids (pgp_uid_t *, pgp_key_t *); short pgp_canencrypt (unsigned char); short pgp_cansign (unsigned char); short pgp_get_abilities (unsigned char); void pgp_free_key (pgp_key_t **kpp); #define pgp_new_keyinfo() safe_calloc (sizeof (pgp_key_t), 1) #endif /* HAVE_PGP */ signing-party-2.12/keyanalyze/pgpring/pgppacket.c000066400000000000000000000073351500571021200221760ustar00rootroot00000000000000/* * Copyright (C) 2001 Thomas Roessler * * 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, write to the Free * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, * MA 02111, USA. */ #include "config.h" #include #include #include #include #include #include "sha1.h" #include "lib.h" #include "pgplib.h" #include "pgppacket.h" #define CHUNKSIZE 1024 static unsigned char *pbuf = NULL; static size_t plen = 0; static int read_material (size_t material, size_t * used, FILE * fp) { if (*used + material >= plen) { unsigned char *p; size_t nplen; nplen = *used + material + CHUNKSIZE; if (!(p = realloc (pbuf, nplen))) /* __MEM_CHECKED__ */ { perror ("realloc"); return -1; } plen = nplen; pbuf = p; } if (fread (pbuf + *used, 1, material, fp) < material) { perror ("fread"); return -1; } *used += material; return 0; } unsigned char *pgp_read_packet (FILE * fp, size_t * len) { size_t used = 0; long startpos; unsigned char ctb; unsigned char b; size_t material; startpos = ftell (fp); if (!plen) { plen = CHUNKSIZE; pbuf = safe_malloc (plen); } if (fread (&ctb, 1, 1, fp) < 1) { if (!feof (fp)) perror ("fread"); goto bail; } if (!(ctb & 0x80)) { goto bail; } if (ctb & 0x40) /* handle PGP 5.0 packets. */ { int partial = 0; pbuf[0] = ctb; used++; do { if (fread (&b, 1, 1, fp) < 1) { perror ("fread"); goto bail; } if (b < 192) { material = b; partial = 0; /* material -= 1; */ } else if (192 <= b && b <= 223) { material = (b - 192) * 256; if (fread (&b, 1, 1, fp) < 1) { perror ("fread"); goto bail; } material += b + 192; partial = 0; /* material -= 2; */ } else if (b < 255) { material = 1 << (b & 0x1f); partial = 1; /* material -= 1; */ } else /* b == 255 */ { unsigned char buf[4]; if (fread (buf, 4, 1, fp) < 1) { perror ("fread"); goto bail; } /*assert( sizeof(material) >= 4 ); */ material = buf[0] << 24; material |= buf[1] << 16; material |= buf[2] << 8; material |= buf[3]; partial = 0; /* material -= 5; */ } if (read_material (material, &used, fp) == -1) goto bail; } while (partial); } else /* Old-Style PGP */ { int bytes = 0; pbuf[0] = 0x80 | ((ctb >> 2) & 0x0f); used++; switch (ctb & 0x03) { case 0: { if (fread (&b, 1, 1, fp) < 1) { perror ("fread"); goto bail; } material = b; break; } case 1: bytes = 2; case 2: { int i; if (!bytes) bytes = 4; material = 0; for (i = 0; i < bytes; i++) { if (fread (&b, 1, 1, fp) < 1) { perror ("fread"); goto bail; } material = (material << 8) + b; } break; } default: goto bail; } if (read_material (material, &used, fp) == -1) goto bail; } if (len) *len = used; return pbuf; bail: fseek (fp, startpos, SEEK_SET); return NULL; } void pgp_release_packet (void) { plen = 0; safe_free (&pbuf); } signing-party-2.12/keyanalyze/pgpring/pgppacket.h000066400000000000000000000033501500571021200221740ustar00rootroot00000000000000/* * Copyright (C) 2001 Thomas Roessler * * 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, write to the Free * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, * MA 02111, USA. */ /* * Definitions for a rudimentary PGP packet parser which is shared * by mutt proper and the PGP public key ring lister. */ #ifndef _PGPPACKET_H # define _PGPPACKET_H enum packet_tags { PT_RES0 = 0, /* reserved */ PT_ESK, /* Encrypted Session Key */ PT_SIG, /* Signature Packet */ PT_CESK, /* Conventionally Encrypted Session Key Packet */ PT_OPS, /* One-Pass Signature Packet */ PT_SECKEY, /* Secret Key Packet */ PT_PUBKEY, /* Public Key Packet */ PT_SUBSECKEY, /* Secret Subkey Packet */ PT_COMPRESSED, /* Compressed Data Packet */ PT_SKE, /* Symmetrically Encrypted Data Packet */ PT_MARKER, /* Marker Packet */ PT_LITERAL, /* Literal Data Packet */ PT_TRUST, /* Trust Packet */ PT_NAME, /* Name Packet */ PT_SUBKEY, /* Subkey Packet */ PT_RES15, /* Reserved */ PT_COMMENT /* Comment Packet */ }; unsigned char *pgp_read_packet (FILE * fp, size_t * len); void pgp_release_packet (void); #endif signing-party-2.12/keyanalyze/pgpring/pgppubring.c000066400000000000000000000433761500571021200224020ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Thomas Roessler * * 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, write to the Free * Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111, USA. */ /* * This is a "simple" PGP key ring dumper. * * The output format is supposed to be compatible to the one GnuPG * emits and Mutt expects. * * Note that the code of this program could be considerably less * complex, but most of it was taken from mutt's second generation * key ring parser. * * You can actually use this to put together some fairly general * PGP key management applications. * */ #include "config.h" #include #include #include #include #include #ifdef HAVE_GETOPT_H # include #endif #include extern char *optarg; extern int optind; #include "lib.h" #include "pgplib.h" #include "pgppacket.h" #ifdef HAVE_FGETPOS #define FGETPOS(fp,pos) fgetpos((fp),&(pos)) #define FSETPOS(fp,pos) fsetpos((fp),&(pos)) #else #define FGETPOS(fp,pos) pos=ftell((fp)); #define FSETPOS(fp,pos) fseek((fp),(pos),SEEK_SET) #endif static short dump_signatures = 0; static short exclude_exp_sigs = 0; static short exclude_exp_keys = 0; static void pgpring_find_candidates (char *ringfile, const char *hints[], int nhints); static void pgpring_dump_keyblock (pgp_key_t *p); int main (int argc, char * const argv[]) { int c; short version = 2; short secring = 0; const char *_kring = NULL; char *env_pgppath, *env_home; char pgppath[_POSIX_PATH_MAX]; char kring[_POSIX_PATH_MAX]; while ((c = getopt (argc, argv, "eE25sk:S")) != EOF) { switch (c) { case 'S': { dump_signatures = 1; break; } case 'e': { exclude_exp_sigs = 1; break; } case 'E': { exclude_exp_keys = 1; break; } case 'k': { _kring = optarg; break; } case '2': case '5': { version = c - '0'; break; } case 's': { secring = 1; break; } default: { fprintf (stderr, "usage: %s [-k | [-2 | -5] [ -s]] [hints]\n", argv[0]); exit (1); } } } if (_kring) strfcpy (kring, _kring, sizeof (kring)); else { if ((env_pgppath = getenv ("PGPPATH"))) strfcpy (pgppath, env_pgppath, sizeof (pgppath)); else if ((env_home = getenv ("HOME"))) snprintf (pgppath, sizeof (pgppath), "%s/.pgp", env_home); else { fprintf (stderr, "%s: Can't determine your PGPPATH.\n", argv[0]); exit (1); } if (secring) snprintf (kring, sizeof (kring), "%s/secring.%s", pgppath, version == 2 ? "pgp" : "skr"); else snprintf (kring, sizeof (kring), "%s/pubring.%s", pgppath, version == 2 ? "pgp" : "pkr"); } pgpring_find_candidates (kring, (const char**) argv + optind, argc - optind); return 0; } /* The actual key ring parser */ static pgp_key_t *pgp_parse_pgp2_key (unsigned char *buff, size_t l) { pgp_key_t *p; unsigned char alg; size_t expl; unsigned long id; time_t gen_time = 0; unsigned short exp_days = 0; size_t j; int i, k; unsigned char scratch[LONG_STRING]; if (l < 12) return NULL; p = pgp_new_keyinfo(); for (i = 0, j = 2; i < 4; i++) gen_time = (gen_time << 8) + buff[j++]; p->gen_time = gen_time; for (i = 0; i < 2; i++) exp_days = (exp_days << 8) + buff[j++]; if (exp_days) { p->exp_time = gen_time + exp_days * 24 * 3600; if (time (NULL) > p->exp_time) p->flags |= KEYFLAG_EXPIRED; } alg = buff[j++]; p->numalg = alg; p->algorithm = pgp_pkalgbytype (alg); p->flags |= pgp_get_abilities (alg); expl = 0; for (i = 0; i < 2; i++) expl = (expl << 8) + buff[j++]; p->keylen = expl; expl = (expl + 7) / 8; if (expl < 4) goto bailout; j += expl - 8; for (k = 0; k < 2; k++) { for (id = 0, i = 0; i < 4; i++) id = (id << 8) + buff[j++]; snprintf ((char *) scratch + k * 8, sizeof (scratch) - k * 8, "%08lX", id); } p->keyid = safe_strdup ((char *) scratch); return p; bailout: safe_free ((void *)&p); return NULL; } static void pgp_make_pgp3_fingerprint (unsigned char *buff, size_t l, unsigned char *digest) { unsigned char dummy; SHA1_CTX context; SHA1Init (&context); dummy = buff[0] & 0x3f; if (dummy == PT_SUBSECKEY || dummy == PT_SUBKEY || dummy == PT_SECKEY) dummy = PT_PUBKEY; dummy = (dummy << 2) | 0x81; SHA1Update (&context, &dummy, 1); dummy = ((l - 1) >> 8) & 0xff; SHA1Update (&context, &dummy, 1); dummy = (l - 1) & 0xff; SHA1Update (&context, &dummy, 1); SHA1Update (&context, buff + 1, l - 1); SHA1Final (digest, &context); } static void skip_bignum (unsigned char *buff, size_t l, size_t j, size_t * toff, size_t n) { size_t len; do { len = (buff[j] << 8) + buff[j + 1]; j += (len + 7) / 8 + 2; } while (j <= l && --n > 0); if (toff) *toff = j; } static pgp_key_t *pgp_parse_pgp3_key (unsigned char *buff, size_t l) { pgp_key_t *p; unsigned char alg; unsigned char digest[SHA1_DIGEST_LENGTH]; unsigned char scratch[LONG_STRING]; time_t gen_time = 0; unsigned long id; int i, k; short len; size_t j; p = pgp_new_keyinfo (); j = 2; for (i = 0; i < 4; i++) gen_time = (gen_time << 8) + buff[j++]; p->gen_time = gen_time; alg = buff[j++]; p->numalg = alg; p->algorithm = pgp_pkalgbytype (alg); p->flags |= pgp_get_abilities (alg); len = (buff[j] << 8) + buff[j + 1]; p->keylen = len; if (alg >= 1 && alg <= 3) skip_bignum (buff, l, j, &j, 2); else if (alg == 16 || alg == 20) skip_bignum (buff, l, j, &j, 3); else if (alg == 17) skip_bignum (buff, l, j, &j, 4); pgp_make_pgp3_fingerprint (buff, j, digest); for (k = 0; k < 2; k++) { for (id = 0, i = SHA1_DIGEST_LENGTH - 8 + k * 4; i < SHA1_DIGEST_LENGTH + (k - 1) * 4; i++) id = (id << 8) + digest[i]; snprintf ((char *) scratch + k * 8, sizeof (scratch) - k * 8, "%08lX", id); } p->keyid = safe_strdup ((char *) scratch); return p; } static pgp_key_t *pgp_parse_keyinfo (unsigned char *buff, size_t l) { if (!buff || l < 2) return NULL; switch (buff[1]) { case 2: case 3: return pgp_parse_pgp2_key (buff, l); case 4: return pgp_parse_pgp3_key (buff, l); default: return NULL; } } static int pgp_parse_pgp2_sig (unsigned char *buff, size_t l, pgp_key_t * p, pgp_sig_t *s) { unsigned char sigtype; time_t sig_gen_time; unsigned long signerid1; unsigned long signerid2; size_t j; int i; if (l < 22) return -1; j = 3; sigtype = buff[j++]; sig_gen_time = 0; for (i = 0; i < 4; i++) sig_gen_time = (sig_gen_time << 8) + buff[j++]; signerid1 = signerid2 = 0; for (i = 0; i < 4; i++) signerid1 = (signerid1 << 8) + buff[j++]; for (i = 0; i < 4; i++) signerid2 = (signerid2 << 8) + buff[j++]; if (sigtype == 0x20 || sigtype == 0x28) p->flags |= KEYFLAG_REVOKED; if (s) { s->sigtype = sigtype; s->sid1 = signerid1; s->sid2 = signerid2; s->gen_time = sig_gen_time; } return 0; } static int pgp_parse_pgp3_sig (unsigned char *buff, size_t l, pgp_key_t * p, pgp_sig_t *s) { unsigned char sigtype; unsigned char pkalg; unsigned char hashalg; unsigned char skt; time_t sig_gen_time = -1; long validity = -1; long key_validity = -1; unsigned long signerid1 = 0; unsigned long signerid2 = 0; size_t ml; size_t j; int i; short ii; short have_critical_spks = 0; if (l < 7) return -1; j = 2; sigtype = buff[j++]; pkalg = buff[j++]; hashalg = buff[j++]; for (ii = 0; ii < 2; ii++) { size_t skl; size_t nextone; ml = (buff[j] << 8) + buff[j + 1]; j += 2; if (j + ml > l) break; nextone = j; while (ml) { j = nextone; skl = buff[j++]; if (!--ml) break; if (skl >= 192) { skl = (skl - 192) * 256 + buff[j++] + 192; if (!--ml) break; } if ((int) ml - (int) skl < 0) break; ml -= skl; nextone = j + skl; skt = buff[j++]; switch (skt & 0x7f) { case 2: /* creation time */ { if (skl < 4) break; sig_gen_time = 0; for (i = 0; i < 4; i++) sig_gen_time = (sig_gen_time << 8) + buff[j++]; break; } case 3: /* expiration time */ { if (skl < 4) break; validity = 0; for (i = 0; i < 4; i++) validity = (validity << 8) + buff[j++]; break; } case 9: /* key expiration time */ { if (skl < 4) break; key_validity = 0; for (i = 0; i < 4; i++) key_validity = (key_validity << 8) + buff[j++]; if (key_validity > 0) p->exp_time = p->gen_time + key_validity; break; } case 16: /* issuer key ID */ { if (skl < 8) break; signerid2 = signerid1 = 0; for (i = 0; i < 4; i++) signerid1 = (signerid1 << 8) + buff[j++]; for (i = 0; i < 4; i++) signerid2 = (signerid2 << 8) + buff[j++]; break; } case 10: /* CMR key */ break; case 4: /* exportable */ case 5: /* trust */ case 6: /* regexp */ case 7: /* revocable */ case 11: /* Pref. symm. alg. */ case 12: /* revocation key */ case 20: /* notation data */ case 21: /* pref. hash */ case 22: /* pref. comp.alg. */ case 23: /* key server prefs. */ case 24: /* pref. key server */ default: { if (skt & 0x80) have_critical_spks = 1; } } } j = nextone; } if (sigtype == 0x20 || sigtype == 0x28) p->flags |= KEYFLAG_REVOKED; if (key_validity != -1 && time (NULL) > p->gen_time + key_validity) p->flags |= KEYFLAG_EXPIRED; if (have_critical_spks) p->flags |= KEYFLAG_CRITICAL; if (s) { s->sigtype = sigtype; s->sid1 = signerid1; s->sid2 = signerid2; if (sig_gen_time > 0) { s->gen_time = sig_gen_time; if (validity > 0) { s->exp_time = sig_gen_time + validity; if (time (NULL) > s->exp_time) s->flags |= SIGFLAG_EXPIRED; } } } return 0; } static int pgp_parse_sig (unsigned char *buff, size_t l, pgp_key_t * p, pgp_sig_t *sig) { if (!buff || l < 2 || !p) return -1; switch (buff[1]) { case 2: case 3: return pgp_parse_pgp2_sig (buff, l, p, sig); case 4: return pgp_parse_pgp3_sig (buff, l, p, sig); default: return -1; } } /* parse one key block, including all subkeys. */ static pgp_key_t *pgp_parse_keyblock (FILE * fp) { unsigned char *buff; unsigned char pt = 0; unsigned char last_pt; size_t l; short err = 0; #ifdef HAVE_FGETPOS fpos_t pos; #else long pos; #endif pgp_key_t *root = NULL; pgp_key_t **last = &root; pgp_key_t *p = NULL; pgp_uid_t *uid = NULL; pgp_uid_t **addr = NULL; pgp_sig_t **lsig = NULL; FGETPOS(fp,pos); while (!err && (buff = pgp_read_packet (fp, &l)) != NULL) { last_pt = pt; pt = buff[0] & 0x3f; /* check if we have read the complete key block. */ if ((pt == PT_SECKEY || pt == PT_PUBKEY) && root) { FSETPOS(fp, pos); return root; } switch (pt) { case PT_SECKEY: case PT_PUBKEY: case PT_SUBKEY: case PT_SUBSECKEY: { if (!(*last = p = pgp_parse_keyinfo (buff, l))) { err = 1; break; } last = &p->next; addr = &p->address; lsig = &p->sigs; if (pt == PT_SUBKEY || pt == PT_SUBSECKEY) { p->flags |= KEYFLAG_SUBKEY; if (p != root) { p->parent = root; p->address = pgp_copy_uids (root->address, p); while (*addr) addr = &(*addr)->next; } } if (pt == PT_SECKEY || pt == PT_SUBSECKEY) p->flags |= KEYFLAG_SECRET; break; } case PT_SIG: { if (lsig) { pgp_sig_t *signature = safe_calloc (sizeof (pgp_sig_t), 1); *lsig = signature; lsig = &signature->next; pgp_parse_sig (buff, l, p, signature); } break; } case PT_TRUST: { if (p && (last_pt == PT_SECKEY || last_pt == PT_PUBKEY || last_pt == PT_SUBKEY || last_pt == PT_SUBSECKEY)) { if (buff[1] & 0x20) { p->flags |= KEYFLAG_DISABLED; } } else if (last_pt == PT_NAME && uid) { uid->trust = buff[1]; } break; } case PT_NAME: { char *chr; if (!addr) break; chr = safe_malloc (l); memcpy (chr, buff + 1, l - 1); chr[l - 1] = '\0'; *addr = uid = safe_calloc (1, sizeof (pgp_uid_t)); /* XXX */ uid->addr = chr; uid->parent = p; uid->trust = 0; addr = &uid->next; lsig = &uid->sigs; /* the following tags are generated by * pgp 2.6.3in. */ if (strstr (chr, "ENCR")) p->flags |= KEYFLAG_PREFER_ENCRYPTION; if (strstr (chr, "SIGN")) p->flags |= KEYFLAG_PREFER_SIGNING; break; } } FGETPOS(fp,pos); } if (err) pgp_free_key (&root); return root; } static int pgpring_string_matches_hint (const char *s, const char *hints[], int nhints) { int i; if (!hints || !nhints) return 1; for (i = 0; i < nhints; i++) { if (mutt_stristr (s, hints[i]) != NULL) return 1; } return 0; } /* * Go through the key ring file and look for keys with * matching IDs. */ static void pgpring_find_candidates (char *ringfile, const char *hints[], int nhints) { FILE *rfp; #ifdef HAVE_FGETPOS fpos_t pos, keypos; #else long pos, keypos; #endif unsigned char *buff = NULL; unsigned char pt = 0; size_t l = 0; short err = 0; if ((rfp = fopen (ringfile, "r")) == NULL) { perror ("fopen"); return; } FGETPOS(rfp,pos); FGETPOS(rfp,keypos); while (!err && (buff = pgp_read_packet (rfp, &l)) != NULL) { pt = buff[0] & 0x3f; if (l < 1) continue; if ((pt == PT_SECKEY) || (pt == PT_PUBKEY)) { keypos = pos; } else if (pt == PT_NAME) { char *tmp = safe_malloc (l); memcpy (tmp, buff + 1, l - 1); tmp[l - 1] = '\0'; /* mutt_decode_utf8_string (tmp, chs); */ if (pgpring_string_matches_hint (tmp, hints, nhints)) { pgp_key_t *p; FSETPOS(rfp, keypos); /* Not bailing out here would lead us into an endless loop. */ if ((p = pgp_parse_keyblock (rfp)) == NULL) err = 1; pgpring_dump_keyblock (p); pgp_free_key (&p); } safe_free (&tmp); } FGETPOS(rfp,pos); } fclose (rfp); } static void print_userid (const char *id) { for (; id && *id; id++) { if (*id >= ' ' && *id <= 'z' && *id != ':') putchar (*id); else printf ("\\x%02x", *id); } } static void pgpring_dump_signatures (pgp_sig_t *sig) { struct tm *tp; time_t t; for (; sig; sig = sig->next) { if (exclude_exp_sigs && (sig->flags & SIGFLAG_EXPIRED)) continue; if (sig->sigtype == 0x10 || sig->sigtype == 0x11 || sig->sigtype == 0x12 || sig->sigtype == 0x13) { printf ("sig::::%08lX%08lX:", sig->sid1, sig->sid2); t = sig->gen_time; tp = gmtime (&t); printf ("%04d-%02d-%02d:", 1900 + tp->tm_year, tp->tm_mon + 1, tp->tm_mday); if (sig->exp_time) { t = sig->exp_time; tp = gmtime (&t); printf ("%04d-%02d-%02d", 1900 + tp->tm_year, tp->tm_mon + 1, tp->tm_mday); } printf ("::::%X:\n", sig->sigtype); } else if (sig->sigtype == 0x20) printf ("rev::::%08lX%08lX::::::%X:\n", sig->sid1, sig->sid2, sig->sigtype); } } static char gnupg_trustletter (int t) { switch (t) { case 1: return 'n'; case 2: return 'm'; case 3: return 'f'; } return 'q'; } static void pgpring_dump_keyblock (pgp_key_t *p) { pgp_uid_t *uid; short first; struct tm *tp; time_t t; for (; p; p = p->next) { if (exclude_exp_keys && \ (p->flags & KEYFLAG_EXPIRED || p->flags & KEYFLAG_REVOKED)) continue; first = 1; if (p->flags & KEYFLAG_SECRET) { if (p->flags & KEYFLAG_SUBKEY) printf ("ssb:"); else printf ("sec:"); } else { if (p->flags & KEYFLAG_SUBKEY) printf ("sub:"); else printf ("pub:"); } if (p->flags & KEYFLAG_REVOKED) putchar ('r'); if (p->flags & KEYFLAG_EXPIRED) putchar ('e'); if (p->flags & KEYFLAG_DISABLED) putchar ('d'); for (uid = p->address; uid; uid = uid->next, first = 0) { if (!first) { printf ("uid:%c::::::::", gnupg_trustletter (uid->trust)); print_userid (uid->addr); printf (":\n"); } else { if (p->flags & KEYFLAG_SECRET) putchar ('u'); else putchar (gnupg_trustletter (uid->trust)); t = p->gen_time; tp = gmtime (&t); printf (":%d:%d:%s:%04d-%02d-%02d:", p->keylen, p->numalg, p->keyid, 1900 + tp->tm_year, tp->tm_mon + 1, tp->tm_mday); if (p->exp_time) { t = p->exp_time; tp = gmtime (&t); printf ("%04d-%02d-%02d", 1900 + tp->tm_year, tp->tm_mon + 1, tp->tm_mday); } printf (":::"); print_userid (uid->addr); printf (":\n"); } if (dump_signatures) { if (first) pgpring_dump_signatures (p->sigs); pgpring_dump_signatures (uid->sigs); } } } } /* * The mutt_gettext () defined in gettext.c requires iconv, * so we do without charset conversion here. */ char *mutt_gettext (const char *message) { return (char *)message; } signing-party-2.12/keyanalyze/pgpring/pgpring.1000066400000000000000000000022371500571021200216000ustar00rootroot00000000000000.\" pgpring, a key ring dumper .\" Manpage Copyright (c) 2004 Matthew Wilcox .\" .\" 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. .\" .TH PGPRING 1 .SH NAME pgpring \- key ring dumper .SH SYNTAX \fBpgpring\fP [ \fB\-k\fP \fIkeyring\fP | \fB\-2\fP | \fB\-5\fP ] [ \fB\-s\fP ] [ \fB\-S\fP ] [ \fB\-e\fP ] [ \fB\-E\fP ] .SH DESCRIPTION \fIpgpring\fP is a key ring dumper. It extracts information from PGP's binary key ring and emits it in an (almost) readable output format understood by .BR mutt (1) and .BR process_keys (1). This output format mimics the one used by the GNU Privacy Guard (GPG). .SH OPTIONS .TP .BI \-k " keyring" Dump the contents of the specified keyring. .TP .B \-2 Use the default keyring for PGP 2.x. .TP .B \-5 Use the default keyring for PGP 5. .TP .B \-s Dump the secret keyring. .TP .B \-S Include signatures. .TP .B \-e Exclude expired signatures. .TP .B \-E Exclude expired keys. .SH AUTHORS Thomas Roessler signing-party-2.12/keyanalyze/pgpring/stamp-h.in000066400000000000000000000000121500571021200217360ustar00rootroot00000000000000timestamp signing-party-2.12/keyanalyze/process_keys.1000066400000000000000000000015731500571021200211770ustar00rootroot00000000000000.\" process_keys, preprocesses keys into input for keyanalyze .\" manpage Copyright (C) 2004 Matthew Wilcox .\" .\" 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. .\" .TH process_keys 1 .SH NAME process_keys \- Web of Trust analysis .SH SYNTAX \fBprocess_keys\fP [ \fB\-S\fP ] .SH DESCRIPTION \fIprocess_keys\fP takes the output from .BR pgpring (1) and turns it into suitable input for .BR keyanalyze (1). .B pgpring must be called with the .B -S option to also dump signatures. It acts as a filter, reading from stdin and writing to stdout. .SH OPTIONS .TP .B \-S Accept keys that are not selfsigned. Not recommended. .SH AUTHOR Thomas Roessler signing-party-2.12/keyanalyze/process_keys.c000066400000000000000000000077211500571021200212620ustar00rootroot00000000000000/* * Does preprocessing of keyrings for an intermediate file to be monged * by keyanalyze. * * Copyright (c)2001 Thomas Roessler * * This program can be freely distributed under the GNU General Public * License. */ #include #include #include #include #include static int DontRequireSelfSig = 0; #define IDBUF 17 struct sig { struct sig *next; char id[IDBUF]; }; struct uid { struct uid *next; struct sig *sigs; unsigned self : 1; }; struct key { char id[IDBUF]; struct uid *uids; unsigned rev : 1; }; static void free_sig (struct sig **sigpp) { struct sig *sigp, *q; if (!sigpp || !*sigpp) return; for (sigp = *sigpp; sigp; sigp = q) { q = sigp->next; free (sigp); } *sigpp = NULL; } static void free_uid (struct uid **uidpp) { struct uid *uidp, *q; if (!uidpp || !*uidpp) return; for (uidp = *uidpp; uidp; uidp = q) { q = uidp->next; free (uidp); } *uidpp = NULL; } static void free_key (struct key **keypp) { struct key *keyp; if (!keypp || !(keyp = *keypp)) return; free_uid (&keyp->uids); free (keyp); *keypp = NULL; } #define new_sig() calloc (sizeof (struct sig), 1) #define new_uid() calloc (sizeof (struct uid), 1) #define new_key() calloc (sizeof (struct key), 1) /* Is a signature with this ID present? */ static int check_sig_id (struct sig *signatures, char *id) { struct sig *s; for (s = signatures; s; s = s->next) if (!strcmp (s->id, id)) return 1; return 0; } /* Is this user ID self-signed? */ static int check_selfsig (struct uid *uid, struct key *key) { return (uid->self = check_sig_id (uid->sigs, key->id)); } /* Append a list of signatures to a different list of signatures */ static void join_siglists (struct sig **sig_d, struct sig **sig_s) { while (*sig_d) sig_d = &((*sig_d)->next); *sig_d = *sig_s; *sig_s = NULL; } /* Clean up a list of signatures - inefficient! */ static void cleanup_siglist (struct sig **sig, char *keyid) { struct sig **last = sig; struct sig *p, *q; for (p = *sig; p; p = q) { q = p->next; if (!strcmp (keyid, p->id) || check_sig_id (p->next, p->id)) { *last = p->next; p->next = NULL; free_sig (&p); } else last = &p->next; } } /* print the information gathered */ static void do_key (struct key *k) { struct sig *interesting_signatures = NULL, *sigp; struct uid *uidp; if (k->rev) return; for (uidp = k->uids; uidp; uidp = uidp->next) if (DontRequireSelfSig || check_selfsig (uidp, k)) join_siglists (&interesting_signatures, &uidp->sigs); cleanup_siglist (&interesting_signatures, k->id); if (interesting_signatures) { printf ("p%s\n", k->id); for (sigp = interesting_signatures; sigp; sigp = sigp->next) printf ("s%s\n", sigp->id); } free_sig (&interesting_signatures); free_uid (&k->uids); } /* the main routine */ int main (int argc, char *argv[]) { char buff[1024]; char *s; struct sig **lastsig = NULL; struct uid **lastuid = NULL; struct key *k = new_key(); lastuid = &k->uids; if (argc == 2 && !strcmp (argv[1], "-S")) DontRequireSelfSig = 1; while (fgets (buff, sizeof (buff), stdin)) { if ((s = strtok (buff, ":"))) { if (!strcmp (s, "pub")) { do_key (k); k->rev = 0; k->uids = new_uid(); lastuid = &k->uids->next; lastsig = &k->uids->sigs; strtok (NULL, ":"); strtok (NULL, ":"); strtok (NULL, ":"); sprintf (k->id, "%s", strtok (NULL, ":")); } else if (!strcmp (s, "rev")) k->rev = 1; else if (!strcmp (s, "uid")) { struct uid *uid = *lastuid = new_uid(); lastuid = &(*lastuid)->next; lastsig = &uid->sigs; } else if (!strcmp (s, "sig")) { struct sig *sig = *lastsig = new_sig(); lastsig = &sig->next; sprintf (sig->id, "%s", strtok (NULL, ":")); } } } do_key (k); return 0; } signing-party-2.12/keyanalyze/scripts/000077500000000000000000000000001500571021200200655ustar00rootroot00000000000000signing-party-2.12/keyanalyze/scripts/1000_bottom.php000066400000000000000000000000431500571021200225370ustar00rootroot00000000000000 signing-party-2.12/keyanalyze/scripts/1000_top.php000066400000000000000000000005561500571021200220460ustar00rootroot00000000000000

[Back to March report]

Top 1000 keys (lowest MSD) for March 2002

signing-party-2.12/keyanalyze/scripts/htmlify_report000077500000000000000000000022001500571021200230540ustar00rootroot00000000000000#!/usr/bin/perl -w use strict; use Getopt::Std; my %options; getopts('k:', \%options); my $keyring = $options{k} ? "--no-default-keyring --keyring=$options{k}" : ""; my %UID; sub get_uid { my $key = shift; return $UID{$key} if $UID{$key}; open G, '-|', qw/gpg --list-key --fixed-list-mode --with-colon/, $keyring, $key or die "gpg: $!"; while() { next unless /^uid:[-qmfue]::::\d*::[\dA-F]*::(.+):$/; my $name = $1; $name =~ s//>/g; $name =~ s/\@/@/g; close G; return $UID{$key} = $name; } close G; } sub uid_link { my $key = shift; #$key =~ /^([\dA-F]{2})/; #return "$key"; return "$key"; } for my $file (@ARGV) { #print STDERR "$file...\n"; open F, "$file" or die "$file: $!"; open H, ">$file.html" or die "$file.html: $!"; print H < $file
EOF
	while() {
		next if /^(This individual|report at)/;
		s/([\dA-F]{8})$/uid_link($1)." ".get_uid($1);/e;
		print H;
	}
	print H "
\n\n"; } signing-party-2.12/keyanalyze/scripts/report_bottom.php000066400000000000000000000006241500571021200234770ustar00rootroot00000000000000
RankHex ID (last 32b) Key Name (Identifier)Comments MSD

For next month

Discussion about this analysis continues on the keyanalyze-discuss mailing list.

If you have any suggestions, please send them my way, especially if you have the algorithms as well. If you're so inclined, please have a look at the code as well. signing-party-2.12/keyanalyze/scripts/report_top.php000066400000000000000000000055301500571021200227760ustar00rootroot00000000000000

[Back to Keyring Analysis Page]

Key Analysis 10 Aug 2001

The following stats are being pulled from a keyring that was exported from pgp.dtype.org on August 9, 2001. Before reading this, please be sure to view the explanation of this analysis and read the FAQ before asking me any questions about it.

The strong set MSD raw analysis is available here. Please read the FAQ to explain how to read this file. This file includes all keys reachable from the strong set. Look up reports for individual keys in the raw output directory. Here you can also see what keys are signed by each key (otherwise very difficult to find).

New This Month

General statistics
Size of binary keyring (bytes): 1,863,975,684(+0.56%)
Number of keys: 1,583,621(+0.37%)
Non-revoked keys with at least one non-self sig: 148,845(+0.77%)
Total non-self sigs on those keys: 306,035 302,662(+1.10%)

The "strong set"
Size of largest strongly connected set: 10,153(+6.18%)
Keys that have signed this set: 14,811(+6.04%)
Keys that this set has signed (target of MSD calculations): 40,249(+5.27%)

Best connected keys (shortest distance to)

Please read about the mean shortest distance (MSD) calculated here in the analysis explanation. Here are the top 50 keys. Look for your own key in this month's raw analysis (see above). Note that the only keys analyzed were those reachable from the strong set. I've included some of my own comments on people I recognize. I'm sorry if you're listed here without a comment. If you email me a quick phrase to describe what you do that would be of interest to readers, I'll put it in.

The average MSD is 6.6224, in the set of 10,153. The median value is 6.1993.

Go to this keyserver's web interface to look up these keys.

signing-party-2.12/keyanalyze/scripts/top50.pl000077500000000000000000000047371500571021200214070ustar00rootroot00000000000000#!/usr/bin/perl -w # this short script is for making the HTML for the top50 report monthly # Copyright (c)2001 M. Drew Streib # This code is released under the GPL version 2 or later. # 2004-09-14: modifications by Christoph Berg : # * use perl to read top50comments.txt # * use gpg --list-key instead of wget # * use strict & warnings # 2008-07-18: modifications by Christoph Berg : # * directly read msd.txt instead of a -sorted variant use strict; use Getopt::Std; #my $keyserver = "http://pks.gpg.cz:11371/pks/lookup?op=vindex&fingerprint=on&search=0x"; #my $keyserver = "http://keyserver.noreply.org/pks/lookup?op=index&fingerprint=on&search=0x"; my $keyserver = "https://keyserver.ubuntu.com/pks/lookup?op=index&fingerprint=on&search=0x"; my %options; getopts('c:k:n:', \%options); my $comments = $options{c} || "top50comments.txt"; my $keyring = $options{k} ? "--no-default-keyring --keyring=$options{k}" : ""; my $top = $options{n} || 50; my %comment; if (open F, $comments) { while() { die "$comments.$.: syntax error" unless /([\dA-F]+)\b ?(.*)/; $comment{$1} = $2; } close F; } my %msd; while (my $line = ) { $line =~ /^\w+\s+(\w+)\s+([\d\.]+)/ or die "cannot parse line $.: $line"; $msd{$1} = $2; } print "
RankHex ID (last 32b) Key Name (Identifier)Comments MSD
\n"; print "\n"; my $oldmsd = 0; my $i = 1; foreach my $key (sort { $msd{$a} <=> $msd{$b} } keys %msd) { my $rank = ""; if($oldmsd != $msd{$key}) { $rank = $i++; } last if $rank and $rank > $top; $oldmsd = $msd{$key}; my $name = ""; open G, '-|', qw/gpg --list-key --fixed-list-mode --with-colon --trust-model always/, $keyring, $key or die "gpg: $!"; while() { #uid:u::::1082202576::1DC0BEA2AC64671CC902D50B8121F6E4E6336E15::Christoph Berg : next unless /^uid:[-qmfue]::::\d*::[\dA-F]*::(.+):$/; $name = $1; $name =~ s//>/g; $name =~ s/\@/@/g; last; } close G; my $comment = $comment{$key} || ""; $key =~ /^([\dA-F]{2})/; #my $prefix = $1; #print "\n"; print "\n"; } print "
#IdNameMSD
$rank$key keyserver$name$comment$msd
$rank$keykeyserver$name $comment$msd{$key}
\n"; signing-party-2.12/keyanalyze/scripts/top50comments.txt000066400000000000000000000032111500571021200233400ustar00rootroot0000000000000009590CFD GA Tech College of computing 8B4608A1 GA Tech College of computing 6916C873 GA Tech College of computing 4F570BA3 PGP security maverick DC4ED62D PGP security maverick 466B4289 ext2fstools, Kerberos, LSB, IETF, other 80B07A4F ext2fstools, Kerberos, LSB, IETF, other 103D4013 ext2fstools, Kerberos, LSB, IETF, other 93674C40 ext2fstools, Kerberos, LSB, IETF, other F95C2F6D Debian maintainer & uni-mainz keyserver admin 09AC0A6A cypherpunks 66FBC18C keyanalyze report, free software law, freedb, other 0A2F87E5 OpenBSD, OpenSSH, IPSEC C2009841 OpenBSD, OpenSSH, IPSEC 0679ED91 manages SURFnet servers, scanned PGP source code 66A74B31 manages SURFnet servers, scanned PGP source code C7A966DD inventor of PGP FAEBD5FC inventor of PGP 0DBF906D MIT security/network manager 5B0358A2 author of GNU Privacy Guard (GPG) 1CF27FD5 author of pks PGP keyserver software DD934139 hosts Swiss PGP keyserver, www.ch.pgp.net 52D1CAB1 security researcher at ETH Zurich 00292B81 security researcher at ETH Zurich 46F3212D Debian developer, postfix junkie 2B48F6F5 ISAAC, crypto guru 2DE30EC1 CERT, you know 09D3E64D USENIX, PGPMoose 7362BE39 chief cryptographer, Intel C3FC4C69 security expert, AT&T Labs ED9547ED Debian Project Leader emeritus, dpkg 2FA3BC2D Debian Project Leader emeritus, dpkg 39F37F5D privacy advocate & security expert 961F4A35 inventor of SSH DB41B387 Free Software Foundation VP 7DFF8533 USENIX, Linux NFSv4, uucp 13D9873D co-author, Linux Kernel Internals 1FE961A1 xfree86 driver coder 94C09C7F debian trust analysis 603F2D01 PKI page, pki-page.org A9FA17FF Debian maintainer BDBFE838 KMail developer BB1D9F6D German computer magazine c't; crypto campaign signing-party-2.12/keyanalyze/willy/000077500000000000000000000000001500571021200175365ustar00rootroot00000000000000signing-party-2.12/keyanalyze/willy/README000066400000000000000000000004151500571021200204160ustar00rootroot00000000000000My scripts require a copy of the global keyring msd-sorted.txt file in the current directory. You can download it from http://keyserver.kjsl.com/~jharris/ka/current/msd-sorted.txt.bz2 and use bunzip to uncompress it. This file is updated approximately every 2 weeks. signing-party-2.12/keyanalyze/willy/cosign000066400000000000000000000015511500571021200207450ustar00rootroot00000000000000#!/usr/bin/perl for (@ARGV) { open(KEY, $_); ($name = $_) =~ s#.*/##; $state = 0; $#to = -1; $#from = -1; $#onlyto = -1; while ($line = ) { if ($line =~ "^Signatures to") { $state = 1; } elsif ($line =~ "^Total:") { $state = 0; } elsif ($line =~ "^Signatures from") { $state = 2; } elsif ($state == 1) { $to[++$#to] = $line; } elsif ($state == 2) { $from[++$#from] = $line; } } close(KEY); @to = sort @to; @from = sort @from; TO: foreach $sigto (@to) { foreach $index (0 .. @from) { if ($sigto eq $from[$index]) { splice(@from, $index, 1); next TO; } } $onlyto[++$#onlyto] = $sigto; } $signed = $#onlyto + 1; $signedby = $#from + 1; print "The following $signedby keys have not signed key $name:\n"; print @from; print " \nKey $name has not signed $signed keys:\n"; print @onlyto; print "\n"; } signing-party-2.12/keyanalyze/willy/msd2html000066400000000000000000000025041500571021200212140ustar00rootroot00000000000000#!/usr/bin/perl # this short script is for making the HTML for the top50 report monthly # Copyright (c)2001 M. Drew Streib # This code is released under the GPL version 2 or later. # Modifications (c) Copyright 2003-2004 Matthew Wilcox for subset analyses. $options = $ARGV[0]; $server='https://keyserver.ubuntu.com/'; $oldmsd = 0; $oldrank = 0; while ($line = ) { $line =~ /\s+(\d+)\s+((\w|\d)+)\s+((\w|\d)+)\s+((\d|\.)+)/; $rank = $1; $key0 = $2; $key = $4; $msd = $6; $keylink = substr($key, 0, 2)."/$key"; if ($msd == $oldmsd) { $rank = $oldrank; } else { $oldrank = $rank; $oldmsd = $msd; } $command = "gpg --options $options --list-keys $key"; $output = `$command`; if ($output =~ /\d\d\d\d\-\d\d\-\d\d (.*) <.*/) { $name = $1; } elsif ($output =~ /\d\d\d\d\-\d\d\-\d\d (.*)\n/) { $name = $1; } else { print "failed to parse: $output"; $name = $rank; } $command = 'grep "'.$key.'" msd-sorted.txt'; $rawmsd = `$command`; if ($rawmsd) { $rawmsd =~ / *(\d+) \w+ \w+ *((\d|\.)+)/; $grank = $1; $gmsd = $2; } else { $grank = ''; $gmsd = ''; } print "$rank$key$name$msd$grank$gmsd\n"; } signing-party-2.12/keyanalyze/willy/non-recip000066400000000000000000000013761500571021200213620ustar00rootroot00000000000000#!/usr/bin/perl $cmd = "gpg --option $ARGV[0]/options --list-keys"; print "Non-reciprocating signers\n"; print "\n"; print `cat $ARGV[0]/non-recip.in`; print "\n"; while () { s/Key //; ($key,$number) = split(/ has not signed /, $_, 2); $name = `$cmd $key |head -1`; chomp $name; $name =~ s#.*-[0-9][0-9] ##; $name =~ s/&/&/; $name =~ s//>/; $name =~ s/\([^)]*\)//; $number =~ s/://; chomp $number; $excuse = `grep -s $key $ARGV[0]/excuses`; chomp $excuse; $excuse =~ s/$key //; print "\n"; } print "
NameKey IDSlacknessExcuse
$name$key$number$excuse
"; signing-party-2.12/keyanalyze/willy/party-table.pl000066400000000000000000000046731500571021200223310ustar00rootroot00000000000000#!/usr/bin/perl # Version: 1.0 # Date: 2001.01.07 # Author: V. Alex Brennen # http://www.cryptnet.net/people/vab/ # License: GPL # Description: # This script was written as part of the gpg keysigning # party howto. It generates a checklist for individuals # participating in a keysigning party. The keysigning # howto lives at: # http://www.cryptnet.net/fdp/crypto/gpg-party.html if($ARGV[0] eq "") { print "\nUsage: party-table.pl > out_file.html\n"; print "\nThe keyring should be the keyring where the public keys for the\n"; print "party participants are stored.\n\n"; exit; } @fps = `gpg --fingerprint --no-default-keyring --keyring $ARGV[0]`; my @parsed; while($line = shift(@fps)) { if($line =~ /^pub/) { $key_info = substr($line,5,14); ($size_type,$id) = split(/\//,$key_info); $size = substr($size_type,0,4); $type = substr($size_type,-1,1); $owner = substr($line,31,-1); $fp_line = shift(@fps); ($trash,$fp) = split(/ = /,$fp_line); chomp $fp; ($fp1,$fp2) = split(/ /,$fp); $fp1 =~ s/ / /g; $fp2 =~ s/ / /g; if($type eq "D"){$type = "DSA";} elsif($type eq "R"){$type = "RSA";} elsif($type eq "G"){$type = "ElG";} $owner =~ s//>\;/; $owner =~ s/@/-at-/; push @parsed, { id => $id, owner => $owner, fp1 => $fp1, fp2 => $fp2, size => $size, type => $type, }; } } print "\n"; print "\n"; print "\n"; foreach my $f (sort {uc($a->{owner}) cmp uc($b->{owner})} @parsed) { $id = $f->{id}; $owner = $f->{owner}; $fp1 = $f->{fp1}; $fp2 = $f->{fp2}; $size = $f->{size}; $type = $f->{type}; print ""; print "\n"; } print "
Key IDKey OwnerKey FingerprintKey SizeKey TypeKey Info Matches?Owner ID Matches?
$id$owner$fp1 $fp2$size$type  
\n"; print ""; signing-party-2.12/keyanalyze/willy/report000066400000000000000000000052431500571021200210000ustar00rootroot00000000000000#!/bin/sh input=${1%%/} output=$1/output if [ ! -e $input ]; then echo Directory $input not found exit 1 fi # functions update() { if [ -e $input/pubring.ids ]; then grep -v '^#' $input/pubring.ids | xargs \ gpg --options $input/options --recv-keys fi test -e $input/pubring.gpg && chmod 644 $input/pubring.gpg rm -f $input/pubring.gpg~ } analyse() { rm -rf $output pgpring -S -k $input/pubring.gpg \ | grep "\(pub\|sig\|rev\|uid\)" \ | sed -e "s/^\([a-z]*\).*:\([0-9A-F]\{16\}\):.*/\1 \2/g" \ -e "s/^uid:.*/uid/" \ | process_keys > $input/preprocess.keys keyanalyze -i $input/preprocess.keys -o $output/ rm $input/preprocess.keys sort -n -k 3 < $output/msd.txt | nl -s ' ' > $output/msd-sorted.txt ./unsign $input cat $output/msd-sorted.txt | ./msd2html $input/options \ > $output/top50table.html cp $input/pubring.ids $input/pubring.gpg $output/ gpg --options $input/options --export -a >$output/pubring.asc } graph() { if [ -e $input/showall ]; then ALL=-a fi if [ -e $input/nograph ]; then RSYNC_DELETE=no else echo -n "Graph generation started at " date gpg --options $input/options --list-sigs | \ sig2dot $ALL 2>/dev/null | \ neato -Tps > $output/graph.ps echo -n "Finished at " date convert $output/graph.ps $output/graph.jpg convert -geometry 320x240 $output/graph.ps \ $output/graph-small.jpg fi } party() { if [ -e $input/party.ids ]; then gpg --options $input/options --export \ `grep -v '^#' $input/party.ids` >$output/party.gpg ./party-table.pl $output/party.gpg >$output/party.html gpg --options $input/options --fingerprint \ `grep -v '^#' $input/party.ids` >$output/sassaman.txt md5sum $output/sassaman.txt >$output/sassaman.md5 sha1sum $output/sassaman.txt >$output/sassaman.sha1 fi } report() { if [ -e $input/report_top.$1.in ]; then date=$(date '+%d %B %Y') sed "s/&date;/$date/" <$input/report_top.$1.in \ > $output/report.$1 cat $output/top50table.html $input/report_bottom.$1.in \ >> $output/report.$1 rm $output/top50table.html fi if [ -e $input/index.$1 ]; then cp -a $input/index.$1 $output/ fi } nonrecip() { if [ -e $input/non-recip.in ]; then ./cosign $output/*/* | \ grep -E 'has not signed ([1-9][0-9]|[5-9])' | \ sort -g -k 1.28 |tac | \ ./non-recip $input >$output/non-recip.html fi } upload() { if [ -e $input/destination ]; then dest=$(sed -e "s/\$input/$input/" < $input/destination) if [ "$RSYNC_DELETE" = "no" ]; then rsync -az $output/ $dest else rsync -az --delete $output/ $dest fi fi } # program update analyse graph party report html report php nonrecip if [ -x $input/update.sh ]; then $input/update.sh $input $output fi upload signing-party-2.12/keyanalyze/willy/sigids000066400000000000000000000003341500571021200207430ustar00rootroot00000000000000#!/bin/sh if [ $# -eq 0 ]; then echo "Must provide at least a key ID" exit 1 fi while [ $# -gt 1 ]; do gpgopts="$gpgopts $1" shift done gpg $gpgopts --fast-list-mode --list-sigs $1 | grep ^sig | cut -c13-20 |sort -u signing-party-2.12/keyanalyze/willy/unsign000066400000000000000000000044051500571021200207670ustar00rootroot00000000000000#!/usr/bin/perl # unsign takes the output from keyanalyze and adds useful information such as # the keys that haven't signed you and you haven't signed within this set. # This functionality should probably be added as an option to keyanalyze. $group=$ARGV[0]; sub print_keys { my ($title, @array) = @_; my $size = $#array + 1; print "\n$title:\n"; foreach (@array) { print " $_ $names{$_}\n"; } print "Total: $size keys in this set\n"; } sub set_diff { my ($firstref, $secondref) = @_; my @result; ELEMENT: foreach $element (@$firstref) { foreach $test (@$secondref) { next ELEMENT if $element eq $test; } push @result, $element; } return @result; } sub read_keyfile { my ($name, $toref, $fromref) = @_; open(KEY, $name) or return 1; my $state = 0; while (my $line = ) { if ($line =~ "^Signatures to") { $state = 1; } elsif ($line =~ "^Total:") { $state = 0; } elsif ($line =~ "^Signatures from") { $state = 2; } elsif ($state == 1) { my @key = split(' ', $line); push @$toref, @key[1]; } elsif ($state == 2) { my @key = split(' ', $line); push @$fromref, @key[1]; } } close(KEY); return 0; } open(IDS, "$group/pubring.ids") or die "Could not open $group\n"; while ($id = ) { next if ($id =~ /^#/); next if ($id =~ /^$/); # chomp $id; $id =~ s/\s+$//; $id = substr($id, -8) if length($id) > 8; push @ids, $id; $name = `gpg --options $group/options --list-keys $id`; $name =~ s/\n.*//s; $name =~ s/^.*[0-9][0-9] //; $name =~ s/@/-at-/g; $names{$id} = $name; } close(IDS); foreach $key (@ids) { my $name = $group . "/output/" . substr($key, 0, 2) . "/" . $key; my @to; my @from; next if read_keyfile($name, \@to, \@from); push @to, $key; push @from, $key; my @nonsigned = set_diff(\@ids, \@from); my @nonsigners = set_diff(\@ids, \@to); my @first = set_diff(\@nonsigned, \@nonsigners); my @third = set_diff(\@nonsigned, \@first); my @second = set_diff(\@nonsigners, \@nonsigned); open(KEY, '>>', $name) or die "Cannot open $name\n"; my $oldfh = select(KEY); print_keys("This key has been signed by, but has not signed", @first); print_keys("This key has signed, but has not been signed by", @second); print_keys("This key is not directly connected to", @third); select($oldfd); close(KEY); } signing-party-2.12/keyart/000077500000000000000000000000001500571021200155215ustar00rootroot00000000000000signing-party-2.12/keyart/BUGS000066400000000000000000000000361500571021200162030ustar00rootroot00000000000000BUGS ---- None at this time. signing-party-2.12/keyart/LICENSE000066400000000000000000000025461500571021200165350ustar00rootroot00000000000000Copyright (c) 2014, Aaron Toponce (c) 2016 Guilhem Moulin All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. signing-party-2.12/keyart/README000066400000000000000000000017561500571021200164120ustar00rootroot00000000000000OpenPGP Random Art ------------------ keyart takes any argument that can uniquely identify a key (name, email address, short key ID, long key ID, fingerprint, etc), and prints out its random visualization ASCII art based on the public key fingerprint. Please see 'doc/drunken-bishop.txt' for more information about the algorithm and changes compared to OpenSSH keys. Usage ----- keyart can take a publicly exported GPG key or keyring to draw the art, or it can use an identifier to uniquely identify a key in your public keyring, such as an email address, short key ID, long key ID or key fingerprint. To produce the random art for a key: $ keyart 0x8086060F +----[DSA 1024]-----+ |E . . ^^i. | |:l. . i.^i:. | |^^.. :.^: : | |: ^ .l | |.. . . : | | . S | | | | | | | | | | | +----[8086060F]-----+ signing-party-2.12/keyart/TODO000066400000000000000000000004611500571021200162120ustar00rootroot00000000000000TODO ---- * Add support for providing multiple exported keys. * Add support for providing a 40-character OpenPGP fingerprint string. * Add support for keyrings. * Add ability to create an HTML/PDF document for keysigning parties. * Create documentation on analysis of art collisions, similar to OpenSSH. signing-party-2.12/keyart/doc/000077500000000000000000000000001500571021200162665ustar00rootroot00000000000000signing-party-2.12/keyart/doc/druken-bishop.txt000066400000000000000000000150211500571021200216000ustar00rootroot00000000000000Reimplement the "Drunken Bishop" walk as implemented by OpenSSH. We use a larger field size for GnuPG due to SHA1 fingerprint sizes (11x19). See http://www.dirk-loss.de/sshvis/drunken_bishop.pdf for the algorithm and security analysis for OpenSSH. The field is as defined: 111111111 0123456789012345678 +-------------------+x (column) 0| | 1| | 2| | 3| | 4| | 5| S | 6| | 7| | 8| | 9| | 10| | +-------------------+ y (row) Each position on the board can be represented by its cartesian coordinates (x,y). We can assign each positition a numerical value by using the equation: pos = x + 19y Each position on the board contains an ASCII character the represents the frequency of visits by the bishop. A blank position has not been visited. The more the bishop has visited a square, the heavier or more dense the ASCII character should be. From light -> dark, the following scale should be used: " .'`^",:;Il!i><~+_-?][}{1)(|\/tfjrxnuvczXYUJCLQ0OZmwqpdbkhao*#MW&8%B@$" See the table below (note, this does not necessarily follow OpenSSH): +-+-+-+-+-+-+-+-+-+-+--+--+--+--+--+--+--+--+--++--+--+ |0|1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18||19|20| +-+-+-+-+-+-+-+-+-+-+--+--+--+--+--+--+--+--+--++--+--+ | |.|^|:|l|i|?|(|f|x|X |Z |# |M |W |& |8 |% |@ ||S |E | +-+-+-+-+-+-+-+-+-+-+--+--+--+--+--+--+--+--+--++--+--+ 'S' and 'E' are special characters that represent the starting and ending location of the bishop respectively. 'S' always starts at coordinates (9,5), which is position 104, the center of the board. Movement is defined by taking the fingerprint, and coverting each character to its binary value. For example, the fingerprint: E041 3539 273A 6534 A3E1 9259 22EE E048 8086 060F has the binary values of: 1110000001000001 0011010100111001 0010011100101010 0110010100110100 1010001111100001 1001001001011001 0010001011101110 1110000001001000 1000000010000110 0000011000001111 In OpenSSH, the movement is found using bit-pairs at a time, left to right, least significant bit to most sifginifant bit. In this implementation for OpenPGP, the same rule applies. So: +-----+-----++-----+-----++-----++-----+-----++-----+-----+ Fingerprint: | E | 0 || 4 | 1 || ... || 0 | 6 || 0 | F | +--+--+--+--++--+--+--+--++-----++--+--+--+--++--+--+--+--+ Bit-pair: |11|10|00|00||01|00|00|01|| ... ||00|00|01|10||00|00|11|11| +--+--+--+--++--+--+--+--++-----++--+--+--+--++--+--+--+--+ Step: |4 |3 |2 |1 ||8 |7 |6 |5 || ... ||76|75|74|73||80|79|78|77| +--+--+--+--++--+--+--+--++-----++--+--+--+--++--+--+--+--+ The direction of our drunken bishop follows standard Chess rules for the bishop piece, moving only on the diagnal across the beard, which is defined as follows: +------+-----+ | Pair | Dir | +------+-----+ N | 00 | NW | ^ +------+-----+ | | 01 | NE | W <--+--> E +------+-----+ | | 10 | SW | v +------+-----+ S | 11 | SE | +------+-----+ The bishop starts in the center of the board at position 104. So, each possible move would place him on the following positions on the board, after the first move: +------+-----+------+ | Pair | Pos | Diff | +------+-----+------+ | 00 | 84 | -20 | +------+-----+------+ | 01 | 86 | -18 | +------+-----+------+ | 10 | 122 | +18 | +------+-----+------+ | 11 | 124 | +20 | +------+-----+------+ We must cleanly handle how the bishop behaves when he reaches the edge of the board, or a corner. We'll define the types of positions as follows: +-------------------+ |aTTTTTTTTTTTTTTTTTb| |LMMMMMMMMMMMMMMMMMR| a = NW corner |LMMMMMMMMMMMMMMMMMR| b = NE corner |LMMMMMMMMMMMMMMMMMR| c = SW corner |LMMMMMMMMMMMMMMMMMR| d = SE corner |LMMMMMMMMMMMMMMMMMR| T = Top edge |LMMMMMMMMMMMMMMMMMR| B = Bottom edge |LMMMMMMMMMMMMMMMMMR| R = Right edge |LMMMMMMMMMMMMMMMMMR| L = Left edge |LMMMMMMMMMMMMMMMMMR| M = Middle pos. |cBBBBBBBBBBBBBBBBBd| +-------------------+ When a bishop finds himself in one of these positions, we'll define his adjusted movement, if necessary, as follows: +-----+------+---------+----------+--------+ | Pos | Bits | Heading | Adjusted | Offset | +-----+------+---------+----------+--------+ | a | 00 | NW | no move | 0 | | | 01 | NE | E | +1 | | | 10 | SW | S | +19 | | | 11 | SE | SE | +20 | +-----+------+---------+----------+--------+ | b | 00 | NW | W | -1 | | | 01 | NE | no move | 0 | | | 10 | SW | SW | +18 | | | 11 | SE | S | +19 | +-----+------+---------+----------+--------+ | c | 00 | NW | N | -19 | | | 01 | NE | NE | -18 | | | 10 | SW | no move | 0 | | | 11 | SE | E | +1 | +-----+------+---------+----------+--------+ | d | 00 | NW | NW | -20 | | | 01 | NE | N | -19 | | | 10 | SW | W | -1 | | | 11 | SE | no move | 0 | +-----+------+---------+----------+--------+ | T | 00 | NW | W | -1 | | | 01 | NE | E | +1 | | | 10 | SW | SW | +18 | | | 11 | SE | SE | +20 | +-----+------+---------+----------+--------+ | B | 00 | NW | NW | -20 | | | 01 | NE | NE | -18 | | | 10 | SW | W | -1 | | | 11 | SE | E | +1 | +-----+------+---------+----------+--------+ | R | 00 | NW | NW | -20 | | | 01 | NE | N | -19 | | | 10 | SW | SW | +18 | | | 11 | SE | S | +19 | +-----+------+---------+----------+--------+ | L | 00 | NW | N | -19 | | | 01 | NE | NE | -18 | | | 10 | SW | S | +19 | | | 11 | SE | SE | +20 | +-----+------+---------+----------+--------+ | M | 00 | NW | NW | -20 | | | 01 | NE | NE | -18 | | | 10 | SW | SW | +18 | | | 11 | SE | SE | +20 | +-----+------+---------+----------+--------+ signing-party-2.12/keyart/doc/examples/000077500000000000000000000000001500571021200201045ustar00rootroot00000000000000signing-party-2.12/keyart/doc/examples/party.sh000077500000000000000000000006441500571021200216060ustar00rootroot00000000000000#!/bin/sh set -ue FILE="${1:-art.txt}" LEN=$(wc -l <"$FILE") I=1 while [ $I -le $LEN ]; do LINE1="$(sed -n "$((I+00))p" "$FILE")" LINE2="$(sed -n "$((I+13))p" "$FILE")" LINE3="$(sed -n "$((I+26))p" "$FILE")" LINE4="$(sed -n "$((I+39))p" "$FILE")" LINE5="$(sed -n "$((I+52))p" "$FILE")" echo "${LINE1}${LINE2}${LINE3}${LINE4}${LINE5}" [ $((I%13)) -eq 0 ] && I=$((I+52)) I=$((I+1)) done signing-party-2.12/keyart/doc/keyart.1000066400000000000000000000051701500571021200176520ustar00rootroot00000000000000.\" Manpage for keyart .\" Aaron Toponce .TH keyart 1 "17 Jun 2014" .SH NAME .B keyart \- Create ASCII art of an OpenPGP key. .SH SYNOPSIS .B keyart [\-c|\-\-color] [\-l|\-\-longid] [\-f|\-\-fingerprint \fIHEX\fR [\-f|\-\-fingerprint \fIHEX\fR ...]] [\-k|\-\-keyring \fIKEYRING\fR [\-k|\-\-keyring \fIKEYRING\fR ...]] [\fIKEYID\fR ...] .SH DESCRIPTION .B keyart creates an ASCII art representation of public OpenPGP keys. The art is an implementation of the Drunken Bishop by Dirk Loss. Documentation about the algorithm can be found in /usr/share/doc/signing-party/keyart/, or as appropriate for your distribution. .B keyart supports printing the ASCII art in both plain text (default) and ANSI color. ANSI color uses a "heat map", where cold (blue) represents squares in the room the drunk bishop has rarely visited, and hot (red) represents squares in the room the drunk bishop has frequently visited. There is a 1:1 ratio of color to printed character. The \fIKEYID\fRs are key identifiers (fingerprint, keyid, user ID, etc. see \fBgpg\fR(1) for details). If no \fIKEYID\fR is given, \fBkeyart\fR creates an ASCII art representation for all keys found. .SH OPTIONS .TP 8 .B \-c | \-\-color Print the ASCII art using ANSI color to the terminal. .TP 8 .B \-l | \-\-longid Print the 16-character long ID of a on OpenPGP key in the footer badge. Default is to print the 8-character short ID. Silently ignored if "\fB\-f | \-\-fingerprint \fIHEX\fR" is passed. .TP 8 .B \-f | \-\-fingerprint \fIHEX\fR Any arbitrary hexadecimal string of any length. Could be an MD5, SHA1, SHA2, or SHA3 hexadecimal checksum. Useful for comparing and analyzing potential collisions with existing keys. This option can be repeated multiple times. .TP 8 .B \-k | \-\-keyring \fIKEYRING\fR An OpenPGP public key file or keyring, from which the \fIKEYID\fRs are to be found. If this option is not present, the \fIKEYID\fRs are found from GnuPG's default keyring (usually ~/.gnupg/pubring.gpg); if it is repeated multiple times, the provided \fIKEYRING\fRs are used as successive sources. .TP 8 .B \-h | \-\-help Print the help message and quit. .SH ENVIRONMENT .TP 8 .I HOME Set the default home directory. .TP 8 .I GNUPGBIN Set the gpg binary. Default: "gpg". .TP 8 .I GNUPGHOME Set the default working directory for gpg. Default: "~/.gnupg". .SH EXIT STATUS .TP 8 .B 0 \fBkeyart\fR was executed successfully. .TP 8 .B 1 \fBgpg\fR(1) or \fBgpg2\fR(1) are not installed. .TP 8 .B 2 The supplied hexadecimal string is not a multiple of 8 bytes. .SH SEE ALSO .BR gpg (1) .SH AUTHOR .B keyart and its documentation is written by Aaron Toponce . signing-party-2.12/keyart/doc/party-worksheet000066400000000000000000000025441500571021200213660ustar00rootroot00000000000000BASICS ------ To create a worksheet for a keysigning party, you will need to import each of the participants keys to your GPG keyring. If you've been storing them in a separate keyring, this should be easy. Once each key has been imported, create a single "art.txt" file with all the keys. Something like: $ keyart 8086060F F34D3BC5 C22D2FCF > art.txt In the examples/ directory of this documentation, is a 'party.sh' script that can transform that art.txt file to 5 key art per line. It will print the output to STDOUT. So, you could redirect to a file with: $ ./party.sh art.txt > worksheet.txt Open 'worksheet.txt' in a text editor, and add newlines as necessary, so the key art is not broken by a page break. Use your text editor 'print preview' for help. Print off your modified 'worksheet.txt' for each key signing party attendee. COLOR WORKSHEET --------------- If you would like to create an ANSI colored worksheet, you can install the Python "ansi2html" library, and redirect your output to an HTML file, which could then be used to print the worksheet: $ sudo easy_install ansi2html $ keyart -c 8086060F F34D3BC5 C22D2FCF > art.txt $ ./party.sh art.txt | ansi2html > worksheet.html You may need to make some HTML adjustments to prevent the printer from breaking up the key art on the page break. Use your browser's 'print preview' for help. signing-party-2.12/keyart/keyart000077500000000000000000000213501500571021200167470ustar00rootroot00000000000000#!/usr/bin/python3 """This script takes a key ID, fingerprint, email address, publicly exported key, or a keyring, and prints the random ASCII art visualization of the key as per the Drunken Bishop algorithm as applied to OpenSSH keys by Dirk Loss.""" import argparse import os import re import subprocess import sys PARSER = argparse.ArgumentParser( description='Creates ASCII art from OpenPGP keys.') PARSER.add_argument('-c', '--color', help='Print the art with ANSI color.', action='store_true') PARSER.add_argument('-l', '--longid', action='store_true', help='Print the 16-character long ID of a key.') PARSER.add_argument('-f', '--fingerprint', type=str, metavar=('HEX'), action='append', help='A hexadecimal string representing a fingerprint.') PARSER.add_argument('-k', '--keyring', type=str, metavar=('KEYRING'), action='append', help='A publicly exported OpenPGP key or keyring.') PARSER.add_argument('keyid', type=str, nargs='*', metavar=('KEYID'), help='A key identifier (email, ID, fingerprint, etc.).') ARGS = PARSER.parse_args() def draw_art(key_size, key_algo, key_curve, key_fpr): """Execute the Drunken Bishop algorithm on a key.""" art = '' f_bytes = [] pos = 104 walk = [pos] visits = [0]*209 temp = '' try: key_bin = bin(int(key_fpr, 16))[2:].zfill(len(key_fpr)*4) except ValueError: print("The supplied fingerprint is not a hexadecimal string.") sys.exit(3) for i, char in enumerate(key_bin): temp += char if i % 2 == 1: f_bytes.append(temp) temp = '' # create a little-endian bit-pair array for i in range(0, len(f_bytes), 4): f_bytes[i], f_bytes[i+3] = f_bytes[i+3], f_bytes[i] f_bytes[i+1], f_bytes[i+2] = f_bytes[i+2], f_bytes[i+1] for pair in f_bytes: if (20 <= pos <= 36 or 39 <= pos <= 55 or 58 <= pos <= 74 or 77 <= pos <= 93 or 96 <= pos <= 112 or 115 <= pos <= 131 or 134 <= pos <= 150 or 153 <= pos <= 169 or 172 <= pos <= 188): if pair == '00': pos -= 20 # Square 'M' elif pair == '01': pos -= 18 elif pair == '10': pos += 18 else: pos += 20 elif 1 <= pos <= 17: # Square 'T' if pair == '00': pos -= 1 elif pair == '01': pos += 1 elif pair == '10': pos += 18 else: pos += 20 elif 191 <= pos <= 207: # Square 'B' if pair == '00': pos -= 20 elif pair == '01': pos -= 18 elif pair == '10': pos -= 1 else: pos += 1 elif pos in [19, 38, 57, 76, 95, 114, 133, 152, 171]: # Square 'L' if pair == '00': pos -= 19 elif pair == '01': pos -= 18 elif pair == '10': pos += 19 else: pos += 20 elif pos in [37, 56, 75, 94, 113, 132, 151, 170, 189]: # Square 'R' if pair == '00': pos -= 20 elif pair == '01': pos -= 19 elif pair == '10': pos += 18 else: pos += 19 elif pos == 0: # Square 'a' if pair == '01': pos += 1 elif pair == '10': pos += 19 elif pair == '11': pos += 20 elif pos == 18: # Square 'b' if pair == '00': pos -= 1 elif pair == '10': pos += 18 elif pair == '11': pos += 19 elif pos == 190: # Square 'c' if pair == '00': pos -= 19 elif pair == '01': pos -= 18 elif pair == '11': pos += 1 else: # Square 'd' if pair == '00': pos -= 20 elif pair == '01': pos -= 19 elif pair == '10': pos -= 1 walk.append(pos) for square in walk: visits[square] += 1 if visits[square] > 18: visits[square] = 18 # See https://tools.ietf.org/html/rfc4880#section-9.1 # Also https://tools.ietf.org/html/rfc6637#section4 if key_algo == '1' or key_algo == '2' or key_algo == '3': header = 'rsa' + key_size # RSA elif key_algo == '16': header = 'elg' + key_size # Elgamal encrypt only elif key_algo == '17': header = 'dsa' + key_size # DSA elif key_algo == '20': header = 'xxx' + key_size # Elgamal encrypt+sign (legacy) elif key_algo == '18' or key_algo == '19' or key_algo == '22': if key_curve is not None and key_curve != '': header = key_curve elif key_algo == '18': header = 'ecdh' + key_size elif key_algo == '19': header = 'ecdsa' + key_size elif key_algo == '22': header = 'eddsa' + key_size else: header = 'N/A' header = "[%s]" % header if len(header) > 19: header = '' art += '+' + header.center(19, '-') + '+\n' for i, visit in enumerate(visits): # Build up the art with the boundaries and newlines if i % 19 == 0: art += "|{}" elif i % 19 == 18: art += "{}|\n" else: art += '{}' # Insert the 'coin' into the art at this position if i == 104: # Starting position art = art.format(_get_coin(visit, ARGS.color, coin='S')) elif i == walk[len(walk)-1]: # Ending position art = art.format(_get_coin(visit, ARGS.color, coin='E')) else: art = art.format(_get_coin(visit, ARGS.color)) if key_size and ARGS.longid: footer = "["+key_fpr[-16:]+"]" elif key_size: footer = "["+key_fpr[-8:]+"]" else: footer = '' art += '+' + footer.center(19, '-') + '+' return art def _get_coin(num_of_hits, ansi_art=False, coin=None): """Returns the coin for this humber of hits. If ansi_art is enabled the coin will be colorized with ansi codes. If coin is not None, it will use that coin instead of the default (used for the 'S' and 'E', start end coins).""" coins = [' ', '.', '^', ':', 'l', 'i', '?', '(', 'f', 'x', 'X', 'Z', '#', 'M', 'W', '&', '8', '%', '@'] colors = ['', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', ''] reset = '' if ansi_art: colors = [ '', # no coin '\033[38;5;21m', # blue (cold) '\033[38;5;39m', '\033[38;5;50m', '\033[38;5;48m', '\033[38;5;46m', # green '\033[38;5;118m', '\033[38;5;190m', '\033[38;5;226m', # yellow '\033[38;5;220m', '\033[38;5;214m', # orange '\033[38;5;208m', '\033[38;5;202m', '\033[38;5;196m', # red '\033[38;5;203m', '\033[38;5;210m', '\033[38;5;217m', # pink '\033[38;5;224m', '\033[38;5;231m' # white (hot) ] reset = '\033[0m' color = colors[num_of_hits] if not coin: coin = coins[num_of_hits] return '{}{}{}'.format(color, coin, reset) def gpg_cmd(cmd): gpg = subprocess.Popen(cmd, stdout=subprocess.PIPE) for lines in gpg.communicate()[0].decode('utf-8').split('\n'): colons = lines.split(':') if colons[0] == 'pub': size = colons[2] algo = colons[3] curve = colons[16] if 16 < len(colons) else None elif colons[0] == 'fpr' and algo is not None: print(draw_art(size, algo, curve, colons[9])) size = None algo = None curve = None if __name__ == '__main__': gpg_bin = os.getenv('GNUPGBIN', 'gpg') strip_nonhex = re.compile('[^a-fA-F0-9]+') cmd = [gpg_bin, '--no-options', '--no-auto-check-trustdb', '--with-colons', '--fingerprint'] if ARGS.fingerprint: for fpr in ARGS.fingerprint: fpr = strip_nonhex.sub('',fpr) if len(fpr) % 8 != 0: print("Hex string must be a multiple of 8 bytes.") sys.exit(2) print(draw_art(None, None, None, fpr)) if ARGS.keyring: cmd.append('--no-default-keyring') cmd.extend(['--keyring=%s' % os.path.abspath(keyring) for keyring in ARGS.keyring]) gpg_cmd(cmd) if ARGS.keyid: cmd.append('--') cmd.extend(ARGS.keyid) gpg_cmd(cmd) signing-party-2.12/keylookup/000077500000000000000000000000001500571021200162445ustar00rootroot00000000000000signing-party-2.12/keylookup/COPYING000066400000000000000000000431111500571021200172770ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 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, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. signing-party-2.12/keylookup/Makefile000066400000000000000000000003431500571021200177040ustar00rootroot00000000000000all: install: install -D keylookup $(DESTDIR)/usr/bin/keylookup install -D -m644 keylookup.1 $(DESTDIR)/usr/share/man/man1/keylookup.1 install -D -m644 NEWS \ $(DESTDIR)/usr/share/doc/signing-party/NEWS.keylookup clean: signing-party-2.12/keylookup/NEWS000066400000000000000000000012211500571021200167370ustar00rootroot00000000000000Release notes for keylookup 3.0 (2005-08-16) * Make keylookup use gpg --search instead of querying the keyserver directly. * Sort keys by creation date. Release notes for keylookup 2.2 (2002-09-24) * Some keyservers have started putting the key type (DSA or RSA) in their replies to searches. Keylookup now handles that. * Untaint keyIDs before passing them to GnuPG as passing tainted argumentes in exec() is now deprecated. (closes: DebianBug #161392) Release notes for keylookup 2.1 (2002-06-17) * Support newstyle gnupg config file (honor-proxy); (closes: SavannahBug #523) * Rewrote TODO in English; (closes: DebianBug #120861). signing-party-2.12/keylookup/keylookup000077500000000000000000000227001500571021200202150ustar00rootroot00000000000000#!/usr/bin/perl -w # Copyright (c) 2000, 2002 Christian Kurz , # Copyright (c) 2000, 2002, 2005 Peter Palfrader # # 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, 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, write to the Free Software Foundation, # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA # # Keylookup is part of signing-party. delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'}; $|=1; # Always flush buffers use strict; use IO::Socket; use IPC::Open3; use Getopt::Long; my $version = '@@VERSION@@'; # Strings to use in the dialog|whiptail frontend my $TITLE = 'Import Keys'; my $BACKTITLE = "KeyLookup $version"; my $INSTRUCTION = 'Select keys to import:'; # my @TPUTCOL=('tput', 'cols'); my @TPUTROW=('tput', 'lines'); my $DEFAULTCOLS = 80; my $DEFAULTROWS = 25; # Size of the dialog boxes, will be set in calcDialogSize; my $MAX_UID_FIELD_LEN; my @DIALOGSIZE; my @WHIPTAILSIZE; # Was the keyserver overriden|given on the command line? # This is used to find out wheter we need to instruct the user # to give the keyserver option to GnuPG. my $keyserverWasSetOnCmdLine = 0; # Maps algorithm numbers to algorithm types as defined in RFC 2400. my %ALGOS = ( 1 => 'R', # RSA 2 => 'r', # RSA encrypt only (deprecated) 3 => 's', # RSA sign only (deprecated) 16 => 'g', # ElGamal encrypt only 20 => 'g', # ElGamal sign and encrypt (all OpenPGP implementations cryptographically broken, do not use. no longer part of OpenPGP) 17 => 'D' # DSA ); # getHits receives all options as a parameter, calls fetchIT to # query a keyserver, processes the output from the keyserver and # stores it in a datastructure for later use. sub getHits($) { my $options = shift; my $pid = open(KID, '-|'); defined ($pid) or die ("Cannot fork: $!\n"); unless ($pid) { close (STDIN); open (STDIN, "/dev/null") || die ("Cannot open /dev/null as stdin: $!\n"); # child my @ops = ($ENV{GNUPGBIN} // 'gpg'); if ($options->{'keyserver'}) { push @ops, '--keyserver='.$options->{'keyserver'}; }; push @ops, '--command-fd=0'; push @ops, '--batch'; push @ops, '--no-tty'; push @ops, '--with-colons'; push @ops, '--fixed-list-mode'; push @ops, '--search'; push @ops, @{$options->{'search'}}; exec(@ops); die ("Cannot exec GnuPG: $!\n"); }; my %keys; my $currentKey; while () { chomp; if ( $_ eq "" ) { next; } my ($type, undef) = split /:/; if ($type eq 'pub') { my ($type, $keyid, $algo, $bits, $created, undef, $revoked) = split /:/; $currentKey = { 'bits' => $bits, 'type' => (defined $ALGOS{$algo} ? $ALGOS{$algo} : '#'.$algo), 'keyid' => $keyid, 'created' => $created, 'revoked' => $revoked, 'uid' => [] }; $keys{ $keyid } = $currentKey; } elsif (defined $currentKey && $type eq 'uid') { my ($type, $name) = split /:/; if ($currentKey->{'revoked'} eq 'r') { $name .= ' [REVOKED]'; }; push @{ $currentKey->{'uid'} }, $name; }; }; close KID; waitpid $pid, 0; return \%keys; }; # returns the number of columns of the terminal sub getCols { my $pid; return $DEFAULTCOLS unless (defined ($pid = open(KID, "-|"))); unless ($pid) { exec (@TPUTCOL); }; my $cols = ; close KID; wait; return (defined $cols) ? $cols : $DEFAULTCOLS; }; # returns the number of lines of the terminal sub getRows { my $pid; return $DEFAULTROWS unless (defined ($pid = open(KID, "-|"))); unless ($pid) { exec (@TPUTROW); }; my $rows = ; close KID; wait; return (defined $rows) ? $rows : $DEFAULTROWS; }; # sets MAX_UID_FIELD_LEN, DIALOGSIZE, and WHIPTAILSIZE sub calcDialogSize { my $COLS = &getCols(); my $ROWS = &getRows(); $MAX_UID_FIELD_LEN = $COLS - 27; @DIALOGSIZE = ($ROWS-7, $COLS-7, $ROWS-14); @WHIPTAILSIZE = ($ROWS-7, $COLS-7, $ROWS-14); } sub prepareForDialog { my $keys = shift; my @keyargs = (); for my $keyid (sort {- ($keys->{$a}->{'created'} <=> $keys->{$b}->{'created'})} keys %$keys) { for (@{ $keys->{$keyid}->{'uid'} }) { push @keyargs, $keys->{$keyid}->{'keyid'}, length() <= $MAX_UID_FIELD_LEN ? $_ : substr($_, 0, $MAX_UID_FIELD_LEN-2) . '..', 'off'; }; my (undef,undef,undef,$mday,$mon,$year,undef,undef,undef) = localtime ($keys->{$keyid}->{'created'}); push @keyargs, $keys->{$keyid}->{'keyid'}, sprintf( "[created: %s-%s-%s]", $year+1900, $mon+1, $mday ), 'off'; push @keyargs, '-'x8, '-'x40, 'off'; }; pop @keyargs; pop @keyargs; pop @keyargs; return \@keyargs; }; sub prepareForTXT { my $keys = shift; my @lines = (); for my $keyid (sort {- ($keys->{$a}->{'created'} <=> $keys->{$b}->{'created'})} keys %$keys) { my (undef,undef,undef,$mday,$mon,$year,undef,undef,undef) = localtime ($keys->{$keyid}->{'created'}); push @lines, sprintf( "%s%s/%s %s-%s-%s\n", $keys->{$keyid}->{'bits'}, $keys->{$keyid}->{'type'}, $keys->{$keyid}->{'keyid'}, $year+1900, $mon+1, $mday ); push @lines, map { ' 'x26 . $_ . "\n" } @{ $keys->{$keyid}->{'uid'} }; push @lines, "\n"; }; return \@lines; }; sub callDialog { my $args = shift; # open(SAVEOUT, ">&STDOUT") || die ("Cannot save STDOUT: $!\n"); # open(SAVEIN , "<&STDIN" ) || die ("Cannot save STDIN: $!\n"); my $pid = open3( '<&STDIN', '>&STDOUT', \*ERRFH, @$args); my %unique; my @keys = grep { !$unique{$_}++ } # get the keyID; can be 8, 16 or 40 nibbles grep /^((([a-zA-Z0-9]{24})?[a-zA-Z0-9]{8})?[a-zA-Z0-9]{8})$/, map { s/\s//g; $_ } ; wait; # open(STDOUT, ">&SAVEOUT") || die "Cannot restore STDOUT: $!\n"; # open(STDIN , "<&SAVEIN") || die "Cannot restore STDIN: $!\n"; return \@keys; }; sub selectKeys { my $keys = shift; my $options = shift; my $frontend = $options->{'frontend'}; $frontend = 'dialog' unless (defined $frontend); if ($frontend eq 'dialog') { unless (`which dialog` && $? == 0) { warn("Dialog not executeable/installed. Falling back to Whiptail\n"); $frontend = 'whiptail'; } }; if ($frontend eq 'whiptail') { unless (`which whiptail` && $? == 0 ) { warn("Whiptail not executeable/installed. Falling back to plain\n"); $frontend = 'plain'; } }; if ( $frontend eq 'dialog' ) { calcDialogSize; my @ARGS = ( 'dialog', '--backtitle', $BACKTITLE, '--separate-output', '--title', $TITLE, '--checklist', $INSTRUCTION, @DIALOGSIZE); push @ARGS, @{&prepareForDialog($keys)}; return &callDialog( \@ARGS ); } elsif ( $frontend eq 'whiptail' ) { calcDialogSize; my @ARGS = ( 'whiptail', '--backtitle', $BACKTITLE, '--separate-output', '--title', $TITLE, '--checklist', $INSTRUCTION, @WHIPTAILSIZE, '--'); push @ARGS, @{&prepareForDialog($keys)}; return &callDialog( \@ARGS ); } else { print for (@{ &prepareForTXT( $keys ) }); if ($keyserverWasSetOnCmdLine) { printf ("Now run gpg --keyserver %s --recv-keys \n", $options->{'keyserver'}); } else { print ("Now run gpg --recv-keys \n"); }; ## If no frontend was selected, or selected frontend was plain, ## exit successfully, otherwise with an exitcode != 0 exit (defined $options->{'frontend'} && $options->{'frontend'} ne "" && $options->{'frontend'} ne "plain"); }; }; sub importKeys { my $keyids = shift; my $options = shift; my @args = ($ENV{GNUPGBIN} // 'gpg'); if ($options->{'keyserver'}) { push @args, '--keyserver='.$options->{'keyserver'}; }; push @args, '--recv-keys'; for my $keyid (@$keyids) { # untaint keyids my ($cleanid) = $keyid =~ /^((([a-zA-Z0-9]{24})?[a-zA-Z0-9]{8})?[a-zA-Z0-9]{8})$/; warn ("keyid '$keyid' has unexpected format - skipping\n"), next unless defined $cleanid; push @args, $cleanid; } print "Calling GnuPG...\n"; exec (@args) || die "can't exec gnupg: $!\n"; # won't return }; sub usage { my $errorcode = shift; print << 'EOF' Syntax: keylookup [options] Options: --keyserver= Select keyserver --frontend= One of whiptail, dialog or plain --importall Import all matched keys --help print this message EOF ; exit($errorcode); }; sub version { print "keylookup $version\nWritten by Christian Kurz and Peter Palfrader.\n"; exit(0); }; my %options; GetOptions( \%options, 'keyserver=s', 'frontend=s', 'importall', 'version', 'help') or &usage(1); &version(0) if ($options{'version'}); &usage(0) if ($options{'help'} || ( scalar(@ARGV) == 0)); ## Take all additional arguments to the program as a search target, ## escape the string for use in URLs. $options{'search'} = \@ARGV; my $keys = getHits( \%options ); my $keyids; if (scalar keys %$keys == 0) { print "GnuPG did not find any keys matching your search string.\n"; exit 0; }; if ($options{'importall'}) { my @allkeys = keys %$keys; $keyids = \@allkeys; } else { $keyids = selectKeys($keys, \%options); # won't return if no interactive frontend }; &importKeys($keyids, \%options) if (scalar @$keyids); # won't return signing-party-2.12/keylookup/keylookup.1000066400000000000000000000062141500571021200203530ustar00rootroot00000000000000.TH keylookup 1 "" Jun-2002 "" .\" manual page (c) 2000, 2001, 2002 Christian Kurz, Peter Palfrader .SH NAME .LP keylookup - Fetch and Import GnuPG keys from keyservers. .SH SYNOPSIS \fBkeylookup\fP [\fIoptions\fP] \fIsearch-string\fP .SH DESCRIPTION .LP \fBkeylookup\fR is a wrapper around gpg \-\-search, allowing you to search for keys on a keyserver. It presents the list of matching keys to the user and allows her to select the keys for importing into the GnuPG keyring. For the search and actual import of keys GnuPG itself is called. .SH OPTIONS .IP "\fB\-\-keyserver\fP=\fIkeyserver\fP" 8 Specify the keyserver to use. If no keyserver is specified, it will parse the GnuPG options file for a default keyserver to use. If no keyserver can be found, \fBkeylookup\fP will abort. .IP "\fB\-\-port\fP=\fIport\fP" 8 Use a port other than 11371. .IP "\fB\-\-frontend\fP=\fIfrontend\fP" 8 \fBkeylookup\fP supports displaying the search results with 3 different frondends. Both \fBwhiptail\fP and \fBdialog\fP are interactive and allow the user to select the keys to import. The third frontend \fBplain\fP is non\-interactive and just prints the keys to STDOUT. The user must then call GnuPG him/herself. If available, \fB/usr/bin/dialog\fP is the default. If it is not available but \fB/usr/bin/whiptail\fP is installed, then this is used instead. If nothing else works, we'll fall back to \fBplain\fP. .IP "\fB\-\-importall\fP" 8 Don't ask the user which keys to import, but instead import all keys matching the \fIsearch-string\fP. If this is given no frontend is needed. .IP "\fB\-\-honor\-http\-proxy\fP" 8 Similar to GnuP \fBkeylookup\fP will only honor the \fBhttp_proxy\fP environment variable if this option is given. If it is not given but your GnuPG options file includes it, then \fBkeylookup\fP will use it. .IP "\fB\-\-help\fP" 8 Print a brief help message and exit successfully. .SH ENVIRONMENT .IP "HOME" 10 Used to locate the default home directory. .IP "GNUPGHOME" 10 If set directory used instead of "~/.gnupg". .IP "GNUPGBIN" 10 If set used as gpg binary instead of "gpg". .IP "http_proxy" 10 Only honored when the option \-\-honor\-http\-proxy is set or honor\-http\-proxy is set in GnuPG's config file. .SH EXAMPLES .IP "keylookup Christian Kurz" will query your default keyserver for Christian's keys and offer you to import them into your keyring with the dialog frontend (if available). .IP "keylookup \-\-honor\-http\-proxy \-\-frontend plain wk@gnupg" will query the default keyserver again, now using the http_proxy if the environment variable is defined and list wk@gnupg's (Werner Koch)'s key on STDOUT. .IP "keylookup \-\-keyserver pgp.mit.edu Peter Palfrader" will now ask the keyserver pgp.mit.edu for my (Peter's) keys and display them for import in dialog. .SH FILES .IP "~/.gnupg/options" 10 GnuPG's options file where \fBkeylookup\fP will take the keyserver and honor\-http\-proxy values from if it exists. .SH "SEE ALSO" \fBgpg\fP(1) .SH BUGS .LP Please report bugs using the Debian bug tracking system at https://bugs.debian.org/. .SH AUTHORS .LP Christian Kurz .br Peter Palfrader signing-party-2.12/sig2dot/000077500000000000000000000000001500571021200155755ustar00rootroot00000000000000signing-party-2.12/sig2dot/Makefile000066400000000000000000000003451500571021200172370ustar00rootroot00000000000000all: install: install -D sig2dot $(DESTDIR)/usr/bin/sig2dot install -D -m644 sig2dot.1 $(DESTDIR)/usr/share/man/man1/sig2dot.1 install -D -m644 README.sig2dot \ $(DESTDIR)/usr/share/doc/signing-party/README.sig2dot clean: signing-party-2.12/sig2dot/README.sig2dot000066400000000000000000000006501500571021200200300ustar00rootroot00000000000000sig2dot ------- Sig2dot parses the output of "gpg --list-sigs" into a format suitable for rendering into a graph by springgraph or graphviz. The syntax definition of the .dot files which sig2dot produces can be found in the graphviz man pages. A copy is here: * http://www.graphviz.org/Documentation.php * http://www.graphviz.org/cvs/doc/info/lang.html -- Christoph Berg Sun, 6 Mar 2005 18:11:08 +0100 signing-party-2.12/sig2dot/sig2dot000077500000000000000000000304441500571021200171030ustar00rootroot00000000000000#!/usr/bin/perl -w # sig2dot v0.29 (c) Darxus@ChaosReigns.com, released under the GPL # Download from: http://www.chaosreigns.com/code/sig2dot/ # sig2dot v0.35-0.37 (c) 2005, 2006 Christoph Berg # Download from: http://ftp.debian.org/debian/pool/main/s/sig2dot/ # 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, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # Parses output of "gpg --list-sigs" into a format # suitable for rendering into a graph by graphviz # (http://www.research.att.com/sw/tools/graphviz/) like so: # # $ gpg --list-sigs --keyring ./phillylinux.gpg | ./sig2dot.pl > phillylinux.dot # $ neato -Tps phillylinux.dot > phillylinux.ps # $ convert phillylinux.ps phillylinux.jpg # # Commandline options: # # -b # Black and white / do not colorize. # # -d # Render graph as it appeared on (ignores more recent # signatures). Date must be in the format "YYYY-MM-DD". # Will also ignore keys that have since been revoked. # # -a # Render all keys, even if they're not signed by any other key. # # -u <"string"> # Support localized output of GnuPG for unknown user IDs. For # example, German users have to write (with sh quotation marks!) # "[User-ID nicht gefunden]" if they use GnuPG with German # messages. Default is "[User id not found]". # # -r <"string"> # Support localized output of GnuPG for revoked keys. For # example, French users have to write "révoqué" if they use # GnuPG with French messages. Default is "[revoked". # # -s stats.html # Produces statistics file with number of signatures per node # # -t <"string"> # Graph title # # -h print help # -v print version # -q be quiet # # Changes: # # v0.9 2000-09-14 19:20 strip trailing whitespace from $id more cleanly # v0.10 2000-09-14 19:33 skip revoked keys at the request of Peter Palfrader # v0.11 Nov 22 21:38 use ID for node name instead of username for uniqueness # v0.12 Dec 15 16:20 use names instead of IDs again in stats.html # v0.13 Jun 19 03:15 red is proportional to signatures # v0.14 Jun 19 03:25 blue is proportional to other keys signed # v0.15 Jun 20 17:16 fixed blue, green is proportional to ratio # v0.16 Jun 20 18:55 uniqed %signedby # v0.17 Jan 10 19:10 Use overlap=scale instead of fixed edge lengths. Requires new version of graphviz. # v0.18 Jan 23 11:53 stats.html is now valid html (v.01 transitional) # v0.23 May 3 18:52 bunch of new stuff, including -b flag (black & white), and fixes devision by zero error # v0.24 May 3 18:59 add black outline to nodes, prettier (changed node attribute "color" to "fillcolor") # v0.25 May 3 19:06 cleaned up anti- devision by zero code a little # v0.26 May 4 00:08 strip all non-digit characters from $renderdate # v0.27 May 10 00:23:49 2002 use {}'s to write 1 line per public key instead of one line per signature (much shorter) # v0.28 Feb 13 2003 Change regex to handle option trust digit # # v0.29 Feb 18 2003 Add -s option to optionally produce statistics file # # v0.30 Feb 18 2003 Make --list-sigs regex more robust # Marco Bodrato # v0.31 Jul 28 2003 Add -u option for localized output of GnuPG # Marcus Frings # further changes are documented in debian/changelog use strict; my $version = "0.37"; my $chartchar = "*"; my $renderdate = ""; my ($stats, $color, $all, $not_found, $revokestr, $title); use Getopt::Std; my %opt; getopts('d:u:r:s:bahqvt:', \%opt); sub version { print < EOT } if ($opt{h}) { version(); print < sigs.dot -a Graph all keys, even if they do not have a signature -b Black and white / do not colorize. -d YYYY-MM-DD Render graph as it appeared on date. -h Print this help and exit. -q Be quiet. -r sting key-is-revoked string (default: "[revoked"). -s stats.html Produces statistics file with number of signatures per node. -t title Graph title -u string user-id-not-found string (default: "[user id not found]"). -v Print version and exit. EOT exit 0; } if ($opt{v}) { version(); exit 0; } if ($opt{d}) { $renderdate = $opt{d}; print STDERR "Printing from date: $renderdate.\n"; $renderdate =~ s/\D+//g; } if ($opt{s}) { $stats = $opt{s}; print STDERR "Print statistics to $stats.\n"; } if ($opt{b}) { $color = 0; print STDERR "Black and White.\n" unless $opt{q}; } else { $color = 1; print STDERR "Color.\n" unless $opt{q}; } if ($opt{a}) { $all = 1; } else { $all = 0; } if ($opt{u}) { $not_found = lc $opt{u}; } else { $not_found = "[user id not found]"; # this changed from gpg 1.2 -> 1.4 } if ($opt{r}) { $revokestr = lc $opt{r}; } else { $revokestr = "[revoked"; # this changed from gpg 1.2 -> 1.4 } if ($opt{t}) { $title = lc $opt{t}; } else { $title = "Keyring Statistics"; } my ($owner, %name, %revlist, %sigstmp, %signedbytmp, %idlist, @names, %revs); while (my $line = ) { chomp $line; # gpg 1.2 #pub 1024D/807CAC25 2003-08-01 Michael Ablassmeier (abi) #sig B3B2A12C 2004-01-28 [User id not found] #sig 3 9456ADE2 2004-02-07 Michael Schiansky # gpg 1.4: #pub 1024D/807CAC25 2003-08-01 #uid Michael Ablassmeier (abi) #sig B3B2A12C 2004-01-28 [User ID not found] #sig 3 9456ADE2 2004-02-07 Michael Schiansky # type id date name if ($line =~ m#(pub|uid|sig|rev|sub)[ !\?][ \dLNPRTX]{0,8} +([^ ]+) +([^ ]+)(?: +"?([^<"]*))?#) # differences: # " " -> "[ !\?]" (to use 'gpg --check-sigs|sig2dot.mio|springgraph|display') # "[ \d]" -> "[ \dLRXP]" (signature attributes) # "[^<]+" -> "[^<]*" (to recognise "pub" lines whitout a name) # if ($line =~ m#([\w]+) [ \d]? +([^ ]+) +([^ ]+) +([^<]+)#) # if ($line =~ m#([\w]+) +([^ ]+) +([^ ]+) +([^<]+)#) { my $type = $1; my $id = $2; my $date = $3; my $name = $4 || ""; $date =~ tr/-//d; if (grep {$type eq $_} qw/sig rev/ and $renderdate ne "" and $date > $renderdate) { print STDERR "Skipping due to date: $line\n"; } else { print STDERR "Using: $line\n" unless $opt{q}; # strip trailing whitespace more cleanly: $name =~ s/\s+$//g; #Remove re: http://bugs.debian.org/202484 #$name =~ s/[^a-zA-Z \.0-9]/_/g; # handle non-7bit names if ($type eq "pub") { $id = (split('/',$id))[1]; $owner = $id; $idlist{$id} = 1 if (index($name, $revokestr) < 0); } # remove comment field $name{$id} = (split ' \(', $name)[0] if $name; # gpg 1.4 fixup # skip revoked keys if (index($name, $revokestr) >= 0) { $revlist{$id} = 1; next; } if ($type eq "uid") { $name{$owner} = $id; # gpg 1.4 fixup } # unless (@{$sigs{$owner}}) # { # @{$sigs{$owner}} = (); # } if ($type eq "sig" and lc $name ne $not_found) { if ($id ne $owner) { push (@{$sigstmp{$owner}},$id); push (@{$signedbytmp{$id}},$owner); } if ($all or $id ne $owner) { push (@names,$id,$owner); } } if ($type eq "rev" and lc $name ne $not_found) { if ($id ne $owner) { push (@{$revs{$owner}},$id); #push (@{$revokedby{$id}},$owner); } } } } else { print STDERR "Skipping due to regex: $line\n" if $line ne ""; } } my (%sigs, %signedby); for my $id (sort {$sigstmp{$a} <=> $sigstmp{$b}} keys %sigstmp) { next if (defined $revlist{$id}); foreach my $owner (@{$signedbytmp{$id}}) { next if (defined $revlist{$owner}); my $revoke = 0; foreach my $revid (@{$revs{$owner}}) { if ($revid eq $id) { $revoke = 1; } } #$res = $revlist{$id}; if (($revoke == 0)) { push (@{$sigs{$owner}},$id); push (@{$signedby{$id}},$owner); } } } print "digraph \"$title\" {\noverlap=scale\nsplines=true\nsep=.1\n"; my %saw; @saw{@names} = (); @names = keys %saw; undef %saw; my $maxsigcount = 0; my (%sigcount); for my $owner (sort {$sigs{$a} <=> $sigs{$b}} keys %sigs) { undef %saw; @saw{@{$sigs{$owner}}} = (); @{$sigs{$owner}} = keys %saw; undef %saw; undef %saw; $signedby{$owner} ||= []; @saw{@{$signedby{$owner}}} = (); @{$signedby{$owner}} = keys %saw; undef %saw; $sigcount{$owner} = scalar(@{$sigs{$owner}}); if ($sigcount{$owner} > $maxsigcount) { $maxsigcount = $sigcount{$owner}; } } my %signedbycount; my ($maxsignedbycount, $maxratio) = (0, 0); for my $owner (sort {$signedby{$a} <=> $signedby{$b}} keys %signedby) { $signedbycount{$owner} = scalar(@{$signedby{$owner}}); if ($signedbycount{$owner} > $maxsignedbycount) { $maxsignedbycount = $signedbycount{$owner}; } if ($sigcount{$owner} and $sigcount{$owner} > 0) { if ($signedbycount{$owner} / $sigcount{$owner} > $maxratio) { $maxratio = $signedbycount{$owner} / $sigcount{$owner}; } } } print "//$maxratio\n"; if ($stats) { open (STATS,">$stats"); print STATS "\n$title\n"; for my $owner (sort {$sigcount{$b} <=> $sigcount{$a}} keys %sigs) { print STATS "
$name{$owner}$sigcount{$owner}\"".\n"; } print STATS "
\n"; close STATS; } print "node [style=filled]\n"; for my $id (@names) { if ((not exists $sigcount{$id}) and (not exists $signedbycount{$id}) and not $all) { next; } next unless (defined $idlist{$id}); if ($color) { my ($red, $green, $blue) = (0, 1/3, 1/3); if ($sigcount{$id}) { $red = $sigcount{$id} / $maxsigcount; } if ($sigcount{$id} && $maxratio != 0) { $green = ($signedbycount{$id} / $sigcount{$id} / $maxratio * .75) * 2/3 + 1/3; } if ($signedbycount{$id} and $maxsignedbycount != 0) { $blue = ($signedbycount{$id} / $maxsignedbycount) * 2/3 + 1/3; } my ($hue,$saturation,$value) = rgb2hsv($red,$green,$blue); printf "//%d %d $red,$green,$blue\n", $sigcount{$id} || 0, $signedbycount{$id} || 0; print "\"$id\" [fillcolor=\"$hue,$saturation,$value\",label=\"$name{$id}\"]\n"; } else { print "\"$id\" [label=\"$name{$id}\"]\n"; } } #print "node [style=solid]\n"; for my $owner (sort keys %sigs) { print "{ "; for my $id (@{$sigs{$owner}}) { print "\"$id\" "; } print "} -> \"$owner\"\n"; } print "}\n"; # Converts rgb to hsv. All numbers are within range 0 to 1 # from http://twiki.org/cgi-bin/view/Codev/WebMap sub rgb2hsv { my ($r, $g ,$b) = @_; my $max = maxof($r, maxof($g, $b)); my $min = minof($r, minof($g, $b)); my $v = $max; my ($s, $h); if ($max > 0.0) { $s = ($max - $min) / $max; } else { $s = 0; } if ($s > 0.0) { my ($rc, $gc, $bc, $diff); $diff = $max - $min; $rc = ($max - $r) / $diff; $gc = ($max - $g) / $diff; $bc = ($max - $b) / $diff; if ($r == $max) { $h = ($bc - $gc) / 6.0; } elsif ($g == $max) { $h = (2.0 + $rc - $bc) / 6.0; } else { $h = (4.0 + $gc - $rc) / 6.0; } } else { $h = 0.0; } if ($h < 0.0) { $h += 1.0; } return ($h, $s, $v); } sub maxof { my ($a, $b) = @_; return $a>$b?$a:$b; } sub minof { my ($a, $b) = @_; return $a<$b?$a:$b; } # vim:sw=2: signing-party-2.12/sig2dot/sig2dot.1000066400000000000000000000050471500571021200172400ustar00rootroot00000000000000.\" Hey, EMACS: -*- nroff -*- .\" First parameter, NAME, should be all caps .\" Second parameter, SECTION, should be 1-8, maybe w/ subsection .\" other parameters are allowed: see man(7), man(1) .TH SIG2DOT 1 "May 31, 2006" .\" Please adjust this date whenever revising the manpage. .\" .\" Some roff macros, for reference: .\" .nh disable hyphenation .\" .hy enable hyphenation .\" .ad l left justify .\" .ad b justify to both left and right margins .\" .nf disable filling .\" .fi enable filling .\" .br insert line break .\" .sp insert n+1 empty lines .\" for manpage-specific macros, see man(7) .SH NAME sig2dot \- creates a .dot file from a GPG signature listing .SH SYNOPSIS .B sig2dot [OPTIONS] < input-file > output-file .br .SH DESCRIPTION This manual page documents briefly the .B sig2dot command. Sig2dot parses the output of "gpg \-\-list\-sigs" and produces a .dot file that can be used as input to a graphing program. .SH OPTIONS \fIsig2dot\fP accepts the following options: .TP 16 .B \-b Black and white / do not colorize. .TP 16 .B \-d Render graph as it appeared on (ignores more recent signatures). Date must be in the format "YYYY\-MM\-DD". Will also ignore keys that have since been revoked. .TP 16 .B \-s Produces a sory HTML statistics file with the number of signatures per node. .TP 16 .B \-a Graph all keys, even if they do not have a signature .TP 16 .B \-r "" Sets the text for the key-revoked string used by GnuPG. The default is what is used in the English version. This parameter can be set for versions of GnuPG localized to other languages. Default: "[revoked". .TP 16 .TP 16 .B \-t "" Sets the title for the graph generated. Default: "Keyring Statistics". .TP 16 .B \-u "<user-not-found-string>" Sets the text for the user-not-found string used by GnuPG. The default is what is used in the English version. This parameter can be set for versions of GnuPG localized to other languages. Default: "[user id not found]". .TP 16 .B \-q Be quiet. .TP 16 .B \-h Print help and exit. .TP 16 .B \-v Print version and exit. .SH EXAMPLE gpg \-\-list\-sigs | sig2dot > sigs.dot .sp 0 springgraph < sigs.dot > sigs.png .SH SEE ALSO .B springgraph(1) .B gnupg(1) .SH AUTHORS Darxus@ChaosReigns.com with modifications by Kevin Rosenberg. Versions 0.35 and later are by Christoph Berg <cb@df7cb.de>. This manual page was written by Kevin M. Rosenberg <kmr@debian.org>, for the Debian GNU/Linux system (but may be used by others). �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������signing-party-2.12/springgraph/���������������������������������������������������������������������0000775�0000000�0000000�00000000000�15005710212�0016546�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������signing-party-2.12/springgraph/Makefile�������������������������������������������������������������0000664�0000000�0000000�00000000375�15005710212�0020213�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������all: install: install -D springgraph $(DESTDIR)/usr/bin/springgraph install -D -m644 springgraph.1 $(DESTDIR)/usr/share/man/man1/springgraph.1 install -D -m644 README.springgraph \ $(DESTDIR)/usr/share/doc/signing-party/README.springgraph clean: �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������signing-party-2.12/springgraph/README.springgraph���������������������������������������������������0000664�0000000�0000000�00000002146�15005710212�0021754�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������springgraph ----------- Springgraph will read in a .dot file description of a graph, which, for each node, specifies its name and which other nodes it is connected to, and then renders a graph. Each node is drawn as an ellipse, and each connection is drawn as an arrow. The node placement is a result of all of the nodes moving away from each other, while all nodes which are connected move toward each other. This movement is repeated until it stabilizes. Springgraph was written as an alternative to neato, which is part of graphviz. It attempts to read the same .dot files used by graphviz, but currently only supports a limited number of node attributes (label and fillcolor). Definition of the .dot files which springgraph renders can be found in the graphviz man pages. A copy is here: * http://www.graphviz.org/Documentation.php * http://www.graphviz.org/cvs/doc/info/lang.html Springgraph only supports the fillcolor and label node attributes, and can only handle two nodes per edge definition ("node1 -> node2", not "node1 -> node2 -> node3"). -- Christoph Berg <cb@df7cb.de> Sun, 6 Mar 2005 17:44:47 +0100 ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������signing-party-2.12/springgraph/springgraph����������������������������������������������������������0000775�0000000�0000000�00000067243�15005710212�0021034�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/perl -w # springgraph v0.79, (c) 2002 Darxus@ChaosReigns.com, released under the GPL # Download current version from: http://www.chaosreigns.com/code/springgraph/ # # This program attempts to render .dot files in a fashion similar to neato, # which is part of graphviz: http://www.research.att.com/sw/tools/graphviz/. # I have never looked at any of the code in graphviz. # # Example usage: # # cat test.dot | ./springgraph.pl -s 3 > springgraph.png # # The "-s 3" specifies the scale, and is optional. All of the node # locations are multiplied by this. Increase the scale to eliminate # node overlaps. Decrease the scale to make the graph smaller. # # Requirements: GD.pm (http://www.perl.com/CPAN/authors/id/L/LD/LDS/) # # Definition of the .dot files which springgraph renders # can be found in the graphviz man pages. A copy is here: # http://www.unisa.edu.au/eie/csec/graphviz/dot.1.html. Springgraph only # supports the fillcolor and label node attributes, and can only handle # two nodes per edge definition ("node1 -> node2", not "node1 -> node2 # -> node3"). # # Springgraph fully supports the .dot files generated by sig2dot # (http://www.chaosreigns.com/code/sig2dot), which generates .dot files # from GPG/PGP signature relationships. # # Thanks to the following for help with the math for the arrowheads: # Mike Joseph <mj@doze.net> # Walt Mankowski <waltman@pobox.com> # Jeff Weisberg <jaw+plug@tcp4me.com> # # Yes, the placement of the freaking arrowheads was by far the hardest # part of writing this program. # # Thanks to Hartmut Palm for cylinder translation/rotation code in # VRML.pm: http://dc.gfz-potsdam.de/~palm/vrmlperl/ # v0.26 May 06 16:12:30 2002 # v0.27 May 06 18:15:38 2002 cleanup # v0.44 May 06 23:56:45 2002 # v0.56 May 07 05:10:02 2002 # v0.60 May 07 23:27:29 2002 arrow heads !! (not filled in due to segfault) # v0.61 May 07 2002 handle absence of beginning double-quote in fillcolor attribute # v0.62 May 08 19:44:04 2002 use getopts to get scale argument # v0.63 May 08 21:29:48 2002 made fillcolor optional again # v0.64 May 08 22:28:40 2002 render http://www.research.att.com/sw/tools/graphviz/examples/undirected/ER.dot.txt # and http://www.research.att.com/sw/tools/graphviz/examples/undirected/process.dot.txt # (added support for undirected graphs ("--" links) # v0.65 May 08 22:44:00 2002 render http://www.research.att.com/sw/tools/graphviz/examples/directed/fsm.dot.txt # (do not attempt to draw a line from a node to itself and cause a devision by zero) # v0.67 May 09 05:53:16 2002 support multiple nodes on one link line, adjusted detection of completion # render http://www.research.att.com/sw/tools/graphviz/examples/directed/unix.dot.txt # (support node names containing spaces) # v0.68 May 09 17:29:06 2002 cleaned up link line processing a bit (removed extraneous define checks) # v0.69 May 09 18:23:19 2002 render http://www.research.att.com/sw/tools/graphviz/examples/undirected/inet.dot.txt # (support {} lists in link (edge) lines) # v0.70 May 10 00:39:20 2002 Strip double-quotes that were getting missed to support sig2dot v0.27. # v0.71 May 11 20:06:17 2002 don't draw twice, added some 3D math (but not output yet) # v0.72 May 11 21:31:20 2002 3D output !!! (via -p flag) # v0.73 May 11 22:34:23 2002 added labels to 3D output # v0.74 May 12 02:07:29 2002 output 3D output suitable for animation # v0.75 May 13 01:45:41 2002 beginnings of vrml output (-v) - colored spheres # v0.76 May 13 04:30:13 2002 added connections between nodes to vrml # output, thanks cylinder translation/rotation # code from VRML.pm by Hartmut Palm: # http://dc.gfz-potsdam.de/~palm/vrmlperl/ # v0.77 May 13 04:41:53 2002 made colors optional in pov and vrml output # v0.78 May 13 06:31:34 2002 removed extra cylinders from vrml output # v0.79 May 13 07:20:23 2002 made 2d output background transparent # v0.80 Mar 19 2003 optimization patch from Marco Bodrato # v0.81 Aug 20 2003 Caption stderr progress notes use Getopt::Std; use strict; use vars qw( $push $pull %node $im $source $dest $nodenum $blue $black $opt_b $bgcol @bgcolor $dist $iter $maxiter $percent $xdist $ydist $newdist2 $xmove $ymove $movecount $rate $nodes %link $continue $done $line @nodelist %saw $name $label $margin $minx $miny $maxx $maxy $scale $nodesize $powderblue $linecol $h $s $v $r $g $b $color $maxxlength $minxlength $pi $twopi $angle @point $width $height $arrowlength $arrowwidth $num $opt_s $edge @parts $part @sources @dests $sourcesstring $destsstring $pov $opt_p $zdist $zmove $pov_or_vrml $opt_v $vrml $opt_t $trans $opt_f $font $fontsize $opt_h $opt_l @linecolor ); $push = 2000; $pull = .1; $maxiter = 400; $rate = 2; $nodes = 5; #$done = 0.1; $done = 0.3; #$done = 3; $margin = 20; #$nodesize = 80; $nodesize = 40; $arrowlength = 10; # pixels $arrowwidth = 10; srand 1; #comment out this line to generate graphs differently every time $pi = 3.141592653589793238462643383279502884197169399375105; # from memory $twopi = $pi * 2; getopts('s:pvhtb:l:f:'); # -h: Show some help if ($opt_h) { usage(); exit 1; } use GD; # -s: set scale if ($opt_s) { $scale = $opt_s; } else { $scale = 1; } # -p: Output as Pov-Ray if ($opt_p) { $pov = 1; } else { $pov = 0; } # -v: Output as VRML if ($opt_v) { $vrml = 1; } else { $vrml = 0; } # -t: Make background transparent if ($opt_t) { $trans = 1; } else { $trans = 0; } # -b: Set background color if ($opt_b) { $trans = 0; $opt_b =~ m/^(..)(..)(..)$/ or die "Invalid color: $opt_b"; @bgcolor = (hex($1), hex($2), hex($3)); } else { @bgcolor = (255, 255, 255); } # -l: Set line color if ($opt_l) { $trans = 0; $opt_l =~ m/^(..)(..)(..)$/ or die "Invalid color: $opt_l"; @linecolor = (hex($1), hex($2), hex($3)); } else { @linecolor = (169, 169, 169); } # -f: Set TrueType font, style and size if ($opt_f) { $opt_f =~ m/^([^:]+)((:[^:]*)(:\d*)?)?$/ or die "Invalid font: $opt_f"; $font = $1; $fontsize = 13; if ($2) { $2 =~ m/^(:[^:]*)(:\d*)?$/; $font .= $1 if defined $1 and $1 ne ':'; $fontsize = $2 if defined $2 and $2 ne ':'; $fontsize =~ s/^://; } } $done = $done / $scale; while ($line = <STDIN>) { undef $name; next if ($line =~ m#^//#); chomp $line; # 2 = arro1, 1 = no arrow if ($line =~ m#^(.*-[>-][^\[]*)#) { $edge = $1; @parts = split(/(-[->])/,$edge); for $part (0 .. $#parts) { if (defined $parts[$part+2] and $parts[$part] ne '->' and $parts[$part] ne '--') { #print ":$parts[$part]:".$parts[$part+1].":".$parts[$part+2].":\n"; undef @sources; undef @dests; $parts[$part] =~ s/^\s*"?//; $parts[$part] =~ s/"?\s*$//; $parts[$part+2] =~ s/^\s*"?//; $parts[$part+2] =~ s/"?\s*;?\s*$//; if ($parts[$part] =~ m#^{(.*)}$#) { $sourcesstring = $1; #print STDERR "sourcesstring:$sourcesstring:\n"; @sources = split(/[\s*;?\s*]/,$sourcesstring); } else { $sources[0] = $parts[$part]; } if ($parts[$part+2] =~ m#^{(.*)}$#) { $destsstring = $1; #print STDERR "destsstring:$destsstring:\n"; @dests = split(/[\s*;?\s*]/,$destsstring); } else { $dests[0] = $parts[$part+2]; } for $source (@sources) { next if ($source eq ""); for $dest (@dests) { next if ($dest eq ""); $source =~ s/^\s*"?//; $source =~ s/"?\s*$//; $dest =~ s/^\s*"?//; $dest =~ s/"?\s*;?\s*$//; $link{$source}{$dest} = 2 if ($parts[$part+1] eq '->'); $link{$source}{$dest} = 1 if ($parts[$part+1] eq '--'); push (@nodelist,$source,$dest); #print STDERR "$source ".$parts[$part+1]." $dest\n"; } } } } # $source = $1; # $dest = $2; # $source =~ s/^\W*//; # $source =~ s/\W*$//; # $dest =~ s/^\W*//; # $dest =~ s/\W*$//; # $link{$source}{$dest} = 2; # push (@nodelist,$source,$dest); # print STDERR "source:$source:dest:$dest:\n"; } else { # if ($line =~ m#^edge# or $line =~ m#^node#) # { # print STDERR "Skipping: $line\n"; # next; # } if ($line =~ m#^(\S+).*\[.*\]#) { $name = $1; $name =~ tr/"//d; if ($name eq 'node' or $name eq 'edge') { next; } #print STDERR "name:$name:\n"; } if ($line =~ m#\[.*label=([^,\]]*).*\]#) { $label = $1; $label =~ tr/"//d; $node{$name}{'label'} = $label; #print STDERR "label:$label:\n"; } if ($line =~ m#\[.*fillcolor="?([\d\.]+),([\d\.]+),([\d\.]+).*\]#) { $h = $1; $s = $2; $v = $3; #print STDERR "hsv:$h:$s:$v:\n"; $h = $h * 360; ($r,$g,$b) = &hsv2rgb($h,$s,$v); $node{$name}{r} = $r; $node{$name}{g} = $g; $node{$name}{b} = $b; #print STDERR "rgb:$r:$g:$b:\n"; } } } undef %saw; @saw{@nodelist} = (); @nodelist = sort keys %saw; # remove sort if undesired undef %saw; if ($pov or $vrml) { $pov_or_vrml = 1; } else { $pov_or_vrml = 0; } for $nodenum (@nodelist) { $node{$nodenum}{x}=rand;# $maxx; $node{$nodenum}{y}=rand;# $maxy; $node{$nodenum}{z}=rand if $pov_or_vrml; unless(defined $node{$nodenum}{'label'}) { $node{$nodenum}{'label'} = $nodenum; } } print STDERR "springgraph iterating until reaches $done\n\n"; #&draw; $continue = 1; $iter = 0; while($continue > $done) { $continue = $done; $iter++; for $nodenum (@nodelist) { $node{$nodenum}{oldx} = $node{$nodenum}{x}; $node{$nodenum}{oldy} = $node{$nodenum}{y}; $node{$nodenum}{oldz} = $node{$nodenum}{z} if $pov_or_vrml; $xmove = 0; $ymove = 0; $zmove = 0 if $pov_or_vrml; } for $source (@nodelist) { $movecount = 0; for $dest (@nodelist) { next if $source eq $dest; # loops are not supported $xdist = $node{$source}{oldx} - $node{$dest}{oldx}; $ydist = $node{$source}{oldy} - $node{$dest}{oldy}; $dist = $xdist**2 + $ydist**2; if ($pov_or_vrml) { $zdist = $node{$source}{oldz} - $node{$dest}{oldz}; $dist += $zdist**2; } # $distance = sqrt($dist); $percent = $push / $dist; if ($link{$source}{$dest}) { $percent -= $pull; } if ($link{$dest}{$source}) { $percent -= $pull; } $percent *= $rate; $xmove -= $xdist * $percent; $ymove -= $ydist * $percent; $zmove -= $zdist * $percent if $pov_or_vrml; $movecount++; # $pullmove = $pull * $dist; # $pushmove = $push / $dist; # print STDERR "dist: $dist, pull: $pullmove, push: $pushmove\n"; # print STDERR "$source to ${dest}, Dist: $dist Want: $wantdist (${percent}x)\n"; # print STDERR "is: $node[$source]{oldx} $node[$source]{oldy} $xdist $ydist, want: $wantxdist $wantydist ($newdist2)\n"; } if ($movecount) { # renormalize if there are multiple nodes $xmove /= $movecount; $ymove /= $movecount; $zmove /= $movecount if $pov_or_vrml; } $node{$source}{x} -= $xmove; $node{$source}{y} -= $ymove; $node{$source}{z} -= $zmove if $pov_or_vrml; if ($xmove > $continue) { $continue = $xmove; } if ($ymove > $continue) { $continue = $ymove; } if (($pov_or_vrml) and $zmove > $continue) { $continue = $zmove; } } #print STDERR "$iter\n"; if (0) { &draw; open XV,'|-', qw/xv -wait 1 -/; #open XV,'|-', qw/xloadimage -delay 1 stdin/; binmode XV; print XV $im->png; close XV; } if ($iter % 20 == 0) { print STDERR "$continue\n"; } } print STDERR "Iterations: $iter\n"; for $source (@nodelist) { for $color ('r', 'g', 'b') { $node{$source}{$color} = 255 unless (defined $node{$source}{$color}); } } if ($pov) { &drawpov; } elsif ($vrml) { &drawvrml; } else { &draw; } undef $maxx; undef $maxy; sub draw { for $nodenum (@nodelist) { if (!(defined $maxx) or (($node{$nodenum}{x} + (length($node{$nodenum}{'label'}) * 8 + 16)/2) > $maxx + (length($node{$nodenum}{'label'}) * 8 + 16)/2)) { $maxx = $node{$nodenum}{x};# + (length($node{$nodenum}{'label'}) * 8 + 16)/2/2 $maxxlength = (length($node{$nodenum}{'label'}) * 8 + 16)/2; } if (!(defined $minx) or (($node{$nodenum}{x} - (length($node{$nodenum}{'label'}) * 8 + 16)/2) < $minx - (length($node{$nodenum}{'label'}) * 8 + 16)/2)) { $minx = $node{$nodenum}{x};# - (length($node{$nodenum}{'label'}) * 8 + 16)/2/2 $minxlength = (length($node{$nodenum}{'label'}) * 8 + 16)/2; } $maxy = $node{$nodenum}{y} if (!(defined $maxy) or $node{$nodenum}{y} > $maxy); $miny = $node{$nodenum}{y} if (!(defined $miny) or $node{$nodenum}{y} < $miny); } for $nodenum (@nodelist) { #$node{$nodenum}{x} = ($node{$nodenum}{x} - $minx) * $scale + $margin; $node{$nodenum}{x} = ($node{$nodenum}{x} - $minx) * $scale + $minxlength -1 ;# + $margin; $node{$nodenum}{y} = ($node{$nodenum}{y} - $miny) * $scale + $nodesize/2 - 1; } $maxx = ($maxx - $minx) * $scale + $minxlength + $maxxlength;# + $margin*2; $maxy = ($maxy - $miny) * $scale + $nodesize/2*2; $im = new GD::Image($maxx,$maxy); $bgcol = $im->colorAllocate(@bgcolor); $im->transparent($bgcol) if $trans; # make transparent $blue = $im->colorAllocate(0,0,255); $powderblue = $im->colorAllocate(176,224,230); $black = $im->colorAllocate(0,0,0); $linecol = $im->colorAllocate(@linecolor); $im->useFontConfig(1) if $opt_f; for $source (@nodelist) { #print STDERR "node: $source $node[$source]{x},$node[$source]{y}\n"; for $dest (@nodelist) { if (defined $link{$source}{$dest} and $link{$source}{$dest} == 2 and $source ne $dest) { $dist = sqrt( abs($node{$source}{x}-$node{$dest}{x})**2 + abs($node{$source}{y}-$node{$dest}{y})**2 ); $xdist = $node{$source}{x} - $node{$dest}{x}; $ydist = $node{$source}{y} - $node{$dest}{y}; $angle = &acos($xdist/$dist); #$angle = atan2($ydist,$xdist); #$angle += $pi if $ydist < 0; #$dist = abs(cos($angle))*(length($node{$dest}{'label'}) * 8 + 16)/2 + abs(sin($angle))*$nodesize/2; $width = (length($node{$dest}{'label'}) * 8 + 16)/2; $height = $nodesize/2; $dist = sqrt( ($height**2 * $width**2) / ( ($height**2 * (cos($angle)**2) ) + ($width**2 * (sin($angle)**2) ) )); #$dist = $dist*40; $xmove = cos($angle)*$dist; $ymove = sin($angle)*$dist; #$ymove = -$ymove if $ydist < 0; # the part mj omitted $point[0]{x} = $xmove; $point[0]{y} = $ymove; $xmove = cos($angle)*($dist+$arrowlength-3); $ymove = sin($angle)*($dist+$arrowlength-3); #$ymove = -$ymove if $ydist < 0; # the part mj omitted $point[3]{x} = $xmove; $point[3]{y} = $ymove; #$angle = $angle + $arrowwidth/2; $dist = 4; $xmove = $xmove + cos($angle)*$dist; $ymove = $ymove + sin($angle)*$dist; #$ymove = -$ymove if $ydist < 0; # the part mj omitted $angle = $angle + $twopi/4; $dist = $arrowwidth/2; $xmove = $xmove + cos($angle)*$dist; $ymove = $ymove + sin($angle)*$dist; #$ymove = -$ymove if $ydist < 0; # the part mj omitted $point[1]{x} = $xmove; $point[1]{y} = $ymove; $angle = $angle + $twopi/2; $dist = $arrowwidth; $xmove = $xmove + cos($angle)*$dist; $ymove = $ymove + sin($angle)*$dist; #$ymove = -$ymove if $ydist < 0; # the part mj omitted $point[2]{x} = $xmove; $point[2]{y} = $ymove; for $num (0 .. 3) { $point[$num]{y} = - $point[$num]{y} if $ydist < 0; } $im->line($node{$dest}{x}+$point[0]{x},$node{$dest}{y}+$point[0]{y},$node{$dest}{x}+$point[1]{x},$node{$dest}{y}+$point[1]{y},$linecol); $im->line($node{$dest}{x}+$point[1]{x},$node{$dest}{y}+$point[1]{y},$node{$dest}{x}+$point[2]{x},$node{$dest}{y}+$point[2]{y},$linecol); $im->line($node{$dest}{x}+$point[2]{x},$node{$dest}{y}+$point[2]{y},$node{$dest}{x}+$point[0]{x},$node{$dest}{y}+$point[0]{y},$linecol); # $xmove = int($node{$dest}{x}+$point[3]{x}); # $ymove = int($node{$dest}{y}+$point[3]{y}); # $im->fillToBorder($xmove,$ymove,$linecol,$powderblue); #$im->fillToBorder($node{$dest}{x}+$point[3]{x},$node{$dest}{y}+$point[3]{y},$linecol,$linecol); #$im->line($point[1]{x},$point[1]{y},$point[2]{x},$point[2]{y},$linecol); #$im->line($point[2]{x},$point[2]{y},$point[0]{x},$point[0]{y},$linecol); #$im->fillToBorder($point[3]{x},$point[3]{y},$linecol,$linecol); #$im->arc($point[3]{x},$point[3]{y},10,10,0,360,$black); # $im->arc($point[0]{x},$point[0]{y},20,20,0,360,$black); # $im->arc($point[1]{x},$point[1]{y},20,20,0,360,$black); # $im->arc($point[2]{x},$point[2]{y},20,20,0,360,$black); #$im->arc($node{$dest}{x}+$xmove,$node{$dest}{y}+$ymove,20,20,0,360,$black); } } } for $source (@nodelist) { for $dest (@nodelist) { if ($link{$source}{$dest}) { $im->line($node{$source}{x},$node{$source}{y},$node{$dest}{x},$node{$dest}{y},$linecol); } } } for $source (@nodelist) { if ($opt_f) { my @bounds = GD::Image::->stringFT($black,$font,$fontsize,0,0,0,$node{$source}{'label'}); $im->arc($node{$source}{x},$node{$source}{y},$bounds[2]-$bounds[0]+1.5*$fontsize,$nodesize,0,360,$black); } else { $im->arc($node{$source}{x},$node{$source}{y},(length($node{$source}{'label'}) * 8 + 16),$nodesize,0,360,$black); } if (defined $node{$source}{r} and defined $node{$source}{g} and defined $node{$source}{b}) { $color = $im->colorResolve($node{$source}{r},$node{$source}{g},$node{$source}{b}); } else { $color = $bgcol; } $im->fillToBorder($node{$source}{x},$node{$source}{y},$black,$color); } for $source (@nodelist) { if ($opt_f) { my @bounds = GD::Image::->stringFT($black,$font,$fontsize,0,0,0,$node{$source}{'label'}); $im->stringFT($black,$font,$fontsize,0,$node{$source}{x} - ($bounds[0]+$bounds[2])/2.0,$node{$source}{y}+$fontsize/2.0,$node{$source}{'label'}); } else { $im->string(gdLargeFont,$node{$source}{x} - (length($node{$source}{'label'}) * 8 / 2),$node{$source}{y}-8,$node{$source}{'label'},$black); } } binmode STDOUT; print $im->png; } sub drawpov { print'// Generated by springgraph, by Darxus@ChaosReigns.com: // http://www.ChaosReigns.com/code/springgraph/ #include "colors.inc" #include "shapes.inc" #include "textures.inc" #include "glass.inc" #include "stones.inc" light_source {<0, 400, -500> color White rotate <0, 360*clock, 0>} light_source {<400, 0, -500> color White rotate <0, 360*clock, 0>} '; for $source (@nodelist) { $node{$source}{x} = $node{$source}{x} * $scale; $node{$source}{y} = $node{$source}{y} * $scale; $node{$source}{z} = $node{$source}{z} * $scale; $node{$source}{r} = $node{$source}{r} / 256; $node{$source}{g} = $node{$source}{g} / 256; $node{$source}{b} = $node{$source}{b} / 256; } for $source (@nodelist) { print "sphere { <$node{$source}{x},$node{$source}{y},$node{$source}{z}>, 15 pigment {color rgb<$node{$source}{r},$node{$source}{g},$node{$source}{b}>}}\n"; print "text { ttf \"crystal.ttf\", \"$node{$source}{'label'}\", 0.5, 0 translate 2*x rotate <0, 360*clock, 0> translate -0.375*y scale 10 translate <$node{$source}{x},$node{$source}{y},$node{$source}{z}> pigment {color rgb<$node{$source}{r},$node{$source}{g},$node{$source}{b}>}}\n"; #print "text { ttf \"crystal.ttf\", \"$node{$source}{'label'}\", 0.5, 0 translate -".scalar(length($node{$source}{'label'})*0.25)."*x scale 10 translate <$node{$source}{x},$node{$source}{y},$node{$source}{z}> pigment {color rgb<$node{$source}{r},$node{$source}{g},$node{$source}{b}>}}\n"; for $dest (@nodelist) { if ($link{$source}{$dest}) { print "cylinder {<$node{$source}{x},$node{$source}{y},$node{$source}{z}>,<$node{$dest}{x},$node{$dest}{y},$node{$dest}{z}> 0.5 pigment {color rgb<0.5,0.5,0.5>}}\n"; } } } print 'camera { location <0, 0, -500> up <0.0, 1.0, 0> right <4/3, 0.0, 0> look_at <0, 0, -1> rotate <0, 360*clock, 0> } '; } sub drawvrml { my ($t,$r,$length,$color); print'#VRML V2.0 utf8 WorldInfo { info ["Generated by springgraph, by Darxus@ChaosReigns.com: http://www.ChaosReigns.com/code/springgraph/"] } '; for $source (@nodelist) { $node{$source}{x} = $node{$source}{x} * $scale; $node{$source}{y} = $node{$source}{y} * $scale; $node{$source}{z} = $node{$source}{z} * $scale; for $color ('r', 'g', 'b') { if (defined $node{$source}{$color}) { $node{$source}{$color} = $node{$source}{$color} / 256; } } } for $source (@nodelist) { print " Transform { translation $node{$source}{x} $node{$source}{y} $node{$source}{z} children [ Shape{ appearance Appearance { material Material { diffuseColor $node{$source}{r} $node{$source}{g} $node{$source}{b} } } geometry Sphere{radius 15} } ] } "; #print "sphere { <$node{$source}{x},$node{$source}{y},$node{$source}{z}>, 15 pigment {color rgb<$node{$source}{r},$node{$source}{g},$node{$source}{b}>}}\n"; #print "text { ttf \"crystal.ttf\", \"$node{$source}{'label'}\", 0.5, 0 translate 2*x rotate <0, 360*clock, 0> translate -0.375*y scale 10 translate <$node{$source}{x},$node{$source}{y},$node{$source}{z}> pigment {color rgb<$node{$source}{r},$node{$source}{g},$node{$source}{b}>}}\n"; #print "text { ttf \"crystal.ttf\", \"$node{$source}{'label'}\", 0.5, 0 translate -".scalar(length($node{$source}{'label'})*0.25)."*x scale 10 translate <$node{$source}{x},$node{$source}{y},$node{$source}{z}> pigment {color rgb<$node{$source}{r},$node{$source}{g},$node{$source}{b}>}}\n"; for $dest (@nodelist) { if ($link{$source}{$dest}) { ($t,$r,$length) = &cylinder($node{$source}{x},$node{$source}{y},$node{$source}{z},$node{$dest}{x},$node{$dest}{y},$node{$dest}{z}); print " Transform { translation $t rotation $r children [ Shape{ appearance Appearance { material Material { diffuseColor 0.5 0.5 0.5 } } geometry Cylinder { radius 0.5 height $length top FALSE bottom FALSE } } ] } "; } } } # print 'camera { # location <0, 0, -500> # up <0.0, 1.0, 0> # right <4/3, 0.0, 0> # look_at <0, 0, -1> # rotate <0, 360*clock, 0> #} #'; } sub hsv2rgb { #from http://faqchest.dynhost.com/prgm/perlu-l/perl-01/perl-0101/perl-010100/perl01010410_17820.html # Given an h/s/v array, return an r/g/b array. # The r/g/b values will each be between 0 and 255. # The h value will be between 0 and 360, and # the s and v values will be between 0 and 1. # my $h = shift; my $s = shift; my $v = shift; # limit this to h values between 0 and 360 and s/v values # between 0 and 1 unless (defined($h) && defined($s) && defined($v) && $h >= 0 && $s >= 0 && $v >= 0 && $h <= 360 && $s <= 1 && $v <= 1) { return (undef, undef, undef); } my $r; my $g; my $b; # 0.003 is less than 1/255; use this to make the floating point # approximation of zero, since the resulting rgb values will # normally be used as integers between 0 and 255. Feel free to # change this approximation of zero to something else, if this # suits you. if ($s < 0.003) { $r = $g = $b = $v; } else { $h /= 60; my $sector = int($h); my $fraction = $h - $sector; my $p = $v * (1 - $s); my $q = $v * (1 - ($s * $fraction)); my $t = $v * (1 - ($s * (1 - $fraction))); if ($sector == 0) { $r = $v; $g = $t; $b = $p; } elsif ($sector == 1) { $r = $q; $g = $v; $b = $p; } elsif ($sector == 2) { $r = $p; $g = $v; $b = $t; } elsif ($sector == 3) { $r = $p; $g = $q; $b = $v; } elsif ($sector == 4) { $r = $t; $g = $p; $b = $v; } else { $r = $v; $g = $p; $b = $q; } } # Convert the r/g/b values to all be between 0 and 255; use the # ol' 0.003 approximation again, with the same comment as above. $r = ($r < 0.003 ? 0.0 : $r * 255); $g = ($g < 0.003 ? 0.0 : $g * 255); $b = ($b < 0.003 ? 0.0 : $b * 255); return ($r, $g, $b); } # from perlfunc(1) sub acos { atan2( sqrt(1 - $_[0] * $_[0]), $_[0] ) } sub cylinder { my ($x1,$y1,$z1,$x2,$y2,$z2) = @_; my ($t, $r, $length, $rx, $ry, $rz, $dist); $x1 = 0 unless $x1; $x2 = 0 unless $x2; $y1 = 0 unless $y1; $y2 = 0 unless $y2; $z1 = 0 unless $z1; $z2 = 0 unless $z2; my $dx=$x1-$x2; my $dy=$y1-$y2; my $dz=$z1-$z2; $length = sqrt($dx**2 + $dy**2 + $dz**2); $rx = $dx; $ry = $dy+$length; $rz = $dz; $dist = sqrt($rx**2 + $ry**2 + $rz**2); if ($dist) { # renormalize if the cylinder is not degenerated $rx /= $dist; $ry /= $dist; $rz /= $dist; } $t = ($x1-($dx/2))." ".($y1-($dy/2))." ".($z1-($dz/2)); $r = "$rx $ry $rz $pi"; return ($t,$r,$length); } sub usage { print <<END springgraph - Render a .dot file into a graphic, taking that dot file on standard in and delivering a PNG on standard out. Usage: springgraph [-p] [-v] [-s scale] [-t] [-b color] [-l color] \\ [-f font[:[style]:size]] [-h] < example.dot > example.png -p Create a file that can be rendered with POV-Ray -v Create a VRML file -s This option specifies the scale. All of the node locations are multiplied by this. Increase the scale to eliminate node overlaps. Decrease the scale to make the graph smaller. -t Make the background of the resulting image transparent. -b Set background color of image, specify it in the form RRGGBB, in hex digits, e.g. FFFFFF is white, 000000 is black, FF0000 is red, ... -l Set the line color, same format as the background color -f Set the (TrueType) font, and optionally the style and size, to use for labels. Example: "DejaVu Serif:Italic:12". -h Show this help END } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������signing-party-2.12/springgraph/springgraph.1��������������������������������������������������������0000664�0000000�0000000�00000005110�15005710212�0021151�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������.\" Hey, EMACS: -*- nroff -*- .\" First parameter, NAME, should be all caps .\" Second parameter, SECTION, should be 1-8, maybe w/ subsection .\" other parameters are allowed: see man(7), man(1) .TH SPRINGGRAPH 1 "September 5, 2005" .\" Please adjust this date whenever revising the manpage. .\" .\" Some roff macros, for reference: .\" .nh disable hyphenation .\" .hy enable hyphenation .\" .ad l left justify .\" .ad b justify to both left and right margins .\" .nf disable filling .\" .fi enable filling .\" .br insert line break .\" .sp <n> insert n+1 empty lines .\" for manpage-specific macros, see man(7) .SH NAME springgraph \- renders a graph from a .dot file .SH SYNOPSIS .B springgraph [OPTIONS] < input-file.dot > output-file.png .br .SH DESCRIPTION Springgraph will read in a .dot file description of a graph, which, for each node, specifies its name and which other nodes it is connected to, and then renders a graph. The output is a PNG file. Each node is drawn as an ellipse, and each connection is drawn as an arrow. The node placement is a result of all of the nodes moving away from each other, while all nodes which are connected move toward each other. This movement is repeated until it stabilizes. Springgraph was written as an alternative to neato, which is part of graphviz. It attempts to read the same .dot files used by graphviz, but currently only supports a limited number of node attributes (label and fillcolor) and can only handle two nodes per edge definition ("node1 -> node2", not "node1 -> node2 -> node3"). .SH OPTIONS \fIspringgraph\fP accepts the following options: .TP 16 .B \-p Create a file that can be rendered with POV-Ray .TP 16 .B \-v create a VRML file .TP 16 .B \-s this option specifies the scale. All of the node locations are multiplied by this. Increase the scale to eliminate node overlaps. Decrease the scale to make the graph smaller. .TP 16 .B \-t make the background of the resulting image transparent. .TP 16 .B \-b set background color of image, specify it in the form RRGGBB, in hex digits, e.g. FFFFFF is white, 000000 is black, FF0000 is red, ... .TP 16 .B \-l set the line color, same format as the background color .TP 16 .B \-h display usage synopsis .SH EXAMPLE digraph { "rene" -> "myon"; "mvo" -> "rene"; } .SH SEE ALSO neato(1) sig2dot(1) http://www.graphviz.org/Documentation.php http://www.graphviz.org/cvs/doc/info/lang.html .SH AUTHOR This manual page was written by Kevin M. Rosenberg <kmr@debian.org>, for the Debian GNU/Linux system (but may be used by others). ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������