Update copyright notices for 2017.
[gnupg.git] / g10 / parse-packet.c
index e2a5ea3..7f44ce5 100644 (file)
@@ -1,6 +1,8 @@
 /* parse-packet.c  - read packets
  * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006,
- *               2007, 2009 Free Software Foundation, Inc.
+ *               2007, 2009, 2010 Free Software Foundation, Inc.
+ * Copyright (C) 2014 Werner Koch
+ * Copyright (C) 2015 g10 Code GmbH
  *
  * This file is part of GnuPG.
  *
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.  
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
  */
 
 #include <config.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <assert.h>
 
 #include "gpg.h"
+#include "util.h"
 #include "packet.h"
 #include "iobuf.h"
-#include "util.h"
-#include "cipher.h"
 #include "filter.h"
 #include "photoid.h"
 #include "options.h"
 #include "main.h"
 #include "i18n.h"
+#include "host2net.h"
+
+
+/* Maximum length of packets to avoid excessive memory allocation.  */
+#define MAX_KEY_PACKET_LENGTH     (256 * 1024)
+#define MAX_UID_PACKET_LENGTH     (  2 * 1024)
+#define MAX_COMMENT_PACKET_LENGTH ( 64 * 1024)
+#define MAX_ATTR_PACKET_LENGTH    ( 16 * 1024*1024)
+
 
 static int mpi_print_mode;
 static int list_mode;
-static FILE *listfp;
+static estream_t listfp;
 
 static int parse (IOBUF inp, PACKET * pkt, int onlykeypkts,
                  off_t * retpos, int *skip, IOBUF out, int do_skip
@@ -49,7 +58,7 @@ static int copy_packet (IOBUF inp, IOBUF out, int pkttype,
                        unsigned long pktlen, int partial);
 static void skip_packet (IOBUF inp, int pkttype,
                         unsigned long pktlen, int partial);
-static void *read_rest (IOBUF inp, size_t pktlen, int partial);
+static void *read_rest (IOBUF inp, size_t pktlen);
 static int parse_marker (IOBUF inp, int pkttype, unsigned long pktlen);
 static int parse_symkeyenc (IOBUF inp, int pkttype, unsigned long pktlen,
                            PACKET * packet);
@@ -78,21 +87,23 @@ static int parse_mdc (IOBUF inp, int pkttype, unsigned long pktlen,
 static int parse_gpg_control (IOBUF inp, int pkttype, unsigned long pktlen,
                              PACKET * packet, int partial);
 
+/* Read a 16-bit value in MSB order (big endian) from an iobuf.  */
 static unsigned short
 read_16 (IOBUF inp)
 {
   unsigned short a;
-  a = iobuf_get_noeof (inp) << 8;
+  a = (unsigned short)iobuf_get_noeof (inp) << 8;
   a |= iobuf_get_noeof (inp);
   return a;
 }
 
 
+/* Read a 32-bit value in MSB order (big endian) from an iobuf.  */
 static unsigned long
 read_32 (IOBUF inp)
 {
   unsigned long a;
-  a = iobuf_get_noeof (inp) << 24;
+  a = (unsigned long)iobuf_get_noeof (inp) << 24;
   a |= iobuf_get_noeof (inp) << 16;
   a |= iobuf_get_noeof (inp) << 8;
   a |= iobuf_get_noeof (inp);
@@ -100,35 +111,47 @@ read_32 (IOBUF inp)
 }
 
 
-/* Read an external representation of an mpi and return the MPI.  The
- * external format is a 16 bit unsigned value stored in network byte
- * order, giving the number of bits for the following integer. The
- * integer is stored with MSB first (left padded with zeroes to align
- * on a byte boundary).  */
+/* Read an external representation of an MPI and return the MPI.  The
+   external format is a 16-bit unsigned value stored in network byte
+   order giving the number of bits for the following integer.  The
+   integer is stored MSB first and is left padded with zero bits to
+   align on a byte boundary.
+
+   The caller must set *RET_NREAD to the maximum number of bytes to
+   read from the pipeline INP.  This function sets *RET_NREAD to be
+   the number of bytes actually read from the pipeline.
+
+   If SECURE is true, the integer is stored in secure memory
+   (allocated using gcry_xmalloc_secure).  */
 static gcry_mpi_t
 mpi_read (iobuf_t inp, unsigned int *ret_nread, int secure)
 {
-  /*FIXME: Needs to be synced with gnupg14/mpi/mpicoder.c */
-
   int c, c1, c2, i;
+  unsigned int nmax = *ret_nread;
   unsigned int nbits, nbytes;
   size_t nread = 0;
   gcry_mpi_t a = NULL;
   byte *buf = NULL;
   byte *p;
 
+  if (!nmax)
+    goto overflow;
+
   if ((c = c1 = iobuf_get (inp)) == -1)
     goto leave;
+  if (++nread == nmax)
+    goto overflow;
   nbits = c << 8;
   if ((c = c2 = iobuf_get (inp)) == -1)
     goto leave;
+  ++nread;
   nbits |= c;
   if (nbits > MAX_EXTERN_MPI_BITS)
     {
       log_error ("mpi too large (%u bits)\n", nbits);
       goto leave;
     }
-  nread = 2;
+
   nbytes = (nbits + 7) / 8;
   buf = secure ? gcry_xmalloc_secure (nbytes + 2) : gcry_xmalloc (nbytes + 2);
   p = buf;
@@ -136,28 +159,29 @@ mpi_read (iobuf_t inp, unsigned int *ret_nread, int secure)
   p[1] = c2;
   for (i = 0; i < nbytes; i++)
     {
-      p[i + 2] = iobuf_get (inp) & 0xff;
-      nread++;
-    }
+      if (nread == nmax)
+       goto overflow;
 
-  if (nread >= 2 && !(buf[0] << 8 | buf[1]))
-    {
-      /* Libgcrypt < 1.5.0 accidently rejects zero-length (i.e. zero)
-         MPIs.  We fix this here.  */
-      a = gcry_mpi_new (0);
-    }
-  else
-    {
-      if (gcry_mpi_scan (&a, GCRYMPI_FMT_PGP, buf, nread, &nread))
-       a = NULL;
+      c = iobuf_get (inp);
+      if (c == -1)
+       goto leave;
+
+      p[i + 2] = c;
+      nread ++;
     }
 
+  if (gcry_mpi_scan (&a, GCRYMPI_FMT_PGP, buf, nread, &nread))
+    a = NULL;
+
+  *ret_nread = nread;
+  gcry_free(buf);
+  return a;
+
+ overflow:
+  log_error ("mpi larger than indicated length (%u bits)\n", 8*nmax);
  leave:
-  gcry_free (buf);
-  if (nread > *ret_nread)
-    log_bug ("mpi larger than packet");
-  else
-    *ret_nread = nread;
+  *ret_nread = nread;
+  gcry_free(buf);
   return a;
 }
 
@@ -167,49 +191,76 @@ set_packet_list_mode (int mode)
 {
   int old = list_mode;
   list_mode = mode;
-  /* FIXME(gcrypt) mpi_print_mode = DBG_MPI; */
-  /* We use stdout print only if invoked by the --list-packets command
-     but switch to stderr in all otehr cases.  This breaks the
+
+  /* We use stdout only if invoked by the --list-packets command
+     but switch to stderr in all other cases.  This breaks the
      previous behaviour but that seems to be more of a bug than
      intentional.  I don't believe that any application makes use of
      this long standing annoying way of printing to stdout except when
      doing a --list-packets. If this assumption fails, it will be easy
      to add an option for the listing stream.  Note that we initialize
-     it only once; mainly because some code may switch the option
-     value later back to 1 and we want to have all output to the same
-     stream.
+     it only once; mainly because there is code which switches
+     opt.list_mode back to 1 and we want to have all output to the
+     same stream.  The MPI_PRINT_MODE will be enabled if the
+     corresponding debug flag is set or if we are in --list-packets
+     and --verbose is given.
 
      Using stderr is not actually very clean because it bypasses the
-     logging code but it is a special thing anyay.  I am not sure
+     logging code but it is a special thing anyway.  I am not sure
      whether using log_stream() would be better.  Perhaps we should
-     enable the list mdoe only with a special option. */
+     enable the list mode only with a special option. */
   if (!listfp)
-    listfp = opt.list_packets == 2 ? stdout : stderr;
+    {
+      if (opt.list_packets)
+        {
+          listfp = es_stdout;
+          if (opt.verbose)
+            mpi_print_mode = 1;
+        }
+      else
+        listfp = es_stderr;
+
+      if (DBG_MPI)
+        mpi_print_mode = 1;
+    }
   return old;
 }
 
 
+/* If OPT.VERBOSE is set, print a warning that the algorithm ALGO is
+   not suitable for signing and encryption.  */
 static void
 unknown_pubkey_warning (int algo)
 {
   static byte unknown_pubkey_algos[256];
 
-  algo &= 0xff;
-  if (!unknown_pubkey_algos[algo])
+  /* First check whether the algorithm is usable but not suitable for
+     encryption/signing.  */
+  if (pubkey_get_npkey (algo))
     {
       if (opt.verbose)
-       log_info (_("can't handle public key algorithm %d\n"), algo);
-      unknown_pubkey_algos[algo] = 1;
+        {
+          if (!pubkey_get_nsig (algo))
+            log_info ("public key algorithm %s not suitable for %s\n",
+                      openpgp_pk_algo_name (algo), "signing");
+          if (!pubkey_get_nenc (algo))
+            log_info ("public key algorithm %s not suitable for %s\n",
+                      openpgp_pk_algo_name (algo), "encryption");
+        }
+    }
+  else
+    {
+      algo &= 0xff;
+      if (!unknown_pubkey_algos[algo])
+        {
+          if (opt.verbose)
+            log_info (_("can't handle public key algorithm %d\n"), algo);
+          unknown_pubkey_algos[algo] = 1;
+        }
     }
 }
 
 
-/* Parse a packet and return it in packet structure.
- * Returns: 0 := valid packet in pkt
- *        -1 := no more packets
- *        >0 := error
- * Note: The function may return an error and a partly valid packet;
- * caller must free this packet.   */
 #ifdef DEBUG_PARSE_PACKET
 int
 dbg_parse_packet (IOBUF inp, PACKET *pkt, const char *dbg_f, int dbg_l)
@@ -220,7 +271,7 @@ dbg_parse_packet (IOBUF inp, PACKET *pkt, const char *dbg_f, int dbg_l)
     {
       rc = parse (inp, pkt, 0, NULL, &skip, NULL, 0, "parse", dbg_f, dbg_l);
     }
-  while (skip);
+  while (skip && ! rc);
   return rc;
 }
 #else /*!DEBUG_PARSE_PACKET*/
@@ -233,7 +284,7 @@ parse_packet (IOBUF inp, PACKET * pkt)
     {
       rc = parse (inp, pkt, 0, NULL, &skip, NULL, 0);
     }
-  while (skip);
+  while (skip && ! rc);
   return rc;
 }
 #endif /*!DEBUG_PARSE_PACKET*/
@@ -256,7 +307,7 @@ dbg_search_packet (IOBUF inp, PACKET * pkt, off_t * retpos, int with_uid,
        parse (inp, pkt, with_uid ? 2 : 1, retpos, &skip, NULL, 0, "search",
               dbg_f, dbg_l);
     }
-  while (skip);
+  while (skip && ! rc);
   return rc;
 }
 #else /*!DEBUG_PARSE_PACKET*/
@@ -269,7 +320,7 @@ search_packet (IOBUF inp, PACKET * pkt, off_t * retpos, int with_uid)
     {
       rc = parse (inp, pkt, with_uid ? 2 : 1, retpos, &skip, NULL, 0);
     }
-  while (skip);
+  while (skip && ! rc);
   return rc;
 }
 #endif /*!DEBUG_PARSE_PACKET*/
@@ -284,6 +335,10 @@ dbg_copy_all_packets (IOBUF inp, IOBUF out, const char *dbg_f, int dbg_l)
 {
   PACKET pkt;
   int skip, rc = 0;
+
+  if (! out)
+    log_bug ("copy_all_packets: OUT may not be NULL.\n");
+
   do
     {
       init_packet (&pkt);
@@ -299,6 +354,10 @@ copy_all_packets (IOBUF inp, IOBUF out)
 {
   PACKET pkt;
   int skip, rc = 0;
+
+  if (! out)
+    log_bug ("copy_all_packets: OUT may not be NULL.\n");
+
   do
     {
       init_packet (&pkt);
@@ -383,12 +442,33 @@ skip_some_packets (IOBUF inp, unsigned n)
 #endif /*!DEBUG_PARSE_PACKET*/
 
 
-/*
- * Parse packet.  Stores 1 at SKIP 1 if the packet should be skipped;
- * this is the case if either ONLYKEYPKTS is set and the parsed packet
- * isn't a key packet or the packet-type is 0, indicating deleted
- * stuff.  If OUT is not NULL, a special copymode is used.
- */
+/* Parse a packet and save it in *PKT.
+
+   If OUT is not NULL and the packet is valid (its type is not 0),
+   then the header, the initial length field and the packet's contents
+   are written to OUT.  In this case, the packet is not saved in *PKT.
+
+   ONLYKEYPKTS is a simple packet filter.  If ONLYKEYPKTS is set to 1,
+   then only public subkey packets, public key packets, private subkey
+   packets and private key packets are parsed.  The rest are skipped
+   (i.e., the header and the contents are read from the pipeline and
+   discarded).  If ONLYKEYPKTS is set to 2, then in addition to the
+   above 4 types of packets, user id packets are also accepted.
+
+   DO_SKIP is a more coarse grained filter.  Unless ONLYKEYPKTS is set
+   to 2 and the packet is a user id packet, all packets are skipped.
+
+   Finally, if a packet is invalid (it's type is 0), it is skipped.
+
+   If a packet is skipped and SKIP is not NULL, then *SKIP is set to
+   1.
+
+   Note: ONLYKEYPKTS and DO_SKIP are only respected if OUT is NULL,
+   i.e., the packets are not simply being copied.
+
+   If RETPOS is not NULL, then the position of INP (as returned by
+   iobuf_tell) is saved there before any data is read from INP.
+  */
 static int
 parse (IOBUF inp, PACKET * pkt, int onlykeypkts, off_t * retpos,
        int *skip, IOBUF out, int do_skip
@@ -403,12 +483,21 @@ parse (IOBUF inp, PACKET * pkt, int onlykeypkts, off_t * retpos,
   int hdrlen;
   int new_ctb = 0, partial = 0;
   int with_uid = (onlykeypkts == 2);
+  off_t pos;
 
   *skip = 0;
-  assert (!pkt->pkt.generic);
-  if (retpos)
-    *retpos = iobuf_tell (inp);
+  log_assert (!pkt->pkt.generic);
+  if (retpos || list_mode)
+    {
+      pos = iobuf_tell (inp);
+      if (retpos)
+        *retpos = pos;
+    }
+  else
+    pos = 0; /* (silence compiler warning) */
 
+  /* The first byte of a packet is the so-called tag.  The highest bit
+     must be set.  */
   if ((ctb = iobuf_get (inp)) == -1)
     {
       rc = -1;
@@ -416,17 +505,31 @@ parse (IOBUF inp, PACKET * pkt, int onlykeypkts, off_t * retpos,
     }
   hdrlen = 0;
   hdr[hdrlen++] = ctb;
+
   if (!(ctb & 0x80))
     {
       log_error ("%s: invalid packet (ctb=%02x)\n", iobuf_where (inp), ctb);
       rc = gpg_error (GPG_ERR_INV_PACKET);
       goto leave;
     }
+
+  /* Immediately following the header is the length.  There are two
+     formats: the old format and the new format.  If bit 6 (where the
+     least significant bit is bit 0) is set in the tag, then we are
+     dealing with a new format packet.  Otherwise, it is an old format
+     packet.  */
   pktlen = 0;
   new_ctb = !!(ctb & 0x40);
   if (new_ctb)
     {
+      /* Get the packet's type.  This is encoded in the 6 least
+        significant bits of the tag.  */
       pkttype = ctb & 0x3f;
+
+      /* Extract the packet's length.  New format packets have 4 ways
+        to encode the packet length.  The value of the first byte
+        determines the encoding and partially determines the length.
+        See section 4.2.2 of RFC 4880 for details.  */
       if ((c = iobuf_get (inp)) == -1)
        {
          log_error ("%s: 1st length byte missing\n", iobuf_where (inp));
@@ -453,16 +556,21 @@ parse (IOBUF inp, PACKET * pkt, int onlykeypkts, off_t * retpos,
         }
       else if (c == 255)
         {
-          pktlen = (hdr[hdrlen++] = iobuf_get_noeof (inp)) << 24;
-          pktlen |= (hdr[hdrlen++] = iobuf_get_noeof (inp)) << 16;
-          pktlen |= (hdr[hdrlen++] = iobuf_get_noeof (inp)) << 8;
-          if ((c = iobuf_get (inp)) == -1)
+         int i;
+         char value[4];
+
+         for (i = 0; i < 4; i ++)
             {
-              log_error ("%s: 4 byte length invalid\n", iobuf_where (inp));
-              rc = gpg_error (GPG_ERR_INV_PACKET);
-              goto leave;
+              if ((c = iobuf_get (inp)) == -1)
+                {
+                  log_error ("%s: 4 byte length invalid\n", iobuf_where (inp));
+                  rc = gpg_error (GPG_ERR_INV_PACKET);
+                  goto leave;
+                }
+              value[i] = hdr[hdrlen++] = c;
             }
-          pktlen |= (hdr[hdrlen++] = c);
+
+         pktlen = buf32_to_ulong (value);
         }
       else /* Partial body length.  */
         {
@@ -472,13 +580,13 @@ parse (IOBUF inp, PACKET * pkt, int onlykeypkts, off_t * retpos,
             case PKT_ENCRYPTED:
             case PKT_ENCRYPTED_MDC:
             case PKT_COMPRESSED:
-              iobuf_set_partial_block_mode (inp, c & 0xff);
+              iobuf_set_partial_body_length_mode (inp, c & 0xff);
               pktlen = 0;      /* To indicate partial length.  */
               partial = 1;
               break;
 
             default:
-              log_error ("%s: partial length for invalid"
+              log_error ("%s: partial length invalid for"
                          " packet type %d\n", iobuf_where (inp), pkttype);
               rc = gpg_error (GPG_ERR_INV_PACKET);
               goto leave;
@@ -487,8 +595,13 @@ parse (IOBUF inp, PACKET * pkt, int onlykeypkts, off_t * retpos,
 
     }
   else
+    /* This is an old format packet.  */
     {
+      /* Extract the packet's type.  This is encoded in bits 2-5.  */
       pkttype = (ctb >> 2) & 0xf;
+
+      /* The type of length encoding is encoded in bits 0-1 of the
+        tag.  */
       lenbytes = ((ctb & 3) == 3) ? 0 : (1 << (ctb & 3));
       if (!lenbytes)
        {
@@ -510,12 +623,26 @@ parse (IOBUF inp, PACKET * pkt, int onlykeypkts, off_t * retpos,
          for (; lenbytes; lenbytes--)
            {
              pktlen <<= 8;
-             pktlen |= hdr[hdrlen++] = iobuf_get_noeof (inp);
+             c = iobuf_get (inp);
+             if (c == -1)
+               {
+                 log_error ("%s: length invalid\n", iobuf_where (inp));
+                 rc = gpg_error (GPG_ERR_INV_PACKET);
+                 goto leave;
+               }
+             pktlen |= hdr[hdrlen++] = c;
            }
        }
     }
 
-  if (pktlen == (unsigned long) (-1))
+  /* Sometimes the decompressing layer enters an error state in which
+     it simply outputs 0xff for every byte read.  If we have a stream
+     of 0xff bytes, then it will be detected as a new format packet
+     with type 63 and a 4-byte encoded length that is 4G-1.  Since
+     packets with type 63 are private and we use them as a control
+     packet, which won't be 4 GB, we reject such packets as
+     invalid.  */
+  if (pkttype == 63 && pktlen == 0xFFFFFFFF)
     {
       /* With some probability this is caused by a problem in the
        * the uncompressing layer - in some error cases it just loops
@@ -526,6 +653,17 @@ parse (IOBUF inp, PACKET * pkt, int onlykeypkts, off_t * retpos,
 
   if (out && pkttype)
     {
+      /* This type of copying won't work if the packet uses a partial
+        body length.  (In other words, this only works if HDR is
+        actually the length.)  Currently, no callers require this
+        functionality so we just log this as an error.  */
+      if (partial)
+       {
+         log_error ("parse: Can't copy partial packet.  Aborting.\n");
+         rc = gpg_error (GPG_ERR_INV_PACKET);
+         goto leave;
+       }
+
       rc = iobuf_write (out, hdr, hdrlen);
       if (!rc)
        rc = copy_packet (inp, out, pkttype, pktlen, partial);
@@ -533,9 +671,13 @@ parse (IOBUF inp, PACKET * pkt, int onlykeypkts, off_t * retpos,
     }
 
   if (with_uid && pkttype == PKT_USER_ID)
+    /* If ONLYKEYPKTS is set to 2, then we never skip user id packets,
+       even if DO_SKIP is set.  */
     ;
   else if (do_skip
+          /* type==0 is not allowed.  This is an invalid packet.  */
           || !pkttype
+          /* When ONLYKEYPKTS is set, we don't skip keys.  */
           || (onlykeypkts && pkttype != PKT_PUBLIC_SUBKEY
               && pkttype != PKT_PUBLIC_KEY
               && pkttype != PKT_SECRET_SUBKEY && pkttype != PKT_SECRET_KEY))
@@ -559,18 +701,21 @@ parse (IOBUF inp, PACKET * pkt, int onlykeypkts, off_t * retpos,
 #endif
     }
 
+  if (list_mode)
+    es_fprintf (listfp, "# off=%lu ctb=%02x tag=%d hlen=%d plen=%lu%s%s\n",
+                (unsigned long)pos, ctb, pkttype, hdrlen, pktlen,
+                partial? (new_ctb ? " partial" : " indeterminate") :"",
+                new_ctb? " new-ctb":"");
+
   pkt->pkttype = pkttype;
-  rc = G10ERR_UNKNOWN_PACKET;  /* default error */
+  rc = GPG_ERR_UNKNOWN_PACKET; /* default error */
   switch (pkttype)
     {
     case PKT_PUBLIC_KEY:
     case PKT_PUBLIC_SUBKEY:
-      pkt->pkt.public_key = xmalloc_clear (sizeof *pkt->pkt.public_key);
-      rc = parse_key (inp, pkttype, pktlen, hdr, hdrlen, pkt);
-      break;
     case PKT_SECRET_KEY:
     case PKT_SECRET_SUBKEY:
-      pkt->pkt.secret_key = xmalloc_clear (sizeof *pkt->pkt.secret_key);
+      pkt->pkt.public_key = xmalloc_clear (sizeof *pkt->pkt.public_key);
       rc = parse_key (inp, pkttype, pktlen, hdr, hdrlen, pkt);
       break;
     case PKT_SYMKEY_ENC:
@@ -622,14 +767,20 @@ parse (IOBUF inp, PACKET * pkt, int onlykeypkts, off_t * retpos,
       rc = parse_marker (inp, pkttype, pktlen);
       break;
     default:
+      /* Unknown packet.  Skip it.  */
       skip_packet (inp, pkttype, pktlen, partial);
       break;
     }
 
  leave:
+  /* FIXME: We leak in case of an error (see the xmalloc's above).  */
   if (!rc && iobuf_error (inp))
-    rc = G10ERR_INV_KEYRING;
-  return rc;
+    rc = GPG_ERR_INV_KEYRING;
+
+  /* FIXME: We use only the error code for now to avoid problems with
+     callers which have not been checked to always use gpg_err_code()
+     when comparing error codes.  */
+  return rc == -1? -1 : gpg_err_code (rc);
 }
 
 
@@ -639,18 +790,32 @@ dump_hex_line (int c, int *i)
   if (*i && !(*i % 8))
     {
       if (*i && !(*i % 24))
-       fprintf (listfp, "\n%4d:", *i);
+       es_fprintf (listfp, "\n%4d:", *i);
       else
-       putc (' ', listfp);
+       es_putc (' ', listfp);
     }
   if (c == -1)
-    fprintf (listfp, " EOF");
+    es_fprintf (listfp, " EOF");
   else
-    fprintf (listfp, " %02x", c);
+    es_fprintf (listfp, " %02x", c);
   ++*i;
 }
 
 
+/* Copy the contents of a packet from the pipeline IN to the pipeline
+   OUT.
+
+   The header and length have already been read from INP and the
+   decoded values are given as PKGTYPE and PKTLEN.
+
+   If the packet is a partial body length packet (RFC 4880, Section
+   4.2.2.4), then iobuf_set_partial_block_modeiobuf_set_partial_block_mode
+   should already have been called on INP and PARTIAL should be set.
+
+   If PARTIAL is set or PKTLEN is 0 and PKTTYPE is PKT_COMPRESSED,
+   copy until the first EOF is encountered on INP.
+
+   Returns 0 on success and an error code if an error occurs.  */
 static int
 copy_packet (IOBUF inp, IOBUF out, int pkttype,
             unsigned long pktlen, int partial)
@@ -661,7 +826,7 @@ copy_packet (IOBUF inp, IOBUF out, int pkttype,
 
   if (partial)
     {
-      while ((n = iobuf_read (inp, buf, 100)) != -1)
+      while ((n = iobuf_read (inp, buf, sizeof (buf))) != -1)
        if ((rc = iobuf_write (out, buf, n)))
          return rc;            /* write error */
     }
@@ -669,7 +834,7 @@ copy_packet (IOBUF inp, IOBUF out, int pkttype,
     {
       log_debug ("copy_packet: compressed!\n");
       /* compressed packet, copy till EOF */
-      while ((n = iobuf_read (inp, buf, 100)) != -1)
+      while ((n = iobuf_read (inp, buf, sizeof (buf))) != -1)
        if ((rc = iobuf_write (out, buf, n)))
          return rc;            /* write error */
     }
@@ -677,7 +842,7 @@ copy_packet (IOBUF inp, IOBUF out, int pkttype,
     {
       for (; pktlen; pktlen -= n)
        {
-         n = pktlen > 100 ? 100 : pktlen;
+         n = pktlen > sizeof (buf) ? sizeof (buf) : pktlen;
          n = iobuf_read (inp, buf, n);
          if (n == -1)
            return gpg_error (GPG_ERR_EOF);
@@ -689,17 +854,20 @@ copy_packet (IOBUF inp, IOBUF out, int pkttype,
 }
 
 
+/* Skip an unknown packet.  PKTTYPE is the packet's type, PKTLEN is
+   the length of the packet's content and PARTIAL is whether partial
+   body length encoding in used (in this case PKTLEN is ignored).  */
 static void
 skip_packet (IOBUF inp, int pkttype, unsigned long pktlen, int partial)
 {
   if (list_mode)
     {
-      fprintf (listfp, ":unknown packet: type %2d, length %lu\n",
-              pkttype, pktlen);
+      es_fprintf (listfp, ":unknown packet: type %2d, length %lu\n",
+                  pkttype, pktlen);
       if (pkttype)
        {
          int c, i = 0;
-         fputs ("dump:", listfp);
+         es_fputs ("dump:", listfp);
          if (partial)
            {
              while ((c = iobuf_get (inp)) != -1)
@@ -714,7 +882,7 @@ skip_packet (IOBUF inp, int pkttype, unsigned long pktlen, int partial)
                    break;
                }
            }
-         putc ('\n', listfp);
+         es_putc ('\n', listfp);
          return;
        }
     }
@@ -722,27 +890,94 @@ skip_packet (IOBUF inp, int pkttype, unsigned long pktlen, int partial)
 }
 
 
+/* Read PKTLEN bytes form INP and return them in a newly allocated
+   buffer.  In case of an error (including reading fewer than PKTLEN
+   bytes from INP before EOF is returned), NULL is returned and an
+   error message is logged.  */
 static void *
-read_rest (IOBUF inp, size_t pktlen, int partial)
+read_rest (IOBUF inp, size_t pktlen)
 {
-  byte *p;
-  int i;
+  int c;
+  byte *buf, *p;
 
-  if (partial)
+  buf = xtrymalloc (pktlen);
+  if (!buf)
     {
-      log_error ("read_rest: can't store stream data\n");
-      p = NULL;
+      gpg_error_t err = gpg_error_from_syserror ();
+      log_error ("error reading rest of packet: %s\n", gpg_strerror (err));
+      return NULL;
     }
-  else
+  for (p = buf; pktlen; pktlen--)
     {
-      p = xmalloc (pktlen);
-      for (i = 0; pktlen; pktlen--, i++)
-       p[i] = iobuf_get (inp);
+      c = iobuf_get (inp);
+      if (c == -1)
+        {
+          log_error ("premature eof while reading rest of packet\n");
+          xfree (buf);
+          return NULL;
+        }
+      *p++ = c;
     }
-  return p;
+
+  return buf;
+}
+
+
+/* Read a special size+body from INP.  On success store an opaque MPI
+   with it at R_DATA.  On error return an error code and store NULL at
+   R_DATA.  Even in the error case store the number of read bytes at
+   R_NREAD.  The caller shall pass the remaining size of the packet in
+   PKTLEN.  */
+static gpg_error_t
+read_size_body (iobuf_t inp, int pktlen, size_t *r_nread,
+                gcry_mpi_t *r_data)
+{
+  char buffer[256];
+  char *tmpbuf;
+  int i, c, nbytes;
+
+  *r_nread = 0;
+  *r_data = NULL;
+
+  if (!pktlen)
+    return gpg_error (GPG_ERR_INV_PACKET);
+  c = iobuf_readbyte (inp);
+  if (c < 0)
+    return gpg_error (GPG_ERR_INV_PACKET);
+  pktlen--;
+  ++*r_nread;
+  nbytes = c;
+  if (nbytes < 2 || nbytes > 254)
+    return gpg_error (GPG_ERR_INV_PACKET);
+  if (nbytes > pktlen)
+    return gpg_error (GPG_ERR_INV_PACKET);
+
+  buffer[0] = nbytes;
+
+  for (i = 0; i < nbytes; i++)
+    {
+      c = iobuf_get (inp);
+      if (c < 0)
+        return gpg_error (GPG_ERR_INV_PACKET);
+      ++*r_nread;
+      buffer[1+i] = c;
+    }
+
+  tmpbuf = xtrymalloc (1 + nbytes);
+  if (!tmpbuf)
+    return gpg_error_from_syserror ();
+  memcpy (tmpbuf, buffer, 1 + nbytes);
+  *r_data = gcry_mpi_set_opaque (NULL, tmpbuf, 8 * (1 + nbytes));
+  if (!*r_data)
+    {
+      xfree (tmpbuf);
+      return gpg_error_from_syserror ();
+    }
+  return 0;
 }
 
 
+/* Parse a marker packet.  */
 static int
 parse_marker (IOBUF inp, int pkttype, unsigned long pktlen)
 {
@@ -770,14 +1005,16 @@ parse_marker (IOBUF inp, int pkttype, unsigned long pktlen)
     }
 
   if (list_mode)
-    fputs (":marker packet: PGP\n", listfp);
+    es_fputs (":marker packet: PGP\n", listfp);
 
   return 0;
 
  fail:
   log_error ("invalid marker packet\n");
+  if (list_mode)
+    es_fputs (":marker packet: [invalid]\n", listfp);
   iobuf_skip_rest (inp, pktlen, 0);
-  return G10ERR_INVALID_PACKET;
+  return GPG_ERR_INV_PACKET;
 }
 
 
@@ -792,6 +1029,8 @@ parse_symkeyenc (IOBUF inp, int pkttype, unsigned long pktlen,
   if (pktlen < 4)
     {
       log_error ("packet(%d) too short\n", pkttype);
+      if (list_mode)
+        es_fprintf (listfp, ":symkey enc packet: [too short]\n");
       rc = gpg_error (GPG_ERR_INV_PACKET);
       goto leave;
     }
@@ -800,12 +1039,16 @@ parse_symkeyenc (IOBUF inp, int pkttype, unsigned long pktlen,
   if (version != 4)
     {
       log_error ("packet(%d) with unknown version %d\n", pkttype, version);
+      if (list_mode)
+        es_fprintf (listfp, ":symkey enc packet: [unknown version]\n");
       rc = gpg_error (GPG_ERR_INV_PACKET);
       goto leave;
     }
   if (pktlen > 200)
     {                          /* (we encode the seskeylen in a byte) */
       log_error ("packet(%d) too large\n", pkttype);
+      if (list_mode)
+        es_fprintf (listfp, ":symkey enc packet: [too large]\n");
       rc = gpg_error (GPG_ERR_INV_PACKET);
       goto leave;
     }
@@ -828,11 +1071,15 @@ parse_symkeyenc (IOBUF inp, int pkttype, unsigned long pktlen,
       break;
     default:
       log_error ("unknown S2K mode %d\n", s2kmode);
+      if (list_mode)
+        es_fprintf (listfp, ":symkey enc packet: [unknown S2K mode]\n");
       goto leave;
     }
   if (minlen > pktlen)
     {
       log_error ("packet with S2K %d too short\n", s2kmode);
+      if (list_mode)
+        es_fprintf (listfp, ":symkey enc packet: [too short]\n");
       rc = gpg_error (GPG_ERR_INV_PACKET);
       goto leave;
     }
@@ -866,26 +1113,25 @@ parse_symkeyenc (IOBUF inp, int pkttype, unsigned long pktlen,
        log_info (_("WARNING: potentially insecure symmetrically"
                    " encrypted session key\n"));
     }
-  assert (!pktlen);
+  log_assert (!pktlen);
 
   if (list_mode)
     {
-      fprintf (listfp,
-              ":symkey enc packet: version %d, cipher %d, s2k %d, hash %d",
-              version, cipher_algo, s2kmode, hash_algo);
+      es_fprintf (listfp,
+                  ":symkey enc packet: version %d, cipher %d, s2k %d, hash %d",
+                  version, cipher_algo, s2kmode, hash_algo);
       if (seskeylen)
-       fprintf (listfp, ", seskey %d bits", (seskeylen - 1) * 8);
-      fprintf (listfp, "\n");
+       es_fprintf (listfp, ", seskey %d bits", (seskeylen - 1) * 8);
+      es_fprintf (listfp, "\n");
       if (s2kmode == 1 || s2kmode == 3)
        {
-         fprintf (listfp, "\tsalt ");
-         for (i = 0; i < 8; i++)
-           fprintf (listfp, "%02x", k->s2k.salt[i]);
+         es_fprintf (listfp, "\tsalt ");
+          es_write_hexstring (listfp, k->s2k.salt, 8, 0, NULL);
          if (s2kmode == 3)
-           fprintf (listfp, ", count %lu (%lu)",
-                    S2K_DECODE_COUNT ((ulong) k->s2k.count),
-                    (ulong) k->s2k.count);
-         fprintf (listfp, "\n");
+           es_fprintf (listfp, ", count %lu (%lu)",
+                        S2K_DECODE_COUNT ((ulong) k->s2k.count),
+                        (ulong) k->s2k.count);
+         es_fprintf (listfp, "\n");
        }
     }
 
@@ -899,7 +1145,6 @@ static int
 parse_pubkeyenc (IOBUF inp, int pkttype, unsigned long pktlen,
                 PACKET * packet)
 {
-  unsigned int n;
   int rc = 0;
   int i, ndata;
   PKT_pubkey_enc *k;
@@ -908,6 +1153,8 @@ parse_pubkeyenc (IOBUF inp, int pkttype, unsigned long pktlen,
   if (pktlen < 12)
     {
       log_error ("packet(%d) too short\n", pkttype);
+      if (list_mode)
+        es_fputs (":pubkey enc packet: [too short]\n", listfp);
       rc = gpg_error (GPG_ERR_INV_PACKET);
       goto leave;
     }
@@ -916,6 +1163,8 @@ parse_pubkeyenc (IOBUF inp, int pkttype, unsigned long pktlen,
   if (k->version != 2 && k->version != 3)
     {
       log_error ("packet(%d) with unknown version %d\n", pkttype, k->version);
+      if (list_mode)
+        es_fputs (":pubkey enc packet: [unknown version]\n", listfp);
       rc = gpg_error (GPG_ERR_INV_PACKET);
       goto leave;
     }
@@ -927,35 +1176,46 @@ parse_pubkeyenc (IOBUF inp, int pkttype, unsigned long pktlen,
   pktlen--;
   k->throw_keyid = 0;  /* Only used as flag for build_packet.  */
   if (list_mode)
-    fprintf (listfp,
-            ":pubkey enc packet: version %d, algo %d, keyid %08lX%08lX\n",
-            k->version, k->pubkey_algo, (ulong) k->keyid[0],
-            (ulong) k->keyid[1]);
+    es_fprintf (listfp,
+                ":pubkey enc packet: version %d, algo %d, keyid %08lX%08lX\n",
+                k->version, k->pubkey_algo, (ulong) k->keyid[0],
+                (ulong) k->keyid[1]);
 
   ndata = pubkey_get_nenc (k->pubkey_algo);
   if (!ndata)
     {
       if (list_mode)
-       fprintf (listfp, "\tunsupported algorithm %d\n", k->pubkey_algo);
+       es_fprintf (listfp, "\tunsupported algorithm %d\n", k->pubkey_algo);
       unknown_pubkey_warning (k->pubkey_algo);
       k->data[0] = NULL; /* No need to store the encrypted data.  */
     }
   else
     {
       for (i = 0; i < ndata; i++)
-       {
-         n = pktlen;
-         k->data[i] = mpi_read (inp, &n, 0);
-         pktlen -= n;
-         if (list_mode)
-           {
-             fprintf (listfp, "\tdata: ");
-             mpi_print (listfp, k->data[i], mpi_print_mode);
-             putc ('\n', listfp);
-           }
-         if (!k->data[i])
-           rc = gpg_error (GPG_ERR_INV_PACKET);
-       }
+        {
+          if (k->pubkey_algo == PUBKEY_ALGO_ECDH && i == 1)
+            {
+              size_t n;
+             rc = read_size_body (inp, pktlen, &n, k->data+i);
+              pktlen -= n;
+            }
+          else
+            {
+             int n = pktlen;
+              k->data[i] = mpi_read (inp, &n, 0);
+              pktlen -= n;
+              if (!k->data[i])
+                rc = gpg_error (GPG_ERR_INV_PACKET);
+            }
+          if (rc)
+            goto leave;
+          if (list_mode)
+            {
+              es_fprintf (listfp, "\tdata: ");
+              mpi_print (listfp, k->data[i], mpi_print_mode);
+              es_putc ('\n', listfp);
+            }
+        }
     }
 
  leave:
@@ -964,6 +1224,12 @@ parse_pubkeyenc (IOBUF inp, int pkttype, unsigned long pktlen,
 }
 
 
+/* Dump a subpacket to LISTFP.  BUFFER contains the subpacket in
+   question and points to the type field in the subpacket header (not
+   the start of the header).  TYPE is the subpacket's type with the
+   critical bit cleared.  CRITICAL is the value of the CRITICAL bit.
+   BUFLEN is the length of the buffer and LENGTH is the length of the
+   subpacket according to the subpacket's header.  */
 static void
 dump_sig_subpkt (int hashed, int type, int critical,
                 const byte * buffer, size_t buflen, size_t length)
@@ -976,98 +1242,115 @@ dump_sig_subpkt (int hashed, int type, int critical,
    * and add an additional notice.  */
   if (type == SIGSUBPKT_ARR && !hashed)
     {
-      fprintf (listfp,
-              "\tsubpkt %d len %u (additional recipient request)\n"
-              "WARNING: PGP versions > 5.0 and < 6.5.8 will automagically "
-              "encrypt to this key and thereby reveal the plaintext to "
-              "the owner of this ARR key. Detailed info follows:\n",
-              type, (unsigned) length);
+      es_fprintf (listfp,
+                  "\tsubpkt %d len %u (additional recipient request)\n"
+                  "WARNING: PGP versions > 5.0 and < 6.5.8 will automagically "
+                  "encrypt to this key and thereby reveal the plaintext to "
+                  "the owner of this ARR key. Detailed info follows:\n",
+                  type, (unsigned) length);
     }
 
   buffer++;
   length--;
 
-  fprintf (listfp, "\t%s%ssubpkt %d len %u (", /*) */
-          critical ? "critical " : "",
-          hashed ? "hashed " : "", type, (unsigned) length);
+  es_fprintf (listfp, "\t%s%ssubpkt %d len %u (",      /*) */
+              critical ? "critical " : "",
+              hashed ? "hashed " : "", type, (unsigned) length);
   if (length > buflen)
     {
-      fprintf (listfp, "too short: buffer is only %u)\n", (unsigned) buflen);
+      es_fprintf (listfp, "too short: buffer is only %u)\n", (unsigned) buflen);
       return;
     }
   switch (type)
     {
     case SIGSUBPKT_SIG_CREATED:
       if (length >= 4)
-       fprintf (listfp, "sig created %s",
-                strtimestamp (buffer_to_u32 (buffer)));
+       es_fprintf (listfp, "sig created %s",
+                    strtimestamp (buf32_to_u32 (buffer)));
       break;
     case SIGSUBPKT_SIG_EXPIRE:
       if (length >= 4)
        {
-         if (buffer_to_u32 (buffer))
-           fprintf (listfp, "sig expires after %s",
-                    strtimevalue (buffer_to_u32 (buffer)));
+         if (buf32_to_u32 (buffer))
+           es_fprintf (listfp, "sig expires after %s",
+                        strtimevalue (buf32_to_u32 (buffer)));
          else
-           fprintf (listfp, "sig does not expire");
+           es_fprintf (listfp, "sig does not expire");
        }
       break;
     case SIGSUBPKT_EXPORTABLE:
       if (length)
-       fprintf (listfp, "%sexportable", *buffer ? "" : "not ");
+       es_fprintf (listfp, "%sexportable", *buffer ? "" : "not ");
       break;
     case SIGSUBPKT_TRUST:
       if (length != 2)
        p = "[invalid trust subpacket]";
       else
-       fprintf (listfp, "trust signature of depth %d, value %d", buffer[0],
-                buffer[1]);
+       es_fprintf (listfp, "trust signature of depth %d, value %d", buffer[0],
+                    buffer[1]);
       break;
     case SIGSUBPKT_REGEXP:
       if (!length)
        p = "[invalid regexp subpacket]";
       else
-       fprintf (listfp, "regular expression: \"%s\"", buffer);
+        {
+          es_fprintf (listfp, "regular expression: \"");
+          es_write_sanitized (listfp, buffer, length, "\"", NULL);
+          p = "\"";
+        }
       break;
     case SIGSUBPKT_REVOCABLE:
       if (length)
-       fprintf (listfp, "%srevocable", *buffer ? "" : "not ");
+       es_fprintf (listfp, "%srevocable", *buffer ? "" : "not ");
       break;
     case SIGSUBPKT_KEY_EXPIRE:
       if (length >= 4)
        {
-         if (buffer_to_u32 (buffer))
-           fprintf (listfp, "key expires after %s",
-                    strtimevalue (buffer_to_u32 (buffer)));
+         if (buf32_to_u32 (buffer))
+           es_fprintf (listfp, "key expires after %s",
+                        strtimevalue (buf32_to_u32 (buffer)));
          else
-           fprintf (listfp, "key does not expire");
+           es_fprintf (listfp, "key does not expire");
        }
       break;
     case SIGSUBPKT_PREF_SYM:
-      fputs ("pref-sym-algos:", listfp);
+      es_fputs ("pref-sym-algos:", listfp);
       for (i = 0; i < length; i++)
-       fprintf (listfp, " %d", buffer[i]);
+       es_fprintf (listfp, " %d", buffer[i]);
       break;
     case SIGSUBPKT_REV_KEY:
-      fputs ("revocation key: ", listfp);
+      es_fputs ("revocation key: ", listfp);
       if (length < 22)
        p = "[too short]";
       else
        {
-         fprintf (listfp, "c=%02x a=%d f=", buffer[0], buffer[1]);
+         es_fprintf (listfp, "c=%02x a=%d f=", buffer[0], buffer[1]);
          for (i = 2; i < length; i++)
-           fprintf (listfp, "%02X", buffer[i]);
+           es_fprintf (listfp, "%02X", buffer[i]);
        }
       break;
     case SIGSUBPKT_ISSUER:
       if (length >= 8)
-       fprintf (listfp, "issuer key ID %08lX%08lX",
-                (ulong) buffer_to_u32 (buffer),
-                (ulong) buffer_to_u32 (buffer + 4));
+       es_fprintf (listfp, "issuer key ID %08lX%08lX",
+                    (ulong) buf32_to_u32 (buffer),
+                    (ulong) buf32_to_u32 (buffer + 4));
+      break;
+    case SIGSUBPKT_ISSUER_FPR:
+      if (length >= 21)
+        {
+          char *tmp;
+          es_fprintf (listfp, "issuer fpr v%d ", buffer[0]);
+          tmp = bin2hex (buffer+1, length-1, NULL);
+          if (tmp)
+            {
+              es_fputs (tmp, listfp);
+              xfree (tmp);
+            }
+        }
       break;
     case SIGSUBPKT_NOTATION:
       {
-       fputs ("notation: ", listfp);
+       es_fputs ("notation: ", listfp);
        if (length < 8)
          p = "[too short]";
        else
@@ -1082,11 +1365,11 @@ dump_sig_subpkt (int hashed, int type, int critical,
              p = "[error]";
            else
              {
-               print_string (listfp, s, n1, ')');
-               putc ('=', listfp);
+               es_write_sanitized (listfp, s, n1, ")", NULL);
+               es_putc ('=', listfp);
 
                if (*buffer & 0x80)
-                 print_string (listfp, s + n1, n2, ')');
+                 es_write_sanitized (listfp, s + n1, n2, ")", NULL);
                else
                  p = "[not human readable]";
              }
@@ -1094,35 +1377,35 @@ dump_sig_subpkt (int hashed, int type, int critical,
       }
       break;
     case SIGSUBPKT_PREF_HASH:
-      fputs ("pref-hash-algos:", listfp);
+      es_fputs ("pref-hash-algos:", listfp);
       for (i = 0; i < length; i++)
-       fprintf (listfp, " %d", buffer[i]);
+       es_fprintf (listfp, " %d", buffer[i]);
       break;
     case SIGSUBPKT_PREF_COMPR:
-      fputs ("pref-zip-algos:", listfp);
+      es_fputs ("pref-zip-algos:", listfp);
       for (i = 0; i < length; i++)
-       fprintf (listfp, " %d", buffer[i]);
+       es_fprintf (listfp, " %d", buffer[i]);
       break;
     case SIGSUBPKT_KS_FLAGS:
-      fputs ("key server preferences:", listfp);
+      es_fputs ("keyserver preferences:", listfp);
       for (i = 0; i < length; i++)
-       fprintf (listfp, " %02X", buffer[i]);
+       es_fprintf (listfp, " %02X", buffer[i]);
       break;
     case SIGSUBPKT_PREF_KS:
-      fputs ("preferred key server: ", listfp);
-      print_string (listfp, buffer, length, ')');
+      es_fputs ("preferred keyserver: ", listfp);
+      es_write_sanitized (listfp, buffer, length, ")", NULL);
       break;
     case SIGSUBPKT_PRIMARY_UID:
       p = "primary user ID";
       break;
     case SIGSUBPKT_POLICY:
-      fputs ("policy: ", listfp);
-      print_string (listfp, buffer, length, ')');
+      es_fputs ("policy: ", listfp);
+      es_write_sanitized (listfp, buffer, length, ")", NULL);
       break;
     case SIGSUBPKT_KEY_FLAGS:
-      fputs ("key flags:", listfp);
+      es_fputs ("key flags:", listfp);
       for (i = 0; i < length; i++)
-       fprintf (listfp, " %02X", buffer[i]);
+       es_fprintf (listfp, " %02X", buffer[i]);
       break;
     case SIGSUBPKT_SIGNERS_UID:
       p = "signer's user ID";
@@ -1130,37 +1413,37 @@ dump_sig_subpkt (int hashed, int type, int critical,
     case SIGSUBPKT_REVOC_REASON:
       if (length)
        {
-         fprintf (listfp, "revocation reason 0x%02x (", *buffer);
-         print_string (listfp, buffer + 1, length - 1, ')');
+         es_fprintf (listfp, "revocation reason 0x%02x (", *buffer);
+         es_write_sanitized (listfp, buffer + 1, length - 1, ")", NULL);
          p = ")";
        }
       break;
     case SIGSUBPKT_ARR:
-      fputs ("Big Brother's key (ignored): ", listfp);
+      es_fputs ("Big Brother's key (ignored): ", listfp);
       if (length < 22)
        p = "[too short]";
       else
        {
-         fprintf (listfp, "c=%02x a=%d f=", buffer[0], buffer[1]);
-         for (i = 2; i < length; i++)
-           fprintf (listfp, "%02X", buffer[i]);
+         es_fprintf (listfp, "c=%02x a=%d f=", buffer[0], buffer[1]);
+          if (length > 2)
+            es_write_hexstring (listfp, buffer+2, length-2, 0, NULL);
        }
       break;
     case SIGSUBPKT_FEATURES:
-      fputs ("features:", listfp);
+      es_fputs ("features:", listfp);
       for (i = 0; i < length; i++)
-       fprintf (listfp, " %02x", buffer[i]);
+       es_fprintf (listfp, " %02x", buffer[i]);
       break;
     case SIGSUBPKT_SIGNATURE:
-      fputs ("signature: ", listfp);
+      es_fputs ("signature: ", listfp);
       if (length < 17)
        p = "[too short]";
       else
-       fprintf (listfp, "v%d, class 0x%02X, algo %d, digest algo %d",
-                buffer[0],
-                buffer[0] == 3 ? buffer[2] : buffer[1],
-                buffer[0] == 3 ? buffer[15] : buffer[2],
-                buffer[0] == 3 ? buffer[16] : buffer[3]);
+       es_fprintf (listfp, "v%d, class 0x%02X, algo %d, digest algo %d",
+                    buffer[0],
+                    buffer[0] == 3 ? buffer[2] : buffer[1],
+                    buffer[0] == 3 ? buffer[15] : buffer[2],
+                    buffer[0] == 3 ? buffer[16] : buffer[3]);
       break;
     default:
       if (type >= 100 && type <= 110)
@@ -1170,7 +1453,7 @@ dump_sig_subpkt (int hashed, int type, int critical,
       break;
     }
 
-  fprintf (listfp, "%s)\n", p ? p : "");
+  es_fprintf (listfp, "%s)\n", p ? p : "");
 }
 
 
@@ -1215,6 +1498,10 @@ parse_one_sig_subpkt (const byte * buffer, size_t n, int type)
       if (n < 8)
        break;
       return 0;
+    case SIGSUBPKT_ISSUER_FPR: /* issuer key ID */
+      if (n < 21)
+       break;
+      return 0;
     case SIGSUBPKT_NOTATION:
       /* minimum length needed, and the subpacket must be well-formed
          where the name length and value length all fit inside the
@@ -1259,10 +1546,12 @@ can_handle_critical (const byte * buffer, size_t n, int type)
     {
     case SIGSUBPKT_NOTATION:
       if (n >= 8)
-       return can_handle_critical_notation (buffer + 8,
-                                            (buffer[4] << 8) | buffer[5]);
-      else
-       return 0;
+       {
+         size_t notation_len = ((buffer[4] << 8) | buffer[5]);
+         if (n - 8 >= notation_len)
+           return can_handle_critical_notation (buffer + 8, notation_len);
+       }
+      return 0;
     case SIGSUBPKT_SIGNATURE:
     case SIGSUBPKT_SIG_CREATED:
     case SIGSUBPKT_SIG_EXPIRE:
@@ -1271,6 +1560,7 @@ can_handle_critical (const byte * buffer, size_t n, int type)
     case SIGSUBPKT_REVOCABLE:
     case SIGSUBPKT_REV_KEY:
     case SIGSUBPKT_ISSUER:     /* issuer key ID */
+    case SIGSUBPKT_ISSUER_FPR: /* issuer fingerprint */
     case SIGSUBPKT_PREF_SYM:
     case SIGSUBPKT_PREF_HASH:
     case SIGSUBPKT_PREF_COMPR:
@@ -1308,11 +1598,10 @@ enum_sig_subpkt (const subpktarea_t * pktbuf, sigsubpkttype_t reqtype,
 
   if (!pktbuf || reqseq == -1)
     {
-      /* return some value different from NULL to indicate that
-       * there is no critical bit we do not understand.  The caller
-       * will never use the value.  Yes I know, it is an ugly hack */
-      return reqtype ==
-       SIGSUBPKT_TEST_CRITICAL ? (const byte *) &pktbuf : NULL;
+      static char dummy[] = "x";
+      /* Return a value different from NULL to indicate that
+       * there is no critical bit we do not understand.  */
+      return reqtype ==        SIGSUBPKT_TEST_CRITICAL ? dummy : NULL;
     }
   buffer = pktbuf->data;
   buflen = pktbuf->len;
@@ -1321,16 +1610,15 @@ enum_sig_subpkt (const subpktarea_t * pktbuf, sigsubpkttype_t reqtype,
       n = *buffer++;
       buflen--;
       if (n == 255) /* 4 byte length header.  */
-       {                       
+       {
          if (buflen < 4)
            goto too_short;
-         n = (buffer[0] << 24) | (buffer[1] << 16)
-           | (buffer[2] << 8) | buffer[3];
+         n = buf32_to_size_t (buffer);
          buffer += 4;
          buflen -= 4;
        }
       else if (n >= 192) /* 4 byte special encoded length header.  */
-       {                       
+       {
          if (buflen < 2)
            goto too_short;
          n = ((n - 192) << 8) + *buffer + 192;
@@ -1396,7 +1684,11 @@ enum_sig_subpkt (const subpktarea_t * pktbuf, sigsubpkttype_t reqtype,
       buflen -= n;
     }
   if (reqtype == SIGSUBPKT_TEST_CRITICAL)
-    return buffer;  /* Used as True to indicate that there is no. */
+    /* Returning NULL means we found a subpacket with the critical bit
+       set that we don't grok.  We've iterated over all the subpackets
+       and haven't found such a packet so we need to return a non-NULL
+       value.  */
+    return buffer;
 
   /* Critical bit we don't understand. */
   if (start)
@@ -1421,14 +1713,13 @@ parse_sig_subpkt (const subpktarea_t * buffer, sigsubpkttype_t reqtype,
 
 
 const byte *
-parse_sig_subpkt2 (PKT_signature * sig, sigsubpkttype_t reqtype,
-                  size_t * ret_n)
+parse_sig_subpkt2 (PKT_signature * sig, sigsubpkttype_t reqtype)
 {
   const byte *p;
 
-  p = parse_sig_subpkt (sig->hashed, reqtype, ret_n);
+  p = parse_sig_subpkt (sig->hashed, reqtype, NULL);
   if (!p)
-    p = parse_sig_subpkt (sig->unhashed, reqtype, ret_n);
+    p = parse_sig_subpkt (sig->unhashed, reqtype, NULL);
   return p;
 }
 
@@ -1437,25 +1728,31 @@ parse_sig_subpkt2 (PKT_signature * sig, sigsubpkttype_t reqtype,
 void
 parse_revkeys (PKT_signature * sig)
 {
-  struct revocation_key *revkey;
+  const byte *revkey;
   int seq = 0;
   size_t len;
 
   if (sig->sig_class != 0x1F)
     return;
 
-  while ((revkey =
-         (struct revocation_key *) enum_sig_subpkt (sig->hashed,
-                                                    SIGSUBPKT_REV_KEY,
-                                                    &len, &seq, NULL)))
+  while ((revkey = enum_sig_subpkt (sig->hashed, SIGSUBPKT_REV_KEY,
+                                   &len, &seq, NULL)))
     {
-      if (len == sizeof (struct revocation_key) 
-          && (revkey->class & 0x80))  /* 0x80 bit must be set.  */
+      if (/* The only valid length is 22 bytes.  See RFC 4880
+            5.2.3.15.  */
+         len == 22
+         /* 0x80 bit must be set on the class.  */
+          && (revkey[0] & 0x80))
        {
          sig->revkey = xrealloc (sig->revkey,
-                                 sizeof (struct revocation_key *) *
+                                 sizeof (struct revocation_key) *
                                  (sig->numrevkeys + 1));
-         sig->revkey[sig->numrevkeys] = revkey;
+
+         /* Copy the individual fields.  */
+         sig->revkey[sig->numrevkeys].class = revkey[0];
+         sig->revkey[sig->numrevkeys].algid = revkey[1];
+         memcpy (sig->revkey[sig->numrevkeys].fpr, &revkey[2], 20);
+
          sig->numrevkeys++;
        }
     }
@@ -1475,6 +1772,8 @@ parse_signature (IOBUF inp, int pkttype, unsigned long pktlen,
   if (pktlen < 16)
     {
       log_error ("packet(%d) too short\n", pkttype);
+      if (list_mode)
+        es_fputs (":signature packet: [too short]\n", listfp);
       goto leave;
     }
   sig->version = iobuf_get_noeof (inp);
@@ -1485,19 +1784,27 @@ parse_signature (IOBUF inp, int pkttype, unsigned long pktlen,
     {
       log_error ("packet(%d) with unknown version %d\n",
                 pkttype, sig->version);
+      if (list_mode)
+        es_fputs (":signature packet: [unknown version]\n", listfp);
       rc = gpg_error (GPG_ERR_INV_PACKET);
       goto leave;
     }
 
   if (!is_v4)
     {
+      if (pktlen == 0)
+       goto underflow;
       md5_len = iobuf_get_noeof (inp);
       pktlen--;
     }
+  if (pktlen == 0)
+    goto underflow;
   sig->sig_class = iobuf_get_noeof (inp);
   pktlen--;
   if (!is_v4)
     {
+      if (pktlen < 12)
+       goto underflow;
       sig->timestamp = read_32 (inp);
       pktlen -= 4;
       sig->keyid[0] = read_32 (inp);
@@ -1505,6 +1812,8 @@ parse_signature (IOBUF inp, int pkttype, unsigned long pktlen,
       sig->keyid[1] = read_32 (inp);
       pktlen -= 4;
     }
+  if (pktlen < 2)
+    goto underflow;
   sig->pubkey_algo = iobuf_get_noeof (inp);
   pktlen--;
   sig->digest_algo = iobuf_get_noeof (inp);
@@ -1512,13 +1821,19 @@ parse_signature (IOBUF inp, int pkttype, unsigned long pktlen,
   sig->flags.exportable = 1;
   sig->flags.revocable = 1;
   if (is_v4) /* Read subpackets.  */
-    { 
+    {
+      if (pktlen < 2)
+       goto underflow;
       n = read_16 (inp);
       pktlen -= 2;  /* Length of hashed data. */
+      if (pktlen < n)
+       goto underflow;
       if (n > 10000)
        {
          log_error ("signature packet: hashed data too long\n");
-         rc = G10ERR_INVALID_PACKET;
+          if (list_mode)
+            es_fputs (":signature packet: [hashed data too long]\n", listfp);
+         rc = GPG_ERR_INV_PACKET;
          goto leave;
        }
       if (n)
@@ -1530,17 +1845,25 @@ parse_signature (IOBUF inp, int pkttype, unsigned long pktlen,
            {
              log_error ("premature eof while reading "
                         "hashed signature data\n");
+              if (list_mode)
+                es_fputs (":signature packet: [premature eof]\n", listfp);
              rc = -1;
              goto leave;
            }
          pktlen -= n;
        }
+      if (pktlen < 2)
+       goto underflow;
       n = read_16 (inp);
       pktlen -= 2;  /* Length of unhashed data.  */
+      if (pktlen < n)
+       goto underflow;
       if (n > 10000)
        {
          log_error ("signature packet: unhashed data too long\n");
-         rc = G10ERR_INVALID_PACKET;
+          if (list_mode)
+            es_fputs (":signature packet: [unhashed data too long]\n", listfp);
+         rc = GPG_ERR_INV_PACKET;
          goto leave;
        }
       if (n)
@@ -1552,6 +1875,8 @@ parse_signature (IOBUF inp, int pkttype, unsigned long pktlen,
            {
              log_error ("premature eof while reading "
                         "unhashed signature data\n");
+              if (list_mode)
+                es_fputs (":signature packet: [premature eof]\n", listfp);
              rc = -1;
              goto leave;
            }
@@ -1559,20 +1884,15 @@ parse_signature (IOBUF inp, int pkttype, unsigned long pktlen,
        }
     }
 
-  if (pktlen < 5)  /* Sanity check.  */
-    {                          
-      log_error ("packet(%d) too short\n", pkttype);
-      rc = G10ERR_INVALID_PACKET;
-      goto leave;
-    }
-
+  if (pktlen < 2)
+    goto underflow;
   sig->digest_start[0] = iobuf_get_noeof (inp);
   pktlen--;
   sig->digest_start[1] = iobuf_get_noeof (inp);
   pktlen--;
 
   if (is_v4 && sig->pubkey_algo)  /* Extract required information.  */
-    {                  
+    {
       const byte *p;
       size_t len;
 
@@ -1584,24 +1904,24 @@ parse_signature (IOBUF inp, int pkttype, unsigned long pktlen,
 
       p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_SIG_CREATED, NULL);
       if (p)
-       sig->timestamp = buffer_to_u32 (p);
+       sig->timestamp = buf32_to_u32 (p);
       else if (!(sig->pubkey_algo >= 100 && sig->pubkey_algo <= 110)
               && opt.verbose)
        log_info ("signature packet without timestamp\n");
 
-      p = parse_sig_subpkt2 (sig, SIGSUBPKT_ISSUER, NULL);
+      p = parse_sig_subpkt2 (sig, SIGSUBPKT_ISSUER);
       if (p)
        {
-         sig->keyid[0] = buffer_to_u32 (p);
-         sig->keyid[1] = buffer_to_u32 (p + 4);
+         sig->keyid[0] = buf32_to_u32 (p);
+         sig->keyid[1] = buf32_to_u32 (p + 4);
        }
       else if (!(sig->pubkey_algo >= 100 && sig->pubkey_algo <= 110)
               && opt.verbose)
        log_info ("signature packet without keyid\n");
 
       p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_SIG_EXPIRE, NULL);
-      if (p && buffer_to_u32 (p))
-       sig->expiredate = sig->timestamp + buffer_to_u32 (p);
+      if (p && buf32_to_u32 (p))
+       sig->expiredate = sig->timestamp + buf32_to_u32 (p);
       if (sig->expiredate && sig->expiredate <= make_timestamp ())
        sig->flags.expired = 1;
 
@@ -1613,6 +1933,17 @@ parse_signature (IOBUF inp, int pkttype, unsigned long pktlen,
       if (p)
        sig->flags.pref_ks = 1;
 
+      p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_SIGNERS_UID, &len);
+      if (p && len)
+        {
+          sig->signers_uid = try_make_printable_string (p, len, 0);
+          if (!sig->signers_uid)
+            {
+              rc = gpg_error_from_syserror ();
+              goto leave;
+            }
+        }
+
       p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_NOTATION, NULL);
       if (p)
        sig->flags.notation = 1;
@@ -1643,7 +1974,7 @@ parse_signature (IOBUF inp, int pkttype, unsigned long pktlen,
          unhashed area.  In theory, anyway, we should never see this
          packet off of a local keyring. */
 
-      p = parse_sig_subpkt2 (sig, SIGSUBPKT_EXPORTABLE, NULL);
+      p = parse_sig_subpkt2 (sig, SIGSUBPKT_EXPORTABLE);
       if (p && *p == 0)
        sig->flags.exportable = 0;
 
@@ -1654,13 +1985,13 @@ parse_signature (IOBUF inp, int pkttype, unsigned long pktlen,
 
   if (list_mode)
     {
-      fprintf (listfp, ":signature packet: algo %d, keyid %08lX%08lX\n"
-              "\tversion %d, created %lu, md5len %d, sigclass 0x%02x\n"
-              "\tdigest algo %d, begin of digest %02x %02x\n",
-              sig->pubkey_algo,
-              (ulong) sig->keyid[0], (ulong) sig->keyid[1],
-              sig->version, (ulong) sig->timestamp, md5_len, sig->sig_class,
-              sig->digest_algo, sig->digest_start[0], sig->digest_start[1]);
+      es_fprintf (listfp, ":signature packet: algo %d, keyid %08lX%08lX\n"
+                  "\tversion %d, created %lu, md5len %d, sigclass 0x%02x\n"
+                  "\tdigest algo %d, begin of digest %02x %02x\n",
+                  sig->pubkey_algo,
+                  (ulong) sig->keyid[0], (ulong) sig->keyid[1],
+                  sig->version, (ulong) sig->timestamp, md5_len, sig->sig_class,
+                  sig->digest_algo, sig->digest_start[0], sig->digest_start[1]);
       if (is_v4)
        {
          parse_sig_subpkt (sig->hashed, SIGSUBPKT_LIST_HASHED, NULL);
@@ -1672,7 +2003,7 @@ parse_signature (IOBUF inp, int pkttype, unsigned long pktlen,
   if (!ndata)
     {
       if (list_mode)
-       fprintf (listfp, "\tunknown algorithm %d\n", sig->pubkey_algo);
+       es_fprintf (listfp, "\tunknown algorithm %d\n", sig->pubkey_algo);
       unknown_pubkey_warning (sig->pubkey_algo);
 
       /* We store the plain material in data[0], so that we are able
@@ -1682,13 +2013,12 @@ parse_signature (IOBUF inp, int pkttype, unsigned long pktlen,
          /* We include a limit to avoid too trivial DoS attacks by
             having gpg allocate too much memory.  */
          log_error ("signature packet: too much data\n");
-         rc = G10ERR_INVALID_PACKET;
+         rc = GPG_ERR_INV_PACKET;
        }
       else
        {
          sig->data[0] =
-           gcry_mpi_set_opaque (NULL, read_rest (inp, pktlen, 0),
-                                pktlen * 8);
+           gcry_mpi_set_opaque (NULL, read_rest (inp, pktlen), pktlen * 8);
          pktlen = 0;
        }
     }
@@ -1701,18 +2031,27 @@ parse_signature (IOBUF inp, int pkttype, unsigned long pktlen,
          pktlen -= n;
          if (list_mode)
            {
-             fprintf (listfp, "\tdata: ");
+             es_fprintf (listfp, "\tdata: ");
              mpi_print (listfp, sig->data[i], mpi_print_mode);
-             putc ('\n', listfp);
+             es_putc ('\n', listfp);
            }
          if (!sig->data[i])
-           rc = G10ERR_INVALID_PACKET;
+           rc = GPG_ERR_INV_PACKET;
        }
     }
 
  leave:
   iobuf_skip_rest (inp, pktlen, 0);
   return rc;
+
+ underflow:
+  log_error ("packet(%d) too short\n", pkttype);
+  if (list_mode)
+    es_fputs (":signature packet: [too short]\n", listfp);
+
+  iobuf_skip_rest (inp, pktlen, 0);
+
+  return GPG_ERR_INV_PACKET;
 }
 
 
@@ -1726,6 +2065,8 @@ parse_onepass_sig (IOBUF inp, int pkttype, unsigned long pktlen,
   if (pktlen < 13)
     {
       log_error ("packet(%d) too short\n", pkttype);
+      if (list_mode)
+        es_fputs (":onepass_sig packet: [too short]\n", listfp);
       rc = gpg_error (GPG_ERR_INV_PACKET);
       goto leave;
     }
@@ -1734,6 +2075,8 @@ parse_onepass_sig (IOBUF inp, int pkttype, unsigned long pktlen,
   if (version != 3)
     {
       log_error ("onepass_sig with unknown version %d\n", version);
+      if (list_mode)
+        es_fputs (":onepass_sig packet: [unknown version]\n", listfp);
       rc = gpg_error (GPG_ERR_INV_PACKET);
       goto leave;
     }
@@ -1750,13 +2093,13 @@ parse_onepass_sig (IOBUF inp, int pkttype, unsigned long pktlen,
   ops->last = iobuf_get_noeof (inp);
   pktlen--;
   if (list_mode)
-    fprintf (listfp,
-            ":onepass_sig packet: keyid %08lX%08lX\n"
-            "\tversion %d, sigclass 0x%02x, digest %d, pubkey %d, "
-            "last=%d\n",
-            (ulong) ops->keyid[0], (ulong) ops->keyid[1],
-            version, ops->sig_class,
-            ops->digest_algo, ops->pubkey_algo, ops->last);
+    es_fprintf (listfp,
+                ":onepass_sig packet: keyid %08lX%08lX\n"
+                "\tversion %d, sigclass 0x%02x, digest %d, pubkey %d, "
+                "last=%d\n",
+                (ulong) ops->keyid[0], (ulong) ops->keyid[1],
+                version, ops->sig_class,
+                ops->digest_algo, ops->pubkey_algo, ops->last);
 
 
  leave:
@@ -1765,67 +2108,21 @@ parse_onepass_sig (IOBUF inp, int pkttype, unsigned long pktlen,
 }
 
 
-static gcry_mpi_t
-read_protected_v3_mpi (IOBUF inp, unsigned long *length)
-{
-  int c;
-  unsigned int nbits, nbytes;
-  unsigned char *buf, *p;
-  gcry_mpi_t val;
-
-  if (*length < 2)
-    {
-      log_error ("mpi too small\n");
-      return NULL;
-    }
-
-  if ((c = iobuf_get (inp)) == -1)
-    return NULL;
-  --*length;
-  nbits = c << 8;
-  if ((c = iobuf_get (inp)) == -1)
-    return NULL;
-  --*length;
-  nbits |= c;
-
-  if (nbits > 16384)
-    {
-      log_error ("mpi too large (%u bits)\n", nbits);
-      return NULL;
-    }
-  nbytes = (nbits + 7) / 8;
-  buf = p = xmalloc (2 + nbytes);
-  *p++ = nbits >> 8;
-  *p++ = nbits;
-  for (; nbytes && *length; nbytes--, --*length)
-    *p++ = iobuf_get (inp);
-  if (nbytes)
-    {
-      log_error ("packet shorter than mpi\n");
-      xfree (buf);
-      return NULL;
-    }
-
-  /* Convert buffer into an opaque MPI.  */
-  val = gcry_mpi_set_opaque (NULL, buf, (p - buf) * 8);
-  return val;
-}
-
-
 static int
 parse_key (IOBUF inp, int pkttype, unsigned long pktlen,
           byte * hdr, int hdrlen, PACKET * pkt)
 {
+  gpg_error_t err = 0;
   int i, version, algorithm;
-  unsigned n;
   unsigned long timestamp, expiredate, max_expiredate;
   int npkey, nskey;
-  int is_v4 = 0;
-  int rc = 0;
   u32 keyid[2];
+  PKT_public_key *pk;
 
   (void) hdr;
 
+  pk = pkt->pkt.public_key; /* PK has been cleared. */
+
   version = iobuf_get_noeof (inp);
   pktlen--;
   if (pkttype == PKT_PUBLIC_SUBKEY && version == '#')
@@ -1834,421 +2131,433 @@ parse_key (IOBUF inp, int pkttype, unsigned long pktlen,
        * luckily all those comments are started by a hash.  */
       if (list_mode)
        {
-         fprintf (listfp, ":rfc1991 comment packet: \"");
+         es_fprintf (listfp, ":rfc1991 comment packet: \"");
          for (; pktlen; pktlen--)
            {
              int c;
-             c = iobuf_get_noeof (inp);
+             c = iobuf_get (inp);
+              if (c == -1)
+                break; /* Ooops: shorter than indicated.  */
              if (c >= ' ' && c <= 'z')
-               putc (c, listfp);
+               es_putc (c, listfp);
              else
-               fprintf (listfp, "\\x%02x", c);
+               es_fprintf (listfp, "\\x%02x", c);
            }
-         fprintf (listfp, "\"\n");
+         es_fprintf (listfp, "\"\n");
        }
       iobuf_skip_rest (inp, pktlen, 0);
       return 0;
     }
   else if (version == 4)
-    is_v4 = 1;
-  else if (version != 2 && version != 3)
+    {
+      /* The only supported version.  Use an older gpg
+         version (i.e. gpg 1.4) to parse v3 packets.  */
+    }
+  else if (version == 2 || version == 3)
+    {
+      if (opt.verbose > 1)
+        log_info ("packet(%d) with obsolete version %d\n", pkttype, version);
+      if (list_mode)
+        es_fprintf (listfp, ":key packet: [obsolete version %d]\n", version);
+      pk->version = version;
+      err = gpg_error (GPG_ERR_LEGACY_KEY);
+      goto leave;
+    }
+  else
     {
       log_error ("packet(%d) with unknown version %d\n", pkttype, version);
-      rc = gpg_error (GPG_ERR_INV_PACKET);
+      if (list_mode)
+        es_fputs (":key packet: [unknown version]\n", listfp);
+      err = gpg_error (GPG_ERR_INV_PACKET);
       goto leave;
     }
 
   if (pktlen < 11)
     {
       log_error ("packet(%d) too short\n", pkttype);
-      rc = gpg_error (GPG_ERR_INV_PACKET);
+      if (list_mode)
+        es_fputs (":key packet: [too short]\n", listfp);
+      err = gpg_error (GPG_ERR_INV_PACKET);
       goto leave;
     }
-
-  timestamp = read_32 (inp);
-  pktlen -= 4;
-  if (is_v4)
+  else if (pktlen > MAX_KEY_PACKET_LENGTH)
     {
-      expiredate = 0;          /* have to get it from the selfsignature */
-      max_expiredate = 0;
+      log_error ("packet(%d) too large\n", pkttype);
+      if (list_mode)
+        es_fputs (":key packet: [too larget]\n", listfp);
+      err = gpg_error (GPG_ERR_INV_PACKET);
+      goto leave;
     }
-  else
-    {
-      unsigned short ndays;
-      ndays = read_16 (inp);
-      pktlen -= 2;
-      if (ndays)
-       expiredate = timestamp + ndays * 86400L;
-      else
-       expiredate = 0;
 
-      max_expiredate = expiredate;
-    }
+  timestamp = read_32 (inp);
+  pktlen -= 4;
+  expiredate = 0;              /* have to get it from the selfsignature */
+  max_expiredate = 0;
   algorithm = iobuf_get_noeof (inp);
   pktlen--;
   if (list_mode)
-    fprintf (listfp, ":%s key packet:\n"
-            "\tversion %d, algo %d, created %lu, expires %lu\n",
-            pkttype == PKT_PUBLIC_KEY ? "public" :
-            pkttype == PKT_SECRET_KEY ? "secret" :
-            pkttype == PKT_PUBLIC_SUBKEY ? "public sub" :
-            pkttype == PKT_SECRET_SUBKEY ? "secret sub" : "??",
-            version, algorithm, timestamp, expiredate);
-
-  if (pkttype == PKT_SECRET_KEY || pkttype == PKT_SECRET_SUBKEY)
-    {
-      PKT_secret_key *sk = pkt->pkt.secret_key;
-
-      sk->timestamp = timestamp;
-      sk->expiredate = expiredate;
-      sk->max_expiredate = max_expiredate;
-      sk->hdrbytes = hdrlen;
-      sk->version = version;
-      sk->is_primary = pkttype == PKT_SECRET_KEY;
-      sk->pubkey_algo = algorithm;
-      sk->req_usage = 0;
-      sk->pubkey_usage = 0;    /* not yet used */
-    }
-  else
-    {
-      PKT_public_key *pk = pkt->pkt.public_key;
+    es_fprintf (listfp, ":%s key packet:\n"
+                "\tversion %d, algo %d, created %lu, expires %lu\n",
+                pkttype == PKT_PUBLIC_KEY ? "public" :
+                pkttype == PKT_SECRET_KEY ? "secret" :
+                pkttype == PKT_PUBLIC_SUBKEY ? "public sub" :
+                pkttype == PKT_SECRET_SUBKEY ? "secret sub" : "??",
+                version, algorithm, timestamp, expiredate);
+
+  pk->timestamp = timestamp;
+  pk->expiredate = expiredate;
+  pk->max_expiredate = max_expiredate;
+  pk->hdrbytes = hdrlen;
+  pk->version = version;
+  pk->flags.primary = (pkttype == PKT_PUBLIC_KEY || pkttype == PKT_SECRET_KEY);
+  pk->pubkey_algo = algorithm;
 
-      pk->timestamp = timestamp;
-      pk->expiredate = expiredate;
-      pk->max_expiredate = max_expiredate;
-      pk->hdrbytes = hdrlen;
-      pk->version = version;
-      pk->is_primary = pkttype == PKT_PUBLIC_KEY;
-      pk->pubkey_algo = algorithm;
-      pk->req_usage = 0;
-      pk->pubkey_usage = 0;    /* not yet used */
-      pk->is_revoked = 0;
-      pk->is_disabled = 0;
-      pk->keyid[0] = 0;
-      pk->keyid[1] = 0;
-    }
   nskey = pubkey_get_nskey (algorithm);
   npkey = pubkey_get_npkey (algorithm);
   if (!npkey)
     {
       if (list_mode)
-       fprintf (listfp, "\tunknown algorithm %d\n", algorithm);
+       es_fprintf (listfp, "\tunknown algorithm %d\n", algorithm);
       unknown_pubkey_warning (algorithm);
     }
 
+  if (!npkey)
+    {
+      /* Unknown algorithm - put data into an opaque MPI.  */
+      pk->pkey[0] = gcry_mpi_set_opaque (NULL,
+                                         read_rest (inp, pktlen), pktlen * 8);
+      pktlen = 0;
+      goto leave;
+    }
+  else
+    {
+      for (i = 0; i < npkey; i++)
+        {
+          if (    (algorithm == PUBKEY_ALGO_ECDSA && (i == 0))
+               || (algorithm == PUBKEY_ALGO_EDDSA && (i == 0))
+               || (algorithm == PUBKEY_ALGO_ECDH  && (i == 0 || i == 2)))
+            {
+              /* Read the OID (i==1) or the KDF params (i==2).  */
+              size_t n;
+             err = read_size_body (inp, pktlen, &n, pk->pkey+i);
+              pktlen -= n;
+            }
+          else
+            {
+              unsigned int n = pktlen;
+              pk->pkey[i] = mpi_read (inp, &n, 0);
+              pktlen -= n;
+              if (!pk->pkey[i])
+                err = gpg_error (GPG_ERR_INV_PACKET);
+            }
+          if (err)
+            goto leave;
+          if (list_mode)
+            {
+              es_fprintf (listfp, "\tpkey[%d]: ", i);
+              mpi_print (listfp, pk->pkey[i], mpi_print_mode);
+              if ((algorithm == PUBKEY_ALGO_ECDSA
+                   || algorithm == PUBKEY_ALGO_EDDSA
+                   || algorithm == PUBKEY_ALGO_ECDH) && i==0)
+                {
+                  char *curve = openpgp_oid_to_str (pk->pkey[0]);
+                  const char *name = openpgp_oid_to_curve (curve, 0);
+                  es_fprintf (listfp, " %s (%s)", name?name:"", curve);
+                  xfree (curve);
+                }
+              es_putc ('\n', listfp);
+            }
+        }
+    }
+  if (list_mode)
+    keyid_from_pk (pk, keyid);
 
   if (pkttype == PKT_SECRET_KEY || pkttype == PKT_SECRET_SUBKEY)
     {
-      PKT_secret_key *sk = pkt->pkt.secret_key;
+      struct seckey_info *ski;
       byte temp[16];
       size_t snlen = 0;
 
-      if (!npkey)
-       {
-         sk->skey[0] = gcry_mpi_set_opaque (NULL, read_rest (inp, pktlen, 0),
-                                            pktlen * 8);
-         pktlen = 0;
-         goto leave;
-       }
+      if (pktlen < 1)
+        {
+          err = gpg_error (GPG_ERR_INV_PACKET);
+          goto leave;
+        }
 
-      for (i = 0; i < npkey; i++)
-       {
-         n = pktlen;
-         sk->skey[i] = mpi_read (inp, &n, 0);
-         pktlen -= n;
-         if (list_mode)
-           {
-             fprintf (listfp, "\tskey[%d]: ", i);
-             mpi_print (listfp, sk->skey[i], mpi_print_mode);
-             putc ('\n', listfp);
-           }
-         if (!sk->skey[i])
-           rc = G10ERR_INVALID_PACKET;
-       }
-      if (rc)  /* One of the MPIs were bad.  */
-       goto leave;
-      sk->protect.algo = iobuf_get_noeof (inp);
+      pk->seckey_info = ski = xtrycalloc (1, sizeof *ski);
+      if (!pk->seckey_info)
+        {
+          err = gpg_error_from_syserror ();
+          goto leave;
+        }
+
+      ski->algo = iobuf_get_noeof (inp);
       pktlen--;
-      sk->protect.sha1chk = 0;
-      if (sk->protect.algo)
+      if (ski->algo)
        {
-         sk->is_protected = 1;
-         sk->protect.s2k.count = 0;
-         if (sk->protect.algo == 254 || sk->protect.algo == 255)
+         ski->is_protected = 1;
+         ski->s2k.count = 0;
+         if (ski->algo == 254 || ski->algo == 255)
            {
              if (pktlen < 3)
                {
-                 rc = G10ERR_INVALID_PACKET;
+                 err = gpg_error (GPG_ERR_INV_PACKET);
                  goto leave;
                }
-             sk->protect.sha1chk = (sk->protect.algo == 254);
-             sk->protect.algo = iobuf_get_noeof (inp);
+             ski->sha1chk = (ski->algo == 254);
+             ski->algo = iobuf_get_noeof (inp);
              pktlen--;
-             /* Note that a sk->protect.algo > 110 is illegal, but
-                I'm not erroring on it here as otherwise there would
-                be no way to delete such a key.  */
-             sk->protect.s2k.mode = iobuf_get_noeof (inp);
+             /* Note that a ski->algo > 110 is illegal, but I'm not
+                erroring on it here as otherwise there would be no
+                way to delete such a key.  */
+             ski->s2k.mode = iobuf_get_noeof (inp);
              pktlen--;
-             sk->protect.s2k.hash_algo = iobuf_get_noeof (inp);
+             ski->s2k.hash_algo = iobuf_get_noeof (inp);
              pktlen--;
-             /* check for the special GNU extension */
-             if (is_v4 && sk->protect.s2k.mode == 101)
+             /* Check for the special GNU extension.  */
+             if (ski->s2k.mode == 101)
                {
                  for (i = 0; i < 4 && pktlen; i++, pktlen--)
                    temp[i] = iobuf_get_noeof (inp);
                  if (i < 4 || memcmp (temp, "GNU", 3))
                    {
                      if (list_mode)
-                       fprintf (listfp, "\tunknown S2K %d\n",
-                                sk->protect.s2k.mode);
-                     rc = G10ERR_INVALID_PACKET;
+                       es_fprintf (listfp, "\tunknown S2K %d\n",
+                                    ski->s2k.mode);
+                     err = gpg_error (GPG_ERR_INV_PACKET);
                      goto leave;
                    }
                  /* Here we know that it is a GNU extension.  What
                   * follows is the GNU protection mode: All values
                   * have special meanings and they are mapped to MODE
                   * with a base of 1000.  */
-                 sk->protect.s2k.mode = 1000 + temp[3];
+                 ski->s2k.mode = 1000 + temp[3];
                }
-             switch (sk->protect.s2k.mode)
+
+              /* Read the salt.  */
+             switch (ski->s2k.mode)
                {
                case 1:
                case 3:
                  for (i = 0; i < 8 && pktlen; i++, pktlen--)
                    temp[i] = iobuf_get_noeof (inp);
-                 memcpy (sk->protect.s2k.salt, temp, 8);
+                  if (i < 8)
+                    {
+                     err = gpg_error (GPG_ERR_INV_PACKET);
+                     goto leave;
+                    }
+                 memcpy (ski->s2k.salt, temp, 8);
                  break;
                }
-             switch (sk->protect.s2k.mode)
+
+              /* Check the mode.  */
+             switch (ski->s2k.mode)
                {
                case 0:
                  if (list_mode)
-                   fprintf (listfp, "\tsimple S2K");
+                   es_fprintf (listfp, "\tsimple S2K");
                  break;
                case 1:
                  if (list_mode)
-                   fprintf (listfp, "\tsalted S2K");
+                   es_fprintf (listfp, "\tsalted S2K");
                  break;
                case 3:
                  if (list_mode)
-                   fprintf (listfp, "\titer+salt S2K");
+                   es_fprintf (listfp, "\titer+salt S2K");
                  break;
                case 1001:
                  if (list_mode)
-                   fprintf (listfp, "\tgnu-dummy S2K");
+                   es_fprintf (listfp, "\tgnu-dummy S2K");
                  break;
                case 1002:
                  if (list_mode)
-                   fprintf (listfp, "\tgnu-divert-to-card S2K");
+                   es_fprintf (listfp, "\tgnu-divert-to-card S2K");
                  break;
                default:
                  if (list_mode)
-                   fprintf (listfp, "\tunknown %sS2K %d\n",
-                            sk->protect.s2k.mode < 1000 ? "" : "GNU ",
-                            sk->protect.s2k.mode);
-                 rc = G10ERR_INVALID_PACKET;
+                   es_fprintf (listfp, "\tunknown %sS2K %d\n",
+                                ski->s2k.mode < 1000 ? "" : "GNU ",
+                                ski->s2k.mode);
+                 err = gpg_error (GPG_ERR_INV_PACKET);
                  goto leave;
                }
 
+              /* Print some info.  */
              if (list_mode)
                {
-                 fprintf (listfp, ", algo: %d,%s hash: %d",
-                          sk->protect.algo,
-                          sk->protect.sha1chk ? " SHA1 protection,"
-                          : " simple checksum,", sk->protect.s2k.hash_algo);
-                 if (sk->protect.s2k.mode == 1 || sk->protect.s2k.mode == 3)
+                 es_fprintf (listfp, ", algo: %d,%s hash: %d",
+                              ski->algo,
+                              ski->sha1chk ? " SHA1 protection,"
+                              : " simple checksum,", ski->s2k.hash_algo);
+                 if (ski->s2k.mode == 1 || ski->s2k.mode == 3)
                    {
-                     fprintf (listfp, ", salt: ");
-                     for (i = 0; i < 8; i++)
-                       fprintf (listfp, "%02x", sk->protect.s2k.salt[i]);
+                     es_fprintf (listfp, ", salt: ");
+                      es_write_hexstring (listfp, ski->s2k.salt, 8, 0, NULL);
                    }
-                 putc ('\n', listfp);
+                 es_putc ('\n', listfp);
                }
 
-             if (sk->protect.s2k.mode == 3)
+              /* Read remaining protection parameters.  */
+             if (ski->s2k.mode == 3)
                {
                  if (pktlen < 1)
                    {
-                     rc = G10ERR_INVALID_PACKET;
+                     err = gpg_error (GPG_ERR_INV_PACKET);
                      goto leave;
                    }
-                 sk->protect.s2k.count = iobuf_get (inp);
+                 ski->s2k.count = iobuf_get (inp);
                  pktlen--;
                  if (list_mode)
-                   fprintf (listfp, "\tprotect count: %lu\n",
-                            (ulong) sk->protect.s2k.count);
+                   es_fprintf (listfp, "\tprotect count: %lu (%lu)\n",
+                                (ulong)S2K_DECODE_COUNT ((ulong)ski->s2k.count),
+                                (ulong) ski->s2k.count);
                }
-             else if (sk->protect.s2k.mode == 1002)
+             else if (ski->s2k.mode == 1002)
                {
                  /* Read the serial number. */
                  if (pktlen < 1)
                    {
-                     rc = G10ERR_INVALID_PACKET;
+                     err = gpg_error (GPG_ERR_INV_PACKET);
                      goto leave;
                    }
                  snlen = iobuf_get (inp);
                  pktlen--;
-                 if (pktlen < snlen || snlen == -1)
+                 if (pktlen < snlen || snlen == (size_t)(-1))
                    {
-                     rc = G10ERR_INVALID_PACKET;
+                     err = gpg_error (GPG_ERR_INV_PACKET);
                      goto leave;
                    }
                }
            }
          else /* Old version; no S2K, so we set mode to 0, hash MD5.  */
-           { 
-              /* Note that a sk->protect.algo > 110 is illegal, but
-                 I'm not erroring on it here as otherwise there would
-                 be no way to delete such a key.  */
-             sk->protect.s2k.mode = 0;
-             sk->protect.s2k.hash_algo = DIGEST_ALGO_MD5;
+           {
+              /* Note that a ski->algo > 110 is illegal, but I'm not
+                 erroring on it here as otherwise there would be no
+                 way to delete such a key.  */
+             ski->s2k.mode = 0;
+             ski->s2k.hash_algo = DIGEST_ALGO_MD5;
              if (list_mode)
-               fprintf (listfp, "\tprotect algo: %d  (hash algo: %d)\n",
-                        sk->protect.algo, sk->protect.s2k.hash_algo);
+               es_fprintf (listfp, "\tprotect algo: %d  (hash algo: %d)\n",
+                            ski->algo, ski->s2k.hash_algo);
            }
-          
+
          /* It is really ugly that we don't know the size
           * of the IV here in cases we are not aware of the algorithm.
           * so a
-          *   sk->protect.ivlen = cipher_get_blocksize(sk->protect.algo);
+          *   ski->ivlen = cipher_get_blocksize (ski->algo);
           * won't work.  The only solution I see is to hardwire it.
           * NOTE: if you change the ivlen above 16, don't forget to
           * enlarge temp.  */
-         sk->protect.ivlen = openpgp_cipher_blocklen (sk->protect.algo);
-         assert (sk->protect.ivlen <= sizeof (temp));
+         ski->ivlen = openpgp_cipher_blocklen (ski->algo);
+         log_assert (ski->ivlen <= sizeof (temp));
 
-         if (sk->protect.s2k.mode == 1001)
-           sk->protect.ivlen = 0;
-         else if (sk->protect.s2k.mode == 1002)
-           sk->protect.ivlen = snlen < 16 ? snlen : 16;
+         if (ski->s2k.mode == 1001)
+           ski->ivlen = 0;
+         else if (ski->s2k.mode == 1002)
+           ski->ivlen = snlen < 16 ? snlen : 16;
 
-         if (pktlen < sk->protect.ivlen)
+         if (pktlen < ski->ivlen)
            {
-             rc = G10ERR_INVALID_PACKET;
+              err = gpg_error (GPG_ERR_INV_PACKET);
              goto leave;
            }
-         for (i = 0; i < sk->protect.ivlen && pktlen; i++, pktlen--)
+         for (i = 0; i < ski->ivlen; i++, pktlen--)
            temp[i] = iobuf_get_noeof (inp);
          if (list_mode)
            {
-             fprintf (listfp,
-                      sk->protect.s2k.mode == 1002 ? "\tserial-number: "
-                      : "\tprotect IV: ");
-             for (i = 0; i < sk->protect.ivlen; i++)
-               fprintf (listfp, " %02x", temp[i]);
-             putc ('\n', listfp);
+             es_fprintf (listfp,
+                          ski->s2k.mode == 1002 ? "\tserial-number: "
+                          : "\tprotect IV: ");
+             for (i = 0; i < ski->ivlen; i++)
+               es_fprintf (listfp, " %02x", temp[i]);
+             es_putc ('\n', listfp);
            }
-         memcpy (sk->protect.iv, temp, sk->protect.ivlen);
+         memcpy (ski->iv, temp, ski->ivlen);
        }
-      else
-       sk->is_protected = 0;
 
       /* It does not make sense to read it into secure memory.
        * If the user is so careless, not to protect his secret key,
        * we can assume, that he operates an open system :=(.
        * So we put the key into secure memory when we unprotect it. */
-      if (sk->protect.s2k.mode == 1001 || sk->protect.s2k.mode == 1002)
+      if (ski->s2k.mode == 1001 || ski->s2k.mode == 1002)
        {
          /* Better set some dummy stuff here.  */
-         sk->skey[npkey] = gcry_mpi_set_opaque (NULL,
+         pk->pkey[npkey] = gcry_mpi_set_opaque (NULL,
                                                 xstrdup ("dummydata"),
                                                 10 * 8);
          pktlen = 0;
        }
-      else if (is_v4 && sk->is_protected)
+      else if (ski->is_protected)
        {
+         if (pktlen < 2) /* At least two bytes for the length.  */
+           {
+              err = gpg_error (GPG_ERR_INV_PACKET);
+             goto leave;
+           }
+
          /* Ugly: The length is encrypted too, so we read all stuff
           * up to the end of the packet into the first SKEY
           * element.  */
-         sk->skey[npkey] = gcry_mpi_set_opaque (NULL,
-                                                read_rest (inp, pktlen, 0),
+         pk->pkey[npkey] = gcry_mpi_set_opaque (NULL,
+                                                read_rest (inp, pktlen),
                                                 pktlen * 8);
+          /* Mark that MPI as protected - we need this information for
+             importing a key.  The OPAQUE flag can't be used because
+             we also store public EdDSA values in opaque MPIs.  */
+          if (pk->pkey[npkey])
+            gcry_mpi_set_flag (pk->pkey[npkey], GCRYMPI_FLAG_USER1);
          pktlen = 0;
          if (list_mode)
-           {
-             fprintf (listfp, "\tencrypted stuff follows\n");
-           }
+            es_fprintf (listfp, "\tskey[%d]: [v4 protected]\n", npkey);
        }
-      else /* The v3 method: The mpi length is not encrypted.  */
-       {       
+      else
+       {
+          /* Not encrypted.  */
          for (i = npkey; i < nskey; i++)
            {
-             if (sk->is_protected)
-               {
-                 sk->skey[i] = read_protected_v3_mpi (inp, &pktlen);
-                 if (list_mode)
-                   fprintf (listfp, "\tskey[%d]: [encrypted]\n", i);
-               }
-             else
-               {
-                 n = pktlen;
-                 sk->skey[i] = mpi_read (inp, &n, 0);
-                 pktlen -= n;
-                 if (list_mode)
-                   {
-                     fprintf (listfp, "\tskey[%d]: ", i);
-                     mpi_print (listfp, sk->skey[i], mpi_print_mode);
-                     putc ('\n', listfp);
-                   }
-               }
-
-             if (!sk->skey[i])
-               rc = G10ERR_INVALID_PACKET;
+              unsigned int n;
+
+              if (pktlen < 2) /* At least two bytes for the length.  */
+                {
+                  err = gpg_error (GPG_ERR_INV_PACKET);
+                  goto leave;
+                }
+              n = pktlen;
+              pk->pkey[i] = mpi_read (inp, &n, 0);
+              pktlen -= n;
+              if (list_mode)
+                {
+                  es_fprintf (listfp, "\tskey[%d]: ", i);
+                  mpi_print (listfp, pk->pkey[i], mpi_print_mode);
+                  es_putc ('\n', listfp);
+                }
+
+             if (!pk->pkey[i])
+               err = gpg_error (GPG_ERR_INV_PACKET);
            }
-         if (rc)
+         if (err)
            goto leave;
 
-         sk->csum = read_16 (inp);
-         pktlen -= 2;
-         if (list_mode)
+         if (pktlen < 2)
            {
-             fprintf (listfp, "\tchecksum: %04hx\n", sk->csum);
+              err = gpg_error (GPG_ERR_INV_PACKET);
+             goto leave;
            }
-       }
-
-      if (list_mode)
-       keyid_from_sk (sk, keyid);
-    }
-  else
-    {
-      PKT_public_key *pk = pkt->pkt.public_key;
-
-      if (!npkey)
-       {
-         pk->pkey[0] = gcry_mpi_set_opaque (NULL,
-                                            read_rest (inp, pktlen, 0),
-                                            pktlen * 8);
-         pktlen = 0;
-         goto leave;
-       }
-
-      for (i = 0; i < npkey; i++)
-       {
-         n = pktlen;
-         pk->pkey[i] = mpi_read (inp, &n, 0);
-         pktlen -= n;
+         ski->csum = read_16 (inp);
+         pktlen -= 2;
          if (list_mode)
-           {
-             fprintf (listfp, "\tpkey[%d]: ", i);
-             mpi_print (listfp, pk->pkey[i], mpi_print_mode);
-             putc ('\n', listfp);
-           }
-         if (!pk->pkey[i])
-           rc = G10ERR_INVALID_PACKET;
+            es_fprintf (listfp, "\tchecksum: %04hx\n", ski->csum);
        }
-      if (rc)
-       goto leave;
-      if (list_mode)
-       keyid_from_pk (pk, keyid);
     }
 
+  /* Note that KEYID below has been initialized above in list_mode.  */
   if (list_mode)
-    fprintf (listfp, "\tkeyid: %08lX%08lX\n",
-            (ulong) keyid[0], (ulong) keyid[1]);
+    es_fprintf (listfp, "\tkeyid: %08lX%08lX\n",
+                (ulong) keyid[0], (ulong) keyid[1]);
 
  leave:
   iobuf_skip_rest (inp, pktlen, 0);
-  return rc;
+  return err;
 }
 
 
@@ -2272,16 +2581,15 @@ parse_attribute_subpkts (PKT_user_id * uid)
       n = *buffer++;
       buflen--;
       if (n == 255)  /* 4 byte length header.  */
-       {                       
+       {
          if (buflen < 4)
            goto too_short;
-         n = (buffer[0] << 24) | (buffer[1] << 16)
-           | (buffer[2] << 8) | buffer[3];
+         n = buf32_to_size_t (buffer);
          buffer += 4;
          buflen -= 4;
        }
       else if (n >= 192)  /* 2 byte special encoded length header.  */
-       {                       
+       {
          if (buflen < 2)
            goto too_short;
          n = ((n - 192) << 8) + *buffer + 192;
@@ -2291,8 +2599,16 @@ parse_attribute_subpkts (PKT_user_id * uid)
       if (buflen < n)
        goto too_short;
 
-      attribs =
-       xrealloc (attribs, (count + 1) * sizeof (struct user_attribute));
+      if (!n)
+        {
+          /* Too short to encode the subpacket type.  */
+          if (opt.verbose)
+            log_info ("attribute subpacket too short\n");
+          break;
+        }
+
+      attribs = xrealloc (attribs,
+                          (count + 1) * sizeof (struct user_attribute));
       memset (&attribs[count], 0, sizeof (struct user_attribute));
 
       type = *buffer;
@@ -2333,11 +2649,13 @@ parse_user_id (IOBUF inp, int pkttype, unsigned long pktlen, PACKET * packet)
      allocatable, and a very large pktlen could actually cause our
      allocation to wrap around in xmalloc to a small number. */
 
-  if (pktlen > 2048)
+  if (pktlen > MAX_UID_PACKET_LENGTH)
     {
       log_error ("packet(%d) too large\n", pkttype);
+      if (list_mode)
+        es_fprintf (listfp, ":user ID packet: [too large]\n");
       iobuf_skip_rest (inp, pktlen, 0);
-      return G10ERR_INVALID_PACKET;
+      return GPG_ERR_INV_PACKET;
     }
 
   packet->pkt.user_id = xmalloc_clear (sizeof *packet->pkt.user_id + pktlen);
@@ -2352,16 +2670,16 @@ parse_user_id (IOBUF inp, int pkttype, unsigned long pktlen, PACKET * packet)
   if (list_mode)
     {
       int n = packet->pkt.user_id->len;
-      fprintf (listfp, ":user ID packet: \"");
-      /* fixme: Hey why don't we replace this with print_string?? */
+      es_fprintf (listfp, ":user ID packet: \"");
+      /* fixme: Hey why don't we replace this with es_write_sanitized?? */
       for (p = packet->pkt.user_id->name; n; p++, n--)
        {
          if (*p >= ' ' && *p <= 'z')
-           putc (*p, listfp);
+           es_putc (*p, listfp);
          else
-           fprintf (listfp, "\\x%02x", *p);
+           es_fprintf (listfp, "\\x%02x", *p);
        }
-      fprintf (listfp, "\"\n");
+      es_fprintf (listfp, "\"\n");
     }
   return 0;
 }
@@ -2370,7 +2688,7 @@ parse_user_id (IOBUF inp, int pkttype, unsigned long pktlen, PACKET * packet)
 void
 make_attribute_uidname (PKT_user_id * uid, size_t max_namelen)
 {
-  assert (max_namelen > 70);
+  log_assert (max_namelen > 70);
   if (uid->numattribs <= 0)
     sprintf (uid->name, "[bad attribute packet of size %lu]",
             uid->attrib_len);
@@ -2409,11 +2727,23 @@ parse_attribute (IOBUF inp, int pkttype, unsigned long pktlen,
 
   (void) pkttype;
 
+  /* We better cap the size of an attribute packet to make DoS not too
+     easy.  16MB should be more then enough for one attribute packet
+     (ie. a photo).  */
+  if (pktlen > MAX_ATTR_PACKET_LENGTH)
+    {
+      log_error ("packet(%d) too large\n", pkttype);
+      if (list_mode)
+        es_fprintf (listfp, ":attribute packet: [too large]\n");
+      iobuf_skip_rest (inp, pktlen, 0);
+      return GPG_ERR_INV_PACKET;
+    }
+
 #define EXTRA_UID_NAME_SPACE 71
   packet->pkt.user_id = xmalloc_clear (sizeof *packet->pkt.user_id
                                       + EXTRA_UID_NAME_SPACE);
   packet->pkt.user_id->ref = 1;
-  packet->pkt.user_id->attrib_data = xmalloc (pktlen);
+  packet->pkt.user_id->attrib_data = xmalloc (pktlen? pktlen:1);
   packet->pkt.user_id->attrib_len = pktlen;
 
   p = packet->pkt.user_id->attrib_data;
@@ -2429,7 +2759,7 @@ parse_attribute (IOBUF inp, int pkttype, unsigned long pktlen,
 
   if (list_mode)
     {
-      fprintf (listfp, ":attribute packet: %s\n", packet->pkt.user_id->name);
+      es_fprintf (listfp, ":attribute packet: %s\n", packet->pkt.user_id->name);
     }
   return 0;
 }
@@ -2444,11 +2774,14 @@ parse_comment (IOBUF inp, int pkttype, unsigned long pktlen, PACKET * packet)
      overflow in the malloc below.  Comment packets are actually not
      anymore define my OpenPGP and we even stopped to use our
      private comment packet.  */
-  if (pktlen > 65536)
+  if (pktlen > MAX_COMMENT_PACKET_LENGTH)
     {
       log_error ("packet(%d) too large\n", pkttype);
+      if (list_mode)
+        es_fprintf (listfp, ":%scomment packet: [too large]\n",
+                    pkttype == PKT_OLD_COMMENT ? "OpenPGP draft " : "");
       iobuf_skip_rest (inp, pktlen, 0);
-      return G10ERR_INVALID_PACKET;
+      return GPG_ERR_INV_PACKET;
     }
   packet->pkt.comment = xmalloc (sizeof *packet->pkt.comment + pktlen - 1);
   packet->pkt.comment->len = pktlen;
@@ -2459,16 +2792,16 @@ parse_comment (IOBUF inp, int pkttype, unsigned long pktlen, PACKET * packet)
   if (list_mode)
     {
       int n = packet->pkt.comment->len;
-      fprintf (listfp, ":%scomment packet: \"", pkttype == PKT_OLD_COMMENT ?
-              "OpenPGP draft " : "");
+      es_fprintf (listfp, ":%scomment packet: \"", pkttype == PKT_OLD_COMMENT ?
+                  "OpenPGP draft " : "");
       for (p = packet->pkt.comment->data; n; p++, n--)
        {
          if (*p >= ' ' && *p <= 'z')
-           putc (*p, listfp);
+           es_putc (*p, listfp);
          else
-           fprintf (listfp, "\\x%02x", *p);
+           es_fprintf (listfp, "\\x%02x", *p);
        }
-      fprintf (listfp, "\"\n");
+      es_fprintf (listfp, "\"\n");
     }
   return 0;
 }
@@ -2481,11 +2814,11 @@ parse_trust (IOBUF inp, int pkttype, unsigned long pktlen, PACKET * pkt)
 
   (void) pkttype;
 
+  pkt->pkt.ring_trust = xmalloc (sizeof *pkt->pkt.ring_trust);
   if (pktlen)
     {
       c = iobuf_get_noeof (inp);
       pktlen--;
-      pkt->pkt.ring_trust = xmalloc (sizeof *pkt->pkt.ring_trust);
       pkt->pkt.ring_trust->trustval = c;
       pkt->pkt.ring_trust->sigcache = 0;
       if (!c && pktlen == 1)
@@ -2498,14 +2831,16 @@ parse_trust (IOBUF inp, int pkttype, unsigned long pktlen, PACKET * pkt)
            pkt->pkt.ring_trust->sigcache = c;
        }
       if (list_mode)
-       fprintf (listfp, ":trust packet: flag=%02x sigcache=%02x\n",
-                pkt->pkt.ring_trust->trustval,
-                pkt->pkt.ring_trust->sigcache);
+       es_fprintf (listfp, ":trust packet: flag=%02x sigcache=%02x\n",
+                    pkt->pkt.ring_trust->trustval,
+                    pkt->pkt.ring_trust->sigcache);
     }
   else
     {
+      pkt->pkt.ring_trust->trustval = 0;
+      pkt->pkt.ring_trust->sigcache = 0;
       if (list_mode)
-       fprintf (listfp, ":trust packet: empty\n");
+       es_fprintf (listfp, ":trust packet: empty\n");
     }
   iobuf_skip_rest (inp, pktlen, 0);
 }
@@ -2524,6 +2859,8 @@ parse_plaintext (IOBUF inp, int pkttype, unsigned long pktlen,
   if (!partial && pktlen < 6)
     {
       log_error ("packet(%d) too short (%lu)\n", pkttype, (ulong) pktlen);
+      if (list_mode)
+        es_fputs (":literal data packet: [too short]\n", listfp);
       rc = gpg_error (GPG_ERR_INV_PACKET);
       goto leave;
     }
@@ -2558,26 +2895,25 @@ parse_plaintext (IOBUF inp, int pkttype, unsigned long pktlen,
     pktlen -= 4;
   pt->len = pktlen;
   pt->buf = inp;
-  pktlen = 0;
 
   if (list_mode)
     {
-      fprintf (listfp, ":literal data packet:\n"
-              "\tmode %c (%X), created %lu, name=\"",
-              mode >= ' ' && mode < 'z' ? mode : '?', mode,
-              (ulong) pt->timestamp);
+      es_fprintf (listfp, ":literal data packet:\n"
+                  "\tmode %c (%X), created %lu, name=\"",
+                  mode >= ' ' && mode < 'z' ? mode : '?', mode,
+                  (ulong) pt->timestamp);
       for (p = pt->name, i = 0; i < namelen; p++, i++)
        {
          if (*p >= ' ' && *p <= 'z')
-           putc (*p, listfp);
+           es_putc (*p, listfp);
          else
-           fprintf (listfp, "\\x%02x", *p);
+           es_fprintf (listfp, "\\x%02x", *p);
        }
-      fprintf (listfp, "\",\n\traw data: ");
+      es_fprintf (listfp, "\",\n\traw data: ");
       if (partial)
-       fprintf (listfp, "unknown length\n");
+       es_fprintf (listfp, "unknown length\n");
       else
-       fprintf (listfp, "%lu bytes\n", (ulong) pt->len);
+       es_fprintf (listfp, "%lu bytes\n", (ulong) pt->len);
     }
 
  leave:
@@ -2603,7 +2939,7 @@ parse_compressed (IOBUF inp, int pkttype, unsigned long pktlen,
   zd->new_ctb = new_ctb;
   zd->buf = inp;
   if (list_mode)
-    fprintf (listfp, ":compressed packet: algo=%d\n", zd->algorithm);
+    es_fprintf (listfp, ":compressed packet: algo=%d\n", zd->algorithm);
   return 0;
 }
 
@@ -2634,6 +2970,8 @@ parse_encrypted (IOBUF inp, int pkttype, unsigned long pktlen,
        {
          log_error ("encrypted_mdc packet with unknown version %d\n",
                     version);
+          if (list_mode)
+            es_fputs (":encrypted data packet: [unknown version]\n", listfp);
          /*skip_rest(inp, pktlen); should we really do this? */
          rc = gpg_error (GPG_ERR_INV_PACKET);
          goto leave;
@@ -2650,7 +2988,9 @@ parse_encrypted (IOBUF inp, int pkttype, unsigned long pktlen,
     {
       /* Actually this is blocksize+2.  */
       log_error ("packet(%d) too short\n", pkttype);
-      rc = G10ERR_INVALID_PACKET;
+      if (list_mode)
+        es_fputs (":encrypted data packet: [too short]\n", listfp);
+      rc = GPG_ERR_INV_PACKET;
       iobuf_skip_rest (inp, pktlen, partial);
       goto leave;
     }
@@ -2663,12 +3003,12 @@ parse_encrypted (IOBUF inp, int pkttype, unsigned long pktlen,
   if (list_mode)
     {
       if (orig_pktlen)
-       fprintf (listfp, ":encrypted data packet:\n\tlength: %lu\n",
-                orig_pktlen);
+       es_fprintf (listfp, ":encrypted data packet:\n\tlength: %lu\n",
+                    orig_pktlen);
       else
-       fprintf (listfp, ":encrypted data packet:\n\tlength: unknown\n");
+       es_fprintf (listfp, ":encrypted data packet:\n\tlength: unknown\n");
       if (ed->mdc_method)
-       fprintf (listfp, "\tmdc_method: %d\n", ed->mdc_method);
+       es_fprintf (listfp, "\tmdc_method: %d\n", ed->mdc_method);
     }
 
   ed->buf = inp;
@@ -2693,7 +3033,7 @@ parse_mdc (IOBUF inp, int pkttype, unsigned long pktlen,
 
   mdc = pkt->pkt.mdc = xmalloc (sizeof *pkt->pkt.mdc);
   if (list_mode)
-    fprintf (listfp, ":mdc packet: length=%lu\n", pktlen);
+    es_fprintf (listfp, ":mdc packet: length=%lu\n", pktlen);
   if (!new_ctb || pktlen != 20)
     {
       log_error ("mdc_packet with invalid encoding\n");
@@ -2710,9 +3050,9 @@ parse_mdc (IOBUF inp, int pkttype, unsigned long pktlen,
 
 
 /*
- * This packet is internally generated by us (ibn armor.c) to transfer
+ * This packet is internally generated by us (in armor.c) to transfer
  * some information to the lower layer.  To make sure that this packet
- * is really a GPG faked one and not one comming from outside, we
+ * is really a GPG faked one and not one coming from outside, we
  * first check that there is a unique tag in it.
  *
  * The format of such a control packet is:
@@ -2732,7 +3072,7 @@ parse_gpg_control (IOBUF inp, int pkttype, unsigned long pktlen,
   (void) pkttype;
 
   if (list_mode)
-    fprintf (listfp, ":packet 63: length %lu ", pktlen);
+    es_fprintf (listfp, ":packet 63: length %lu ", pktlen);
 
   sesmark = get_session_marker (&sesmarklen);
   if (pktlen < sesmarklen + 1) /* 1 is for the control bytes */
@@ -2746,7 +3086,7 @@ parse_gpg_control (IOBUF inp, int pkttype, unsigned long pktlen,
     goto skipit;  /* Definitely too large.  We skip it to avoid an
                      overflow in the malloc.  */
   if (list_mode)
-    puts ("- gpg control packet");
+    es_fputs ("- gpg control packet", listfp);
 
   packet->pkt.gpg_control = xmalloc (sizeof *packet->pkt.gpg_control
                                     + pktlen - 1);
@@ -2765,7 +3105,7 @@ parse_gpg_control (IOBUF inp, int pkttype, unsigned long pktlen,
       int c;
 
       i = 0;
-      fprintf (listfp, "- private (rest length %lu)\n", pktlen);
+      es_fprintf (listfp, "- private (rest length %lu)\n", pktlen);
       if (partial)
        {
          while ((c = iobuf_get (inp)) != -1)
@@ -2780,7 +3120,7 @@ parse_gpg_control (IOBUF inp, int pkttype, unsigned long pktlen,
                break;
            }
        }
-      putc ('\n', listfp);
+      es_putc ('\n', listfp);
     }
   iobuf_skip_rest (inp, pktlen, 0);
   return gpg_error (GPG_ERR_INV_PACKET);
@@ -2807,4 +3147,3 @@ create_gpg_control (ctrlpkttype_t type, const byte * data, size_t datalen)
 
   return packet;
 }
-