summaryrefslogtreecommitdiff
path: root/contrib/pgcrypto/pgp-decrypt.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/pgcrypto/pgp-decrypt.c')
-rw-r--r--contrib/pgcrypto/pgp-decrypt.c1156
1 files changed, 1156 insertions, 0 deletions
diff --git a/contrib/pgcrypto/pgp-decrypt.c b/contrib/pgcrypto/pgp-decrypt.c
new file mode 100644
index 0000000000..edffc6a344
--- /dev/null
+++ b/contrib/pgcrypto/pgp-decrypt.c
@@ -0,0 +1,1156 @@
+/*
+ * pgp-decrypt.c
+ * OpenPGP decrypt.
+ *
+ * Copyright (c) 2005 Marko Kreen
+ * 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 AUTHOR 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 AUTHOR 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.
+ *
+ * $PostgreSQL: pgsql/contrib/pgcrypto/pgp-decrypt.c,v 1.1 2005/07/10 13:46:28 momjian Exp $
+ */
+
+#include <postgres.h>
+
+#include "px.h"
+#include "mbuf.h"
+#include "pgp.h"
+
+#define NO_CTX_SIZE 0
+#define ALLOW_CTX_SIZE 1
+#define NO_COMPR 0
+#define ALLOW_COMPR 1
+#define NO_MDC 0
+#define NEED_MDC 1
+
+#define PKT_NORMAL 1
+#define PKT_STREAM 2
+#define PKT_CONTEXT 3
+
+#define MAX_CHUNK (16*1024*1024)
+
+static int
+parse_new_len(PullFilter * src, int *len_p)
+{
+ uint8 b;
+ int len;
+ int pkttype = PKT_NORMAL;
+
+ GETBYTE(src, b);
+ if (b <= 191)
+ len = b;
+ else if (b >= 192 && b <= 223)
+ {
+ len = ((unsigned) (b) - 192) << 8;
+ GETBYTE(src, b);
+ len += 192 + b;
+ }
+ else if (b == 255)
+ {
+ GETBYTE(src, b);
+ len = b;
+ GETBYTE(src, b);
+ len = (len << 8) | b;
+ GETBYTE(src, b);
+ len = (len << 8) | b;
+ GETBYTE(src, b);
+ len = (len << 8) | b;
+ }
+ else
+ {
+ len = 1 << (b & 0x1F);
+ pkttype = PKT_STREAM;
+ }
+
+ if (len < 0 || len > MAX_CHUNK)
+ {
+ px_debug("parse_new_len: weird length");
+ return PXE_PGP_CORRUPT_DATA;
+ }
+
+ *len_p = len;
+ return pkttype;
+}
+
+static int
+parse_old_len(PullFilter * src, int *len_p, int lentype)
+{
+ uint8 b;
+ int len;
+
+ GETBYTE(src, b);
+ len = b;
+
+ if (lentype == 1)
+ {
+ GETBYTE(src, b);
+ len = (len << 8) | b;
+ }
+ else if (lentype == 2)
+ {
+ GETBYTE(src, b);
+ len = (len << 8) | b;
+ GETBYTE(src, b);
+ len = (len << 8) | b;
+ GETBYTE(src, b);
+ len = (len << 8) | b;
+ }
+
+ if (len < 0 || len > MAX_CHUNK)
+ {
+ px_debug("parse_old_len: weird length");
+ return PXE_PGP_CORRUPT_DATA;
+ }
+ *len_p = len;
+ return PKT_NORMAL;
+}
+
+/* returns pkttype or 0 on eof */
+int
+pgp_parse_pkt_hdr(PullFilter * src, uint8 *tag, int *len_p, int allow_ctx)
+{
+ int lentype;
+ int res;
+ uint8 *p;
+
+ /* EOF is normal here, thus we dont use GETBYTE */
+ res = pullf_read(src, 1, &p);
+ if (res < 0)
+ return res;
+ if (res == 0)
+ return 0;
+
+ if ((*p & 0x80) == 0)
+ {
+ px_debug("pgp_parse_pkt_hdr: not pkt hdr");
+ return PXE_PGP_CORRUPT_DATA;
+ }
+
+ if (*p & 0x40)
+ {
+ *tag = *p & 0x3f;
+ res = parse_new_len(src, len_p);
+ }
+ else
+ {
+ lentype = *p & 3;
+ *tag = (*p >> 2) & 0x0F;
+ if (lentype == 3)
+ res = allow_ctx ? PKT_CONTEXT : PXE_PGP_CORRUPT_DATA;
+ else
+ res = parse_old_len(src, len_p, lentype);
+ }
+ return res;
+}
+
+/*
+ * Packet reader
+ */
+struct PktData {
+ int type;
+ int len;
+};
+
+static int pktreader_pull(void *priv, PullFilter *src, int len,
+ uint8 **data_p, uint8 *buf, int buflen)
+{
+ int res;
+ struct PktData *pkt = priv;
+
+ /* PKT_CONTEXT means: whatever there is */
+ if (pkt->type == PKT_CONTEXT)
+ return pullf_read(src, len, data_p);
+
+ if (pkt->len == 0)
+ {
+ /* this was last chunk in stream */
+ if (pkt->type == PKT_NORMAL)
+ return 0;
+
+ /* next chunk in stream */
+ res = parse_new_len(src, &pkt->len);
+ if (res < 0)
+ return res;
+ pkt->type = res;
+ }
+
+ if (len > pkt->len)
+ len = pkt->len;
+
+ res = pullf_read(src, len, data_p);
+ if (res > 0)
+ pkt->len -= res;
+
+ return res;
+}
+
+static void
+pktreader_free(void *priv)
+{
+ struct PktData *pkt = priv;
+ memset(pkt, 0, sizeof(*pkt));
+ px_free(pkt);
+}
+
+static struct PullFilterOps pktreader_filter = {
+ NULL, pktreader_pull, pktreader_free
+};
+
+/* needs helper function to pass several parameters */
+int
+pgp_create_pkt_reader(PullFilter **pf_p, PullFilter *src, int len,
+ int pkttype, PGP_Context *ctx)
+{
+ int res;
+ struct PktData *pkt = px_alloc(sizeof(*pkt));
+ pkt->type = pkttype;
+ pkt->len = len;
+ res = pullf_create(pf_p, &pktreader_filter, pkt, src);
+ if (res < 0)
+ px_free(pkt);
+ return res;
+}
+
+/*
+ * Prefix check filter
+ */
+
+static int prefix_init(void **priv_p, void *arg, PullFilter *src)
+{
+ PGP_Context *ctx = arg;
+ int len;
+ int res;
+ uint8 *buf;
+ uint8 tmpbuf[PGP_MAX_BLOCK + 2];
+
+ len = pgp_get_cipher_block_size(ctx->cipher_algo);
+ if (len > sizeof(tmpbuf))
+ return PXE_BUG;
+
+ res = pullf_read_max(src, len + 2, &buf, tmpbuf);
+ if (res < 0)
+ return res;
+ if (res != len + 2)
+ {
+ px_debug("prefix_init: short read");
+ memset(tmpbuf, 0, sizeof(tmpbuf));
+ return PXE_PGP_CORRUPT_DATA;
+ }
+
+ if (buf[len - 2] != buf[len] || buf[len - 1] != buf[len + 1])
+ {
+ px_debug("prefix_init: corrupt prefix");
+ /*
+ * The original purpose of the 2-byte check was
+ * to show user a friendly "wrong key" message.
+ * This made following possible:
+ *
+ * "An Attack on CFB Mode Encryption As Used By OpenPGP"
+ * by Serge Mister and Robert Zuccherato
+ *
+ * To avoid being 'oracle', we delay reporting, which
+ * basically means we prefer to run into corrupt packet
+ * header.
+ *
+ * We _could_ throw PXE_PGP_CORRUPT_DATA here, but
+ * there is possibility of attack via timing, so we don't.
+ */
+ ctx->corrupt_prefix = 1;
+ }
+ memset(tmpbuf, 0, sizeof(tmpbuf));
+ return 0;
+}
+
+static struct PullFilterOps prefix_filter = {
+ prefix_init, NULL, NULL
+};
+
+
+/*
+ * Decrypt filter
+ */
+
+static int decrypt_init(void **priv_p, void *arg, PullFilter *src)
+{
+ PGP_CFB *cfb = arg;
+
+ *priv_p = cfb;
+
+ /* we need to write somewhere, so ask for a buffer */
+ return 4096;
+}
+
+static int decrypt_read(void *priv, PullFilter *src, int len,
+ uint8 **data_p, uint8 *buf, int buflen)
+{
+ PGP_CFB *cfb = priv;
+ uint8 *tmp;
+ int res;
+
+ res = pullf_read(src, len, &tmp);
+ if (res > 0) {
+ pgp_cfb_decrypt(cfb, tmp, res, buf);
+ *data_p = buf;
+ }
+ return res;
+}
+
+struct PullFilterOps pgp_decrypt_filter = {
+ decrypt_init, decrypt_read, NULL
+};
+
+
+/*
+ * MDC hasher filter
+ */
+
+static int mdc_init(void **priv_p, void *arg, PullFilter *src)
+{
+ PGP_Context *ctx = arg;
+ *priv_p = ctx;
+ return pgp_load_digest(PGP_DIGEST_SHA1, &ctx->mdc_ctx);
+}
+
+static void mdc_free(void *priv)
+{
+ PGP_Context *ctx = priv;
+ if (ctx->use_mdcbuf_filter)
+ return;
+ px_md_free(ctx->mdc_ctx);
+ ctx->mdc_ctx = NULL;
+}
+
+// fixme: clarify
+static int mdc_finish(PGP_Context *ctx, PullFilter *src,
+ int len, uint8 **data_p)
+{
+ int res;
+ uint8 hash[20];
+ uint8 tmpbuf[22];
+
+ if (len + 1 > sizeof(tmpbuf))
+ return PXE_BUG;
+
+ /* read data */
+ res = pullf_read_max(src, len + 1, data_p, tmpbuf);
+ if (res < 0)
+ return res;
+ if (res == 0)
+ {
+ if (ctx->mdc_checked == 0)
+ {
+ px_debug("no mdc");
+ return PXE_PGP_CORRUPT_DATA;
+ }
+ return 0;
+ }
+
+ if (ctx->in_mdc_pkt > 1)
+ {
+ px_debug("mdc_finish: several times here?");
+ return PXE_PGP_CORRUPT_DATA;
+ }
+ ctx->in_mdc_pkt++;
+
+ if (res != 20)
+ {
+ px_debug("mdc_finish: read failed, res=%d", res);
+ return PXE_PGP_CORRUPT_DATA;
+ }
+
+ /*
+ * ok, we got the hash, now check
+ */
+ px_md_finish(ctx->mdc_ctx, hash);
+ res = memcmp(hash, *data_p, 20);
+ memset(hash, 0, 20);
+ memset(tmpbuf, 0, sizeof(tmpbuf));
+ if (res != 0)
+ {
+ px_debug("mdc_finish: mdc failed");
+ return PXE_PGP_CORRUPT_DATA;
+ }
+ ctx->mdc_checked = 1;
+ return len;
+}
+
+static int mdc_read(void *priv, PullFilter *src, int len,
+ uint8 **data_p, uint8 *buf, int buflen)
+{
+ int res;
+ PGP_Context *ctx = priv;
+
+ /* skip this filter? */
+ if (ctx->use_mdcbuf_filter)
+ return pullf_read(src, len, data_p);
+
+ if (ctx->in_mdc_pkt)
+ return mdc_finish(ctx, src, len, data_p);
+
+ res = pullf_read(src, len, data_p);
+ if (res < 0)
+ return res;
+ if (res == 0)
+ {
+ px_debug("mdc_read: unexpected eof");
+ return PXE_PGP_CORRUPT_DATA;
+ }
+ px_md_update(ctx->mdc_ctx, *data_p, res);
+
+ return res;
+}
+
+static struct PullFilterOps mdc_filter = {
+ mdc_init, mdc_read, mdc_free
+};
+
+
+/*
+ * Combined Pkt reader and MDC hasher.
+ *
+ * For the case of SYMENCRYPTED_MDC packet, where
+ * the data part has 'context length', which means
+ * that data packet ends 22 bytes before end of parent
+ * packet, which is silly.
+ */
+#define MDCBUF_LEN 8192
+struct MDCBufData {
+ PGP_Context *ctx;
+ int eof;
+ int buflen;
+ int avail;
+ uint8 *pos;
+ int mdc_avail;
+ uint8 mdc_buf[22];
+ uint8 buf[MDCBUF_LEN];
+};
+
+static int mdcbuf_init(void **priv_p, void *arg, PullFilter *src)
+{
+ PGP_Context *ctx = arg;
+ struct MDCBufData *st;
+
+ st = px_alloc(sizeof(*st));
+ memset(st, 0, sizeof(*st));
+ st->buflen = sizeof(st->buf);
+ st->ctx = ctx;
+ *priv_p = st;
+
+ /* take over the work of mdc_filter */
+ ctx->use_mdcbuf_filter = 1;
+
+ return 0;
+}
+
+static int mdcbuf_finish(struct MDCBufData *st)
+{
+ uint8 hash[20];
+ int res;
+
+ st->eof = 1;
+
+ if (st->mdc_buf[0] != 0xD3 || st->mdc_buf[1] != 0x14)
+ {
+ px_debug("mdcbuf_finish: bad MDC pkt hdr");
+ return PXE_PGP_CORRUPT_DATA;
+ }
+ px_md_update(st->ctx->mdc_ctx, st->mdc_buf, 2);
+ px_md_finish(st->ctx->mdc_ctx, hash);
+ res = memcmp(hash, st->mdc_buf + 2, 20);
+ memset(hash, 0, 20);
+ if (res)
+ {
+ px_debug("mdcbuf_finish: MDC does not match");
+ res = PXE_PGP_CORRUPT_DATA;
+ }
+ return res;
+}
+
+static void mdcbuf_load_data(struct MDCBufData *st, uint8 *src, int len)
+{
+ uint8 *dst = st->pos + st->avail;
+ memcpy(dst, src, len);
+ px_md_update(st->ctx->mdc_ctx, src, len);
+ st->avail += len;
+}
+
+static void mdcbuf_load_mdc(struct MDCBufData *st, uint8 *src, int len)
+{
+ memmove(st->mdc_buf + st->mdc_avail, src, len);
+ st->mdc_avail += len;
+}
+
+static int mdcbuf_refill(struct MDCBufData *st, PullFilter *src)
+{
+ uint8 *data;
+ int res;
+ int need;
+
+ /* put avail data in start */
+ if (st->avail > 0 && st->pos != st->buf)
+ memmove(st->buf, st->pos, st->avail);
+ st->pos = st->buf;
+
+ /* read new data */
+ need = st->buflen + 22 - st->avail - st->mdc_avail;
+ res = pullf_read(src, need, &data);
+ if (res < 0)
+ return res;
+ if (res == 0)
+ return mdcbuf_finish(st);
+
+ /* add to buffer */
+ if (res >= 22)
+ {
+ mdcbuf_load_data(st, st->mdc_buf, st->mdc_avail);
+ st->mdc_avail = 0;
+
+ mdcbuf_load_data(st, data, res - 22);
+ mdcbuf_load_mdc(st, data + res - 22, 22);
+ }
+ else
+ {
+ int canmove = st->mdc_avail + res - 22;
+ if (canmove > 0)
+ {
+ mdcbuf_load_data(st, st->mdc_buf, canmove);
+ st->mdc_avail -= canmove;
+ memmove(st->mdc_buf, st->mdc_buf + canmove, st->mdc_avail);
+ }
+ mdcbuf_load_mdc(st, data, res);
+ }
+ return 0;
+}
+
+static int mdcbuf_read(void *priv, PullFilter *src, int len,
+ uint8 **data_p, uint8 *buf, int buflen)
+{
+ struct MDCBufData *st = priv;
+ int res;
+
+ if (!st->eof && len > st->avail)
+ {
+ res = mdcbuf_refill(st, src);
+ if (res < 0)
+ return res;
+ }
+
+ if (len > st->avail)
+ len = st->avail;
+
+ *data_p = st->pos;
+ st->pos += len;
+ st->avail -= len;
+ return len;
+}
+
+static void
+mdcbuf_free(void *priv)
+{
+ struct MDCBufData *st = priv;
+ px_md_free(st->ctx->mdc_ctx);
+ st->ctx->mdc_ctx = NULL;
+ memset(st, 0, sizeof(*st));
+ px_free(st);
+}
+
+static struct PullFilterOps mdcbuf_filter = {
+ mdcbuf_init, mdcbuf_read, mdcbuf_free
+};
+
+
+/*
+ * Decrypt separate session key
+ */
+static int
+decrypt_key(PGP_Context *ctx, const uint8 *src, int len)
+{
+ int res;
+ uint8 algo;
+ PGP_CFB *cfb;
+
+ res = pgp_cfb_create(&cfb, ctx->s2k_cipher_algo,
+ ctx->s2k.key, ctx->s2k.key_len, 0, NULL);
+ if (res < 0)
+ return res;
+
+ pgp_cfb_decrypt(cfb, src, 1, &algo);
+ src ++;
+ len --;
+
+ pgp_cfb_decrypt(cfb, src, len, ctx->sess_key);
+ pgp_cfb_free(cfb);
+ ctx->sess_key_len = len;
+ ctx->cipher_algo = algo;
+
+ if (pgp_get_cipher_key_size(algo) != len) {
+ px_debug("sesskey bad len: algo=%d, expected=%d, got=%d",
+ algo, pgp_get_cipher_key_size(algo), len);
+ return PXE_PGP_CORRUPT_DATA;
+ }
+ return 0;
+}
+
+/*
+ * Handle key packet
+ */
+static int
+parse_symenc_sesskey(PGP_Context * ctx, PullFilter * src)
+{
+ uint8 *p;
+ int res;
+ uint8 tmpbuf[PGP_MAX_KEY + 2];
+ uint8 ver;
+
+ GETBYTE(src, ver);
+ GETBYTE(src, ctx->s2k_cipher_algo);
+ if (ver != 4)
+ {
+ px_debug("bad key pkt ver");
+ return PXE_PGP_CORRUPT_DATA;
+ }
+
+ /*
+ * read S2K info
+ */
+ res = pgp_s2k_read(src, &ctx->s2k);
+ if (res < 0)
+ return res;
+ ctx->s2k_mode = ctx->s2k.mode;
+ ctx->s2k_digest_algo = ctx->s2k.digest_algo;
+
+ /*
+ * generate key from password
+ */
+ res = pgp_s2k_process(&ctx->s2k, ctx->s2k_cipher_algo,
+ ctx->sym_key, ctx->sym_key_len);
+ if (res < 0)
+ return res;
+
+ /*
+ * do we have separate session key?
+ */
+ res = pullf_read_max(src, PGP_MAX_KEY + 2, &p, tmpbuf);
+ if (res < 0)
+ return res;
+
+ if (res == 0)
+ {
+ /*
+ * no, s2k key is session key
+ */
+ memcpy(ctx->sess_key, ctx->s2k.key, ctx->s2k.key_len);
+ ctx->sess_key_len = ctx->s2k.key_len;
+ ctx->cipher_algo = ctx->s2k_cipher_algo;
+ res = 0;
+ ctx->use_sess_key = 0;
+ }
+ else
+ {
+ /*
+ * yes, decrypt it
+ */
+ if (res < 17 || res > PGP_MAX_KEY + 1)
+ {
+ px_debug("expect key, but bad data");
+ return PXE_PGP_CORRUPT_DATA;
+ }
+ ctx->use_sess_key = 1;
+ res = decrypt_key(ctx, p, res);
+ }
+
+ memset(tmpbuf, 0, sizeof(tmpbuf));
+ return res;
+}
+
+static int
+copy_crlf(MBuf *dst, uint8 *data, int len, int *got_cr)
+{
+ uint8 *data_end = data + len;
+ uint8 tmpbuf[1024];
+ uint8 *tmp_end = tmpbuf + sizeof(tmpbuf);
+ uint8 *p;
+ int res;
+
+ p = tmpbuf;
+ if (*got_cr) {
+ if (*data != '\n')
+ *p++ = '\r';
+ *got_cr = 0;
+ }
+ while (data < data_end) {
+ if (*data == '\r')
+ {
+ if (data + 1 < data_end)
+ {
+ if (*(data + 1) == '\n')
+ data++;
+ }
+ else
+ {
+ *got_cr = 1;
+ break;
+ }
+ }
+ *p++ = *data++;
+ if (p >= tmp_end)
+ {
+ res = mbuf_append(dst, tmpbuf, p - tmpbuf);
+ if (res < 0)
+ return res;
+ p = tmpbuf;
+ }
+ }
+ if (p - tmpbuf > 0)
+ {
+ res = mbuf_append(dst, tmpbuf, p - tmpbuf);
+ if (res < 0)
+ return res;
+ }
+ return 0;
+}
+
+static int
+parse_literal_data(PGP_Context * ctx, MBuf * dst, PullFilter * pkt)
+{
+ int type;
+ int name_len;
+ int res;
+ uint8 *buf;
+ uint8 tmpbuf[4];
+ int got_cr = 0;
+
+ GETBYTE(pkt, type);
+ GETBYTE(pkt, name_len);
+
+ /* skip name */
+ while (name_len > 0)
+ {
+ res = pullf_read(pkt, name_len, &buf);
+ if (res < 0)
+ return res;
+ if (res == 0)
+ break;
+ name_len -= res;
+ }
+ if (name_len > 0)
+ {
+ px_debug("parse_literal_data: unexpected eof");
+ return PXE_PGP_CORRUPT_DATA;
+ }
+
+ /* skip date */
+ res = pullf_read_max(pkt, 4, &buf, tmpbuf);
+ if (res != 4)
+ {
+ px_debug("parse_literal_data: unexpected eof");
+ return PXE_PGP_CORRUPT_DATA;
+ }
+ memset(tmpbuf, 0, 4);
+
+ /* check if text */
+ if (ctx->text_mode)
+ if (type != 't' && type != 'u')
+ {
+ px_debug("parse_literal_data: data type=%c", type);
+ return PXE_PGP_NOT_TEXT;
+ }
+
+ ctx->unicode_mode = (type == 'u') ? 1 : 0;
+
+ /* read data */
+ while (1) {
+ res = pullf_read(pkt, 32*1024, &buf);
+ if (res <= 0)
+ break;
+
+ if (ctx->text_mode && ctx->convert_crlf)
+ res = copy_crlf(dst, buf, res, &got_cr);
+ else
+ res = mbuf_append(dst, buf, res);
+ if (res < 0)
+ break;
+ }
+ if (res >= 0 && got_cr)
+ res = mbuf_append(dst, "\r", 1);
+ return res;
+}
+
+/* process_data_packets and parse_compressed_data call each other */
+static int process_data_packets(PGP_Context * ctx, MBuf * dst,
+ PullFilter * src, int allow_compr, int need_mdc);
+
+static int
+parse_compressed_data(PGP_Context * ctx, MBuf * dst, PullFilter * pkt)
+{
+ int res;
+ uint8 type;
+ PullFilter *pf_decompr;
+
+ GETBYTE(pkt, type);
+
+ ctx->compress_algo = type;
+ switch (type)
+ {
+ case PGP_COMPR_NONE:
+ res = process_data_packets(ctx, dst, pkt, NO_COMPR, NO_MDC);
+ break;
+
+ case PGP_COMPR_ZIP:
+ case PGP_COMPR_ZLIB:
+ res = pgp_decompress_filter(&pf_decompr, ctx, pkt);
+ if (res >= 0)
+ {
+ res = process_data_packets(ctx, dst, pf_decompr,
+ NO_COMPR, NO_MDC);
+ pullf_free(pf_decompr);
+ }
+ break;
+
+ case PGP_COMPR_BZIP2:
+ px_debug("parse_compressed_data: bzip2 unsupported");
+ res = PXE_PGP_UNSUPPORTED_COMPR;
+ break;
+
+ default:
+ px_debug("parse_compressed_data: unknown compr type");
+ res = PXE_PGP_CORRUPT_DATA;
+ }
+
+ return res;
+}
+
+static int
+process_data_packets(PGP_Context * ctx, MBuf * dst, PullFilter * src,
+ int allow_compr, int need_mdc)
+{
+ uint8 tag;
+ int len,
+ res;
+ int got_data = 0;
+ int got_mdc = 0;
+ PullFilter *pkt = NULL;
+ uint8 *tmp;
+
+ while (1)
+ {
+ res = pgp_parse_pkt_hdr(src, &tag, &len, ALLOW_CTX_SIZE);
+ if (res <= 0)
+ break;
+
+
+ /* mdc packet should be last */
+ if (got_mdc)
+ {
+ px_debug("process_data_packets: data after mdc");
+ res = PXE_PGP_CORRUPT_DATA;
+ break;
+ }
+
+ /* context length inside SYMENC_MDC needs special handling */
+ if (need_mdc && res == PKT_CONTEXT)
+ res = pullf_create(&pkt, &mdcbuf_filter, ctx, src);
+ else
+ res = pgp_create_pkt_reader(&pkt, src, len, res, ctx);
+ if (res < 0)
+ break;
+
+ switch (tag)
+ {
+ case PGP_PKT_LITERAL_DATA:
+ got_data = 1;
+ res = parse_literal_data(ctx, dst, pkt);
+ break;
+ case PGP_PKT_COMPRESSED_DATA:
+ if (allow_compr == 0)
+ {
+ px_debug("process_data_packets: unexpected compression");
+ res = PXE_PGP_CORRUPT_DATA;
+ }
+ else if (got_data)
+ {
+ /*
+ * compr data must be alone
+ */
+ px_debug("process_data_packets: only one cmpr pkt allowed");
+ res = PXE_PGP_CORRUPT_DATA;
+ }
+ else
+ {
+ got_data = 1;
+ res = parse_compressed_data(ctx, dst, pkt);
+ }
+ break;
+ case PGP_PKT_MDC:
+ if (need_mdc == NO_MDC)
+ {
+ px_debug("process_data_packets: unexpected MDC");
+ res = PXE_PGP_CORRUPT_DATA;
+ break;
+ }
+
+ /* notify mdc_filter */
+ ctx->in_mdc_pkt = 1;
+
+ res = pullf_read(pkt, 8192, &tmp);
+ if (res > 0)
+ got_mdc = 1;
+ break;
+ default:
+ px_debug("process_data_packets: unexpected pkt tag=%d", tag);
+ res = PXE_PGP_CORRUPT_DATA;
+ }
+
+ pullf_free(pkt);
+ pkt = NULL;
+
+ if (res < 0)
+ break;
+ }
+
+ if (pkt)
+ pullf_free(pkt);
+
+ if (res < 0)
+ return res;
+
+ if (!got_data)
+ {
+ px_debug("process_data_packets: no data");
+ res = PXE_PGP_CORRUPT_DATA;
+ }
+ if (need_mdc && !got_mdc && !ctx->use_mdcbuf_filter)
+ {
+ px_debug("process_data_packets: got no mdc");
+ res = PXE_PGP_CORRUPT_DATA;
+ }
+ return res;
+}
+
+static int
+parse_symenc_data(PGP_Context * ctx, PullFilter * pkt, MBuf * dst)
+{
+ int res;
+ PGP_CFB *cfb = NULL;
+ PullFilter *pf_decrypt = NULL;
+ PullFilter *pf_prefix = NULL;
+
+ res = pgp_cfb_create(&cfb, ctx->cipher_algo,
+ ctx->sess_key, ctx->sess_key_len, 1, NULL);
+ if (res < 0)
+ goto out;
+
+ res = pullf_create(&pf_decrypt, &pgp_decrypt_filter, cfb, pkt);
+ if (res < 0)
+ goto out;
+
+ res = pullf_create(&pf_prefix, &prefix_filter, ctx, pf_decrypt);
+ if (res < 0)
+ goto out;
+
+ res = process_data_packets(ctx, dst, pf_prefix, ALLOW_COMPR, NO_MDC);
+
+out:
+ if (pf_prefix)
+ pullf_free(pf_prefix);
+ if (pf_decrypt)
+ pullf_free(pf_decrypt);
+ if (cfb)
+ pgp_cfb_free(cfb);
+
+ return res;
+}
+
+static int
+parse_symenc_mdc_data(PGP_Context * ctx, PullFilter * pkt, MBuf * dst)
+{
+ int res;
+ PGP_CFB *cfb = NULL;
+ PullFilter *pf_decrypt = NULL;
+ PullFilter *pf_prefix = NULL;
+ PullFilter *pf_mdc = NULL;
+ uint8 ver;
+
+ GETBYTE(pkt, ver);
+ if (ver != 1)
+ {
+ px_debug("parse_symenc_mdc_data: pkt ver != 1");
+ return PXE_PGP_CORRUPT_DATA;
+ }
+
+ res = pgp_cfb_create(&cfb, ctx->cipher_algo,
+ ctx->sess_key, ctx->sess_key_len, 0, NULL);
+ if (res < 0)
+ goto out;
+
+ res = pullf_create(&pf_decrypt, &pgp_decrypt_filter, cfb, pkt);
+ if (res < 0)
+ goto out;
+
+ res = pullf_create(&pf_mdc, &mdc_filter, ctx, pf_decrypt);
+ if (res < 0)
+ goto out;
+
+ res = pullf_create(&pf_prefix, &prefix_filter, ctx, pf_mdc);
+ if (res < 0)
+ goto out;
+
+ res = process_data_packets(ctx, dst, pf_prefix, ALLOW_COMPR, NEED_MDC);
+
+out:
+ if (pf_prefix)
+ pullf_free(pf_prefix);
+ if (pf_mdc)
+ pullf_free(pf_mdc);
+ if (pf_decrypt)
+ pullf_free(pf_decrypt);
+ if (cfb)
+ pgp_cfb_free(cfb);
+
+ return res;
+}
+
+/*
+ * skip over packet contents
+ */
+int
+pgp_skip_packet(PullFilter *pkt)
+{
+ int res = 1;
+ uint8 *tmp;
+ while (res > 0)
+ res = pullf_read(pkt, 32*1024, &tmp);
+ return res < 0 ? res : 0;
+}
+
+/*
+ * expect to be at packet end, any data is error
+ */
+int
+pgp_expect_packet_end(PullFilter *pkt)
+{
+ int res = 1;
+ uint8 *tmp;
+ while (res > 0)
+ {
+ res = pullf_read(pkt, 32*1024, &tmp);
+ if (res > 0)
+ {
+ px_debug("pgp_expect_packet_end: got data");
+ return PXE_PGP_CORRUPT_DATA;
+ }
+ }
+ return res < 0 ? res : 0;
+}
+
+int
+pgp_decrypt(PGP_Context * ctx, MBuf * msrc, MBuf * mdst)
+{
+ int res;
+ PullFilter *src = NULL;
+ PullFilter *pkt = NULL;
+ uint8 tag;
+ int len;
+ int got_key = 0;
+ int got_data = 0;
+
+ res = pullf_create_mbuf_reader(&src, msrc);
+
+ while (res >= 0) {
+ res = pgp_parse_pkt_hdr(src, &tag, &len, NO_CTX_SIZE);
+ if (res <= 0)
+ break;
+
+ res = pgp_create_pkt_reader(&pkt, src, len, res, ctx);
+ if (res < 0)
+ break;
+
+ res = PXE_PGP_CORRUPT_DATA;
+ switch (tag) {
+ case PGP_PKT_MARKER:
+ res = pgp_skip_packet(pkt);
+ break;
+ case PGP_PKT_PUBENCRYPTED_SESSKEY:
+ /* fixme: skip those */
+ res = pgp_parse_pubenc_sesskey(ctx, pkt);
+ got_key = 1;
+ break;
+ case PGP_PKT_SYMENCRYPTED_SESSKEY:
+ if (got_key)
+ /* Theoretically, there could be several keys,
+ * both public and symmetric, all of which
+ * encrypt same session key. Decrypt should try
+ * with each one, before failing.
+ */
+ px_debug("pgp_decrypt: using first of several keys");
+ else
+ {
+ got_key = 1;
+ res = parse_symenc_sesskey(ctx, pkt);
+ }
+ break;
+ case PGP_PKT_SYMENCRYPTED_DATA:
+ if (!got_key)
+ px_debug("pgp_decrypt: have data but no key");
+ else if (got_data)
+ px_debug("pgp_decrypt: got second data packet");
+ else
+ {
+ got_data = 1;
+ ctx->disable_mdc = 1;
+ res = parse_symenc_data(ctx, pkt, mdst);
+ }
+ break;
+ case PGP_PKT_SYMENCRYPTED_DATA_MDC:
+ if (!got_key)
+ px_debug("pgp_decrypt: have data but no key");
+ else if (got_data)
+ px_debug("pgp_decrypt: several data pkts not supported");
+ else
+ {
+ got_data = 1;
+ ctx->disable_mdc = 0;
+ res = parse_symenc_mdc_data(ctx, pkt, mdst);
+ }
+ break;
+ default:
+ px_debug("pgp_decrypt: unknown tag: 0x%02x", tag);
+ }
+ pullf_free(pkt);
+ pkt = NULL;
+ }
+
+ if (pkt)
+ pullf_free(pkt);
+
+ if (src)
+ pullf_free(src);
+
+ if (res < 0)
+ return res;
+
+ if (!got_data || ctx->corrupt_prefix)
+ res = PXE_PGP_CORRUPT_DATA;
+
+ return res;
+}
+