sm: Print Yubikey attestation extensions with --dump-cert.
[gnupg.git] / g10 / armor.c
index 99142a7..9727665 100644 (file)
@@ -1,11 +1,12 @@
-/* armor.c - Armor flter
- *     Copyright (C) 1998, 1999 Free Software Foundation, Inc.
+/* armor.c - Armor filter
+ * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006,
+ *               2007 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
  * 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
+ * the Free Software Foundation; either version 3 of the License, or
  * (at your option) any later version.
  *
  * GnuPG is distributed in the hope that it will be useful,
@@ -14,8 +15,7 @@
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
  */
 
 #include <config.h>
 #include <stdlib.h>
 #include <string.h>
 #include <errno.h>
-#include <assert.h>
 #include <ctype.h>
 
-#include "errors.h"
-#include "iobuf.h"
-#include "memory.h"
-#include "util.h"
-#include "iobuf.h"
+#include "gpg.h"
+#include "../common/status.h"
+#include "../common/iobuf.h"
+#include "../common/util.h"
 #include "filter.h"
 #include "packet.h"
 #include "options.h"
 #include "main.h"
-#include "status.h"
-#include "i18n.h"
-
+#include "../common/i18n.h"
 
 #define MAX_LINELEN 20000
 
-#define CRCINIT 0xB704CE
-#define CRCPOLY 0X864CFB
-#define CRCUPDATE(a,c) do {                                                \
-                       a = ((a) << 8) ^ crc_table[((a)&0xff >> 16) ^ (c)]; \
-                       a &= 0x00ffffff;                                    \
-                   } while(0)
-static u32 crc_table[256];
-static byte bintoasc[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
-                        "abcdefghijklmnopqrstuvwxyz"
-                        "0123456789+/";
-static byte asctobin[256]; /* runtime initialized */
+static const byte bintoasc[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+                               "abcdefghijklmnopqrstuvwxyz"
+                               "0123456789+/";
+static u32 asctobin[4][256]; /* runtime initialized */
 static int is_initialized;
 
 
@@ -114,68 +103,184 @@ static char *tail_strings[] = {
 };
 
 
+static int armor_filter ( void *opaque, int control,
+                          iobuf_t chain, byte *buf, size_t *ret_len);
 
-static void
-initialize(void)
+
+
+\f
+/* Create a new context for armor filters.  */
+armor_filter_context_t *
+new_armor_context (void)
 {
-    int i, j;
-    u32 t;
-    byte *s;
-
-    /* init the crc lookup table */
-    crc_table[0] = 0;
-    for(i=j=0; j < 128; j++ ) {
-       t = crc_table[j];
-       if( t & 0x00800000 ) {
-           t <<= 1;
-           crc_table[i++] = t ^ CRCPOLY;
-           crc_table[i++] = t;
-       }
-       else {
-           t <<= 1;
-           crc_table[i++] = t;
-           crc_table[i++] = t ^ CRCPOLY;
+  armor_filter_context_t *afx;
+  gpg_error_t err;
+
+  afx = xcalloc (1, sizeof *afx);
+  if (afx)
+    {
+      err = gcry_md_open (&afx->crc_md, GCRY_MD_CRC24_RFC2440, 0);
+      if (err != 0)
+       {
+         log_error ("gcry_md_open failed for GCRY_MD_CRC24_RFC2440: %s",
+                   gpg_strerror (err));
+         xfree (afx);
+         return NULL;
        }
+
+      afx->refcount = 1;
     }
-    /* build the helptable for radix64 to bin conversion */
-    for(i=0; i < 256; i++ )
-       asctobin[i] = 255; /* used to detect invalid characters */
+
+  return afx;
+}
+
+/* Release an armor filter context.  Passing NULL is explicitly
+   allowed and a no-op.  */
+void
+release_armor_context (armor_filter_context_t *afx)
+{
+  if (!afx)
+    return;
+  log_assert (afx->refcount);
+  if ( --afx->refcount )
+    return;
+  gcry_md_close (afx->crc_md);
+  xfree (afx);
+}
+
+/* Push the armor filter onto the iobuf stream IOBUF.  */
+int
+push_armor_filter (armor_filter_context_t *afx, iobuf_t iobuf)
+{
+  int rc;
+
+  afx->refcount++;
+  rc = iobuf_push_filter (iobuf, armor_filter, afx);
+  if (rc)
+    afx->refcount--;
+  return rc;
+}
+
+
+
+
+
+static void
+initialize(void)
+{
+    u32 i;
+    const byte *s;
+
+    /* Build the helptable for radix64 to bin conversion.  Value 0xffffffff is
+       used to detect invalid characters.  */
+    memset (asctobin, 0xff, sizeof(asctobin));
     for(s=bintoasc,i=0; *s; s++,i++ )
-       asctobin[*s] = i;
+      {
+       asctobin[0][*s] = i << (0 * 6);
+       asctobin[1][*s] = i << (1 * 6);
+       asctobin[2][*s] = i << (2 * 6);
+       asctobin[3][*s] = i << (3 * 6);
+      }
 
     is_initialized=1;
 }
 
-/****************
- * Check whether this is an armored file or not
- * See also parse-packet.c for details on this code
+
+static inline u32
+get_afx_crc (armor_filter_context_t *afx)
+{
+  const byte *crc_buf;
+  u32 crc;
+
+  crc_buf = gcry_md_read (afx->crc_md, GCRY_MD_CRC24_RFC2440);
+
+  crc = crc_buf[0];
+  crc <<= 8;
+  crc |= crc_buf[1];
+  crc <<= 8;
+  crc |= crc_buf[2];
+
+  return crc;
+}
+
+
+/*
+ * Check whether this is an armored file.  See also
+ * parse-packet.c for details on this code.
+ *
+ * Note that the buffer BUF needs to be at least 2 bytes long.  If in
+ * doubt that the second byte to 0.
+ *
  * Returns: True if it seems to be armored
  */
 static int
-is_armored( const byte *buf )
+is_armored (const byte *buf)
 {
-    int ctb, pkttype;
+  int ctb, pkttype;
+  int indeterminate_length_allowed;
 
     ctb = *buf;
     if( !(ctb & 0x80) )
-       return 1; /* invalid packet: assume it is armored */
+      /* The most significant bit of the CTB must be set.  Since it is
+         cleared, this is not a binary OpenPGP message.  Assume it is
+         armored.  */
+      return 1;
+
     pkttype =  ctb & 0x40 ? (ctb & 0x3f) : ((ctb>>2)&0xf);
     switch( pkttype ) {
-      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_SYMKEY_ENC:
+      case PKT_ONEPASS_SIG:
+      case PKT_SECRET_KEY:
+      case PKT_PUBLIC_KEY:
+      case PKT_SECRET_SUBKEY:
+      case PKT_MARKER:
+      case PKT_RING_TRUST:
+      case PKT_USER_ID:
+      case PKT_PUBLIC_SUBKEY:
+      case PKT_ATTRIBUTE:
+      case PKT_MDC:
+       indeterminate_length_allowed = 0;
+        break;
+
       case PKT_COMPRESSED:
       case PKT_ENCRYPTED:
-       return 0; /* seems to be a regular packet: not armored */
+      case PKT_ENCRYPTED_MDC:
+      case PKT_PLAINTEXT:
+      case PKT_OLD_COMMENT:
+      case PKT_COMMENT:
+      case PKT_GPG_CONTROL:
+       indeterminate_length_allowed = 1;
+        break;
+
+      default:
+        /* Invalid packet type.  */
+        return 1;
     }
 
-    return 1;
+    if (! indeterminate_length_allowed)
+      /* It is only legal to use an indeterminate length with a few
+         packet types.  If a packet uses an indeterminate length, but
+         that is not allowed, then the data is not valid binary
+         OpenPGP data.  */
+      {
+        int new_format;
+        int indeterminate_length;
+
+        new_format = !! (ctb & (1 << 6));
+        if (new_format)
+          indeterminate_length = (buf[1] >= 224 && buf[1] < 255);
+        else
+          indeterminate_length = (ctb & 3) == 3;
+
+        if (indeterminate_length)
+          return 1;
+      }
+
+    /* The first CTB seems legit.  It is probably not armored
+       data.  */
+    return 0;
 }
 
 
@@ -187,14 +292,17 @@ is_armored( const byte *buf )
 int
 use_armor_filter( IOBUF a )
 {
-    byte buf[1];
+    byte buf[2];
     int n;
 
-    n = iobuf_peek(a, buf, 1 );
+    /* fixme: there might be a problem with iobuf_peek */
+    n = iobuf_peek (a, buf, 2);
     if( n == -1 )
        return 0; /* EOF, doesn't matter whether armored or not */
     if( !n )
        return 1; /* can't check it: try armored */
+    if (n != 2)
+       return 0; /* short buffer */
     return is_armored(buf);
 }
 
@@ -226,7 +334,7 @@ parse_hash_header( const char *line )
        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++ )
            ;
@@ -238,10 +346,14 @@ parse_hash_header( const char *line )
            found |= 1;
        else if( !strncmp( s, "SHA1", s2-s ) )
            found |= 2;
-       else if( !strncmp( s, "MD5", s2-s ) )
-           found |= 4;
-       else if( !strncmp( s, "TIGER", s2-s ) )
+       else if( !strncmp( s, "SHA224", s2-s ) )
            found |= 8;
+       else if( !strncmp( s, "SHA256", s2-s ) )
+           found |= 16;
+       else if( !strncmp( s, "SHA384", s2-s ) )
+           found |= 32;
+       else if( !strncmp( s, "SHA512", s2-s ) )
+           found |= 64;
        else
            return 0;
        for(; *s2 && (*s2==' ' || *s2 == '\t'); s2++ )
@@ -254,33 +366,20 @@ parse_hash_header( const char *line )
     return found;
 }
 
-
-
-
-static unsigned
-trim_trailing_spaces( byte *line, unsigned len )
+/* Returns true if this is a valid armor tag as per RFC-2440bis-21. */
+static int
+is_armor_tag(const char *line)
 {
-    byte *p, *mark;
-    unsigned n;
-
-    for(mark=NULL, p=line, n=0; n < len; n++, p++ ) {
-       if( strchr(" \t\r\n", *p ) ) {
-           if( !mark )
-               mark = p;
-       }
-       else
-           mark = NULL;
-    }
+  if(strncmp(line,"Version",7)==0
+     || strncmp(line,"Comment",7)==0
+     || strncmp(line,"MessageID",9)==0
+     || strncmp(line,"Hash",4)==0
+     || strncmp(line,"Charset",7)==0)
+    return 1;
 
-    if( mark ) {
-       *mark = 0;
-       return mark - line;
-    }
-    return len;
+  return 0;
 }
 
-
-
 /****************
  * Check whether this is a armor line.
  * returns: -1 if it is not a armor header or the index number of the
@@ -303,10 +402,26 @@ is_armor_header( byte *line, unsigned len )
        return -1;
     save_p = p;
     p += 5;
-    if( *p == '\r' )
-       p++;
-    if( *p == '\n' )
+
+    /* Some Windows environments seem to add whitespace to the end of
+       the line, so we strip it here.  This becomes strict if
+       --rfc2440 is set since 2440 reads "The header lines, therefore,
+       MUST start at the beginning of a line, and MUST NOT have text
+       following them on the same line."  It is unclear whether "text"
+       refers to all text or just non-whitespace text.  4880 clarified
+       this was only non-whitespace text. */
+
+    if(RFC2440)
+      {
+       if( *p == '\r' )
+         p++;
+       if( *p == '\n' )
+         p++;
+      }
+    else
+      while(*p==' ' || *p=='\r' || *p=='\n' || *p=='\t')
        p++;
+
     if( *p )
        return -1; /* garbage after dashes */
     save_c = *save_p; *save_p = 0;
@@ -332,38 +447,74 @@ is_armor_header( byte *line, unsigned len )
  *      >0: Good header line
  */
 static int
-parse_header_line( armor_filter_context_t *afx, byte *line, unsigned len )
+parse_header_line( armor_filter_context_t *afx, byte *line, unsigned int len )
 {
     byte *p;
     int hashes=0;
+    unsigned int len2;
+
+    len2 = length_sans_trailing_ws ( line, len );
+    if( !len2 ) {
+        afx->buffer_pos = len2;  /* (it is not the fine way to do it here) */
+       return 0; /* WS only: same as empty line */
+    }
+
+    /*
+      This is fussy.  The spec says that a header line is delimited
+      with a colon-space pair.  This means that a line such as
+      "Comment: " (with nothing else) is actually legal as an empty
+      string comment.  However, email and cut-and-paste being what it
+      is, that trailing space may go away.  Therefore, we accept empty
+      headers delimited with only a colon.  --rfc2440, as always,
+      makes this strict and enforces the colon-space pair. -dms
+    */
 
-    if( *line == '\n' || ( len && (*line == '\r' && line[1]=='\n') ) )
-       return 0; /* empty line */
-    len = trim_trailing_spaces( line, len );
     p = strchr( line, ':');
-    if( !p || !p[1] ) {
-       log_error(_("invalid armor header: "));
-       print_string( stderr, line, len, 0 );
-       putc('\n', stderr);
+    if( !p || (RFC2440 && p[1]!=' ')
+       || (!RFC2440 && p[1]!=' ' && p[1]!='\n' && p[1]!='\r'))
+      {
+       log_error (_("invalid armor header: "));
+       es_write_sanitized (log_get_stream (), line, len, NULL, NULL);
+       log_printf ("\n");
        return -1;
-    }
+      }
+
+    /* Chop off the whitespace we detected before */
+    len=len2;
+    line[len2]='\0';
 
     if( opt.verbose ) {
        log_info(_("armor header: "));
-       print_string( stderr, line, len, 0 );
-       putc('\n', stderr);
+       es_write_sanitized (log_get_stream (), line, len, NULL, NULL);
+       log_printf ("\n");
     }
 
-    if( afx->in_cleartext ) {
+    if( afx->in_cleartext )
+      {
        if( (hashes=parse_hash_header( line )) )
-           afx->hashes |= hashes;
+         afx->hashes |= hashes;
        else if( strlen(line) > 15 && !memcmp( line, "NotDashEscaped:", 15 ) )
-           afx->not_dash_escaped = 1;
-       else {
+         afx->not_dash_escaped = 1;
+       else
+         {
            log_error(_("invalid clearsig header\n"));
            return -1;
-       }
-    }
+         }
+      }
+    else if(!is_armor_tag(line))
+      {
+       /* Section 6.2: "Unknown keys should be reported to the user,
+          but OpenPGP should continue to process the message."  Note
+          that in a clearsigned message this applies to the signature
+          part (i.e. "BEGIN PGP SIGNATURE") and not the signed data
+          ("BEGIN PGP SIGNED MESSAGE").  The only key allowed in the
+          signed data section is "Hash". */
+
+       log_info(_("unknown armor header: "));
+       es_write_sanitized (log_get_stream (), line, len, NULL, NULL);
+       log_printf ("\n");
+      }
+
     return 1;
 }
 
@@ -399,7 +550,7 @@ check_input( armor_filter_context_t *afx, IOBUF a )
     /* (the line is always a C string but maybe longer) */
     if( *line == '\n' || ( len && (*line == '\r' && line[1]=='\n') ) )
        ;
-    else if( !is_armored( line ) ) {
+    else if (len >= 2 && !is_armored (line)) {
        afx->inp_checked = 1;
        afx->inp_bypass = 1;
        return 0;
@@ -413,7 +564,7 @@ check_input( armor_filter_context_t *afx, IOBUF a )
            if( hdr_line == BEGIN_SIGNED_MSG_IDX ) {
                if( afx->in_cleartext ) {
                    log_error(_("nested clear text signatures\n"));
-                   rc = G10ERR_INVALID_ARMOR;
+                   rc = gpg_error (GPG_ERR_INV_ARMOR);
                }
                afx->in_cleartext = 1;
            }
@@ -429,9 +580,9 @@ check_input( armor_filter_context_t *afx, IOBUF a )
        } while( !maxlen );
     }
 
-    /* parse the header lines */
+    /* Parse the header lines.  */
     while(len) {
-       /* read the next line (skip all truncated lines) */
+       /* Read the next line (skip all truncated lines). */
        do {
            maxlen = MAX_LINELEN;
            afx->buffer_len = iobuf_read_line( a, &afx->buffer,
@@ -442,8 +593,8 @@ check_input( armor_filter_context_t *afx, IOBUF a )
 
        i = parse_header_line( afx, line, len );
        if( i <= 0 ) {
-           if( i )
-               rc = G10ERR_INVALID_ARMOR;
+           if (i && RFC2440)
+               rc = GPG_ERR_INV_ARMOR;
            break;
        }
     }
@@ -451,12 +602,11 @@ check_input( armor_filter_context_t *afx, IOBUF a )
 
     if( rc )
        invalid_armor();
-    else if( afx->in_cleartext ) {
+    else if( afx->in_cleartext )
        afx->faked = 1;
-    }
     else {
        afx->inp_checked = 1;
-       afx->crc = CRCINIT;
+       gcry_md_reset (afx->crc_md);
        afx->idx = 0;
        afx->radbuf[0] = 0;
     }
@@ -464,7 +614,8 @@ check_input( armor_filter_context_t *afx, IOBUF a )
     return rc;
 }
 
-
+#define PARTIAL_CHUNK 512
+#define PARTIAL_POW   9
 
 /****************
  * Fake a literal data packet and wait for the next armor line
@@ -477,37 +628,34 @@ fake_packet( armor_filter_context_t *afx, IOBUF a,
 {
     int rc = 0;
     size_t len = 0;
-    unsigned emplines = afx->empty;
     int lastline = 0;
     unsigned maxlen, n;
     byte *p;
+    byte tempbuf[PARTIAL_CHUNK];
+    size_t tempbuf_len=0;
 
-    len = 2;   /* reserve 2 bytes for the length header */
-    size -= 3; /* and 1 for empline handling and 2 for the term header */
-               /* or the appended CR,LF */
-    while( !rc && len < size ) {
-       if( emplines ) {
-           while( emplines && len < size ) {
-               buf[len++] = '\r';
-               buf[len++] = '\n';
-               emplines--;
-           }
-           continue;
-       }
-
+    while( !rc && size-len>=(PARTIAL_CHUNK+1)) {
+       /* copy what we have in the line buffer */
        if( afx->faked == 1 )
-           afx->faked++;  /* skip the first (empty) line */
-       else {
-           while( len < size && afx->buffer_pos < afx->buffer_len )
-               buf[len++] = afx->buffer[afx->buffer_pos++];
-           if( afx->buffer_pos >= afx->buffer_len
-               && !afx->not_dash_escaped ) {
-               buf[len++] = '\r';
-               buf[len++] = '\n';
-           }
-           if( len >= size )
+           afx->faked++; /* skip the first (empty) line */
+       else
+         {
+           /* It's full, so write this partial chunk */
+           if(tempbuf_len==PARTIAL_CHUNK)
+             {
+               buf[len++]=0xE0+PARTIAL_POW;
+               memcpy(&buf[len],tempbuf,PARTIAL_CHUNK);
+               len+=PARTIAL_CHUNK;
+               tempbuf_len=0;
                continue;
-       }
+             }
+
+           while( tempbuf_len < PARTIAL_CHUNK
+                  && afx->buffer_pos < afx->buffer_len )
+             tempbuf[tempbuf_len++] = afx->buffer[afx->buffer_pos++];
+           if( tempbuf_len==PARTIAL_CHUNK )
+             continue;
+         }
 
        /* read the next line */
        maxlen = MAX_LINELEN;
@@ -515,49 +663,97 @@ fake_packet( armor_filter_context_t *afx, IOBUF a,
        afx->buffer_len = iobuf_read_line( a, &afx->buffer,
                                           &afx->buffer_size, &maxlen );
        if( !afx->buffer_len ) {
-           rc = -1; /* eof */
+           rc = -1; /* eof (should not happen) */
            continue;
        }
        if( !maxlen )
            afx->truncated++;
-       if( !afx->not_dash_escaped )
-           afx->buffer_len = trim_trailing_spaces( afx->buffer,
-                                                   afx->buffer_len );
+
        p = afx->buffer;
        n = afx->buffer_len;
 
-       if( n > 2 && *p == '-' ) {
-           /* check for dash escaped or armor header */
-           if( p[1] == ' ' && !afx->not_dash_escaped ) {
-               /* issue a warning if it is not regular encoded */
-               if( p[2] != '-' && !( n > 6 && !memcmp(p+2, "From ", 5))) {
-                   log_info(_("invalid dash escaped line: "));
-                   print_string( stderr, p, n, 0 );
-                   putc('\n', stderr);
-               }
-               afx->buffer_pos = 2; /* skip */
-           }
-           else if( n >= 15 &&  p[1] == '-' && p[2] == '-' && p[3] == '-' ) {
-               if( is_armor_header( p, n ) != BEGIN_SIGNATURE ) {
-                   log_info(_("unexpected armor:"));
-                   print_string( stderr, p, n, 0 );
-                   putc('\n', stderr);
-               }
-               lastline = 1;
-               if( len >= 2 && !afx->not_dash_escaped )
-                   len -= 2; /* remove the last CR,LF */
-               rc = -1;
-           }
-       }
+       /* Armor header or dash-escaped line? */
+       if(p[0]=='-')
+         {
+           /* 2440bis-10: When reversing dash-escaping, an
+              implementation MUST strip the string "- " if it occurs
+              at the beginning of a line, and SHOULD warn on "-" and
+              any character other than a space at the beginning of a
+              line.  */
+
+           if(p[1]==' ' && !afx->not_dash_escaped)
+             {
+               /* It's a dash-escaped line, so skip over the
+                  escape. */
+               afx->buffer_pos = 2;
+             }
+           else if(p[1]=='-' && p[2]=='-' && p[3]=='-' && p[4]=='-')
+             {
+               /* Five dashes in a row mean it's probably armor
+                  header. */
+               int type = is_armor_header( p, n );
+               if( afx->not_dash_escaped && type != BEGIN_SIGNATURE )
+                 ; /* this is okay */
+               else
+                 {
+                   if( type != BEGIN_SIGNATURE )
+                     {
+                       log_info(_("unexpected armor: "));
+                       es_write_sanitized (log_get_stream (), p, n,
+                                            NULL, NULL);
+                       log_printf ("\n");
+                     }
+
+                   lastline = 1;
+                   rc = -1;
+                 }
+             }
+           else if(!afx->not_dash_escaped)
+             {
+               /* Bad dash-escaping. */
+               log_info (_("invalid dash escaped line: "));
+               es_write_sanitized (log_get_stream (), p, n, NULL, NULL);
+               log_printf ("\n");
+             }
+         }
+
+       /* Now handle the end-of-line canonicalization */
+       if( !afx->not_dash_escaped )
+         {
+           int crlf = n > 1 && p[n-2] == '\r' && p[n-1]=='\n';
+
+           afx->buffer_len=
+             trim_trailing_chars( &p[afx->buffer_pos], n-afx->buffer_pos,
+                                  " \t\r\n");
+           afx->buffer_len+=afx->buffer_pos;
+           /* the buffer is always allocated with enough space to append
+            * the removed [CR], LF and a Nul
+            * The reason for this complicated procedure is to keep at least
+            * the original type of lineending - handling of the removed
+            * trailing spaces seems to be impossible in our method
+            * of faking a packet; either we have to use a temporary file
+            * or calculate the hash here in this module and somehow find
+            * a way to send the hash down the processing line (well, a special
+            * faked packet could do the job).
+            */
+           if( crlf )
+             afx->buffer[afx->buffer_len++] = '\r';
+           afx->buffer[afx->buffer_len++] = '\n';
+           afx->buffer[afx->buffer_len] = '\0';
+         }
     }
 
-    buf[0] = (len-2) >> 8;
-    buf[1] = (len-2);
     if( lastline ) { /* write last (ending) length header */
-       if( buf[0] || buf[1] ) { /* only if we have some text */
-           buf[len++] = 0;
-           buf[len++] = 0;
-       }
+        if(tempbuf_len<192)
+         buf[len++]=tempbuf_len;
+       else
+         {
+           buf[len++]=((tempbuf_len-192)/256) + 192;
+           buf[len++]=(tempbuf_len-192) % 256;
+         }
+       memcpy(&buf[len],tempbuf,tempbuf_len);
+       len+=tempbuf_len;
+
        rc = 0;
        afx->faked = 0;
        afx->in_cleartext = 0;
@@ -586,31 +782,39 @@ fake_packet( armor_filter_context_t *afx, IOBUF a,
            }
        }
        afx->inp_checked = 1;
-       afx->crc = CRCINIT;
+       gcry_md_reset (afx->crc_md);
        afx->idx = 0;
        afx->radbuf[0] = 0;
     }
 
-    afx->empty = emplines;
     *retn = len;
     return rc;
 }
 
 
+static int
+invalid_crc(void)
+{
+  if ( opt.ignore_crc_error )
+    return 0;
+  log_inc_errorcount();
+  return gpg_error (GPG_ERR_INV_ARMOR);
+}
+
 
 static int
 radix64_read( armor_filter_context_t *afx, IOBUF a, size_t *retn,
              byte *buf, size_t size )
 {
     byte val;
-    int c, c2;
+    int c;
+    u32 binc;
     int checkcrc=0;
     int rc = 0;
     size_t n = 0;
-    int  idx, i;
-    u32 crc;
+    int idx, onlypad=0;
+    int skip_fast = 0;
 
-    crc = afx->crc;
     idx = afx->idx;
     val = afx->radbuf[0];
     for( n=0; n < size; ) {
@@ -630,52 +834,184 @@ radix64_read( armor_filter_context_t *afx, IOBUF a, size_t *retn,
        }
 
       again:
+       binc = asctobin[0][c];
+
+       if( binc != 0xffffffffUL )
+         {
+           if( idx == 0 && skip_fast == 0
+               && afx->buffer_pos + (16 - 1) < afx->buffer_len
+               && n + 12 < size)
+             {
+               /* Fast path for radix64 to binary conversion.  */
+               u32 b0,b1,b2,b3;
+
+               /* Speculatively load 15 more input bytes.  */
+               b0 = binc << (3 * 6);
+               b0 |= asctobin[2][afx->buffer[afx->buffer_pos + 0]];
+               b0 |= asctobin[1][afx->buffer[afx->buffer_pos + 1]];
+               b0 |= asctobin[0][afx->buffer[afx->buffer_pos + 2]];
+               b1  = asctobin[3][afx->buffer[afx->buffer_pos + 3]];
+               b1 |= asctobin[2][afx->buffer[afx->buffer_pos + 4]];
+               b1 |= asctobin[1][afx->buffer[afx->buffer_pos + 5]];
+               b1 |= asctobin[0][afx->buffer[afx->buffer_pos + 6]];
+               b2  = asctobin[3][afx->buffer[afx->buffer_pos + 7]];
+               b2 |= asctobin[2][afx->buffer[afx->buffer_pos + 8]];
+               b2 |= asctobin[1][afx->buffer[afx->buffer_pos + 9]];
+               b2 |= asctobin[0][afx->buffer[afx->buffer_pos + 10]];
+               b3  = asctobin[3][afx->buffer[afx->buffer_pos + 11]];
+               b3 |= asctobin[2][afx->buffer[afx->buffer_pos + 12]];
+               b3 |= asctobin[1][afx->buffer[afx->buffer_pos + 13]];
+               b3 |= asctobin[0][afx->buffer[afx->buffer_pos + 14]];
+
+               /* Check if any of the input bytes were invalid. */
+               if( (b0 | b1 | b2 | b3) != 0xffffffffUL )
+                 {
+                   /* All 16 bytes are valid. */
+                   buf[n + 0] = b0 >> (2 * 8);
+                   buf[n + 1] = b0 >> (1 * 8);
+                   buf[n + 2] = b0 >> (0 * 8);
+                   buf[n + 3] = b1 >> (2 * 8);
+                   buf[n + 4] = b1 >> (1 * 8);
+                   buf[n + 5] = b1 >> (0 * 8);
+                   buf[n + 6] = b2 >> (2 * 8);
+                   buf[n + 7] = b2 >> (1 * 8);
+                   buf[n + 8] = b2 >> (0 * 8);
+                   buf[n + 9] = b3 >> (2 * 8);
+                   buf[n + 10] = b3 >> (1 * 8);
+                   buf[n + 11] = b3 >> (0 * 8);
+                   afx->buffer_pos += 16 - 1;
+                   n += 12;
+                   continue;
+                 }
+               else if( b0 == 0xffffffffUL )
+                 {
+                   /* byte[1..3] have invalid character(s).  Switch to slow
+                      path.  */
+                   skip_fast = 1;
+                 }
+               else if( b1 == 0xffffffffUL )
+                 {
+                   /* byte[4..7] have invalid character(s), first 4 bytes are
+                      valid.  */
+                   buf[n + 0] = b0 >> (2 * 8);
+                   buf[n + 1] = b0 >> (1 * 8);
+                   buf[n + 2] = b0 >> (0 * 8);
+                   afx->buffer_pos += 4 - 1;
+                   n += 3;
+                   skip_fast = 1;
+                   continue;
+                 }
+               else if( b2 == 0xffffffffUL )
+                 {
+                   /* byte[8..11] have invalid character(s), first 8 bytes are
+                      valid.  */
+                   buf[n + 0] = b0 >> (2 * 8);
+                   buf[n + 1] = b0 >> (1 * 8);
+                   buf[n + 2] = b0 >> (0 * 8);
+                   buf[n + 3] = b1 >> (2 * 8);
+                   buf[n + 4] = b1 >> (1 * 8);
+                   buf[n + 5] = b1 >> (0 * 8);
+                   afx->buffer_pos += 8 - 1;
+                   n += 6;
+                   skip_fast = 1;
+                   continue;
+                 }
+               else /*if( b3 == 0xffffffffUL )*/
+                 {
+                   /* byte[12..15] have invalid character(s), first 12 bytes
+                      are valid.  */
+                   buf[n + 0] = b0 >> (2 * 8);
+                   buf[n + 1] = b0 >> (1 * 8);
+                   buf[n + 2] = b0 >> (0 * 8);
+                   buf[n + 3] = b1 >> (2 * 8);
+                   buf[n + 4] = b1 >> (1 * 8);
+                   buf[n + 5] = b1 >> (0 * 8);
+                   buf[n + 6] = b2 >> (2 * 8);
+                   buf[n + 7] = b2 >> (1 * 8);
+                   buf[n + 8] = b2 >> (0 * 8);
+                   afx->buffer_pos += 12 - 1;
+                   n += 9;
+                   skip_fast = 1;
+                   continue;
+                 }
+             }
+
+           switch(idx)
+             {
+               case 0: val =  binc << 2; break;
+               case 1: val |= (binc>>4)&3; buf[n++]=val;val=(binc<<4)&0xf0;break;
+               case 2: val |= (binc>>2)&15; buf[n++]=val;val=(binc<<6)&0xc0;break;
+               case 3: val |= binc&0x3f; buf[n++] = val; break;
+             }
+           idx = (idx+1) % 4;
+
+           continue;
+         }
+
+       skip_fast = 0;
+
        if( c == '\n' || c == ' ' || c == '\r' || c == '\t' )
            continue;
        else if( c == '=' ) { /* pad character: stop */
            /* some mailers leave quoted-printable encoded characters
             * so we try to workaround this */
            if( afx->buffer_pos+2 < afx->buffer_len ) {
-               int c1, c2, c3;
-               c1 = afx->buffer[afx->buffer_pos];
-               c2 = afx->buffer[afx->buffer_pos+1];
-               c3 = afx->buffer[afx->buffer_pos+2];
-               if( isxdigit(c1) && isxdigit(c2) && strchr( "=\n\r\t ", c3 )) {
+               int cc1, cc2, cc3;
+               cc1 = afx->buffer[afx->buffer_pos];
+               cc2 = afx->buffer[afx->buffer_pos+1];
+               cc3 = afx->buffer[afx->buffer_pos+2];
+               if( isxdigit(cc1) && isxdigit(cc2)
+                                 && strchr( "=\n\r\t ", cc3 )) {
                    /* well it seems to be the case - adjust */
-                   c = isdigit(c1)? (c1 - '0'): (toupper(c1)-'A'+10);
+                   c = isdigit(cc1)? (cc1 - '0'): (ascii_toupper(cc1)-'A'+10);
                    c <<= 4;
-                   c |= isdigit(c2)? (c2 - '0'): (toupper(c2)-'A'+10);
+                   c |= isdigit(cc2)? (cc2 - '0'): (ascii_toupper(cc2)-'A'+10);
                    afx->buffer_pos += 2;
+                   afx->qp_detected = 1;
                    goto again;
                }
            }
 
+            /* Occasionally a bug MTA will leave the = escaped as
+               =3D.  If the 4 characters following that are valid
+               Radix64 characters and they are following by a new
+               line, assume that this is the case and skip the
+               3D.  */
+            if (afx->buffer_pos + 6 < afx->buffer_len
+                && afx->buffer[afx->buffer_pos + 0] == '3'
+                && afx->buffer[afx->buffer_pos + 1] == 'D'
+                && asctobin[0][afx->buffer[afx->buffer_pos + 2]] != 0xffffffffUL
+                && asctobin[0][afx->buffer[afx->buffer_pos + 3]] != 0xffffffffUL
+                && asctobin[0][afx->buffer[afx->buffer_pos + 4]] != 0xffffffffUL
+                && asctobin[0][afx->buffer[afx->buffer_pos + 5]] != 0xffffffffUL
+                && afx->buffer[afx->buffer_pos + 6] == '\n')
+              {
+                afx->buffer_pos += 2;
+                afx->qp_detected = 1;
+              }
+
+           if (!n)
+             onlypad = 1;
+
            if( idx == 1 )
                buf[n++] = val;
            checkcrc++;
            break;
        }
-       else if( (c = asctobin[(c2=c)]) == 255 ) {
-           log_error(_("invalid radix64 character %02x skipped\n"), c2);
+       else {
+           log_error(_("invalid radix64 character %02X skipped\n"), c);
            continue;
        }
-       switch(idx) {
-         case 0: val =  c << 2; break;
-         case 1: val |= (c>>4)&3; buf[n++]=val;val=(c<<4)&0xf0;break;
-         case 2: val |= (c>>2)&15; buf[n++]=val;val=(c<<6)&0xc0;break;
-         case 3: val |= c&0x3f; buf[n++] = val; break;
-       }
-       idx = (idx+1) % 4;
     }
 
-    for(i=0; i < n; i++ )
-       crc = (crc << 8) ^ crc_table[((crc >> 16)&0xff) ^ buf[i]];
-    crc &= 0x00ffffff;
-    afx->crc = crc;
     afx->idx = idx;
     afx->radbuf[0] = val;
 
+    if( n )
+      gcry_md_write (afx->crc_md, buf, n);
+
     if( checkcrc ) {
+       gcry_md_final (afx->crc_md);
        afx->any_data = 1;
        afx->inp_checked=0;
        afx->faked = 0;
@@ -698,19 +1034,19 @@ radix64_read( armor_filter_context_t *afx, IOBUF a, size_t *retn,
                continue;
            break;
        }
-       if( c == -1 )
+       if( !afx->buffer_len )
            log_error(_("premature eof (no CRC)\n"));
        else {
            u32 mycrc = 0;
            idx = 0;
            do {
-               if( (c = asctobin[c]) == 255 )
+               if( (binc = asctobin[0][c]) == 0xffffffffUL )
                    break;
                switch(idx) {
-                 case 0: val =  c << 2; break;
-                 case 1: val |= (c>>4)&3; mycrc |= val << 16;val=(c<<4)&0xf0;break;
-                 case 2: val |= (c>>2)&15; mycrc |= val << 8;val=(c<<6)&0xc0;break;
-                 case 3: val |= c&0x3f; mycrc |= val; break;
+                 case 0: val =  binc << 2; break;
+                 case 1: val |= (binc>>4)&3; mycrc |= val << 16;val=(binc<<4)&0xf0;break;
+                 case 2: val |= (binc>>2)&15; mycrc |= val << 8;val=(binc<<6)&0xc0;break;
+                 case 3: val |= binc&0x3f; mycrc |= val; break;
                }
                for(;;) {
                    if( afx->buffer_pos < afx->buffer_len )
@@ -732,22 +1068,29 @@ radix64_read( armor_filter_context_t *afx, IOBUF a, size_t *retn,
                if( !afx->buffer_len )
                    break; /* eof */
            } while( ++idx < 4 );
-           if( c == -1 ) {
-               log_error(_("premature eof (in CRC)\n"));
-               rc = G10ERR_INVALID_ARMOR;
+           if( !afx->buffer_len ) {
+               log_info(_("premature eof (in CRC)\n"));
+               rc = invalid_crc();
+           }
+           else if( idx == 0 ) {
+               /* No CRC at all is legal ("MAY") */
+               rc=0;
            }
            else if( idx != 4 ) {
-               log_error(_("malformed CRC\n"));
-               rc = G10ERR_INVALID_ARMOR;
+               log_info(_("malformed CRC\n"));
+               rc = invalid_crc();
            }
-           else if( mycrc != afx->crc ) {
-               log_error(_("CRC error; %06lx - %06lx\n"),
-                                   (ulong)afx->crc, (ulong)mycrc);
-               rc = G10ERR_INVALID_ARMOR;
+           else if( mycrc != get_afx_crc (afx) ) {
+               log_info (_("CRC error; %06lX - %06lX\n"),
+                                   (ulong)get_afx_crc (afx), (ulong)mycrc);
+               rc = invalid_crc();
            }
            else {
                rc = 0;
-             #if 0
+                /* FIXME: Here we should emit another control packet,
+                 * so that we know in mainproc that we are processing
+                 * a clearsign message */
+#if 0
                for(rc=0;!rc;) {
                    rc = 0 /*check_trailer( &fhdr, c )*/;
                    if( !rc ) {
@@ -758,69 +1101,190 @@ 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"));
-                   rc = G10ERR_INVALID_ARMOR;
+                   log_error(_("premature eof (in trailer)\n"));
+                   rc = GPG_ERR_INVALID_ARMOR;
                }
                else {
                    log_error(_("error in trailer line\n"));
-                   rc = G10ERR_INVALID_ARMOR;
+                   rc = GPG_ERR_INVALID_ARMOR;
                }
-             #endif
+#endif
            }
        }
     }
 
-    if( !n )
+    if( !n && !onlypad )
        rc = -1;
 
     *retn = n;
     return rc;
 }
 
+static void
+armor_output_buf_as_radix64 (armor_filter_context_t *afx, IOBUF a,
+                            byte *buf, size_t size)
+{
+  byte radbuf[sizeof (afx->radbuf)];
+  byte outbuf[64 + sizeof (afx->eol)];
+  unsigned int eollen = strlen (afx->eol);
+  u32 in, in2;
+  int idx, idx2;
+  int i;
+
+  idx = afx->idx;
+  idx2 = afx->idx2;
+  memcpy (radbuf, afx->radbuf, sizeof (afx->radbuf));
+
+  if (size && (idx || idx2))
+    {
+      /* preload eol to outbuf buffer */
+      memcpy (outbuf + 4, afx->eol, sizeof (afx->eol));
+
+      for (; size && (idx || idx2); buf++, size--)
+       {
+         radbuf[idx++] = *buf;
+         if (idx > 2)
+           {
+             idx = 0;
+             in = (u32)radbuf[0] << (2 * 8);
+             in |= (u32)radbuf[1] << (1 * 8);
+             in |= (u32)radbuf[2] << (0 * 8);
+             outbuf[0] = bintoasc[(in >> 18) & 077];
+             outbuf[1] = bintoasc[(in >> 12) & 077];
+             outbuf[2] = bintoasc[(in >> 6) & 077];
+             outbuf[3] = bintoasc[(in >> 0) & 077];
+             if (++idx2 >= (64/4))
+               { /* pgp doesn't like 72 here */
+                 idx2=0;
+                 iobuf_write (a, outbuf, 4 + eollen);
+               }
+             else
+               {
+                 iobuf_write (a, outbuf, 4);
+               }
+           }
+       }
+    }
+
+  if (size >= (64/4)*3)
+    {
+      /* preload eol to outbuf buffer */
+      memcpy (outbuf + 64, afx->eol, sizeof(afx->eol));
+
+      do
+       {
+         /* idx and idx2 == 0 */
+
+         for (i = 0; i < (64/8); i++)
+           {
+             in = (u32)buf[0] << (2 * 8);
+             in |= (u32)buf[1] << (1 * 8);
+             in |= (u32)buf[2] << (0 * 8);
+             in2 = (u32)buf[3] << (2 * 8);
+             in2 |= (u32)buf[4] << (1 * 8);
+             in2 |= (u32)buf[5] << (0 * 8);
+             outbuf[i*8+0] = bintoasc[(in >> 18) & 077];
+             outbuf[i*8+1] = bintoasc[(in >> 12) & 077];
+             outbuf[i*8+2] = bintoasc[(in >> 6) & 077];
+             outbuf[i*8+3] = bintoasc[(in >> 0) & 077];
+             outbuf[i*8+4] = bintoasc[(in2 >> 18) & 077];
+             outbuf[i*8+5] = bintoasc[(in2 >> 12) & 077];
+             outbuf[i*8+6] = bintoasc[(in2 >> 6) & 077];
+             outbuf[i*8+7] = bintoasc[(in2 >> 0) & 077];
+             buf+=6;
+             size-=6;
+           }
+
+         /* pgp doesn't like 72 here */
+         iobuf_write (a, outbuf, 64 + eollen);
+       }
+      while (size >= (64/4)*3);
+
+      /* restore eol for tail handling */
+      if (size)
+       memcpy (outbuf + 4, afx->eol, sizeof (afx->eol));
+    }
+
+  for (; size; buf++, size--)
+    {
+      radbuf[idx++] = *buf;
+      if (idx > 2)
+       {
+         idx = 0;
+         in = (u32)radbuf[0] << (2 * 8);
+         in |= (u32)radbuf[1] << (1 * 8);
+         in |= (u32)radbuf[2] << (0 * 8);
+         outbuf[0] = bintoasc[(in >> 18) & 077];
+         outbuf[1] = bintoasc[(in >> 12) & 077];
+         outbuf[2] = bintoasc[(in >> 6) & 077];
+         outbuf[3] = bintoasc[(in >> 0) & 077];
+         if (++idx2 >= (64/4))
+           { /* pgp doesn't like 72 here */
+             idx2=0;
+             iobuf_write (a, outbuf, 4 + eollen);
+           }
+         else
+           {
+             iobuf_write (a, outbuf, 4);
+           }
+       }
+    }
+
+  memcpy (afx->radbuf, radbuf, sizeof (afx->radbuf));
+  afx->idx = idx;
+  afx->idx2 = idx2;
+}
+
 /****************
  * This filter is used to handle the armor stuff
  */
-int
+static int
 armor_filter( void *opaque, int control,
             IOBUF a, byte *buf, size_t *ret_len)
 {
     size_t size = *ret_len;
     armor_filter_context_t *afx = opaque;
-    int rc=0, i, c;
+    int rc=0, c;
     byte radbuf[3];
     int  idx, idx2;
     size_t n=0;
     u32 crc;
-  #if 0
+#if 0
     static FILE *fp ;
 
     if( !fp ) {
        fp = fopen("armor.out", "w");
        assert(fp);
     }
-  #endif
+#endif
 
     if( DBG_FILTER )
        log_debug("armor-filter: control: %d\n", control );
     if( control == IOBUFCTRL_UNDERFLOW && afx->inp_bypass ) {
        n = 0;
        if( afx->buffer_len ) {
+            /* Copy the data from AFX->BUFFER to BUF.  */
            for(; n < size && afx->buffer_pos < afx->buffer_len; n++ )
                buf[n++] = afx->buffer[afx->buffer_pos++];
            if( afx->buffer_pos >= afx->buffer_len )
                afx->buffer_len = 0;
        }
+        /* If there is still space in BUF, read directly into it.  */
        for(; n < size; n++ ) {
            if( (c=iobuf_get(a)) == -1 )
                break;
            buf[n] = c & 0xff;
        }
        if( !n )
+            /* We didn't get any data.  EOF.  */
            rc = -1;
        *ret_len = n;
     }
     else if( control == IOBUFCTRL_UNDERFLOW ) {
-       if( size < 15+(4*15) )  /* need space for up to 4 onepass_sigs */
+        /* We need some space for the faked packet.  The minmum
+         * required size is the PARTIAL_CHUNK size plus a byte for the
+         * length itself */
+       if( size < PARTIAL_CHUNK+1 )
            BUG(); /* supplied buffer too short */
 
        if( afx->faked )
@@ -828,7 +1292,7 @@ armor_filter( void *opaque, int control,
        else if( !afx->inp_checked ) {
            rc = check_input( afx, a );
            if( afx->inp_bypass ) {
-               for(n=0; n < size && afx->buffer_pos < afx->buffer_len; n++ )
+               for(n=0; n < size && afx->buffer_pos < afx->buffer_len; )
                    buf[n++] = afx->buffer[afx->buffer_pos++];
                if( afx->buffer_pos >= afx->buffer_len )
                    afx->buffer_len = 0;
@@ -836,51 +1300,52 @@ armor_filter( void *opaque, int control,
                    rc = -1;
            }
            else if( afx->faked ) {
-               unsigned hashes = afx->hashes;
+               unsigned int hashes = afx->hashes;
+                const byte *sesmark;
+                size_t sesmarklen;
+
+                sesmark = get_session_marker( &sesmarklen );
+                if ( sesmarklen > 20 )
+                    BUG();
+
                /* the buffer is at least 15+n*15 bytes long, so it
                 * is easy to construct the packets */
 
-               hashes &= 1|2|4|8;
-               if( !hashes )
-                   hashes |= 4;  /* default to MD 5 */
+               hashes &= 1|2|8|16|32|64;
+               if( !hashes ) {
+                   hashes |= 2;  /* Default to SHA-1. */
+               }
                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[n++] = 0xaf; /* old packet format, type 11, var length */
-               buf[n++] = 0;    /* set the length header */
-               buf[n++] = 6;
+                /* First a gpg control packet... */
+                buf[n++] = 0xff; /* new format, type 63, 1 length byte */
+                n++;   /* see below */
+                memcpy(buf+n, sesmark, sesmarklen ); n+= sesmarklen;
+                buf[n++] = CTRLPKT_CLEARSIGN_START;
+                buf[n++] = afx->not_dash_escaped? 0:1; /* sigclass */
+                if( hashes & 1 )
+                    buf[n++] = DIGEST_ALGO_RMD160;
+                if( hashes & 2 )
+                    buf[n++] = DIGEST_ALGO_SHA1;
+                if( hashes & 8 )
+                    buf[n++] = DIGEST_ALGO_SHA224;
+                if( hashes & 16 )
+                    buf[n++] = DIGEST_ALGO_SHA256;
+                if( hashes & 32 )
+                    buf[n++] = DIGEST_ALGO_SHA384;
+                if( hashes & 64 )
+                    buf[n++] = DIGEST_ALGO_SHA512;
+                buf[1] = n - 2;
+
+               /* ...followed by an invented plaintext packet.
+                  Amusingly enough, this packet is not compliant with
+                  2440 as the initial partial length is less than 512
+                  bytes.  Of course, we'll accept it anyway ;) */
+
+               buf[n++] = 0xCB; /* new packet format, type 11 */
+               buf[n++] = 0xE1; /* 2^1 == 2 bytes */
                buf[n++] = 't';  /* canonical text mode */
                buf[n++] = 0;    /* namelength */
+               buf[n++] = 0xE2; /* 2^2 == 4 more bytes */
                memset(buf+n, 0, 4); /* timestamp */
                n += 4;
            }
@@ -889,118 +1354,140 @@ armor_filter( void *opaque, int control,
        }
        else
            rc = radix64_read( afx, a, &n, buf, size );
-      #if 0
+#if 0
        if( n )
            if( fwrite(buf, n, 1, fp ) != 1 )
                BUG();
-      #endif
+#endif
        *ret_len = n;
     }
-    else if( control == IOBUFCTRL_FLUSH ) {
+    else if( control == IOBUFCTRL_FLUSH && !afx->cancel ) {
        if( !afx->status ) { /* write the header line */
+           const char *s;
+           strlist_t comment=opt.comments;
+
            if( afx->what >= DIM(head_strings) )
                log_bug("afx->what=%d", afx->what);
            iobuf_writestr(a, "-----");
            iobuf_writestr(a, head_strings[afx->what] );
-           iobuf_writestr(a, "-----\n");
-           iobuf_writestr(a, "Version: GnuPG v"  VERSION " ("
-                                           PRINTABLE_OS_NAME ")\n");
-
-           if( opt.comment_string ) {
-               const char *s = opt.comment_string;
+           iobuf_writestr(a, "-----" );
+           iobuf_writestr(a,afx->eol);
+           if (opt.emit_version)
+             {
+               iobuf_writestr (a, "Version: "GNUPG_NAME" v");
+                for (s=VERSION; *s && *s != '.'; s++)
+                  iobuf_writebyte (a, *s);
+                if (opt.emit_version > 1 && *s)
+                  {
+                    iobuf_writebyte (a, *s++);
+                    for (; *s && *s != '.'; s++)
+                      iobuf_writebyte (a, *s);
+                    if (opt.emit_version > 2)
+                      {
+                        for (; *s && *s != '-' && !spacep (s); s++)
+                          iobuf_writebyte (a, *s);
+                        if (opt.emit_version > 3)
+                          iobuf_writestr (a, " (" PRINTABLE_OS_NAME ")");
+                      }
+                  }
+               iobuf_writestr(a,afx->eol);
+             }
+
+           /* write the comment strings */
+           for(s=comment->d;comment;comment=comment->next,s=comment->d)
+             {
                iobuf_writestr(a, "Comment: " );
-               for( ; *s; s++ ) {
+               for( ; *s; s++ )
+                 {
                    if( *s == '\n' )
-                       iobuf_writestr(a, "\\n" );
+                     iobuf_writestr(a, "\\n" );
                    else if( *s == '\r' )
-                       iobuf_writestr(a, "\\r" );
+                     iobuf_writestr(a, "\\r" );
                    else if( *s == '\v' )
-                       iobuf_writestr(a, "\\v" );
+                     iobuf_writestr(a, "\\v" );
                    else
-                       iobuf_put(a, *s );
-               }
-               iobuf_put(a, '\n' );
-           }
-           else
-               iobuf_writestr(a,
-                   "Comment: For info see http://www.gnupg.org\n");
-           if( afx->hdrlines )
-               iobuf_writestr(a, afx->hdrlines);
-           iobuf_put(a, '\n');
+                     iobuf_put(a, *s );
+                 }
+
+               iobuf_writestr(a,afx->eol);
+             }
+
+           if ( afx->hdrlines ) {
+                for ( s = afx->hdrlines; *s; s++ ) {
+#ifdef HAVE_DOSISH_SYSTEM
+                    if ( *s == '\n' )
+                        iobuf_put( a, '\r');
+#endif
+                    iobuf_put(a, *s );
+                }
+            }
+
+           iobuf_writestr(a,afx->eol);
            afx->status++;
            afx->idx = 0;
            afx->idx2 = 0;
-           afx->crc = CRCINIT;
+           gcry_md_reset (afx->crc_md);
        }
-       crc = afx->crc;
-       idx = afx->idx;
-       idx2 = afx->idx2;
-       for(i=0; i < idx; i++ )
-           radbuf[i] = afx->radbuf[i];
-
-       for(i=0; i < size; i++ )
-           crc = (crc << 8) ^ crc_table[((crc >> 16)&0xff) ^ buf[i]];
-       crc &= 0x00ffffff;
-
-       for( ; size; buf++, size-- ) {
-           radbuf[idx++] = *buf;
-           if( idx > 2 ) {
-               idx = 0;
-               c = bintoasc[(*radbuf >> 2) & 077];
-               iobuf_put(a, c);
-               c = bintoasc[(((*radbuf<<4)&060)|((radbuf[1] >> 4)&017))&077];
-               iobuf_put(a, c);
-               c = bintoasc[(((radbuf[1]<<2)&074)|((radbuf[2]>>6)&03))&077];
-               iobuf_put(a, c);
-               c = bintoasc[radbuf[2]&077];
-               iobuf_put(a, c);
-               if( ++idx2 >= (64/4) ) { /* pgp doesn't like 72 here */
-                   iobuf_put(a, '\n');
-                   idx2=0;
-               }
-           }
-       }
-       for(i=0; i < idx; i++ )
-           afx->radbuf[i] = radbuf[i];
-       afx->idx = idx;
-       afx->idx2 = idx2;
-       afx->crc  = crc;
+
+       if( size ) {
+           gcry_md_write (afx->crc_md, buf, size);
+           armor_output_buf_as_radix64 (afx, a, buf, size);
+        }
     }
-    else if( control == IOBUFCTRL_INIT ) {
+    else if( control == IOBUFCTRL_INIT )
+      {
        if( !is_initialized )
-           initialize();
+         initialize();
+
+       /* Figure out what we're using for line endings if the caller
+          didn't specify. */
+       if(afx->eol[0]==0)
+         {
+#ifdef HAVE_DOSISH_SYSTEM
+           afx->eol[0]='\r';
+           afx->eol[1]='\n';
+#else
+           afx->eol[0]='\n';
+#endif
+         }
+      }
+    else if( control == IOBUFCTRL_CANCEL ) {
+       afx->cancel = 1;
     }
     else if( control == IOBUFCTRL_FREE ) {
-       if( afx->status ) { /* pad, write cecksum, and bottom line */
-           crc = afx->crc;
+       if( afx->cancel )
+           ;
+       else if( afx->status ) { /* pad, write cecksum, and bottom line */
+           gcry_md_final (afx->crc_md);
+           crc = get_afx_crc (afx);
            idx = afx->idx;
            idx2 = afx->idx2;
-           for(i=0; i < idx; i++ )
-               radbuf[i] = afx->radbuf[i];
            if( idx ) {
-               c = bintoasc[(*radbuf>>2)&077];
+               c = bintoasc[(afx->radbuf[0]>>2)&077];
                iobuf_put(a, c);
                if( idx == 1 ) {
-                   c = bintoasc[((*radbuf << 4) & 060) & 077];
+                   c = bintoasc[((afx->radbuf[0] << 4) & 060) & 077];
                    iobuf_put(a, c);
                    iobuf_put(a, '=');
                    iobuf_put(a, '=');
                }
                else { /* 2 */
-                   c = bintoasc[(((*radbuf<<4)&060)|((radbuf[1]>>4)&017))&077];
+                   c = bintoasc[(((afx->radbuf[0]<<4)&060)
+                                  |((afx->radbuf[1]>>4)&017))&077];
                    iobuf_put(a, c);
-                   c = bintoasc[((radbuf[1] << 2) & 074) & 077];
+                   c = bintoasc[((afx->radbuf[1] << 2) & 074) & 077];
                    iobuf_put(a, c);
                    iobuf_put(a, '=');
                }
-               if( ++idx2 >= (64/4) ) { /* pgp doesn't like 72 here */
-                   iobuf_put(a, '\n');
+               if( ++idx2 >= (64/4) )
+                 { /* pgp doesn't like 72 here */
+                   iobuf_writestr(a,afx->eol);
                    idx2=0;
-               }
+                 }
            }
            /* may need a linefeed */
            if( idx2 )
-               iobuf_put(a, '\n');
+             iobuf_writestr(a,afx->eol);
            /* write the CRC */
            iobuf_put(a, '=');
            radbuf[0] = crc >>16;
@@ -1014,26 +1501,61 @@ armor_filter( void *opaque, int control,
            iobuf_put(a, c);
            c = bintoasc[radbuf[2]&077];
            iobuf_put(a, c);
-           iobuf_put(a, '\n');
-           /* and the the trailer */
+           iobuf_writestr(a,afx->eol);
+           /* and the trailer */
            if( afx->what >= DIM(tail_strings) )
                log_bug("afx->what=%d", afx->what);
            iobuf_writestr(a, "-----");
            iobuf_writestr(a, tail_strings[afx->what] );
-           iobuf_writestr(a, "-----\n");
+           iobuf_writestr(a, "-----" );
+           iobuf_writestr(a,afx->eol);
        }
-       else if( !afx->any_data && !afx->inp_bypass )
+       else if( !afx->any_data && !afx->inp_bypass ) {
            log_error(_("no valid OpenPGP data found.\n"));
+           afx->no_openpgp_data = 1;
+           write_status_text( STATUS_NODATA, "1" );
+       }
        if( afx->truncated )
            log_info(_("invalid armor: line longer than %d characters\n"),
                      MAX_LINELEN );
-       m_free( afx->buffer );
+       /* issue an error to enforce dissemination of correct software */
+       if( afx->qp_detected )
+           log_error(_("quoted printable character in armor - "
+                       "probably a buggy MTA has been used\n") );
+       xfree( afx->buffer );
        afx->buffer = NULL;
+        release_armor_context (afx);
     }
     else if( control == IOBUFCTRL_DESC )
-       *(char**)buf = "armor_filter";
+        mem2str (buf, "armor_filter", *ret_len);
     return rc;
 }
 
 
-
+/****************
+ * create a radix64 encoded string.
+ */
+char *
+make_radix64_string( const byte *data, size_t len )
+{
+    char *buffer, *p;
+
+    buffer = p = xmalloc( (len+2)/3*4 + 1 );
+    for( ; len >= 3 ; len -= 3, data += 3 ) {
+       *p++ = bintoasc[(data[0] >> 2) & 077];
+       *p++ = bintoasc[(((data[0] <<4)&060)|((data[1] >> 4)&017))&077];
+       *p++ = bintoasc[(((data[1]<<2)&074)|((data[2]>>6)&03))&077];
+       *p++ = bintoasc[data[2]&077];
+    }
+    if( len == 2 ) {
+       *p++ = bintoasc[(data[0] >> 2) & 077];
+       *p++ = bintoasc[(((data[0] <<4)&060)|((data[1] >> 4)&017))&077];
+       *p++ = bintoasc[((data[1]<<2)&074)];
+    }
+    else if( len == 1 ) {
+       *p++ = bintoasc[(data[0] >> 2) & 077];
+       *p++ = bintoasc[(data[0] <<4)&060];
+    }
+    *p = 0;
+    return buffer;
+}