summaryrefslogtreecommitdiffstats
path: root/g10/sig-check.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 16:14:06 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 16:14:06 +0000
commiteee068778cb28ecf3c14e1bf843a95547d72c42d (patch)
tree0e07b30ddc5ea579d682d5dbe57998200d1c9ab7 /g10/sig-check.c
parentInitial commit. (diff)
downloadgnupg2-eee068778cb28ecf3c14e1bf843a95547d72c42d.tar.xz
gnupg2-eee068778cb28ecf3c14e1bf843a95547d72c42d.zip
Adding upstream version 2.2.40.upstream/2.2.40upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'g10/sig-check.c')
-rw-r--r--g10/sig-check.c1229
1 files changed, 1229 insertions, 0 deletions
diff --git a/g10/sig-check.c b/g10/sig-check.c
new file mode 100644
index 0000000..eeaf6f0
--- /dev/null
+++ b/g10/sig-check.c
@@ -0,0 +1,1229 @@
+/* sig-check.c - Check a signature
+ * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003,
+ * 2004, 2006 Free Software Foundation, Inc.
+ * Copyright (C) 2015, 2016 g10 Code GmbH
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "gpg.h"
+#include "../common/util.h"
+#include "packet.h"
+#include "keydb.h"
+#include "main.h"
+#include "../common/status.h"
+#include "../common/i18n.h"
+#include "options.h"
+#include "pkglue.h"
+#include "../common/compliance.h"
+
+static int check_signature_end (PKT_public_key *pk, PKT_signature *sig,
+ gcry_md_hd_t digest,
+ int *r_expired, int *r_revoked,
+ PKT_public_key *ret_pk);
+
+static int check_signature_end_simple (PKT_public_key *pk, PKT_signature *sig,
+ gcry_md_hd_t digest);
+
+
+/* Statistics for signature verification. */
+struct
+{
+ unsigned int total; /* Total number of verifications. */
+ unsigned int cached; /* Number of seen cache entries. */
+ unsigned int goodsig;/* Number of good verifications from the cache. */
+ unsigned int badsig; /* Number of bad verifications from the cache. */
+} cache_stats;
+
+
+/* Dump verification stats. */
+void
+sig_check_dump_stats (void)
+{
+ log_info ("sig_cache: total=%u cached=%u good=%u bad=%u\n",
+ cache_stats.total, cache_stats.cached,
+ cache_stats.goodsig, cache_stats.badsig);
+}
+
+
+static gpg_error_t
+check_key_verify_compliance (PKT_public_key *pk)
+{
+ gpg_error_t err = 0;
+
+ if (!gnupg_pk_is_allowed (opt.compliance, PK_USE_VERIFICATION,
+ pk->pubkey_algo, 0, pk->pkey,
+ nbits_from_pk (pk),
+ NULL))
+ {
+ /* Compliance failure. */
+ log_info (_("key %s may not be used for signing in %s mode\n"),
+ keystr_from_pk (pk),
+ gnupg_compliance_option_string (opt.compliance));
+ if (opt.flags.override_compliance_check)
+ log_info (_("continuing verification anyway due to option %s\n"),
+ "--override-compliance-failure");
+ else
+ {
+ log_inc_errorcount (); /* We used log info above. */
+ err = gpg_error (GPG_ERR_PUBKEY_ALGO);
+ }
+ }
+
+ return err;
+}
+
+
+
+/* Check a signature. This is shorthand for check_signature2 with
+ the unnamed arguments passed as NULL. */
+int
+check_signature (ctrl_t ctrl, PKT_signature *sig, gcry_md_hd_t digest)
+{
+ return check_signature2 (ctrl, sig, digest, NULL, NULL, NULL, NULL, NULL);
+}
+
+
+/* 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 FORCED_PK is not NULL this public key is used to verify the
+ * signature and no other public key is looked up.
+ *
+ * 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,
+ PKT_public_key *forced_pk,
+ 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, forced_pk))
+ rc = gpg_error (GPG_ERR_NO_PUBKEY);
+ else if ((rc = check_key_verify_compliance (pk)))
+ ;/* Compliance failure. */
+ 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];
+
+ 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);
+ }
+
+ if (r_pk)
+ *r_pk = pk;
+ else
+ {
+ release_public_key_parts (pk);
+ xfree (pk);
+ }
+
+ return rc;
+}
+
+
+/* The signature SIG was generated with the public key PK. Check
+ * whether the signature is valid in the following sense:
+ *
+ * - Make sure the public key was created before the signature was
+ * generated.
+ *
+ * - Make sure the public key was created in the past
+ *
+ * - Check whether PK has expired (set *R_EXPIRED to 1 if so and 0
+ * otherwise)
+ *
+ * - Check whether PK has been revoked (set *R_REVOKED to 1 if so
+ * and 0 otherwise).
+ *
+ * If either of the first two tests fail, returns an error code.
+ * Otherwise returns 0. (Thus, this function doesn't fail if the
+ * public key is expired or revoked.) */
+static int
+check_signature_metadata_validity (PKT_public_key *pk, PKT_signature *sig,
+ int *r_expired, int *r_revoked)
+{
+ u32 cur_time;
+
+ if (r_expired)
+ *r_expired = 0;
+ if (r_revoked)
+ *r_revoked = 0;
+
+ if (pk->timestamp > sig->timestamp )
+ {
+ ulong d = pk->timestamp - sig->timestamp;
+ if ( d < 86400 )
+ {
+ log_info (ngettext
+ ("public key %s is %lu second newer than the signature\n",
+ "public key %s is %lu seconds newer than the signature\n",
+ d), keystr_from_pk (pk), d);
+ }
+ else
+ {
+ d /= 86400;
+ log_info (ngettext
+ ("public key %s is %lu day newer than the signature\n",
+ "public key %s is %lu days newer than the signature\n",
+ d), keystr_from_pk (pk), d);
+ }
+ if (!opt.ignore_time_conflict)
+ return GPG_ERR_TIME_CONFLICT; /* pubkey newer than signature. */
+ }
+
+ cur_time = make_timestamp ();
+ if (pk->timestamp > cur_time)
+ {
+ ulong d = pk->timestamp - cur_time;
+ if (d < 86400)
+ {
+ log_info (ngettext("key %s was created %lu second"
+ " in the future (time warp or clock problem)\n",
+ "key %s was created %lu seconds"
+ " in the future (time warp or clock problem)\n",
+ d), keystr_from_pk (pk), d);
+ }
+ else
+ {
+ d /= 86400;
+ log_info (ngettext("key %s was created %lu day"
+ " in the future (time warp or clock problem)\n",
+ "key %s was created %lu days"
+ " in the future (time warp or clock problem)\n",
+ d), keystr_from_pk (pk), d);
+ }
+ if (!opt.ignore_time_conflict)
+ return GPG_ERR_TIME_CONFLICT;
+ }
+
+ /* Check whether the key has expired. We check the has_expired
+ * flag which is set after a full evaluation of the key (getkey.c)
+ * as well as a simple compare to the current time in case the
+ * merge has for whatever reasons not been done. */
+ if (pk->has_expired || (pk->expiredate && pk->expiredate < cur_time))
+ {
+ char buf[11];
+ if (opt.verbose)
+ log_info (_("Note: signature key %s expired %s\n"),
+ keystr_from_pk(pk), asctimestamp( pk->expiredate ) );
+ snprintf (buf, sizeof buf, "%lu",(ulong)pk->expiredate);
+ write_status_text (STATUS_KEYEXPIRED, buf);
+ if (r_expired)
+ *r_expired = 1;
+ }
+
+ if (pk->flags.revoked)
+ {
+ if (opt.verbose)
+ log_info (_("Note: signature key %s has been revoked\n"),
+ keystr_from_pk(pk));
+ if (r_revoked)
+ *r_revoked=1;
+ }
+
+ return 0;
+}
+
+
+/* Finish generating a signature and check it. Concretely: make 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), finish computing the
+ * digest by adding the relevant data from the signature packet, and
+ * check that the signature verifies the digest.
+ *
+ * DIGEST contains a hash context, which has already hashed 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.")
+ *
+ * SIG is the signature to check.
+ *
+ * PK is the public key used to generate the signature.
+ *
+ * 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 RET_PK is not NULL, PK is copied into RET_PK on success.
+ *
+ * Returns 0 on success. An error code other. */
+static int
+check_signature_end (PKT_public_key *pk, PKT_signature *sig,
+ gcry_md_hd_t digest,
+ int *r_expired, int *r_revoked, PKT_public_key *ret_pk)
+{
+ int rc = 0;
+
+ if ((rc = check_signature_metadata_validity (pk, sig,
+ r_expired, r_revoked)))
+ return rc;
+
+ if ((rc = check_signature_end_simple (pk, sig, digest)))
+ return rc;
+
+ if (!rc && ret_pk)
+ copy_public_key(ret_pk,pk);
+
+ return rc;
+}
+
+
+/* This function is similar to check_signature_end, but it only checks
+ * whether the signature was generated by PK. It does not check
+ * expiration, revocation, etc. */
+static int
+check_signature_end_simple (PKT_public_key *pk, PKT_signature *sig,
+ gcry_md_hd_t digest)
+{
+ gcry_mpi_t result = NULL;
+ int rc = 0;
+
+ if (!opt.flags.allow_weak_digest_algos)
+ {
+ if (is_weak_digest (sig->digest_algo))
+ {
+ print_digest_rejected_note (sig->digest_algo);
+ return GPG_ERR_DIGEST_ALGO;
+ }
+ }
+
+ /* For key signatures check that the key has a cert usage. We may
+ * do this only for subkeys because the primary may always issue key
+ * signature. The latter may not be reflected in the pubkey_usage
+ * field because we need to check the key signatures to extract the
+ * key usage. */
+ if (!pk->flags.primary
+ && IS_CERT (sig) && !(pk->pubkey_usage & PUBKEY_USAGE_CERT))
+ {
+ rc = gpg_error (GPG_ERR_WRONG_KEY_USAGE);
+ if (!opt.quiet)
+ log_info (_("bad key signature from key %s: %s (0x%02x, 0x%x)\n"),
+ keystr_from_pk (pk), gpg_strerror (rc),
+ sig->sig_class, pk->pubkey_usage);
+ return rc;
+ }
+
+ /* For data signatures check that the key has sign usage. */
+ if (!IS_BACK_SIG (sig) && IS_SIG (sig)
+ && !(pk->pubkey_usage & PUBKEY_USAGE_SIG))
+ {
+ rc = gpg_error (GPG_ERR_WRONG_KEY_USAGE);
+ if (!opt.quiet)
+ log_info (_("bad data signature from key %s: %s (0x%02x, 0x%x)\n"),
+ keystr_from_pk (pk), gpg_strerror (rc),
+ sig->sig_class, pk->pubkey_usage);
+ return rc;
+ }
+
+ /* Make sure the digest algo is enabled (in case of a detached
+ * signature). */
+ gcry_md_enable (digest, sig->digest_algo);
+
+ /* Complete the digest. */
+ if (sig->version >= 4)
+ gcry_md_putc (digest, sig->version);
+
+ gcry_md_putc( digest, sig->sig_class );
+ if (sig->version < 4)
+ {
+ u32 a = sig->timestamp;
+ gcry_md_putc (digest, ((a >> 24) & 0xff));
+ gcry_md_putc (digest, ((a >> 16) & 0xff));
+ gcry_md_putc (digest, ((a >> 8) & 0xff));
+ gcry_md_putc (digest, ( a & 0xff));
+ }
+ else
+ {
+ byte buf[6];
+ size_t n;
+ gcry_md_putc (digest, sig->pubkey_algo);
+ gcry_md_putc (digest, sig->digest_algo);
+ if (sig->hashed)
+ {
+ n = sig->hashed->len;
+ gcry_md_putc (digest, (n >> 8) );
+ gcry_md_putc (digest, n );
+ gcry_md_write (digest, sig->hashed->data, n);
+ n += 6;
+ }
+ else
+ {
+ /* Two octets for the (empty) length of the hashed
+ * section. */
+ gcry_md_putc (digest, 0);
+ gcry_md_putc (digest, 0);
+ n = 6;
+ }
+ /* Add some magic per Section 5.2.4 of RFC 4880. */
+ buf[0] = sig->version;
+ buf[1] = 0xff;
+ buf[2] = n >> 24;
+ buf[3] = n >> 16;
+ buf[4] = n >> 8;
+ buf[5] = n;
+ gcry_md_write( digest, buf, 6 );
+ }
+ gcry_md_final( digest );
+
+ /* Convert the digest to an MPI. */
+ result = encode_md_value (pk, digest, sig->digest_algo );
+ if (!result)
+ return GPG_ERR_GENERAL;
+
+ /* Verify the signature. */
+ rc = pk_verify (pk->pubkey_algo, result, sig->data, pk->pkey);
+ gcry_mpi_release (result);
+
+ if (!rc && sig->flags.unknown_critical)
+ {
+ log_info(_("assuming bad signature from key %s"
+ " due to an unknown critical bit\n"),keystr_from_pk(pk));
+ rc = GPG_ERR_BAD_SIGNATURE;
+ }
+
+ return rc;
+}
+
+
+/* Add a uid node to a hash context. See section 5.2.4, paragraph 4
+ * of RFC 4880. */
+static void
+hash_uid_packet (PKT_user_id *uid, gcry_md_hd_t md, PKT_signature *sig )
+{
+ if (uid->attrib_data)
+ {
+ if (sig->version >=4)
+ {
+ byte buf[5];
+ buf[0] = 0xd1; /* packet of type 17 */
+ buf[1] = uid->attrib_len >> 24; /* always use 4 length bytes */
+ buf[2] = uid->attrib_len >> 16;
+ buf[3] = uid->attrib_len >> 8;
+ buf[4] = uid->attrib_len;
+ gcry_md_write( md, buf, 5 );
+ }
+ gcry_md_write( md, uid->attrib_data, uid->attrib_len );
+ }
+ else
+ {
+ if (sig->version >=4)
+ {
+ byte buf[5];
+ buf[0] = 0xb4; /* indicates a userid packet */
+ buf[1] = uid->len >> 24; /* always use 4 length bytes */
+ buf[2] = uid->len >> 16;
+ buf[3] = uid->len >> 8;
+ buf[4] = uid->len;
+ gcry_md_write( md, buf, 5 );
+ }
+ gcry_md_write( md, uid->name, uid->len );
+ }
+}
+
+static void
+cache_sig_result ( PKT_signature *sig, int result )
+{
+ if (!result)
+ {
+ sig->flags.checked = 1;
+ sig->flags.valid = 1;
+ }
+ else if (gpg_err_code (result) == GPG_ERR_BAD_SIGNATURE)
+ {
+ sig->flags.checked = 1;
+ sig->flags.valid = 0;
+ }
+ else
+ {
+ sig->flags.checked = 0;
+ sig->flags.valid = 0;
+ }
+}
+
+
+/* SIG is a key revocation signature. Check if this signature was
+ * generated by any of the public key PK's designated revokers.
+ *
+ * PK is the public key that SIG allegedly revokes.
+ *
+ * SIG is the revocation signature to check.
+ *
+ * This function avoids infinite recursion, which can happen if two
+ * keys are designed revokers for each other and they revoke each
+ * other. This is done by observing that if a key A is revoked by key
+ * B we still consider the revocation to be valid even if B is
+ * revoked. Thus, we don't need to determine whether B is revoked to
+ * determine whether A has been revoked by B, we just need to check
+ * the signature.
+ *
+ * Returns 0 if sig is valid (i.e. pk is revoked), non-0 if not
+ * revoked. We are careful to make sure that GPG_ERR_NO_PUBKEY is
+ * only returned when a revocation signature is from a valid
+ * revocation key designated in a revkey subpacket, but the revocation
+ * key itself isn't present.
+ *
+ * XXX: This code will need to be modified if gpg ever becomes
+ * multi-threaded. Note that this guarantees that a designated
+ * revocation sig will never be considered valid unless it is actually
+ * valid, as well as being issued by a revocation key in a valid
+ * direct signature. Note also that this is written so that a revoked
+ * revoker can still issue revocations: i.e. If A revokes B, but A is
+ * revoked, B is still revoked. I'm not completely convinced this is
+ * the proper behavior, but it matches how PGP does it. -dms */
+int
+check_revocation_keys (ctrl_t ctrl, PKT_public_key *pk, PKT_signature *sig)
+{
+ static int busy=0;
+ int i;
+ int rc = GPG_ERR_GENERAL;
+
+ log_assert (IS_KEY_REV(sig));
+ log_assert ((sig->keyid[0]!=pk->keyid[0]) || (sig->keyid[0]!=pk->keyid[1]));
+
+ /* Avoid infinite recursion. Consider the following:
+ *
+ * - We want to check if A is revoked.
+ *
+ * - C is a designated revoker for B and has revoked B.
+ *
+ * - B is a designated revoker for A and has revoked A.
+ *
+ * When checking if A is revoked (in merge_selfsigs_main), we
+ * observe that A has a designed revoker. As such, we call this
+ * function. This function sees that there is a valid revocation
+ * signature, which is signed by B. It then calls check_signature()
+ * to verify that the signature is good. To check the sig, we need
+ * to lookup B. Looking up B means calling merge_selfsigs_main,
+ * which checks whether B is revoked, which calls this function to
+ * see if B was revoked by some key.
+ *
+ * In this case, the added level of indirection doesn't hurt. It
+ * just means a bit more work. However, if C == A, then we'd end up
+ * in a loop. But, it doesn't make sense to look up C anyways: even
+ * if B is revoked, we conservatively consider a valid revocation
+ * signed by B to revoke A. Since this is the only place where this
+ * type of recursion can occur, we simply cause this function to
+ * fail if it is entered recursively. */
+ if (busy)
+ {
+ /* Return an error (i.e. not revoked), but mark the pk as
+ uncacheable as we don't really know its revocation status
+ until it is checked directly. */
+ pk->flags.dont_cache = 1;
+ return rc;
+ }
+
+ busy=1;
+
+ /* es_printf("looking at %08lX with a sig from %08lX\n",(ulong)pk->keyid[1],
+ (ulong)sig->keyid[1]); */
+
+ /* is the issuer of the sig one of our revokers? */
+ if( !pk->revkey && pk->numrevkeys )
+ BUG();
+ else
+ for(i=0;i<pk->numrevkeys;i++)
+ {
+ /* The revoker's keyid. */
+ u32 keyid[2];
+
+ keyid_from_fingerprint (ctrl, pk->revkey[i].fpr,
+ MAX_FINGERPRINT_LEN, keyid);
+
+ if(keyid[0]==sig->keyid[0] && keyid[1]==sig->keyid[1])
+ /* The signature was generated by a designated revoker.
+ Verify the signature. */
+ {
+ gcry_md_hd_t md;
+
+ if (gcry_md_open (&md, sig->digest_algo, 0))
+ BUG ();
+ hash_public_key(md,pk);
+ /* Note: check_signature only checks that the signature
+ is good. It does not fail if the key is revoked. */
+ rc = check_signature (ctrl, sig, md);
+ cache_sig_result(sig,rc);
+ gcry_md_close (md);
+ break;
+ }
+ }
+
+ busy=0;
+
+ return rc;
+}
+
+/* Check that the backsig BACKSIG from the subkey SUB_PK to its
+ * primary key MAIN_PK is valid.
+ *
+ * Backsigs (0x19) have the same format as binding sigs (0x18), but
+ * this function is simpler than check_key_signature in a few ways.
+ * For example, there is no support for expiring backsigs since it is
+ * questionable what such a thing actually means. Note also that the
+ * sig cache check here, unlike other sig caches in GnuPG, is not
+ * persistent. */
+int
+check_backsig (PKT_public_key *main_pk,PKT_public_key *sub_pk,
+ PKT_signature *backsig)
+{
+ gcry_md_hd_t md;
+ int rc;
+
+ /* Always check whether the algorithm is available. Although
+ gcry_md_open would throw an error, some libgcrypt versions will
+ print a debug message in that case too. */
+ if ((rc=openpgp_md_test_algo (backsig->digest_algo)))
+ return rc;
+
+ if(!opt.no_sig_cache && backsig->flags.checked)
+ return backsig->flags.valid? 0 : gpg_error (GPG_ERR_BAD_SIGNATURE);
+
+ rc = gcry_md_open (&md, backsig->digest_algo,0);
+ if (!rc)
+ {
+ hash_public_key(md,main_pk);
+ hash_public_key(md,sub_pk);
+ rc = check_signature_end (sub_pk, backsig, md, NULL, NULL, NULL);
+ cache_sig_result(backsig,rc);
+ gcry_md_close(md);
+ }
+
+ return rc;
+}
+
+
+/* Check that a signature over a key is valid. This is a
+ * specialization of check_key_signature2 with the unnamed parameters
+ * passed as NULL. See the documentation for that function for more
+ * details. */
+int
+check_key_signature (ctrl_t ctrl, kbnode_t root, kbnode_t node,
+ int *is_selfsig)
+{
+ return check_key_signature2 (ctrl, root, node, NULL, NULL,
+ is_selfsig, NULL, NULL);
+}
+
+
+/* Returns whether SIGNER generated the signature SIG over the packet
+ * PACKET, which is a key, subkey or uid, and comes from the key block
+ * KB. (KB is PACKET's corresponding keyblock; we don't assume that
+ * SIG has been added to the keyblock.)
+ *
+ * If SIGNER is set, then checks whether SIGNER generated the
+ * signature. Otherwise, uses SIG->KEYID to find the alleged signer.
+ * This parameter can be used to effectively override the alleged
+ * signer that is stored in SIG.
+ *
+ * KB may be NULL if SIGNER is set.
+ *
+ * Unlike check_key_signature, this function ignores any cached
+ * results! That is, it does not consider SIG->FLAGS.CHECKED and
+ * SIG->FLAGS.VALID nor does it set them.
+ *
+ * This doesn't check the signature's semantic mean. Concretely, it
+ * doesn't check whether a non-self signed revocation signature was
+ * created by a designated revoker. In fact, it doesn't return an
+ * error for a binding generated by a completely different key!
+ *
+ * Returns 0 if the signature is valid. Returns GPG_ERR_SIG_CLASS if
+ * this signature can't be over PACKET. Returns GPG_ERR_NOT_FOUND if
+ * the key that generated the signature (according to SIG) could not
+ * be found. Returns GPG_ERR_BAD_SIGNATURE if the signature is bad.
+ * Other errors codes may be returned if something else goes wrong.
+ *
+ * IF IS_SELFSIG is not NULL, sets *IS_SELFSIG to 1 if this is a
+ * self-signature (by the key's primary key) or 0 if not.
+ *
+ * If RET_PK is not NULL, returns a copy of the public key that
+ * generated the signature (i.e., the signer) on success. This must
+ * be released by the caller using release_public_key_parts (). */
+gpg_error_t
+check_signature_over_key_or_uid (ctrl_t ctrl, PKT_public_key *signer,
+ PKT_signature *sig, KBNODE kb, PACKET *packet,
+ int *is_selfsig, PKT_public_key *ret_pk)
+{
+ int rc;
+ PKT_public_key *pripk = kb->pkt->pkt.public_key;
+ gcry_md_hd_t md;
+ int signer_alloced = 0;
+ int stub_is_selfsig;
+
+ if (!is_selfsig)
+ is_selfsig = &stub_is_selfsig;
+
+ rc = openpgp_pk_test_algo (sig->pubkey_algo);
+ if (rc)
+ return rc;
+ rc = openpgp_md_test_algo (sig->digest_algo);
+ if (rc)
+ return rc;
+
+ /* A signature's class indicates the type of packet that it
+ signs. */
+ if (IS_BACK_SIG (sig) || IS_KEY_SIG (sig) || IS_KEY_REV (sig))
+ {
+ /* Key revocations can only be over primary keys. */
+ if (packet->pkttype != PKT_PUBLIC_KEY)
+ return gpg_error (GPG_ERR_SIG_CLASS);
+ }
+ else if (IS_SUBKEY_SIG (sig) || IS_SUBKEY_REV (sig))
+ {
+ if (packet->pkttype != PKT_PUBLIC_SUBKEY)
+ return gpg_error (GPG_ERR_SIG_CLASS);
+ }
+ else if (IS_UID_SIG (sig) || IS_UID_REV (sig))
+ {
+ if (packet->pkttype != PKT_USER_ID)
+ return gpg_error (GPG_ERR_SIG_CLASS);
+ }
+ else
+ return gpg_error (GPG_ERR_SIG_CLASS);
+
+ /* PACKET is the right type for SIG. */
+
+ if (signer)
+ {
+ if (signer->keyid[0] == pripk->keyid[0]
+ && signer->keyid[1] == pripk->keyid[1])
+ *is_selfsig = 1;
+ else
+ *is_selfsig = 0;
+ }
+ else
+ {
+ /* Get the signer. If possible, avoid a look up. */
+ if (sig->keyid[0] == pripk->keyid[0]
+ && sig->keyid[1] == pripk->keyid[1])
+ {
+ /* Issued by the primary key. */
+ signer = pripk;
+ *is_selfsig = 1;
+ }
+ else
+ {
+ /* See if one of the subkeys was the signer (although this
+ * is extremely unlikely). */
+ kbnode_t ctx = NULL;
+ kbnode_t n;
+
+ while ((n = walk_kbnode (kb, &ctx, 0)))
+ {
+ PKT_public_key *subk;
+
+ if (n->pkt->pkttype != PKT_PUBLIC_SUBKEY)
+ continue;
+
+ subk = n->pkt->pkt.public_key;
+ if (sig->keyid[0] == subk->keyid[0]
+ && sig->keyid[1] == subk->keyid[1])
+ {
+ /* Issued by a subkey. */
+ signer = subk;
+ break;
+ }
+ }
+
+ if (! signer)
+ {
+ /* Signer by some other key. */
+ *is_selfsig = 0;
+ if (ret_pk)
+ {
+ signer = ret_pk;
+ /* FIXME: Using memset here is probematic because it
+ * assumes that there are no allocated fields in
+ * SIGNER. */
+ memset (signer, 0, sizeof (*signer));
+ signer_alloced = 1;
+ }
+ else
+ {
+ signer = xmalloc_clear (sizeof (*signer));
+ signer_alloced = 2;
+ }
+
+ if (IS_CERT (sig))
+ signer->req_usage = PUBKEY_USAGE_CERT;
+
+ rc = get_pubkey_for_sig (ctrl, signer, sig, NULL);
+ if (rc)
+ {
+ xfree (signer);
+ signer = NULL;
+ signer_alloced = 0;
+ goto leave;
+ }
+ }
+ }
+ }
+
+ /* We checked above that we supported this algo, so an error here is
+ * a bug. */
+ if (gcry_md_open (&md, sig->digest_algo, 0))
+ BUG ();
+
+ /* Hash the relevant data. */
+
+ if (IS_KEY_SIG (sig) || IS_KEY_REV (sig))
+ {
+ log_assert (packet->pkttype == PKT_PUBLIC_KEY);
+ hash_public_key (md, packet->pkt.public_key);
+ rc = check_signature_end_simple (signer, sig, md);
+ }
+ else if (IS_BACK_SIG (sig))
+ {
+ log_assert (packet->pkttype == PKT_PUBLIC_KEY);
+ hash_public_key (md, packet->pkt.public_key);
+ hash_public_key (md, signer);
+ rc = check_signature_end_simple (signer, sig, md);
+ }
+ else if (IS_SUBKEY_SIG (sig) || IS_SUBKEY_REV (sig))
+ {
+ log_assert (packet->pkttype == PKT_PUBLIC_SUBKEY);
+ hash_public_key (md, pripk);
+ hash_public_key (md, packet->pkt.public_key);
+ rc = check_signature_end_simple (signer, sig, md);
+ }
+ else if (IS_UID_SIG (sig) || IS_UID_REV (sig))
+ {
+ log_assert (packet->pkttype == PKT_USER_ID);
+ if (sig->digest_algo == DIGEST_ALGO_SHA1 && !*is_selfsig
+ && sig->timestamp > 1547856000
+ && !opt.flags.allow_weak_key_signatures)
+ {
+ /* If the signature was created using SHA-1 we consider this
+ * signature invalid because it makes it possible to mount a
+ * chosen-prefix collision. We don't do this for
+ * self-signatures or for signatures created before the
+ * somewhat arbitrary cut-off date 2019-01-19. */
+ print_sha1_keysig_rejected_note ();
+ rc = gpg_error (GPG_ERR_DIGEST_ALGO);
+ }
+ else
+ {
+ hash_public_key (md, pripk);
+ hash_uid_packet (packet->pkt.user_id, md, sig);
+ rc = check_signature_end_simple (signer, sig, md);
+ }
+ }
+ else
+ {
+ /* We should never get here. (The first if above should have
+ * already caught this error.) */
+ BUG ();
+ }
+
+ gcry_md_close (md);
+
+ leave:
+ if (! rc && ret_pk && ret_pk != signer)
+ copy_public_key (ret_pk, signer);
+
+ if (signer_alloced)
+ {
+ /* We looked up SIGNER; it is not a pointer into KB. */
+ release_public_key_parts (signer);
+ /* Free if we also allocated the memory. */
+ if (signer_alloced == 2)
+ xfree (signer);
+ }
+
+ return rc;
+}
+
+
+/* Check that a signature over a key (e.g., a key revocation, key
+ * binding, user id certification, etc.) is valid. If the function
+ * detects a self-signature, it uses the public key from the specified
+ * key block and does not bother looking up the key specified in the
+ * signature packet.
+ *
+ * ROOT is a keyblock.
+ *
+ * NODE references a signature packet that appears in the keyblock
+ * that should be verified.
+ *
+ * If CHECK_PK is set, the specified key is sometimes preferred for
+ * verifying signatures. See the implementation for details.
+ *
+ * If RET_PK is not NULL, the public key that successfully verified
+ * the signature is copied into *RET_PK.
+ *
+ * If IS_SELFSIG is not NULL, *IS_SELFSIG is set to 1 if NODE is a
+ * self-signature.
+ *
+ * If R_EXPIREDATE is not NULL, *R_EXPIREDATE is set to the expiry
+ * date.
+ *
+ * If R_EXPIRED is not NULL, *R_EXPIRED is set to 1 if PK has been
+ * expired (0 otherwise). Note: PK being revoked does not cause this
+ * function to fail.
+ *
+ *
+ * If OPT.NO_SIG_CACHE is not set, this function will first check if
+ * the result of a previous verification is already cached in the
+ * signature packet's data structure.
+ *
+ * TODO: add r_revoked here as well. It has the same problems as
+ * r_expiredate and r_expired and the cache. */
+int
+check_key_signature2 (ctrl_t ctrl,
+ kbnode_t root, kbnode_t node, PKT_public_key *check_pk,
+ PKT_public_key *ret_pk, int *is_selfsig,
+ u32 *r_expiredate, int *r_expired )
+{
+ PKT_public_key *pk;
+ PKT_signature *sig;
+ int algo;
+ int rc;
+
+ if (is_selfsig)
+ *is_selfsig = 0;
+ if (r_expiredate)
+ *r_expiredate = 0;
+ if (r_expired)
+ *r_expired = 0;
+ log_assert (node->pkt->pkttype == PKT_SIGNATURE);
+ log_assert (root->pkt->pkttype == PKT_PUBLIC_KEY);
+
+ pk = root->pkt->pkt.public_key;
+ sig = node->pkt->pkt.signature;
+ algo = sig->digest_algo;
+
+ /* Check whether we have cached the result of a previous signature
+ * check. Note that we may no longer have the pubkey or hash
+ * needed to verify a sig, but can still use the cached value. A
+ * cache refresh detects and clears these cases. */
+ if ( !opt.no_sig_cache )
+ {
+ cache_stats.total++;
+ if (sig->flags.checked) /* Cached status available. */
+ {
+ cache_stats.cached++;
+ if (is_selfsig)
+ {
+ u32 keyid[2];
+
+ keyid_from_pk (pk, keyid);
+ if (keyid[0] == sig->keyid[0] && keyid[1] == sig->keyid[1])
+ *is_selfsig = 1;
+ }
+ /* BUG: This is wrong for non-self-sigs... needs to be the
+ * actual pk. */
+ rc = check_signature_metadata_validity (pk, sig, r_expired, NULL);
+ if (rc)
+ return rc;
+ if (sig->flags.valid)
+ {
+ cache_stats.goodsig++;
+ return 0;
+ }
+ cache_stats.badsig++;
+ return gpg_error (GPG_ERR_BAD_SIGNATURE);
+ }
+ }
+
+ rc = openpgp_pk_test_algo(sig->pubkey_algo);
+ if (rc)
+ return rc;
+ rc = openpgp_md_test_algo(algo);
+ if (rc)
+ return rc;
+
+ if (IS_KEY_REV (sig))
+ {
+ u32 keyid[2];
+ keyid_from_pk( pk, keyid );
+
+ /* Is it a designated revoker? */
+ if (keyid[0] != sig->keyid[0] || keyid[1] != sig->keyid[1])
+ rc = check_revocation_keys (ctrl, pk, sig);
+ else
+ {
+ rc = check_signature_metadata_validity (pk, sig,
+ r_expired, NULL);
+ if (! rc)
+ rc = check_signature_over_key_or_uid (ctrl, pk, sig,
+ root, root->pkt,
+ is_selfsig, ret_pk);
+ }
+ }
+ else if (IS_SUBKEY_REV (sig) || IS_SUBKEY_SIG (sig))
+ {
+ kbnode_t snode = find_prev_kbnode (root, node, PKT_PUBLIC_SUBKEY);
+
+ if (snode)
+ {
+ rc = check_signature_metadata_validity (pk, sig,
+ r_expired, NULL);
+ if (! rc)
+ {
+ /* A subkey revocation (0x28) must be a self-sig, but a
+ * subkey signature (0x18) needn't be. */
+ rc = check_signature_over_key_or_uid (ctrl,
+ IS_SUBKEY_SIG (sig)
+ ? NULL : pk,
+ sig, root, snode->pkt,
+ is_selfsig, ret_pk);
+ }
+ }
+ else
+ {
+ if (opt.verbose)
+ {
+ if (IS_SUBKEY_REV (sig))
+ log_info (_("key %s: no subkey for subkey"
+ " revocation signature\n"), keystr_from_pk(pk));
+ else if (sig->sig_class == 0x18)
+ log_info(_("key %s: no subkey for subkey"
+ " binding signature\n"), keystr_from_pk(pk));
+ }
+ rc = GPG_ERR_SIG_CLASS;
+ }
+ }
+ else if (IS_KEY_SIG (sig)) /* direct key signature */
+ {
+ rc = check_signature_metadata_validity (pk, sig,
+ r_expired, NULL);
+ if (! rc)
+ rc = check_signature_over_key_or_uid (ctrl, pk, sig, root, root->pkt,
+ is_selfsig, ret_pk);
+ }
+ else if (IS_UID_SIG (sig) || IS_UID_REV (sig))
+ {
+ kbnode_t unode = find_prev_kbnode (root, node, PKT_USER_ID);
+
+ if (unode)
+ {
+ rc = check_signature_metadata_validity (pk, sig, r_expired, NULL);
+ if (! rc)
+ {
+ /* If this is a self-sig, ignore check_pk. */
+ rc = check_signature_over_key_or_uid
+ (ctrl,
+ keyid_cmp (pk_keyid (pk), sig->keyid) == 0 ? pk : check_pk,
+ sig, root, unode->pkt, NULL, ret_pk);
+ }
+ }
+ else
+ {
+ if (!opt.quiet)
+ log_info ("key %s: no user ID for key signature packet"
+ " of class %02x\n",keystr_from_pk(pk),sig->sig_class);
+ rc = GPG_ERR_SIG_CLASS;
+ }
+ }
+ else
+ {
+ log_info ("sig issued by %s with class %d (digest: %02x %02x)"
+ " is not valid over a user id or a key id, ignoring.\n",
+ keystr (sig->keyid), sig->sig_class,
+ sig->digest_start[0], sig->digest_start[1]);
+ rc = gpg_error (GPG_ERR_BAD_SIGNATURE);
+ }
+
+ cache_sig_result (sig, rc);
+
+ return rc;
+}