示例#1
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);
    }
示例#2
0
文件: pubkey-enc.c 项目: larryv/gnupg
/*
 * 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;
}