MINIPGP - an implementation of PGP Version 2 core routines using
          SSLeay's crypto lib and the zlib compression lib.

SSLeay can be found from http://www.psy.uq.edu.au:8080/~ftp/Crypto/

zlib's home is at http://www.cdrom.com/pub/infozip/zlib/

Version 1.2

This source code free to use, redistribute, and modify for any
purpose, with the only restriction that if you modify anything, you
either have to say specifically what the modifications are, or not
associate your work with this one.  This is only to prevent a crippled
version from being misidentified with this code.  It uses libraries
which are under different restrictions, so check those terms too.

[Much of this was written prior to the release of PGP 5.0, so if I refer
to PGP, it means some variant of 2.6 or 2.7]

libpgp

Includes basic keyring handling.  Looks up keys based on keyid - uses 32 or 64
bit and returns full RSA structures, and handles passphrases for secret
keyrings.  Also has experimental support for user ID matching.

crypto glue - the handlers for each packet type including the public key
packet, conventionally encrypted packet, and signature packets with support for
both creation and decrypting.

Note: a small conversion must be made between the result of the RSA functions
as implemented in SSLeay and PGP mpi numbers, since the former requires the
buffer to have enough bits for the key's N, and the latter won't allow leading
zero bytes in the mpi integers, e.g. RSA needs 512/001234... and PGP needs
504/1234 (or 501/84D...).

Strangely enough, this routine (and the other routines) contain no actual
crypto.  The crypto routines are provided by SSLeay, unless you add the
compatability stuff at the end of this file.

armor, dearmor, literal, deliteral functions.  I should add canonical text
conversion, but the awk scripts are easier and I am lazy.  Also zip/unzip
using zlib 1.04 by Jean-loup Gailly and Mark Adler.

In the above cases, I have tried to avoid seeks, but could not do so for
conventional encryption and literal functions (alternately the input size is
needed).  Another place where I was lazy is in using file pointers instead of
going all the way to SSLeay's BIO functions.

Many routines have comments about returning data instead of actually doing so.
Some more verification should be done.  I also don't clear all the buffers
before freeing them which I should do for security.  I expect someone else can
make the improvements.

TESTMODE allows grabbing the first public key if the keyid is zero, and is
used for test.sh since it generates new keys each pass.  (Zero always grabs
the first, i.e. the default secret key, TESTMODE only affects public keys).

---

libpgp.h

prototypes and other declarations for the above.

---

minipgp.c

An example program which can be built to do most things pgp can for encryption
and decryption.

test.sh shows the process

For an ascii armored packet, the first run will dearmor the packets.

If it is a conventionally encrypted packet
    minipgp -p "passphrase"
will decrypt it, Note: passphrase is what was given to the -c

For a public key encrypted packet,
    minipgp -p seckeypass
will decrypt it, Note: in this case seckeypass is for the secret key

Since these are usually compressed, running minipgp again will decompres the
packet, which will normally be a literal, or a signature before the literal.

To remove the literal container, use
    minipgp -x
(this will skip any signature or other packets)

To check the signature, you can send the decompressed packet to
    minipgp -d
(-d says check the contents of the literal, not the whole packet)

To check a detached signature for filex, send the sig file through
    minipgp -f filex

To package stuff within a literal, run it through
    minipgp -l
(note it cannot pipe out because it must do a seek)

To create a signature packet, use
    minipgp [-p seckeypass] [-t] [-d] -u keyid 
keyid can be zero where it will use the first key it finds.  The -t is to set
the text mode bit, so using canonical text and this will sign text messages
properly.

"cat" the sig file and the literal packet.

To compress this, use
    minipgp -z

Then to encrypt, use
    minipgp -e keyid [-e keyid2 ...]
and it will create a random conventional key and encrypt out for each user
specified (This is what PGP does), then encrypt the input to the output using
the conventional key.

Or for conventional encryption, use
    minipgp -p "passphrase" -c

(note: neither encryption can pipe out because it must do a seek)

And if you want to armor it,
    minipgp -a

For encryption, decryption or signatures, you can specify -k keyringpath to
use a different keyring

PGP has something called canonical text, which strips trailing spaces and
forces line terminations to be CRLF.  This is what the -t option does under
PGP.  The awk scripts do this adjustment using AWK.  The space stripping may
only be for clearsign mode.

MISCELLANEOUS

msgcheck.awk - check a message signed with "pgp -sat"

pgp-sat.awk - (filter) sign a message emulating "pgp -sat"

test.sh - shows everything this package can do including key
    revocation packets, as a test loop.

pullkey - demo program for extracting key packets for signing keys

signkey - script to sign public keys

revokey - script to create a key revocation certificate

keygen [-n numbits] [-p "pass phrase"] [-u "user id"] >seckey.pgp 2>pubkey.pgp
    Generates secret and public key with numbits.  Defaults to 1024 bits,
    "Unknown User", and no pass phrase.  These have to be added to the PGP
    keyring by PGP, or can just be concatenated (see next section).

NOTE: this will work with any RSA key size that SSLeay supports, including
4096 bit keys.

WHAT I DONT DO

PGP has a complex key trust system, e.g. a key is trusted if so many marginal
trust signatures exist, and so many completely trusted, etc.  The only thing I
do check for is a disabled key in pgpkey.c to prevent its use.  Keyring
editing, verification, trust chains is something for someone else to do, and I
don't even generate the trust CTB, but you can use something like echo -ne
"\260\001\000" for defaults or "\260\001\207" for keys on the secret ring.

Part of this is the keyring editing functions.  PGP uses a linear file, and
the functions involve the trust stuff.  The whole thing should be redone using
a database.  For minipgp, you can just cat new keys to the end of the existing
keyrings, though PGP will complain because of the lack of trust packets if you
try to use them as rings instead of "adding" them to existing PGP keyrings.

PGP also has a number of things which it does in certain ways which aren't
easily specified - like when it does/doesn't strip trailing spaces, batchmode,
verbose and normal messages, etc. which I am not going to even attempt to
clone.  I am limiting things to interoperability - PGP should properly handle
the files I generate, and my program should be able to handle the files PGP
generates, so any script using PGP could be modified to use minipgp and still
work.

Others are welcome to extend this into a drop-in replacement for PGP.

------------------------

How to adapt pgpcrypt.c to support older version packets - with the suggested
(but untested, except for 2.6.2+) mods, minipgp should be able to READ
anything from any version 2 PGP.  These are based on my initial work on
minipgp before I used the specific rsa functions.  Since I don't have early
versions of PGP, I can't fully test the mods.

int                 decpke2(FILE * pkt, unsigned char *passwdhash)
#if READ_OLD_PKE
  {
    BIGNUM             *temp = BN_new();

    temp = pgp2BN(&bp, 0);
    RSA_mod_exp(temp, temp, key);    /* rsa decrypt */
    j = BN_bn2bin(temp, dbuf);    /* find where conv key is */
    BN_free(temp);
  }
  if (dbuf[0] == 1)
    j = 19;
  else if (dbuf[0] != 2)
    return -1;
#else
  i -= 12;
  j = 256 * *bp++;
  j += *bp++;
  ll = BN_num_bytes(key->n);
  while (i < ll)
    memmove(&bp[1], bp, i++), bp[0] = 0, j += 8;
  j = RSA_private_decrypt((j + 7) / 8, bp, dbuf, key);
#endif

int                 sigchk2(FILE * cert, FILE * check, int delit)
int ver;
#if READ_OLD_SIG
  ver = fgetc(cert);
  if( ver != 3 && ver != 2 )
     return -4;
#else
  if( fgetc(cert) != 3 )
    return -4;    /* version, should be 3 */
#endif
#if READ_OLD_SIG
    {
    BIGNUM temp=BN_new();
    BN_CTX *ctx = BN_CTX_new();
    unsigned char *bp = dbuf;
    temp = pgp2BN(&bp, 0);
    BN_mod_exp(temp, temp, key->e, key->n, ctx);
    j = BN_bn2bin(temp, dbuf);
    BN_free(temp);
    BN_CTX_free(ctx);
    }
    if (ver == 2 && memcmp(&dbuf[1], mdck, 2))
        j = 17; /* old style MD
    else if( ver == 3 && dbuf[1] == 0xff && dbuf[2] == 0xff ) { 
      if (memcmp(dbuf, asnchk, 18) || memcmp(&dbuf[18], mdck, 2))
            return -2;
    }
    else
        return -1;
    memcpy(mdhash, &dbuf[j - 16], 16);
#else
  j = 256 * dbuf[0];
  j += dbuf[1];
  i -= 21;
  fread(dbuf, 1, i, cert);
  ll = BN_num_bytes(key->n);
  while (i < ll)
    memmove(&dbuf[1], dbuf, i++), dbuf[0] = 0, j += 8;
  j = RSA_public_decrypt((j + 7) / 8, dbuf, dbuf, key);
  RSA_free(key);
  if (memcmp(dbuf, asnchk, 18) || memcmp(&dbuf[18], mdck, 2))
    return -2;
  memcpy(mdhash, &dbuf[18], 16);
#endif
