(parse_dn_part): Map common OIDs to human readable
[gnupg.git] / g10 / parse-packet.c
index 4281ef1..c922eb5 100644 (file)
@@ -1,14 +1,15 @@
 /* parse-packet.c  - read packets
 /* parse-packet.c  - read packets
- *     Copyright (C) 1998 Free Software Foundation, Inc.
+ * Copyright (C) 1998, 1999, 2000, 2001, 2002,
+ *               2003 Free Software Foundation, Inc.
  *
  *
- * This file is part of GNUPG.
+ * This file is part of GnuPG.
  *
  *
- * GNUPG is free software; you can redistribute it and/or modify
+ * GnuPG is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version.
  *
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version.
  *
- * GNUPG is distributed in the hope that it will be useful,
+ * GnuPG is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
 #include "cipher.h"
 #include "memory.h"
 #include "filter.h"
 #include "cipher.h"
 #include "memory.h"
 #include "filter.h"
+#include "photoid.h"
 #include "options.h"
 #include "options.h"
+#include "main.h"
+#include "i18n.h"
 
 
-static mpi_print_mode = 0;
-static list_mode = 0;
+static int mpi_print_mode = 0;
+static int list_mode = 0;
 
 
-static int  parse( IOBUF inp, PACKET *pkt, int reqtype,
-                  ulong *retpos, int *skip, IOBUF out, int do_skip );
-static int  copy_packet( IOBUF inp, IOBUF out, int pkttype,
+static int  parse( iobuf_t inp, PACKET *pkt, int onlykeypkts,
+                  off_t *retpos, int *skip, iobuf_t out, int do_skip
+#ifdef DEBUG_PARSE_PACKET
+                  ,const char *dbg_w, const char *dbg_f, int dbg_l
+#endif
+                );
+static int  copy_packet( iobuf_t inp, iobuf_t out, int pkttype,
                                               unsigned long pktlen );
                                               unsigned long pktlen );
-static void skip_packet( IOBUF inp, int pkttype, unsigned long pktlen );
-static void skip_rest( IOBUF inp, unsigned long pktlen );
-static int  parse_publickey( IOBUF inp, int pkttype, unsigned long pktlen,
+static void skip_packet( iobuf_t inp, int pkttype, unsigned long pktlen );
+static void skip_rest( iobuf_t inp, unsigned long pktlen );
+static void *read_rest( iobuf_t inp, size_t pktlen );
+static int  parse_symkeyenc( iobuf_t inp, int pkttype, unsigned long pktlen,
+                                                            PACKET *packet );
+static int  parse_pubkeyenc( iobuf_t inp, int pkttype, unsigned long pktlen,
                                                             PACKET *packet );
                                                             PACKET *packet );
-static int  parse_signature( IOBUF inp, int pkttype, unsigned long pktlen,
+static int  parse_signature( iobuf_t inp, int pkttype, unsigned long pktlen,
                                                         PKT_signature *sig );
                                                         PKT_signature *sig );
-static int  parse_onepass_sig( IOBUF inp, int pkttype, unsigned long pktlen,
+static int  parse_onepass_sig( iobuf_t inp, int pkttype, unsigned long pktlen,
                                                        PKT_onepass_sig *ops );
                                                        PKT_onepass_sig *ops );
-static int  parse_certificate( IOBUF inp, int pkttype, unsigned long pktlen,
+static int  parse_key( iobuf_t inp, int pkttype, unsigned long pktlen,
                                      byte *hdr, int hdrlen, PACKET *packet );
                                      byte *hdr, int hdrlen, PACKET *packet );
-static int  parse_user_id( IOBUF inp, int pkttype, unsigned long pktlen,
+static int  parse_user_id( iobuf_t inp, int pkttype, unsigned long pktlen,
                                                           PACKET *packet );
                                                           PACKET *packet );
-static void parse_subkey( IOBUF inp, int pkttype, unsigned long pktlen );
-static int  parse_comment( IOBUF inp, int pkttype, unsigned long pktlen,
+static int  parse_attribute( iobuf_t inp, int pkttype, unsigned long pktlen,
                                                           PACKET *packet );
                                                           PACKET *packet );
-static void parse_trust( IOBUF inp, int pkttype, unsigned long pktlen );
-static int  parse_plaintext( IOBUF inp, int pkttype, unsigned long pktlen,
-                                                               PACKET *pkt );
-static int  parse_compressed( IOBUF inp, int pkttype, unsigned long pktlen,
+static int  parse_comment( iobuf_t inp, int pkttype, unsigned long pktlen,
                                                           PACKET *packet );
                                                           PACKET *packet );
-static int  parse_encrypted( IOBUF inp, int pkttype, unsigned long pktlen,
+static void parse_trust( iobuf_t inp, int pkttype, unsigned long pktlen,
                                                           PACKET *packet );
                                                           PACKET *packet );
-#if 0
-static u16
-checksum( byte *p )
-{
-    u16 n, a;
-
-    n = *p++ << 8;
-    n |= *p++;
-    for(a=0; n; n-- )
-       a += *p++;
-    return a;
-}
-#endif
+static int  parse_plaintext( iobuf_t inp, int pkttype, unsigned long pktlen,
+                                              PACKET *packet, int new_ctb);
+static int  parse_compressed( iobuf_t inp, int pkttype, unsigned long pktlen,
+                                              PACKET *packet, int new_ctb );
+static int  parse_encrypted( iobuf_t inp, int pkttype, unsigned long pktlen,
+                                              PACKET *packet, int new_ctb);
+static int  parse_mdc( iobuf_t inp, int pkttype, unsigned long pktlen,
+                                              PACKET *packet, int new_ctb);
+static int  parse_gpg_control( iobuf_t inp, int pkttype, unsigned long pktlen,
+                               PACKET *packet );
 
 static unsigned short
 
 static unsigned short
-read_16(IOBUF inp)
+read_16(iobuf_t inp)
 {
     unsigned short a;
     a = iobuf_get_noeof(inp) << 8;
 {
     unsigned short a;
     a = iobuf_get_noeof(inp) << 8;
@@ -86,7 +90,7 @@ read_16(IOBUF inp)
 }
 
 static unsigned long
 }
 
 static unsigned long
-read_32(IOBUF inp)
+read_32(iobuf_t inp)
 {
     unsigned long a;
     a =  iobuf_get_noeof(inp) << 24;
 {
     unsigned long a;
     a =  iobuf_get_noeof(inp) << 24;
@@ -96,26 +100,29 @@ read_32(IOBUF inp)
     return a;
 }
 
     return a;
 }
 
-static unsigned long
-buffer_to_u32( const byte *buffer )
-{
-    unsigned long a;
-    a =  *buffer << 24;
-    a |= buffer[1] << 16;
-    a |= buffer[2] << 8;
-    a |= buffer[3];
-    return a;
-}
 
 int
 set_packet_list_mode( int mode )
 {
     int old = list_mode;
     list_mode = mode;
 
 int
 set_packet_list_mode( int mode )
 {
     int old = list_mode;
     list_mode = mode;
-    mpi_print_mode = DBG_MPI;
+    /* FIXME(gcrypt) mpi_print_mode = DBG_MPI; */
     return old;
 }
 
     return old;
 }
 
+static void
+unknown_pubkey_warning( int algo )
+{
+    static byte unknown_pubkey_algos[256];
+
+    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
  * Returns: 0 := valid packet in pkt
 /****************
  * Parse a Packet and return it in packet
  * Returns: 0 := valid packet in pkt
@@ -124,8 +131,20 @@ set_packet_list_mode( int mode )
  * Note: The function may return an error and a partly valid packet;
  * caller must free this packet.
  */
  * 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_t inp, PACKET *pkt, const char *dbg_f, int dbg_l )
+{
+    int skip, rc;
+
+    do {
+       rc = parse( inp, pkt, 0, NULL, &skip, NULL, 0, "parse", dbg_f, dbg_l );
+    } while( skip );
+    return rc;
+}
+#else
 int
 int
-parse_packet( IOBUF inp, PACKET *pkt )
+parse_packet( iobuf_t inp, PACKET *pkt )
 {
     int skip, rc;
 
 {
     int skip, rc;
 
@@ -134,26 +153,54 @@ parse_packet( IOBUF inp, PACKET *pkt )
     } while( skip );
     return rc;
 }
     } while( skip );
     return rc;
 }
+#endif
 
 /****************
 
 /****************
- * Like parse packet, but do only return packets of the given type.
+ * Like parse packet, but only return secret or public (sub)key packets.
  */
  */
+#ifdef DEBUG_PARSE_PACKET
 int
 int
-search_packet( IOBUF inp, PACKET *pkt, int pkttype, ulong *retpos )
+dbg_search_packet( iobuf_t inp, PACKET *pkt, off_t *retpos, int with_uid,
+                  const char *dbg_f, int dbg_l )
 {
     int skip, rc;
 
     do {
 {
     int skip, rc;
 
     do {
-       rc = parse( inp, pkt, pkttype, retpos, &skip, NULL, 0 );
+       rc = parse( inp, pkt, with_uid?2:1, retpos, &skip, NULL, 0, "search", dbg_f, dbg_l );
     } while( skip );
     return rc;
 }
     } while( skip );
     return rc;
 }
+#else
+int
+search_packet( iobuf_t inp, PACKET *pkt, off_t *retpos, int with_uid )
+{
+    int skip, rc;
+
+    do {
+       rc = parse( inp, pkt, with_uid?2:1, retpos, &skip, NULL, 0 );
+    } while( skip );
+    return rc;
+}
+#endif
 
 /****************
  * Copy all packets from INP to OUT, thereby removing unused spaces.
  */
 
 /****************
  * Copy all packets from INP to OUT, thereby removing unused spaces.
  */
+#ifdef DEBUG_PARSE_PACKET
+int
+dbg_copy_all_packets( iobuf_t inp, iobuf_t out,
+                  const char *dbg_f, int dbg_l )
+{
+    PACKET pkt;
+    int skip, rc=0;
+    do {
+       init_packet(&pkt);
+    } while( !(rc = parse( inp, &pkt, 0, NULL, &skip, out, 0, "copy", dbg_f, dbg_l )));
+    return rc;
+}
+#else
 int
 int
-copy_all_packets( IOBUF inp, IOBUF out )
+copy_all_packets( iobuf_t inp, iobuf_t out )
 {
     PACKET pkt;
     int skip, rc=0;
 {
     PACKET pkt;
     int skip, rc=0;
@@ -162,13 +209,30 @@ copy_all_packets( IOBUF inp, IOBUF out )
     } while( !(rc = parse( inp, &pkt, 0, NULL, &skip, out, 0 )));
     return rc;
 }
     } while( !(rc = parse( inp, &pkt, 0, NULL, &skip, out, 0 )));
     return rc;
 }
+#endif
 
 /****************
  * Copy some packets from INP to OUT, thereby removing unused spaces.
 
 /****************
  * Copy some packets from INP to OUT, thereby removing unused spaces.
- * Stop after at offset STOPoff (i.e. don't copy the packet at this offset)
+ * Stop at offset STOPoff (i.e. don't copy packets at this or later offsets)
  */
  */
+#ifdef DEBUG_PARSE_PACKET
 int
 int
-copy_some_packets( IOBUF inp, IOBUF out, ulong stopoff )
+dbg_copy_some_packets( iobuf_t inp, iobuf_t out, off_t stopoff,
+                  const char *dbg_f, int dbg_l )
+{
+    PACKET pkt;
+    int skip, rc=0;
+    do {
+       if( iobuf_tell(inp) >= stopoff )
+           return 0;
+       init_packet(&pkt);
+    } while( !(rc = parse( inp, &pkt, 0, NULL, &skip, out, 0,
+                                    "some", dbg_f, dbg_l )) );
+    return rc;
+}
+#else
+int
+copy_some_packets( iobuf_t inp, iobuf_t out, off_t stopoff )
 {
     PACKET pkt;
     int skip, rc=0;
 {
     PACKET pkt;
     int skip, rc=0;
@@ -179,12 +243,28 @@ copy_some_packets( IOBUF inp, IOBUF out, ulong stopoff )
     } while( !(rc = parse( inp, &pkt, 0, NULL, &skip, out, 0 )) );
     return rc;
 }
     } while( !(rc = parse( inp, &pkt, 0, NULL, &skip, out, 0 )) );
     return rc;
 }
+#endif
 
 /****************
  * Skip over N packets
  */
 
 /****************
  * Skip over N packets
  */
+#ifdef DEBUG_PARSE_PACKET
 int
 int
-skip_some_packets( IOBUF inp, unsigned n )
+dbg_skip_some_packets( iobuf_t inp, unsigned n,
+                  const char *dbg_f, int dbg_l )
+{
+    int skip, rc=0;
+    PACKET pkt;
+
+    for( ;n && !rc; n--) {
+       init_packet(&pkt);
+       rc = parse( inp, &pkt, 0, NULL, &skip, NULL, 1, "skip", dbg_f, dbg_l );
+    }
+    return rc;
+}
+#else
+int
+skip_some_packets( iobuf_t inp, unsigned n )
 {
     int skip, rc=0;
     PACKET pkt;
 {
     int skip, rc=0;
     PACKET pkt;
@@ -195,61 +275,91 @@ skip_some_packets( IOBUF inp, unsigned n )
     }
     return rc;
 }
     }
     return rc;
 }
+#endif
+
 
 /****************
 
 /****************
- * Parse packet. Set the variable skip points to to 1 if the packet
- * should be skipped; this is the case if either there is a
- * requested packet type and the parsed packet doesn't match or the
+ * Parse packet. Set the variable skip points to 1 if the packet
+ * should be skipped; this is the case if either ONLYKEYPKTS is set
+ * and the parsed packet isn't one or the
  * packet-type is 0, indicating deleted stuff.
  * if OUT is not NULL, a special copymode is used.
  */
 static int
  * packet-type is 0, indicating deleted stuff.
  * if OUT is not NULL, a special copymode is used.
  */
 static int
-parse( IOBUF inp, PACKET *pkt, int reqtype, ulong *retpos,
-       int *skip, IOBUF out, int do_skip )
+parse( iobuf_t inp, PACKET *pkt, int onlykeypkts, off_t *retpos,
+       int *skip, iobuf_t out, int do_skip
+#ifdef DEBUG_PARSE_PACKET
+       ,const char *dbg_w, const char *dbg_f, int dbg_l
+#endif
+     )
 {
 {
-    int rc, c, ctb, pkttype, lenbytes;
+    int rc=0, c, ctb, pkttype, lenbytes;
     unsigned long pktlen;
     unsigned long pktlen;
-    byte hdr[5];
+    byte hdr[8];
     int hdrlen;
     int hdrlen;
-    int pgp3 = 0;
+    int new_ctb = 0;
+    int with_uid = (onlykeypkts == 2);
 
     *skip = 0;
     assert( !pkt->pkt.generic );
     if( retpos )
        *retpos = iobuf_tell(inp);
 
     *skip = 0;
     assert( !pkt->pkt.generic );
     if( retpos )
        *retpos = iobuf_tell(inp);
-    if( (ctb = iobuf_get(inp)) == -1 )
-       return -1;
+
+    if( (ctb = iobuf_get(inp)) == -1 ) {
+       rc = -1;
+       goto leave;
+    }
     hdrlen=0;
     hdr[hdrlen++] = ctb;
     if( !(ctb & 0x80) ) {
     hdrlen=0;
     hdr[hdrlen++] = ctb;
     if( !(ctb & 0x80) ) {
-       log_error("%s: invalid packet (ctb=%02x)\n", iobuf_where(inp), ctb );
-       return G10ERR_INVALID_PACKET;
+        log_error("%s: invalid packet (ctb=%02x)\n", iobuf_where(inp), ctb );
+       rc = GPG_ERR_INV_PACKET;
+       goto leave;
     }
     pktlen = 0;
     }
     pktlen = 0;
-    pgp3 = !!(ctb & 0x40);
-    if( pgp3 ) {
-       pkttype =  ctb & 0x3f;
+    new_ctb = !!(ctb & 0x40);
+    if( new_ctb ) {
+        pkttype = ctb & 0x3f;
        if( (c = iobuf_get(inp)) == -1 ) {
            log_error("%s: 1st length byte missing\n", iobuf_where(inp) );
        if( (c = iobuf_get(inp)) == -1 ) {
            log_error("%s: 1st length byte missing\n", iobuf_where(inp) );
-           return G10ERR_INVALID_PACKET;
-       }
-       hdr[hdrlen++] = c;
-       if( c < 192 )
-           pktlen = c;
-       else if( c < 224 ) {
-           pktlen = (c - 192) * 256;
-           if( (c = iobuf_get(inp)) == -1 ) {
-               log_error("%s: 2nd length byte missing\n", iobuf_where(inp) );
-               return G10ERR_INVALID_PACKET;
-           }
-           hdr[hdrlen++] = c;
-           pktlen += c + 192;
+           rc = GPG_ERR_INV_PACKET;
+           goto leave;
        }
        }
-       else { /* partial body length */
-           pktlen = 1 << (c & 0x1f);
-           log_debug("partial body length of %lu bytes\n", pktlen );
-           iobuf_set_partial_block_mode(inp, pktlen);
-           pktlen = 0;/* to indicate partial length */
+        if (pkttype == PKT_COMPRESSED) {
+             iobuf_set_partial_block_mode(inp, c & 0xff);
+             pktlen = 0;/* to indicate partial length */
+        }
+        else {
+             hdr[hdrlen++] = c;
+             if( c < 192 )
+                  pktlen = c;
+             else if( c < 224 ) {
+                  pktlen = (c - 192) * 256;
+                  if( (c = iobuf_get(inp)) == -1 ) {
+                       log_error("%s: 2nd length byte missing\n",
+                                 iobuf_where(inp) );
+                       rc = GPG_ERR_INV_PACKET;
+                       goto leave;
+                  }
+                  hdr[hdrlen++] = c;
+                  pktlen += c + 192;
+             }
+             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 ) {
+                       log_error("%s: 4 byte length invalid\n",
+                                 iobuf_where(inp) );
+                       rc = GPG_ERR_INV_PACKET;
+                       goto leave;
+                  }
+                  pktlen |= (hdr[hdrlen++] = c );
+             }
+             else { /* partial body length */
+                  iobuf_set_partial_block_mode(inp, c & 0xff);
+                  pktlen = 0;/* to indicate partial length */
+             }
        }
     }
     else {
        }
     }
     else {
@@ -257,9 +367,29 @@ parse( IOBUF inp, PACKET *pkt, int reqtype, ulong *retpos,
        lenbytes = ((ctb&3)==3)? 0 : (1<<(ctb & 3));
        if( !lenbytes ) {
            pktlen = 0; /* don't know the value */
        lenbytes = ((ctb&3)==3)? 0 : (1<<(ctb & 3));
        if( !lenbytes ) {
            pktlen = 0; /* don't know the value */
-           if( pkttype != PKT_COMPRESSED )
-               iobuf_set_block_mode(inp, 1);
-       }
+            switch (pkttype) {
+              case PKT_ENCRYPTED:
+              case PKT_PLAINTEXT:
+                /* These partial length encodings are from an very
+                  early GnuPG release and deprecated.  However we
+                  still support them read-wise.  Note, that we should
+                  not allow them for any key related packets, because
+                  this might render a keyring unusable if an errenous
+                  packet indicated this mode but not complying to it
+                  gets imported. */
+                iobuf_set_block_mode(inp, 1);
+               break;
+
+              case PKT_COMPRESSED:
+                break; /* the orginal pgp 2 way. */
+
+              default:
+                log_error ("%s: old style partial length "
+                           "for invalid packet type\n", iobuf_where(inp) );
+                rc = gpg_error (GPG_ERR_INV_PACKET);
+                goto leave;
+            }
+       }
        else {
            for( ; lenbytes; lenbytes-- ) {
                pktlen <<= 8;
        else {
            for( ; lenbytes; lenbytes-- ) {
                pktlen <<= 8;
@@ -268,72 +398,111 @@ parse( IOBUF inp, PACKET *pkt, int reqtype, ulong *retpos,
        }
     }
 
        }
     }
 
+    if (pktlen == 0xffffffff) {
+        /* with a some probability this is caused by a problem in the
+         * the uncompressing layer - in some error cases it just loops
+         * and spits out 0xff bytes. */
+        log_error ("%s: garbled packet detected\n", iobuf_where(inp) );
+       g10_exit (2);
+    }
+
     if( out && pkttype ) {
     if( out && pkttype ) {
-       if( iobuf_write( out, hdr, hdrlen ) == -1 )
-           rc = G10ERR_WRITE_FILE;
-       else
+        rc = iobuf_write( out, hdr, hdrlen );
+       if (!rc)
            rc = copy_packet(inp, out, pkttype, pktlen );
            rc = copy_packet(inp, out, pkttype, pktlen );
-       return rc;
+       goto leave;
     }
 
     }
 
-    if( do_skip || !pkttype || (reqtype && pkttype != reqtype) ) {
-       skip_packet(inp, pkttype, pktlen);
+    if (with_uid && pkttype == PKT_USER_ID)
+        ;
+    else if( do_skip 
+        || !pkttype
+        || (onlykeypkts && pkttype != PKT_PUBLIC_SUBKEY
+                        && pkttype != PKT_PUBLIC_KEY
+                        && pkttype != PKT_SECRET_SUBKEY
+                        && pkttype != PKT_SECRET_KEY  ) ) {
+       skip_rest(inp, pktlen);
        *skip = 1;
        *skip = 1;
-       return 0;
+       rc = 0;
+       goto leave;
     }
 
     }
 
-    if( DBG_PACKET )
+    if( DBG_PACKET ) {
+#ifdef DEBUG_PARSE_PACKET
+       log_debug("parse_packet(iob=%d): type=%d length=%lu%s (%s.%s.%d)\n",
+                  iobuf_id(inp), pkttype, pktlen, new_ctb?" (new_ctb)":"",
+                   dbg_w, dbg_f, dbg_l );
+#else
        log_debug("parse_packet(iob=%d): type=%d length=%lu%s\n",
        log_debug("parse_packet(iob=%d): type=%d length=%lu%s\n",
-                  iobuf_id(inp), pkttype, pktlen, pgp3?" (pgp3)":"" );
+                  iobuf_id(inp), pkttype, pktlen, new_ctb?" (new_ctb)":"" );
+#endif
+    }
     pkt->pkttype = pkttype;
     pkt->pkttype = pkttype;
-    rc = G10ERR_UNKNOWN_PACKET; /* default error */
+    rc = GPG_ERR_UNKNOWN_PACKET; /* default error */
     switch( pkttype ) {
     switch( pkttype ) {
-      case PKT_PUBLIC_CERT:
-       pkt->pkt.public_cert = m_alloc_clear(sizeof *pkt->pkt.public_cert );
-       rc = parse_certificate(inp, pkttype, pktlen, hdr, hdrlen, pkt );
+      case PKT_PUBLIC_KEY:
+      case PKT_PUBLIC_SUBKEY:
+       pkt->pkt.public_key = xcalloc (1,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 = xcalloc (1,sizeof *pkt->pkt.secret_key );
+       rc = parse_key(inp, pkttype, pktlen, hdr, hdrlen, pkt );
        break;
        break;
-      case PKT_SECRET_CERT:
-      case PKT_SECKEY_SUBCERT:
-       pkt->pkt.secret_cert = m_alloc_clear(sizeof *pkt->pkt.secret_cert );
-       rc = parse_certificate(inp, pkttype, pktlen, hdr, hdrlen, pkt );
+      case PKT_SYMKEY_ENC:
+       rc = parse_symkeyenc( inp, pkttype, pktlen, pkt );
        break;
       case PKT_PUBKEY_ENC:
        break;
       case PKT_PUBKEY_ENC:
-       rc = parse_publickey(inp, pkttype, pktlen, pkt );
+       rc = parse_pubkeyenc(inp, pkttype, pktlen, pkt );
        break;
       case PKT_SIGNATURE:
        break;
       case PKT_SIGNATURE:
-       pkt->pkt.signature = m_alloc_clear(sizeof *pkt->pkt.signature );
+       pkt->pkt.signature = xcalloc (1,sizeof *pkt->pkt.signature );
        rc = parse_signature(inp, pkttype, pktlen, pkt->pkt.signature );
        break;
       case PKT_ONEPASS_SIG:
        rc = parse_signature(inp, pkttype, pktlen, pkt->pkt.signature );
        break;
       case PKT_ONEPASS_SIG:
-       pkt->pkt.onepass_sig = m_alloc_clear(sizeof *pkt->pkt.onepass_sig );
+       pkt->pkt.onepass_sig = xcalloc (1,sizeof *pkt->pkt.onepass_sig );
        rc = parse_onepass_sig(inp, pkttype, pktlen, pkt->pkt.onepass_sig );
        break;
       case PKT_USER_ID:
        rc = parse_user_id(inp, pkttype, pktlen, pkt );
        break;
        rc = parse_onepass_sig(inp, pkttype, pktlen, pkt->pkt.onepass_sig );
        break;
       case PKT_USER_ID:
        rc = parse_user_id(inp, pkttype, pktlen, pkt );
        break;
-      case PKT_PUBKEY_SUBCERT:
-       parse_subkey(inp, pkttype, pktlen);
+      case PKT_ATTRIBUTE:
+       pkt->pkttype = pkttype = PKT_USER_ID;  /* we store it in the userID */
+       rc = parse_attribute(inp, pkttype, pktlen, pkt);
        break;
        break;
+      case PKT_OLD_COMMENT:
       case PKT_COMMENT:
        rc = parse_comment(inp, pkttype, pktlen, pkt);
        break;
       case PKT_RING_TRUST:
       case PKT_COMMENT:
        rc = parse_comment(inp, pkttype, pktlen, pkt);
        break;
       case PKT_RING_TRUST:
-       parse_trust(inp, pkttype, pktlen);
+       parse_trust(inp, pkttype, pktlen, pkt);
+       rc = 0;
        break;
       case PKT_PLAINTEXT:
        break;
       case PKT_PLAINTEXT:
-       rc = parse_plaintext(inp, pkttype, pktlen, pkt );
+       rc = parse_plaintext(inp, pkttype, pktlen, pkt, new_ctb );
        break;
       case PKT_COMPRESSED:
        break;
       case PKT_COMPRESSED:
-       rc = parse_compressed(inp, pkttype, pktlen, pkt );
+       rc = parse_compressed(inp, pkttype, pktlen, pkt, new_ctb );
        break;
       case PKT_ENCRYPTED:
        break;
       case PKT_ENCRYPTED:
-       rc = parse_encrypted(inp, pkttype, pktlen, pkt );
+      case PKT_ENCRYPTED_MDC:
+       rc = parse_encrypted(inp, pkttype, pktlen, pkt, new_ctb );
        break;
        break;
+      case PKT_MDC:
+       rc = parse_mdc(inp, pkttype, pktlen, pkt, new_ctb );
+       break;
+      case PKT_GPG_CONTROL:
+        rc = parse_gpg_control(inp, pkttype, pktlen, pkt );
+        break;
       default:
        skip_packet(inp, pkttype, pktlen);
        break;
     }
 
       default:
        skip_packet(inp, pkttype, pktlen);
        break;
     }
 
+  leave:
+    if( !rc && iobuf_error(inp) )
+       rc = GPG_ERR_INV_KEYRING;
     return rc;
 }
 
     return rc;
 }
 
@@ -355,30 +524,31 @@ dump_hex_line( int c, int *i )
 
 
 static int
 
 
 static int
-copy_packet( IOBUF inp, IOBUF out, int pkttype, unsigned long pktlen )
+copy_packet( iobuf_t inp, iobuf_t out, int pkttype, unsigned long pktlen )
 {
 {
-    int n;
+    int rc, n;
     char buf[100];
 
     if( iobuf_in_block_mode(inp) ) {
        while( (n = iobuf_read( inp, buf, 100 )) != -1 )
     char buf[100];
 
     if( iobuf_in_block_mode(inp) ) {
        while( (n = iobuf_read( inp, buf, 100 )) != -1 )
-           if( iobuf_write(out, buf, n ) )
-               return G10ERR_WRITE_FILE; /* write error */
+           if( (rc = iobuf_write(out, buf, n )) )
+               return rc; /* write error */
     }
     else if( !pktlen && pkttype == PKT_COMPRESSED ) {
     }
     else if( !pktlen && pkttype == PKT_COMPRESSED ) {
+       log_debug("copy_packet: compressed!\n");
        /* compressed packet, copy till EOF */
        while( (n = iobuf_read( inp, buf, 100 )) != -1 )
        /* compressed packet, copy till EOF */
        while( (n = iobuf_read( inp, buf, 100 )) != -1 )
-           if( iobuf_write(out, buf, n ) )
-               return G10ERR_WRITE_FILE; /* write error */
+           if( (rc = iobuf_write(out, buf, n )) )
+               return rc; /* write error */
     }
     else {
        for( ; pktlen; pktlen -= n ) {
            n = pktlen > 100 ? 100 : pktlen;
            n = iobuf_read( inp, buf, n );
            if( n == -1 )
     }
     else {
        for( ; pktlen; pktlen -= n ) {
            n = pktlen > 100 ? 100 : pktlen;
            n = iobuf_read( inp, buf, n );
            if( n == -1 )
-               return G10ERR_READ_FILE;
-           if( iobuf_write(out, buf, n ) )
-               return G10ERR_WRITE_FILE; /* write error */
+              return GPG_ERR_GENERAL; /* FIXME(gcrypt): read error*/;
+           if( (rc = iobuf_write(out, buf, n )) )
+               return rc; /* write error */
        }
     }
     return 0;
        }
     }
     return 0;
@@ -386,13 +556,17 @@ copy_packet( IOBUF inp, IOBUF out, int pkttype, unsigned long pktlen )
 
 
 static void
 
 
 static void
-skip_packet( IOBUF inp, int pkttype, unsigned long pktlen )
+skip_packet( iobuf_t inp, int pkttype, unsigned long pktlen )
 {
     if( list_mode ) {
 {
     if( list_mode ) {
-       printf(":unknown packet: type %2d, length %lu\n", pkttype, pktlen );
+       if( pkttype == PKT_MARKER )
+           fputs(":marker packet:\n", stdout );
+       else
+           printf(":unknown packet: type %2d, length %lu\n", pkttype, pktlen);
        if( pkttype ) {
            int c, i=0 ;
        if( pkttype ) {
            int c, i=0 ;
-           printf("dump:");
+           if( pkttype != PKT_MARKER )
+               fputs("dump:", stdout );
            if( iobuf_in_block_mode(inp) ) {
                while( (c=iobuf_get(inp)) != -1 )
                    dump_hex_line(c, &i);
            if( iobuf_in_block_mode(inp) ) {
                while( (c=iobuf_get(inp)) != -1 )
                    dump_hex_line(c, &i);
@@ -409,7 +583,7 @@ skip_packet( IOBUF inp, int pkttype, unsigned long pktlen )
 }
 
 static void
 }
 
 static void
-skip_rest( IOBUF inp, unsigned long pktlen )
+skip_rest( iobuf_t inp, unsigned long pktlen )
 {
     if( iobuf_in_block_mode(inp) ) {
        while( iobuf_get(inp) != -1 )
 {
     if( iobuf_in_block_mode(inp) ) {
        while( iobuf_get(inp) != -1 )
@@ -417,82 +591,481 @@ skip_rest( IOBUF inp, unsigned long pktlen )
     }
     else {
        for( ; pktlen; pktlen-- )
     }
     else {
        for( ; pktlen; pktlen-- )
-           iobuf_get(inp);
+           if( iobuf_get(inp) == -1 )
+               break;
+    }
+}
+
+
+static void *
+read_rest( iobuf_t inp, size_t pktlen )
+{
+    byte *p;
+    int i;
+
+    if( iobuf_in_block_mode(inp) ) {
+       log_error("read_rest: can't store stream data\n");
+       p = NULL;
+    }
+    else {
+       p = xmalloc ( pktlen );
+       for(i=0; pktlen; pktlen--, i++ )
+           p[i] = iobuf_get(inp);
     }
     }
+    return p;
 }
 
 
 }
 
 
+
 static int
 static int
-parse_publickey( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *packet )
+parse_symkeyenc( iobuf_t inp, int pkttype, unsigned long pktlen, PACKET *packet )
 {
 {
-    unsigned n;
+    PKT_symkey_enc *k;
+    int rc = 0;
+    int i, version, s2kmode, cipher_algo, hash_algo, seskeylen, minlen;
+
+    if( pktlen < 4 ) {
+       log_error("packet(%d) too short\n", pkttype);
+        rc = GPG_ERR_INV_PACKET;
+       goto leave;
+    }
+    version = iobuf_get_noeof(inp); pktlen--;
+    if( version != 4 ) {
+       log_error("packet(%d) with unknown version %d\n", pkttype, version);
+        rc = GPG_ERR_INV_PACKET;
+       goto leave;
+    }
+    if( pktlen > 200 ) { /* (we encode the seskeylen in a byte) */
+       log_error("packet(%d) too large\n", pkttype);
+        rc = GPG_ERR_INV_PACKET;
+       goto leave;
+    }
+    cipher_algo = iobuf_get_noeof(inp); pktlen--;
+    s2kmode = iobuf_get_noeof(inp); pktlen--;
+    hash_algo = iobuf_get_noeof(inp); pktlen--;
+    switch( s2kmode ) {
+      case 0:  /* simple s2k */
+       minlen = 0;
+       break;
+      case 1:  /* salted s2k */
+       minlen = 8;
+       break;
+      case 3:  /* iterated+salted s2k */
+       minlen = 9;
+       break;
+      default:
+       log_error("unknown S2K %d\n", s2kmode );
+       goto leave;
+    }
+    if( minlen > pktlen ) {
+       log_error("packet with S2K %d too short\n", s2kmode );
+        rc = GPG_ERR_INV_PACKET;
+       goto leave;
+    }
+    seskeylen = pktlen - minlen;
+    k = packet->pkt.symkey_enc = xcalloc (1, sizeof *packet->pkt.symkey_enc
+                                               + seskeylen - 1 );
+    k->version = version;
+    k->cipher_algo = cipher_algo;
+    k->s2k.mode = s2kmode;
+    k->s2k.hash_algo = hash_algo;
+    if( s2kmode == 1 || s2kmode == 3 ) {
+       for(i=0; i < 8 && pktlen; i++, pktlen-- )
+           k->s2k.salt[i] = iobuf_get_noeof(inp);
+    }
+    if( s2kmode == 3 ) {
+       k->s2k.count = iobuf_get(inp); pktlen--;
+    }
+    k->seskeylen = seskeylen;
+    for(i=0; i < seskeylen && pktlen; i++, pktlen-- )
+       k->seskey[i] = iobuf_get_noeof(inp);
+    assert( !pktlen );
+
+    if( list_mode ) {
+       printf(":symkey enc packet: version %d, cipher %d, s2k %d, hash %d\n",
+                           version, cipher_algo, s2kmode, hash_algo);
+       if( s2kmode == 1 || s2kmode == 3 ) {
+           printf("\tsalt ");
+           for(i=0; i < 8; i++ )
+               printf("%02x", k->s2k.salt[i]);
+           if( s2kmode == 3 )
+               printf(", count %lu\n", (ulong)k->s2k.count );
+           printf("\n");
+       }
+    }
+
+  leave:
+    skip_rest(inp, pktlen);
+    return rc;
+}
+
+static int
+parse_pubkeyenc( iobuf_t inp, int pkttype, unsigned long pktlen, PACKET *packet )
+{
+    unsigned int n;
+    int rc = 0;
+    int i, ndata;
     PKT_pubkey_enc *k;
 
     PKT_pubkey_enc *k;
 
-    k = packet->pkt.pubkey_enc = m_alloc(sizeof *packet->pkt.pubkey_enc );
+    k = packet->pkt.pubkey_enc = xcalloc (1,sizeof *packet->pkt.pubkey_enc);
     if( pktlen < 12 ) {
        log_error("packet(%d) too short\n", pkttype);
     if( pktlen < 12 ) {
        log_error("packet(%d) too short\n", pkttype);
+        rc = GPG_ERR_INV_PACKET;
        goto leave;
     }
     k->version = iobuf_get_noeof(inp); pktlen--;
     if( k->version != 2 && k->version != 3 ) {
        log_error("packet(%d) with unknown version %d\n", pkttype, k->version);
        goto leave;
     }
     k->version = iobuf_get_noeof(inp); pktlen--;
     if( k->version != 2 && k->version != 3 ) {
        log_error("packet(%d) with unknown version %d\n", pkttype, k->version);
+        rc = GPG_ERR_INV_PACKET;
        goto leave;
     }
     k->keyid[0] = read_32(inp); pktlen -= 4;
     k->keyid[1] = read_32(inp); pktlen -= 4;
     k->pubkey_algo = iobuf_get_noeof(inp); pktlen--;
        goto leave;
     }
     k->keyid[0] = read_32(inp); pktlen -= 4;
     k->keyid[1] = read_32(inp); pktlen -= 4;
     k->pubkey_algo = iobuf_get_noeof(inp); pktlen--;
+    k->throw_keyid = 0; /* only used as flag for build_packet */
     if( list_mode )
     if( list_mode )
-       printf(":public key encoded packet: version %d, keyid %08lX%08lX\n",
-                           k->version, (ulong)k->keyid[0], (ulong)k->keyid[1]);
-    if( k->pubkey_algo == PUBKEY_ALGO_ELGAMAL ) {
-       n = pktlen;
-       k->d.elg.a = mpi_read(inp, &n, 0); pktlen -=n;
-       n = pktlen;
-       k->d.elg.b = mpi_read(inp, &n, 0 ); pktlen -=n;
-       if( list_mode ) {
-           printf("\telg a: ");
-           mpi_print(stdout, k->d.elg.a, mpi_print_mode );
-           printf("\n\telg b: ");
-           mpi_print(stdout, k->d.elg.b, mpi_print_mode );
-           putchar('\n');
-       }
+       printf(":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 )
+           printf("\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 if( k->pubkey_algo == PUBKEY_ALGO_RSA ) {
-       n = pktlen;
-       k->d.rsa.rsa_integer = mpi_read(inp, &n, 0 ); pktlen -=n;
-       if( list_mode ) {
-           printf("\trsa integer: ");
-           mpi_print(stdout, k->d.rsa.rsa_integer, mpi_print_mode );
-           putchar('\n');
+    else {
+       for( i=0; i < ndata; i++ ) {
+           n = pktlen;
+           k->data[i] = mpi_read(inp, &n, 0); pktlen -=n;
+           if( list_mode ) {
+               printf("\tdata: ");
+               mpi_print(stdout, k->data[i], mpi_print_mode );
+               putchar('\n');
+           }
+            if (!k->data[i])
+                rc = GPG_ERR_INV_PACKET;
        }
     }
        }
     }
-    else if( list_mode )
-       printf("\tunknown algorithm %d\n", k->pubkey_algo );
-
 
   leave:
     skip_rest(inp, pktlen);
 
   leave:
     skip_rest(inp, pktlen);
-    return 0;
+    return rc;
+}
+
+
+static void
+dump_sig_subpkt( int hashed, int type, int critical,
+                const byte *buffer, size_t buflen, size_t length )
+{
+    const char *p=NULL;
+    int i;
+
+    /* The CERT has warning out with explains how to use GNUPG to
+     * detect the ARRs - we print our old message here when it is a faked
+     * ARR and add an additional notice */
+    if ( type == SIGSUBPKT_ARR && !hashed ) {
+        printf("\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--;
+   
+    printf("\t%s%ssubpkt %d len %u (", /*)*/
+             critical ? "critical ":"",
+             hashed ? "hashed ":"", type, (unsigned)length );
+    if( length > buflen ) {
+       printf("too short: buffer is only %u)\n", (unsigned)buflen );
+       return;
+    }
+    switch( type ) {
+      case SIGSUBPKT_SIG_CREATED:
+       if( length >= 4 )
+           printf("sig created %s", strtimestamp( buffer_to_u32(buffer) ) );
+       break;
+      case SIGSUBPKT_SIG_EXPIRE:
+       if( length >= 4 )
+           printf("sig expires after %s",
+                                    strtimevalue( buffer_to_u32(buffer) ) );
+       break;
+      case SIGSUBPKT_EXPORTABLE:
+       if( length )
+           printf("%sexportable", *buffer? "":"not ");
+       break;
+      case SIGSUBPKT_TRUST:
+       if(length!=2)
+         p="[invalid trust subpacket]";
+       else
+         printf("trust signature of depth %d, value %d",buffer[0],buffer[1]);
+       break;
+      case SIGSUBPKT_REGEXP:
+       if(!length)
+         p="[invalid regexp subpacket]";
+       else
+         printf("regular expression: \"%s\"",buffer);
+       break;
+      case SIGSUBPKT_REVOCABLE:
+       if( length )
+           printf("%srevocable", *buffer? "":"not ");
+       break;
+      case SIGSUBPKT_KEY_EXPIRE:
+       if( length >= 4 )
+           printf("key expires after %s",
+                                   strtimevalue( buffer_to_u32(buffer) ) );
+       break;
+      case SIGSUBPKT_PREF_SYM:
+       fputs("pref-sym-algos:", stdout );
+       for( i=0; i < length; i++ )
+           printf(" %d", buffer[i] );
+       break;
+      case SIGSUBPKT_REV_KEY:
+       fputs("revocation key: ", stdout );
+       if( length < 22 )
+           p = "[too short]";
+       else {
+           printf("c=%02x a=%d f=", buffer[0], buffer[1] );
+           for( i=2; i < length; i++ )
+               printf("%02X", buffer[i] );
+       }
+       break;
+      case SIGSUBPKT_ISSUER:
+       if( length >= 8 )
+           printf("issuer key ID %08lX%08lX",
+                     (ulong)buffer_to_u32(buffer),
+                     (ulong)buffer_to_u32(buffer+4) );
+       break;
+      case SIGSUBPKT_NOTATION:
+       {
+           fputs("notation: ", stdout );
+           if( length < 8 )
+               p = "[too short]";
+           else {
+               const byte *s = buffer;
+               size_t n1, n2;
+
+               n1 = (s[4] << 8) | s[5];
+               n2 = (s[6] << 8) | s[7];
+               s += 8;
+               if( 8+n1+n2 != length )
+                   p = "[error]";
+               else {
+                   print_string( stdout, s, n1, ')' );
+                   putc( '=', stdout );
+
+                   if( *buffer & 0x80 )
+                     print_string( stdout, s+n1, n2, ')' );
+                   else
+                     p = "[not human readable]";
+               }
+           }
+       }
+       break;
+      case SIGSUBPKT_PREF_HASH:
+       fputs("pref-hash-algos:", stdout );
+       for( i=0; i < length; i++ )
+           printf(" %d", buffer[i] );
+       break;
+      case SIGSUBPKT_PREF_COMPR:
+       fputs("pref-zip-algos:", stdout );
+       for( i=0; i < length; i++ )
+           printf(" %d", buffer[i] );
+       break;
+      case SIGSUBPKT_KS_FLAGS:
+       fputs("key server preferences:",stdout);
+       for(i=0;i<length;i++)
+         printf(" %02X", buffer[i]);
+       break;
+      case SIGSUBPKT_PREF_KS:
+       fputs("preferred key server: ", stdout );
+       print_string( stdout, buffer, length, ')' );
+       break;
+      case SIGSUBPKT_PRIMARY_UID:
+       p = "primary user ID";
+       break;
+      case SIGSUBPKT_POLICY:
+       fputs("policy: ", stdout );
+       print_string( stdout, buffer, length, ')' );
+       break;
+      case SIGSUBPKT_KEY_FLAGS:
+        fputs ( "key flags:", stdout );
+        for( i=0; i < length; i++ )
+            printf(" %02X", buffer[i] );
+       break;
+      case SIGSUBPKT_SIGNERS_UID:
+       p = "signer's user ID";
+       break;
+      case SIGSUBPKT_REVOC_REASON:
+        if( length ) {
+           printf("revocation reason 0x%02x (", *buffer );
+           print_string( stdout, buffer+1, length-1, ')' );
+           p = ")";
+       }
+       break;
+      case SIGSUBPKT_ARR:
+        fputs("Big Brother's key (ignored): ", stdout );
+       if( length < 22 )
+           p = "[too short]";
+       else {
+           printf("c=%02x a=%d f=", buffer[0], buffer[1] );
+           for( i=2; i < length; i++ )
+               printf("%02X", buffer[i] );
+       }
+        break;
+      case SIGSUBPKT_FEATURES:
+        fputs ( "features:", stdout );
+        for( i=0; i < length; i++ )
+            printf(" %02x", buffer[i] );
+       break;
+      default:
+       if(type>=100 && type<=110)
+         p="experimental / private subpacket";
+       else
+         p = "?";
+       break;
+    }
+
+    printf("%s)\n", p? p: "");
+}
+
+/****************
+ * Returns: >= 0 offset into buffer
+ *         -1 unknown type
+ *         -2 unsupported type
+ *         -3 subpacket too short
+ */
+int
+parse_one_sig_subpkt( const byte *buffer, size_t n, int type )
+{
+    switch( type ) {
+      case SIGSUBPKT_REV_KEY:
+       if(n < 22)
+         break;
+       return 0;
+      case SIGSUBPKT_SIG_CREATED:
+      case SIGSUBPKT_SIG_EXPIRE:
+      case SIGSUBPKT_KEY_EXPIRE:
+       if( n < 4 )
+           break;
+       return 0;
+      case SIGSUBPKT_KEY_FLAGS:
+      case SIGSUBPKT_KS_FLAGS:
+      case SIGSUBPKT_PREF_SYM:
+      case SIGSUBPKT_PREF_HASH:
+      case SIGSUBPKT_PREF_COMPR:
+      case SIGSUBPKT_POLICY:
+      case SIGSUBPKT_PREF_KS:
+      case SIGSUBPKT_FEATURES:
+      case SIGSUBPKT_REGEXP:
+       return 0;
+      case SIGSUBPKT_EXPORTABLE:
+      case SIGSUBPKT_REVOCABLE:
+       if( !n )
+           break;
+       return 0;
+      case SIGSUBPKT_ISSUER: /* issuer key ID */
+       if( n < 8 )
+           break;
+       return 0;
+      case SIGSUBPKT_NOTATION:
+       if( n < 8 ) /* minimum length needed */
+           break;
+       return 0;
+      case SIGSUBPKT_REVOC_REASON:
+       if( !n  )
+           break;
+       return 0;
+      case SIGSUBPKT_PRIMARY_UID:
+          if ( n != 1 )
+              break;
+          return 0;   
+      case SIGSUBPKT_TRUST:
+         if ( n != 2 )
+             break;
+         return 0;
+      default: return -1;
+    }
+    return -3;
+}
+
+
+static int
+can_handle_critical( const byte *buffer, size_t n, int type )
+{
+    switch( type ) {
+      case SIGSUBPKT_NOTATION:
+       if( n >= 8 && (*buffer & 0x80) )
+           return 1; /* human readable is handled */
+       return 0;
+
+      case SIGSUBPKT_SIG_CREATED:
+      case SIGSUBPKT_SIG_EXPIRE:
+      case SIGSUBPKT_KEY_EXPIRE:
+      case SIGSUBPKT_EXPORTABLE:
+      case SIGSUBPKT_REVOCABLE:
+      case SIGSUBPKT_REV_KEY:
+      case SIGSUBPKT_ISSUER:/* issuer key ID */
+      case SIGSUBPKT_PREF_SYM:
+      case SIGSUBPKT_PREF_HASH:
+      case SIGSUBPKT_PREF_COMPR:
+      case SIGSUBPKT_KEY_FLAGS:
+      case SIGSUBPKT_PRIMARY_UID:
+      case SIGSUBPKT_FEATURES:
+      case SIGSUBPKT_TRUST:
+      case SIGSUBPKT_REGEXP:
+       /* Is it enough to show the policy or keyserver? */
+      case SIGSUBPKT_POLICY:
+      case SIGSUBPKT_PREF_KS:
+       return 1;
+
+      default:
+       return 0;
+    }
 }
 
 
 }
 
 
-static const byte *
-parse_subpkt( const byte *buffer, int reqtype )
+const byte *
+enum_sig_subpkt( const subpktarea_t *pktbuf, sigsubpkttype_t reqtype,
+                size_t *ret_n, int *start, int *critical )
 {
 {
-    int buflen = (*buffer << 8) | buffer[1];
+    const byte *buffer;
+    int buflen;
     int type;
     int type;
-    int critical;
+    int critical_dummy;
+    int offset;
     size_t n;
     size_t n;
+    int seq = 0;
+    int reqseq = start? *start: 0;
+
+    if(!critical)
+      critical=&critical_dummy;
 
 
-    buffer += 2;
-    for(;;) {
-       if( !buflen )
-           return NULL; /* end of packets; not found */
+    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;
+    }
+    buffer = pktbuf->data;
+    buflen = pktbuf->len;
+    while( buflen ) {
        n = *buffer++; buflen--;
        n = *buffer++; buflen--;
-       if( n >= 192 ) {
+       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];
+           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;
            if( buflen < 2 )
                goto too_short;
            n = (( n - 192 ) << 8) + *buffer + 192;
+           buffer++;
            buflen--;
        }
        if( buflen < n )
            buflen--;
        }
        if( buflen < n )
@@ -500,81 +1073,124 @@ parse_subpkt( const byte *buffer, int reqtype )
        type = *buffer;
        if( type & 0x80 ) {
            type &= 0x7f;
        type = *buffer;
        if( type & 0x80 ) {
            type &= 0x7f;
-           critical = 1;
+           *critical = 1;
        }
        else
        }
        else
-           critical = 0;
-       if( reqtype < 0 ) { /* list packets */
-           printf("\t%ssubpacket %d of length %u (%s)\n",
-                   reqtype == -1 ? "hashed ":"", type, n,
-                   type == 2 ? "signature creation time"
-                 : type == 3 ? "signature expiration time"
-                 : type == 4 ? "exportable"
-                 : type == 5 ? "trust signature"
-                 : type == 6 ? "regular expression"
-                 : type == 7 ? "revocable"
-                 : type == 9 ? "key expiration time"
-                 : type ==10 ? "additional recipient request"
-                 : type ==11 ? "preferred symmetric algorithms"
-                 : type ==12 ? "revocation key"
-                 : type ==16 ? "issuer key ID"
-                 : type ==20 ? "notation data"
-                 : type ==21 ? "preferred hash algorithms"
-                 : type ==22 ? "preferred compression algorithms"
-                 : type ==23 ? "key server preferences"
-                 : type ==24 ? "preferred key server"
-                             : "?");
+           *critical = 0;
+       if( !(++seq > reqseq) )
+           ;
+       else if( reqtype == SIGSUBPKT_TEST_CRITICAL ) {
+           if( *critical ) {
+               if( n-1 > buflen+1 )
+                   goto too_short;
+               if( !can_handle_critical(buffer+1, n-1, type ) )
+                 {
+                   if(opt.verbose)
+                     log_info(_("subpacket of type %d has "
+                                "critical bit set\n"),type);
+                   if( start )
+                     *start = seq;
+                   return NULL; /* this is an error */
+                 }
+           }
+       }
+       else if( reqtype < 0 ) /* list packets */
+           dump_sig_subpkt( reqtype == SIGSUBPKT_LIST_HASHED,
+                                   type, *critical, buffer, buflen, n );
+       else if( type == reqtype ) { /* found */
+           buffer++;
+           n--;
+           if( n > buflen )
+               goto too_short;
+           if( ret_n )
+               *ret_n = n;
+           offset = parse_one_sig_subpkt(buffer, n, type );
+           switch( offset ) {
+             case -3:
+               log_error("subpacket of type %d too short\n", type);
+               return NULL;
+             case -2:
+               return NULL;
+             case -1:
+               BUG(); /* not yet needed */
+             default:
+               break;
+           }
+           if( start )
+               *start = seq;
+           return buffer+offset;
        }
        }
-       else if( type == reqtype )
-           break; /* found */
        buffer += n; buflen -=n;
     }
        buffer += n; buflen -=n;
     }
-    buffer++;
-    n--;
-    if( n > buflen )
-       goto too_short;
-    switch( type ) {
-      case 2: /* signature creation time */
-       if( n < 4 )
-           break;
-       return buffer;
-      case 16:/* issuer key ID */
-       if( n < 8 )
-           break;
-       return buffer;
-      case 3: /* signature expiration time */
-      case 4: /* exportable */
-      case 5: /* trust signature */
-      case 6: /* regular expression */
-      case 7: /* revocable */
-      case 9: /* key expiration time */
-      case 10:/* additional recipient request */
-      case 11:/* preferred symmetric algorithms */
-      case 12:/* revocation key */
-      case 20:/* notation data */
-      case 21:/* preferred hash algorithms */
-      case 22:/* preferred compression algorithms */
-      case 23:/* key server preferences */
-      case 24:/* preferred key server */
-      default: BUG(); /* not yet needed */
-    }
-    log_error("subpacket of type %d too short\n", type);
-    return NULL;
+    if( reqtype == SIGSUBPKT_TEST_CRITICAL )
+       return buffer; /* as value true to indicate that there is no */
+                      /* critical bit we don't understand */
+    if( start )
+       *start = -1;
+    return NULL; /* end of packets; not found */
 
   too_short:
     log_error("buffer shorter than subpacket\n");
 
   too_short:
     log_error("buffer shorter than subpacket\n");
+    if( start )
+       *start = -1;
     return NULL;
 }
 
 
     return NULL;
 }
 
 
+const byte *
+parse_sig_subpkt (const subpktarea_t *buffer, sigsubpkttype_t reqtype,
+                  size_t *ret_n)
+{
+    return enum_sig_subpkt( buffer, reqtype, ret_n, NULL, NULL );
+}
+
+const byte *
+parse_sig_subpkt2 (PKT_signature *sig, sigsubpkttype_t reqtype,
+                   size_t *ret_n )
+{
+    const byte *p;
+
+    p = parse_sig_subpkt (sig->hashed, reqtype, ret_n );
+    if( !p )
+       p = parse_sig_subpkt (sig->unhashed, reqtype, ret_n );
+    return p;
+}
+
+/* Find all revocation keys. Look in hashed area only. */
+void parse_revkeys(PKT_signature *sig)
+{
+  struct revocation_key *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)))
+    {
+      if(len==sizeof(struct revocation_key) &&
+        (revkey->class&0x80)) /* 0x80 bit must be set */
+       {
+         sig->revkey=xrealloc(sig->revkey,
+                         sizeof(struct revocation_key *)*(sig->numrevkeys+1));
+         sig->revkey[sig->numrevkeys]=revkey;
+         sig->numrevkeys++;
+       }
+    }
+}
+
 static int
 static int
-parse_signature( IOBUF inp, int pkttype, unsigned long pktlen,
+parse_signature( iobuf_t inp, int pkttype, unsigned long pktlen,
                                          PKT_signature *sig )
 {
     int md5_len=0;
     unsigned n;
     int is_v4=0;
     int rc=0;
                                          PKT_signature *sig )
 {
     int md5_len=0;
     unsigned n;
     int is_v4=0;
     int rc=0;
+    int i, ndata;
 
     if( pktlen < 16 ) {
        log_error("packet(%d) too short\n", pkttype);
 
     if( pktlen < 16 ) {
        log_error("packet(%d) too short\n", pkttype);
@@ -585,6 +1201,7 @@ parse_signature( IOBUF inp, int pkttype, unsigned long pktlen,
        is_v4=1;
     else if( sig->version != 2 && sig->version != 3 ) {
        log_error("packet(%d) with unknown version %d\n", pkttype, sig->version);
        is_v4=1;
     else if( sig->version != 2 && sig->version != 3 ) {
        log_error("packet(%d) with unknown version %d\n", pkttype, sig->version);
+        rc = GPG_ERR_INV_PACKET;
        goto leave;
     }
 
        goto leave;
     }
 
@@ -599,19 +1216,22 @@ parse_signature( IOBUF inp, int pkttype, unsigned long pktlen,
     }
     sig->pubkey_algo = iobuf_get_noeof(inp); pktlen--;
     sig->digest_algo = iobuf_get_noeof(inp); pktlen--;
     }
     sig->pubkey_algo = iobuf_get_noeof(inp); pktlen--;
     sig->digest_algo = iobuf_get_noeof(inp); 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( 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");
-           rc = G10ERR_INVALID_PACKET;
+           rc = GPG_ERR_INV_PACKET;
            goto leave;
        }
        if( n ) {
            goto leave;
        }
        if( n ) {
-           sig->hashed_data = m_alloc( n + 2 );
-           sig->hashed_data[0] = n << 8;
-           sig->hashed_data[1] = n;
-           if( iobuf_read(inp, sig->hashed_data+2, n ) != n ) {
-               log_error("premature eof while reading hashed signature data\n");
+           sig->hashed = xmalloc (sizeof (*sig->hashed) + n - 1 );
+            sig->hashed->size = n;
+           sig->hashed->len = n;
+           if( iobuf_read (inp, sig->hashed->data, n ) != n ) {
+               log_error ("premature eof while reading "
+                           "hashed signature data\n");
                rc = -1;
                goto leave;
            }
                rc = -1;
                goto leave;
            }
@@ -620,15 +1240,16 @@ parse_signature( IOBUF inp, int pkttype, unsigned long pktlen,
        n = read_16(inp); pktlen -= 2; /* length of unhashed data */
        if( n > 10000 ) {
            log_error("signature packet: unhashed data too long\n");
        n = read_16(inp); pktlen -= 2; /* length of unhashed data */
        if( n > 10000 ) {
            log_error("signature packet: unhashed data too long\n");
-           rc = G10ERR_INVALID_PACKET;
+           rc = GPG_ERR_INV_PACKET;
            goto leave;
        }
        if( n ) {
            goto leave;
        }
        if( n ) {
-           sig->unhashed_data = m_alloc( n + 2 );
-           sig->unhashed_data[0] = n << 8;
-           sig->unhashed_data[1] = n;
-           if( iobuf_read(inp, sig->unhashed_data+2, n ) != n ) {
-               log_error("premature eof while reading unhashed signature data\n");
+           sig->unhashed = xmalloc (sizeof(*sig->unhashed) + n - 1 );
+            sig->unhashed->size = n;
+           sig->unhashed->len = n;
+           if( iobuf_read(inp, sig->unhashed->data, n ) != n ) {
+               log_error("premature eof while reading "
+                          "unhashed signature data\n");
                rc = -1;
                goto leave;
            }
                rc = -1;
                goto leave;
            }
@@ -638,80 +1259,134 @@ parse_signature( IOBUF inp, int pkttype, unsigned long pktlen,
 
     if( pktlen < 5 ) { /* sanity check */
        log_error("packet(%d) too short\n", pkttype);
 
     if( pktlen < 5 ) { /* sanity check */
        log_error("packet(%d) too short\n", pkttype);
-       rc = G10ERR_INVALID_PACKET;
+       rc = GPG_ERR_INV_PACKET;
        goto leave;
     }
 
     sig->digest_start[0] = iobuf_get_noeof(inp); pktlen--;
     sig->digest_start[1] = iobuf_get_noeof(inp); pktlen--;
 
        goto leave;
     }
 
     sig->digest_start[0] = iobuf_get_noeof(inp); pktlen--;
     sig->digest_start[1] = iobuf_get_noeof(inp); pktlen--;
 
-    if( is_v4 ) { /*extract required informations */
+    if( is_v4 && sig->pubkey_algo ) { /*extract required information */
        const byte *p;
        const byte *p;
-       p = parse_subpkt( sig->hashed_data, 2 );
-       if( !p )
-           log_error("signature packet without timestamp\n");
-       else
-           sig->timestamp = buffer_to_u32(p);
-       p = parse_subpkt( sig->unhashed_data, 16 );
-       if( !p )
-           log_error("signature packet without keyid\n");
-       else {
-           sig->keyid[0] = buffer_to_u32(p);
-           sig->keyid[1] = buffer_to_u32(p+4);
+       size_t len;
+
+       /* set sig->flags.unknown_critical if there is a
+        * critical bit set for packets which we do not understand */
+       if( !parse_sig_subpkt (sig->hashed, SIGSUBPKT_TEST_CRITICAL, NULL)
+          || !parse_sig_subpkt (sig->unhashed, SIGSUBPKT_TEST_CRITICAL,
+                                                                       NULL) )
+       {
+           sig->flags.unknown_critical = 1;
+       }
+
+       p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_SIG_CREATED, NULL );
+       if(p)
+         sig->timestamp = buffer_to_u32(p);
+       else if(!(sig->pubkey_algo>=100 && sig->pubkey_algo<=110))
+         log_error("signature packet without timestamp\n");
+
+       p = parse_sig_subpkt2( sig, SIGSUBPKT_ISSUER, NULL );
+       if( p )
+       {
+          sig->keyid[0] = buffer_to_u32(p);
+          sig->keyid[1] = buffer_to_u32(p+4);
        }
        }
+       else if(!(sig->pubkey_algo>=100 && sig->pubkey_algo<=110))
+         log_error("signature packet without keyid\n");
+
+       p=parse_sig_subpkt(sig->hashed,SIGSUBPKT_SIG_EXPIRE,NULL);
+       if(p)
+         sig->expiredate=sig->timestamp+buffer_to_u32(p);
+       if(sig->expiredate && sig->expiredate<=make_timestamp())
+           sig->flags.expired=1;
+
+       p=parse_sig_subpkt(sig->hashed,SIGSUBPKT_POLICY,NULL);
+       if(p)
+         sig->flags.policy_url=1;
+
+       p=parse_sig_subpkt(sig->hashed,SIGSUBPKT_PREF_KS,NULL);
+       if(p)
+         sig->flags.pref_ks=1;
+
+       p=parse_sig_subpkt(sig->hashed,SIGSUBPKT_NOTATION,NULL);
+       if(p)
+         sig->flags.notation=1;
+
+       p=parse_sig_subpkt(sig->hashed,SIGSUBPKT_REVOCABLE,NULL);
+       if(p && *p==0)
+         sig->flags.revocable=0;
+
+       p=parse_sig_subpkt(sig->hashed,SIGSUBPKT_TRUST,&len);
+       if(p && len==2)
+         {
+           sig->trust_depth=p[0];
+           sig->trust_value=p[1];
+
+           /* Only look for a regexp if there is also a trust
+              subpacket. */
+           sig->trust_regexp=
+             parse_sig_subpkt(sig->hashed,SIGSUBPKT_REGEXP,&len);
+
+           /* If the regular expression is of 0 length, there is no
+              regular expression. */
+           if(len==0)
+             sig->trust_regexp=NULL;
+         }
+
+       /* We accept the exportable subpacket from either the hashed
+          or unhashed areas as older versions of gpg put it in the
+          unhashed area.  In theory, anyway, we should never see this
+          packet off of a local keyring. */
+
+       p=parse_sig_subpkt2(sig,SIGSUBPKT_EXPORTABLE,NULL);
+       if(p && *p==0)
+         sig->flags.exportable=0;
+
+       /* Find all revocation keys. */
+       if(sig->sig_class==0x1F)
+         parse_revkeys(sig);
     }
 
     if( list_mode ) {
     }
 
     if( list_mode ) {
-       printf(":signature packet: keyid %08lX%08lX\n"
+       printf(":signature packet: algo %d, keyid %08lX%08lX\n"
               "\tversion %d, created %lu, md5len %d, sigclass %02x\n"
               "\tdigest algo %d, begin of digest %02x %02x\n",
               "\tversion %d, created %lu, md5len %d, sigclass %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 ) {
                (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_subpkt( sig->hashed_data, -1 );
-           parse_subpkt( sig->unhashed_data, -2 );
-       }
-    }
-    if( sig->pubkey_algo == PUBKEY_ALGO_ELGAMAL ) {
-       n = pktlen;
-       sig->d.elg.a = mpi_read(inp, &n, 0 ); pktlen -=n;
-       n = pktlen;
-       sig->d.elg.b = mpi_read(inp, &n, 0 ); pktlen -=n;
-       if( list_mode ) {
-           printf("\telg a: ");
-           mpi_print(stdout, sig->d.elg.a, mpi_print_mode );
-           printf("\n\telg b: ");
-           mpi_print(stdout, sig->d.elg.b, mpi_print_mode );
-           putchar('\n');
+           parse_sig_subpkt (sig->hashed,   SIGSUBPKT_LIST_HASHED, NULL );
+           parse_sig_subpkt (sig->unhashed, SIGSUBPKT_LIST_UNHASHED, NULL);
        }
     }
        }
     }
-    else if( sig->pubkey_algo == PUBKEY_ALGO_DSA ) {
-       n = pktlen;
-       sig->d.dsa.r = mpi_read(inp, &n, 0 ); pktlen -=n;
-       n = pktlen;
-       sig->d.dsa.s = mpi_read(inp, &n, 0 ); pktlen -=n;
-       if( list_mode ) {
-           printf("\tdsa r: ");
-           mpi_print(stdout, sig->d.elg.a, mpi_print_mode );
-           printf("\n\tdsa s: ");
-           mpi_print(stdout, sig->d.elg.b, mpi_print_mode );
-           putchar('\n');
-       }
+
+    ndata = pubkey_get_nsig(sig->pubkey_algo);
+    if( !ndata ) {
+       if( list_mode )
+           printf("\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
+        * to write it back with build_packet() */
+       sig->data[0] = gcry_mpi_set_opaque(NULL, read_rest(inp, pktlen),
+                                           pktlen*8 );
+       pktlen = 0;
     }
     }
-    else if( sig->pubkey_algo == PUBKEY_ALGO_RSA ) {
-       n = pktlen;
-       sig->d.rsa.rsa_integer = mpi_read(inp, &n, 0 ); pktlen -=n;
-       if( list_mode ) {
-           printf("\trsa integer: ");
-           mpi_print(stdout, sig->d.rsa.rsa_integer, mpi_print_mode );
-           putchar('\n');
+    else {
+       for( i=0; i < ndata; i++ ) {
+           n = pktlen;
+           sig->data[i] = mpi_read(inp, &n, 0 );
+           pktlen -=n;
+           if( list_mode ) {
+               printf("\tdata: ");
+               mpi_print(stdout, sig->data[i], mpi_print_mode );
+               putchar('\n');
+           }
+            if (!sig->data[i])
+                rc = GPG_ERR_INV_PACKET;
        }
     }
        }
     }
-    else if( list_mode )
-       printf("\tunknown algorithm %d\n", sig->pubkey_algo );
-
 
   leave:
     skip_rest(inp, pktlen);
 
   leave:
     skip_rest(inp, pktlen);
@@ -720,18 +1395,21 @@ parse_signature( IOBUF inp, int pkttype, unsigned long pktlen,
 
 
 static int
 
 
 static int
-parse_onepass_sig( IOBUF inp, int pkttype, unsigned long pktlen,
+parse_onepass_sig( iobuf_t inp, int pkttype, unsigned long pktlen,
                                             PKT_onepass_sig *ops )
 {
     int version;
                                             PKT_onepass_sig *ops )
 {
     int version;
+    int rc = 0;
 
     if( pktlen < 13 ) {
        log_error("packet(%d) too short\n", pkttype);
 
     if( pktlen < 13 ) {
        log_error("packet(%d) too short\n", pkttype);
+        rc = GPG_ERR_INV_PACKET;
        goto leave;
     }
     version = iobuf_get_noeof(inp); pktlen--;
     if( version != 3 ) {
        log_error("onepass_sig with unknown version %d\n", version);
        goto leave;
     }
     version = iobuf_get_noeof(inp); pktlen--;
     if( version != 3 ) {
        log_error("onepass_sig with unknown version %d\n", version);
+        rc = GPG_ERR_INV_PACKET;
        goto leave;
     }
     ops->sig_class = iobuf_get_noeof(inp); pktlen--;
        goto leave;
     }
     ops->sig_class = iobuf_get_noeof(inp); pktlen--;
@@ -750,408 +1428,523 @@ parse_onepass_sig( IOBUF inp, int pkttype, unsigned long pktlen,
 
   leave:
     skip_rest(inp, pktlen);
 
   leave:
     skip_rest(inp, pktlen);
-    return 0;
+    return rc;
 }
 
 
 }
 
 
+static gcry_mpi_t
+read_protected_v3_mpi (iobuf_t 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 tham mpi\n");
+      xfree (buf);
+      return NULL;
+    }
+
+  /* convert buffer into an opaque gcry_mpi_t */
+  val = gcry_mpi_set_opaque (NULL, buf, (p-buf)*8); 
+  return val;
+}
 
 
 static int
 
 
 static int
-parse_certificate( IOBUF inp, int pkttype, unsigned long pktlen,
+parse_key( iobuf_t inp, int pkttype, unsigned long pktlen,
                              byte *hdr, int hdrlen, PACKET *pkt )
 {
     int i, version, algorithm;
     unsigned n;
                              byte *hdr, int hdrlen, PACKET *pkt )
 {
     int i, version, algorithm;
     unsigned n;
-    unsigned long timestamp;
-    unsigned short valid_period;
+    unsigned long timestamp, expiredate, max_expiredate;
+    int npkey, nskey;
     int is_v4=0;
     int rc=0;
 
     int is_v4=0;
     int rc=0;
 
-
-    if( pktlen < 12 ) {
-       log_error("packet(%d) too short\n", pkttype);
-       goto leave;
-    }
     version = iobuf_get_noeof(inp); pktlen--;
     version = iobuf_get_noeof(inp); pktlen--;
-    if( version == 4 )
+    if( pkttype == PKT_PUBLIC_SUBKEY && version == '#' ) {
+       /* early versions of G10 use old PGP comments packets;
+        * luckily all those comments are started by a hash */
+       if( list_mode ) {
+           printf(":rfc1991 comment packet: \"" );
+           for( ; pktlen; pktlen-- ) {
+               int c;
+               c = iobuf_get_noeof(inp);
+               if( c >= ' ' && c <= 'z' )
+                   putchar(c);
+               else
+                   printf("\\x%02x", c );
+           }
+           printf("\"\n");
+       }
+       skip_rest(inp, pktlen);
+       return 0;
+    }
+    else if( version == 4 )
        is_v4=1;
     else if( version != 2 && version != 3 ) {
        log_error("packet(%d) with unknown version %d\n", pkttype, version);
        is_v4=1;
     else if( version != 2 && version != 3 ) {
        log_error("packet(%d) with unknown version %d\n", pkttype, version);
+        rc = GPG_ERR_INV_PACKET;
+       goto leave;
+    }
+
+    if( pktlen < 11 ) {
+       log_error("packet(%d) too short\n", pkttype);
+        rc = GPG_ERR_INV_PACKET;
        goto leave;
     }
 
     timestamp = read_32(inp); pktlen -= 4;
        goto leave;
     }
 
     timestamp = read_32(inp); pktlen -= 4;
-    if( is_v4 )
-       valid_period = 0;
+    if( is_v4 ) {
+       expiredate = 0; /* have to get it from the selfsignature */
+       max_expiredate = 0;
+    }
     else {
     else {
-       valid_period = read_16(inp); pktlen -= 2;
+       unsigned short ndays;
+       ndays = read_16(inp); pktlen -= 2;
+       if( ndays )
+           expiredate = timestamp + ndays * 86400L;
+       else
+           expiredate = 0;
+
+       max_expiredate=expiredate;
     }
     algorithm = iobuf_get_noeof(inp); pktlen--;
     if( list_mode )
        printf(":%s key packet:\n"
     }
     algorithm = iobuf_get_noeof(inp); pktlen--;
     if( list_mode )
        printf(":%s key packet:\n"
-              "\tversion %d, created %lu, valid for %hu days\n",
-               pkttype == PKT_PUBLIC_CERT? "public" :
-               pkttype == PKT_SECRET_CERT? "secret" :
-               pkttype == PKT_PUBKEY_SUBCERT? "public sub" :
-               pkttype == PKT_SECKEY_SUBCERT? "secret sub" : "??",
-               version, timestamp, valid_period );
-    if( pkttype == PKT_SECRET_CERT )  {
-       pkt->pkt.secret_cert->timestamp = timestamp;
-       pkt->pkt.secret_cert->valid_days = valid_period;
-       pkt->pkt.secret_cert->hdrbytes = hdrlen;
-       pkt->pkt.secret_cert->version = version;
-       pkt->pkt.secret_cert->pubkey_algo = algorithm;
+              "\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 {
     }
     else {
-       pkt->pkt.public_cert->timestamp = timestamp;
-       pkt->pkt.public_cert->valid_days = valid_period;
-       pkt->pkt.public_cert->hdrbytes    = hdrlen;
-       pkt->pkt.public_cert->version     = version;
-       pkt->pkt.public_cert->pubkey_algo = algorithm;
-    }
-
-    if( algorithm == PUBKEY_ALGO_ELGAMAL ) {
-       MPI elg_p, elg_g, elg_y;
-       n = pktlen; elg_p = mpi_read(inp, &n, 0 ); pktlen -=n;
-       n = pktlen; elg_g = mpi_read(inp, &n, 0 ); pktlen -=n;
-       n = pktlen; elg_y = mpi_read(inp, &n, 0 ); pktlen -=n;
-       if( list_mode ) {
-           printf(  "\telg p: ");
-           mpi_print(stdout, elg_p, mpi_print_mode  );
-           printf("\n\telg g: ");
-           mpi_print(stdout, elg_g, mpi_print_mode  );
-           printf("\n\telg y: ");
-           mpi_print(stdout, elg_y, mpi_print_mode  );
-           putchar('\n');
+       PKT_public_key *pk = pkt->pkt.public_key;
+
+       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 )
+           printf("\tunknown algorithm %d\n", algorithm );
+       unknown_pubkey_warning( algorithm );
+    }
+
+
+    if( pkttype == PKT_SECRET_KEY || pkttype == PKT_SECRET_SUBKEY ) {
+       PKT_secret_key *sk = pkt->pkt.secret_key;
+       byte temp[16];
+        size_t snlen = 0;
+
+       if( !npkey ) {
+           sk->skey[0] = gcry_mpi_set_opaque( NULL, read_rest(inp, pktlen),
+                                               pktlen*8 );
+           pktlen = 0;
+           goto leave;
        }
        }
-       if( pkttype == PKT_PUBLIC_CERT ) {
-           pkt->pkt.public_cert->d.elg.p = elg_p;
-           pkt->pkt.public_cert->d.elg.g = elg_g;
-           pkt->pkt.public_cert->d.elg.y = elg_y;
+
+       for(i=0; i < npkey; i++ ) {
+           n = pktlen; sk->skey[i] = mpi_read(inp, &n, 0 ); pktlen -=n;
+           if( list_mode ) {
+               printf(  "\tskey[%d]: ", i);
+               mpi_print(stdout, sk->skey[i], mpi_print_mode  );
+               putchar('\n');
+           }
+            if (!sk->skey[i])
+                rc = GPG_ERR_INV_PACKET;
        }
        }
-       else {
-           PKT_secret_cert *cert = pkt->pkt.secret_cert;
-           byte temp[8];
-
-           pkt->pkt.secret_cert->d.elg.p = elg_p;
-           pkt->pkt.secret_cert->d.elg.g = elg_g;
-           pkt->pkt.secret_cert->d.elg.y = elg_y;
-           cert->d.elg.protect.algo = iobuf_get_noeof(inp); pktlen--;
-           if( cert->d.elg.protect.algo ) {
-               cert->d.elg.is_protected = 1;
-               cert->d.elg.protect.count = 0;
-               if( cert->d.elg.protect.algo == 255 ) {
-                   if( pktlen < 3 ) {
-                       rc = G10ERR_INVALID_PACKET;
-                       goto leave;
-                   }
-                   cert->d.elg.protect.algo = iobuf_get_noeof(inp); pktlen--;
-                   cert->d.elg.protect.s2k  = iobuf_get_noeof(inp); pktlen--;
-                   cert->d.elg.protect.hash = iobuf_get_noeof(inp); pktlen--;
-                   switch( cert->d.elg.protect.s2k ) {
-                     case 1:
-                     case 3:
-                       for(i=0; i < 8 && pktlen; i++, pktlen-- )
-                           temp[i] = iobuf_get_noeof(inp);
-                       memcpy(cert->d.elg.protect.salt, temp, 8 );
-                       break;
-                   }
-                   switch( cert->d.elg.protect.s2k ) {
-                     case 0: if( list_mode ) printf(  "\tsimple S2K" );
-                       break;
-                     case 1: if( list_mode ) printf(  "\tsalted S2K" );
-                       break;
-                     case 3: if( list_mode ) printf(  "\titer+salt S2K" );
-                       break;
-                     default:
+        if (rc) /* one of the MPIs were bad */
+            goto leave;
+       sk->protect.algo = iobuf_get_noeof(inp); pktlen--;
+        sk->protect.sha1chk = 0;
+       if( sk->protect.algo ) {
+           sk->is_protected = 1;
+           sk->protect.s2k.count = 0;
+           if( sk->protect.algo == 254 || sk->protect.algo == 255 ) {
+               if( pktlen < 3 ) {
+                   rc = GPG_ERR_INV_PACKET;
+                   goto leave;
+               }
+                sk->protect.sha1chk = (sk->protect.algo == 254);
+               sk->protect.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); pktlen--;
+               sk->protect.s2k.hash_algo = iobuf_get_noeof(inp); pktlen--;
+               /* check for the special GNU extension */
+               if( is_v4 && sk->protect.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 )
                            printf(  "\tunknown S2K %d\n",
                        if( list_mode )
                            printf(  "\tunknown S2K %d\n",
-                                               cert->d.elg.protect.s2k );
-                       rc = G10ERR_INVALID_PACKET;
+                                               sk->protect.s2k.mode );
+                       rc = GPG_ERR_INV_PACKET;
                        goto leave;
                    }
                        goto leave;
                    }
-
-                   if( list_mode ) {
-                       printf(", algo: %d, hash: %d",
-                                        cert->d.elg.protect.algo,
-                                        cert->d.elg.protect.hash );
-                       if( cert->d.elg.protect.s2k == 1
-                           || cert->d.elg.protect.s2k == 3 ) {
-                           printf(", salt: ");
-                           for(i=0; i < 8; i++ )
-                               printf("%02x", cert->d.elg.protect.salt[i]);
-                       }
-                       putchar('\n');
-                   }
-
-                   if( cert->d.elg.protect.s2k == 3 ) {
-                       if( !pktlen ) {
-                           rc = G10ERR_INVALID_PACKET;
-                           goto leave;
-                       }
-                       cert->d.elg.protect.count = iobuf_get_noeof(inp);
-                       pktlen--;
-                   }
-
+                   /* 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 in the mode with a base of 1000.
+                    */
+                   sk->protect.s2k.mode = 1000 + temp[3];
                }
                }
-               else {
-                   if( list_mode )
-                       printf(  "\tprotect algo: %d\n",
-                                               cert->d.elg.protect.algo);
-                   /* old version, we don't have a S2K, so we fake one */
-                   cert->d.elg.protect.s2k = 0;
-                   /* We need this kludge to cope with old GNUPG versions */
-                   cert->d.elg.protect.hash =
-                        cert->d.elg.protect.algo == CIPHER_ALGO_BLOWFISH?
-                                     DIGEST_ALGO_RMD160 : DIGEST_ALGO_MD5;
+               switch( sk->protect.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 );
+                   break;
                }
                }
-               if( pktlen < 8 ) {
-                   rc = G10ERR_INVALID_PACKET;
+               switch( sk->protect.s2k.mode ) {
+                 case 0: if( list_mode ) printf(  "\tsimple S2K" );
+                   break;
+                 case 1: if( list_mode ) printf(  "\tsalted S2K" );
+                   break;
+                 case 3: if( list_mode ) printf(  "\titer+salt S2K" );
+                   break;
+                 case 1001: if( list_mode ) printf(  "\tgnu-dummy S2K" );
+                   break;
+                 case 1002: if (list_mode) printf("\tgnu-divert-to-card S2K");
+                   break;
+                 default:
+                   if( list_mode )
+                       printf(  "\tunknown %sS2K %d\n",
+                                sk->protect.s2k.mode < 1000? "":"GNU ",
+                                                  sk->protect.s2k.mode );
+                   rc = GPG_ERR_INV_PACKET;
                    goto leave;
                }
                    goto leave;
                }
-               for(i=0; i < 8 && pktlen; i++, pktlen-- )
-                   temp[i] = iobuf_get_noeof(inp);
+
                if( list_mode ) {
                if( list_mode ) {
-                   printf(  "\tprotect IV: ");
-                   for(i=0; i < 8; i++ )
-                       printf(" %02x", temp[i] );
+                   printf(", 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 ) {
+                       printf(", salt: ");
+                       for(i=0; i < 8; i++ )
+                           printf("%02x", sk->protect.s2k.salt[i]);
+                   }
                    putchar('\n');
                }
                    putchar('\n');
                }
-               memcpy(cert->d.elg.protect.iv, temp, 8 );
-           }
-           else
-               cert->d.elg.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 him. */
-           n = pktlen; cert->d.elg.x = mpi_read(inp, &n, 0 ); pktlen -=n;
-
-           cert->d.elg.csum = read_16(inp); pktlen -= 2;
-           if( list_mode ) {
-               printf("\t[secret value x is not shown]\n"
-                      "\tchecksum: %04hx\n", cert->d.elg.csum);
-           }
-         /*log_mpidump("elg p=", cert->d.elg.p );
-           log_mpidump("elg g=", cert->d.elg.g );
-           log_mpidump("elg y=", cert->d.elg.y );
-           log_mpidump("elg x=", cert->d.elg.x ); */
-       }
-    }
-    else if( algorithm == PUBKEY_ALGO_DSA ) {
-       MPI dsa_p, dsa_q, dsa_g, dsa_y;
-       n = pktlen; dsa_p = mpi_read(inp, &n, 0 ); pktlen -=n;
-       n = pktlen; dsa_q = mpi_read(inp, &n, 0 ); pktlen -=n;
-       n = pktlen; dsa_g = mpi_read(inp, &n, 0 ); pktlen -=n;
-       n = pktlen; dsa_y = mpi_read(inp, &n, 0 ); pktlen -=n;
-       if( list_mode ) {
-           printf(  "\tdsa p: ");
-           mpi_print(stdout, dsa_p, mpi_print_mode  );
-           printf("\n\tdsa q: ");
-           mpi_print(stdout, dsa_q, mpi_print_mode  );
-           printf("\n\tdsa g: ");
-           mpi_print(stdout, dsa_g, mpi_print_mode  );
-           printf("\n\tdsa y: ");
-           mpi_print(stdout, dsa_y, mpi_print_mode  );
-           putchar('\n');
-       }
-       if( pkttype == PKT_PUBLIC_CERT ) {
-           pkt->pkt.public_cert->d.dsa.p = dsa_p;
-           pkt->pkt.public_cert->d.dsa.q = dsa_q;
-           pkt->pkt.public_cert->d.dsa.g = dsa_g;
-           pkt->pkt.public_cert->d.dsa.y = dsa_y;
-       }
-       else {
-           PKT_secret_cert *cert = pkt->pkt.secret_cert;
-           byte temp[8];
-
-           pkt->pkt.secret_cert->d.dsa.p = dsa_p;
-           pkt->pkt.secret_cert->d.dsa.q = dsa_q;
-           pkt->pkt.secret_cert->d.dsa.g = dsa_g;
-           pkt->pkt.secret_cert->d.dsa.y = dsa_y;
-           cert->d.dsa.protect.algo = iobuf_get_noeof(inp); pktlen--;
-           if( cert->d.dsa.protect.algo ) {
-               cert->d.dsa.is_protected = 1;
-               cert->d.dsa.protect.count = 0;
-               if( cert->d.dsa.protect.algo == 255 ) {
-                   if( pktlen < 3 ) {
-                       rc = G10ERR_INVALID_PACKET;
-                       goto leave;
-                   }
-                   cert->d.dsa.protect.algo = iobuf_get_noeof(inp); pktlen--;
-                   cert->d.dsa.protect.s2k  = iobuf_get_noeof(inp); pktlen--;
-                   cert->d.dsa.protect.hash = iobuf_get_noeof(inp); pktlen--;
-                   switch( cert->d.dsa.protect.s2k ) {
-                     case 1:
-                     case 3:
-                       for(i=0; i < 8 && pktlen; i++, pktlen-- )
-                           temp[i] = iobuf_get_noeof(inp);
-                       memcpy(cert->d.dsa.protect.salt, temp, 8 );
-                       break;
-                   }
-                   switch( cert->d.dsa.protect.s2k ) {
-                     case 0: if( list_mode ) printf(  "\tsimple S2K" );
-                       break;
-                     case 1: if( list_mode ) printf(  "\tsalted S2K" );
-                       break;
-                     case 3: if( list_mode ) printf(  "\titer+salt S2K" );
-                       break;
-                     default:
-                       if( list_mode )
-                           printf(  "\tunknown S2K %d\n",
-                                               cert->d.dsa.protect.s2k );
-                       rc = G10ERR_INVALID_PACKET;
-                       goto leave;
-                   }
 
 
-                   if( list_mode ) {
-                       printf(", algo: %d, hash: %d",
-                                        cert->d.dsa.protect.algo,
-                                        cert->d.dsa.protect.hash );
-                       if( cert->d.dsa.protect.s2k == 1
-                           || cert->d.dsa.protect.s2k == 3 ) {
-                           printf(", salt: ");
-                           for(i=0; i < 8; i++ )
-                               printf("%02x", cert->d.dsa.protect.salt[i]);
-                       }
-                       putchar('\n');
-                   }
-
-                   if( cert->d.dsa.protect.s2k == 3 ) {
-                       if( !pktlen ) {
-                           rc = G10ERR_INVALID_PACKET;
-                           goto leave;
-                       }
-                       cert->d.dsa.protect.count = iobuf_get_noeof(inp);
-                       pktlen--;
+               if( sk->protect.s2k.mode == 3 ) {
+                   if( pktlen < 1 ) {
+                       rc = GPG_ERR_INV_PACKET;
+                       goto leave;
                    }
                    }
-
-               }
-               else {
+                   sk->protect.s2k.count = iobuf_get(inp);
+                   pktlen--;
                    if( list_mode )
                    if( list_mode )
-                       printf(  "\tprotect algo: %d\n",
-                                               cert->d.dsa.protect.algo);
-                   /* old version, we don't have a S2K, so we fake one */
-                   cert->d.dsa.protect.s2k = 0;
-                   cert->d.dsa.protect.hash = DIGEST_ALGO_MD5;
-               }
-               if( pktlen < 8 ) {
-                   rc = G10ERR_INVALID_PACKET;
-                   goto leave;
+                       printf("\tprotect count: %lu\n",
+                                           (ulong)sk->protect.s2k.count);
                }
                }
-               for(i=0; i < 8 && pktlen; i++, pktlen-- )
-                   temp[i] = iobuf_get_noeof(inp);
-               if( list_mode ) {
-                   printf(  "\tprotect IV: ");
-                   for(i=0; i < 8; i++ )
-                       printf(" %02x", temp[i] );
-                   putchar('\n');
+               else if( sk->protect.s2k.mode == 1002 ) {
+                    /* Read the serial number. */
+                    if (pktlen < 1) {
+                       rc = GPG_ERR_INV_PACKET;
+                       goto leave;
+                   }
+                   snlen = iobuf_get (inp);
+                   pktlen--;
+                    if (pktlen < snlen || snlen == -1) {
+                       rc = GPG_ERR_INV_PACKET;
+                       goto leave;
+                    }
                }
                }
-               memcpy(cert->d.dsa.protect.iv, temp, 8 );
            }
            }
-           else
-               cert->d.dsa.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 him. */
-           n = pktlen; cert->d.dsa.x = mpi_read(inp, &n, 0 ); pktlen -=n;
-
-           cert->d.dsa.csum = read_16(inp); pktlen -= 2;
+           /* 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. */
+           else { /* old version; no S2K, so we set mode to 0, hash MD5 */
+               sk->protect.s2k.mode = 0;
+               sk->protect.s2k.hash_algo = DIGEST_ALGO_MD5;
+               if( list_mode )
+                   printf(  "\tprotect algo: %d  (hash algo: %d)\n",
+                        sk->protect.algo, sk->protect.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);
+            * won't work.  The only solution I see is to hardwire it here.
+            * NOTE: if you change the ivlen above 16, don't forget to
+            * enlarge temp.
+            */
+           switch( sk->protect.algo ) {
+             case 7: case 8: case 9: /* reserved for AES */
+             case 10: /* Twofish */
+               sk->protect.ivlen = 16;
+               break;
+             default:
+               sk->protect.ivlen = 8;
+           }
+           if( sk->protect.s2k.mode == 1001 )
+               sk->protect.ivlen = 0;
+           else if( sk->protect.s2k.mode == 1002 ) {
+                if (snlen > 16)
+                    log_info ("WARNING: serial number of card truncated\n");
+               sk->protect.ivlen = snlen < 16? snlen : 16;
+
+            }
+           if( pktlen < sk->protect.ivlen ) {
+               rc = GPG_ERR_INV_PACKET;
+               goto leave;
+           }
+           for(i=0; i < sk->protect.ivlen && pktlen; i++, pktlen-- )
+               temp[i] = iobuf_get_noeof(inp);
            if( list_mode ) {
            if( list_mode ) {
-               printf("\t[secret value x is not shown]\n"
-                      "\tchecksum: %04hx\n", cert->d.dsa.csum);
+               printf( sk->protect.s2k.mode == 1002? "\tserial-number: "
+                                                    : "\tprotect IV: ");
+               for(i=0; i < sk->protect.ivlen; i++ )
+                   printf(" %02x", temp[i] );
+               putchar('\n');
            }
            }
-         /*log_mpidump("dsa p=", cert->d.dsa.p );
-           log_mpidump("dsa q=", cert->d.dsa.q );
-           log_mpidump("dsa g=", cert->d.dsa.g );
-           log_mpidump("dsa y=", cert->d.dsa.y );
-           log_mpidump("dsa x=", cert->d.dsa.x ); */
+           memcpy(sk->protect.iv, temp, sk->protect.ivlen );
        }
        }
-    }
-    else if( algorithm == PUBKEY_ALGO_RSA ) {
-       MPI rsa_pub_mod, rsa_pub_exp;
-
-       n = pktlen; rsa_pub_mod = mpi_read(inp, &n, 0); pktlen -=n;
-       n = pktlen; rsa_pub_exp = mpi_read(inp, &n, 0 ); pktlen -=n;
-       if( list_mode ) {
-           printf(  "\tpublic modulus  n:  ");
-           mpi_print(stdout, rsa_pub_mod, mpi_print_mode  );
-           printf("\n\tpublic exponent e: ");
-           mpi_print(stdout, rsa_pub_exp, mpi_print_mode  );
-           putchar('\n');
+       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 ) {
+           /* better set some dummy stuff here */
+           sk->skey[npkey] = gcry_mpi_set_opaque(NULL, xstrdup ("dummydata"),
+                                                  10*8);
+           pktlen = 0;
        }
        }
-       if( pkttype == PKT_PUBLIC_CERT ) {
-           pkt->pkt.public_cert->d.rsa.rsa_n = rsa_pub_mod;
-           pkt->pkt.public_cert->d.rsa.rsa_e = rsa_pub_exp;
+       else if( is_v4 && sk->is_protected ) {
+           /* 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),
+                                                  pktlen*8 );
+           pktlen = 0;
+           if( list_mode ) {
+               printf("\tencrypted stuff follows\n");
+           }
        }
        }
-       else {
-           PKT_secret_cert *cert = pkt->pkt.secret_cert;
-           byte temp[8];
-
-           pkt->pkt.secret_cert->d.rsa.rsa_n = rsa_pub_mod;
-           pkt->pkt.secret_cert->d.rsa.rsa_e = rsa_pub_exp;
-           cert->d.rsa.protect_algo = iobuf_get_noeof(inp); pktlen--;
-           if( list_mode )
-               printf(  "\tprotect algo: %d\n", cert->d.rsa.protect_algo);
-           if( cert->d.rsa.protect_algo ) {
-               cert->d.rsa.is_protected = 1;
-               for(i=0; i < 8 && pktlen; i++, pktlen-- )
-                   temp[i] = iobuf_get_noeof(inp);
-               if( list_mode ) {
-                   printf(  "\tprotect IV: ");
-                   for(i=0; i < 8; i++ )
-                       printf(" %02x", temp[i] );
-                   putchar('\n');
-               }
-               if( cert->d.rsa.protect_algo == CIPHER_ALGO_BLOWFISH )
-                   memcpy(cert->d.rsa.protect.blowfish.iv, temp, 8 );
+       else { /* v3 method: the mpi length is not encrypted */
+           for(i=npkey; i < nskey; i++ ) {
+                if ( sk->is_protected ) {
+                    sk->skey[i] = read_protected_v3_mpi (inp, &pktlen);
+                    if( list_mode ) 
+                        printf(  "\tskey[%d]: [encrypted]\n", i);
+                }
+                else {
+                    n = pktlen;
+                    sk->skey[i] = mpi_read(inp, &n, 0 );
+                    pktlen -=n;
+                    if( list_mode ) {
+                        printf(  "\tskey[%d]: ", i);
+                        mpi_print(stdout, sk->skey[i], mpi_print_mode  );
+                        putchar('\n');
+                    }
+                }
+
+                if (!sk->skey[i])
+                    rc = GPG_ERR_INV_PACKET;
            }
            }
-           else
-               cert->d.rsa.is_protected = 0;
-           /* (See comments at the code for elg keys) */
-           n = pktlen; cert->d.rsa.rsa_d = mpi_read(inp, &n, 0 ); pktlen -=n;
-           n = pktlen; cert->d.rsa.rsa_p = mpi_read(inp, &n, 0 ); pktlen -=n;
-           n = pktlen; cert->d.rsa.rsa_q = mpi_read(inp, &n, 0 ); pktlen -=n;
-           n = pktlen; cert->d.rsa.rsa_u = mpi_read(inp, &n, 0 ); pktlen -=n;
-
-           cert->d.rsa.csum = read_16(inp); pktlen -= 2;
+            if (rc)
+                goto leave;
+
+           sk->csum = read_16(inp); pktlen -= 2;
            if( list_mode ) {
            if( list_mode ) {
-               printf("\t[secret values d,p,q,u are not shown]\n"
-                      "\tchecksum: %04hx\n", cert->d.rsa.csum);
+               printf("\tchecksum: %04hx\n", sk->csum);
            }
            }
-        /* log_mpidump("rsa n=", cert->d.rsa.rsa_n );
-           log_mpidump("rsa e=", cert->d.rsa.rsa_e );
-           log_mpidump("rsa d=", cert->d.rsa.rsa_d );
-           log_mpidump("rsa p=", cert->d.rsa.rsa_p );
-           log_mpidump("rsa q=", cert->d.rsa.rsa_q );
-           log_mpidump("rsa u=", cert->d.rsa.rsa_u ); */
        }
     }
        }
     }
-    else if( list_mode )
-       printf("\tunknown algorithm %d\n", algorithm );
+    else {
+       PKT_public_key *pk = pkt->pkt.public_key;
 
 
+       if( !npkey ) {
+           pk->pkey[0] = gcry_mpi_set_opaque( NULL, read_rest(inp, pktlen),
+                                               pktlen*8 );
+           pktlen = 0;
+           goto leave;
+       }
+
+       for(i=0; i < npkey; i++ ) {
+           n = pktlen; pk->pkey[i] = mpi_read(inp, &n, 0 ); pktlen -=n;
+           if( list_mode ) {
+               printf(  "\tpkey[%d]: ", i);
+               mpi_print(stdout, pk->pkey[i], mpi_print_mode  );
+               putchar('\n');
+           }
+            if (!pk->pkey[i])
+                rc = GPG_ERR_INV_PACKET;
+       }
+        if (rc)
+            goto leave;
+    }
 
   leave:
     skip_rest(inp, pktlen);
     return rc;
 }
 
 
   leave:
     skip_rest(inp, pktlen);
     return rc;
 }
 
+/* Attribute subpackets have the same format as v4 signature
+   subpackets.  This is not part of OpenPGP, but is done in several
+   versions of PGP nevertheless. */
+int
+parse_attribute_subpkts(PKT_user_id *uid)
+{
+  size_t n;
+  int count=0;
+  struct user_attribute *attribs=NULL;
+  const byte *buffer=uid->attrib_data;
+  int buflen=uid->attrib_len;
+  byte type;
+
+  xfree (uid->attribs);
+
+  while(buflen)
+    {
+      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];
+       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;
+       buffer++;
+       buflen--;
+      }
+      if( buflen < n )
+       goto too_short;
+
+      attribs=xrealloc(attribs,(count+1)*sizeof(struct user_attribute));
+      memset(&attribs[count],0,sizeof(struct user_attribute));
+
+      type=*buffer;
+      buffer++;
+      buflen--;
+      n--;
+
+      attribs[count].type=type;
+      attribs[count].data=buffer;
+      attribs[count].len=n;
+      buffer+=n;
+      buflen-=n;
+      count++;
+    }
+
+  uid->attribs=attribs;
+  uid->numattribs=count;
+  return count;
+
+ too_short:
+  log_error("buffer shorter than attribute subpacket\n");
+  uid->attribs=attribs;
+  uid->numattribs=count;
+  return count;
+}
+
+static void setup_user_id(PACKET *packet)
+{
+  packet->pkt.user_id->ref = 1;
+  packet->pkt.user_id->attribs = NULL;
+  packet->pkt.user_id->attrib_data = NULL;
+  packet->pkt.user_id->attrib_len = 0;
+  packet->pkt.user_id->is_primary = 0;
+  packet->pkt.user_id->is_revoked = 0;
+  packet->pkt.user_id->is_expired = 0;
+  packet->pkt.user_id->expiredate = 0;
+  packet->pkt.user_id->created = 0;
+  packet->pkt.user_id->help_key_usage = 0;
+  packet->pkt.user_id->help_key_expire = 0;
+  packet->pkt.user_id->prefs = NULL;
+  packet->pkt.user_id->namehash = NULL;
+}
 
 static int
 
 static int
-parse_user_id( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *packet )
+parse_user_id( iobuf_t inp, int pkttype, unsigned long pktlen, PACKET *packet )
 {
     byte *p;
 
 {
     byte *p;
 
-    packet->pkt.user_id = m_alloc(sizeof *packet->pkt.user_id  + pktlen - 1);
+    packet->pkt.user_id = xmalloc (sizeof *packet->pkt.user_id  + pktlen);
     packet->pkt.user_id->len = pktlen;
     packet->pkt.user_id->len = pktlen;
+
+    setup_user_id(packet);
+
     p = packet->pkt.user_id->name;
     for( ; pktlen; pktlen--, p++ )
        *p = iobuf_get_noeof(inp);
     p = packet->pkt.user_id->name;
     for( ; pktlen; pktlen--, p++ )
        *p = iobuf_get_noeof(inp);
+    *p = 0;
 
     if( list_mode ) {
        int n = packet->pkt.user_id->len;
 
     if( list_mode ) {
        int n = packet->pkt.user_id->len;
-       printf(":user id packet: \"");
+       printf(":user ID packet: \"");
+       /* fixme: Hey why don't we replace this with print_string?? */
        for(p=packet->pkt.user_id->name; n; p++, n-- ) {
            if( *p >= ' ' && *p <= 'z' )
                putchar(*p);
        for(p=packet->pkt.user_id->name; n; p++, n-- ) {
            if( *p >= ' ' && *p <= 'z' )
                putchar(*p);
@@ -1164,44 +1957,75 @@ parse_user_id( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *packet )
 }
 
 
 }
 
 
-static void
-parse_subkey( IOBUF inp, int pkttype, unsigned long pktlen )
+void
+make_attribute_uidname(PKT_user_id *uid, size_t max_namelen)
 {
 {
-    int version;
-
-    version = iobuf_get_noeof(inp); pktlen--;
-    if( pkttype == PKT_PUBKEY_SUBCERT && version == '#' ) {
-       /* early versions of G10 use old comments packets; luckily all those
-        * comments are started by a hash */
-       if( list_mode ) {
-           printf(":old comment packet: \"" );
-           for( ; pktlen; pktlen-- ) {
-               int c;
-               c = iobuf_get_noeof(inp);
-               if( c >= ' ' && c <= 'z' )
-                   putchar(c);
-               else
-                   printf("\\x%02x", c );
-           }
-           printf("\"\n");
+  assert ( max_namelen > 70 );
+  if(uid->numattribs<=0)
+    sprintf(uid->name,"[bad attribute packet of size %lu]",uid->attrib_len);
+  else if(uid->numattribs>1)
+    sprintf(uid->name,"[%d attributes of size %lu]",
+           uid->numattribs,uid->attrib_len);
+  else
+    {
+      /* Only one attribute, so list it as the "user id" */
+
+      if(uid->attribs->type==ATTRIB_IMAGE)
+       {
+         u32 len;
+         byte type;
+
+         if(parse_image_header(uid->attribs,&type,&len))
+           sprintf(uid->name,"[%.20s image of size %lu]",
+                   image_type_to_string(type,1),(ulong)len);
+         else
+           sprintf(uid->name,"[invalid image]");
        }
        }
-       skip_rest(inp, pktlen);
-       return;
+      else
+       sprintf(uid->name,"[unknown attribute of size %lu]",
+               (ulong)uid->attribs->len);
     }
 
     }
 
-    if( list_mode )
-       printf(":public subkey packet: \"" );
-    skip_rest(inp, pktlen);
+  uid->len = strlen(uid->name);
 }
 
 }
 
+static int
+parse_attribute( iobuf_t inp, int pkttype, unsigned long pktlen, PACKET *packet )
+{
+    byte *p;
+
+#define EXTRA_UID_NAME_SPACE 71
+    packet->pkt.user_id = xmalloc (sizeof *packet->pkt.user_id
+                                  + EXTRA_UID_NAME_SPACE);
+
+    setup_user_id(packet);
+
+    packet->pkt.user_id->attrib_data = xmalloc (pktlen);
+    packet->pkt.user_id->attrib_len = pktlen;
+    p = packet->pkt.user_id->attrib_data;
+    for( ; pktlen; pktlen--, p++ )
+       *p = iobuf_get_noeof(inp);
+
+    /* Now parse out the individual attribute subpackets.  This is
+       somewhat pointless since there is only one currently defined
+       attribute type (jpeg), but it is correct by the spec. */
+    parse_attribute_subpkts(packet->pkt.user_id);
+
+    make_attribute_uidname(packet->pkt.user_id, EXTRA_UID_NAME_SPACE);
+
+    if( list_mode ) {
+       printf(":attribute packet: %s\n", packet->pkt.user_id->name );
+    }
+    return 0;
+}
 
 
 static int
 
 
 static int
-parse_comment( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *packet )
+parse_comment( iobuf_t inp, int pkttype, unsigned long pktlen, PACKET *packet )
 {
     byte *p;
 
 {
     byte *p;
 
-    packet->pkt.comment = m_alloc(sizeof *packet->pkt.comment + pktlen - 1);
+    packet->pkt.comment = xmalloc (sizeof *packet->pkt.comment + pktlen - 1);
     packet->pkt.comment->len = pktlen;
     p = packet->pkt.comment->data;
     for( ; pktlen; pktlen--, p++ )
     packet->pkt.comment->len = pktlen;
     p = packet->pkt.comment->data;
     for( ; pktlen; pktlen--, p++ )
@@ -1209,7 +2033,8 @@ parse_comment( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *packet )
 
     if( list_mode ) {
        int n = packet->pkt.comment->len;
 
     if( list_mode ) {
        int n = packet->pkt.comment->len;
-       printf(":comment packet: \"");
+       printf(":%scomment packet: \"", pkttype == PKT_OLD_COMMENT?
+                                        "OpenPGP draft " : "" );
        for(p=packet->pkt.comment->data; n; p++, n-- ) {
            if( *p >= ' ' && *p <= 'z' )
                putchar(*p);
        for(p=packet->pkt.comment->data; n; p++, n-- ) {
            if( *p >= ' ' && *p <= 'z' )
                putchar(*p);
@@ -1223,33 +2048,66 @@ parse_comment( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *packet )
 
 
 static void
 
 
 static void
-parse_trust( IOBUF inp, int pkttype, unsigned long pktlen )
+parse_trust( iobuf_t inp, int pkttype, unsigned long pktlen, PACKET *pkt )
 {
 {
-    int c;
-
-    c = iobuf_get_noeof(inp);
-    if( list_mode )
-       printf(":trust packet: flag=%02x\n", c );
+  int c;
+
+  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)
+        {
+          c = iobuf_get_noeof (inp);
+          pktlen--;
+          /* we require that bit 7 of the sigcache is 0 (easier eof handling)*/
+          if ( !(c & 0x80) )
+            pkt->pkt.ring_trust->sigcache = c;
+        }
+      if( list_mode )
+       printf(":trust packet: flag=%02x sigcache=%02x\n",
+               pkt->pkt.ring_trust->trustval,
+               pkt->pkt.ring_trust->sigcache);
+    }
+  else
+    {
+      if( list_mode )
+       printf(":trust packet: empty\n");
+    }
+  skip_rest (inp, pktlen);
 }
 
 
 static int
 }
 
 
 static int
-parse_plaintext( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *pkt )
+parse_plaintext( iobuf_t inp, int pkttype, unsigned long pktlen,
+                                       PACKET *pkt, int new_ctb )
 {
 {
-    int mode, namelen;
+    int rc = 0;
+    int mode, namelen, partial=0;
     PKT_plaintext *pt;
     byte *p;
     int c, i;
 
     if( pktlen && pktlen < 6 ) {
        log_error("packet(%d) too short (%lu)\n", pkttype, (ulong)pktlen);
     PKT_plaintext *pt;
     byte *p;
     int c, i;
 
     if( pktlen && pktlen < 6 ) {
        log_error("packet(%d) too short (%lu)\n", pkttype, (ulong)pktlen);
+        rc = GPG_ERR_INV_PACKET;
        goto leave;
     }
        goto leave;
     }
+    /* A packet length of zero indicates partial body length.  A zero
+       data length isn't a zero length packet due to the header (mode,
+       name, etc), so this is accurate. */
+    if(pktlen==0)
+      partial=1;
     mode = iobuf_get_noeof(inp); if( pktlen ) pktlen--;
     namelen = iobuf_get_noeof(inp); if( pktlen ) pktlen--;
     mode = iobuf_get_noeof(inp); if( pktlen ) pktlen--;
     namelen = iobuf_get_noeof(inp); if( pktlen ) pktlen--;
-    pt = pkt->pkt.plaintext = m_alloc(sizeof *pkt->pkt.plaintext + namelen -1);
+    pt = pkt->pkt.plaintext = xmalloc (sizeof *pkt->pkt.plaintext + namelen -1);
+    pt->new_ctb = new_ctb;
     pt->mode = mode;
     pt->namelen = namelen;
     pt->mode = mode;
     pt->namelen = namelen;
+    pt->is_partial = partial;
     if( pktlen ) {
        for( i=0; pktlen > 4 && i < namelen; pktlen--, i++ )
            pt->name[i] = iobuf_get_noeof(inp);
     if( pktlen ) {
        for( i=0; pktlen > 4 && i < namelen; pktlen--, i++ )
            pt->name[i] = iobuf_get_noeof(inp);
@@ -1281,12 +2139,13 @@ parse_plaintext( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *pkt )
     }
 
   leave:
     }
 
   leave:
-    return 0;
+    return rc;
 }
 
 
 static int
 }
 
 
 static int
-parse_compressed( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *pkt )
+parse_compressed( iobuf_t inp, int pkttype, unsigned long pktlen,
+                 PACKET *pkt, int new_ctb )
 {
     PKT_compressed *zd;
 
 {
     PKT_compressed *zd;
 
@@ -1294,9 +2153,10 @@ parse_compressed( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *pkt )
      * (this should be the last object in a file or
      * the compress algorithm should know the length)
      */
      * (this should be the last object in a file or
      * the compress algorithm should know the length)
      */
-    zd = pkt->pkt.compressed = m_alloc(sizeof *pkt->pkt.compressed );
-    zd->len = 0; /* not yet used */
+    zd = pkt->pkt.compressed = xmalloc (sizeof *pkt->pkt.compressed );
     zd->algorithm = iobuf_get_noeof(inp);
     zd->algorithm = iobuf_get_noeof(inp);
+    zd->len = 0; /* not used */ 
+    zd->new_ctb = new_ctb;
     zd->buf = inp;
     if( list_mode )
        printf(":compressed packet: algo=%d\n", zd->algorithm);
     zd->buf = inp;
     if( list_mode )
        printf(":compressed packet: algo=%d\n", zd->algorithm);
@@ -1305,30 +2165,168 @@ parse_compressed( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *pkt )
 
 
 static int
 
 
 static int
-parse_encrypted( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *pkt )
+parse_encrypted( iobuf_t inp, int pkttype, unsigned long pktlen,
+                                      PACKET *pkt, int new_ctb )
 {
 {
+    int rc = 0;
     PKT_encrypted *ed;
     PKT_encrypted *ed;
+    unsigned long orig_pktlen = pktlen;
 
 
-    ed = pkt->pkt.encrypted =  m_alloc(sizeof *pkt->pkt.encrypted );
+    ed = pkt->pkt.encrypted =  xmalloc (sizeof *pkt->pkt.encrypted );
     ed->len = pktlen;
     ed->len = pktlen;
+    /* we don't know the extralen which is (cipher_blocksize+2)
+       because the algorithm ist not specified in this packet.
+       However, it is only important to know this for some sanity
+       checks on the packet length - it doesn't matter that we can't
+       do it */
+    ed->extralen = 0;
     ed->buf = NULL;
     ed->buf = NULL;
-    if( pktlen && pktlen < 10 ) {
+    ed->new_ctb = new_ctb;
+    ed->mdc_method = 0;
+    if( pkttype == PKT_ENCRYPTED_MDC ) {
+       /* fixme: add some pktlen sanity checks */
+       int version;
+
+       version = iobuf_get_noeof(inp); 
+        if (orig_pktlen)
+            pktlen--;
+       if( version != 1 ) {
+           log_error("encrypted_mdc packet with unknown version %d\n",
+                                                               version);
+            /*skip_rest(inp, pktlen); should we really do this? */
+            rc = GPG_ERR_INV_PACKET;
+           goto leave;
+       }
+       ed->mdc_method = DIGEST_ALGO_SHA1;
+    }
+    if( orig_pktlen && pktlen < 10 ) { /* actually this is blocksize+2 */
        log_error("packet(%d) too short\n", pkttype);
        log_error("packet(%d) too short\n", pkttype);
+        rc = GPG_ERR_INV_PACKET;
        skip_rest(inp, pktlen);
        goto leave;
     }
        skip_rest(inp, pktlen);
        goto leave;
     }
-    if( list_mode )
-       if( pktlen )
-           printf(":encrypted data packet:\n\tlength: %lu\n", pktlen-10);
+    if( list_mode ) {
+       if( orig_pktlen )
+           printf(":encrypted data packet:\n\tlength: %lu\n", orig_pktlen);
        else
            printf(":encrypted data packet:\n\tlength: unknown\n");
        else
            printf(":encrypted data packet:\n\tlength: unknown\n");
+       if( ed->mdc_method )
+           printf("\tmdc_method: %d\n", ed->mdc_method );
+    }
 
     ed->buf = inp;
     pktlen = 0;
 
   leave:
 
     ed->buf = inp;
     pktlen = 0;
 
   leave:
-    return 0;
+    return rc;
+}
+
+
+static int
+parse_mdc( iobuf_t inp, int pkttype, unsigned long pktlen,
+                                  PACKET *pkt, int new_ctb )
+{
+    int rc = 0;
+    PKT_mdc *mdc;
+    byte *p;
+
+    mdc = pkt->pkt.mdc=  xmalloc (sizeof *pkt->pkt.mdc );
+    if( list_mode )
+       printf(":mdc packet: length=%lu\n", pktlen);
+    if( !new_ctb || pktlen != 20 ) {
+       log_error("mdc_packet with invalid encoding\n");
+        rc = GPG_ERR_INV_PACKET;
+       goto leave;
+    }
+    p = mdc->hash;
+    for( ; pktlen; pktlen--, p++ )
+       *p = iobuf_get_noeof(inp);
+
+  leave:
+    return rc;
 }
 
 
 }
 
 
+/*
+ * This packet is internally generated by PGG (by 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 first check that tehre is a unique tag in it.
+ * The format of such a control packet is:
+ *   n byte  session marker
+ *   1 byte  control type CTRLPKT_xxxxx
+ *   m byte  control data
+ */
 
 
+static int
+parse_gpg_control( iobuf_t inp,
+                   int pkttype, unsigned long pktlen, PACKET *packet )
+{
+    byte *p;
+    const byte *sesmark;
+    size_t sesmarklen;
+    int i;
+
+    if ( list_mode )
+        printf(":packet 63: length %lu ",  pktlen);
+
+    sesmark = get_session_marker ( &sesmarklen );
+    if ( pktlen < sesmarklen+1 ) /* 1 is for the control bytes */
+        goto skipit;
+    for( i=0; i < sesmarklen; i++, pktlen-- ) {
+       if ( sesmark[i] != iobuf_get_noeof(inp) )
+            goto skipit;
+    }
+    if ( list_mode )
+        puts ("- gpg control packet");
+
+    packet->pkt.gpg_control = xmalloc (sizeof *packet->pkt.gpg_control
+                                      + pktlen - 1);
+    packet->pkt.gpg_control->control = iobuf_get_noeof(inp); pktlen--;
+    packet->pkt.gpg_control->datalen = pktlen;
+    p = packet->pkt.gpg_control->data;
+    for( ; pktlen; pktlen--, p++ )
+       *p = iobuf_get_noeof(inp);
+
+    return 0;
+
+ skipit:
+    if ( list_mode ) {
+        int c;
+
+        i=0;
+        printf("- private (rest length %lu)\n",  pktlen);
+        if( iobuf_in_block_mode(inp) ) {
+            while( (c=iobuf_get(inp)) != -1 )
+                dump_hex_line(c, &i);
+        }
+        else {
+            for( ; pktlen; pktlen-- )
+                dump_hex_line(iobuf_get(inp), &i);
+        }
+        putchar('\n');
+    }
+    skip_rest(inp,pktlen);
+    return GPG_ERR_INV_PACKET;
+}
+
+/* create a gpg control packet to be used internally as a placeholder */
+PACKET *
+create_gpg_control( ctrlpkttype_t type, const byte *data, size_t datalen )
+{
+    PACKET *packet;
+    byte *p;
+
+    packet = xmalloc ( sizeof *packet );
+    init_packet(packet);
+    packet->pkttype = PKT_GPG_CONTROL;
+    packet->pkt.gpg_control = xmalloc (sizeof *packet->pkt.gpg_control
+                                      + datalen - 1);
+    packet->pkt.gpg_control->control = type;
+    packet->pkt.gpg_control->datalen = datalen;
+    p = packet->pkt.gpg_control->data;
+    for( ; datalen; datalen--, p++ )
+       *p = *data++;
+
+    return packet;
+}