* keygen.c (proc_parameter_file): Default key and subkey usage flags to
[gnupg.git] / g10 / encode.c
index 6727c08..5bbe7cf 100644 (file)
@@ -1,5 +1,6 @@
 /* encode.c - encode data
- * Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc.
+ * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004,
+ *               2005 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
@@ -15,7 +16,8 @@
  *
  * 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
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
  */
 
 #include <config.h>
 #include "i18n.h"
 #include "status.h"
 
-static int encode_simple( const char *filename, int mode, int compat );
+static int encode_simple( const char *filename, int mode, int use_seskey );
 static int write_pubkey_enc_from_list( PK_LIST pk_list, DEK *dek, IOBUF out );
 
-
-
 /****************
  * Encode FILENAME with only the symmetric cipher.  Take input from
  * stdin if FILENAME is NULL.
@@ -50,17 +50,7 @@ static int write_pubkey_enc_from_list( PK_LIST pk_list, DEK *dek, IOBUF out );
 int
 encode_symmetric( const char *filename )
 {
-    int compat = 1;
-
-#if 0    
-    /* We don't want to use it because older gnupg version can't
-       handle it and we can presume that a lot of scripts are running
-       with the expert mode set.  Some time in the future we might
-       want to allow for it. */
-    if ( opt.expert )
-        compat = 0; /* PGP knows how to handle this mode. */
-#endif
-    return encode_simple( filename, 1, compat );
+    return encode_simple( filename, 1, 0 );
 }
 
 /****************
@@ -70,42 +60,46 @@ encode_symmetric( const char *filename )
 int
 encode_store( const char *filename )
 {
-    return encode_simple( filename, 0, 1 );
+    return encode_simple( filename, 0, 0 );
 }
 
 static void
-encode_sesskey( DEK *dek, DEK **ret_dek, byte *enckey )
+encode_seskey( DEK *dek, DEK **seskey, byte *enckey )
 {
     CIPHER_HANDLE hd;
-    DEK *c;
     byte buf[33];
 
-    assert ( dek->keylen < 32 );
-    
-    c = m_alloc_clear( sizeof *c );
-    c->keylen = dek->keylen;
-    c->algo = dek->algo;
-    make_session_key( c );
-    /*log_hexdump( "thekey", c->key, c->keylen );*/
-
-    buf[0] = c->algo;
-    memcpy( buf + 1, c->key, c->keylen );
+    assert ( dek->keylen <= 32 );
+    if(!*seskey)
+      {
+       *seskey=xmalloc_clear(sizeof(DEK));
+       (*seskey)->keylen=dek->keylen;
+       (*seskey)->algo=dek->algo;
+       make_session_key(*seskey);
+       /*log_hexdump( "thekey", c->key, c->keylen );*/
+      }
+
+    buf[0] = (*seskey)->algo;
+    memcpy( buf + 1, (*seskey)->key, (*seskey)->keylen );
     
     hd = cipher_open( dek->algo, CIPHER_MODE_CFB, 1 );
     cipher_setkey( hd, dek->key, dek->keylen );
     cipher_setiv( hd, NULL, 0 );
-    cipher_encrypt( hd, buf, buf, c->keylen + 1 );
+    cipher_encrypt( hd, buf, buf, (*seskey)->keylen + 1 );
     cipher_close( hd );
 
-    memcpy( enckey, buf, c->keylen + 1 );
+    memcpy( enckey, buf, (*seskey)->keylen + 1 );
     wipememory( buf, sizeof buf ); /* burn key */
-    *ret_dek = c;
 }
 
 /* We try very hard to use a MDC */
 static int
 use_mdc(PK_LIST pk_list,int algo)
 {
+  /* RFC-1991 and 2440 don't have MDC */
+  if(RFC1991 || RFC2440)
+    return 0;
+
   /* --force-mdc overrides --disable-mdc */
   if(opt.force_mdc)
     return 1;
@@ -148,12 +142,15 @@ use_mdc(PK_LIST pk_list,int algo)
   return 0; /* No MDC */
 }
 
+/* We don't want to use use_seskey yet because older gnupg versions
+   can't handle it, and there isn't really any point unless we're
+   making a message that can be decrypted by a public key or
+   passphrase. */
 static int
-encode_simple( const char *filename, int mode, int compat )
+encode_simple( const char *filename, int mode, int use_seskey )
 {
     IOBUF inp, out;
     PACKET pkt;
-    DEK *dek = NULL;
     PKT_plaintext *pt = NULL;
     STRING2KEY *s2k = NULL;
     byte enckey[33];
@@ -164,7 +161,8 @@ encode_simple( const char *filename, int mode, int compat )
     armor_filter_context_t afx;
     compress_filter_context_t zfx;
     text_filter_context_t tfx;
-    int do_compress = opt.compress && !opt.rfc1991;
+    progress_filter_context_t pfx;
+    int do_compress = !RFC1991 && default_compress_algo();
 
     memset( &cfx, 0, sizeof cfx);
     memset( &afx, 0, sizeof afx);
@@ -173,57 +171,71 @@ encode_simple( const char *filename, int mode, int compat )
     init_packet(&pkt);
     
     /* prepare iobufs */
-    if( !(inp = iobuf_open(filename)) ) {
-       log_error(_("%s: can't open: %s\n"), filename? filename: "[stdin]",
-                                       strerror(errno) );
+    inp = iobuf_open(filename);
+    if (inp)
+      iobuf_ioctl (inp,3,1,NULL); /* disable fd caching */
+    if (inp && is_secured_file (iobuf_get_fd (inp)))
+      {
+        iobuf_close (inp);
+        inp = NULL;
+        errno = EPERM;
+      }
+    if( !inp ) {
+       log_error(_("can't open `%s': %s\n"), filename? filename: "[stdin]",
+                  strerror(errno) );
        return G10ERR_OPEN_FILE;
     }
 
+    handle_progress (&pfx, inp, filename);
+
     if( opt.textmode )
        iobuf_push_filter( inp, text_filter, &tfx );
 
     /* Due the the fact that we use don't use an IV to encrypt the
        session key we can't use the new mode with RFC1991 because
        it has no S2K salt. RFC1991 always uses simple S2K. */
-    if ( opt.rfc1991 && !compat )
-        compat = 1;
+    if ( RFC1991 && use_seskey )
+        use_seskey = 0;
     
     cfx.dek = NULL;
     if( mode ) {
-       s2k = m_alloc_clear( sizeof *s2k );
-       s2k->mode = opt.rfc1991? 0:opt.s2k_mode;
-       s2k->hash_algo = opt.def_digest_algo ? opt.def_digest_algo
-                                            : opt.s2k_digest_algo;
+       s2k = xmalloc_clear( sizeof *s2k );
+       s2k->mode = RFC1991? 0:opt.s2k_mode;
+       s2k->hash_algo=S2K_DIGEST_ALGO;
        cfx.dek = passphrase_to_dek( NULL, 0,
-                  opt.def_cipher_algo ? opt.def_cipher_algo
-                                     : opt.s2k_cipher_algo , s2k, 2, NULL );
+                                    default_cipher_algo(), s2k, 2,
+                                     NULL, NULL);
        if( !cfx.dek || !cfx.dek->keylen ) {
            rc = G10ERR_PASSPHRASE;
-           m_free(cfx.dek);
-           m_free(s2k);
+           xfree(cfx.dek);
+           xfree(s2k);
            iobuf_close(inp);
            log_error(_("error creating passphrase: %s\n"), g10_errstr(rc) );
            return rc;
        }
-        if (!compat && s2k->mode != 1 && s2k->mode != 3) {
-            compat = 1;
+        if (use_seskey && s2k->mode != 1 && s2k->mode != 3) {
+            use_seskey = 0;
             log_info (_("can't use a symmetric ESK packet "
                         "due to the S2K mode\n"));
         }
 
-        if ( !compat ) {            
-            seskeylen = cipher_get_keylen( opt.def_cipher_algo ?
-                                           opt.def_cipher_algo:
-                                           opt.s2k_cipher_algo ) / 8;
-            encode_sesskey( cfx.dek, &dek, enckey );
-            m_free( cfx.dek ); cfx.dek = dek;
-        }
+        if ( use_seskey )
+         {
+           DEK *dek = NULL;
+            seskeylen = cipher_get_keylen( default_cipher_algo() ) / 8;
+            encode_seskey( cfx.dek, &dek, enckey );
+            xfree( cfx.dek ); cfx.dek = dek;
+         }
+
+       if(opt.verbose)
+         log_info(_("using cipher %s\n"),
+                  cipher_algo_to_string(cfx.dek->algo));
 
        cfx.dek->use_mdc=use_mdc(NULL,cfx.dek->algo);
     }
 
-    if (opt.compress == -1 && cfx.dek && cfx.dek->use_mdc &&
-       is_file_compressed(filename, &rc))
+    if (do_compress && cfx.dek && cfx.dek->use_mdc
+       && is_file_compressed(filename, &rc))
       {
         if (opt.verbose)
           log_info(_("`%s' already compressed\n"), filename);
@@ -232,27 +244,20 @@ encode_simple( const char *filename, int mode, int compat )
 
     if( rc || (rc = open_outfile( filename, opt.armor? 1:0, &out )) ) {
        iobuf_cancel(inp);
-       m_free(cfx.dek);
-       m_free(s2k);
+       xfree(cfx.dek);
+       xfree(s2k);
        return rc;
     }
 
     if( opt.armor )
        iobuf_push_filter( out, armor_filter, &afx );
-  #ifdef ENABLE_COMMENT_PACKETS
-    else {
-       write_comment( out, "#created by GNUPG v" VERSION " ("
-                                           PRINTABLE_OS_NAME ")");
-       if( opt.comment_string )
-           write_comment( out, opt.comment_string );
-    }
-  #endif
-    if( s2k && !opt.rfc1991 ) {
-       PKT_symkey_enc *enc = m_alloc_clear( sizeof *enc + seskeylen + 1 );
+
+    if( s2k && !RFC1991 ) {
+       PKT_symkey_enc *enc = xmalloc_clear( sizeof *enc + seskeylen + 1 );
        enc->version = 4;
        enc->cipher_algo = cfx.dek->algo;
        enc->s2k = *s2k;
-        if ( !compat && seskeylen ) {
+        if ( use_seskey && seskeylen ) {
             enc->seskeylen = seskeylen + 1; /* algo id */
             memcpy( enc->seskey, enckey, seskeylen + 1 );
         }
@@ -260,7 +265,7 @@ encode_simple( const char *filename, int mode, int compat )
        pkt.pkt.symkey_enc = enc;
        if( (rc = build_packet( out, &pkt )) )
            log_error("build symkey packet failed: %s\n", g10_errstr(rc) );
-       m_free(enc);
+       xfree(enc);
     }
 
     if (!opt.no_literal) {
@@ -269,13 +274,13 @@ encode_simple( const char *filename, int mode, int compat )
            char *s = make_basename( opt.set_filename ? opt.set_filename
                                                      : filename,
                                     iobuf_get_real_fname( inp ) );
-           pt = m_alloc( sizeof *pt + strlen(s) - 1 );
+           pt = xmalloc( sizeof *pt + strlen(s) - 1 );
            pt->namelen = strlen(s);
            memcpy(pt->name, s, pt->namelen );
-           m_free(s);
+           xfree(s);
        }
        else { /* no filename */
-           pt = m_alloc( sizeof *pt - 1 );
+           pt = xmalloc( sizeof *pt - 1 );
            pt->namelen = 0;
        }
     }
@@ -291,23 +296,31 @@ encode_simple( const char *filename, int mode, int compat )
        either partial length or fixed length with the new style
        messages. */
 
-    if( filename && !opt.textmode ) {
-       if( !(filesize = iobuf_get_filelength(inp)) )
-           log_info(_("%s: WARNING: empty file\n"), filename );
-        /* we can't yet encode the length of very large files,
-         * so we switch to partial lengthn encoding in this case */
-        if ( filesize >= IOBUF_FILELENGTH_LIMIT )
-            filesize = 0;
-
-    }
+    if ( !iobuf_is_pipe_filename (filename) && *filename && !opt.textmode )
+      {
+        off_t tmpsize;
+        int overflow;
+
+       if ( !(tmpsize = iobuf_get_filelength(inp, &overflow))
+             && !overflow )
+          log_info(_("WARNING: `%s' is an empty file\n"), filename );
+        /* We can't encode the length of very large files because
+           OpenPGP uses only 32 bit for file sizes.  So if the the
+           size of a file is larger than 2^32 minus some bytes for
+           packet headers, we switch to partial length encoding. */
+        if ( tmpsize < (IOBUF_FILELENGTH_LIMIT - 65536) )
+          filesize = tmpsize;
+        else
+          filesize = 0;
+      }
     else
-       filesize = opt.set_filesize ? opt.set_filesize : 0; /* stdin */
+      filesize = opt.set_filesize ? opt.set_filesize : 0; /* stdin */
 
     if (!opt.no_literal) {
        pt->timestamp = make_timestamp();
        pt->mode = opt.textmode? 't' : 'b';
        pt->len = filesize;
-       pt->new_ctb = !pt->len && !opt.rfc1991;
+       pt->new_ctb = !pt->len && !RFC1991;
        pt->buf = inp;
        pkt.pkttype = PKT_PLAINTEXT;
        pkt.pkt.plaintext = pt;
@@ -328,10 +341,7 @@ encode_simple( const char *filename, int mode, int compat )
       {
         if (cfx.dek && cfx.dek->use_mdc)
           zfx.new_ctb = 1;
-       zfx.algo=opt.def_compress_algo;
-       if(zfx.algo==-1)
-         zfx.algo=DEFAULT_COMPRESS_ALGO;
-       iobuf_push_filter( out, compress_filter, &zfx );
+       push_compress_filter(out,&zfx,default_compress_algo());
       }
 
     /* do the work */
@@ -365,30 +375,79 @@ encode_simple( const char *filename, int mode, int compat )
     if (pt)
        pt->buf = NULL;
     free_packet(&pkt);
-    m_free(cfx.dek);
-    m_free(s2k);
+    xfree(cfx.dek);
+    xfree(s2k);
     return rc;
 }
 
+int
+setup_symkey(STRING2KEY **symkey_s2k,DEK **symkey_dek)
+{
+  *symkey_s2k=xmalloc_clear(sizeof(STRING2KEY));
+  (*symkey_s2k)->mode = opt.s2k_mode;
+  (*symkey_s2k)->hash_algo = S2K_DIGEST_ALGO;
+
+  *symkey_dek=passphrase_to_dek(NULL,0,opt.s2k_cipher_algo,
+                               *symkey_s2k,2,NULL,NULL);
+  if(!*symkey_dek || !(*symkey_dek)->keylen)
+    {
+      xfree(*symkey_dek);
+      xfree(*symkey_s2k);
+      return G10ERR_PASSPHRASE;
+    }
+
+  return 0;
+}
+
+static int
+write_symkey_enc(STRING2KEY *symkey_s2k,DEK *symkey_dek,DEK *dek,IOBUF out)
+{
+  int rc,seskeylen=cipher_get_keylen(dek->algo)/8;
+
+  PKT_symkey_enc *enc;
+  byte enckey[33];
+  PACKET pkt;
+
+  enc=xmalloc_clear(sizeof(PKT_symkey_enc)+seskeylen+1);
+  encode_seskey(symkey_dek,&dek,enckey);
+
+  enc->version = 4;
+  enc->cipher_algo = opt.s2k_cipher_algo;
+  enc->s2k = *symkey_s2k;
+  enc->seskeylen = seskeylen + 1; /* algo id */
+  memcpy( enc->seskey, enckey, seskeylen + 1 );
+
+  pkt.pkttype = PKT_SYMKEY_ENC;
+  pkt.pkt.symkey_enc = enc;
+
+  if((rc=build_packet(out,&pkt)))
+    log_error("build symkey_enc packet failed: %s\n",g10_errstr(rc));
+
+  xfree(enc);
+  return rc;
+}
+
 /****************
  * Encrypt the file with the given userids (or ask if none
  * is supplied).
  */
 int
-encode_crypt( const char *filename, STRLIST remusr )
+encode_crypt( const char *filename, STRLIST remusr, int use_symkey )
 {
     IOBUF inp = NULL, out = NULL;
     PACKET pkt;
     PKT_plaintext *pt = NULL;
+    DEK *symkey_dek = NULL;
+    STRING2KEY *symkey_s2k = NULL;
     int rc = 0, rc2 = 0;
     u32 filesize;
     cipher_filter_context_t cfx;
     armor_filter_context_t afx;
     compress_filter_context_t zfx;
     text_filter_context_t tfx;
+    progress_filter_context_t pfx;
     PK_LIST pk_list,work_list;
-    int do_compress = opt.compress && !opt.rfc1991;
-
+    int do_compress = opt.compress_algo && !RFC1991;
 
     memset( &cfx, 0, sizeof cfx);
     memset( &afx, 0, sizeof afx);
@@ -396,25 +455,37 @@ encode_crypt( const char *filename, STRLIST remusr )
     memset( &tfx, 0, sizeof tfx);
     init_packet(&pkt);
 
+    if(use_symkey
+       && (rc=setup_symkey(&symkey_s2k,&symkey_dek)))
+      return rc;
+
     if( (rc=build_pk_list( remusr, &pk_list, PUBKEY_USAGE_ENC)) )
        return rc;
 
-    if(opt.pgp2) {
+    if(PGP2) {
       for(work_list=pk_list; work_list; work_list=work_list->next)
        if(!(is_RSA(work_list->pk->pubkey_algo) &&
             nbits_from_pk(work_list->pk)<=2048))
          {
            log_info(_("you can only encrypt to RSA keys of 2048 bits or "
                       "less in --pgp2 mode\n"));
-           log_info(_("this message may not be usable by %s\n"),"PGP 2.x");
-           opt.pgp2=0;
+           compliance_failure();
            break;
          }
     }
 
     /* prepare iobufs */
-    if( !(inp = iobuf_open(filename)) ) {
-       log_error(_("can't open %s: %s\n"), filename? filename: "[stdin]",
+    inp = iobuf_open(filename);
+    if (inp)
+      iobuf_ioctl (inp,3,1,NULL); /* disable fd caching */
+    if (inp && is_secured_file (iobuf_get_fd (inp)))
+      {
+        iobuf_close (inp);
+        inp = NULL;
+        errno = EPERM;
+      }
+    if( !inp ) {
+       log_error(_("can't open `%s': %s\n"), filename? filename: "[stdin]",
                                        strerror(errno) );
        rc = G10ERR_OPEN_FILE;
        goto leave;
@@ -422,25 +493,19 @@ encode_crypt( const char *filename, STRLIST remusr )
     else if( opt.verbose )
        log_info(_("reading from `%s'\n"), filename? filename: "[stdin]");
 
+    handle_progress (&pfx, inp, filename);
+
     if( opt.textmode )
        iobuf_push_filter( inp, text_filter, &tfx );
 
     if( (rc = open_outfile( filename, opt.armor? 1:0, &out )) )
        goto leave;
 
-
     if( opt.armor )
        iobuf_push_filter( out, armor_filter, &afx );
-  #ifdef ENABLE_COMMENT_PACKETS
-    else {
-       write_comment( out, "#created by GNUPG v" VERSION " ("
-                                           PRINTABLE_OS_NAME ")");
-       if( opt.comment_string )
-           write_comment( out, opt.comment_string );
-    }
-  #endif
+
     /* create a session key */
-    cfx.dek = m_alloc_secure_clear (sizeof *cfx.dek);
+    cfx.dek = xmalloc_secure_clear (sizeof *cfx.dek);
     if( !opt.def_cipher_algo ) { /* try to get it from the prefs */
        cfx.dek->algo = select_algo_from_prefs(pk_list,PREFTYPE_SYM,-1,NULL);
        /* The only way select_algo_from_prefs can fail here is when
@@ -453,11 +518,10 @@ encode_crypt( const char *filename, STRLIST remusr )
        if( cfx.dek->algo == -1 ) {
            cfx.dek->algo = CIPHER_ALGO_3DES;
 
-           if( opt.pgp2 ) {
+           if( PGP2 ) {
              log_info(_("unable to use the IDEA cipher for all of the keys "
                         "you are encrypting to.\n"));
-             log_info(_("this message may not be usable by %s\n"),"PGP 2.x");
-             opt.pgp2=0;
+             compliance_failure();
            }
        }
     }
@@ -465,8 +529,8 @@ encode_crypt( const char *filename, STRLIST remusr )
       if(!opt.expert &&
         select_algo_from_prefs(pk_list,PREFTYPE_SYM,
                                opt.def_cipher_algo,NULL)!=opt.def_cipher_algo)
-       log_info(_("forcing symmetric cipher %s (%d) "
-                  "violates recipient preferences\n"),
+       log_info(_("WARNING: forcing symmetric cipher %s (%d)"
+                  " violates recipient preferences\n"),
                 cipher_algo_to_string(opt.def_cipher_algo),
                 opt.def_cipher_algo);
 
@@ -480,8 +544,7 @@ encode_crypt( const char *filename, STRLIST remusr )
        not have a MDC to give some protection against chosen
        ciphertext attacks. */
 
-    if (opt.compress == -1 && cfx.dek->use_mdc &&
-       is_file_compressed(filename, &rc2) )
+    if (do_compress && cfx.dek->use_mdc && is_file_compressed(filename, &rc2) )
       {
         if (opt.verbose)
           log_info(_("`%s' already compressed\n"), filename);
@@ -501,39 +564,55 @@ encode_crypt( const char *filename, STRLIST remusr )
     if( rc  )
        goto leave;
 
+    /* We put the passphrase (if any) after any public keys as this
+       seems to be the most useful on the recipient side - there is no
+       point in prompting a user for a passphrase if they have the
+       secret key needed to decrypt. */
+    if(use_symkey && (rc=write_symkey_enc(symkey_s2k,symkey_dek,cfx.dek,out)))
+      goto leave;
+
     if (!opt.no_literal) {
        /* setup the inner packet */
        if( filename || opt.set_filename ) {
            char *s = make_basename( opt.set_filename ? opt.set_filename
                                                      : filename,
                                     iobuf_get_real_fname( inp ) );
-           pt = m_alloc( sizeof *pt + strlen(s) - 1 );
+           pt = xmalloc( sizeof *pt + strlen(s) - 1 );
            pt->namelen = strlen(s);
            memcpy(pt->name, s, pt->namelen );
-           m_free(s);
+           xfree(s);
        }
        else { /* no filename */
-           pt = m_alloc( sizeof *pt - 1 );
+           pt = xmalloc( sizeof *pt - 1 );
            pt->namelen = 0;
        }
     }
 
-    if( filename && !opt.textmode ) {
-       if( !(filesize = iobuf_get_filelength(inp)) )
-           log_info(_("%s: WARNING: empty file\n"), filename );
-        /* we can't yet encode the length of very large files,
-         * so we switch to partial length encoding in this case */
-        if ( filesize >= IOBUF_FILELENGTH_LIMIT )
-            filesize = 0;
-    }
+    if (!iobuf_is_pipe_filename (filename) && *filename && !opt.textmode )
+      {
+        off_t tmpsize;
+        int overflow;
+
+       if ( !(tmpsize = iobuf_get_filelength(inp, &overflow))
+             && !overflow )
+          log_info(_("WARNING: `%s' is an empty file\n"), filename );
+        /* We can't encode the length of very large files because
+           OpenPGP uses only 32 bit for file sizes.  So if the the
+           size of a file is larger than 2^32 minus some bytes for
+           packet headers, we switch to partial length encoding. */
+        if (tmpsize < (IOBUF_FILELENGTH_LIMIT - 65536) )
+          filesize = tmpsize;
+        else
+          filesize = 0;
+      }
     else
-       filesize = opt.set_filesize ? opt.set_filesize : 0; /* stdin */
+      filesize = opt.set_filesize ? opt.set_filesize : 0; /* stdin */
 
     if (!opt.no_literal) {
        pt->timestamp = make_timestamp();
        pt->mode = opt.textmode ? 't' : 'b';
        pt->len = filesize;
-       pt->new_ctb = !pt->len && !opt.rfc1991;
+       pt->new_ctb = !pt->len && !RFC1991;
        pt->buf = inp;
        pkt.pkttype = PKT_PLAINTEXT;
        pkt.pkt.plaintext = pt;
@@ -547,19 +626,21 @@ encode_crypt( const char *filename, STRLIST remusr )
 
     /* register the compress filter */
     if( do_compress ) {
-       int compr_algo = opt.def_compress_algo;
+       int compr_algo = opt.compress_algo;
 
        if(compr_algo==-1)
          {
            if((compr_algo=
                select_algo_from_prefs(pk_list,PREFTYPE_ZIP,-1,NULL))==-1)
              compr_algo=DEFAULT_COMPRESS_ALGO;
+           /* Theoretically impossible to get here since uncompressed
+              is implicit. */
          }
        else if(!opt.expert &&
                select_algo_from_prefs(pk_list,PREFTYPE_ZIP,
                                       compr_algo,NULL)!=compr_algo)
-         log_info(_("forcing compression algorithm %s (%d) "
-                    "violates recipient preferences\n"),
+         log_info(_("WARNING: forcing compression algorithm %s (%d)"
+                    " violates recipient preferences\n"),
                   compress_algo_to_string(compr_algo),compr_algo);
 
        /* algo 0 means no compression */
@@ -567,8 +648,7 @@ encode_crypt( const char *filename, STRLIST remusr )
          {
             if (cfx.dek && cfx.dek->use_mdc)
               zfx.new_ctb = 1;
-           zfx.algo = compr_algo;
-           iobuf_push_filter( out, compress_filter, &zfx );
+           push_compress_filter(out,&zfx,compr_algo);
          }
     }
 
@@ -604,7 +684,9 @@ encode_crypt( const char *filename, STRLIST remusr )
     if( pt )
        pt->buf = NULL;
     free_packet(&pkt);
-    m_free(cfx.dek);
+    xfree(cfx.dek);
+    xfree(symkey_dek);
+    xfree(symkey_s2k);
     release_pk_list( pk_list );
     return rc;
 }
@@ -628,7 +710,7 @@ encrypt_filter( void *opaque, int control,
     }
     else if( control == IOBUFCTRL_FLUSH ) { /* encrypt */
        if( !efx->header_okay ) {
-           efx->cfx.dek = m_alloc_secure_clear( sizeof *efx->cfx.dek );
+           efx->cfx.dek = xmalloc_secure_clear( sizeof *efx->cfx.dek );
 
            if( !opt.def_cipher_algo  ) { /* try to get it from the prefs */
                efx->cfx.dek->algo =
@@ -663,6 +745,14 @@ encrypt_filter( void *opaque, int control,
            if( rc )
                return rc;
 
+           if(efx->symkey_s2k && efx->symkey_dek)
+             {
+               rc=write_symkey_enc(efx->symkey_s2k,efx->symkey_dek,
+                                   efx->cfx.dek,a);
+               if(rc)
+                 return rc;
+             }
+
            iobuf_push_filter( a, cipher_filter, &efx->cfx );
 
            efx->header_okay = 1;
@@ -670,8 +760,11 @@ encrypt_filter( void *opaque, int control,
        rc = iobuf_write( a, buf, size );
 
     }
-    else if( control == IOBUFCTRL_FREE ) {
-    }
+    else if( control == IOBUFCTRL_FREE )
+      {
+       xfree(efx->symkey_dek);
+       xfree(efx->symkey_s2k);
+      }
     else if( control == IOBUFCTRL_DESC ) {
        *(char**)buf = "encrypt_filter";
     }
@@ -696,21 +789,16 @@ write_pubkey_enc_from_list( PK_LIST pk_list, DEK *dek, IOBUF out )
        pk = pk_list->pk;
 
        print_pubkey_algo_note( pk->pubkey_algo );
-       enc = m_alloc_clear( sizeof *enc );
+       enc = xmalloc_clear( sizeof *enc );
        enc->pubkey_algo = pk->pubkey_algo;
        keyid_from_pk( pk, enc->keyid );
-       enc->throw_keyid = opt.throw_keyid;
+       enc->throw_keyid = (opt.throw_keyid || (pk_list->flags&1));
 
-       if(opt.throw_keyid && (opt.pgp2 || opt.pgp6 || opt.pgp7))
+       if(opt.throw_keyid && (PGP2 || PGP6 || PGP7 || PGP8))
          {
            log_info(_("you may not use %s while in %s mode\n"),
-                    "--throw-keyid",
-                    opt.pgp2?"--pgp2":opt.pgp6?"--pgp6":"--pgp7");
-
-           log_info(_("this message may not be usable by %s\n"),
-                    opt.pgp2?"PGP 2.x":opt.pgp6?"PGP 6.x":"PGP 7.x");
-
-           opt.pgp2=opt.pgp6=opt.pgp7=0;
+                    "--throw-keyid",compliance_option_string());
+           compliance_failure();
          }
 
        /* Okay, what's going on: We have the session key somewhere in
@@ -722,7 +810,7 @@ write_pubkey_enc_from_list( PK_LIST pk_list, DEK *dek, IOBUF out )
         * algorithm number PK->PUBKEY_ALGO and pass it to pubkey_encrypt
         * which returns the encrypted value in the array ENC->DATA.
         * This array has a size which depends on the used algorithm
-        * (e.g. 2 for ElGamal).  We don't need frame anymore because we
+        * (e.g. 2 for Elgamal).  We don't need frame anymore because we
         * have everything now in enc->data which is the passed to
         * build_packet()
         */
@@ -734,11 +822,11 @@ write_pubkey_enc_from_list( PK_LIST pk_list, DEK *dek, IOBUF out )
            log_error("pubkey_encrypt failed: %s\n", g10_errstr(rc) );
        else {
            if( opt.verbose ) {
-               char *ustr = get_user_id_string_printable (enc->keyid);
+               char *ustr = get_user_id_string_native (enc->keyid);
                log_info(_("%s/%s encrypted for: \"%s\"\n"),
                    pubkey_algo_to_string(enc->pubkey_algo),
                    cipher_algo_to_string(dek->algo), ustr );
-               m_free(ustr);
+               xfree(ustr);
            }
            /* and write it */
            init_packet(&pkt);
@@ -780,8 +868,8 @@ encode_crypt_files(int nfiles, char **files, STRLIST remusr)
             }
           line[strlen(line)-1] = '\0';
           print_file_status(STATUS_FILE_START, line, 2);
-          if ( (rc = encode_crypt(line, remusr)) )
-            log_error("%s: encryption failed: %s\n",
+          if ( (rc = encode_crypt(line, remusr, 0)) )
+            log_error("encryption of `%s' failed: %s\n",
                       print_fname_stdin(line), g10_errstr(rc) );
           write_status( STATUS_FILE_DONE );
         }
@@ -791,8 +879,8 @@ encode_crypt_files(int nfiles, char **files, STRLIST remusr)
       while (nfiles--)
         {
           print_file_status(STATUS_FILE_START, *files, 2);
-          if ( (rc = encode_crypt(*files, remusr)) )
-            log_error("%s: encryption failed: %s\n",
+          if ( (rc = encode_crypt(*files, remusr, 0)) )
+            log_error("encryption of `%s' failed: %s\n",
                       print_fname_stdin(*files), g10_errstr(rc) );
           write_status( STATUS_FILE_DONE );
           files++;