static void
initialize(void)
{
    /* The data buffer is allocated somewhat larger, so that
     * we can use this extra space (which is allocated in secure memory)
     * as a temporary hash buffer */
    rndpool = secure_alloc ? xmalloc_secure_clear(POOLSIZE+BLOCKLEN)
              : xmalloc_clear(POOLSIZE+BLOCKLEN);
    keypool = secure_alloc ? xmalloc_secure_clear(POOLSIZE+BLOCKLEN)
              : xmalloc_clear(POOLSIZE+BLOCKLEN);
    is_initialized = 1;
}
/****************
 * Open a cipher handle for use with algorithm ALGO, in mode MODE
 * and put it into secure memory if SECURE is true.
 */
CIPHER_HANDLE
cipher_open( int algo, int mode, int secure )
{
    CIPHER_HANDLE hd;
    int i;

    fast_random_poll();
    do {
        for(i=0; cipher_table[i].name; i++ )
            if( cipher_table[i].algo == algo )
                break;
    } while( !cipher_table[i].name && load_cipher_modules() );
    if( !cipher_table[i].name ) {
        log_fatal("cipher_open: algorithm %d not available\n", algo );
        return NULL;
    }

    /* ? perform selftest here and mark this with a flag in cipher_table ? */

    hd = secure ? xmalloc_secure_clear( sizeof *hd
                                        + cipher_table[i].contextsize
                                        - sizeof(PROPERLY_ALIGNED_TYPE) )
         : xmalloc_clear( sizeof *hd + cipher_table[i].contextsize
                          - sizeof(PROPERLY_ALIGNED_TYPE)  );
    hd->algo = algo;
    hd->blocksize = cipher_table[i].blocksize;
    hd->setkey	= cipher_table[i].setkey;
    hd->encrypt = cipher_table[i].encrypt;
    hd->decrypt = cipher_table[i].decrypt;

    if( mode == CIPHER_MODE_AUTO_CFB ) {
        if( algo >= 100 )
            hd->mode = CIPHER_MODE_CFB;
        else
            hd->mode = CIPHER_MODE_PHILS_CFB;
    }
    else
        hd->mode = mode;

#ifdef ALLOW_DUMMY
    if( algo == CIPHER_ALGO_DUMMY )
        hd->mode = CIPHER_MODE_DUMMY;
#endif

    return hd;
}
Example #3
0
/* Return a new DEK object using the string-to-key specifier S2K.  Use
   KEYID and PUBKEY_ALGO to prompt the user.  Returns NULL is the user
   selected to cancel the passphrase entry and if CANCELED is not
   NULL, sets it to true.

   MODE 0:  Allow cached passphrase
        1:  Ignore cached passphrase
        2:  Ditto, but create a new key
        3:  Allow cached passphrase; use the S2K salt as the cache ID
        4:  Ditto, but create a new key
*/
DEK *
passphrase_to_dek_ext (u32 *keyid, int pubkey_algo,
                       int cipher_algo, STRING2KEY *s2k, int mode,
                       const char *tryagain_text,
                       const char *custdesc, const char *custprompt,
                       int *canceled)
{
  char *pw = NULL;
  DEK *dek;
  STRING2KEY help_s2k;
  int dummy_canceled;
  char s2k_cacheidbuf[1+16+1], *s2k_cacheid = NULL;

  if (!canceled)
    canceled = &dummy_canceled;
  *canceled = 0;

  if ( !s2k )
    {
      log_assert (mode != 3 && mode != 4);
      /* This is used for the old rfc1991 mode
       * Note: This must match the code in encode.c with opt.rfc1991 set */
      s2k = &help_s2k;
      s2k->mode = 0;
      s2k->hash_algo = S2K_DIGEST_ALGO;
    }

  /* Create a new salt or what else to be filled into the s2k for a
     new key.  */
  if ((mode == 2 || mode == 4) && (s2k->mode == 1 || s2k->mode == 3))
    {
      gcry_randomize (s2k->salt, 8, GCRY_STRONG_RANDOM);
      if ( s2k->mode == 3 )
        {
          /* We delay the encoding until it is really needed.  This is
             if we are going to dynamically calibrate it, we need to
             call out to gpg-agent and that should not be done during
             option processing in main().  */
          if (!opt.s2k_count)
            opt.s2k_count = encode_s2k_iterations (0);
          s2k->count = opt.s2k_count;
        }
    }

  /* If we do not have a passphrase available in NEXT_PW and status
     information are request, we print them now. */
  if ( !next_pw && is_status_enabled() )
    {
      char buf[50];

      if ( keyid )
        {
          emit_status_need_passphrase (keyid,
                                       keyid[2] && keyid[3]? keyid+2:NULL,
                                       pubkey_algo);
	}
      else
        {
          snprintf (buf, sizeof buf -1, "%d %d %d",
                    cipher_algo, s2k->mode, s2k->hash_algo );
          write_status_text ( STATUS_NEED_PASSPHRASE_SYM, buf );
	}
    }

  /* If we do have a keyID, we do not have a passphrase available in
     NEXT_PW, we are not running in batch mode and we do not want to
     ignore the passphrase cache (mode!=1), print a prompt with
     information on that key. */
  if ( keyid && !opt.batch && !next_pw && mode!=1 )
    {
      PKT_public_key *pk = xmalloc_clear( sizeof *pk );
      char *p;

      p = get_user_id_native(keyid);
      tty_printf ("\n");
      tty_printf (_("You need a passphrase to unlock the secret key for\n"
                    "user: \"%s\"\n"),p);
      xfree(p);

      if ( !get_pubkey( pk, keyid ) )
        {
          const char *s = openpgp_pk_algo_name ( pk->pubkey_algo );

          tty_printf (_("%u-bit %s key, ID %s, created %s"),
                      nbits_from_pk( pk ), s?s:"?", keystr(keyid),
                      strtimestamp(pk->timestamp) );
          if ( keyid[2] && keyid[3]
               && keyid[0] != keyid[2] && keyid[1] != keyid[3] )
            {
              if ( keystrlen () > 10 )
                {
                  tty_printf ("\n");
                  tty_printf (_("         (subkey on main key ID %s)"),
                              keystr(&keyid[2]) );
                }
              else
                tty_printf ( _(" (main key ID %s)"), keystr(&keyid[2]) );
            }
          tty_printf("\n");
	}

      tty_printf("\n");
      free_public_key (pk);
    }

  if ( next_pw )
    {
      /* Simply return the passphrase we already have in NEXT_PW. */
      pw = next_pw;
      next_pw = NULL;
    }
  else if ( have_static_passphrase () )
    {
      /* Return the passphrase we have stored in FD_PASSWD. */
      pw = xmalloc_secure ( strlen(fd_passwd)+1 );
      strcpy ( pw, fd_passwd );
    }
  else
    {
      if ((mode == 3 || mode == 4) && (s2k->mode == 1 || s2k->mode == 3))
	{
	  memset (s2k_cacheidbuf, 0, sizeof s2k_cacheidbuf);
	  *s2k_cacheidbuf = 'S';
	  bin2hex (s2k->salt, 8, s2k_cacheidbuf + 1);
	  s2k_cacheid = s2k_cacheidbuf;
	}

      if (opt.pinentry_mode == PINENTRY_MODE_LOOPBACK)
        {
          char buf[32];

          snprintf (buf, sizeof (buf), "%u", 100);
          write_status_text (STATUS_INQUIRE_MAXLEN, buf);
        }

      /* Divert to the gpg-agent. */
      pw = passphrase_get (keyid, mode == 2, s2k_cacheid,
                           (mode == 2 || mode == 4)? opt.passphrase_repeat : 0,
                           tryagain_text, custdesc, custprompt, canceled);
      if (*canceled)
        {
          xfree (pw);
	  write_status( STATUS_MISSING_PASSPHRASE );
          return NULL;
        }
    }

  if ( !pw || !*pw )
    write_status( STATUS_MISSING_PASSPHRASE );

  /* Hash the passphrase and store it in a newly allocated DEK object.
     Keep a copy of the passphrase in LAST_PW for use by
     get_last_passphrase(). */
  dek = xmalloc_secure_clear ( sizeof *dek );
  dek->algo = cipher_algo;
  if ( (!pw || !*pw) && (mode == 2 || mode == 4))
    dek->keylen = 0;
  else
    {
      gpg_error_t err;

      dek->keylen = openpgp_cipher_get_algo_keylen (dek->algo);
      if (!(dek->keylen > 0 && dek->keylen <= DIM(dek->key)))
        BUG ();
      err = gcry_kdf_derive (pw, strlen (pw),
                             s2k->mode == 3? GCRY_KDF_ITERSALTED_S2K :
                             s2k->mode == 1? GCRY_KDF_SALTED_S2K :
                             /* */           GCRY_KDF_SIMPLE_S2K,
                             s2k->hash_algo, s2k->salt, 8,
                             S2K_DECODE_COUNT(s2k->count),
                             dek->keylen, dek->key);
      if (err)
        {
          log_error ("gcry_kdf_derive failed: %s", gpg_strerror (err));
          xfree (pw);
          xfree (dek);
	  write_status( STATUS_MISSING_PASSPHRASE );
          return NULL;
        }
    }
  if (s2k_cacheid)
    memcpy (dek->s2k_cacheid, s2k_cacheid, sizeof dek->s2k_cacheid);
  xfree(last_pw);
  last_pw = pw;
  return dek;
}
/****************
 * 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;
}
Example #6
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;
}
Example #7
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;
}
Example #8
0
/* Return a new DEK object using the string-to-key specifier S2K.
 * Returns NULL if the user canceled the passphrase entry and if
 * CANCELED is not NULL, sets it to true.
 *
 * If CREATE is true a new passphrase sll be created.  If NOCACHE is
 * true the symmetric key caching will not be used.  */
DEK *
passphrase_to_dek (int cipher_algo, STRING2KEY *s2k,
                   int create, int nocache,
                   const char *tryagain_text, int *canceled)
{
  char *pw = NULL;
  DEK *dek;
  STRING2KEY help_s2k;
  int dummy_canceled;
  char s2k_cacheidbuf[1+16+1];
  char *s2k_cacheid = NULL;

  if (!canceled)
    canceled = &dummy_canceled;
  *canceled = 0;

  if ( !s2k )
    {
      log_assert (create && !nocache);
      /* This is used for the old rfc1991 mode
       * Note: This must match the code in encode.c with opt.rfc1991 set */
      memset (&help_s2k, 0, sizeof (help_s2k));
      s2k = &help_s2k;
      s2k->hash_algo = S2K_DIGEST_ALGO;
    }

  /* Create a new salt or what else to be filled into the s2k for a
     new key.  */
  if (create && (s2k->mode == 1 || s2k->mode == 3))
    {
      gcry_randomize (s2k->salt, 8, GCRY_STRONG_RANDOM);
      if ( s2k->mode == 3 )
        {
          /* We delay the encoding until it is really needed.  This is
             if we are going to dynamically calibrate it, we need to
             call out to gpg-agent and that should not be done during
             option processing in main().  */
          if (!opt.s2k_count)
            opt.s2k_count = encode_s2k_iterations (0);
          s2k->count = opt.s2k_count;
        }
    }

  /* If we do not have a passphrase available in NEXT_PW and status
     information are request, we print them now. */
  if ( !next_pw && is_status_enabled() )
    {
      char buf[50];

      snprintf (buf, sizeof buf, "%d %d %d",
                cipher_algo, s2k->mode, s2k->hash_algo );
      write_status_text ( STATUS_NEED_PASSPHRASE_SYM, buf );
    }

  if ( next_pw )
    {
      /* Simply return the passphrase we already have in NEXT_PW. */
      pw = next_pw;
      next_pw = NULL;
    }
  else if ( have_static_passphrase () )
    {
      /* Return the passphrase we have stored in FD_PASSWD. */
      pw = xmalloc_secure ( strlen(fd_passwd)+1 );
      strcpy ( pw, fd_passwd );
    }
  else
    {
      if (!nocache && (s2k->mode == 1 || s2k->mode == 3))
	{
	  memset (s2k_cacheidbuf, 0, sizeof s2k_cacheidbuf);
	  *s2k_cacheidbuf = 'S';
	  bin2hex (s2k->salt, 8, s2k_cacheidbuf + 1);
	  s2k_cacheid = s2k_cacheidbuf;
	}

      if (opt.pinentry_mode == PINENTRY_MODE_LOOPBACK)
        {
          char buf[32];

          snprintf (buf, sizeof (buf), "%u", 100);
          write_status_text (STATUS_INQUIRE_MAXLEN, buf);
        }

      /* Divert to the gpg-agent. */
      pw = passphrase_get (create && nocache, s2k_cacheid,
                           create? opt.passphrase_repeat : 0,
                           tryagain_text, canceled);
      if (*canceled)
        {
          xfree (pw);
	  write_status( STATUS_MISSING_PASSPHRASE );
          return NULL;
        }
    }

  if ( !pw || !*pw )
    write_status( STATUS_MISSING_PASSPHRASE );

  /* Hash the passphrase and store it in a newly allocated DEK object.
     Keep a copy of the passphrase in LAST_PW for use by
     get_last_passphrase(). */
  dek = xmalloc_secure_clear ( sizeof *dek );
  dek->algo = cipher_algo;
  if ( (!pw || !*pw) && create)
    dek->keylen = 0;
  else
    {
      gpg_error_t err;

      dek->keylen = openpgp_cipher_get_algo_keylen (dek->algo);
      if (!(dek->keylen > 0 && dek->keylen <= DIM(dek->key)))
        BUG ();
      err = gcry_kdf_derive (pw, strlen (pw),
                             s2k->mode == 3? GCRY_KDF_ITERSALTED_S2K :
                             s2k->mode == 1? GCRY_KDF_SALTED_S2K :
                             /* */           GCRY_KDF_SIMPLE_S2K,
                             s2k->hash_algo, s2k->salt, 8,
                             S2K_DECODE_COUNT(s2k->count),
                             dek->keylen, dek->key);
      if (err)
        {
          log_error ("gcry_kdf_derive failed: %s", gpg_strerror (err));
          xfree (pw);
          xfree (dek);
	  write_status( STATUS_MISSING_PASSPHRASE );
          return NULL;
        }
    }
  if (s2k_cacheid)
    memcpy (dek->s2k_cacheid, s2k_cacheid, sizeof dek->s2k_cacheid);
  xfree(last_pw);
  last_pw = pw;
  return dek;
}