#!/usr/local/bin/perl -s
$dmy=<<"-----BEGIN PGP SIGNED MESSAGE-----"
-----BEGIN PGP SIGNED MESSAGE-----

#!/usr/local/bin/perl -s
   ;                           # Deep in the heart of PGP, there is a reason
                               # for this semicolon.
# Make sure you use perl 5.000 or higher!  This program won't even
# compile with a lower one.

# PGPACKET
# PGP Packet analyzer, by Mark E. Shoulson ([email protected])

# (c) Mark E. Shoulson 1995, 1996
# Version 2.15, January 1996

# Permission is granted to distribute this program freely and make
# modifications or improvements.  Please keep the name of the author on
# modified versions, AND note modifications as the products of those
# who made them.  This program is signed with pgp key 0x429C07E1 and should
# run both before and after being checked with pgp.

# Please let me know if you like this program.

# This program takes any PGP output and prints out a report of each packet
# it comes across (what type, what's in it, etc).  It's not going to crack
# anything or do any real work (like uncompressing or decrypting); it just
# analyzes the structure of the file.

# Version 2.0 (and above) can handle ascii-armored files as well.  It does
# its level best to detect when you have an ascii-armored file (it assumes
# that if the file "looks" like text (perl's -T test), it's ascii-armored),
# and will try to skip past any junk at the beginning of the file to get to
# the real stuff.  I'm not 100% sure of its ability to find the end of the
# pgp stuff, though; I'm pretty sure there's a possibility of failure.
# Just be prepared, until I fix that.  For signed cleartext, it will skip
# the cleartext and analyze only the signature.  Currently it will only
# handle the first chunk of PGP ascii-armor in a file.  You can also force
# ascii-armor mode with the -a option.

# Actual data is generally not printed, except for N and E of public keys
# and user-ID information, unless the -v (verbose) option is given.  Other
# information may be parsed out and displayed, but I don't see much reason
# for printing out the encrypted D or encrypted data.  I read all the stuff
# in anyway, often wasting variables and space on it, in case you actually
# want to do anything with it.

# Use the -u option to enable looking up keyIDs in your pubring file (use
# -u=keyfile to specify an alternate file).  Makes it easy to find out who
# else someone has included on the readers-list...

# -h gives a usage note.

# Some symbolic names for CTB's...
# Manifest constants would be better, but who cares?
# CTB types:

$CTB_PKE=01;                    # Public-Key Encrypted
$CTB_SKE=02;                    # Secret-Key Encypted (signature)
$CTB_SEC=05;                    # Secret Key Certificate
$CTB_PUB=06;                    # Public Key Certificate
$CTB_COMP=010;                  # Compressed Data
$CTB_CKE=011;                   # Conventional-Key Encrypted
$CTB_RAW=013;                   # Raw literal data, filename and mode
$CTB_TRUST=014;                 # Keyring Trust Packet
$CTB_UID=015;                   # User ID Packet
$CTB_CMT=016;                   # Comment Packet

sub ctb {
   # Stolen from Adam Back's extract-pub, slightly modified for my
   # own stylistic preferences.

   my ($c,$d,$ct,$lf,$n,$len,$fh);
   # This is the only EOF check in the program, aside from packets
   # which read the rest of the file.  So it won't be terribly
   # clean with files that end in the wrong places.  Cope.
   if (@_) {
       ($fh)=@_;
   }
   else {
       $fh="PGP";
   }
   return () if eof($fh);
   # Get the next character, which is the CTB.
   $d = getc($fh);
   $c=unpack('C',$d);
   # bit 7 must be set, or this is no CTB.
   return (-1,$c) unless $c&0200;
   $ct=$c&074;
   $ct>>=2;
   $lf=$c&03;
   if ($lf==0){
       $lf=1;
   } elsif ($lf==1){
       $lf=2;
   } elsif ($lf==2){
       $lf=4;
   } elsif ($lf==3){
       $lf=0;
   }
   if ($lf){
       read($fh,$n,$lf);
       $n="\0"x(4-$lf).$n;
       $len=unpack('N',$n);
   } else {
       # This isn't really checked for in most of the handlers.  Perhaps
       # more of them should?
       $len=0;
   }
   return ($ct,$len,$lf);
}

@months=("Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep",
        "Oct","Nov","Dec");
sub date_fmt {
   my($time);
   my($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst);
   # If I'm going to make this available, I might as well avoid
   # the month-first/day-first problem.  'Course, by rights I should
   # do something about the English month-names.  Maybe DD MM YYYY with
   # month in roman numerals (e.g. 15 VII 1993)?  If you feel like it.

   ($time)=@_;
   ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)=gmtime($time);
   return(sprintf("%d %s %d  %02d:%02d:%02d",
                  $mday,$months[$mon],$year+1900,$hour,$min,$sec));
}

sub data_print {
   my($data,$pack);

   ($data,$pack)=@_;
   $data=unpack('H*',$data) unless $pack;
   print "Data:\t0x\U$data\E\n";
}

sub length_warn {
   my($len,$str);

   ($len)=@_;
   # Warn if length is non-zero.
   return unless $len;         # Check this first, since it's the usual case.
   if ($len>0) {
       $str="long";
   }
   else {
       $str="short";
       $len= -$len;
   }
   print "Warning! Length field is $len bytes too $str!\n" unless $t;
}

sub rmpi {
   # read a PGP format Multi-Precision Integer
   my($len,$nlen,$n,$fh);

   if (@_) {
       ($fh)=@_;
   }
   else {
       $fh="PGP";
   }
   read($fh,$len,2);$len=unpack('n',$len);
   $nlen=int($len/8);
   $nlen++ if $len%8;
   read($fh,$n,$nlen);
   return unpack('H*',$n);
}

sub findkey {
   # Find a keyID in the pubring file and print out its UserID.
   # For use with the -u function
   my($targ,$ct,$len,$lf,$dmy,$nn,$uid);

   unless (stat(PUBRING)) {
       # Silently return if the file handle isn't open for
       # some reason.
       return "";
   }
   ($targ)=@_;
   seek PUBRING,0,0;           # rewind the filehandle
   while (($ct,$len,$lf)=&ctb(PUBRING)) {
       unless ($ct==$CTB_PUB) {
           # Eat the packet.
           read(PUBRING,$dmy,$len);
           next;
       }
     outer:
       # OK, it's a public key packet.  Is it the right one?
       read(PUBRING,$dmy,1+4+2+1); # Skip version, timestamp, validity, alg.
       $nn=&rmpi(PUBRING);     # Get the N.
       # It's the one we're looking for if the last 16 chars match.
       # Eat the rest of the packet.
       read(PUBRING,$dmy,$len-length($nn)/2-2-1-4-2-1);
       next unless $nn=~/$targ$/i; # Carry on if no match.
       # It's the right one.  Look for a UID packet.
     inner:
       while (($ct,$len,$lf)=&ctb(PUBRING)) {
           if ($ct==$CTB_PUB) {
               # New public key.  No userID, I guess.
               # Start again.  Have to use a goto.
               goto outer;
           }
           unless ($ct==$CTB_UID) {
               # Skip non-uid packets.
               read(PUBRING,$dmy,$len);
               next;
           }
           # At long last, found the uid!
           read(PUBRING,$uid,$len);
           print "    User ID for above key: \"$uid\"\n";
           next;
       }
   }
}

sub null_handler {
   # Dummy handler; just skip $len bytes.
   my($len,$dmy);

   ($len)=@_;
   print "(No handler known.  Skipping $len bytes)\n" unless $t;
   read(PGP,$dmy,$len);
   &data_print($dmy) if $v&&!$t;
}

sub comment_handler {
   # Just skip $len bytes.
   # Same as above, minus the message.  Both are included for modularity.
   my($len,$dmy);

   ($len)=@_;
   print "$len bytes of comment.\n";
   read(PGP,$dmy,$len);
   print "Comment:\t$dmy\n" if $v&&!$t;
}

sub uid_handler {
   my($len,$uid);

   ($len)=@_;
   read (PGP,$uid,$len);
   printf "User ID:%s\"$uid\"\n",($t?" ":"\t");
}

sub pub_handler {
   my($len,$vb,$ts,$valid,$alg,$ee,$nn);

   ($len)=@_;
   read(PGP,$vb,1); $len--; $vb=unpack('C',$vb);
   read(PGP,$ts,4); $len-=4; $ts=unpack('N',$ts);
   read(PGP,$valid,2); $len-=2; $valid=unpack('n',$valid);
   read(PGP,$alg,1); $len--; $alg=unpack('c',$alg);
   $nn=&rmpi(); $len-=length($nn)/2+2;
   $ee=&rmpi(); $len-=length($ee)/2+2;
   unless ($t) {
       printf ("Version Byte:\t%d\n",$vb);
       print "Key Created:\t".&date_fmt($ts)."\n";
       print "Valid for",($valid > 0? ":\t $valid days" : "ever"),"\n";
       printf("Algorithm:\t%d (%s)\n",$alg,($alg==1?"RSA":"??"));
       print "N:\t0x\U$nn\E\n";
       print "E:\t0x\U$ee\E\n";
       &length_warn($len) if $len;
   }
   else {
       printf "Key ID: 0x%s\n",substr("\U$nn",-16);
   }
}

sub conv_handler {
   my ($dmy,$len);

   # Yet another "just suck 'em in" handler.
   ($len)=@_;
   print "$len bytes of data...\n";
   read(PGP,$dmy,$len);
   &data_print($dmy) if $v&&!$t;
}

sub comp_handler {
   my ($dmy,$len,$c);

   ($len)=@_;
   read(PGP,$dmy,1); $len--; $dmy=unpack('C',$dmy);
   printf ("Algorithm:\t%d (%s)\n",$dmy,($dmy==1?"ZIP":"??")) unless $t;
   if ($len>0) {
       print "$len bytes of data...\n";
       read(PGP,$dmy,$len);
       &data_print($dmy) if $v&&!$t;
   }
   else {
       # Presumably runs to the end of the file.
       print "And then a mess of data...\n";
       print "Data:\t0x" if $v&&!$t;
       while (!eof(PGP)) {
           $c=getc(PGP);
           if ($v&&!$t) {
               $c=unpack('H2',$c);
               print "\U$c\E";
           }
       }
       print "\n" if $v&&!$t;
   }
}

sub trust_handler {
   my($len,$byte);
   my($work,$ind);

   ($len)=@_;
   read(PGP,$byte,$len);
   $ind='';                    # Indent set to null.
   # Three different meanings, depending on previous packet.
   # If the previous packet is neither key nor userID nor signature,
   # set $ind (so we can tell later) and remark.
   # If TERSE, just print a newline and get on with life.
   if ($t) {
       print "\n";
       return;
   }
   if ($lastctb!=$CTB_PUB && $lastctb!=$CTB_SEC && $lastctb!=$CTB_UID &&
       $lastctb!=$CTB_SKE) {
       # If it's not after a permitted type, then show *all* the
       # possible meanings, in a slightly different format.
       print
           "Corrupt file! Trust packet should follow key, userID, or sig.\n";
       $ind="\t";
   }
   printf("Trust byte: %s\n",unpack("B8",$byte));
   $byte=unpack('C',$byte);
   if ($ind || $lastctb==$CTB_PUB || $lastctb==$CTB_SEC) {
       print "Trust information if previous packet is key:\n" if $ind;
       # If preceding packet is a KEY packet:
       print "${ind}Trust of this key owner:\t";
       $work=$byte&07;
       if ($work==00) {
           print "Undefined\n";
       }
       elsif ($work==01) {
           print "Unknown\n";
       }
       elsif ($work==02) {
           print "Usually do not trust to sign\n";
       }
       elsif ($work==03) {
           print "(reserved 011)\n";
       }
       elsif ($work==04) {
           print "(reserved 100)\n";
       }
       elsif ($work==05) {
           print "Usually trust to sign\n";
       }
       elsif ($work==06) {
           print "Always trust to sign\n";
       }
       else {
           print "Present on secret ring.\n";
       }
       $work=$byte&040;
       if ($work) {
           print "${ind}DISABLED\n";
       }
       else {
           print "${ind}Enabled\n";
       }
       # Other bits reserved
       $work=$byte&0200;
       if ($work) {
           print "${ind}Ultimate Trust: Present on secret ring.\n";
       }
   }

   # If preceding packet is UID:
   if ($ind || $lastctb==$CTB_UID) {
       print "Trust information if previous packet is user ID:\n" if $ind;
       $work=$byte&03;
       if ($work==00) {
           print "${ind}Unknown if this ID belongs to associated key\n";
       }
       elsif ($work==01) {
           print "${ind}ID not trusted to belong to associated key\n";
       }
       elsif ($work==02) {
           print
              "${ind}Marginal confidence that ID belongs to associated key\n";
       }
       else {
           print "${ind}Complete trust that key belongs to associated ID\n";
       }
       # Other bits reserved
       $work=$byte&0200;
       if ($work) {
           print "${ind}Will only warn if this key is used\n";
       }
       else {
           print "${ind}Will warn and verify before using this key\n";
       }
   }
   if ($ind || $lastctb==$CTB_SKE) {
       # If previous packet is SIG:
       print "Trust information if previous packet is signature:\n" if $ind;
       print "${ind}Trust of the owner of the signing key:\t";
       $work=$byte&07;
       if ($work==00) {
           print "Undefined\n";
       }
       elsif ($work==01) {
           print "Unknown\n";
       }
       elsif ($work==02) {
           print "Usually do not trust to sign\n";
       }
       elsif ($work==03) {
           print "(reserved 011)\n";
       }
       elsif ($work==04) {
           print "(reserved 100)\n";
       }
       elsif ($work==05) {
           print "Usually trust to sign\n";
       }
       elsif ($work==06) {
           print "Always trust to sign\n";
       }
       else {
           print "Present on secret ring.\n";
       }
       # Other bits reserved
       $work=$byte&0100;
       if ($work) {
           print "${ind}Key checking pass has checked this signature\n";
       }
       else {
           print "${ind}Key checking pass does not trust this signature\n";
       }
       $work=$byte&0200;
       if ($work) {
           print
            "${ind}Contiguous chain of trusted sigs to ultimate one exists\n";
       }
       else {
           print
        "${ind}No contiguous chain of trusted sigs to ultimate one exists\n";
       }
   }
}

sub pke_handler {
   my ($len,$version,$kid,$alg,$dmy);

   ($len)=@_;
   read(PGP,$version,1); $len--;
   $version=unpack('C',$version);
   read(PGP,$kid,8); $len-=8;
   $kid=unpack('H*',$kid);
   read(PGP,$alg,1); $len--;
   $dmy=&rmpi();
   printf ("Version:\t%d\n",$version) unless $t;
   print "Key ID:\t0x\U$kid\E\n";
   unless ($t) {
       &findkey($kid) if $u;
       $alg=unpack('C',$alg);
       printf ("Algorithm:\t%d (%s)\n",$alg,(1==$alg ? "RSA" : "??"));
       print length($dmy)/2, " bytes of data.\n";
       $len-=length($dmy)/2+2; # add two bytes for length field
       &data_print($dmy,1) if $v;
       &length_warn($len) if $len;
   }
}

sub ske_handler {
   my ($len,$vb,$md5len,$class,$ts,$kid,$pkalg,$digalg,$chk,$dmy);

   ($len)=@_;
   read(PGP,$vb,1); $len--; $vb=unpack('C',$vb);
   read(PGP,$md5len,1); $len--; $md5len=unpack('C',$md5len);
   read(PGP,$class,1); $len--; $class=unpack('C',$class);
   printf("Version:\t%d\n",$vb) unless $t;
   printf("Adding %d bytes of header to digest\n",$md5len) unless $t;
   if ($class==0x0) {
       print "Signature of binary document";
   }
   elsif ($class==0x1) {
       print "Signature of canonical text document";
   }
   elsif ($class==0x10) {
       print "Generic Key certification";
   }
   # Most of these are unsupported
   elsif ($class==0x11) {
       print "Persona Key certification (unsupported)";
   }
   elsif ($class==0x12) {
       print "Casual ID Key certification (unsupported)";
   }
   elsif ($class==0x13) {
       print "Positive ID Key certification (unsupported)";
   }
   elsif ($class==0x20) {
       print "Key Compromise certificate, revoking public key";
   }
   elsif ($class==0x30) {
       print "Key/UserID Revocation certificate (unsupported)";
   }
   elsif ($class==0x40) {
       print "Timestamp of signature (unsupported)";
   }
   else {
       printf("Unknown signature classification (%x)",$class);
   }
   read(PGP,$ts,4); $len-=4; $ts=unpack('N',$ts);
   read(PGP,$kid,8); $len-=8; $kid=unpack("H*",$kid);
   read(PGP,$pkalg,1); $len--; $pkalg=unpack('C',$pkalg);
   read(PGP,$digalg,1); $len--; $digalg=unpack('C',$digalg);
   read(PGP,$chk,2); $len-=2; $chk=unpack('H*',$chk);
   $dmy=&rmpi();
   unless ($t) {
       print "\nSignature Created:\t".&date_fmt($ts)."\n";
       print "Signing Key ID:\t0x\U$kid\E\n";
       &findkey($kid) if $u;
       printf("Public Key Algorithm:\t%d (%s)\n",
              $pkalg,($pkalg==1?"RSA":"??"));
       printf("Message Digest Algorithm:\t%d (%s)\n",
              $digalg,($digalg==1?"MD5":"??"));
       print "Check bytes:\t0x\U$chk\E\n";
       print length($dmy)/2, " bytes of data\n";
       $len-=length($dmy)/2+2; # Add two for bitcount field
       &data_print($dmy,1) if $v;
       &length_warn($len) if $len;
   }
   else {
       print " (0x\U$kid\E)\n";
   }
}

sub sec_handler {
   my($len,$vb,$ts,$valid,$alg,$nn,$ee,$ciph,$iv,$dd,$pp,$qq,$uu,
      $chksum);

   ($len)=@_;
   read(PGP,$vb,1); $len--; $vb=unpack('C',$vb);
   read(PGP,$ts,4); $len-=4; $ts=unpack('N',$ts);
   read(PGP,$valid,2); $len-=2; $valid=unpack('n',$valid);
   read(PGP,$alg,1); $len--; $alg=unpack('C',$alg);
   $nn=&rmpi(); $len-=length($nn)/2+2;
   $ee=&rmpi(); $len-=length($ee)/2+2;
   read(PGP,$ciph,1); $len--; $ciph=unpack('C',$ciph);
   unless ($t) {
       print "Version Byte:\t$vb\n";
       print "Key Created:\t".&date_fmt($ts)."\n";
       printf("Valid for%s\n",($valid?"\t$valid days":"ever"));
       printf("Algorithm:\t%d (%s)\n",$alg,(1==$alg?"RSA":"??"));
       print "N:\t0x\U$nn\E\n";
       print "E:\t0x\U$ee\E\n";
       printf("Protection Algorithm:\t%d (%s)\n",$ciph,
              ($ciph ? (1==$ciph ? "IDEA" : "??") : "None"));
   }
   else {
       printf "0x%s\n",substr("\U$nn",-16,16);
   }
   if ($ciph) {
       read(PGP,$iv,8); $len-=8;
       print "8 bytes of cipher feedback\n" unless $t;
   }
   $dd=&rmpi(); $len-=length($dd)/2+2;
   $pp=&rmpi(); $len-=length($pp)/2+2;
   $qq=&rmpi(); $len-=length($qq)/2+2;
   $uu=&rmpi(); $len-=length($uu)/2+2;
   read(PGP,$chksum,2); $len-=2; $chksum=unpack('H*',$chksum);
   unless ($t) {
       if ($ciph && !$v) {
           printf "D: (encrypted: %d bytes)\n",length($dd)/2;
           printf "P: (encrypted: %d bytes)\n",length($pp)/2;
           printf "Q: (encrypted: %d bytes)\n",length($qq)/2;
           printf "U: (encrypted: %d bytes)\n",length($uu)/2;
       }
       else {
           # If there's no cipher encryption, print out P, Q, D, and U.
           print "D:\t0x\U$dd\E\n";
           print "P:\t0x\U$pp\E\n";
           print "Q:\t0x\U$qq\E\n";
           print "U:\t0x\U$uu\E\n";
       }
       print "Checksum:\t0x\U$chksum\E\n";
       &length_warn($len) if $len;
   }
}

sub raw_handler {
   my($len,$mode,$namelen,$name,$ts,$data);

   ($len)=@_;
   read(PGP,$mode,1); $len--;
   read(PGP,$namelen,1); $len--; $namelen=unpack('C',$namelen);
   read(PGP,$name,$namelen); $len-=$namelen;
   read(PGP,$ts,4); $len-=4; $ts=unpack('N',$ts);
   # There's also 'l'ocal mode, undocumented.
   printf("Mode:\t%s (%s)\n",$mode,
          ('b' eq $mode ? "binary" : ('t' eq $mode ? "canonical text" :
                                      "unknown"))) unless $t;
   print "Length of filename:\t$namelen\n" unless $t;
   print "Filename:\t$name\n";
   unless ($t) {
       if ($ts) {
           print "Timestamp:\t".&date_fmt($ts)."\n";
       }
       else {
           # Timestamp can be (and often is) zero, which may mean
           # whatever, but certainly doesn't mean it was created
           # 1 Jan 1970.  Better just to say it's zero.
           print "Timestamp:\tZero\n";
       }
   }
   if ($len>0) {
       read(PGP,$data,$len);
       print "$len bytes of data.\n" unless $t;
       &data_print($data) if $v&&!$t;
   }
   else {
       print "And then a mess of data...\n" unless $t;
       while (!eof(PGP)) {
           $c=getc(PGP);
           if ($v&&!$t) {
               $c=unpack('H2',$c);
               print "\U$c\E";
           }
       }
       print "\n" if $v&&!$t;
   }
}

sub process_packet {
   my($ct,$lf,$len,$typestr,$handler);

   return 0 unless (($ct,$len,$lf)=&ctb());
   if ($ct<0) {
       # Error!  CTB with bit 7 not set found.
       print "\nINVALID CTB ($len)!  Bit 7 not set.\n";
       exit(10);
   }
   if ($ct==$CTB_PKE) {
       $typestr="Public-Key Encrypted Packet";
       $handler=\&pke_handler;
   }
   elsif ($ct==$CTB_SKE) {
       $typestr="Secret-Key Encrypted Packet (signature)";
       $handler=\&ske_handler;
   }
   elsif ($ct==$CTB_SEC) {
       $typestr="Secret Key Packet";
       $handler=\&sec_handler;
   }
   elsif ($ct==$CTB_PUB) {
       $typestr="Public Key Packet";
       $handler=\&pub_handler;
   }
   elsif ($ct==$CTB_COMP) {
       $typestr="Compressed Data Packet";
       $handler=\&comp_handler;
   }
   elsif ($ct==$CTB_CKE) {
       $typestr="Conventionally Encrypted Packet";
       $handler=\&conv_handler;
   }
   elsif ($ct==$CTB_RAW) {
       $typestr="Raw Data Packet";
       $handler=\&raw_handler;
   }
   elsif ($ct==$CTB_TRUST) {
       $typestr="Keyring Trust Packet";
       $handler=\&trust_handler;
   }
   elsif ($ct==$CTB_UID) {
       $typestr="User ID Packet";
       $handler=\&uid_handler;
   }
   elsif ($ct==$CTB_CMT) {
       $typestr="Comment Packet";
       $handler=\&comment_handler;
   }
   else {
       $typestr=sprintf("UNKNOWN PACKET!! (%o)",$ct);
       $handler=\&null_handler;
   }
   print "\n---------------------------\nPacket Type:\t" unless $t;
   print "$typestr",($t?": ":"\n");
   print "Length:\t$len\n" unless $t;
   &$handler($len);
   $lastctb=$ct;
   return(1);
}

# MAIN PROGRAM

die "Usage: $0 [-v] [-a] [-v] [-u[=pubfile]] [pgpfile], stopped"
   if $#ARGV>1;                # -v for verbose, -a to force ascii mode.
# Check an option or two...
if ($h) {
   # print help message and exit
   print<<"EEOOFF"
PGPACKET: analyze the output of PGP.

Usage: $0 [options] [pgpfile]

If no file is specified, reads stdin.

Options:
\t-v: Verbose.  Print out more information.
\t-t: Terse.  Print out very little information.  Overrides -v and -u.
\t-a: ASCII mode.  Presume ASCII mode, even if first char is 8-bit
\t-u[=pubring]: Look up keyIDs in pubring file.
\t\tIf pubring is not specified, defaults to ~/.pgp/pubring.pgp.

EEOOFF
   ;
   exit(1);
}
if ($u) {
   # User-ID lookup enabled.  Open the pubring.
   if ($u==1) {
       # No value specified.  Default to $HOME/.pgp/pubring.pgp
       # I realize that this makes it tough to specify a keyring file
       # named "1".
       $u=$ENV{'HOME'}.'/.pgp/pubring.pgp';
   }
   unless (open(PUBRING,$u)) {
       print "Warning: $u could not be opened.  User-ID lookup disabled.\n";
       $u=0;
   }
}
if (@ARGV) {
   die "Couldn't open $ARGV[0]" unless open(PGP,$ARGV[0]);
}
else {
   open(PGP,"<&STDIN");
}
# Test for binaryness.
if (-T PGP || $a) {
   # If it's a text file, assume this is ascii-armored and dearmor it.
   # To dearmor, try forking and running a dearmor script in the child,
   # piping the output back to the parent.
   # Have to read in the first line here.  Otherwise, the -T test eats
   # the first line in the child, so if you're starting right at the
   # -----BEGIN line it won't find it.
   $_=<PGP>;
   open(PGP2,"<&PGP");
   if (!open(PGP,"-|")) {
       # In the child...
       # Search for and eat frame.
       # Skip past the header.  Note special-case, to ignore the top of a
       # signed plain-text document; skip down to the real sig.
       until (/^-----BEGIN PGP / &! /^-----BEGIN PGP SIGNED MESSAGE/) {
           $_=<PGP2>;
       }
       $_=<PGP2> until /^\s*\n?$/;
       while (1) {
           $last=0;
           $_=<PGP2>;
           # equals signs are used to pad out the end.
           s/^([^-=]*)[-=].*/\1/;
           # Only last chunk may be less than 64chars.  This may not always
           # be able to exclude the checksum.  Oh well.
           $last=1 if length($_)<64;
           tr@A-Za-z0-9+/@`!-_@d; # matching `
           $_=pack('C',length($_)*3/4+040).$_;
           $_=unpack("u",$_);
           $_=~s/\0$// if $last;       # Strip *one* trailing null
           print $_;
           last if $last;
       }
       # And finish!  The child needs to exit here.
       exit(0);
   }
   close(PGP2);
}
# Otherwise, just continue.
$lastctb=0;                     # Last seen ctb;
while (1) {
   last unless &process_packet;
}
exit(0);

$dmy=<<"-END"
- -END
   ;

-----BEGIN PGP SIGNATURE-----
Version: 2.6.2

iQCVAwUBMPPgkdjId3FCnAfhAQFSlgP/VZPqwalySFqnGWtW4kF8m00MTkKxXyF8
bfdAJHdZ9kKBWJO1YJpFlxcMmzZD04MkQsa99hBwZDu3H/rJf3tk9zF31RuFLvuE
H292mj88BAzI4M/O+J9v31Xh+kOFCpulAn5rj7vm0OkYApJeaQaFUB9tlgeV0xdH
xbebYFTQCcM=
=KPto
-----END PGP SIGNATURE-----
-END
   ;