import works
[gnupg.git] / g10 / armor.c
index 6a432d3..03a393b 100644 (file)
@@ -32,8 +32,8 @@
 #include "filter.h"
 #include "packet.h"
 #include "options.h"
-
-
+#include "main.h"
+#include "status.h"
 
 
 #define CRCINIT 0xB704CE
@@ -49,25 +49,35 @@ static byte bintoasc[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
 static byte asctobin[256]; /* runtime initialized */
 static int is_initialized;
 
+
 typedef enum {
-    fhdrINIT=0,
-    fhdrLF,
-    fhdrWAIT,
-    fhdrWAITINFO,
-    fhdrWAITCLEARSIG,
-    fhdrDASH,
-    fhdrHEAD,
-    fhdrDASH2,
-    fhdrINFO
+    fhdrHASArmor,
+    fhdrNOArmor,
+    fhdrINIT,
+    fhdrINITCont,
+    fhdrINITSkip,
+    fhdrCHECKBegin,
+    fhdrWAITHeader,
+    fhdrWAITClearsig,
+    fhdrSKIPHeader,
+    fhdrCLEARSIG,
+    fhdrREADClearsig,
+    fhdrEMPTYClearsig,
+    fhdrCHECKClearsig,
+    fhdrCHECKClearsig2,
+    fhdrCHECKDashEscaped,
+    fhdrCHECKDashEscaped2,
+    fhdrCHECKDashEscaped3,
+    fhdrREADClearsigNext,
+    fhdrENDClearsig,
+    fhdrENDClearsigHelp,
+    fhdrTESTSpaces,
+    fhdrTEXT,
+    fhdrERROR,
+    fhdrERRORShow,
+    fhdrEOF
 } fhdr_state_t;
 
-struct fhdr_struct {
-    fhdr_state_t state;
-    int count;
-    int hdr_line; /* number of the header line */
-    char buf[256];
-};
-
 
 /* if we encounter this armor string with this index, go
  * into a mode, which fakes packets and wait for the next armor */
@@ -77,16 +87,23 @@ static char *head_strings[] = {
     "BEGIN PGP PUBLIC KEY BLOCK",
     "BEGIN PGP SIGNATURE",
     "BEGIN PGP SIGNED MESSAGE",
+    "BEGIN PGP ARMORED FILE",
     NULL
 };
 static char *tail_strings[] = {
     "END PGP MESSAGE",
     "END PGP PUBLIC KEY BLOCK",
     "END PGP SIGNATURE",
+    "END dummy",
+    "END PGP ARMORED FILE",
     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 void
 initialize(void)
 {
@@ -124,18 +141,14 @@ initialize(void)
  * Returns: True if it seems to be armored
  */
 static int
-is_armored( byte *buf, size_t len )
+is_armored( byte *buf )
 {
     int ctb, pkttype;
 
-    if( len < 28 )
-       return 0; /* not even enough space for the "---BEGIN"... */
-
     ctb = *buf;
     if( !(ctb & 0x80) )
        return 1; /* invalid packet: assume it is armored */
     pkttype =  ctb & 0x40 ? (ctb & 0x3f) : ((ctb>>2)&0xf);
-    /*lenbytes = (ctb & 0x40) || ((ctb&3)==3)? 0 : (1<<(ctb & 3));*/
     switch( pkttype ) {
       case PKT_PUBLIC_CERT:
       case PKT_SECRET_CERT:
@@ -152,120 +165,424 @@ is_armored( byte *buf, size_t len )
 }
 
 
+/****************
+ * Try to check wether the iobuf is armored
+ * Returns true if this may be the case; the caller should use the
+ *        filter to do further processing.
+ */
+int
+use_armor_filter( IOBUF a )
+{
+    byte buf[1];
+    int n;
+
+    n = iobuf_peek(a, buf, 1 );
+    if( n == -1 )
+       return 0; /* EOF, doesn't matter wether armored or not */
+    if( !n )
+       return 1; /* can't check it: try armored */
+    return is_armored(buf);
+}
+
+
+
+
+static void
+invalid_armor(void)
+{
+    write_status(STATUS_BADARMOR);
+    g10_exit(1); /* stop here */
+}
 
 
+/****************
+ * check wether 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.
+ */
 static int
-find_header( struct fhdr_struct *fhdr, int c )
+parse_hash_header( const char *line )
+{
+    const char *s, *s2;
+    unsigned found = 0;
+
+    if( strlen(line) < 6  || strlen(line) > 60 )
+       return 0; /* too short or too long */
+    if( memcmp( line, "Hash:", 5 ) )
+       return 0; /* invalid header */
+    s = line+5;
+    for(s=line+5;;s=s2) {
+       for(; *s && (*s==' ' || *s == '\t'); s++ )
+           ;
+       if( !*s )
+           break;
+       for(s2=s+1; *s2 && *s2!=' ' && *s2 != '\t' && *s2 != ','; s2++ )
+           ;
+       if( !strncmp( s, "RIPEMD160", s2-s ) )
+           found |= 1;
+       else if( !strncmp( s, "SHA1", s2-s ) )
+           found |= 2;
+       else if( !strncmp( s, "MD5", s2-s ) )
+           found |= 4;
+       else if( !strncmp( s, "MD2", s2-s ) )
+           found |= 8;
+       else
+           return 0;
+       for(; *s2 && (*s2==' ' || *s2 == '\t'); s2++ )
+           ;
+       if( *s2 && *s2 != ',' )
+           return 0;
+       if( *s2 )
+           s2++;
+    }
+    return found;
+}
+
+
+/****************
+ * parse an ascii armor.
+ * Returns: the state,
+ *         the remaining bytes in BUF are returned in RBUFLEN.
+ *         r_empty return the # of empty lines before the buffer
+ */
+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 i;
+    int c=0, i;
     const char *s;
+    byte *p;
+    size_t buflen;
+    int cont;
+    int clearsig=0;
+    int hdr_line=0;
+    unsigned empty = 0;
 
-    if( c == '\n' ) {
-       switch( fhdr->state ) {
-         case fhdrINFO:
-           if( !fhdr->count )
-               return 1;   /* blank line: data starts with the next line */
-           fhdr->buf[fhdr->count] = 0;
-           log_debug("armor line: '%s'\n", fhdr->buf );
-           /* fall through */
-         case fhdrWAITINFO:
-           fhdr->state = fhdrINFO;
-           fhdr->count = 0;
+    buflen = *r_buflen;
+    assert(buflen >= 100 );
+    buflen -= 3; /* reserved room for CR,LF and one extra */
+
+    do {
+       switch( state ) {
+         case fhdrHASArmor:
+           /* read at least the first byte to check wether it is armored
+            * or not */
+           c = 0;
+           for(n=0; n < 28 && (c=iobuf_get2(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 ) )
+               state = fhdrNOArmor;
+           else if( c == '\n' )
+               state = fhdrCHECKBegin;
+           else
+               state = fhdrINITCont;
            break;
-         case fhdrWAITCLEARSIG:
-           if( fhdr->count++ == 1 ) /* skip the empty line */
-               return 1; /* clear signed text follows */
-           else if( fhdr->count > 1 )
-               fhdr->state = fhdrWAIT; /* was not valid */
+
+         case fhdrINIT: /* read some stuff into buffer */
+           n = 0;
+         case fhdrINITCont: /* read more stuff into buffer */
+           c = 0;
+           for(; n < buflen && (c=iobuf_get2(a)) != -1 && c != '\n'; )
+               buf[n++] = c;
+           state = c == '\n' ? fhdrCHECKBegin :
+                    c == -1  ? fhdrEOF : fhdrINITSkip;
            break;
-         default:
-           fhdr->state = fhdrLF;
+
+         case fhdrINITSkip:
+           if( c == '\n' )
+               n = 0;
+           else {
+               while( (c=iobuf_get2(a)) != -1 && c != '\n' )
+                   ;
+           }
+           state =  c == -1? fhdrEOF : fhdrINIT;
            break;
-       }
-    }
-    else {
-       switch( fhdr->state ) {
-         case fhdrINIT:
-         case fhdrLF:
-           if( c == '-' ) {
-               fhdr->state = fhdrDASH;
-               fhdr->count = 1;
+
+         case fhdrSKIPHeader:
+           while( (c=iobuf_get2(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'; )
+               buf[n++] = c;
+           buf[n] = 0;
+           if( n < buflen || c == '\n' ) {
+               if( n && buf[0] != '\r') { /* maybe a header */
+                   if( strchr( buf, ':') ) { /* yes */
+                       if( opt.verbose ) {
+                           log_info("armor header: ");
+                           print_string( stderr, buf, n );
+                           putc('\n', stderr);
+                       }
+                       if( clearsig && !parse_hash_header( buf ) ) {
+                           log_error("invalid clearsig header\n");
+                           state = fhdrERROR;
+                       }
+                       else
+                           state = fhdrWAITHeader;
+                   }
+                   else
+                       state = fhdrCHECKDashEscaped3;
+               }
+               else if( !n || (buf[0] == '\r' && !buf[1]) ) { /* empty line */
+                   if( clearsig )
+                       state = fhdrWAITClearsig;
+                   else {
+                       /* this is not really correct: if we do not have
+                        * a clearsig and no armor lines we are not allowed
+                        * to have an empty line */
+                       n = 0;
+                       state = fhdrTEXT;
+                   }
+               }
+               else {
+                   log_error("invalid armor header: ");
+                   print_string( stderr, buf, n );
+                   putc('\n', stderr);
+                   state = fhdrERROR;
+               }
+           }
+           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 );
+                       fputs("[...]\n", stderr);  /* indicate it is truncated */
+                   }
+                   state = fhdrSKIPHeader;  /* skip rest of line */
+               }
+               else /* line too long */
+                   state = fhdrERROR;
            }
            else
-               fhdr->state = fhdrWAIT;
+               state = fhdrEOF;
            break;
-         case fhdrWAIT:
-         case fhdrWAITINFO:
-         case fhdrWAITCLEARSIG:
+
+         case fhdrWAITClearsig: /* skip all empty lines (for clearsig) */
+           c = 0;
+           for(n=0; n < buflen && (c=iobuf_get2(a)) != -1 && c != '\n'; )
+               buf[n++] = c;
+           if( n < buflen || c == '\n' ) {
+               buf[n] = 0;
+               if( !n || (buf[0]=='\r' && !buf[1]) ) /* empty line */
+                   ;
+               else
+                   state = fhdrCHECKDashEscaped3;
+           }
+           else {
+               /* fixme: we should check wether this line continues
+                *   it is poosible that we have only read ws until here
+                *   and more stuff is to come */
+               state = fhdrEOF;
+           }
            break;
-         case fhdrDASH:
-           if( c == '-' ) {
-               if( ++fhdr->count == 5 ) {
-                   fhdr->state = fhdrHEAD;
-                   fhdr->count = 0;
-               }
+
+         case fhdrENDClearsig:
+         case fhdrCHECKBegin:
+           state = state == fhdrCHECKBegin ? fhdrINITSkip : fhdrERRORShow;
+           if( n < 15 )
+               break;  /* too short */
+           if( memcmp( buf, "-----", 5 ) )
+               break;
+           buf[n] = 0;
+           p = strstr(buf+5, "-----");
+           if( !p )
+               break;
+           *p = 0;
+           p += 5;
+           if( *p == '\r' )
+               p++;
+           if( *p )
+               break; /* garbage after dashes */
+           p = buf+5;
+           for(i=0; (s=head_strings[i]); i++ )
+               if( !strcmp(s, p) )
+                   break;
+           if( !s )
+               break; /* unknown begin line */
+           /* 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]);
+           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*/
+           n = 0;
+           c = 0;
+           while( n < buflen && (c=iobuf_get2(a)) != -1 && c != '\n' )
+               buf[n++] = c;
+           buf[n] = 0;
+           if( c == -1 )
+               state = fhdrEOF;
+           else if( !n || ( buf[0]=='\r' && !buf[1] ) ) {
+               state = fhdrEMPTYClearsig;
+               empty++;
            }
+           else if( c == '\n' )
+               state = fhdrCHECKClearsig2;
            else
-               fhdr->state = fhdrWAIT;
+               state = fhdrCHECKClearsig;
            break;
-         case fhdrHEAD:
-           if( c != '-' ) {
-               if( fhdr->count < DIM(fhdr->buf)-1 )
-                   fhdr->buf[fhdr->count++] = c;
+
+         case fhdrCHECKDashEscaped3:
+           if( !(n > 1 && buf[0] == '-' && buf[1] == ' ' ) ) {
+               state = fhdrTEXT;
+               break;
+           }
+           /* fall through */
+         case fhdrCHECKDashEscaped2:
+         case fhdrCHECKDashEscaped:
+           /* check dash escaped line */
+           if( buf[2] == '-' || ( n > 6 && !memcmp(buf+2, "From ", 5))) {
+               for(i=2; i < n; i++ )
+                   buf[i-2] = buf[i];
+               n -= 2;
+               buf[n] = 0; /* not really needed */
+               state = state == fhdrCHECKDashEscaped3 ? fhdrTEXT :
+                       state == fhdrCHECKDashEscaped2 ?
+                                fhdrREADClearsig : fhdrTESTSpaces;
            }
            else {
-               fhdr->buf[fhdr->count] = 0;
-               for(i=0; (s=head_strings[i]); i++ )
-                   if( !strcmp(s,fhdr->buf) )
-                       break;
-               if( s ) { /* found string; wait for trailing dashes */
-                   fhdr->hdr_line = i;
-                   fhdr->state = fhdrDASH2;
-                   fhdr->count = 1;
-               }
-               else
-                   fhdr->state = fhdrWAIT;
+               log_error("invalid dash escaped line: ");
+               print_string( stderr, buf, n );
+               putc('\n', stderr);
+               state = fhdrERROR;
            }
            break;
 
-         case fhdrDASH2:
-           if( c == '-' ) {
-               if( ++fhdr->count == 5 ) {
-                   fhdr->state = fhdrWAITINFO;
-                   log_debug("armor head: '%s'\n",
-                                       head_strings[fhdr->hdr_line]);
-                   fhdr->state = fhdr->hdr_line == BEGIN_SIGNED_MSG_IDX
-                                  ? fhdrWAITCLEARSIG : fhdrWAITINFO;
-                   if( fhdr->state == fhdrWAITCLEARSIG )
-                       fhdr->count = 0;
-               }
+         case fhdrCHECKClearsig:
+           /* check the clearsig line */
+           if( n > 15 && !memcmp(buf, "-----", 5 ) )
+               state = fhdrENDClearsig;
+           else if( buf[0] == '-' && buf[1] == ' ' )
+               state = fhdrCHECKDashEscaped;
+           else {
+               state = fhdrTESTSpaces;
+           }
+           break;
+
+         case fhdrCHECKClearsig2:
+           /* check the clearsig line */
+           if( n > 15 && !memcmp(buf, "-----", 5 ) )
+               state = fhdrENDClearsig;
+           else if( buf[0] == '-' && buf[1] == ' ' )
+               state = fhdrCHECKDashEscaped2;
+           else {
+               state = fhdrREADClearsig;
            }
+           break;
+
+         case fhdrREADClearsigNext:
+           /* Read to the end of the line, do not care about checking
+            * for dashed escaped text of headers */
+           c = 0;
+           n = 0;
+           while( n < buflen && (c=iobuf_get2(a)) != -1 && c != '\n' )
+               buf[n++] = c;
+           buf[n] = 0;
+           if( c == -1 )
+               state = fhdrEOF;
+           else if( c == '\n' )
+               state = fhdrREADClearsig;
            else
-               fhdr->state = fhdrWAIT;
+               state = fhdrTESTSpaces;
            break;
 
-         case fhdrINFO:
-           if( fhdr->count < DIM(fhdr->buf)-1 )
-               fhdr->buf[fhdr->count++] = c;
+         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
+            * counting spaces is not enough, because it may be a
+            * mix of different white space chacters */
+           IOBUF b = iobuf_temp();
+           while( (c=iobuf_get2(a)) != -1 && c != '\n' ) {
+               iobuf_put(b,c);
+               if( c != ' ' && c != '\t' && c != '\r' )
+                   break;
+           }
+           if( c == '\n' ) {
+               /* okay we can skip the rest of the line */
+               iobuf_close(b);
+               state = fhdrREADClearsig;
+           }
+           else {
+               iobuf_unget_and_close_temp(a,b);
+               state = fhdrREADClearsigNext;
+           }
+         } break;
+
+         case fhdrERRORShow:
+           log_error("invalid clear text header: ");
+           print_string( stderr, buf, n );
+           putc('\n', stderr);
+           state = fhdrERROR;
            break;
 
-         default: abort();
+         default: BUG();
+       }
+       switch( state ) {
+         case fhdrINIT:
+         case fhdrINITCont:
+         case fhdrINITSkip:
+         case fhdrCHECKBegin:
+         case fhdrWAITHeader:
+         case fhdrWAITClearsig:
+         case fhdrSKIPHeader:
+         case fhdrEMPTYClearsig:
+         case fhdrCHECKClearsig:
+         case fhdrCHECKClearsig2:
+         case fhdrCHECKDashEscaped:
+         case fhdrCHECKDashEscaped2:
+         case fhdrCHECKDashEscaped3:
+         case fhdrTESTSpaces:
+         case fhdrERRORShow:
+           cont = 1;
+           break;
+         default: cont = 0;
+       }
+    } while( cont );
+
+    if( clearsig && state == fhdrTEXT )
+       state = fhdrCLEARSIG;
+
+    if( state == fhdrCLEARSIG || state == fhdrREADClearsig ) {
+       /* append CR,LF after removing trailing wspaces */
+       for(p=buf+n-1; n; n--, p-- ) {
+           assert( *p != '\n' );
+           if( *p != ' ' && *p != '\t' && *p != '\r' ) {
+               p[1] = '\r';
+               p[2] = '\n';
+               n += 2;
+               break;
+           }
+       }
+       if( !n ) {
+           buf[0] = '\r';
+           buf[1] = '\n';
+           n = 2;
        }
     }
-    return 0;
-}
 
-/****************
- * check wether the trailer is okay.
- * Returns: 0 := still in trailer
- *         -1 := Okay
- *         1 := Error in trailer
- */
-static int
-check_trailer( struct fhdr_struct *fhdr, int c )
-{
-    return -1;
-    /* FIXME: implement this ! */
+
+    *r_buflen = n;
+    *r_empty = empty;
+    return state;
 }
 
 
@@ -274,59 +591,49 @@ static int
 check_input( armor_filter_context_t *afx, IOBUF a )
 {
     int rc = 0;
-    int c;
-    size_t n = 0, nn=0;
-    struct fhdr_struct fhdr;
+    size_t n;
+    fhdr_state_t state = afx->parse_state;
+    unsigned emplines;
 
-    assert( DIM(afx->helpbuf) >= 50 );
-    memset( &fhdr, 0, sizeof(fhdr) );
+    if( state != fhdrENDClearsig )
+       state = fhdrHASArmor;
 
-    /* read a couple of bytes */
-    for( n=0; n < DIM(afx->helpbuf); n++ ) {
-       if( (c=iobuf_get(a)) == -1 )
-           break;
-       afx->helpbuf[n] = c & 0xff;
-    }
+    n = DIM(afx->helpbuf);
+    state = find_header( state, afx->helpbuf, &n, a, afx->helplen, &emplines);
+    switch( state ) {
+      case fhdrNOArmor:
+       afx->inp_checked = 1;
+       afx->inp_bypass = 1;
+       afx->helplen = n;
+       break;
 
-    if( !n )
+      case fhdrERROR:
+       invalid_armor();
+       break;
+
+      case fhdrEOF:
        rc = -1;
-    else if( is_armored( afx->helpbuf, n ) ) {
-       for(nn=0; nn < n; nn++ )
-           if( find_header( &fhdr, afx->helpbuf[nn] ) )
-               break;
-       if( nn == n ) { /* continue read */
-           while( (c=iobuf_get(a)) != -1 )
-               if( find_header( &fhdr, c ) )
-                   break;
-           if( c == -1 )
-               rc = -1; /* eof */
-       }
-       if( !rc && fhdr.hdr_line == BEGIN_SIGNED_MSG_IDX ) {
-           /* start fake package mode (for clear signatures) */
-           nn++;
-           afx->helplen = n;
-           afx->helpidx = nn;
-           afx->templen = 0;
-           afx->tempidx = 0;
-           afx->fake = m_alloc_clear( sizeof(struct fhdr_struct) );
-       }
-       else if( !rc ) {
-           /* next byte to read or helpbuf[nn+1]
-            * is the first rad64 byte */
-           nn++;
-           afx->inp_checked = 1;
-           afx->crc = CRCINIT;
-           afx->idx = 0;
-           afx->radbuf[0] = 0;
-           afx->helplen = n;
-           afx->helpidx = nn;
-       }
-    }
-    else {
+       break;
+
+      case fhdrCLEARSIG: /* start fake package mode (for clear signatures) */
+       afx->helplen = n;
+       afx->helpidx = 0;
+       afx->faked = 1;
+       break;
+
+      case fhdrTEXT:
+       afx->helplen = n;
+       afx->helpidx = 0;
        afx->inp_checked = 1;
-       afx->inp_bypass = 1;
+       afx->crc = CRCINIT;
+       afx->idx = 0;
+       afx->radbuf[0] = 0;
+       break;
+
+      default: BUG();
     }
 
+    afx->parse_state = state;
     return rc;
 }
 
@@ -338,71 +645,83 @@ fake_packet( armor_filter_context_t *afx, IOBUF a,
             size_t *retn, byte *buf, size_t size  )
 {
     int rc = 0;
-    int c;
-    size_t n = 0;
-    struct fhdr_struct *fhdr = afx->fake;
-    byte *helpbuf = afx->helpbuf;
-    int helpidx = afx->helpidx;
-    int helplen = afx->helplen;
-    byte *tempbuf = afx->tempbuf;
-    int tempidx = afx->tempidx;
-    int templen = afx->templen;
-
-    /* FIXME: have to read one ahead or do some other mimic to
-     * get rid of the lf before the "begin signed message"
-     */
+    size_t len = 0;
+    size_t n, nn;
+    fhdr_state_t state = afx->parse_state;
+    unsigned emplines = afx->empty;
+
     size = 100; /* FIXME: only used for testing (remove it)  */
-    n = 2; /* reserve 2 bytes for the length header */
-    while( n < size-2 ) { /* and 2 for the term header */
-       if( templen && (fhdr->state == fhdrWAIT || fhdr->state == fhdrLF) ) {
-           if( tempidx < templen ) {
-               buf[n++] = tempbuf[tempidx++];
-               continue;
+
+    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 ) {
+       if( emplines ) {
+           while( emplines && len < size ) {
+               buf[len++] = '\r';
+               buf[len++] = '\n';
+               emplines--;
            }
-           tempidx = templen = 0;
+           continue;
+       }
+       if( state == fhdrENDClearsigHelp ) {
+           state = fhdrENDClearsig;
+           afx->faked = 0;
+           rc = -1;
+           continue;
+       }
+       if( 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];
+           afx->helpidx = nn;
+           continue;
+       }
+       if( state == fhdrEOF ) {
+           rc = -1;
+           continue;
        }
+       /* read a new one */
+       n = DIM(afx->helpbuf);
+       afx->helpidx = 0;
+       state = find_header( state, afx->helpbuf, &n, a, 0, &emplines );
+       switch( state) {
+         case fhdrERROR:
+           invalid_armor();
+           break;
 
-       if( helpidx < helplen )
-           c = helpbuf[helpidx++];
-       else if( (c=iobuf_get(a)) == -1 )
+         case fhdrEOF:
+           rc = -1;
            break;
-       if( find_header( fhdr, c ) ) {
-           m_free(afx->fake);
-           afx->fake = NULL;
-           afx->inp_checked = 1;
-           afx->crc = CRCINIT;
-           afx->idx = 0;
-           afx->radbuf[0] = 0;
-           /* we don't need to care about the tempbuf */
+
+         case fhdrCLEARSIG:
+           BUG();
+
+         case fhdrREADClearsig:
+         case fhdrREADClearsigNext:
+           afx->helplen = n;
            break;
-       }
-       if( fhdr->state == fhdrWAIT || fhdr->state == fhdrLF ) {
-           if( templen ) {
-               tempidx = 0;
-               continue;
-           }
-           buf[n++] = c;
-       }
-       else if( fhdr->state == fhdrWAITINFO
-               || fhdr->state == fhdrINFO )
-           ;
-       else { /* store it in another temp. buf */
-           assert( templen < DIM(afx->tempbuf) );
-           tempbuf[templen++] = c;
+
+         case fhdrENDClearsig:
+           assert( emplines );
+           emplines--; /* don't count the last one */
+           state = fhdrENDClearsigHelp;
+           afx->helplen = n;
+           break;
+
+         default: BUG();
        }
     }
-    buf[0] = (n-2) >> 8;
-    buf[1] = (n-2);
-    if( !afx->fake ) { /* write last (ending) length header */
-       buf[n++] = 0;
-       buf[n++] = 0;
+    buf[0] = (len-2) >> 8;
+    buf[1] = (len-2);
+    if( state == fhdrENDClearsig ) { /* write last (ending) length header */
+       buf[len++] = 0;
+       buf[len++] = 0;
+       rc = 0;
     }
 
-    afx->helpidx = helpidx;
-    afx->helplen = helplen;
-    afx->tempidx = tempidx;
-    afx->templen = templen;
-    *retn = n;
+    afx->parse_state = state;
+    afx->empty = emplines;
+    *retn = len;
     return rc;
 }
 
@@ -493,11 +812,10 @@ radix64_read( armor_filter_context_t *afx, IOBUF a, size_t *retn,
                log_error("CRC error; %06lx - %06lx\n",
                                    (ulong)afx->crc, (ulong)mycrc);
            else {
-               struct fhdr_struct fhdr;
-
-               memset( &fhdr, 0, sizeof(fhdr) );
+               rc = 0;
+             #if 0
                for(rc=0;!rc;) {
-                   rc = check_trailer( &fhdr, c );
+                   rc = 0 /*check_trailer( &fhdr, c )*/;
                    if( !rc ) {
                        if( afx->helpidx < afx->helplen )
                            c = afx->helpbuf[afx->helpidx++];
@@ -511,6 +829,7 @@ radix64_read( armor_filter_context_t *afx, IOBUF a, size_t *retn,
                    log_error("premature eof (in Trailer)\n");
                else
                    log_error("error in trailer line\n");
+             #endif
            }
        }
     }
@@ -537,6 +856,14 @@ armor_filter( void *opaque, int control,
     int  idx, idx2;
     size_t n=0;
     u32 crc;
+  #if 0
+    static FILE *fp ;
+
+    if( !fp ) {
+       fp = fopen("armor.out", "w");
+       assert(fp);
+    }
+  #endif
 
     if( DBG_FILTER )
        log_debug("armor-filter: control: %d\n", control );
@@ -551,8 +878,8 @@ armor_filter( void *opaque, int control,
        *ret_len = n;
     }
     else if( control == IOBUFCTRL_UNDERFLOW ) {
-       if( size < 20 )
-           BUG(); /* supplied buffer maybe too short */
+       if( size < 30 )
+           BUG(); /* supplied buffer too short */
 
        if( afx->inp_eof ) {
            *ret_len = 0;
@@ -561,29 +888,51 @@ armor_filter( void *opaque, int control,
            return -1;
        }
 
-       if( afx->fake )
+       if( afx->faked )
            rc = fake_packet( afx, a, &n, buf, size );
        else if( !afx->inp_checked ) {
            rc = check_input( afx, a );
-           if( afx->inp_bypass )
-               ;
-           else if( afx->fake ) {
-               /* the buffer is at least 20 bytes long, so it
-                * is easy to construct a packet */
-               buf[0] = 0xaf; /* old packet format, type 11, var length */
-               buf[1] = 0;    /* set the length header */
-               buf[2] = 6;
-               buf[3] = 't';  /* canonical text */
-               buf[4] = 0;    /* namelength */
-               buf[5] = buf[6] = buf[7] = buf[8] = 0; /* timestamp */
-               n = 9;
+           if( afx->inp_bypass ) {
+               for( n=0; n < size && n < afx->helplen; n++ )
+                   buf[n] = afx->helpbuf[n];
+               if( !n )
+                   rc = -1;
+               assert( n == afx->helplen );
+               afx->helplen = 0;
+           }
+           else if( afx->faked ) {
+               /* the buffer is at least 30 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 */
+
+               /* 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;
            }
            else if( !rc )
                rc = radix64_read( afx, a, &n, buf, size );
        }
        else
            rc = radix64_read( afx, a, &n, buf, size );
-
+      #if 0
+       if( n )
+           if( fwrite(buf, n, 1, fp ) != 1 )
+               BUG();
+      #endif
        *ret_len = n;
     }
     else if( control == IOBUFCTRL_FLUSH ) {
@@ -593,8 +942,9 @@ 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 pre-release "  VERSION "\n");
-           iobuf_writestr(a, "Comment: This is a alpha test version!\n\n");
+           iobuf_writestr(a, "Version: G10 v"  VERSION " ("
+                                           PRINTABLE_OS_NAME ")\n");
+           iobuf_writestr(a, "Comment: This is an alpha version!\n\n");
            afx->status++;
            afx->idx = 0;
            afx->idx2 = 0;
@@ -693,3 +1043,5 @@ armor_filter( void *opaque, int control,
     return rc;
 }
 
+
+