gpg: Limit keysize for unattended key generation to useful values.
[gnupg.git] / g10 / encode.c
index 4486cba..88d0a69 100644 (file)
@@ -1,12 +1,12 @@
 /* encode.c - encode data
- * Copyright (C) 1998, 1999, 2000, 2001, 2002,
- *               2003 Free Software Foundation, Inc.
+ * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
+ *               2006, 2009 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,
@@ -15,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 <http://www.gnu.org/licenses/>.
  */
 
 #include <config.h>
 #include <errno.h>
 #include <assert.h>
 
+#include "gpg.h"
 #include "options.h"
 #include "packet.h"
-#include "errors.h"
+#include "status.h"
 #include "iobuf.h"
 #include "keydb.h"
-#include "memory.h"
 #include "util.h"
 #include "main.h"
 #include "filter.h"
 #include "trustdb.h"
 #include "i18n.h"
 #include "status.h"
+#include "pkglue.h"
+
 
 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.
@@ -64,33 +63,39 @@ encode_store( const char *filename )
     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;
+    gcry_cipher_hd_t hd;
     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 );
-    
-    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_close( hd );
-
-    memcpy( enckey, buf, c->keylen + 1 );
+    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 );*/
+      }
+
+    /* The encrypted session key is prefixed with a one-octet algorithm id.  */
+    buf[0] = (*seskey)->algo;
+    memcpy( buf + 1, (*seskey)->key, (*seskey)->keylen );
+
+    /* We only pass already checked values to the following fucntion,
+       thus we consider any failure as fatal.  */
+    if (openpgp_cipher_open (&hd, dek->algo, GCRY_CIPHER_MODE_CFB, 1))
+      BUG ();
+    if (gcry_cipher_setkey (hd, dek->key, dek->keylen))
+      BUG ();
+    gcry_cipher_setiv (hd, NULL, 0);
+    gcry_cipher_encrypt (hd, buf, (*seskey)->keylen + 1, NULL, 0);
+    gcry_cipher_close (hd);
+
+    memcpy( enckey, buf, (*seskey)->keylen + 1 );
     wipememory( buf, sizeof buf ); /* burn key */
-    *ret_dek = c;
 }
 
 /* We try very hard to use a MDC */
@@ -112,7 +117,7 @@ use_mdc(PK_LIST pk_list,int algo)
 
   if(select_mdc_from_pklist(pk_list))
     return 1;
-  
+
   /* The keys don't support MDC, so now we do a bit of a hack - if any
      of the AESes or TWOFISH are in the prefs, we assume that the user
      can handle a MDC.  This is valid for PGP 7, which can handle MDCs
@@ -137,9 +142,12 @@ use_mdc(PK_LIST pk_list,int algo)
 
   /* Last try.  Use MDC for the modern ciphers. */
 
-  if(cipher_get_blocksize(algo)!=8)
+  if (openpgp_cipher_get_algo_blklen (algo) != 8)
     return 1;
 
+  if (opt.verbose)
+    warn_missing_mdc_from_pklist (pk_list);
+
   return 0; /* No MDC */
 }
 
@@ -152,7 +160,6 @@ 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];
@@ -160,26 +167,37 @@ encode_simple( const char *filename, int mode, int use_seskey )
     int seskeylen = 0;
     u32 filesize;
     cipher_filter_context_t cfx;
-    armor_filter_context_t afx;
+    armor_filter_context_t  *afx = NULL;
     compress_filter_context_t zfx;
     text_filter_context_t tfx;
-    progress_filter_context_t pfx;
-    int do_compress = opt.compress && !RFC1991;
+    progress_filter_context_t *pfx;
+    int do_compress = !RFC1991 && default_compress_algo();
 
+    pfx = new_progress_context ();
     memset( &cfx, 0, sizeof cfx);
-    memset( &afx, 0, sizeof afx);
     memset( &zfx, 0, sizeof zfx);
     memset( &tfx, 0, sizeof tfx);
     init_packet(&pkt);
-    
+
     /* prepare iobufs */
-    if( !(inp = iobuf_open(filename)) ) {
-       log_error(_("%s: can't open: %s\n"), filename? filename: "[stdin]",
-                                       strerror(errno) );
-       return G10ERR_OPEN_FILE;
+    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 ) {
+        rc = gpg_error_from_syserror ();
+       log_error(_("can't open `%s': %s\n"), filename? filename: "[stdin]",
+                  strerror(errno) );
+        release_progress_context (pfx);
+       return rc;
     }
 
-    handle_progress (&pfx, inp, filename);
+    handle_progress (pfx, inp, filename);
 
     if( opt.textmode )
        iobuf_push_filter( inp, text_filter, &tfx );
@@ -189,21 +207,24 @@ encode_simple( const char *filename, int mode, int use_seskey )
        it has no S2K salt. RFC1991 always uses simple S2K. */
     if ( RFC1991 && use_seskey )
         use_seskey = 0;
-    
+
     cfx.dek = NULL;
     if( mode ) {
-       s2k = m_alloc_clear( sizeof *s2k );
+        int canceled;
+
+       s2k = xmalloc_clear( sizeof *s2k );
        s2k->mode = RFC1991? 0:opt.s2k_mode;
-       s2k->hash_algo = opt.s2k_digest_algo;
+       s2k->hash_algo=S2K_DIGEST_ALGO;
        cfx.dek = passphrase_to_dek( NULL, 0,
-                                    default_cipher_algo(), s2k, 2,
-                                     NULL, NULL);
+                                    default_cipher_algo(), s2k, 4,
+                                     NULL, &canceled);
        if( !cfx.dek || !cfx.dek->keylen ) {
-           rc = G10ERR_PASSPHRASE;
-           m_free(cfx.dek);
-           m_free(s2k);
+           rc = gpg_error (canceled? GPG_ERR_CANCELED:GPG_ERR_INV_PASSPHRASE);
+           xfree(cfx.dek);
+           xfree(s2k);
            iobuf_close(inp);
-           log_error(_("error creating passphrase: %s\n"), g10_errstr(rc) );
+           log_error(_("error creating passphrase: %s\n"), gpg_strerror (rc));
+            release_progress_context (pfx);
            return rc;
        }
         if (use_seskey && s2k->mode != 1 && s2k->mode != 3) {
@@ -212,42 +233,46 @@ encode_simple( const char *filename, int mode, int use_seskey )
                         "due to the S2K mode\n"));
         }
 
-        if ( use_seskey ) {            
-            seskeylen = cipher_get_keylen( 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 = openpgp_cipher_get_algo_keylen (default_cipher_algo ());
+            encode_seskey( cfx.dek, &dek, enckey );
+            xfree( cfx.dek ); cfx.dek = dek;
+         }
+
+       if(opt.verbose)
+         log_info(_("using cipher %s\n"),
+                  openpgp_cipher_algo_name (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);
-        do_compress = 0;        
+        do_compress = 0;
       }
 
     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);
+        release_progress_context (pfx);
        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 ( opt.armor )
+      {
+        afx = new_armor_context ();
+       push_armor_filter (afx, out);
+      }
+
     if( s2k && !RFC1991 ) {
-       PKT_symkey_enc *enc = m_alloc_clear( sizeof *enc + seskeylen + 1 );
+       PKT_symkey_enc *enc = xmalloc_clear( sizeof *enc + seskeylen + 1 );
        enc->version = 4;
        enc->cipher_algo = cfx.dek->algo;
        enc->s2k = *s2k;
@@ -259,25 +284,11 @@ encode_simple( const char *filename, int mode, int use_seskey )
        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) {
-       /* 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->namelen = strlen(s);
-           memcpy(pt->name, s, pt->namelen );
-           m_free(s);
-       }
-       else { /* no filename */
-           pt = m_alloc( sizeof *pt - 1 );
-           pt->namelen = 0;
-       }
-    }
+    if (!opt.no_literal)
+      pt=setup_plaintext_name(filename,inp);
 
     /* Note that PGP 5 has problems decrypting symmetrically encrypted
        data if the file length is in the inner packet. It works when
@@ -290,12 +301,14 @@ encode_simple( const char *filename, int mode, int use_seskey )
        either partial length or fixed length with the new style
        messages. */
 
-    if (filename && *filename && !(*filename == '-' && !filename[1])
-        && !opt.textmode ) {
+    if ( !iobuf_is_pipe_filename (filename) && *filename && !opt.textmode )
+      {
         off_t tmpsize;
+        int overflow;
 
-       if ( !(tmpsize = iobuf_get_filelength(inp)) )
-          log_info(_("%s: WARNING: empty file\n"), filename );
+       if ( !(tmpsize = iobuf_get_filelength(inp, &overflow))
+             && !overflow && opt.verbose)
+          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
@@ -304,9 +317,9 @@ encode_simple( const char *filename, int mode, int use_seskey )
           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();
@@ -333,8 +346,7 @@ encode_simple( const char *filename, int mode, int use_seskey )
       {
         if (cfx.dek && cfx.dek->use_mdc)
           zfx.new_ctb = 1;
-       zfx.algo=default_compress_algo();
-       iobuf_push_filter( out, compress_filter, &zfx );
+       push_compress_filter(out,&zfx,default_compress_algo());
       }
 
     /* do the work */
@@ -348,9 +360,9 @@ encode_simple( const char *filename, int mode, int use_seskey )
        byte copy_buffer[4096];
        int  bytes_copied;
        while ((bytes_copied = iobuf_read(inp, copy_buffer, 4096)) != -1)
-           if (iobuf_write(out, copy_buffer, bytes_copied) == -1) {
-               rc = G10ERR_WRITE_FILE;
-               log_error("copying input to output failed: %s\n", g10_errstr(rc) );
+           if ( (rc=iobuf_write(out, copy_buffer, bytes_copied)) ) {
+               log_error ("copying input to output failed: %s\n",
+                           gpg_strerror (rc) );
                break;
            }
        wipememory(copy_buffer, 4096); /* burn buffer */
@@ -368,40 +380,102 @@ encode_simple( const char *filename, int mode, int use_seskey )
     if (pt)
        pt->buf = NULL;
     free_packet(&pkt);
-    m_free(cfx.dek);
-    m_free(s2k);
+    xfree(cfx.dek);
+    xfree(s2k);
+    release_armor_context (afx);
+    release_progress_context (pfx);
     return rc;
 }
 
+int
+setup_symkey(STRING2KEY **symkey_s2k,DEK **symkey_dek)
+{
+  int canceled;
+
+  *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, 4, NULL, &canceled);
+  if(!*symkey_dek || !(*symkey_dek)->keylen)
+    {
+      xfree(*symkey_dek);
+      xfree(*symkey_s2k);
+      return gpg_error (canceled?GPG_ERR_CANCELED:GPG_ERR_BAD_PASSPHRASE);
+    }
+
+  return 0;
+}
+
+static int
+write_symkey_enc(STRING2KEY *symkey_s2k,DEK *symkey_dek,DEK *dek,IOBUF out)
+{
+  int rc, seskeylen = openpgp_cipher_get_algo_keylen (dek->algo);
+
+  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_t 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;
+    armor_filter_context_t *afx = NULL;
     compress_filter_context_t zfx;
     text_filter_context_t tfx;
-    progress_filter_context_t pfx;
+    progress_filter_context_t *pfx;
     PK_LIST pk_list,work_list;
-    int do_compress = opt.compress && !RFC1991;
-
+    int do_compress = opt.compress_algo && !RFC1991;
 
+    pfx = new_progress_context ();
     memset( &cfx, 0, sizeof cfx);
-    memset( &afx, 0, sizeof afx);
     memset( &zfx, 0, sizeof zfx);
     memset( &tfx, 0, sizeof tfx);
     init_packet(&pkt);
 
+    if(use_symkey
+       && (rc=setup_symkey(&symkey_s2k,&symkey_dek)))
+      {
+        release_progress_context (pfx);
+        return rc;
+      }
+
     if( (rc=build_pk_list( remusr, &pk_list, PUBKEY_USAGE_ENC)) )
+      {
+        release_progress_context (pfx);
        return rc;
+      }
 
     if(PGP2) {
       for(work_list=pk_list; work_list; work_list=work_list->next)
@@ -416,16 +490,26 @@ encode_crypt( const char *filename, STRLIST remusr )
     }
 
     /* prepare iobufs */
-    if( !(inp = iobuf_open(filename)) ) {
-       log_error(_("can't open %s: %s\n"), filename? filename: "[stdin]",
-                                       strerror(errno) );
-       rc = G10ERR_OPEN_FILE;
+    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 ) {
+        rc = gpg_error_from_syserror ();
+       log_error(_("can't open `%s': %s\n"),
+                  filename? filename: "[stdin]",
+                  gpg_strerror (rc) );
        goto leave;
     }
     else if( opt.verbose )
        log_info(_("reading from `%s'\n"), filename? filename: "[stdin]");
 
-    handle_progress (&pfx, inp, filename);
+    handle_progress (pfx, inp, filename);
 
     if( opt.textmode )
        iobuf_push_filter( inp, text_filter, &tfx );
@@ -433,19 +517,14 @@ encode_crypt( const char *filename, STRLIST remusr )
     if( (rc = open_outfile( filename, opt.armor? 1:0, &out )) )
        goto leave;
 
+    if ( opt.armor )
+      {
+        afx = new_armor_context ();
+       push_armor_filter (afx, out);
+      }
 
-    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
@@ -464,14 +543,22 @@ encode_crypt( const char *filename, STRLIST remusr )
              compliance_failure();
            }
        }
+
+        /* In case 3DES has been selected, print a warning if
+           any key does not have a preference for AES.  This
+           should help to indentify why encrypting to several
+           recipients falls back to 3DES. */
+        if (opt.verbose
+            && cfx.dek->algo == CIPHER_ALGO_3DES)
+          warn_missing_aes_from_pklist (pk_list);
     }
     else {
       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"),
-                cipher_algo_to_string(opt.def_cipher_algo),
+       log_info(_("WARNING: forcing symmetric cipher %s (%d)"
+                  " violates recipient preferences\n"),
+                openpgp_cipher_algo_name (opt.def_cipher_algo),
                 opt.def_cipher_algo);
 
       cfx.dek->algo = opt.def_cipher_algo;
@@ -484,12 +571,11 @@ 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);
-        do_compress = 0;        
+        do_compress = 0;
       }
     if (rc2)
       {
@@ -499,46 +585,41 @@ encode_crypt( const char *filename, STRLIST remusr )
 
     make_session_key( cfx.dek );
     if( DBG_CIPHER )
-       log_hexdump("DEK is: ", cfx.dek->key, cfx.dek->keylen );
+       log_printhex ("DEK is: ", cfx.dek->key, cfx.dek->keylen );
 
     rc = write_pubkey_enc_from_list( pk_list, cfx.dek, out );
     if( rc  )
        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->namelen = strlen(s);
-           memcpy(pt->name, s, pt->namelen );
-           m_free(s);
-       }
-       else { /* no filename */
-           pt = m_alloc( sizeof *pt - 1 );
-           pt->namelen = 0;
-       }
-    }
+    /* 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)
+      pt=setup_plaintext_name(filename,inp);
 
-    if (filename && *filename && !(*filename == '-' && !filename[1])
-        && !opt.textmode ) {
+    if (!iobuf_is_pipe_filename (filename) && *filename && !opt.textmode )
+      {
         off_t tmpsize;
+        int overflow;
 
-       if ( !(tmpsize = iobuf_get_filelength(inp)) )
-          log_info(_("%s: WARNING: empty file\n"), filename );
+       if ( !(tmpsize = iobuf_get_filelength(inp, &overflow))
+             && !overflow && opt.verbose)
+          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) )
+        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();
@@ -558,7 +639,7 @@ 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)
          {
@@ -571,8 +652,8 @@ encode_crypt( const char *filename, STRLIST remusr )
        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 */
@@ -580,8 +661,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);
          }
     }
 
@@ -596,10 +676,9 @@ encode_crypt( const char *filename, STRLIST remusr )
        byte copy_buffer[4096];
        int  bytes_copied;
        while ((bytes_copied = iobuf_read(inp, copy_buffer, 4096)) != -1)
-           if (iobuf_write(out, copy_buffer, bytes_copied) == -1) {
-               rc = G10ERR_WRITE_FILE;
-               log_error("copying input to output failed: %s\n",
-                          g10_errstr(rc) );
+           if ( (rc=iobuf_write(out, copy_buffer, bytes_copied)) ) {
+               log_error ("copying input to output failed: %s\n",
+                           gpg_strerror (rc));
                break;
            }
        wipememory(copy_buffer, 4096); /* burn buffer */
@@ -617,8 +696,12 @@ 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 );
+    release_armor_context (afx);
+    release_progress_context (pfx);
     return rc;
 }
 
@@ -641,7 +724,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 =
@@ -651,6 +734,14 @@ encrypt_filter( void *opaque, int control,
                      * happen if we do not have any public keys in the list */
                    efx->cfx.dek->algo = DEFAULT_CIPHER_ALGO;
                 }
+
+                /* In case 3DES has been selected, print a warning if
+                   any key does not have a preference for AES.  This
+                   should help to indentify why encrypting to several
+                   recipients falls back to 3DES. */
+                if (opt.verbose
+                    && efx->cfx.dek->algo == CIPHER_ALGO_3DES)
+                  warn_missing_aes_from_pklist (efx->pk_list);
            }
            else {
              if(!opt.expert &&
@@ -659,7 +750,7 @@ encrypt_filter( void *opaque, int control,
                                        NULL)!=opt.def_cipher_algo)
                log_info(_("forcing symmetric cipher %s (%d) "
                           "violates recipient preferences\n"),
-                        cipher_algo_to_string(opt.def_cipher_algo),
+                        openpgp_cipher_algo_name (opt.def_cipher_algo),
                         opt.def_cipher_algo);
 
              efx->cfx.dek->algo = opt.def_cipher_algo;
@@ -669,13 +760,21 @@ encrypt_filter( void *opaque, int control,
 
            make_session_key( efx->cfx.dek );
            if( DBG_CIPHER )
-               log_hexdump("DEK is: ",
-                            efx->cfx.dek->key, efx->cfx.dek->keylen );
+               log_printhex ("DEK is: ",
+                              efx->cfx.dek->key, efx->cfx.dek->keylen );
 
            rc = write_pubkey_enc_from_list( efx->pk_list, efx->cfx.dek, a );
            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;
@@ -683,8 +782,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";
     }
@@ -704,12 +806,12 @@ write_pubkey_enc_from_list( PK_LIST pk_list, DEK *dek, IOBUF out )
     int rc;
 
     for( ; pk_list; pk_list = pk_list->next ) {
-       MPI frame;
+       gcry_mpi_t frame;
 
        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 || (pk_list->flags&1));
@@ -730,23 +832,24 @@ 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()
         */
-       frame = encode_session_key( dek, pubkey_nbits( pk->pubkey_algo,
-                                                         pk->pkey ) );
-       rc = pubkey_encrypt( pk->pubkey_algo, enc->data, frame, pk->pkey );
-       mpi_free( frame );
+       frame = encode_session_key (dek, pubkey_nbits (pk->pubkey_algo,
+                                                       pk->pkey) );
+       rc = pk_encrypt (pk->pubkey_algo, enc->data, frame, pk->pkey);
+       gcry_mpi_release (frame);
        if( rc )
-           log_error("pubkey_encrypt failed: %s\n", g10_errstr(rc) );
+           log_error ("pubkey_encrypt failed: %s\n", gpg_strerror (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);
+                         openpgp_pk_algo_name (enc->pubkey_algo),
+                         openpgp_cipher_algo_name (dek->algo),
+                         ustr );
+               xfree(ustr);
            }
            /* and write it */
            init_packet(&pkt);
@@ -764,16 +867,16 @@ write_pubkey_enc_from_list( PK_LIST pk_list, DEK *dek, IOBUF out )
 }
 
 void
-encode_crypt_files(int nfiles, char **files, STRLIST remusr)
+encode_crypt_files(int nfiles, char **files, strlist_t remusr)
 {
   int rc = 0;
 
   if (opt.outfile)
     {
       log_error(_("--output doesn't work for this command\n"));
-      return;        
+      return;
     }
-    
+
   if (!nfiles)
     {
       char line[2048];
@@ -788,8 +891,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 );
         }
@@ -799,8 +902,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++;