Esempio n. 1
0
/*
 * Write pubkey-enc packets from the list of PKs to OUT.
 */
static int
write_pubkey_enc_from_list (ctrl_t ctrl, PK_LIST pk_list, DEK *dek, iobuf_t out)
{
  if (opt.throw_keyids && (PGP7 || PGP8))
    {
      log_info(_("option '%s' may not be used in %s mode\n"),
               "--throw-keyids",
               gnupg_compliance_option_string (opt.compliance));
      compliance_failure();
    }

  for ( ; pk_list; pk_list = pk_list->next )
    {
      PKT_public_key *pk = pk_list->pk;
      int throw_keyid = (opt.throw_keyids || (pk_list->flags&1));
      int rc = write_pubkey_enc (ctrl, pk, throw_keyid, dek, out);
      if (rc)
        return rc;
    }

  return 0;
}
Esempio n. 2
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
 * 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;
  int compliant;

  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)))
        {
          release_progress_context (pfx);
          return rc;
        }
    }

  /* Prepare iobufs. */
#ifdef HAVE_W32_SYSTEM
  if (filefd == -1)
    inp = iobuf_open (filename);
  else
    {
      inp = NULL;
      gpg_err_set_errno (ENOSYS);
    }
#else
  if (filefd == GNUPG_INVALID_FD)
    inp = iobuf_open (filename);
  else
    inp = iobuf_fdopen_nc (FD2INT(filefd), "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;
    }

  /* Check compliance.  */
  if (! gnupg_cipher_is_allowed (opt.compliance, 1, cfx.dek->algo,
                                 GCRY_CIPHER_MODE_CFB))
    {
      log_error (_("cipher algorithm '%s' may not be used in %s mode\n"),
		 openpgp_cipher_algo_name (cfx.dek->algo),
		 gnupg_compliance_option_string (opt.compliance));
      rc = gpg_error (GPG_ERR_CIPHER_ALGO);
      goto leave;
    }

  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);
      goto leave;
    }

  compliant = gnupg_cipher_is_compliant (CO_DE_VS, cfx.dek->algo,
                                         GCRY_CIPHER_MODE_CFB);

  {
    pk_list_t pkr;

    for (pkr = pk_list; pkr; pkr = pkr->next)
      {
        PKT_public_key *pk = pkr->pk;
        unsigned int nbits = nbits_from_pk (pk);

        if (!gnupg_pk_is_compliant (opt.compliance,
                                    pk->pubkey_algo, pk->pkey, nbits, NULL))
          log_info (_("WARNING: key %s is not suitable for encryption"
                      " in %s mode\n"),
                    keystr_from_pk (pk),
                    gnupg_compliance_option_string (opt.compliance));

        if (compliant
            && !gnupg_pk_is_compliant (CO_DE_VS, pk->pubkey_algo, pk->pkey,
                                       nbits, NULL))
          compliant = 0;
      }

  }

  if (compliant)
    write_status_strings (STATUS_ENCRYPTION_COMPLIANCE_MODE,
                          gnupg_status_compliance_flag (CO_DE_VS),
                          NULL);

  cfx.dek->use_aead = use_aead (pk_list, cfx.dek->algo);
  if (!cfx.dek->use_aead)
    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 or AEAD.  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 || cfx.dek->use_aead)
      && 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 (cfx.dek->key, cfx.dek->keylen, "DEK is: ");

  rc = write_pubkey_enc_from_list (ctrl, 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, cfx.dek->use_aead,
                                            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 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.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;

  /* Register the cipher filter. */
  iobuf_push_filter (out,
                     cfx.dek->use_aead? cipher_filter_aead
                     /**/             : cipher_filter_cfb,
                     &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 || cfx.dek->use_aead))
            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, NULL);
  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;
}
Esempio n. 3
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;
}
Esempio n. 4
0
/* Check a signature.
 *
 * Looks up the public key that created the signature (SIG->KEYID)
 * from the key db.  Makes sure that the signature is valid (it was
 * not created prior to the key, the public key was created in the
 * past, and the signature does not include any unsupported critical
 * features), finishes computing the hash of the signature data, and
 * checks that the signature verifies the digest.  If the key that
 * generated the signature is a subkey, this function also verifies
 * that there is a valid backsig from the subkey to the primary key.
 * Finally, if status fd is enabled and the signature class is 0x00 or
 * 0x01, then a STATUS_SIG_ID is emitted on the status fd.
 *
 * SIG is the signature to check.
 *
 * DIGEST contains a valid hash context that already includes the
 * signed data.  This function adds the relevant meta-data from the
 * signature packet to compute the final hash.  (See Section 5.2 of
 * RFC 4880: "The concatenation of the data being signed and the
 * signature data from the version number through the hashed subpacket
 * data (inclusive) is hashed.")
 *
 * If R_EXPIREDATE is not NULL, R_EXPIREDATE is set to the key's
 * expiry.
 *
 * If R_EXPIRED is not NULL, *R_EXPIRED is set to 1 if PK has expired
 * (0 otherwise).  Note: PK being expired does not cause this function
 * to fail.
 *
 * If R_REVOKED is not NULL, *R_REVOKED is set to 1 if PK has been
 * revoked (0 otherwise).  Note: PK being revoked does not cause this
 * function to fail.
 *
 * If R_PK is not NULL, the public key is stored at that address if it
 * was found; other wise NULL is stored.
 *
 * Returns 0 on success.  An error code otherwise.  */
gpg_error_t
check_signature2 (ctrl_t ctrl,
                  PKT_signature *sig, gcry_md_hd_t digest, u32 *r_expiredate,
		  int *r_expired, int *r_revoked, PKT_public_key **r_pk)
{
  int rc=0;
  PKT_public_key *pk;

  if (r_expiredate)
    *r_expiredate = 0;
  if (r_expired)
    *r_expired = 0;
  if (r_revoked)
    *r_revoked = 0;
  if (r_pk)
    *r_pk = NULL;

  pk = xtrycalloc (1, sizeof *pk);
  if (!pk)
    return gpg_error_from_syserror ();

  if  ((rc=openpgp_md_test_algo(sig->digest_algo)))
    {
      /* We don't have this digest. */
    }
  else if (!gnupg_digest_is_allowed (opt.compliance, 0, sig->digest_algo))
    {
      /* Compliance failure.  */
      log_info (_("digest algorithm '%s' may not be used in %s mode\n"),
                gcry_md_algo_name (sig->digest_algo),
                gnupg_compliance_option_string (opt.compliance));
      rc = gpg_error (GPG_ERR_DIGEST_ALGO);
    }
  else if ((rc=openpgp_pk_test_algo(sig->pubkey_algo)))
    {
      /* We don't have this pubkey algo. */
    }
  else if (!gcry_md_is_enabled (digest,sig->digest_algo))
    {
      /* Sanity check that the md has a context for the hash that the
       * sig is expecting.  This can happen if a onepass sig header
       * does not match the actual sig, and also if the clearsign
       * "Hash:" header is missing or does not match the actual sig. */
      log_info(_("WARNING: signature digest conflict in message\n"));
      rc = gpg_error (GPG_ERR_GENERAL);
    }
  else if (get_pubkey_for_sig (ctrl, pk, sig))
    rc = gpg_error (GPG_ERR_NO_PUBKEY);
  else if (!gnupg_pk_is_allowed (opt.compliance, PK_USE_VERIFICATION,
                                 pk->pubkey_algo, pk->pkey,
                                 nbits_from_pk (pk),
                                 NULL))
    {
      /* Compliance failure.  */
      log_error (_("key %s may not be used for signing in %s mode\n"),
                 keystr_from_pk (pk),
                 gnupg_compliance_option_string (opt.compliance));
      rc = gpg_error (GPG_ERR_PUBKEY_ALGO);
    }
  else if (!pk->flags.valid)
    {
      /* You cannot have a good sig from an invalid key.  */
      rc = gpg_error (GPG_ERR_BAD_PUBKEY);
    }
  else
    {
      if (r_expiredate)
        *r_expiredate = pk->expiredate;

      rc = check_signature_end (pk, sig, digest, r_expired, r_revoked, NULL);

      /* Check the backsig.  This is a back signature (0x19) from
       * the subkey on the primary key.  The idea here is that it
       * should not be possible for someone to "steal" subkeys and
       * claim them as their own.  The attacker couldn't actually
       * use the subkey, but they could try and claim ownership of
       * any signatures issued by it.  */
      if (!rc && !pk->flags.primary && pk->flags.backsig < 2)
        {
          if (!pk->flags.backsig)
            {
              log_info (_("WARNING: signing subkey %s is not"
                          " cross-certified\n"),keystr_from_pk(pk));
              log_info (_("please see %s for more information\n"),
                        "https://gnupg.org/faq/subkey-cross-certify.html");
              /* The default option --require-cross-certification
               * makes this warning an error.  */
              if (opt.flags.require_cross_cert)
                rc = gpg_error (GPG_ERR_GENERAL);
            }
          else if(pk->flags.backsig == 1)
            {
              log_info (_("WARNING: signing subkey %s has an invalid"
                          " cross-certification\n"), keystr_from_pk(pk));
              rc = gpg_error (GPG_ERR_GENERAL);
            }
        }

    }

    if( !rc && sig->sig_class < 2 && is_status_enabled() ) {
	/* This signature id works best with DLP algorithms because
	 * they use a random parameter for every signature.  Instead of
	 * this sig-id we could have also used the hash of the document
	 * and the timestamp, but the drawback of this is, that it is
	 * not possible to sign more than one identical document within
	 * one second.	Some remote batch processing applications might
	 * like this feature here.
         *
         * Note that before 2.0.10, we used RIPE-MD160 for the hash
         * and accidentally didn't include the timestamp and algorithm
         * information in the hash.  Given that this feature is not
         * commonly used and that a replay attacks detection should
         * not solely be based on this feature (because it does not
         * work with RSA), we take the freedom and switch to SHA-1
         * with 2.0.10 to take advantage of hardware supported SHA-1
         * implementations.  We also include the missing information
         * in the hash.  Note also the SIG_ID as computed by gpg 1.x
         * and gpg 2.x didn't matched either because 2.x used to print
         * MPIs not in PGP format.  */
	u32 a = sig->timestamp;
	int nsig = pubkey_get_nsig( sig->pubkey_algo );
	unsigned char *p, *buffer;
        size_t n, nbytes;
        int i;
        char hashbuf[20];  /* We use SHA-1 here.  */

      nbytes = 6;
      for (i=0; i < nsig; i++ )
        {
          if (gcry_mpi_print (GCRYMPI_FMT_USG, NULL, 0, &n, sig->data[i]))
            BUG();
          nbytes += n;
        }

      /* Make buffer large enough to be later used as output buffer.  */
      if (nbytes < 100)
        nbytes = 100;
      nbytes += 10;  /* Safety margin.  */

      /* Fill and hash buffer.  */
      buffer = p = xmalloc (nbytes);
      *p++ = sig->pubkey_algo;
      *p++ = sig->digest_algo;
      *p++ = (a >> 24) & 0xff;
      *p++ = (a >> 16) & 0xff;
      *p++ = (a >>  8) & 0xff;
      *p++ =  a & 0xff;
      nbytes -= 6;
      for (i=0; i < nsig; i++ )
        {
          if (gcry_mpi_print (GCRYMPI_FMT_PGP, p, nbytes, &n, sig->data[i]))
            BUG();
          p += n;
          nbytes -= n;
        }
      gcry_md_hash_buffer (GCRY_MD_SHA1, hashbuf, buffer, p-buffer);

      p = make_radix64_string (hashbuf, 20);
      sprintf (buffer, "%s %s %lu",
               p, strtimestamp (sig->timestamp), (ulong)sig->timestamp);
      xfree (p);
      write_status_text (STATUS_SIG_ID, buffer);
      xfree (buffer);
    }
Esempio n. 5
0
/*
 * Get the session key from a pubkey enc packet and return it in DEK,
 * which should have been allocated in secure memory by the caller.
 */
gpg_error_t
get_session_key (ctrl_t ctrl, PKT_pubkey_enc * k, DEK * dek)
{
  PKT_public_key *sk = NULL;
  int rc;

  if (DBG_CLOCK)
    log_clock ("get_session_key enter");

  rc = openpgp_pk_test_algo2 (k->pubkey_algo, PUBKEY_USAGE_ENC);
  if (rc)
    goto leave;

  if ((k->keyid[0] || k->keyid[1]) && !opt.try_all_secrets)
    {
      sk = xmalloc_clear (sizeof *sk);
      sk->pubkey_algo = k->pubkey_algo; /* We want a pubkey with this algo.  */
      if (!(rc = get_seckey (ctrl, sk, k->keyid)))
        {
          /* Check compliance.  */
          if (! gnupg_pk_is_allowed (opt.compliance, PK_USE_DECRYPTION,
                                     sk->pubkey_algo,
                                     sk->pkey, nbits_from_pk (sk), NULL))
            {
              log_info (_("key %s is not suitable for decryption"
                          " in %s mode\n"),
                        keystr_from_pk (sk),
                        gnupg_compliance_option_string (opt.compliance));
              rc = gpg_error (GPG_ERR_PUBKEY_ALGO);
            }
          else
            rc = get_it (ctrl, k, dek, sk, k->keyid);
        }
    }
  else if (opt.skip_hidden_recipients)
    rc = gpg_error (GPG_ERR_NO_SECKEY);
  else  /* Anonymous receiver: Try all available secret keys.  */
    {
      void *enum_context = NULL;
      u32 keyid[2];

      for (;;)
        {
          free_public_key (sk);
          sk = xmalloc_clear (sizeof *sk);
          rc = enum_secret_keys (ctrl, &enum_context, sk);
          if (rc)
            {
              rc = GPG_ERR_NO_SECKEY;
              break;
            }
          if (sk->pubkey_algo != k->pubkey_algo)
            continue;
          if (!(sk->pubkey_usage & PUBKEY_USAGE_ENC))
            continue;
          keyid_from_pk (sk, keyid);
          if (!opt.quiet)
            log_info (_("anonymous recipient; trying secret key %s ...\n"),
                      keystr (keyid));

          /* Check compliance.  */
          if (! gnupg_pk_is_allowed (opt.compliance, PK_USE_DECRYPTION,
                                     sk->pubkey_algo,
                                     sk->pkey, nbits_from_pk (sk), NULL))
            {
              log_info (_("key %s is not suitable for decryption"
                          " in %s mode\n"),
                          keystr_from_pk (sk),
                          gnupg_compliance_option_string (opt.compliance));
              continue;
            }

          rc = get_it (ctrl, k, dek, sk, keyid);
          if (!rc)
            {
              if (!opt.quiet)
                log_info (_("okay, we are the anonymous recipient.\n"));
              break;
            }
          else if (gpg_err_code (rc) == GPG_ERR_FULLY_CANCELED)
            break; /* Don't try any more secret keys.  */
        }
      enum_secret_keys (ctrl, &enum_context, NULL);  /* free context */
    }

 leave:
  free_public_key (sk);
  if (DBG_CLOCK)
    log_clock ("get_session_key leave");
  return rc;
}