summaryrefslogtreecommitdiff
path: root/g10/encode.c
diff options
context:
space:
mode:
Diffstat (limited to 'g10/encode.c')
-rw-r--r--g10/encode.c912
1 files changed, 912 insertions, 0 deletions
diff --git a/g10/encode.c b/g10/encode.c
new file mode 100644
index 0000000..3c4e0a2
--- /dev/null
+++ b/g10/encode.c
@@ -0,0 +1,912 @@
+/* encode.c - encode data
+ * 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+
+#include "gpg.h"
+#include "options.h"
+#include "packet.h"
+#include "status.h"
+#include "iobuf.h"
+#include "keydb.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.
+ */
+int
+encode_symmetric( const char *filename )
+{
+ return encode_simple( filename, 1, 0 );
+}
+
+/****************
+ * Encode FILENAME as a literal data packet only. Take input from
+ * stdin if FILENAME is NULL.
+ */
+int
+encode_store( const char *filename )
+{
+ return encode_simple( filename, 0, 0 );
+}
+
+
+static void
+encode_seskey( DEK *dek, DEK **seskey, byte *enckey )
+{
+ gcry_cipher_hd_t hd;
+ byte buf[33];
+
+ 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 );*/
+ }
+
+ /* 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 */
+}
+
+/* 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;
+
+ if(opt.disable_mdc)
+ return 0;
+
+ /* Do the keys really support MDC? */
+
+ 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
+ though it will not generate them. 2440bis allows this, by the
+ way. */
+
+ if(select_algo_from_prefs(pk_list,PREFTYPE_SYM,
+ CIPHER_ALGO_AES,NULL)==CIPHER_ALGO_AES)
+ return 1;
+
+ if(select_algo_from_prefs(pk_list,PREFTYPE_SYM,
+ CIPHER_ALGO_AES192,NULL)==CIPHER_ALGO_AES192)
+ return 1;
+
+ if(select_algo_from_prefs(pk_list,PREFTYPE_SYM,
+ CIPHER_ALGO_AES256,NULL)==CIPHER_ALGO_AES256)
+ return 1;
+
+ if(select_algo_from_prefs(pk_list,PREFTYPE_SYM,
+ CIPHER_ALGO_TWOFISH,NULL)==CIPHER_ALGO_TWOFISH)
+ return 1;
+
+ /* Last try. Use MDC for the modern ciphers. */
+
+ if (openpgp_cipher_get_algo_blklen (algo) != 8)
+ return 1;
+
+ if (opt.verbose)
+ warn_missing_mdc_from_pklist (pk_list);
+
+ 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 use_seskey )
+{
+ IOBUF inp, out;
+ PACKET pkt;
+ PKT_plaintext *pt = NULL;
+ STRING2KEY *s2k = NULL;
+ byte enckey[33];
+ int rc = 0;
+ int seskeylen = 0;
+ u32 filesize;
+ cipher_filter_context_t cfx;
+ armor_filter_context_t *afx = NULL;
+ compress_filter_context_t zfx;
+ text_filter_context_t tfx;
+ progress_filter_context_t *pfx;
+ int do_compress = !RFC1991 && default_compress_algo();
+
+ pfx = new_progress_context ();
+ memset( &cfx, 0, sizeof cfx);
+ memset( &zfx, 0, sizeof zfx);
+ memset( &tfx, 0, sizeof tfx);
+ init_packet(&pkt);
+
+ /* prepare iobufs */
+ 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);
+
+ 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 ( RFC1991 && use_seskey )
+ use_seskey = 0;
+
+ cfx.dek = NULL;
+ if( mode ) {
+ int canceled;
+
+ 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,
+ default_cipher_algo(), s2k, 4,
+ NULL, &canceled);
+ if( !cfx.dek || !cfx.dek->keylen ) {
+ 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"), gpg_strerror (rc));
+ release_progress_context (pfx);
+ return rc;
+ }
+ 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 ( 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 (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;
+ }
+
+ if( rc || (rc = open_outfile( filename, opt.armor? 1:0, &out )) ) {
+ iobuf_cancel(inp);
+ xfree(cfx.dek);
+ xfree(s2k);
+ release_progress_context (pfx);
+ return rc;
+ }
+
+ if ( opt.armor )
+ {
+ afx = new_armor_context ();
+ push_armor_filter (afx, out);
+ }
+
+ 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 ( use_seskey && seskeylen ) {
+ 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 packet failed: %s\n", g10_errstr(rc) );
+ xfree(enc);
+ }
+
+ 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
+ only partial length headers are use. In the past, we always
+ used partial body length here, but since PGP 2, PGP 6, and PGP
+ 7 need the file length, and nobody should be using PGP 5
+ nowadays anyway, this is now set to the file length. Note also
+ that this only applies to the RFC-1991 style symmetric
+ messages, and not the RFC-2440 style. PGP 6 and 7 work with
+ either partial length or fixed length with the new style
+ messages. */
+
+ if ( !iobuf_is_pipe_filename (filename) && *filename && !opt.textmode )
+ {
+ off_t tmpsize;
+ int overflow;
+
+ 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) )
+ filesize = tmpsize;
+ else
+ filesize = 0;
+ }
+ else
+ 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 && !RFC1991;
+ pt->buf = inp;
+ pkt.pkttype = PKT_PLAINTEXT;
+ pkt.pkt.plaintext = pt;
+ cfx.datalen = filesize && !do_compress ? calc_packet_length( &pkt ) : 0;
+ }
+ else
+ {
+ cfx.datalen = filesize && !do_compress ? filesize : 0;
+ pkt.pkttype = 0;
+ pkt.pkt.generic = NULL;
+ }
+
+ /* register the cipher filter */
+ if( mode )
+ iobuf_push_filter( out, cipher_filter, &cfx );
+ /* register the compress filter */
+ if( do_compress )
+ {
+ if (cfx.dek && cfx.dek->use_mdc)
+ zfx.new_ctb = 1;
+ push_compress_filter(out,&zfx,default_compress_algo());
+ }
+
+ /* do the work */
+ if (!opt.no_literal) {
+ if( (rc = build_packet( out, &pkt )) )
+ log_error("build_packet failed: %s\n", g10_errstr(rc) );
+ }
+ else {
+ /* user requested not to create a literal packet,
+ * so we copy the plain data */
+ byte copy_buffer[4096];
+ int bytes_copied;
+ while ((bytes_copied = iobuf_read(inp, copy_buffer, 4096)) != -1)
+ 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 */
+ }
+
+ /* finish the stuff */
+ iobuf_close(inp);
+ if (rc)
+ iobuf_cancel(out);
+ else {
+ iobuf_close(out); /* fixme: check returncode */
+ if (mode)
+ write_status( STATUS_END_ENCRYPTION );
+ }
+ if (pt)
+ pt->buf = NULL;
+ free_packet(&pkt);
+ 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_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 = NULL;
+ 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_algo && !RFC1991;
+
+ pfx = new_progress_context ();
+ memset( &cfx, 0, sizeof cfx);
+ 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)
+ 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"));
+ compliance_failure();
+ break;
+ }
+ }
+
+ /* prepare iobufs */
+ 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);
+
+ 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 )
+ {
+ afx = new_armor_context ();
+ push_armor_filter (afx, out);
+ }
+
+ /* create a session key */
+ 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
+ mixing v3 and v4 keys, as v4 keys have an implicit
+ preference entry for 3DES, and the pk_list cannot be empty.
+ In this case, use 3DES anyway as it's the safest choice -
+ perhaps the v3 key is being used in an OpenPGP
+ implementation and we know that the implementation behind
+ any v4 key can handle 3DES. */
+ if( cfx.dek->algo == -1 ) {
+ cfx.dek->algo = CIPHER_ALGO_3DES;
+
+ if( PGP2 ) {
+ log_info(_("unable to use the IDEA cipher for all of the keys "
+ "you are encrypting to.\n"));
+ 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(_("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;
+ }
+
+ cfx.dek->use_mdc=use_mdc(pk_list,cfx.dek->algo);
+
+ /* Only do the is-file-already-compressed check if we are using a
+ MDC. This forces compressed files to be re-compressed if we do
+ not have a MDC to give some protection against chosen
+ ciphertext attacks. */
+
+ 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;
+ }
+ if (rc2)
+ {
+ rc = rc2;
+ goto leave;
+ }
+
+ make_session_key( cfx.dek );
+ if( DBG_CIPHER )
+ 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;
+
+ /* 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 (!iobuf_is_pipe_filename (filename) && *filename && !opt.textmode )
+ {
+ off_t tmpsize;
+ int overflow;
+
+ 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) )
+ filesize = tmpsize;
+ else
+ filesize = 0;
+ }
+ else
+ 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 && !RFC1991;
+ pt->buf = inp;
+ pkt.pkttype = PKT_PLAINTEXT;
+ pkt.pkt.plaintext = pt;
+ cfx.datalen = filesize && !do_compress? calc_packet_length( &pkt ) : 0;
+ }
+ else
+ cfx.datalen = filesize && !do_compress ? filesize : 0;
+
+ /* register the cipher filter */
+ iobuf_push_filter( out, cipher_filter, &cfx );
+
+ /* register the compress filter */
+ if( do_compress ) {
+ 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(_("WARNING: forcing compression algorithm %s (%d)"
+ " violates recipient preferences\n"),
+ compress_algo_to_string(compr_algo),compr_algo);
+
+ /* algo 0 means no compression */
+ if( compr_algo )
+ {
+ if (cfx.dek && cfx.dek->use_mdc)
+ zfx.new_ctb = 1;
+ push_compress_filter(out,&zfx,compr_algo);
+ }
+ }
+
+ /* do the work */
+ if (!opt.no_literal) {
+ if( (rc = build_packet( out, &pkt )) )
+ log_error("build_packet failed: %s\n", g10_errstr(rc) );
+ }
+ else {
+ /* user requested not to create a literal packet, so we copy
+ the plain data */
+ byte copy_buffer[4096];
+ int bytes_copied;
+ while ((bytes_copied = iobuf_read(inp, copy_buffer, 4096)) != -1)
+ 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 */
+ }
+
+ /* finish the stuff */
+ leave:
+ iobuf_close(inp);
+ if( rc )
+ iobuf_cancel(out);
+ else {
+ iobuf_close(out); /* fixme: check returncode */
+ write_status( STATUS_END_ENCRYPTION );
+ }
+ if( pt )
+ pt->buf = NULL;
+ free_packet(&pkt);
+ xfree(cfx.dek);
+ xfree(symkey_dek);
+ xfree(symkey_s2k);
+ release_pk_list( pk_list );
+ release_armor_context (afx);
+ release_progress_context (pfx);
+ return rc;
+}
+
+
+
+
+/****************
+ * Filter to do a complete public key encryption.
+ */
+int
+encrypt_filter( void *opaque, int control,
+ IOBUF a, byte *buf, size_t *ret_len)
+{
+ size_t size = *ret_len;
+ encrypt_filter_context_t *efx = opaque;
+ int rc=0;
+
+ if( control == IOBUFCTRL_UNDERFLOW ) { /* decrypt */
+ BUG(); /* not used */
+ }
+ else if( control == IOBUFCTRL_FLUSH ) { /* encrypt */
+ if( !efx->header_okay ) {
+ 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 =
+ select_algo_from_prefs(efx->pk_list,PREFTYPE_SYM,-1,NULL);
+ if( efx->cfx.dek->algo == -1 ) {
+ /* because 3DES is implicitly in the prefs, this can only
+ * 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 &&
+ select_algo_from_prefs(efx->pk_list,PREFTYPE_SYM,
+ opt.def_cipher_algo,
+ NULL)!=opt.def_cipher_algo)
+ log_info(_("forcing symmetric cipher %s (%d) "
+ "violates recipient preferences\n"),
+ openpgp_cipher_algo_name (opt.def_cipher_algo),
+ opt.def_cipher_algo);
+
+ efx->cfx.dek->algo = opt.def_cipher_algo;
+ }
+
+ efx->cfx.dek->use_mdc = use_mdc(efx->pk_list,efx->cfx.dek->algo);
+
+ make_session_key( efx->cfx.dek );
+ if( DBG_CIPHER )
+ 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;
+ }
+ rc = iobuf_write( a, buf, size );
+
+ }
+ else if( control == IOBUFCTRL_FREE )
+ {
+ xfree(efx->symkey_dek);
+ xfree(efx->symkey_s2k);
+ }
+ else if( control == IOBUFCTRL_DESC ) {
+ *(char**)buf = "encrypt_filter";
+ }
+ return rc;
+}
+
+
+/****************
+ * Write pubkey-enc packets from the list of PKs to OUT.
+ */
+static int
+write_pubkey_enc_from_list( PK_LIST pk_list, DEK *dek, IOBUF out )
+{
+ PACKET pkt;
+ PKT_public_key *pk;
+ PKT_pubkey_enc *enc;
+ int rc;
+
+ for( ; pk_list; pk_list = pk_list->next ) {
+ gcry_mpi_t frame;
+
+ pk = pk_list->pk;
+
+ print_pubkey_algo_note( pk->pubkey_algo );
+ 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));
+
+ if(opt.throw_keyid && (PGP2 || PGP6 || PGP7 || PGP8))
+ {
+ log_info(_("you may not use %s while in %s mode\n"),
+ "--throw-keyid",compliance_option_string());
+ compliance_failure();
+ }
+
+ /* Okay, what's going on: We have the session key somewhere in
+ * the structure DEK and want to encode this session key in
+ * an integer value of n bits. pubkey_nbits gives us the
+ * number of bits we have to use. We then encode the session
+ * key in some way and we get it back in the big intger value
+ * FRAME. Then we use FRAME, the public key PK->PKEY and the
+ * 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
+ * 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 = pk_encrypt (pk->pubkey_algo, enc->data, frame, pk->pkey);
+ gcry_mpi_release (frame);
+ if( rc )
+ log_error ("pubkey_encrypt failed: %s\n", gpg_strerror (rc) );
+ else {
+ if( opt.verbose ) {
+ char *ustr = get_user_id_string_native (enc->keyid);
+ log_info(_("%s/%s encrypted for: \"%s\"\n"),
+ gcry_pk_algo_name (enc->pubkey_algo),
+ openpgp_cipher_algo_name (dek->algo),
+ ustr );
+ xfree(ustr);
+ }
+ /* and write it */
+ init_packet(&pkt);
+ pkt.pkttype = PKT_PUBKEY_ENC;
+ pkt.pkt.pubkey_enc = enc;
+ rc = build_packet( out, &pkt );
+ if( rc )
+ log_error("build_packet(pubkey_enc) failed: %s\n", g10_errstr(rc));
+ }
+ free_pubkey_enc(enc);
+ if( rc )
+ return rc;
+ }
+ return 0;
+}
+
+void
+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;
+ }
+
+ if (!nfiles)
+ {
+ char line[2048];
+ unsigned int lno = 0;
+ while ( fgets(line, DIM(line), stdin) )
+ {
+ lno++;
+ if (!*line || line[strlen(line)-1] != '\n')
+ {
+ log_error("input line %u too long or missing LF\n", lno);
+ return;
+ }
+ line[strlen(line)-1] = '\0';
+ print_file_status(STATUS_FILE_START, line, 2);
+ 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 );
+ }
+ }
+ else
+ {
+ while (nfiles--)
+ {
+ print_file_status(STATUS_FILE_START, *files, 2);
+ 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++;
+ }
+ }
+}