gpg: Screen keyserver responses.
[gnupg.git] / g10 / parse-packet.c
index 42d680a..c69393a 100644 (file)
@@ -1,6 +1,7 @@
 /* parse-packet.c  - read packets
  * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006,
  *               2007, 2009, 2010 Free Software Foundation, Inc.
+ * Copyright (C) 2014 Werner Koch
  *
  * This file is part of GnuPG.
  *
@@ -15,7 +16,7 @@
  * 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 <http://www.gnu.org/licenses/>.
  */
 
 #include <config.h>
@@ -28,7 +29,6 @@
 #include "util.h"
 #include "packet.h"
 #include "iobuf.h"
-#include "cipher.h"
 #include "filter.h"
 #include "photoid.h"
 #include "options.h"
@@ -49,7 +49,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);
@@ -108,27 +108,32 @@ read_32 (IOBUF inp)
 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;
@@ -137,27 +142,23 @@ mpi_read (iobuf_t inp, unsigned int *ret_nread, int secure)
   for (i = 0; i < nbytes; i++)
     {
       p[i + 2] = iobuf_get (inp) & 0xff;
+      if (nread == nmax)
+        goto overflow;
       nread++;
     }
 
-  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;
-    }
+  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;
 }
 
@@ -403,11 +404,18 @@ 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);
+  if (retpos || list_mode)
+    {
+      pos = iobuf_tell (inp);
+      if (retpos)
+        *retpos = pos;
+    }
+  else
+    pos = 0; /* (silence compiler warning) */
 
   if ((ctb = iobuf_get (inp)) == -1)
     {
@@ -559,6 +567,12 @@ 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? " partial":"",
+                new_ctb? " new-ctb":"");
+
   pkt->pkttype = pkttype;
   rc = G10ERR_UNKNOWN_PACKET;  /* default error */
   switch (pkttype)
@@ -720,27 +734,93 @@ 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 NULL is returned and a error messages
+   printed.  */
 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)
 {
@@ -774,6 +854,8 @@ parse_marker (IOBUF inp, int pkttype, unsigned long pktlen)
 
  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;
 }
@@ -790,6 +872,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;
     }
@@ -798,12 +882,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;
     }
@@ -826,11 +914,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;
     }
@@ -896,7 +988,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;
@@ -905,6 +996,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;
     }
@@ -913,6 +1006,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;
     }
@@ -939,40 +1034,31 @@ parse_pubkeyenc (IOBUF inp, int pkttype, unsigned long pktlen,
     }
   else
     {
-      if( k->pubkey_algo != PUBKEY_ALGO_ECDH )  {
-        for (i = 0; i < ndata; i++)
-         {
-           n = pktlen;
-           k->data[i] = mpi_read (inp, &n, 0);
-           pktlen -= n;
-           if (list_mode)
-             {
-               es_fprintf (listfp, "\tdata: ");
-               mpi_print (listfp, k->data[i], mpi_print_mode);
-               es_putc ('\n', listfp);
-             }
-           if (!k->data[i])
-             rc = gpg_error (GPG_ERR_INV_PACKET);
-         }
-      }
-      else  
+      for (i = 0; i < ndata; i++)
         {
-               byte encr_buf[255];
-               assert( ndata == 2 );
-               n = pktlen; k->data[0] = mpi_read(inp, &n, 0); pktlen -=n;
-               rc = iobuf_read_size_body( inp, encr_buf, sizeof(encr_buf), pktlen, k->data+1 );
-               if( rc )
-                       goto leave;
-               if( list_mode ) {
-                       es_fprintf (listfp, "\tdata: ");
-                       mpi_print(listfp, k->data[0], mpi_print_mode );
-                       es_putc ('\n', listfp);
-                       es_fprintf (listfp, "\tdata: [% 3d bytes] ", encr_buf[0]);
-                       mpi_print(listfp, k->data[1], mpi_print_mode );
-                       es_putc ('\n', listfp);
-               }
-               pktlen -= (encr_buf[0]+1);
-      }
+          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:
@@ -1338,7 +1424,7 @@ 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)
@@ -1347,7 +1433,7 @@ enum_sig_subpkt (const subpktarea_t * pktbuf, sigsubpkttype_t reqtype,
          buflen -= 4;
        }
       else if (n >= 192) /* 4 byte special encoded length header.  */
-       {                       
+       {
          if (buflen < 2)
            goto too_short;
          n = ((n - 192) << 8) + *buffer + 192;
@@ -1466,7 +1552,7 @@ parse_revkeys (PKT_signature * sig)
                                                     SIGSUBPKT_REV_KEY,
                                                     &len, &seq, NULL)))
     {
-      if (len == sizeof (struct revocation_key) 
+      if (len == sizeof (struct revocation_key)
           && (revkey->class & 0x80))  /* 0x80 bit must be set.  */
        {
          sig->revkey = xrealloc (sig->revkey,
@@ -1492,6 +1578,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);
@@ -1502,6 +1590,8 @@ 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;
     }
@@ -1529,12 +1619,14 @@ parse_signature (IOBUF inp, int pkttype, unsigned long pktlen,
   sig->flags.exportable = 1;
   sig->flags.revocable = 1;
   if (is_v4) /* Read subpackets.  */
-    { 
+    {
       n = read_16 (inp);
       pktlen -= 2;  /* Length of hashed data. */
       if (n > 10000)
        {
          log_error ("signature packet: hashed data too long\n");
+          if (list_mode)
+            es_fputs (":signature packet: [hashed data too long]\n", listfp);
          rc = G10ERR_INVALID_PACKET;
          goto leave;
        }
@@ -1547,6 +1639,8 @@ 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;
            }
@@ -1557,6 +1651,8 @@ parse_signature (IOBUF inp, int pkttype, unsigned long pktlen,
       if (n > 10000)
        {
          log_error ("signature packet: unhashed data too long\n");
+          if (list_mode)
+            es_fputs (":signature packet: [unhashed data too long]\n", listfp);
          rc = G10ERR_INVALID_PACKET;
          goto leave;
        }
@@ -1569,6 +1665,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;
            }
@@ -1577,8 +1675,10 @@ parse_signature (IOBUF inp, int pkttype, unsigned long pktlen,
     }
 
   if (pktlen < 5)  /* Sanity check.  */
-    {                          
+    {
       log_error ("packet(%d) too short\n", pkttype);
+      if (list_mode)
+        es_fputs (":signature packet: [too short]\n", listfp);
       rc = G10ERR_INVALID_PACKET;
       goto leave;
     }
@@ -1589,7 +1689,7 @@ parse_signature (IOBUF inp, int pkttype, unsigned long pktlen,
   pktlen--;
 
   if (is_v4 && sig->pubkey_algo)  /* Extract required information.  */
-    {                  
+    {
       const byte *p;
       size_t len;
 
@@ -1704,8 +1804,7 @@ parse_signature (IOBUF inp, int pkttype, unsigned long pktlen,
       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;
        }
     }
@@ -1743,6 +1842,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;
     }
@@ -1751,6 +1852,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;
     }
@@ -1835,7 +1938,6 @@ parse_key (IOBUF inp, int pkttype, unsigned long pktlen,
 {
   gpg_error_t err = 0;
   int i, version, algorithm;
-  unsigned n;
   unsigned long timestamp, expiredate, max_expiredate;
   int npkey, nskey;
   int is_v4 = 0;
@@ -1875,6 +1977,8 @@ parse_key (IOBUF inp, int pkttype, unsigned long pktlen,
   else if (version != 2 && version != 3)
     {
       log_error ("packet(%d) with unknown version %d\n", pkttype, version);
+      if (list_mode)
+        es_fputs (":key packet: [unknown version]\n", listfp);
       err = gpg_error (GPG_ERR_INV_PACKET);
       goto leave;
     }
@@ -1882,6 +1986,8 @@ parse_key (IOBUF inp, int pkttype, unsigned long pktlen,
   if (pktlen < 11)
     {
       log_error ("packet(%d) too short\n", pkttype);
+      if (list_mode)
+        es_fputs (":key packet: [too short]\n", listfp);
       err = gpg_error (GPG_ERR_INV_PACKET);
       goto leave;
     }
@@ -1933,78 +2039,54 @@ parse_key (IOBUF inp, int pkttype, unsigned long pktlen,
       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, 0),
-                                         pktlen * 8);
+                                         read_rest (inp, pktlen), pktlen * 8);
       pktlen = 0;
       goto leave;
     }
   else
     {
-      /* Fill in public key parameters.  */
-      if( algorithm != PUBKEY_ALGO_ECDSA && algorithm != PUBKEY_ALGO_ECDH )  {
-        for (i = 0; i < npkey; i++)
-         {
-           n = pktlen;
-           pk->pkey[i] = mpi_read (inp, &n, 0);
-           pktlen -= n;
-           if (list_mode)
-             {
-               es_fprintf (listfp, "\tpkey[%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);
-         }
-      }
-      else  {
-            /* note that the code in this function ignores the errors */
-           byte name_oid[256];
-           err = iobuf_read_size_body( inp, name_oid, sizeof(name_oid), pktlen, pk->pkey+0 );
-           if( err )
-               goto leave;
-           n = name_oid[0];
-           if( list_mode )
-              es_fprintf (listfp,   "\tpkey[0]: curve OID [%d] ...%02x %02x\n", 
-                       n, name_oid[1+n-2], name_oid[1+n-1] );
-           pktlen -= (n+1);
-           /* set item [1], which corresponds to the public key; these two fields are all we need to uniquely define the key */
-           // log_debug("Parsing ecc public key in the public packet, pktlen=%lu\n", pktlen);
-           n = pktlen; pk->pkey[1] = mpi_read( inp, &n, 0 ); pktlen -=n;
-           if( pk->pkey[1]==NULL )
-               err = gpg_error(G10ERR_INVALID_PACKET);
-           else if( list_mode ) {
-               es_fprintf (listfp,   "\tpkey[1]: ");
-               mpi_print(listfp, pk->pkey[1], mpi_print_mode);
-               es_putc ('\n', listfp);
-           }
-           /* One more field for ECDH */
-           if( algorithm == PUBKEY_ALGO_ECDH )  {
-#define kek_params name_oid
-              err = iobuf_read_size_body( inp, kek_params, sizeof(kek_params), pktlen, pk->pkey+2 );
-              if( err )
-                goto leave;
-              n = kek_params[0];
-              if( kek_params[1] != 1 ) {
-                       log_error("invalid ecdh KEK parameters field type in private key: understand type 1, but found 0x%02x\n", kek_params[1]);
-                       err = gpg_error(G10ERR_INVALID_PACKET);
-                       goto leave;
-              }
-              if( list_mode )
-                es_fprintf (listfp,   "\tpkey[2]: KEK params type=01 hash:%d sym-algo:%d\n", kek_params[1+n-2], kek_params[1+n-1] );
-              pktlen -= (n+1);
-#undef kek_params
-           }
-     }
-      if (err)
-       goto leave;
+      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]);
+                  es_fprintf (listfp, " %s (%s)",
+                              openpgp_oid_to_curve (curve), curve);
+                  xfree (curve);
+                }
+              es_putc ('\n', listfp);
+            }
+        }
     }
-
   if (list_mode)
     keyid_from_pk (pk, keyid);
 
@@ -2133,7 +2215,8 @@ parse_key (IOBUF inp, int pkttype, unsigned long pktlen,
                  ski->s2k.count = iobuf_get (inp);
                  pktlen--;
                  if (list_mode)
-                   es_fprintf (listfp, "\tprotect count: %lu\n",
+                   es_fprintf (listfp, "\tprotect count: %lu (%lu)\n",
+                                (ulong)S2K_DECODE_COUNT ((ulong)ski->s2k.count),
                                 (ulong) ski->s2k.count);
                }
              else if (ski->s2k.mode == 1002)
@@ -2154,7 +2237,7 @@ parse_key (IOBUF inp, int pkttype, unsigned long pktlen,
                }
            }
          else /* Old version; no S2K, so we set mode to 0, hash 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.  */
@@ -2164,7 +2247,7 @@ parse_key (IOBUF inp, int pkttype, unsigned long pktlen,
                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
@@ -2217,26 +2300,33 @@ parse_key (IOBUF inp, int pkttype, unsigned long pktlen,
           * up to the end of the packet into the first SKEY
           * element.  */
          pk->pkey[npkey] = gcry_mpi_set_opaque (NULL,
-                                                read_rest (inp, pktlen, 0),
+                                                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)
             es_fprintf (listfp, "\tskey[%d]: [v4 protected]\n", npkey);
        }
-      else 
-       {       
+      else
+       {
           /* The v3 method: The mpi length is not encrypted.  */
          for (i = npkey; i < nskey; i++)
            {
              if (ski->is_protected)
                {
                  pk->pkey[i] = read_protected_v3_mpi (inp, &pktlen);
+                  if (pk->pkey[i])
+                    gcry_mpi_set_flag (pk->pkey[i], GCRYMPI_FLAG_USER1);
                  if (list_mode)
                    es_fprintf (listfp, "\tskey[%d]: [v3 protected]\n", i);
                }
              else
                {
-                 n = pktlen;
+                 unsigned int n = pktlen;
                  pk->pkey[i] = mpi_read (inp, &n, 0);
                  pktlen -= n;
                  if (list_mode)
@@ -2290,7 +2380,7 @@ 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)
@@ -2299,7 +2389,7 @@ parse_attribute_subpkts (PKT_user_id * uid)
          buflen -= 4;
        }
       else if (n >= 192)  /* 2 byte special encoded length header.  */
-       {                       
+       {
          if (buflen < 2)
            goto too_short;
          n = ((n - 192) << 8) + *buffer + 192;
@@ -2354,6 +2444,8 @@ parse_user_id (IOBUF inp, int pkttype, unsigned long pktlen, PACKET * packet)
   if (pktlen > 2048)
     {
       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;
     }
@@ -2427,11 +2519,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 > 16*1024*1024)
+    {
+      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 G10ERR_INVALID_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;
@@ -2465,6 +2569,9 @@ parse_comment (IOBUF inp, int pkttype, unsigned long pktlen, PACKET * packet)
   if (pktlen > 65536)
     {
       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;
     }
@@ -2542,6 +2649,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;
     }
@@ -2652,6 +2761,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;
@@ -2668,6 +2779,8 @@ parse_encrypted (IOBUF inp, int pkttype, unsigned long pktlen,
     {
       /* Actually this is blocksize+2.  */
       log_error ("packet(%d) too short\n", pkttype);
+      if (list_mode)
+        es_fputs (":encrypted data packet: [too short]\n", listfp);
       rc = G10ERR_INVALID_PACKET;
       iobuf_skip_rest (inp, pktlen, partial);
       goto leave;
@@ -2825,4 +2938,3 @@ create_gpg_control (ctrlpkttype_t type, const byte * data, size_t datalen)
 
   return packet;
 }
-