last local commit
[gnupg.git] / g10 / armor.c
index 1fc8ae0..aa56090 100644 (file)
@@ -1,14 +1,14 @@
 /* armor.c - Armor filter
- *     Copyright (c) 1997 by Werner Koch (dd9jn)
+ *     Copyright (C) 1998 Free Software Foundation, Inc.
  *
- * This file is part of G10.
+ * This file is part of GNUPG.
  *
- * G10 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.
  *
- * G10 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.
@@ -34,6 +34,7 @@
 #include "options.h"
 #include "main.h"
 #include "status.h"
+#include "i18n.h"
 
 
 #define CRCINIT 0xB704CE
@@ -51,7 +52,7 @@ static int is_initialized;
 
 
 typedef enum {
-    fhdrHASArmor,
+    fhdrHASArmor = 0,
     fhdrNOArmor,
     fhdrINIT,
     fhdrINITCont,
@@ -62,6 +63,7 @@ typedef enum {
     fhdrSKIPHeader,
     fhdrCLEARSIG,
     fhdrREADClearsig,
+    fhdrNullClearsig,
     fhdrEMPTYClearsig,
     fhdrCHECKClearsig,
     fhdrCHECKClearsig2,
@@ -80,14 +82,16 @@ typedef enum {
 
 
 /* if we encounter this armor string with this index, go
- * into a mode, which fakes packets and wait for the next armor */
+ * into a mode which fakes packets and wait for the next armor */
 #define BEGIN_SIGNED_MSG_IDX 3
 static char *head_strings[] = {
     "BEGIN PGP MESSAGE",
     "BEGIN PGP PUBLIC KEY BLOCK",
     "BEGIN PGP SIGNATURE",
     "BEGIN PGP SIGNED MESSAGE",
-    "BEGIN PGP ARMORED FILE",
+    "BEGIN PGP ARMORED FILE",       /* gnupg extension */
+    "BEGIN PGP PRIVATE KEY BLOCK",
+    "BEGIN PGP SECRET KEY BLOCK",   /* only used by pgp2 */
     NULL
 };
 static char *tail_strings[] = {
@@ -96,12 +100,17 @@ static char *tail_strings[] = {
     "END PGP SIGNATURE",
     "END dummy",
     "END PGP ARMORED FILE",
+    "END PGP PRIVATE KEY BLOCK",
+    "END PGP SECRET KEY BLOCK",
     NULL
 };
 
 
-static fhdr_state_t find_header( fhdr_state_t state, byte *buf,
-                    size_t *r_buflen, IOBUF a, size_t n, unsigned *r_empty);
+static fhdr_state_t find_header( fhdr_state_t state,
+                                byte *buf, size_t *r_buflen,
+                                IOBUF a, size_t n,
+                                unsigned *r_empty, int *r_hashes,
+                                int only_keyblocks );
 
 
 static void
@@ -136,7 +145,7 @@ initialize(void)
 }
 
 /****************
- * Check wether this is a armored file or not
+ * Check whether this is an armored file or not
  * See also parse-packet.c for details on this code
  * Returns: True if it seems to be armored
  */
@@ -150,11 +159,14 @@ is_armored( byte *buf )
        return 1; /* invalid packet: assume it is armored */
     pkttype =  ctb & 0x40 ? (ctb & 0x3f) : ((ctb>>2)&0xf);
     switch( pkttype ) {
-      case PKT_PUBLIC_CERT:
-      case PKT_SECRET_CERT:
+      case PKT_MARKER:
+      case PKT_SYMKEY_ENC:
+      case PKT_PUBLIC_KEY:
+      case PKT_SECRET_KEY:
       case PKT_PUBKEY_ENC:
       case PKT_SIGNATURE:
       case PKT_COMMENT:
+      case PKT_OLD_COMMENT:
       case PKT_PLAINTEXT:
       case PKT_COMPRESSED:
       case PKT_ENCRYPTED:
@@ -166,7 +178,7 @@ is_armored( byte *buf )
 
 
 /****************
- * Try to check wether the iobuf is armored
+ * Try to check whether the iobuf is armored
  * Returns true if this may be the case; the caller should use the
  *        filter to do further processing.
  */
@@ -178,7 +190,7 @@ use_armor_filter( IOBUF a )
 
     n = iobuf_peek(a, buf, 1 );
     if( n == -1 )
-       return 0; /* EOF, doesn't matter wether armored or not */
+       return 0; /* EOF, doesn't matter whether armored or not */
     if( !n )
        return 1; /* can't check it: try armored */
     return is_armored(buf);
@@ -196,11 +208,11 @@ invalid_armor(void)
 
 
 /****************
- * check wether the armor header is valid on a signed message.
+ * check whether the armor header is valid on a signed message.
  * this is for security reasons: the header lines are not included in the
  * hash and by using some creative formatting rules, Mallory could fake
  * any text at the beginning of a document; assuming it is read with
- * a simple viewer. We do only allow the Hash Header.
+ * a simple viewer. We only allow the Hash Header.
  */
 static int
 parse_hash_header( const char *line )
@@ -226,7 +238,7 @@ parse_hash_header( const char *line )
            found |= 2;
        else if( !strncmp( s, "MD5", s2-s ) )
            found |= 4;
-       else if( !strncmp( s, "MD2", s2-s ) )
+       else if( !strncmp( s, "TIGER", s2-s ) )
            found |= 8;
        else
            return 0;
@@ -249,7 +261,8 @@ parse_hash_header( const char *line )
  */
 static fhdr_state_t
 find_header( fhdr_state_t state, byte *buf, size_t *r_buflen,
-                                       IOBUF a, size_t n, unsigned *r_empty)
+            IOBUF a, size_t n, unsigned *r_empty, int *r_hashes,
+            int only_keyblocks )
 {
     int c=0, i;
     const char *s;
@@ -262,22 +275,25 @@ find_header( fhdr_state_t state, byte *buf, size_t *r_buflen,
 
     buflen = *r_buflen;
     assert(buflen >= 100 );
-    buflen -= 3; /* reserved room for CR,LF and one extra */
+    buflen -= 4; /* reserved room for CR,LF, and two extra */
 
     do {
        switch( state ) {
          case fhdrHASArmor:
-           /* read at least the first byte to check wether it is armored
+           /* read at least the first byte to check whether it is armored
             * or not */
            c = 0;
-           for(n=0; n < 28 && (c=iobuf_get2(a)) != -1 && c != '\n'; )
+           for(n=0; n < 28 && (c=iobuf_get(a)) != -1 && c != '\n'; )
                buf[n++] = c;
            if( !n && c == '\n' )
                state = fhdrCHECKBegin;
            else if( !n  || c == -1 )
                state = fhdrNOArmor; /* too short */
-           else if( !is_armored( buf ) )
+           else if( !is_armored( buf ) ) {
                state = fhdrNOArmor;
+               if( c == '\n' )
+                   buf[n++] = c;
+           }
            else if( c == '\n' )
                state = fhdrCHECKBegin;
            else
@@ -288,7 +304,7 @@ find_header( fhdr_state_t state, byte *buf, size_t *r_buflen,
            n = 0;
          case fhdrINITCont: /* read more stuff into buffer */
            c = 0;
-           for(; n < buflen && (c=iobuf_get2(a)) != -1 && c != '\n'; )
+           for(; n < buflen && (c=iobuf_get(a)) != -1 && c != '\n'; )
                buf[n++] = c;
            state = c == '\n' ? fhdrCHECKBegin :
                     c == -1  ? fhdrEOF : fhdrINITSkip;
@@ -298,38 +314,46 @@ find_header( fhdr_state_t state, byte *buf, size_t *r_buflen,
            if( c == '\n' )
                n = 0;
            else {
-               while( (c=iobuf_get2(a)) != -1 && c != '\n' )
+               while( (c=iobuf_get(a)) != -1 && c != '\n' )
                    ;
            }
            state =  c == -1? fhdrEOF : fhdrINIT;
            break;
 
          case fhdrSKIPHeader:
-           while( (c=iobuf_get2(a)) != -1 && c != '\n' )
+           while( (c=iobuf_get(a)) != -1 && c != '\n' )
                ;
            state =  c == -1? fhdrEOF : fhdrWAITHeader;
            break;
 
          case fhdrWAITHeader: /* wait for Header lines */
            c = 0;
-           for(n=0; n < buflen && (c=iobuf_get2(a)) != -1 && c != '\n'; )
+           for(n=0; n < buflen && (c=iobuf_get(a)) != -1 && c != '\n'; )
                buf[n++] = c;
            buf[n] = 0;
            if( n < buflen || c == '\n' ) {
                if( n && buf[0] != '\r') { /* maybe a header */
                    if( strchr( buf, ':') ) { /* yes */
+                       int hashes=0;
+                       if( buf[n-1] == '\r' )
+                           buf[--n] = 0;
                        if( opt.verbose ) {
-                           log_info("armor header: ");
-                           print_string( stderr, buf, n );
+                           log_info(_("armor header: "));
+                           print_string( stderr, buf, n, 0 );
                            putc('\n', stderr);
                        }
-                       if( clearsig && !parse_hash_header( buf ) ) {
-                           log_error("invalid clearsig header\n");
+                       if( clearsig && !(hashes=parse_hash_header( buf )) ) {
+                           log_error(_("invalid clearsig header\n"));
                            state = fhdrERROR;
                        }
-                       else
+                       else {
                            state = fhdrWAITHeader;
+                           if( r_hashes )
+                               *r_hashes |= hashes;
+                       }
                    }
+                   else if( clearsig && n > 15 && !memcmp(buf, "-----", 5 ) )
+                       state = fhdrNullClearsig;
                    else
                        state = fhdrCHECKDashEscaped3;
                }
@@ -345,8 +369,8 @@ find_header( fhdr_state_t state, byte *buf, size_t *r_buflen,
                    }
                }
                else {
-                   log_error("invalid armor header: ");
-                   print_string( stderr, buf, n );
+                   log_error(_("invalid armor header: "));
+                   print_string( stderr, buf, n, 0 );
                    putc('\n', stderr);
                    state = fhdrERROR;
                }
@@ -354,8 +378,8 @@ find_header( fhdr_state_t state, byte *buf, size_t *r_buflen,
            else if( c != -1 ) {
                if( strchr( buf, ':') ) { /* buffer to short, but this is okay*/
                    if( opt.verbose ) {
-                       log_info("armor header: ");
-                       print_string( stderr, buf, n );
+                       log_info(_("armor header: "));
+                       print_string( stderr, buf, n, 0 );
                        fputs("[...]\n", stderr);  /* indicate it is truncated */
                    }
                    state = fhdrSKIPHeader;  /* skip rest of line */
@@ -367,25 +391,29 @@ find_header( fhdr_state_t state, byte *buf, size_t *r_buflen,
                state = fhdrEOF;
            break;
 
-         case fhdrWAITClearsig: /* skip all empty lines (for clearsig) */
+         case fhdrWAITClearsig: /* skip the empty line (for clearsig) */
            c = 0;
-           for(n=0; n < buflen && (c=iobuf_get2(a)) != -1 && c != '\n'; )
+           for(n=0; n < buflen && (c=iobuf_get(a)) != -1 && c != '\n'; )
                buf[n++] = c;
            if( n < buflen || c == '\n' ) {
                buf[n] = 0;
-               if( !n || (buf[0]=='\r' && !buf[1]) ) /* empty line */
-                   ;
+               if( n > 15 && !memcmp(buf, "-----", 5 ) )
+                   state = fhdrNullClearsig;
                else
                    state = fhdrCHECKDashEscaped3;
            }
            else {
-               /* fixme: we should check wether this line continues
-                *   it is poosible that we have only read ws until here
+               /* fixme: we should check whether this line continues
+                *   it is possible that we have only read ws until here
                 *   and more stuff is to come */
                state = fhdrEOF;
            }
            break;
 
+         case fhdrNullClearsig: /* zero length cleartext */
+           state = fhdrENDClearsig;
+           break;
+
          case fhdrENDClearsig:
          case fhdrCHECKBegin:
            state = state == fhdrCHECKBegin ? fhdrINITSkip : fhdrERRORShow;
@@ -409,23 +437,26 @@ find_header( fhdr_state_t state, byte *buf, size_t *r_buflen,
                    break;
            if( !s )
                break; /* unknown begin line */
+           if( only_keyblocks && i != 1 && i != 5 && i != 6 )
+               break; /* not a keyblock armor */
+
            /* found the begin line */
            hdr_line = i;
            state = fhdrWAITHeader;
            if( hdr_line == BEGIN_SIGNED_MSG_IDX )
                clearsig = 1;
            if( opt.verbose > 1 )
-               log_info("armor: %s\n", head_strings[hdr_line]);
+               log_info(_("armor: %s\n"), head_strings[hdr_line]);
            break;
 
          case fhdrCLEARSIG:
          case fhdrEMPTYClearsig:
          case fhdrREADClearsig:
            /* we are at the start of a line: read a clearsig into the buffer
-            * we have to look for a the header line or dashed escaped text*/
+            * we have to look for a header line or dashed escaped text*/
            n = 0;
            c = 0;
-           while( n < buflen && (c=iobuf_get2(a)) != -1 && c != '\n' )
+           while( n < buflen && (c=iobuf_get(a)) != -1 && c != '\n' )
                buf[n++] = c;
            buf[n] = 0;
            if( c == -1 )
@@ -459,8 +490,8 @@ find_header( fhdr_state_t state, byte *buf, size_t *r_buflen,
                                 fhdrREADClearsig : fhdrTESTSpaces;
            }
            else {
-               log_error("invalid dash escaped line: ");
-               print_string( stderr, buf, n );
+               log_error(_("invalid dash escaped line: "));
+               print_string( stderr, buf, n, 0 );
                putc('\n', stderr);
                state = fhdrERROR;
            }
@@ -493,7 +524,7 @@ find_header( fhdr_state_t state, byte *buf, size_t *r_buflen,
             * for dashed escaped text of headers */
            c = 0;
            n = 0;
-           while( n < buflen && (c=iobuf_get2(a)) != -1 && c != '\n' )
+           while( n < buflen && (c=iobuf_get(a)) != -1 && c != '\n' )
                buf[n++] = c;
            buf[n] = 0;
            if( c == -1 )
@@ -505,13 +536,13 @@ find_header( fhdr_state_t state, byte *buf, size_t *r_buflen,
            break;
 
          case fhdrTESTSpaces: {
-           /* but must check wether the rest of the line
-            * does only contain white spaces; this is problematic
-            * since we may have to restore the stuffs. simply
+           /* but must check whether the rest of the line
+            * only contains white spaces; this is problematic
+            * since we may have to restore the stuff simply
             * counting spaces is not enough, because it may be a
-            * mix of different white space chacters */
+            * mix of different white space characters */
            IOBUF b = iobuf_temp();
-           while( (c=iobuf_get2(a)) != -1 && c != '\n' ) {
+           while( (c=iobuf_get(a)) != -1 && c != '\n' ) {
                iobuf_put(b,c);
                if( c != ' ' && c != '\t' && c != '\r' )
                    break;
@@ -528,8 +559,8 @@ find_header( fhdr_state_t state, byte *buf, size_t *r_buflen,
          } break;
 
          case fhdrERRORShow:
-           log_error("invalid clear text header: ");
-           print_string( stderr, buf, n );
+           log_error(_("invalid clear text header: "));
+           print_string( stderr, buf, n, 0 );
            putc('\n', stderr);
            state = fhdrERROR;
            break;
@@ -579,14 +610,13 @@ find_header( fhdr_state_t state, byte *buf, size_t *r_buflen,
        }
     }
 
-
     *r_buflen = n;
     *r_empty = empty;
     return state;
 }
 
 
-/* figure out wether the data is armored or not */
+/* figure out whether the data is armored or not */
 static int
 check_input( armor_filter_context_t *afx, IOBUF a )
 {
@@ -599,7 +629,9 @@ check_input( armor_filter_context_t *afx, IOBUF a )
        state = fhdrHASArmor;
 
     n = DIM(afx->helpbuf);
-    state = find_header( state, afx->helpbuf, &n, a, afx->helplen, &emplines);
+    state = find_header( state, afx->helpbuf, &n, a,
+                               afx->helplen, &emplines, &afx->hashes,
+                               afx->only_keyblocks );
     switch( state ) {
       case fhdrNOArmor:
        afx->inp_checked = 1;
@@ -615,6 +647,7 @@ check_input( armor_filter_context_t *afx, IOBUF a )
        rc = -1;
        break;
 
+      case fhdrNullClearsig:
       case fhdrCLEARSIG: /* start fake package mode (for clear signatures) */
        afx->helplen = n;
        afx->helpidx = 0;
@@ -650,8 +683,6 @@ fake_packet( armor_filter_context_t *afx, IOBUF a,
     fhdr_state_t state = afx->parse_state;
     unsigned emplines = afx->empty;
 
-    size = 100; /* FIXME: only used for testing (remove it)  */
-
     len = 2;   /* reserve 2 bytes for the length header */
     size -= 3; /* and 1 for empline handling and 2 for the term header */
     while( !rc && len < size ) {
@@ -669,7 +700,8 @@ fake_packet( armor_filter_context_t *afx, IOBUF a,
            rc = -1;
            continue;
        }
-       if( afx->helpidx < afx->helplen ) { /* flush the last buffer */
+       if( state != fhdrNullClearsig
+           && afx->helpidx < afx->helplen ) { /* flush the last buffer */
            n = afx->helplen;
            for(nn=afx->helpidx; len < size && nn < n ; nn++ )
                buf[len++] = afx->helpbuf[nn];
@@ -683,7 +715,10 @@ fake_packet( armor_filter_context_t *afx, IOBUF a,
        /* read a new one */
        n = DIM(afx->helpbuf);
        afx->helpidx = 0;
-       state = find_header( state, afx->helpbuf, &n, a, 0, &emplines );
+       state = find_header( state, afx->helpbuf, &n, a,
+                             state == fhdrNullClearsig? afx->helplen:0,
+                                               &emplines, &afx->hashes,
+                                               afx->only_keyblocks );
        switch( state) {
          case fhdrERROR:
            invalid_armor();
@@ -702,8 +737,6 @@ fake_packet( armor_filter_context_t *afx, IOBUF a,
            break;
 
          case fhdrENDClearsig:
-           assert( emplines );
-           emplines--; /* don't count the last one */
            state = fhdrENDClearsigHelp;
            afx->helplen = n;
            break;
@@ -714,8 +747,10 @@ fake_packet( armor_filter_context_t *afx, IOBUF a,
     buf[0] = (len-2) >> 8;
     buf[1] = (len-2);
     if( state == fhdrENDClearsig ) { /* write last (ending) length header */
-       buf[len++] = 0;
-       buf[len++] = 0;
+       if( buf[0] || buf[1] ) { /* write only if length of text is > 0 */
+           buf[len++] = 0;
+           buf[len++] = 0;
+       }
        rc = 0;
     }
 
@@ -756,7 +791,7 @@ radix64_read( armor_filter_context_t *afx, IOBUF a, size_t *retn,
            break;
        }
        else if( (c = asctobin[(c2=c)]) == 255 ) {
-           log_error("invalid radix64 character %02x skipped\n", c2);
+           log_error(_("invalid radix64 character %02x skipped\n"), c2);
            continue;
        }
        switch(idx) {
@@ -774,7 +809,10 @@ radix64_read( armor_filter_context_t *afx, IOBUF a, size_t *retn,
     afx->idx = idx;
     afx->radbuf[0] = val;
     if( checkcrc ) {
-       afx->inp_eof = 1; /*assume eof */
+       afx->any_data = 1;
+       afx->inp_checked=0;
+       afx->faked = 0;
+       afx->parse_state = 0;
        for(;;) { /* skip lf and pad characters */
            if( afx->helpidx < afx->helplen )
                c = afx->helpbuf[afx->helpidx++];
@@ -786,7 +824,7 @@ radix64_read( armor_filter_context_t *afx, IOBUF a, size_t *retn,
            break;
        }
        if( c == -1 )
-           log_error("premature eof (no CRC)\n");
+           log_error(_("premature eof (no CRC)\n"));
        else {
            u32 mycrc = 0;
            idx = 0;
@@ -804,13 +842,19 @@ radix64_read( armor_filter_context_t *afx, IOBUF a, size_t *retn,
                else if( (c=iobuf_get(a)) == -1 )
                    break;
            } while( ++idx < 4 );
-           if( c == -1 )
-               log_error("premature eof (in CRC)\n");
-           else if( idx != 4 )
-               log_error("malformed CRC\n");
-           else if( mycrc != afx->crc )
-               log_error("CRC error; %06lx - %06lx\n",
+           if( c == -1 ) {
+               log_error(_("premature eof (in CRC)\n"));
+               rc = G10ERR_INVALID_ARMOR;
+           }
+           else if( idx != 4 ) {
+               log_error(_("malformed CRC\n"));
+               rc = G10ERR_INVALID_ARMOR;
+           }
+           else if( mycrc != afx->crc ) {
+               log_error(_("CRC error; %06lx - %06lx\n"),
                                    (ulong)afx->crc, (ulong)mycrc);
+               rc = G10ERR_INVALID_ARMOR;
+           }
            else {
                rc = 0;
              #if 0
@@ -825,10 +869,14 @@ radix64_read( armor_filter_context_t *afx, IOBUF a, size_t *retn,
                }
                if( rc == -1 )
                    rc = 0;
-               else if( rc == 2 )
-                   log_error("premature eof (in Trailer)\n");
-               else
-                   log_error("error in trailer line\n");
+               else if( rc == 2 ) {
+                   log_error(_("premature eof (in Trailer)\n"));
+                   rc = G10ERR_INVALID_ARMOR;
+               }
+               else {
+                   log_error(_("error in trailer line\n"));
+                   rc = G10ERR_INVALID_ARMOR;
+               }
              #endif
            }
        }
@@ -841,9 +889,8 @@ radix64_read( armor_filter_context_t *afx, IOBUF a, size_t *retn,
     return rc;
 }
 
-
 /****************
- * The filter is used to handle the armor stuff
+ * This filter is used to handle the armor stuff
  */
 int
 armor_filter( void *opaque, int control,
@@ -878,16 +925,9 @@ armor_filter( void *opaque, int control,
        *ret_len = n;
     }
     else if( control == IOBUFCTRL_UNDERFLOW ) {
-       if( size < 30 )
+       if( size < 15+(4*15) )  /* need space for up to 4 onepass_sigs */
            BUG(); /* supplied buffer too short */
 
-       if( afx->inp_eof ) {
-           *ret_len = 0;
-           if( DBG_FILTER )
-               log_debug("armor-filter: eof due to inp_eof flag\n" );
-           return -1;
-       }
-
        if( afx->faked )
            rc = fake_packet( afx, a, &n, buf, size );
        else if( !afx->inp_checked ) {
@@ -901,27 +941,53 @@ armor_filter( void *opaque, int control,
                afx->helplen = 0;
            }
            else if( afx->faked ) {
-               /* the buffer is at least 30 bytes long, so it
+               unsigned hashes = afx->hashes;
+               /* the buffer is at least 15+n*15 bytes long, so it
                 * is easy to construct the packets */
 
-               /* first a onepass signature packet */
-               buf[0] = 0x90; /* old packet forma, type 4, 1 length byte */
-               buf[1] = 13;   /* length */
-               buf[2] = 3;    /* version */
-               buf[3] = 0x01; /* sigclass 0x01 (data in canonical text mode)*/
-               buf[4] = 0;    /* digest algo (don't know) */
-               buf[5] = 0;    /* public key algo (don't know) */
-               memset(buf+6, 0, 8); /* don't know the keyid */
-               buf[14] = 1;   /* this is the last one */
+               hashes &= 1|2|4|8;
+               if( !hashes )
+                   hashes |= 4;  /* default to MD 5 */
+               n=0;
+               do {
+                   /* first some onepass signature packets */
+                   buf[n++] = 0x90; /* old format, type 4, 1 length byte */
+                   buf[n++] = 13;   /* length */
+                   buf[n++] = 3;    /* version */
+                   buf[n++] = 0x01; /* sigclass 0x01 (canonical text mode)*/
+                   if( hashes & 1 ) {
+                       hashes &= ~1;
+                       buf[n++] = DIGEST_ALGO_RMD160;
+                   }
+                   else if( hashes & 2 ) {
+                       hashes &= ~2;
+                       buf[n++] = DIGEST_ALGO_SHA1;
+                   }
+                   else if( hashes & 4 ) {
+                       hashes &= ~4;
+                       buf[n++] = DIGEST_ALGO_MD5;
+                   }
+                   else if( hashes & 8 ) {
+                       hashes &= ~8;
+                       buf[n++] = DIGEST_ALGO_TIGER;
+                   }
+                   else
+                       buf[n++] = 0;    /* (don't know) */
+
+                   buf[n++] = 0;    /* public key algo (don't know) */
+                   memset(buf+n, 0, 8); /* don't know the keyid */
+                   n += 8;
+                   buf[n++] = !hashes;   /* last one */
+               } while( hashes );
 
                /* followed by a plaintext packet */
-               buf[15] = 0xaf; /* old packet format, type 11, var length */
-               buf[16] = 0;    /* set the length header */
-               buf[17] = 6;
-               buf[18] = 't';  /* canonical text mode */
-               buf[19] = 0;    /* namelength */
-               memset(buf+20, 0, 4); /* timestamp */
-               n = 24;
+               buf[n++] = 0xaf; /* old packet format, type 11, var length */
+               buf[n++] = 0;    /* set the length header */
+               buf[n++] = 6;
+               buf[n++] = 't';  /* canonical text mode */
+               buf[n++] = 0;    /* namelength */
+               memset(buf+n, 0, 4); /* timestamp */
+               n += 4;
            }
            else if( !rc )
                rc = radix64_read( afx, a, &n, buf, size );
@@ -942,9 +1008,27 @@ armor_filter( void *opaque, int control,
            iobuf_writestr(a, "-----");
            iobuf_writestr(a, head_strings[afx->what] );
            iobuf_writestr(a, "-----\n");
-           iobuf_writestr(a, "Version: G10 v"  VERSION " ("
+           iobuf_writestr(a, "Version: GNUPG v"  VERSION " ("
                                            PRINTABLE_OS_NAME ")\n");
-           iobuf_writestr(a, "Comment: This is an alpha version!\n");
+
+           if( opt.comment_string ) {
+               const char *s = opt.comment_string;
+               iobuf_writestr(a, "Comment: " );
+               for( ; *s; s++ ) {
+                   if( *s == '\n' )
+                       iobuf_writestr(a, "\\n" );
+                   else if( *s == '\r' )
+                       iobuf_writestr(a, "\\r" );
+                   else if( *s == '\v' )
+                       iobuf_writestr(a, "\\v" );
+                   else
+                       iobuf_put(a, *s );
+               }
+               iobuf_put(a, '\n' );
+           }
+           else
+               iobuf_writestr(a,
+                   "Comment: For info finger gcrypt@ftp.guug.de\n");
            if( afx->hdrlines )
                iobuf_writestr(a, afx->hdrlines);
            iobuf_put(a, '\n');
@@ -975,7 +1059,7 @@ armor_filter( void *opaque, int control,
                iobuf_put(a, c);
                c = bintoasc[radbuf[2]&077];
                iobuf_put(a, c);
-               if( ++idx2 > (72/4) ) {
+               if( ++idx2 >= (72/4) ) {
                    iobuf_put(a, '\n');
                    idx2=0;
                }
@@ -1014,10 +1098,13 @@ armor_filter( void *opaque, int control,
                    iobuf_put(a, c);
                    iobuf_put(a, '=');
                }
-               ++idx2;
+               if( ++idx2 >= (72/4) ) {
+                   iobuf_put(a, '\n');
+                   idx2=0;
+               }
            }
            /* may need a linefeed */
-           if( idx2 < (72/4) )
+           if( idx2 )
                iobuf_put(a, '\n');
            /* write the CRC */
            iobuf_put(a, '=');
@@ -1040,6 +1127,8 @@ armor_filter( void *opaque, int control,
            iobuf_writestr(a, tail_strings[afx->what] );
            iobuf_writestr(a, "-----\n");
        }
+       else if( !afx->any_data && !afx->inp_bypass )
+           log_error(_("no valid RFC1991 or OpenPGP data found.\n"));
     }
     else if( control == IOBUFCTRL_DESC )
        *(char**)buf = "armor_filter";