Пример #1
0
/****************
 * Handle a compressed packet
 */
int
handle_compressed( void *procctx, PKT_compressed *cd,
		   int (*callback)(IOBUF, void *), void *passthru )
{
    compress_filter_context_t *cfx;
    int rc;

    if( cd->algorithm < 1 || cd->algorithm > 2	)
	return G10ERR_COMPR_ALGO;
    cfx = m_alloc_clear (sizeof *cfx);
    cfx->algo = cd->algorithm;
    cfx->release = release_context;
    iobuf_push_filter( cd->buf, compress_filter, cfx );
    if( callback )
	rc = callback(cd->buf, passthru );
    else
	rc = proc_packets(procctx, cd->buf);
    cd->buf = NULL;
    return rc;
}
Пример #2
0
void
handle_progress (progress_filter_context_t *pfx, IOBUF inp, const char *name)
{
  off_t filesize = 0;

  if (!pfx)
    return;

  log_assert (opt.enable_progress_filter);
  log_assert (is_status_enabled ());

  if ( !iobuf_is_pipe_filename (name) && *name )
    filesize = iobuf_get_filelength (inp, NULL);
  else if (opt.set_filesize)
    filesize = opt.set_filesize;

  /* register the progress filter */
  pfx->what = xstrdup (name ? name : "stdin");
  pfx->total = filesize;
  pfx->refcount++;
  iobuf_push_filter (inp, progress_filter, pfx);
}
Пример #3
0
/****************
 * Decrypt the data, specified by ED with the key DEK.
 */
int
decrypt_data( void *procctx, PKT_encrypted *ed, DEK *dek )
{
  decode_filter_ctx_t dfx;
  byte *p;
  int rc=0, c, i;
  byte temp[32];
  unsigned blocksize;
  unsigned nprefix;
  
  dfx = xtrycalloc (1, sizeof *dfx);
  if (!dfx)
    return gpg_error_from_syserror ();
  dfx->refcount = 1;

  if ( opt.verbose && !dek->algo_info_printed )
    {
      if (!openpgp_cipher_test_algo (dek->algo))
        log_info (_("%s encrypted data\n"), 
                  openpgp_cipher_algo_name (dek->algo));
      else
        log_info (_("encrypted with unknown algorithm %d\n"), dek->algo );
      dek->algo_info_printed = 1;
    }
  rc = openpgp_cipher_test_algo (dek->algo);
  if (rc)
    goto leave;
  blocksize = openpgp_cipher_get_algo_blklen (dek->algo);
  if ( !blocksize || blocksize > 16 )
    log_fatal ("unsupported blocksize %u\n", blocksize );
  nprefix = blocksize;
  if ( ed->len && ed->len < (nprefix+2) )
    BUG();

  if ( ed->mdc_method ) 
    {
      if (gcry_md_open (&dfx->mdc_hash, ed->mdc_method, 0 ))
        BUG ();
      if ( DBG_HASHING )
        gcry_md_start_debug (dfx->mdc_hash, "checkmdc");
    }

  rc = openpgp_cipher_open (&dfx->cipher_hd, dek->algo,
			    GCRY_CIPHER_MODE_CFB,
			    (GCRY_CIPHER_SECURE
			     | ((ed->mdc_method || dek->algo >= 100)?
				0 : GCRY_CIPHER_ENABLE_SYNC)));
  if (rc)
    {
      /* We should never get an error here cause we already checked
       * that the algorithm is available.  */
      BUG();
    }


  /* log_hexdump( "thekey", dek->key, dek->keylen );*/
  rc = gcry_cipher_setkey (dfx->cipher_hd, dek->key, dek->keylen);
  if ( gpg_err_code (rc) == GPG_ERR_WEAK_KEY )
    {
      log_info(_("WARNING: message was encrypted with"
                 " a weak key in the symmetric cipher.\n"));
      rc=0;
    }
  else if( rc )
    {
      log_error("key setup failed: %s\n", g10_errstr(rc) );
      goto leave;
    }

  if (!ed->buf) 
    {
      log_error(_("problem handling encrypted packet\n"));
      goto leave;
    }

  gcry_cipher_setiv (dfx->cipher_hd, NULL, 0);

  if ( ed->len )
    {
      for (i=0; i < (nprefix+2) && ed->len; i++, ed->len-- ) 
        {
          if ( (c=iobuf_get(ed->buf)) == -1 )
            break;
          else
            temp[i] = c;
        }
    }
  else 
    {
      for (i=0; i < (nprefix+2); i++ )
        if ( (c=iobuf_get(ed->buf)) == -1 )
          break;
        else
          temp[i] = c;
    }
  
  gcry_cipher_decrypt (dfx->cipher_hd, temp, nprefix+2, NULL, 0);
  gcry_cipher_sync (dfx->cipher_hd);
  p = temp;
  /* log_hexdump( "prefix", temp, nprefix+2 ); */
  if (dek->symmetric
      && (p[nprefix-2] != p[nprefix] || p[nprefix-1] != p[nprefix+1]) )
    {
      rc = gpg_error (GPG_ERR_BAD_KEY);
      goto leave;
    }
  
  if ( dfx->mdc_hash )
    gcry_md_write (dfx->mdc_hash, temp, nprefix+2);

  dfx->refcount++;
  if ( ed->mdc_method )
    iobuf_push_filter ( ed->buf, mdc_decode_filter, dfx );
  else
    iobuf_push_filter ( ed->buf, decode_filter, dfx );

  proc_packets ( procctx, ed->buf );
  ed->buf = NULL;
  if ( ed->mdc_method && dfx->eof_seen == 2 )
    rc = gpg_error (GPG_ERR_INV_PACKET);
  else if ( ed->mdc_method )
    { 
      /* We used to let parse-packet.c handle the MDC packet but this
         turned out to be a problem with compressed packets: With old
         style packets there is no length information available and
         the decompressor uses an implicit end.  However we can't know
         this implicit end beforehand (:-) and thus may feed the
         decompressor with more bytes than actually needed.  It would
         be possible to unread the extra bytes but due to our weird
         iobuf system any unread is non reliable due to filters
         already popped off.  The easy and sane solution is to care
         about the MDC packet only here and never pass it to the
         packet parser.  Fortunatley the OpenPGP spec requires a
         strict format for the MDC packet so that we know that 22
         bytes are appended.  */
      int datalen = gcry_md_get_algo_dlen (ed->mdc_method);

      assert (dfx->cipher_hd);
      assert (dfx->mdc_hash);
      gcry_cipher_decrypt (dfx->cipher_hd, dfx->defer, 22, NULL, 0);
      gcry_md_write (dfx->mdc_hash, dfx->defer, 2);
      gcry_md_final (dfx->mdc_hash);

      if (dfx->defer[0] != '\xd3' || dfx->defer[1] != '\x14' )
        {
          log_error("mdc_packet with invalid encoding\n");
          rc = gpg_error (GPG_ERR_INV_PACKET);
        }
      else if (datalen != 20
               || memcmp (gcry_md_read (dfx->mdc_hash, 0),
                          dfx->defer+2,datalen ))
        rc = gpg_error (GPG_ERR_BAD_SIGNATURE);
      /* log_printhex("MDC message:", dfx->defer, 22); */
      /* log_printhex("MDC calc:", gcry_md_read (dfx->mdc_hash,0), datalen); */
    }
  
  
 leave:
  release_dfx_context (dfx);
  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;
                }
	    }
	    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"),
			 cipher_algo_to_string(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_hexdump("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;
}
/****************
 * Encrypt the file with the given userids (or ask if none
 * is supplied).
 */
int
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_algo && !RFC1991;

    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)))
      return rc;

    if( (rc=build_pk_list( remusr, &pk_list, PUBKEY_USAGE_ENC)) )
	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 ) {
	log_error(_("can't open `%s': %s\n"), filename? filename: "[stdin]",
					strerror(errno) );
	rc = G10ERR_OPEN_FILE;
	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 )
	iobuf_push_filter( out, armor_filter, &afx );

    /* 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();
	    }
	}
    }
    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"),
		 cipher_algo_to_string(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_hexdump("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) {
	/* 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 = xmalloc( sizeof *pt + strlen(s) - 1 );
	    pt->namelen = strlen(s);
	    memcpy(pt->name, s, pt->namelen );
	    xfree(s);
	}
	else { /* no filename */
	    pt = xmalloc( sizeof *pt - 1 );
	    pt->namelen = 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 */

    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 (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 */
  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 );
    return rc;
}
/* 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;
}
Пример #7
0
/****************
 * Decrypt the data, specified by ED with the key DEK.
 */
int
decrypt_data (ctrl_t ctrl, void *procctx, PKT_encrypted *ed, DEK *dek)
{
  decode_filter_ctx_t dfx;
  byte *p;
  int rc=0, c, i;
  byte temp[32];
  unsigned blocksize;
  unsigned nprefix;

  dfx = xtrycalloc (1, sizeof *dfx);
  if (!dfx)
    return gpg_error_from_syserror ();
  dfx->refcount = 1;

  if ( opt.verbose && !dek->algo_info_printed )
    {
      if (!openpgp_cipher_test_algo (dek->algo))
        log_info (_("%s encrypted data\n"),
                  openpgp_cipher_algo_name (dek->algo));
      else
        log_info (_("encrypted with unknown algorithm %d\n"), dek->algo );
      dek->algo_info_printed = 1;
    }

  {
    char buf[20];

    snprintf (buf, sizeof buf, "%d %d", ed->mdc_method, dek->algo);
    write_status_text (STATUS_DECRYPTION_INFO, buf);
  }

  if (opt.show_session_key)
    {
      char numbuf[25];
      char *hexbuf;

      snprintf (numbuf, sizeof numbuf, "%d:", dek->algo);
      hexbuf = bin2hex (dek->key, dek->keylen, NULL);
      if (!hexbuf)
        {
          rc = gpg_error_from_syserror ();
          goto leave;
        }
      log_info ("session key: '%s%s'\n", numbuf, hexbuf);
      write_status_strings (STATUS_SESSION_KEY, numbuf, hexbuf, NULL);
      xfree (hexbuf);
    }

  rc = openpgp_cipher_test_algo (dek->algo);
  if (rc)
    goto leave;
  blocksize = openpgp_cipher_get_algo_blklen (dek->algo);
  if ( !blocksize || blocksize > 16 )
    log_fatal ("unsupported blocksize %u\n", blocksize );
  nprefix = blocksize;
  if ( ed->len && ed->len < (nprefix+2) )
    {
       /* An invalid message.  We can't check that during parsing
          because we may not know the used cipher then.  */
      rc = gpg_error (GPG_ERR_INV_PACKET);
      goto leave;
    }

  if ( ed->mdc_method )
    {
      if (gcry_md_open (&dfx->mdc_hash, ed->mdc_method, 0 ))
        BUG ();
      if ( DBG_HASHING )
        gcry_md_debug (dfx->mdc_hash, "checkmdc");
    }

  rc = openpgp_cipher_open (&dfx->cipher_hd, dek->algo,
			    GCRY_CIPHER_MODE_CFB,
			    (GCRY_CIPHER_SECURE
			     | ((ed->mdc_method || dek->algo >= 100)?
				0 : GCRY_CIPHER_ENABLE_SYNC)));
  if (rc)
    {
      /* We should never get an error here cause we already checked
       * that the algorithm is available.  */
      BUG();
    }


  /* log_hexdump( "thekey", dek->key, dek->keylen );*/
  rc = gcry_cipher_setkey (dfx->cipher_hd, dek->key, dek->keylen);
  if ( gpg_err_code (rc) == GPG_ERR_WEAK_KEY )
    {
      log_info(_("WARNING: message was encrypted with"
                 " a weak key in the symmetric cipher.\n"));
      rc=0;
    }
  else if( rc )
    {
      log_error("key setup failed: %s\n", gpg_strerror (rc) );
      goto leave;
    }

  if (!ed->buf)
    {
      log_error(_("problem handling encrypted packet\n"));
      goto leave;
    }

  gcry_cipher_setiv (dfx->cipher_hd, NULL, 0);

  if ( ed->len )
    {
      for (i=0; i < (nprefix+2) && ed->len; i++, ed->len-- )
        {
          if ( (c=iobuf_get(ed->buf)) == -1 )
            break;
          else
            temp[i] = c;
        }
    }
  else
    {
      for (i=0; i < (nprefix+2); i++ )
        if ( (c=iobuf_get(ed->buf)) == -1 )
          break;
        else
          temp[i] = c;
    }

  gcry_cipher_decrypt (dfx->cipher_hd, temp, nprefix+2, NULL, 0);
  gcry_cipher_sync (dfx->cipher_hd);
  p = temp;
  /* log_hexdump( "prefix", temp, nprefix+2 ); */
  if (dek->symmetric
      && (p[nprefix-2] != p[nprefix] || p[nprefix-1] != p[nprefix+1]) )
    {
      rc = gpg_error (GPG_ERR_BAD_KEY);
      goto leave;
    }

  if ( dfx->mdc_hash )
    gcry_md_write (dfx->mdc_hash, temp, nprefix+2);

  dfx->refcount++;
  dfx->partial = ed->is_partial;
  dfx->length = ed->len;
  if ( ed->mdc_method )
    iobuf_push_filter ( ed->buf, mdc_decode_filter, dfx );
  else
    iobuf_push_filter ( ed->buf, decode_filter, dfx );

  if (opt.unwrap_encryption)
    {
      char *filename;
      estream_t fp;
      rc = get_output_file ("", 0, ed->buf, &filename, &fp);
      if (! rc)
        {
          iobuf_t output = iobuf_esopen (fp, "w", 0);
          armor_filter_context_t *afx = NULL;

          if (opt.armor)
            {
              afx = new_armor_context ();
              push_armor_filter (afx, output);
            }

          iobuf_copy (output, ed->buf);
          if ((rc = iobuf_error (ed->buf)))
            log_error (_("error reading '%s': %s\n"),
                       filename, gpg_strerror (rc));
          else if ((rc = iobuf_error (output)))
            log_error (_("error writing '%s': %s\n"),
                       filename, gpg_strerror (rc));

          iobuf_close (output);
          if (afx)
            release_armor_context (afx);
        }
    }
  else
    proc_packets (ctrl, procctx, ed->buf );

  ed->buf = NULL;
  if (dfx->eof_seen > 1 )
    rc = gpg_error (GPG_ERR_INV_PACKET);
  else if ( ed->mdc_method )
    {
      /* We used to let parse-packet.c handle the MDC packet but this
         turned out to be a problem with compressed packets: With old
         style packets there is no length information available and
         the decompressor uses an implicit end.  However we can't know
         this implicit end beforehand (:-) and thus may feed the
         decompressor with more bytes than actually needed.  It would
         be possible to unread the extra bytes but due to our weird
         iobuf system any unread is non reliable due to filters
         already popped off.  The easy and sane solution is to care
         about the MDC packet only here and never pass it to the
         packet parser.  Fortunatley the OpenPGP spec requires a
         strict format for the MDC packet so that we know that 22
         bytes are appended.  */
      int datalen = gcry_md_get_algo_dlen (ed->mdc_method);

      log_assert (dfx->cipher_hd);
      log_assert (dfx->mdc_hash);
      gcry_cipher_decrypt (dfx->cipher_hd, dfx->defer, 22, NULL, 0);
      gcry_md_write (dfx->mdc_hash, dfx->defer, 2);
      gcry_md_final (dfx->mdc_hash);

      if (   dfx->defer[0] != '\xd3'
          || dfx->defer[1] != '\x14'
          || datalen != 20
          || memcmp (gcry_md_read (dfx->mdc_hash, 0), dfx->defer+2, datalen))
        rc = gpg_error (GPG_ERR_BAD_SIGNATURE);
      /* log_printhex("MDC message:", dfx->defer, 22); */
      /* log_printhex("MDC calc:", gcry_md_read (dfx->mdc_hash,0), datalen); */
    }


 leave:
  release_dfx_context (dfx);
  return rc;
}
Пример #8
0
/*
 * Filter to do a complete public key encryption.
 */
int
encrypt_filter (void *opaque, int control,
                iobuf_t 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_CRYPTO)
            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;
}
Пример #9
0
/*
 * Encrypt the file with the given userids (or ask if none is
 * supplied).  Either FILENAME or FILEFD must be given, but not both.
 * The caller may provide a checked list of public keys in
 * PROVIDED_PKS; if not the function builds a list of keys on its own.
 *
 * Note that FILEFD is currently only used by cmd_encrypt in the the
 * not yet finished server.c.
 */
int
encrypt_crypt (ctrl_t ctrl, int filefd, const char *filename,
               strlist_t remusr, int use_symkey, pk_list_t provided_keys,
               int outputfd)
{
  iobuf_t inp = NULL;
  iobuf_t 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;
  int do_compress;

  if (filefd != -1 && filename)
    return gpg_error (GPG_ERR_INV_ARG);  /* Both given.  */

  do_compress = !!opt.compress_algo;

  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 (provided_keys)
    pk_list = provided_keys;
  else
    {
      if ((rc = build_pk_list (ctrl, remusr, &pk_list, PUBKEY_USAGE_ENC)))
        {
          release_progress_context (pfx);
          return rc;
        }
    }

  /* Prepare iobufs. */
#ifdef HAVE_W32_SYSTEM
  if (filefd == -1)
    inp = iobuf_open_fd_or_name (GNUPG_INVALID_FD, filename, "rb");
  else
    {
      inp = NULL;
      gpg_err_set_errno (ENOSYS);
    }
#else
  inp = iobuf_open_fd_or_name (filefd, filename, "rb");
#endif
  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)
    {
      char xname[64];

      rc = gpg_error_from_syserror ();
      if (filefd != -1)
        snprintf (xname, sizeof xname, "[fd %d]", filefd);
      else if (!filename)
        strcpy (xname, "[stdin]");
      else
        *xname = 0;
      log_error (_("can't open '%s': %s\n"),
                 *xname? xname : filename, gpg_strerror (rc) );
      goto leave;
    }

  if (opt.verbose)
    log_info (_("reading from '%s'\n"), iobuf_get_fname_nonnull (inp));

  handle_progress (pfx, inp, filename);

  if (opt.textmode)
    iobuf_push_filter (inp, text_filter, &tfx);

  rc = open_outfile (outputfd, filename, opt.armor? 1:0, 0, &out);
  if (rc)
    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;
        }

      /* 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_CRYPTO)
    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);

  /* Get the size of the file if possible, i.e., if it is a real file.  */
  if (filename && *filename
      && !iobuf_is_pipe_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;
      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)
        {
          compr_algo = select_algo_from_prefs (pk_list, PREFTYPE_ZIP, -1, NULL);
          if (compr_algo == -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", 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)
        {
          rc = iobuf_write (out, copy_buffer, bytes_copied);
          if (rc)
            {
              log_error ("copying input to output failed: %s\n",
                         gpg_strerror (rc));
              break;
            }
        }
      wipememory (copy_buffer, 4096); /* Burn the 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);
  if (!provided_keys)
    release_pk_list (pk_list);
  release_armor_context (afx);
  release_progress_context (pfx);
  return rc;
}
Пример #10
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;
}
Пример #11
0
int
main (int argc, char *argv[])
{
  (void) argc;
  (void) argv;

  /* A simple test to make sure filters work.  We use a static buffer
     and then add a filter in front of it that returns every other
     character.  */
  {
    char *content = "0123456789abcdefghijklm";
    iobuf_t iobuf;
    int c;
    int n;
    int rc;

    iobuf = iobuf_temp_with_content (content, strlen (content));
    rc = iobuf_push_filter (iobuf, every_other_filter, NULL);
    assert (rc == 0);

    n = 0;
    while ((c = iobuf_readbyte (iobuf)) != -1)
      {
	/* printf ("%d: %c\n", n + 1, (char) c); */
	assert (content[2 * n + 1] == c);
	n ++;
      }
    /* printf ("Got EOF after reading %d bytes (content: %d)\n", */
    /*         n, strlen (content)); */
    assert (n == strlen (content) / 2);

    iobuf_close (iobuf);
  }

  /* A simple test to check buffering.  Make sure that when we add a
     filter to a pipeline, any buffered data gets processed by the */
  {
    char *content = "0123456789abcdefghijklm";
    iobuf_t iobuf;
    int c;
    int n;
    int rc;
    int i;

    iobuf = iobuf_temp_with_content (content, strlen (content));

    n = 0;
    for (i = 0; i < 10; i ++)
      {
	c = iobuf_readbyte (iobuf);
	assert (content[i] == c);
	n ++;
      }

    rc = iobuf_push_filter (iobuf, every_other_filter, NULL);
    assert (rc == 0);

    while ((c = iobuf_readbyte (iobuf)) != -1)
      {
	/* printf ("%d: %c\n", n + 1, (char) c); */
	assert (content[2 * (n - 5) + 1] == c);
	n ++;
      }
    assert (n == 10 + (strlen (content) - 10) / 2);

    iobuf_close (iobuf);
  }


  /* A simple test to check that iobuf_read_line works.  */
  {
    /* - 3 characters plus new line
       - 4 characters plus new line
       - 5 characters plus new line
       - 5 characters, no new line
     */
    char *content = "abc\ndefg\nhijkl\nmnopq";
    iobuf_t iobuf;
    byte *buffer;
    unsigned size;
    unsigned max_len;
    int n;

    iobuf = iobuf_temp_with_content (content, strlen(content));

    /* We read a line with 3 characters plus a newline.  If we
       allocate a buffer that is 5 bytes long, then no reallocation
       should be required.  */
    size = 5;
    buffer = malloc (size);
    assert (buffer);
    max_len = 100;
    n = iobuf_read_line (iobuf, &buffer, &size, &max_len);
    assert (n == 4);
    assert (strcmp (buffer, "abc\n") == 0);
    assert (size == 5);
    assert (max_len == 100);
    free (buffer);

    /* We now read a line with 4 characters plus a newline.  This
       requires 6 bytes of storage.  We pass a buffer that is 5 bytes
       large and we allow the buffer to be grown.  */
    size = 5;
    buffer = malloc (size);
    max_len = 100;
    n = iobuf_read_line (iobuf, &buffer, &size, &max_len);
    assert (n == 5);
    assert (strcmp (buffer, "defg\n") == 0);
    assert (size >= 6);
    /* The string shouldn't have been truncated (max_len == 0).  */
    assert (max_len == 100);
    free (buffer);

    /* We now read a line with 5 characters plus a newline.  This
       requires 7 bytes of storage.  We pass a buffer that is 5 bytes
       large and we don't allow the buffer to be grown.  */
    size = 5;
    buffer = malloc (size);
    max_len = 5;
    n = iobuf_read_line (iobuf, &buffer, &size, &max_len);
    assert (n == 4);
    /* Note: the string should still have a trailing \n.  */
    assert (strcmp (buffer, "hij\n") == 0);
    assert (size == 5);
    /* The string should have been truncated (max_len == 0).  */
    assert (max_len == 0);
    free (buffer);

    /* We now read a line with 6 characters without a newline.  This
       requires 7 bytes of storage.  We pass a NULL buffer and we
       don't allow the buffer to be grown larger than 5 bytes.  */
    size = 5;
    buffer = NULL;
    max_len = 5;
    n = iobuf_read_line (iobuf, &buffer, &size, &max_len);
    assert (n == 4);
    /* Note: the string should still have a trailing \n.  */
    assert (strcmp (buffer, "mno\n") == 0);
    assert (size == 5);
    /* The string should have been truncated (max_len == 0).  */
    assert (max_len == 0);
    free (buffer);

    iobuf_close (iobuf);
  }

  {
    /* - 10 characters, EOF
       - 17 characters, EOF
     */
    char *content = "abcdefghijklmnopq";
    char *content2 = "0123456789";
    iobuf_t iobuf;
    int rc;
    int c;
    int n;
    int lastc = 0;
    struct content_filter_state *state;

    iobuf = iobuf_temp_with_content (content, strlen(content));
    rc = iobuf_push_filter (iobuf,
			    content_filter,
                            state=content_filter_new (content2));
    assert (rc == 0);

    n = 0;
    while (1)
      {
	c = iobuf_readbyte (iobuf);
	if (c == -1 && lastc == -1)
	  {
	    /* printf("Two EOFs in a row.  Done.\n");  */
	    assert (n == 27);
	    break;
	  }

	lastc = c;

	if (c == -1)
	  {
	    /* printf("After %d bytes, got EOF.\n", n); */
	    assert (n == 10 || n == 27);
	  }
	else
	  {
	    n ++;
	    /* printf ("%d: '%c' (%d)\n", n, c, c); */
	  }
      }

    iobuf_close (iobuf);
    free (state);
  }

  /* Write some data to a temporary filter.  Push a new filter.  The
     already written data should not be processed by the new
     filter.  */
  {
    iobuf_t iobuf;
    int rc;
    char *content = "0123456789";
    char *content2 = "abc";
    char buffer[4096];
    int n;

    iobuf = iobuf_temp ();
    assert (iobuf);

    rc = iobuf_write (iobuf, content, strlen (content));
    assert (rc == 0);

    rc = iobuf_push_filter (iobuf, double_filter, NULL);
    assert (rc == 0);

    /* Include a NUL.  */
    rc = iobuf_write (iobuf, content2, strlen (content2) + 1);
    assert (rc == 0);

    n = iobuf_temp_to_buffer (iobuf, buffer, sizeof (buffer));
#if 0
    printf ("Got %d bytes\n", n);
    printf ("buffer: `");
    fwrite (buffer, n, 1, stdout);
    fputc ('\'', stdout);
    fputc ('\n', stdout);
#endif

    assert (n == strlen (content) + 2 * (strlen (content2) + 1));
    assert (strcmp (buffer, "0123456789aabbcc") == 0);

    iobuf_close (iobuf);
  }

  {
    iobuf_t iobuf;
    int rc;
    char *content = "0123456789";
    int n;
    int c;
    char buffer[strlen (content)];

    iobuf = iobuf_temp_with_content (content, strlen (content));
    assert (iobuf);

    rc = iobuf_push_filter (iobuf, every_other_filter, NULL);
    assert (rc == 0);
    rc = iobuf_push_filter (iobuf, every_other_filter, NULL);
    assert (rc == 0);

    for (n = 0; (c = iobuf_get (iobuf)) != -1; n ++)
      {
	/* printf ("%d: `%c'\n", n, c);  */
	buffer[n] = c;
      }

    assert (n == 2);
    assert (buffer[0] == '3');
    assert (buffer[1] == '7');

    iobuf_close (iobuf);
  }

  return 0;
}