Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Postgres/contrib/pgcrypto/   (Postgres Database Version 18.4©)  Datei vom 11.4.2026 mit Größe 23 kB image not shown  

Quelle  pgp-decrypt.c

  Sprache: 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.
 *
 * contrib/pgcrypto/pgp-decrypt.c
 */


#include "postgres.h"

#include "mbuf.h"
#include "pgp.h"
#include "px.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 don't 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);

 while (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;

 px_memset(pkt, 0sizeof(*pkt));
 pfree(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 = palloc(sizeof(*pkt));

 pkt->type = pkttype;
 pkt->len = len;
 res = pullf_create(pf_p, &pktreader_filter, pkt, src);
 if (res < 0)
  pfree(pkt);
 return res;
}

/*
 * Prefix check filter
 * https://tools.ietf.org/html/rfc4880#section-5.7
 * https://tools.ietf.org/html/rfc4880#section-5.13
 */


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);
 /* Make sure we have space for prefix */
 if (len > PGP_MAX_BLOCK)
  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");
  px_memset(tmpbuf, 0sizeof(tmpbuf));
  return PXE_PGP_CORRUPT_DATA;
 }

 if (buf[len - 2] != buf[len] || buf[len - 1] != buf[len + 1])
 {
  px_debug("prefix_init: corrupt prefix");
  /* report error in pgp_decrypt() */
  ctx->corrupt_prefix = 1;
 }
 px_memset(tmpbuf, 0sizeof(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)
{
 int   res;
 uint8  hash[20];
 uint8  tmpbuf[20];
 uint8    *data;

 /* should not happen */
 if (ctx->use_mdcbuf_filter)
  return PXE_BUG;

 /* It's SHA1 */
 if (len != 20)
  return PXE_PGP_CORRUPT_DATA;

 /* mdc_read should not call px_md_update */
 ctx->in_mdc_pkt = 1;

 /* read data */
 res = pullf_read_max(src, len, &data, tmpbuf);
 if (res < 0)
  return res;
 if (res == 0)
 {
  px_debug("no mdc");
  return PXE_PGP_CORRUPT_DATA;
 }

 /* 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, 20);
 px_memset(hash, 020);
 px_memset(tmpbuf, 0sizeof(tmpbuf));
 if (res != 0)
 {
  px_debug("mdc_finish: mdc failed");
  return PXE_PGP_CORRUPT_DATA;
 }
 ctx->mdc_checked = 1;
 return 0;
}

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 || ctx->in_mdc_pkt)
  return pullf_read(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_DATA_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 = palloc0(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 + 220);
 px_memset(hash, 020);
 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 - 2222);
 }
 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;
 px_memset(st, 0sizeof(*st));
 pfree(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_count = s2k_decode_count(ctx->s2k.iter);
 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);
 }

 px_memset(tmpbuf, 0sizeof(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;
 }
 px_memset(tmpbuf, 0sizeof(tmpbuf));
 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;
 }
 px_memset(tmpbuf, 04);

 /*
  * If called from an SQL function that returns text, pgp_decrypt() rejects
  * inputs not self-identifying as text.
 */

 if (ctx->text_mode)
  if (type != 't' && type != 'u')
  {
   px_debug("parse_literal_data: data type=%c", type);
   ctx->unexpected_binary = true;
  }

 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;
 uint8    *discard_buf;

 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");
   /* report error in pgp_decrypt() */
   ctx->unsupported_compr = 1;

   /*
    * Discard the compressed data, allowing it to first affect any
    * MDC digest computation.
 */

   while (1)
   {
    res = pullf_read(pkt, 32 * 1024, &discard_buf);
    if (res <= 0)
     break;
   }

   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;

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

    res = mdc_finish(ctx, pkt, len);
    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;
}

/*
 * expect to be at packet end, any data is error
 */

int
pgp_expect_packet_end(PullFilter *pkt)
{
 int   res;
 uint8    *tmp;

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

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;

 /*
  * Report a failure of the prefix_init() "quick check" now, rather than
  * upon detection, to hinder timing attacks.  pgcrypto is not generally
  * secure against timing attacks, but this helps.
 */

 if (!got_data || ctx->corrupt_prefix)
  return PXE_PGP_CORRUPT_DATA;

 /*
  * Code interpreting purportedly-decrypted data prior to this stage shall
  * report no error other than PXE_PGP_CORRUPT_DATA.  (PXE_BUG is okay so
  * long as it remains unreachable.)  This ensures that an attacker able to
  * choose a ciphertext and receive a corresponding decryption error
  * message cannot use that oracle to gather clues about the decryption
  * key.  See "An Attack on CFB Mode Encryption As Used By OpenPGP" by
  * Serge Mister and Robert Zuccherato.
  *
  * A problematic value in the first octet of a Literal Data or Compressed
  * Data packet may indicate a simple user error, such as the need to call
  * pgp_sym_decrypt_bytea instead of pgp_sym_decrypt.  Occasionally,
  * though, it is the first symptom of the encryption key not matching the
  * decryption key.  When this was the only problem encountered, report a
  * specific error to guide the user; otherwise, we will have reported
  * PXE_PGP_CORRUPT_DATA before now.  A key mismatch makes the other errors
  * into red herrings, and this avoids leaking clues to attackers.
 */

 if (ctx->unsupported_compr)
  return PXE_PGP_UNSUPPORTED_COMPR;
 if (ctx->unexpected_binary)
  return PXE_PGP_NOT_TEXT;

 return res;
}

Messung V0.5 in Prozent
C=93 H=91 G=91

¤ Dauer der Verarbeitung: 0.15 Sekunden  (vorverarbeitet am  2026-08-08) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.