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;
}
Exemple #2
0
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;
}
Exemple #3
0
static int
xxxx_do_check( PKT_secret_key *sk, const char *tryagain_text, int mode,
               int *canceled )
{
    gpg_error_t err;
    byte *buffer;
    u16 csum=0;
    int i, res;
    size_t nbytes;

    if( sk->is_protected ) { /* remove the protection */
	DEK *dek = NULL;
	u32 keyid[4]; /* 4! because we need two of them */
	gcry_cipher_hd_t cipher_hd=NULL;
	PKT_secret_key *save_sk;

	if( sk->protect.s2k.mode == 1001 ) {
	    log_info(_("secret key parts are not available\n"));
	    return GPG_ERR_UNUSABLE_SECKEY;
	}
	if( sk->protect.algo == CIPHER_ALGO_NONE )
	    BUG();
	if( openpgp_cipher_test_algo( sk->protect.algo ) ) {
	    log_info(_("protection algorithm %d%s is not supported\n"),
			sk->protect.algo,sk->protect.algo==1?" (IDEA)":"" );
	    return GPG_ERR_CIPHER_ALGO;
	}
	if(gcry_md_test_algo (sk->protect.s2k.hash_algo))
	  {
	    log_info(_("protection digest %d is not supported\n"),
		     sk->protect.s2k.hash_algo);
	    return GPG_ERR_DIGEST_ALGO;
	  }
	keyid_from_sk( sk, keyid );
	keyid[2] = keyid[3] = 0;
	if (!sk->flags.primary)
          {
            keyid[2] = sk->main_keyid[0];
            keyid[3] = sk->main_keyid[1];
          }
	dek = passphrase_to_dek( keyid, sk->pubkey_algo, sk->protect.algo,
				 &sk->protect.s2k, mode,
                                 tryagain_text, canceled );
        if (!dek && canceled && *canceled)
	    return GPG_ERR_CANCELED;


	err = openpgp_cipher_open (&cipher_hd, sk->protect.algo,
				   GCRY_CIPHER_MODE_CFB,
				   (GCRY_CIPHER_SECURE
				    | (sk->protect.algo >= 100 ?
				       0 : GCRY_CIPHER_ENABLE_SYNC)));
        if (err)
          log_fatal ("cipher open failed: %s\n", gpg_strerror (err) );

	err = gcry_cipher_setkey (cipher_hd, dek->key, dek->keylen);
        if (err)
          log_fatal ("set key failed: %s\n", gpg_strerror (err) );

	xfree(dek);
	save_sk = copy_secret_key( NULL, sk );

	gcry_cipher_setiv ( cipher_hd, sk->protect.iv, sk->protect.ivlen );

	csum = 0;
	if( sk->version >= 4 ) {
            int ndata;
	    unsigned int ndatabits;
	    byte *p, *data;
            u16 csumc = 0;

	    i = pubkey_get_npkey(sk->pubkey_algo);

            assert ( gcry_mpi_get_flag (sk->skey[i], GCRYMPI_FLAG_OPAQUE ));
            p = gcry_mpi_get_opaque ( sk->skey[i], &ndatabits );
            ndata = (ndatabits+7)/8;

            if ( ndata > 1 )
              csumc = buf16_to_u16 (p+ndata-2);
	    data = xmalloc_secure ( ndata );
	    gcry_cipher_decrypt ( cipher_hd, data, ndata, p, ndata );
	    gcry_mpi_release (sk->skey[i]); sk->skey[i] = NULL ;

	    p = data;
            if (sk->protect.sha1chk) {
                /* This is the new SHA1 checksum method to detect
                   tampering with the key as used by the Klima/Rosa
                   attack */
                sk->csum = 0;
                csum = 1;
                if( ndata < 20 )
                    log_error("not enough bytes for SHA-1 checksum\n");
                else {
                    gcry_md_hd_t h;

                    if ( gcry_md_open (&h, DIGEST_ALGO_SHA1, 1))
                        BUG(); /* Algo not available. */
                    gcry_md_write (h, data, ndata - 20);
                    gcry_md_final (h);
                    if (!memcmp (gcry_md_read (h, DIGEST_ALGO_SHA1),
                                 data + ndata - 20, 20) )
                      {
                        /* Digest does match.  We have to keep the old
                           style checksum in sk->csum, so that the
                           test used for unprotected keys does work.
                           This test gets used when we are adding new
                           keys. */
                        sk->csum = csum = checksum (data, ndata-20);
                      }
                    gcry_md_close (h);
                }
            }
            else {
                if( ndata < 2 ) {
                    log_error("not enough bytes for checksum\n");
                    sk->csum = 0;
                    csum = 1;
                }
                else {
                    csum = checksum( data, ndata-2);
                    sk->csum = data[ndata-2] << 8 | data[ndata-1];
                    if ( sk->csum != csum ) {
                        /* This is a PGP 7.0.0 workaround */
                        sk->csum = csumc; /* take the encrypted one */
                    }
                }
            }

            /* Must check it here otherwise the mpi_read_xx would fail
               because the length may have an arbitrary value */
            if( sk->csum == csum ) {
                for( ; i < pubkey_get_nskey(sk->pubkey_algo); i++ ) {
                    if ( gcry_mpi_scan( &sk->skey[i], GCRYMPI_FMT_PGP,
                                        p, ndata, &nbytes))
                      {
                        /* Checksum was okay, but not correctly
                           decrypted.  */
                        sk->csum = 0;
                        csum = 1;
                        break;
                      }
                    ndata -= nbytes;
                    p += nbytes;
                }
                /* Note: at this point ndata should be 2 for a simple
                   checksum or 20 for the sha1 digest */
            }
	    xfree(data);
	}
	else {
	    for(i=pubkey_get_npkey(sk->pubkey_algo);
		    i < pubkey_get_nskey(sk->pubkey_algo); i++ ) {
                byte *p;
                size_t ndata;
                unsigned int ndatabits;

                assert (gcry_mpi_get_flag (sk->skey[i], GCRYMPI_FLAG_OPAQUE));
                p = gcry_mpi_get_opaque (sk->skey[i], &ndatabits);
                ndata = (ndatabits+7)/8;
                assert (ndata >= 2);
                assert (ndata == ((p[0] << 8 | p[1]) + 7)/8 + 2);
                buffer = xmalloc_secure (ndata);
		gcry_cipher_sync (cipher_hd);
                buffer[0] = p[0];
                buffer[1] = p[1];
                gcry_cipher_decrypt (cipher_hd, buffer+2, ndata-2,
                                     p+2, ndata-2);
                csum += checksum (buffer, ndata);
                gcry_mpi_release (sk->skey[i]);

		err = gcry_mpi_scan( &sk->skey[i], GCRYMPI_FMT_PGP,
				     buffer, ndata, &ndata );
		xfree (buffer);
                if (err)
                  {
                    /* Checksum was okay, but not correctly
                       decrypted.  */
                    sk->csum = 0;
                    csum = 1;
                    break;
                  }
/*  		csum += checksum_mpi (sk->skey[i]); */
	    }
	}
	gcry_cipher_close ( cipher_hd );

	/* Now let's see whether we have used the correct passphrase. */
	if( csum != sk->csum ) {
	    copy_secret_key( sk, save_sk );
            passphrase_clear_cache ( keyid, NULL, sk->pubkey_algo );
	    free_secret_key( save_sk );
	    return gpg_error (GPG_ERR_BAD_PASSPHRASE);
	}

	/* The checksum may fail, so we also check the key itself. */
	res = pk_check_secret_key ( sk->pubkey_algo, sk->skey );
	if( res ) {
	    copy_secret_key( sk, save_sk );
            passphrase_clear_cache ( keyid, NULL, sk->pubkey_algo );
	    free_secret_key( save_sk );
	    return gpg_error (GPG_ERR_BAD_PASSPHRASE);
	}
	free_secret_key( save_sk );
	sk->is_protected = 0;
    }
    else { /* not protected, assume it is okay if the checksum is okay */
	csum = 0;
	for(i=pubkey_get_npkey(sk->pubkey_algo);
		i < pubkey_get_nskey(sk->pubkey_algo); i++ ) {
	    csum += checksum_mpi( sk->skey[i] );
	}
	if( csum != sk->csum )
	    return GPG_ERR_CHECKSUM;
    }

    return 0;
}
/* 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;
    compress_filter_context_t zfx;
    text_filter_context_t tfx;
    progress_filter_context_t pfx;
    int do_compress = !RFC1991 && default_compress_algo();

    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 */
    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 ( RFC1991 && use_seskey )
        use_seskey = 0;
    
    cfx.dek = NULL;
    if( mode ) {
	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, 2,
                                     NULL, NULL);
	if( !cfx.dek || !cfx.dek->keylen ) {
	    rc = G10ERR_PASSPHRASE;
	    xfree(cfx.dek);
	    xfree(s2k);
	    iobuf_close(inp);
	    log_error(_("error creating passphrase: %s\n"), g10_errstr(rc) );
	    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 = 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 (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);
	return rc;
    }

    if( opt.armor )
	iobuf_push_filter( out, armor_filter, &afx );

    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 )
          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 (iobuf_write(out, copy_buffer, bytes_copied) == -1) {
		rc = G10ERR_WRITE_FILE;
		log_error("copying input to output failed: %s\n", g10_errstr(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);
    return rc;
}
Exemple #5
0
/* 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
encrypt_simple (const char *filename, int mode, int use_seskey)
{
  iobuf_t inp, out;
  PACKET pkt;
  PKT_plaintext *pt = NULL;
  STRING2KEY *s2k = NULL;
  void *enckey = NULL;
  size_t enckeylen = 0;
  int rc = 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 = !!default_compress_algo();

  if (!gnupg_rng_is_compliant (opt.compliance))
    {
      rc = gpg_error (GPG_ERR_FORBIDDEN);
      log_error (_("%s is not compliant with %s mode\n"),
                 "RNG",
                 gnupg_compliance_option_string (opt.compliance));
      write_status_error ("random-compliance", rc);
      return rc;
    }

  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, IOBUF_IOCTL_NO_CACHE, 1, NULL);
  if (inp && is_secured_file (iobuf_get_fd (inp)))
    {
      iobuf_close (inp);
      inp = NULL;
      gpg_err_set_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 );

  cfx.dek = NULL;
  if ( mode )
    {
      int canceled;
      aead_algo_t aead_algo;

      s2k = xmalloc_clear( sizeof *s2k );
      s2k->mode = opt.s2k_mode;
      s2k->hash_algo = S2K_DIGEST_ALGO;
      cfx.dek = passphrase_to_dek (default_cipher_algo (), s2k, 1, 0,
                                   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"));
        }

      /* See whether we want to use AEAD.  */
      aead_algo = use_aead (NULL, cfx.dek->algo);

      if ( use_seskey )
        {
          DEK *dek = NULL;

          rc = encrypt_seskey (cfx.dek, aead_algo, &dek, &enckey, &enckeylen);
          if (rc)
            {
              xfree (cfx.dek);
              xfree (s2k);
              iobuf_close (inp);
              release_progress_context (pfx);
              return rc;
            }
          /* Replace key in DEK.  */
          xfree (cfx.dek);
          cfx.dek = dek;
        }

      if (aead_algo)
        cfx.dek->use_aead = aead_algo;
      else
        cfx.dek->use_mdc = !!use_mdc (NULL, cfx.dek->algo);

      if (opt.verbose)
        log_info(_("using cipher %s.%s\n"),
                 openpgp_cipher_algo_name (cfx.dek->algo),
                 cfx.dek->use_aead? openpgp_aead_algo_name (cfx.dek->use_aead)
                 /**/             : "CFB");
    }

  if (do_compress
      && cfx.dek
      && (cfx.dek->use_mdc || cfx.dek->use_aead)
      && is_file_compressed(filename, &rc))
    {
      if (opt.verbose)
        log_info(_("'%s' already compressed\n"), filename);
      do_compress = 0;
    }

  if ( rc || (rc = open_outfile (-1, filename, opt.armor? 1:0, 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 )
    {
      /* Fixme: This is quite similar to write_symkey_enc.  */
      PKT_symkey_enc *enc = xmalloc_clear (sizeof *enc + enckeylen);
      enc->version = cfx.dek->use_aead ? 5 : 4;
      enc->cipher_algo = cfx.dek->algo;
      enc->aead_algo = cfx.dek->use_aead;
      enc->s2k = *s2k;
      if (enckeylen)
        {
          enc->seskeylen = enckeylen;
          memcpy (enc->seskey, enckey, enckeylen);
        }
      pkt.pkttype = PKT_SYMKEY_ENC;
      pkt.pkt.symkey_enc = enc;
      if ((rc = build_packet( out, &pkt )))
        log_error("build symkey packet failed: %s\n", gpg_strerror (rc) );
      xfree (enc);
      xfree (enckey);
      enckey = NULL;
    }

  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
         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)
    {
      /* Note that PT has been initialized above in !no_literal mode.  */
      pt->timestamp = make_timestamp();
      pt->mode = opt.mimemode? 'm' : opt.textmode? 't' : 'b';
      pt->len = filesize;
      pt->new_ctb = !pt->len;
      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,
                       cfx.dek->use_aead? cipher_filter_aead
                       /**/             : cipher_filter_cfb,
                       &cfx );

  /* Register the compress filter. */
  if ( do_compress )
    {
      if (cfx.dek && (cfx.dek->use_mdc || cfx.dek->use_aead))
        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", gpg_strerror (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, NULL);
  xfree (enckey);
  xfree (cfx.dek);
  xfree (s2k);
  release_armor_context (afx);
  release_progress_context (pfx);
  return rc;
}