Logo Search packages:      
Sourcecode: postgresql-8.4 version File versions  Download package

pgp-decrypt.c

/*
 * 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.8 2009/06/11 14:48:52 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;
}

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;
      }

      /* safety check */
      if (ctx->in_mdc_pkt > 1)
      {
            px_debug("mdc_finish: several times here?");
            return PXE_PGP_CORRUPT_DATA;
      }
      ctx->in_mdc_pkt++;

      /* is the packet sane? */
      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, (const uint8 *) "\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;
}

Generated by  Doxygen 1.6.0   Back to index