/* $Id: pgp.c,v 1.40 2015/09/29 13:17:42 onoe Exp $ */

/*-
 * Copyright (c) 1999-2001 Atsushi Onoe
 * 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.
 */

#include <sys/types.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>

#ifdef USE_OPENSSL
#include <openssl/bn.h>
#include <openssl/rsa.h>
#include <openssl/evp.h>
#include <openssl/rand.h>
#include <openssl/dsa.h>
#else /* USE_OPENSSL */
#include <rsa.h>
#include <evp.h>
#include <rand.h>
#include <dsa.h>
#endif /* USE_OPENSSL */
#ifdef USE_IDEA
extern const EVP_CIPHER *EVP_idea_cfb64(void);
#endif /* USE_IDEA */

#include <zlib.h>

#include "pgp.h"

#define	GET2(p)	(p += 2, (p[-2] << 8) | p[-1])
#define	GET4(p)	(p += 4, (p[-4] << 24) | (p[-3] << 16) | (p[-2] << 8) | p[-1])
#define	PUT2(p, v)	((*p++ = ((u_long)(v) >> 8) & 0xff), \
			 (*p++ = ((u_long)(v) & 0xff)))
#define	PUT4(p, v)	((*p++ = ((u_long)(v) >> 24) & 0xff), \
			 (*p++ = ((u_long)(v) >> 16) & 0xff), \
			 (*p++ = ((u_long)(v) >> 8) & 0xff), \
			 (*p++ = (u_long)(v) & 0xff))

u_char pgp_nullkeyid[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };

static struct pgp_pkt * pgp_parsepkt(const u_char **pp, const u_char *ep);
static int pgp_makepkt(struct pgp_pkt *pkt, u_char **bufp, int *lenp);
static const EVP_CIPHER * pgp_symalg_cipher(int alg);
static BIGNUM * pgp_decrypt_mpi(EVP_CIPHER_CTX *ctx, const u_char **pp, u_short *sum);
static BIGNUM * pgp_parse_mpi(const u_char **pp);

#ifdef PGP_DEBUG
#define	PGP_DPRINT(X)	printf X
#else /* PGP_DEBUG */
#define	PGP_DPRINT(X)
#endif /* PGP_DEBUG */

#if defined(PGP_DEBUG) || defined(PGPMIME_DEBUG)
void pgp_showpkt(struct pgp_pkt *pkt);

static char *pgp_show_tag(int tag);
static char *pgp_show_pubalg(int alg);
static char *pgp_show_symalg(int alg);
static char *pgp_show_hashalg(int alg);
static char *pgp_show_compalg(int alg);
static char *pgp_show_signtype(int type);
static char *pgp_show_keyid(const u_char *p);
#endif /* PGP_DEBUG || PGPMIME_DEBUG */

struct pgp_pkt *
pgp_getpkt(struct pgp_pkt *head, int tag)
{
	struct pgp_pkt *pkt;

	for (pkt = head; pkt; pkt = pkt->next) {
		if (pkt->tag == tag)
			break;
	}
	return pkt;
}

static int
pgp_parse_subpkt(const u_char **pp, const u_char *ep, struct pgp_pkt *pkt)
{
	const u_char *p, *np;
	int plen;

	if (pkt->tag != PGP_TAG_SIGN)
		return -1;
	for (p = *pp; p < ep; p = np) {
		plen = *p++;
		if (plen == 255)		/* five octet length */
			plen = GET4(p);
		else if (plen >= 192)
			plen = ((plen - 192) << 8) + *p++ + 192;
		np = p + plen;
		switch (*p++ & 0x7f) {
		case PGP_SIGSUB_SIGCREATE:
			pkt->un.sign.ctime = GET4(p);
			break;
		case PGP_SIGSUB_SIGEXPIRE:
			pkt->un.sign.exptime = GET4(p);
			break;
		case PGP_SIGSUB_ISSUER_ID:
			memcpy(pkt->un.sign.keyid, p, 8);
			p += 8;
			break;
		default:
			if (p[-1] & PGP_SIGSUB_CRITICAL) {
				PGP_DPRINT(("ERROR: critical in subpacket: %02x\n", p[-1]));
				return -1;
			}
			break;
		}
	}
	*pp = p;
	return 0;
}

static struct pgp_pkt *
pgp_parsepkt(const u_char **pp, const u_char *ep)
{
	const u_char *p = *pp, *s;
	u_char *q;
	struct pgp_pkt *pkt, pktbuf;
	int plen;
	int i;

	pkt = &pktbuf;
	memset(pkt, 0, sizeof(*pkt));
	if (!(*p & 0x80)) {
		PGP_DPRINT(("ERROR: reserved flag is set in header: %02x (%d left)\n", *p, ep - p));
		*pp = ep;	/* cannot parse more */
		return NULL;
	}
	if (*p & 0x40) {
		pkt->flags |= PGP_FLAG_NEW;
		pkt->tag = *p++ & 0x3f;
		for (;;) {
			plen = *p++;
			if (plen < 192) {		/* one octet length */
				break;
			}
			if (plen < 224) {		/* two-octet length */
				plen = ((plen - 192) << 8) + *p++ + 192;
				break;
			}
			if (plen == 255) {		/* five octet length */
				plen = GET4(p);
				break;
			}
			/* partial length */
			plen = 1 << (plen & 0x1f);
			q = realloc(pkt->pbuf, pkt->dlen + plen);
			if (q == NULL) {
				PGP_DPRINT(("ERROR: realloc(%d) failed\n",
					pkt->dlen + plen));
				*pp = ep;	/* cannot parse more */
				return NULL;
			}
			pkt->pbuf = q;
			memcpy(pkt->pbuf + pkt->dlen, p, plen);
			pkt->dlen += plen;
			p += plen;
		}
	} else {	/* old packet */
		pkt->tag = (*p & 0x3c) >> 2;
		switch (*p++ & 0x03) {
		case 0: plen = *p++; break;
		case 1: plen = GET2(p); break;
		case 2: plen = GET4(p); break;
		default: plen = ep - p; break;
		}
	}
	if (p + plen > ep || plen < 0) {
		PGP_DPRINT(("ERROR: bad plen(%d)\n", plen));
		*pp = ep;	/* cannot parse more */
		return NULL;
	}
	if (pkt->pbuf != NULL) {
		q = realloc(pkt->pbuf, pkt->dlen + plen);
		if (q == NULL) {
			PGP_DPRINT(("ERROR: realloc(%d) failed\n",
				pkt->dlen + plen));
			*pp = ep;	/* cannot parse more */
			return NULL;
		}
		pkt->pbuf = q;
		memcpy(pkt->pbuf + pkt->dlen, p, plen);
		pkt->dlen += plen;
		*pp = p + plen;
		p = pkt->pbuf;
		ep = p + pkt->dlen;
	} else {
		if (p + plen > ep) {
			PGP_DPRINT(("ERROR: run out of buffer: %d > %d\n", plen, ep - p));
			*pp = ep;	/* cannot parse more */
			return NULL;
		}
		*pp = ep = p + plen;
	}
	s = p;
	switch (pkt->tag) {
	case PGP_TAG_PUBENC_SESKEY:
		pkt->un.pubses.ver = *p++;
		if (pkt->un.pubses.ver < 2 || pkt->un.pubses.ver > 3) {
			PGP_DPRINT(("ERROR: %s: unknown version(%d)\n",
				pgp_show_tag(pkt->tag), pkt->un.pubses.ver));
			return NULL;
		}
		memcpy(pkt->un.pubses.keyid, p, 8);
		p += 8;
		pkt->un.pubses.pubalg = *p++;
		break;
	case PGP_TAG_SIGN:
		pkt->un.sign.ver = *p++;
		switch (pkt->un.sign.ver) {
		case 2:
		case 3:
			/* Version 3 signature */
			if (*p++ != 5) {
				PGP_DPRINT(("ERROR: %s: length(%d)!=5\n",
					pgp_show_tag(pkt->tag), p[-1]));
				return NULL;
			}
			pkt->un.sign.type = *p++;
			pkt->un.sign.ctime = GET4(p);
			memcpy(pkt->un.sign.keyid, p, 8);
			p += 8;
			pkt->un.sign.pubalg = *p++;
			pkt->un.sign.hashalg = *p++;
			pkt->un.sign.hash = GET2(p);
			break;
		case 4:
			pkt->un.sign.type = *p++;
			pkt->un.sign.pubalg = *p++;
			pkt->un.sign.hashalg = *p++;
			if ((plen = GET2(p)) > 0) {	/* hashed */
				if (pgp_parse_subpkt(&p, p + plen, pkt) < 0)
					return NULL;
			}
			pkt->un.sign.hdbuf = s;
			pkt->un.sign.hdlen = p - s;
			if ((plen = GET2(p)) > 0) {	/* unhashed */
				if (pgp_parse_subpkt(&p, p + plen, pkt) < 0)
					return NULL;
			}
			pkt->un.sign.hash = GET2(p);
			break;
		default:
			PGP_DPRINT(("ERROR: %s: unknown version(%d)\n",
				pgp_show_tag(pkt->tag), pkt->un.sign.ver));
			return NULL;
		}
		break;
	case PGP_TAG_ONEPASS_SIGN:
		pkt->un.opsign.ver = *p++;
		pkt->un.opsign.type = *p++;
		pkt->un.opsign.hashalg = *p++;
		pkt->un.opsign.pubalg = *p++;
		memcpy(pkt->un.opsign.keyid, p, 8);
		p += 8;
		pkt->un.opsign.nestflg = *p++;
		break;
	case PGP_TAG_PUBLIC_KEY:
	case PGP_TAG_PUBLIC_SUBKEY:
	case PGP_TAG_SECRET_KEY:
	case PGP_TAG_SECRET_SUBKEY:
		pkt->un.pubkey.ver = *p++;
		if (pkt->un.pubkey.ver < 2 || pkt->un.pubkey.ver > 4) {
			PGP_DPRINT(("ERROR: %s: unknown version(%d)\n",
				pgp_show_tag(pkt->tag), pkt->un.pubkey.ver));
			return NULL;
		}
		pkt->un.pubkey.tstamp = GET4(p);
		if (pkt->un.pubkey.ver < 4)
			pkt->un.pubkey.expire = GET2(p);
		pkt->un.pubkey.pubalg = *p++;
		pkt->un.pubkey.key = EVP_PKEY_new();
		switch (pkt->un.pubkey.pubalg) {
		case PGP_PUB_RSA:
		    {
			RSA *rsa;

			if ((rsa = RSA_new()) == NULL) {
				PGP_DPRINT(("ERROR: RSA_new failed\n"));
				return NULL;
			}
			EVP_PKEY_assign_RSA(pkt->un.pubkey.key, rsa);
			if (pkt->un.pubkey.ver < 4) {
				i = (((p[0] << 8) | p[1]) + 7) / 8;
				memcpy(pkt->un.pubkey.keyid, p + 2 + i - 8, 8);
			}
			rsa->n = pgp_parse_mpi(&p);
			rsa->e = pgp_parse_mpi(&p);
			break;
		    }
		case PGP_PUB_DSA:
		    {
			DSA *dsa;

			if ((dsa = DSA_new()) == NULL) {
				PGP_DPRINT(("ERROR: DSA_new failed\n"));
				return NULL;
			}
			EVP_PKEY_assign_DSA(pkt->un.pubkey.key, dsa);
			dsa->p = pgp_parse_mpi(&p);
			dsa->q = pgp_parse_mpi(&p);
			dsa->g = pgp_parse_mpi(&p);
			dsa->pub_key = pgp_parse_mpi(&p);
			break;
		    }
		case PGP_PUB_ELGAMAL_ENC:
		    {
			DH *dh;

			if ((dh = DH_new()) == NULL) {
				PGP_DPRINT(("ERROR: DH_new failed\n"));
				return NULL;
			}
			EVP_PKEY_assign_DH(pkt->un.pubkey.key, dh);
			dh->p = pgp_parse_mpi(&p);
			dh->g = pgp_parse_mpi(&p);
			dh->pub_key = pgp_parse_mpi(&p);
			break;
		    }
		default:
			PGP_DPRINT(("ERROR: %s: unknown pubalg(%d)\n",
				pgp_show_tag(pkt->tag), pkt->un.pubkey.pubalg));
			return NULL;
		}
		if (pkt->un.pubkey.ver >= 4) {
			/* generate keyid here! */
			EVP_MD_CTX md_ctx;
			u_char hdr[3];
			u_char mdbuf[EVP_MAX_MD_SIZE];
			u_int mdlen;

			i = p - s;
			EVP_DigestInit(&md_ctx, EVP_sha1());
			hdr[0] = 0x80 | (PGP_TAG_PUBLIC_KEY << 2) | 1;
			hdr[1] = i >> 8;
			hdr[2] = i & 0xff;
			EVP_DigestUpdate(&md_ctx, hdr, sizeof(hdr));
			EVP_DigestUpdate(&md_ctx, s, i);
			EVP_DigestFinal(&md_ctx, mdbuf, &mdlen);
			memcpy(pkt->un.pubkey.keyid, mdbuf + mdlen - 8, 8);
		}
		if (pkt->tag == PGP_TAG_PUBLIC_KEY
		||  pkt->tag == PGP_TAG_PUBLIC_SUBKEY)
			break;
		pkt->un.pubkey.symalg = *p++;
		pkt->un.pubkey.hashalg = PGP_HASH_MD5;
		if (pkt->un.pubkey.symalg == 254)
			pkt->un.pubkey.sha1sum = 1;
		if (pkt->un.pubkey.symalg == 255 ||
		    pkt->un.pubkey.symalg == 254) {
			pkt->un.pubkey.symalg = *p++;
			pkt->un.pubkey.s2k = *p++;
			pkt->un.pubkey.hashalg = *p++;
			if (pkt->un.pubkey.s2k & PGP_S2K_SALT) {
				memcpy(pkt->un.pubkey.salt, p, 8);
				p += 8;
			}
			if (pkt->un.pubkey.s2k & PGP_S2K_ITER) {
				pkt->un.pubkey.count =
					(16 + (*p & 0xf)) << ((*p >> 4) + 6);
				p++;
			}
		}
		if (pkt->un.pubkey.symalg != 0) {
			memcpy(pkt->un.pubkey.iv, p, 8);
			p += 8;
		}
		break;
	case PGP_TAG_COMP_DATA:
		pkt->un.compdat.compalg = *p++;
		break;
	case PGP_TAG_SYMENC_DATA:
		break;
	case PGP_TAG_SYMENC_MDC_DATA:
		pkt->un.symdat.mdcver = *p++;
		break;
	case PGP_TAG_LITERAL_DATA:
		pkt->un.litdat.type = *p++;
		i = *p++;
		if (i > 0) {
			if ((pkt->un.litdat.name = malloc(i + 1)) == NULL) {
				PGP_DPRINT(("ERROR: malloc(%d) failed\n", i + 1));
				return NULL;
			}
			memcpy(pkt->un.litdat.name, p, i);
			pkt->un.litdat.name[i] = '\0';
			p += i;
		}
		pkt->un.litdat.mtime = GET4(p);
		break;
	case PGP_TAG_USER_ID:
		i = ep - p;
		if ((pkt->un.userid.user = malloc(i + 1)) == NULL) {
			PGP_DPRINT(("ERROR: malloc(%d) failed\n", i + 1));
			return NULL;
		}
		memcpy(pkt->un.userid.user, p, i);
		p += i;
		pkt->un.userid.user[i] = '\0';
		break;
	}
	pkt->dbuf = (u_char *)p;	/*XXX*/
	pkt->dlen = ep - p;
	if (pkt->dlen < 0) {
		PGP_DPRINT(("ERROR: bad data len(%d)\n", pkt->dlen));
		return NULL;
	}

	pkt = malloc(sizeof(*pkt));
	if (pkt == NULL) {
		PGP_DPRINT(("ERROR: malloc(%d) failed\n", sizeof(*pkt)));
		return NULL;
	}
	memcpy(pkt, &pktbuf, sizeof(*pkt));
	return pkt;
}

static int
pgp_setup_subpkt(struct pgp_pkt *pkt)
{
	u_char *p;
	int hdlen;
	u_char hdbuf[256];	/*XXX*/

	if (pkt->tag != PGP_TAG_SIGN)
		return -1;
	if (pkt->un.sign.hdbuf != NULL)
		return 0;

	p = hdbuf;

	/* sign header */
	*p++ = pkt->un.sign.ver;
	*p++ = pkt->un.sign.type;
	*p++ = pkt->un.sign.pubalg;
	*p++ = pkt->un.sign.hashalg;

	/* hashed sub packet length; updated later */
	PUT2(p, 0);

	*p++ = 1 + 4;
	*p++ = PGP_SIGSUB_SIGCREATE;
	PUT4(p, pkt->un.sign.ctime);

	/* XXX: put preference here! */

	hdlen = p - hdbuf;
	p = hdbuf + 4;
	PUT2(p, hdlen - 6);

	if ((p = malloc(hdlen)) == NULL)
		return -1;
	memcpy(p, hdbuf, hdlen);
	pkt->un.sign.hdbuf = p;
	pkt->un.sign.hdlen = hdlen;
	return 0;
}

static int
pgp_makepkt(struct pgp_pkt *pkt, u_char **bufp, int *lenp)
{
	int hlen, len, tlen;
	u_char *p;
	u_char hdr[8];
	u_char buf[256];	/*XXX*/

	p = buf;
	switch (pkt->tag) {
	case PGP_TAG_PUBENC_SESKEY:
		*p++ = pkt->un.pubses.ver;
		memcpy(p, pkt->un.pubses.keyid, 8);
		p += 8;
		*p++ = pkt->un.pubses.pubalg;
		break;
	case PGP_TAG_SIGN:
		if (pkt->un.sign.ver < 4) {
			*p++ = pkt->un.sign.ver;
			*p++ = 5;
			*p++ = pkt->un.sign.type;
			PUT4(p, pkt->un.sign.ctime);
			memcpy(p, pkt->un.sign.keyid, 8);
			p += 8;
			*p++ = pkt->un.sign.pubalg;
			*p++ = pkt->un.sign.hashalg;
		} else {
			memcpy(p, pkt->un.sign.hdbuf, pkt->un.sign.hdlen);
			p += pkt->un.sign.hdlen;
			/* unhashed sub packet */
			/* XXX: should be pgp_setup_subpkt */
			if (memcmp(pkt->un.sign.keyid, pgp_nullkeyid, 8) == 0) {
				PUT2(p, 0);
			} else {
				PUT2(p, 1 + 1 + 8);
				*p++ = 1 + 8;
				*p++ = PGP_SIGSUB_ISSUER_ID;
				memcpy(p, pkt->un.sign.keyid, 8);
				p += 8;
			}
		}
		PUT2(p, pkt->un.sign.hash);
		break;
	case PGP_TAG_COMP_DATA:
		*p++ = pkt->un.compdat.compalg;
		break;
	case PGP_TAG_SYMENC_DATA:
	case PGP_TAG_SYMENC_MDC_DATA:
		break;
	case PGP_TAG_MARKER:
		break;
	case PGP_TAG_LITERAL_DATA:
		*p++ = pkt->un.litdat.type;
		if (pkt->un.litdat.name == NULL) {
			*p++ = 0;
		} else {
			len = strlen(pkt->un.litdat.name);
			*p++ = len;
			memcpy(p, pkt->un.litdat.name, len);
			p += len;
		}
		PUT4(p, pkt->un.litdat.mtime);
		break;
	default:
		PGP_DPRINT(("TODO: MAKEPKT: %s\n",
			pgp_show_tag(pkt->tag)));
		return -1;
	}
	len = p - buf;
	tlen = len + pkt->dlen;
	p = hdr;
	if (pkt->flags & PGP_FLAG_NEW) {
		*p++ = 0xc0 | pkt->tag;
		if (tlen < 192)	{		/* one-octet length */
			*p++ = tlen;
		} else if (tlen < 8384) {	/* two-octet length */
			*p++ = (tlen >> 8) + 192;
			*p++ = tlen & 0xff;
		} else {			/* five octet length */
			*p++ = 255;
			PUT4(p, tlen);
		}
	} else {
		*p = 0x80 | (pkt->tag << 2);
#ifdef notdef	/* PGP 2.6 unrecognize this */
		if (tlen < 256) {
			p++;
			*p++ = tlen;
		} else
#endif
		if (tlen < 65536) {
			*p++ |= 1;
			PUT2(p, tlen);
		} else {
			*p++ |= 2;
			PUT4(p, tlen);
		}
	}
	hlen = p - hdr;
	if (*bufp != NULL) {
		if (*lenp < hlen + tlen)
			return -1;
	} else {
		if ((*bufp = malloc(hlen + tlen)) == NULL)
			return -1;
	}
	*lenp = hlen + tlen;
	memcpy(*bufp, hdr, hlen);
	memcpy(*bufp + hlen, buf, len);
	memcpy(*bufp + hlen + len, pkt->dbuf, pkt->dlen);
	return 0;
}

struct pgp_pkt *
pgp_parseall(const u_char *p, const u_char *ep)
{
	struct pgp_pkt *pkt, *tpkt, *ppkt, *pkthead;
	struct pgp_pkt **signp, **subkeyp, **userp;
	int subtag;

#ifdef USE_OPENSSL
	OpenSSL_add_all_algorithms();	/*XXX*/
#else
	SSLeay_add_all_algorithms();	/*XXX*/
#endif /* USE_OPENSSL */

	/* parse all */
	pkthead = ppkt = NULL;
	while (p < ep) {
		if ((pkt = pgp_parsepkt(&p, ep)) == NULL)
			continue;
#ifdef PGP_DEBUG
		pgp_showpkt(pkt);
#endif /* PGP_DEBUG */
		if (pkthead == NULL) {
			pkthead = ppkt = pkt;
		} else {
			ppkt->next = pkt;
			pkt->prev = ppkt;
			ppkt = pkt;
		}
	}
	/* construct ring information */
	for (pkt = pkthead; pkt; pkt = pkt->next) {
		switch (pkt->tag) {
		case PGP_TAG_SYMENC_DATA:
		case PGP_TAG_SYMENC_MDC_DATA:
			for (tpkt = pkt->prev; tpkt; tpkt = tpkt->prev) {
				if (tpkt->tag == PGP_TAG_SYMENC_DATA ||
				    tpkt->tag == PGP_TAG_SYMENC_MDC_DATA)
					break;
				if (tpkt->tag == PGP_TAG_PUBENC_SESKEY)
					tpkt->un.pubses.ref_symdat = pkt;
			}
			break;
		case PGP_TAG_MDC:
			for (tpkt = pkt->prev; tpkt; tpkt = tpkt->prev) {
				if (tpkt->tag == pkt->tag)
					break;
				if (tpkt->tag == PGP_TAG_SYMENC_MDC_DATA) {
					tpkt->un.symdat.ref_mdc = pkt;
					break;
				}
			}
			break;
		case PGP_TAG_PUBLIC_KEY:
		case PGP_TAG_SECRET_KEY:
			subtag = (pkt->tag == PGP_TAG_PUBLIC_KEY) ?
				PGP_TAG_PUBLIC_SUBKEY : PGP_TAG_SECRET_SUBKEY;
			signp = &pkt->un.pubkey.ref_sign;
			subkeyp = &pkt->un.pubkey.ref_subkey;
			userp = &pkt->un.pubkey.ref_user;
			for (tpkt = pkt->next; tpkt; tpkt = tpkt->next) {
				if (tpkt->tag == subtag) {
					tpkt->un.pubkey.ref_user = pkt->un.pubkey.ref_user;
					*subkeyp = tpkt;
					subkeyp = &tpkt->un.pubkey.ref_subkey;
					signp = &tpkt->un.pubkey.ref_sign;
				}
				if (tpkt->tag == PGP_TAG_USER_ID) {
					*userp = tpkt;
					userp = &tpkt->un.userid.ref_user;
					signp = &tpkt->un.userid.ref_sign;
				}
				if (tpkt->tag == PGP_TAG_SIGN) {
					*signp = tpkt;
					signp = &tpkt->un.sign.ref_sign;
				}
				if (tpkt->tag == pkt->tag)
					break;
			}
			break;
		}
	}
	return pkthead;
}

void
pgp_freepkt(struct pgp_pkt *pkt)
{
	struct pgp_pkt *npkt;

	for (; pkt; pkt = npkt) {
		npkt = pkt->next;
		switch (pkt->tag) {
		case PGP_TAG_PUBENC_SESKEY:
			if (pkt->un.pubses.seskey)
				free(pkt->un.pubses.seskey);
			break;
		case PGP_TAG_SIGN:
			if (pkt->un.sign.mdbuf)
				free(pkt->un.sign.mdbuf);
			if (pkt->un.sign.hctx)
				free(pkt->un.sign.hctx);
			if (pkt->un.sign.pkt)
				pgp_freepkt(pkt->un.sign.pkt);
			break;
		case PGP_TAG_PUBLIC_KEY:
		case PGP_TAG_PUBLIC_SUBKEY:
		case PGP_TAG_SECRET_KEY:
		case PGP_TAG_SECRET_SUBKEY:
			if (pkt->un.pubkey.key)
				EVP_PKEY_free(pkt->un.pubkey.key);
			break;
		case PGP_TAG_COMP_DATA:
			if (pkt->un.compdat.pkt)
				pgp_freepkt(pkt->un.compdat.pkt);
			if (pkt->un.compdat.decbuf)
				free(pkt->un.compdat.decbuf);
			break;
		case PGP_TAG_SYMENC_DATA:
		case PGP_TAG_SYMENC_MDC_DATA:
			if (pkt->un.symdat.pkt)
				pgp_freepkt(pkt->un.symdat.pkt);
			if (pkt->un.symdat.decbuf)
				free(pkt->un.symdat.decbuf);
			break;
		case PGP_TAG_LITERAL_DATA:
			if (pkt->un.litdat.name)
				free(pkt->un.litdat.name);
			break;
		case PGP_TAG_USER_ID:
			if (pkt->un.userid.user)
				free(pkt->un.userid.user);
			break;
		}
		if (pkt->pbuf)
			free(pkt->pbuf);
		free(pkt);
	}
}

static const EVP_CIPHER *
pgp_symalg_cipher(int alg)
{
	switch (alg) {
	case PGP_SYM_PLAIN:		return EVP_enc_null();
#ifdef USE_IDEA
	case PGP_SYM_IDEA:		return EVP_idea_cfb64();
#endif
	case PGP_SYM_DES3:		return EVP_des_ede3_cfb();
	case PGP_SYM_CAST5:		return EVP_cast5_cfb();
	case PGP_SYM_BLOWFISH:		return EVP_bf_cfb();
	case PGP_SYM_SAFER_SK128:	return NULL;
	case PGP_SYM_DES_SK:		return NULL;
#ifdef AES_BLOCK_SIZE
	case PGP_SYM_AES_128:		return EVP_aes_128_cfb();
	case PGP_SYM_AES_192:		return EVP_aes_192_cfb();
	case PGP_SYM_AES_256:		return EVP_aes_256_cfb();
#endif
	}
	return NULL;
}

static const EVP_MD *
pgp_hashalg_md(int alg)
{
	switch (alg) {
	case PGP_HASH_MD5:		return EVP_md5();
	case PGP_HASH_SHA1:		return EVP_sha1();
	case PGP_HASH_RIPEMD160:	return EVP_ripemd160();
	case PGP_HASH_DWSHA:		return NULL;
#ifndef OPENSSL_NO_MD2
	case PGP_HASH_MD2:		return EVP_md2();
#endif
	case PGP_HASH_TIGER192:		return NULL;
	case PGP_HASH_HAVAL_5_160:	return NULL;
	}
	return NULL;
}

static BIGNUM *
pgp_decrypt_mpi(EVP_CIPHER_CTX *ctx, const u_char **pp, u_short *sum)
{
	const u_char *p = *pp;
	int i, len;
	u_char buf[256], *b = buf;
	BIGNUM *n;

	*sum += p[0] + p[1];
	len = (GET2(p) + 7) / 8;
	*pp = p + len;
	if (len > sizeof(buf)) {
		if ((b = malloc(len)) == NULL)
			return NULL;
	}
	/* XXX: resync needed if not multiple 8 */
	EVP_DecryptUpdate(ctx, b, &len, p, len);
	for (i = 0; i < len; i++)
		*sum += b[i];
	n = BN_bin2bn(b, len, NULL);
	if (b != buf)
		free(b);
	return n;
}

static BIGNUM *
pgp_parse_mpi(const u_char **pp)
{
	const u_char *p = *pp;
	int len;
	BIGNUM *n;

	len = (GET2(p) + 7) / 8;
	n = BN_bin2bn(p, len, NULL);
	p += len;
	*pp = p;
	return n;
}

int
pgp_decrypt_seckey(struct pgp_pkt *pkt, int (*passwd_callback)(char *buf, int size, struct pgp_pkt *pkt))
{
	const EVP_CIPHER *cipher;
	const EVP_MD *md;
	EVP_CIPHER_CTX ctx;
	u_char mdbuf[EVP_MAX_MD_SIZE];
	u_int mdlen;
	EVP_MD_CTX md_ctx;
	u_char key[EVP_MAX_KEY_LENGTH];
	char pass[BUFSIZ];
	u_char iv[8];
	const u_char *p;
	u_char *buf;
	u_short sum;
	int i, off, len, plen, ret;
	BN_CTX *bn_ctx;
	BIGNUM *n, *v;
	RSA *rsa;
	DSA *dsa;
	DH *dh;

	if (pkt->tag != PGP_TAG_SECRET_KEY
	&&  pkt->tag != PGP_TAG_SECRET_SUBKEY)
		return -1;
	if (pkt->flags & PGP_FLAG_DECRYPTED)
		return 0;
	if ((cipher = pgp_symalg_cipher(pkt->un.pubkey.symalg)) == NULL)
		return -1;
	if (pkt->un.pubkey.symalg) {
		if ((md = pgp_hashalg_md(pkt->un.pubkey.hashalg)) == NULL)
			return -1;
		memcpy(iv, pkt->un.pubkey.iv, 8);
		len = plen = (*passwd_callback)(pass, sizeof(pass), pkt);
		if (pkt->un.pubkey.s2k & PGP_S2K_SALT)
			len += 8;
		if (pkt->un.pubkey.s2k & PGP_S2K_ITER) {
			if (len < pkt->un.pubkey.count)
				len = pkt->un.pubkey.count;
		}
		EVP_DigestInit(&md_ctx, md);
		for (off = 0; off < len; off += i) {
			if (pkt->un.pubkey.s2k & PGP_S2K_SALT) {
				i = off + 8 < len ? 8 : len - off;
				EVP_DigestUpdate(&md_ctx, pkt->un.pubkey.salt, i);
				off += i;
			}
			i = off + plen < len ? plen : len - off;
			EVP_DigestUpdate(&md_ctx, (u_char *)pass, i);
		}
		EVP_DigestFinal(&md_ctx, mdbuf, &mdlen);
		memcpy(key, mdbuf, EVP_CIPHER_key_length(cipher));
		memset(mdbuf, 0, sizeof(mdbuf));
	}
	EVP_DecryptInit(&ctx, cipher, key, iv);
	sum = 0;
	ret = -1;
	buf = NULL;
	v = NULL;
	if ((bn_ctx = BN_CTX_new()) == NULL)
		goto bad;
	if ((v = BN_new()) == NULL)
		goto bad;

	if (pkt->un.pubkey.ver < 4) {
		if (pkt->un.pubkey.pubalg != PGP_PUB_RSA)
			goto bad;
		rsa = ((EVP_PKEY *)pkt->un.pubkey.key)->pkey.rsa;
		p = pkt->dbuf;
		rsa->d = pgp_decrypt_mpi(&ctx, &p, &sum);
		rsa->p = pgp_decrypt_mpi(&ctx, &p, &sum);
		rsa->q = pgp_decrypt_mpi(&ctx, &p, &sum);
		/* XXX ignore u */
		n = pgp_decrypt_mpi(&ctx, &p, &sum);
		BN_free(n);
		if (sum != GET2(p))
			goto bad;
		goto rsacheck;
	}

	if ((buf = malloc(pkt->dlen)) == NULL)
		goto bad;
	EVP_DecryptUpdate(&ctx, buf, &len, pkt->dbuf, pkt->dlen);
	if (pkt->un.pubkey.sha1sum) {
		EVP_DigestInit(&md_ctx, EVP_sha1());
		EVP_DigestUpdate(&md_ctx, buf, len - 20);
		EVP_DigestFinal(&md_ctx, mdbuf, &mdlen);
		if (memcmp(mdbuf, buf + len - mdlen, mdlen) != 0)
			goto bad;
	} else {
		for (i = 0, p = buf, len -= 2; i < len; i++)
			sum += *p++;
		if (sum != GET2(p))
			goto bad;
	}
	p = buf;

	switch (pkt->un.pubkey.pubalg) {
	case PGP_PUB_RSA:
		rsa = ((EVP_PKEY *)pkt->un.pubkey.key)->pkey.rsa;
		rsa->d = pgp_parse_mpi(&p);
		rsa->p = pgp_parse_mpi(&p);
		rsa->q = pgp_parse_mpi(&p);
		/* XXX ignore u */
		n = pgp_parse_mpi(&p);
		BN_free(n);
  rsacheck:
		if (rsa->d == NULL || rsa->p == NULL || rsa->q == NULL)
			break;
		/* check validity */
		if (!BN_mul(v, rsa->p, rsa->q, bn_ctx)
		||  BN_ucmp(v, rsa->n) != 0)
			break;
		ret = 0;
		break;
	case PGP_PUB_DSA:
		dsa = ((EVP_PKEY *)pkt->un.pubkey.key)->pkey.dsa;
		dsa->priv_key = pgp_parse_mpi(&p);
		if (dsa->priv_key == NULL)
			goto bad;
		/* check validity */
		if (!BN_mod_exp(v, dsa->g, dsa->priv_key, dsa->p, bn_ctx)
		||  BN_ucmp(v, dsa->pub_key) != 0)
			break;
		ret = 0;
		break;
	case PGP_PUB_ELGAMAL_ENC:
		dh = ((EVP_PKEY *)pkt->un.pubkey.key)->pkey.dh;
		dh->priv_key = pgp_parse_mpi(&p);
		if (dh->priv_key == NULL)
			break;
		/* check validity */
		if (!BN_mod_exp(v, dh->g, dh->priv_key, dh->p, bn_ctx)
		||  BN_ucmp(v, dh->pub_key) != 0)
			break;
		ret = 0;
		break;
	default:
		break;
	}
	if (ret == 0)
		pkt->flags |= PGP_FLAG_DECRYPTED;
  bad:
	EVP_CIPHER_CTX_cleanup(&ctx);
	memset(key, 0, sizeof(key));
	if (buf)
		free(buf);
	if (v)
		BN_free(v);
	if (bn_ctx)
		BN_CTX_free(bn_ctx);
	return ret;
}

int
pgp_decrypt_seskey(struct pgp_pkt *pkt, struct pgp_pkt *seckey)
{
	int len;
	int i;
	const u_char *p;
	u_char *buf;
	u_short sum;
	EVP_PKEY *pkey;

	if ((seckey->tag != PGP_TAG_SECRET_KEY && seckey->tag != PGP_TAG_SECRET_SUBKEY)
	||  pkt->tag != PGP_TAG_PUBENC_SESKEY
	||  pkt->un.pubses.pubalg != seckey->un.pubkey.pubalg)
		return -1;
	if (memcmp(pkt->un.pubses.keyid, seckey->un.pubkey.keyid, 8) != 0
	&&  memcmp(pkt->un.pubses.keyid, pgp_nullkeyid, 8) != 0)
		return -1;
	p = pkt->dbuf;
	sum = 0;

	switch (pkt->un.pubses.pubalg) {
	case PGP_PUB_RSA:
		len = (GET2(p) + 7) / 8;
		if (pkt->dlen != len + 2)
			return -1;
		if ((buf = malloc(len)) == NULL)
			return -1;
		pkey = seckey->un.pubkey.key;
		len = RSA_private_decrypt(len, p, buf,
		    pkey->pkey.rsa, RSA_PKCS1_PADDING);
		break;
	case PGP_PUB_ELGAMAL_ENC:
	    {
		BIGNUM *yy, *k;
		BN_CTX *bn_ctx;
		DH *dh = ((EVP_PKEY *)seckey->un.pubkey.key)->pkey.dh;

		yy = pgp_parse_mpi(&p);
		k = pgp_parse_mpi(&p);
		if (yy == NULL || k == NULL) {
			if (yy)
				BN_free(yy);
			if (k)
				BN_free(k);
			return -1;
		}
		bn_ctx = BN_CTX_new();
		BN_mod_exp(yy, yy, dh->priv_key, dh->p, bn_ctx);
		BN_mod_inverse(yy, yy, dh->p, bn_ctx);
		BN_mod_mul(k, k, yy, dh->p, bn_ctx);
		len = BN_num_bytes(k);
		if ((buf = malloc(len)) == NULL)
			return -1;
		BN_bn2bin(k, buf);
		BN_CTX_free(bn_ctx);
		BN_free(k);
		BN_free(yy);
		/* padding */
		p = buf;
		if (*p++ != 2) {
			free(buf);
			return -1;
		}
		for (i = 1; i < len; i++) {
			if (*p++ == 0)
				break;
		}
		len -= i + 1;
		if (len < 0) {
			free(buf);
			return -1;
		}
		memmove(buf, p, len);
		break;
	    }
	default:
		return -1;
	}
	p = buf;
	for (i = 1, p = buf + 1; i < len - 2; i++)
		sum += *p++;
	if (sum != GET2(p)) {
		free(buf);
		return -1;
	}
	pkt->un.pubses.symalg = *buf;
	pkt->un.pubses.seskey = buf;
	pkt->un.pubses.seslen = len;
	return 0;
}

int
pgp_encrypt_seskey(struct pgp_pkt *pkt, struct pgp_pkt *pubkey)
{
	int len, ret;
	int i;
	u_char *p, *buf;
	BIGNUM *n;
	EVP_PKEY *pkey;

	if (pubkey->tag != PGP_TAG_PUBLIC_KEY
	&&  pubkey->tag != PGP_TAG_PUBLIC_SUBKEY
	&&  pubkey->tag != PGP_TAG_SECRET_KEY
	&&  pubkey->tag != PGP_TAG_SECRET_SUBKEY)
		return -1;

	if (pkt->tag != PGP_TAG_PUBENC_SESKEY)
		return -1;
	pkt->un.pubses.pubalg = pubkey->un.pubkey.pubalg;
	memcpy(pkt->un.pubses.keyid, pubkey->un.pubkey.keyid, 8);

	ret = -1;
	switch (pkt->un.pubses.pubalg) {
	case PGP_PUB_RSA:
		if ((buf = malloc(EVP_PKEY_size(pubkey->un.pubkey.key) + 2)) == NULL)
			break;
		pkey = pubkey->un.pubkey.key;
		len = RSA_public_encrypt(pkt->un.pubses.seslen,
		    pkt->un.pubses.seskey, buf + 2, pkey->pkey.rsa,
		    RSA_PKCS1_PADDING);
		n = BN_bin2bn(buf + 2, len, NULL);
		i = BN_num_bits(n);
		BN_free(n);
		p = buf;
		PUT2(p, i);
		pkt->dbuf = pkt->pbuf = buf;
		pkt->dlen = 2 + len;
		ret = 0;
		break;
	case PGP_PUB_ELGAMAL_ENC:
	    {
		BIGNUM yy, xx, k, z;
		BIGNUM *m;
		BN_CTX *bn_ctx;
		DH *dh = ((EVP_PKEY *)pubkey->un.pubkey.key)->pkey.dh;
		u_char *buf;

		len = BN_num_bytes(dh->p);
		if ((buf = malloc(len)) == NULL)
			return -1;
		p = buf;
		/* padding */
		*p++ = 0;
		*p++ = 2;
		i = len - pkt->un.pubses.seslen - 3;
		RAND_bytes(p, i);
		for (; i > 0; p++, i--) {
			while (*p == 0)
				RAND_bytes(p, 1);
		}
		*p++ = 0;
		memcpy(p, pkt->un.pubses.seskey, pkt->un.pubses.seslen);
		m = BN_bin2bn(buf, len, NULL);
		free(buf);
		if (m == NULL)
			return -1;

		BN_init(&yy); BN_init(&xx); BN_init(&k); BN_init(&z);
		i = BN_num_bits(dh->p) / 2;	/* XXX */
		bn_ctx = BN_CTX_new();
		if (BN_rand(&xx, i, 0, 1)
		&&  BN_mod_exp(&yy, dh->g, &xx, dh->p, bn_ctx)
		&&  BN_mod_exp(&z, dh->pub_key, &xx, dh->p, bn_ctx)
		&&  BN_mod_mul(&k, m, &z, dh->p, bn_ctx)
		&&  (buf = malloc(BN_num_bytes(&yy) + BN_num_bytes(&k) + 2)) != NULL) {
			p = buf;
			i = BN_num_bits(&yy);
			PUT2(p, i);
			p += BN_bn2bin(&yy, p);
			i = BN_num_bits(&k);
			PUT2(p, i);
			p += BN_bn2bin(&k, p);
			pkt->dbuf = buf;
			pkt->dlen = p - buf;
			ret = 0;
		}
		BN_CTX_free(bn_ctx);
		BN_free(&yy); BN_free(&xx); BN_free(&k); BN_free(&z);
		BN_free(m);
		break;
	    }
	default:
		break;
	}
	return ret;
}

int
pgp_generate_seskey(struct pgp_pkt *pkt)
{
	const EVP_CIPHER *cipher;
	int keylen;
	u_char *p;
	int len, i;
	u_short sum;

	if (pkt->tag != PGP_TAG_PUBENC_SESKEY
	||  (cipher = pgp_symalg_cipher(pkt->un.pubses.symalg)) == NULL)
		return -1;
	keylen = EVP_CIPHER_key_length(cipher);
	pkt->un.pubses.seslen = 1 + keylen + 2;
	if ((pkt->un.pubses.seskey = malloc(pkt->un.pubses.seslen)) == NULL)
		return -1;
	RAND_bytes(pkt->un.pubses.seskey + 1, keylen);

	p = pkt->un.pubses.seskey;
	len = pkt->un.pubses.seslen;
	*p++ = pkt->un.pubses.symalg;
	for (i = 1, sum = 0; i < len - 2; i++)
		sum += *p++;
	PUT2(p, sum);
	return 0;
}

int
pgp_decrypt_symdat(struct pgp_pkt *pkt, struct pgp_pkt *seskey)
{
	const EVP_CIPHER *cipher;
	EVP_CIPHER_CTX ctx;
	u_char iv[20], bs;
	u_char *p, *decbuf;
	int declen, tlen;
	u_int mdlen;
	EVP_MD_CTX md_ctx;

	if ((pkt->tag != PGP_TAG_SYMENC_DATA && pkt->tag != PGP_TAG_SYMENC_MDC_DATA)
	||  seskey->tag != PGP_TAG_PUBENC_SESKEY
	||  (cipher = pgp_symalg_cipher(seskey->un.pubses.symalg)) == NULL)
		return -1;

	memset(iv, 0, sizeof(iv));
	bs = EVP_CIPHER_iv_length(cipher);
	EVP_DecryptInit(&ctx, cipher, seskey->un.pubses.seskey + 1, iv);

	EVP_DecryptUpdate(&ctx, iv, &tlen, pkt->dbuf, bs + 2);
	if (iv[bs-2] != iv[bs] || iv[bs-1] != iv[bs+1]) {
		EVP_CIPHER_CTX_cleanup(&ctx);
		return -1;
	}

	/* XXX: resync */
	if (pkt->tag == PGP_TAG_SYMENC_DATA) {
		EVP_DecryptUpdate(&ctx, iv, &tlen, iv, bs - 2);
		memmove(ctx.iv + bs - 2, ctx.iv, 2);
		memcpy(ctx.iv, pkt->dbuf + 2, bs - 2);
	}

	if ((decbuf = malloc(pkt->dlen)) == NULL)
		return -1;
	EVP_DecryptUpdate(&ctx, decbuf, &declen, pkt->dbuf + (bs + 2), pkt->dlen - (bs + 2));
	pkt->un.symdat.decbuf = decbuf;
	pkt->un.symdat.declen = declen;
	EVP_CIPHER_CTX_cleanup(&ctx);

	if (pkt->tag == PGP_TAG_SYMENC_MDC_DATA) {
		p = decbuf + declen - 20;
		/* must be MDC */
		if (p[-2] != 0xd3 || p[-1] != 0x14) {
			free(decbuf);
			return -1;
		}
		EVP_DigestInit(&md_ctx, EVP_sha1());
		EVP_DigestUpdate(&md_ctx, iv, bs+2);
		EVP_DigestUpdate(&md_ctx, decbuf, declen - 20);
		EVP_DigestFinal(&md_ctx, iv, &mdlen);
		if (memcmp(iv, p, 20) != 0) {
			free(decbuf);
			return -1;
		}
	}

	p = decbuf;
	if ((pkt->un.symdat.pkt = pgp_parseall(p, p + declen)) == NULL)
		return -1;
	return 0;
}

int
pgp_encrypt_symdat(struct pgp_pkt *pkt, struct pgp_pkt *seskey)
{
	const EVP_CIPHER *cipher;
	EVP_CIPHER_CTX ctx;
	u_char iv[8], rand[16];
	u_char *encbuf;
	int enclen;

	if ((pkt->tag != PGP_TAG_SYMENC_DATA && pkt->tag != PGP_TAG_SYMENC_MDC_DATA)
	||  seskey->tag != PGP_TAG_PUBENC_SESKEY
	||  (cipher = pgp_symalg_cipher(seskey->un.pubses.symalg)) == NULL)
		return -1;
	pkt->un.symdat.decbuf = NULL;
	if (pgp_makepkt(pkt->un.symdat.pkt, &pkt->un.symdat.decbuf, &pkt->un.symdat.declen) < 0)
		return -1;

	memset(iv, 0, sizeof(iv));
	EVP_EncryptInit(&ctx, cipher, seskey->un.pubses.seskey + 1, iv);

	if ((encbuf = malloc(pkt->un.symdat.declen + 10)) == NULL)
		return -1;

	memset(rand, 0, sizeof(rand));
	RAND_bytes(rand, 8);
	rand[8] = rand[6];
	rand[9] = rand[7];

	EVP_EncryptUpdate(&ctx, encbuf, &enclen, rand, 16);	/* actually 10 */

	/* XXX: resync */
	memmove(ctx.iv + 6, ctx.iv, 2);
	memcpy(ctx.iv, encbuf + 2, 6);

	EVP_EncryptUpdate(&ctx, encbuf + 10, &enclen, pkt->un.symdat.decbuf, pkt->un.symdat.declen);
	pkt->dbuf = pkt->pbuf = encbuf;
	pkt->dlen = enclen + 10;
	EVP_CIPHER_CTX_cleanup(&ctx);
	return 0;
}

int
pgp_decomp_data(struct pgp_pkt *pkt)
{
	z_stream z;
	const u_char *p, *ep;
	u_char *q, *decbuf;
	int cc, size;

	if (pkt->tag != PGP_TAG_COMP_DATA)
		return -1;
	switch (pkt->un.compdat.compalg) {
	case PGP_COMP_NONE:
		p = pkt->dbuf;
		ep = p + pkt->dlen;
		break;
	case PGP_COMP_ZIP:
	case PGP_COMP_ZLIB:
		size = BUFSIZ;
		if ((decbuf = malloc(size)) == NULL)
			return -1;
		memset(&z, 0, sizeof(z));
		z.next_in = pkt->dbuf;
		z.avail_in = pkt->dlen + 4/*XXX*/;
		z.next_out = decbuf;
		z.avail_out = size;
		if (pkt->un.compdat.compalg == PGP_COMP_ZIP) {
			if (inflateInit2(&z, -15) != Z_OK)
				return -1;
		} else {
			if (inflateInit(&z) != Z_OK)
				return -1;
		}
		for (;;) {
			cc = inflate(&z, Z_SYNC_FLUSH);
			if (cc == Z_STREAM_END)
				break;
			if (cc < 0) {
				free(decbuf);
				return -1;
			}
			if (z.avail_out == 0) {
				q = realloc(decbuf, size + BUFSIZ);
				if (q == NULL) {
					inflateEnd(&z);
					free(decbuf);
					return -1;
				}
				decbuf = q;
				size += BUFSIZ;
				z.next_out = decbuf + z.total_out;
				z.avail_out = BUFSIZ;
			}
		}
		pkt->un.compdat.decbuf = decbuf;
		pkt->un.compdat.declen = z.total_out;
		inflateEnd(&z);

		p = pkt->un.compdat.decbuf;
		ep = p + pkt->un.compdat.declen;
		break;
	default:
		return -1;
	}
	if ((pkt->un.compdat.pkt = pgp_parseall(p, ep)) == NULL)
		return -1;
	return 0;
}

int
pgp_comp_data(struct pgp_pkt *pkt)
{
	z_stream z;
	u_char *p, *encbuf;
	int len;
	int cc, size;

	if (pkt->tag != PGP_TAG_COMP_DATA)
		return -1;
	p = NULL;
	if (pgp_makepkt(pkt->un.compdat.pkt, &p, &len) < 0)
		return -1;
	pkt->un.compdat.decbuf = p;
	pkt->un.compdat.declen = len;
	switch (pkt->un.compdat.compalg) {
	case PGP_COMP_NONE:
		pkt->dbuf = p;
		pkt->dlen = len;
		break;
	case PGP_COMP_ZIP:
		size = ((len + BUFSIZ - 1) / BUFSIZ) * BUFSIZ;
		if ((encbuf = malloc(size)) == NULL)
			return -1;
		memset(&z, 0, sizeof(z));
		z.next_in = p;
		z.avail_in = len;
		z.next_out = encbuf;
		z.avail_out = size;
		/* 13 for PGP2 ? */
		if (deflateInit2(&z, Z_DEFAULT_COMPRESSION, Z_DEFLATED, -13, 8, Z_DEFAULT_STRATEGY) != Z_OK)
			return -1;
		for (;;) {
			cc = deflate(&z, Z_FINISH);
			if (cc == Z_STREAM_END)
				break;
			if (cc < 0) {
				free(encbuf);
				return -1;
			}
			if (z.avail_out == 0) {
				p = realloc(encbuf, size + BUFSIZ);
				if (p == NULL) {
					deflateEnd(&z);
					free(encbuf);
					return -1;
				}
				encbuf = p;
				size += BUFSIZ;
				z.next_out = encbuf + z.total_out;
				z.avail_out = BUFSIZ;
			}
		}
		pkt->dbuf = pkt->pbuf = encbuf;
		pkt->dlen = z.total_out;
		inflateEnd(&z);
		break;
	case PGP_COMP_ZLIB:
		PGP_DPRINT(("TODO: %s: %s compression\n",
			pgp_show_tag(pkt->tag),
			pgp_show_compalg(pkt->un.compdat.compalg)));
		/* TODO */
	default:
		return -1;
	}
	return 0;
}

/* XXX: PARSE ASN1! */
static const EVP_MD *
pgp_asn1_md(const u_char **pp, int len)
{
#ifndef OPENSSL_NO_MD2
	static const u_char asn1_md2[] = {
		0x30, 0x20, 0x30, 0x0C, 0x06, 0x08, 0x2A, 0x86,
		0x48, 0x86, 0xF7, 0x0D, 0x02, 0x02, 0x05, 0x00,
		0x04, 0x10
	};
#endif
	static const u_char asn1_md5[] = {
		0x30, 0x20, 0x30, 0x0C, 0x06, 0x08, 0x2A, 0x86,
		0x48, 0x86, 0xF7, 0x0D, 0x02, 0x05, 0x05, 0x00,
		0x04, 0x10
	};
	static const u_char asn1_ripemd160[] = {
		0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2B, 0x24,
		0x03, 0x02, 0x01, 0x05, 0x00, 0x04, 0x14
	};
	static const u_char asn1_sha1[] = {
		0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0E,
		0x03, 0x02, 0x1A, 0x05, 0x00, 0x04, 0x14
	};

#ifndef OPENSSL_NO_MD2
	if (len > sizeof(asn1_md2)
	&&  memcmp(*pp, asn1_md2, sizeof(asn1_md2)) == 0) {
		*pp += sizeof(asn1_md2);
		return EVP_md2();
	}
#endif
	if (len > sizeof(asn1_md5)
	&&  memcmp(*pp, asn1_md5, sizeof(asn1_md5)) == 0) {
		*pp += sizeof(asn1_md5);
		return EVP_md5();
	}
	if (len > sizeof(asn1_ripemd160)
	&&  memcmp(*pp, asn1_ripemd160, sizeof(asn1_ripemd160)) == 0) {
		*pp += sizeof(asn1_ripemd160);
		return EVP_ripemd160();
	}
	if (len > sizeof(asn1_sha1)
	&&  memcmp(*pp, asn1_sha1, sizeof(asn1_sha1)) == 0) {
		*pp += sizeof(asn1_sha1);
		return EVP_sha1();
	}
	return NULL;
}

int
pgp_verify_sign(struct pgp_pkt *sign, struct pgp_pkt *pubkey)
{
	u_char *buf;
	int ret, len;
	const u_char *p;
	const EVP_MD *md;

	if (pubkey->tag != PGP_TAG_PUBLIC_KEY
	||  sign->tag != PGP_TAG_SIGN
	||  sign->un.sign.pubalg != pubkey->un.pubkey.pubalg)
		return -1;
	if (memcmp(sign->un.sign.keyid, pubkey->un.pubkey.keyid, 8) != 0
	&&  memcmp(sign->un.sign.keyid, pgp_nullkeyid, 8) != 0)
		return -1;
	p = sign->un.sign.mdbuf;
	if (p != NULL && GET2(p) != sign->un.sign.hash)
		return -1;

	p = sign->dbuf;
	buf = NULL;
	ret = -1;
	switch (sign->un.sign.pubalg) {
	case PGP_PUB_RSA:
		len = (GET2(p) + 7) / 8;
		if (sign->dlen != len + 2)
			break;
		if ((buf = malloc(len)) == NULL)
			break;
		len = RSA_public_decrypt(len, p, buf,
				 ((EVP_PKEY *)pubkey->un.pubkey.key)->pkey.rsa,
				 RSA_PKCS1_PADDING);
		if (len <= 0)
			break;
		p = buf;
		if (sign->un.sign.mdbuf == NULL) {
			if ((sign->un.sign.pkt = pgp_parseall(p, p + len)) != NULL)
				ret = 0;
			break;
		}
		md = pgp_asn1_md(&p, len);
		if (md == NULL || md != pgp_hashalg_md(sign->un.sign.hashalg))
			break;
		len -= (p - buf);
		if (len == sign->un.sign.mdlen
		&&  memcmp(p, sign->un.sign.mdbuf, len) == 0)
			ret = 0;
		break;
	case PGP_PUB_DSA:
	    {
		DSA *dsa;
		BIGNUM *r, *s;
		BIGNUM *h;
		BIGNUM w, u1, u2, v;
		BN_CTX *bn_ctx;

		dsa = ((EVP_PKEY *)pubkey->un.pubkey.key)->pkey.dsa;
		r = pgp_parse_mpi(&p);
		s = pgp_parse_mpi(&p);
		h = BN_bin2bn(sign->un.sign.mdbuf, sign->un.sign.mdlen, NULL);
		if (dsa->p == NULL || dsa->q == NULL || dsa->g == NULL ||
		    dsa->pub_key == NULL || r == NULL || s == NULL || h == NULL)
			goto dsa_err;
		bn_ctx = BN_CTX_new();
		BN_init(&w); BN_init(&u1); BN_init(&u2); BN_init(&v);
		if (BN_mod_inverse(&w, s, dsa->q, bn_ctx)
		&&  BN_mod_mul(&u1, h, &w, dsa->q, bn_ctx)
		&&  BN_mod_mul(&u2, r, &w, dsa->q, bn_ctx)
		&&  BN_mod_exp2_mont(&v, dsa->g, &u1, dsa->pub_key, &u2,
					dsa->p, bn_ctx, NULL)
		&&  BN_mod(&v, &v, dsa->q, bn_ctx)
		&&  BN_ucmp(&v, r) == 0)
			ret = 0;
		BN_CTX_free(bn_ctx);
		BN_free(&w); BN_free(&u1); BN_free(&u2); BN_free(&v);
  dsa_err:
		if (r) BN_free(r);
		if (s) BN_free(s);
		if (h) BN_free(h);
		break;
	    }
	default:
		break;
	}
	if (buf)
		free(buf);
	return ret;
}

int
pgp_sign_sign(struct pgp_pkt *sign, struct pgp_pkt *seckey)
{
	u_char *p;
	u_char *buf, *sbuf;
	int ret, len;
	static const u_char asn1_md5[] = {
		0x30, 0x20, 0x30, 0x0C, 0x06, 0x08, 0x2A, 0x86,
		0x48, 0x86, 0xF7, 0x0D, 0x02, 0x05, 0x05, 0x00,
		0x04, 0x10
	};

	if (seckey->tag != PGP_TAG_SECRET_KEY
	||  sign->tag != PGP_TAG_SIGN
	||  sign->un.sign.pubalg != seckey->un.pubkey.pubalg
	||  memcmp(sign->un.sign.keyid, seckey->un.pubkey.keyid, 8) != 0)
		return -1;
	p = sign->un.sign.mdbuf;
	sign->un.sign.hash = GET2(p);
	sbuf = buf = NULL;
	ret = -1;
	switch (sign->un.sign.pubalg) {
	case PGP_PUB_RSA:
	    {
		RSA *rsa = ((EVP_PKEY *)seckey->un.pubkey.key)->pkey.rsa;
		BIGNUM *n;

		if (sign->un.sign.hashalg != PGP_HASH_MD5)
			break;
		len = sign->un.sign.mdlen;
		if ((buf = malloc(len + sizeof(asn1_md5))) == NULL)
			break;
		memcpy(buf, asn1_md5, sizeof(asn1_md5));
		memcpy(buf + sizeof(asn1_md5), sign->un.sign.mdbuf, len);
		len += sizeof(asn1_md5);
		if ((sbuf = malloc(BN_num_bytes(rsa->n) + 2)) == NULL)
			break;
		len = RSA_private_encrypt(len, buf, sbuf + 2, rsa,
				 RSA_PKCS1_PADDING);
		if (len <= 0) {
			free(sbuf);
			break;
		}
		sign->dbuf = sign->pbuf = sbuf;
		sign->dlen = len + 2;
		n = BN_bin2bn(sbuf + 2, len, NULL);
		len = BN_num_bits(n);
		BN_free(n);
		p = sbuf;
		PUT2(p, len);
		ret = 0;
		break;
	    }
	case PGP_PUB_DSA:
	    {
		DSA *dsa = ((EVP_PKEY *)seckey->un.pubkey.key)->pkey.dsa;
		BIGNUM *kinv, *r, s, h;
		BN_CTX *bn_ctx;

		bn_ctx = BN_CTX_new();
		kinv = NULL; r = NULL;
		BN_init(&s); BN_init(&h);
		if (DSA_sign_setup(dsa, bn_ctx, &kinv, &r)
		&&  BN_bin2bn(sign->un.sign.mdbuf, sign->un.sign.mdlen, &h)
		&&  BN_mod_mul(&s, dsa->priv_key, r, dsa->q, bn_ctx)
		&&  BN_add(&s, &s, &h)
		&&  (BN_cmp(&s, dsa->q) < 0 || BN_sub(&s, &s, dsa->q))
		&&  BN_mod_mul(&s, &s, kinv, dsa->q, bn_ctx)) {
			len = BN_num_bytes(r) + BN_num_bytes(&s) + 4;
			if ((sbuf = malloc(len)) != NULL) {
				sign->dbuf = sign->pbuf = sbuf;
				sign->dlen = len;
				p = sbuf;
				len = BN_num_bits(r);
				PUT2(p, len);
				p += BN_bn2bin(r, p);
				len = BN_num_bits(&s);
				PUT2(p, len);
				p += BN_bn2bin(&s, p);
				ret = 0;
			}
		}
		BN_free(kinv); BN_free(r); BN_free(&s); BN_free(&h);
		BN_CTX_free(bn_ctx);
		break;
	    }
	default:
		break;
	}
	if (buf)
		free(buf);
	return ret;
}

static void
pgp_crc24(u_char *p, int len, long *crcp)
{
	int i;
	long crc;

	crc = *crcp;
	if (crc < 0)
		crc = 0xb704ce;
	while (len--) {
		crc ^= (*p++) << 16;
		for (i = 0; i < 8; i++) {
			crc <<= 1;
			if (crc & 0x1000000)
				crc ^= 0x1864cfb;
		}
	}
	*crcp = crc & 0xffffff;
}

struct armor_ctx {
	FILE	*fp;
	int	w;
	long	crc;
	u_long	data;
	char	*type;
#ifdef PGPMIME_DEBUG
	FILE	*dfp;
#endif /* PGPMIME_DEBUG */
};

#ifdef CUE
extern char b64[];
#else
const static char b64[] =
	"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
#endif

static void
pgp_armor_init(struct armor_ctx *ctx, FILE *fp, int tag, char *header)
{

	ctx->fp = fp;
	ctx->w = 0;
	ctx->crc = -1;
	ctx->data = 0;
	switch (tag) {
	case PGP_TAG_SIGN:		ctx->type = "SIGNATURE";	break;
	case PGP_TAG_PUBLIC_KEY:	ctx->type = "PUBLIC KEY BLOCK";	break;
	default:			ctx->type = "MESSAGE";		break;
	}
	fprintf(fp, "-----BEGIN PGP %s-----\n", ctx->type);
	if (header)
		fprintf(fp, "%s\n", header);
	putc('\n', fp);
#ifdef PGPMIME_DEBUG
	ctx->dfp = fopen("/tmp/s.pgp", "w");
#endif /* PGPMIME_DEBUG */
}

static void
pgp_armor_update(struct armor_ctx *ctx, u_char *p, int len)
{
	int i;

#ifdef PGPMIME_DEBUG
	if (ctx->dfp) fwrite(p, len, 1, ctx->dfp);
#endif /* PGPMIME_DEBUG */
	pgp_crc24(p, len, &ctx->crc);
	for (i = 0; i < len; i++) {
		ctx->data |= *p++ << (8 * (2 - ctx->w % 3));
		if (++ctx->w % 3 == 0) {
			putc(b64[(ctx->data >> 18) & 0x3f], ctx->fp);
			putc(b64[(ctx->data >> 12) & 0x3f], ctx->fp);
			putc(b64[(ctx->data >>  6) & 0x3f], ctx->fp);
			putc(b64[ ctx->data        & 0x3f], ctx->fp);
			ctx->data = 0;
			if (ctx->w >= 48) {
				putc('\n', ctx->fp);
				ctx->w = 0;
			}
		}
	}
}

static void
pgp_armor_final(struct armor_ctx *ctx)
{
	u_char *p;
	u_char sum[4];

#ifdef PGPMIME_DEBUG
	fclose(ctx->dfp);
	ctx->dfp = NULL;
#endif /* PGPMIME_DEBUG */
	switch (ctx->w % 3) {
	case 0:
		break;
	case 1:
		putc(b64[(ctx->data >> 18) & 0x3f], ctx->fp);
		putc(b64[(ctx->data >> 12) & 0x3f], ctx->fp);
		putc('=', ctx->fp);
		putc('=', ctx->fp);
		break;
	case 2:
		putc(b64[(ctx->data >> 18) & 0x3f], ctx->fp);
		putc(b64[(ctx->data >> 12) & 0x3f], ctx->fp);
		putc(b64[(ctx->data >>  6) & 0x3f], ctx->fp);
		putc('=', ctx->fp);
		break;
	}
	fprintf(ctx->fp, "\n=");
	p = sum;
	PUT4(p, ctx->crc);
	ctx->w = 0;
	ctx->data = 0;
	pgp_armor_update(ctx, sum + 1, 3);
	fprintf(ctx->fp, "\n-----END PGP %s-----\n", ctx->type);
}

int
pgp_armor_all(FILE *fp, struct pgp_pkt *pkt, char *header)
{
	struct armor_ctx ctx;
	u_char *buf;
	int len, ret;

	ret = 0;
	pgp_armor_init(&ctx, fp, pkt->tag, header);
	for (; pkt; pkt = pkt->next) {
		buf = NULL;
		if (pgp_makepkt(pkt, &buf, &len) < 0) {
			ret = -1;
			break;
		}
		pgp_armor_update(&ctx, buf, len);
		free(buf);
	}
	pgp_armor_final(&ctx);
#ifdef PGPMIME_DEBUG_XXX
    {
	/* XXX */
	FILE bak;
	bak = *stdout;
	*stdout = *fp;
	pgp_showpkt(pkt);
	*fp = *sTdout;
	*stdout = bak;
    }
#endif/* PTPGMIME_DEBUG */
	return ret;
}

int
pgp_hash_data_init(struct pgp_pkt *sign)
{
	const EVP_MD *md;
	EVP_MD_CTX *ctx;

	if ((md = pgp_hashalg_md(sign->un.sign.hashalg)) == NULL)
		return -1;
	if ((ctx = malloc(sizeof(*ctx))) == NULL)
		return -1;
	EVP_DigestInit(ctx, md);
	if (sign->un.sign.hctx)
		free(sign->un.sign.hctx);
	sign->un.sign.hctx = ctx;
	return 0;
}

int
pgp_hash_data_update(const char *p, const char *ep, struct pgp_pkt *sign, int legacy)
{
	const char *s, *q;
	int len;
	EVP_MD_CTX *ctx;

	if ((ctx = sign->un.sign.hctx) == NULL)
		return -1;
	for (; p < ep; p++) {
		for (s = p; p < ep && *p != '\n'; p++)
			;
		len = p - s;
		if (legacy) {
			if (p >= s + 2 && s[0] == '-' && s[1] == ' ') {
				s += 2;
				len -= 2;
			}
			for (q = p; --q >= s; len--) {
				if (*q != ' ')
					break;
			}
		}
		if (len)
			EVP_DigestUpdate(ctx, (u_char *)s, len);
		if (p == ep)
			break;
		if (p == s || p[-1] != '\r')
			EVP_DigestUpdate(ctx, (u_char *)"\r\n", 2);
		else
			EVP_DigestUpdate(ctx, (u_char *)"\n", 1);
	}
	return 0;
}

int
pgp_hash_data_final(struct pgp_pkt *sign)
{
	u_char mdbuf[EVP_MAX_MD_SIZE];
	u_int mdlen;
	u_char *p;
	u_char extra[6];
	EVP_MD_CTX *ctx;

	if ((ctx = sign->un.sign.hctx) == NULL)
		return -1;
	if (sign->un.sign.ver < 4) {
		extra[0] = sign->un.sign.type;
		p = &extra[1];
		PUT4(p, sign->un.sign.ctime);
		EVP_DigestUpdate(ctx, extra, 5);
	} else {
		if (sign->un.sign.hdbuf == NULL) {
			if (pgp_setup_subpkt(sign) < 0)
				return -1;
		}
		mdlen = (u_long)sign->un.sign.hdlen;
		EVP_DigestUpdate(ctx, sign->un.sign.hdbuf, mdlen);
		extra[0] = sign->un.sign.ver;
		extra[1] = 0xff;
		p = &extra[2];
		PUT4(p, mdlen);
		EVP_DigestUpdate(ctx, extra, 6);
	}
	EVP_DigestFinal(ctx, mdbuf, &mdlen);
	sign->un.sign.hctx = NULL;
	free(ctx);
	if (sign->un.sign.mdbuf)
		free(sign->un.sign.mdbuf);
	if ((sign->un.sign.mdbuf = malloc(mdlen)) == NULL)
		return -1;
	memcpy(sign->un.sign.mdbuf, mdbuf, mdlen);
	sign->un.sign.mdlen = mdlen;
	return 0;
}

#if defined(PGP_DEBUG) || defined(PGPMIME_DEBUG)
static char *
pgp_show_tag(int tag)
{
	static char buf[40];

	switch (tag) {
	case PGP_TAG_PUBENC_SESKEY:  return "public-key encrypted session packet";
	case PGP_TAG_SIGN:           return "signature packet";
	case PGP_TAG_SYMENC_SESKEY:  return "symmetric-key encrypted session packet";
	case PGP_TAG_ONEPASS_SIGN:   return "one-pass signature packet";
	case PGP_TAG_SECRET_KEY:     return "secret-key packet";
	case PGP_TAG_PUBLIC_KEY:     return "public-key packet";
	case PGP_TAG_SECRET_SUBKEY:  return "secret subkey packet";
	case PGP_TAG_COMP_DATA:      return "compressed data packet";
	case PGP_TAG_SYMENC_DATA:    return "symmetrically encrypted data packet";
	case PGP_TAG_MARKER:         return "marker packet";
	case PGP_TAG_LITERAL_DATA:   return "literal data packet";
	case PGP_TAG_TRUST:          return "trust packet";
	case PGP_TAG_USER_ID:        return "user id packet";
	case PGP_TAG_PUBLIC_SUBKEY:  return "public subkey packet";
	case PGP_TAG_USER_ATTR:      return "user attribute packet";
	case PGP_TAG_SYMENC_MDC_DATA:return "symmetrically encrypted integrity protected data packet";
	case PGP_TAG_MDC:            return "modification detection code packet";
	}
	snprintf(buf, sizeof(buf), "unknown tag (%d)", tag);
	return buf;
}

static char *
pgp_show_pubalg(int alg)
{
	static char buf[40];

	switch (alg) {
	case PGP_PUB_RSA:		return "RSA (Encrypt or Sign)";
	case PGP_PUB_RSA_ENC:		return "RSA Encrypt-Only";
	case PGP_PUB_RSA_SIG:		return "RSA Sign-Only";
	case PGP_PUB_ELGAMAL_ENC:	return "Elgamal (Encrypt-Only)";
	case PGP_PUB_DSA:		return "DSA";
	case PGP_PUB_E_CURVE:		return "Elliptic Curve";
	case PGP_PUB_ECDSA:		return "ECDSA";
	case PGP_PUB_ELGAMAL:		return "Elgamal (Encrypt or Sign)";
	case PGP_PUB_DIFFIE_HELLMAN:	return "Diffie-Hellman";
	}
	snprintf(buf, sizeof(buf), "unknown pubalg (%d)", alg);
	return buf;
}

static char *
pgp_show_symalg(int alg)
{
	static char buf[40];

	switch (alg) {
	case PGP_SYM_PLAIN:		return "PLAIN";
	case PGP_SYM_IDEA:		return "IDEA";
	case PGP_SYM_DES3:		return "DES3";
	case PGP_SYM_CAST5:		return "CAST5";
	case PGP_SYM_BLOWFISH:		return "BLOWFISH";
	case PGP_SYM_SAFER_SK128:	return "SAFER_SK128";
	case PGP_SYM_DES_SK:		return "DES_SK";
	case PGP_SYM_AES_128:		return "AES_128";
	case PGP_SYM_AES_192:		return "AES_192";
	case PGP_SYM_AES_256:		return "AES_256";
	}
	snprintf(buf, sizeof(buf), "unknown symalg (%d)", alg);
	return buf;
}

static char *
pgp_show_hashalg(int alg)
{
	static char buf[40];

	switch (alg) {
	case PGP_HASH_MD5:		return "MD5";
	case PGP_HASH_SHA1:		return "SHA1";
	case PGP_HASH_RIPEMD160:	return "RIPEMD160";
	case PGP_HASH_DWSHA:		return "DWSHA";
#ifndef OPENSSL_NO_MD2
	case PGP_HASH_MD2:		return "MD2";
#endif
	case PGP_HASH_TIGER192:		return "TIGER192";
	case PGP_HASH_HAVAL_5_160:	return "HAVAL-5-160";
	}
	snprintf(buf, sizeof(buf), "unknown hashalg (%d)", alg);
	return buf;
}

static char *
pgp_show_compalg(int alg)
{
	static char buf[40];

	switch (alg) {
	case PGP_COMP_NONE:		return "NONE";
	case PGP_COMP_ZIP:		return "ZIP";
	case PGP_COMP_ZLIB:		return "ZLIB";
	}
	snprintf(buf, sizeof(buf), "unknown compalg (%d)", alg);
	return buf;
}

static char *
pgp_show_signtype(int type)
{
	static char buf[40];

	switch (type) {
	case PGP_SIG_BINDOC:		return "Signature of a binary document";
	case PGP_SIG_TEXT:		return "Signature of a canonical text document";
	case PGP_SIG_STANDALONE:	return "Standalone signature";
	case PGP_SIG_GEN_CERT:		return "Generic certification of a User ID and Public Key packet";
	case PGP_SIG_PERSONA_CERT:	return "Persona certification of a User ID and Public Key packet";
	case PGP_SIG_CASUAL_CERT:	return "Casual certification of a User ID and Public Key packet";
	case PGP_SIG_POSITIVE_CERT:	return "Positive certification of a User ID and Public Key packet";
	case PGP_SIG_SUBKEY_BIND:	return "Subkey Binding Signature";
	case PGP_SIG_ONKEY:		return "Signature directly on a key";
	case PGP_SIG_REVOKE_KEY:	return "Key revocation signature";
	case PGP_SIG_REVOKE_SUBKEY:	return "Subkey revocation signature";
	case PGP_SIG_REVOKE_CERT:	return "Certification revocation signature";
	case PGP_SIG_STAMP:		return "Timestamp signature";
	}
	snprintf(buf, sizeof(buf), "unknown signtype (%d)", type);
	return buf;
}

static char *
pgp_show_keyid(const u_char *p)
{
	int i;
	char *s;
	static char buf[40];
	const static char hex[] = "0123456789abcdef";

	for (i = 0, s = buf; i < 8; i++) {
		if (i)
			*s++ = ' ';
		*s++ = hex[((u_char *)p)[i] >> 4];
		*s++ = hex[((u_char *)p)[i] & 3];
	}
	*s = '\0';
	return buf;
}

static void
pgp_show_subpkt(u_char *p)
{
	u_char *ep, *np;
	char c;
	int plen;

	plen = GET2(p);
	for (ep = p + plen; p < ep; p = np) {
		plen = *p++;
		if (plen == 255)		/* five octet length */
			plen = GET4(p);
		else if (plen >= 192)
			plen = ((plen - 192) << 8) + *p++ + 192;
		np = p + plen;
		if (*p & PGP_SIGSUB_CRITICAL)
			printf(" (CRIT)");
		switch (*p++ & 0x7f) {
		case PGP_SIGSUB_SIGCREATE:	printf(" SIGCREATE");	break;
		case PGP_SIGSUB_SIGEXPIRE:	printf(" SIGEXPIRE");	break;
		case PGP_SIGSUB_CERT:		printf(" CERT");	break;
		case PGP_SIGSUB_TRUST:		printf(" TRUST");	break;
		case PGP_SIGSUB_REGEX:		printf(" REGEX");	break;
		case PGP_SIGSUB_REVOCABLE:	printf(" REVOCABLE");	break;
		case PGP_SIGSUB_KEYEXPIRE:	printf(" KEYEXPIRE");	break;
		case PGP_SIGSUB_PREF_SYM:	printf(" PREF_SYM");
			for (c = '('; p < np; c = ',')
				printf("%c%s", c, pgp_show_symalg(*p++));
			printf(")");
			break;
		case PGP_SIGSUB_REV_KEY:	printf(" REV_KEY");	break;
		case PGP_SIGSUB_ISSUER_ID:	printf(" ISSUER_ID");	break;
		case PGP_SIGSUB_NOTE:		printf(" NOTE");	break;
		case PGP_SIGSUB_PREF_HASH:	printf(" PREF_HASH");
			for (c = '('; p < np; c = ',')
				printf("%c%s", c, pgp_show_hashalg(*p++));
			printf(")");
			break;
		case PGP_SIGSUB_PREF_COMP:	printf(" PREF_COMP");
			for (c = '('; p < np; c = ',')
				printf("%c%s", c, pgp_show_compalg(*p++));
			printf(")");
			break;
		case PGP_SIGSUB_KSERV_PREF:	printf(" KSERV_PREF");
			switch (*p++) {
			case 0x00: printf("(none)"); break;
			case 0x80: printf("(nomod)"); break;
			default: printf("(%d)", p[-1]); break;
			}
			break;
		case PGP_SIGSUB_PREF_KSERV:	printf(" PREF_KSERV");	break;
		case PGP_SIGSUB_PRIM_USERID:	printf(" PRIM_USERID");	break;
		case PGP_SIGSUB_POLICY_URL:	printf(" POLICY_URL");	break;
		case PGP_SIGSUB_KEY_FLAGS:	printf(" KEY_FLAGS");	break;
		case PGP_SIGSUB_SIGN_USERID:	printf(" SIGN_USERID");	break;
		case PGP_SIGSUB_REASON_REV:	printf(" REASON_REV");	break;
		default:	printf(" #%d", p[-1]); break;
		}
	}
}

void
pgp_showpkt(struct pgp_pkt *pkt)
{
	int i;

	printf("%s", pgp_show_tag(pkt->tag));
	if (pkt->flags & PGP_FLAG_NEW)
		printf(" (new)");
	printf("\n");
	switch (pkt->tag) {
	case PGP_TAG_PUBENC_SESKEY:
		printf("\tversion: %d\n", pkt->un.pubses.ver);
		printf("\tkeyid: %s\n", pgp_show_keyid(pkt->un.pubses.keyid));
		printf("\tpublic key algorithm: %s\n", pgp_show_pubalg(pkt->un.pubses.pubalg));
		break;
	case PGP_TAG_SIGN:
		printf("\tversion: %d\n", pkt->un.sign.ver);
		printf("\ttype: %s\n", pgp_show_signtype(pkt->un.sign.type));
		printf("\tctime: %s", ctime(&pkt->un.sign.ctime));
		if (pkt->un.sign.exptime) {
			time_t tmptime =
			    pkt->un.sign.ctime + pkt->un.sign.exptime;
			printf("\texptime: %s", ctime(&tmptime));
		}
		printf("\tkeyid: %s\n", pgp_show_keyid(pkt->un.sign.keyid));
		printf("\tpublic key algorithm: %s\n", pgp_show_pubalg(pkt->un.sign.pubalg));
		printf("\thash algorithm: %s\n", pgp_show_hashalg(pkt->un.sign.hashalg));
		printf("\thash: %04x\n", pkt->un.sign.hash);
		if (pkt->un.sign.hdbuf) {
			printf("\thashed subpacket:");
			pgp_show_subpkt(pkt->un.sign.hdbuf + 4);
			printf("\n\tunhashed subpacket:");
			pgp_show_subpkt(pkt->un.sign.hdbuf + pkt->un.sign.hdlen);
			printf("\n");
		}
		break;
	case PGP_TAG_SYMENC_SESKEY: break;
	case PGP_TAG_ONEPASS_SIGN:
		printf("\tversion: %d\n", pkt->un.opsign.ver);
		printf("\ttype: %s\n", pgp_show_signtype(pkt->un.opsign.type));
		printf("\tkeyid: %s\n", pgp_show_keyid(pkt->un.opsign.keyid));
		printf("\thash algorithm: %s\n", pgp_show_hashalg(pkt->un.opsign.hashalg));
		printf("\tpublic key algorithm: %s\n", pgp_show_pubalg(pkt->un.opsign.pubalg));
		printf("\tnested flag: %d\n", pkt->un.opsign.nestflg);
		break;
	case PGP_TAG_SECRET_KEY:
	case PGP_TAG_PUBLIC_KEY:
	case PGP_TAG_SECRET_SUBKEY:
	case PGP_TAG_PUBLIC_SUBKEY:
		printf("\tversion: %d\n", pkt->un.pubkey.ver);
		printf("\ttstamp: %s", ctime((time_t *)&pkt->un.pubkey.tstamp));
		printf("\texpire: ");
		if (pkt->un.pubkey.expire)
			printf("%d days\n", pkt->un.pubkey.expire);
		else
			printf("never\n");
		printf("\tpublic key algorithm: %s\n", pgp_show_pubalg(pkt->un.pubkey.pubalg));
		printf("\tkeyid: %s\n", pgp_show_keyid(pkt->un.pubkey.keyid));
		if (pkt->tag == PGP_TAG_PUBLIC_KEY
		||  pkt->tag == PGP_TAG_PUBLIC_SUBKEY)
			break;
		printf("\tencryption algorithm: %s\n", pgp_show_symalg(pkt->un.pubkey.symalg));
		if (pkt->un.pubkey.symalg)
			printf("\tencryption iv: %s\n", pgp_show_keyid(pkt->un.pubkey.iv));
		printf("\tencryption hashalg: %s\n", pgp_show_hashalg(pkt->un.pubkey.hashalg));
		if (pkt->un.pubkey.s2k & PGP_S2K_SALT)
			printf("\tencryption salt: %s\n", pgp_show_keyid(pkt->un.pubkey.salt));
		if (pkt->un.pubkey.s2k & PGP_S2K_ITER)
			printf("\tencryption count: %d\n", pkt->un.pubkey.count);
#if 0
		RSA_print_fp(stdout, ((EVP_PKEY *)pkt->un.pubkey.key)->pkey.rsa, 8);
#endif
		break;
	case PGP_TAG_COMP_DATA:
		printf("\tcompression algorithm: %s\n", pgp_show_compalg(pkt->un.compdat.compalg));
		break;
	case PGP_TAG_SYMENC_DATA: break;
	case PGP_TAG_SYMENC_MDC_DATA: break;
	case PGP_TAG_MARKER: break;
	case PGP_TAG_LITERAL_DATA:
		printf("\ttype: %c\n", pkt->un.litdat.type);
		printf("\tname: %s\n", pkt->un.litdat.name);
		if (pkt->un.litdat.mtime)
			printf("\ttstamp: %s", ctime(&pkt->un.litdat.mtime));
		printf("\tdata: (%u)\n\"", pkt->dlen);
		fwrite(pkt->dbuf, pkt->dlen, 1, stdout);
		printf("\"\n");
		break;
	case PGP_TAG_TRUST: break;
	case PGP_TAG_USER_ID:
		printf("\tuser: %s\n", pkt->un.userid.user);
		break;
	}
	if (pkt->dlen) {
		printf("\tunparsed: (%d)", pkt->dlen);
		for (i = 0; i < (pkt->dlen > 16 ? 16 : pkt->dlen); i++)
			printf(" %02x", pkt->dbuf[i]);
		printf("\n");
	}
}
#endif /* PGP_DEBUG || PGPMIME_DEBUG */
